From a01687d7ad836c24621d8af26c08d760bae7eb0d Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Wed, 12 Oct 2016 09:38:50 -0500 Subject: [PATCH 01/97] Added basic namespace handling. The new code is wrapped in NAMESPACE HANDLING comments. C++: register_namespace(): registers a namespace with an instance of ChaiScript (supports delayed namespace generation) import(): imports a namespace as a global Dynamic_Object ChaiScript: import(): imports a namespace namespace(): generates and registers a new namespace --- .../chaiscript/language/chaiscript_engine.hpp | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 003af4e2..1033d770 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -50,6 +50,10 @@ namespace chaiscript { + // NAMESPACE HANLDING + // Namespace typedef to provide cleaner and more explicit syntax to users. + typedef dispatch::Dynamic_Object Namespace; + // END NAMESPACE HANDLING namespace detail { @@ -74,6 +78,19 @@ namespace chaiscript chaiscript::detail::Dispatch_Engine m_engine; + // NAMESPACE HANDLING + std::map m_namespaces; + std::map> m_namespace_generators; + + /// Helper function to add a namespace to the engine. + void add_namespace(const std::string& t_namespace_name) { + if (m_namespaces.count(t_namespace_name)) { + if (!m_engine.get_scripting_objects().count(t_namespace_name)) // Add namespace if it hasn't already been added. + m_engine.add_global(var(m_namespaces[t_namespace_name]), t_namespace_name); + } + } + // END NAMESPACE HANDLING + /// Evaluates the given string in by parsing it and running the results through the evaluator Boxed_Value do_eval(const std::string &t_input, const std::string &t_filename = "__EVAL__", bool /* t_internal*/ = false) { @@ -184,6 +201,11 @@ namespace chaiscript m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global_const(t_bv, t_name); }), "add_global_const"); m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global(t_bv, t_name); }), "add_global"); m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ set_global(t_bv, t_name); }), "set_global"); + + // NAMESPACE HANDLING + m_engine.add(fun([this](const std::string& t_namespace_name) { register_namespace(t_namespace_name); import(t_namespace_name); }), "namespace"); + m_engine.add(fun([this](const std::string& t_namespace_name) { import(t_namespace_name); }), "import"); + // END NAMESPACE HANDLING } @@ -694,6 +716,57 @@ namespace chaiscript T eval_file(const std::string &t_filename, const Exception_Handler &t_handler = Exception_Handler()) { return m_engine.boxed_cast(eval_file(t_filename, t_handler)); } + + // NAMESPACE HANDLING + /// \brief Imports a namespace object into the global scope of this ChaiScript instance. + void import(const std::string& t_namespace_name) + { + if (m_namespaces.count(t_namespace_name)) + add_namespace(t_namespace_name); + else if (m_namespace_generators.count(t_namespace_name)) { + m_namespace_generators[t_namespace_name](); + add_namespace(t_namespace_name); + } + else + throw std::runtime_error("No registered namespace: " + t_namespace_name); + } + + /// \brief Registers a new namespace with ChaiScript engine. + /// \throw std::runtime_error In the case that the namespace name was already registered. + void register_namespace(const std::string& t_namespace_name) { + if (!m_namespaces.count(t_namespace_name)) + m_namespaces.emplace(std::make_pair(t_namespace_name, dispatch::Dynamic_Object())); + else + throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); + } + + /// \brief Registers a new namespace with ChaiScript engine. + /// \throw std::runtime_error In the case that the namespace name was already registered. + void register_namespace(const dispatch::Dynamic_Object& namespace_object, const std::string& t_namespace_name) { + if (!m_namespaces.count(t_namespace_name)) + m_namespaces.emplace(std::make_pair(t_namespace_name, namespace_object)); + else + throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); + } + + /// \brief Registers a new namespace with ChaiScript engine. Permits move semantics. + /// \throw std::runtime_error In the case that the namespace name was already registered. + void register_namespace(dispatch::Dynamic_Object&& t_namespace_object, const std::string& t_namespace_name) { + if (!m_namespaces.count(t_namespace_name)) + m_namespaces.emplace(std::make_pair(t_namespace_name, t_namespace_object)); + } + + /// \brief Registers a namespace generator, which delays generation of the namespace until it is imported, saving memory if it is never used. + /// \throw std::runtime_error In the case that the namespace name was already registered. + void register_namespace(const std::function& t_namespace_generator, const std::string& t_namespace_name) { + if (!m_namespaces.count(t_namespace_name)) { + std::function namespace_generator = [=]() { register_namespace(t_namespace_generator(), t_namespace_name); }; + m_namespace_generators.emplace(std::make_pair(t_namespace_name, namespace_generator)); + } + else + throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); + } + // END NAMESPACE HANDLING }; } From d2c2962eb7725b80958b9d4e5348be280dffba20 Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Mon, 17 Oct 2016 08:23:51 -0500 Subject: [PATCH 02/97] Added braces to better distinguish case statements. Added more comments to namespace handling functions. Added mutex protection to import. --- .../chaiscript/language/chaiscript_engine.hpp | 75 ++++++++++++------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 1033d770..fe484ca8 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -49,11 +49,9 @@ #include "../dispatchkit/exception_specification.hpp" namespace chaiscript -{ - // NAMESPACE HANLDING - // Namespace typedef to provide cleaner and more explicit syntax to users. - typedef dispatch::Dynamic_Object Namespace; - // END NAMESPACE HANDLING +{ + /// Namespace typedef to provide cleaner and more explicit syntax to users. + typedef dispatch::Dynamic_Object Namespace; namespace detail { @@ -78,18 +76,18 @@ namespace chaiscript chaiscript::detail::Dispatch_Engine m_engine; - // NAMESPACE HANDLING std::map m_namespaces; std::map> m_namespace_generators; /// Helper function to add a namespace to the engine. - void add_namespace(const std::string& t_namespace_name) { + void add_namespace(const std::string& t_namespace_name) + { if (m_namespaces.count(t_namespace_name)) { - if (!m_engine.get_scripting_objects().count(t_namespace_name)) // Add namespace if it hasn't already been added. + if (!m_engine.get_scripting_objects().count(t_namespace_name)) { // Add namespace if it hasn't already been added. m_engine.add_global(var(m_namespaces[t_namespace_name]), t_namespace_name); + } } - } - // END NAMESPACE HANDLING + } /// Evaluates the given string in by parsing it and running the results through the evaluator Boxed_Value do_eval(const std::string &t_input, const std::string &t_filename = "__EVAL__", bool /* t_internal*/ = false) @@ -202,10 +200,8 @@ namespace chaiscript m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global(t_bv, t_name); }), "add_global"); m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ set_global(t_bv, t_name); }), "set_global"); - // NAMESPACE HANDLING m_engine.add(fun([this](const std::string& t_namespace_name) { register_namespace(t_namespace_name); import(t_namespace_name); }), "namespace"); - m_engine.add(fun([this](const std::string& t_namespace_name) { import(t_namespace_name); }), "import"); - // END NAMESPACE HANDLING + m_engine.add(fun([this](const std::string& t_namespace_name) { import(t_namespace_name); }), "import"); } @@ -717,56 +713,77 @@ namespace chaiscript return m_engine.boxed_cast(eval_file(t_filename, t_handler)); } - // NAMESPACE HANDLING /// \brief Imports a namespace object into the global scope of this ChaiScript instance. + /// \param[in] t_namespace_name Name of the namespace to import. + /// \throw std::runtime_error In the case that the namespace name was never registered. void import(const std::string& t_namespace_name) { - if (m_namespaces.count(t_namespace_name)) + chaiscript::detail::threading::shared_lock l(m_mutex); + + if (m_namespaces.count(t_namespace_name)) { add_namespace(t_namespace_name); + } else if (m_namespace_generators.count(t_namespace_name)) { m_namespace_generators[t_namespace_name](); add_namespace(t_namespace_name); } - else + else { throw std::runtime_error("No registered namespace: " + t_namespace_name); + } } /// \brief Registers a new namespace with ChaiScript engine. + /// \param[in] t_namespace_name Name of the namespace to register. /// \throw std::runtime_error In the case that the namespace name was already registered. - void register_namespace(const std::string& t_namespace_name) { - if (!m_namespaces.count(t_namespace_name)) + void register_namespace(const std::string& t_namespace_name) + { + if (!m_namespaces.count(t_namespace_name)) { m_namespaces.emplace(std::make_pair(t_namespace_name, dispatch::Dynamic_Object())); - else + } + else { throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); + } } /// \brief Registers a new namespace with ChaiScript engine. + /// \param[in] t_namespace_object Namespace object to register. + /// \param[in] t_namespace_name Name of the Namespace object being registered. /// \throw std::runtime_error In the case that the namespace name was already registered. - void register_namespace(const dispatch::Dynamic_Object& namespace_object, const std::string& t_namespace_name) { - if (!m_namespaces.count(t_namespace_name)) - m_namespaces.emplace(std::make_pair(t_namespace_name, namespace_object)); - else + void register_namespace(const dispatch::Dynamic_Object& t_namespace_object, const std::string& t_namespace_name) + { + if (!m_namespaces.count(t_namespace_name)) { + m_namespaces.emplace(std::make_pair(t_namespace_name, t_namespace_object)); + } + else { throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); + } } /// \brief Registers a new namespace with ChaiScript engine. Permits move semantics. + /// \param[in] t_namespace_object Namespace object to register. + /// \param[in] t_namespace_name Name of the Namespace object being registered. /// \throw std::runtime_error In the case that the namespace name was already registered. - void register_namespace(dispatch::Dynamic_Object&& t_namespace_object, const std::string& t_namespace_name) { - if (!m_namespaces.count(t_namespace_name)) + void register_namespace(dispatch::Dynamic_Object&& t_namespace_object, const std::string& t_namespace_name) + { + if (!m_namespaces.count(t_namespace_name)) { m_namespaces.emplace(std::make_pair(t_namespace_name, t_namespace_object)); + } } /// \brief Registers a namespace generator, which delays generation of the namespace until it is imported, saving memory if it is never used. + /// \param[in] t_namespace_generator Namespace generator function. + /// \param[in] t_namespace_name Name of the Namespace function being registered. /// \throw std::runtime_error In the case that the namespace name was already registered. - void register_namespace(const std::function& t_namespace_generator, const std::string& t_namespace_name) { + void register_namespace(const std::function& t_namespace_generator, const std::string& t_namespace_name) + { if (!m_namespaces.count(t_namespace_name)) { std::function namespace_generator = [=]() { register_namespace(t_namespace_generator(), t_namespace_name); }; m_namespace_generators.emplace(std::make_pair(t_namespace_name, namespace_generator)); } - else + else { throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); - } - // END NAMESPACE HANDLING + } + } }; } From d61e322c1d14ebf8fa507bc397721b76c10f32e9 Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Tue, 18 Oct 2016 08:53:37 -0500 Subject: [PATCH 03/97] Added unit tests for namespaces. These demonstrate the global scope of namespaces, defining functions and variables within namespaces, and namespace nesting by copy or reference. --- unittests/namespaces.chai | 7 +++++++ unittests/namespaces_nested_copy.chai | 9 +++++++++ unittests/namespaces_nested_ref.chai | 9 +++++++++ 3 files changed, 25 insertions(+) create mode 100644 unittests/namespaces.chai create mode 100644 unittests/namespaces_nested_copy.chai create mode 100644 unittests/namespaces_nested_ref.chai diff --git a/unittests/namespaces.chai b/unittests/namespaces.chai new file mode 100644 index 00000000..51bea60f --- /dev/null +++ b/unittests/namespaces.chai @@ -0,0 +1,7 @@ +namespace("math") + +math.square = fun(x) { x * x } +math.sum_squares = fun(x, y) { math.square(x) + math.square(y) } + +assert_equal(16, math.square(4)) +assert_equal(29, math.sum_squares(2, 5)) \ No newline at end of file diff --git a/unittests/namespaces_nested_copy.chai b/unittests/namespaces_nested_copy.chai new file mode 100644 index 00000000..7ec8ff53 --- /dev/null +++ b/unittests/namespaces_nested_copy.chai @@ -0,0 +1,9 @@ +namespace("parent") +namespace("child") + +child.x = 3.0 +parent.child = child +parent.child.x = 5.0 + +assert_equal(3.0, child.x) +assert_equal(5.0, parent.child.x) \ No newline at end of file diff --git a/unittests/namespaces_nested_ref.chai b/unittests/namespaces_nested_ref.chai new file mode 100644 index 00000000..5be991cb --- /dev/null +++ b/unittests/namespaces_nested_ref.chai @@ -0,0 +1,9 @@ +namespace("parent") +namespace("child") + +child.x = 3.0 +parent.child := child +parent.child.x = 5.0 + +assert_equal(5.0, child.x) +assert_equal(5.0, parent.child.x) \ No newline at end of file From c45be80bf5e5986a878957e28d564d639cc2643f Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Tue, 18 Oct 2016 09:26:28 -0500 Subject: [PATCH 04/97] Added namespace documentation to the cheatsheet.md --- cheatsheet.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/cheatsheet.md b/cheatsheet.md index ab85c167..ea65ca61 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -119,6 +119,18 @@ chai.add_global(chaiscript::var(somevar), "somevar"); // global non-const, throw chai.set_global(chaiscript::var(somevar), "somevar"); // global non-const, overwrites existing object ``` +## Adding Namespaces + +``` +chaiscript::Namespace math; +math["pi"] = chaiscript::const_var(3.14159); +math["sin"] = chaiscript::var(chaiscript::fun([](const double x) { return sin(x); })); +chai.register_namespace(math, "math"); +``` + +Namespaces are imported to make them available for scripting +``` + # Using STL ChaiScript recognize many types from STL, but you have to add specific instantiation yourself. @@ -423,6 +435,20 @@ o.f = fun(y) { print(this.x + y); } o.f(10); // prints 13 ``` +## Namespaces + +Namespaces in ChaiScript are Dynamic Objects with global scope + +``` +namespace("math") // create a new namespace + +math.square = fun(x) { x * x } // add a function to the "math" namespace +math.sum_squares = fun(x, y) { math.square(x) + math.square(y) } + +print(math.square(4)) // prints 16 +print(math.sum_squares(2, 5)) // prints 29 +``` + ### Option Explicit If you want to disable dynamic parameter definitions, you can `set_explicit`. From 96d2eddce1e2374ed53c18b16949505923c2681f Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Tue, 18 Oct 2016 09:29:14 -0500 Subject: [PATCH 05/97] Fixing cheatsheet.md namespace documentation --- cheatsheet.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/cheatsheet.md b/cheatsheet.md index ea65ca61..ab2d8e33 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -129,6 +129,27 @@ chai.register_namespace(math, "math"); ``` Namespaces are imported to make them available for scripting + +``` +chai.import("math"); // importing via C++ (_not generally recommended_) +``` + +``` +import("math") // importing via ChaiScript (_recommended_) +print(math.pi) // prints 3.14159 +``` + +### Delayed Namespace Generation + +Passing a lambda function that returns a namespace will delay the namespace generation until `import` is called. +This saves memory and computing costs if a namespace is not imported into every ChaiScript instance. +``` +chai.register_namespace([]() { + chaiscript::Namespace math; + math["pi"] = chaiscript::const_var(3.14159); + math["sin"] = chaiscript::var(chaiscript::fun([](const double x) { return sin(x); })); + return math; }, + "math"); ``` # Using STL From 9f1ba21c5ebd80c1442c7ecc4d038f9bd77fda0b Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Tue, 18 Oct 2016 09:34:45 -0500 Subject: [PATCH 06/97] Cleaning up namespace documentation --- cheatsheet.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cheatsheet.md b/cheatsheet.md index ab2d8e33..4fe33ff8 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -130,16 +130,18 @@ chai.register_namespace(math, "math"); Namespaces are imported to make them available for scripting +Importing via C++ (_not generally recommended_) ``` -chai.import("math"); // importing via C++ (_not generally recommended_) +chai.import("math"); ``` +Importing via ChaiScript (recommended) ``` -import("math") // importing via ChaiScript (_recommended_) +import("math") print(math.pi) // prints 3.14159 ``` -### Delayed Namespace Generation +#### Delayed Namespace Generation Passing a lambda function that returns a namespace will delay the namespace generation until `import` is called. This saves memory and computing costs if a namespace is not imported into every ChaiScript instance. From 08abf41dfbf08342398405649d901306dd8974d4 Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Tue, 18 Oct 2016 09:37:41 -0500 Subject: [PATCH 07/97] Another cheatsheet.md namespace update --- cheatsheet.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cheatsheet.md b/cheatsheet.md index 4fe33ff8..cbc78bfa 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -128,14 +128,12 @@ math["sin"] = chaiscript::var(chaiscript::fun([](const double x) { return sin(x) chai.register_namespace(math, "math"); ``` -Namespaces are imported to make them available for scripting - -Importing via C++ (_not generally recommended_) +Import namespace via C++ (_not generally recommended_) ``` chai.import("math"); ``` -Importing via ChaiScript (recommended) +Import namespace via ChaiScript (_recommended_) ``` import("math") print(math.pi) // prints 3.14159 From fac5a3906681ad92be9cd775ebffc0dde60cd460 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 23 Feb 2017 17:54:59 -0700 Subject: [PATCH 08/97] Update readme.md for 6.0.0 --- readme.md | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/readme.md b/readme.md index 26efc9a7..58cd1bd1 100644 --- a/readme.md +++ b/readme.md @@ -105,26 +105,3 @@ The shortest complete example possible follows: } - -Or, if you want to compile the std lib into your code, which reduces -runtime requirements. - - /// main.cpp - - #include - #include - - double function(int i, double j) - { - return i * j; - } - - int main() - { - chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); - chai.add(chaiscript::fun(&function), "function"); - - double d = chai.eval("function(3, 4.75);"); - } - - From 534897d8358bd61be8452852a04b00919a77310d Mon Sep 17 00:00:00 2001 From: Ionoclast Laboratories Date: Thu, 2 Mar 2017 11:27:10 -0800 Subject: [PATCH 09/97] Fixes path reference and code formatting in readme. Changes Example.cpp's directory "src" => "samples" to match repo. Change code example from indented quote to highlighted code block. --- readme.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/readme.md b/readme.md index 58cd1bd1..8bf4deec 100644 --- a/readme.md +++ b/readme.md @@ -79,7 +79,7 @@ directory, and for more in-depth look at the language, the unit tests in the "unittests" directory cover the most ground. For examples of how to register parts of your C++ application, see -"example.cpp" in the "src" directory. Example.cpp is verbose and shows every +"example.cpp" in the "samples" directory. Example.cpp is verbose and shows every possible way of working with the library. For further documentation generate the doxygen documentation in the build folder or see the website http://www.chaiscript.com. @@ -87,21 +87,21 @@ http://www.chaiscript.com. The shortest complete example possible follows: - /// main.cpp +```C++ +/// main.cpp - #include +#include - double function(int i, double j) - { - return i * j; - } - - int main() - { - chaiscript::ChaiScript chai; - chai.add(chaiscript::fun(&function), "function"); - - double d = chai.eval("function(3, 4.75);"); - } +double function(int i, double j) +{ + return i * j; +} +int main() +{ + chaiscript::ChaiScript chai; + chai.add(chaiscript::fun(&function), "function"); + double d = chai.eval("function(3, 4.75);"); +} +``` From 698dfb06db216630d3fa2f54a85b7c658059c9e5 Mon Sep 17 00:00:00 2001 From: ftk Date: Sun, 5 Mar 2017 20:54:01 +0300 Subject: [PATCH 10/97] Loadable module support can be disabled by defining CHAISCRIPT_NO_DYNLOAD --- include/chaiscript/language/chaiscript_engine.hpp | 7 ++++--- include/chaiscript/language/chaiscript_unknown.hpp | 4 ++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 5d8b6f0d..27b46981 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -36,12 +36,13 @@ #include #endif -#if defined(_POSIX_VERSION) && !defined(__CYGWIN__) +#if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__) #include #endif - -#ifdef CHAISCRIPT_WINDOWS +#if defined(CHAISCRIPT_NO_DYNLOAD) +#include "chaiscript_unknown.hpp" +#elif defined(CHAISCRIPT_WINDOWS) #include "chaiscript_windows.hpp" #elif _POSIX_VERSION #include "chaiscript_posix.hpp" diff --git a/include/chaiscript/language/chaiscript_unknown.hpp b/include/chaiscript/language/chaiscript_unknown.hpp index 9e1d7afc..8fc1d494 100644 --- a/include/chaiscript/language/chaiscript_unknown.hpp +++ b/include/chaiscript/language/chaiscript_unknown.hpp @@ -16,7 +16,11 @@ namespace chaiscript { Loadable_Module(const std::string &, const std::string &) { +#ifdef CHAISCRIPT_NO_DYNLOAD + throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)"); +#else throw chaiscript::exception::load_module_error("Loadable module support not available for your platform"); +#endif } ModulePtr m_moduleptr; From 84f9c44ab628b9445e2bf62c95609f7625ce244b Mon Sep 17 00:00:00 2001 From: ftk Date: Sun, 5 Mar 2017 21:23:05 +0300 Subject: [PATCH 11/97] Do not register load_module by default when dynamic loading is disabled --- include/chaiscript/chaiscript_defines.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index cc349a0b..be837c26 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -216,7 +216,11 @@ namespace chaiscript { static inline std::vector default_options() { +#ifdef CHAISCRIPT_NO_DYNLOAD + return {Options::No_Load_Modules, Options::External_Scripts}; +#else return {Options::Load_Modules, Options::External_Scripts}; +#endif } } #endif From 72cb9bd940530dbb56ebb68b5fedb14cfe48c05c Mon Sep 17 00:00:00 2001 From: ftk Date: Sun, 5 Mar 2017 21:26:01 +0300 Subject: [PATCH 12/97] Compile out module path search code when module support is disabled --- include/chaiscript/language/chaiscript_engine.hpp | 2 +- samples/fun_call_performance.cpp | 2 ++ src/main.cpp | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 27b46981..16110f00 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -243,7 +243,7 @@ namespace chaiscript m_parser(std::move(parser)), m_engine(*m_parser) { -#if defined(_POSIX_VERSION) && !defined(__CYGWIN__) +#if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__) // If on Unix, add the path of the current executable to the module search path // as windows would do diff --git a/samples/fun_call_performance.cpp b/samples/fun_call_performance.cpp index a519052e..b8bda5ae 100644 --- a/samples/fun_call_performance.cpp +++ b/samples/fun_call_performance.cpp @@ -68,6 +68,7 @@ std::vector default_search_paths() { std::vector paths; +#ifndef CHAISCRIPT_NO_DYNLOAD #ifdef CHAISCRIPT_WINDOWS // force no unicode CHAR path[4096]; int size = GetModuleFileNameA(0, path, sizeof(path) - 1); @@ -137,6 +138,7 @@ std::vector default_search_paths() paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/"); } #endif +#endif // ifndef CHAISCRIPT_NO_DYNLOAD return paths; } diff --git a/src/main.cpp b/src/main.cpp index 4179a6bf..78044e06 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -71,6 +71,7 @@ std::vector default_search_paths() { std::vector paths; +#ifndef CHAISCRIPT_NO_DYNLOAD #ifdef CHAISCRIPT_WINDOWS // force no unicode CHAR path[4096]; int size = GetModuleFileNameA(nullptr, path, sizeof(path)-1); @@ -140,6 +141,7 @@ std::vector default_search_paths() paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/"); } #endif +#endif // ifndef CHAISCRIPT_NO_DYNLOAD return paths; } From c2f7ca3aa220911eb6dde46560c71c0dec9177e2 Mon Sep 17 00:00:00 2001 From: ftk Date: Sun, 5 Mar 2017 21:48:59 +0300 Subject: [PATCH 13/97] Using runtime stdlib constructor will result in compilation error --- include/chaiscript/language/chaiscript_engine.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 16110f00..b2df16bd 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -279,6 +279,7 @@ namespace chaiscript build_eval_system(t_lib, t_opts); } +#ifndef CHAISCRIPT_NO_DYNLOAD /// \brief Constructor for ChaiScript. /// /// This version of the ChaiScript constructor attempts to find the stdlib module to load @@ -308,6 +309,12 @@ namespace chaiscript throw; } } +#else // CHAISCRIPT_NO_DYNLOAD +explicit ChaiScript_Basic(std::unique_ptr &&parser, + std::vector t_module_paths = {}, + std::vector t_use_paths = {}, + const std::vector &t_opts = chaiscript::default_options()) = delete; +#endif parser::ChaiScript_Parser_Base &get_parser() { From 60c43233c68ba52dd4363d8bc92262e5a7c385b6 Mon Sep 17 00:00:00 2001 From: ftk Date: Sun, 5 Mar 2017 21:55:01 +0300 Subject: [PATCH 14/97] More clear error message in load_module --- include/chaiscript/language/chaiscript_engine.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index b2df16bd..37131773 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -556,6 +556,10 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars /// \throw chaiscript::exception::load_module_error In the event that no matching module can be found. std::string load_module(const std::string &t_module_name) { +#ifdef CHAISCRIPT_NO_DYNLOAD + (void)t_module_name; // -Wunused-parameter + throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)"); +#else std::vector errors; std::string version_stripped_name = t_module_name; size_t version_pos = version_stripped_name.find("-" + Build_Info::version()); @@ -589,6 +593,7 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars } throw chaiscript::exception::load_module_error(t_module_name, errors); +#endif } /// \brief Load a binary module from a dynamic library. Works on platforms that support From d22c27b62791584e6a5f910be98b223e7041414c Mon Sep 17 00:00:00 2001 From: ftk Date: Wed, 8 Mar 2017 12:31:30 +0300 Subject: [PATCH 15/97] Added option to disable dynload in cmakelists.txt --- CMakeLists.txt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fbba252b..6409ea51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ ELSE() project(chaiscript) option(MULTITHREAD_SUPPORT_ENABLED "Multithreaded Support Enabled" TRUE) +option(DYNLOAD_ENABLED "Dynamic Loading Support Enabled" TRUE) option(BUILD_MODULES "Build Extra Modules (stl)" TRUE) @@ -221,9 +222,15 @@ if(NOT MULTITHREAD_SUPPORT_ENABLED) add_definitions(-DCHAISCRIPT_NO_THREADS) endif() +if(NOT DYNLOAD_ENABLED) + add_definitions(-DCHAISCRIPT_NO_DYNLOAD) +endif() + if(CMAKE_HOST_UNIX) - if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Haiku") - list(APPEND LIBS "dl") + if(DYNLOAD_ENABLED) + if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Haiku") + list(APPEND LIBS "dl") + endif() endif() if(MULTITHREAD_SUPPORT_ENABLED) From bd4a458c319dbc160d49e38afb4d7ad11ae65b41 Mon Sep 17 00:00:00 2001 From: njlr Date: Wed, 8 Mar 2017 19:47:07 +0000 Subject: [PATCH 16/97] * Added Buck build --- .buckconfig | 0 .gitignore | 5 +++++ BUCK | 14 ++++++++++++++ buckaroo.json | 4 ++++ samples/BUCK | 12 ++++++++++++ 5 files changed, 35 insertions(+) create mode 100644 .buckconfig create mode 100644 .gitignore create mode 100644 BUCK create mode 100644 buckaroo.json create mode 100644 samples/BUCK diff --git a/.buckconfig b/.buckconfig new file mode 100644 index 00000000..e69de29b diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..9e6e4429 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/buck-out/ +/.buckd/ +/buckaroo/ +.buckconfig.local +BUCKAROO_DEPS diff --git a/BUCK b/BUCK new file mode 100644 index 00000000..e01db658 --- /dev/null +++ b/BUCK @@ -0,0 +1,14 @@ +include_defs('//BUCKAROO_DEPS') + +prebuilt_cxx_library( + name = 'chaiscript', + header_only = True, + header_namespace = 'chaiscript', + exported_headers = subdir_glob([ + ('include/chaiscript', '**/*.hpp'), + ]), + visibility = [ + 'PUBLIC', + ], + deps = BUCKAROO_DEPS, +) diff --git a/buckaroo.json b/buckaroo.json new file mode 100644 index 00000000..afc50314 --- /dev/null +++ b/buckaroo.json @@ -0,0 +1,4 @@ +{ + "name": "chaiscript", + "dependencies": {} +} diff --git a/samples/BUCK b/samples/BUCK new file mode 100644 index 00000000..7dd4982c --- /dev/null +++ b/samples/BUCK @@ -0,0 +1,12 @@ +cxx_binary( + name = 'example', + srcs = [ + 'example.cpp', + ], + compiler_flags = [ + '-std=c++14', + ], + deps = [ + '//:chaiscript', + ], +) From 12100cce99e9807d553c2764abb58de55dcf1e95 Mon Sep 17 00:00:00 2001 From: ftk Date: Wed, 8 Mar 2017 12:37:04 +0300 Subject: [PATCH 17/97] Updated travis.yml --- .travis.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index c7a33cf2..1d6e283e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,10 @@ matrix: sudo: false env: GCC_VER="4.9" compiler: gcc + - os: linux + sudo: false + env: GCC_VER="4.9" CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1 + compiler: gcc - os: linux sudo: false env: GCC_VER="5" @@ -30,7 +34,10 @@ matrix: - os: osx compiler: clang osx_image: xcode8 - + - os: osx + compiler: clang + osx_image: xcode8 + env: CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1 env: global: @@ -40,14 +47,14 @@ env: before_install: - if [ "${GCC_VER}" != "" ]; then export CXX="g++-$GCC_VER" CC="gcc-$GCC_VER" GCOV="gcov-$GCC_VER" ; fi - - if [ "${GCC_VER}" == "5" ]; then export CPPCHECK=1 COVERAGE=1 FUZZY_CMD="-D RUN_FUZZY_TESTS:BOOL=TRUE" ; fi + - if [ "${GCC_VER}" == "5" && "${BUILD_ONLY}" != "1"]; then export CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS+=" -D RUN_FUZZY_TESTS:BOOL=TRUE" ; fi - pip install --user cpp-coveralls script: - - cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug $FUZZY_CMD . + - cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug $CMAKE_OPTIONS . - cmake --build . -- -j2 - - ctest - - if [ ${COVERAGE} = 1 ]; then bash <(curl -s https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -x $GCOV -a "-s `pwd`" ; fi + - if [ "${BUILD_ONLY}" != "1" ]; then ctest; fi + - if [ "${COVERAGE}" = "1" ]; then bash <(curl -s https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -x $GCOV -a "-s `pwd`" ; fi #after_script: # - if [ ${CPPCHECK} = 1 ]; then contrib/codeanalysis/runcppcheck.sh ; fi From f53a1ed9513615d15a676062dcd516ae8b5ba7f9 Mon Sep 17 00:00:00 2001 From: ftk Date: Wed, 8 Mar 2017 13:00:34 +0300 Subject: [PATCH 18/97] Fix compilation of multithreaded_test --- unittests/multithreaded_test.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/unittests/multithreaded_test.cpp b/unittests/multithreaded_test.cpp index ff06620a..e2d0240b 100644 --- a/unittests/multithreaded_test.cpp +++ b/unittests/multithreaded_test.cpp @@ -2,6 +2,9 @@ #include +#ifdef CHAISCRIPT_NO_DYNLOAD +#include +#endif #include #include @@ -57,18 +60,22 @@ int main() } std::vector modulepaths; + +#ifdef CHAISCRIPT_NO_DYNLOAD + chaiscript::ChaiScript chai(/* unused */modulepaths, usepaths); +#else modulepaths.push_back(""); if (modulepath) { modulepaths.push_back(modulepath); } - // For this test we are going to load the dynamic stdlib // to make sure it continues to work chaiscript::ChaiScript_Basic chai( std::make_unique>(), modulepaths,usepaths); +#endif std::vector > threads; From 12829ee5d24bbbd7a91910ee002819af0b02a0df Mon Sep 17 00:00:00 2001 From: ftk Date: Sat, 11 Mar 2017 15:42:24 +0300 Subject: [PATCH 19/97] Simplified travis.yml --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1d6e283e..01ab0ea8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ matrix: compiler: gcc - os: linux sudo: false - env: GCC_VER="5" + env: GCC_VER="5" CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" compiler: gcc - os: osx compiler: clang @@ -47,7 +47,6 @@ env: before_install: - if [ "${GCC_VER}" != "" ]; then export CXX="g++-$GCC_VER" CC="gcc-$GCC_VER" GCOV="gcov-$GCC_VER" ; fi - - if [ "${GCC_VER}" == "5" && "${BUILD_ONLY}" != "1"]; then export CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS+=" -D RUN_FUZZY_TESTS:BOOL=TRUE" ; fi - pip install --user cpp-coveralls script: From 561c5bc981ead1fd8f38e5949696382691ea2c2a Mon Sep 17 00:00:00 2001 From: Glen Fraser Date: Tue, 14 Mar 2017 12:01:51 +0100 Subject: [PATCH 20/97] Handle negative numbers in JSONParse::parse_number - fix issue #334, where negative numbers loaded from JSON were being parsed as 0. - add unit tests to cover these cases. --- include/chaiscript/utility/json.hpp | 11 +++++++---- unittests/json_3.chai | 1 + unittests/json_4.chai | 1 + unittests/json_9.chai | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index 96decc09..6fe2e584 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -567,10 +567,13 @@ struct JSONParser { std::string val, exp_str; char c = '\0'; bool isDouble = false; + bool isNegative = false; long exp = 0; for (; offset < str.size() ;) { c = str[offset++]; - if( (c == '-') || (c >= '0' && c <= '9') ) { + if( c == '-' ) { + isNegative = true; + } else if( c >= '0' && c <= '9' ) { val += c; } else if( c == '.' ) { val += c; @@ -608,12 +611,12 @@ struct JSONParser { --offset; if( isDouble ) { - return JSON(chaiscript::parse_num( val ) * std::pow( 10, exp )); + return JSON((isNegative?-1:1) * chaiscript::parse_num( val ) * std::pow( 10, exp )); } else { if( !exp_str.empty() ) { - return JSON(static_cast(chaiscript::parse_num( val )) * std::pow( 10, exp )); + return JSON((isNegative?-1:1) * static_cast(chaiscript::parse_num( val )) * std::pow( 10, exp )); } else { - return JSON(chaiscript::parse_num( val )); + return JSON((isNegative?-1:1) * chaiscript::parse_num( val )); } } } diff --git a/unittests/json_3.chai b/unittests/json_3.chai index d3f222e4..11ce7dcb 100644 --- a/unittests/json_3.chai +++ b/unittests/json_3.chai @@ -1 +1,2 @@ assert_equal(from_json("100"), 100) +assert_equal(from_json("-100"), -100) diff --git a/unittests/json_4.chai b/unittests/json_4.chai index 22d388c7..34ecfa9d 100644 --- a/unittests/json_4.chai +++ b/unittests/json_4.chai @@ -1 +1,2 @@ assert_equal(from_json("1.234"), 1.234) +assert_equal(from_json("-1.234"), -1.234) diff --git a/unittests/json_9.chai b/unittests/json_9.chai index 15127387..d8c64239 100644 --- a/unittests/json_9.chai +++ b/unittests/json_9.chai @@ -1,2 +1,2 @@ -assert_equal(from_json("[1,2,3]"), [1,2,3]) +assert_equal(from_json("[1,-2,3]"), [1,-2,3]) From 491b95099dba0aae2c95d373aea6fc99a77ce3be Mon Sep 17 00:00:00 2001 From: Glen Fraser Date: Tue, 14 Mar 2017 13:01:09 +0100 Subject: [PATCH 21/97] In JSONParser::parse_number(), only allow a single '-' at start - also, don't allow multiple '.' decimal points. Add unit tests to cover these cases. --- include/chaiscript/utility/json.hpp | 10 ++++++---- unittests/json_4.chai | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index 6fe2e584..676b4699 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -569,13 +569,15 @@ struct JSONParser { bool isDouble = false; bool isNegative = false; long exp = 0; + if( offset < str.size() && str[offset] == '-' ) { + isNegative = true; + ++offset; + } for (; offset < str.size() ;) { c = str[offset++]; - if( c == '-' ) { - isNegative = true; - } else if( c >= '0' && c <= '9' ) { + if( c >= '0' && c <= '9' ) { val += c; - } else if( c == '.' ) { + } else if( c == '.' && !isDouble ) { val += c; isDouble = true; } else { diff --git a/unittests/json_4.chai b/unittests/json_4.chai index 34ecfa9d..ae070241 100644 --- a/unittests/json_4.chai +++ b/unittests/json_4.chai @@ -1,2 +1,22 @@ assert_equal(from_json("1.234"), 1.234) assert_equal(from_json("-1.234"), -1.234) + +auto caught = false; +try { + from_json("-1-5.3"); +} +catch(e) { + assert_equal("JSON ERROR: Number: unexpected character '-'", e.what()); + caught = true; +} +assert_equal(caught, true); + +caught = false; +try { + from_json("-15.3.2"); +} +catch(e) { + assert_equal("JSON ERROR: Number: unexpected character '.'", e.what()); + caught = true; +} +assert_equal(caught, true); From be2fec02d9ee3669975affd157b3773782d0fb60 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 21 Mar 2017 10:10:25 -0700 Subject: [PATCH 22/97] Simplify usage of Thread_Specific object --- include/chaiscript/chaiscript_threading.hpp | 24 +++++++++---------- .../chaiscript/dispatchkit/dispatchkit.hpp | 2 +- .../dispatchkit/type_conversions.hpp | 4 +--- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/include/chaiscript/chaiscript_threading.hpp b/include/chaiscript/chaiscript_threading.hpp index cb5e129c..01bda2bb 100644 --- a/include/chaiscript/chaiscript_threading.hpp +++ b/include/chaiscript/chaiscript_threading.hpp @@ -67,44 +67,44 @@ namespace chaiscript class Thread_Storage { public: - - explicit Thread_Storage(void *t_key) - : m_key(t_key) - { - } + Thread_Storage() = default; + Thread_Storage(const Thread_Storage &) = delete; + Thread_Storage(Thread_Storage &&) = delete; + Thread_Storage &operator=(const Thread_Storage &) = delete; + Thread_Storage &operator=(Thread_Storage &&) = delete; ~Thread_Storage() { - t().erase(m_key); + t().erase(this); } inline const T *operator->() const { - return &(t()[m_key]); + return &(t()[this]); } inline const T &operator*() const { - return t()[m_key]; + return t()[this]; } inline T *operator->() { - return &(t()[m_key]); + return &(t()[this]); } inline T &operator*() { - return t()[m_key]; + return t()[this]; } void *m_key; private: - static std::unordered_map &t() + static std::unordered_map &t() { - thread_local static std::unordered_map my_t; + thread_local std::unordered_map my_t; return my_t; } }; diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index c4e61983..d8bfb25b 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -452,7 +452,7 @@ namespace chaiscript }; explicit Dispatch_Engine(chaiscript::parser::ChaiScript_Parser_Base &parser) - : m_stack_holder(this), + : m_stack_holder(), m_parser(parser) { } diff --git a/include/chaiscript/dispatchkit/type_conversions.hpp b/include/chaiscript/dispatchkit/type_conversions.hpp index e16693a5..d9d2f374 100644 --- a/include/chaiscript/dispatchkit/type_conversions.hpp +++ b/include/chaiscript/dispatchkit/type_conversions.hpp @@ -338,9 +338,7 @@ namespace chaiscript : m_mutex(), m_conversions(), m_convertableTypes(), - m_num_types(0), - m_thread_cache(this), - m_conversion_saves(this) + m_num_types(0) { } From 204faa82c188cb9444b5ecdc79ef3d3199f44566 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 21 Mar 2017 11:58:21 -0700 Subject: [PATCH 23/97] Add failing static chaiscript test --- CMakeLists.txt | 3 +++ unittests/static_chaiscript.cpp | 5 +++++ 2 files changed, 8 insertions(+) create mode 100644 unittests/static_chaiscript.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index fbba252b..5e57c177 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -427,6 +427,9 @@ if(BUILD_TESTING) target_link_libraries(compiled_tests ${LIBS} ${CHAISCRIPT_LIBS}) ADD_CATCH_TESTS(compiled_tests) + add_executable(static_chaiscript unittests/static_chaiscript.cpp) + target_link_libraries(static_chaiscript_test ${LIBS}) + add_test(NAME Static_ChaiScript_Test COMMAND static_chaiscript_test) add_executable(boxed_cast_test unittests/boxed_cast_test.cpp) target_link_libraries(boxed_cast_test ${LIBS}) diff --git a/unittests/static_chaiscript.cpp b/unittests/static_chaiscript.cpp new file mode 100644 index 00000000..fb003044 --- /dev/null +++ b/unittests/static_chaiscript.cpp @@ -0,0 +1,5 @@ +#include + +static chaiscript::ChaiScript chai; + +int main() {} From a281d9571e00788784a10bdae054f92cb3e3fb1a Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 21 Mar 2017 11:58:33 -0700 Subject: [PATCH 24/97] Add workaround for chaiscript used as static closes #338 --- include/chaiscript/chaiscript_threading.hpp | 35 +++++++++++++++------ 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/include/chaiscript/chaiscript_threading.hpp b/include/chaiscript/chaiscript_threading.hpp index 01bda2bb..4a255735 100644 --- a/include/chaiscript/chaiscript_threading.hpp +++ b/include/chaiscript/chaiscript_threading.hpp @@ -75,17 +75,19 @@ namespace chaiscript ~Thread_Storage() { - t().erase(this); + if (!destroyed) { + t().erase(this); + } } inline const T *operator->() const { - return &(t()[this]); + return &(t()[const_cast(this)]); } inline const T &operator*() const { - return t()[this]; + return t()[const_cast(this)]; } inline T *operator->() @@ -98,15 +100,30 @@ namespace chaiscript return t()[this]; } - - void *m_key; - private: - static std::unordered_map &t() + struct Map_Holder { + std::unordered_map *, T> map; + + Map_Holder() = default; + Map_Holder(const Map_Holder &) = delete; + Map_Holder(Map_Holder &&) = delete; + Map_Holder& operator=(Map_Holder &&) = delete; + Map_Holder& operator=(const Map_Holder &&) = delete; + ~Map_Holder() { + // here is the theory: + // * If the Map_Holder is destroyed before the Thread_Storage, a flag will get set + // * If destroyed after the Thread_Storage, the * will have been removed from `map` and nothing will happen + for(auto &elem : map) { elem.first->destroyed = true; } + } + }; + + static std::unordered_map *, T> &t() { - thread_local std::unordered_map my_t; - return my_t; + thread_local Map_Holder my_map; + return my_map.map; } + + bool destroyed{false}; }; #else // threading disabled From 9847618cf379c5156d7c0a75c519efa2a1ce2f58 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 21 Mar 2017 12:17:30 -0700 Subject: [PATCH 25/97] Fix use after move during parsing closes #337 --- CMakeLists.txt | 2 +- include/chaiscript/language/chaiscript_parser.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e57c177..c5e2a82b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -427,7 +427,7 @@ if(BUILD_TESTING) target_link_libraries(compiled_tests ${LIBS} ${CHAISCRIPT_LIBS}) ADD_CATCH_TESTS(compiled_tests) - add_executable(static_chaiscript unittests/static_chaiscript.cpp) + add_executable(static_chaiscript_test unittests/static_chaiscript.cpp) target_link_libraries(static_chaiscript_test ${LIBS}) add_test(NAME Static_ChaiScript_Test COMMAND static_chaiscript_test) diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 163d2bb3..f60fc959 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -2551,7 +2551,7 @@ namespace chaiscript if (Statements(true)) { if (m_position.has_more()) { - throw exception::eval_error("Unparsed input", File_Position(m_position.line, m_position.col), t_fname); + throw exception::eval_error("Unparsed input", File_Position(m_position.line, m_position.col), *m_filename); } else { build_match>(0); } From 252ea8072ded9067b8b7af4c53aced817395c35a Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 30 May 2017 08:29:43 -0600 Subject: [PATCH 26/97] Add failing test for const return type #340 --- unittests/compiled_tests.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index c1716ec9..86dcc092 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -12,6 +12,8 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunknown-pragmas" #pragma GCC diagnostic ignored "-Wparentheses" +// This one is necessary for the const return non-reference test +#pragma GCC diagnostic ignored "-Wignored-qualifiers" #endif @@ -1270,3 +1272,20 @@ TEST_CASE("Test reference member being registered") } +const int add_3(const int &i) +{ + return i + 3; +} + +TEST_CASE("Test returning by const non-reference") +{ + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + // Note, C++ will not allow us to do this: + // chai.add(chaiscript::fun(&Reference_MyClass::x) , "x"); + chai.add(chaiscript::fun(&add_3), "add_3"); + auto v = chai.eval("add_3(12)"); + CHECK(v == 15); +} + + + From 5c9b16bdced1b6bc5cae34a326dcbf23fe50f7fd Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 30 May 2017 08:34:17 -0600 Subject: [PATCH 27/97] Fix handling of const return types #430 --- include/chaiscript/dispatchkit/handle_return.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/chaiscript/dispatchkit/handle_return.hpp b/include/chaiscript/dispatchkit/handle_return.hpp index 8243333b..8570c8d0 100644 --- a/include/chaiscript/dispatchkit/handle_return.hpp +++ b/include/chaiscript/dispatchkit/handle_return.hpp @@ -181,9 +181,9 @@ namespace chaiscript template struct Handle_Return { - static Boxed_Value handle(const Ret &r) + static Boxed_Value handle(Ret r) { - return Boxed_Value(std::cref(r)); + return Boxed_Value(std::move(r)); } }; From a999ea3692d43650956ed5dd8c26716121ea0aef Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 30 May 2017 08:34:17 -0600 Subject: [PATCH 28/97] Fix handling of const return types #430 --- include/chaiscript/dispatchkit/handle_return.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/chaiscript/dispatchkit/handle_return.hpp b/include/chaiscript/dispatchkit/handle_return.hpp index 8243333b..8570c8d0 100644 --- a/include/chaiscript/dispatchkit/handle_return.hpp +++ b/include/chaiscript/dispatchkit/handle_return.hpp @@ -181,9 +181,9 @@ namespace chaiscript template struct Handle_Return { - static Boxed_Value handle(const Ret &r) + static Boxed_Value handle(Ret r) { - return Boxed_Value(std::cref(r)); + return Boxed_Value(std::move(r)); } }; From 9f8b57c145da9251e343750f416880092a90e6b5 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 30 May 2017 09:16:20 -0600 Subject: [PATCH 29/97] Enable ChaiScript compilation for C++17 Closes #348 This works by taking into account the fact that `noexcept` is now part of the type system in C++17. However, this would conflict with pre-C++17 compilers, so I have added a feature macro around it --- .../dispatchkit/register_function.hpp | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/include/chaiscript/dispatchkit/register_function.hpp b/include/chaiscript/dispatchkit/register_function.hpp index ef17fb86..e660790d 100644 --- a/include/chaiscript/dispatchkit/register_function.hpp +++ b/include/chaiscript/dispatchkit/register_function.hpp @@ -48,6 +48,7 @@ namespace chaiscript chaiscript::make_shared>(t)); } + template Proxy_Function fun(Ret (*func)(Param...)) { @@ -77,13 +78,45 @@ namespace chaiscript } - template::value>::type*/> Proxy_Function fun(T Class::* m /*, typename std::enable_if::value>::type* = 0*/ ) { return Proxy_Function(chaiscript::make_shared>(m)); } +// only compile this bit if noexcept is part of the type system +// +#if __cpp_noexcept_function_type >= 201510 + template + Proxy_Function fun(Ret (*func)(Param...) noexcept) + { + auto fun_call = dispatch::detail::Fun_Caller(func); + + return Proxy_Function( + chaiscript::make_shared>(fun_call)); + + } + + template + Proxy_Function fun(Ret (Class::*t_func)(Param...) const noexcept) + { + auto call = dispatch::detail::Const_Caller(t_func); + + return Proxy_Function( + chaiscript::make_shared>(call)); + } + + template + Proxy_Function fun(Ret (Class::*t_func)(Param...) noexcept) + { + auto call = dispatch::detail::Caller(t_func); + + return Proxy_Function( + chaiscript::make_shared>(call)); + + } +#endif + From bd736eddec762e6708663607048d1b2b80e65077 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 30 May 2017 11:33:12 -0600 Subject: [PATCH 30/97] Deprecate GLOBAL #247 --- include/chaiscript/language/chaiscript_parser.hpp | 2 +- unittests/global.chai | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index f60fc959..93e69343 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -2172,7 +2172,7 @@ namespace chaiscript throw exception::eval_error("Incomplete variable declaration", File_Position(m_position.line, m_position.col), *m_filename); } - } else if (Keyword("GLOBAL") || Keyword("global")) { + } else if (Keyword("global")) { retval = true; if (!(Reference() || Id(true))) { diff --git a/unittests/global.chai b/unittests/global.chai index 0374e6d3..7f8959a6 100644 --- a/unittests/global.chai +++ b/unittests/global.chai @@ -1,12 +1,12 @@ // Test global -GLOBAL g = 3; +global g = 3; assert_true(g == 3); var v := g; assert_true(v == 3); -GLOBAL g = 2; +global g = 2; assert_true(g == 2); assert_true(v == 2); From 3b48983bc25299896052801b7c3f9608dcdbdb8b Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 31 May 2017 13:54:45 -0600 Subject: [PATCH 31/97] Revert "Add workaround for chaiscript used as static" This reverts commit a281d9571e00788784a10bdae054f92cb3e3fb1a. --- include/chaiscript/chaiscript_threading.hpp | 35 ++++++--------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/include/chaiscript/chaiscript_threading.hpp b/include/chaiscript/chaiscript_threading.hpp index 4a255735..01bda2bb 100644 --- a/include/chaiscript/chaiscript_threading.hpp +++ b/include/chaiscript/chaiscript_threading.hpp @@ -75,19 +75,17 @@ namespace chaiscript ~Thread_Storage() { - if (!destroyed) { - t().erase(this); - } + t().erase(this); } inline const T *operator->() const { - return &(t()[const_cast(this)]); + return &(t()[this]); } inline const T &operator*() const { - return t()[const_cast(this)]; + return t()[this]; } inline T *operator->() @@ -100,30 +98,15 @@ namespace chaiscript return t()[this]; } + + void *m_key; + private: - struct Map_Holder { - std::unordered_map *, T> map; - - Map_Holder() = default; - Map_Holder(const Map_Holder &) = delete; - Map_Holder(Map_Holder &&) = delete; - Map_Holder& operator=(Map_Holder &&) = delete; - Map_Holder& operator=(const Map_Holder &&) = delete; - ~Map_Holder() { - // here is the theory: - // * If the Map_Holder is destroyed before the Thread_Storage, a flag will get set - // * If destroyed after the Thread_Storage, the * will have been removed from `map` and nothing will happen - for(auto &elem : map) { elem.first->destroyed = true; } - } - }; - - static std::unordered_map *, T> &t() + static std::unordered_map &t() { - thread_local Map_Holder my_map; - return my_map.map; + thread_local std::unordered_map my_t; + return my_t; } - - bool destroyed{false}; }; #else // threading disabled From bdd0a12bb71cd5dd3ac47152e9ec5ffb44f1dca4 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 31 May 2017 14:09:07 -0600 Subject: [PATCH 32/97] ChaiScript can only support static in non-threading mode --- cheatsheet.md | 3 +++ include/chaiscript/chaiscript_threading.hpp | 2 +- unittests/static_chaiscript.cpp | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cheatsheet.md b/cheatsheet.md index 2e2c218f..8cf3d3f4 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -15,6 +15,9 @@ chaiscript::ChaiScript chai; // loads stdlib from loadable module on file system chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); // compiles in stdlib ``` +Note that ChaiScript cannot be used as a global / static object unless it is being compiled with `CHAISCRIPT_NO_THREADS`. + + # Adding Things To The Engine ## Adding a Function / Method / Member diff --git a/include/chaiscript/chaiscript_threading.hpp b/include/chaiscript/chaiscript_threading.hpp index 01bda2bb..79feaa1e 100644 --- a/include/chaiscript/chaiscript_threading.hpp +++ b/include/chaiscript/chaiscript_threading.hpp @@ -144,7 +144,7 @@ namespace chaiscript class Thread_Storage { public: - explicit Thread_Storage(void *) + explicit Thread_Storage() { } diff --git a/unittests/static_chaiscript.cpp b/unittests/static_chaiscript.cpp index fb003044..beeac2d1 100644 --- a/unittests/static_chaiscript.cpp +++ b/unittests/static_chaiscript.cpp @@ -1,3 +1,9 @@ + +#define CHAISCRIPT_NO_THREADS + +/// ChaiScript as a static is unsupported with thread support enabled +/// + #include static chaiscript::ChaiScript chai; From ab90c6171063ed0e6d145c8cb35e74161e1ad380 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 31 May 2017 14:36:09 -0600 Subject: [PATCH 33/97] Add gcc6/7 to travis --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.travis.yml b/.travis.yml index 01ab0ea8..4a1202bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,8 @@ addons: packages: - g++-4.9 - g++-5 + - g++-6 + - g++-7 coverity_scan: project: name: "ChaiScript/ChaiScript" @@ -31,6 +33,14 @@ matrix: sudo: false env: GCC_VER="5" CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" compiler: gcc + - os: linux + sudo: false + env: GCC_VER="6" CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" + compiler: gcc + - os: linux + sudo: false + env: GCC_VER="7" CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" + compiler: gcc - os: osx compiler: clang osx_image: xcode8 From 562ca5aee6f21703e37012648a870156e6c0178e Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 31 May 2017 14:44:14 -0600 Subject: [PATCH 34/97] gcc-6 only, not 7 yet --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4a1202bf..0aadff1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,10 +37,6 @@ matrix: sudo: false env: GCC_VER="6" CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" compiler: gcc - - os: linux - sudo: false - env: GCC_VER="7" CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" - compiler: gcc - os: osx compiler: clang osx_image: xcode8 From 94f7bfec2b7a09a1e3fda8afbcfa057ecb084c9b Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 31 May 2017 14:46:20 -0600 Subject: [PATCH 35/97] Remove gcc-7 from package list for travis --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0aadff1e..7471e1ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,6 @@ addons: - g++-4.9 - g++-5 - g++-6 - - g++-7 coverity_scan: project: name: "ChaiScript/ChaiScript" From d720d069ca3f6656c5d9779925915031e44e3422 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 31 May 2017 14:56:51 -0600 Subject: [PATCH 36/97] Attempt to add visual studio 2017 to appveyor --- appveyor.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 50c59211..042e1e59 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,15 +1,17 @@ version: 5.8.x.{build} -os: Visual Studio 2015 +image: + - Visual Studio 2017 environment: matrix: - - {} + - VS_VERSION: "Visual Studio 14" + - VS_VERSION: "Visual Studio 15" build_script: - cmd: >- mkdir build cd build - cmake c:\Projects\chaiscript -G "Visual Studio 14" + cmake c:\Projects\chaiscript -G "%VS_VERSION%" cmake --build . --config Debug test_script: From 76c7712507109d6e2a512d7090d9aacf86e18fe4 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Mon, 5 Jun 2017 19:36:40 -0600 Subject: [PATCH 37/97] Test custom exception handling #351 --- unittests/compiled_tests.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 86dcc092..4cb09da7 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -1288,4 +1288,32 @@ TEST_CASE("Test returning by const non-reference") } +struct MyException : std::runtime_error +{ + using std::runtime_error::runtime_error; + int value = 5; +}; + +void throws_a_thing() +{ + throw MyException("Hello World"); +} + +TEST_CASE("Test throwing and catching custom exception") +{ + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + chai.add(chaiscript::user_type(), "MyException"); + chai.add(chaiscript::base_class()); // be sure to register base class relationship + chai.add(chaiscript::fun(&throws_a_thing), "throws_a_thing"); + chai.add(chaiscript::fun(&MyException::value), "value"); + + const auto s = chai.eval("fun(){ try { throws_a_thing(); } catch (MyException ex) { return ex.what(); } }()"); + CHECK(s == "Hello World"); + + // this has an explicit clone to prevent returning a pointer to the `value` from inside of MyException + const auto i = chai.eval("fun(){ try { throws_a_thing(); } catch (MyException ex) { var v = clone(ex.value); print(v); return v; } }()"); + CHECK(i == 5); +} + + From 36e61dec0a1412ba2a09e15c6f45785f24915daf Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 6 Jun 2017 14:59:05 -0600 Subject: [PATCH 38/97] Fix defaults for dynload options --- include/chaiscript/chaiscript.hpp | 2 +- unittests/compiled_tests.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/chaiscript/chaiscript.hpp b/include/chaiscript/chaiscript.hpp index 2d28c962..dbf6d73b 100644 --- a/include/chaiscript/chaiscript.hpp +++ b/include/chaiscript/chaiscript.hpp @@ -832,7 +832,7 @@ namespace chaiscript public: ChaiScript(std::vector t_modulepaths = {}, std::vector t_usepaths = {}, - const std::vector &t_opts = {}) + const std::vector &t_opts = chaiscript::default_options()) : ChaiScript_Basic( chaiscript::Std_Lib::library(), std::make_unique>(), diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 4cb09da7..6e241572 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -1316,4 +1316,10 @@ TEST_CASE("Test throwing and catching custom exception") } +TEST_CASE("Test ability to get 'use' function from default construction") +{ + chaiscript::ChaiScript chai; + const auto use_function = chai.eval>("use"); +} + From 700a6205521f81105f19c30618664e7b93add5a1 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 6 Jun 2017 16:47:23 -0600 Subject: [PATCH 39/97] Add option to compile in C++17 mode for testing --- CMakeLists.txt | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e152d71..048c2e54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ option(BUILD_SAMPLES "Build Samples Folder" FALSE) option(RUN_FUZZY_TESTS "Run tests generated by AFL" FALSE) option(USE_STD_MAKE_SHARED "Use std::make_shared instead of chaiscript::make_shared" FALSE) option(RUN_PERFORMANCE_TESTS "Run Performance Tests" FALSE) +option(BUILD_IN_CPP17_MODE "Build with C++17 flags" FALSE) mark_as_advanced(USE_STD_MAKE_SHARED) @@ -150,12 +151,20 @@ if(CMAKE_COMPILER_IS_GNUCC) execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) if(GCC_VERSION VERSION_LESS 4.9) - set(CPP11_FLAG "-std=c++1y") + set(CPP14_FLAG "-std=c++1y") else() - set(CPP11_FLAG "-std=c++14") + if (BUILD_IN_CPP17_MODE) + set(CPP14_FLAG "-std=c++1z") + else() + set(CPP14_FLAG "-std=c++14") + endif() endif() else() - set(CPP11_FLAG "-std=c++14") + if (BUILD_IN_CPP17_MODE) + set(CPP14_FLAG "-std=c++1z") + else() + set(CPP14_FLAG "-std=c++14") + endif() endif() if(MSVC) @@ -179,7 +188,7 @@ if(MSVC) # how to workaround or fix the error. So I'm disabling it globally. add_definitions(/wd4503) else() - add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -pedantic ${CPP11_FLAG}) + add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -pedantic ${CPP14_FLAG}) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors -Wno-documentation-unknown-command) @@ -197,12 +206,12 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") if(USE_LIBCXX) add_definitions(-stdlib=libc++) - set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG} -stdlib=libc++") + set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG} -stdlib=libc++") else() - set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG}") + set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG}") endif() elseif(CMAKE_COMPILER_IS_GNUCC) - set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP11_FLAG}") + set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG}") endif() # limitations in MinGW require us to make an optimized build From 5a5600914c47a8c8cadb7c19d1b2f600a9a4aa55 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 21 Jun 2017 21:27:48 -0600 Subject: [PATCH 40/97] Move away from shared_ptr for parse nodes --- include/chaiscript/chaiscript_defines.hpp | 10 + include/chaiscript/dispatchkit/bootstrap.hpp | 10 +- .../dispatchkit/proxy_functions.hpp | 14 +- .../chaiscript/language/chaiscript_common.hpp | 93 ++++++-- .../chaiscript/language/chaiscript_engine.hpp | 4 +- .../chaiscript/language/chaiscript_eval.hpp | 224 ++++++++++-------- .../language/chaiscript_optimizer.hpp | 188 ++++++++------- .../chaiscript/language/chaiscript_parser.hpp | 54 ++--- src/main.cpp | 2 +- 9 files changed, 357 insertions(+), 242 deletions(-) diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index be837c26..5b2e84f4 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -93,6 +93,16 @@ namespace chaiscript { #endif } + template + inline std::unique_ptr make_unique(Arg && ... arg) + { +#ifdef CHAISCRIPT_USE_STD_MAKE_SHARED + return std::make_unique(std::forward(arg)...); +#else + return std::unique_ptr(static_cast(new D(std::forward(arg)...))); +#endif + } + struct Build_Info { static int version_major() { diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index cbb91a51..8aedb353 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -305,13 +305,13 @@ namespace chaiscript static bool has_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) { const auto pf = std::dynamic_pointer_cast(t_pf); - return pf && pf->get_parse_tree(); + return bool(pf); } - static chaiscript::AST_NodePtr get_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) + static const chaiscript::AST_Node &get_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) { const auto pf = std::dynamic_pointer_cast(t_pf); - if (pf && pf->get_parse_tree()) + if (pf) { return pf->get_parse_tree(); } else { @@ -545,7 +545,7 @@ namespace chaiscript std::vector retval; std::transform(t_eval_error.call_stack.begin(), t_eval_error.call_stack.end(), std::back_inserter(retval), - &chaiscript::var &>); + &chaiscript::var); return retval; }), "call_stack"} } ); @@ -574,7 +574,7 @@ namespace chaiscript const auto children = t_node.get_children(); std::transform(children.begin(), children.end(), std::back_inserter(retval), - &chaiscript::var &>); + &chaiscript::var &>); return retval; }), "children"} } diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index bb57c698..2afda8dd 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -41,7 +41,7 @@ namespace chaiscript class Boxed_Number; struct AST_Node; - typedef std::shared_ptr AST_NodePtr; + typedef std::unique_ptr AST_NodePtr; namespace dispatch { @@ -346,8 +346,8 @@ namespace chaiscript { public: Dynamic_Proxy_Function( - int t_arity=-1, - AST_NodePtr t_parsenode = AST_NodePtr(), + const int t_arity, + std::shared_ptr t_parsenode, Param_Types t_param_types = Param_Types(), Proxy_Function t_guard = Proxy_Function()) : Proxy_Function_Base(build_param_type_list(t_param_types), t_arity), @@ -379,9 +379,9 @@ namespace chaiscript return m_guard; } - AST_NodePtr get_parse_tree() const + const AST_Node &get_parse_tree() const { - return m_parsenode; + return *m_parsenode; } @@ -445,7 +445,7 @@ namespace chaiscript private: Proxy_Function m_guard; - AST_NodePtr m_parsenode; + std::shared_ptr m_parsenode; }; @@ -457,7 +457,7 @@ namespace chaiscript Dynamic_Proxy_Function_Impl( Callable t_f, int t_arity=-1, - AST_NodePtr t_parsenode = AST_NodePtr(), + std::shared_ptr t_parsenode = AST_NodePtr(), Param_Types t_param_types = Param_Types(), Proxy_Function t_guard = Proxy_Function()) : Dynamic_Proxy_Function( diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 602efd54..5fe377c4 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -124,8 +124,10 @@ namespace chaiscript /// \brief Typedef for pointers to AST_Node objects. Used in building of the AST_Node tree - typedef std::shared_ptr AST_NodePtr; - typedef std::shared_ptr AST_NodePtr_Const; + typedef std::unique_ptr AST_NodePtr; + typedef std::unique_ptr AST_NodePtr_Const; + + struct AST_Node_Trace; /// \brief Classes which may be thrown during error cases when ChaiScript is executing. @@ -168,7 +170,7 @@ namespace chaiscript File_Position start_position; std::string filename; std::string detail; - std::vector call_stack; + std::vector call_stack; eval_error(const std::string &t_why, const File_Position &t_where, const std::string &t_fname, const std::vector &t_parameters, const std::vector &t_functions, @@ -228,26 +230,26 @@ namespace chaiscript template static AST_Node_Type id(const T& t) { - return t->identifier; + return t.identifier; } template static std::string pretty(const T& t) { - return t->pretty_print(); + return t.pretty_print(); } template static const std::string &fname(const T& t) { - return t->filename(); + return t.filename(); } template static std::string startpos(const T& t) { std::ostringstream oss; - oss << t->start().line << ", " << t->start().column; + oss << t.start().line << ", " << t.start().column; return oss.str(); } @@ -321,7 +323,7 @@ namespace chaiscript } } - retval += "\n Defined at " + format_location(dynfun->get_parse_tree()); + retval += "\n Defined at " + format_location(dynfun->get_parse_tree()); } return retval; @@ -330,20 +332,15 @@ namespace chaiscript template static std::string format_guard(const T &t) { - return t->pretty_print(); + return t.pretty_print(); } template static std::string format_location(const T &t) { - if (t) { - std::ostringstream oss; - oss << "(" << t->filename() << " " << t->start().line << ", " << t->start().column << ")"; - return oss.str(); - } else { - return "(internal)"; - } - + std::ostringstream oss; + oss << "(" << t.filename() << " " << t.start().line << ", " << t.start().column << ")"; + return oss.str(); } static std::string format_detail(const std::vector &t_functions, @@ -492,7 +489,7 @@ namespace chaiscript /// \brief Struct that doubles as both a parser ast_node and an AST node. - struct AST_Node : std::enable_shared_from_this { + struct AST_Node { public: const AST_Node_Type identifier; const std::string text; @@ -516,14 +513,14 @@ namespace chaiscript oss << text; - for (auto & elem : this->get_children()) { - oss << elem->pretty_print() << ' '; + for (auto & elem : get_children()) { + oss << elem.get().pretty_print() << ' '; } return oss.str(); } - virtual std::vector get_children() const = 0; + virtual std::vector> get_children() const = 0; virtual Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const = 0; @@ -534,8 +531,8 @@ namespace chaiscript oss << t_prepend << "(" << ast_node_type_to_string(this->identifier) << ") " << this->text << " : " << this->location.start.line << ", " << this->location.start.column << '\n'; - for (auto & elem : this->get_children()) { - oss << elem->to_string(t_prepend + " "); + for (auto & elem : get_children()) { + oss << elem.get().to_string(t_prepend + " "); } return oss.str(); } @@ -568,12 +565,60 @@ namespace chaiscript }; + struct AST_Node_Trace + { + const AST_Node_Type identifier; + const std::string text; + Parse_Location location; + + const std::string &filename() const { + return *location.filename; + } + + const File_Position &start() const { + return location.start; + } + + const File_Position &end() const { + return location.end; + } + + std::string pretty_print() const + { + std::ostringstream oss; + + oss << text; + + for (const auto & elem : children) { + oss << elem.pretty_print() << ' '; + } + + return oss.str(); + } + + std::vector get_children(const AST_Node &node) + { + const auto node_children = node.get_children(); + return std::vector(node_children.begin(), node_children.end()); + } + + AST_Node_Trace(const AST_Node &node) + : identifier(node.identifier), text(node.text), + location(node.location), children(get_children(node)) + { + } + + + std::vector children; + + }; + namespace parser { class ChaiScript_Parser_Base { public: virtual AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) = 0; - virtual void debug_print(AST_NodePtr t, std::string prepend = "") const = 0; + virtual void debug_print(const AST_Node &t, std::string prepend = "") const = 0; virtual void *get_tracer_ptr() = 0; virtual ~ChaiScript_Parser_Base() = default; ChaiScript_Parser_Base() = default; diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 37131773..dc0b8536 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -332,9 +332,9 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars AST_NodePtr parse(const std::string &t_input, const bool t_debug_print = false) { - const auto ast = m_parser->parse(t_input, "PARSE"); + auto ast = m_parser->parse(t_input, "PARSE"); if (t_debug_print) { - m_parser->debug_print(ast); + m_parser->debug_print(*ast); } return ast; } diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 95e2218e..4388649d 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -47,13 +47,13 @@ namespace chaiscript { template struct AST_Node_Impl; - template using AST_Node_Impl_Ptr = typename std::shared_ptr>; + template using AST_Node_Impl_Ptr = typename std::unique_ptr>; namespace detail { /// Helper function that will set up the scope around a function call, including handling the named function parameters template - static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_Node_Impl_Ptr &t_node, const std::vector &t_param_names, const std::vector &t_vals, const std::map *t_locals=nullptr, bool has_this_capture = false) { + static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_Node_Impl &t_node, const std::vector &t_param_names, const std::vector &t_vals, const std::map *t_locals=nullptr, bool has_this_capture = false) { chaiscript::detail::Dispatch_State state(t_ss); const Boxed_Value *thisobj = [&]() -> const Boxed_Value *{ @@ -83,7 +83,7 @@ namespace chaiscript } try { - return t_node->eval(state); + return t_node.eval(state); } catch (detail::Return_Value &rv) { return std::move(rv.retval); } @@ -106,8 +106,14 @@ namespace chaiscript } - std::vector get_children() const final { - return {children.begin(), children.end()}; + std::vector> get_children() const final { + std::vector> retval; + retval.reserve(children.size()); + for (auto &&child : children) { + retval.emplace_back(*child); + } + + return retval; } Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const final @@ -116,7 +122,7 @@ namespace chaiscript T::trace(t_e, this); return eval_internal(t_e); } catch (exception::eval_error &ee) { - ee.call_stack.push_back(shared_from_this()); + ee.call_stack.push_back(*this); throw; } } @@ -364,44 +370,44 @@ namespace chaiscript AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { } - static std::string get_arg_name(const AST_Node_Impl_Ptr &t_node) { - if (t_node->children.empty()) + static std::string get_arg_name(const AST_Node_Impl &t_node) { + if (t_node.children.empty()) { - return t_node->text; - } else if (t_node->children.size() == 1) { - return t_node->children[0]->text; + return t_node.text; + } else if (t_node.children.size() == 1) { + return t_node.children[0]->text; } else { - return t_node->children[1]->text; + return t_node.children[1]->text; } } - static std::vector get_arg_names(const AST_Node_Impl_Ptr &t_node) { + static std::vector get_arg_names(const AST_Node_Impl &t_node) { std::vector retval; - for (const auto &node : t_node->children) + for (const auto &node : t_node.children) { - retval.push_back(get_arg_name(node)); + retval.push_back(get_arg_name(*node)); } return retval; } - static std::pair get_arg_type(const AST_Node_Impl_Ptr &t_node, const chaiscript::detail::Dispatch_State &t_ss) + static std::pair get_arg_type(const AST_Node_Impl &t_node, const chaiscript::detail::Dispatch_State &t_ss) { - if (t_node->children.size() < 2) + if (t_node.children.size() < 2) { return {}; } else { - return {t_node->children[0]->text, t_ss->get_type(t_node->children[0]->text, false)}; + return {t_node.children[0]->text, t_ss->get_type(t_node.children[0]->text, false)}; } } - static dispatch::Param_Types get_arg_types(const AST_Node_Impl_Ptr &t_node, const chaiscript::detail::Dispatch_State &t_ss) { + static dispatch::Param_Types get_arg_types(const AST_Node_Impl &t_node, const chaiscript::detail::Dispatch_State &t_ss) { std::vector> retval; - for (const auto &child : t_node->children) + for (const auto &child : t_node.children) { - retval.push_back(get_arg_type(child, t_ss)); + retval.push_back(get_arg_type(*child, t_ss)); } return dispatch::Param_Types(std::move(retval)); @@ -621,9 +627,15 @@ namespace chaiscript template struct Lambda_AST_Node final : AST_Node_Impl { Lambda_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(t_ast_node_text, AST_Node_Type::Lambda, std::move(t_loc), std::move(t_children)), - m_param_names(Arg_List_AST_Node::get_arg_names(this->children[1])), - m_this_capture(has_this_capture(this->children[0]->children)) + AST_Node_Impl(t_ast_node_text, + AST_Node_Type::Lambda, + std::move(t_loc), + std::vector>(std::make_move_iterator(t_children.begin()), + std::make_move_iterator(std::prev(t_children.end()))) + ), + m_param_names(Arg_List_AST_Node::get_arg_names(*this->children[1])), + m_this_capture(has_this_capture(this->children[0]->children)), + m_lambda_node(std::move(t_children.back())) { } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { @@ -637,18 +649,18 @@ namespace chaiscript }(); const auto numparams = this->children[1]->children.size(); - const auto param_types = Arg_List_AST_Node::get_arg_types(this->children[1], t_ss); + const auto param_types = Arg_List_AST_Node::get_arg_types(*this->children[1], t_ss); - const auto &lambda_node = this->children.back(); std::reference_wrapper engine(*t_ss); return Boxed_Value( dispatch::make_dynamic_proxy_function( - [engine, lambda_node, param_names = this->m_param_names, captures, this_capture = this->m_this_capture](const std::vector &t_params) + [engine, lambda_node = this->m_lambda_node, param_names = this->m_param_names, captures, + this_capture = this->m_this_capture] (const std::vector &t_params) { - return detail::eval_function(engine, lambda_node, param_names, t_params, &captures, this_capture); + return detail::eval_function(engine, *lambda_node, param_names, t_params, &captures, this_capture); }, - static_cast(numparams), lambda_node, param_types + static_cast(numparams), m_lambda_node, param_types ) ); } @@ -664,7 +676,7 @@ namespace chaiscript private: const std::vector m_param_names; const bool m_this_capture = false; - + const std::shared_ptr> m_lambda_node; }; template @@ -699,55 +711,81 @@ namespace chaiscript template struct Def_AST_Node final : AST_Node_Impl { + + std::shared_ptr> m_body_node; + std::shared_ptr> m_guard_node; + Def_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Def, std::move(t_loc), std::move(t_children)) { } + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Def, std::move(t_loc), + std::vector>(std::make_move_iterator(t_children.begin()), + std::make_move_iterator(std::prev(t_children.end(), has_guard(t_children)?2:1))) + ), + m_body_node(get_body_node(std::move(t_children))), + m_guard_node(get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2)) + + { } + + static std::shared_ptr> get_guard_node(std::vector> &&vec, bool has_guard) + { + if (has_guard) { + return std::move(*std::prev(vec.end(), 2)); + } else { + return {}; + } + } + + static std::shared_ptr> get_body_node(std::vector> &&vec) + { + return std::move(vec.back()); + } + + static bool has_guard(const std::vector> &t_children) + { + if ((t_children.size() > 2) && (t_children[1]->identifier == AST_Node_Type::Arg_List)) { + if (t_children.size() > 3) { + return true; + } + } + else { + if (t_children.size() > 2) { + return true; + } + } + return false; + } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ std::vector t_param_names; size_t numparams = 0; - AST_Node_Impl_Ptr guardnode; dispatch::Param_Types param_types; - if ((this->children.size() > 2) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) { + if ((this->children.size() > 1) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) { numparams = this->children[1]->children.size(); - t_param_names = Arg_List_AST_Node::get_arg_names(this->children[1]); - param_types = Arg_List_AST_Node::get_arg_types(this->children[1], t_ss); - - if (this->children.size() > 3) { - guardnode = this->children[2]; - } - } - else { - //no parameters - numparams = 0; - - if (this->children.size() > 2) { - guardnode = this->children[1]; - } + t_param_names = Arg_List_AST_Node::get_arg_names(*this->children[1]); + param_types = Arg_List_AST_Node::get_arg_types(*this->children[1], t_ss); } std::reference_wrapper engine(*t_ss); std::shared_ptr guard; - if (guardnode) { + if (m_guard_node) { guard = dispatch::make_dynamic_proxy_function( - [engine, guardnode, t_param_names](const std::vector &t_params) + [engine, guardnode = m_guard_node, t_param_names](const std::vector &t_params) { - return detail::eval_function(engine, guardnode, t_param_names, t_params); + return detail::eval_function(engine, *guardnode, t_param_names, t_params); }, - static_cast(numparams), guardnode); + static_cast(numparams), m_guard_node); } try { const std::string & l_function_name = this->children[0]->text; - const auto & func_node = this->children.back(); t_ss->add( dispatch::make_dynamic_proxy_function( - [engine, guardnode, func_node, t_param_names](const std::vector &t_params) + [engine, func_node = m_body_node, t_param_names](const std::vector &t_params) { - return detail::eval_function(engine, func_node, t_param_names, t_params); + return detail::eval_function(engine, *func_node, t_param_names, t_params); }, - static_cast(numparams), this->children.back(), + static_cast(numparams), m_body_node, param_types, guard), l_function_name); } catch (const exception::name_conflict_error &e) { throw exception::eval_error("Function redefined '" + e.name() + "'"); @@ -1230,32 +1268,32 @@ namespace chaiscript } for (size_t i = 1; i < end_point; ++i) { chaiscript::eval::detail::Scope_Push_Pop catch_scope(t_ss); - AST_Node_Impl_Ptr catch_block = this->children[i]; + auto &catch_block = *this->children[i]; - if (catch_block->children.size() == 1) { + if (catch_block.children.size() == 1) { //No variable capture, no guards - retval = catch_block->children[0]->eval(t_ss); + retval = catch_block.children[0]->eval(t_ss); break; - } else if (catch_block->children.size() == 2 || catch_block->children.size() == 3) { - const auto name = Arg_List_AST_Node::get_arg_name(catch_block->children[0]); + } else if (catch_block.children.size() == 2 || catch_block.children.size() == 3) { + const auto name = Arg_List_AST_Node::get_arg_name(*catch_block.children[0]); if (dispatch::Param_Types( - std::vector>{Arg_List_AST_Node::get_arg_type(catch_block->children[0], t_ss)} + std::vector>{Arg_List_AST_Node::get_arg_type(*catch_block.children[0], t_ss)} ).match(std::vector{t_except}, t_ss.conversions()).first) { t_ss.add_object(name, t_except); - if (catch_block->children.size() == 2) { + if (catch_block.children.size() == 2) { //Variable capture, no guards - retval = catch_block->children[1]->eval(t_ss); + retval = catch_block.children[1]->eval(t_ss); break; } - else if (catch_block->children.size() == 3) { + else if (catch_block.children.size() == 3) { //Variable capture, guards bool guard = false; try { - guard = boxed_cast(catch_block->children[1]->eval(t_ss)); + guard = boxed_cast(catch_block.children[1]->eval(t_ss)); } catch (const exception::bad_boxed_cast &) { if (this->children.back()->identifier == AST_Node_Type::Finally) { this->children.back()->children[0]->eval(t_ss); @@ -1263,7 +1301,7 @@ namespace chaiscript throw exception::eval_error("Guard condition not boolean"); } if (guard) { - retval = catch_block->children[2]->eval(t_ss); + retval = catch_block.children[2]->eval(t_ss); break; } } @@ -1335,8 +1373,18 @@ namespace chaiscript template struct Method_AST_Node final : AST_Node_Impl { + std::shared_ptr> m_body_node; + std::shared_ptr> m_guard_node; + Method_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Method, std::move(t_loc), std::move(t_children)) { } + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Method, std::move(t_loc), + std::vector>(std::make_move_iterator(t_children.begin()), + std::make_move_iterator(std::prev(t_children.end(), Def_AST_Node::has_guard(t_children)?2:1))) + ), + m_body_node(Def_AST_Node::get_body_node(std::move(t_children))), + m_guard_node(Def_AST_Node::get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2)) + { + } Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ @@ -1348,39 +1396,27 @@ namespace chaiscript std::vector t_param_names{"this"}; dispatch::Param_Types param_types; - if ((this->children.size() > 3) + if ((this->children.size() > 2) && (this->children[2]->identifier == AST_Node_Type::Arg_List)) { - auto args = Arg_List_AST_Node::get_arg_names(this->children[2]); + auto args = Arg_List_AST_Node::get_arg_names(*this->children[2]); t_param_names.insert(t_param_names.end(), args.begin(), args.end()); - param_types = Arg_List_AST_Node::get_arg_types(this->children[2], t_ss); - - if (this->children.size() > 4) { - guardnode = this->children[3]; - } - } - else { - //no parameters - - if (this->children.size() > 3) { - guardnode = this->children[2]; - } + param_types = Arg_List_AST_Node::get_arg_types(*this->children[2], t_ss); } const size_t numparams = t_param_names.size(); std::shared_ptr guard; std::reference_wrapper engine(*t_ss); - if (guardnode) { + if (m_guard_node) { guard = dispatch::make_dynamic_proxy_function( - [engine, t_param_names, guardnode](const std::vector &t_params) { - return chaiscript::eval::detail::eval_function(engine, guardnode, t_param_names, t_params); + [engine, t_param_names, guardnode = m_guard_node](const std::vector &t_params) { + return chaiscript::eval::detail::eval_function(engine, *guardnode, t_param_names, t_params); }, - static_cast(numparams), guardnode); + static_cast(numparams), m_guard_node); } try { const std::string & function_name = this->children[1]->text; - auto node = this->children.back(); if (function_name == class_name) { param_types.push_front(class_name, Type_Info()); @@ -1388,10 +1424,10 @@ namespace chaiscript t_ss->add( std::make_shared(class_name, dispatch::make_dynamic_proxy_function( - [engine, t_param_names, node](const std::vector &t_params) { - return chaiscript::eval::detail::eval_function(engine, node, t_param_names, t_params); + [engine, t_param_names, node = m_body_node](const std::vector &t_params) { + return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params); }, - static_cast(numparams), node, param_types, guard + static_cast(numparams), m_body_node, param_types, guard ) ), function_name); @@ -1402,15 +1438,17 @@ namespace chaiscript auto type = t_ss->get_type(class_name, false); param_types.push_front(class_name, type); - t_ss->add(std::make_shared(class_name, + t_ss->add( + std::make_shared(class_name, dispatch::make_dynamic_proxy_function( - [engine, t_param_names, node](const std::vector &t_params) { - return chaiscript::eval::detail::eval_function(engine, node, t_param_names, t_params); + [engine, t_param_names, node = m_body_node](const std::vector &t_params) { + return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params); }, - static_cast(numparams), node, param_types, guard), type), + static_cast(numparams), m_body_node, param_types, guard), type), function_name); } } catch (const exception::name_conflict_error &e) { + std::cout << "Method!!" << std::endl; throw exception::eval_error("Method redefined '" + e.name() + "'"); } return void_var(); diff --git a/include/chaiscript/language/chaiscript_optimizer.hpp b/include/chaiscript/language/chaiscript_optimizer.hpp index 996b6971..675d092c 100644 --- a/include/chaiscript/language/chaiscript_optimizer.hpp +++ b/include/chaiscript/language/chaiscript_optimizer.hpp @@ -24,17 +24,26 @@ namespace chaiscript { template auto optimize(eval::AST_Node_Impl_Ptr p) { - (void)std::initializer_list{ (p = static_cast(*this).optimize(p), 0)... }; + (void)std::initializer_list{ (p = static_cast(*this).optimize(std::move(p)), 0)... }; return p; } }; template - auto child_at(const eval::AST_Node_Impl_Ptr &node, const size_t offset) { - if (node->children[offset]->identifier == AST_Node_Type::Compiled) { - return dynamic_cast&>(*node->children[offset]).m_original_node; + eval::AST_Node_Impl &child_at(eval::AST_Node_Impl &node, const size_t offset) { + if (node.children[offset]->identifier == AST_Node_Type::Compiled) { + return *(dynamic_cast &>(*node.children[offset]).m_original_node); } else { - return node->children[offset]; + return *node.children[offset]; + } + } + + template + const eval::AST_Node_Impl &child_at(const eval::AST_Node_Impl &node, const size_t offset) { + if (node.children[offset]->identifier == AST_Node_Type::Compiled) { + return *(dynamic_cast &>(*node.children[offset]).m_original_node); + } else { + return *node.children[offset]; } @@ -48,24 +57,24 @@ namespace chaiscript { } template - auto child_count(const eval::AST_Node_Impl_Ptr &node) { - if (node->identifier == AST_Node_Type::Compiled) { - return dynamic_cast&>(*node).m_original_node->children.size(); + auto child_count(const eval::AST_Node_Impl &node) { + if (node.identifier == AST_Node_Type::Compiled) { + return dynamic_cast&>(node).m_original_node->children.size(); } else { - return node->children.size(); + return node.children.size(); } } template - auto make_compiled_node(const eval::AST_Node_Impl_Ptr &original_node, std::vector> children, Callable callable) + auto make_compiled_node(eval::AST_Node_Impl_Ptr original_node, std::vector> children, Callable callable) { - return chaiscript::make_shared, eval::Compiled_AST_Node>(original_node, std::move(children), std::move(callable)); + return chaiscript::make_unique, eval::Compiled_AST_Node>(std::move(original_node), std::move(children), std::move(callable)); } struct Return { template - auto optimize(const eval::AST_Node_Impl_Ptr &p) + auto optimize(eval::AST_Node_Impl_Ptr p) { if ( (p->identifier == AST_Node_Type::Def || p->identifier == AST_Node_Type::Lambda) && !p->children.empty()) @@ -75,7 +84,7 @@ namespace chaiscript { auto &block_last_child = last_child->children.back(); if (block_last_child->identifier == AST_Node_Type::Return) { if (block_last_child->children.size() == 1) { - last_child->children.back() = block_last_child->children[0]; + last_child->children.back() = std::move(block_last_child->children[0]); } } } @@ -86,9 +95,9 @@ namespace chaiscript { }; template - bool contains_var_decl_in_scope(const T &node) + bool contains_var_decl_in_scope(const eval::AST_Node_Impl &node) { - if (node->identifier == AST_Node_Type::Var_Decl) { + if (node.identifier == AST_Node_Type::Var_Decl) { return true; } @@ -96,8 +105,8 @@ namespace chaiscript { for (size_t i = 0; i < num; ++i) { const auto &child = child_at(node, i); - if (child->identifier != AST_Node_Type::Block - && child->identifier != AST_Node_Type::For + if (child.identifier != AST_Node_Type::Block + && child.identifier != AST_Node_Type::For && contains_var_decl_in_scope(child)) { return true; } @@ -108,15 +117,16 @@ namespace chaiscript { struct Block { template - auto optimize(const eval::AST_Node_Impl_Ptr &node) { + auto optimize(eval::AST_Node_Impl_Ptr node) { if (node->identifier == AST_Node_Type::Block) { - if (!contains_var_decl_in_scope(node)) + if (!contains_var_decl_in_scope(*node)) { if (node->children.size() == 1) { - return node->children[0]; + return std::move(node->children[0]); } else { - return chaiscript::make_shared, eval::Scopeless_Block_AST_Node>(node->text, node->location, node->children); + return chaiscript::make_unique, eval::Scopeless_Block_AST_Node>(node->text, node->location, + std::move(node->children)); } } } @@ -127,7 +137,7 @@ namespace chaiscript { struct Dead_Code { template - auto optimize(const eval::AST_Node_Impl_Ptr &node) { + auto optimize(eval::AST_Node_Impl_Ptr node) { if (node->identifier == AST_Node_Type::Block) { std::vector keepers; @@ -135,10 +145,10 @@ namespace chaiscript { keepers.reserve(num_children); for (size_t i = 0; i < num_children; ++i) { - auto child = node->children[i]; - if ( (child->identifier != AST_Node_Type::Id - && child->identifier != AST_Node_Type::Constant - && child->identifier != AST_Node_Type::Noop) + const auto &child = *node->children[i]; + if ( (child.identifier != AST_Node_Type::Id + && child.identifier != AST_Node_Type::Constant + && child.identifier != AST_Node_Type::Noop) || i == num_children - 1) { keepers.push_back(i); } @@ -147,12 +157,16 @@ namespace chaiscript { if (keepers.size() == num_children) { return node; } else { - std::vector> new_children; - for (const auto x : keepers) - { - new_children.push_back(node->children[x]); - } - return chaiscript::make_shared, eval::Block_AST_Node>(node->text, node->location, new_children); + const auto new_children = [&](){ + std::vector> retval; + for (const auto x : keepers) + { + retval.push_back(std::move(node->children[x])); + } + return retval; + }; + + return chaiscript::make_unique, eval::Block_AST_Node>(node->text, node->location, new_children()); } } else { return node; @@ -162,29 +176,30 @@ namespace chaiscript { struct Unused_Return { template - auto optimize(const eval::AST_Node_Impl_Ptr &node) { + auto optimize(eval::AST_Node_Impl_Ptr node) { if ((node->identifier == AST_Node_Type::Block || node->identifier == AST_Node_Type::Scopeless_Block) && !node->children.empty()) { for (size_t i = 0; i < node->children.size()-1; ++i) { - auto child = node->children[i]; + auto child = node->children[i].get(); if (child->identifier == AST_Node_Type::Fun_Call) { - node->children[i] = chaiscript::make_shared, eval::Unused_Return_Fun_Call_AST_Node>(child->text, child->location, std::move(child->children)); + node->children[i] = chaiscript::make_unique, eval::Unused_Return_Fun_Call_AST_Node>(child->text, child->location, + std::move(child->children)); } } } else if ((node->identifier == AST_Node_Type::For || node->identifier == AST_Node_Type::While) - && child_count(node) > 0) { - auto child = child_at(node, child_count(node) - 1); - if (child->identifier == AST_Node_Type::Block - || child->identifier == AST_Node_Type::Scopeless_Block) + && child_count(*node) > 0) { + auto &child = child_at(*node, child_count(*node) - 1); + if (child.identifier == AST_Node_Type::Block + || child.identifier == AST_Node_Type::Scopeless_Block) { auto num_sub_children = child_count(child); for (size_t i = 0; i < num_sub_children; ++i) { - auto sub_child = child_at(child, i); - if (sub_child->identifier == AST_Node_Type::Fun_Call) { - child->children[i] = chaiscript::make_shared, eval::Unused_Return_Fun_Call_AST_Node>(sub_child->text, sub_child->location, std::move(sub_child->children)); + auto &sub_child = child_at(child, i); + if (sub_child.identifier == AST_Node_Type::Fun_Call) { + child.children[i] = chaiscript::make_unique, eval::Unused_Return_Fun_Call_AST_Node>(sub_child.text, sub_child.location, std::move(sub_child.children)); } } } @@ -195,17 +210,17 @@ namespace chaiscript { struct If { template - auto optimize(const eval::AST_Node_Impl_Ptr &node) { + auto optimize(eval::AST_Node_Impl_Ptr node) { if ((node->identifier == AST_Node_Type::If) && node->children.size() >= 2 && node->children[0]->identifier == AST_Node_Type::Constant) { - const auto condition = std::dynamic_pointer_cast>(node->children[0])->m_value; + const auto condition = dynamic_cast *>(node->children[0].get())->m_value; if (condition.get_type_info().bare_equal_type_info(typeid(bool))) { if (boxed_cast(condition)) { - return node->children[1]; + return std::move(node->children[1]); } else if (node->children.size() == 3) { - return node->children[2]; + return std::move(node->children[2]); } } } @@ -216,7 +231,7 @@ namespace chaiscript { struct Partial_Fold { template - auto optimize(const eval::AST_Node_Impl_Ptr &node) { + auto optimize(eval::AST_Node_Impl_Ptr node) { // Fold right side if (node->identifier == AST_Node_Type::Binary @@ -228,9 +243,10 @@ namespace chaiscript { const auto &oper = node->text; const auto parsed = Operators::to_operator(oper); if (parsed != Operators::Opers::invalid) { - const auto rhs = std::dynamic_pointer_cast>(node->children[1])->m_value; + const auto rhs = dynamic_cast *>(node->children[1].get())->m_value; if (rhs.get_type_info().is_arithmetic()) { - return chaiscript::make_shared, eval::Fold_Right_Binary_Operator_AST_Node>(node->text, node->location, node->children, rhs); + return chaiscript::make_unique, eval::Fold_Right_Binary_Operator_AST_Node>(node->text, node->location, + std::move(node->children), rhs); } } } catch (const std::exception &) { @@ -244,7 +260,7 @@ namespace chaiscript { struct Constant_Fold { template - auto optimize(const eval::AST_Node_Impl_Ptr &node) { + auto optimize(eval::AST_Node_Impl_Ptr node) { if (node->identifier == AST_Node_Type::Prefix && node->children.size() == 1 @@ -253,14 +269,14 @@ namespace chaiscript { try { const auto &oper = node->text; const auto parsed = Operators::to_operator(oper, true); - const auto lhs = std::dynamic_pointer_cast>(node->children[0])->m_value; + const auto lhs = dynamic_cast *>(node->children[0].get())->m_value; const auto match = oper + node->children[0]->text; if (parsed != Operators::Opers::invalid && parsed != Operators::Opers::bitwise_and && lhs.get_type_info().is_arithmetic()) { const auto val = Boxed_Number::do_oper(parsed, lhs); - return chaiscript::make_shared, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); } else if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && oper == "!") { - return chaiscript::make_shared, eval::Constant_AST_Node>(std::move(match), node->location, Boxed_Value(!boxed_cast(lhs))); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, Boxed_Value(!boxed_cast(lhs))); } } catch (const std::exception &) { //failure to fold, that's OK @@ -271,8 +287,8 @@ namespace chaiscript { && node->children[1]->identifier == AST_Node_Type::Constant) { try { - const auto lhs = std::dynamic_pointer_cast>(node->children[0])->m_value; - const auto rhs = std::dynamic_pointer_cast>(node->children[1])->m_value; + const auto lhs = dynamic_cast &>(*node->children[0]).m_value; + const auto rhs = dynamic_cast &>(*node->children[1]).m_value; if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && rhs.get_type_info().bare_equal_type_info(typeid(bool))) { const auto match = node->children[0]->text + " " + node->text + " " + node->children[1]->text; const auto val = [lhs_val = boxed_cast(lhs), rhs_val = boxed_cast(rhs), id = node->identifier] { @@ -280,7 +296,7 @@ namespace chaiscript { else { return Boxed_Value(lhs_val || rhs_val); } }(); - return chaiscript::make_shared, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); } } catch (const std::exception &) { //failure to fold, that's OK @@ -294,12 +310,12 @@ namespace chaiscript { const auto &oper = node->text; const auto parsed = Operators::to_operator(oper); if (parsed != Operators::Opers::invalid) { - const auto lhs = std::dynamic_pointer_cast>(node->children[0])->m_value; - const auto rhs = std::dynamic_pointer_cast>(node->children[1])->m_value; + const auto lhs = dynamic_cast &>(*node->children[0]).m_value; + const auto rhs = dynamic_cast &>(*node->children[1]).m_value; if (lhs.get_type_info().is_arithmetic() && rhs.get_type_info().is_arithmetic()) { const auto val = Boxed_Number::do_oper(parsed, lhs, rhs); const auto match = node->children[0]->text + " " + oper + " " + node->children[1]->text; - return chaiscript::make_shared, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); } } } catch (const std::exception &) { @@ -312,13 +328,13 @@ namespace chaiscript { && node->children[1]->children.size() == 1 && node->children[1]->children[0]->identifier == AST_Node_Type::Constant) { - const auto arg = std::dynamic_pointer_cast>(node->children[1]->children[0])->m_value; + const auto arg = dynamic_cast &>(*node->children[1]->children[0]).m_value; if (arg.get_type_info().is_arithmetic()) { const auto &fun_name = node->children[0]->text; const auto make_constant = [&node, &fun_name](auto val){ const auto match = fun_name + "(" + node->children[1]->children[0]->text + ")"; - return chaiscript::make_shared, eval::Constant_AST_Node>(std::move(match), node->location, Boxed_Value(val)); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, Boxed_Value(val)); }; if (fun_name == "double") { @@ -344,35 +360,36 @@ namespace chaiscript { struct For_Loop { template - auto optimize(const eval::AST_Node_Impl_Ptr &for_node) { + auto optimize(eval::AST_Node_Impl_Ptr for_node) { if (for_node->identifier != AST_Node_Type::For) { return for_node; } - const auto eq_node = child_at(for_node, 0); - const auto binary_node = child_at(for_node, 1); - const auto prefix_node = child_at(for_node, 2); + const auto &eq_node = child_at(*for_node, 0); + const auto &binary_node = child_at(*for_node, 1); + const auto &prefix_node = child_at(*for_node, 2); - if (eq_node->identifier == AST_Node_Type::Equation + if (child_count(*for_node) == 4 + && eq_node.identifier == AST_Node_Type::Equation && child_count(eq_node) == 2 - && child_at(eq_node, 0)->identifier == AST_Node_Type::Var_Decl - && child_at(eq_node, 1)->identifier == AST_Node_Type::Constant - && binary_node->identifier == AST_Node_Type::Binary - && binary_node->text == "<" + && child_at(eq_node, 0).identifier == AST_Node_Type::Var_Decl + && child_at(eq_node, 1).identifier == AST_Node_Type::Constant + && binary_node.identifier == AST_Node_Type::Binary + && binary_node.text == "<" && child_count(binary_node) == 2 - && child_at(binary_node, 0)->identifier == AST_Node_Type::Id - && child_at(binary_node, 0)->text == child_at(child_at(eq_node,0), 0)->text - && child_at(binary_node, 1)->identifier == AST_Node_Type::Constant - && prefix_node->identifier == AST_Node_Type::Prefix - && prefix_node->text == "++" + && child_at(binary_node, 0).identifier == AST_Node_Type::Id + && child_at(binary_node, 0).text == child_at(child_at(eq_node,0), 0).text + && child_at(binary_node, 1).identifier == AST_Node_Type::Constant + && prefix_node.identifier == AST_Node_Type::Prefix + && prefix_node.text == "++" && child_count(prefix_node) == 1 - && child_at(prefix_node, 0)->identifier == AST_Node_Type::Id - && child_at(prefix_node, 0)->text == child_at(child_at(eq_node,0), 0)->text) + && child_at(prefix_node, 0).identifier == AST_Node_Type::Id + && child_at(prefix_node, 0).text == child_at(child_at(eq_node,0), 0).text) { - const Boxed_Value &begin = std::dynamic_pointer_cast>(child_at(eq_node, 1))->m_value; - const Boxed_Value &end = std::dynamic_pointer_cast>(child_at(binary_node, 1))->m_value; - const std::string &id = child_at(prefix_node, 0)->text; + const Boxed_Value &begin = dynamic_cast &>(child_at(eq_node, 1)).m_value; + const Boxed_Value &end = dynamic_cast &>(child_at(binary_node, 1)).m_value; + const std::string &id = child_at(prefix_node, 0).text; if (begin.get_type_info().bare_equal(user_type()) && end.get_type_info().bare_equal(user_type())) { @@ -380,9 +397,14 @@ namespace chaiscript { const auto start_int = boxed_cast(begin); const auto end_int = boxed_cast(end); - const auto body = child_at(for_node, 3); - - return make_compiled_node(for_node, {body}, + // note that we are moving the last element out, then popping the empty shared_ptr + // from the vector + std::vector> body_vector; + auto body_child = std::move(for_node->children[3]); + for_node->children.pop_back(); + body_vector.emplace_back(std::move(body_child)); + + return make_compiled_node(std::move(for_node), std::move(body_vector), [id, start_int, end_int](const std::vector> &children, const chaiscript::detail::Dispatch_State &t_ss) { assert(children.size() == 1); chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 93e69343..73575ef5 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -397,7 +397,7 @@ namespace chaiscript return m_optimizer; } - ChaiScript_Parser(const ChaiScript_Parser &) = default; + ChaiScript_Parser(const ChaiScript_Parser &) = delete; ChaiScript_Parser &operator=(const ChaiScript_Parser &) = delete; ChaiScript_Parser(ChaiScript_Parser &&) = default; ChaiScript_Parser &operator=(ChaiScript_Parser &&) = delete; @@ -406,10 +406,10 @@ namespace chaiscript bool char_in_alphabet(char c, detail::Alphabet a) const { return m_alphabet[a][static_cast(c)]; } /// Prints the parsed ast_nodes as a tree - void debug_print(AST_NodePtr t, std::string prepend = "") const override { - std::cout << prepend << "(" << ast_node_type_to_string(t->identifier) << ") " << t->text << " : " << t->start().line << ", " << t->start().column << '\n'; - for (const auto &node : t->get_children()) { - debug_print(node, prepend + " "); + void debug_print(const AST_Node &t, std::string prepend = "") const override { + std::cout << prepend << "(" << ast_node_type_to_string(t.identifier) << ") " << t.text << " : " << t.start().line << ", " << t.start().column << '\n'; + for (const auto &node : t.get_children()) { + debug_print(node.get(), prepend + " "); } } @@ -452,7 +452,7 @@ namespace chaiscript /// \todo fix the fact that a successful match that captured no ast_nodes doesn't have any real start position m_match_stack.push_back( m_optimizer.optimize( - chaiscript::make_shared, NodeType>( + chaiscript::make_unique, NodeType>( std::move(t_text), std::move(filepos), std::move(new_children))) @@ -779,9 +779,9 @@ namespace chaiscript } template - std::shared_ptr> make_node(std::string t_match, const int t_prev_line, const int t_prev_col, Param && ...param) + std::unique_ptr> make_node(std::string t_match, const int t_prev_line, const int t_prev_col, Param && ...param) { - return chaiscript::make_shared, T>(std::move(t_match), Parse_Location(m_filename, t_prev_line, t_prev_col, m_position.line, m_position.col), std::forward(param)...); + return chaiscript::make_unique, T>(std::move(t_match), Parse_Location(m_filename, t_prev_line, t_prev_col, m_position.line, m_position.col), std::forward(param)...); } /// Reads a number from the input, detecting if it's an integer or floating point @@ -1759,7 +1759,7 @@ namespace chaiscript if ((is_if_init && num_children == 3) || (!is_if_init && num_children == 2)) { - m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); + m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } if (!is_if_init) { @@ -1849,7 +1849,7 @@ namespace chaiscript { return false; } else { - m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); + m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } } @@ -1859,13 +1859,13 @@ namespace chaiscript { return false; } else { - m_match_stack.push_back(chaiscript::make_shared, eval::Constant_AST_Node>(Boxed_Value(true))); + m_match_stack.push_back(chaiscript::make_unique, eval::Constant_AST_Node>(Boxed_Value(true))); } } if (!Equation()) { - m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); + m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } return true; @@ -2005,7 +2005,7 @@ namespace chaiscript } if (m_match_stack.size() == prev_stack_top) { - m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); + m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } build_match>(prev_stack_top); @@ -2029,7 +2029,7 @@ namespace chaiscript } if (m_match_stack.size() == prev_stack_top) { - m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); + m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } build_match>(prev_stack_top); @@ -2105,13 +2105,13 @@ namespace chaiscript } if (m_match_stack.back()->children.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); } - auto dot_access = m_match_stack.back()->children[0]; - auto func_call = m_match_stack.back(); + auto dot_access = std::move(m_match_stack.back()->children[0]); + auto func_call = std::move(m_match_stack.back()); m_match_stack.pop_back(); func_call->children.erase(func_call->children.begin()); if (dot_access->children.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); } - func_call->children.insert(func_call->children.begin(), dot_access->children.back()); + func_call->children.insert(func_call->children.begin(), std::move(dot_access->children.back())); dot_access->children.pop_back(); dot_access->children.push_back(std::move(func_call)); if (dot_access->children.size() != 2) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); @@ -2517,24 +2517,23 @@ namespace chaiscript AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) override { - ChaiScript_Parser parser(*this); - parser.m_match_stack.clear(); + ChaiScript_Parser parser(m_tracer, m_optimizer); return parser.parse_internal(t_input, t_fname); } eval::AST_Node_Impl_Ptr parse_instr_eval(const std::string &t_input) { - const auto last_position = m_position; - const auto last_filename = m_filename; - const auto last_match_stack = std::exchange(m_match_stack, decltype(m_match_stack){}); + auto last_position = m_position; + auto last_filename = m_filename; + auto last_match_stack = std::exchange(m_match_stack, decltype(m_match_stack){}); - const auto retval = parse_internal(t_input, "instr eval"); + auto retval = parse_internal(t_input, "instr eval"); m_position = std::move(last_position); m_filename = std::move(last_filename); m_match_stack = std::move(last_match_stack); - return std::dynamic_pointer_cast>(retval); + return eval::AST_Node_Impl_Ptr(dynamic_cast*>(retval.release())); } /// Parses the given input string, tagging parsed ast_nodes with the given m_filename. @@ -2546,7 +2545,6 @@ namespace chaiscript while (m_position.has_more() && (!Eol())) { ++m_position; } - /// \todo respect // -*- coding: utf-8 -*- on line 1 or 2 see: http://evanjones.ca/python-utf8.html) } if (Statements(true)) { @@ -2556,10 +2554,12 @@ namespace chaiscript build_match>(0); } } else { - m_match_stack.push_back(chaiscript::make_shared, eval::Noop_AST_Node>()); + m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } - return m_match_stack.front(); + AST_NodePtr retval(std::move(m_match_stack.front())); + m_match_stack.clear(); + return retval; } }; } diff --git a/src/main.cpp b/src/main.cpp index 78044e06..e826def0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -247,7 +247,7 @@ void interactive(chaiscript::ChaiScript_Basic& chai) catch (const chaiscript::exception::eval_error &ee) { std::cout << ee.what(); if ( !ee.call_stack.empty() ) { - std::cout << "during evaluation at (" << ee.call_stack[0]->start().line << ", " << ee.call_stack[0]->start().column << ")"; + std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")"; } std::cout << '\n'; } From 77315ae4b9cc71a7d1d9512cfdf641491b5cdd81 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 22 Jun 2017 09:32:49 -0600 Subject: [PATCH 41/97] Fix non-shared_ptr tree code --- include/chaiscript/language/chaiscript_eval.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 4388649d..3b9c7837 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -718,7 +718,7 @@ namespace chaiscript Def_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Def, std::move(t_loc), std::vector>(std::make_move_iterator(t_children.begin()), - std::make_move_iterator(std::prev(t_children.end(), has_guard(t_children)?2:1))) + std::make_move_iterator(std::prev(t_children.end(), has_guard(t_children, 1)?2:1))) ), m_body_node(get_body_node(std::move(t_children))), m_guard_node(get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2)) @@ -739,15 +739,15 @@ namespace chaiscript return std::move(vec.back()); } - static bool has_guard(const std::vector> &t_children) + static bool has_guard(const std::vector> &t_children, const std::size_t offset) { - if ((t_children.size() > 2) && (t_children[1]->identifier == AST_Node_Type::Arg_List)) { - if (t_children.size() > 3) { + if ((t_children.size() > 2 + offset) && (t_children[1+offset]->identifier == AST_Node_Type::Arg_List)) { + if (t_children.size() > 3 + offset) { return true; } } else { - if (t_children.size() > 2) { + if (t_children.size() > 2 + offset) { return true; } } @@ -1379,7 +1379,7 @@ namespace chaiscript Method_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Method, std::move(t_loc), std::vector>(std::make_move_iterator(t_children.begin()), - std::make_move_iterator(std::prev(t_children.end(), Def_AST_Node::has_guard(t_children)?2:1))) + std::make_move_iterator(std::prev(t_children.end(), Def_AST_Node::has_guard(t_children, 1)?2:1))) ), m_body_node(Def_AST_Node::get_body_node(std::move(t_children))), m_guard_node(Def_AST_Node::get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2)) From 0c31d81711785fd73cfac5b07083f7f075416e1d Mon Sep 17 00:00:00 2001 From: Mike Bogdanov Date: Thu, 13 Jul 2017 12:41:23 +0300 Subject: [PATCH 42/97] fixed PVS-Studio warnings V728 --- include/chaiscript/language/chaiscript_parser.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 163d2bb3..97cbca2f 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -995,7 +995,7 @@ namespace chaiscript int in_interpolation = 0; bool in_quote = false; - while (m_position.has_more() && ((*m_position != '\"') || ((*m_position == '\"') && (in_interpolation > 0)) || ((*m_position == '\"') && (prev_char == '\\')))) { + while (m_position.has_more() && ((*m_position != '\"') || (in_interpolation > 0) || (prev_char == '\\'))) { if (!Eol_()) { if (prev_char == '$' && *m_position == '{') { @@ -1281,7 +1281,7 @@ namespace chaiscript char prev_char = *m_position; ++m_position; - while (m_position.has_more() && ((*m_position != '\'') || ((*m_position == '\'') && (prev_char == '\\')))) { + while (m_position.has_more() && ((*m_position != '\'') || (prev_char == '\\'))) { if (!Eol_()) { if (prev_char == '\\') { prev_char = 0; From ea03a5462f0a9edfc07c536d540ba9c1e66307e9 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 18 Jul 2017 16:58:09 -0600 Subject: [PATCH 43/97] Wrap up build issues for dropping of shared_ptr --- include/chaiscript/language/chaiscript_engine.hpp | 6 +++--- samples/fun_call_performance.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index dc0b8536..0e07f4ce 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -186,7 +186,7 @@ namespace chaiscript } m_engine.add(fun([this](const std::string &t_str){ return internal_eval(t_str); }), "eval"); - m_engine.add(fun([this](const AST_NodePtr &t_ast){ return eval(t_ast); }), "eval"); + m_engine.add(fun([this](const AST_Node &t_ast){ return eval(t_ast); }), "eval"); m_engine.add(fun([this](const std::string &t_str, const bool t_dump){ return parse(t_str, t_dump); }), "parse"); m_engine.add(fun([this](const std::string &t_str){ return parse(t_str); }), "parse"); @@ -321,10 +321,10 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars return *m_parser; } - const Boxed_Value eval(const AST_NodePtr &t_ast) + const Boxed_Value eval(const AST_Node &t_ast) { try { - return t_ast->eval(chaiscript::detail::Dispatch_State(m_engine)); + return t_ast.eval(chaiscript::detail::Dispatch_State(m_engine)); } catch (const exception::eval_error &t_ee) { throw Boxed_Value(t_ee); } diff --git a/samples/fun_call_performance.cpp b/samples/fun_call_performance.cpp index b8bda5ae..31fa606c 100644 --- a/samples/fun_call_performance.cpp +++ b/samples/fun_call_performance.cpp @@ -252,7 +252,7 @@ void interactive(chaiscript::ChaiScript& chai) catch (const chaiscript::exception::eval_error &ee) { std::cout << ee.what(); if (ee.call_stack.size() > 0) { - std::cout << "during evaluation at (" << ee.call_stack[0]->start().line << ", " << ee.call_stack[0]->start().column << ")"; + std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")"; } std::cout << std::endl; } From cfb2e663d36d84130aacd1abd6807e73452c3f6a Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 19 Jul 2017 10:09:44 -0600 Subject: [PATCH 44/97] Fix unhandled exception found via libfuzzer --- .../chaiscript/language/chaiscript_parser.hpp | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 73575ef5..d6cf7402 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -1051,23 +1051,30 @@ namespace chaiscript Char_Parser &operator=(const Char_Parser &) = delete; ~Char_Parser(){ - if (is_octal) { - process_octal(); - } + try { + if (is_octal) { + process_octal(); + } - if (is_hex) { - process_hex(); - } + if (is_hex) { + process_hex(); + } - if (is_unicode) { - process_unicode(); + if (is_unicode) { + process_unicode(); + } + } catch (const std::invalid_argument &) { + // escape sequence was invalid somehow, we'll pick this + // up in the next part of parsing } } void process_hex() { - auto val = stoll(hex_matches, nullptr, 16); - match.push_back(char_type(val)); + if (!hex_matches.empty()) { + auto val = stoll(hex_matches, nullptr, 16); + match.push_back(char_type(val)); + } hex_matches.clear(); is_escaped = false; is_hex = false; @@ -1076,8 +1083,10 @@ namespace chaiscript void process_octal() { - auto val = stoll(octal_matches, nullptr, 8); - match.push_back(char_type(val)); + if (!octal_matches.empty()) { + auto val = stoll(octal_matches, nullptr, 8); + match.push_back(char_type(val)); + } octal_matches.clear(); is_escaped = false; is_octal = false; @@ -1086,9 +1095,11 @@ namespace chaiscript void process_unicode() { - auto val = stoll(hex_matches, nullptr, 16); - hex_matches.clear(); - match += detail::Char_Parser_Helper::str_from_ll(val); + if (!hex_matches.empty()) { + auto val = stoll(hex_matches, nullptr, 16); + hex_matches.clear(); + match += detail::Char_Parser_Helper::str_from_ll(val); + } is_escaped = false; is_unicode = false; } @@ -1254,6 +1265,7 @@ namespace chaiscript cparser.saw_interpolation_marker = false; } else { cparser.parse(*s, start.line, start.col, *m_filename); + ++s; } } From d8da295e40aeb171b4bf1c612c1186f2d4b392fc Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 19 Jul 2017 10:47:17 -0600 Subject: [PATCH 45/97] Check string accesses during JSON parsing --- include/chaiscript/utility/json.hpp | 38 ++++++++++++------------ include/chaiscript/utility/json_wrap.hpp | 6 +++- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index 676b4699..693f19da 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -463,7 +463,7 @@ struct JSONParser { } static void consume_ws( const std::string &str, size_t &offset ) { - while( isspace( str[offset] ) && offset <= str.size() ) { ++offset; } + while( isspace( str.at(offset) ) && offset <= str.size() ) { ++offset; } } static JSON parse_object( const std::string &str, size_t &offset ) { @@ -471,29 +471,29 @@ struct JSONParser { ++offset; consume_ws( str, offset ); - if( str[offset] == '}' ) { + if( str.at(offset) == '}' ) { ++offset; return Object; } for (;offset= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) { val += c; } else { @@ -569,12 +569,12 @@ struct JSONParser { bool isDouble = false; bool isNegative = false; long exp = 0; - if( offset < str.size() && str[offset] == '-' ) { + if( offset < str.size() && str.at(offset) == '-' ) { isNegative = true; ++offset; } for (; offset < str.size() ;) { - c = str[offset++]; + c = str.at(offset++); if( c >= '0' && c <= '9' ) { val += c; } else if( c == '.' && !isDouble ) { @@ -585,7 +585,7 @@ struct JSONParser { } } if( offset < str.size() && (c == 'E' || c == 'e' )) { - c = str[ offset++ ]; + c = str.at(offset++); if( c == '-' ) { exp_str += '-'; } else if( c == '+' ) { @@ -595,7 +595,7 @@ struct JSONParser { } for (; offset < str.size() ;) { - c = str[ offset++ ]; + c = str.at(offset++); if( c >= '0' && c <= '9' ) { exp_str += c; } else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { @@ -646,7 +646,7 @@ struct JSONParser { static JSON parse_next( const std::string &str, size_t &offset ) { char value; consume_ws( str, offset ); - value = str[offset]; + value = str.at(offset); switch( value ) { case '[' : return parse_array( str, offset ); case '{' : return parse_object( str, offset ); diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp index c0af1cd8..6a6ccd02 100644 --- a/include/chaiscript/utility/json_wrap.hpp +++ b/include/chaiscript/utility/json_wrap.hpp @@ -63,7 +63,11 @@ namespace chaiscript static Boxed_Value from_json(const std::string &t_json) { - return from_json( json::JSON::Load(t_json) ); + try { + return from_json( json::JSON::Load(t_json) ); + } catch (...) { + throw std::runtime_error("Unparsed JSON input"); + } } static std::string to_json(const Boxed_Value &t_bv) From b42316a27581c5cef8a4b825d78bf7836703ce0f Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 19 Jul 2017 13:19:17 -0600 Subject: [PATCH 46/97] More careful with json errors --- include/chaiscript/utility/json_wrap.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp index 6a6ccd02..2aa81f34 100644 --- a/include/chaiscript/utility/json_wrap.hpp +++ b/include/chaiscript/utility/json_wrap.hpp @@ -65,7 +65,7 @@ namespace chaiscript { try { return from_json( json::JSON::Load(t_json) ); - } catch (...) { + } catch (const std::out_of_range& ) { throw std::runtime_error("Unparsed JSON input"); } } From f03659c8656301b4b304542e6276a7834d3f6f5b Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 19 Jul 2017 13:19:36 -0600 Subject: [PATCH 47/97] More careful testing of 'for' parses --- include/chaiscript/language/chaiscript_parser.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index d6cf7402..06a51dfa 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -1908,9 +1908,17 @@ namespace chaiscript throw exception::eval_error("Incomplete 'for' block", File_Position(m_position.line, m_position.col), *m_filename); } + const auto num_children = m_match_stack.size() - prev_stack_top; + if (classic_for) { + if (num_children != 4) { + throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename); + } build_match>(prev_stack_top); } else { + if (num_children != 3) { + throw exception::eval_error("Incomplete ranged-for expression", File_Position(m_position.line, m_position.col), *m_filename); + } build_match>(prev_stack_top); } } From 14eaefdceba3be88265a6992b677292ba8dcbf4c Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 19 Jul 2017 15:52:34 -0600 Subject: [PATCH 48/97] Make `front()` `back()` checked --- .../chaiscript/dispatchkit/bootstrap_stl.hpp | 69 ++++++++++++++++--- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index 6710ed00..7f409a3a 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -335,9 +335,24 @@ namespace chaiscript template void back_insertion_sequence_type(const std::string &type, Module& m) { - typedef typename ContainerType::reference (ContainerType::*backptr)(); - - m.add(fun(static_cast(&ContainerType::back)), "back"); + m.add(fun([](ContainerType &container)->decltype(auto){ + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.back()); + } + } + ) + , "back"); + m.add(fun([](const ContainerType &container)->decltype(auto){ + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.back()); + } + } + ) + , "back"); typedef void (ContainerType::*push_back)(const typename ContainerType::value_type &); @@ -380,13 +395,29 @@ namespace chaiscript template void front_insertion_sequence_type(const std::string &type, Module& m) { - typedef typename ContainerType::reference (ContainerType::*front_ptr)(); - typedef typename ContainerType::const_reference (ContainerType::*const_front_ptr)() const; typedef void (ContainerType::*push_ptr)(typename ContainerType::const_reference); typedef void (ContainerType::*pop_ptr)(); - m.add(fun(static_cast(&ContainerType::front)), "front"); - m.add(fun(static_cast(&ContainerType::front)), "front"); + m.add(fun([](ContainerType &container)->decltype(auto){ + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + } + ) + , "front"); + + m.add(fun([](const ContainerType &container)->decltype(auto){ + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + } + ) + , "front"); + m.add(fun(static_cast(&ContainerType::push_front)), [&]()->std::string{ @@ -577,11 +608,27 @@ namespace chaiscript { m.add(user_type(), type); - typedef typename VectorType::reference (VectorType::*frontptr)(); - typedef typename VectorType::const_reference (VectorType::*constfrontptr)() const; + m.add(fun([](VectorType &container)->decltype(auto){ + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + } + ) + , "front"); + + m.add(fun([](const VectorType &container)->decltype(auto){ + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + } + ) + , "front"); + - m.add(fun(static_cast(&VectorType::front)), "front"); - m.add(fun(static_cast(&VectorType::front)), "front"); back_insertion_sequence_type(type, m); From f465d2cecaab97b3e765e0072596ea8e136254fb Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 20 Jul 2017 06:10:31 -0600 Subject: [PATCH 49/97] Make sure to not deref null parse node --- include/chaiscript/dispatchkit/proxy_functions.hpp | 11 ++++++++++- include/chaiscript/language/chaiscript_common.hpp | 6 ++++-- include/chaiscript/language/chaiscript_eval.hpp | 4 +++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index 2afda8dd..0c60315f 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -354,6 +354,7 @@ namespace chaiscript m_param_types(std::move(t_param_types)), m_guard(std::move(t_guard)), m_parsenode(std::move(t_parsenode)) { + // assert(t_parsenode); } @@ -379,9 +380,17 @@ namespace chaiscript return m_guard; } + bool has_parse_tree() const { + return static_cast(m_parsenode); + } + const AST_Node &get_parse_tree() const { - return *m_parsenode; + if (m_parsenode) { + return *m_parsenode; + } else { + throw std::runtime_error("Dynamic_Proxy_Function does not have parse_tree"); + } } diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 5fe377c4..b157cac2 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -262,6 +262,7 @@ namespace chaiscript bool t_dot_notation, const chaiscript::detail::Dispatch_Engine &t_ss) { + assert(t_func); int arity = t_func->get_arity(); std::vector types = t_func->get_param_types(); @@ -310,14 +311,14 @@ namespace chaiscript std::shared_ptr dynfun = std::dynamic_pointer_cast(t_func); - if (dynfun) + if (dynfun && dynfun->has_parse_tree()) { Proxy_Function f = dynfun->get_guard(); if (f) { auto dynfunguard = std::dynamic_pointer_cast(f); - if (dynfunguard) + if (dynfunguard && dynfunguard->has_parse_tree()) { retval += " : " + format_guard(dynfunguard->get_parse_tree()); } @@ -350,6 +351,7 @@ namespace chaiscript std::stringstream ss; if (t_functions.size() == 1) { + assert(t_functions[0]); ss << " Expected: " << format_types(t_functions[0], t_dot_notation, t_ss) << '\n'; } else { ss << " " << t_functions.size() << " overloads available:\n"; diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 3b9c7837..a9819440 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -288,7 +288,9 @@ namespace chaiscript template struct Fun_Call_AST_Node : AST_Node_Impl { Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, std::move(t_loc), std::move(t_children)) { } + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, std::move(t_loc), std::move(t_children)) { + assert(!this->children.empty()); + } template Boxed_Value do_eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const From 0f74597139d6ec8bb519b460e425c461ec839fc7 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 20 Jul 2017 15:08:53 -0600 Subject: [PATCH 50/97] Limit when coverage happens to only one build target --- .travis.yml | 2 +- CMakeLists.txt | 25 +- src/libfuzzer_client.cpp | 342 +++++++++++++++++++++++ src/main.cpp | 21 +- unittests/fuzz_unit_test.inc | 53 ++++ unittests/fuzzy_tests-2016-06-29.tar.bz2 | Bin 84871 -> 0 bytes unittests/fuzzy_tests-2017-07-20.tar.bz2 | Bin 0 -> 129965 bytes 7 files changed, 417 insertions(+), 26 deletions(-) create mode 100644 src/libfuzzer_client.cpp create mode 100644 unittests/fuzz_unit_test.inc delete mode 100644 unittests/fuzzy_tests-2016-06-29.tar.bz2 create mode 100644 unittests/fuzzy_tests-2017-07-20.tar.bz2 diff --git a/.travis.yml b/.travis.yml index 7471e1ee..888922b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ matrix: compiler: gcc - os: linux sudo: false - env: GCC_VER="5" CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" + env: GCC_VER="5" CPPCHECK=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" compiler: gcc - os: linux sudo: false diff --git a/CMakeLists.txt b/CMakeLists.txt index 048c2e54..effc7aed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -314,32 +314,17 @@ if (RUN_FUZZY_TESTS) file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/unittests") execute_process( - COMMAND ${CMAKE_COMMAND} -E tar xjf ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzzy_tests-2016-06-29.tar.bz2 + COMMAND ${CMAKE_COMMAND} -E tar xjf ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzzy_tests-2017-07-20.tar.bz2 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/unittests ) - file(GLOB FUZZY_CRASH_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/crashes/id*) - list(SORT FUZZY_CRASH_TESTS) + file(GLOB FUZZY_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/MINIMIZED/*) + list(SORT FUZZY_TESTS) - file(GLOB FUZZY_EXCEPTION_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/exceptions/id*) - list(SORT FUZZY_EXCEPTION_TESTS) - - - foreach(filename ${FUZZY_CRASH_TESTS}) + foreach(filename ${FUZZY_TESTS}) message(STATUS "Adding test ${filename}") - add_test(${filename} chai "-e" ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/crashes/unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename}) - endforeach() - - set_property(TEST ${FUZZY_CRASH_TESTS} - PROPERTY ENVIRONMENT - "CHAI_USE_PATH=${CMAKE_BINARY_DIR}/unittests/" - "CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/" - ) - - foreach(filename ${FUZZY_EXCEPTION_TESTS}) - message(STATUS "Adding test ${filename}") - add_test(${filename} chai "--exception" ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/exceptions/unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename}) + add_test(fuzz.${filename} chai "-e" "--exception" "--any-exception" ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzz_unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename}) endforeach() set_property(TEST ${FUZZY_EXCEPTION_TESTS} diff --git a/src/libfuzzer_client.cpp b/src/libfuzzer_client.cpp new file mode 100644 index 00000000..1c90d0ba --- /dev/null +++ b/src/libfuzzer_client.cpp @@ -0,0 +1,342 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + + +#include +#include +#include + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include "../static_libs/chaiscript_parser.hpp" +#include "../static_libs/chaiscript_stdlib.hpp" + + +#ifdef READLINE_AVAILABLE +#include +#include +#else + +char *mystrdup (const char *s) { + size_t len = strlen(s); // Space for length plus nul + char *d = static_cast(malloc (len+1)); + if (d == nullptr) { return nullptr; } // No memory +#ifdef CHAISCRIPT_MSVC + strcpy_s(d, len+1, s); // Copy the characters +#else + strncpy(d,s,len); // Copy the characters +#endif + d[len] = '\0'; + return d; // Return the new string +} + +char* readline(const char* p) +{ + std::string retval; + std::cout << p ; + std::getline(std::cin, retval); + return std::cin.eof() ? nullptr : mystrdup(retval.c_str()); +} + + +void add_history(const char* /*unused*/){} +void using_history(){} +#endif + + + +void *cast_module_symbol(std::vector (*t_path)()) +{ + union cast_union + { + std::vector (*in_ptr)(); + void *out_ptr; + }; + + cast_union c; + c.in_ptr = t_path; + return c.out_ptr; +} + +std::vector default_search_paths() +{ + std::vector paths; + +#ifndef CHAISCRIPT_NO_DYNLOAD +#ifdef CHAISCRIPT_WINDOWS // force no unicode + CHAR path[4096]; + int size = GetModuleFileNameA(nullptr, path, sizeof(path)-1); + + std::string exepath(path, size); + + size_t lastslash = exepath.rfind('\\'); + size_t secondtolastslash = exepath.rfind('\\', lastslash - 1); + if (lastslash != std::string::npos) + { + paths.push_back(exepath.substr(0, lastslash)); + } + + if (secondtolastslash != std::string::npos) + { + return {exepath.substr(0, secondtolastslash) + "\\lib\\chaiscript\\"}; + } +#else + + std::string exepath; + + std::vector buf(2048); + ssize_t size = -1; + + if ((size = readlink("/proc/self/exe", &buf.front(), buf.size())) >= 0) + { + exepath = std::string(&buf.front(), static_cast(size)); + } + + if (exepath.empty()) + { + if ((size = readlink("/proc/curproc/file", &buf.front(), buf.size())) >= 0) + { + exepath = std::string(&buf.front(), static_cast(size)); + } + } + + if (exepath.empty()) + { + if ((size = readlink("/proc/self/path/a.out", &buf.front(), buf.size())) >= 0) + { + exepath = std::string(&buf.front(), static_cast(size)); + } + } + + if (exepath.empty()) + { + Dl_info rInfo; + memset( &rInfo, 0, sizeof(rInfo) ); + if ( dladdr(cast_module_symbol(&default_search_paths), &rInfo) == 0 || rInfo.dli_fname == nullptr ) { + return paths; + } + + exepath = std::string(rInfo.dli_fname); + } + + size_t lastslash = exepath.rfind('/'); + + size_t secondtolastslash = exepath.rfind('/', lastslash - 1); + if (lastslash != std::string::npos) + { + paths.push_back(exepath.substr(0, lastslash+1)); + } + + if (secondtolastslash != std::string::npos) + { + paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/"); + } +#endif +#endif // ifndef CHAISCRIPT_NO_DYNLOAD + + return paths; +} + +void help(int n) { + if ( n >= 0 ) { + std::cout << "ChaiScript evaluator. To evaluate an expression, type it and press .\n"; + std::cout << "Additionally, you can inspect the runtime system using:\n"; + std::cout << " dump_system() - outputs all functions registered to the system\n"; + std::cout << " dump_object(x) - dumps information about the given symbol\n"; + } else { + std::cout << "usage : chai [option]+\n"; + std::cout << "option:" << '\n'; + std::cout << " -h | --help" << '\n'; + std::cout << " -i | --interactive" << '\n'; + std::cout << " -c | --command cmd" << '\n'; + std::cout << " -v | --version" << '\n'; + std::cout << " - --stdin" << '\n'; + std::cout << " filepath" << '\n'; + } +} + +bool throws_exception(const std::function &f) +{ + try { + f(); + } catch (...) { + return true; + } + + return false; +} + +chaiscript::exception::eval_error get_eval_error(const std::function &f) +{ + try { + f(); + } catch (const chaiscript::exception::eval_error &e) { + return e; + } + + throw std::runtime_error("no exception throw"); +} + +std::string get_next_command() { + std::string retval("quit"); + if ( ! std::cin.eof() ) { + char *input_raw = readline("eval> "); + if ( input_raw != nullptr ) { + add_history(input_raw); + + std::string val(input_raw); + size_t pos = val.find_first_not_of("\t \n"); + if (pos != std::string::npos) + { + val.erase(0, pos); + } + pos = val.find_last_not_of("\t \n"); + if (pos != std::string::npos) + { + val.erase(pos+1, std::string::npos); + } + + retval = val; + + ::free(input_raw); + } + } + if( retval == "quit" + || retval == "exit" + || retval == "help" + || retval == "version") + { + retval += "(0)"; + } + return retval; +} + +// We have to wrap exit with our own because Clang has a hard time with +// function pointers to functions with special attributes (system exit being marked NORETURN) +void myexit(int return_val) { + exit(return_val); +} + +void interactive(chaiscript::ChaiScript_Basic& chai) +{ + using_history(); + + for (;;) { + std::string input = get_next_command(); + try { + // evaluate input + chaiscript::Boxed_Value val = chai.eval(input); + + //Then, we try to print the result of the evaluation to the user + if (!val.get_type_info().bare_equal(chaiscript::user_type())) { + try { + std::cout << chai.eval >("to_string")(val) << '\n'; + } + catch (...) {} //If we can't, do nothing + } + } + catch (const chaiscript::exception::eval_error &ee) { + std::cout << ee.what(); + if ( !ee.call_stack.empty() ) { + std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")"; + } + std::cout << '\n'; + } + catch (const std::exception &e) { + std::cout << e.what(); + std::cout << '\n'; + } + } +} + +double now() +{ + using namespace std::chrono; + auto now = high_resolution_clock::now(); + return duration_cast>(now.time_since_epoch()).count(); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + chaiscript::ChaiScript chai; + + chai.eval( R"chaiscript( +def assert_equal(x, y) +{ + if (x == y) + { + // Passes + } else { + // Fails + print("assert_equal failure: got '" + to_string(y) + "' expected '" + to_string(x) + "'"); +// exit(-1); + } +} + +def assert_false(f) +{ + if (f) + { + print("assert_false failure"); +// exit(-1); + } +} + +def assert_true(f) +{ + if (!f) + { + print("assert_true failure"); +// exit(-1); + } +} + +def assert_not_equal(x, y) +{ + if (!(x == y)) + { + // Passes + } else { + // Fails + print("assert_not_equal failure: got " + to_string(y) + " which was not expected."); +// exit(-1); + } +} + +def assert_throws(desc, x) +{ + if (throws_exception(x)) + { + // Passes + } else { + // Fails + print("assert_throws failure, function did not throw exception: " + to_string(desc)); +// exit(-1); + } +})chaiscript"); + + try { + chai.eval(std::string(reinterpret_cast(data), size)); + } catch (const chaiscript::exception::eval_error &ee) { + std::cout << ee.pretty_print(); + std::cout << '\n'; + } catch (const chaiscript::Boxed_Value &e) { + std::cout << "Unhandled exception thrown of type " << e.get_type_info().name() << '\n'; + } catch (const chaiscript::exception::load_module_error &e) { + std::cout << "Unhandled module load error\n" << e.what() << '\n'; + } catch (const std::exception &e) { + std::cout << "unhandled unknown exception: " << e.what() << '\n'; + } + + return 0; +} + + diff --git a/src/main.cpp b/src/main.cpp index e826def0..f3bd2d82 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -308,6 +308,7 @@ int main(int argc, char *argv[]) bool eval_error_ok = false; bool boxed_exception_ok = false; + bool any_exception_ok = false; for (int i = 0; i < argc; ++i) { if ( i == 0 && argc > 1 ) { @@ -344,6 +345,9 @@ int main(int argc, char *argv[]) } else if ( arg == "--exception" ) { boxed_exception_ok = true; continue; + } else if ( arg == "--any-exception" ) { + any_exception_ok = true; + continue; } else if ( arg == "-i" || arg == "--interactive" ) { mode = eInteractive ; } else if ( arg.find('-') == 0 ) { @@ -383,11 +387,18 @@ int main(int argc, char *argv[]) catch (const chaiscript::exception::load_module_error &e) { std::cout << "Unhandled module load error\n" << e.what() << '\n'; } - -// catch (std::exception &e) { -// std::cout << e.what() << '\n'; -// return EXIT_FAILURE; -// } + catch (std::exception &e) { + std::cout << "Unhandled standard exception: " << e.what() << '\n'; + if (!any_exception_ok) { + throw; + } + } + catch (...) { + std::cout << "Unhandled unknown exception" << '\n'; + if (!any_exception_ok) { + throw; + } + } } return EXIT_SUCCESS; diff --git a/unittests/fuzz_unit_test.inc b/unittests/fuzz_unit_test.inc new file mode 100644 index 00000000..87b8e518 --- /dev/null +++ b/unittests/fuzz_unit_test.inc @@ -0,0 +1,53 @@ +def assert_equal(x, y) +{ + if (x == y) + { + // Passes + } else { + // Fails + print("assert_equal failure: got '" + to_string(y) + "' expected '" + to_string(x) + "'"); +// exit(-1); + } +} + +def assert_false(f) +{ + if (f) + { + print("assert_false failure"); +// exit(-1); + } +} + +def assert_true(f) +{ + if (!f) + { + print("assert_true failure"); +// exit(-1); + } +} + +def assert_not_equal(x, y) +{ + if (!(x == y)) + { + // Passes + } else { + // Fails + print("assert_not_equal failure: got " + to_string(y) + " which was not expected."); +// exit(-1); + } +} + +def assert_throws(desc, x) +{ + if (throws_exception(x)) + { + // Passes + } else { + // Fails + print("assert_throws failure, function did not throw exception: " + to_string(desc)); +// exit(-1); + } +} diff --git a/unittests/fuzzy_tests-2016-06-29.tar.bz2 b/unittests/fuzzy_tests-2016-06-29.tar.bz2 deleted file mode 100644 index 95fd20ae03b75b4446c7d9108652d70ab788a428..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 84871 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 a#n@+c`R7lYuI!Khi@744C`d3a_(K4rF{6V3 diff --git a/unittests/fuzzy_tests-2017-07-20.tar.bz2 b/unittests/fuzzy_tests-2017-07-20.tar.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..35df734fc0c5c0ac4cd4a4afe63ff27f485c0684 GIT binary patch literal 129965 zcmagFcUV(F(>I(zLN5s<^cspG(m^_yKmwsBH0eSp(xgfi6+#OEh0vr+?;uU2Dpio8 zR6&ZOQbj;4i2e0`p8LDr_rGuc*qz<^?ac1kIlFtEoulvQqp5^(khXRR(^p`kUs}2K z_y3RH#+LpxTL=6vooNBUfEb44B45=naYqEJ*Z-}*KKefl!+(pO#3YxZ^xwSZ_O zfzcxFN}sLYU5|jQT$`&@ie$65al47)vv{|9AB4Z65);Zvc;;_q_?wS)teNjoWc!i(~DJ z6N=+`9`<4r01!I;A0&ZU6ad_%KFW)D+t3qqgY9&WF#zxbAU_BOAOQpbfPUcX;Qy*T z8h-?!f45P@`3Nuz3;_J>DARs8%kcr&^$!NzV|>)|Pw9UMf5us+a8G3rd~X|tdzAV76Ma4o{vQb7njV0&Z>R0sWx&52fTRxqRF=)s#Q^}K9E1OZ0Dks(1mJuW zzW$Le4hG1ii~pny*m!kp*+^Y{AXn?O4ig3dfXBlB1<#%VL~k5!(yswj0cBu-003Ea zd}H&I^S6JXhyQ&dm689o&GjDx8*%?h0RZ9Y|IPoC(o66E*TsZqjXF%M9dqRx@+V@B zn$%WcCLT`Z^L8_)|3x#fOG&i!bxpr`kr%J;?^oExmfhDo(coPi5^sCHIepb{@0GUh zhvWaN8{jx7=LTbJL5BhWI(ybH^sDe)i1Gm6>gx|S-mTaEw`>2?i!N});C~p#YyA)V z+y96CPw`)0TX`b?WgKw?2CdS9Ctla%{!{zEbd4VUPoPRKyfmoPHq!95%jc!P&;6+X zPyA1t|3g6EKOHrIvVU{7%GH27umxW#b$$2$nEod^*4_M18}PUd5&gA7iU`P?5qJPk z@@ED&tUj&5^CXzm31oT?UND?o-Jc-fut}0cY?5$iAwv$;G@w(pBEiEJVyjgq zM%A~>w}Gkt45$Rl!T?nZ*b1?$Bwvx_Y@@O`cEGI9Kfhpz`_I7sUs}3IXH{pIS|z~E zZdiELFrV8@!Z5kehXEiBV2%TMda|=gbAuGk00sPt&>HFdd;p!NFRexb&4!^2Nmnp~ z7WBoSRc;f;Z>?VnSbtJ2ty%m++$%Cyy{&6l=ra*h7?T{g8YjTc4lGmlnfQ!7uujEBn?!7Gye0YpKZ@5?^=$!Eg-t-sdYo;GggOdpkwSCT4vvmu&JR z@BRtk-Tr=0b!7MXS1m90f^TiwtzX_U6S&uCS-6YO_cgse4~{*QZ|Dg2JTsksd~WAc z!Hwgm#fJwDn6Wjs&R~YlOU|qMlicsTuH`AwO?j&=ZL(hYG{JPfQ!HIT^ICFRpylc? zMOKn-Zti!+*vF{*N~d*N$=>(wT$a<*2-Z4vxE4vBygu*-4OdwsznlVPypQ#8BJmq& zblgcXYUi73|#tMewb|uGjv7hZ`BXYl`%bc zOBc*4qVsE-aL+ShU${-uOOVD0U!t6i&Ezcqzw9!||1gbG)m# zu3i)vp@+OBsh@m&*>gQlJ|%`ZaQ!?o?%#5i4qd0o7QfavQvGQp<>|081p>|-TPH2E za6`HzN_lB|$$CSn zsA?&E|D|$XB-iyt(^z=~`y*IitJgr_uPX=W?s|8oz@%Vj@=#3NJvBUA1=piXVGjpT zBIx3UAom@WIPIaf+puQ^Sl>w7ZEY)QtNQ`4##o7i@S?J!oO;EjwGf__vFtz|7SQ<% zPb-I>(nrW#u6V$p*!9MzBp`6Vn>0W;k{DeLHE{JBR<9Lvr2DjyIarz+q}E#?;o#HK z`^;*)o2UtnGzvv+Foa^;PG&&=88!yB(4FKf76zV(kYh1@Key!LZF zFLhC}t527+?hGDcmOgyu z5qHo(IR~pk|6zAEgbs!bJr#S>A`EA)3+JNKYRYyBiOtU%lHLjlLNM@uzO<{|qIP47 zKhSU=vXYcSuT>|rUX^L=n6=fLw9L;cK(S12UF)5%5E&f1XDj=SMkOV z#IVHFNftE4h+D^A7Yr>nNYj4zgfTSgjioQ@9JcArxammVBLZbUbXZUtl3L12O?@Ka z#ualpC!<+$fC6^3epJ}7py+rfFns-h^aEKSMU51zl81DaH}Pk(;fN7u&K z+dxP#La+EyhLehAUCne4R{33}s>(#W>(~56jcvvep0U#V?hlebwhi^DV&KC2yZ^x|2@0tSD${UF_VP{ z5o+SQ{+Q*G;x&h37A1et3kxM5;K6PUZZ=U-rfu=;)7Sg5Z%^G3T77e@thw@Z*S}v^ zPd%)hx!iv?UWB?FK;}-I^)!^;EDH`XL|5MM*=`mYINjpoRu&rVUYDq>3iqz?>D4*B z7}OEstcBmnbFO~O9sNYh%%9==l=JtYRfUAPp=H-E@5?WkVzkGw(wF2)W|*y3ybZjb z33q9wCs!gPO%Wc^SUb5(clz$n#EQI4BoMO|)eJ`f=`;Su$5Q9wVmZ_`BvlgTqAV)E zNHr}+Yfe$^EO=Bp`PtE#619;|_{H9E?Ia3+bBVP4+kgvmEo|?eql3EqM`uM;vaax~ zAyCf~N_MZnen<)J$(nIUd=4Z787p+N^?#j-N>quKTUFt*m$q?j#^=hCkjSUK_27HL z$+F1%&t|RtdhVyZK4Z<6C0#_29|x4llsvk%?VRt{>$&v8uL_o34*Blmr9_pI5Lo8* z;4IFWPW{Sf^0^W!$9udAb(0Cq?9}sM(X?^E-_){ndfeC49-hTTcMU_p49~nZ=AuV6 zFzj96nlsD<>or8VSl-Z^)PEgQk5>B)V+ruhl%devPYYN)!3xjFut9rA7JMrK2Ii5a zK}fPhTO8I-f{O(#Zmb({Zq!&|zN(xFNf&=$uIR9&oH&UeMP$Nw;plc*(fG37vM=5r zDr8}>dD!422JO_FGhDy}^^~iUVKLFq$||&p$EU-R&LeVY`bb%F{W&Hp3;ux2k#myD zgAP|&;b~%JG9xmEqD3LsSlq4Z%V=mT*@Q9nYk{Wda++}JH)hjo6j>??B?FGfn|F~X zT5~2nHJ4a$X$UrYWE)<_rGe_FWMi97B-MHC(UoP=I{Aia7zQaEO&@|tw7PlqY^F1_ z2I|&^6Ju&B39P1O!ZE?Nxa2!I=uR6q+*VdWF`*3H3Io$4VR1LTw6XcY4JfRq-U+iA z4TIqaXU#`k@5Yc4mrN2JIPfB}>QqItxC|w(iia+f;4s5}7Soh6Ji{cLY{e=iSp}=c zx3aZoK`o_?N0{u#?92Z#W|^I7h=xomD}qAB(Z&Sa1ISr`nTcOv$nzaHZxgOzi^e!7 zaDIYf{1#)8s9!&hcAvY?NUxG;Chmh{dA61ub2K}}CxLgMC^KOeT?n8=A zCug_&Mei9&%}2~HVM*ejMIQAcezIxV3gWNkTYMvYA9!#hFegWzNcU0ClU1PpA{RSO zZG0)UlcJA-e#t-|%E#K(9__;1qoRz&@-G;%p>O$^SEyWgp z?pXZz%(7myRZh&BUfpIMPW_`+_xSCe-Xz0oY|5fr7<%NKoly2(&C9b4L(xlZg!frD zf>8OkW*_y-CL%nR=znmNIk9Tv(u|T3OVUv|E|lUFJLM`{jSdPX^CN7tF@^I*gAMkF zIUDY6W1+FHj^OcSYE~%v)T(CMm*vA^$~e`Pxq*IX%50M}gR`hB<&v&ub#-K&9Iu`K z``4jh$D*=d5XG_5FU-w@Bdt!Kgox0VF@EBQF;#A9RT6z1m&573~)?*3AFxbBE4^UhKJ!llK9i>pM%=mN){eSKg)9fJs+&mm6(b=Q*FL z-+b_4=XbI9w`Sg(PgD*+FU{cs@02tiWizLyYt1{z<(W5>)sE2gNOWf8nX9;hc%iBA z&Sg(`P8Pk-E%$5coJmJ_O6_=BjFMl~A63bnhF#4tIW1K&w+p)YM0`f|X|cnlQ~BQy z1MiNE$J#!=F=?l$v$4216gK_Mri1~%lH!%xEBWSnG0Ai>3$c;obA0_<@5+_KlC>+wR(DGr z%xkKQKpK}!ICPw5tJWOPgd?f-ho>g= zLm&OOX^czc^vs)IQV|4&aY|i7^ek2<3UE*Bf_fG-kqn4@%Qr@ZUxYzI;}}ASo%Gkw zY%aD^I6A(5xkCW;0Dg4?;?jC$9)ITGe)z%d{@1tn%YRY<98C1us;uki_(f_R%9Smi zsce{ye50#CLQ+cRtO;a}9od^lqDoKcqG>jYlF0qn8K#|h(FheW1cd910#MRS@y_p1?`*?luQW}Twj2WUN9)=2v;0i$ zD@ltrvdgx7f)8|)J5$cz#rpPjrFwr8aI{vh*U9a>7b#^K_2Y|ETP$m%*GTA2d{yhvJvSch`DdSy8G>Bllck%Z`?L?DcEx)jRB+*9!({ohGWg+I;sz`dci$H}jFr zI-9!XD|0Rb54Jl8X_ibJG3V~-e~=yWuF1|D7!wN6j(>{+jSd}EV7fxTR?FTMNOvCb zsTBTja)8}Vj=hhOAqOPX1+PTv>8cZ*Dgv8Aj0S7K9L3u`k7WG49~rDC#e^*MT$C0o z^b|6=F`+V^tBcP$eQ2hfsPQ3xiS6O~Udod8Yv*$UcRh?ZLw#~xw}SXfA=5h~u(%eo z1Dv%V>Jn?cDB4K=#;ogq1uHNPiB7BY55hP^be!j}d#`BFp&fLAP*CrpeI1Fy@ z=sE@Qy*w$ry{}&4tT8o39uj=cQ8?JweDy%_QVcTwBi@&>}mLtkS zPAY%JDb>{2C}(>u+fJN+{Vedr$5L-Q;cXL0D+_n}GVrQ_yGfHxpZXHtr(Tig)h*Th zZ+oAPI%W?yw%4`fhL#MrY+Tlpve>LXnqT?h7>%FM^4_FY%5%KU+Q{B~*J}C5QURk>X4|q7rj>MG)1?x3j^c5ju_Jm&yq5=yEY@XM^t-QEG6r+UlTuMU`FN+^g|m9+gIu9 zXcN+D4y!SDTJpyj-%Gbw&9=1dwvU3p&wpf^(n<@ab*iH$k%_65^m{Ya#cv` z5OnJ@`(EO@YhWrJF0_2NupIB;G;#03<*=?ZR8$s1h0bn4X1daW&OkE>deu(2G65AL z75u?vO3&`JJ(ToVZVO3BFJHf!xV=z%5aaV%^GlXb#X}{j!)i6?g?e5)m%)QsiTAaO zFr%EpN4i&puNUSTnbm7g$d}N*d9!`}awFVopV52fLr95Saqd8tx5Qf3=1=-iS!MO` z{!UL#RrA~116Sk~QW(B@t=PJ;*S~6OecR5)*Wl7}dbguV<_12$(U3v~4R$u-e>sS~ z);=$ne6aft`tnKdbjUNgmW6YYZ_L(@q`nB>E>^U8rg!gpqkHMVL1x-`N=WdX5E(c) zL{da*%a^xz<=1N2_e!=q4xG6)#Xx~Ftc-bhr({4oOXeGa!_#kVEMdyHq@aS(rsZ>CNd3z!Pywn^HC{dq*05_lv)q%ky(1}9I{l{FD< z?Gs?Owj@co4e_6P2^b7UZ9rBiL5PM3Y8qc1d5EH9kE&ym#1ctl6t#|wYJyTJWO5sf zVBgRN#u9AFl|Z|H=V(fWl8vW5r42%akQAX%j186uu^-}*#M+aeAjUkvRxHQ{mWn5A z_%R&RO>D>pi}L_OiS|kuAdv=7Cs9YSSQ4UQ)KCcy#oF?baWD^EwE>X| zfs%?d@WK%o?EY{8yH(kPs&h=VdH@yS}T5r}Y48xoZX zIfNrsAW>sPuq_A&rm{k@@brJvMQ*c&+AGl@_6?{?33$3ajl|SuV`ERLkbqlYiua^+|@inz*W3t*O$8n(&S zclH^%pSPFHwQ#+fxHgyUJ7=^)_S9wicU~IqU}zjZ;v0}p)Vg{uGz)!#kYMv@dLEm03@szw~ibe>0Kcr(b1W z_RO&(?C6Vpympv^>shtgfOW)c&S-_{50n_^PM=vf7inuWf6x*KCWAS>)xF{3_hF5p zHC<}UQ=UsJV@J|*EH{SiR+d6PVN(Q-1;?VWwXLycx`MYOoh(ZIMf3cc^@n-ZIqk>; zWWGl&)=!1q-d7#u2WgpIO?%#R?L+?*bG%{88#j&0MLAm*b?z5}ub_@2UTldxKjW{t zrbRT@410TwutiVGeIW7lhPk(msE3>LYYAFC49^TcG!+U|xC`oi;miyykq>)Znc9_?;m3uABwvaIKt6;28VcnN8#E1E*Kt z2Hx0@Gl=>u*$3DweyR6fX1U5?$hE5VRPC$KhsDpaJ*G3Wt8ZKT7~2aIlUl>eVM2W< z$hzI0f_TF4H@}a2Nexeg?YffJSP4q~5-tkv#LZ+%aS!Te=FAT4eaVe!OZb3HSS9I8 zaamrp{f?xv$W}v2lkj8;DI_ezU&%h<(gop-oG$Afv80>={zXfP#Y-b9z9auWt&MbK z?L~#J*=q7n5}&b0pQ!E*>lb%Gx=RO2)Z5~gxRRS&_TZpQuURaVy+wHZ{Dh?_J0>H- z>&bbx(0R2tW?cAW)(MfUK|}45+&;L=HmggL^Fx+qO{0R6bLU#>^x8;YO`dt#_aZq} zR&66$ysImw*p;GW5{rXEl514WMOAF&&s%TJU~ETieYNwgiV6?v>_4^1h7g^Jgs(^M zpXRRG4A`zRzqrDgCSo}mbxk+L&Z8=K`0I21Xyg_T>k2&U&GH07N+?oq&T3AUhdZgc+pb!4wr5%5E(M|(e~q~N@l*aE})fgx|@gTSRb zT%OX_9JG>kBR!YKEo{&=a>#}JW8IF$sxMXsDFL^8KMCT7KNX(~Zb5w{`nXD;^uo^Y z&f<+6W*YI&Aid>JW{-8{9QvPp`g&5K<^K}u&*WcGc~@St7(dgWZIm}#;ybWDYNR*P z9maQuC9pS3GPdeKo9~@L@3p4hGf&LK)0nB!Ea41?GNec=SD?JXU5>Hu{gYW@vD9$h z`{;RhJ)KnNw2&+PH9Mp4u0$IP7+NGM6EQBD_~F7C$CZm-fl%WS79p%K47_8Kq>u2yz@ry=i9T3|B@hj~@e6JR_vp#nBfxsd2K*2!`Z%90jIy|Jb`9u-$cPX>LZdDBu$ON^jeaQF zikxOv5teXt_iwk*u)sfT0QzeL?iexVI;zAENf2?7?NtY+2H*21$$erJ(=z(&yH#J)8B6YF{8*fw@1e~)+Uu)BtEHpKMv^~AJ_-UEj9`Y&Ji zaaSMzMrmBZ4(KZ$6T`DFj9qKp%Lqwn(I8l?5#&WbgLlb~ChF~mr zGU1_A@y#AQ62nL37k*DP06SFCqDCWN`~KH6x?AL*-}^2xSoy3z z)7f-Ig;`dHfB=Uype)AT4_9TAonUP0KU;?fL1IHPgXwSgH5Zz-9}*VWuCp1Br&7!R z>W^2ISr7367%{L%*$y7DTo_RvKz!;TAvLsO8A{_8K%+u0WjOt&_m_+d+(uvI^oH2t zU-GTJ@g;vu{P9gyU}}T0{mG-XYR{|5#|k|uCaCU!sk`ZB29^^u3hc&h7jr#v;a&Eu zcIw8B$tztoeA+5e)#3|a(RQz=O+=h$VqsL}V z$_GI|Ov+jE`I0JCYC^9<6lt#R8cYI6531op<&?~{w!`Xy@%&8{ z4eyG1ei$?7{)!ZuvUM-40AUeZMtgrOH1FZxP9U93VV?JgS#XMPSZRK(sxskLYA&{O z*BTxCKISd9R^{=`u3@YD`d?CmH5yo)&vU*Yxf`n6*XPK!HTYB!@uUV5GlU)2w1$w! zi4*ybN^tk`qj{OzCQY0FB3IyYA1m;;9<5}!9 z5vZ7LZ#k6XW%q#kc2R9?gqBw2G-?_Oduy_2ctj1g!paywnvPN9*5hJkcp)A)UvN{^ z1gRiytZIEtat;BB1GDhC6%~ou6uSG$2GU)?u^?agw5fR6^N%^YTdOBO+4r)O|G-1c zv})ks)~vE(g>=00ABA{A+<&mR;v0zy%w7uZos;ZKMvkpjq^?D82fwLa*{*sPcqK1E zUW$c48!-^{YvI?Qs!1G&*><;llah35@KwyKCdl~kduMYBpls((*BfP0%~^~s+1~NG zCufKKr0FsGVl@-QhJhDL^v=oRd&I==#SOc_&1~RiInGbyf?(gO<&^AszHeBVjGX6@ zkf$k*IbDrKRxGe@*rFE9#c`^S8_QnpP4RwoTtczi%KGKVBv5xY<_vwDYagT$FNTI7 zsDW)^$)+YgZli{WyWT>iHC;ik$`OOpCFz0@nY|aQnLASk>lXan8WodQ44tG})gP5M z_0(F9MTZ_d7jT|a&JGv`+sbRZb$*;#TNns!V3YdPXy+tDyefl0&BOF_x6dAQ1^-)( z)$$s)6IO4(WO|qjTjsna_$sBzn}~3{sKV08(O3EPa%=sGrhlNe@*H6j@>?I+qwJlxG?=IjCf8*^`pI%pGOqUO!=$H1>BCVZY zR}CO+50|U+SJP4F8Ax-I%)U&Ff0^k_24TYunKSBkcO$R4EOq9DHOklVy^Zgx3rx;M z<-yV$8u;z&6OXdKX z4f+lFSW2R28W4X}^>*Kv`uGu5M>K(u9JNCMf;fe!cfj}GGhN=kc~B$%^R*Koq8y_0 z8cbVNiK<-_W?p`yts(u~Y$2l-Z#v`R`$FSKDCp#M27}*Y{PEA4gSBxRFnUmaibftl zk@@0^a|JA3$&_aC7;s0bZ&gNvuUOI9gq*QSS(f}Ey*kK<{`lhp$^Td7m=@R06Tq`; zyAAg-V1<)E3S}6wL zk?KqP7%u&hZ1<73^1$!Cd&v8Dri}5vMmLOYxT9cURgOAY}@iOy}$A zMXZ&MOpH`K6qFWmv3Wrxx+AhXEEA{iJN#Hx-%xozRqL|zQ#JXu{D_8j6_GLMb=hXo z32`-IGf{>+Z!M%d9|~O%%4`!=<^&1-fuG;Jqd@Y^W=QONj)yaekfq?j`WrMcu8N7e z6&AzfMQa$oY7Q_kssJIjhF%XZJ#VEPy>~H4Xk=$-e-ttb8e~JumIsEbUgqvz*kz5T zcw9sgENlSkVxozPYI*TCa3*oAjiNhAz888xV~?_a>GsmTI85Wd&mVQ3v|>-#C!l!8 zQmuFK`NzEg!r|Z`L*Yv z$79l1B?{tj=dL4x8wS!f{~qD5hOV)ahAMp?rexCxK9?k;=y4y`Wtm1F*z=j$?LCb1 z;$w!h%FQ;SX>{AGl3R*>@#T-sn0v+_RQPz(Lm14}B}It3<(gi45k0NbN~Xkfe0?yw zmNd5aT(OQ|G@q=Zk^ROgACgxGasHdz?my&kb zT15@)jBhyrDJ0Xs;wBGh-oOzPF4|)3!XCvDnvyV)|xQW`{-p2_Fk#(JU1k zfaw&HIKiL~$8;2N5&8DgHa6g&Ix+SPqM5(cbJmP0p&V}BgQI{tCE+F?F$abOYNC0o zQzC&i(XG@bLjkKgg7jO^71BUoM3^hH%zY};P)_Y&#>Z?qfUK%Q=IkiG8+h06-jeZy zA_RSJ113TM@}77e{Z1m*HnEB+ag@uvsz$y`CK!>3Z(qz+U}0Ds$kq82jESP##OS?Z z3EpwWs(Qh=pxh1+A2JsmL_!RScA^hXsCEyfa&5K2tUblz*W(uE?AdItvcVmPJ@d+f zU;^>t!Xs#TPa-%SsPCxs4HZ|poAVSH$1|vPRE7aT+y)iJL0;wtm!!@hvzULYz7>d0 z2EvYk(qkctaY=7$jvS313STml#B*u#8kjNQy-hP^rN+<`nQ%aQrlBd7K<~p`!wf={ zeZp1A2Xez0?my>AoNh|`0;DL>$0dA>Ll08G*!i|#eHSP1m!lG3((I@5EQ1Pwn@l3c z6OzUca6=O_>FNwCnG#)eB^BM6vJiZ96ekTeRRI-Okt<5q*#B01);m^-59YI69-Mz7 z`Ot&??Yn@Pft}%;$3LUHt3L``w(<7pjVeOj<@9F zss(du_E7ZoBl63GWr{sZcL5_L-*xhR~bD zmVHmt@|$*TKHoS*HrgMLC`p&@Xp}E;nCm1J&n>HNI2RszfCkGgAEw9U8@?Tm1ih%v zm?6FXvNwy>WlQq~ecgNXzG^om+v}(N5c?|fmsS7eprSnYJk+09G>b7*oW~;iapQce zB6jU+7v1KP@lmpSIp_Qc{YU3n{ADw(y6eCwttO`rJyx&Se{=v3hs>_rq`QPqiWF~G znM~0#)0v%2$UxMd)Jpi>hkW0D7{+d6$Gj15G+0_p%opKPbl048)aD`>KOHq}H=SsE~tf32zc^XNBWt7`Y1c;lA3AXkdq&f~q!ARPsLAWIGc_?uxWvoXal_#8} zZUpzF@&;JH%3ouDuf!S=pB>%j*=|Vpl5kdd|Hi1d_iK_{UjFqZeiqccscsSl<|}Qi z$777eO|jBL7U^L1(?EzKABZn;db)@vH8d)YnN+$Rmvfzi>@|rz5q_yL@Dqp;OC?^z)a`{OF)`dLqY3DsE1_g6`hfKT6f?hpV;)S+=4!T zu^hooAN%8K?U+BnRqlJ8oZ`(u71vW39AIX{l+po28lDGBPBIyvW-shCT_E^$Cakpf zN!`5?u*j_Ou$$%S#Iu%QX`>h|Una{`;VhN;}SB6r>C{#<~w zcRZgutT8QrAixeB&Gi(^$J6W9_vVK5xCbII_=kyU8wg@w9uBDDV?H;HE~MY!7sysj_RO-1Poi8Rof5O9+U;{D*W3HHu`&vF z%)vK5SY?YnD`4IgzXq5rzJyFde(|mmQ>(BYBdyjU{5BsPkpuys^UAynI7~=Durk?~ z-x#qX5PzzgX7Y(c%qwI&pMP2LL~%hyd-Fec`jhHfsytcFseHUc2*db3an^GQJf>qX zR9!n$pt^NoIx!+&E`9(xe|9zbBpR)Q?;ZMrghM@ zbJ4~A_gcCrx24EIUuxd;HQ_Wzrf=VD?==ya8Q=-OdKhX9(PUN4twaF~NLYu>BD6BS zDLpA7aKEP%rtdYKkzrDLKl#MtqWVRJzg&OZ->m6$#>FaB8!Y{5_V{|bN8nM`JdSQ7hEmBy!SG+f^l6ZC+@ynx%F-hI^PC&6e>@u( zyoLeB@MY;8@d#eXJ6Y=neD?qL=#$G5W26f+I{3tA>v#RnSl;Vh*W~jBTz=fv{hQIg zy!h{55I}wFWBY-f;?@srLI*JLFCPwGd7#XQaG!!pWVxGZjUIq2-J0&%FJi}J9jo0S z?NKM4JCq2A@C@QJvYZ>(Bri!|KMi-#wE;U9LY9=%&KLNn9A?7Kp7w(Q)Gz!R|e1LnsA(@tG48u?bbRavc8t5@A%sAYn)Rzk>mB_ z){kD%BWS^>kJT@7_a+@}3)#Go9(_-5$2sUz+S*-rnu$euo^ck+%s`gHpo?|^KXgk3 zGlt^V$Q0D-MH*Ond*_$4#q)!1dc}Lj?>^mpHI_Yh%;%D8)T(ZKy6o1VHfiZdNVZk2 zdo+)D9C)uL=ykpBbFHdh$y{8h7tQ)Yo{uK|#Jai3&aDwlb?&$yoY+SvGaH@8gJK6s zIheXQ8>?x7k(yqA!o3pxm&`N!{7n4DcoxMdKdcWWfhkTvKBrK@i?y5xVq+$Ghq6{b zseWDo`Q#Pcl6}kn)A@dO(aJfnme>`BpSid2sT1z;-??eg3f|JaT5jLOCCRpP7l@B} z#ud;j!^{;S<*C7HL*)+2Cd=l$JbWWB5_*PCNQp1DKT^`FtqlZ8U7e|=BUxl@kiu|O(}=iSlgGauMsOE zEJ93K^E#m7lWqNfQAtZ!T)>5tBW>>84oRyeJ&9?5%4W=u~nxCYTX-0i;69pN{M5)4% z>An%`EPCX@T6=9DId%SFcT%E(>gc&PcW{j&=V<(Uqi@^P^8uWs^KU2zTg{g_5|9sSk|g+@ka%o`)v-0UW_k-F(nT7B`eLt*36FSyzDvXm(mk>#MVY zqB%vfOedvu+O`v{S0!v_5=MDJ?_}Tae_quD?o_TgfWBc=E&A30X|yL>ssfrNYr%+gia+I3 z?&IJ<DUr zdCapoWG1d3w@K~Bv28MNLX?=1A7$XfLpS0rCGFl0TYd6axpymWyEyfe$=0)r>v4be z=zU>dH-Yvok=#;Q;IxGIoagA4*S?;f(*If^jdqtTv8B8qR&g-Jv{^*S#0vc>S1~nA z#7lQw&bOw@+~TvY@FIU9HGIsZWdi>2(SI7$22;7|g}r=VSiGAuouhBNM_6$&2ot6U zDKxc4{cfkxE7M&kPKd4$?zp~i&s{PgGbIa7#&6+nri0Xi`V>`o6d=*TfY)gVx`2N> z&lhyN1P#H!{(+Um;>_M@55J(q5SPh?1m*AU_B)ce&gXouS=Gs3Sgf6PkIuCHOE4dx z`mCK7G911l&(r-WI9YJ|bw$hbqZ_XR$M`?Oa|g8rxHFz^8qB~yTPlLqQW8>~;R+0Z zf~LSgflluY4j0b7cQxp_j`zDD&|f;1?+@?TAY`;}MBMN0S0DX8D4pk8OIyqme@!V+ z_)=TGSY>7=)9?cLw6%);O}DuB_^e)&B>@p~bw}=A2rTmP*e~JmZeiLBHc*s4M7czh}FtJ~%M|1Qk$WfpGgE(hm8CrG7CqCty7(o#oe{AH& z>%}|Ipfttc-cAq@Px9gBL!U--_3<3H+R^91{5JT4>#UNrgT}~By=lSbYy&P<2t1<&r*htL5aFVg1 zKTnPhsqH3*VR{t@Nwvq~C)a}IfXobr$WsMpCxa&48;s&-4y#Bg=g|d9AU50v+;fky7^4h|K9Pg?sp21h6OXymUnieXL5~2x;|;OfLZALr7d{mD`M6_iZs}r%Wo74hZweF8sDmZ=cIcvM99S?RJu^x)r8%Yv*Yn>pqH zo!=h)xQ3{@uj1k z8$qU%U#@Q)*_i!~vknzGuJ`cQS_ESGpcKA~SMJnWG;r?g9eqEGQP>#YLVt=WJmhEz z79I?nayJPHIH(ZSJR16*=}AZ&k-~1i{&@MSMhFAy;#5}xCUIMA{Gw&A>NSs6Q$w+u z6%^+QDd^V)N00#&re@^Hoz1M61xN$NpONuM9U_Z!gNxRnN`jT-gLuRQ#Er$;jK_=Z=_|B+UGCz!cd2wOprFfj z?Yw#4D}LXSYuPVif3)mrz+FjgE;1S~Cdb}hXS`fJS9$r;%_j>%mR*-|LqQV(kIvRj z?~yWt1AvK;5c2gr!y2ZbeMkAx2|qHTxUF%yVBbQWv-1ACVsEgZs5a5V>JTbL>7zGug^vKn+PH?| zFcFnys`o6?ND7kNvMfG`D&Y%*OpbXhOMVJX+v}S&E^V86kkLm%{j(5*3@O)3IzZtz z89Lb+q&RD7o2;Rj5^2!FrxKf)Ac_(zh#R?T6--~6E!G_bY(TAA@7yr%t-D(sf3OETeCWz09b)10Q)4HNYqmc{EU>5u1bF5pV&^nEp7w4S3^r|Bq`zDC62`zM-s<2ot_d0)$o#MS8(k1+Xn)_8 zZlWg4A!#a9x_pIGKK-=dWt8f;Qv(nlP?^O{W~-P7A1)Q4u^4T#=14!RF~#`ajrj`h zW{7!arga5t(^%uLQWi1eUb{Ws29dmKL43vUCd?Kk&kYDLp@?dV`>`3+svoD+d2)EC z&K132W=(cAmPB$Hitz#<*=xp;-|b(RG4VGbtsj6N^%_`W48EwJ7-)Q8HOy0%V35S8 zN~eKMG2!OiBS{q5gkO)o5!<~qHsNuoxVk|;7A9p@oQa7*0!|8k*(9xZ3n{~dprRqi zR`Jr8XO*3GuLjcNF(6pn^-Q`Gx;HSD+F%DaG5zFvD#J%~qyhYndE0q?`$=H#)$`3! zJ5)y8F5n>`XNdG@=4~cUs%9O+%+r`pb7zAhuX-&9H8CB?}O^w%&9J> z`B&hkSfe5CSHl%3D}qwhH$BWm>-Ky1_a#~YgKeJL%mo6k8Ql{`j|awL=VU#ZA-)tg zJYW3eAyh2>hYgZ*K~ly6cCX#@a+zuP{{THe!oSi0 zeKya9!Ux1`FF0(0-nJaSHI?q_DD0CrWID%^8=ldR%H9(tZY3YCiKHQOk|BWE2c(@I zzhKx`!@hZ&3+E!LZ^2*hqt;@k>{O8tMk)v5#YD_tbb=%Oj}$v$*5BnHWMLF6auRz|9!BvE=!8Y#*QB}xgR^x5d`C9R9bJB$cflY=KuHq`V-Qp1 z4TkAtC=#hks=;PtJ`kgn%LzNOZvlr;VM2Vx6C0qt00{e{3 zyou$_pa`?r6!hr#z`h(P6GDI+&{PdfBB;hd9lI7S39&-$SipoZpn+iw0fAV5SEzsK zhFNAF4_ph0rDR3x{$~=Uq&EGFDa;tUM)oRg#On~$5KVr^AnTn&ozK<`iILw#I{Je) z7luHA0#>urt)Rtt;t26UPL1>MzhYc1(SgI)!SMG`XA%Sx@#`FPJ-l|?dRQ31BoTPv zoRAUay%y|mk<7CO@DD{In+rjS$oK=l@Mi2N5CG@N=8K;`*LjE>sG5;XQDUP2fIw}h z-WTZ75Eaq_@I(#;aw@-wxdgFY{K49&9o_oop)*G5?&wFQ?950i*abyVuVk4v> zNdycu*!qib-wwbVNlJ9|s(K+pbC&153E(R(U>u?@tvdDIBh;{s6EsUa_XPL380&h! zeE}ZB`Q9u{R^$2xv28Izm?6^xUZ4%wqMTjy3)l)q1d;P5k%F*5U;>Ds$ihWPK^YK8 zp)8?-h*Sg)#+;0AOenBs1fhHp3cVrOidai*&V^!{DvxRhJ^EmJAa@F*KU;X)xFg2N z6aoYUfiVojw_m^aXj8p0#&(L$XMT>$0@y^skRVK9Vxtx^&pTq8FR3oBNf8KwE8u=w z$hjZ}-*;c3hVE=h2d1kO7E|c%F$0~NG2B*It?YpiE+oMKLS*6-j7N&Or-ykC&VK!} zYYutaY=KjTgSW$ zD!uZLKaT1+p{}x6&E%!*jKb)l5nXVyg&sHD*#hu$+ixJ^zkf&p;f5J1U+{2Pdnn7^ zbtz4A^|H)F2%f_z84S^3z{Mtuo9-;^1 zfsp1!(k9>KYdeOIg7;LMhLh5PS<8>PwO3bEz&X3@cY_wFT9bG>fyM@-S7)GZs>$(^l14K!0|cG`JLqP}9Kq*Rh#LviBtlI1 zbFi8TfHi0Ux|xW7v#$>Q0asY_7N{o7@s!6QoxD+;fAm>POUihw(oMdTR22F_5nj- zR!wRd5Nt2C0B;0v!;H1Xfsx_g6NHd}POEj{WfCakiKt*Cz*P1H5J;wpl51ZLsuf`| zcV-2t5J(Mblq3T3IZ0gSCrM6Qh7h(&HwO8!D#B(gD|vcR6}p%T^-eDgj0{5aZkjaE zx=tbrV4=7(r5lC_V5CxcJCL)wglR>Dy4ndTHC?i=L=`w<6vFd+uoc_Z8bkGvdP?UE zcBJKQ^}xg|4z>z8UBp}Dpp^y0TqO%?>+>121^2@xGeDB?NzMy{ad4q6b&OkqU@>5p z1q{Z73<2zJcz!R?ZoAkD-+ zKcm2)O#}%{!ia1X?f?WycrqkFIW-h41*xqEzoVjMv?76(gm(K0p)Dq;twcbOuG<$ z1}ppW3nC(X@Q5MVILbhLz=5!bOfFUJ$RKcn5XOpYSc*)b(Md{DVb%<9W{AchQA|K! zwFruUfU*d(0!9prBqcy7U{X{%0_nlwAeSfwJfMq{9WhXVmK`)U8-|`BZX)FTXq1bC zDnl3%L9#O+v-#ZHvlJwXlRb(^<{JkB;wHePZFw$lMLhBhTL483Fi9s>UvhbYX|Z|{ z0>d;Y6RP!HDmaV+gt&MhCtJ&)PJl5}I=Yl>b>8eQ1%+fD+(oc3IoqW4#t72Bxo7U| zxL^Sh(G|Hx>>x#f7)28xWrTViCa@nF1t|cYOan0?6pQUolX^^2kVYKLsrs+Dqu64Fu#6M$d`@QesNQ5T{LfpChc zJmXz}IpCMgC=;}#6#L%b5>eZ#WJG-O3I*)ahi)pN^WK&0WI;f-im$4)Q4)Ry9$w4y z+{zTfpv!D*+7Km(bts@YC+#AKo{?7MdXP`4GKP!#ek&@PM1q$I)RykpHpB$q-{}S) z@!#wea(#W$!;)(uSh^rPi{+HT({R8$0kx5A-YXSzPzrAdV>8a+p=pZ`6o)tk^W7CO zAd``jayUmOAVVUem_!03MLUsQkbAI@Bx4dlJ1`Uz6kn7>C{s5iC4xWVjgHEhgX&h1GA7USaG^zJ+40XGVU ztmWE>UA4QzF7iXCO>)IPh$5Wi05n7ZnzLGZ((`vVqfL7@Et}KZ-RxE8_l7X(!^Vrk zJnDGqXn>j;>DlHmR%$v;1%iZ5k{r;POf@@?Bl13HAc1S*}@ zEL%L{pk zRp4qaMQS7IL?kH>Dj?+IecPa62?p9r>9qeLF@WMBa6-!BtBTed5XM1=GLK3F_mD*9 zME2nNuv4!9ElL}b-YBZ<^i*hklwh9^8I zI~KF|{Uh9nG6HYKOk!T2n5I71SiZzgm#`ePYPoz*(OC#2v@?lJdHI5nQ+gxp{I^f^ zydwjSs>}&33b&n{QGWh)-8Z~d%&JIjP!=dq`m67(u4az~s?*+$20Zc*VJ5Vy!Y^E( zccA?u4)}MR$>DlaZ^YpQ_a?)3%fcI^T_fW05)K0bLcnQA76acTT;so^v&X-3_JqEQ zZ%+p*f6`>8Kw`txhZ1Pp0>MKJIh)c%(V8Vcni#yn_Ch>KUWZW>h=#7d#LRCGAJ>jDMa;fRYpl8#^XPh($5R79_Ra3Yv_QIm zB2DoMNd-T;Ec^mKK)_)jS~XcBn0FQWnF-!Y^gq(wPDJk}leah5bTyVaLFP`Rf2N)Ax@#6%65QzZj{#`4uL#tnM0RE{5T04sn zoAZ$DOPuMbSs@s0Q-$ZTng!^@NU$I&eh6G>54NMVEDS%%X{8;~ z4{p6UD`OSDlhjw4cL#%+Q~OZvn*crHJue_r5Q^*qg<6LQAJiq77NIE!(M2Tz2vUI& zE*K!i3$_XF+FA?2X-RO9j8X!yP$VKD5C|avV5C%sc2rS{MFs>^7|8}mBp8;9_?0C~ z1`QN|G*LhWC_{!XZq$S#1c(F`kq9dli2))E84@xqSfLgR0D`cI6e6-L0szJ=2N;1u zU=S#oA_y%M0!USo7jR%$PB;aW2m-_bf|3#-91v3;1Onuw9vu|wmIEN+pe+?KJmB

gnKa)?EWvKFC8l9*uErX)yw?}-jf+=gG1%N?FF-a?yjnHoa$YQN9 zB<71^-X!0+t5Pb&;exaT1&SW7HZTR#1Os{ra_>`I0J?}~RSI6w2!#D_K8u_%ta7ax z*CCC_W9>xnYWQ}GB5_)E=S;VsYVrE@l0Z`ct@Mck3I``xLDRP~F0c(EL5c<+&j>&< zCZ%C|-rj4kl?=)Br@0F!H9MXEV{M(_yc#2%iJ8xIKBg8td_c?eB3^m&7Y>!SSKgzwtcWfboc9;FYc^K{9m#62PC>2%mz|JVK_TjF1@wMIbc~z)4Un zeDC;=7Q{E2Lj@QfcUp%3;qsdc!#rwzpO83_Xh?zC>a2D`LgUMUKuxN@l1*QTfxa5D#=f59Gm1llVB-(u({zym5KrfHNRK zB#nnD$D}+9Hx=U3Hs&$M!DTH$HHOzu2}Qs;Nm>qQgHBx5es^; zvW0>8DW|7OH{YFh*DvpT98P^Y>%)%L)HIssi9ZtRPbEVPNwR6sZYc_h6`0O^@8R1J zGsoBa%GZzP?ANUgeDOk{{W?exA6|)iIgK@T(l z({(wMXvDfTDJTfYsUWVckj6ARVT6KwjeW+A+HB4syqq=EgbHL51o8h_=UFh=8a!8#WtDEovykQ$RAfp;v&JsLs7Br!Jlr+&kUesJIC#`=;U1cF0op-7B^ zFgt*8?*BegcM`y6hcFlc5zGohBRr;;)`82Sktq8oEgf<$>^182pW?wg8PI&|pN7a8 z&#ShZMj;dy2t`;U1W1pu=+YJpe{l43ZPn2Dq8}GTMG+{pB^tvT@T<`Uf)WaJHZC>ZZ8Q_*@25=cn{*zTn40Xb-_0S3s+Mn;9HHZ*aIaX{EtW3U zRzU)y6p#_U1hfW#)JJ_WXu4{PK;SYd6v$7h5|OrIQbI&xi3ERAkV6LYGMM(f@oMRA%D}Py&KWaOVuwB>y=; zOc#}ZWeHrnusse6xGz~~Dcyh>peh{Xn20+Ed)_|BcQ@DEdqsmeM3K;j$e&?e4Y*)q z-sr!A-l^K-US0wtaS&c3oPh=&1PK}H{K+i1P?Jo8ii;8~ zR8QKO4xEGTeZMOYg(Z7-gF&wO*1fX^VUT1&f(Rf6d;)+#*7H%s2!sw&SCR;v>WXgS z2O{3YEC(1-P#$F^!=$tBsIjoQrVJYnk+RiNm1wH*z$$T_hk(T$EK&qe*rzNF8cbPb z;##u<13BI~ICQ7pZ^ z;c3MZO>Pl7qQHblIh+uvszf>>93QK{r+4pQ)c2u}Du&Z;&gXW2t%fcLK|&#V6r{I- zv6BNZd=^pQM(jcY9s(v4znaO_f>&VC14fA~Tig7cyto?wZC8XRUS<&)03<~AWkuS- zM?6qItVQp{AC3V3(TGS0Vxpe08avQ@gbiEVJW0TTdJ&+wVgv>NKLk|gyq-uTgox+i z+8L^IE=U1=sVX2HrvV^u+X4%nCffD2Uhg2J7$rmy zv$aO_Q8uK3tk^UHX(^+zFpr}YKD>dTW#kdpkTn!-VioR$j?XJ(q&vtQJ;5q@8$pw?vh~E@%;sU8sJw=X%aZ*c(G3KRm zV74Iqy74SHzzZ87P(lh=fFWKPZM(wzQ9<}dU`Lob&nLz6MPu0+1qs@`v(q)#yh7xujG4%nKP2 zAn=iCMk9y>1=D7HXqx0rP)y4YLIAZWSMj-qnJyph%imJh4GP;Xut5J1Kg zp!%N=?q1KnkOs-8*Nhgat6g*--8QI=&4Do7-VyGngoY^CXW7mdQ`VO1%*}1RMG=l z;)>n^7hS*AIj)7qa3U5I-^N2af35c1&egGTP$ZX@e ziK#tS3swUx?XZy}BPBWU136XHc0f*39!j1`1|+JYol2m{%L1VB!W zMFY=Z(P~3O)}Tz02YL{K1Ivxicc>8Kw_hr2NlKC{=pl4ncZJkgs(SHMK06oqn}rgF zZ|;aUv4J4H$Q!e)7u`VbC{rh+01U#5w1`**v?NVp1}J=xMuTW!swP9|DHYhTWV#eb zG66t4A#5^6^6T+H&2iV&T>IX+_kLf+G-vP2;H%y*%jkB#{C%U?@%V#66f@wk_mcl( zQj=(XJ1G#)odu!ibRS^_fO=P*{N5Yf&NWyXjFV$f)FS zRdv>wg9&P5|4D@gkWdc*XrqW40RZGe zBIb_--!O>y;Ri_01af4dB8bT1qhH!L7#R_N;r?3?2|-1qPv?w3%xQ=T3Vk2;3Ghg2 z`RG*HQ4kqyz>DaU0tF2Qv2JY(a)BAF`Sfj$wG_Dpm+P*zTGXVS(rnp=s`#S^Nmy*n zkLb167#%b;{u*Th^+x5I>xyo^?(fHnxaRM_)}slQS6t%*&u7KNo0&0JtX-Kx)jV0M zptqRpa|G(;%TEu;=SDVsi_`CBl~pCTRKGtJq!c?}JI1qh)}}u0@v~h#HfuG<$i;Kl zC+4`*4U2MyGM8ZnL$?60JfrZ>O5al46JIo_XpAAZu}E0eAy@9;`T4ZO)YMlR`|B$4 zMYgV3-P?kNShbz!7#<{Tg#}e}`JWr`#t$|c8R)1Oeywu4MHZPUgqD&gyI2?s-4dtX z>O|*=Ssdb<+qGCZ!m4icqg%9LrMZB?ssj-ALxIT*LDsg+G$q|A8SnMN|8)|^rGOMO zhXTT6hkT>Fqpj7=cW3QnnBo`bHk3qBOQ{c{4aNvcU=$D^x}cSw*jX%D0x2DWK)u5o+=!$1`3>X=fId{#FLlv@z5@acY zU=Z%YgOVX3O1DP{rfL)-(bsLjm%5jLV}<#N2by>Daa171Mky#Si6KEI$%K{i4cRrkSaN~v7=_Lhec|St-w)z?boRF@bN8~ zF(u@&5@^jJ_uvGe7@`P0^PBt6Oa`-1t1XDhQi{G>I)0}0A+`hxVdW<-sGPAhGcsAC z9+=OV0#lkgMxUv4XF0U>c}6Hw$E1lW8EP@eEk`+ZUUh3U^#jZf_-D4#W#W207vZYsu7$nyf4Xu|pTK>GgnU}*=54HT=~hRpz9OLPrFIow^H zEW;@nfDj^0%2LUpf+O^~&(zqL1Y$vgihy!ROhp7H=BHch0~JnkVqnlXI2C5r{*Dyx z*~vOUVM(c2+&n69sGZRa*yGV810nMX&~Smc4x8z4th#|q+>4~(tZw%IIF9yCaL@PmGP8&?7yX_I34}D$JWda@qr;FK4qeVT97h;x>y0yjM5!l zYjSq)G9Bk=Z_GiWfS@%qj0iY5CF($jTupJT4f%c{hLxlWCS+s0aH;Vy(aI};AvAKT z*$k0WIyb@#y-V7lwUPj4u|k4bh=%Y$M#Bz8XFCuA(F+j&cZQuPr<#4ZT@DD3?#U2e zV2mK4*MfKAE&F#z*@Ix#ZcIBExNJRlwUNP?!3YNgzQgjRmXNSt6(yCh9&m^&&l1o; z*qYHEX(EAP0&0}tbmFpNIp_n*=8%Oh1*jJ_JXoERa~scFwV2i3}qxU2X~Q2 z()3Fj@vkhH0TVmXyh|OGtxuK8&1YIx_~V0?GkIr@Do$2Vublw8%gjKgPMEX5&CKv) zSZxVK21U^EilXqE_9xAl&xv^u-y|!b~wi&3G9v21fCVhzB!#p+h6df;cKbg5Uwe*(z9EOC`9tM2MZ> z!A*c-19&-FlNFnRf|rok#5a*Fp^}oz#%~C6FE)hMj}#2|c~U||Ik~dcmMq~M0|~ro zwVh=q<3{5|>c>iZQ!F@s)a29b1N@p-d`qz$v^0JzdNDAG+<})a_W|jL%Z85BtgC3V z#f=kA6xc{ z5YBb)^7ZrWe=k1|33c`SusA%m+tbiNfgvCgCzh!qhmrwH1~3s`X(@uIwNU&sroL{A z{?V9EMR*{OUENo|)c&IYQ%up`MwRaG=q!RrAY&T?Iu8di ziov4Eq1hfLx_~TXm}nF%Y2ltJgP7x9RNTEHRMG$l42XcX29G>DaDGw=)8?gc<_ziS zp<0h4&|k%z?03sM-Oo4Yv_Ic`+>Q9>vK8n7S|-*yVg-DElSRRF59UF_NL`*b)IK}z z*ZXz_(;w$U9h6*awl?0p>vUJ~r!TX-p3+*3pm(+Z&UB|Xr5uQlYhS_Qp6|2S_)#+2 zKtM^1eCAES4G!bX6NYRSS3;}5r80>`unJRr!}?@ljDZ9h5Cwjl)aT!TWYhx`{!r4Z zUb_r^>DlrW#Pa|FoP!`rCtPTRAtO?v2%gjxDku&G@I5#wu!Yq+bJ>d{B!VxY72kjc z9z6;4U=M09uOCJN{5XK+5LEn9D3t|I+f(d)7jc9x1b%2HZf-mG%g{|<*`!PI_fO3C z&%>Wj)1PLGL2wxf4mffM#An7nEEDdTl}Zig4880lOmG_yk1*ipEH>A+>aS`KI=E~8 z1+5>niAJDEfW4w3wh0bsG*7hfI32&E)lM7}A`9;(W42x5H?-RLd%;wyr+xqruLMJ; z2cr=S{aSv!4|?sREy?NvwLXFc7gdQ7AFUM*I1P{FP%pt0v=oU4%{gp#<%tQ;EBd81 z^~de>_BZMrkG=0e@^ko4lso^UtI{ctcQEIgbLsC8&40@U%muQ;8_+kPgZ1#Mi~ymV zv~GP6XlaVMqM?(&*7gPlle}Q!YBq)m60D*u0xRwWOVJ_Sg#eqdSJsQR<#ntqna~Z` zt$`-WN)6f^NDm4?IHrdrjEoFJnK%P8v`g1^AUdRiW`@vFJP_JxGjCzZk+nArwN=oe zfMye7SvsIyb|dR#7bVk946O#O2w21$jw`0fY3?Y^{Ff|>Oz9U6hS)e38~}=jcQfa> zxa%C2kV6taEJQl6HAM&;Dw>1S2%HxyN|X*^7$K-4gqv_(0CEM$y}-T(ve`jI-cm#d zjxQ{6&eMWmerVXo5ivlmwgk&tq|&-&CL9)(s~skUHig5NH=}=o)Fw_w2T(Xx${~Rf zH=tzdZ3zWrMX)|UGbz7t6wxdu=xsWY8AD6PJ^K99j;m!VQ7u2RAEV<_|p5Ov6Z6EC6k-qz;gF_&~%)oqlB!oM-y_WcqxJ&;%s1(Dk2Z`rvnf#00T{EUC`r^gVh-?%V1rkiLXsL?BD<&t z7Rbt6x}YTo{=_(jAqK?Mp~0AHU>b_2TMiKnZ^=OB)^;y|YE&=+C0dO5rbF{YUqdZ? z^yUEdHM$Y-1gdF3u@yJO4Y;j@UH$R+L=ZQuv|*rqduzmK;WkE^A4+ zGr=;0BO;+Yc+??qX$TC8JAM5sW*Oa>8$k+S(YhhL8VYmw{2rfUpL3WF3G0TAZKkTL zB5|A)0lvCW?a2WQ;sD&^`I=Iqh>{KAVD<0}q&T1vR^D*F!)YnF$~qJv6i`*bp3^3J z%Y#1zENSGo=R^&0MBN4D-pu0k_a_Jq=aP^=GvYrtdn3mubIqhRL>EVW^gDbv`0y0h zKQ;=U{>ee-CmDEwd5DZ9HRuD@GEsmRkJa%F$V7t*LM3s4EzE`G?ul(%uI5BIf$PkH z!F}-@g~U+t69zCM6{PC6u)^eM&>;}j(Efn7p)W?WS%?`0z-9^SKUe2JPJYI}U$6FU zJX(Cd8UptPfxYS|dZX{)H>gL*G<$)-e=FMU9jo#K6U6BJ|5v|7a@;c@E$=TIXhQ_c zkV+2XTR~dlZ9qko7htb|p@M^iqZ5|7C%;NHxNx|V>OXxS*O4-v+Bk`K<#HX$!$Qt$>a6<^53sL()mxZRNXtD zlAqgQAZHrCxf5J|P8@x>3Dl2YrM#oiU8)4;W|Nx?-lg{B&#cZ}%VVHAx({C3k4xO| zX6|-M4=zP7c_3`qxPHIKXnsTMNB2%7<-nCbiVGOR)&xc@1c(CzngLQEA`^b~!?q|> zx2_apdeD$Nyqmm!eQ4KzdGh1K?C5(Oz71znvx+u~AFteM`Y}QVu5)+R3;DhKKEmAG zEstW11u5VH$a&SE4`^%xx03% z9G{3?N=9ENhz%6iFk#XR(l`iMu|^hp{Je>#bj}^%qiKnVPRfuC@Kx7qXEqvj!+<}+(6Zpnr>d{Puzkr2LV$Ff z3{9=ZP&%Ra5Q7icDH<}wBDX=PNU{J(rRb%B2c0rqNy1$|)nX+=3;s9VAsfje*N z*I6l`UvW!Gr_opb4WJKTCY*P^A@1ZN$c7t)6w74@1H}YXx(K3r&=DVRr=WhXV-V~1 zJ>gYEGsZ*+2<<0NpJjTStToL-ker#kc%+PDnt`cPnUqFL1?VR{A^#sv4I`%=L7w3h zSddXgS71XkQK*y>bkX{&3DnM>WLuLnRFtv~AfIn46L?A5?@l81y8iu9 za=G&xl!b!s@>(w`=L#~&=5_GpJCAhKQ(G%usu+?>xz2?3XhFg4juN<#T{?6uF3!Ce z<;D{?EgKlyP05ad%X*!=kC>fffs)ddcIJU|k>n4EUWY3r4T7$ZH@e=cA$z&gZacY& z-dADrQtezKD5$7Ytmq;`lU>t1^usVb@BlpX!N;z>1A9^8$E znx5F;`LM1*2<797TZ7DCd^uKH#&?>##U4(SAucu1#2 z1*5eVqyU6Zs~rC~Rf8x!U|>HuCs_l222=sQiPpQflVc-Kj@gK#H+BhpTNVeN$i@P| zPGKP6B#60Yqi<@n5<+&Xs%w{D`?{^59P*#AT!yqdkSPP~+n#oA+_9^^l$1Iflp$l^ zi$%eUAVm@=NNG^5asXO`kzW(wQwbp7L#shbkl+xYE=v532CT3M3whx}#f3Tg#$@|| zU&89NwvIi@yUAAQiE*Q4)|NTJ1(1N?p?}zL@He%3Q%nf|e&$ELVQ|<;K1)JW1cF4&WUOTrTQPH&O>) z^g}LKSEoCNmtI=!;lB)P&pi2a@Y%*w5_TaN1&9D7DC@5D{`2JM!^Zu$nD*VAi*Y~L*O^RF4;7KZ>nNe$wOlp~J|458!G z!;d{zhkw24!`FJEY3Vxh)^I|QJshMwnLAYz7s32+kHZgyH_ng~e^96oSzxo+^%l(l_4 zW1jGqIH6;k3UI3*M|yHk8B`SDaKL|W?EMSsB87ZG5b6D)@nMIUz-O z0*w+1J_HEGAyuNgo}-Xn*$gngwFx|YJiPm4hLU+5`eS_CdRK< za~)Y*aLhUPsY=ajDW>{c;M;8ueH00wR*A0-H(N$gG80CvTDPwj+UR6j(R({~(uUop zyKURIJoRp@rWl#%y)Fx;`iwe10!Qb-W12t@f!(q&0OF6*?V#!ODKou+?8mo2I z;s)3v1uz9{KR$f;9|HKFMdJiW*#4lePw12vv34i#L|n7kGL-UX+&yz^`|m)VWqCu7 zLfCxoj^z{;Q!4GPS^$sOGnHM<##L(b_PpUVOKEZ3L!mwm9gGukxV?RzdrcTS27*!| zQJKvq2$F4y8h=a-`e0SH^ZTy!W_OT31#>>@d?&hpRVeA9r_kbi!#(j3eNw98I-QZBqbHPbx#cy{sUvm4+Ja5v*XTPJGo+Y%gCD+8WdlbT7ce6ioo_sc#B3bYXrK zCscc=zqjyJL)Y3B(T%3InPE246mW`3Dt>02)evK6$y_D!bN5 z*_G7Y+~EAsHo-OHs=%6Zp>fSfYlvuzqWlbY=*qS1s@ceg3torJ!f z`aMhW<(powKHZ_xO?rJx;^W!H)`Q|i1!?nBap8y_9vryKEw>x6&<0B3sK&^0aTK~XP#e1G9Xq$% zcU~KqYT3C=*BFLfEUYH2H`Q^}LD7{VTIpr4r(Tnz=e`doJ+H4)8Ie(kG<860l|x)q zJo{(ITx)(8$>XFuY=>kzp)kqYnbV9w&S;q9c|aL%<#m6wkntB`8_l;-q#LuiPl-7;`MxGjXtXOV?+gCI!FaXB@|kGaSwz)0~CObj6OCOz`s1diF;UKM>0Vv? z^W%CK?R5J8NK4>ngbr7T9~^!G_<%IXG=3vF2rOfV05~8L9~#I$P*E3)=U{+5W}Cd8 zcgKo0nP~c&ozq*R#T`+}oZizK;66!#)+MmL0WFzZ_hg(&i1%lX@Kn0{{r+dk~<$ z8|i>_LjteZ^ni_kb$?N$A@y;e7pw;yUsPz&=+;)QvNR5a3v5LUL%_sBpuWh376OhZbV- z!s`exqKhYnZ{^bzdyO;0efqYn26a6xcR!psAV@<7Fd+~#B{7|1(gsQ`q}Fa)uc5Hx zzWHi%??-|jPa@18oqf(JKtJQ(KRd)K`S3D?rG)H$Sg@;_;ENqpu({CyzkfJJDXsC4FhAaw1}@4ySE1}J3siO|IO5+I^C=n2Nb zxMjcv`14@5#uj72C_65!rlxzMCx<@*esfCw{-1xJ0=|A{lisc6{rf$A*`fI%`NyMA zir8&WhjYPHOWMdIn2y+3dV@{LflDnW%#_m&Yv`Oc|gm~w{l&mOWzGqrjMQH zJ6fCL#L>K>8F*AN<*^;}#YoQn`$s?k|2jVy{ku@L z3#^*mkfcEP45v6EAzRKJSp7H~GJv0PzI=sQ5b$18H=M;@1@NO4n(N1hY;Om=0x1#!_ULD>e5=EsJLTh^Jg=7( zrqe+FuiwL)`sC6&5VC1A)_!hO3ZVlm{#*}XAZUh`!G5FNjZJ|+-}YMF37Pk zhdOxm+OY12WNY=K9IYLO-JU`!F;GjluZIZ+CuqE3m^<}5DAhH0k=t52cIL~k%KbZZ z-=9vs(|dCT9pr_C5TsclXo%EM`)Lp?0|l`}0+0y2l-u2c(6JL3iz3Z5hnIRmrmcb{b!e)Lp%Z)Bfr~P4eNUz+MDBU6ABP&A>)EE^tPY9 zE)@94B@c!N@IY*Q)21z;Zp2KYiDO!kBIHrDxwW*gx2%v)NZD=Nq`=#YOP4LLzPFxY zlH@I@e$FRZ2(Cq66;weTC6Qsa07^e}QKCA;rACJc=)nt^=%3S2lk_v*)?SP(7Vmt` zH79S5Q0yvQN2m700Rtbc3TI)=V9q+70vFXXRr#)*RPDB~d%AOKDHE(VnT_!kPQpP$ zqD0*4Pq``SUIouaUhBMgoAHkdvnjYA0oalzqkGC5DSR32-%@cJJlDy&l$xOTQN7__ z-C;f#bC0V>d>D#UXz#plTg6CFNVl8Ac|xzU<=lI(Fc#k$-NQ{_r-qU*HJs^yva4p<8{^X9O{WVAz4+++7At?d;q75OyipMN^-kf z+7C0#9w_kLtTs0Qyd&t;J?>XmAl;O^!f7qqp1wz0At+}ewiBNf%64QW{f0rrK`qtWtPGRD2$FK*E)<$tFP+x`|{Qf%uj2M-+pX92xbueI!;p~%aIt<`t=}16uFtOQJPV#!Dx|1Oz79IqZMp6=AA4oiLQfQ!PT+%qh58Zu*GQjgA*)xmwSxN|H=myX z0t4BN&=oV&Cfc01f$>)scnDzWD)GE| zw|nDxc_pZ%+@hwYUT>`x8m>K)>z3VciC#VIE#NRRl_wG=7NlBRk{X*XJ>hnJbb=KV zJ#SnQ^~Vp)0;kd*P%^!mt1qT9xrF$M=6;zOX1Wf5d#WAd%_ah=k;#Xa8`wl19!`T? ziQ`9;%@ArHetJbb)5YfptC#H_+PZ*}2+f~Kqdo#;mkoo4*<095IVR{As+fUEH1n}m zPF(^6TIa^e^}PX-BM-Xl&N989dk0n%++n5WCydOw+x2QzZF3~?ZT(&12WBhYo!elw z#x}{BnO4F<7_RJCs4?tHpD9+prKN~nx7ckh^4ZAf z=WndWeHIwsIC5q4fgV;xUQ7rzd4%vx*C5%UO&VqEkk&=Q^a!=9Wngx|iR8<3xF-5r zB)67VE4^qKx$M@BY(wj7ZSyKk`&vMIM0m~p_rM3QA@g2I_<4Q$GP4Uuz0k_~VEJh+ z(&)V8ruV>q06By8vox$g12fyFePMBGSR}BNjqsko#E-&2ZaHfQ6yPU8? zOs@g?Hap(8NT#YyyG^NcMyua2LPBDCQ19DkqoS^z<|ao@GVq zw%4g3FJj7E!t8`t`GYJFs@aSn&rd*BTy^-m@k54`HAp^*oJ7OyiT3Sp5l? z!Wj|sBZL9?L9dU1b8Ucd=gTvpW?id9&IhrxM%kGS;XQi23V7$Z3HtcS{cnH<9~?jj z;T`)sVK!VIw8~>J=Rdr#b!yO0+NHSf5-FaG;39sc`La`5R)*1@s!vp;^g)eE>9Nrp zUWICHI2yQJ8&vb$i{~#RTlpVdPm#DRcPgRI&5mX3NI^hRSWXz(bad&CxYY!4Y}p~p z&I-QHNP)x@u{LL6L?HPfW}}Rkx-KE_&+mUfb#Qk#@XE~}ndGR-@;ig-xn5n>y+=cyE_MrY={v1p<|&bx!cGsCqnRycd1X;9 z!uEDZv58C^9Z_KO?&J-VcXYOLbAsMYuCQ@?VsQ!QH`z4hA?8)y$=n^c4ip=1$Z7K z+XM9smzjH_89|0(*KQxXkGmZ&x2R~ttao9&-;WLWg>v`ZS8 z(>##AwPn&K(&OjB&|2m;0$L)UctEaRe0)2J;!I>G&#K2|lq>Tm@f?Hh+LC=h@sB)t zHER$xaq4&yCgB1XVWTjI#>FHOFdjUIiCo;qJ&)Aba+OGP!kHj;|6bBqgN|q>XsEwD zMKr0Z2JjwCem;=R)#&nQ#Ooc%`XJd(SSm$+2mrs5JNqaEi6PHb2W&z6m-<(7|-zU2Z=;9sXX87O{JDmagV0yY@0_^?I%7-<&2`stO7y3juJ$8qO7KWh&ds z28e8e1>(O%NXVxkpmPH`3t>j)oWP+>|fUr796iTR=42rzL3UNMkX+tFW z%~a|MnzpqsF|RI*?qXK|?M5vAM@etpf4qQ6R_t!Nm|ybN9u8)Rl=TZu11l@|@m%+L zzdis1S-U=G1o%z94o3k=IqsJcV4iY0OT_4M;62{ybVKTYp!fl_++8eK`$Hs%xcSKY zDxfE$Z<9(YOGpGs*JfXUpul9VO2`CfcU#ZJlS~0bL3Cc>mM+@GH%}K=D(ce|V&imD zY(2p{!`z)Ae&`RLckAZor(U@$N)w`@JUn5Ol_2F$OwY##ObFs%a!{U zcU1TR-=72S$7RXL=+lUT8N+M$zzF^9b0hozZJG~(@1Fo87;!_6LC-%0Nq8xVFVW#8 z$G{8^l!N~B6c)%Vliy!(GwL;BCZS3{5ZOCh`lU=v^{#4$dDihI;9dnJdl8ZlO@ zBj%Mk(KAz)v#lg?qu8o!F zcHdK1iZJGkUHN6sX)dAf+%>ymPW4nZOh_Q_$jN2{>e zK)88;Hf6`Xy~V}9Tbmjys>dbOBc5W6Zy|B45lRRNLL!OyAce9-$AuY)j#Vut|Ao3f zhrsX8fCwAR?gq%!U)SjU2#>Eb^Wb;j1$@>UmdH?NJc??>f?2Q-fs(E?yx2k@A`y`R zG2q#fztx7p{b{u)F&Z`85reL(p94}b>AMr7Gt;1jP^1YyqR zzI^Av^QvD2PdFl=y4leBJdauVd~eGu+4k&D*f`H-KpqCaq$q;bcM7_YpReT)@h^8v z?>EolGW?PH>drx5PGWe!ufzBOAb(;11*k*yA*rpG96@&^In3_?UHk^J($-dKN)6Uo z4qoP0JS>MtT*XzGdiGHDh*dpYr0aE*{IF8F+66&F-8cOJoO3X(8}1ktm*B22nR3@g z%fxee$$F*3B$7a$?tzM`D2R%Rf;gCP=B}->k#5psz^H&8h~`dOV4Ez-(_?u8MXcw; z1PfbWf@A^XAS3`aNrN^BfB=YsA`{?!^v4{tCd_^W$s<_{2!y0aQ}KPQ!>#%{UePuD z;VqBPoezN91HJFA{`U}1mDwe5Xw!8t`JwQp$Ub}k0QvLaD$iEVp&QEn`vcbxm;v*c zEsMJwbJX6zt9K`v$E<(2=k`PAz65>y&hH52!M+h(CpN1h?L` zJN3jKjr}yNp5aFlGd_t6z2)IEedTvH+qf#wGZ1F{p56Zsp8>)j7Sz2ob%<;A7=X_V2o=bZ>Mp5wRi{a5qf$Tu9>qpQf+Vft zomT0fx~yXCzAF`e_q7gWJ%A1O^y%X+jeDk?{lojb2<1iEz7efD8<*(I;0F8v-=V8g zz3GGQU14MH|NfPVs?l&*Rgjz_aBV{0{!*i!PJL~fOYer06w~EJgKGoPxUv79|HsC=L&x5 za+9Y17v7IfpMC&u!H|VNr+UmA*h=t}n42UtT8Gv+?+jFhM+pQy2B|`d4l(p3Q|@(h zTR?j4;4d$sdK`-^G)&;XU4;DuPH4>wU3N*t972~z9qS0RYd33pp}f(Rit4_vc~%?a zs?z6HJ8eP7V_(5mc3aSC|%&##Zo!Fc+dCJRQQ2 z0!K2w;g&w1nC$jj}mRI2?!PytPm@h{&eB2}CG8fLW+#ip%JY{rm10!03Dc z7t|i`pQTZG1pAc>&e)=ma6Sfr08w$41FPi)4jwNbKY!MLpGpr$%(vG7`R{@7k*B5| z`ni1t+LzaA4S~k~SfM~=Eg1!2umG_7(I_e30Uj5G1SmdG06(<3 z8cA}L+~9@QA1D=q_*dw0O78t_nf$I*sK0l4-M>9_O41ye+$F_GPh1(0Go1@@?? z-=gp3Hy%m?G_TMc(#bnxIpJ8qt!DsAd94x6&OoB2>Eb76oc)UW{pE-X;MHe+Qfvw& zegYI>3gSDvyfvD^s?D1AN#g=b3w~DngZfKPFvsZX*gjv+jElEpKesPByF2QsKo6by^Ay{HR-=Bliv8-|+eypKFu(-i{C8H~@VQfF<=W)I&Tf=iWdIPtT>_JGy5UEhs-erO0;s zhegQ8<-E`hp5RA&`-AuZ`~dm=0q_7sJ_o=BVdoy#^uKzT#~-8Gu8fPa)Q3M4^4M*F z_GbSm*Mr+&MB4ozzCv$XW_nnio0KM@g7HLfpsUv|kOh5q!DLj{h;|^?ld4=H<{56_>=ar)-KR*)CxKwy6u)evQf*co41HsbfZTka~w1Vup=QBhqO7;8!i zEkPJFFe}C^K0=_J_;o4jbr~r=B_yrsAc}jCucxPG2L#_f2S5XnoW4iw@-$h|6UZ~Z ze%7DlTD|~x=gwD9fXcrKcz*ZLpUqy^!1;g%NIwR$0T&uj)s8t|PTTL{rw#ms*bLWt z*$01hH`-FvHb+z(u(RZgx>wroD+C1F2W$lk>YztD+aUZ68CUv4n&L2|W^vAXN##&iyia|Z zZis}Mhr3ZB!%^bm`hfq{Kg-AD{QLCxq91=g$Fr~a@@Ok!9s|6MFU{xRTJJ<&);P*n zFR5G)WeRuQqGD;J=Y;X~cffw}h?4F5(ZKLT^MG8NZ+PU!&TS)|=}%w3J=1aZM|>*V zG!Og0J|8~Zgo^TR3a^aN{{gKTv`#~PF_4BlDezOkGT4>li zF5q?MA6RI04XiK8<xz;Y{u=oVrX(<-Df$^EcLtB>~P4) z8{Dl=9xTiVX>Ow{78o29{51*KZM-~usfG~&rAEJ#}+4Y{zc$^RFMsx>?uq&^> zai_1v`*kmeFxF#>{YIP)Ve9=M6F&AVJ-rXK`)zJZhtCl!aR*(thj;??lqYyynXz#= ztepCHHd8Nh1Vr&Lfyg??ewk*@ZP@CO_p#2)WECdt<6+B6wwp|ocaz=}>mKlm_!#Tp zR-vv?dmo)=2Vy4+(3WM7tr)pFWk^J&}5Umc%{lev;86M(h$n$hdE%I z?2w*dNOd9u^$@r9c{ZL+=OirrCx2FZZLo_76SF%__d|{#x);oXagb zT2=Mh=ZFS$wpWBrB%I_v^;^bl6i!820_<>TRo``U;3E#rg+iXGb((93ckTIdokRDsL2z`geCdyASE3gx(fN8Dvz+lG&6s7i~q zJFVWK+Q(jNInoC%&ssLT9^gKor-$EG2fHoI?8gvZ6>}C2uZ?X))cgovb(}wL1K-?= zPpB2h)onk%I3}11v+Kv+RDHy0 zL1U8JHe7wb6@$}F^q(}bjokUoP{k2dU4?AqExh@<;vVmhB40du7TDdSP-N-O1W$*J zT4OXy`%S*yQ>B}}gB$wlrCT}V-g33msQ(gGd)JKXH49gKCeEpf&NwU^(@W=wI9*X? zhS7TW0x;G2M-$76DX#-!z1K`2XAH^(#{HO9#ZdSh9In*3Y0`8xOa^pL=$p1+VJ|7J9XZ=&WProzp36#(PS7Q z3i>5MmO!iRF1tvFF_z(C3U)gO!hJ&HZUe1)tS)A~w)XB|DCd4(1b9nASof7N9PN1P z2ILho(WS+~SCWeuBOq6p)L=yhZoDerMjb9zxk0VCpqTqp)ec&vT3wIpwYoN4Pc8=T3_Vc!=D@Tz*ZX z_hY4ANRGN;4-$!BWaPlLLOQT5-6@|!sA(zMc{I5mSpu^6Tcl?`T9EBOpL4Xf7Zbdw_OwLtJF7Y?X7yF7D61_TBj5|q6K!h7VuP#+ zEP@_Iz7<}wc^1P{4g8lI)E~)=HPzo5nU0UrBeqvT4(JCF^rL0M{1Zb7(j!?O<)6O}1HzL@1JIN!U)E+U2 zY@u+h^*Sxr#yEdXRTMt~(Ywb{+V<&~aWoqpi-01J;FV^m&3D9`8_iX`4-Us=U$jHw%o&n>o$` z*&j0B(2>hVG)WSmOs3bCYK0FZJtPu7iUzq!7xl_A07jG$BDX1>$Mlef^PY0mnU5^0 z#0Nh^3sEAK_LK6#p|gNXwfbqnbGNnKS4rXC_cVrQy#1-G0fnq2yrz-IF^^IVB$19* zSR%2qg07sIYF`9$aeI7g<;8H852ZcyhOIR2I)$fHGBmpr+M?|?>94eJi zM*PLRnBaOu(Eyr2TF)q}g}W+Aol(dENJ31bgLdh#MtLygnaiOvxBy|Yr<v{lWV`eEa6v!WHtqJx+6Qf;Fma-ieg5`w7IMf6SakYU0S zqFXmRn?>>9d&UjJZmLa6oc44wB4<$WVQRFTh{+?F&>lOikvh*MjE*y=7~?5YpAsmd zxZyVil!=YWGhbgNl+w862W{P}<>5PM!|qO|-o>?|B>BYA(B+|H{5n`AbI;oh_q|Sf zeUVJffj3z7qerXK^(`2CHXBK2;$W4JTGwWHR?3t;bjHP-PR|OJRSzF|#QF;h(%%tz z0(t8M95W82Dt29JZSX2VasC_UBtwe;9mVEM%H-ZyU52c8tDZ;Vo`87A_J zblt2NxU8A=ZWz18v(G2fh2z^TVeN#S*e@l^4k+C7oP@r86O;$Nz;sw%tPwZuu6O47 z+^;*>rOw@bb!(oA*;8S z7z(lq+X+F)9QIIbo?{|45MdEcC@L7jED1jaAr1y0mLZs1{zJQ+K3xSc_7Kii%mUq- z9@GJ~?J6G(MxfME5c{z<`jfB#bd3R#Prq_51=F^YPcL)NJ#Ob)&E&?dedc`Y`PcOy z?4Q*PDvL*|kDiJy3Dkji$#6&OA%#R$SjAOXt~RqeF7B?{@i-(ognC!7o!}sba0S=D zNQV3oci@+Z2J{8=P5PF-W2a^!>qY`5sHp6U1>hmB3a-5rNogQw z!iWZ$2{|DreD9~HOW#ahGn;peGY49bN`gCJhUkbgDwc}th(pKgbg+<^U=t_+9kJr) z)4q4U``3P)d1pHVaD)NX1$D$jEwrZe6dmpA05wF1Zyr`i1u`A`bjAS3Y9}@U%dRus z=yuOt8Mb0>2s@&KvM2^o6I2bcS9BK8MbQwLXR?8+23YId%Nu348pyjXn>1>`O^U>* zsNV;23%RO*HpC545XL6jzh0fqx8tuY$6yg2lubHmLu^a!*JO5HhVh!~IO&+rPb~QO z^5b4kI$)ErbVHYwPKh~YwkKY-q#S-&mzrVX+swVhcyy3=zUp{!JIT_OKw?;D#14uy z2R{24VB(w-U;`noKq0RFNCMQRh}#6&s#uAA`TCFB_4cMi_pPv(?E`inx9*Jj$Xk`) zQuaN{fZiDKqXDe}PSYWQFQ(w@mDE2qtt?Tli(4o4{shnG#0DR*Jl}nlz7mp1w)^k0 zT#0K#?M@%%nz+a_&&luKC^$p4h}iL=lRLh_$Td>6RvpUOgwRvU8Goq3z0}x?`Vqg#c8xP}fxEm~QO0-VGV8 z!M&dqHZ0XOKp{b={7ba_H?Smm{gs`-gWy;O_n&_AQo6C&<+3Lzh=2frL`3|d=hq|i z9V~CesHJ*FMyZK{VWKcZKmZ>D@6Uk7l=OV{>YIhtVV+cvYthf6amx4}4~_DqtLa(h z<>lAV+Rj^%xF5g`_y9(7_28#F6Rqqgvwl~#DVHk2ew=vQm%tFQIK4F? zyS-Its42nv2u3{Pxu}aGeO#Jp?>&G;*aATx3AKAid|?2V^E<>o_PsmetcRa05!{&d zb49{f&GpMf`aRv&$JY!#oAa{8R7+@brPS1psGMjkcA0n;A<^aA=!th3iT5aXJF2fhc3?wMfg zfXH*P)CNwFj|C068u2cFK4-xB! z2K){?Up#mtUiaE>qS-aG^h;R+GtmIoqxX(=2A(DI#ow4CO*qdO{H+Gs+eL#vV6p*3 z93LrCI(O#voUDByQu=qTPkw;7#Fytg5Q-!Gr)fRO~QAtcG4FgvS?J({rVb-rcf;=RW zNF-Kj3pO8uK?sI!hyo$~44JI4c}LJRKUr-*tkpHn<4{4hu9W#?-X=gj~FL7nh}W^+EKyPKwB4`9?K@y>yH;8VtAidQkla#gkA;=z9Sk1 z?APx3@$>C#Js?-0+r^v!DbD-uG-S$*%E{pjz^7M_Q}$>QOsIQI5htV9obR)KM`NPS z$6S|&^*NFXazc?#ZT&IQE)#d0I274Qe2*&UTWa6O7rg1g|CD==vF?a81JDTlczy|o z(fF?)Fi2+K%Y#BKA-H(fXmT+qUVQ+Ps-VS1if=gFs;ak)Rt#`^oS=Hi%g4YHe?|CQ zxA=Kvz{k=NUq}FFz~dPx!2tf>x7Gj%`O|JedUc7sbRO&pazqV4K3E0IKJ*v$iprg9 z`X2E3S>)RpIshF24z)3`-*3nSU3*tj9A6^61qyu-KK%F{50i?D-aZe}`okcidFJ>5 zVE=6-Y7_650sfV$K+F93e?ELbgj{()t-LueHNGiVH_lx8kai2h5Icg};|}-gh}$~R>mF_qr()@jva0}KLj%x6<>lIgu5`1K|LtU!Z{FzIO=uvn^cY~fzZZ@ z>@i#ds5nZR?B-Z=R*6z%z9Sr$!{HJNSTX}JvA7}#R_j>auUj?fL=Z&H%;tj89aS{6 zH~@Zp3i=6_EjQBlZ{JK{J_h{xMis}s5aj+(nJ>UYAfT;Y$)ADqQCW6Fe!t!~z7%Z6 zkGatK)cbq(`N^gADu9+axQ7@%e6ZxBHo*j9P{$U@JTT>J!f}s)8S}qsYsJBh*wOp- zIrJGi5zAhD0PoBD(9;EF>K1O!`|EZu=gkj^4BVtc`)rmA&m{*ykPsCI;wa~Ykk{)J zU)(Rbbn|#P-3~!`sEoI=Xw1NNS^^24;j^HLK2xrr1$LkdaBvQ*vT%?J%VHrPe}VBrQ6J`xq6ovLK<6;j=iwW z>0b}Mc(GV1AqNPetMSF0tCq8zV!CX~Ua?+m7AnRuilyX5g`g1(#7vNY96kr&0(}SZ z-c+QTH$&^nkKevwH2cmUq>umu-v%+TLFV<-yo_Pn_!;x&PD=rTcqvd7XWnnX_QmK2 z&H3}=@^w$c;I7e2k#nf|HGCKdPpGm08iDD>vrX?%OjNdQ5AVtV`C%P4hPL~W@2kC| zDlUh)A-V#eS+E!9fEX`@?tNAJ{&`<~4woJApJM~-3k>_<2flp#<^B8l&i$fAU4KCN zhC{bxX%{wJwQQJDIaUlz?>Fu5(EmdDpI@*N9q`;W`oR;ZVSh@NcxffheKDmo`_<_N z5iwAB@(i|0AxT{dm9$Ka=@!oXB#K$(%z;bqO|*Sp&d_nVr`hkhR$ARgJ*GYzllAl= zyA;`;&F%G=tb@r&L(so-cbtMVed2|OfN1&^{wXd6x(9qQywX80l4t^&3WR?c4B15l zl~}`7c&zIOt|+Po?|ZU$x-C&IgWBiHPh9w_h(CxBP^i|woDK8<0|;NjK3DC55ar`V zTHmMGXb*vaA3tUVhDXp-<2>aa2?)!U~No7H#0%)!GJX5>#Nq1=o`T1;NS z65|cZj$UHG2}3Jt@Iy=nK?f=(;Ht$@dG@PFF$JXz#SJ8=6%|S-xW2eWC=d@n4;v|NN1c=^rNy_A)IqAjpnD3 z_0RIvv~S4K6dnb+uH8f%YyOv+k)JV zza=2Zaa8X4+>CkWI{ZVtdiD%IC`2oO__UWDq{xgmP49fOE3zEk*ixQRb~=YgLt!LE zI5TZQav#?IfR@?R)j_hZ^kZYc7Issfqf+7dg5cN-@Q=L^RJWU`+xNQhp=ykE;upOpKfJwP7Qb4J;7`K?=gC2??vn*z$7ypp37Gc+ElCSY4^xV?N)uB z?oM5@7TvRShMD-&TrA}HdFAK2<_FPA*={K6yS}Yt_4~})>h=4`&W_x`W2XX9n2EWi z77@;gUXglx3{q#6PJ~a@AU0(ene1HkoYQEy*wyAArEClz za$4%>zPqSFxI+p$_0EkXJXx;HY6>m4)k_leGs$Ko>+l9Co4t1H(13?__8`PH_Nd=s zzQ|&ItW0cV2t>k%QYq8qt!?ky6-8s5C)pM(= zarymU*+Z}FGu2)1w}A{a;5Er~N<^Z+?BWT9hZT#6VBT7m;17etX+y@Y_8b|U`{wUx zgn-St+>Wm{jqXU{5c8d|rX$DW&CMshVxj}i$_SxzM>pP{1}QWTOsWPb6Rw>2-Fa5u z8pT0UCqrJ`H%WRRcPH$DHG)|kck1rvhvDu6WxXMlQ~Bjjy5cF=Nr{`9#*-JQksUR z2dK(|-fn@I3ehYXPYt``(U7Y0RTpShD@sF5L$dFox#Z%!^toW)&h4}9^$Xn!@9__= zVXxTyK6hqv4A<&&owV6Zj3p|M|Vl| zrOS=M(}2{WQ!itmBwRY1!9`{9%5sstYfPdtE?w?QnNNuxL%b|}wm!Jid;maXN!UL? zCKd2D_UuSsvD{ZNE;IUAfv?&WWEgPZq}gOfqDRWNE9OBHnFDch!rO|S-YzG3nK8j>+NseG``&Repq|q+)pJWG#FT|f& zLok_bc^L<~YRm4^LcP%WGf|Q2+C65J4hIq4*N79;U|t2OS0R{&dio4K7!+`BH(^4I z9_4(lWnLlgqU`tKSZ`sLAFr(5k9wth-Eny>nWgHk!umbhD|+PNi|C+9ud3mC6d*W2 z_;eJDP?lcmsh$~A>2I3v+2%N*sZs6n8n_u?`YF8$V}~_v;+w!?)89yhQK}DNxIB^s z@bO{JH)7i+prBZv$bc#dyFCR5$s~4wUc{gLgG2~%SN_<8I!1BQ=iZGJG-*B_W%Pr1v*6d0i zm`~(Y8Lz!&MN*L!yiDxVOSo{~s#fjd7lX>!Zm5-Z_qH;s%o|x_8(bJZ0VkfjW4Fw~ z>jD4So?fOzOr$8)g$dia}7bh-E@$WoKj;_C*m(-v?d(hdY%kh+%4f5 zaJ6K4XU2`845-3jajC4m=L+wTUFx{Qgg-fIln7C1%5!q0e6aa< z=mg#sdVra=tUVzY&Tl5NZhS_FF!Ho~!#;PeyGg38{!{sJzds3|=Zip9WDs%22zI(GpN5MC2}JLmF$#kV^KS?h2@d7d(9fD(?g=U!4Cvhv z19|#8N>hy9?hL3uo6*ry6efNuwxR)Sn?weeZp}bIRc^zv$~8s1n&+H4J&AFuqN4T( zA3K2n*!_(zc@UtS;6hfXpgSto$F;88z&OKv;0p5yx(1`&yT5Kg_R_<-$>Fs@O?=Qa z_YY!4*S3Sjpc&a!c~g1oAht#L;mJz+Nbl05ncQIzzW~E{l7o)nVBIdob`{1fb0K|n znu>wq=}NIoVEIQg`M*DJ(yVQg@ru7Z<@#Oyefo;gUq2+X^|*4U7I%3LMPlH6-!uio z91n#3&0;JiW%jOFtd62i+QB!Il?&AEWlUwE+E!LJaEh36OU@!9!|y^L7Z&_i!`>qH z$hpsqa}DlqxP6H0$lyjDXEbVy0^C;5ybO?H7Ga{(RrZ*5$&mOwc44`G?kKL|@hM!! zZsvmz!_HCL4|3a8eWzB{-k4LYYUhiW2ev&l)!s{k=`vdJgI>597JQ}x9C|+W(~*hQ z={;qT@h6+Sj@)kXMS17%xU!&O#?#9L>*pz@_N|J77-Dp)N}qxzsZU2X%*KObXplg^ z{CNEZLJ^%Dry1wm4uBwUzHn3RdOr%td_4l-P5-#8VNQ4u#KbUk7mTr~u>Q)Tf zgy_m>^4R$h>QULXZq}n8DyL4+;tLkWg%7wfcsh*}6pAeIl?s-+Q_rhu5XUoYxQr$h z_RCGhXknZ4@*_S zW9Ctj(T*nJBgyWplQDRy-Mp8GMpruCU7H7*f-)9Chz})GbD*JxI_C*M{Yl}Ryj{8B zA^~B4#~}Q1qAid&ZlUZ6r{5CK&3Vtv0V0LjD}x9~L+4{0rLRu@HVAwZBwq-hkOh&~vj>=!huM==sBBo-nac%w&71qwmD z$+hgO;D~VOCZs?~>Jm}EZoXDH#>%?MRKG@@9~`JGI5*5 zXF05&4dWF};+d^uEWBUC_4dU@MNk{ru-RNYb7ctStmhc^q~v`x1H~uT40~(FJbov~ zG+)rob~Xs5MfiS4h@umw-axfcAr?kPNctY?FYi_F3(Sa=nik^(5P^~#}CCl;{*}?K<#x! zT^*65jv1rLeuv?gOM8OWXUN>;lpM4Nuq-rwcjY4MaWi(018!lQWXmZtNVEVEd+td) zRaFtVy+O5biFQn>J_8gos2~XmQHvrPx5Q8^X~rL9R7N7_F0-qk#Xi10xFq%`r-Qbz zF^BZS+;7@+^}j%0uy8(azz>BVr_4_FoPTT6{j{#@%k#fJd%-ivN8?m*o6BY^0rLJq ztk`|Y=L6@!01ux&1doK<$=Dg*57hevf#3xK^?K{CK4BRWXURwr;~rd(QXw2hZ;TZ$`%XU#)(f{jMkF`U-FDsck}%w+s{N zxv8HIp}17NzH%{(@=dK#iZ!_b>07SF`Z?fgQ||}a3QbIQ>_>vUh1JTaCH>% zq`?n*LVEle`lJE#L*#!8Q?$T<-vh$O({?xTcT6+@hvxhT?`h1l1HOv#l>Qm;v+5B1 z0MPg~_all+d^&@+c6;i_o%Ei|gY)M6_)+dh)%Mb>vKkL1`m_I&P%*Wrj9>tw?f&+w4858}d@)-gU#MKwZzt_$+sJA2> z2IuvRKc74L9eBS(mG(5!C69W=wrF;MD;iYu28n8f z9r+2P^vpz8Zh7x#G@6vale2ve%Zg0<<{YD?EpvU@2 z;ZM)Mgl-QWSo%(0kk5b#@B_aHK728So-AdE1ETx-qAVQ46|6dwDou)HhPrDMXi=Ex zNT`u66QXg$2k(Z%M2a+#+pSn-aAQ$preDA>TIAJpcewe}k_Qoh{OI*#wsO`?S*T{iS_lCf7<)%|WTB^h1h`yaht z7dL#ou0@Y*E=?ldQJkz>XHFU)gE*X3O0Xj6ruA<~eo1@Cv?YkHX@~NPKP^8DD6-i} z{FuZ!Y^S$i_t88MfJln5Gm&)BRGMX8nsI;+w)t@69=SXDys$(+ZXgdk`uZJpFW1-K zngASDeg%w37wu^9W7BHgty&@)mq?%rn{UTmx8yf6(}iMb(SFT)G;O~v#0vT$@IHs% zvCL=gCugo>Mbp*bOmchnnFxx>xF0+7=Enu{Gwk{|UDA#tWI18wzZbyo&j5WNo>j;( z^4@X2;jiA&h0tHr9e{v+`3Ng;fHDZTLgb!rkWjv=7?F=}P-!QY*bW4SdnF6RO3~))A5bt zl==I9xxcOJ{;hix_S?EA`ZuhEkJP(ofczEd;jYbFmpl2P_ti}OviKhP^Sry^o@}PO zMPw~s0&srBWdWa^B_-k@o%xeIi<05leXs!c&pIextV!A@GA|Ns4!NoDHhmmVD@qI=M=B0h-;`xzP*uS2XzefjVB0cU$%?P#<*c`cgJ` zPweEI&tFe_%HFJ4cBpCEdBp9}CAr@KRI9d7YU9hsq;-!w`WUa%d^AhnBV3-Y{J)EV zpA*c#l725iA1;S&Embn|nX5HR=&B{>6~%CvO2s%|mCLe2&@=Sdr+1%E_3c#bTt3O* z27#=O{KRk6-hL>2-~wAlMtR!J!@l%X)WP~Wq4RzK8{O&fRXa||(p>!+hZ4mIC&J z415oUey;7w`Zx$q38px2=w;yRF}IL4BYEN*s?FwPRZDGKb8tDkjn^zjd4#IQes$gq z<@eq!p9h=}9cmq$*7}&ud}P}5jnepBv#<=I!R}-%p4j+=oCbwWNCgRJ%w(iyrd8(L zX3Vmkr>Tf4BBpkVYMw6YgCb-y>;QfMeS2Y_262n;E?eH0G?_k%eg#@H_yfW6x)z;k zNTX-`c`4?K&mKac(E{AYDUs{$+GyKv(rR`9+;~{XjWF~FoUhtk%CX`B6!eID@C!#D z3ZO&B?=yXb`{55ax8QUFR}p!hD@eUlk2?L3^9RlN0EIjcHZQIbdA7ep=o$Bc^8s&} z1(#WazUw(rTjJ>4U2i6#QstI9?RbgRsqJNkKyhyjI^FIVTN~sSH+#^Ur67oxXT{Xk z0nTlfXTzo?A98w8o=8Dn2b<0(efJ^LaqBFTrsEgEepA;d2sw%c4w1F z1{(NbZG28YGz)uMsyrkhCR*xoi3EF<7DWTXspTXZ)t3gk(0SAL%kVm3vT7-vZX2Eo*pE>4DV_f4&yw%TQ4t(V|3-M?S6DkU;yhP7tF7-KRVc_+VG9)>;&@VAozIft@xV*D2I1sqsZM<}R z7fA71j%#zZK@r=I&v`AT=%^T5lWjdufP$cfMuzrxo@SqUVDnQ#Q=FIcEsL`|8s%9R z9436T+uI}$@x$;w2k#2+xfx0N&#9M;d zeGBw%YuC3?mTh@VwxN=|yZ=#C?Wy97ApKaUS|IFL3S-wsCS>i+U3gX#kC(01>Y?5n zj^#aN^;I^{DjSRf0!X2_cZi?MgpIWuEf&Jie8?3A_f<+v(4USJl(wP(dGH`jq%JOQ zENo8RiY_hg7Q?npG%?7qJ9q`k*3*I~<3L;ygg00FR`Gw@=HH=`m_tS`0(oaG!5-DI zr;J!Sra^s?nRyU(cj~B}Ex#`L=M6+W-0LvVy0d%RS>J66@pk(h=W<~bXI=J`Dj>a& zgx?PX&J1Bsob6FZv;=rtnt{lrT}12LYHd^Op#i%wR|KEFFu@(MAE`IQSeBuCcj3@;ObDMZ6CbaW+uazo-NIT(`fdd+9=Tpanf- z$m_sQj>DgeZAF~=(C{<%rh2+ZHzv4r+-iTzA>8+yo5Qn;fwE5KD4(-A*@MdNSWgSq zQG$JTX0sd7?|rM_!Tv9h9`DZI9=IjO!G~|Za?8?{_vvRr3x-Cyv&PCy9)V>v_2aHy z=pLu5vb8(m?;&BcCV0E$w4O@42<32Rrj^{aesqg!;A4l8{;zX;5cD^dXtr#70qsZ-9wTlNQr zeQzK=003V=puga9dU_b!fw5gdnUtvKK9s7|2Rluo>@Cn<$5h*0mfPKihUy`Dy3#@C zG}5){b%cOV9gE4|8&Py^Y~~@t9im+bhECz;PuWA2W5yO-FLxV@1SP}`pb#_AZ0@}J z@p(d+JJ~~8gG9)O40%Cjba@9*@2Fm6iF*uOZy^Z1@MIo=>(EQxdS3bcKD_A5K!CjR zLU8Pqxj4fCoXLLYC)g)rO6qul)mrJ^uOw*r982$1*?U)YxS~F(J$Q@vCRUbR^QY@^ zDzi+Ox_1sw5PTPW>091P@B}6BC%en8>DC+G4#07wjAjND|)> zEBiaeOYe6R3FmyA==g#fM1@%C{2N0a_9$>GqfR)RbA2Yc_dEmsRm^&aQ$Fy5x=SJB=r zq>tUdE2i{nBflC@w?k_ERd|#U)C*dlDk61|OO*9d;YmhG6JH7KFu2&5*dAB6C)T!s zu8UYt)WzpKbsSW=j%=ZUb-5B_!}kU|ECoZhn|M#(c>QA`y+LaI|KvZq3~Kxt`|mT4 z;EL=4hSz^cbpcSHgDB$fpRQ_tnF)VMBgA)>E{|ewjH2#|8z6O~#)I`=T=8Go-RO7A zjic@A=L`*BacpJ#>gKCyznrcy}Io1Z&133h8#Aco(Pvw6xxSVpEE^xOS6H;2Ru7IAP*S&(?DUjf*|j`a)>Ej-)1}h zv>(?W>>iAjd@zrgMS)+ETloZ`u~5gWiI+>6aXCvDP^u_*4h7MR7T}xih`ebIzI^wG zGQ{MCjnX@DE+z|}aAHVdr<1CeBjU`Gs;<*n+I4PaY0ex}k%+wL@;jaG>ZudU-R9&* zoR^%(InkVOITYDYa794hCERu+7ObKacR9s%eJ?{Jg|JODJG5FsJG(WuI}U3Vontmu z@vUyH-Z1A|YRLd3hNOT5FQwmE^Frbc(ds-L;oV;c#+VnYnmOqU|jaj>NJI+K35HwWtX73zc8)iNKUd)|koLPk%q6HU9FFhxdN(5IB# z680R&KXqKLF!>UJwH0eX^b;!~;=FZdnbS7-;V{_pfpG}QhgEYO#d!4~%B{MPT}KiS zJztpQ)M5#p?+#_>Zk@JkS=L(I^+_O?{!#a5=tz7Fn%B2Tae4xjx)yN7J7Lejj z3|-9NaT%2!s14x|J4mDfq9BLfjVTh?;8nadg$E=tz{Tag_>;HZwFw@y0KiU(stS;V z19T^;QmB$dOyTcj(Ls0}Z@0P6#eUx}Q^POneO_L}+dTE|=ULTWcXnjeX}fO}E^B(n z(^O+#wkk6S2pZ~=7-Y`dph*%(-ZsK$V>j+O#(L0o1lI8^wGe6wSRBUqd(*f z8(}kWy%&M-JM-(MW_BHDz4XOE&b&jejq~7VzyU#`seVtq{YCEZtDQB^Hl{DY4uB=- zS+RSQ!AsF|>KSv39W6ADAP3C{ZVxWG$wZ*JU`onHH>BOZAqIc}zMwZy_zS!zbxu#W zh}&Ot=gk78UZZqnD5F=U(vQDs!p>WZjG@MQ0)DD+ivk!MiN#khA0LGYFMN3mZ{-bj z7N(M7F>6}Fa`Q`|D;yw}`6IyOAtKA{@IX7s*qy!VeN^9!iHK(8AwmGu=PiwFI{i#^Eecivu31dwu4 zjYJgw2?CZ%i5RLoDY-Gd??!d%P*oI)D!Ix$R%tHPQfIws0YC$6$z3*LqC*nY#|kD- zn77e1+=FZHf{cJtQa4fqf%c*E@2088zIyl__!fyDu$+1YQL-4mx7CtwpE>g_Dub){ z!oj%v3yxXvL_P?BXUUs<*vGg%)-ulhNF)jG!0*YRL5c0~JrC0d!=;l|HouqiUhqE@s=<0*dLnP=U{ky0>|709`AkRu7@E5xa2!u z2TFai)@N^MtHWQMiQDYv#F-B|9p)4Wuv;VZKzU;T;ByW$DYwF2(MjJQtxg>16f&*x zCD)3WcmiS>_;6nyq($qNEQt#7+i`5vI6)ePo4M$MprfuglxnW+**wY1UH8wgE?e$- zc>{P$*N<6O+V~y-#Ngw1(vE)UhHJCOMOH*9WAyO-X{_ zCLnS=H&8|N3*T@J?}C0)`_H7oo$P1dP*?|mB=Q1#tO7Jz>W(XU7M#G4SbD#xG@pLW z1y4HyQ?MEdhZGS-6Ad3q6~UDnRb$G`V8qNq_kLBKSKP$TA2xj*u{}hQ@T89(W1u=n zyoTjCfoM*MuQ+Yq(s(j@J~!kn^ht_gH{%JnGY$M=h(5kIIEO_Z@M06&p$VBkLt&bP zn*5E<(l4#sLObB)M;QkBHJc+HoTt8eqUq~g#Hx=0j*Q)YDonc@*50frUqdk;HVFjy z1NYy3Fdl>=fI@_1L||km^W-r7dqSzX`!c!Pv`?fy>>M;ChkpPO z^*5bOe~#S0NeV+Z-fCmC+8?zVM}s@?K7b)V*!Va%_wPLt7z>rFk?D~tSkp2@vUIWu zpq51ARH>Sjq5Q61$<@kOp+ZU$CvQ{qKcB~(HILpQKNt8)e-Gh^oa6PFH}l~p!xkZj zmcMs9LnGHxlP<@7KphVAyH*yZoE6{SwaDOk{=6PPQxdn$Jn03yNVnCo8D_C=S&OMcf_v(-v@#nAa>OULx<*+r?8pv(!+B-%i zvHsQFK$dm(yN^TYmKRE^zfEz_K8Tq{v($Xx0&hW7w>i!jhqO;D%ly3a=71xgq_TJG zu6}QTcxnLypK7x(>U4?rEzZC!GxNh#{ws4?F?h#_Ihgn~QQyy@Z||XV}p z2EzD@X`u~nuTlB;g+E@|MJ9#fiQFeyv<_m#8$wdwHJ_mk?8TSVZa78596lxtEHxaG zniNsPLDR9gqZ9PB+(7nqr-Q}LJ>VM&StlWWdsg#FE~Zc z6>{)38=_hXn)kxke*B95N5}A0@o{i`PvB}ll8*yJmEvU4D?E~JvwpKIMM$f?<9s!D zfvw5Mnd9BR9b?PCKD<*N%GIZq{(?=Vojf-=9AWvO58ugNd-HNzQP*UkSX+=7AQ|un zziRm=$u9K^*mp(5`g9*G00QBW(;T;W+*10o<46Ty7~Rq*5#VyU_25cKL7A9aR=C>y z$4fE@P0R5--o8zJVC(nl8EuUNS zBv>?s=1L}t>!Z88g_noR=-ozrdB^GM*qwxz zBhCyPVuWQmwPG1u7wPGANVSB7kwn6j5QP9pF<3HQa+v7q@L#N0*7H>DA~S-h;hN{p zh<*f!ego*o(h1b*2@^+#g=IGlelYwd;^8;pK3chcB45#urE%^7F#vr743DNk_GI*s z_$;Hpe)&)A``i%u;80H0LcDm}zQ*|^Z~$U9A6vz^IkS#_IT>xh(+tr1ES;`>?ekiI zYm{6ee-M1j*%7n*FU^O14|{AxN~YK9Ob|$hhsQsuP#Z1xGwyrq_`8#Qm~Gmcn>~1+ zwZt7b)0Oi@+V05hUk4H6lv&kVR|!H3GI`44&~)Hk)vz#5%odKE)iE8I8bVNMfe4=8 zyOAna#3w4M2lSQqtQ2P+y*U>qXHbuOed6Hx7F1a9OX5}sI4DjZ6PBo`3(3E9XP80I zI3Sw$M6AF?Z+P28i(#kCF+|FAfuXt8DjdG|nD((Up}W)Pv%W))18_0hjv}7zaJxR9 z?MfGCp5n@6JO`<@xl9#ZJVYKY2?rOcf|5w7&U?1s6PHmHXU1-iD7^4!ukODG;jH;L z!Q|!v^VOw&9(7G?fMABx(UBM;_spa_>{lqk`A~!ArRTaG-os~UT-auT{Az2+Clh$h zTki{aS4?ke)oJ2a5o~Hr=x}WI(Sy2tFuLRozYKbme4fpRofy*A`E2_g%C7Y|QphYR z?@XiiG#;VNkVuJ71F^`pn7Z_G@%XXT?yX219pYL%@W!Acv6HvrednG1%V!fNMUdQi z;UOvTy{MTt;gKI>&!5NbBXCsFwBg{+NmnF0lLDL^ad}b>qg5S4qjh0(Vei4%HkH|> z3m+Kp4s;6NU^By|9(OHE?JEKs-o`)Vs-6wF&^%|DQMf46&z29_8x|-`opeS9( z?H;8fc^6IkF2>Y@i8xoyd#*HxVN}t0IFd=wd-gyLIk>DhZ#r659apRA+p$BGMLRrb zJK&uDDy9za4xZfH!E|=LOi<*9e82;aC|V{&0-}W@%T%#o!2-fAVn{oDHnAXWI}qxM z`cOp81c;2t095IUao7lvV6@F!%usEDkk*R&a7;qXPdF3HXL7uDxn zM|IARv(Jus3k}>?_iV+HOxgLK5jGWG5%5ono5JhACbY#Yyp@i%n8OFe_bqh}!9hIG zLQvhi3O}1kNC3(EZ}er}#>!Hz}l4#%Cu4K!x-Y7!KcNxClT zEDBaA0=3IG>+I5xvbe#n+XW6*=4;rl|4(nh^nQW3P9)dgKRH{@o_aIye!Ce8D{(p! ztk67j=4p+2iS($Ch61Bf>Dz1V4kV78Eos^fW#<)n8lB-~K5);0evaJ2eD8Tpg~GcG zDp7-6?PR+io4s-gvR9YhX|pe@sqOb>ENzZG<>-tMGU#fXVub|u9NA5pgIswJmK}|9 zL02w=_LT=WvBA!sAXwl(U$O8@SrUW5XJir5X{7aN>jvc3EDXYUU!&g5FJ+c0yaFK&>ee+PM zXFI(j^{H}{4Ux49xm=E>RB(Jf&IZ~cDtEo>YrZU6zp67G2&}OE+~Lo?dCac~d&q_q zzPaGcaXWN`l9RL6curR`#YXQvi=C2;eioG7Y}gEVy=oL_As(~Uh8qnpbecG6w0sLGO3mUl22mQx`eI>>g66F@}*4#0||Vb%9-8h=X%EFV=cvQZdmXKOT(OQzk4|>$->>ZXIF`& zOhytZ;x4qmx4uGxw~{^>>H^3v(vK47JrpR1waM?b(F--b*w|Rt@}d-)H;azXQP^Jj zN^-o#>m}}LXLQIRYeeGLoa0P+J;26}w02|1yczU4)jy(&H^IKf*`5XWzO58g?M6a~ zFQ(VYljgGoZtr=Tga-$kyUuZC)VM3*EZzq?P95Op5lu%k!@=S61`wSA*HTJS?7>Nl z7)lBn0FSCFvya8}r&nk&{3eh_UYiV<(KX}78EXN>z|o|wvjVpn<{Cr!zD3vE0t@ba ze)sADx;~juq}U&m@39l4&I%KqnsQU!=ib^N<&am3Q*{fG*r1MRAUPw|OrNZ1UH!=4 za^RFT6R5H>T<3}8VQ%#r7UP2X)3V%j18)+_w+kX{Nt;1=hn#W&n#>((mKBk{A@_sU zb2nbZ8YIpLiL+w{R|D6%HXuFr5Yb0FO~cNpj}Xp|o2A$5q6m9o*>~q+`dv03JQ;I( zCCCx(8d9L|E<0evDKQ;CI`UPjC9A`VwOeeSQJraRyma%xL_d?mgF~2+db5_!^SMZV z-VDkT@OLoV*g9NW z3fsTOhHdZJNWP~(ckNI6nF|KLdGXP42lu8Pnw;~bXtwBPe8SSZnCS>1)TY{`DZ_7O zVP|hpC#YJb-D)zTYV8y!F6C`Jv9~809YCn>HxY=w>rgZ%6z+D-!Rq2kGb0sm?iQ|K z`dVYP?P;7?V|ym>RUdA+w$tMGr{8N=5+t0Yr8(Un9v^h#`dY`rEFg&M5Q4Dv29>^LXOl}6FD$R znVOYc(;GK2t_F>pZz`)9w(^wSy2dfMa&!C-K$OXVNWve0!LS6fV4d=Fau>i4ydru~ zL*YZrNMJ$u(J6bO9sJ0jyEkA@52j>fnTJ`NOE-F8&;|*% zkZ52mBBX`l5-LOu=r7-8N`3?#54v2-W}lUVz9z8NU`NY}4ydPYln6aYj;M!ZP{~>_ z51|prf?WvQkxpzkxSQp!*7Y~bYS_7z*nwe3zEf606!J1a5`hp$BI*)oYX1dBu_q2MgY`tt8C$P8Awfz zI_tzn+OTKxeoY_h#i-S`C-dB6w%g+^2z=jw`*gIoE))50BOyWpJOcZldOcF_*B`%U z;2~V%P!q4$`h5=k)is~4LVYiJ^XNGEAQEGJ_JBQ&!nd(Mp`+sCOLGJ-;68a{-E889 zUT3&NAlXo3)Z7iS5R)CM;;)^@i^$a3EDUES6LESEyyk~GMd}>Hgeq%P?@rsqG6J{9 zh_pdg*5^v2vVCrrdnOPS>1KdNMZLl&vJ~Q|i&mdE+wL8XUf;9?vz|xFxa#Bi@q<=V zFSkud(0i6#1v;9SS>Q=)9`xzw%w>Inj&^-2HO?QzLg59e5-1o5tdS4sAK(ZG6bL~O zDUg1@D!*SKyZVnF&vAM7C1Z)#53HWe4*UV>=;LLrALZ{W^hiAWKqQ<7vwwl%=oXJ} zx5#2mjKdG4y?#USyUN--?mq8}x6tOp5i>{?dVqUo*;id|0BUH*(uU-{ zI(-K8FV~EZz8|x{X*X0-mz4$H0tu3rYCYk(;D#U1pd6pAK8s5$&yVC)?usIsuhXu5 zyIl@k{m7$a!SHn(p(1d60p$vo0v}y-JoitJIpRyo)s;&E#dJR3CD*!-Qqh-_Y+d1I zzlnX*yPo#_z|Wkw;Aklsu~?LnQEzT~9X*Ejz5_(k`t*03h-#L!BO_Ozo(K zxnO-pEJbVeSVw>khOm4>9+z(keCBIbLKpCp?Dgm2`%iK5dsX6iudnx?dLlo4 z1BGHuKS`~9;FJ(wdU^NPpcOLK^XA`LUd8>oizV~%uZ_^;bD*JLSv%cdc<0C15)U4I z();JG&##e2Z+_CN!w4U7u%g+^nx*A)Mj!3g4UG2iem@t@`buht}_!`}76_A8f8V=Gz() ziq=>Jy}-TrT&s|;EL>(9Q+p)E2v*qF}GjB z6TWcD8 zz`&}A!9|FR6I)&vd|lD|uUerG*TY=re>=_4Nq>H{G(>mfgJ70>J9GD+Y=6qg(+KaSdmzerpRO-kfnfgm_CBxD6TW3J+)2J3Lbo11_n_VH*a|w{P-i}A zox&ddaBXj{Ugaq|-%))5Wgj{5T;1!QzdeYm3w2!OzE|F~+YUE*-?@||CA2nrNz@vP z539VC`z3Vi*A6|kp1XPja@`oiskPlm1(}rUf3EW6vD>R$cYYZQvKzA(kx8Nb(NM1& za0V;>&$pL+ zbKzgDeJ**3qGy0CA^U!)e_xUKXW5glu(dM?QJ889AQ%As&evWY0%O@z+~VI`%3;9M z`Oq6p=aim@-hZ(EVhO*S=U2_7^{zR`i%&ql2gb6*6#o;~1=r)WQm8WJ#dm8Pt0+AdA`2@xBuDHe(JqNc!4r?u zcXNPXQVYCq(nynE%BjGPkh|t4?;NqCAY7M)9u#{04gHTE=rq>Jv*;(Y?vMp^Bctk6 z<@rI+Z|`B9JM~wIJz@ER9StO|BNQHviorU$2c7{0vg87UDjS@R0wEJdT45a4)`Bo zEa-E7&!=FvM9k(NRiJqMBVEJ^!)6*TTwvMz-S7_wkmZfQQSXPBTXHKeD;{rgR9=^L zFrJg}3Cn3SM2l-VyfW0|u~7g*g^Gn+A* z#;HA1Oc4bbz!Y#{0fsP8h#$cCiQm6<4Gt}~{>>aB{jd+WyFR`Q_l7zjh0ixd(6gWr zb!Bg0mEWz>@ZoXHI3fy)&(Qi`e(zC7WOLLG!Jwh{;oHvFN4C8JFDv+m+?FBlM1BYe zcazzz#b2r2(U)H2JGyA-3gb_H(HBA3uW8)j{RgpI@Q~-kQJ1f$=7NQToBI#JPB(N> zV`E}-nAy~HTcIHLF0}Eu-mdb>53OF)H-A#?K)*41se1$9;No2-c(zwICQWd;e2$k{ zU=}&=BC6X%pJfMT9gQ&;gw~qtJG?7OvvQMYUv-n?&HOJ zeHW~w;L6bS`*oVhx+uMNptAnR5Jlj3C_BOH&);x|kR|q{D5ltcso!4f!x0_NtVhnA z?fmUUH!I0T&7BU~YNOqfDeZc*Ya*>@-Ct60!tyuQkYyw4AB@a-vIQXqp`s3h(hV6xi8=_*}O>S}N8mT*Qcm_4h z>_f?oy$?6VrMZOSHQQzDZt+?3*{yxq^3r8~jkR_iAW)1Hcf6iQ(l>p}am2wQl@qyY zhg4c4AVVop#Dz=hh08Z7;P7M}{@xJjE}^@DPMABVYjNYW^C_rH;vP7j}D7zTLwn5FZzJ3X4UZR_^-c8Py#ive3H&ULD-k!#E;bB&%;4*_#mwCzVWHbhE^9t%`B7cFcP1lD4RQ#C%<3Ka9Ok{yW5|ioAECxq8 zFYM$smxM!)Li?iOryht+4x`=ijyN5^3k=P$FO1N!ZU<6$tZPr>?1ODvBs7Z^L1Ll_+00}N`@O`H=^^Wfk9_t+UkJ+O zZ;4*-bRZb%5`d+ToDM|OO#x{i zNuQbR2MH9@vCZ31bK-7OSoVczlBqGAy~rk3M5;|8P6~XIQrj*zRiQ%oa8n3{xzXZ- zo#z~Tb%qRpoaWC6Z@%tQa4Ipc!pX-Pw*l5;PXdzkqC{{>{BS(EL8#e0^7@&l5{1Fr zaCixg5RO2w$njDf^$>9|fSZqg#+5D?cHLoM{NqKDeuE6`Ka)f%7u^YOJdC9r1)HL&e$i5YB>c}~B$wLRaO)5S{mWttWxbhMrjqiudRrC}UTQ^Fn**#>S zMgj6jR8cw-2iuF`d$Sv-APY_Y9C_ef==ZVB&TN%%XKzr?cb(D91U}yN?Tw^*K3Q8) zvjS$T!x*j)IKk7NcM1C4j|Q#Aza*vPjA3##xc=~Vh`x1r>~g6fpzU!lifq1W-S_fzPV{QA`@)LOzw>t7(#1e#c5(g3rWJeuePdL@p6=tUkRJ&P9Et`O?mE zJagON%I{{8jGV}3e6%=We2U;q@M9xjyF=AOf)k~#>agE>Nuw>Hw!PkM!NXLIE%qa{ zy_YyW`W_qTyzRvg9x;b+OjH}eeU;hweSu>8SA4Fgl8?eX2Pr+l8-#bG;?aqw;VE&4 z-dArFmM@&+j%CaS&O>N!Y{!cJUP5guCu&Iq?+a3{YC@vBw5DePz(wW)u}nGn`ayl_ z%6A|t!46S;q3#-iGI~6!mgc|H>S_*9ht``5Rc|nPwthm(d6jzC_w3c#(@HFrfzTb2 zslAJ=t;9}t9s|AG*9Sqz=c_oZjV`Y6%2LPdev7C@FLs#5-Wt1X&GHmGmRmGOTRJbeFXrXz8L8YDiaSe40QaJe?QuZLFp7w-+w| z4=_B$U&Yhl_eU$ij`7xX$d6(k2%*=8G8>QDErG?j*Hk-YlDK#{Xmp94Hj}$39mO7K9uykA?TU6EvDRp%?GxM$At>%2)jFHNo_P9A6~t1g<8$k*D}lHE-S=i_Jr z=C}Lj53E9I8TFgFu%lKXZ1GQfniDs{B1EOx#?12`m(#s_-)~}h^=sAY^(NiWJH7MQ zIsX;#dpMAv}$3 zlg<0%fBW+N|1bZ&xuAo~9=q}RxL!3kphzG{!a!Rd(YmHrx8ZH~=&o3Krj>LGMhNyx zRs6PTxK>DHX~O3&zZ=H!Yk+)xzMb=5O7EwCZGH>zAD;Fln16;25jRO z?yFhH!(uUtimMS~2#Y2(>2<+rW%q!2gft&|XA_^7Da)PU*N%L<*M0pP@#7N^vnBoj z9Y;cX7{VK*AoOrwu9>K4iSQ!24*P1f>mSqmFS*N!biCt;dbI?&AAL-}M$eNxEXd$Y zY5n^279b$(SKsj|7uN|&JwFNpognn~G#Ea+gvI780QgJq%@KopKcg-@g5=NQcN2CI~%-)*qW4c3$jdAt~@;R zm|+Y!WzHLlMiqS$?^=)_p5ktIoLT)TN^)usv>NGMiwYe+XWEKTg} zvH1azvJ$t)TGZW>>cNn4IJtapEBnhWUBE^XRGnE4^O7Noz}@8Ch;Y>a_ksuRn3*9u zk_;xzYi9MJcZV!w`u{@^cs=7lg&B`Rz1+qF(mU%D zr$SSOMQuPZNA#>a37q%6FuLiInkK`}iRdEna}j5bQ{BdL@z;Pm-Agnt7XDUhlpad2 zC$<_Rrr!JRolyjXye2mew~N?f4t#+}*na)`cHMs=AHO-p+-J$-86NR6 z>0gL;IM-Kgldanx3&k4YN40bt7)Ua`Y%|YljkR$S+oOjqz4t)uvL2MFN%N$f_pNue zoL(qZG8J!Ep6vr*_$P!Y!|tz#)h-F_-+Pkh8-w8{Y@ALXkG_enXSp;w?=)}SMxj(D zVzI5|EW`xn5fF2tgVQoW`08qHW^O1##Hzwzsu;gHfmjG%8Z~9D(_AxWKKt?MeDUMk z%JYt#%k5eN{sA4>Uu9N9M0o|e+zR>@dH_02TxJ0taif`+_G@*FJOGr~6X=5Cc1m}uZvS7N zUb$`th;>7;btna}r1kZH*!*NjsqS=s;Q}cA$8SwG(ys5*sd4CMT=>`2n0LdKCGK01 z*1q$y>)~DsUqm_YVZWIyg-X~`B$Y5ffRkG!w3rLBE3`qA#$@B|0J^CRxseRP0Yh<-+!`XxCv?eVUU3&}tp z1Pd@vf$m8)xC>#Hhn`$Lsulx|_VbC!Mv^>U{rXbS!~vBxFM7Oi_N~`P*Y}y;gf|4P z?*{AdntXc-F)tya8}U2!jDXBME&lo7Mcgi7diEU%Nv-=&sL#VV-YWvq^%6f% z+^*w^93w8+el>PJ{S*x6Iqb@IP9r!GYwHxp(js|aVT?cnNz-*F;Tx&6FuUe(Nwm%2 zDZAgd;2!GES|N4eWKcL9T&Nc87YYbGhQv#J1VTR6-k67Md;+n{!0c;b$C zM*&FG%nXp!g!Dm_QH1Me+cUU82_fiZM4W3z&Jpv*PfmS%a;|#=m*@LL1@ylc@&olp zJ`gLz>w|PYte0`-d!aMEJP;qCe`D!~Ek$e72~G38Bj-tGKnQ#96Z7rwnI$H3F#0O= zl~WUk*(lXnsDS_|57I9(jTf4|&d_*T8upVkXnXN>P_2HV6}ZihC2g z;M0V|+=9QPr-gO)&@e40bbO`Z&P5YWbWsh2x*mEPAX(^+{Y4Ixy01^^gr)RI`=gRZ zhPwkK2=PeRr3Z7l&EID0tco)tAV6(Mz(_C!DsyO8O`Teu7pgk7bShARQo_@H%tT3v zkS$JAT)?Otf6x5u{QOsOSuPl~EE5zR!}Ytxj@#IG5nb;IPH~E(*$|b7=OVg|LFo6? z6H&NWY}0F+{_3YL#V(_(kDk2#zu|cZ8V&?E^3RLjoPrND2niXitKK!IPIt7tnXAqs z*S?HVQ5d2sh}QJAt`BjpOjY;JxW|LXpR3i_yz_HjU8|>HGYkrj3w;)iR8QR9+xh;g^q5G^qdUoscM}G_?gYdiw`+kf* z;zlfeZc2PiztmJ|pXrRYBb+Z7nLxceFQQ%EP(u{9dD2RVD9aO=UPePZt}}LHzAmi- zcbBKUWM?e_@Ea#VfI>WB-qv{JJUnrkdGimz$WXUI46%>`R;f}bNgD0m)|eCs#PLZD zaZ3F3ryPMR^9|GHv+LuE^Ud&`+5kN7obZB3MO9hGwNz!a;|)w!F02Ry4vh9$2l60- zr^Sv?=|$YnG%r@NgTrE8j3eBdlo1UBY{Z48jVZ3CDmjo;fi7F{va8hN2$o15lDe8! zq#n77e0}F0EYR={6767a4Jn~D4PU`m5@22Se%9ltC=Nv z0ssOc3XQVBA&thgReB*38#*T>8XqN3dGO}Z5w*|1P8a7Hr~ogp^$Z@2?#>Vgk&1vJ z;{ES}!XP`0fBN@jFGMr$?xw>hQFE4qD(8tc({$n)Qbapr0|4I9Tz_l2-X&JNx`{K% zqA_K2LpsMtTlohE?Y-U;*2`kLAP&z4zlR()b9;p!oJ(OxkM_0ceceObBNM*?>rVPrWD>5J2`P{g<_a`KLF@Fy{K+Zx@5u@pU;SnSOo}%|`rd zeR3K6bG(bZNldDNFgb_#X!O#2x$)@?9Vl$q$$_BoU6vc1m;* zaS4JT2!+TrjP^OQKoH5!?ce*eu5fZ&aGQ4!86x}6Z8Lo{R88EUN*`*p~{c8T5kT;3T1FZ`YfHnqZ1{@eDGKk{nP@v?dYKpkk&% z2a+OCA9U{~t8v<=%~U~I0SF{mM3W-~S_%@Hi2jVL%|vAF?Rih<@*@8ueznkN^&?-z z=uh{x7w{_tkFErc(MF}zMZW!@WgOg6L|xki6)6EbsVO|qRxkQ zs%Fo#F!alCc5fm=L%q=6#}H*;mZYBUW^16#6rKmwD)=ARAHe=WAsG+t{4bvm;*s44 zAKQ!iM#nG1lrN?vrYbC!jP4*P%%Dt!N{?3E>Wj5MF%d*oX7NpMIdwGMvALV+dHh=pI=G9~-k3qey)h9ROq0C(mm+s9WfO@ce#cirdjO#9rwAmgcc zZ*S+&;$SlT_l1c2;p@x&+EM%Vx${9_9D5j9Z9!M$m@cY`T>)JD`JI%F#~)`&zS>8e z3BI?uA2(mjUsYO5di;Hj+Y|YsA|F6A=g*(s2sZA8uPMwTpn!Ko0tF)?Dq!I-EL#$U z^~|KfIy5n4Cry|jeT-bB&)?u*z)|V{Z{Pd?$qVR7(x< zkKHO-^gSxZ!Yz<{q7FUEWkTMbx41x|55nO3j?1x3ruhfn@cS_PU53gQxOIPTXAZpm zPjGeRE`a5mv)^VClxu=y+$~PUjJnC}lLP`%dV#3XuM4h>hJwQ5cBRfLHrFC48I%Y+ z)#ft~H>eu*Vqrn&aI2D+pwJ%y0h_8R85VQd*2oKDK2jFOkkoLEx76x-oXJ?Zhcjl^OWoSIxZo#HSl>g)0|wA6#FaHDqO*dBZ6 z#J)mO_6XnEKrV${?}Ai_n}@_3;}<+nBW^gOS#?x|OrCD~an;xV8#^=Jhvr7YnKaSV&Y)BdRB@SNo1ITy88-nFlx>)gTnUZeraQ|Z3#M>vfgYuGG( z&TiG|zDIK^i#_o7)TC4+q+6Bc3xEh_lQHtxKqFt{!m1d9$=A*&ZDBcZxW|-Vv5b@teSL zThG3G?@ZtwIo**7cYZsxrquwUbwYCP4bUZZ^|s0pG`hE|qFpFg4%Wgib|M>zE>N*q zd}MXPP5_yAOr}hN$;~^^H;}9uHbqW`!wt?=^C6iNOkPVKj&^Ct&$M9H6YkWj5ZhHA zReP1A%QELl_A;}@#Z=&`8F8EpYK&*M+GNU_GCZyh*k}lrC=fLfj9PzKJ6*uMf2BMW zWiz&6`|l6Z$EWd6MLvm_dsr@N&C=vm%0W8t=d?_jNGmq{G#)f#~XA>_;4SbCf zG*U$K&YF~k-k^pK6PBl7yPc48R3fZW?~9~`=l4uRnnJh0YuoN%5ca(EvADtBNT+bE zy*zTJ8}PoLB=^@6)d^tQX?+BhEJIHngHR3%FH!E@y9rgO~tbz~*p$3h^M@woiI z3_~4n0+e0CXjQnO(d`&c2H2&wN-MEPU8B}4nNZ!s;qcy{XNka`;h|3lCp+gAP$1;w z#>roT^84!zFknY4?cE#9%5$7}D+pc1;;yLO7m>Nekq%l#^iIMP$$aoj?+pDu@s?8j zB?Bk#4bJ);sx=fWg!4G_JKR)hzRCH8i#d%YFQd}!KdWnsQ^mI{zf_HMh6yyE#FpOi zACt$aK3fJY^uG}K0+H;DI?(F%2r)Ufs6E1*F1HiBBJ}BGr-6x+Pt=?>OR;C0_sUCm z*FHa*E2JEz9_4q~B8dmeM!w9CD9xhYaka%QgF;TT(*01R3+tyFvk3xM5za-?61om- zS?EsiuYJ;0(k3q4x;4*DH>{9eXk!*?HJ&ALy7K(ktbRdixsVx*LZo>MjO#8NKI(8o zDZN+?eIRHr3A_6pUyi=mSdk~}cOq4iL6?fJ8kq7rfksw4%EWBT;WMr3mBsrJIne6X zJG711=~;4wBUN@PqC$s_BgDc=Jl{Li87+eH--{50@Wz1oz9a7_M!CiBCZPP{NStpm z!N*6F66xJGk?w~{oH+I%glZwB><enk_)9hnU98quFFFv?i>g2KckxIRjdhvs=wbHdxr;D*Xf zr4K@aK)2_CdvcURr}-iY^ZVBcfg+#6D#vlK2lh1ZtB zq31bsxwym80UuHaL%p#_6L~6kF^^n<;o|w{*DX+QiI^#+T)!o#ZtVjum%G!-#pRM- zlizXe4IX(S+$t$3A3a}%OeVOIm(U@WwQl#;o?K01zb2)PV@8e@Z0PzQiAC7PmTe`I zGVf=>Xk^9Uo~#U8EClO$dSRS%j`<9HX)EDAC3O1Z!{}wy^^9ntA}>U80OS`RjowUD za*2Y1!UcFYDLd%`SZQwdU35L`SLy==Yi{e@xMeYRh|y7*MNTeyC~41$Lc;^R^hD9W zIR5VVsf(yxuD?odZ>;`^&Eib3MPAu=W)fWM-i!h0kzicKDEUR9f z@b>r)d@dk#R51?yG%@5;Dr?9T`X7bxKezY#-v9*vhc6jyuhcB+jtpdP2bu=2Fe&PO zSJ)?iZ$7h=hD&pQ6D|+(e;EE>*Ym3#|2<;jkU#VMfmZ&A!{j{f<(zL}#IP_F`xSI= zlea7@0S=MXBFh{d3MU2U72&&rXgt^8_LHul=!WZpUFHy$ zyJ2qBG6{ak^XD5#l#YCuhJqyDJEjna)+8HA;|3gW5LKBsa_#oNSPaEo2L;$@(Wa) zR_3%hk_{KTTa$T-pH?ZLtpkxJxgor@(=nD+M-EfEN{USsc)hEA%Hh>&*F8-H@akqm zoloq+7&iBvj@Gxcw{EL@i*V0V5^Ywpdz{WhZ7iN*!L7B(JDamKYGOC*B^I7UYfBQvMspbj zaTH|qyJp7&*Iv=>dXC_T&#_mesHXbIYr}yY#ab}A@p6ohInadFWLm?N-EOYTZQig| znV)rkCwRYW4e)%aB>M;)&!k{_8MJ*E8UX(rvJS`{l^d_mJiS7gBJUmuW8mr)(Z|N} zL!+Af`~6zAjAgkkFMH=}VJ|iqLiei2jNLb~+8g#wB81-cTxzJms+V`_ z0|SGIUoWBAC-W?6$8oX-FEL*h?>~?}|3|=|#rYFK_XFwo7JmLj&p2uLpNi@f-hzM4 zqHFAwL}hkqJ27_v{VypMIks!aF81u440`8P5R0eKq?GMGl7a+~AtS6&*@OJ1bVkll z`S0gmbYKU^yoRFQ0P4mVRG|mn`=rx!jikSJ1%ut;F_{4giwXjvsgoFM<%xwFEx@QS zvk+tF;2($5ePNVE%WsL{$Vh^o=OC{2-_yf5>s=jIao4-&-(0qIvkDOXz}G$>)_fQH z`bybtu4)9@Y6^-yC}{$Ax$j?Gs6H%OX%}WdK`zQjZj2zz$n=0ptTX`h2hynCiB5jQ zu6(8N`|Yo^I6kUt;Z0k8zE%SJ=(2r|I6E$*&ztF3F?RASW4vu?<6(#@@xFZof3>|7 z%dt?7d&~OYtPu0Cd{fjueS`dV=NHaC%i$3{oN6DHIMQm@`JRqj8*R#4e_!xGKtIqA z!1(=I<^Gp{Gxhosq_2A$7&-bKQU;rQf6nNu^>rvOxR^#jGy3vs89dCK^%fQV$i&>Z z5G%m#2f8*MZjIyum9+{B#bdT@mbI^GRXOiS0pMKn_4nKghDdvE*LpmvUHM2AMFWT^ zgaT-#@WeWHz>MyM*YpA6Z1MEN;WL;npo1s)_wC()i$u^r*tq;}B6yCXDSnpUiu(aJ zVS(Sy%#!!U-sdqTTS+ytX`y@A%iaj5D!7`Z57(ghhm2(% z!^`7TkqAY?LLBH|n|+Dwi^U6?mjWK**Svzi0ae$j&s*M%b)LIW-HYvUdb~);36OQfdtDcE4(j_o-HARw zTRCoN)B$=Ir)U?##d=(n$ABviYKG`gd~tE9>y{JCz3IRNUo`@44hrj7gzSoky+O z_4|=R(_EYwv5Myr3x&Dfjy$9>$hSLJF1d@^Qx8%;_sQC_#Pz-uyJcZhfl{GK3-f)I zqpoIGUf1G7`Tf$rR#cnuGmRY$bIV^0y7F{gst2!$)LACIphYSkQn>RZC=L#D;;!e9r(KAs;BE#5_eKh)}uQXZOFYKy$(R z__>>ngZ#^Gu|4$+!amp)={#>Dkk$5j_MuQ zmv*pUIVTPag-HSIVVB6}Jq ze-d$|!}vd|%=3I@Nl$>En_4aSvY_MH4Uiz6yG7wh{Ieho5DH|hQ8W+JZG@jbeAurK zz(Eeym^EpJaJ~?Le~ig4GrzvsedPGUAR$`pY&-rn73am1t_HdJc;8HQ ztI<6wbx$NSHAg%1u<-tLzXUzl>q}=tkKEP8kZz+A2%@F39bkne)l#ACwRQGNq-Doc zB}RQ(mN2lUmZB9ZiG_-=RUY`h_Lql)lX}>3lU7qoe7K9AS~FcO9aYD@#mU9TTW{OG zWpZ%_)i?Us+1Z%SVq*{qW(iQB6;mK=ig=?%;A(RlB*7I`p$j<_=SbJ_I!y2NxM^B> zXFl#{CLFk0v&EAo{NVn6zV;t$@hQJrfUw5Dst?YooBHdJ>ZO9&Ge4nQ8FPQvg*h_N(xrDp~Yg$i4d1MvupB89S*gdq`*-4*y&V2cc zy?iUH#|Q#NU!Mln^vKKv(2#uqq9(=|%XYH$Uf>mIT9rcXsBWb^L#L5Mz8OeP=vKR2 znX|QqQ1I{1pKiVl_~RUiat>Pv19qL%J_V1%5b*qI=hAcr#^$pn18_cYIwAAWd_V*) zXmw13ke?ht3`6Aud`98R!fpHt;D7;-3MU=0+Y>!?#*K2vXBm?ZrzzQx$*Kv{#_nC( zH!fzX7aM4o<62F%w7(s@GeqIg8E(?&if!3RP;!85+n)T{RkUS6knN7ByDdR+0ohcB z{yMCdzN1EXY}2Pz>op|~ymPRcB9#TL)Eclf{(SCN^}oNbiIN?muh)e=9)Mu`P=vp3 zq@&Pb=`770{JQ+{r#q=6p)d+Dbl$}9ajOO9iKOMO=d9_2FGlFBq0_^L=4MMsAWw}R zZS54#%^`yn1F4O6Md zTZR_8+ul+jHguMYH*CG4yQkV+GePX=I_S}T`oau6T$0BeqWwVrM?^O~BCzce$ zaZ82nFQ_o~_`L8ihfw|(Bx}I;v>xDpyrxSxjs~Pcu#c0)-gCVI({7OkSh;Lm zh1<2yeOU~HsEjfgUcW6HbN0MkJ>E!Ca{ouhZ=K?G9s+MF9BzWQn zpB&;4fZ+$yi<{4gJN}hxWpL24kFh^A!ngS zUXFrSPM-7ySEC#8DX@MMeDnA=eNS7{?soHtDzRMLgu;`MA?xfh( z^NaN5bAfC*!nEgo+%B2>7~FT|RWTW_ zS`s`#upAIs>mOHHdde?^4byK_5J<&{4)E6w;OLGD4^Su>427C~P_4MSVD-gUtIY!g z-%SU58;bGYO+SSb%~P;^>4g+C=(D z&))W9nz%eDDYrKRlU_bxOpdn62iL3v&TuBiQ7oAKz(Q=<6iKw3QiLenChWi#@L_6h z(y2nHjSpw8fNjrVO*p5jI*>B+>JHUrSs)h1-pP6SYbgYg(jJ($b9YCNsj{A5V2&4b zeiyhvYCNFgS~h1lt!wH&2ou#cMA;>o)`?; zk0DEKXJdwXil=FX5q_+3qNUC*p+~2O%O0KL^E#4Q-f{i7X${xdgcxM#fbUnm?w*0Q zps#mAq<7Yd28CZi?KcU?Z0sWP{&2hO=P~cP zdFr}n=p-^bLQe8;yf;v+7o93}FcIPRjyCB&x|q*%f#H3>l}Qa(-F9YJPYN?S^3dmQ zcDcd?iz?ahIYC)->f(jrPZvz#I)gL? zs>l<-5xaES76}}kcRT_ErN8T)zPx>CZsZpjbH@7Z84qMu{eM0O!2WN50G$fVB#|9u z4*9x%efFZ*aF~fRm4pcNYvjv`cePv>TIKnhoE~@aQ~Y1gpANM5qUnD3LFS2i@T=}O z+E+g&d6R8cKOIYBrd6Mvq)@rW>@Grzvhhm=8-&eqHHqnb#U|YC`nl*L1v~I|m#EW8 zcYwKd5!`#&^yG1dd*5_eUxm>u@uzt(GJKx(bZK(vUKCwl(AZa0KKHr_vFdHj# zdOVl-`ogAr?0w#o@=Y3nmOPfRByA%t(tlVWC{rq$>vaRZDVq+UMRm491rWMuzC&3= zcWKjN?TMpWLc3Hv)aH;lr z7Hfb=^*op4^j@9d(*uLz@^TZiIFB1m=Je}>1|W}9ADo3xVEYb9y)xdcY(EldInqD3 zePq3;Q`6&2J_m%Q;)idKDavKM73A+Mw3w*6{bR~7d7+SMC`kRl|KR@v|5L?dGP--O zvtLS_vZXY{>9i*?6e=-jDng_1ywucV%&7X810Pw!i!9*8J3h0<7Axfjk<%u#&P!kN z%C20$0y}d&=RllCnyKivO2`v*I?6}92WU!1`tM-@#8s>jTNhhyJ)9H{twExTT;jpM=$MDcUNM2ras z^85|nfcYV#qhCKqA}lDP#L_3xORTQ!XhCTF2y`e>xs{+dn6@qoM%e6`1g%0InS%E{ zr3BU?rCTmKT%2KXbBELd+T=ZpvY}+;M}vL47Y>&dgM10WghZswsqu=?_9j5?)1zzG z6@v_j+2!vJzCilRrPPfZchJ+1M|mFbq?F{@^<$X#(N2(b)w=1S+9EkORNx?T<&;kk z;^Z-#k71Jol?X)Lt~R{;hyx*>;=i9C*uL=3MWNj@1Dzuqj?C;Rq}v6H8{ayTrb9gD z_fAC{zK>4vr@3ZZvrl7X;@J*%1o$%%-V8sJ&_`#mXmC{v%=W5H(qMsw<#^x!gZbOs zv*?fCXF0S-*`Vat<5%QDAYqKZ+)ukvD)q(cZ1&0+BYz(uc*c93cjyTSOLKXL8l8J= z;S66CDTH|~Tbh*BbX}Ej;`^y@P(VihFM14n$l#X-@0D7n^-CB`jc8J9P!nFRcwpGTzA(|BY7lUtS_Bk0i<;(lcsQ;gId2T zJPx+xDutA9E#u6Zqu6-CEe4IuUV$M)Gu>@s2xkmtquny|n-&PenoW5Vg!kpFBa9pl zT!xo79S3Zqv4`o;1FQolXNR67`Gv~z5$lexeWV-DPm@@@m86WWu=&Alrf4kH_L}c= z#bRO$0TcKATf40r%!}hE_9D8o3YMuCgd7A{So6=hjUF% zbXyy+E0daGfqudy1@O-cN8NPlJF`N#HUm8P(^NcKv@<7*oW)@iP=y&Vyyqe<7vTF` zTgpl%C3^Vxs%km&g{eTWvuh>CD~sDul746(AC*%s?PmI?Gh9bvTO^#h;P;!14^4um zx=FTjfjX=!cDVBmedkR8)JmkRxiQNb;+yeQ@3)DPUFwzFBy^v#qca^Yi*h@<604sf zVTcTuogMZ%xJ33k%Y#Xr!abNFX5qcBR{UIz5%Eq!)e%^ad|exKF#`8L>zEzi)?91Hg^YBcH2~A#C z$%#9U-v^fjR_DWpDFx8RPT@+`#hABxtig32cLrOSU}#s8t?&%YrgKY^Xc(V2GPDG>`=>tOS z_N(FqzkS3^frOmU>eRtcK_)rYQaa;0RggXrtvO+Ju4TuKc_2{ap`P8z`8+6jBWT%|zaU%E|64qNSl z?uQW(ZD&rJBa!aD`0s%Qfu5*;cgKo3Pkg9~^qpwp_!BRWgp%PD3sf*i41!qUY;P@c zK^k6S)PzEa-l5CV#q4tsqm)`QnGuAQ?FL%fI+W_qpFMRdoqaVOYWA&_>7@f(6e0Lw zYg*9i(i&b5IV;Qt;I8Kdw)a1y$J9oCOxL(RfaWG<-+2s$9c(weX6;(~_!1LG*f-(z zX5w3nGLxl^>>BJ{Ea;kc`b-&L8a=w6^fRMGS0L@DE4A0#DeY@NzALvv^g`lq#Gb`T zM(-&%JO_n^x9513XyKQ2R6XXq5a?rJiZ7_N1)sXTmgY%=_rTutZ8imKvk*Fs8hN#? zR^x@wN}V0GX#`hWf$Hac7Jxm@y>{tNlWl$S^ z3`aZBgp)3}sb8(b@ndf}RJ8ZTKIp~7_G75?uDn-sw#gv%A3S+5WIkx;y;{j)dcj@W z7>ogt?cuuA#S=Nsn?gGABDhFdy+x2T=ey!(&2({f;rDhML5InFU4Il!NB6^uMyFL!s1n|R`{elZecIw~yp?jA-PI2(_KM66$u4v=`LTS&#HD@1B>o?X^Bix9T@$O$pN3 z6nV}r@Y+w)hr|P#fQeS>$x}5<7lk?3euF2PvKgaI5RH2j(XP?i+03sey(&eDVXg;h z-keUFGoiDjNo7WC8J5#Q+h>+kixK&QCwBt*0ts}>V=9R)!FS<~;i@P3y#GAm_kQ!d zGRf!K`dtTM?pLjAbXgmh_q@lU9ZlXov@xyd_p@YLPIOAbyf-)|2)rx}u1?Tv@b{^< zswHIKiWnyy*NbJ30Iar)I>QINCE~+hd5bqI)n<=<_YBTR!VQho8IFn$pgVX=z@pmY zu*5e}*5?4BN#vFmkdVeB!;3~`?S?4D10FY0pI-(l)sic4OSwI`Y%MV^HiS89ME8MwxdxVA_o>3+ zGV`euMdGPv_D`?N0B*t?De7xIe!6jww|p})QOv`ms`M%dQTLYR?|NeEmW@mgjeha% zdc;G=ua6nK?xGaYqk&aYmhc1`=wZvf-DI3>@1``R?vJYr=k#;lw*$u)Wb-ecE!aIR zbX`u^uuOtPg-&|b^;d=H)bEjfsnAUzts2Q~8Pq{GJ)adl%b-u6$LRN%Kg0R`kQC{9 zQOef1i;sghiBo^TdKiPA2nT8)cyI-@74o7x+6yL?YKK3YM-I9J$~5-N$2mT?nkc*S*GhvT2rk)- z66=eAu_8SycLKB&*P=t`8@)zM;p)eyK&T^O-l(fC!i%J61Xy@KC_WHEL&eW zUpdt*bAW8V6On4MM|T*hO?We-ZX=B4ilfCP4?&8Dj0*M&m1^M8_Kg5Uij%In9?vW? zc*IW5Vaa@G%;H_6sc zPL8^w@=gbuLcQw=cimqvI2GQwC6hKQsFc$;H_5&AKSy7$ttBts>i4{s(#PnMdv1LN znhJub7n-72R>&M?uTxh=+9SbOkwh9}9Q8-%xkA~?YcF8Wt^G+a=R6Xhyf+w_L;6in zr+)a#mDFA^=P_6M+dja%*u$NyK=ya^Bs_cMpGa2V< zPhOu#nsvZcVER15hu*{3UHhKFj(d&anv@gFk`5P{PW7c;Cx;`RTbT%PmrC*>N4+DV z&T2t6vkr0)Jvp;Sg*#5+p050C))w-_b(+2AFI6mYsHiZqlz^(kTxl$i`YnUP2D78f;%rYq(;#JFalcSr`MYT!fVZ|4z(%X2COWbaY9C8oTFKCvC5M$L3 zMeBnaMx69a3DT9t@L|a|O>H}a?^W}GKG}#!YsqfX0P_&{o5K8>Kj|D|58fOJ`kDA7|wLFVn$?p{rG|7go(5<~@cFD1(>wMmsy?1MM-4VQ+ zc5pi$X*anpy?HzZhuQ1;`Ph+DOUFWR#z*W<#~2$i`pk>o2S(RBr2QCEP-ZTs`L^an z-`Os*@ZlTP1f5?}C@67dJ6(yu8F$#gDbI(*M8`~yry{mHoPUQ?<)-e`aJBbv+oXBcoSE<>m>9b9b5|@SoMjK zJ;`;wrUcFJA?Q$|?U@(mLJ zA8>~0?_gNVu2-aPK9t=7(|te4O92xx)$SV!ZqBZoK%k-7N;RwmIZgA-TxY|VY0<&g zGj;CiFW!GA;(uTIzq+qIKlUtE41bXd>XFs|yVLlr3RZ*dsIE+Y`l?xe%n3Jd>`#)m zZ~H3c%Jsh+h1+}t6@urnImAfGn2t5SVe$}@tcjmR*I<}o@fc(LeIBB2Gw@1re51pUl9;y(;|eS6>Z z?noYe`v50%6(IN$HYLpRQnVc^W-B+j>ighXT|=~xEPTjiDlLp+F9d!q(f=w_AMsLdCDP# z_%rBt3G>^BSu{eg)!x4pFJHztj^+u8A(%m2g0yF~Se0|RE`NLeC~(1K&bjZRw4va< z=O%`MDA(LSt&AM@Qz5ndM{ME+Fb~>wWomHb7ImEAGxNT@@oI%93sw2f z`li#V;>{hMgjuk8=vSAzy(zUkb-Zluim*e{zZ6QELuXEji2@G;#ff;xHG$1@e68X; z$6uVI8xCV!^~k#^x>Ev-5wx@zImMbUY%iotm54-QGq3_y#ndH*wYDvG>M*rVcdi4; zBjD{5JI^l`+jy(P6i@X7& zE-v-mb%YBXH7+@DQa~sRLprqU9QB;NAENoapI>qH()s1K^OZzAjNfXbza+*ls?xqj zjKLoCr_|$nJxq_5%b99Db!~*l?0C)*ce(3fL~1?^o*nu!`P$_#HH(-~1K)8^ZA$%R z)P9TyJTJ*<_@|vVLE;TQc4@hpCh5)X>9caM@`T8jnOO6|BRy!w8^_|?Hfi$pWSEjT zz7o$zS)$j|@@VfS`t;&RF!Ulx#Wu=h&oXm8zAAoNbkTRifbk(>!6nIJB;so4obm4E zoDMYRWYbx^fzd_nv=2BPC1JTQVsVD<{9W*(z4NquEmiivmoLm5Y;`di0q;XqS{`h8 zOd{_&M7-x1ICtfRUG6dKmjtjP^?ALE-DFNuv3cX(t6j*Y&zG=pHWCz^2%e#Je1-1z z6Rc{BMb4=V*wS!{RXc6gzI(NPu-ET;eW;qPi0kMpx^r8BzPupSgwDgk!mh0G=PNX1 zm`{jRMKJ_zj*mE$)sFZ$Mb=VKovDD!KX8V9A$+0)K*SvyLnJ*&NVfMg#7bvziwJyH zFlJqTu%U6e#egt;;rr3g1z=|bT}d8R3#%>+>k*tV)AnKV0kmgfIOqmbGoam}J+UtP zl#_%O%?KSOWf|V3=D&lj2H-C}z3+#c%(*Wuh;O9GCYV=hqtL)+O@kpGe>;;@TRp3VDZhkIRkR<+-llhoBeTZo(>)Q` z%+Bfw_r1=Jrk0J@H>LtMmRQ~H6EAY4eUKp6le=-ZUQ)gvt_JL5p|(anl=5`1+7v{p z-jOeI5)Qo@t2~*9i@I3EDO0~F^2d!s?>#|t#=hrm1>X1A95B^8TK6&Ll4G&$5hV57 z`Iz1xwr)~dv6);Hnz30*0dyot%6Xx$OK`*-1t(WYL*5D;PkTAoXm?lz*BtR*qWm0k zbP5i-S>jDa$7y*$S{&{L`<&GpYM%Q_57=BiPFGHrv?9?Ngzfywf!uy|8Tyn}eDisL z#SLuc^m5jBRHH6VJo^$G*(Jc@Fu=ff8K7U3ZIRXU4EBeSVZa*lc4;-mWD)yjKRPMe zuJeck4AafyPIH;Z$m??b(C0#kil2gd+*f7M4fzi)mR+T;!Cz!Wr^vV*N)9Y5b(f?) zb8B4<4xUV(RC-ULo>P=M;CGx{8SJcIPy`!z1i0s&9nxiM#m+!Kon?l|pb9vJk<(US zKG+3!3>V3G{&rimw+|nA-YrV>nD`q(8bPnvHwFB&^ftIBS=1d;)cF(_ENN**RC?lK{2Ouo0$9&_`Sl%e`)0aD3);OKVyk=Am-T=4O+u>Ch7k;Y+$9Clh*rc`N0PoT6Ex^& z5cq|TA;T7K*GD#$9eC1a<=rQYWjZY{OY}LX-zLTFq-Z2}5Q&>mi!+!CzS{ZXZWEG< zVptCOc9y{dneG=TT@lgPQyTsO(pz}u{CwAo#XCG|nVtc0OUh>pXtERnyS|>CL#Kxc zytwA@LKR>t(~%&Z!U2CgB9;Vr2u7lBuMFJE_staNgJpW>N=my~K0TtOrvY|vWj6rq z_Kf;t92+a|0~iJP&03w%;NYZosF0SIiH6H4drD2grC#ADdRc&p3D;6fX+j-0gl&i;#QNXJD9|C0%<|7!cKI4ba zpRfEPVF8L z+LMiK?jw8E#M69qXI>kL*RYfXDiK)u%+u+(@dvk=TxG{TMB+up)Q8WQ47=Cfh8f2i z)hp>Y<8F|F&6cILn4{0*tHp?hSSnUsFlA+C8rhE>0ZMeSDy0@3WF=0m1xoe2Q($Zj zeh8giEG}fSN{9nmHY)wf%+DNMk)k<9DDk`2-)h^=kS&S{NVwd#@_o$d&NWNb^vjbX z)~-wCJHt1szQ$J7<*jxdt}XqX4d%WBQv>>EN*CN^pEE~t_R61LiVBB|;?l+fX@Gd(MTlC7Kn)OEL(z(ctJ1O{lep-$p{4;>lyV0b zbUNJ}{kgKSxax@kSlcy;OJhK_wn@0{ZIr|VO}ZF{#`~<33)G-wFFebLkn)da5-aOy zjF(N%-dA$q!YHPUOiX7V)bwnecvU(jZtI6`8?_zKxx^rRk+sQRX>d>4!+28nz0UQ$ z-tdtFYP{ma`*?%ixcAb^ma6&A_qQaa-*(@v-cgBKr|aO&r~I zOi7^uv6!BRIKX|pA3``4XL)oBNE5!ZUr;Y;4E%EzJiy%$SY6lenp7T#Nw}ar_Y5B+ zIlSI@k|3-+S{>v=fiXs(j++cyWHh(O+)6^@A9jHT`cvw@ha0{yF=7Rs6eQ$dxEl?P z;gK4i-bMDi;x9tq&i9=^T{{yY51bXIY zPWvb|sA!gm1?XoJ*lLcSiR)rJbL{Ob;-5Kw(V98+UUBa_#laRGB}yS6qIjE(Z5mFm z3v1qCHxBVL$Dt_5r7=qFK4j7muXKhZI^9{G_ud4CI1MDnMHn@alA3eob<)Hr&GWtG zp{FC0vk~#Lz>3$!whp$vA4#5rd#C7xF`qcoYcL{odkMOv5uSR0P)gxe-kS7UlO3LH zZIOQi{LfDWLErMFpY5n8kLrXA7lQu_>uoJA{gdV^2=56*nds1P!DI05v~^F7x9~32 zJl3%rJvGTM8JSE*`#0IL&<6-xSPeV_l|I$1?LT$As|#M0j}O5e4`|bbqPJq+ilm2~ zZvgFVr^QKVJf)lV$pdURUzY~eFp8G`ibWM}VcRia#9TXr;d^0RNl=h^2)HFtz^h5~ z54x%{+wQGW(2*!4ZpXR`>Wi)}(;H2mIoA`N<+ZnYz0hFylZK4e4cp3OGDnsaVX4vE zwE|p#J_Ga}Bq_&O5-QV%BHl%APkV$`+hC?04sQ3{==hT+x7a8Fy%%3&A34YQy4-Q_ zN<{A)i0;mTLmTIDrR%^Ydbmw~ixA!{#~u;tN9HSSRm*z2o%M;(7j_uWMB)lUti?&6 z!C{K?l(W=NESI|h#V%km+OIRlylObwz2g&F_Ro{mQDm1Qxd%=UHI-#BWsM>ini4q+ zEeIgKZDyG1<(xoswIe+^Zd?}gsAphj(aYM@d^`o)a3n3G=XhqwxQl~)itj;R${UjX zrXPv!bJ)DA9@``-R)+#DBJFlK04YC#@=E5G~Fl&i{5TYFTw4m@55zD+TjaZ`a zxRX1Gg4L$1Ot>U?n-vg=3xZV8RTMY5jv)Ea{m!vq`nC2^^+`7>ld?Z=^x|f*PND4W zh`RUe%ksW?;eL7Ld=Gj{;&AN3enAHToik|o94{V^x!whJK!Q8i-5HQdI|%YGt9qU{ zdgw&CM|-mE9I0^UX<{PZ)^s?xrOF&e3+RB7;|f8LN*6g!woVhidFB_mC*JAynswcr zEkw6E+9CA-@Do?Vf;gS&W9KlIp0xOU~PQMs+H*4THI*zIF0^C z;D4U|zI^x*>fgVA6)@fZPySP>UtjeF;-bIT>@T~WatRUrnV}~@My6r1_TmA!BaK$s zZ5E58+RW*;zj`BCE8P%Mq>PPk*m|5JodIqvT+%-M(3icWFF4vK*A+%jmTGBFl! zGv<|B48Cd|%gFQB!>O9)A^1_!Inow{a$N<2v4{6~y(dn-K#6ktOtp}^mz5CHz{J|L zuJoHm=xXc}3@o%Z66`$OUIlpx84Zs>vwcm?&r|0*$V|mMp!BmgU%%j2@Aw1o1V6Yx z2m%n`;`#bBIw9{4Zd^Qm_pR9N?)3XzUg;p79}CoZ+C1Pp#27@0BxFGQu?N_MA=ZOI zB+Q4(j;AKdfb@(K__B0iK!1P?kWAKr+k)Pgv~1IvPo)qqF}A3JDL z0o4Kml$=qdIk)~!x06ba0mJX}0sPC4wc(&KP>}$T4`KnX0DWjCme37O#O-T5a30c1 z2l!Da96ZU^>^B721uqp-`2FvnjnR3y9aqmUJgu|Q>H-+W&No{TISl)?fK&iDqM~Pl zCtvbse7wI-9@X476{oh8EubhJsEF){b3jh);D`a7j0IoW9~Kf1#-9{`aL1bzK4~72 zHAJ8xB#dAl)ijQ^V56vG*5@}!+lz72VHg7?sbo_k#DQ{IR{RvJNI)t9lx{&12nXwx zj-mmWFi>rDT?2h82GI-aI1wpgKopC5gftSU>_kKB1U>u=2SgOfQW7F@!*{9T2w)Un z(QZ5=WMo?uUi~9S|?%5J(?56J^QyDkiT91F94g2_yC8b>z3Ns6gDsG*E*I zYDj^Yjt9x6l(=}PlWf8UkCmeb4@An~ED=$RRegvk7K^Eh@8v<3RNkp+4z-YOEOp)~ z=?)Mtv{;V5U=ABMz8X-mw_?ayQ`Cx32Rv?&k~US$YGG44(syB+3uS~5iFN6b2gZu- zE{)!lwo227s~iO$=J-&ik&*yGhU8E!&{E`MWiGeoabnoou)J55Af8%6!nX_&V2x90 zL1+qDRsl<(5kBlP%99je~ltnX>x+yH{Gk+P{(sYL(4`;LP{2}&dM)DK(NS{5-k`nKA((L%V1#!}m5c)`X%PHdyUg4+I^$*xJ^72~j;O7aY26kcc=t{hH zg+b}{AHNg3v(UZ?L^FN$%ScC|Y*`_nfV4iFP^ z$U45!_tUdG9lQ5Zc>|P8vz>M^)mZe~QJ0!BRR$sF3J z7ERY5K2M7Af^dh{FC88ZTJKN5e-Ev6;X*G^A{!tUAa05jCDDLW`gB#Fl9HLMQ6R!7 znSFmk6P3tAFc=E~FCrkkaUG;oPYhLY5Q~ff!9op)fJg-xkRd{!82~%qGe_dDHL+n# zLj^IV1MR^|1R?|hJF!rqOKov20U!q0mHgPKnus|6dw!4zi1{!j%M%s>vIyY2b8n5z z3Z!sPM36NkG*cJ_7`x+i1Suxy63DHV5d%aO?Xk&=juZ==F*I1D*FM}c6$^o_o@Sfp zryCnG1iIgSA-&`1ZpZ8g@Wpen9D&47DA#a zH|ZVlq^AaalafMR5O5%u&^geSQZP>R6fg*Zb-K`+AShT;2))QCWWp~TBv}@OW=gaQ zo!RYN?R&tbE&=V%_O7qBLR>u4v!c%g=GRZ!=`@`yy5>i^oL+t$GGKSNB~?Ql-zSf| zo3_vM}1d$*>gngX4lXCj=0NUFBCfwL39>E(Y8o_9vyP7eFZ91c$|3Fjzk;bY0BH zhgq46%?Y93EOs@}YMc1i@1f#4=lJER_t+@dz%;<7ADTIpBDBJVSRXA8!XAky10k9| z(sVh|oJv1bfnLrjX&AN#&3{7744H{lP)2)`R1}Cr3(sO7N>m`m@cL6`X0i~EobI)` zU{DA`#-xKfrB(`(#W0hr=<>}0<=@pMuMT1hG@R7yRKGDM1+ID;1MqkIyr6h2A1wZWavA@-pAbqY3z7pIKo*oh2kDfHR?Yv-ClX)!Uq4;`xSv1x zdj7bdiC^nMJ=vlAs=p&FXb#EQ`@I@?Fb9k`e(F%?2(RaVQ==cXf*wH++u#$w5H1a9 zK=8SLp0UIo3RDBf1aev9aYO3D%BJV%DpG^>OdEIizCYI2@n$=)f2bWra>K3%wGHDC zE~XS77!WoaPhbGpukVvgRMG}?7=(e=fG6Bh?!n?n6$K#xktI-|9Nu~MmkHkgWQOR{^W!ZJw`3OrDOYFE^}?#y86Wo@ETrBI%oYJ6UO$m8brD- zmgoo3?-oZ~1Tw?4AxsJZVlWASYR=ynfo31nCd&l08*~ZHE4ZpkP=>x>z{oUkYv=u= zXQ}2fFZ`qr&+z$WohOXsQ|5qg0RVYoh8Dx3fm?L_d_2#gRAm< zgR*viX7#lIae>41I179o|5MfTHfdh*m?1<{FK=HfJPUgta89w0qdYGEllt!l0$rih ze+I|bfFD8FtGGo@Xeew=F+RO`_bV6dZyV54jEk>t7@R_p6_OBOP%;w=oTVt20R*Ar zSbPxv4|c^)(lN+8f4Z&jIhvhvWIq@2bo}OV!f<-n0DsY~`IZ1MGSPNU%VI*(t6^M6>n4e$E-{U2{9#NGE^r!u}rT>vXU)W1Xf`=2Gq_A+?f z&90i${O$i%+xT$evi-QP-!lVcj`Tis@_FUMp`rNyz4qUT`VS$-d_E1X+t2F#yJ{BI z-*-D}pUhuQW z+ur(muTwwxpxk9KyNUnPk>sqkH!aj=wBJ81oW=1~VVR@swZ8YGyYnwD+l_*;6n2dB52snvLsc3-PCz~Fe1ciOFH zqgtOAX<_mG8vOVEecWIA@m+U`{5Rj16O3aqxh_YU=Xp$4joha4OK-~#0KcG~m&<#o z)MvXdJGK9}dy4PLxXx|tb-ynoqW*qwJ+R2}JC4&Kwz=Hvt^a4Up{wZ5w79YT-c*`i zLlH-g+T(Sz|C8H$%hh*!?G>lu$nyIC4x`_)e-!>_#qs!$DxMz)cV(ZK)$96Pf&8of zo8IaEQ%%%reed}$FK7NX`tPm7{+!R1>Hd!QdHrg4_Z}@iS1Utj%y^mH*DjC0^mDHL zU&Y^)?q>1d%k6YnUY26F$Cc-{Jpk)Foxa0q@_c%Gs~(g3{T?S{&FR@;ncsdp4JId} z;_-Sc%YRi1{tbtF*Khfr@8;}$&hPH$r*FD{O1I(R{55yH8-5qy{tE0l|EZ;mtfS*} z{IBEoy-xoRd(cl8S*5+x?tI#s-lw?l^BiZ=u>YUy#r+q!%G*Ehk)4~f)$}9a@L|*7 z_J2xVw}X9C>-8Pak87akHaoh1w-wp-U$408admt6s$wts`>yK(^VRbGIBWf^8tFRz z-@e!7{q47RajoVtKZ7-k?JE6P_&>z`p7Yq2>uJw?^sIe98a}soVbbndrJ>)}GTN!VN$4_uPxsko zy^jyi@%-#()W&$5OW826vsd2>`HwBU3xC`DCqv_>zy1Ge!)>+VwD-E$dL6fo&+Qk^ zV!1o!eU0VqcG|D*=D6I|&mq+Jylro_x$9?Fe0yA{H>&gbr}uk5FMX@N*XMc)96nPa zZ|wHoZT45&+vpM9CqKI7m0zBj_isL?*D0L)bvtdP=UZd*b(@R*)&3UmDY2!3??TeP zmq|yV_HM4Z&TomwQPley-b%wYl-N@LljqdpKaRt-$k4h=)?6xBuM?R1Ddv8s%XQ-S zKhpcCd;Lem^uCTS$NbsaopxIXVQ0Y3c|5P%eR;A#JDe^0_Sa+gIXL}GUN=jSw6e$k+>pE|-&3fB-@|PS>=h56fLznltjXv`oapV3DBkcG;z8#;HqT@4Hzdzrl zBK6%5gQDE(a$WWY{twr`eC79fvd7@rJ=1Ud ze!^$t*lB$_mi}I@-!15F{p`(tH(UQMc71j~@@aK>jsE@EeD_1H$@~-br>DvG8ZK|L zi}*>~OZ9Nnb-z<3==cAJir8lFI?RV@L*vxX`<_&Zw`ZSaacIt$wY`Lk0#FBA7i@j=nZD)0|iCm8VFN@B))toMmKfmNPI-c*n z$ZkFyZ*Q#9BCiQu4lgNv4(pEgz7=y@9u|Z4d|WQxXS?5jAKl>GVDcJ?cOO0PRsXvT zx6=Jwe!q$7f40K^t=@7S-#NhXO68yX-ZqnKrtIuL zJ3Sp&2JX_&1I5{W%RSGth1+-icX!E9!PRFod|q?)GZopc$L>k^e)pB5it_)g+-E-* zU-@k)@aXy+JT8O7^($|-A)Q+3zN39~Xhz zU~Rv?A5H0OyF7-AE9&jAv0s}r*F|yZ=Faf@ zeveUW(DxYuqVKhLSRHp2yZt{;2T^rHBQkdD{NQ9hWAC?I-_5Vncweur@7a^gEv%gVVcrv`a1O7_1(ehKJQuVT;e!9=C`o9&+}Uk3q_bdo1Bi0zmE1d zn>^pz^7Wg~7ZsB0KE1|+Rq^TJ{WS6UT{o?-yQiP)e1omC#e7%(rrOiZ_W7^=p3$D7%GeU<*x5yXEFXSln>=)b=wn#OTHZZFg8mG?To z8p`kE`LozxpVq_VH+!9iV#=03$G(q4`gpybV^#NfkH>4uz20d&QPz7q+7$m*!-J0Y z?z;R}=9#iy=eOJLZTIl=czO1I2TzgVbUWUU5f@&&4d#EW%IEh~d^jtA?q{&a*>ha? zk?Z~ZUhiY$Neh)LO`a2(`bKCYkp9gcj>}PKBTd(P%oyvQ@ zUMh|!UAgA?5bHS&e@DE%*XMZ6ZSI#x=4R+r@bZ6a?|R%hSL|nY*`GFkNtc}!b?xp1 z{qJM@IZnTj)bdn&9NrE~+0){3-&4l;W<}8dOeeG2-@>lrsK?%RzkfAx*&XLIa|eZZ z?>8$C58J}f{9JZVrO@<|_J5D3x`W@e(Mzb?dHMd6jGy%TKU*b3X@CBwUcs98KX)P8 zSb0C|?c+a08r)y^)cv`@hyVzPXu7iF$QgEl8sI%{nzBfN3Z_rob9P{qJCid)`wVh@ z&j7&&e=`zeyue@kV}ZKJ*p48G{{md(Jpd-kOZ$2GS`YG@-{1Lrw^!5M&HbP5zxYF` zHf!E5{k{8Z-p70Dg7Lc}AVLxn5hRijijj)FVgewk%!e4gedGBD3U3(;xD@|pW;$O8 zK>szsv7h7r)O(&M#(+D}NdMRd)c?Q_(--|AtRL@V&$th4gZ%CtVPwyuCBwpCZ}o2G za~@89$a8*YBD)MhL%qa!0ML{t=>ZS%bhuD6QU8`82a|4YE;>ekN5*;d?GJVatUv7o zK%at_Jj*v!ht?%AQ-L9XTe>tQj(R`GifMv@v_ygGul>KHb6R@TVc!Rbu9kbShgVD8 zH+r-Gnd*O+gZ`6c#kF>Od9FA;N+a)*-*y_evB*<=f(g5*GI3?KvaV2ZYYphbY57)3^9 zmzjZ3Z9QlQV|*zU$O#DGpabDxjKi=%==^s2b7AL?qxW1F>tO%8EX7CBNhD&ViUHuB z3Z)ZBZy5v-AUxw3kQ{R3`-z>%ZQYON0eyoffJQ_0)HjBZ+BEh5))- zgBYTSDEFdmkcFv@QibA~#S?57Oa%-gI3WQuVlPw`!Xp9*5s7>dJLOl{fnqd*+aNI( zNfbX)%rIm`5rQqDc2t606e2qqjj|mozxKz%%knaJil}7-N74HfWQ16=*DOE6(3BDb zAQDsYY$kU@y%h>07!iPAD;Mj~<{C&bB!18aG!s0jr|j4(%e4y!BnpNtSEWsaym$Tc zOX^|qR>~b%fb>Dkf?OSmC=q0WG8jUJ*(tx-Su9X8)I{q*T`UiZQ5@)bpgyz^Eag$< zLh?`_`Lb;xV3-ryRh!3hdq5hn7*DtAms%uz2HiPDUw25f5^L0Nd_sZa(@#D|nS7tB zfOCp~ke-bo2?j!b(!o%ok|GEUP!eCUB}T%45P|?IG9tup6*=tD0AV|HQa{;URd$3lbs`cOUL1C76HD*`sCANL3AHIfP9|| z!I%0-nCQ<$(NT&XV=?~q!Li`MIw|0 z%@SU~Jt97N^MsA(1SI>6a${1AmT(7Gni)694u;cH`Hx_zOU{}wlQ(Bg$84A;N2Ul= z;N7LNfo|slCuFhmrWE!%KcOslAg9?}rodnkke?$I!xV}@qREB}53HKATh;-*mpG93 zC!Z2n6#S?jD+e14q>s0K`y>Art{*2(5@Jl>$Tl=oglxe26HWxS@SweZQoRf~YWq zP{GY`0C3<T1Fvx`93J}F#7KJ@IWE+qIK?wn&U*1Fm)QLQ<008llASbg0Xqv~m zJ!GY@j5NOY!B;{=0viZn;ZH3{yUakb0ok7Z$DWTbdTRQedwP7EJ_BBFLqcR;a6v~) zq@ed8quP!DF%*yt3UIN0Cs(Zi-ip0oCx8P+i|Ld)Zv~M$AktY(T$DCz95-w=8Up2- zF>a@x2Ev0G7VV20$g(g}Mu-?<5d=42fzC=b)_nZE{QSQbgX~;GW28J9E$&yP${0h5fvRAnbrhpnlyyxdF{ZP7C~JiNB&_Y`LgTSIMs1GY zk-6^-#Rc-HVT}~M=2osdFjZEpm}WZ{zBm&qD@H~T+X~Y{X~+FPd2qxvRencktKM}> zJOs}RN{OpR=S~sfFKZ3ebi~~QQf|c1*c!xk@>Ux|I*iT*iDj%z&~w$C1GL1&UH~=_ zGbcm}+FLnco-G&;PH$-uJq7 zA?3#mo-_=FV#M>$rJKC2FZ&-$gb(WhJMt#SKsrPo5<%dh*|!Z|1UN&f&Aey6AZdsi zqBfC%(!*88_cVFbsPYg{kU%y-kQU(Sh;4ut>r#SewtyjN6g_B!U=(%)HJT|#Q4tRW z4;d7=B$=FQff@>gLW*H#pnSxN=lCm&F9gP3Uk7~`-eC|&*ns@8QsM^$4v13&dH`&{ zO^KsI9+Uz2x~~*Z@?s;R28gE(Q)WDxtCfNv5&;N?H6$#oh=k!>mRE=coj)Gi_)><> zgXhJbm1qnhhQ`lTkY(!IiMABP(F2W)j+p=k-mAp+y3$9XgjEi=|$D4Bys(Dsr*Cm z6u-w8r|f%Pkwwyac?TZwhU`;sIqcH)wa^*6_gm6o0se482bn1bm5zYw2&@7t0(RhY zKu>-kX6f_&o0AWBGU6tvNj<8-JDi$38~uz8&E;l-nhpdaVf*Fh?^yxtZk6CFkdi>w z!%>naLXlUR9UR2=`GYoigaUvwsn~1?Y(pZ1#J>n3h$-iQl_C!m6S6>RAd9jhCTkHy z-4syc2r3|02nYaLDjbm#%LE6eC;{`uK`kQ%Fp4-sOgnoVRD&bgq%H_&hV&Mo-Ua0~ z9B0iG8!Dd`DZ~W41o=b+DKSDNVTG=Y84w}!O+vl_c;C1o5)){4=tXwKLgFb9cp|R^ z3!v>XKWY|J6N|BHYy!C@ zq%ByGhLhfYQ$7YmA~BKzGgEXAA}@jnXozES3RF!{gc$`Wo#;^`A>$R`G6Xb4(nbjT zG5?|nZ>qn275ZjrF5$nhW^b32U|PXy@ttd|T~$|=8*KPFUHm=taWTbI28w4U7FFW@ zPd#S=oP0xMK0R;Y-~heoml;aU5Wl#vD{)FsnyAEp184vjz(nIn3KOnTA}XF%DC=S) z=(0dp*obvwA`kuWd}upj6K3kNS@2q|L%7vV0v(X*Se6+|4S?VfE)k_0a;}{mhLcEg z%@|^q;yD%?{2ezpXr?%jbqsKtkPP<-D=at>E&V7H&nY^{!sNzI#?iQptFmx>bzijn zm|d{c-{y(vMhmvzXD&W6`!W2W^azD!2eAR}LA1I)V8r)|X;35SMnEXyWFnQ0NP8MA z=IDu89MA%bvAhE{elC*{fdnGVcvQEvHy-Fqvt(Os!hkAve?Qdkdfi^<55MmoE_>~6 z|FFN?kMWQFq@EQQ`$_&7GMOtIly+@g#dJsfwx(G3fz}`=NnQqBq_|n_s2``|KK) ztH3wkqgl3d5-Nca-6H)YEHKtXhX*65?OsuB*+x?nFsUY1Nkvt%u={1 z3W?wukQzu&N?S1eFn%j9S8O~|ch~nKP+q*mMK#R)o{s-Z&IU?iTzj{tJaRjzb8nrH zhkOr;>@m;4Fak@B4+2?&5nth|M1x8x&O2xC)r!S2=j>3cghXtG&aD&^A=fz2`~4i= zMl}E*5n=TpV_^AQF?#>azSuWfVLvlWq0DN7!QE)#Nd080mDGob z8Z6|X-W|ieWW{Y2N^Kx7!p4ISkeeAJb z2WN509Me}@#R?aDtVa}dYn?fjmwjWa^HL;oOV6*`rqm7(ygjq<`Lq@Xx2%3k81SFG z9=V=Xf-SeX>*<(g@Ps?OT~2m73_w*`T;!SA+W5q^qn*pl7+~wR03-+8?}jFo{Jo|gd;^L^)NdFf6Z`U$x6?E(=;Wf>lb zo3~&kT$y87^t76Uh`gmdx(F2WL70j#nJz72uQS^!bXFoFXY;#m$0||Z<+N^bvm}!8 zrkyK+^Nq_Y;Do0_vQbc11gO=0_qSC`$%OaXE*zX@pb3hX)dmj)l6gKU0dc1()I-tZ zl-fB^?#UrPavGjD#Z_t*?iz+bCuR(<4>920#aslL{^u+cGoWU2zRUR3MzRrxmiQJfk#pm=_L&fQq6(HqX=J5wpWiv8b{o`?*TbMw+R@1 zBZkgFk0DG7v1Ixb)KG@~VngLcJs}bg1H5!HzTxxvesiCDjhU78+gqPrR5bFuDASy9 z6K*Xc2fMl57XuReTD#PZ)CJ?XXOGjKLhPdMtDf;nx=9uXst1uuK|)53_==kv?=LN+ zJx_2&a{V6C8gt~dd%IoXDUUNr)qSCad7%XzY3g66lfNH5>eu0&kDqPwDMr-x!-Ndu z8Ob(a!l@}~0$x6j%Rt8OZ^G3QZQz-N*N-@o!-Dam?fCpzMUNH5jq|U;*NN~gIJk}$ zjEjXL0ZW=1`*Iy!g?jzGDToo%f=3fVzBLrYVlUNd(;c=I*_rES7Lztvl)Wv*jLF zO?4yH}g&$!54SJ|lT zpKGfc^hW(E-E~1$?QdE&vV5|S0oBlc>m2tv$kxeimxL6fx(poN{A*=knD|lR&J(w9y<>ZRaFuJ;4%4*etGoj< z%}ow%Eg?RXge*$}J}I%7JCTQLu#q&7Zx#!NB%|Es2Y;@M9?S88RxX zN$s5v@PL{#LMy!N?{LLaOx^HG`}FHR*RkC)7LJ150t8ZJ8ERKys2h|A-d518Btxts zcC^WFy7D31Quk2UeW|_dW!oT^P9U$In{J2uukR|KxA)p0t=7-D-3TYIsn5%Gf^We& zS61FT9_{{U(@Z;xww0OmS$XB5xll!3UG6uC63)Cs+&ym>a9`Rzs9oniS$ys9w1YFK z3dccFYaFyw6@i~yzFn^O%N!F_%~4M?_HTQr79Ikb?x;$1b==QckJ2Gk%=A1>%l10u z#8Tg3a1kwcYVD!yACJ9ynessn!IGw&9(B657~KxRW5iK4Z*N4gFAG9RNUk#;+!k$| znt6kz3KQ*kZuBv5+3kEQ-P=uEJH)=z>5ziuLeCY^f+Q&R%GSB-IKyU4>oAkx81pY4 zOn46h;whCi#l|-2)1x$LyO&6L+?hgUi|IS`iHp4!)@yQtTfP9EOmt58{UUN3_D(T* z_rB`e!Fv~cW2=y5emdnxxcS}k+fnFvkotue>yYbom>s<+O%+3*iqa(|fgE#o0;bUo$|?g}U00G-tmka<&k~>r&fG{4Uwx3p%gyIQG`321x_(X{ zB{bk?G^GsO1cn?Xdv3NdJSY^WT;pz)y0=f1L16=aqEH@QN^xV)WHAv>0)sqro_JR2 z&Ux>6+&PpFid!tBH{~cG;*KAWv9*j`Jkj74w*`;QGC%y{zgPg7>IKW$veFcU{$ry{d1VLH1s#P0lw+t{Xc} z;&M%@!lUhtV{#8UbaN24#HW~zUa+W6$E|@%Z32M#*xq)Id|C<1o97t-#6!aKyi_@o zNF0ZtdE{=ejy zWQg~jj_9lT`^G~Vc?L~f#LCi_9&0pHZ!KXXn0ypW!+Be~cI&EhGnN)wflDDSddoIx&chM9ZU_dvPUew@3~s}}}6KHme7-Irgcuy#Cp^LYU} z!A#6jMK1)C&X&gY-1*X=j?IuZ%shP0|077G#_2e>HozCs&xqkPJG&{hz-uUZf z>ykNAaLIb6ThlP6TaNXauh|~p#=omU`p{tpRx9A?-ikhC`D@x9YvfD^IfYO+Pbk^K z;~`%;y*m#)Kz@7>WRImERtfnTJN6-)pL)2J%UG~M5uTsA`61Zw@DuE@&)wl3FJ)>$ z@9$y%$b~e;59WAQNk2b4`X%oD8~k}BzsQvBurbyLbnl^wx5cK08z=$kjCAkn64pTz zB?)Wmdgh1%#8xn6Qr-w)kVh1f(sCHtj5)K|m&f;7sQ&Z)|G7VZhHkP@zJ~@nll%|l zUIk*APXfH2$zmtupgBw9mzjY2p5=x;w>xl~J~o)}?<)}V9*xu9=AD*g%hWfI5wm(v zC>)ufr0`cvN6IyW@V4KG4DxFc3fxAD=(8A2mhy zH3B~!ogCah1+zQfvHC)2QT(P#=Jpw8tya;Zvuv!|HfhgOIvV1myMTyykb?OBgo+SI z5QKvWPI{h-?`qG(qkmodX6{|rJKf`d;rvPG_~+>Z#r%GvAMpM*RAu+^!oNs<)cIqR z23QS#on{Uy%q_{&I|&_^nn(z(>rmV+Fud8Mi@4Za=fcWm-9oR56PTvKS>Fe#alFOj zRUzt@4`jN6Ll6~mon4~{EcH7+`4_+{8cTZsctnKLC|4O?^fPhenjC3truO38R>g7B zEn50a^1k*rI+jr@d>gFV;h*+?2X&OVKWBMiWG4Ol11zE-Nmo)+Fc8%xt8UTCUh;RI z&Y9Of=lALzm>g(L5*J_@2j!g`$c=KorOuU5ra9CIk(L-ZT98?4MPjFl=*ST+2}Lwc zimAymke@j%qUnJUKH4?Wh}!B;9FV<=0p_WE!tNp`TRv-txI?^YL=3BO_k}Dsyd=ok zN*l|p%X;OaIR7EvCd8gC$7T^ZtE zdn1Eb+>X<4H(dRQeQV|X=9#@OKwmqw$@N(tH2R;{HWI1iaUOT3)s4G$s@yh)v4mxj4#j+xX z72k==?PfXl`!Ayd5Zzi{vh$K~TcTa-JfHUetM>=&ALU85ia!4B`Xl%7{yr zynFF8B!KzzeI%a?v=S|e9FQ~bIPHWlM^PaBxLXc&up)-iQ=0Z^h7 z{_lN^k$6z=Th|l~8SdBJZ@q{;;?w6JQYsrIzWS%AU2tU>q{K@K1>8~cA0PE(hp9a1n*HMH8l#tn=?588JZ zN{+aQI^cwYrkJ9-&*KX%3{t{3ngU@Uh0p`-!3$ZW5;p=UzI;Qu0gzTgMF|u^jK=Gmoy5smCqI0)!ZXha443izZ~lZ>9s?im3Kr2s+|_=0#GN za77Hoq6Tk%jW06p20AUEy-?IcOyTKRx-D$}K5_gUBM<7m&F3Is_FW)JR$G5`d% zD$*UZ*10z)!J-~f9$DpLlt5_^>5rNYlHg6q4G=WupUM{i%(AhTvz`IpPJXB4829!e1rnQRgMM(E!#|1fUp-C#euXs~@HLHoF2wOipS~oBg_Xp@{q{$${;B;=i{G#GUQF zea;vhQA}d<%b;-LMyme#WnYecLSG^sh=l~eqJ!zA1I;HX&sK?`84R-6EG;CdUrwwf zHPBLRq+BBwBmyAYy$VDj0c4(4P>~CwL)d^&Fl_{j*ss8XVFCmo5fc5HDP0ht65$b` zA`&19L1EQ|l*lZE;8qflv4tUG0TK)$Q3#;{5=h9vP_IB}EhREj1qF)60aB<6fFLDA ze>MQ65L*NqB9Mxa;-vo3Vvb`Vh!RmC`jP`dK*2ypAs0lbWgwCvLX?}Rs3npJ6%-9E zqChBLqXOg^2m&7h1q=n-5jf?6TvUSaL!dp6ogT{}E+n5{rnM2L8^tV|s-QunK?CHyHhA5L3)7t41x73$ApQWs!L ziNVFP<#mR~Et`!O&u+Z0=)VV>e4F=l!PGzljIttpm>~$MfpFs$X*wVUK>{S4JvQjL zxD$RaZVG%X5xKuSC|V%^_t037lu1!;#jykm86r;Y`nNuNwF%xf7gZ0giP(an>_iao zK^wv%rkO@?ijYg69f!BTUc?M82tcKgKA5Ep_4}N?9UYr?xD3&5*5=V}>g#~EwhJcH zZORjJi5@tv1hB^}5H>gx{I4bX7>Tt#_mfEOIhjz|EeuhsQn`nQ;sZdIy~>T!4qu3L zEDP3ty&$^RRrO8Cygn@Z0+&<@L_(ND1A*1=3m&%{L2l6G;I{n%dw!J6@W}0WJ>geW zGi}dsgQ)S~xg$lG-OUJwxyw?RJXW76QyC_ptb3lP@d^dn+gWgUKD*=6A7|EyrG?5U z=-^zwMr^+eZc-gI5DEQpYipX^8wU4!hM-OD2OWuYc9+Q%mLhrg1HQ#o!R0z=H0BJ} zjEw-FF5NhUe(0%)I$M+K=|?#c<$fHh#F2~tjQ>P`U=M-#O=Wk2YF_+jtly>V8vy^yJedsM!;@o~VOtNF;$f(VU*E@p-oUosG8i39z4G zOCJ&DrG`_^-mdqO;Hv5`O~nr5z;A=0pQEht{jO%UBk$LbrtRY6g}Z&GiBGD%`#`6C zvKR;kWO_7s^lZ&XQ_bmV?QcCz3+3b^>h$=hagnl}uE$!9n~wo(IjWipekm9`v>V@T(V zA;zyt=aKjTA0O!b&L1vvm%35g`}UMZ{-HDVA2&{qc#11rvB+PuCFMn2-0GLDdZ+GT zQKgxv_6%?w`su)Rm@khotc)~0_j+aO7~tQBi|=_|1!e3z)j3X)^D-~+KRkaxmq((g080Ttvf#9nrCHGCaC@yhM~)3B3u+0)J9 z@|KsarJRCaq4g%Y_TPPCo=6+hYcXo3SsAKB??ZwmvrMXBJmeb{a%&7lvd*WC&5!ru zD<8M%9Ms+-ZgC}FxlvPdLKbFC_cRVZgx}E*47|px_+*ixM+=wZzceA1^M|gL>&1-q zY1TRBF;@Q3o%@nMLY-)K4+hVo6GXwKJ@ULzZodFPi(g>?avAYUV{eYm@EsQ?gl)^n zqNP6M2O0=DB?|bXEjZzh8YD5}+t8-zQaK-D^8z^O@#b!|v&4Ns^ZN2Xbx(gTVDv9+Ym|%KkuB(I(bD`WLuF__74i%@T~fW4T)m>_P)4RsT$fyxSmNWpn1O+dcL-O8C7W zMAv=W4OVj4ZZO=6lBA_^X#%Oo(H)N(?mIBH#&KSRo2hA2m(JZ?bD=wYD&I9dgrl>u zdQl!~hp9}4nmlgs@CM*@@0{LVE_!?KIk@-T_iRVqb?-~8&x*>MBK=q0Qy25OFxCZ< z(qWVF+K6r}YjX=VYk)gx#jw-n!r~yNA-TtKQ6Ba%?Y)`1D}Fx~!*u)JhOaXxd5dG@ zf$R#}NEx{M5tMbcaGAM^spe)b8eFGl^MeKIIi=|+DEGKf9fg_j`4HVo^pcF_RElO9 z6Wp#c6dQp)7qIaV-$q^tHgkbpol@bO^D)|`!MS09I@aYBT!I7&_+S+0VDE#)f z5dCmo=eeC6NOq&IP7?I{9RlZQ4f%eR=SgWdtzjMA#;;R`f3WER`0?edHWYR9^p`*Gnf)VYw_x|Wyp{dZp!Q3V2kD6 z5W{rY=g$~YUYjiMc^)4dQ_q^%I^@h3-8Tx3af(3;O8{ZCDxLBXMUlatb|)Lh)W(=K-hUna%uEA4Y$` zPNzd+kPI7pN#f}*(6Rf@m)I}98mJP(#26rSReWiz7S`u#p7`_5z;b;3v2J``32Abx zFzN&fc6INTx88K@o;?eTM0aqz3EeU}?{?#Ie7uN#^LWh(65MG@F3(5A^v2Q=wb#)K z=5?fqOSu_U+%IhRZ<`Ag!^bxTXw!XV=~HR6>#-*{h;eFcta!3^R=q)!({H^5-}>Gm_zYcT`!l; zug3LQpI!Wys?v1J({B2sPzf#)0+E*@q5^PFqe07rUk6^IL|DS!0XdMq8BlLl^piCz zZ1$f$=H>W2H)nCs&u((rTg7fY_gY}O-`4r^F-m-$a220*9Q|5)-nSbszJBR~3|uEl z1s|&Ot0wMzWsbBL6?)%&&dJ4G4uPqy3OQVR&BJX0$3@}_C{0qa->%{;c|uKvT}XKh z_qe`jcVVMJo<6LF_2{U}n>F-42l4&iJ|bf(D4g*H!UDmCi1BUw+jFmk`%xW41xaSo zijbCiVWhRW)F?eolW9_1kU=C%rp2YIdKJEuhmxhfD-p@H3KJ>LwekOw$n(7Vo!H@9 zDl9$}Hdri|rnvGzFYig8#9bA&JUi3oD)3Re6LN$L?+mbo^NzlBWh)W{@~;Yb%$dQZ z)l}s6XBAQc$6mD#P`dbS>wbF#58bmda`pFZbmkMdidLMoesR!~=&b6QL;AWU!*xn~ z(>oeiQ6GW&5S2p*`&QqJ)!-OkJHLWI@}KD*U&g4gTKnRYfx{z2$j!`=f-)j1z=47Q zcyqj;UhCcWbReJtr3tj9o|%iKMNa?2>3px<3k+mo{C|!x|2){Bg0**tBn~Jm8T_8d zlEW>Vb6s~Z{y!xsCwc%4&|mQS;CrwGc?*qf6H0t2N@buTNdW2(2bighpbR+=HV2R< zrHpga;0d1H{vQn(EBCy$q?b2QhNq{Y{0_b4r^J5xY?UI#Ur@Z};ARkcgAq7zS(e+o zFm`peN5`=3Fl~9wU#bLtEXj;eC%VCZVRp;hbh2dUWv^hJU%nm?`+ekf+Xa>6Boe;a z@{X1SJ2`q4-FWNF0~M6J&HUk_#ptVh4*Xo8?W@*_*h41-$}#o zyxu+C4k{}Z2fnru-QG_Z)!t?348hxm??D+t?7y=IB%roRW`JLJjc7I-2@D@czeNLz z+O9foN=B zc9(H))0DGid&a5TFtkCBaA~gmHh#AC=PYV7#4TX-7Y01zwMF{qfpu}ijbvSqf;kWb zd^qp96(?K8Yh%NsLAHs$u40uBd7F^tyWBMM7c1D#828(llvXk+yRIKxd6aPj%xm&n zk9&U0>Jy@E^E3ov45MN^NWyt3@KW!*wN#=F5f)_YFiTku2L`jiO7?=#<^~>FM+DYp z$;;tRmbbFoDgbG!u}X@0)-Lg@e6Yxj?eKM@9$n1ENrTgO??CUS)4hexs`s9e^pAnK zfd$_A3ZJojhL3uCg-@4eoaa1!0d8V^O$r0{TK7~f_#gIbL%mae9&E**mU_f?sJ)0VXGBWh18zfeMKbV$w|QG}a{ z=5|J6Ps6lJj$I-Ql`Kp5(ZjhzxjJ@q6RCG}=VdqNLNzRhJJq`r5%ym%iKi`f5QrXO z%Iy{iW%YPlOQ)#ZdmHtzmw5%+@RB!3?}OCbI9uhdhiJtq4Z4nOarbiWY?)gj0nmz!6U$VX*5TmHySU`Lbh5P638zkm%I|7Q-?KMp?DIB@ImBeNJj zP9W<3TIbRGO*!5r3zR|6h~+psyxHY1`G61azI^%fTJywUUh8jH82yi+MbE`ryX954 zKJ)H%n$K)6j1$c2C+A_=;myTcXN+c9%{A8UB}q}~irkoOcc@h(9ey-1zdVlfjYDXZ z{@gT>jWV3$u5me!6ROy(ql7`=MC#lRUKsQ&;wWXV@C&A1l=bnw)>boX^W&8`^H1F% zIE}vZa+q@L6l%TicDY5=gk@cz&f4WCiorJ=UA$Xp#>c=e+pL08I^Ocsx1>$Q5<{LG zU9pt6JV0FyuB~aJ(D0bD3CeXn?%7WKB!dJU)#(MlAx+$j`3jMGG8)3L{gsN4x6u}p zCF>&%Vr8>l>?VtA*RtDpZP~i{bhtU7b1+c)YI6$vEKqs=iong`1P|ZL^iI80L?VeO z4rVAe5x6O)AF_xRRB?*v6TJbU66pbq5HX36DP&4eILMWOFJbmzAoe1FW>7sc^$xLc zrosn2e6S2oi^jvO`$x&YvGsn{-@mfu)(py`J?x-MQVSpqO6d*>Q7Z+J(oD$9hqTR^ z&4H6&5V%a_Bgn*7NN>O zBiKYax+_KC_G}YmDB>c!wo$3ro3mNoVi|Jzo+bQFC6`MOK5)dBo`tU(qKMD;k62GD zNX_`j<|0WznkLn~?Q&;1yTC5@c;~JVS8VTa7mw9WidJ&lf&fE6yua#oN+69=@W5J6 zt~Vtnl|a_R3S)TTOQ3Wx$k{0i6H-o!LaoILqSoz%M8J)a&VtC^Y_=#3lhBA6LsG;s zq&JLsAX^L~=|Z$v7!VCm{lp)_A7ul!2_g|@0C*8f04lZTLb|%Xq}81o_E%TbKvgBg z$Rd1GH{ycD6<-Ms?y3%7i$#CYb zWXJSulQtapga_og6G#b^3$w~OG@0fQ(2yEcGK=Vtyc$MO$Z;DyW!cXXUb=F+c)fLt zn!wH{0_;noWp)ISJ-hKrFv;*i1Uc1kNg+H~&&kdQ)>+lIz zeU3+f^gS?F=g^WhxBDY0&S7@hqm>*>J|@WVI^l4}!K%T@me8dV2Lir8SPxuZP&T;B z_mv3z%~ID~-8PQ0Jclka!hnvCspIwYif7Zgz3ByUcAfct?R!WErlE%Rjio#~;_#EP z#7W?31g|QU25tm6+*RuF6-OogUbOb&g2+|^;;kr#a2Qc6>iVK&QAmo=>%O++XqtD< z(&^xO=IZt!2iCRGAT)i`=JM#@ybknVx6SFJabzO~J3Vw&8-tz<)$3gX(T_*FIWaZS zSD}<|=lT85RCM>s7ITlp?{+^flNXKoys=o?pTCcZC;}VG-Rj42+j&$XXf;|V<4~Fh z5(@ROTLtGH{%I<&8vMIA0EVqT9(R3>m6hh9j@y*h*z61|h*wmON)LNrFZ+iheV}hb z;G# zNf^je4pybmG5ksHdrMFlC3)QFh)0K@k%MM6sD92`vbpnJ zXBu2wqcZz)BiT+5r>a>=Uhh1Fp6@0$YrScMY^Owhj5ulX+@cG`($=Bl%j_J*6g6*e zzCS9i(pXr6u!#FNna3VX+1JX5d~2twSwu{0CQ@#@iLXphx2X`E*0@XOMdFRERM9+w z9!6G^V=gaLm&w)QzjL-S!Bz8E4M+!VEdW0jDZg!TvWU;|YeqXEw#aK}@pDwl5 z1$#YJAde@7D=n4IQZT_X-Dc}JUM0aQNs9+!4Y_TQGt>3@Ty{G|J*U2V8-#D~W45y} zLuSIA>K|LL;3-a1TAhnbYbP>7Li?e2H0u>jNqbvO$Xt$o(Vmys#mZ) z1)TG75_knXh+AOi0XbEUBWHXb``x(r1-a91gjp!`%4|4O&v=)Q4hi36zHv%+7rou4 z8eLjFGBiA_Gh)OVnlF$jQHLw@sO0dYWb!)VVc2?$6K zIyPB(kyT0R1nt8b@+AA#*`suySYCWMVFDPAMyTt7bImv8B-Qm@%fF`chwYrmlvwkkJwP_YF9jAf^mnVLqR*JyF5ocbDiY))sTorEu(=GX1r{fnldmsi3B8?ENnj$M1K44Gy}}L}(6lE7tL{yfx|sA#r?kM;rP3Z1 zHyaO$%^C7~8LVnptnwJueBRY97~o~{jN`Ybp2ATt5aT9y;FnR$;_RTVd?^x0(H!(l^d%HalIdvzD9pfKsdz@{QL&P7 zUa2HUM{At+O?5+1MxzA~BOhj2*SuNST-nZ#z3%|fWL<2MhV~z(9I8X(`v6)c3HU7Q z#R<)OzTo)$C$k)H6Quh#G`t!?v)USrz~sbZ7LqF^Bazomt5d) zgKl6Dky%>=T8L>njGqP&GnZusb{X6ErX#RW7FBv6k$*YOiG#2cl6*SWdyZ4099hlfjgXRtfC|K}$E!ws% z0UT4`9~HP5evRAF2Mw`E*5r?rouhk!c^e&YXf^IVkA8Vqp5ZKnA7bpnqn_BGcWfFn z(T(No@u6?n!I-wZLtHw@L+3p{_i(-3<8|CDhVW>)2~XDyjwgi|Wf4>-Vp@3u{KMTT zyC?O%`qE0frE8(eQjjk<64E4_c5skUT4!HKqda0|FgVHOLD!);x*Hc84@%_|_^tL^ zBI3?HlSv3g;O2)B!FlHgs~fbw9`pB%q6G9k-@D)szg~B`$Pb(Cf4CrO9_1^0f|7Eh zX61$Mtn+DHOIVYquQZES)6S=-bv3&UM-26=rl(s&2IPjmDUi}Z%nit?W#!K3Yk<}b zonGZI%Su6;Q^z=|ZF!tf;jw#L)XHHTbomLvw8HEw*NHfGek}KdWVd>?hZIf`(k=)o zE*GM)qF)OVL&Q2Nl%XZw$3{MMOwk_ zI7jZ+uZTRpa<0E_S5{kfW1~B)tr_nAKs%`PaM|FF9Dwq#NEEu~(Tx)iKLv1P>9l7l z;XZ;XE;xN4V4FO-rrJ!#V8rt?qq(!h`AoY~A!n z(7er|C>g+~y>51CW_)91t4nlF9GSnHPAx2ktb$BPze2Ph)7Jwb;EA&7K1F?5=hwQi zPZ=k>F2a_TGVmKsL=cIoQCtri6=7SRDw~JA^^YyKJY_~Vy&Wp`C^D0@lm&5y=Ryb zJgfv61&R}(QUggCKq3gi0E`vzmK?_D;bNiB&Bq~JhYv;_81x*vdM7Pt#CY}3=R1Lp zFY_aye?(cL^Z56TrUdM1)v!lGpx#aJdEP*aA|F%_1;rLuT9S$(l^P?u$kqWB#WK6=e+%q=NU>IpL?94&M{!=sp1?b>cpg>Ck(})%GqpxdYJ)EchU@SMlRRZTNvEE4mGc zq&?%f>}m6+B0^mXn93Fc)`q{!3uN^TEA1Xm&Vryxs&L26Wy|9rKXT} ztQwK-w_bP6*UpKWM=G<^X8o4vEOr8;SU60Sijoy%$ba(;+4Ae}(5gc}XWbAZaY_2P zUC>>aNca2$$0N|Z?_(1xQp7Z?kjwcpcZA=smWGBOWH?LJ?M%gu8`}Mv=F4<5pdUVW z8gHsQJ+2eYU~6-n%kAW&Zluk)^Q>$sEQh#5b`=9m z#PHnZuSZ!nL#i@r@O38gC!DkNzHCnp_r8_$0kOT0Zw_wwrVGb%=M+1DxU5X-nD@fv zuSg+Z%qcqze4cX2J(CPwm|GuvEqMImA`bVnPQ|>DLy<&D!5fuq$P{j;iLPifm0Yel ztw`j?Ju9zdFb%K6eok`Y()R_Zd{3D|-10#tCi^*8(Ssicch4RpB`x80GOxV`lU6FF zhim!IpDku`46iSUjPH;jRZJBW5EG<&M#LqSC=)1~8fATKRMS%(14QLH=phI7+wPf9 zTlA9y36vV1a9dYyJMhLRa9fy!aJu2hCy^fdW#W0oV&>-s7kAuzWO8dg;$!A2qx8z2 zvImFcOeApk5yS_ycjBzov7GnFTk55u_Iy{ecP)Tt?7n-@-m2Lr<0{fHlcQXBNp^6^ z(VM-5o_v?K)M9@hJy5ScYMw%Ek=BOYCx|NTzCraj#amF}>I3Jwyy{O{(~NkzapZZD z73J)4Uw~#E<9YK+gNFi|mQHLj&_Zf?>4QK43F1-)S?OlMA`o%Rh0#s(i>VWQdx zFO05v`%8-SgTme9qataYr|E9frwp?xTYn^t~FF^`^=%|SiawKXjWoaAHN2Y16I zd?ltmiy*%q;IrGT_ae1VNIZcsadAWdwZI-T;7=yvchO`@-G_0J@$^>v9WEha*;)kk z&PlS78evqm9l6q`@ouc}=-LuX0`8i+T#HhpTrXU1)FqD>*UF~8b0(E*K=`FIq=PP9 z-(Q#Bf*@&%*Rw5m*}svynNO=cIGDshyj1Xq8W2D2fIX(g0Ed$mDPpKZ0s=wP5v3O> zC7}osC{EZNu0;b_o9lpvAbS2BhQZPumuhN2=OO}YQXsj-^2ABumzDfk@{f7D0mzsD zMxX;k6eoYjIB?oIVfoi0VGTGZf1E9MjWofavRH~x2aGLc3p{`@V6lMl0JJGgxG<_U zba?i8uil~uz93E+B7!0{hQkP;bHzJvKSP|q8_hNXC=m>D0PI^|+o1Rr4n715wc{5k)U5pVD=U?LD-^uNmNPp)<wuO_-*BrVh2|a5OMqDbibwD3 z3su23fP`0;=ujN-LZfx6WD$Y&A|Yjv2FCK8Vd=rlt-+;x`a>``kt9$uspIE1s7sK( z1X80SN&(NxxQUR-hoTB*pkOBT9q7VLs$ z!lmy~47sb>S(RS*gLsXf={Q`CaZrun`YItDJ)%NUs;tO#hj>G zeQD?Y=wkvh;3&%ld0aSWQ!+Z4#m#PbGZv)B4jeEE{F=+fWX`g1xjye* zeG^N{HLg96wUTLCEM_YNSt3~^!?B>9(Ayh4={S`X(lY@eW?WTZpFpC6)xAcRIZ=+X zm%DsnBk7>ZCQ(r6oFY3=F273atKQCfz)vtlYH>heH zyNc}4l2vYQpDG|W!C6}e!Ja*F@2KfZD4K7FxLz^gVZU3Ve8=EtQc$Kn2V#@ta-Kxj z!4TUe_q#YRDJ$jI!|6&dE8a`q^`ei>jVJBH%blU+(34+BK)tUPR`^QB>-XO>(jJ0sR>#@WiLSyb` z7zyin)DkBbdsou~8qU95eOnLA^-$z0z8h=5Yu;1JZep@}4fy%W?OoBH+zz1Y47yuE zD#|80I#AF)n2m;)ax!ht&F5OGp*Jj3pCnv00VfZo8MsPZTV25NnfpbhReWwtzMMTG zmJ>;6MXxzmaDC_R0vfq49Oma$C}ybn2PPtVXKtzupn*+BxOH)156J1pw%SP9Mq5+7 zzaT|=J!b{(5*%u7sLZ#F*8NO9)#_s*wmpb#hnli@DOJ|)d3(*@dP#TK(#cST2!&V7 zjsiKZ0{eQRL#I9z8!0R52$vJWv|H{-*s#1O5C|X68)|LO;$+y%SL(&07k5!dV+<5 z3!aPvMhuLK8@a+PTHfhjy@gdWUNxBEBtCZYNg?se0;aYnmD*=erDx zU2|SKq7Tf{67v;k>_jZ}`Y25?J=$CwXCpe*JPY1Y6!TsU5bR@}+pfNsA`lcSXN7WN z4^><7@*`yp4znp~JFIMm*o8QgE%N-qNWMnj3m=$MqhL`OIUb&Q@l&s_P$xeg7TuAcJ5gS~?75M=TyqoID`yu9N`O}SJc`A|G@xd+xw z^mGMi%A5>PIgOQV1iZr@bG|wQY6K$Kre;iQ|D*oqehT)}O8v|POK5@wH~jNozX)`; z??dn5XA=j=q$$Ai`5O4QL&Q_Q`8KvZhypry>b?XIJIUagtIoJ&dAJ({D|vpm9&c|` zV_6fl;ZWy>Q`ch@X!&hf>)i9=z3)4)cS?R6~VLjl`mrLW*eer9v}nZ zpv2ME8(MpFJzWa@-(%bh`bpBhiwAem(9+C4mizC#52LQ8Svf;Tve`u~)UD7^41-l^ z>u|jNBypS~ff*$!U+}ly8gR&v*bIS(9*eI{47cBJz#}AoS9uq57fYbJ0~zS&VeH2h zF#Ayh#m{PVV|m3~4iZdK(m*0RPq}Tby?LF$I6caGvuo~$Ba43bl1Q}!j97sKnd;t& z9_B{$s^HaptIIM4=82a(css8ghwiKQEFYUkWJs>*J4nQny#r?vR|!DmNbD@aWm7*cZcn0C#9{&9$szFE5Ww>&POh;crtq#=ks<+Wsh0qhjXt1QD~eFpJ5() z!Mw|Cj&E6El$^uPopM+2V7k3pke*8#*hnqs8O~uCy~D2*?4sx?$=2tEV=*@~3JDv+ zPtI2WpzC1W2U%aIOv>>&qXiSOYbnNZ4#77McpN!Ou|*EToMMWIhojocl)F!y%!c%Y zrq)WmEi3Uc;OjXAD3^YQQ;&W5^ZU~tZQhO)xlBN4Um1cSf<;S|4K`~=Zcn!5#&LZHNa?m7Oze79 z(xb1Ldg1EovNg?)P3+1SG--JJUQTDA3HZ8=T;F?}0oa>zRVI7RlEOq`4fE5>k_DGx zG)H}pUCzPqO}bgC+3W0VJG-q8PY}4gBJ?CY2yYXQ%^pP$ zgU0R=8}CGBxL-(1Rv;2U)m9ZKi}C>=;(i$;l@rpK=197~uutvvxFJ<$A{@`%%nIPW z2LtpyrfEkYpPLT@>fSbeb*Jg`gHU)J$HvSv^dF0Q!NMvY!t(4`mmT-xVnE1}pu0>rIHdqGk zlfSBpZq>X7oiN_^cL~3%PaKQ78%(}cffoBd``7zs@s&7Ow9jQ#U4IWT8UBP+<)4Nv zlnQI+B`K?V%>pz9z@$}wUv1o_vy1H``@N3&tC?Pol~jE^g4G0`d0MrK8L)WV?|5m| z#WLmV_`5D0Ig{@ngQt=1r#O7=RoZBzW&-(HdESlMvWjq5s1=>hTiZ!pYb3?<$>LXM zQmt7}1G+V~JOi0w5V}8>_$@`Pu7H`|icDsh@q8#Xgz|kr*Sh0@zl7*~+0(--JkF(B z4D^NzI(!IN^*fsOys};7?H-7DvY?N_$GWJkzS8q=PHX$=)w0rLLzi{jIM4zVZH3jAV;DIah@RmYJ16zIaI zKTMz^(IXmsmPlf(?qH-gmn>q;GE zYaRzXon1HJpV+^w!<_DU(M@Cye*3%jC`(f~bV~3+V3RN;L<-_#e(702##Ma`H?E-)w_#%WlWcrwC2%eQF&y z6i_2_AnUUl>A0@;dhRED9+?{0r6~P#rGPTUaZzBuTK_k^pGZFE&`BomG95}&1;)BI zyh2o;u2~Bmmy#^Po?B9eMh)k}-2!1VfB{4UwXE6z+bsvgPz21M52*ir`d)q|N9ia- z_8IY#=q(yAQya=1;l@8OXnT=(yj1Lq`dZy&jF7flcWkG;q3cf>8#w+FO*_wT89a;~ z7p|;lh2PdAFR<0VmT|6jql7OCU2slCt^~2mR6h@?DXwp9E5`_=J( zUejgwx(t$(gDk(NV!#a&?NdCy15Umie#Lp&qG6N~(HNh-m)V^^%IN!?zZL-Uz_4q2 zwpP6Hm|HhI_iDb*kPhta!ut3$F9^Yk$w|?A0Sk}-fX;O&XoCm?ff*Hm#Q>546b{5t zu(qWrL?R8vinI`eLckW0sa2xNyCU2~I*Urc!3xUD({gVs>sL;vTC*Id*7McLYm+!l z#0dW-Ls0@EyQVD{^7HP^-p5~av6gOdn{2PufS8A|>o|6&V~EGGrXeVZMK-l*ryYF- z*S3qEjdh$K3Pf%O5o-)Dh@mwGxq_0{^zCgrT9b3AkVc^LMqIwp^mc;7P=%@+F1NP1mE(|C^uz4q8)anvF8=zh39juVh&+<`sz26JZyCqw^uT1m4 z^P75D8Q3JM3#Yyo>o0roe3QY=LX8_l^yumnC%0JK!tK1`ydV`HB<8;w9nYA)0j3zv zcT8g*l+Mjaizp2_VI*Nd%x>l789<^akz%A>H5%uu$-RtcV{1zk@4C8O6VM}z* ze8C#v^2%qcq1-`U;7V?_yquH-<~!TCESB6p@^3`)-VhD9Pe}IZluNWXYmqhH(&6=o zx{qe>w>enM_FF9Nd`%hjfc?A>@OA5h_%xd0@pQtuqxTci=bk;2slDlz(f9d3vAKAV z7=f+{O2trwAp^ZhDGl`v4SDR6V3d64eko1@0AxebC3lb@U8UPJftBQ1j2wPg0DX{oNU^WyI=Wo;rn`lz$ zyjHtFbt%M;tVF&na|8<-*{y)2G$cjElU-&2@eJd&1$ROo0F(Midi)I>m?5eqDr*CG zNU$5@uD}HZBtDqksAU0m6{St#lp9l0YzlQn5DE**y$ABd8r~%p$SdcJ>fm4qF@zFx z5n(%DF#||n;|wDT+j7GRh4my0_M7npOJf9m$Q)i8^o>RFNEyfnj)sY<`pHl+0QxGz zDi(^spdzv%6}D%!7URyNyrd#Ui3ZA^L$@Tn zpapZ)BW#a<8&B5&#!K zEdX7RQSru<_+Vt%Q)op@T=W210yc`hXoGq2n=LUAcTO)0s#nu(gE#3N2P)+V6eD=Py`r^6zbBJ znrl!82t}j~Kt?U;Pdv^D!Hh^SAtJyaz~UWp#;UIvKS2E$u5jATB%Ku`s0(0m^Fo-fbn~aXWmxb{|t%t=frQuRAUT zZDJ{e*wf8|$UgK?M+aV&d&2g6HY_-Nk5@`_!|-W>1IvYligKs-z8FDkU%z|EPPT)J#QLU`NhAV=!A$J!Q8`1g73iI2*zk zw$6p6aw93Mkxgfter}1v@ixUT8)hVCUv-74>!BNdhk3kcwnxbBEF?L6X2gDHIn?Kp zcO_7pXWnYyZgxyxh_t*yhqvlWb{*nk=xY#No>9D)jpiS4){VD0iVY`s#&GNP6W-Ec z=C7aIt~=SIRL8X29%p^HQlzhJPqdrbM(kf$U&;-{?e_+v9b4x0RO%V0)XP>XB5}yY z_0NLmc`xdYkvlYcGnz^LYWfp1$4H&;^?-g>YnR?pw-0{FFIgD>KR=)Y>irOZz#Z<| z#d!N&7xT|`AVfLttmbL2)`fRr)v6@lQ6h;z9~(W1IVzep&7}oe*yG0|Ss${i-kQ%$ zN?FW19Db|0c_Ic1NDh|3^rfO_$lfwHa~!h6bX04Ig$~6}L&p8!<&8%1!Ngez_^>!w zXy!*;MlPM)PFac#y|71_#`BbCor(8aC$>BX&W*}*bez0vZ_7O5hke5{qK7$T%)`lm zE*Cf^hkajqiVS;>^%47FqXhEvykx(-A`%^0#Y@bGzUlh+=s%qLkNVnhTv z2B`y=)Qkmt-Ds^*zjp^*XP!@PpB&=ELqc23>dO8U!`Tqoq~{dmpVxa7N5d|5z1K#Q zp4RwX;C4sUo^d4bs8BVkKIvIKcF|^%%fVA1wWE?@MuZqL`DBz{qTUlp*lHhs=HVU)??e^w5|h znT7+BnABOC zDvv(Hr6KioWZTOL>!p#0PItdlOG>r0b)6yJ7CQXZKJ2{Q{q>{XsLIBsVg@@cE;{y$SZ}&;@?|=oIVy#mnKv@4bI^P4t4mjZ92oI|5J#?1DvW2zbPSHO(?!z#?`(6$U^hkl6M-(0Rd9IlN?-3t-+3C-= zBPEcVsJ~W%Etc!IGu{d<^d>XUs(wDNIe1NC_v=*H*`rRwMX+dQtVDGtxn7=+JyZ~k zY%*Sq6DUMm#!%$3-ia}#3psdR8LJ^N6SMDO7kR7UcAWD^-;t^G&sjJ4)_v`Nwo#4C zHGYfS;OMkghraLY_1nhF!OG_3`W%h>)NnU1ObRuHu212otN90MD3IYjNMe{`2<$ z;6s z-tZ`7V>}cm!ea-Jx`@#(MX(lH;eqv&JZp$}?)u+9WduBkqtnB0agC)K8^T~U} z@%?|KAAMEJijd+hFsh~*4r>gt1oBG$rNSyv^w8x?il%PtsM`rW^}HaN^Ryx1^UfzH zGnW!f;Pw~a3~unA$p?@H&Ro|wzZWBg*uGQ}tFfSjt|{ZG#pg{2m=q;A*eJ?Yb4%|J zY~2=LNrvaOJq{$*c)a6WXe0Z=>((irw^F=VSMGgIIhD#oz{)=tmaj9^w<~vhlx$&3 zuf1cZ&i$VZphp9oLA$YJzQ}i8ABMkuIpmDg?{i zw!`B%&Uc4T*|U1%dik2F!O2jdVV@5vH7=Ukce_W|=t(W@S04n!)u7$=LU4-MHi|Jv zz2#`Q3ph-p;$cgxpEU%%jPbf5F+n}yd*O!l(0E5#>VO%J0G?)VSOrLf$A`EMTuXWrHq>Dq`Al>1iq)FZ(FEzPd;EUnqrmltarUnOwa4!2kb!(ylDj^+=Ee2H_oLt zI;wT~tetSsNU~^g7_N=s21tlggxcU_J9w@Dh%I+UMD$CqQ2Q5X3Sj}F7<%+-)WHLB zf^S(HMf}JqpiN>pwGb={TX(plWrkpAUZm0V@ao{~^>;dK)oClxMg$LAS-}(5miG*i zY6faG8}4)IYUL0~3zc5Gcvj9bn&Gp-8qB#v{uRexE@K?Oyz}X0?aBF*dbz^uGj@}# z7Cr{f2Aoi zR01*lTTZWHcBEvP%sdy*HD8`s4AObC6P6&ILEd}47v3+cb@;dmvFp5n!zRB5&RPGx z{@mvUYEOgr&-bfYv|n(3q2-d5{hL%B*{h((AMS^vsD7o8Mml^x6$h69zit?cYt?+9(_+~2`YONr-R;b zXyUX+5}RpQWc+;xu;;ap-CH&9bVe5TJ&x(&v~9mluV@-3+(V0A*(sPT_nD3p)57d0 z3ZFp5cvP@{MNe|!Qhc=$#1`(fXCEc@tF%fM!QJYU&tT(^!d9nV7f+Z_CDSqfO!fW2 z?EO$HBl?cVz_QLSnb*Qi;dbKj{=*?yo;NuBC~VF%D<2o5Jq=YUK2AxgDi;o{^DBz3 z2T~10*>T89?B}NY$)x2!8I>D%xP_v} zSZ-nFNw{XqN<4Iz z?^lCd@^vUYoeSaI9o*A!T_dgtYqrp4HO^1H9_*WI_5#)m{Fn3x^nCap{{=sj;s+NH zsr(|J8346}ih3ackPtoS0MsJ|OQ0Fy#i0*s0{%2d1|*7{x{ND}aGilp%ANDrEUC-_ zSPv<}HNzkbZ^aJYMITxz?L`Ar5guOWM@L5QYYVq9i|105K@7NW zm|xkafh~A;JALGqCsl%{c1d?=baPULUlJwDNrGM}b)O z$uwBHTp|{|CYN3nxpe5g-xM5R;f3IEUhioTCDRbmuaKio9cXZ(k??TLP!eO!(T^#3 z#>N-I{@+4-rftIP!UB={FT*r?p?+k{+qL`r!Snb0_!s!?_nN-PbB?ckeuC%R`WI(O zS^88xztEoxt7GP#Cm_4Psf>4It+e0S zJ!1CYW5|lcKk)s)A6rHJUuX5wSwg&B{4{SJ2hZdcAP{!tw+#BNs{DUqIR^kh%!E|U zr#5}uH}`R`(|(GZ^Uj~I+(Z{V{kOckXj`6E%x%4*>qjVmM&tlf2&LYQpA~d zkMc3EM>=qMdtkGz^u6^!NpVz@svT*#r{$WcY;msRxgTcTv%D_z-6n4m@e4)Dd9a+L z_aAC3qs9hd&3?A$quX6e%;4-Xx%Ub;qTXwSpFh>VwF;DWPA&w;)HZN9a*z3|1M8msB+Hq#rX~5(&tpE zg7}lxnjamSccHLjrCpVlTi=xs+y#4h4lOyO>t}iiEoW8Z^LeJT$Fx4}K53WB^TK=}nGkd8UxjFr zR-VgywbMK=PwT<+=fDrpfCvDH{Lzk&lTV)RKr|u&E+ln0okIH<&-mYkeZ9P$0@LK` ztNw3eL!CH3dwJ#0XfST_5g&r%^gacj+u-_1hAy$q)J#6tr|JWI`TU?0;)o{vc>zEb zJ=%AKyQ|1oJ0FGk%dPsG0L5dNT0M^0$ zxGUNdgagpPaSiw0(A+ZUK(f{>g2{+Jp%&AjMd) zf?SA51qp~~RPVq5&I&H5dD`pkVh0>VJYXHCD;j|W_|bUTSP1*5398f=QFFZ^7=#xp z6!*T{a!5-e`bS$mstbB6@-Y_3tOI2te8K^o27qDj;DgkP0ca*{T{bn@G{W3oWcPC* z1V2V`r^^xe<}g4x(1D4MLJ_b7tF#Gk-gHG19DetY3Hqpw+QlJ(7@|NQUIhWk`_Mmw z@O#=0zp?lEJ*|ptL&u1R5EF891>}s1w0yz+7>tNhjEkZ&T?R<%q%i4K%8_d^4db7+ zy$=o@)FTyLn9@nJzCE4tR36!K;367Dmuki86JdYZpNl(-4w2V-$(Iiq1kv1dlaz#x8DD z^FodkE%(y=5yy6T>eHtY#`fGajV573HAdh8a&}B($eWIVE&D@A4ylA87*3xwl)9r` zi-o`oVSBeWZ-v`>Z@!_@D3jV zyK1H=_m#Pp0tMFd0RdG2Ut<9*pn8RiZETvGYi7)}wHE1Zx*k*yjZ0r!*AX@&h#L?Q zw$R&ut|pBE`&xU9s@)`Wx4k^o4)>K&8<8Z(=yMfkQ!pDvxLF54ao=HkqEy`NE?nnP zL7%j})aLZ)i-b4r5aI(ZKzFrDq{*2V?h~%SduW~DI)ZTh(F9gQ;RT3M`fEHb?{@N+ zm1?X#Hto`l@{z<|AKpj`T=hfnfRolzo{^tgvF_g5y^SA{)iL+PtfRdHZLuEbr6zh7 zajnj-E%(s`KI9)g95);tu;Mo$xbdLL+1^e7oEB2xpMA(8X90a06faLBhjuOIespMT zmv9~lMweGy7SoENw*q$ot<@tzKO4#wS9odsKL9;AUt{RT+5e`3kK5$~r^Jp0a&MLw0Wq5#DtvnX9yY4J_NM^4?rAj#IQ{$du>Pua(KoNb zAqm-svK2hum0^AE8!lSTFD*yV->}1eckKa2?$b_!2U3~Dc~%JE^oeB^eb@t}Sbv@1YO{*a>6q?cTd>S2*+77MMZ)uS z{6lF62CV`d%`1gX%<6U%N7Pnr?RU;)oFH=YS1X5r_NUG=c=rzV^P4rC_TCl2PVgS- zLQ2n`=p!+FIm5B~A3gwWYmbTgaQVym_E`tNV>6;tYF6#z8dH=bj<77+y!~eF^Uc2a zqVWXXX9?rgD*;MD$40MsG#ra|>GDU}KHqt|QJu;q?Ov_+*sH<90&;%QauGNytIkk& z)`W@71K7KAn2UQ5Nvs|N zBQriT3Ug}?Q0m@UC{jzpo< zVBu|8_8v#A9_pvDTYKR5sYFPwfksDt%IHy`GHMFX6w+nwhlk_e^Ye#n*`E2gc^NpA6{>F_&C9X37QQ|cW`a=2kNCY!Ah2aVho5S1F>+o5sO z@po18hRa@#FI4l(d`h|H<2&anU82Q2B9VMabypu3c0gg9#yp}WZsNZ@w<;ORO_CWj zjafMKu_(;xa72NPEezGEijncxDK%e5tB9Ih{odG}0&p^)iq+j&k9MU!Y(WPM27YdX z!>5z5TFDqP^tU&B$`Cnb4D9AAt)jlG2%kZhLrQs)6V>`&QY_4IV#L^B*~8si`6))b zAWBd@byy{YyCW`eIeWWb7Wr;XeO~Q)?A3TegIwK(<3@Yh9L4h6?inw4#m+C9JY2~b zf^nyVeudAZ?}4eeJsE06e|Um$Vy#fF&B6_Mx4YC*n+_#NYVoT?XJ3?&C@8)(N;Wt{ zRE%tBt{jXhhmMVv3o`uupvwvHK6|1G6e|hc5iXAo0m)p(JoLI11$5e6?|~4{PJ?t% z_uT1JcKNGEylJ!R74nMbldQhpa=V2`VTCj`G#bqbFIX1kOkPO^V2<*8fOgNDVE(E) z=ODU%^%a?wR&87#a3(REYn2zoVQXl6x9gQ z^@7effMY&Iag(X{U)INc8h0!=4;F_!y?{eLw>aJ67w-`Amp<+1CCFx+~SCt(%i0;_lQ{S3gcwuv{Mp3<|uL7 zd*>#H#x=$wGYrz6PfNKSGi8kAC@c>+3U+bX0<}VNlHRSy&1qzHY4;o5$MeSLtT9fw zOv~8Ik7-N<--<>0qUT_7uv5=*eDB>3HDRv-_DP4#Y0{iAhHAII`>Ar>8|rc~mj^X- z&(^6{V9J6b6YhtPh1gmXgX>Is=&t~#+0_%hg&sd2o#;ZkA9P`|sp)FHv^(BcNe8!+ z!)^3%=t04srxl%7ARAsAhm%N%SP2PgMyNeo#or7PyS7rLMrkRvYR`xjp%rRX)vn#5 z)nV2wI&7`ldn=0Ce7^g0b8gOg&+mQD^E{G}2chm(@Gk15_v@Zm#M-=9jM=sIlNKdg z<|>?9?D2*D`*%OrV7s^Xb(6r$*S()DrfmaV-OuLaeU6*%F{a7OT4oyw3en-kl7SnY zpC#FDM(Ou>I2k&1t3NdHisRD8P9!6ov^I>Y2}aW&J5Fw!M}5on(XO=J@NOSy$?;L` zZO{91_GNBDE5$EEQ?X&&CX#nli0;oeou{ZNT2h|XBy3vp_|+3cGuh-)442#*cx9O0h74r69In1DRkqnYP@H_|WXohQ>QKf%IYxen z`Djx34qW=p)@VMn^1cS`$B zNcXG2`=~{aW!NMwWBR$)k8$h!5BK7K|0sRFPtNU}NA(7Kn}$fV-__0t!B^4MCJ#%K(6cS5QK=!f zyy)MR=RblP6J!^M#o}}p-6*%WxG~_B}X5-zj@{`JunM;SKh3-LUl2^%4yyTCK z(c;LEVP=Us0f+BjoYw>GWg=Y+wL%7>KRM5+bMYo_{Yt#v^_t09^`>pxd9&!9M`4kB zVzVX5^C4@pI#0t~#Z%^$ZV_MpwcEOG;;;p&gxZcCCMQG2a@2f-+ISo9rU$&N^nJTW zb8{`qnEN;1i3#Dc*O#>Zt#|G*uctrfs$K%^&sxG&Y)23mn<)lX$ZGpi>F{ev9MgAo zVW#bi!X?iW)#>l&^11(BzI?D0(0Mb@*XA1gt4QQ3DQ;Y+!UfYpM`D2brWQQ={qfh2 z$vMew+v4BF>FL4#Qty61G;y5OYa>&Ig>?@%_v+FQEQRZUZguZsZj$oZ41FB**kHL1 z@AvtVwWNFQ_48@d$1HD2Iq@cPZ8|1dz6@W@f5VnwxTQ$dVw#u>=ur8lzPoUJ{#}3L(P^q(zH#^^K{g12om%SJhz-J zH!?hvS1pN#fv>Oab1N5z1~CqyI&%)q>sgk?4~*Vgw7;SD&6iWi_h?-egXvCV(Y?;s zWdx(T{yU9m@c`ML8x7f2IPbj;-T^Y(T}#{6l?Au zQ__q|;?PLn{=3!pRcqFg{}AHtgA4Hm{cag|Qv3Yqb#qb3Rulg;b*sEz@TkzLzc|Wd z+oY+Gsphn!aq2E^E#^AI4!rD1uQsA@zlJcb zhKCGe^0`vy2mUks;-nolydmA0Grm61jD|rD0xigz9kr3)y{l`!FWg?BJU%dZvp;*~ zm2|tt`N3yL;j@ER89kDAq3i=)3ZD|UU%)=tmU}%iu7>7N_2Rl@^ z;z@ox1MuD2ZW4t<7mpKU=y>)kiDNhg=eR>b+zl1l^)u|clN1|#yRd9QJny}&snt}V z=JyQipjDH~`GCo=LK%A6DzBYpwNI^A7E~Q-7zRQNZ`4$U$4%dFb3R#nxP4vf_ohOf z&dXPfn0YQP_dCJYKR+VfTBvI7oys8+xrVdueEM}&EZe~0%HI`f&miQL(n&B;b1ESI zhbn!)X78H`yF=sO4epo8UdUd#65UtN&;FL{`gZeiEoY&>0_`G5%z%j39aWy$?Rx`l z$3=D-O%L=Eowt2lpM|)vfVMn$m_I!Z@Op+h3ziyB%78z61Fy~Wv`UK$e)RpBQ25bn z7cH5eMZ~#49{O#3%7zJ>gM6)@v<(%l^qx>+Z)r&=qUY z*A%62((uRxmHBb-O%Nl=Ik;{2-!+Am+Nr7JdDZr{_|+F)8s8nhf6C*O7WSlv>F=n) z221la7R-!GbY3jS-0n+QsaPH23#Z`J1$3zCQADQaV7*I`=90bsvsNZ+$14v9+|K^g zZt@kyuHRvB%?gGb->K>5`>Ni%>`DI~Ky6#N;4(^uxc`*>5=2oexQTfuNaq^0nhi}?w&iQ<;G|5DD(2=H5si(=%t=3 zloe6spxH|w(SjY-Gp@^Nml~VGu$KdrXxaf(GQN5Nn4)Vv;Thlr0hlj(IE)mnf^``? z-Z01sDE5VXVrc#&30CiwMRzF(u7qBqLwJXhVkJlo=%)(C^iaGN;lF3$^F4=ZG=OVX z3TV6!)w(U?O$K%mBtcM=IiN8_@IA%{nNun8BYCL#JMBqQ{8D82o2Z{cmb1z{!(kE2 zrp*gC(NY|=8ZV16ZiJlgfD1DKV=3Pz?JCAN$)goZp<|7E$yu3DEXq-k@B?^4asabD zAu|K&y9t>%E_;^QXK{5A4A2B$$33Ze`77x{=`S~|_k@G;H*+VG)g;(6bTO?OX+!TxlTR+YBY-E3OM znTU9}eAPqfxlYMlQAjhNO^ZNU0|2gij?TB2(_C4#`Q{KU-Ux@k*@uPxWfC_DeTzAT zQlFb}Fp_?g3r7jV#yfl)*qapWc@oil)ivM|l&kjA((T>C z)=Fd4F7_Qmov0}m#%a2P729#^CSsukc+^QCl;#P<2!Iz*murymfO_}P}tt>vMwuBxH%(#V5nZ)!Qu6*c*I2jU*g z_~wX(SiNKohz{Uv#3%T6D5)1*FlQfI0w*{JBeAIKFDKpH9t%13dh!MdKE2Kji$K6N zNK!OoXH=dP`JHnkTJ03sXk$|L4CR4!XF4dIP)p@yEVZ6CKdY)ofAREbT(bP(ey;6q zbB*uPoWkCdh-u`_yiW2e!I-8Iql8|t)pLJBGxXza(#>HhA+aI;IN8^OZJyXWD+3^3 zOXqJa8;(mq622G<`!An{ANdoaR?J=7Fv71vY?f9gzrQe zRqx^b0gkviBq=)qJmUc-1(EGbu8wCydG|p?-@tu(!k+WvG>G>D}!N(vCL|` z1xvg_sh?`uQ|z-<$7h`dh-eh43onSJgKbVpMKj1qEaPs}S zpZ~S@qIyrN=}-kJtx3CbvmFWuyOXhHk0^W zv$!9Y5|KCtS>x{*fpTmj#9qLh>I4i4zO=#tLZX5BAdb-DuQ-)cYIOl8%@(|Z!kmHiW&{$>>4Vkjk|;UXHd;n&>TP8hx7DdefYUQKRP3stF)g zEYtCAG_R=@hAb=~nwby0Lh0wRS}*%bBe{Y1)}pIX^fo(Q$?PKY{R}i@D^#N;B@}3g z{>Ep9)wz+!9K8FaT3)FziTIIKn1W}NO@i{GOxSgjX>$0`A{ef&cL>!b9v!aCu-5pa zb~*AxBCe@Nc2tUb({@hj_Fn}+Or0PCsYK&p2}T0fC{3r2`{9rmDxR3EX&cHJ6c=lIqz1@2l>{r&-re~ zK8iHP%b;u6(OT4A`nS^0uO9rZ#{Z`I!u}$fyC%4RI-;!Vs^78fd?*sm2 zlOK(iu7h}9e=hV=>!iUq_bq&UFfp+;E?nF1;2CxQ{Aq`1NG-iJBXy)XqD9P z>1W%IHTciPislm~Rq(xY6E_q5q~~lo(%CwBo@11Xr4k##E=5m-~k$&TOBkuRKH*9y8J`Q6y^XP zzLdj68*f(;*|r0{==E(QTu)$}fi5Z9(sx>nR`+e)cZMGaTs!cDF!b@f;2fi_v&2^p zsZ?^eG4o&qt;@AXf4sN`ByiQ12QhXA9`YGpw0t|5bJ-1^k5T5M0YgF6#-klzg}mE0 zCBgdgbv1VX)=p%W-^f z00r4^BkQm`=}>tTL>DZFFlN*(%&L$=pkGNuIp*Z&84naZ&5*Bv&yv7sK9rTP4yaqr z8(4c2r>+c6257!V89>P?@I-VUSeWv_8DPZags%mz5NY_pJR87C?Wu8xn~W(Gh1$V* znh@u-%=z2xeM6wNSeTn!@YUJ(EIL56Bs>r&9{(93V7Y>m ziN`?l0AZD_ntaU5Ed6tDrW4>a|MK$J@x$ez$xMdA!6u;fe*=`;!c^`@`>*}621u`n zi9DvTuE^hB$)wDxLSNepTz7!s4n_ASU!WbH%MYTFOGOXC%knf|BQ0Hp~kA%`Bi3*L@iNCSaso zjr!x>VR+p!HTRJR`<=hToworiF0uO8y6im9EDrsSw`Zuv9&i-WGLmu3v_h)!lKR&; zZh&jXWT@CP6D83WgjW5SH>tnw5A!431#sKy^cLQxvkq--X~PRewiPpNW@n~;O#esFEvM3un=SB1e*AO5v+wEeN_0~Mp&5}%VNti!#?Z;e zQUDFAIvtuF7wR}0hDB9F#b{ae5@sEi^Loo_EGN;adK_?rXq=(B4tay#Ne0%>Irf4E z%vEhFljGMlTJ6c^1c|FiTKd%2&`!{d05!%Xnw%vxSWI&c#w z?yZSqp{W?%mCVnT`PO~u`j`nd`MU~G9oFi5YtpC?=Q4Ds00Ef<==rr$BOiw)y+0HC zb3LT~hXQn`l~enlvFQ$5uyA%E*bxrj&5ZfgB}&1ZmWCZvX#kHiiM+AV@F9eX1|V)Z z9~7~WNDLajWeOt8u+^J>Bb%{zS}sSExJe&v7=E$@V#M}3Zq^*+h4u%|5Yyl%$sjDG z_6i7lj~&k{5Fag2gmB{98*}7SyALw|&yjlJ4!taXO|=myZ#8N-Rg0Y?yiO&2&OK49 z{Tm)(IlJY$5wQ0#K*-hf8GZAip3ygQvCL>$Ok#>U(WvV$U$@jV$p+bxQmhw|sXCzD zW6QJqFU{TiX*9V{ZUjDZcopUFB~H>zz)HdKJKy4;J$oajOg^s^3_aQYXTJKrxYjgP zo6OQT5*7R`miH^sg~Y}HOS-M^6qEjj0c#q=oJaUK$E)D?$%C^rX`EtSjYo{*Z8cHx zl~Df8V1~83Vp8h%E>*cK+*+UDa3VMo=$NR}Vny$ettC>Lzf5>Q1LPL@IS{5%3>pOE z5@4n=63F7kYG1>2=ko}Cl7aPlD@#f8^!I<}2|K+?&$PU6u9WA7Am|IC7FwBLm=92H zMJ4Hf`Hx*?twNX<#d#Mycx74ssI$Z}F;f8R9Za`SEV@;oHoT5Bvo1!QT*J%{r6*M^ zVYLcCe)fFFEa6aFvnoiw7XHC&EJVtckNHyAlRgM2@qli&H&fsQ{}lmQ!fa;TIF|9Z z#J7K>d9huv;vbP;q-IhzGWjG^pF%vvTHCRA^*nH=hULj(VDtohIpD0vc1qZ!k@Q-qo2U$ z5(*C64YyfQ*SZqoj0az{EgWY9>|4AvZSWhNRKkI05(f=PE-E>W$8p2UXVabDUy4+& z{Ej7U$7D^#$$nQ8FOQszKVzoN1}~8qy)KYU%os$kArm;*xH}{GNg*%GQgN-`lav4eGe{~Vw7Tuy!--xvZDVKIreZTEvY>W?PMdzM;Ay*=PU4r znN@)6b}eZGYt8T8f@Pb?hDSVeC-TB%T}h$rS6CJ3q^O+hp0wOm#xgpo@qp5;sI@A+_68=S@VoJ&l ze99-}C9D-BT3kombz?6{^`Cqi7Zuq!GSDN>0jXfbm54Y zk9^*_l^#-Q0V(nW#ew_E4bY5vQhx8qJ`PM{+W|GbK@=IeSO4lA>8&VFs%(oVfJsYO z>J~aG(WF%y&3~{!F%S|oM~AdOgE`CO+v`-UMk$&2Yv7T-II~d_ ziQ7?_|Kr80M?^nQoyf%B0RF{rx}DE-U)!&sD9Ftg?O{TmJGoHk9nyS-D20R2v+@DD zg$kI1Db4!{)J3fN&QCe8KBO!GVpKhpv*WSjTLCo4gdde`EVlB>z&;Bt9W}}S6~~9t z=x~B){s3$A1HS^gX%D;egdKT&HE6YL?=>|(Z27x$N3M~{5WxG!o|(p5KxjdcdF3Bx z#Pwfkx$S>8Wlc?pd9aMd*6KXi%SY4bmX1#NbWI+0*N{%NGqJz5c*>GfGcXp)m8{)4 z(3GxF*b$$;2<)9j3}AV}tHYPEe<^6>Cl`p-1GFPgzV1@XK1pma%PO*RG&P-K&*MP8 zFP=)cZ))5z?qeD~admi9^k*QuP)DYA0BBuec70HU5~qP@7T}DniPsVnAzqFH_RkU6 z5SqeNVH#hiNjharJcXRY9nxBfh60!8Z^Itn7LwkmZe}?4k^zy1=@*&9VZR7dJ#XIV zYkgc*Pl=_5(D4<2aola0`lcUAks`idEMka%?qDP;l6fo@oHEws^#D$d)hwXG9u3)L zafM=*#kH~K<&GrpOVnE|Ge4S=?%}$&e=4LQl!0h{?vOj->hFp#fate^Kxivi@|q)^ zJM+wKS>{&fyE;+NAc@ACBO-qap94DSp7Y^bB*ob?bb!WK5Y}AE7*bGBe%I=TuS3L_ zo@>;{G|Jurx!H#mURT?>ZU)&ttR1aUy=U!Y1sshWCCW47?r2~wKkD>L`2as)^4k$i zH4s2DZUC%5gMQp$-iJiVssreW&^U7^%XeTiA7S#|9GM+&$)2#wcXr74$zF$iI2VTl zGNr?ktO0=YR%R?E$W~fdWmsKS8Y&K6Ou9=ArZWtT;1DObsk$9^RvQL{9IEw>3H_^K zJnf}7@5}kQ5m1+i;z^onu#Se)neM;J?iNyZF@s?0-_x$*sTj2%fU=WuiaWoGeC#*6 zLvLnU_l4q!rQx!m9h~8EJy=ac*h#S-?S&oiR|pPbNRK;>H#+I?U;&y-8QVDW*0fa-U^+6DVN^7u6{zw-#6&xk zz<=sxIf!|cN#KIRlg%Tpf%xWd@c+>91AzbRXW=TNQTz7XDO7z3rG9{tOEh@1@2-%V zx+rBt94DOfPgNPOREBP)0T{k%c~wK56m>IF>Nm_cRtDO3d26AL@M!?^F(#5WKq#qH zoF@JJ<~Nmlxt<+HoSR#VnnyWmGFy_d99cy=FGy>q^hg44z3I=;Jq~@%9;X^NR#Cd( zd?}82A~4T&Rab-Ly0$spsSVyQGv1>~N}TQeNL2Zqx}L34!N%!&m#xEMlKS+&==#t3 zPm*ZLI$qdl7Wg}OPxMgbk!D+7$iO-CD7xrNI#GYp0BQ=Y^VG-Hgs^f}+(hsMXJgBT zovsy}I9@^Yaw~S~JZT}hbW1+3G;$xFQy+aQYpd%M^i5u9KpF(mL^O@~aoFU%u~Spk z+|N~`5<4>p9+L@r5E9mfEy1?v8oT9 z1N<@ti#mF=XFR92HRc*r+^m=dtrKWJyis4JCPFnNS|k_nduuJ{I?<6=ppm7jLM7FT zcciD;qFfMCWq+{r9#@hm5bE^;Iq373rSL?|)_wH7r?o!}e)Y<sCprXhl~+c7Jd!H zdJPf^bQt>i+pGHunh-0b@$rf^`riHiNX*C1->5~;k}T0RQX#Y=JDDd?1al(>Z5q=owvLww-z-PV7Ev`YJ>#BgEcqihYp(_asE}yPEUtXxSb18> zw6MLs>zvUv_IQOR6j;=%dH3$s3>;+T`_vtr)?rnh44FF#V4;->{!%wXa~IO?@<}c& z$%wgoHnMCQBb5AL7ja{Sx=S)Ml(RQsPN(5%=QOA7sFZDc5kq-a^+eBIGj{LUkW;mU z2|6M#BPg~M<1awuJmlMb@S>ue_ZtXdV8Oig9%kGs+oFUHQog0Jb9yIGydaAt^uD#` z{eS>mvj?GIZ$)u(ifwM8-V)FIz||dA(cs zvwdFm1lS{9SIDqM+-}&RuKkh2xA4fxW8oiNoiVLY!6630PjsoWG{%g@f@~!>_de`C zF@a-t**-7bk36#yc_Z`)N;BnUE`Z0>>zPK0D?DX4*I*to4dN46qF|o&6cZ>ETA;En znlvUHQ;_i2KIc0g{0o5i3gGOxf`Z>@H6Fx9tKX=ZDMvzvkoYZKcr>%R7rV|qQ3QJh z|17Jfmprj{(uAB+Y=FdEvx3_2@r`k4R06%OftaScod#80q5|WK$RY^{5}=fDff1_bCtJ2q_RktAejHVc6HK8Vt`n$ej2cqb)xb{O}?Y`%@W_8j@agqagJ96(n@ zNiskxV!S$Ut%%M7*c3!gk|SwpmC3GA{HG0BuLld-s6JPNA-WB%^!Y?blOOTAi~rhs z4-{VUJws@Akn+8N~LZ{e9LWjBt{+Fg-0JYYn=p&aj5q=Lm zs2-#8alDm zRHN~;z0m$8)gFi-dW)+6X?2k_f$K?~HI?E}1v3dm-AtmGDuJR7;n#^;v{2hT4D$OJ zq`*xgOfk5){C#2V$UBqTrl`Dy$A4y4hIz%2Kj4$>f_`W;9wUt*=;WC}9)g&4_?v** zIlx5j(YRi}QW*nlI^Go9k8AEIqeg$Jt1E5;1@{a-LBRRU@_eFTn8*YEw>H89E9?(TAT_BI`Q3>cjPGXYp901Sr_o+v`TeI5hL zLZ@HK{b4KFpO17@54Jsn{>YFkHgIzIb1+irJGU-y)|8=MjQtvH79jIwr&jU(!rVu9 zBy*JuJ46h7w|3^kN9#7xF|h?W%i)449`Yv{-hiZbjXUu~rwDyrandpkUY!4Y<@pbr zaJ#0;@6sTj{mp0Kcq9Q4mc^Vi$)JP4u$(5%{8QzzQ>`mqB~>(_mM?~Ei|<~ zd>cc?tLpHxQxjQN+hu4ZJP|<HY%3%&9>Y{M^>`pyf;(RMyk*@WiAgh}ndr8$JgiWbQ(%#pHo zU2qRVrpZ}6M%#$rQHPi3vj|7t(9>-2%d?&7aCL3R_jcW5iRctARSsNV+p}7Ec;}T_ zkAVC|a*Qn%(6V49fd+NUbot&Bv3QU}mAJvwuSe?Qt*J9tp#Qnf&(l%<(6?CP6<9PG zf6d&Oc(m&#*Ve_*+_vBK^Y{AB`ebJjBYo-3ym?l<<~WI_9UK;T4OHK|3D^pIw0oa% z!*^Bcn)zzpWmHM-n8coAlE=Rl#))JsRo5SQ)039`6Q&I_uRV$UA ziAu8F5+^J$NN7Kpq0;Vj04_f#hUsA!DL|v$&@?`xO_{Wq5B%lAFINE680$#X2pwhw zxYA-dMzqSH)ueh&$11}1E6TJ*FAZYgV8GDK`A6jPD)utVQ*cGi5QIiHq~blWy@E%D z-S1Yz$9UHIoDSq)e?Q9^J^oiu?X_K%qtBRl(uZ3mWBHeObzGA@Uu*75F)yhKgWcz{ zR@a#|;u2X0d0S6T{sF{WqQUg?OU?X(Y=?CTxhiAbs6L}(M#DE^>7-qo(;UVksgazr zLl@=SIfg3V&Zx%e9U~7M>rT2{Rof;LlEUE1EyhDJGcR8b^qf~}X_{pvqD$d`Gj=Km zI5AxX<(O~2rVry76(AHl9ANGdj!p?Q%ZVt4>U?y;h0R(jAlCkqZmT`F7s~>L3(X9x zzDys;y!OR_TQT0|f7iNNAAQtTIeX5J5ySW$DixW1>MyJ&qrEuRJyhlgzoCsGhBC7R zRRL787VySZyQ5FUt)i{K_h|`gHj~hJK*EOX5LFf>wCc^U|Cuz~RfXn~F#S*&EK2P5 z`)3C{-Oq`sf1ST`7XbIWsY|cECM?%IE%j4<{V*2p_XjGKRt1GhHE9@4khTE-Wr zaZ8)Qn6n5m;%}GTPRC$W54l9y6;(+Xy}{pID2luomuTYJQR?2^4`^1Z;eG zTY(YD#OoZ2+<OsuD53-Gk z-27GMQOq&!nKy3ulwZC4JhaY7`u*;I_S;Ih`HkYz1g~8u?o!Smprna#RMfkz(3?!( zCo@;aLgyFnY#tmKRT{dB{eaJ7T)*!6EfeVMv>G3XvgH_RqIj8r9Pl1mEBR$NG$^x6lC&0_hwtfn{km96wAl)BUXt%y#@3{8d^TE zsuKw7CcPBb=oo@egsTi<9u4Tc_8N?LW%?I%U(;%!@NVx&_|nlOZ7}t~Fq)2{3lD;m zT?Dv=CBz*&*aZkQ;mI&P_NP?qxAQ+=ZXFUyXcNva=GTb04>0|LsSMS##0GmU@IYz9 zx*^^4$W^j8g**r}`f96_4#{_+N;CV@@(Sd3-LYCDi#p~B76wlI2-XEV%3Q~p-9{-p z@c4ECY{?PU8d734-6VSUGQ#RN>|&(rNLSpRIbr9v9OxbphWduaD;I#VprHtQdGF7F z&rLH^%NfM}7pYXngdj}*1V#~-hj&UXVgeR421pzG_)f5gd$fQ^K`HhXH2Ml0`UH+DmA0ov z#>0uUP?2D^S-AS!N-cGw2HdJZ1B_1Czyn^P>Fwi@{50kW3W#E}qK8#PKY^35)HuV^ zIB~pt0idN@ItD9H%>10H&|^m@GFk+P1rsP_aY(?=s^Jt6D^t|=dDZ&) z`&BuV1_FX%gt2mpCbUqQRnESw`hx?+H)FRF{4}KlLuAH|WVN1x0(LeC8Z zSYwBcVcxoMgE^4<*la`RN7za{OK)V(ewd4r^AAxhg%<`J18A@j#GnN?Ajo@QCx#ns z8V)h-nnFveAF$`%YT7AHe>Hc|OB~#ysz|MV+V7_8@dk5dx04;9L>nSc^PM6{Ri@)x|r!LLvKpuBJ zc--&f8&lu<-t(FrT8v9=WHv6#|C*hOUJVVq5yVkub0hT;#!1($N-m!DJ}YA+!plW9 zfk{a-uHEmtWFoNEtAAHTdJO5oo+#8F&FXsnX8%|5*emv|UdHl zE{M~-&g`cO;c`+}O*L2j_QGa3bouDPG!Ck2-^D=>Iw}Gi(w^)~jQF%*d4l+3EEj7f zELyx^Y{`i_fG-lo00*Sz)U&gP62c6*sa4y&n6=^et3NslZ@AUcda zIwRaZ%zu|K*w;GTZwhL%(t+gN9|ZImV37tp=%t{hSByT(Vuf)yYgQBbXM%vt zey9x<2CziMJJCf^-d?W|BISH8OLxh#`aJdD5LzLGkpiOb+zYf#UWm}e(osX zenA9Z!H7k`o%6c@=>2*eK;!vy3P^m3UtB8fTQ%I!p1Wq@&jlw~&`aUnr-r1>@UTDe zdqzd}LB9YqTgJT2dm72(OBX#3RI_37ap(Rs&y9E2&zfQ3B&Gu9Xk{b;nV+Kh4^RoiyFW(dZH896xM_jt{m@9y>OS^p-G2p}=?3=%I-hO&D8g3MT0Km7J)z^9W{KA+L^tgl09!-C7kMG5X zEuZ?9)Lbx#p8FphN!r-`3ym167P|3t@2{$DsRLqlIWaF!3El!@dIY9c0@`T{G6lBa zdI?r{ti?fh=Id1f-Av_e0N3J_3{`Yb7U^m>l{V~W( zm&u0v)60heDw{4k(TdGB+WpgUJKmq@`bcVD&mLzUC`&pVVL{uz_tG#@F2dqBW$K{| z)$fQw08!a3f8wr0A=kYQsK1rFX^Z9HsaxDX3P2nHq@W=mCk}9~T+N?LB_}6OD_Jk> z#>*vhmsT>yo>pA3x_j|Lk9X+83=0o7+Wm}~>-(I;b!ue1&E56tLf&ey>Mv9NtQBu9 zH0jtVN~d4*vT!!in8`LksWv3NAMi8!6aEAgylg}V3ryN5+o@+oz}G!rI^1q@$2>bf zGK?lgK0bE+23cHw_wgmotwS_*`15|FJ3WW&q#G;2&v;`Fl)r`?E}^^j`hNTsM|&Jy z><5~s`n+<1E5`i|ikOxZ4w760-tg0dNP*3bFXFnwN5ZF=`Tj&0ob)N3GnKcOO`PdO zc(#6em(8U1HP6iH%Fq0}I)I?({TGX6uhi0*KITSal|RLuba011vCMcg7M2thup@I` za~x68r@n%vddKvOVxnfqH$wJ;GZt~FPG@~N^BS-E_eyO!E*$WONKJx@|DPU8fhz8h z<~o9A1%35#w|-n??{B?2TDz+oAG0>97Nh*IW*v=Ebw6Nwty;>^crOi`eL0iB)E|n3 zWXz8 Date: Fri, 21 Jul 2017 11:09:53 +0100 Subject: [PATCH 51/97] * Added Buckaroo.pm package --- BUCK | 5 +---- buckaroo.json | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/BUCK b/BUCK index e01db658..0bf9e3ad 100644 --- a/BUCK +++ b/BUCK @@ -1,7 +1,5 @@ -include_defs('//BUCKAROO_DEPS') - prebuilt_cxx_library( - name = 'chaiscript', + name = 'chaiscript', header_only = True, header_namespace = 'chaiscript', exported_headers = subdir_glob([ @@ -10,5 +8,4 @@ prebuilt_cxx_library( visibility = [ 'PUBLIC', ], - deps = BUCKAROO_DEPS, ) diff --git a/buckaroo.json b/buckaroo.json index afc50314..95db1764 100644 --- a/buckaroo.json +++ b/buckaroo.json @@ -1,4 +1,3 @@ { - "name": "chaiscript", - "dependencies": {} + "name": "ChaiScript" } From 3f299333ccfd4124746ce87e763679d7698c0434 Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Tue, 5 Sep 2017 12:01:37 -0500 Subject: [PATCH 52/97] Switched to recursive mutex Removed namespaces_nested_ref.chai --- include/chaiscript/language/chaiscript_engine.hpp | 13 ++++++++++++- unittests/namespaces_nested_ref.chai | 9 --------- 2 files changed, 12 insertions(+), 10 deletions(-) delete mode 100644 unittests/namespaces_nested_ref.chai diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index fe484ca8..6a96f132 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -718,7 +718,7 @@ namespace chaiscript /// \throw std::runtime_error In the case that the namespace name was never registered. void import(const std::string& t_namespace_name) { - chaiscript::detail::threading::shared_lock l(m_mutex); + chaiscript::detail::threading::unique_lock l(m_use_mutex); if (m_namespaces.count(t_namespace_name)) { add_namespace(t_namespace_name); @@ -737,6 +737,8 @@ namespace chaiscript /// \throw std::runtime_error In the case that the namespace name was already registered. void register_namespace(const std::string& t_namespace_name) { + chaiscript::detail::threading::unique_lock l(m_use_mutex); + if (!m_namespaces.count(t_namespace_name)) { m_namespaces.emplace(std::make_pair(t_namespace_name, dispatch::Dynamic_Object())); } @@ -751,6 +753,8 @@ namespace chaiscript /// \throw std::runtime_error In the case that the namespace name was already registered. void register_namespace(const dispatch::Dynamic_Object& t_namespace_object, const std::string& t_namespace_name) { + chaiscript::detail::threading::unique_lock l(m_use_mutex); + if (!m_namespaces.count(t_namespace_name)) { m_namespaces.emplace(std::make_pair(t_namespace_name, t_namespace_object)); } @@ -765,9 +769,14 @@ namespace chaiscript /// \throw std::runtime_error In the case that the namespace name was already registered. void register_namespace(dispatch::Dynamic_Object&& t_namespace_object, const std::string& t_namespace_name) { + chaiscript::detail::threading::unique_lock l(m_use_mutex); + if (!m_namespaces.count(t_namespace_name)) { m_namespaces.emplace(std::make_pair(t_namespace_name, t_namespace_object)); } + else { + throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); + } } /// \brief Registers a namespace generator, which delays generation of the namespace until it is imported, saving memory if it is never used. @@ -776,6 +785,8 @@ namespace chaiscript /// \throw std::runtime_error In the case that the namespace name was already registered. void register_namespace(const std::function& t_namespace_generator, const std::string& t_namespace_name) { + chaiscript::detail::threading::unique_lock l(m_use_mutex); + if (!m_namespaces.count(t_namespace_name)) { std::function namespace_generator = [=]() { register_namespace(t_namespace_generator(), t_namespace_name); }; m_namespace_generators.emplace(std::make_pair(t_namespace_name, namespace_generator)); diff --git a/unittests/namespaces_nested_ref.chai b/unittests/namespaces_nested_ref.chai deleted file mode 100644 index 5be991cb..00000000 --- a/unittests/namespaces_nested_ref.chai +++ /dev/null @@ -1,9 +0,0 @@ -namespace("parent") -namespace("child") - -child.x = 3.0 -parent.child := child -parent.child.x = 5.0 - -assert_equal(5.0, child.x) -assert_equal(5.0, parent.child.x) \ No newline at end of file From ff78d315835500c97dd2126d26dc3f4431100309 Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Tue, 5 Sep 2017 13:25:41 -0500 Subject: [PATCH 53/97] Simplified namespace handling code and requiring all namespace registration to allow for delayed generation. This simplifies generating namespaces by the user and leads to more efficient code. --- .../chaiscript/language/chaiscript_engine.hpp | 81 +++---------------- 1 file changed, 11 insertions(+), 70 deletions(-) diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index e24a6c7e..ed3b1141 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -55,8 +55,8 @@ namespace chaiscript { - /// Namespace typedef to provide cleaner and more explicit syntax to users. - typedef dispatch::Dynamic_Object Namespace; + /// Namespace alias to provide cleaner and more explicit syntax to users. + using Namespace = dispatch::Dynamic_Object; namespace detail { @@ -81,18 +81,7 @@ namespace chaiscript chaiscript::detail::Dispatch_Engine m_engine; - std::map m_namespaces; - std::map> m_namespace_generators; - - /// Helper function to add a namespace to the engine. - void add_namespace(const std::string& t_namespace_name) - { - if (m_namespaces.count(t_namespace_name)) { - if (!m_engine.get_scripting_objects().count(t_namespace_name)) { // Add namespace if it hasn't already been added. - m_engine.add_global(var(m_namespaces[t_namespace_name]), t_namespace_name); - } - } - } + std::map> m_namespace_generators; /// Evaluates the given string in by parsing it and running the results through the evaluator Boxed_Value do_eval(const std::string &t_input, const std::string &t_filename = "__EVAL__", bool /* t_internal*/ = false) @@ -211,7 +200,7 @@ namespace chaiscript m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global(t_bv, t_name); }), "add_global"); m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ set_global(t_bv, t_name); }), "set_global"); - m_engine.add(fun([this](const std::string& t_namespace_name) { register_namespace(t_namespace_name); import(t_namespace_name); }), "namespace"); + m_engine.add(fun([this](const std::string& t_namespace_name) { register_namespace([](Namespace& space) {}, t_namespace_name); import(t_namespace_name); }), "namespace"); m_engine.add(fun([this](const std::string& t_namespace_name) { import(t_namespace_name); }), "import"); } @@ -729,76 +718,28 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars { chaiscript::detail::threading::unique_lock l(m_use_mutex); - if (m_namespaces.count(t_namespace_name)) { - add_namespace(t_namespace_name); + if (m_engine.get_scripting_objects().count(t_namespace_name)) { + throw std::runtime_error("Namespace: " + t_namespace_name + " was already defined"); } else if (m_namespace_generators.count(t_namespace_name)) { - m_namespace_generators[t_namespace_name](); - add_namespace(t_namespace_name); + m_engine.add_global(var(std::ref(m_namespace_generators[t_namespace_name]())), t_namespace_name); } else { throw std::runtime_error("No registered namespace: " + t_namespace_name); } } - /// \brief Registers a new namespace with ChaiScript engine. - /// \param[in] t_namespace_name Name of the namespace to register. - /// \throw std::runtime_error In the case that the namespace name was already registered. - void register_namespace(const std::string& t_namespace_name) - { - chaiscript::detail::threading::unique_lock l(m_use_mutex); - - if (!m_namespaces.count(t_namespace_name)) { - m_namespaces.emplace(std::make_pair(t_namespace_name, dispatch::Dynamic_Object())); - } - else { - throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); - } - } - - /// \brief Registers a new namespace with ChaiScript engine. - /// \param[in] t_namespace_object Namespace object to register. - /// \param[in] t_namespace_name Name of the Namespace object being registered. - /// \throw std::runtime_error In the case that the namespace name was already registered. - void register_namespace(const dispatch::Dynamic_Object& t_namespace_object, const std::string& t_namespace_name) - { - chaiscript::detail::threading::unique_lock l(m_use_mutex); - - if (!m_namespaces.count(t_namespace_name)) { - m_namespaces.emplace(std::make_pair(t_namespace_name, t_namespace_object)); - } - else { - throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); - } - } - - /// \brief Registers a new namespace with ChaiScript engine. Permits move semantics. - /// \param[in] t_namespace_object Namespace object to register. - /// \param[in] t_namespace_name Name of the Namespace object being registered. - /// \throw std::runtime_error In the case that the namespace name was already registered. - void register_namespace(dispatch::Dynamic_Object&& t_namespace_object, const std::string& t_namespace_name) - { - chaiscript::detail::threading::unique_lock l(m_use_mutex); - - if (!m_namespaces.count(t_namespace_name)) { - m_namespaces.emplace(std::make_pair(t_namespace_name, t_namespace_object)); - } - else { - throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); - } - } - /// \brief Registers a namespace generator, which delays generation of the namespace until it is imported, saving memory if it is never used. /// \param[in] t_namespace_generator Namespace generator function. /// \param[in] t_namespace_name Name of the Namespace function being registered. /// \throw std::runtime_error In the case that the namespace name was already registered. - void register_namespace(const std::function& t_namespace_generator, const std::string& t_namespace_name) + void register_namespace(const std::function& t_namespace_generator, const std::string& t_namespace_name) { chaiscript::detail::threading::unique_lock l(m_use_mutex); - if (!m_namespaces.count(t_namespace_name)) { - std::function namespace_generator = [=]() { register_namespace(t_namespace_generator(), t_namespace_name); }; - m_namespace_generators.emplace(std::make_pair(t_namespace_name, namespace_generator)); + if (!m_namespace_generators.count(t_namespace_name)) { + // contain the namespace object memory within the m_namespace_generators map + m_namespace_generators.emplace(std::make_pair(t_namespace_name, [=, space = Namespace()]() mutable -> Namespace& { t_namespace_generator(space); return space; })); } else { throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); From 037faddab4810fdb845aa3a3f12ec20e10cd3197 Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Tue, 5 Sep 2017 13:47:53 -0500 Subject: [PATCH 54/97] Updated cheatsheet.md for latest namespace implementation --- cheatsheet.md | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/cheatsheet.md b/cheatsheet.md index f25107c0..28debbca 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -168,37 +168,21 @@ chai.set_global(chaiscript::var(somevar), "somevar"); // global non-const, overw ## Adding Namespaces +Namespaces will not be populated until `import` is called. +This saves memory and computing costs if a namespace is not imported into every ChaiScript instance. ``` -chaiscript::Namespace math; -math["pi"] = chaiscript::const_var(3.14159); -math["sin"] = chaiscript::var(chaiscript::fun([](const double x) { return sin(x); })); -chai.register_namespace(math, "math"); +chai.register_namespace([](chaiscript::Namespace& math) { + math["pi"] = chaiscript::const_var(3.14159); + math["sin"] = chaiscript::var(chaiscript::fun([](const double x) { return sin(x); })); }, + "math"); ``` -Import namespace via C++ (_not generally recommended_) -``` -chai.import("math"); -``` - -Import namespace via ChaiScript (_recommended_) +Import namespace in ChaiScript ``` import("math") print(math.pi) // prints 3.14159 ``` -#### Delayed Namespace Generation - -Passing a lambda function that returns a namespace will delay the namespace generation until `import` is called. -This saves memory and computing costs if a namespace is not imported into every ChaiScript instance. -``` -chai.register_namespace([]() { - chaiscript::Namespace math; - math["pi"] = chaiscript::const_var(3.14159); - math["sin"] = chaiscript::var(chaiscript::fun([](const double x) { return sin(x); })); - return math; }, - "math"); -``` - # Using STL ChaiScript recognize many types from STL, but you have to add specific instantiation yourself. From a87147a12dc726501c2e63ef2fc6473f7d1b9676 Mon Sep 17 00:00:00 2001 From: Mario Lang Date: Thu, 14 Sep 2017 17:41:11 +0200 Subject: [PATCH 55/97] Upgrade samples where it improves readability --- samples/example.cpp | 28 ++++++++++++++-------------- samples/inheritance.cpp | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/samples/example.cpp b/samples/example.cpp index 9a0f0070..4810a127 100644 --- a/samples/example.cpp +++ b/samples/example.cpp @@ -51,11 +51,9 @@ struct System void do_callbacks(const std::string &inp) { log("Running Callbacks: " + inp); - for (std::map >::iterator itr = m_callbacks.begin(); - itr != m_callbacks.end(); - ++itr) + for (auto & m_callback : m_callbacks) { - log("Callback: " + itr->first, itr->second(inp)); + log("Callback: " + m_callback.first, m_callback.second(inp)); } } }; @@ -88,25 +86,25 @@ int main(int /*argc*/, char * /*argv*/[]) { // The function "{ 'Callback1' + x }" is created in chaiscript and passed into our C++ application // in the "add_callback" function of struct System the chaiscript function is converted into a // std::function, so it can be handled and called easily and type-safely - chai.eval("system.add_callback(\"#1\", fun(x) { \"Callback1 \" + x });"); + chai.eval(R"(system.add_callback("#1", fun(x) { "Callback1 " + x });)"); // Because we are sharing the "system" object with the chaiscript engine we have equal // access to it both from within chaiscript and from C++ code system.do_callbacks("TestString"); - chai.eval("system.do_callbacks(\"TestString\");"); + chai.eval(R"(system.do_callbacks("TestString");)"); // The log function is overloaded, therefore we have to give the C++ compiler a hint as to which // version we want to register. One way to do this is to create a typedef of the function pointer // then cast your function to that typedef. - typedef void (*PlainLog)(const std::string &); - typedef void (*ModuleLog)(const std::string &, const std::string &); + using PlainLog = void (*)(const std::string &); + using ModuleLog = void (*)(const std::string &, const std::string &); chai.add(fun(PlainLog(&log)), "log"); chai.add(fun(ModuleLog(&log)), "log"); - chai.eval("log(\"Test Message\")"); + chai.eval(R"(log("Test Message"))"); // A shortcut to using eval is just to use the chai operator() - chai("log(\"Test Module\", \"Test Message\");"); + chai(R"(log("Test Module", "Test Message");)"); //Finally, it is possible to register a lambda as a system function, in this //way, we can, for instance add a bound member function to the system @@ -115,7 +113,9 @@ int main(int /*argc*/, char * /*argv*/[]) { //Call bound version of do_callbacks chai("do_callbacks()"); - std::function caller = chai.eval >("fun() { system.do_callbacks(\"From Functor\"); }"); + std::function caller = chai.eval >( + R"(fun() { system.do_callbacks("From Functor"); })" + ); caller(); @@ -134,7 +134,7 @@ int main(int /*argc*/, char * /*argv*/[]) { std::cout << "scripti: " << scripti << '\n'; scripti *= 2; std::cout << "scripti (updated): " << scripti << '\n'; - chai("print(\"Scripti from chai: \" + to_string(scripti))"); + chai(R"(print("Scripti from chai: " + to_string(scripti)))"); //To do: Add examples of handling Boxed_Values directly when needed @@ -146,7 +146,7 @@ int main(int /*argc*/, char * /*argv*/[]) { log("Functor test output", ss.str()); chai.add(var(std::shared_ptr()), "nullvar"); - chai("print(\"This should be true.\"); print(nullvar.is_var_null())"); + chai(R"(print("This should be true."); print(nullvar.is_var_null()))"); // test the global const action chai.add_global_const(const_var(1), "constvar"); @@ -160,7 +160,7 @@ int main(int /*argc*/, char * /*argv*/[]) { // Test ability to register a function that excepts a shared_ptr version of a type - chai("take_shared_ptr(\"Hello World as a shared_ptr\");"); + chai(R"(take_shared_ptr("Hello World as a shared_ptr");)"); chai.add(fun(&bound_log, std::string("Msg")), "BoundFun"); diff --git a/samples/inheritance.cpp b/samples/inheritance.cpp index aba619a4..1c9ddb63 100644 --- a/samples/inheritance.cpp +++ b/samples/inheritance.cpp @@ -122,7 +122,7 @@ int main() assert(myderived.getValue() == "1234"); - chai.eval("myderived.setValue(\"new\")"); // set the value via chaiscript + chai.eval(R"(myderived.setValue("new"))"); // set the value via chaiscript assert(myderived.getValue() == "new"); // call the other derived method via chaiscript and return the value to c++ land: From ee3f828b8c33fc161613897da851cd6da1e56aea Mon Sep 17 00:00:00 2001 From: Mario Lang Date: Fri, 15 Sep 2017 10:12:47 +0200 Subject: [PATCH 56/97] Allow bootstrapping hpx::lcos::future Which has an overloaded get(error_code &). Use a lambda in standard_library::future_type to disambiguate. --- include/chaiscript/dispatchkit/bootstrap_stl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index 7f409a3a..eca3cf38 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -733,7 +733,7 @@ namespace chaiscript m.add(user_type(), type); m.add(fun([](const FutureType &t) { return t.valid(); }), "valid"); - m.add(fun(&FutureType::get), "get"); + m.add(fun([](FutureType &t) { return t.get(); }), "get"); m.add(fun(&FutureType::wait), "wait"); } template From 0fa0def11270d9403f6fc83a946af31629d7c6ac Mon Sep 17 00:00:00 2001 From: Mario Lang Date: Mon, 18 Sep 2017 15:00:52 +0200 Subject: [PATCH 57/97] Use range-based for --- include/chaiscript/dispatchkit/dispatchkit.hpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index d8bfb25b..2e71e543 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -1143,24 +1143,17 @@ namespace chaiscript { std::cout << "Registered Types: \n"; std::vector > types = get_types(); - for (std::vector >::const_iterator itr = types.begin(); - itr != types.end(); - ++itr) + for (auto const &type: get_types()) { - std::cout << itr->first << ": "; - std::cout << itr->second.bare_name(); - std::cout << '\n'; + std::cout << type.first << ": " << type.second.bare_name() << '\n'; } std::cout << '\n'; - std::vector > funcs = get_functions(); std::cout << "Functions: \n"; - for (std::vector >::const_iterator itr = funcs.begin(); - itr != funcs.end(); - ++itr) + for (auto const &func: get_functions()) { - dump_function(*itr); + dump_function(func); } std::cout << '\n'; } From 3e521d29522016e75960ba7da3af71c652c0675b Mon Sep 17 00:00:00 2001 From: Mario Lang Date: Mon, 18 Sep 2017 17:00:04 +0200 Subject: [PATCH 58/97] Delete now useless local copy --- include/chaiscript/dispatchkit/dispatchkit.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 2e71e543..ae729819 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -1142,7 +1142,6 @@ namespace chaiscript void dump_system() const { std::cout << "Registered Types: \n"; - std::vector > types = get_types(); for (auto const &type: get_types()) { std::cout << type.first << ": " << type.second.bare_name() << '\n'; From c6237cc528c2d378dca9f74d8d455a88756b1fad Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 21 Sep 2017 08:55:32 -0600 Subject: [PATCH 59/97] Add `+= char` for string type --- include/chaiscript/dispatchkit/bootstrap_stl.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index 7f409a3a..7785f9fb 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -706,6 +706,8 @@ namespace chaiscript m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_of(f, pos); } ), "find_last_of"); m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_not_of(f, pos); } ), "find_last_not_of"); m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_not_of(f, pos); } ), "find_first_not_of"); + + m.add(fun([](String *s, typename String::value_type c) -> decltype(auto) { return (*s += c); } ), "+="); m.add(fun([](String *s) { s->clear(); } ), "clear"); m.add(fun([](const String *s) { return s->empty(); } ), "empty"); From 15196af5d6b5712575ba49aa908600b8d43da050 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Mon, 2 Oct 2017 09:52:51 -0600 Subject: [PATCH 60/97] Remove one case of UB union work --- include/chaiscript/language/chaiscript_posix.hpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/include/chaiscript/language/chaiscript_posix.hpp b/include/chaiscript/language/chaiscript_posix.hpp index 62f1c117..87b66881 100644 --- a/include/chaiscript/language/chaiscript_posix.hpp +++ b/include/chaiscript/language/chaiscript_posix.hpp @@ -41,7 +41,7 @@ namespace chaiscript struct DLSym { DLSym(DLModule &t_mod, const std::string &t_symbol) - : m_symbol(cast_symbol(dlsym(t_mod.m_data, t_symbol.c_str()))) + : m_symbol(reinterpret_cast(dlsym(t_mod.m_data, t_symbol.c_str()))) { if (!m_symbol) { @@ -49,19 +49,6 @@ namespace chaiscript } } - static T cast_symbol(void *p) - { - union cast_union - { - T func_ptr; - void *in_ptr; - }; - - cast_union c; - c.in_ptr = p; - return c.func_ptr; - } - T m_symbol; }; From 1541cce1d91df027dad2612dbd034de14df5fd36 Mon Sep 17 00:00:00 2001 From: dinghram Date: Tue, 3 Oct 2017 08:43:01 -0600 Subject: [PATCH 61/97] Change long to int64_t to remove OS ambiguity Linux compilers interpret "long" as 64 bit, Visual Studio on Windows interprets "long" as 32 bit. In order to remove ambiguity, from_json should use int64_t rather than long when parsing integers. --- include/chaiscript/utility/json.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index 693f19da..2912a50e 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -131,7 +131,7 @@ class JSON } Internal( double d ) : Float( d ), Type(Class::Floating) {} - Internal( long l ) : Int( l ), Type(Class::Integral) {} + Internal( int64_t l ) : Int( l ), Type(Class::Integral) {} Internal( bool b ) : Bool( b ), Type(Class::Boolean) {} Internal( std::string s ) : String(std::make_unique(std::move(s))), Type(Class::String) {} Internal() : Type(Class::Null) {} @@ -192,7 +192,7 @@ class JSON std::unique_ptr Map; std::unique_ptr String; double Float = 0; - long Int = 0; + int64_t Int = 0; bool Bool = false; Class Type = Class::Null; @@ -248,7 +248,7 @@ class JSON explicit JSON( T b, typename enable_if::value>::type* = nullptr ) : internal( static_cast(b) ) {} template - explicit JSON( T i, typename enable_if::value && !is_same::value>::type* = nullptr ) : internal( static_cast(i) ) {} + explicit JSON( T i, typename enable_if::value && !is_same::value>::type* = nullptr ) : internal( static_cast(i) ) {} template explicit JSON( T f, typename enable_if::value>::type* = nullptr ) : internal( static_cast(f) ) {} @@ -335,8 +335,8 @@ class JSON return ok ? internal.Float : 0.0; } - long to_int() const { bool b; return to_int( b ); } - long to_int( bool &ok ) const { + int64_t to_int() const { bool b; return to_int( b ); } + int64_t to_int( bool &ok ) const { ok = (internal.Type == Class::Integral); return ok ? internal.Int : 0; } @@ -568,7 +568,7 @@ struct JSONParser { char c = '\0'; bool isDouble = false; bool isNegative = false; - long exp = 0; + int64_t exp = 0; if( offset < str.size() && str.at(offset) == '-' ) { isNegative = true; ++offset; @@ -605,7 +605,7 @@ struct JSONParser { break; } } - exp = chaiscript::parse_num( exp_str ); + exp = chaiscript::parse_num( exp_str ); } else if( offset < str.size() && (!isspace( c ) && c != ',' && c != ']' && c != '}' )) { throw std::runtime_error(std::string("JSON ERROR: Number: unexpected character '") + c + "'"); @@ -616,9 +616,9 @@ struct JSONParser { return JSON((isNegative?-1:1) * chaiscript::parse_num( val ) * std::pow( 10, exp )); } else { if( !exp_str.empty() ) { - return JSON((isNegative?-1:1) * static_cast(chaiscript::parse_num( val )) * std::pow( 10, exp )); + return JSON((isNegative?-1:1) * static_cast(chaiscript::parse_num( val )) * std::pow( 10, exp )); } else { - return JSON((isNegative?-1:1) * chaiscript::parse_num( val )); + return JSON((isNegative?-1:1) * chaiscript::parse_num( val )); } } } From 2b735d1b3acb6c346f55beb9125324b0b37fe997 Mon Sep 17 00:00:00 2001 From: superfunc Date: Sat, 21 Oct 2017 15:14:27 -0700 Subject: [PATCH 62/97] Fix link in readme --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 8bf4deec..8323a128 100644 --- a/readme.md +++ b/readme.md @@ -25,7 +25,7 @@ Release under the BSD license, see "license.txt" for details. Introduction ============ -[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/ChaiScript/ChaiScript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Gitter](https://badges.gitter.im/JoinChat.svg)](https://gitter.im/ChaiScript/ChaiScript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ChaiScript is one of the only embedded scripting language designed from the ground up to directly target C++ and take advantage of modern C++ development From 79d985d6ffdde4a42e40e07a2ad72f97788be78a Mon Sep 17 00:00:00 2001 From: Glen Fraser Date: Tue, 24 Oct 2017 20:10:50 +0200 Subject: [PATCH 63/97] Fix JSON parsing for floats with negative exponents - also add unit tests to cover some broken (now fixed) cases. --- include/chaiscript/utility/json.hpp | 7 ++++--- unittests/json_14.chai | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 unittests/json_14.chai diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index 693f19da..838bd5b9 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -568,6 +568,7 @@ struct JSONParser { char c = '\0'; bool isDouble = false; bool isNegative = false; + bool isExpNegative = false; long exp = 0; if( offset < str.size() && str.at(offset) == '-' ) { isNegative = true; @@ -587,7 +588,7 @@ struct JSONParser { if( offset < str.size() && (c == 'E' || c == 'e' )) { c = str.at(offset++); if( c == '-' ) { - exp_str += '-'; + isExpNegative = true; } else if( c == '+' ) { // do nothing } else { @@ -603,9 +604,9 @@ struct JSONParser { } else { break; -} + } } - exp = chaiscript::parse_num( exp_str ); + exp = chaiscript::parse_num( exp_str ) * (isExpNegative?-1:1); } else if( offset < str.size() && (!isspace( c ) && c != ',' && c != ']' && c != '}' )) { throw std::runtime_error(std::string("JSON ERROR: Number: unexpected character '") + c + "'"); diff --git a/unittests/json_14.chai b/unittests/json_14.chai new file mode 100644 index 00000000..e6490a1d --- /dev/null +++ b/unittests/json_14.chai @@ -0,0 +1,5 @@ +assert_equal(from_json("9.9e-02"), 9.9e-02) +assert_equal(from_json("-13.57e+3"), -13570.0) +assert_equal(from_json("1E-01"), 0.1) +assert_equal(from_json("-314159e-5"), -3.14159) +assert_equal(from_json("5e+04"), 50000) From 784c3a9720fbb148fd45adfc2919ed54937c3fea Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 21 Nov 2017 07:12:17 -0700 Subject: [PATCH 64/97] Add slow test for creating variables References #356 --- performance_tests/create_variables.chai | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 performance_tests/create_variables.chai diff --git a/performance_tests/create_variables.chai b/performance_tests/create_variables.chai new file mode 100644 index 00000000..e63e1a46 --- /dev/null +++ b/performance_tests/create_variables.chai @@ -0,0 +1,11 @@ +def var_test(int n) +{ + for (var i = 0; i < n; ++i) { + var j = 0 + } +} + +var n = 500000 +var_test(n) // takes 2.6 s + + From 50a2773081131cbb15e8d43e2b5aebdf18975fbf Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 21 Nov 2017 09:01:17 -0700 Subject: [PATCH 65/97] Reduce cost of cloning common built in types Re: #356 --- .../chaiscript/dispatchkit/boxed_number.hpp | 4 ++ .../chaiscript/language/chaiscript_eval.hpp | 39 ++++++++++--------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/include/chaiscript/dispatchkit/boxed_number.hpp b/include/chaiscript/dispatchkit/boxed_number.hpp index 8f1e871f..dafc1234 100644 --- a/include/chaiscript/dispatchkit/boxed_number.hpp +++ b/include/chaiscript/dispatchkit/boxed_number.hpp @@ -521,6 +521,10 @@ namespace chaiscript validate_boxed_number(bv); } + static Boxed_Value clone(const Boxed_Value &t_bv) { + return Boxed_Number(t_bv).get_as(t_bv.get_type_info()).bv; + } + static bool is_floating_point(const Boxed_Value &t_bv) { const Type_Info &inp_ = t_bv.get_type_info(); diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index a9819440..508f4d3a 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -88,6 +88,23 @@ namespace chaiscript return std::move(rv.retval); } } + + inline Boxed_Value clone_if_necessary(Boxed_Value incoming, std::atomic_uint_fast32_t &t_loc, const chaiscript::detail::Dispatch_State &t_ss) + { + if (!incoming.is_return_value()) + { + if (incoming.get_type_info().is_arithmetic()) { + return Boxed_Number::clone(incoming); + } else if (incoming.get_type_info().bare_equal_type_info(typeid(bool))) { + return Boxed_Value(*static_cast(incoming.get_const_ptr())); + } else { + return t_ss->call_function("clone", t_loc, {incoming}, t_ss.conversions()); + } + } else { + incoming.reset_return_value(); + return incoming; + } + } } template @@ -459,11 +476,7 @@ namespace chaiscript lhs.reset_return_value(); return rhs; } else { - if (!rhs.is_return_value()) - { - rhs = t_ss->call_function("clone", m_clone_loc, {rhs}, t_ss.conversions()); - } - rhs.reset_return_value(); + rhs = detail::clone_if_necessary(std::move(rhs), m_clone_loc, t_ss); } } @@ -1056,12 +1069,7 @@ namespace chaiscript if (!this->children.empty()) { vec.reserve(this->children[0]->children.size()); for (const auto &child : this->children[0]->children) { - auto obj = child->eval(t_ss); - if (!obj.is_return_value()) { - vec.push_back(t_ss->call_function("clone", m_loc, {obj}, t_ss.conversions())); - } else { - vec.push_back(std::move(obj)); - } + vec.push_back(detail::clone_if_necessary(child->eval(t_ss), m_loc, t_ss)); } } return const_var(std::move(vec)); @@ -1086,12 +1094,8 @@ namespace chaiscript std::map retval; for (const auto &child : this->children[0]->children) { - auto obj = child->children[1]->eval(t_ss); - if (!obj.is_return_value()) { - obj = t_ss->call_function("clone", m_loc, {obj}, t_ss.conversions()); - } - - retval[t_ss->boxed_cast(child->children[0]->eval(t_ss))] = std::move(obj); + retval.insert(std::make_pair(t_ss->boxed_cast(child->children[0]->eval(t_ss)), + detail::clone_if_necessary(child->children[1]->eval(t_ss), m_loc, t_ss))); } return const_var(std::move(retval)); @@ -1450,7 +1454,6 @@ namespace chaiscript function_name); } } catch (const exception::name_conflict_error &e) { - std::cout << "Method!!" << std::endl; throw exception::eval_error("Method redefined '" + e.name() + "'"); } return void_var(); From 830766393895501fc0da6212f5b13e2d367ed9bc Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 21 Nov 2017 10:26:58 -0700 Subject: [PATCH 66/97] Look for and optimize decl assignments Re: #356 --- .../chaiscript/language/chaiscript_common.hpp | 4 +-- .../chaiscript/language/chaiscript_eval.hpp | 21 ++++++++++++++++ .../language/chaiscript_optimizer.hpp | 25 +++++++++++++++++-- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index b157cac2..b1e16688 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -62,7 +62,7 @@ namespace chaiscript /// Types of AST nodes available to the parser and eval - enum class AST_Node_Type { Id, Fun_Call, Unused_Return_Fun_Call, Arg_List, Equation, Var_Decl, + enum class AST_Node_Type { Id, Fun_Call, Unused_Return_Fun_Call, Arg_List, Equation, Var_Decl, Assign_Decl, Array_Call, Dot_Access, Lambda, Block, Scopeless_Block, Def, While, If, For, Ranged_For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Continue, Map_Pair, Value_Range, Inline_Range, Try, Catch, Finally, Method, Attr_Decl, @@ -77,7 +77,7 @@ namespace chaiscript { /// Helper lookup to get the name of each node type inline const char *ast_node_type_to_string(AST_Node_Type ast_node_type) { - static const char * const ast_node_types[] = { "Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl", + static const char * const ast_node_types[] = { "Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl", "Assign_Decl", "Array_Call", "Dot_Access", "Lambda", "Block", "Scopeless_Block", "Def", "While", "If", "For", "Ranged_For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range", "Inline_Range", "Try", "Catch", "Finally", "Method", "Attr_Decl", diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 508f4d3a..e0933b71 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -555,6 +555,27 @@ namespace chaiscript } }; + template + struct Assign_Decl_AST_Node final : AST_Node_Impl { + Assign_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : + AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Assign_Decl, std::move(t_loc), std::move(t_children)) { } + + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + const std::string &idname = this->children[0]->text; + + try { + Boxed_Value bv(detail::clone_if_necessary(this->children[1]->eval(t_ss), m_loc, t_ss)); + bv.reset_return_value(); + t_ss.add_object(idname, bv); + return bv; + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Variable redefined '" + e.name() + "'"); + } + } + private: + mutable std::atomic_uint_fast32_t m_loc = {0}; + }; + template struct Array_Call_AST_Node final : AST_Node_Impl { diff --git a/include/chaiscript/language/chaiscript_optimizer.hpp b/include/chaiscript/language/chaiscript_optimizer.hpp index 675d092c..55a19c58 100644 --- a/include/chaiscript/language/chaiscript_optimizer.hpp +++ b/include/chaiscript/language/chaiscript_optimizer.hpp @@ -97,7 +97,7 @@ namespace chaiscript { template bool contains_var_decl_in_scope(const eval::AST_Node_Impl &node) { - if (node.identifier == AST_Node_Type::Var_Decl) { + if (node.identifier == AST_Node_Type::Var_Decl || node.identifier == AST_Node_Type::Assign_Decl) { return true; } @@ -208,6 +208,27 @@ namespace chaiscript { } }; + struct Assign_Decl { + template + auto optimize(eval::AST_Node_Impl_Ptr node) { + if ((node->identifier == AST_Node_Type::Equation) + && node->text == "=" + && node->children.size() == 2 + && node->children[0]->identifier == AST_Node_Type::Var_Decl + ) + { + std::vector> new_children; + new_children.push_back(std::move(node->children[0]->children[0])); + new_children.push_back(std::move(node->children[1])); + return chaiscript::make_unique, eval::Assign_Decl_AST_Node>(node->text, + node->location, std::move(new_children) ); + } + + return node; + } + }; + + struct If { template auto optimize(eval::AST_Node_Impl_Ptr node) { @@ -440,7 +461,7 @@ namespace chaiscript { }; typedef Optimizer Optimizer_Default; + optimizer::If, optimizer::Return, optimizer::Dead_Code, optimizer::Block, optimizer::For_Loop, optimizer::Assign_Decl> Optimizer_Default; } } From 680f9b92429515dc83bf400ed774bb117abde1e3 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Tue, 21 Nov 2017 14:06:42 -0700 Subject: [PATCH 67/97] Fix variable redeclaration from last merge conflict fix. --- include/chaiscript/utility/json.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index 121b9390..d7c632c6 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -570,7 +570,6 @@ struct JSONParser { bool isNegative = false; int64_t exp = 0; bool isExpNegative = false; - long exp = 0; if( offset < str.size() && str.at(offset) == '-' ) { isNegative = true; ++offset; From 2c30268bf2d60267f4daba0b505f4c0ce0f3751f Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Wed, 22 Nov 2017 14:05:09 -0600 Subject: [PATCH 68/97] Improved parsing speed of parse_num --- include/chaiscript/chaiscript_defines.hpp | 86 +++++++++++++---------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index 5b2e84f4..3157f429 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -166,48 +166,56 @@ namespace chaiscript { } - template + template auto parse_num(const char *t_str) -> typename std::enable_if::value, T>::type { - T t = 0; - T base = 0; - T decimal_place = 0; - bool exponent = false; - bool neg_exponent = false; + T t = 0; + T base; + T decimal_place = 0; + int exponent = 0; - const auto final_value = [](const T val, const T baseval, const bool hasexp, const bool negexp) -> T { - if (!hasexp) { - return val; - } else { - return baseval * std::pow(T(10), val*T(negexp?-1:1)); - } - }; - - for(; *t_str != '\0'; ++t_str) { - char c = *t_str; - if (c == '.') { - decimal_place = 10; - } else if (c == 'e' || c == 'E') { - exponent = true; - decimal_place = 0; - base = t; - t = 0; - } else if (c == '-' && exponent) { - neg_exponent = true; - } else if (c == '+' && exponent) { - neg_exponent = false; - } else if (c < '0' || c > '9') { - return final_value(t, base, exponent, neg_exponent); - } else if (decimal_place < T(10)) { - t *= T(10); - t += T(c - '0'); - } else { - t += (T(c - '0') / (T(decimal_place))); - decimal_place *= 10; - } - } - - return final_value(t, base, exponent, neg_exponent); + for (char c;; ++t_str) { + c = *t_str; + switch (c) + { + case '.': + decimal_place = 10; + break; + case 'e': + case 'E': + decimal_place = 0; + base = t; + t = 0; + break; + case '-': + exponent = -1; + break; + case '+': + exponent = 1; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (decimal_place < 10) { + t *= 10; + t += c - '0'; + } + else { + t += (c - '0') / decimal_place; + decimal_place *= 10; + } + break; + default: + return exponent ? base * std::pow(T(10), t * exponent) : t; + } + } } template From bcd01f3b03ff95f3d21a942dd9d6c1dcd5dd397b Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Wed, 22 Nov 2017 14:51:50 -0600 Subject: [PATCH 69/97] Fixed issue when lacking positive exponent sign --- include/chaiscript/chaiscript_defines.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index 3157f429..4c303307 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -183,6 +183,7 @@ namespace chaiscript { break; case 'e': case 'E': + exponent = 1; decimal_place = 0; base = t; t = 0; @@ -191,7 +192,6 @@ namespace chaiscript { exponent = -1; break; case '+': - exponent = 1; break; case '0': case '1': From af76d1bb42ee66ff38c35313ace1bcd1d1fe3cad Mon Sep 17 00:00:00 2001 From: arcoRocks <33952704+arcoRocks@users.noreply.github.com> Date: Fri, 24 Nov 2017 10:15:06 +0100 Subject: [PATCH 70/97] Update json_wrap.hpp Fix for #381 --- include/chaiscript/utility/json_wrap.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp index 2aa81f34..7b3ef66f 100644 --- a/include/chaiscript/utility/json_wrap.hpp +++ b/include/chaiscript/utility/json_wrap.hpp @@ -142,6 +142,8 @@ namespace chaiscript // not a dynamic object } + if (t_bv.is_null()) return json::JSON(); // a null value + throw std::runtime_error("Unknown object type to convert to JSON"); } From 035319bbd006ad09a513968d4a5f2944b5242608 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sun, 26 Nov 2017 22:31:35 -0700 Subject: [PATCH 71/97] Fix "compiled loop" optimization --- include/chaiscript/language/chaiscript_optimizer.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/chaiscript/language/chaiscript_optimizer.hpp b/include/chaiscript/language/chaiscript_optimizer.hpp index 55a19c58..c1edb73a 100644 --- a/include/chaiscript/language/chaiscript_optimizer.hpp +++ b/include/chaiscript/language/chaiscript_optimizer.hpp @@ -392,21 +392,21 @@ namespace chaiscript { const auto &prefix_node = child_at(*for_node, 2); if (child_count(*for_node) == 4 - && eq_node.identifier == AST_Node_Type::Equation + && eq_node.identifier == AST_Node_Type::Assign_Decl && child_count(eq_node) == 2 - && child_at(eq_node, 0).identifier == AST_Node_Type::Var_Decl + && child_at(eq_node, 0).identifier == AST_Node_Type::Id && child_at(eq_node, 1).identifier == AST_Node_Type::Constant && binary_node.identifier == AST_Node_Type::Binary && binary_node.text == "<" && child_count(binary_node) == 2 && child_at(binary_node, 0).identifier == AST_Node_Type::Id - && child_at(binary_node, 0).text == child_at(child_at(eq_node,0), 0).text + && child_at(binary_node, 0).text == child_at(eq_node,0).text && child_at(binary_node, 1).identifier == AST_Node_Type::Constant && prefix_node.identifier == AST_Node_Type::Prefix && prefix_node.text == "++" && child_count(prefix_node) == 1 && child_at(prefix_node, 0).identifier == AST_Node_Type::Id - && child_at(prefix_node, 0).text == child_at(child_at(eq_node,0), 0).text) + && child_at(prefix_node, 0).text == child_at(eq_node,0).text) { const Boxed_Value &begin = dynamic_cast &>(child_at(eq_node, 1)).m_value; const Boxed_Value &end = dynamic_cast &>(child_at(binary_node, 1)).m_value; From ed9c0747fb6fa24e48c0bebcecc50c1f34811738 Mon Sep 17 00:00:00 2001 From: arcoRocks <33952704+arcoRocks@users.noreply.github.com> Date: Tue, 28 Nov 2017 08:06:46 +0100 Subject: [PATCH 72/97] Update json_wrap.hpp --- include/chaiscript/utility/json_wrap.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp index 7b3ef66f..ce3072c9 100644 --- a/include/chaiscript/utility/json_wrap.hpp +++ b/include/chaiscript/utility/json_wrap.hpp @@ -142,8 +142,8 @@ namespace chaiscript // not a dynamic object } - if (t_bv.is_null()) return json::JSON(); // a null value - + if (t_bv.is_null()) return json::JSON(); // a null value + throw std::runtime_error("Unknown object type to convert to JSON"); } From dee2ce5c56d20d0078c76cb76c44e8480bafdca9 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 30 Nov 2017 09:47:20 -0700 Subject: [PATCH 73/97] Add failing test for ranged_for with variable --- unittests/ranged_for.chai | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/unittests/ranged_for.chai b/unittests/ranged_for.chai index 9ef34fed..4e98d7b9 100644 --- a/unittests/ranged_for.chai +++ b/unittests/ranged_for.chai @@ -18,4 +18,9 @@ for (x : retro(range([1,2,3,4]))) { assert_true(result > .6 && result < .7); +var m=["a": ["t": "val"], "b": ["t": "val"]]; +for (i : m) { + var &ref=i.second["t"]; + print(ref); +} From 56140608ef546ca3f22261e80d0771bc91d9e325 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 30 Nov 2017 10:06:32 -0700 Subject: [PATCH 74/97] Fix scope optimizations for ranged for and refs Closes #383 --- include/chaiscript/language/chaiscript_optimizer.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/chaiscript/language/chaiscript_optimizer.hpp b/include/chaiscript/language/chaiscript_optimizer.hpp index c1edb73a..a8118fba 100644 --- a/include/chaiscript/language/chaiscript_optimizer.hpp +++ b/include/chaiscript/language/chaiscript_optimizer.hpp @@ -97,7 +97,7 @@ namespace chaiscript { template bool contains_var_decl_in_scope(const eval::AST_Node_Impl &node) { - if (node.identifier == AST_Node_Type::Var_Decl || node.identifier == AST_Node_Type::Assign_Decl) { + if (node.identifier == AST_Node_Type::Var_Decl || node.identifier == AST_Node_Type::Assign_Decl || node.identifier == AST_Node_Type::Reference) { return true; } @@ -107,6 +107,7 @@ namespace chaiscript { const auto &child = child_at(node, i); if (child.identifier != AST_Node_Type::Block && child.identifier != AST_Node_Type::For + && child.identifier != AST_Node_Type::Ranged_For && contains_var_decl_in_scope(child)) { return true; } From 136539867c1f790926ec7ee3c05b4f2ddd3fee53 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 30 Nov 2017 10:19:56 -0700 Subject: [PATCH 75/97] Remove outdated vim support Closes #288 --- contrib/vim | 1 + contrib/vim/README.txt | 7 -- contrib/vim/ftdetect/chaiscript.vim | 2 - contrib/vim/indent/chaiscript.vim | 50 --------------- contrib/vim/syntax/chaiscript.vim | 99 ----------------------------- 5 files changed, 1 insertion(+), 158 deletions(-) create mode 100644 contrib/vim delete mode 100644 contrib/vim/README.txt delete mode 100644 contrib/vim/ftdetect/chaiscript.vim delete mode 100644 contrib/vim/indent/chaiscript.vim delete mode 100644 contrib/vim/syntax/chaiscript.vim diff --git a/contrib/vim b/contrib/vim new file mode 100644 index 00000000..0877d674 --- /dev/null +++ b/contrib/vim @@ -0,0 +1 @@ +vim support can be found at https://github.com/ChaiScript/vim-chaiscript diff --git a/contrib/vim/README.txt b/contrib/vim/README.txt deleted file mode 100644 index a79b1e75..00000000 --- a/contrib/vim/README.txt +++ /dev/null @@ -1,7 +0,0 @@ -Install ftdetect, indent and syntax subdirectories to: - -~/.vim/ - -See the vim documentation on this: - -http://vimdoc.sourceforge.net/htmldoc/syntax.html#mysyntaxfile diff --git a/contrib/vim/ftdetect/chaiscript.vim b/contrib/vim/ftdetect/chaiscript.vim deleted file mode 100644 index c4ce5ef2..00000000 --- a/contrib/vim/ftdetect/chaiscript.vim +++ /dev/null @@ -1,2 +0,0 @@ -au BufRead,BufNewFile *.chai set filetype=chaiscript - diff --git a/contrib/vim/indent/chaiscript.vim b/contrib/vim/indent/chaiscript.vim deleted file mode 100644 index 247e1a6e..00000000 --- a/contrib/vim/indent/chaiscript.vim +++ /dev/null @@ -1,50 +0,0 @@ -" Vim indent file -" Language: ChaiScript -" Maintainer: Jason Turner - -" Only load this indent file when no other was loaded. -if exists("b:did_indent") - finish -endif -let b:did_indent = 1 - -setlocal indentexpr=GetChaiScriptIndent() -setlocal autoindent - -" Only define the function once. -if exists("*GetChaiScriptIndent") - finish -endif - -function! GetChaiScriptIndent() - " Find a non-blank line above the current line. - let lnum = prevnonblank(v:lnum - 1) - - " Hit the start of the file, use zero indent. - if lnum == 0 - return 0 - endif - - " Add a 'shiftwidth' after lines that start a block: - " lines containing a { - let ind = indent(lnum) - let flag = 0 - let prevline = getline(lnum) - if prevline =~ '^.*{.*' - let ind = ind + &shiftwidth - let flag = 1 - endif - - " Subtract a 'shiftwidth' after lines containing a { followed by a } - " to keep it balanced - if flag == 1 && prevline =~ '.*{.*}.*' - let ind = ind - &shiftwidth - endif - - " Subtract a 'shiftwidth' on lines ending with } - if getline(v:lnum) =~ '^\s*\%(}\)' - let ind = ind - &shiftwidth - endif - - return ind -endfunction diff --git a/contrib/vim/syntax/chaiscript.vim b/contrib/vim/syntax/chaiscript.vim deleted file mode 100644 index b442aaa9..00000000 --- a/contrib/vim/syntax/chaiscript.vim +++ /dev/null @@ -1,99 +0,0 @@ -" Vim syntax file -" Language: ChaiScript -" Maintainer: Jason Turner - -" Quit when a (custom) syntax file was already loaded -if exists("b:current_syntax") - finish -end - -let s:cpo_save = &cpo -set cpo&vim - -syn case match - -" syncing method -syn sync fromstart - -" Strings -syn region chaiscriptString start=+"+ end=+"+ skip=+\\\\\|\\"+ contains=chaiscriptSpecial,chaiscriptEval,@Spell - -" Escape characters -syn match chaiscriptSpecial contained "\\[\\abfnrtv\'\"]\|\\\d\{,3}" - -" String evals -syn region chaiscriptEval contained start="${" end="}" - -" integer number -syn match chaiscriptNumber "\<\d\+\>" - -" floating point number, with dot, optional exponent -syn match chaiscriptFloat "\<\d\+\.\d*\%(e[-+]\=\d\+\)\=\>" - -" floating point number, starting with a dot, optional exponent -syn match chaiscriptFloat "\.\d\+\%(e[-+]\=\d\+\)\=\>" - -" floating point number, without dot, with exponent -syn match chaiscriptFloat "\<\d\+e[-+]\=\d\+\>" - -" Hex strings -syn match chaiscriptNumber "\<0x\x\+\>" - -" Binary strings -syn match chaiscriptNumber "\<0b[01]\+\>" - -" Various language features -syn keyword chaiscriptCond if else -syn keyword chaiscriptRepeat while for do -syn keyword chaiscriptStatement break continue return switch case default -syn keyword chaiscriptExceptions try catch throw - -"Keyword -syn keyword chaiscriptKeyword def true false attr - -"Built in types -syn keyword chaiscriptType fun var auto - -"Built in funcs, keep it simple -syn keyword chaiscriptFunc eval throw - -"Let's treat all backtick operator function lookups as built in too -syn region chaiscriptFunc matchgroup=chaiscriptFunc start="`" end="`" - -" Account for the "[1..10]" syntax, treating it as an operator -" Intentionally leaving out all of the normal, well known operators -syn match chaiscriptOperator "\.\." - -" Guard seperator as an operator -syn match chaiscriptOperator ":" - -" Comments -syn match chaiscriptComment "//.*$" contains=@Spell -syn region chaiscriptComment matchgroup=chaiscriptComment start="/\*" end="\*/" contains=@Spell - - - -hi def link chaiscriptExceptions Exception -hi def link chaiscriptKeyword Keyword -hi def link chaiscriptStatement Statement -hi def link chaiscriptRepeat Repeat -hi def link chaiscriptString String -hi def link chaiscriptNumber Number -hi def link chaiscriptFloat Float -hi def link chaiscriptOperator Operator -hi def link chaiscriptConstant Constant -hi def link chaiscriptCond Conditional -hi def link chaiscriptFunction Function -hi def link chaiscriptComment Comment -hi def link chaiscriptTodo Todo -hi def link chaiscriptError Error -hi def link chaiscriptSpecial SpecialChar -hi def link chaiscriptFunc Identifier -hi def link chaiscriptType Type -hi def link chaiscriptEval Special - -let b:current_syntax = "chaiscript" - -let &cpo = s:cpo_save -unlet s:cpo_save -" vim: nowrap sw=2 sts=2 ts=8 noet From f6ffcd948118301438b42071edd0367f6c3b7f06 Mon Sep 17 00:00:00 2001 From: arcoRocks <33952704+arcoRocks@users.noreply.github.com> Date: Mon, 4 Dec 2017 13:41:59 +0100 Subject: [PATCH 76/97] Fix for #387 --- include/chaiscript/language/chaiscript_parser.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 77eec4e2..349354d9 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -914,7 +914,7 @@ namespace chaiscript } break; case utility::fnv1a_32("__FUNC__"): { std::string fun_name = "NOT_IN_FUNCTION"; - for (size_t idx = m_match_stack.size() - 1; idx > 0; --idx) + for (size_t idx = m_match_stack.empty() ? 0 : m_match_stack.size() - 1; idx > 0; --idx) { if (m_match_stack[idx-1]->identifier == AST_Node_Type::Id && m_match_stack[idx-0]->identifier == AST_Node_Type::Arg_List) { @@ -927,7 +927,7 @@ namespace chaiscript } break; case utility::fnv1a_32("__CLASS__"): { std::string fun_name = "NOT_IN_CLASS"; - for (size_t idx = m_match_stack.size() - 1; idx > 1; --idx) + for (size_t idx = m_match_stack.empty() ? 0 : m_match_stack.size() - 1; idx > 1; --idx) { if (m_match_stack[idx-2]->identifier == AST_Node_Type::Id && m_match_stack[idx-1]->identifier == AST_Node_Type::Id From e78f8a73f966d987faa1e9cb13a0a7fd62f9f5e1 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Mon, 4 Dec 2017 19:55:38 -0700 Subject: [PATCH 77/97] Add .clang-format file --- .clang-format | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..65317ab1 --- /dev/null +++ b/.clang-format @@ -0,0 +1,107 @@ +IndentWidth: 2 +UseTab: Never + +AccessModifierOffset: -2 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: false +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: TopLevelDefinitions +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyNamespace: true + SplitEmptyRecord: true +BreakAfterJavaFieldAnnotations: false +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Allman +BreakBeforeInheritanceComma: true +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: BeforeComma +BreakConstructorInitializersBeforeComma: true +BreakStringLiterals: true +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 5 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: false +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: true +FixNamespaceComments: false +ForEachMacros: +- foreach +- Q_FOREACH +- BOOST_FOREACH +IncludeCategories: +- Priority: 2 + Regex: ^"(llvm|llvm-c|clang|clang-c)/ +- Priority: 3 + Regex: ^(<|"(gtest|gmock|isl|json)/) +- Priority: 1 + Regex: .* +IncludeIsMainRegex: (Test)?$ +IndentCaseLabels: false +IndentWrappedFunctionNames: false +JavaScriptQuotes: Single +JavaScriptWrapImports: false +KeepEmptyLinesAtTheStartOfBlocks: true +Language: Cpp +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: Inner +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: false +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 23 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 104 +PenaltyBreakString: 916 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 80 +PointerAlignment: Left +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 8 +UseTab: Never + From 6c18c64270b790437d4876d637d08115cc7064ee Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sat, 16 Dec 2017 10:21:02 -0700 Subject: [PATCH 78/97] Formatting updates --- .clang-format | 67 ++++++++++++++++++++++----------------------------- cheatsheet.md | 12 ++++----- 2 files changed, 35 insertions(+), 44 deletions(-) diff --git a/.clang-format b/.clang-format index 65317ab1..d0c095e1 100644 --- a/.clang-format +++ b/.clang-format @@ -1,59 +1,56 @@ -IndentWidth: 2 -UseTab: Never - AccessModifierOffset: -2 AlignAfterOpenBracket: DontAlign AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Left -AlignOperands: false +AlignOperands: true AlignTrailingComments: false -AllowAllParametersOfDeclarationOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: true AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: Inline +AllowShortFunctionsOnASingleLine: All AllowShortIfStatementsOnASingleLine: true -AllowShortLoopsOnASingleLine: false +AllowShortLoopsOnASingleLine: true AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: TopLevelDefinitions +AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: true -AlwaysBreakTemplateDeclarations: true -BinPackArguments: true -BinPackParameters: true +AlwaysBreakTemplateDeclarations: false +BinPackArguments: false +BinPackParameters: false BraceWrapping: - AfterClass: false + AfterClass: true AfterControlStatement: false AfterEnum: false AfterFunction: true AfterNamespace: false AfterObjCDeclaration: false - AfterStruct: false + AfterStruct: true AfterUnion: false BeforeCatch: false BeforeElse: false IndentBraces: false - SplitEmptyFunction: true + SplitEmptyFunction: false SplitEmptyNamespace: true SplitEmptyRecord: true -BreakAfterJavaFieldAnnotations: false -BreakBeforeBinaryOperators: All -BreakBeforeBraces: Allman +BreakAfterJavaFieldAnnotations: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom BreakBeforeInheritanceComma: true -BreakBeforeTernaryOperators: false -BreakConstructorInitializers: BeforeComma -BreakConstructorInitializersBeforeComma: true +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakConstructorInitializersBeforeComma: false BreakStringLiterals: true ColumnLimit: 0 CommentPragmas: '^ IWYU pragma:' -CompactNamespaces: true +CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false -ConstructorInitializerIndentWidth: 5 +ConstructorInitializerIndentWidth: 2 ContinuationIndentWidth: 2 Cpp11BracedListStyle: false DerivePointerAlignment: true DisableFormat: false ExperimentalAutoDetectBinPacking: true -FixNamespaceComments: false +FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH @@ -67,38 +64,32 @@ IncludeCategories: Regex: .* IncludeIsMainRegex: (Test)?$ IndentCaseLabels: false -IndentWrappedFunctionNames: false -JavaScriptQuotes: Single -JavaScriptWrapImports: false +IndentWidth: 2 +IndentWrappedFunctionNames: true +JavaScriptQuotes: Leave +JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: true Language: Cpp MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 2 NamespaceIndentation: Inner -ObjCBlockIndentWidth: 4 +ObjCBlockIndentWidth: 7 ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: false -PenaltyBreakAssignment: 2 -PenaltyBreakBeforeFirstCallParameter: 23 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 104 -PenaltyBreakString: 916 -PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 80 -PointerAlignment: Left +PointerAlignment: Right ReflowComments: true SortIncludes: false SortUsingDeclarations: false -SpaceAfterCStyleCast: true +SpaceAfterCStyleCast: false SpaceAfterTemplateKeyword: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 +SpacesBeforeTrailingComments: 0 SpacesInAngles: false SpacesInCStyleCastParentheses: false -SpacesInContainerLiterals: false +SpacesInContainerLiterals: true SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 diff --git a/cheatsheet.md b/cheatsheet.md index 28debbca..266ddc71 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -5,7 +5,7 @@ ChaiScript tries to follow the [Semantic Versioning](http://semver.org/) scheme. * Major Version Number: API changes / breaking changes * Minor Version Number: New Features * Patch Version Number: Minor changes / enhancements - + # Initializing ChaiScript @@ -37,7 +37,7 @@ chai.add(chaiscript::fun(&Class::method_name, Class_instance_ptr), "method_name" chai.add(chaiscript::fun(&Class::member_name, Class_instance_ptr), "member_name"); ``` -### With Overloads +### With Overloads #### Preferred @@ -69,9 +69,9 @@ chai.add(chaiscript::fun(static_cast(&Derived::data)), "data"); ``` chai.add( chaiscript::fun( - [](bool type) { - if (type) { return "x"; } - else { return "y"; } + [](bool type) { + if (type) { return "x"; } + else { return "y"; } }), "function_name"); ``` @@ -168,7 +168,7 @@ chai.set_global(chaiscript::var(somevar), "somevar"); // global non-const, overw ## Adding Namespaces -Namespaces will not be populated until `import` is called. +Namespaces will not be populated until `import` is called. This saves memory and computing costs if a namespace is not imported into every ChaiScript instance. ``` chai.register_namespace([](chaiscript::Namespace& math) { From 783b8b7361cc3bdf05028b275acf7ad3d6400299 Mon Sep 17 00:00:00 2001 From: Glen Fraser Date: Thu, 11 Jan 2018 19:37:24 +0100 Subject: [PATCH 79/97] In to_json(), maintain the "type" of empty maps and vectors - fix issue #399. - make to_json() with an empty Map, Vector or Dynamic_Object return a similar/compatible type (JSON object or array), rather than "null". - include the fix for #381 (to_json() support for null values), so that can also be unit tested. --- include/chaiscript/utility/json_wrap.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp index 2aa81f34..05b9e404 100644 --- a/include/chaiscript/utility/json_wrap.hpp +++ b/include/chaiscript/utility/json_wrap.hpp @@ -80,7 +80,7 @@ namespace chaiscript try { const std::map m = chaiscript::boxed_cast &>(t_bv); - json::JSON obj; + json::JSON obj(json::JSON::Class::Object); for (const auto &o : m) { obj[o.first] = to_json_object(o.second); @@ -93,7 +93,7 @@ namespace chaiscript try { const std::vector v = chaiscript::boxed_cast &>(t_bv); - json::JSON obj; + json::JSON obj(json::JSON::Class::Array); for (size_t i = 0; i < v.size(); ++i) { obj[i] = to_json_object(v[i]); @@ -132,7 +132,7 @@ namespace chaiscript try { const chaiscript::dispatch::Dynamic_Object &o = boxed_cast(t_bv); - json::JSON obj; + json::JSON obj(json::JSON::Class::Object); for (const auto &attr : o.get_attrs()) { obj[attr.first] = to_json_object(attr.second); @@ -142,6 +142,8 @@ namespace chaiscript // not a dynamic object } + if (t_bv.is_null()) return json::JSON(); // a null value + throw std::runtime_error("Unknown object type to convert to JSON"); } From 3d97c93e494fec79c9e85e46716ab02128984056 Mon Sep 17 00:00:00 2001 From: Glen Fraser Date: Thu, 11 Jan 2018 19:43:32 +0100 Subject: [PATCH 80/97] Add unit test to validate to_json() --- unittests/json_15.chai | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 unittests/json_15.chai diff --git a/unittests/json_15.chai b/unittests/json_15.chai new file mode 100644 index 00000000..7e8ad652 --- /dev/null +++ b/unittests/json_15.chai @@ -0,0 +1,18 @@ +// Various to_json() tests +assert_equal(to_json(-13570), "-13570") +assert_equal(to_json(0.654321), "0.654321") +assert_equal(to_json("ChaiScript"), "\"ChaiScript\"") +assert_equal(to_json(true), "true") +assert_equal(to_json([1, 2, 3]), "[1, 2, 3]") +assert_equal(to_json(Vector()), "[]") // empty vector +assert_equal(to_json([]), "[]") // empty vector +assert_equal(to_json(Map()), "{\n\n}") // empty map +assert_equal(to_json(Dynamic_Object()), "{\n\n}") // empty object + +// Round-trip JSON tests +assert_equal(from_json(to_json([])), []) +assert_equal(from_json(to_json(Map())), Map()) +assert_equal(to_json(from_json("null")), "null") +assert_equal(from_json(to_json(["a": 5, "b": "stuff"])), ["a": 5, "b": "stuff"]) +auto x = [3.5, true, false, "test", [], Vector(), Map()] +assert_equal(from_json(to_json(x)), x) From eb93760f1bb61b82488387ac0e24d613c249b447 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 19 Jan 2018 10:26:31 -0700 Subject: [PATCH 81/97] Fix building on MSVC in C++17 mode Closes #403 #396 #395 --- CMakeLists.txt | 4 ++ include/chaiscript/chaiscript_defines.hpp | 2 +- .../dispatchkit/register_function.hpp | 2 +- .../chaiscript/language/chaiscript_engine.hpp | 72 +++++++++---------- 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index effc7aed..e98ed1d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,6 +170,10 @@ endif() if(MSVC) add_definitions(/W4 /w14545 /w34242 /w34254 /w34287 /w44263 /w44265 /w44296 /w44311 /w44826 /we4289 /w14546 /w14547 /w14549 /w14555 /w14619 /w14905 /w14906 /w14928) + if (BUILD_IN_CPP17_MODE) + add_definitions(/std:c++17) + endif() + if (MSVC_VERSION STREQUAL "1800") # VS2013 doesn't have magic statics add_definitions(/w44640) diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index 4c303307..93d9187e 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -170,7 +170,7 @@ namespace chaiscript { auto parse_num(const char *t_str) -> typename std::enable_if::value, T>::type { T t = 0; - T base; + T base{}; T decimal_place = 0; int exponent = 0; diff --git a/include/chaiscript/dispatchkit/register_function.hpp b/include/chaiscript/dispatchkit/register_function.hpp index e660790d..2206b024 100644 --- a/include/chaiscript/dispatchkit/register_function.hpp +++ b/include/chaiscript/dispatchkit/register_function.hpp @@ -86,7 +86,7 @@ namespace chaiscript // only compile this bit if noexcept is part of the type system // -#if __cpp_noexcept_function_type >= 201510 +#if __cpp_noexcept_function_type >= 201510 || (_MSVC_LANG > 201403L && _MSC_VER >= 1912) template Proxy_Function fun(Ret (*func)(Param...) noexcept) { diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index ed3b1141..8f0c0aa1 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -54,8 +54,8 @@ #include "../dispatchkit/exception_specification.hpp" namespace chaiscript -{ - /// Namespace alias to provide cleaner and more explicit syntax to users. +{ + /// Namespace alias to provide cleaner and more explicit syntax to users. using Namespace = dispatch::Dynamic_Object; namespace detail @@ -200,7 +200,7 @@ namespace chaiscript m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global(t_bv, t_name); }), "add_global"); m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ set_global(t_bv, t_name); }), "set_global"); - m_engine.add(fun([this](const std::string& t_namespace_name) { register_namespace([](Namespace& space) {}, t_namespace_name); import(t_namespace_name); }), "namespace"); + m_engine.add(fun([this](const std::string& t_namespace_name) { register_namespace([](Namespace& /*space*/) {}, t_namespace_name); import(t_namespace_name); }), "namespace"); m_engine.add(fun([this](const std::string& t_namespace_name) { import(t_namespace_name); }), "import"); } @@ -711,39 +711,39 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars return m_engine.boxed_cast(eval_file(t_filename, t_handler)); } - /// \brief Imports a namespace object into the global scope of this ChaiScript instance. - /// \param[in] t_namespace_name Name of the namespace to import. - /// \throw std::runtime_error In the case that the namespace name was never registered. - void import(const std::string& t_namespace_name) - { - chaiscript::detail::threading::unique_lock l(m_use_mutex); - - if (m_engine.get_scripting_objects().count(t_namespace_name)) { - throw std::runtime_error("Namespace: " + t_namespace_name + " was already defined"); - } - else if (m_namespace_generators.count(t_namespace_name)) { - m_engine.add_global(var(std::ref(m_namespace_generators[t_namespace_name]())), t_namespace_name); - } - else { - throw std::runtime_error("No registered namespace: " + t_namespace_name); - } - } - - /// \brief Registers a namespace generator, which delays generation of the namespace until it is imported, saving memory if it is never used. - /// \param[in] t_namespace_generator Namespace generator function. - /// \param[in] t_namespace_name Name of the Namespace function being registered. - /// \throw std::runtime_error In the case that the namespace name was already registered. - void register_namespace(const std::function& t_namespace_generator, const std::string& t_namespace_name) - { - chaiscript::detail::threading::unique_lock l(m_use_mutex); - - if (!m_namespace_generators.count(t_namespace_name)) { - // contain the namespace object memory within the m_namespace_generators map - m_namespace_generators.emplace(std::make_pair(t_namespace_name, [=, space = Namespace()]() mutable -> Namespace& { t_namespace_generator(space); return space; })); - } - else { - throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); - } + /// \brief Imports a namespace object into the global scope of this ChaiScript instance. + /// \param[in] t_namespace_name Name of the namespace to import. + /// \throw std::runtime_error In the case that the namespace name was never registered. + void import(const std::string& t_namespace_name) + { + chaiscript::detail::threading::unique_lock l(m_use_mutex); + + if (m_engine.get_scripting_objects().count(t_namespace_name)) { + throw std::runtime_error("Namespace: " + t_namespace_name + " was already defined"); + } + else if (m_namespace_generators.count(t_namespace_name)) { + m_engine.add_global(var(std::ref(m_namespace_generators[t_namespace_name]())), t_namespace_name); + } + else { + throw std::runtime_error("No registered namespace: " + t_namespace_name); + } + } + + /// \brief Registers a namespace generator, which delays generation of the namespace until it is imported, saving memory if it is never used. + /// \param[in] t_namespace_generator Namespace generator function. + /// \param[in] t_namespace_name Name of the Namespace function being registered. + /// \throw std::runtime_error In the case that the namespace name was already registered. + void register_namespace(const std::function& t_namespace_generator, const std::string& t_namespace_name) + { + chaiscript::detail::threading::unique_lock l(m_use_mutex); + + if (!m_namespace_generators.count(t_namespace_name)) { + // contain the namespace object memory within the m_namespace_generators map + m_namespace_generators.emplace(std::make_pair(t_namespace_name, [=, space = Namespace()]() mutable -> Namespace& { t_namespace_generator(space); return space; })); + } + else { + throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); + } } }; From ad606c7cfa5006fca64fdac7ae183e0e34d36501 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 19 Jan 2018 10:46:37 -0700 Subject: [PATCH 82/97] Update to catch2 for MSVC2017 compat --- unittests/catch.hpp | 19076 ++++++++++++++++++--------------- unittests/compiled_tests.cpp | 8 +- 2 files changed, 10647 insertions(+), 8437 deletions(-) diff --git a/unittests/catch.hpp b/unittests/catch.hpp index a11ecce3..ef720908 100644 --- a/unittests/catch.hpp +++ b/unittests/catch.hpp @@ -1,17 +1,17 @@ /* - * Catch v1.5.7 - * Generated: 2016-09-27 10:45:46.824849 + * Catch v2.1.0 + * Generated: 2018-01-10 13:51:15.378034 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. + * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp -#define TWOBLUECUBES_CATCH_HPP_INCLUDED #ifdef __clang__ # pragma clang system_header @@ -19,34 +19,61 @@ # pragma GCC system_header #endif -// #included from: internal/catch_suppress_warnings.h +// start catch_suppress_warnings.h #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC -# pragma clang diagnostic ignored "-Wglobal-constructors" -# pragma clang diagnostic ignored "-Wvariadic-macros" -# pragma clang diagnostic ignored "-Wc99-extensions" # pragma clang diagnostic ignored "-Wunused-variable" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wc++98-compat" -# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ -# pragma GCC diagnostic ignored "-Wvariadic-macros" # pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wparentheses" # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wpadded" #endif +// end catch_suppress_warnings.h #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS #endif +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED @@ -54,64 +81,37 @@ # endif #endif -// #included from: internal/catch_notimplemented_exception.h -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED +// start catch_user_interfaces.h -// #included from: catch_common.h -#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED +namespace Catch { + unsigned int rngSeed(); +} -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h -#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr -#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) +// start catch_common.h -#include -#include -#include +// start catch_compiler_capabilities.h -// #included from: catch_compiler_capabilities.h -#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED - -// Detect a number of compiler features - mostly C++11/14 conformance - by compiler +// Detect a number of compiler features - by compiler // The following features are defined: // -// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported? -// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported? -// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods -// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? -// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported -// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? -// CATCH_CONFIG_CPP11_OVERRIDE : is override supported? -// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) - -// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? - -// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too // **************** // In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. -// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 - #ifdef __cplusplus -# if __cplusplus >= 201103L -# define CATCH_CPP11_OR_GREATER -# endif - # if __cplusplus >= 201402L # define CATCH_CPP14_OR_GREATER # endif @@ -120,307 +120,151 @@ #ifdef __clang__ -# if __has_feature(cxx_nullptr) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic pop" ) -# if __has_feature(cxx_noexcept) -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif - -# if defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -# endif +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic pop" ) #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// -// Borland -#ifdef __BORLANDC__ +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) -#endif // __BORLANDC__ - -//////////////////////////////////////////////////////////////////////////////// -// EDG -#ifdef __EDG_VERSION__ - -#endif // __EDG_VERSION__ - -//////////////////////////////////////////////////////////////////////////////// -// Digital Mars -#ifdef __DMC__ - -#endif // __DMC__ - -//////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ - -# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS # endif -# if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) -# endif +#endif -// - otherwise more recent versions define __cplusplus >= 201103L -// and will get picked up below +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif -#endif // __GNUC__ +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE + +#endif // __CYGWIN__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ #ifdef _MSC_VER -#if (_MSC_VER >= 1600) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - -#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) -#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#endif +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// -// Use variadic macros if the compiler supports them -#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ - ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ - ( defined __GNUC__ && __GNUC__ >= 3 ) || \ - ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) - -#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS - +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER #endif -// Use __COUNTER__ if the compiler supports it -#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ - ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \ - ( defined __clang__ && __clang_major__ >= 3 ) - -#define CATCH_INTERNAL_CONFIG_COUNTER - -#endif - -//////////////////////////////////////////////////////////////////////////////// -// C++ language feature support - -// catch all support for C++11 -#if defined(CATCH_CPP11_OR_GREATER) - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# endif - -# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# endif - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) -# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG -# endif - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) -# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -# endif - -#endif // __cplusplus >= 201103L - -// Now set the actual defines based on the above + anything the user has configured -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NULLPTR -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NOEXCEPT -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_GENERATED_METHODS -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_IS_ENUM -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_TUPLE -#endif -#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) -# define CATCH_CONFIG_VARIADIC_MACROS -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_LONG_LONG -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_OVERRIDE -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_UNIQUE_PTR -#endif #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER #endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS #endif -// noexcept support: -#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) -# define CATCH_NOEXCEPT noexcept -# define CATCH_NOEXCEPT_IS(x) noexcept(x) +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) #else -# define CATCH_NOEXCEPT throw() -# define CATCH_NOEXCEPT_IS(x) +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) #endif -// nullptr support -#ifdef CATCH_CONFIG_CPP11_NULLPTR -# define CATCH_NULL nullptr -#else -# define CATCH_NULL NULL -#endif - -// override support -#ifdef CATCH_CONFIG_CPP11_OVERRIDE -# define CATCH_OVERRIDE override -#else -# define CATCH_OVERRIDE -#endif - -// unique_ptr support -#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR -# define CATCH_AUTO_PTR( T ) std::unique_ptr -#else -# define CATCH_AUTO_PTR( T ) std::auto_ptr -#endif +#include +#include +#include namespace Catch { - struct IConfig; - struct CaseSensitive { enum Choice { Yes, No }; }; class NonCopyable { -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS NonCopyable( NonCopyable const& ) = delete; NonCopyable( NonCopyable && ) = delete; NonCopyable& operator = ( NonCopyable const& ) = delete; NonCopyable& operator = ( NonCopyable && ) = delete; -#else - NonCopyable( NonCopyable const& info ); - NonCopyable& operator = ( NonCopyable const& ); -#endif protected: - NonCopyable() {} + NonCopyable(); virtual ~NonCopyable(); }; - class SafeBool { - public: - typedef void (SafeBool::*type)() const; - - static type makeSafe( bool value ) { - return value ? &SafeBool::trueValue : 0; - } - private: - void trueValue() const {} - }; - - template - inline void deleteAll( ContainerT& container ) { - typename ContainerT::const_iterator it = container.begin(); - typename ContainerT::const_iterator itEnd = container.end(); - for(; it != itEnd; ++it ) - delete *it; - } - template - inline void deleteAllValues( AssociativeContainerT& container ) { - typename AssociativeContainerT::const_iterator it = container.begin(); - typename AssociativeContainerT::const_iterator itEnd = container.end(); - for(; it != itEnd; ++it ) - delete it->second; - } - - bool startsWith( std::string const& s, std::string const& prefix ); - bool endsWith( std::string const& s, std::string const& suffix ); - bool contains( std::string const& s, std::string const& infix ); - void toLowerInPlace( std::string& s ); - std::string toLower( std::string const& s ); - std::string trim( std::string const& str ); - bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); - - struct pluralise { - pluralise( std::size_t count, std::string const& label ); - - friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); - - std::size_t m_count; - std::string m_label; - }; - struct SourceLineInfo { - SourceLineInfo(); - SourceLineInfo( char const* _file, std::size_t _line ); - SourceLineInfo( SourceLineInfo const& other ); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; SourceLineInfo( SourceLineInfo && ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo& operator = ( SourceLineInfo && ) = default; -# endif - bool empty() const; - bool operator == ( SourceLineInfo const& other ) const; - bool operator < ( SourceLineInfo const& other ) const; - std::string file; + bool empty() const noexcept; + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; std::size_t line; }; std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - // This is just here to avoid compiler warnings with macro constants and boolean literals - inline bool isTrue( bool value ){ return value; } - inline bool alwaysTrue() { return true; } - inline bool alwaysFalse() { return false; } - - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); - - void seedRng( IConfig const& config ); - unsigned int rngSeed(); - // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as // >> stuff +StreamEndStop struct StreamEndStop { - std::string operator+() { - return std::string(); - } + std::string operator+() const; }; template T const& operator + ( T const& value, StreamEndStop ) { @@ -428,200 +272,42 @@ namespace Catch { } } -#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) -#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); - -#include +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) +// end catch_common.h namespace Catch { - class NotImplementedException : public std::exception - { - public: - NotImplementedException( SourceLineInfo const& lineInfo ); - NotImplementedException( NotImplementedException const& ) {} - - virtual ~NotImplementedException() CATCH_NOEXCEPT {} - - virtual const char* what() const CATCH_NOEXCEPT; - - private: - std::string m_what; - SourceLineInfo m_lineInfo; + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); }; } // end namespace Catch -/////////////////////////////////////////////////////////////////////////////// -#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS -// #included from: internal/catch_context.h -#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h -// #included from: catch_interfaces_generators.h -#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED - -#include - -namespace Catch { - - struct IGeneratorInfo { - virtual ~IGeneratorInfo(); - virtual bool moveNext() = 0; - virtual std::size_t getCurrentIndex() const = 0; - }; - - struct IGeneratorsForTest { - virtual ~IGeneratorsForTest(); - - virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; - virtual bool moveNext() = 0; - }; - - IGeneratorsForTest* createGeneratorsForTest(); - -} // end namespace Catch - -// #included from: catch_ptr.hpp -#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -namespace Catch { - - // An intrusive reference counting smart pointer. - // T must implement addRef() and release() methods - // typically implementing the IShared interface - template - class Ptr { - public: - Ptr() : m_p( CATCH_NULL ){} - Ptr( T* p ) : m_p( p ){ - if( m_p ) - m_p->addRef(); - } - Ptr( Ptr const& other ) : m_p( other.m_p ){ - if( m_p ) - m_p->addRef(); - } - ~Ptr(){ - if( m_p ) - m_p->release(); - } - void reset() { - if( m_p ) - m_p->release(); - m_p = CATCH_NULL; - } - Ptr& operator = ( T* p ){ - Ptr temp( p ); - swap( temp ); - return *this; - } - Ptr& operator = ( Ptr const& other ){ - Ptr temp( other ); - swap( temp ); - return *this; - } - void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } - T* get() const{ return m_p; } - T& operator*() const { return *m_p; } - T* operator->() const { return m_p; } - bool operator !() const { return m_p == CATCH_NULL; } - operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } - - private: - T* m_p; - }; - - struct IShared : NonCopyable { - virtual ~IShared(); - virtual void addRef() const = 0; - virtual void release() const = 0; - }; - - template - struct SharedImpl : T { - - SharedImpl() : m_rc( 0 ){} - - virtual void addRef() const { - ++m_rc; - } - virtual void release() const { - if( --m_rc == 0 ) - delete this; - } - - mutable unsigned int m_rc; - }; - -} // end namespace Catch - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif +// start catch_interfaces_testcase.h +#include #include -#include -#include - -namespace Catch { - - class TestCase; - class Stream; - struct IResultCapture; - struct IRunner; - struct IGeneratorsForTest; - struct IConfig; - - struct IContext - { - virtual ~IContext(); - - virtual IResultCapture* getResultCapture() = 0; - virtual IRunner* getRunner() = 0; - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; - virtual bool advanceGeneratorsForCurrentTest() = 0; - virtual Ptr getConfig() const = 0; - }; - - struct IMutableContext : IContext - { - virtual ~IMutableContext(); - virtual void setResultCapture( IResultCapture* resultCapture ) = 0; - virtual void setRunner( IRunner* runner ) = 0; - virtual void setConfig( Ptr const& config ) = 0; - }; - - IContext& getCurrentContext(); - IMutableContext& getCurrentMutableContext(); - void cleanUpContext(); - Stream createStream( std::string const& streamName ); - -} - -// #included from: internal/catch_test_registry.hpp -#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED - -// #included from: catch_interfaces_testcase.h -#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED - -#include namespace Catch { class TestSpec; - struct ITestCase : IShared { + struct ITestInvoker { virtual void invoke () const = 0; - protected: - virtual ~ITestCase(); + virtual ~ITestInvoker(); }; + using ITestCasePtr = std::shared_ptr; + class TestCase; struct IConfig; @@ -637,144 +323,216 @@ namespace Catch { } +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include + +namespace Catch { + + class StringData; + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. c_str() must return a null terminated + /// string, however, and so the StringRef will internally take ownership + /// (taking a copy), if necessary. In theory this ownership is not externally + /// visible - but it does mean (substring) StringRefs should not be shared between + /// threads. + class StringRef { + public: + using size_type = std::size_t; + + private: + friend struct StringRefTestAccess; + + char const* m_start; + size_type m_size; + + char* m_data = nullptr; + + void takeOwnership(); + + static constexpr char const* const s_empty = ""; + + public: // construction/ assignment + StringRef() noexcept + : StringRef( s_empty, 0 ) + {} + + StringRef( StringRef const& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ) + {} + + StringRef( StringRef&& other ) noexcept + : m_start( other.m_start ), + m_size( other.m_size ), + m_data( other.m_data ) + { + other.m_data = nullptr; + } + + StringRef( char const* rawChars ) noexcept; + + StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + ~StringRef() noexcept { + delete[] m_data; + } + + auto operator = ( StringRef const &other ) noexcept -> StringRef& { + delete[] m_data; + m_data = nullptr; + m_start = other.m_start; + m_size = other.m_size; + return *this; + } + + operator std::string() const; + + void swap( StringRef& other ) noexcept; + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != ( StringRef const& other ) const noexcept -> bool; + + auto operator[] ( size_type index ) const noexcept -> char; + + public: // named queries + auto empty() const noexcept -> bool { + return m_size == 0; + } + auto size() const noexcept -> size_type { + return m_size; + } + + auto numberOfCharacters() const noexcept -> size_type; + auto c_str() const -> char const*; + + public: // substrings and searches + auto substr( size_type start, size_type size ) const noexcept -> StringRef; + + private: // ownership queries - may not be consistent between calls + auto isOwned() const noexcept -> bool; + auto isSubstring() const noexcept -> bool; + auto data() const noexcept -> char const*; + }; + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; + auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; + + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } + +} // namespace Catch + +// end catch_stringref.h namespace Catch { template -class MethodTestCase : public SharedImpl { - +class TestInvokerAsMethod : public ITestInvoker { + void (C::*m_testAsMethod)(); public: - MethodTestCase( void (C::*method)() ) : m_method( method ) {} + TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} - virtual void invoke() const { + void invoke() const override { C obj; - (obj.*m_method)(); + (obj.*m_testAsMethod)(); } - -private: - virtual ~MethodTestCase() {} - - void (C::*m_method)(); }; -typedef void(*TestFunction)(); +auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; -struct NameAndDesc { - NameAndDesc( const char* _name = "", const char* _description= "" ) - : name( _name ), description( _description ) - {} +template +auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsMethod( testAsMethod ); +} - const char* name; - const char* description; +struct NameAndTags { + NameAndTags( StringRef name_ = StringRef(), StringRef tags_ = StringRef() ) noexcept; + StringRef name; + StringRef tags; }; -void registerTestCase - ( ITestCase* testCase, - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ); - -struct AutoReg { - - AutoReg - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ); - - template - AutoReg - ( void (C::*method)(), - char const* className, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { - - registerTestCase - ( new MethodTestCase( method ), - className, - nameAndDesc, - lineInfo ); - } - +struct AutoReg : NonCopyable { + AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept; ~AutoReg(); - -private: - AutoReg( AutoReg const& ); - void operator= ( AutoReg const& ); }; -void registerTestCaseFunction - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ); - } // end namespace Catch -#ifdef CATCH_CONFIG_VARIADIC_MACROS +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ + static void TestName() + #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ + namespace{ \ + struct TestName : ClassName { \ + void test(); \ + }; \ + } \ + void TestName::test() + +#endif + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ \ struct TestName : ClassName{ \ void test(); \ }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ void TestName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS -#else - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ - static void TestName(); \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ - static void TestName() - #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ - INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) +// end catch_test_registry.h +// start catch_capture.hpp - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } +// start catch_assertionhandler.h - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ - namespace{ \ - struct TestCaseName : ClassName{ \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ - } \ - void TestCaseName::test() - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ - INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) +// start catch_assertioninfo.h - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ - Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); -#endif - -// #included from: internal/catch_capture.hpp -#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED - -// #included from: catch_result_builder.h -#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED - -// #included from: catch_result_type.h -#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED +// start catch_result_type.h namespace Catch { @@ -799,12 +557,8 @@ namespace Catch { }; }; - inline bool isOk( ResultWas::OfType resultType ) { - return ( resultType & ResultWas::FailureBit ) == 0; - } - inline bool isJustInfo( int flags ) { - return flags == ResultWas::Info; - } + bool isOk( ResultWas::OfType resultType ); + bool isJustInfo( int flags ); // ResultDisposition::Flags enum struct ResultDisposition { enum Flags { @@ -815,699 +569,85 @@ namespace Catch { SuppressFail = 0x08 // Failures are reported but do not fail the test }; }; - inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { - return static_cast( static_cast( lhs ) | static_cast( rhs ) ); - } + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); - inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } - inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } - inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + bool shouldContinueOnFailure( int flags ); + inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + bool shouldSuppressFailure( int flags ); } // end namespace Catch -// #included from: catch_assertionresult.h -#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED - -#include - +// end catch_result_type.h namespace Catch { struct AssertionInfo { - AssertionInfo() {} - AssertionInfo( std::string const& _macroName, - SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ); - - std::string macroName; + StringRef macroName; SourceLineInfo lineInfo; - std::string capturedExpression; + StringRef capturedExpression; ResultDisposition::Flags resultDisposition; - }; - struct AssertionResultData - { - AssertionResultData() : resultType( ResultWas::Unknown ) {} - - std::string reconstructedExpression; - std::string message; - ResultWas::OfType resultType; - }; - - class AssertionResult { - public: - AssertionResult(); - AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); - ~AssertionResult(); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - AssertionResult( AssertionResult const& ) = default; - AssertionResult( AssertionResult && ) = default; - AssertionResult& operator = ( AssertionResult const& ) = default; - AssertionResult& operator = ( AssertionResult && ) = default; -# endif - - bool isOk() const; - bool succeeded() const; - ResultWas::OfType getResultType() const; - bool hasExpression() const; - bool hasMessage() const; - std::string getExpression() const; - std::string getExpressionInMacro() const; - bool hasExpandedExpression() const; - std::string getExpandedExpression() const; - std::string getMessage() const; - SourceLineInfo getSourceInfo() const; - std::string getTestMacroName() const; - - protected: - AssertionInfo m_info; - AssertionResultData m_resultData; + // We want to delete this constructor but a compiler bug in 4.8 means + // the struct is then treated as non-aggregate + //AssertionInfo() = delete; }; } // end namespace Catch -// #included from: catch_matchers.hpp -#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED +// end catch_assertioninfo.h +// start catch_decomposer.h -namespace Catch { -namespace Matchers { - namespace Impl { +// start catch_tostring.h - namespace Generic { - template class AllOf; - template class AnyOf; - template class Not; - } - - template - struct Matcher : SharedImpl - { - typedef ExpressionT ExpressionType; - - virtual ~Matcher() {} - virtual Ptr clone() const = 0; - virtual bool match( ExpressionT const& expr ) const = 0; - virtual std::string toString() const = 0; - - Generic::AllOf operator && ( Matcher const& other ) const; - Generic::AnyOf operator || ( Matcher const& other ) const; - Generic::Not operator ! () const; - }; - - template - struct MatcherImpl : Matcher { - - virtual Ptr > clone() const { - return Ptr >( new DerivedT( static_cast( *this ) ) ); - } - }; - - namespace Generic { - template - class Not : public MatcherImpl, ExpressionT> { - public: - explicit Not( Matcher const& matcher ) : m_matcher(matcher.clone()) {} - Not( Not const& other ) : m_matcher( other.m_matcher ) {} - - virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE { - return !m_matcher->match( expr ); - } - - virtual std::string toString() const CATCH_OVERRIDE { - return "not " + m_matcher->toString(); - } - private: - Ptr< Matcher > m_matcher; - }; - - template - class AllOf : public MatcherImpl, ExpressionT> { - public: - - AllOf() {} - AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} - - AllOf& add( Matcher const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( !m_matchers[i]->match( expr ) ) - return false; - return true; - } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) - oss << " and "; - oss << m_matchers[i]->toString(); - } - oss << " )"; - return oss.str(); - } - - AllOf operator && ( Matcher const& other ) const { - AllOf allOfExpr( *this ); - allOfExpr.add( other ); - return allOfExpr; - } - - private: - std::vector > > m_matchers; - }; - - template - class AnyOf : public MatcherImpl, ExpressionT> { - public: - - AnyOf() {} - AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} - - AnyOf& add( Matcher const& matcher ) { - m_matchers.push_back( matcher.clone() ); - return *this; - } - virtual bool match( ExpressionT const& expr ) const - { - for( std::size_t i = 0; i < m_matchers.size(); ++i ) - if( m_matchers[i]->match( expr ) ) - return true; - return false; - } - virtual std::string toString() const { - std::ostringstream oss; - oss << "( "; - for( std::size_t i = 0; i < m_matchers.size(); ++i ) { - if( i != 0 ) - oss << " or "; - oss << m_matchers[i]->toString(); - } - oss << " )"; - return oss.str(); - } - - AnyOf operator || ( Matcher const& other ) const { - AnyOf anyOfExpr( *this ); - anyOfExpr.add( other ); - return anyOfExpr; - } - - private: - std::vector > > m_matchers; - }; - - } // namespace Generic - - template - Generic::AllOf Matcher::operator && ( Matcher const& other ) const { - Generic::AllOf allOfExpr; - allOfExpr.add( *this ); - allOfExpr.add( other ); - return allOfExpr; - } - - template - Generic::AnyOf Matcher::operator || ( Matcher const& other ) const { - Generic::AnyOf anyOfExpr; - anyOfExpr.add( *this ); - anyOfExpr.add( other ); - return anyOfExpr; - } - - template - Generic::Not Matcher::operator ! () const { - return Generic::Not( *this ); - } - - namespace StdString { - - inline std::string makeString( std::string const& str ) { return str; } - inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } - - struct CasedString - { - CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_str( adjustString( str ) ) - {} - std::string adjustString( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No - ? toLower( str ) - : str; - - } - std::string toStringSuffix() const - { - return m_caseSensitivity == CaseSensitive::No - ? " (case insensitive)" - : ""; - } - CaseSensitive::Choice m_caseSensitivity; - std::string m_str; - }; - - struct Equals : MatcherImpl { - Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( str, caseSensitivity ) - {} - Equals( Equals const& other ) : m_data( other.m_data ){} - - virtual ~Equals(); - - virtual bool match( std::string const& expr ) const { - return m_data.m_str == m_data.adjustString( expr );; - } - virtual std::string toString() const { - return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; - }; - - struct Contains : MatcherImpl { - Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - Contains( Contains const& other ) : m_data( other.m_data ){} - - virtual ~Contains(); - - virtual bool match( std::string const& expr ) const { - return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos; - } - virtual std::string toString() const { - return "contains: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; - }; - - struct StartsWith : MatcherImpl { - StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - - StartsWith( StartsWith const& other ) : m_data( other.m_data ){} - - virtual ~StartsWith(); - - virtual bool match( std::string const& expr ) const { - return startsWith( m_data.adjustString( expr ), m_data.m_str ); - } - virtual std::string toString() const { - return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; - }; - - struct EndsWith : MatcherImpl { - EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) - : m_data( substr, caseSensitivity ){} - EndsWith( EndsWith const& other ) : m_data( other.m_data ){} - - virtual ~EndsWith(); - - virtual bool match( std::string const& expr ) const { - return endsWith( m_data.adjustString( expr ), m_data.m_str ); - } - virtual std::string toString() const { - return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix(); - } - - CasedString m_data; - }; - } // namespace StdString - } // namespace Impl - - // The following functions create the actual matcher objects. - // This allows the types to be inferred - template - inline Impl::Generic::Not Not( Impl::Matcher const& m ) { - return Impl::Generic::Not( m ); - } - - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ); - } - template - inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3 ) { - return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); - } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ); - } - template - inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, - Impl::Matcher const& m2, - Impl::Matcher const& m3 ) { - return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); - } - - inline Impl::StdString::Equals Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Equals( str, caseSensitivity ); - } - inline Impl::StdString::Equals Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity ); - } - inline Impl::StdString::Contains Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Contains( substr, caseSensitivity ); - } - inline Impl::StdString::Contains Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { - return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity ); - } - inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { - return Impl::StdString::StartsWith( substr ); - } - inline Impl::StdString::StartsWith StartsWith( const char* substr ) { - return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); - } - inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { - return Impl::StdString::EndsWith( substr ); - } - inline Impl::StdString::EndsWith EndsWith( const char* substr ) { - return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); - } - -} // namespace Matchers - -using namespace Matchers; - -} // namespace Catch - -namespace Catch { - - struct TestFailureException{}; - - template class ExpressionLhs; - - struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; - - struct CopyableStream { - CopyableStream() {} - CopyableStream( CopyableStream const& other ) { - oss << other.oss.str(); - } - CopyableStream& operator=( CopyableStream const& other ) { - oss.str(""); - oss << other.oss.str(); - return *this; - } - std::ostringstream oss; - }; - - class ResultBuilder { - public: - ResultBuilder( char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition, - char const* secondArg = "" ); - - template - ExpressionLhs operator <= ( T const& operand ); - ExpressionLhs operator <= ( bool value ); - - template - ResultBuilder& operator << ( T const& value ) { - m_stream.oss << value; - return *this; - } - - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); - - ResultBuilder& setResultType( ResultWas::OfType result ); - ResultBuilder& setResultType( bool result ); - ResultBuilder& setLhs( std::string const& lhs ); - ResultBuilder& setRhs( std::string const& rhs ); - ResultBuilder& setOp( std::string const& op ); - - void endExpression(); - - std::string reconstructExpression() const; - AssertionResult build() const; - - void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); - void captureResult( ResultWas::OfType resultType ); - void captureExpression(); - void captureExpectedException( std::string const& expectedMessage ); - void captureExpectedException( Matchers::Impl::Matcher const& matcher ); - void handleResult( AssertionResult const& result ); - void react(); - bool shouldDebugBreak() const; - bool allowThrows() const; - - private: - AssertionInfo m_assertionInfo; - AssertionResultData m_data; - struct ExprComponents { - ExprComponents() : testFalse( false ) {} - bool testFalse; - std::string lhs, rhs, op; - } m_exprComponents; - CopyableStream m_stream; - - bool m_shouldDebugBreak; - bool m_shouldThrow; - }; - -} // namespace Catch - -// Include after due to circular dependency: -// #included from: catch_expression_lhs.hpp -#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED - -// #included from: catch_evaluate.hpp -#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4389) // '==' : signed/unsigned mismatch -#endif - -#include - -namespace Catch { -namespace Internal { - - enum Operator { - IsEqualTo, - IsNotEqualTo, - IsLessThan, - IsGreaterThan, - IsLessThanOrEqualTo, - IsGreaterThanOrEqualTo - }; - - template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; - template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; - template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; - template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; - template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; - template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; - template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; - - template - inline T& opCast(T const& t) { return const_cast(t); } - -// nullptr_t support based on pull request #154 from Konstantin Baumann -#ifdef CATCH_CONFIG_CPP11_NULLPTR - inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } -#endif // CATCH_CONFIG_CPP11_NULLPTR - - // So the compare overloads can be operator agnostic we convey the operator as a template - // enum, which is used to specialise an Evaluator for doing the comparison. - template - class Evaluator{}; - - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs) { - return bool( opCast( lhs ) == opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) != opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) < opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) > opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) >= opCast( rhs ) ); - } - }; - template - struct Evaluator { - static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return bool( opCast( lhs ) <= opCast( rhs ) ); - } - }; - - template - bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { - return Evaluator::evaluate( lhs, rhs ); - } - - // This level of indirection allows us to specialise for integer types - // to avoid signed/ unsigned warnings - - // "base" overload - template - bool compare( T1 const& lhs, T2 const& rhs ) { - return Evaluator::evaluate( lhs, rhs ); - } - - // unsigned X to int - template bool compare( unsigned int lhs, int rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned long lhs, int rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned char lhs, int rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - - // unsigned X to long - template bool compare( unsigned int lhs, long rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned long lhs, long rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - template bool compare( unsigned char lhs, long rhs ) { - return applyEvaluator( lhs, static_cast( rhs ) ); - } - - // int to unsigned X - template bool compare( int lhs, unsigned int rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( int lhs, unsigned long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( int lhs, unsigned char rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - - // long to unsigned X - template bool compare( long lhs, unsigned int rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long lhs, unsigned long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long lhs, unsigned char rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - - // pointer to long (when comparing against NULL) - template bool compare( long lhs, T* rhs ) { - return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); - } - template bool compare( T* lhs, long rhs ) { - return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); - } - - // pointer to int (when comparing against NULL) - template bool compare( int lhs, T* rhs ) { - return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); - } - template bool compare( T* lhs, int rhs ) { - return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); - } - -#ifdef CATCH_CONFIG_CPP11_LONG_LONG - // long long to unsigned X - template bool compare( long long lhs, unsigned int rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long long lhs, unsigned long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long long lhs, unsigned long long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( long long lhs, unsigned char rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - - // unsigned long long to X - template bool compare( unsigned long long lhs, int rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( unsigned long long lhs, long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( unsigned long long lhs, long long rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - template bool compare( unsigned long long lhs, char rhs ) { - return applyEvaluator( static_cast( lhs ), rhs ); - } - - // pointer to long long (when comparing against NULL) - template bool compare( long long lhs, T* rhs ) { - return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); - } - template bool compare( T* lhs, long long rhs ) { - return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); - } -#endif // CATCH_CONFIG_CPP11_LONG_LONG - -#ifdef CATCH_CONFIG_CPP11_NULLPTR - // pointer to nullptr_t (when comparing against nullptr) - template bool compare( std::nullptr_t, T* rhs ) { - return Evaluator::evaluate( nullptr, rhs ); - } - template bool compare( T* lhs, std::nullptr_t ) { - return Evaluator::evaluate( lhs, nullptr ); - } -#endif // CATCH_CONFIG_CPP11_NULLPTR - -} // end of namespace Internal -} // end of namespace Catch - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -// #included from: catch_tostring.h -#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED - -#include -#include -#include #include #include +#include +#include +// start catch_stream.h + +#include +#include +#include + +namespace Catch { + + std::ostream& cout(); + std::ostream& cerr(); + std::ostream& clog(); + + class StringRef; + + struct IStream { + virtual ~IStream(); + virtual std::ostream& stream() const = 0; + }; + + auto makeStream( StringRef const &filename ) -> IStream const*; + + class ReusableStringStream { + std::size_t m_index; + std::ostream* m_oss; + public: + ReusableStringStream(); + ~ReusableStringStream(); + + auto str() const -> std::string; + + template + auto operator << ( T const& value ) -> ReusableStringStream& { + *m_oss << value; + return *this; + } + auto get() -> std::ostream& { return *m_oss; } + + static void cleanup(); + }; +} + +// end catch_stream.h #ifdef __OBJC__ -// #included from: catch_objc_arc.hpp -#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED +// start catch_objc_arc.hpp #import @@ -1549,357 +689,821 @@ inline id performOptionalSelector( id obj, SEL sel ) { #define CATCH_ARC_STRONG __strong #endif +// end catch_objc_arc.hpp #endif -#ifdef CATCH_CONFIG_CPP11_TUPLE -#include +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless #endif -#ifdef CATCH_CONFIG_CPP11_IS_ENUM -#include -#endif +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); namespace Catch { + // Bring in operator<< from global namespace into Catch namespace + using ::operator<<; -// Why we're here. -template -std::string toString( T const& value ); + namespace Detail { -// Built in overloads + extern const std::string unprintableString; -std::string toString( std::string const& value ); -std::string toString( std::wstring const& value ); -std::string toString( const char* const value ); -std::string toString( char* const value ); -std::string toString( const wchar_t* const value ); -std::string toString( wchar_t* const value ); -std::string toString( int value ); -std::string toString( unsigned long value ); -std::string toString( unsigned int value ); -std::string toString( const double value ); -std::string toString( const float value ); -std::string toString( bool value ); -std::string toString( char value ); -std::string toString( signed char value ); -std::string toString( unsigned char value ); + std::string rawMemoryToString( const void *object, std::size_t size ); -#ifdef CATCH_CONFIG_CPP11_LONG_LONG -std::string toString( long long value ); -std::string toString( unsigned long long value ); -#endif + template + std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } -#ifdef CATCH_CONFIG_CPP11_NULLPTR -std::string toString( std::nullptr_t ); -#endif + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype(std::declval() << std::declval(), std::true_type()); -#ifdef __OBJC__ - std::string toString( NSString const * const& nsstring ); - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); - std::string toString( NSObject* const& nsObject ); -#endif + template + static auto test(...)->std::false_type; -namespace Detail { + public: + static const bool value = decltype(test(0))::value; + }; - extern const std::string unprintableString; + template + std::string convertUnknownEnumToString( E e ); - struct BorgType { - template BorgType( T const& ); - }; + template + typename std::enable_if::value, std::string>::type convertUnstreamable( T const& ) { + return Detail::unprintableString; + }; + template + typename std::enable_if::value, std::string>::type convertUnstreamable( T const& value ) { + return convertUnknownEnumToString( value ); + }; - struct TrueType { char sizer[1]; }; - struct FalseType { char sizer[2]; }; + } // namespace Detail - TrueType& testStreamable( std::ostream& ); - FalseType testStreamable( FalseType ); + // If we decide for C++14, change these to enable_if_ts + template + struct StringMaker { + template + static + typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type + convert(const Fake& value) { + ReusableStringStream rss; + rss << value; + return rss.str(); + } - FalseType operator<<( std::ostream const&, BorgType const& ); - - template - struct IsStreamInsertable { - static std::ostream &s; - static T const&t; - enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; - }; - -#if defined(CATCH_CONFIG_CPP11_IS_ENUM) - template::value - > - struct EnumStringMaker - { - static std::string convert( T const& ) { return unprintableString; } - }; - - template - struct EnumStringMaker - { - static std::string convert( T const& v ) - { - return ::Catch::toString( - static_cast::type>(v) - ); + template + static + typename std::enable_if::value, std::string>::type + convert( const Fake& value ) { + return Detail::convertUnstreamable( value ); } }; -#endif - template - struct StringMakerBase { -#if defined(CATCH_CONFIG_CPP11_IS_ENUM) - template - static std::string convert( T const& v ) - { - return EnumStringMaker::convert( v ); + + namespace Detail { + + // This function dispatches all stringification requests inside of Catch. + // Should be preferably called fully qualified, like ::Catch::Detail::stringify + template + std::string stringify(const T& e) { + return ::Catch::StringMaker::type>::type>::convert(e); } -#else - template - static std::string convert( T const& ) { return unprintableString; } -#endif + + template + std::string convertUnknownEnumToString( E e ) { + return ::Catch::Detail::stringify(static_cast::type>(e)); + } + + } // namespace Detail + + // Some predefined specializations + + template<> + struct StringMaker { + static std::string convert(const std::string& str); + }; + template<> + struct StringMaker { + static std::string convert(const std::wstring& wstr); }; template<> - struct StringMakerBase { - template - static std::string convert( T const& _value ) { - std::ostringstream oss; - oss << _value; - return oss.str(); + struct StringMaker { + static std::string convert(char const * str); + }; + template<> + struct StringMaker { + static std::string convert(char * str); + }; + template<> + struct StringMaker { + static std::string convert(wchar_t const * str); + }; + template<> + struct StringMaker { + static std::string convert(wchar_t * str); + }; + + template + struct is_string_array : std::false_type {}; + + template + struct is_string_array : std::true_type {}; + + template + struct is_string_array : std::true_type {}; + + template + struct is_string_array : std::true_type {}; + + template + struct StringMaker { + static std::string convert(const char* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + template + struct StringMaker { + static std::string convert(const char* str) { + return ::Catch::Detail::stringify(std::string{ str }); + } + }; + template + struct StringMaker { + static std::string convert(const char* str) { + return ::Catch::Detail::stringify(std::string{ str }); } }; - std::string rawMemoryToString( const void *object, std::size_t size ); + template<> + struct StringMaker { + static std::string convert(int value); + }; + template<> + struct StringMaker { + static std::string convert(long value); + }; + template<> + struct StringMaker { + static std::string convert(long long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned int value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long long value); + }; - template - inline std::string rawMemoryToString( const T& object ) { - return rawMemoryToString( &object, sizeof(object) ); - } + template<> + struct StringMaker { + static std::string convert(bool b); + }; -} // end namespace Detail + template<> + struct StringMaker { + static std::string convert(char c); + }; + template<> + struct StringMaker { + static std::string convert(signed char c); + }; + template<> + struct StringMaker { + static std::string convert(unsigned char c); + }; -template -struct StringMaker : - Detail::StringMakerBase::value> {}; + template<> + struct StringMaker { + static std::string convert(std::nullptr_t); + }; -template -struct StringMaker { - template - static std::string convert( U* p ) { - if( !p ) - return "NULL"; - else - return Detail::rawMemoryToString( p ); - } -}; + template<> + struct StringMaker { + static std::string convert(float value); + }; + template<> + struct StringMaker { + static std::string convert(double value); + }; -template -struct StringMaker { - static std::string convert( R C::* p ) { - if( !p ) - return "NULL"; - else - return Detail::rawMemoryToString( p ); - } -}; + template + struct StringMaker { + template + static std::string convert(U* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; -namespace Detail { - template - std::string rangeToString( InputIterator first, InputIterator last ); -} - -//template -//struct StringMaker > { -// static std::string convert( std::vector const& v ) { -// return Detail::rangeToString( v.begin(), v.end() ); -// } -//}; - -template -std::string toString( std::vector const& v ) { - return Detail::rangeToString( v.begin(), v.end() ); -} - -#ifdef CATCH_CONFIG_CPP11_TUPLE - -// toString for tuples -namespace TupleDetail { - template< - typename Tuple, - std::size_t N = 0, - bool = (N < std::tuple_size::value) - > - struct ElementPrinter { - static void print( const Tuple& tuple, std::ostream& os ) - { - os << ( N ? ", " : " " ) - << Catch::toString(std::get(tuple)); - ElementPrinter::print(tuple,os); - } - }; - - template< - typename Tuple, - std::size_t N - > - struct ElementPrinter { - static void print( const Tuple&, std::ostream& ) {} - }; - -} - -template -struct StringMaker> { - - static std::string convert( const std::tuple& tuple ) - { - std::ostringstream os; - os << '{'; - TupleDetail::ElementPrinter>::print( tuple, os ); - os << " }"; - return os.str(); - } -}; -#endif // CATCH_CONFIG_CPP11_TUPLE - -namespace Detail { - template - std::string makeString( T const& value ) { - return StringMaker::convert( value ); - } -} // end namespace Detail - -/// \brief converts any type to a string -/// -/// The default template forwards on to ostringstream - except when an -/// ostringstream overload does not exist - in which case it attempts to detect -/// that and writes {?}. -/// Overload (not specialise) this template for custom typs that you don't want -/// to provide an ostream overload for. -template -std::string toString( T const& value ) { - return StringMaker::convert( value ); -} + template + struct StringMaker { + static std::string convert(R C::* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; namespace Detail { - template - std::string rangeToString( InputIterator first, InputIterator last ) { - std::ostringstream oss; - oss << "{ "; - if( first != last ) { - oss << Catch::toString( *first ); - for( ++first ; first != last ; ++first ) - oss << ", " << Catch::toString( *first ); + template + std::string rangeToString(InputIterator first, InputIterator last) { + ReusableStringStream rss; + rss << "{ "; + if (first != last) { + rss << ::Catch::Detail::stringify(*first); + for (++first; first != last; ++first) + rss << ", " << ::Catch::Detail::stringify(*first); + } + rss << " }"; + return rss.str(); } - oss << " }"; - return oss.str(); - } -} - -} // end namespace Catch - -namespace Catch { - -// Wraps the LHS of an expression and captures the operator and RHS (if any) - -// wrapping them all in a ResultBuilder object -template -class ExpressionLhs { - ExpressionLhs& operator = ( ExpressionLhs const& ); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - ExpressionLhs& operator = ( ExpressionLhs && ) = delete; -# endif - -public: - ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {} -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - ExpressionLhs( ExpressionLhs const& ) = default; - ExpressionLhs( ExpressionLhs && ) = default; -# endif - - template - ResultBuilder& operator == ( RhsT const& rhs ) { - return captureExpression( rhs ); } - template - ResultBuilder& operator != ( RhsT const& rhs ) { - return captureExpression( rhs ); - } +#ifdef __OBJC__ + template<> + struct StringMaker { + static std::string convert(NSString * nsstring) { + if (!nsstring) + return "nil"; + return std::string("@") + [nsstring UTF8String]; + } + }; + template<> + struct StringMaker { + static std::string convert(NSObject* nsObject) { + return ::Catch::Detail::stringify([nsObject description]); + } - template - ResultBuilder& operator < ( RhsT const& rhs ) { - return captureExpression( rhs ); - } + }; + namespace Detail { + inline std::string stringify( NSString* nsstring ) { + return StringMaker::convert( nsstring ); + } - template - ResultBuilder& operator > ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - template - ResultBuilder& operator <= ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - template - ResultBuilder& operator >= ( RhsT const& rhs ) { - return captureExpression( rhs ); - } - - ResultBuilder& operator == ( bool rhs ) { - return captureExpression( rhs ); - } - - ResultBuilder& operator != ( bool rhs ) { - return captureExpression( rhs ); - } - - void endExpression() { - bool value = m_lhs ? true : false; - m_rb - .setLhs( Catch::toString( value ) ) - .setResultType( value ) - .endExpression(); - } - - // Only simple binary expressions are allowed on the LHS. - // If more complex compositions are required then place the sub expression in parentheses - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); - template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); - -private: - template - ResultBuilder& captureExpression( RhsT const& rhs ) { - return m_rb - .setResultType( Internal::compare( m_lhs, rhs ) ) - .setLhs( Catch::toString( m_lhs ) ) - .setRhs( Catch::toString( rhs ) ) - .setOp( Internal::OperatorTraits::getName() ); - } - -private: - ResultBuilder& m_rb; - T m_lhs; -}; - -} // end namespace Catch - - -namespace Catch { - - template - inline ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { - return ExpressionLhs( *this, operand ); - } - - inline ExpressionLhs ResultBuilder::operator <= ( bool value ) { - return ExpressionLhs( *this, value ); - } + } // namespace Detail +#endif // __OBJC__ } // namespace Catch -// #included from: catch_message.h -#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED +////////////////////////////////////////////////////// +// Separate std-lib types stringification, so it can be selectively enabled +// This means that we do not bring in + +#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) +# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +// Separate std::pair specialization +#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) +#include +namespace Catch { + template + struct StringMaker > { + static std::string convert(const std::pair& pair) { + ReusableStringStream rss; + rss << "{ " + << ::Catch::Detail::stringify(pair.first) + << ", " + << ::Catch::Detail::stringify(pair.second) + << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER + +// Separate std::tuple specialization +#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) +#include +namespace Catch { + namespace Detail { + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size::value) + > + struct TupleElementPrinter { + static void print(const Tuple& tuple, std::ostream& os) { + os << (N ? ", " : " ") + << ::Catch::Detail::stringify(std::get(tuple)); + TupleElementPrinter::print(tuple, os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct TupleElementPrinter { + static void print(const Tuple&, std::ostream&) {} + }; + + } + + template + struct StringMaker> { + static std::string convert(const std::tuple& tuple) { + ReusableStringStream rss; + rss << '{'; + Detail::TupleElementPrinter>::print(tuple, rss.get()); + rss << " }"; + return rss.str(); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER + +namespace Catch { + struct not_this_one {}; // Tag type for detecting which begin/ end are being selected + + // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace + using std::begin; + using std::end; + + not_this_one begin( ... ); + not_this_one end( ... ); + + template + struct is_range { + static const bool value = + !std::is_same())), not_this_one>::value && + !std::is_same())), not_this_one>::value; + }; + + template + std::string rangeToString( Range const& range ) { + return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); + } + + // Handle vector specially + template + std::string rangeToString( std::vector const& v ) { + ReusableStringStream rss; + rss << "{ "; + bool first = true; + for( bool b : v ) { + if( first ) + first = false; + else + rss << ", "; + rss << ::Catch::Detail::stringify( b ); + } + rss << " }"; + return rss.str(); + } + + template + struct StringMaker::value && !is_string_array::value>::type> { + static std::string convert( R const& range ) { + return rangeToString( range ); + } + }; + +} // namespace Catch + +// Separate std::chrono::duration specialization +#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#include +#include +#include + +namespace Catch { + +template +struct ratio_string { + static std::string symbol(); +}; + +template +std::string ratio_string::symbol() { + Catch::ReusableStringStream rss; + rss << '[' << Ratio::num << '/' + << Ratio::den << ']'; + return rss.str(); +} +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; +template <> +struct ratio_string { + static std::string symbol(); +}; + + //////////// + // std::chrono::duration specializations + template + struct StringMaker> { + static std::string convert(std::chrono::duration const& duration) { + ReusableStringStream rss; + rss << duration.count() << ' ' << ratio_string::symbol() << 's'; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " s"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " m"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const& duration) { + ReusableStringStream rss; + rss << duration.count() << " h"; + return rss.str(); + } + }; + + //////////// + // std::chrono::time_point specialization + // Generic time_point cannot be specialized, only std::chrono::time_point + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; + } + }; + // std::chrono::time_point specialization + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const& time_point) { + auto converted = std::chrono::system_clock::to_time_t(time_point); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &converted); +#else + std::tm* timeInfo = std::gmtime(&converted); +#endif + + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + }; +} +#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_tostring.h +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#pragma warning(disable:4018) // more "signed/unsigned mismatch" +#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +#pragma warning(disable:4180) // qualifier applied to function type has no meaning +#endif + +namespace Catch { + + struct ITransientExpression { + auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + auto getResult() const -> bool { return m_result; } + virtual void streamReconstructedExpression( std::ostream &os ) const = 0; + + ITransientExpression( bool isBinaryExpression, bool result ) + : m_isBinaryExpression( isBinaryExpression ), + m_result( result ) + {} + + // We don't actually need a virtual destructor, but many static analysers + // complain if it's not here :-( + virtual ~ITransientExpression(); + + bool m_isBinaryExpression; + bool m_result; + + }; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); + + template + class BinaryExpr : public ITransientExpression { + LhsT m_lhs; + StringRef m_op; + RhsT m_rhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + formatReconstructedExpression + ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); + } + + public: + BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) + : ITransientExpression{ true, comparisonResult }, + m_lhs( lhs ), + m_op( op ), + m_rhs( rhs ) + {} + }; + + template + class UnaryExpr : public ITransientExpression { + LhsT m_lhs; + + void streamReconstructedExpression( std::ostream &os ) const override { + os << Catch::Detail::stringify( m_lhs ); + } + + public: + explicit UnaryExpr( LhsT lhs ) + : ITransientExpression{ false, lhs ? true : false }, + m_lhs( lhs ) + {} + }; + + // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) + template + auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return lhs == rhs; }; + template + auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } + template + auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + template + auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } + + template + auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return lhs != rhs; }; + template + auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } + template + auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + template + auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } + + template + class ExprLhs { + LhsT m_lhs; + public: + explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} + + template + auto operator == ( RhsT const& rhs ) -> BinaryExpr const { + return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; + } + auto operator == ( bool rhs ) -> BinaryExpr const { + return { m_lhs == rhs, m_lhs, "==", rhs }; + } + + template + auto operator != ( RhsT const& rhs ) -> BinaryExpr const { + return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; + } + auto operator != ( bool rhs ) -> BinaryExpr const { + return { m_lhs != rhs, m_lhs, "!=", rhs }; + } + + template + auto operator > ( RhsT const& rhs ) -> BinaryExpr const { + return { m_lhs > rhs, m_lhs, ">", rhs }; + } + template + auto operator < ( RhsT const& rhs ) -> BinaryExpr const { + return { m_lhs < rhs, m_lhs, "<", rhs }; + } + template + auto operator >= ( RhsT const& rhs ) -> BinaryExpr const { + return { m_lhs >= rhs, m_lhs, ">=", rhs }; + } + template + auto operator <= ( RhsT const& rhs ) -> BinaryExpr const { + return { m_lhs <= rhs, m_lhs, "<=", rhs }; + } + + auto makeUnaryExpr() const -> UnaryExpr { + return UnaryExpr{ m_lhs }; + } + }; + + void handleExpression( ITransientExpression const& expr ); + + template + void handleExpression( ExprLhs const& expr ) { + handleExpression( expr.makeUnaryExpr() ); + } + + struct Decomposer { + template + auto operator <= ( T const& lhs ) -> ExprLhs { + return ExprLhs{ lhs }; + } + + auto operator <=( bool value ) -> ExprLhs { + return ExprLhs{ value }; + } + }; + +} // end namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// end catch_decomposer.h +// start catch_interfaces_capture.h + +#include + +namespace Catch { + + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + struct Counts; + struct BenchmarkInfo; + struct BenchmarkStats; + struct AssertionReaction; + + struct ITransientExpression; + + struct IResultCapture { + + virtual ~IResultCapture(); + + virtual bool sectionStarted( SectionInfo const& sectionInfo, + Counts& assertions ) = 0; + virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + + virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; + virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0; + + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 0; + + virtual void handleFatalErrorCondition( StringRef message ) = 0; + + virtual void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) = 0; + virtual void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) = 0; + virtual void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) = 0; + virtual void handleIncomplete + ( AssertionInfo const& info ) = 0; + virtual void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) = 0; + + virtual bool lastAssertionPassed() = 0; + virtual void assertionPassed() = 0; + + // Deprecated, do not use: + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; + }; + + IResultCapture& getResultCapture(); +} + +// end catch_interfaces_capture.h +namespace Catch { + + struct TestFailureException{}; + struct AssertionResultData; + struct IResultCapture; + class RunContext; + + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; + + ITransientExpression const* m_transientExpression = nullptr; + bool m_isNegated; + public: + LazyExpression( bool isNegated ); + LazyExpression( LazyExpression const& other ); + LazyExpression& operator = ( LazyExpression const& ) = delete; + + explicit operator bool() const; + + friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; + }; + + struct AssertionReaction { + bool shouldDebugBreak = false; + bool shouldThrow = false; + }; + + class AssertionHandler { + AssertionInfo m_assertionInfo; + AssertionReaction m_reaction; + bool m_completed = false; + IResultCapture& m_resultCapture; + + public: + AssertionHandler + ( StringRef macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ); + ~AssertionHandler() { + if ( !m_completed ) { + m_resultCapture.handleIncomplete( m_assertionInfo ); + } + } + + template + void handleExpr( ExprLhs const& expr ) { + handleExpr( expr.makeUnaryExpr() ); + } + void handleExpr( ITransientExpression const& expr ); + + void handleMessage(ResultWas::OfType resultType, StringRef const& message); + + void handleExceptionThrownAsExpected(); + void handleUnexpectedExceptionNotThrown(); + void handleExceptionNotThrownAsExpected(); + void handleThrowingCallSkipped(); + void handleUnexpectedInflightException(); + + void complete(); + void setCompleted(); + + // query + auto allowThrows() const -> bool; + }; + + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ); + +} // namespace Catch + +// end catch_assertionhandler.h +// start catch_message.h #include @@ -1911,27 +1515,32 @@ namespace Catch { ResultWas::OfType _type ); std::string macroName; + std::string message; SourceLineInfo lineInfo; ResultWas::OfType type; - std::string message; unsigned int sequence; - bool operator == ( MessageInfo const& other ) const { - return sequence == other.sequence; - } - bool operator < ( MessageInfo const& other ) const { - return sequence < other.sequence; - } + bool operator == ( MessageInfo const& other ) const; + bool operator < ( MessageInfo const& other ) const; private: static unsigned int globalCount; }; - struct MessageBuilder { + struct MessageStream { + + template + MessageStream& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + ReusableStringStream m_stream; + }; + + struct MessageBuilder : MessageStream { MessageBuilder( std::string const& macroName, SourceLineInfo const& lineInfo, - ResultWas::OfType type ) - : m_info( macroName, lineInfo, type ) - {} + ResultWas::OfType type ); template MessageBuilder& operator << ( T const& value ) { @@ -1940,13 +1549,11 @@ namespace Catch { } MessageInfo m_info; - std::ostringstream m_stream; }; class ScopedMessage { public: - ScopedMessage( MessageBuilder const& builder ); - ScopedMessage( ScopedMessage const& other ); + explicit ScopedMessage( MessageBuilder const& builder ); ~ScopedMessage(); MessageInfo m_info; @@ -1954,305 +1561,177 @@ namespace Catch { } // end namespace Catch -// #included from: catch_interfaces_capture.h -#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED +// end catch_message.h +#if !defined(CATCH_CONFIG_DISABLE) -#include - -namespace Catch { - - class TestCase; - class AssertionResult; - struct AssertionInfo; - struct SectionInfo; - struct SectionEndInfo; - struct MessageInfo; - class ScopedMessageBuilder; - struct Counts; - - struct IResultCapture { - - virtual ~IResultCapture(); - - virtual void assertionEnded( AssertionResult const& result ) = 0; - virtual bool sectionStarted( SectionInfo const& sectionInfo, - Counts& assertions ) = 0; - virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; - virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; - virtual void pushScopedMessage( MessageInfo const& message ) = 0; - virtual void popScopedMessage( MessageInfo const& message ) = 0; - - virtual std::string getCurrentTestName() const = 0; - virtual const AssertionResult* getLastResult() const = 0; - - virtual void handleFatalErrorCondition( std::string const& message ) = 0; - }; - - IResultCapture& getResultCapture(); -} - -// #included from: catch_debugger.h -#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED - -// #included from: catch_platform.h -#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED - -#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) -#define CATCH_PLATFORM_MAC -#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) -#define CATCH_PLATFORM_IPHONE -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -#define CATCH_PLATFORM_WINDOWS -#endif - -#include - -namespace Catch{ - - bool isDebuggerActive(); - void writeToDebugConsole( std::string const& text ); -} - -#ifdef CATCH_PLATFORM_MAC - - // The following code snippet based on: - // http://cocoawithlove.com/2008/03/break-into-debugger.html - #ifdef DEBUG - #if defined(__ppc64__) || defined(__ppc__) - #define CATCH_BREAK_INTO_DEBUGGER() \ - if( Catch::isDebuggerActive() ) { \ - __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ - : : : "memory","r0","r3","r4" ); \ - } - #else - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );} - #endif - #endif - -#elif defined(_MSC_VER) - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); } -#elif defined(__MINGW32__) - extern "C" __declspec(dllimport) void __stdcall DebugBreak(); - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); } -#endif - -#ifndef CATCH_BREAK_INTO_DEBUGGER -#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); -#endif - -// #included from: catch_interfaces_runner.h -#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED - -namespace Catch { - class TestCase; - - struct IRunner { - virtual ~IRunner(); - virtual bool aborting() const = 0; - }; -} - -/////////////////////////////////////////////////////////////////////////////// -// In the event of a failure works out if the debugger needs to be invoked -// and/or an exception thrown and takes appropriate action. -// This needs to be done as a macro so the debugger will stop in the user -// source code rather than in Catch library code -#define INTERNAL_CATCH_REACT( resultBuilder ) \ - if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ - resultBuilder.react(); - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - try { \ - CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - ( __catchResult <= expr ).endExpression(); \ - } \ - catch( ... ) { \ - __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::isTrue( false && !!(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ - INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ - if( Catch::getResultCapture().getLastResult()->succeeded() ) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ - INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ - if( !Catch::getResultCapture().getLastResult()->succeeded() ) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - try { \ - expr; \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - } \ - catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ - } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ - if( __catchResult.allowThrows() ) \ - try { \ - expr; \ - __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ - } \ - catch( ... ) { \ - __catchResult.captureExpectedException( matcher ); \ - } \ - else \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) - -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ - if( __catchResult.allowThrows() ) \ - try { \ - expr; \ - __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ - } \ - catch( exceptionType ) { \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - } \ - catch( ... ) { \ - __catchResult.useActiveException( resultDisposition ); \ - } \ - else \ - __catchResult.captureResult( Catch::ResultWas::Ok ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) - -/////////////////////////////////////////////////////////////////////////////// -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ - __catchResult.captureResult( messageType ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ #else - #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ - do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - __catchResult << log + ::Catch::StreamEndStop(); \ - __catchResult.captureResult( messageType ); \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" #endif -/////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_INFO( log, macroName ) \ - Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; +#if defined(CATCH_CONFIG_FAST_COMPILE) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ +// Another way to speed-up compilation is to omit local try-catch for REQUIRE* +// macros. +#define INTERNAL_CATCH_TRY +#define INTERNAL_CATCH_CATCH( capturer ) + +#else // CATCH_CONFIG_FAST_COMPILE + +#define INTERNAL_CATCH_TRY try +#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } + +#endif + +#define INTERNAL_CATCH_REACT( handler ) handler.complete(); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ + CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( (void)0, false && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look + // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( Catch::getResultCapture().lastAssertionPassed() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ + INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ + if( !Catch::getResultCapture().lastAssertionPassed() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ try { \ - std::string matcherAsString = (matcher).toString(); \ - __catchResult \ - .setLhs( Catch::toString( arg ) ) \ - .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \ - .setOp( "matches" ) \ - .setResultType( (matcher).match( arg ) ); \ - __catchResult.captureExpression(); \ - } catch( ... ) { \ - __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ } \ - INTERNAL_CATCH_REACT( __catchResult ) \ - } while( Catch::alwaysFalse() ) + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) -// #included from: internal/catch_section.h -#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) -// #included from: catch_section_info.h -#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(expr); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) -// #included from: catch_totals.hpp -#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ + catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO( macroName, log ) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ); + +/////////////////////////////////////////////////////////////////////////////// +// Although this is matcher-based, it can be used with just a string +#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( ... ) { \ + Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_capture.hpp +// start catch_section.h + +// start catch_section_info.h + +// start catch_totals.h #include namespace Catch { struct Counts { - Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); - Counts operator - ( Counts const& other ) const { - Counts diff; - diff.passed = passed - other.passed; - diff.failed = failed - other.failed; - diff.failedButOk = failedButOk - other.failedButOk; - return diff; - } - Counts& operator += ( Counts const& other ) { - passed += other.passed; - failed += other.failed; - failedButOk += other.failedButOk; - return *this; - } + std::size_t total() const; + bool allPassed() const; + bool allOk() const; - std::size_t total() const { - return passed + failed + failedButOk; - } - bool allPassed() const { - return failed == 0 && failedButOk == 0; - } - bool allOk() const { - return failed == 0; - } - - std::size_t passed; - std::size_t failed; - std::size_t failedButOk; + std::size_t passed = 0; + std::size_t failed = 0; + std::size_t failedButOk = 0; }; struct Totals { - Totals operator - ( Totals const& other ) const { - Totals diff; - diff.assertions = assertions - other.assertions; - diff.testCases = testCases - other.testCases; - return diff; - } + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); - Totals delta( Totals const& prevTotals ) const { - Totals diff = *this - prevTotals; - if( diff.assertions.failed > 0 ) - ++diff.testCases.failed; - else if( diff.assertions.failedButOk > 0 ) - ++diff.testCases.failedButOk; - else - ++diff.testCases.passed; - return diff; - } - - Totals& operator += ( Totals const& other ) { - assertions += other.assertions; - testCases += other.testCases; - return *this; - } + Totals delta( Totals const& prevTotals ) const; Counts assertions; Counts testCases; }; } +// end catch_totals.h +#include + namespace Catch { struct SectionInfo { @@ -2267,9 +1746,7 @@ namespace Catch { }; struct SectionEndInfo { - SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) - : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) - {} + SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ); SectionInfo sectionInfo; Counts prevAssertions; @@ -2278,31 +1755,29 @@ namespace Catch { } // end namespace Catch -// #included from: catch_timer.h -#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED +// end catch_section_info.h +// start catch_timer.h -#ifdef CATCH_PLATFORM_WINDOWS -typedef unsigned long long uint64_t; -#else -#include -#endif +#include namespace Catch { - class Timer { - public: - Timer() : m_ticks( 0 ) {} - void start(); - unsigned int getElapsedMicroseconds() const; - unsigned int getElapsedMilliseconds() const; - double getElapsedSeconds() const; + auto getCurrentNanosecondsSinceEpoch() -> uint64_t; + auto getEstimatedClockResolution() -> uint64_t; - private: - uint64_t m_ticks; + class Timer { + uint64_t m_nanoseconds = 0; + public: + void start(); + auto getElapsedNanoseconds() const -> uint64_t; + auto getElapsedMicroseconds() const -> uint64_t; + auto getElapsedMilliseconds() const -> unsigned int; + auto getElapsedSeconds() const -> double; }; } // namespace Catch +// end catch_timer.h #include namespace Catch { @@ -2313,7 +1788,7 @@ namespace Catch { ~Section(); // This indicates whether the section should be executed or not - operator bool() const; + explicit operator bool() const; private: SectionInfo m_info; @@ -2326,204 +1801,62 @@ namespace Catch { } // end namespace Catch -#ifdef CATCH_CONFIG_VARIADIC_MACROS #define INTERNAL_CATCH_SECTION( ... ) \ if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) -#else - #define INTERNAL_CATCH_SECTION( name, desc ) \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) -#endif -// #included from: internal/catch_generators.hpp -#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED +// end catch_section.h +// start catch_benchmark.h -#include -#include +#include #include -#include namespace Catch { -template -struct IGenerator { - virtual ~IGenerator() {} - virtual T getValue( std::size_t index ) const = 0; - virtual std::size_t size () const = 0; -}; + class BenchmarkLooper { -template -class BetweenGenerator : public IGenerator { -public: - BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} + std::string m_name; + std::size_t m_count = 0; + std::size_t m_iterationsToRun = 1; + uint64_t m_resolution; + Timer m_timer; - virtual T getValue( std::size_t index ) const { - return m_from+static_cast( index ); - } - - virtual std::size_t size() const { - return static_cast( 1+m_to-m_from ); - } - -private: - - T m_from; - T m_to; -}; - -template -class ValuesGenerator : public IGenerator { -public: - ValuesGenerator(){} - - void add( T value ) { - m_values.push_back( value ); - } - - virtual T getValue( std::size_t index ) const { - return m_values[index]; - } - - virtual std::size_t size() const { - return m_values.size(); - } - -private: - std::vector m_values; -}; - -template -class CompositeGenerator { -public: - CompositeGenerator() : m_totalSize( 0 ) {} - - // *** Move semantics, similar to auto_ptr *** - CompositeGenerator( CompositeGenerator& other ) - : m_fileInfo( other.m_fileInfo ), - m_totalSize( 0 ) - { - move( other ); - } - - CompositeGenerator& setFileInfo( const char* fileInfo ) { - m_fileInfo = fileInfo; - return *this; - } - - ~CompositeGenerator() { - deleteAll( m_composed ); - } - - operator T () const { - size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); - - typename std::vector*>::const_iterator it = m_composed.begin(); - typename std::vector*>::const_iterator itEnd = m_composed.end(); - for( size_t index = 0; it != itEnd; ++it ) + static auto getResolution() -> uint64_t; + public: + // Keep most of this inline as it's on the code path that is being timed + BenchmarkLooper( StringRef name ) + : m_name( name ), + m_resolution( getResolution() ) { - const IGenerator* generator = *it; - if( overallIndex >= index && overallIndex < index + generator->size() ) - { - return generator->getValue( overallIndex-index ); - } - index += generator->size(); + reportStart(); + m_timer.start(); } - CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); - return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so - } - void add( const IGenerator* generator ) { - m_totalSize += generator->size(); - m_composed.push_back( generator ); - } + explicit operator bool() { + if( m_count < m_iterationsToRun ) + return true; + return needsMoreIterations(); + } - CompositeGenerator& then( CompositeGenerator& other ) { - move( other ); - return *this; - } + void increment() { + ++m_count; + } - CompositeGenerator& then( T value ) { - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( value ); - add( valuesGen ); - return *this; - } - -private: - - void move( CompositeGenerator& other ) { - std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); - m_totalSize += other.m_totalSize; - other.m_composed.clear(); - } - - std::vector*> m_composed; - std::string m_fileInfo; - size_t m_totalSize; -}; - -namespace Generators -{ - template - CompositeGenerator between( T from, T to ) { - CompositeGenerator generators; - generators.add( new BetweenGenerator( from, to ) ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2 ) { - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - generators.add( valuesGen ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2, T val3 ){ - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - valuesGen->add( val3 ); - generators.add( valuesGen ); - return generators; - } - - template - CompositeGenerator values( T val1, T val2, T val3, T val4 ) { - CompositeGenerator generators; - ValuesGenerator* valuesGen = new ValuesGenerator(); - valuesGen->add( val1 ); - valuesGen->add( val2 ); - valuesGen->add( val3 ); - valuesGen->add( val4 ); - generators.add( valuesGen ); - return generators; - } - -} // end namespace Generators - -using namespace Generators; + void reportStart(); + auto needsMoreIterations() -> bool; + }; } // end namespace Catch -#define INTERNAL_CATCH_LINESTR2( line ) #line -#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) +#define BENCHMARK( name ) \ + for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() ) -#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) +// end catch_benchmark.h +// start catch_interfaces_exception.h -// #included from: internal/catch_interfaces_exception.h -#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED - -#include -#include - -// #included from: catch_interfaces_registry_hub.h -#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED +// start catch_interfaces_registry_hub.h #include +#include namespace Catch { @@ -2533,21 +1866,31 @@ namespace Catch { struct IExceptionTranslator; struct IReporterRegistry; struct IReporterFactory; + struct ITagAliasRegistry; + class StartupExceptionRegistry; + + using IReporterFactoryPtr = std::shared_ptr; struct IRegistryHub { virtual ~IRegistryHub(); virtual IReporterRegistry const& getReporterRegistry() const = 0; virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; + + virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; }; struct IMutableRegistryHub { virtual ~IMutableRegistryHub(); - virtual void registerReporter( std::string const& name, Ptr const& factory ) = 0; - virtual void registerListener( Ptr const& factory ) = 0; + virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0; + virtual void registerListener( IReporterFactoryPtr const& factory ) = 0; virtual void registerTest( TestCase const& testInfo ) = 0; virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; + virtual void registerStartupException() noexcept = 0; }; IRegistryHub& getRegistryHub(); @@ -2557,12 +1900,21 @@ namespace Catch { } -namespace Catch { +// end catch_interfaces_registry_hub.h +#if defined(CATCH_CONFIG_DISABLE) + #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \ + static std::string translatorName( signature ) +#endif - typedef std::string(*exceptionTranslateFunction)(); +#include +#include +#include + +namespace Catch { + using exceptionTranslateFunction = std::string(*)(); struct IExceptionTranslator; - typedef std::vector ExceptionTranslators; + using ExceptionTranslators = std::vector>; struct IExceptionTranslator { virtual ~IExceptionTranslator(); @@ -2584,10 +1936,10 @@ namespace Catch { : m_translateFunction( translateFunction ) {} - virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { + std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { try { if( it == itEnd ) - throw; + std::rethrow_exception(std::current_exception()); else return (*it)->translate( it+1, itEnd ); } @@ -2612,200 +1964,675 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ static std::string translatorName( signature ); \ - namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ static std::string translatorName( signature ) #define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) -// #included from: internal/catch_approx.hpp -#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED +// end catch_interfaces_exception.h +// start catch_approx.h -#include -#include +#include +#include namespace Catch { namespace Detail { class Approx { + private: + bool equalityComparisonImpl(double other) const; + public: - explicit Approx ( double value ) - : m_epsilon( std::numeric_limits::epsilon()*100 ), - m_scale( 1.0 ), - m_value( value ) - {} + explicit Approx ( double value ); - Approx( Approx const& other ) - : m_epsilon( other.m_epsilon ), - m_scale( other.m_scale ), - m_value( other.m_value ) - {} + static Approx custom(); - static Approx custom() { - return Approx( 0 ); - } - - Approx operator()( double value ) { - Approx approx( value ); + template ::value>::type> + Approx operator()( T const& value ) { + Approx approx( static_cast(value) ); approx.epsilon( m_epsilon ); + approx.margin( m_margin ); approx.scale( m_scale ); return approx; } - friend bool operator == ( double lhs, Approx const& rhs ) { - // Thanks to Richard Harris for his help refining this formula - return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) ); + template ::value>::type> + explicit Approx( T const& value ): Approx(static_cast(value)) + {} + + template ::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + auto lhs_v = static_cast(lhs); + return rhs.equalityComparisonImpl(lhs_v); } - friend bool operator == ( Approx const& lhs, double rhs ) { + template ::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { return operator==( rhs, lhs ); } - friend bool operator != ( double lhs, Approx const& rhs ) { + template ::value>::type> + friend bool operator != ( T const& lhs, Approx const& rhs ) { return !operator==( lhs, rhs ); } - friend bool operator != ( Approx const& lhs, double rhs ) { + template ::value>::type> + friend bool operator != ( Approx const& lhs, T const& rhs ) { return !operator==( rhs, lhs ); } - Approx& epsilon( double newEpsilon ) { - m_epsilon = newEpsilon; + template ::value>::type> + friend bool operator <= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) < rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator <= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value < static_cast(rhs) || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value > static_cast(rhs) || lhs == rhs; + } + + template ::value>::type> + Approx& epsilon( T const& newEpsilon ) { + double epsilonAsDouble = static_cast(newEpsilon); + if( epsilonAsDouble < 0 || epsilonAsDouble > 1.0 ) { + throw std::domain_error + ( "Invalid Approx::epsilon: " + + Catch::Detail::stringify( epsilonAsDouble ) + + ", Approx::epsilon has to be between 0 and 1" ); + } + m_epsilon = epsilonAsDouble; return *this; } - Approx& scale( double newScale ) { - m_scale = newScale; + template ::value>::type> + Approx& margin( T const& newMargin ) { + double marginAsDouble = static_cast(newMargin); + if( marginAsDouble < 0 ) { + throw std::domain_error + ( "Invalid Approx::margin: " + + Catch::Detail::stringify( marginAsDouble ) + + ", Approx::Margin has to be non-negative." ); + + } + m_margin = marginAsDouble; return *this; } - std::string toString() const { - std::ostringstream oss; - oss << "Approx( " << Catch::toString( m_value ) << " )"; - return oss.str(); + template ::value>::type> + Approx& scale( T const& newScale ) { + m_scale = static_cast(newScale); + return *this; } + std::string toString() const; + private: double m_epsilon; + double m_margin; double m_scale; double m_value; }; } template<> -inline std::string toString( Detail::Approx const& value ) { - return value.toString(); -} +struct StringMaker { + static std::string convert(Catch::Detail::Approx const& value); +}; } // end namespace Catch -// #included from: internal/catch_interfaces_tag_alias_registry.h -#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED +// end catch_approx.h +// start catch_string_manip.h -// #included from: catch_tag_alias.h -#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED +#include +#include + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; +} + +// end catch_string_manip.h +#ifndef CATCH_CONFIG_DISABLE_MATCHERS +// start catch_capture_matchers.h + +// start catch_matchers.h + +#include +#include + +namespace Catch { +namespace Matchers { + namespace Impl { + + template struct MatchAllOf; + template struct MatchAnyOf; + template struct MatchNotOf; + + class MatcherUntypedBase { + public: + MatcherUntypedBase() = default; + MatcherUntypedBase ( MatcherUntypedBase const& ) = default; + MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete; + std::string toString() const; + + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; + }; + + template + struct MatcherMethod { + virtual bool match( ObjectT const& arg ) const = 0; + }; + template + struct MatcherMethod { + virtual bool match( PtrT* arg ) const = 0; + }; + + template + struct MatcherBase : MatcherUntypedBase, MatcherMethod { + + MatchAllOf operator && ( MatcherBase const& other ) const; + MatchAnyOf operator || ( MatcherBase const& other ) const; + MatchNotOf operator ! () const; + }; + + template + struct MatchAllOf : MatcherBase { + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (!matcher->match(arg)) + return false; + } + return true; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " and "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAllOf& operator && ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + template + struct MatchAnyOf : MatcherBase { + + bool match( ArgT const& arg ) const override { + for( auto matcher : m_matchers ) { + if (matcher->match(arg)) + return true; + } + return false; + } + std::string describe() const override { + std::string description; + description.reserve( 4 + m_matchers.size()*32 ); + description += "( "; + bool first = true; + for( auto matcher : m_matchers ) { + if( first ) + first = false; + else + description += " or "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAnyOf& operator || ( MatcherBase const& other ) { + m_matchers.push_back( &other ); + return *this; + } + + std::vector const*> m_matchers; + }; + + template + struct MatchNotOf : MatcherBase { + + MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} + + bool match( ArgT const& arg ) const override { + return !m_underlyingMatcher.match( arg ); + } + + std::string describe() const override { + return "not " + m_underlyingMatcher.toString(); + } + MatcherBase const& m_underlyingMatcher; + }; + + template + MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { + return MatchAllOf() && *this && other; + } + template + MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { + return MatchAnyOf() || *this || other; + } + template + MatchNotOf MatcherBase::operator ! () const { + return MatchNotOf( *this ); + } + + } // namespace Impl + +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch + +// end catch_matchers.h +// start catch_matchers_floating.h + +#include +#include + +namespace Catch { +namespace Matchers { + + namespace Floating { + + enum class FloatingPointKind : uint8_t; + + struct WithinAbsMatcher : MatcherBase { + WithinAbsMatcher(double target, double margin); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + double m_margin; + }; + + struct WithinUlpsMatcher : MatcherBase { + WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType); + bool match(double const& matchee) const override; + std::string describe() const override; + private: + double m_target; + int m_ulps; + FloatingPointKind m_type; + }; + + } // namespace Floating + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff); + Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff); + Floating::WithinAbsMatcher WithinAbs(double target, double margin); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.h +// start catch_matchers_string.h #include namespace Catch { +namespace Matchers { - struct TagAlias { - TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} + namespace StdString { - std::string tag; - SourceLineInfo lineInfo; - }; + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); + std::string adjustString( std::string const& str ) const; + std::string caseSensitivitySuffix() const; - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; -} // end namespace Catch + struct StringMatcherBase : MatcherBase { + StringMatcherBase( std::string const& operation, CasedString const& comparator ); + std::string describe() const override; -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } -// #included from: catch_option.hpp -#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED + CasedString m_comparator; + std::string m_operation; + }; + + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher( CasedString const& comparator ); + bool match( std::string const& source ) const override; + }; + + struct RegexMatcher : MatcherBase { + RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity ); + bool match( std::string const& matchee ) const override; + std::string describe() const override; + + private: + std::string m_regex; + CaseSensitive::Choice m_caseSensitivity; + }; + + } // namespace StdString + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_string.h +// start catch_matchers_vector.h + +#include namespace Catch { +namespace Matchers { - // An optional type - template - class Option { - public: - Option() : nullableValue( CATCH_NULL ) {} - Option( T const& _value ) - : nullableValue( new( storage ) T( _value ) ) - {} - Option( Option const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL ) - {} - - ~Option() { - reset(); - } - - Option& operator= ( Option const& _other ) { - if( &_other != this ) { - reset(); - if( _other ) - nullableValue = new( storage ) T( *_other ); + namespace Vector { + namespace Detail { + template + size_t count(InputIterator first, InputIterator last, T const& item) { + size_t cnt = 0; + for (; first != last; ++first) { + if (*first == item) { + ++cnt; + } + } + return cnt; + } + template + bool contains(InputIterator first, InputIterator last, T const& item) { + for (; first != last; ++first) { + if (*first == item) { + return true; + } + } + return false; } - return *this; - } - Option& operator = ( T const& _value ) { - reset(); - nullableValue = new( storage ) T( _value ); - return *this; } - void reset() { - if( nullableValue ) - nullableValue->~T(); - nullableValue = CATCH_NULL; - } + template + struct ContainsElementMatcher : MatcherBase> { - T& operator*() { return *nullableValue; } - T const& operator*() const { return *nullableValue; } - T* operator->() { return nullableValue; } - const T* operator->() const { return nullableValue; } + ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} - T valueOr( T const& defaultValue ) const { - return nullableValue ? *nullableValue : defaultValue; - } + bool match(std::vector const &v) const override { + for (auto const& el : v) { + if (el == m_comparator) { + return true; + } + } + return false; + } - bool some() const { return nullableValue != CATCH_NULL; } - bool none() const { return nullableValue == CATCH_NULL; } + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } - bool operator !() const { return nullableValue == CATCH_NULL; } - operator SafeBool::type() const { - return SafeBool::makeSafe( some() ); - } + T const& m_comparator; + }; - private: - T* nullableValue; - char storage[sizeof(T)]; - }; + template + struct ContainsMatcher : MatcherBase> { -} // end namespace Catch + ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + bool match(std::vector const &v) const override { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (auto const& comparator : m_comparator) { + auto present = false; + for (const auto& el : v) { + if (el == comparator) { + present = true; + break; + } + } + if (!present) { + return false; + } + } + return true; + } + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + } + + std::vector const& m_comparator; + }; + + template + struct EqualsMatcher : MatcherBase> { + + EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + + bool match(std::vector const &v) const override { + // !TBD: This currently works if all elements can be compared using != + // - a more general approach would be via a compare template that defaults + // to using !=. but could be specialised for, e.g. std::vector etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } + std::string describe() const override { + return "Equals: " + ::Catch::Detail::stringify( m_comparator ); + } + std::vector const& m_comparator; + }; + + template + struct UnorderedEqualsMatcher : MatcherBase> { + UnorderedEqualsMatcher(std::vector const& target) : m_target(target) {} + bool match(std::vector const& vec) const override { + // Note: This is a reimplementation of std::is_permutation, + // because I don't want to include inside the common path + if (m_target.size() != vec.size()) { + return false; + } + auto lfirst = m_target.begin(), llast = m_target.end(); + auto rfirst = vec.begin(), rlast = vec.end(); + // Cut common prefix to optimize checking of permuted parts + while (lfirst != llast && *lfirst != *rfirst) { + ++lfirst; ++rfirst; + } + if (lfirst == llast) { + return true; + } + + for (auto mid = lfirst; mid != llast; ++mid) { + // Skip already counted items + if (Detail::contains(lfirst, mid, *mid)) { + continue; + } + size_t num_vec = Detail::count(rfirst, rlast, *mid); + if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) { + return false; + } + } + + return true; + } + + std::string describe() const override { + return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); + } + private: + std::vector const& m_target; + }; + + } // namespace Vector + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + + template + Vector::ContainsMatcher Contains( std::vector const& comparator ) { + return Vector::ContainsMatcher( comparator ); + } + + template + Vector::ContainsElementMatcher VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher( comparator ); + } + + template + Vector::EqualsMatcher Equals( std::vector const& comparator ) { + return Vector::EqualsMatcher( comparator ); + } + + template + Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const& target) { + return Vector::UnorderedEqualsMatcher(target); + } + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_vector.h namespace Catch { - struct ITagAliasRegistry { - virtual ~ITagAliasRegistry(); - virtual Option find( std::string const& alias ) const = 0; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + template + class MatchExpr : public ITransientExpression { + ArgT const& m_arg; + MatcherT m_matcher; + StringRef m_matcherString; + public: + MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString ) + : ITransientExpression{ true, matcher.match( arg ) }, + m_arg( arg ), + m_matcher( matcher ), + m_matcherString( matcherString ) + {} - static ITagAliasRegistry const& get(); + void streamReconstructedExpression( std::ostream &os ) const override { + auto matcherAsString = m_matcher.toString(); + os << Catch::Detail::stringify( m_arg ) << ' '; + if( matcherAsString == Detail::unprintableString ) + os << m_matcherString; + else + os << matcherAsString; + } }; -} // end namespace Catch + using StringMatcher = Matchers::Impl::MatcherBase; + + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ); + + template + auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString ) -> MatchExpr { + return MatchExpr( arg, matcher, matcherString ); + } + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + INTERNAL_CATCH_TRY { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher ) ); \ + } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \ + do { \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ + if( catchAssertionHandler.allowThrows() ) \ + try { \ + static_cast(__VA_ARGS__ ); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } \ + catch( exceptionType const& ex ) { \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \ + } \ + catch( ... ) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT( catchAssertionHandler ) \ + } while( false ) + +// end catch_capture_matchers.h +#endif // These files are included here so the single_include script doesn't put them // in the conditionally compiled sections -// #included from: internal/catch_test_case_info.h -#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED +// start catch_test_case_info.h #include -#include +#include +#include #ifdef __clang__ #pragma clang diagnostic push @@ -2814,7 +2641,7 @@ namespace Catch { namespace Catch { - struct ITestCase; + struct ITestInvoker; struct TestCaseInfo { enum SpecialProperties{ @@ -2822,30 +2649,31 @@ namespace Catch { IsHidden = 1 << 1, ShouldFail = 1 << 2, MayFail = 1 << 3, - Throws = 1 << 4 + Throws = 1 << 4, + NonPortable = 1 << 5, + Benchmark = 1 << 6 }; TestCaseInfo( std::string const& _name, std::string const& _className, std::string const& _description, - std::set const& _tags, + std::vector const& _tags, SourceLineInfo const& _lineInfo ); - TestCaseInfo( TestCaseInfo const& other ); - - friend void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ); + friend void setTags( TestCaseInfo& testCaseInfo, std::vector tags ); bool isHidden() const; bool throws() const; bool okToFail() const; bool expectedToFail() const; + std::string tagsAsString() const; + std::string name; std::string className; std::string description; - std::set tags; - std::set lcaseTags; - std::string tagsAsString; + std::vector tags; + std::vector lcaseTags; SourceLineInfo lineInfo; SpecialProperties properties; }; @@ -2853,8 +2681,7 @@ namespace Catch { class TestCase : public TestCaseInfo { public: - TestCase( ITestCase* testCase, TestCaseInfo const& info ); - TestCase( TestCase const& other ); + TestCase( ITestInvoker* testCase, TestCaseInfo const& info ); TestCase withName( std::string const& _newName ) const; @@ -2862,16 +2689,14 @@ namespace Catch { TestCaseInfo const& getTestCaseInfo() const; - void swap( TestCase& other ); bool operator == ( TestCase const& other ) const; bool operator < ( TestCase const& other ) const; - TestCase& operator = ( TestCase const& other ); private: - Ptr test; + std::shared_ptr test; }; - TestCase makeTestCase( ITestCase* testCase, + TestCase makeTestCase( ITestInvoker* testCase, std::string const& className, std::string const& name, std::string const& description, @@ -2882,10 +2707,21 @@ namespace Catch { #pragma clang diagnostic pop #endif +// end catch_test_case_info.h +// start catch_interfaces_runner.h + +namespace Catch { + + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; +} + +// end catch_interfaces_runner.h #ifdef __OBJC__ -// #included from: internal/catch_objc.hpp -#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED +// start catch_objc.hpp #import @@ -2909,7 +2745,7 @@ namespace Catch { namespace Catch { - class OcMethod : public SharedImpl { + class OcMethod : public ITestInvoker { public: OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} @@ -2945,9 +2781,9 @@ namespace Catch { } } - inline size_t registerTestMethods() { - size_t noTestMethods = 0; - int noClasses = objc_getClassList( CATCH_NULL, 0 ); + inline std::size_t registerTestMethods() { + std::size_t noTestMethods = 0; + int noClasses = objc_getClassList( nullptr, 0 ); Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); objc_getClassList( classes, noClasses ); @@ -2966,7 +2802,7 @@ namespace Catch { std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); const char* className = class_getName( cls ); - getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo("",0) ) ); noTestMethods++; } } @@ -2976,69 +2812,74 @@ namespace Catch { return noTestMethods; } +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) + namespace Matchers { namespace Impl { namespace NSStringMatchers { - template - struct StringHolder : MatcherImpl{ + struct StringHolder : MatcherBase{ StringHolder( NSString* substr ) : m_substr( [substr copy] ){} StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} StringHolder() { arcSafeRelease( m_substr ); } - NSString* m_substr; + bool match( NSString* arg ) const override { + return false; + } + + NSString* CATCH_ARC_STRONG m_substr; }; - struct Equals : StringHolder { + struct Equals : StringHolder { Equals( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + bool match( NSString* str ) const override { return (str != nil || m_substr == nil ) && [str isEqualToString:m_substr]; } - virtual std::string toString() const { - return "equals string: " + Catch::toString( m_substr ); + std::string describe() const override { + return "equals string: " + Catch::Detail::stringify( m_substr ); } }; - struct Contains : StringHolder { + struct Contains : StringHolder { Contains( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + bool match( NSString* str ) const { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location != NSNotFound; } - virtual std::string toString() const { - return "contains string: " + Catch::toString( m_substr ); + std::string describe() const override { + return "contains string: " + Catch::Detail::stringify( m_substr ); } }; - struct StartsWith : StringHolder { + struct StartsWith : StringHolder { StartsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + bool match( NSString* str ) const override { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == 0; } - virtual std::string toString() const { - return "starts with: " + Catch::toString( m_substr ); + std::string describe() const override { + return "starts with: " + Catch::Detail::stringify( m_substr ); } }; - struct EndsWith : StringHolder { + struct EndsWith : StringHolder { EndsWith( NSString* substr ) : StringHolder( substr ){} - virtual bool match( ExpressionType const& str ) const { + bool match( NSString* str ) const override { return (str != nil || m_substr == nil ) && [str rangeOfString:m_substr].location == [str length] - [m_substr length]; } - virtual std::string toString() const { - return "ends with: " + Catch::toString( m_substr ); + std::string describe() const override { + return "ends with: " + Catch::Detail::stringify( m_substr ); } }; @@ -3061,61 +2902,52 @@ namespace Catch { using namespace Matchers; +#endif // CATCH_CONFIG_DISABLE_MATCHERS + } // namespace Catch /////////////////////////////////////////////////////////////////////////////// -#define OC_TEST_CASE( name, desc )\ -+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ -{\ +#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix +#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \ +{ \ return @ name; \ -}\ -+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ +} \ ++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \ { \ return @ desc; \ } \ --(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) +-(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix ) +#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ ) + +// end catch_objc.hpp #endif -#ifdef CATCH_IMPL -// #included from: internal/catch_impl.hpp -#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED +#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES +// start catch_external_interfaces.h -// Collect all the implementation files together here -// These are the equivalent of what would usually be cpp files +// start catch_reporter_bases.hpp -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wweak-vtables" -#endif +// start catch_interfaces_reporter.h -// #included from: ../catch_session.hpp -#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED +// start catch_config.hpp -// #included from: internal/catch_commandline.hpp -#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED - -// #included from: catch_config.hpp -#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED - -// #included from: catch_test_spec_parser.hpp -#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED +// start catch_test_spec_parser.h #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif -// #included from: catch_test_spec.hpp -#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED +// start catch_test_spec.h #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif -// #included from: catch_wildcard_pattern.hpp -#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED +// start catch_wildcard_pattern.h namespace Catch { @@ -3129,119 +2961,68 @@ namespace Catch public: - WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_wildcard( NoWildcard ), - m_pattern( adjustCase( pattern ) ) - { - if( startsWith( m_pattern, "*" ) ) { - m_pattern = m_pattern.substr( 1 ); - m_wildcard = WildcardAtStart; - } - if( endsWith( m_pattern, "*" ) ) { - m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); - m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); - } - } - virtual ~WildcardPattern(); - virtual bool matches( std::string const& str ) const { - switch( m_wildcard ) { - case NoWildcard: - return m_pattern == adjustCase( str ); - case WildcardAtStart: - return endsWith( adjustCase( str ), m_pattern ); - case WildcardAtEnd: - return startsWith( adjustCase( str ), m_pattern ); - case WildcardAtBothEnds: - return contains( adjustCase( str ), m_pattern ); - } + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ); + virtual ~WildcardPattern() = default; + virtual bool matches( std::string const& str ) const; -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunreachable-code" -#endif - throw std::logic_error( "Unknown enum" ); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } private: - std::string adjustCase( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; - } + std::string adjustCase( std::string const& str ) const; CaseSensitive::Choice m_caseSensitivity; - WildcardPosition m_wildcard; + WildcardPosition m_wildcard = NoWildcard; std::string m_pattern; }; } +// end catch_wildcard_pattern.h #include #include +#include namespace Catch { class TestSpec { - struct Pattern : SharedImpl<> { + struct Pattern { virtual ~Pattern(); virtual bool matches( TestCaseInfo const& testCase ) const = 0; }; + using PatternPtr = std::shared_ptr; + class NamePattern : public Pattern { public: - NamePattern( std::string const& name ) - : m_wildcardPattern( toLower( name ), CaseSensitive::No ) - {} + NamePattern( std::string const& name ); virtual ~NamePattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return m_wildcardPattern.matches( toLower( testCase.name ) ); - } + virtual bool matches( TestCaseInfo const& testCase ) const override; private: WildcardPattern m_wildcardPattern; }; class TagPattern : public Pattern { public: - TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + TagPattern( std::string const& tag ); virtual ~TagPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { - return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); - } + virtual bool matches( TestCaseInfo const& testCase ) const override; private: std::string m_tag; }; class ExcludedPattern : public Pattern { public: - ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + ExcludedPattern( PatternPtr const& underlyingPattern ); virtual ~ExcludedPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + virtual bool matches( TestCaseInfo const& testCase ) const override; private: - Ptr m_underlyingPattern; + PatternPtr m_underlyingPattern; }; struct Filter { - std::vector > m_patterns; + std::vector m_patterns; - bool matches( TestCaseInfo const& testCase ) const { - // All patterns in a filter must match for the filter to be a match - for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) { - if( !(*it)->matches( testCase ) ) - return false; - } - return true; - } + bool matches( TestCaseInfo const& testCase ) const; }; public: - bool hasFilters() const { - return !m_filters.empty(); - } - bool matches( TestCaseInfo const& testCase ) const { - // A TestSpec matches if any filter matches - for( std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) - if( it->matches( testCase ) ) - return true; - return false; - } + bool hasFilters() const; + bool matches( TestCaseInfo const& testCase ) const; private: std::vector m_filters; @@ -3254,96 +3035,75 @@ namespace Catch { #pragma clang diagnostic pop #endif +// end catch_test_spec.h +// start catch_interfaces_tag_alias_registry.h + +#include + +namespace Catch { + + struct TagAlias; + + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + // Nullptr if not present + virtual TagAlias const* find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + + static ITagAliasRegistry const& get(); + }; + +} // end namespace Catch + +// end catch_interfaces_tag_alias_registry.h namespace Catch { class TestSpecParser { - enum Mode{ None, Name, QuotedName, Tag }; - Mode m_mode; - bool m_exclusion; - std::size_t m_start, m_pos; + enum Mode{ None, Name, QuotedName, Tag, EscapedName }; + Mode m_mode = None; + bool m_exclusion = false; + std::size_t m_start = std::string::npos, m_pos = 0; std::string m_arg; + std::vector m_escapeChars; TestSpec::Filter m_currentFilter; TestSpec m_testSpec; - ITagAliasRegistry const* m_tagAliases; + ITagAliasRegistry const* m_tagAliases = nullptr; public: - TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + TestSpecParser( ITagAliasRegistry const& tagAliases ); + + TestSpecParser& parse( std::string const& arg ); + TestSpec testSpec(); - TestSpecParser& parse( std::string const& arg ) { - m_mode = None; - m_exclusion = false; - m_start = std::string::npos; - m_arg = m_tagAliases->expandAliases( arg ); - for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) - visitChar( m_arg[m_pos] ); - if( m_mode == Name ) - addPattern(); - return *this; - } - TestSpec testSpec() { - addFilter(); - return m_testSpec; - } private: - void visitChar( char c ) { - if( m_mode == None ) { - switch( c ) { - case ' ': return; - case '~': m_exclusion = true; return; - case '[': return startNewMode( Tag, ++m_pos ); - case '"': return startNewMode( QuotedName, ++m_pos ); - default: startNewMode( Name, m_pos ); break; - } - } - if( m_mode == Name ) { - if( c == ',' ) { - addPattern(); - addFilter(); - } - else if( c == '[' ) { - if( subString() == "exclude:" ) - m_exclusion = true; - else - addPattern(); - startNewMode( Tag, ++m_pos ); - } - } - else if( m_mode == QuotedName && c == '"' ) - addPattern(); - else if( m_mode == Tag && c == ']' ) - addPattern(); - } - void startNewMode( Mode mode, std::size_t start ) { - m_mode = mode; - m_start = start; - } - std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + void visitChar( char c ); + void startNewMode( Mode mode, std::size_t start ); + void escape(); + std::string subString() const; + template void addPattern() { std::string token = subString(); + for( std::size_t i = 0; i < m_escapeChars.size(); ++i ) + token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); + m_escapeChars.clear(); if( startsWith( token, "exclude:" ) ) { m_exclusion = true; token = token.substr( 8 ); } if( !token.empty() ) { - Ptr pattern = new T( token ); + TestSpec::PatternPtr pattern = std::make_shared( token ); if( m_exclusion ) - pattern = new TestSpec::ExcludedPattern( pattern ); + pattern = std::make_shared( pattern ); m_currentFilter.m_patterns.push_back( pattern ); } m_exclusion = false; m_mode = None; } - void addFilter() { - if( !m_currentFilter.m_patterns.empty() ) { - m_testSpec.m_filters.push_back( m_currentFilter ); - m_currentFilter = TestSpec::Filter(); - } - } + + void addFilter(); }; - inline TestSpec parseTestSpec( std::string const& arg ) { - return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); - } + TestSpec parseTestSpec( std::string const& arg ); } // namespace Catch @@ -3351,20 +3111,21 @@ namespace Catch { #pragma clang diagnostic pop #endif -// #included from: catch_interfaces_config.h -#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED +// end catch_test_spec_parser.h +// start catch_interfaces_config.h -#include +#include #include #include +#include namespace Catch { - struct Verbosity { enum Level { - NoOutput = 0, - Quiet, - Normal - }; }; + enum class Verbosity { + Quiet = 0, + Normal, + High + }; struct WarnAbout { enum What { Nothing = 0x00, @@ -3386,10 +3147,16 @@ namespace Catch { Yes, No }; }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; class TestSpec; - struct IConfig : IShared { + struct IConfig : NonCopyable { virtual ~IConfig(); @@ -3405,76 +3172,21 @@ namespace Catch { virtual TestSpec const& testSpec() const = 0; virtual RunTests::InWhatOrder runOrder() const = 0; virtual unsigned int rngSeed() const = 0; + virtual int benchmarkResolutionMultiple() const = 0; virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; }; + + using IConfigPtr = std::shared_ptr; } -// #included from: catch_stream.h -#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED - -// #included from: catch_streambuf.h -#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED - -#include - -namespace Catch { - - class StreamBufBase : public std::streambuf { - public: - virtual ~StreamBufBase() CATCH_NOEXCEPT; - }; -} - -#include -#include -#include - -namespace Catch { - - std::ostream& cout(); - std::ostream& cerr(); - - struct IStream { - virtual ~IStream() CATCH_NOEXCEPT; - virtual std::ostream& stream() const = 0; - }; - - class FileStream : public IStream { - mutable std::ofstream m_ofs; - public: - FileStream( std::string const& filename ); - virtual ~FileStream() CATCH_NOEXCEPT; - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; - }; - - class CoutStream : public IStream { - mutable std::ostream m_os; - public: - CoutStream(); - virtual ~CoutStream() CATCH_NOEXCEPT; - - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; - }; - - class DebugOutStream : public IStream { - CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf; - mutable std::ostream m_os; - public: - DebugOutStream(); - virtual ~DebugOutStream() CATCH_NOEXCEPT; - - public: // IStream - virtual std::ostream& stream() const CATCH_OVERRIDE; - }; -} +// end catch_interfaces_config.h +// Libstdc++ doesn't like incomplete classes for unique_ptr #include #include #include -#include -#include #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 @@ -3482,48 +3194,32 @@ namespace Catch { namespace Catch { + struct IStream; + struct ConfigData { + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listTestNamesOnly = false; - ConfigData() - : listTests( false ), - listTags( false ), - listReporters( false ), - listTestNamesOnly( false ), - showSuccessfulTests( false ), - shouldDebugBreak( false ), - noThrow( false ), - showHelp( false ), - showInvisibles( false ), - filenamesAsTags( false ), - abortAfter( -1 ), - rngSeed( 0 ), - verbosity( Verbosity::Normal ), - warnings( WarnAbout::Nothing ), - showDurations( ShowDurations::DefaultForReporter ), - runOrder( RunTests::InDeclarationOrder ), - useColour( UseColour::Auto ) - {} + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; - bool listTests; - bool listTags; - bool listReporters; - bool listTestNamesOnly; + int abortAfter = -1; + unsigned int rngSeed = 0; + int benchmarkResolutionMultiple = 100; - bool showSuccessfulTests; - bool shouldDebugBreak; - bool noThrow; - bool showHelp; - bool showInvisibles; - bool filenamesAsTags; - - int abortAfter; - unsigned int rngSeed; - - Verbosity::Level verbosity; - WarnAbout::What warnings; - ShowDurations::OrNot showDurations; - RunTests::InWhatOrder runOrder; - UseColour::YesOrNo useColour; + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; + UseColour::YesOrNo useColour = UseColour::Auto; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; std::string outputFilename; std::string name; @@ -3531,1481 +3227,644 @@ namespace Catch { std::vector reporterNames; std::vector testsOrTags; + std::vector sectionsToRun; }; - class Config : public SharedImpl { - private: - Config( Config const& other ); - Config& operator = ( Config const& other ); - virtual void dummy(); + class Config : public IConfig { public: - Config() - {} + Config() = default; + Config( ConfigData const& data ); + virtual ~Config() = default; - Config( ConfigData const& data ) - : m_data( data ), - m_stream( openStream() ) - { - if( !data.testsOrTags.empty() ) { - TestSpecParser parser( ITagAliasRegistry::get() ); - for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) - parser.parse( data.testsOrTags[i] ); - m_testSpec = parser.testSpec(); - } - } + std::string const& getFilename() const; - virtual ~Config() { - } + bool listTests() const; + bool listTestNamesOnly() const; + bool listTags() const; + bool listReporters() const; - std::string const& getFilename() const { - return m_data.outputFilename ; - } + std::string getProcessName() const; - bool listTests() const { return m_data.listTests; } - bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } - bool listTags() const { return m_data.listTags; } - bool listReporters() const { return m_data.listReporters; } + std::vector const& getReporterNames() const; + std::vector const& getSectionsToRun() const override; - std::string getProcessName() const { return m_data.processName; } + virtual TestSpec const& testSpec() const override; - bool shouldDebugBreak() const { return m_data.shouldDebugBreak; } - - std::vector getReporterNames() const { return m_data.reporterNames; } - - int abortAfter() const { return m_data.abortAfter; } - - TestSpec const& testSpec() const { return m_testSpec; } - - bool showHelp() const { return m_data.showHelp; } - bool showInvisibles() const { return m_data.showInvisibles; } + bool showHelp() const; // IConfig interface - virtual bool allowThrows() const { return !m_data.noThrow; } - virtual std::ostream& stream() const { return m_stream->stream(); } - virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } - virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } - virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } - virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } - virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; } - virtual unsigned int rngSeed() const { return m_data.rngSeed; } - virtual UseColour::YesOrNo useColour() const { return m_data.useColour; } + bool allowThrows() const override; + std::ostream& stream() const override; + std::string name() const override; + bool includeSuccessfulResults() const override; + bool warnAboutMissingAssertions() const override; + ShowDurations::OrNot showDurations() const override; + RunTests::InWhatOrder runOrder() const override; + unsigned int rngSeed() const override; + int benchmarkResolutionMultiple() const override; + UseColour::YesOrNo useColour() const override; + bool shouldDebugBreak() const override; + int abortAfter() const override; + bool showInvisibles() const override; + Verbosity verbosity() const override; private: - IStream const* openStream() { - if( m_data.outputFilename.empty() ) - return new CoutStream(); - else if( m_data.outputFilename[0] == '%' ) { - if( m_data.outputFilename == "%debug" ) - return new DebugOutStream(); - else - throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); - } - else - return new FileStream( m_data.outputFilename ); - } + IStream const* openStream(); ConfigData m_data; - CATCH_AUTO_PTR( IStream const ) m_stream; + std::unique_ptr m_stream; TestSpec m_testSpec; }; } // end namespace Catch -// #included from: catch_clara.h -#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED - -// Use Catch's value for console width (store Clara's off to the side, if present) -#ifdef CLARA_CONFIG_CONSOLE_WIDTH -#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH -#undef CLARA_CONFIG_CONSOLE_WIDTH -#endif -#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH - -// Declare Clara inside the Catch namespace -#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { -// #included from: ../external/clara.h - -// Version 0.0.2.4 - -// Only use header guard if we are not using an outer namespace -#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) - -#ifndef STITCH_CLARA_OPEN_NAMESPACE -#define TWOBLUECUBES_CLARA_H_INCLUDED -#define STITCH_CLARA_OPEN_NAMESPACE -#define STITCH_CLARA_CLOSE_NAMESPACE -#else -#define STITCH_CLARA_CLOSE_NAMESPACE } -#endif - -#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE - -// ----------- #included from tbc_text_format.h ----------- - -// Only use header guard if we are not using an outer namespace -#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) -#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -#define TBC_TEXT_FORMAT_H_INCLUDED -#endif +// end catch_config.hpp +// start catch_assertionresult.h #include -#include -#include -#include - -// Use optional outer namespace -#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { -#endif - -namespace Tbc { - -#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH - const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - struct TextAttributes { - TextAttributes() - : initialIndent( std::string::npos ), - indent( 0 ), - width( consoleWidth-1 ), - tabChar( '\t' ) - {} - - TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } - TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } - TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } - - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos - }; - - class Text { - public: - Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) - : attr( _attr ) - { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; - - while( !remainder.empty() ) { - if( lines.size() >= 1000 ) { - lines.push_back( "... message truncated due to excessive size" ); - return; - } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); - std::size_t pos = remainder.find_first_of( '\n' ); - if( pos <= width ) { - width = pos; - } - pos = remainder.find_last_of( _attr.tabChar, width ); - if( pos != std::string::npos ) { - tabPos = pos; - if( remainder[width] == '\n' ) - width--; - remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); - } - - if( width == remainder.size() ) { - spliceLine( indent, remainder, width ); - } - else if( remainder[width] == '\n' ) { - spliceLine( indent, remainder, width ); - if( width <= 1 || remainder.size() != 1 ) - remainder = remainder.substr( 1 ); - indent = _attr.indent; - } - else { - pos = remainder.find_last_of( wrappableChars, width ); - if( pos != std::string::npos && pos > 0 ) { - spliceLine( indent, remainder, pos ); - if( remainder[0] == ' ' ) - remainder = remainder.substr( 1 ); - } - else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; - } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; - } - } - } - - void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { - lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); - _remainder = _remainder.substr( _pos ); - } - - typedef std::vector::const_iterator const_iterator; - - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } - std::string toString() const { - std::ostringstream oss; - oss << *this; - return oss.str(); - } - - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { - for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it ) { - if( it != _text.begin() ) - _stream << "\n"; - _stream << *it; - } - return _stream; - } - - private: - std::string str; - TextAttributes attr; - std::vector lines; - }; - -} // end namespace Tbc - -#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE -} // end outer namespace -#endif - -#endif // TBC_TEXT_FORMAT_H_INCLUDED - -// ----------- end of #include from tbc_text_format.h ----------- -// ........... back in clara.h - -#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE - -// ----------- #included from clara_compilers.h ----------- - -#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED -#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED - -// Detect a number of compiler features - mostly C++11/14 conformance - by compiler -// The following features are defined: -// -// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? -// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? -// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods -// CLARA_CONFIG_CPP11_OVERRIDE : is override supported? -// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) - -// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? - -// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? - -// In general each macro has a _NO_ form -// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 - -#ifdef __clang__ - -#if __has_feature(cxx_nullptr) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -#if __has_feature(cxx_noexcept) -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#endif - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ - -#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -// - otherwise more recent versions define __cplusplus >= 201103L -// and will get picked up below - -#endif // __GNUC__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#ifdef _MSC_VER - -#if (_MSC_VER >= 1600) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - -#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#endif - -#endif // _MSC_VER - -//////////////////////////////////////////////////////////////////////////////// -// C++ language feature support - -// catch all support for C++11 -#if defined(__cplusplus) && __cplusplus >= 201103L - -#define CLARA_CPP11_OR_GREATER - -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) -#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR -#endif - -#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT -#endif - -#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#endif - -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) -#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE -#endif -#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif - -#endif // __cplusplus >= 201103L - -// Now set the actual defines based on the above + anything the user has configured -#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_NULLPTR -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_NOEXCEPT -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_GENERATED_METHODS -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_OVERRIDE -#endif -#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) -#define CLARA_CONFIG_CPP11_UNIQUE_PTR -#endif - -// noexcept support: -#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) -#define CLARA_NOEXCEPT noexcept -# define CLARA_NOEXCEPT_IS(x) noexcept(x) -#else -#define CLARA_NOEXCEPT throw() -# define CLARA_NOEXCEPT_IS(x) -#endif - -// nullptr support -#ifdef CLARA_CONFIG_CPP11_NULLPTR -#define CLARA_NULL nullptr -#else -#define CLARA_NULL NULL -#endif - -// override support -#ifdef CLARA_CONFIG_CPP11_OVERRIDE -#define CLARA_OVERRIDE override -#else -#define CLARA_OVERRIDE -#endif - -// unique_ptr support -#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR -# define CLARA_AUTO_PTR( T ) std::unique_ptr -#else -# define CLARA_AUTO_PTR( T ) std::auto_ptr -#endif - -#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED - -// ----------- end of #include from clara_compilers.h ----------- -// ........... back in clara.h - -#include -#include -#include - -#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -#define CLARA_PLATFORM_WINDOWS -#endif - -// Use optional outer namespace -#ifdef STITCH_CLARA_OPEN_NAMESPACE -STITCH_CLARA_OPEN_NAMESPACE -#endif - -namespace Clara { - - struct UnpositionalTag {}; - - extern UnpositionalTag _; - -#ifdef CLARA_CONFIG_MAIN - UnpositionalTag _; -#endif - - namespace Detail { - -#ifdef CLARA_CONSOLE_WIDTH - const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - using namespace Tbc; - - inline bool startsWith( std::string const& str, std::string const& prefix ) { - return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; - } - - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - template struct RemoveConstRef{ typedef T type; }; - - template struct IsBool { static const bool value = false; }; - template<> struct IsBool { static const bool value = true; }; - - template - void convertInto( std::string const& _source, T& _dest ) { - std::stringstream ss; - ss << _source; - ss >> _dest; - if( ss.fail() ) - throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); - } - inline void convertInto( std::string const& _source, std::string& _dest ) { - _dest = _source; - } - inline void convertInto( std::string const& _source, bool& _dest ) { - std::string sourceLC = _source; - std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower ); - if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) - _dest = true; - else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) - _dest = false; - else - throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); - } - - template - struct IArgFunction { - virtual ~IArgFunction() {} -#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS - IArgFunction() = default; - IArgFunction( IArgFunction const& ) = default; -#endif - virtual void set( ConfigT& config, std::string const& value ) const = 0; - virtual bool takesArg() const = 0; - virtual IArgFunction* clone() const = 0; - }; - - template - class BoundArgFunction { - public: - BoundArgFunction() : functionObj( CLARA_NULL ) {} - BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} - BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {} - BoundArgFunction& operator = ( BoundArgFunction const& other ) { - IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; - delete functionObj; - functionObj = newFunctionObj; - return *this; - } - ~BoundArgFunction() { delete functionObj; } - - void set( ConfigT& config, std::string const& value ) const { - functionObj->set( config, value ); - } - bool takesArg() const { return functionObj->takesArg(); } - - bool isSet() const { - return functionObj != CLARA_NULL; - } - private: - IArgFunction* functionObj; - }; - - template - struct NullBinder : IArgFunction{ - virtual void set( C&, std::string const& ) const {} - virtual bool takesArg() const { return true; } - virtual IArgFunction* clone() const { return new NullBinder( *this ); } - }; - - template - struct BoundDataMember : IArgFunction{ - BoundDataMember( M C::* _member ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - convertInto( stringValue, p.*member ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } - M C::* member; - }; - template - struct BoundUnaryMethod : IArgFunction{ - BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - typename RemoveConstRef::type value; - convertInto( stringValue, value ); - (p.*member)( value ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } - void (C::*member)( M ); - }; - template - struct BoundNullaryMethod : IArgFunction{ - BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} - virtual void set( C& p, std::string const& stringValue ) const { - bool value; - convertInto( stringValue, value ); - if( value ) - (p.*member)(); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } - void (C::*member)(); - }; - - template - struct BoundUnaryFunction : IArgFunction{ - BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} - virtual void set( C& obj, std::string const& stringValue ) const { - bool value; - convertInto( stringValue, value ); - if( value ) - function( obj ); - } - virtual bool takesArg() const { return false; } - virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } - void (*function)( C& ); - }; - - template - struct BoundBinaryFunction : IArgFunction{ - BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} - virtual void set( C& obj, std::string const& stringValue ) const { - typename RemoveConstRef::type value; - convertInto( stringValue, value ); - function( obj, value ); - } - virtual bool takesArg() const { return !IsBool::value; } - virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } - void (*function)( C&, T ); - }; - - } // namespace Detail - - inline std::vector argsToVector( int argc, char const* const* const argv ) { - std::vector args( static_cast( argc ) ); - for( std::size_t i = 0; i < static_cast( argc ); ++i ) - args[i] = argv[i]; - - return args; - } - - class Parser { - enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional }; - Mode mode; - std::size_t from; - bool inQuotes; - public: - - struct Token { - enum Type { Positional, ShortOpt, LongOpt }; - Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} - Type type; - std::string data; - }; - - Parser() : mode( None ), from( 0 ), inQuotes( false ){} - - void parseIntoTokens( std::vector const& args, std::vector& tokens ) { - const std::string doubleDash = "--"; - for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i ) - parseIntoTokens( args[i], tokens); - } - - void parseIntoTokens( std::string const& arg, std::vector& tokens ) { - for( std::size_t i = 0; i <= arg.size(); ++i ) { - char c = arg[i]; - if( c == '"' ) - inQuotes = !inQuotes; - mode = handleMode( i, c, arg, tokens ); - } - } - Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - switch( mode ) { - case None: return handleNone( i, c ); - case MaybeShortOpt: return handleMaybeShortOpt( i, c ); - case ShortOpt: - case LongOpt: - case SlashOpt: return handleOpt( i, c, arg, tokens ); - case Positional: return handlePositional( i, c, arg, tokens ); - default: throw std::logic_error( "Unknown mode" ); - } - } - - Mode handleNone( std::size_t i, char c ) { - if( inQuotes ) { - from = i; - return Positional; - } - switch( c ) { - case '-': return MaybeShortOpt; -#ifdef CLARA_PLATFORM_WINDOWS - case '/': from = i+1; return SlashOpt; -#endif - default: from = i; return Positional; - } - } - Mode handleMaybeShortOpt( std::size_t i, char c ) { - switch( c ) { - case '-': from = i+1; return LongOpt; - default: from = i; return ShortOpt; - } - } - Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) - return mode; - - std::string optName = arg.substr( from, i-from ); - if( mode == ShortOpt ) - for( std::size_t j = 0; j < optName.size(); ++j ) - tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) ); - else if( mode == SlashOpt && optName.size() == 1 ) - tokens.push_back( Token( Token::ShortOpt, optName ) ); - else - tokens.push_back( Token( Token::LongOpt, optName ) ); - return None; - } - Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { - if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos ) - return mode; - - std::string data = arg.substr( from, i-from ); - tokens.push_back( Token( Token::Positional, data ) ); - return None; - } - }; - - template - struct CommonArgProperties { - CommonArgProperties() {} - CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} - - Detail::BoundArgFunction boundField; - std::string description; - std::string detail; - std::string placeholder; // Only value if boundField takes an arg - - bool takesArg() const { - return !placeholder.empty(); - } - void validate() const { - if( !boundField.isSet() ) - throw std::logic_error( "option not bound" ); - } - }; - struct OptionArgProperties { - std::vector shortNames; - std::string longName; - - bool hasShortName( std::string const& shortName ) const { - return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); - } - bool hasLongName( std::string const& _longName ) const { - return _longName == longName; - } - }; - struct PositionalArgProperties { - PositionalArgProperties() : position( -1 ) {} - int position; // -1 means non-positional (floating) - - bool isFixedPositional() const { - return position != -1; - } - }; - - template - class CommandLine { - - struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { - Arg() {} - Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} - - using CommonArgProperties::placeholder; // !TBD - - std::string dbgName() const { - if( !longName.empty() ) - return "--" + longName; - if( !shortNames.empty() ) - return "-" + shortNames[0]; - return "positional args"; - } - std::string commands() const { - std::ostringstream oss; - bool first = true; - std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); - for(; it != itEnd; ++it ) { - if( first ) - first = false; - else - oss << ", "; - oss << "-" << *it; - } - if( !longName.empty() ) { - if( !first ) - oss << ", "; - oss << "--" << longName; - } - if( !placeholder.empty() ) - oss << " <" << placeholder << ">"; - return oss.str(); - } - }; - - typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr; - - friend void addOptName( Arg& arg, std::string const& optName ) - { - if( optName.empty() ) - return; - if( Detail::startsWith( optName, "--" ) ) { - if( !arg.longName.empty() ) - throw std::logic_error( "Only one long opt may be specified. '" - + arg.longName - + "' already specified, now attempting to add '" - + optName + "'" ); - arg.longName = optName.substr( 2 ); - } - else if( Detail::startsWith( optName, "-" ) ) - arg.shortNames.push_back( optName.substr( 1 ) ); - else - throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); - } - friend void setPositionalArg( Arg& arg, int position ) - { - arg.position = position; - } - - class ArgBuilder { - public: - ArgBuilder( Arg* arg ) : m_arg( arg ) {} - - // Bind a non-boolean data member (requires placeholder string) - template - void bind( M C::* field, std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundDataMember( field ); - m_arg->placeholder = placeholder; - } - // Bind a boolean data member (no placeholder required) - template - void bind( bool C::* field ) { - m_arg->boundField = new Detail::BoundDataMember( field ); - } - - // Bind a method taking a single, non-boolean argument (requires a placeholder string) - template - void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); - m_arg->placeholder = placeholder; - } - - // Bind a method taking a single, boolean argument (no placeholder string required) - template - void bind( void (C::* unaryMethod)( bool ) ) { - m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); - } - - // Bind a method that takes no arguments (will be called if opt is present) - template - void bind( void (C::* nullaryMethod)() ) { - m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); - } - - // Bind a free function taking a single argument - the object to operate on (no placeholder string required) - template - void bind( void (* unaryFunction)( C& ) ) { - m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); - } - - // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) - template - void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { - m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); - m_arg->placeholder = placeholder; - } - - ArgBuilder& describe( std::string const& description ) { - m_arg->description = description; - return *this; - } - ArgBuilder& detail( std::string const& detail ) { - m_arg->detail = detail; - return *this; - } - - protected: - Arg* m_arg; - }; - - class OptBuilder : public ArgBuilder { - public: - OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} - OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} - - OptBuilder& operator[]( std::string const& optName ) { - addOptName( *ArgBuilder::m_arg, optName ); - return *this; - } - }; - - public: - - CommandLine() - : m_boundProcessName( new Detail::NullBinder() ), - m_highestSpecifiedArgPosition( 0 ), - m_throwOnUnrecognisedTokens( false ) - {} - CommandLine( CommandLine const& other ) - : m_boundProcessName( other.m_boundProcessName ), - m_options ( other.m_options ), - m_positionalArgs( other.m_positionalArgs ), - m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), - m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) - { - if( other.m_floatingArg.get() ) - m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); - } - - CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { - m_throwOnUnrecognisedTokens = shouldThrow; - return *this; - } - - OptBuilder operator[]( std::string const& optName ) { - m_options.push_back( Arg() ); - addOptName( m_options.back(), optName ); - OptBuilder builder( &m_options.back() ); - return builder; - } - - ArgBuilder operator[]( int position ) { - m_positionalArgs.insert( std::make_pair( position, Arg() ) ); - if( position > m_highestSpecifiedArgPosition ) - m_highestSpecifiedArgPosition = position; - setPositionalArg( m_positionalArgs[position], position ); - ArgBuilder builder( &m_positionalArgs[position] ); - return builder; - } - - // Invoke this with the _ instance - ArgBuilder operator[]( UnpositionalTag ) { - if( m_floatingArg.get() ) - throw std::logic_error( "Only one unpositional argument can be added" ); - m_floatingArg.reset( new Arg() ); - ArgBuilder builder( m_floatingArg.get() ); - return builder; - } - - template - void bindProcessName( M C::* field ) { - m_boundProcessName = new Detail::BoundDataMember( field ); - } - template - void bindProcessName( void (C::*_unaryMethod)( M ) ) { - m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); - } - - void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { - typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; - std::size_t maxWidth = 0; - for( it = itBegin; it != itEnd; ++it ) - maxWidth = (std::max)( maxWidth, it->commands().size() ); - - for( it = itBegin; it != itEnd; ++it ) { - Detail::Text usage( it->commands(), Detail::TextAttributes() - .setWidth( maxWidth+indent ) - .setIndent( indent ) ); - Detail::Text desc( it->description, Detail::TextAttributes() - .setWidth( width - maxWidth - 3 ) ); - - for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { - std::string usageCol = i < usage.size() ? usage[i] : ""; - os << usageCol; - - if( i < desc.size() && !desc[i].empty() ) - os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) - << desc[i]; - os << "\n"; - } - } - } - std::string optUsage() const { - std::ostringstream oss; - optUsage( oss ); - return oss.str(); - } - - void argSynopsis( std::ostream& os ) const { - for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { - if( i > 1 ) - os << " "; - typename std::map::const_iterator it = m_positionalArgs.find( i ); - if( it != m_positionalArgs.end() ) - os << "<" << it->second.placeholder << ">"; - else if( m_floatingArg.get() ) - os << "<" << m_floatingArg->placeholder << ">"; - else - throw std::logic_error( "non consecutive positional arguments with no floating args" ); - } - // !TBD No indication of mandatory args - if( m_floatingArg.get() ) { - if( m_highestSpecifiedArgPosition > 1 ) - os << " "; - os << "[<" << m_floatingArg->placeholder << "> ...]"; - } - } - std::string argSynopsis() const { - std::ostringstream oss; - argSynopsis( oss ); - return oss.str(); - } - - void usage( std::ostream& os, std::string const& procName ) const { - validate(); - os << "usage:\n " << procName << " "; - argSynopsis( os ); - if( !m_options.empty() ) { - os << " [options]\n\nwhere options are: \n"; - optUsage( os, 2 ); - } - os << "\n"; - } - std::string usage( std::string const& procName ) const { - std::ostringstream oss; - usage( oss, procName ); - return oss.str(); - } - - ConfigT parse( std::vector const& args ) const { - ConfigT config; - parseInto( args, config ); - return config; - } - - std::vector parseInto( std::vector const& args, ConfigT& config ) const { - std::string processName = args[0]; - std::size_t lastSlash = processName.find_last_of( "/\\" ); - if( lastSlash != std::string::npos ) - processName = processName.substr( lastSlash+1 ); - m_boundProcessName.set( config, processName ); - std::vector tokens; - Parser parser; - parser.parseIntoTokens( args, tokens ); - return populate( tokens, config ); - } - - std::vector populate( std::vector const& tokens, ConfigT& config ) const { - validate(); - std::vector unusedTokens = populateOptions( tokens, config ); - unusedTokens = populateFixedArgs( unusedTokens, config ); - unusedTokens = populateFloatingArgs( unusedTokens, config ); - return unusedTokens; - } - - std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { - std::vector unusedTokens; - std::vector errors; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); - for(; it != itEnd; ++it ) { - Arg const& arg = *it; - - try { - if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || - ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { - if( arg.takesArg() ) { - if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) - errors.push_back( "Expected argument to option: " + token.data ); - else - arg.boundField.set( config, tokens[++i].data ); - } - else { - arg.boundField.set( config, "true" ); - } - break; - } - } - catch( std::exception& ex ) { - errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); - } - } - if( it == itEnd ) { - if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) - unusedTokens.push_back( token ); - else if( errors.empty() && m_throwOnUnrecognisedTokens ) - errors.push_back( "unrecognised option: " + token.data ); - } - } - if( !errors.empty() ) { - std::ostringstream oss; - for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); - it != itEnd; - ++it ) { - if( it != errors.begin() ) - oss << "\n"; - oss << *it; - } - throw std::runtime_error( oss.str() ); - } - return unusedTokens; - } - std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { - std::vector unusedTokens; - int position = 1; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - typename std::map::const_iterator it = m_positionalArgs.find( position ); - if( it != m_positionalArgs.end() ) - it->second.boundField.set( config, token.data ); - else - unusedTokens.push_back( token ); - if( token.type == Parser::Token::Positional ) - position++; - } - return unusedTokens; - } - std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { - if( !m_floatingArg.get() ) - return tokens; - std::vector unusedTokens; - for( std::size_t i = 0; i < tokens.size(); ++i ) { - Parser::Token const& token = tokens[i]; - if( token.type == Parser::Token::Positional ) - m_floatingArg->boundField.set( config, token.data ); - else - unusedTokens.push_back( token ); - } - return unusedTokens; - } - - void validate() const - { - if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) - throw std::logic_error( "No options or arguments specified" ); - - for( typename std::vector::const_iterator it = m_options.begin(), - itEnd = m_options.end(); - it != itEnd; ++it ) - it->validate(); - } - - private: - Detail::BoundArgFunction m_boundProcessName; - std::vector m_options; - std::map m_positionalArgs; - ArgAutoPtr m_floatingArg; - int m_highestSpecifiedArgPosition; - bool m_throwOnUnrecognisedTokens; - }; - -} // end namespace Clara - -STITCH_CLARA_CLOSE_NAMESPACE -#undef STITCH_CLARA_OPEN_NAMESPACE -#undef STITCH_CLARA_CLOSE_NAMESPACE - -#endif // TWOBLUECUBES_CLARA_H_INCLUDED -#undef STITCH_CLARA_OPEN_NAMESPACE - -// Restore Clara's value for console width, if present -#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH -#endif - -#include namespace Catch { - inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } - inline void abortAfterX( ConfigData& config, int x ) { - if( x < 1 ) - throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); - config.abortAfter = x; - } - inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } - inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } + struct AssertionResultData + { + AssertionResultData() = delete; - inline void addWarning( ConfigData& config, std::string const& _warning ) { - if( _warning == "NoAssertions" ) - config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); - else - throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" ); - } - inline void setOrder( ConfigData& config, std::string const& order ) { - if( startsWith( "declared", order ) ) - config.runOrder = RunTests::InDeclarationOrder; - else if( startsWith( "lexical", order ) ) - config.runOrder = RunTests::InLexicographicalOrder; - else if( startsWith( "random", order ) ) - config.runOrder = RunTests::InRandomOrder; - else - throw std::runtime_error( "Unrecognised ordering: '" + order + "'" ); - } - inline void setRngSeed( ConfigData& config, std::string const& seed ) { - if( seed == "time" ) { - config.rngSeed = static_cast( std::time(0) ); - } - else { - std::stringstream ss; - ss << seed; - ss >> config.rngSeed; - if( ss.fail() ) - throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" ); - } - } - inline void setVerbosity( ConfigData& config, int level ) { - // !TBD: accept strings? - config.verbosity = static_cast( level ); - } - inline void setShowDurations( ConfigData& config, bool _showDurations ) { - config.showDurations = _showDurations - ? ShowDurations::Always - : ShowDurations::Never; - } - inline void setUseColour( ConfigData& config, std::string const& value ) { - std::string mode = toLower( value ); + AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); - if( mode == "yes" ) - config.useColour = UseColour::Yes; - else if( mode == "no" ) - config.useColour = UseColour::No; - else if( mode == "auto" ) - config.useColour = UseColour::Auto; - else - throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); - } - inline void forceColour( ConfigData& config ) { - config.useColour = UseColour::Yes; - } - inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { - std::ifstream f( _filename.c_str() ); - if( !f.is_open() ) - throw std::domain_error( "Unable to load input file: " + _filename ); + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; - std::string line; - while( std::getline( f, line ) ) { - line = trim(line); - if( !line.empty() && !startsWith( line, "#" ) ) { - if( !startsWith( line, "\"" ) ) - line = "\"" + line + "\""; - addTestOrTags( config, line + "," ); - } - } - } + std::string reconstructExpression() const; + }; - inline Clara::CommandLine makeCommandLineParser() { + class AssertionResult { + public: + AssertionResult() = delete; + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); - using namespace Clara; - CommandLine cli; + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + StringRef getTestMacroName() const; - cli.bindProcessName( &ConfigData::processName ); - - cli["-?"]["-h"]["--help"] - .describe( "display usage information" ) - .bind( &ConfigData::showHelp ); - - cli["-l"]["--list-tests"] - .describe( "list all/matching test cases" ) - .bind( &ConfigData::listTests ); - - cli["-t"]["--list-tags"] - .describe( "list all/matching tags" ) - .bind( &ConfigData::listTags ); - - cli["-s"]["--success"] - .describe( "include successful tests in output" ) - .bind( &ConfigData::showSuccessfulTests ); - - cli["-b"]["--break"] - .describe( "break into debugger on failure" ) - .bind( &ConfigData::shouldDebugBreak ); - - cli["-e"]["--nothrow"] - .describe( "skip exception tests" ) - .bind( &ConfigData::noThrow ); - - cli["-i"]["--invisibles"] - .describe( "show invisibles (tabs, newlines)" ) - .bind( &ConfigData::showInvisibles ); - - cli["-o"]["--out"] - .describe( "output filename" ) - .bind( &ConfigData::outputFilename, "filename" ); - - cli["-r"]["--reporter"] -// .placeholder( "name[:filename]" ) - .describe( "reporter to use (defaults to console)" ) - .bind( &addReporterName, "name" ); - - cli["-n"]["--name"] - .describe( "suite name" ) - .bind( &ConfigData::name, "name" ); - - cli["-a"]["--abort"] - .describe( "abort at first failure" ) - .bind( &abortAfterFirst ); - - cli["-x"]["--abortx"] - .describe( "abort after x failures" ) - .bind( &abortAfterX, "no. failures" ); - - cli["-w"]["--warn"] - .describe( "enable warnings" ) - .bind( &addWarning, "warning name" ); - -// - needs updating if reinstated -// cli.into( &setVerbosity ) -// .describe( "level of verbosity (0=no output)" ) -// .shortOpt( "v") -// .longOpt( "verbosity" ) -// .placeholder( "level" ); - - cli[_] - .describe( "which test or tests to use" ) - .bind( &addTestOrTags, "test name, pattern or tags" ); - - cli["-d"]["--durations"] - .describe( "show test durations" ) - .bind( &setShowDurations, "yes|no" ); - - cli["-f"]["--input-file"] - .describe( "load test names to run from a file" ) - .bind( &loadTestNamesFromFile, "filename" ); - - cli["-#"]["--filenames-as-tags"] - .describe( "adds a tag for the filename" ) - .bind( &ConfigData::filenamesAsTags ); - - // Less common commands which don't have a short form - cli["--list-test-names-only"] - .describe( "list all/matching test cases names only" ) - .bind( &ConfigData::listTestNamesOnly ); - - cli["--list-reporters"] - .describe( "list all reporters" ) - .bind( &ConfigData::listReporters ); - - cli["--order"] - .describe( "test case order (defaults to decl)" ) - .bind( &setOrder, "decl|lex|rand" ); - - cli["--rng-seed"] - .describe( "set a specific seed for random numbers" ) - .bind( &setRngSeed, "'time'|number" ); - - cli["--force-colour"] - .describe( "force colourised output (deprecated)" ) - .bind( &forceColour ); - - cli["--use-colour"] - .describe( "should output be colourised" ) - .bind( &setUseColour, "yes|no" ); - - return cli; - } + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; } // end namespace Catch -// #included from: internal/catch_list.hpp -#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED +// end catch_assertionresult.h +// start catch_option.hpp -// #included from: catch_text.h -#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED +namespace Catch { -#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH - -#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch -// #included from: ../external/tbc_text_format.h -// Only use header guard if we are not using an outer namespace -#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED -# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -# endif -# else -# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED -# endif -#endif -#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -#include -#include -#include - -// Use optional outer namespace -#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { -#endif - -namespace Tbc { - -#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH - const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; -#else - const unsigned int consoleWidth = 80; -#endif - - struct TextAttributes { - TextAttributes() - : initialIndent( std::string::npos ), - indent( 0 ), - width( consoleWidth-1 ), - tabChar( '\t' ) + // An optional type + template + class Option { + public: + Option() : nullableValue( nullptr ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) {} - TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } - TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } - TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } - TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } + ~Option() { + reset(); + } - std::size_t initialIndent; // indent of first line, or npos - std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos - std::size_t width; // maximum width of text, including indent. Longer text will wrap - char tabChar; // If this char is seen the indent is changed to current pos - }; - - class Text { - public: - Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) - : attr( _attr ) - { - std::string wrappableChars = " [({.,/|\\-"; - std::size_t indent = _attr.initialIndent != std::string::npos - ? _attr.initialIndent - : _attr.indent; - std::string remainder = _str; - - while( !remainder.empty() ) { - if( lines.size() >= 1000 ) { - lines.push_back( "... message truncated due to excessive size" ); - return; - } - std::size_t tabPos = std::string::npos; - std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); - std::size_t pos = remainder.find_first_of( '\n' ); - if( pos <= width ) { - width = pos; - } - pos = remainder.find_last_of( _attr.tabChar, width ); - if( pos != std::string::npos ) { - tabPos = pos; - if( remainder[width] == '\n' ) - width--; - remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); - } - - if( width == remainder.size() ) { - spliceLine( indent, remainder, width ); - } - else if( remainder[width] == '\n' ) { - spliceLine( indent, remainder, width ); - if( width <= 1 || remainder.size() != 1 ) - remainder = remainder.substr( 1 ); - indent = _attr.indent; - } - else { - pos = remainder.find_last_of( wrappableChars, width ); - if( pos != std::string::npos && pos > 0 ) { - spliceLine( indent, remainder, pos ); - if( remainder[0] == ' ' ) - remainder = remainder.substr( 1 ); - } - else { - spliceLine( indent, remainder, width-1 ); - lines.back() += "-"; - } - if( lines.size() == 1 ) - indent = _attr.indent; - if( tabPos != std::string::npos ) - indent += tabPos; - } + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; } - void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { - lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); - _remainder = _remainder.substr( _pos ); + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = nullptr; } - typedef std::vector::const_iterator const_iterator; + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } - const_iterator begin() const { return lines.begin(); } - const_iterator end() const { return lines.end(); } - std::string const& last() const { return lines.back(); } - std::size_t size() const { return lines.size(); } - std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } - std::string toString() const { - std::ostringstream oss; - oss << *this; - return oss.str(); + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; } - inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { - for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); - it != itEnd; ++it ) { - if( it != _text.begin() ) - _stream << "\n"; - _stream << *it; - } - return _stream; + bool some() const { return nullableValue != nullptr; } + bool none() const { return nullableValue == nullptr; } + + bool operator !() const { return nullableValue == nullptr; } + explicit operator bool() const { + return some(); } private: - std::string str; - TextAttributes attr; - std::vector lines; + T *nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; }; -} // end namespace Tbc +} // end namespace Catch -#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE -} // end outer namespace -#endif - -#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED -#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +// end catch_option.hpp +#include +#include +#include +#include +#include namespace Catch { - using Tbc::Text; - using Tbc::TextAttributes; -} -// #included from: catch_console_colour.hpp -#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED + struct ReporterConfig { + explicit ReporterConfig( IConfigPtr const& _fullConfig ); + + ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ); + + std::ostream& stream() const; + IConfigPtr fullConfig() const; + + private: + std::ostream* m_stream; + IConfigPtr m_fullConfig; + }; + + struct ReporterPreferences { + bool shouldRedirectStdOut = false; + }; + + template + struct LazyStat : Option { + LazyStat& operator=( T const& _value ) { + Option::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option::reset(); + used = false; + } + bool used = false; + }; + + struct TestRunInfo { + TestRunInfo( std::string const& _name ); + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ); + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ); + + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = default; + AssertionStats& operator = ( AssertionStats && ) = default; + virtual ~AssertionStats(); + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ); + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; + virtual ~SectionStats(); + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ); + + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; + virtual ~TestCaseStats(); + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ); + TestGroupStats( GroupInfo const& _groupInfo ); + + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; + virtual ~TestGroupStats(); + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ); + + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; + virtual ~TestRunStats(); + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + struct BenchmarkInfo { + std::string name; + }; + struct BenchmarkStats { + BenchmarkInfo info; + std::size_t iterations; + uint64_t elapsedTimeInNanoseconds; + }; + + struct IStreamingReporter { + virtual ~IStreamingReporter() = default; + + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + // static std::set getSupportedVerbosities() + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases( std::string const& spec ) = 0; + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + // *** experimental *** + virtual void benchmarkStarting( BenchmarkInfo const& ) {} + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + + // *** experimental *** + virtual void benchmarkEnded( BenchmarkStats const& ) {} + + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + // Default empty implementation provided + virtual void fatalErrorEncountered( StringRef name ); + + virtual bool isMulti() const; + }; + using IStreamingReporterPtr = std::unique_ptr; + + struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + using IReporterFactoryPtr = std::shared_ptr; + + struct IReporterRegistry { + using FactoryMap = std::map; + using Listeners = std::vector; + + virtual ~IReporterRegistry(); + virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + virtual Listeners const& getListeners() const = 0; + }; + + void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ); + +} // end namespace Catch + +// end catch_interfaces_reporter.h +#include +#include +#include +#include +#include +#include +#include + +namespace Catch { + void prepareExpandedExpression(AssertionResult& result); + + // Returns double formatted as %.3f (format expected on output) + std::string getFormattedDuration( double duration ); + + template + struct StreamingReporterBase : IStreamingReporter { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + throw std::domain_error( "Verbosity level not supported by this reporter" ); + } + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + ~StreamingReporterBase() override = default; + + void noMatchingTestCases(std::string const&) override {} + + void testRunStarting(TestRunInfo const& _testRunInfo) override { + currentTestRunInfo = _testRunInfo; + } + void testGroupStarting(GroupInfo const& _groupInfo) override { + currentGroupInfo = _groupInfo; + } + + void testCaseStarting(TestCaseInfo const& _testInfo) override { + currentTestCaseInfo = _testInfo; + } + void sectionStarting(SectionInfo const& _sectionInfo) override { + m_sectionStack.push_back(_sectionInfo); + } + + void sectionEnded(SectionStats const& /* _sectionStats */) override { + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { + currentTestCaseInfo.reset(); + } + void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { + currentGroupInfo.reset(); + } + void testRunEnded(TestRunStats const& /* _testRunStats */) override { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + void skipTest(TestCaseInfo const&) override { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + IConfigPtr m_config; + std::ostream& stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + struct CumulativeReporterBase : IStreamingReporter { + template + struct Node { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + using ChildNodes = std::vector>; + T value; + ChildNodes children; + }; + struct SectionNode { + explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} + virtual ~SectionNode() = default; + + bool operator == (SectionNode const& other) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator == (std::shared_ptr const& other) const { + return operator==(*other); + } + + SectionStats stats; + using ChildSections = std::vector>; + using Assertions = std::vector; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + bool operator() (std::shared_ptr const& node) const { + return ((node->stats.sectionInfo.name == m_other.name) && + (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); + } + void operator=(BySectionInfo const&) = delete; + + private: + SectionInfo const& m_other; + }; + + using TestCaseNode = Node; + using TestGroupNode = Node; + using TestRunNode = Node; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) + throw std::domain_error( "Verbosity level not supported by this reporter" ); + } + ~CumulativeReporterBase() override = default; + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() { + return { Verbosity::Normal }; + } + + void testRunStarting( TestRunInfo const& ) override {} + void testGroupStarting( GroupInfo const& ) override {} + + void testCaseStarting( TestCaseInfo const& ) override {} + + void sectionStarting( SectionInfo const& sectionInfo ) override { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + std::shared_ptr node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = std::make_shared( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + auto it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = std::make_shared( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = std::move(node); + } + + void assertionStarting(AssertionInfo const&) override {} + + bool assertionEnded(AssertionStats const& assertionStats) override { + assert(!m_sectionStack.empty()); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression(const_cast( assertionStats.assertionResult ) ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back(assertionStats); + return true; + } + void sectionEnded(SectionStats const& sectionStats) override { + assert(!m_sectionStack.empty()); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const& testCaseStats) override { + auto node = std::make_shared(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(m_rootSection); + m_testCases.push_back(node); + m_rootSection.reset(); + + assert(m_deepestSection); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + void testGroupEnded(TestGroupStats const& testGroupStats) override { + auto node = std::make_shared(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); + } + void testRunEnded(TestRunStats const& testRunStats) override { + auto node = std::make_shared(testRunStats); + node->children.swap(m_testGroups); + m_testRuns.push_back(node); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + void skipTest(TestCaseInfo const&) override {} + + IConfigPtr m_config; + std::ostream& stream; + std::vector m_assertions; + std::vector>> m_sections; + std::vector> m_testCases; + std::vector> m_testGroups; + + std::vector> m_testRuns; + + std::shared_ptr m_rootSection; + std::shared_ptr m_deepestSection; + std::vector> m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; + } + + struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase( ReporterConfig const& _config ); + + void assertionStarting(AssertionInfo const&) override; + bool assertionEnded(AssertionStats const&) override; + }; + +} // end namespace Catch + +// end catch_reporter_bases.hpp +// start catch_console_colour.h namespace Catch { @@ -5047,458 +3906,419 @@ namespace Catch { // Use constructed object for RAII guard Colour( Code _colourCode ); - Colour( Colour const& other ); + Colour( Colour&& other ) noexcept; + Colour& operator=( Colour&& other ) noexcept; ~Colour(); // Use static method for one-shot changes static void use( Code _colourCode ); private: - bool m_moved; + bool m_moved = false; }; - inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } + std::ostream& operator << ( std::ostream& os, Colour const& ); } // end namespace Catch -// #included from: catch_interfaces_reporter.h -#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED +// end catch_console_colour.h +// start catch_reporter_registrars.hpp -#include -#include -#include -#include - -namespace Catch -{ - struct ReporterConfig { - explicit ReporterConfig( Ptr const& _fullConfig ) - : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} - - ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) - : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} - - std::ostream& stream() const { return *m_stream; } - Ptr fullConfig() const { return m_fullConfig; } - - private: - std::ostream* m_stream; - Ptr m_fullConfig; - }; - - struct ReporterPreferences { - ReporterPreferences() - : shouldRedirectStdOut( false ) - {} - - bool shouldRedirectStdOut; - }; - - template - struct LazyStat : Option { - LazyStat() : used( false ) {} - LazyStat& operator=( T const& _value ) { - Option::operator=( _value ); - used = false; - return *this; - } - void reset() { - Option::reset(); - used = false; - } - bool used; - }; - - struct TestRunInfo { - TestRunInfo( std::string const& _name ) : name( _name ) {} - std::string name; - }; - struct GroupInfo { - GroupInfo( std::string const& _name, - std::size_t _groupIndex, - std::size_t _groupsCount ) - : name( _name ), - groupIndex( _groupIndex ), - groupsCounts( _groupsCount ) - {} - - std::string name; - std::size_t groupIndex; - std::size_t groupsCounts; - }; - - struct AssertionStats { - AssertionStats( AssertionResult const& _assertionResult, - std::vector const& _infoMessages, - Totals const& _totals ) - : assertionResult( _assertionResult ), - infoMessages( _infoMessages ), - totals( _totals ) - { - if( assertionResult.hasMessage() ) { - // Copy message into messages list. - // !TBD This should have been done earlier, somewhere - MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); - builder << assertionResult.getMessage(); - builder.m_info.message = builder.m_stream.str(); - - infoMessages.push_back( builder.m_info ); - } - } - virtual ~AssertionStats(); - -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - AssertionStats( AssertionStats const& ) = default; - AssertionStats( AssertionStats && ) = default; - AssertionStats& operator = ( AssertionStats const& ) = default; - AssertionStats& operator = ( AssertionStats && ) = default; -# endif - - AssertionResult assertionResult; - std::vector infoMessages; - Totals totals; - }; - - struct SectionStats { - SectionStats( SectionInfo const& _sectionInfo, - Counts const& _assertions, - double _durationInSeconds, - bool _missingAssertions ) - : sectionInfo( _sectionInfo ), - assertions( _assertions ), - durationInSeconds( _durationInSeconds ), - missingAssertions( _missingAssertions ) - {} - virtual ~SectionStats(); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - SectionStats( SectionStats const& ) = default; - SectionStats( SectionStats && ) = default; - SectionStats& operator = ( SectionStats const& ) = default; - SectionStats& operator = ( SectionStats && ) = default; -# endif - - SectionInfo sectionInfo; - Counts assertions; - double durationInSeconds; - bool missingAssertions; - }; - - struct TestCaseStats { - TestCaseStats( TestCaseInfo const& _testInfo, - Totals const& _totals, - std::string const& _stdOut, - std::string const& _stdErr, - bool _aborting ) - : testInfo( _testInfo ), - totals( _totals ), - stdOut( _stdOut ), - stdErr( _stdErr ), - aborting( _aborting ) - {} - virtual ~TestCaseStats(); - -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestCaseStats( TestCaseStats const& ) = default; - TestCaseStats( TestCaseStats && ) = default; - TestCaseStats& operator = ( TestCaseStats const& ) = default; - TestCaseStats& operator = ( TestCaseStats && ) = default; -# endif - - TestCaseInfo testInfo; - Totals totals; - std::string stdOut; - std::string stdErr; - bool aborting; - }; - - struct TestGroupStats { - TestGroupStats( GroupInfo const& _groupInfo, - Totals const& _totals, - bool _aborting ) - : groupInfo( _groupInfo ), - totals( _totals ), - aborting( _aborting ) - {} - TestGroupStats( GroupInfo const& _groupInfo ) - : groupInfo( _groupInfo ), - aborting( false ) - {} - virtual ~TestGroupStats(); - -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestGroupStats( TestGroupStats const& ) = default; - TestGroupStats( TestGroupStats && ) = default; - TestGroupStats& operator = ( TestGroupStats const& ) = default; - TestGroupStats& operator = ( TestGroupStats && ) = default; -# endif - - GroupInfo groupInfo; - Totals totals; - bool aborting; - }; - - struct TestRunStats { - TestRunStats( TestRunInfo const& _runInfo, - Totals const& _totals, - bool _aborting ) - : runInfo( _runInfo ), - totals( _totals ), - aborting( _aborting ) - {} - virtual ~TestRunStats(); - -# ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS - TestRunStats( TestRunStats const& _other ) - : runInfo( _other.runInfo ), - totals( _other.totals ), - aborting( _other.aborting ) - {} -# else - TestRunStats( TestRunStats const& ) = default; - TestRunStats( TestRunStats && ) = default; - TestRunStats& operator = ( TestRunStats const& ) = default; - TestRunStats& operator = ( TestRunStats && ) = default; -# endif - - TestRunInfo runInfo; - Totals totals; - bool aborting; - }; - - class MultipleReporters; - - struct IStreamingReporter : IShared { - virtual ~IStreamingReporter(); - - // Implementing class must also provide the following static method: - // static std::string getDescription(); - - virtual ReporterPreferences getPreferences() const = 0; - - virtual void noMatchingTestCases( std::string const& spec ) = 0; - - virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; - virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; - virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; - - virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; - - // The return value indicates if the messages buffer should be cleared: - virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; - - virtual void sectionEnded( SectionStats const& sectionStats ) = 0; - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; - virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; - - virtual void skipTest( TestCaseInfo const& testInfo ) = 0; - - virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; } - }; - - struct IReporterFactory : IShared { - virtual ~IReporterFactory(); - virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; - virtual std::string getDescription() const = 0; - }; - - struct IReporterRegistry { - typedef std::map > FactoryMap; - typedef std::vector > Listeners; - - virtual ~IReporterRegistry(); - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; - virtual FactoryMap const& getFactories() const = 0; - virtual Listeners const& getListeners() const = 0; - }; - - Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ); - -} - -#include -#include namespace Catch { - inline std::size_t listTests( Config const& config ) { + template + class ReporterRegistrar { - TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) - Catch::cout() << "Matching test cases:\n"; - else { - Catch::cout() << "All available test cases:\n"; - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + class ReporterFactory : public IReporterFactory { + + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); + } + + virtual std::string getDescription() const override { + return T::getDescription(); + } + }; + + public: + + explicit ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, std::make_shared() ); } - - std::size_t matchedTests = 0; - TextAttributes nameAttr, tagsAttr; - nameAttr.setInitialIndent( 2 ).setIndent( 4 ); - tagsAttr.setIndent( 6 ); - - std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Colour::Code colour = testCaseInfo.isHidden() - ? Colour::SecondaryText - : Colour::None; - Colour colourGuard( colour ); - - Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; - if( !testCaseInfo.tags.empty() ) - Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; - } - - if( !config.testSpec().hasFilters() ) - Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl; - else - Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; - return matchedTests; - } - - inline std::size_t listTestsNamesOnly( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( !config.testSpec().hasFilters() ) - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - std::size_t matchedTests = 0; - std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - matchedTests++; - TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - if( startsWith( testCaseInfo.name, "#" ) ) - Catch::cout() << "\"" << testCaseInfo.name << "\"" << std::endl; - else - Catch::cout() << testCaseInfo.name << std::endl; - } - return matchedTests; - } - - struct TagInfo { - TagInfo() : count ( 0 ) {} - void add( std::string const& spelling ) { - ++count; - spellings.insert( spelling ); - } - std::string all() const { - std::string out; - for( std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); - it != itEnd; - ++it ) - out += "[" + *it + "]"; - return out; - } - std::set spellings; - std::size_t count; }; - inline std::size_t listTags( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( config.testSpec().hasFilters() ) - Catch::cout() << "Tags for matching test cases:\n"; - else { - Catch::cout() << "All available tags:\n"; - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); - } + template + class ListenerRegistrar { - std::map tagCounts; + class ListenerFactory : public IReporterFactory { - std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); - it != itEnd; - ++it ) { - for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), - tagItEnd = it->getTestCaseInfo().tags.end(); - tagIt != tagItEnd; - ++tagIt ) { - std::string tagName = *tagIt; - std::string lcaseTagName = toLower( tagName ); - std::map::iterator countIt = tagCounts.find( lcaseTagName ); - if( countIt == tagCounts.end() ) - countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; - countIt->second.add( tagName ); + virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { + return std::unique_ptr( new T( config ) ); } + virtual std::string getDescription() const override { + return std::string(); + } + }; + + public: + + ListenerRegistrar() { + getMutableRegistryHub().registerListener( std::make_shared() ); } + }; +} - for( std::map::const_iterator countIt = tagCounts.begin(), - countItEnd = tagCounts.end(); - countIt != countItEnd; - ++countIt ) { - std::ostringstream oss; - oss << " " << std::setw(2) << countIt->second.count << " "; - Text wrapper( countIt->second.all(), TextAttributes() - .setInitialIndent( 0 ) - .setIndent( oss.str().size() ) - .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); - Catch::cout() << oss.str() << wrapper << "\n"; - } - Catch::cout() << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; - return tagCounts.size(); - } +#if !defined(CATCH_CONFIG_DISABLE) - inline std::size_t listReporters( Config const& /*config*/ ) { - Catch::cout() << "Available reporters:\n"; - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); - IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; - std::size_t maxNameLen = 0; - for(it = itBegin; it != itEnd; ++it ) - maxNameLen = (std::max)( maxNameLen, it->first.size() ); +#define CATCH_REGISTER_REPORTER( name, reporterType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } \ + CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS - for(it = itBegin; it != itEnd; ++it ) { - Text wrapper( it->second->getDescription(), TextAttributes() - .setInitialIndent( 0 ) - .setIndent( 7+maxNameLen ) - .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); - Catch::cout() << " " - << it->first - << ":" - << std::string( maxNameLen - it->first.size() + 2, ' ' ) - << wrapper << "\n"; - } - Catch::cout() << std::endl; - return factories.size(); - } +#define CATCH_REGISTER_LISTENER( listenerType ) \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#else // CATCH_CONFIG_DISABLE - inline Option list( Config const& config ) { - Option listedCount; - if( config.listTests() ) - listedCount = listedCount.valueOr(0) + listTests( config ); - if( config.listTestNamesOnly() ) - listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); - if( config.listTags() ) - listedCount = listedCount.valueOr(0) + listTags( config ); - if( config.listReporters() ) - listedCount = listedCount.valueOr(0) + listReporters( config ); - return listedCount; - } +#define CATCH_REGISTER_REPORTER(name, reporterType) +#define CATCH_REGISTER_LISTENER(listenerType) + +#endif // CATCH_CONFIG_DISABLE + +// end catch_reporter_registrars.hpp +// Allow users to base their work off existing reporters +// start catch_reporter_compact.h + +namespace Catch { + + struct CompactReporter : StreamingReporterBase { + + using StreamingReporterBase::StreamingReporterBase; + + ~CompactReporter() override; + + static std::string getDescription(); + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases(std::string const& spec) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& _assertionStats) override; + + void sectionEnded(SectionStats const& _sectionStats) override; + + void testRunEnded(TestRunStats const& _testRunStats) override; + + }; } // end namespace Catch -// #included from: internal/catch_run_context.hpp -#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED +// end catch_reporter_compact.h +// start catch_reporter_console.h -// #included from: catch_test_case_tracker.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif + +namespace Catch { + // Fwd decls + struct SummaryColumn; + class TablePrinter; + + struct ConsoleReporter : StreamingReporterBase { + std::unique_ptr m_tablePrinter; + + ConsoleReporter(ReporterConfig const& config); + ~ConsoleReporter() override; + static std::string getDescription(); + + void noMatchingTestCases(std::string const& spec) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& _assertionStats) override; + + void sectionStarting(SectionInfo const& _sectionInfo) override; + void sectionEnded(SectionStats const& _sectionStats) override; + + void benchmarkStarting(BenchmarkInfo const& info) override; + void benchmarkEnded(BenchmarkStats const& stats) override; + + void testCaseEnded(TestCaseStats const& _testCaseStats) override; + void testGroupEnded(TestGroupStats const& _testGroupStats) override; + void testRunEnded(TestRunStats const& _testRunStats) override; + + private: + + void lazyPrint(); + + void lazyPrintWithoutClosingBenchmarkTable(); + void lazyPrintRunInfo(); + void lazyPrintGroupInfo(); + void printTestCaseAndSectionHeader(); + + void printClosedHeader(std::string const& _name); + void printOpenHeader(std::string const& _name); + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString(std::string const& _string, std::size_t indent = 0); + + void printTotals(Totals const& totals); + void printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row); + + void printTotalsDivider(Totals const& totals); + void printSummaryDivider(); + + private: + bool m_headerPrinted = false; + }; + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +// end catch_reporter_console.h +// start catch_reporter_junit.h + +// start catch_xmlwriter.h -#include -#include -#include #include +namespace Catch { + + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; + + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); + + void encodeTo( std::ostream& os ) const; + + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); + + private: + std::string m_str; + ForWhat m_forWhat; + }; + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ); + + ScopedElement( ScopedElement&& other ) noexcept; + ScopedElement& operator=( ScopedElement&& other ) noexcept; + + ~ScopedElement(); + + ScopedElement& writeText( std::string const& text, bool indent = true ); + + template + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer = nullptr; + }; + + XmlWriter( std::ostream& os = Catch::cout() ); + ~XmlWriter(); + + XmlWriter( XmlWriter const& ) = delete; + XmlWriter& operator=( XmlWriter const& ) = delete; + + XmlWriter& startElement( std::string const& name ); + + ScopedElement scopedElement( std::string const& name ); + + XmlWriter& endElement(); + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); + + XmlWriter& writeAttribute( std::string const& name, bool attribute ); + + template + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + ReusableStringStream rss; + rss << attribute; + return writeAttribute( name, rss.str() ); + } + + XmlWriter& writeText( std::string const& text, bool indent = true ); + + XmlWriter& writeComment( std::string const& text ); + + void writeStylesheetRef( std::string const& url ); + + XmlWriter& writeBlankLine(); + + void ensureTagClosed(); + + private: + + void writeDeclaration(); + + void newlineIfNecessary(); + + bool m_tagIsOpen = false; + bool m_needsNewline = false; + std::vector m_tags; + std::string m_indent; + std::ostream& m_os; + }; + +} + +// end catch_xmlwriter.h +namespace Catch { + + class JunitReporter : public CumulativeReporterBase { + public: + JunitReporter(ReporterConfig const& _config); + + ~JunitReporter() override; + + static std::string getDescription(); + + void noMatchingTestCases(std::string const& /*spec*/) override; + + void testRunStarting(TestRunInfo const& runInfo) override; + + void testGroupStarting(GroupInfo const& groupInfo) override; + + void testCaseStarting(TestCaseInfo const& testCaseInfo) override; + bool assertionEnded(AssertionStats const& assertionStats) override; + + void testCaseEnded(TestCaseStats const& testCaseStats) override; + + void testGroupEnded(TestGroupStats const& testGroupStats) override; + + void testRunEndedCumulative() override; + + void writeGroup(TestGroupNode const& groupNode, double suiteTime); + + void writeTestCase(TestCaseNode const& testCaseNode); + + void writeSection(std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode); + + void writeAssertions(SectionNode const& sectionNode); + void writeAssertion(AssertionStats const& stats); + + XmlWriter xml; + Timer suiteTimer; + std::string stdOutForSuite; + std::string stdErrForSuite; + unsigned int unexpectedExceptions = 0; + bool m_okToFail = false; + }; + +} // end namespace Catch + +// end catch_reporter_junit.h +// start catch_reporter_xml.h + +namespace Catch { + class XmlReporter : public StreamingReporterBase { + public: + XmlReporter(ReporterConfig const& _config); + + ~XmlReporter() override; + + static std::string getDescription(); + + virtual std::string getStylesheetRef() const; + + void writeSourceInfo(SourceLineInfo const& sourceInfo); + + public: // StreamingReporterBase + + void noMatchingTestCases(std::string const& s) override; + + void testRunStarting(TestRunInfo const& testInfo) override; + + void testGroupStarting(GroupInfo const& groupInfo) override; + + void testCaseStarting(TestCaseInfo const& testInfo) override; + + void sectionStarting(SectionInfo const& sectionInfo) override; + + void assertionStarting(AssertionInfo const&) override; + + bool assertionEnded(AssertionStats const& assertionStats) override; + + void sectionEnded(SectionStats const& sectionStats) override; + + void testCaseEnded(TestCaseStats const& testCaseStats) override; + + void testGroupEnded(TestGroupStats const& testGroupStats) override; + + void testRunEnded(TestRunStats const& testRunStats) override; + + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth = 0; + }; + +} // end namespace Catch + +// end catch_reporter_xml.h + +// end catch_external_interfaces.h +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +#ifdef CATCH_IMPL +// start catch_impl.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// Keep these here for external reporters +// start catch_test_case_tracker.h + +#include +#include +#include + namespace Catch { namespace TestCaseTracking { - struct ITracker : SharedImpl<> { + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); + }; + + struct ITracker; + + using ITrackerPtr = std::shared_ptr; + + struct ITracker { virtual ~ITracker(); // static queries - virtual std::string name() const = 0; + virtual NameAndLocation const& nameAndLocation() const = 0; // dynamic queries virtual bool isComplete() const = 0; // Successfully completed or failed @@ -5513,8 +4333,8 @@ namespace TestCaseTracking { virtual void fail() = 0; virtual void markAsNeedingAnotherRun() = 0; - virtual void addChild( Ptr const& child ) = 0; - virtual ITracker* findChild( std::string const& name ) = 0; + virtual void addChild( ITrackerPtr const& child ) = 0; + virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0; virtual void openChild() = 0; // Debug/ checking @@ -5530,47 +4350,23 @@ namespace TestCaseTracking { CompletedCycle }; - Ptr m_rootTracker; - ITracker* m_currentTracker; - RunState m_runState; + ITrackerPtr m_rootTracker; + ITracker* m_currentTracker = nullptr; + RunState m_runState = NotStarted; public: - static TrackerContext& instance() { - static TrackerContext s_instance; - return s_instance; - } - - TrackerContext() - : m_currentTracker( CATCH_NULL ), - m_runState( NotStarted ) - {} + static TrackerContext& instance(); ITracker& startRun(); + void endRun(); - void endRun() { - m_rootTracker.reset(); - m_currentTracker = CATCH_NULL; - m_runState = NotStarted; - } + void startCycle(); + void completeCycle(); - void startCycle() { - m_currentTracker = m_rootTracker.get(); - m_runState = Executing; - } - void completeCycle() { - m_runState = CompletedCycle; - } - - bool completedCycle() const { - return m_runState == CompletedCycle; - } - ITracker& currentTracker() { - return *m_currentTracker; - } - void setCurrentTracker( ITracker* tracker ) { - m_currentTracker = tracker; - } + bool completedCycle() const; + ITracker& currentTracker(); + void setCurrentTracker( ITracker* tracker ); }; class TrackerBase : public ITracker { @@ -5583,214 +4379,82 @@ namespace TestCaseTracking { CompletedSuccessfully, Failed }; + class TrackerHasName { - std::string m_name; + NameAndLocation m_nameAndLocation; public: - TrackerHasName( std::string const& name ) : m_name( name ) {} - bool operator ()( Ptr const& tracker ) { - return tracker->name() == m_name; - } + TrackerHasName( NameAndLocation const& nameAndLocation ); + bool operator ()( ITrackerPtr const& tracker ) const; }; - typedef std::vector > Children; - std::string m_name; + + using Children = std::vector; + NameAndLocation m_nameAndLocation; TrackerContext& m_ctx; ITracker* m_parent; Children m_children; - CycleState m_runState; + CycleState m_runState = NotStarted; + public: - TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent ) - : m_name( name ), - m_ctx( ctx ), - m_parent( parent ), - m_runState( NotStarted ) - {} - virtual ~TrackerBase(); + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); - virtual std::string name() const CATCH_OVERRIDE { - return m_name; - } - virtual bool isComplete() const CATCH_OVERRIDE { - return m_runState == CompletedSuccessfully || m_runState == Failed; - } - virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { - return m_runState == CompletedSuccessfully; - } - virtual bool isOpen() const CATCH_OVERRIDE { - return m_runState != NotStarted && !isComplete(); - } - virtual bool hasChildren() const CATCH_OVERRIDE { - return !m_children.empty(); - } + NameAndLocation const& nameAndLocation() const override; + bool isComplete() const override; + bool isSuccessfullyCompleted() const override; + bool isOpen() const override; + bool hasChildren() const override; - virtual void addChild( Ptr const& child ) CATCH_OVERRIDE { - m_children.push_back( child ); - } + void addChild( ITrackerPtr const& child ) override; - virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE { - Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) ); - return( it != m_children.end() ) - ? it->get() - : CATCH_NULL; - } - virtual ITracker& parent() CATCH_OVERRIDE { - assert( m_parent ); // Should always be non-null except for root - return *m_parent; - } + ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override; + ITracker& parent() override; - virtual void openChild() CATCH_OVERRIDE { - if( m_runState != ExecutingChildren ) { - m_runState = ExecutingChildren; - if( m_parent ) - m_parent->openChild(); - } - } + void openChild() override; - virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; } - virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; } + bool isSectionTracker() const override; + bool isIndexTracker() const override; - void open() { - m_runState = Executing; - moveToThis(); - if( m_parent ) - m_parent->openChild(); - } + void open(); - virtual void close() CATCH_OVERRIDE { + void close() override; + void fail() override; + void markAsNeedingAnotherRun() override; - // Close any still open children (e.g. generators) - while( &m_ctx.currentTracker() != this ) - m_ctx.currentTracker().close(); - - switch( m_runState ) { - case NotStarted: - case CompletedSuccessfully: - case Failed: - throw std::logic_error( "Illogical state" ); - - case NeedsAnotherRun: - break;; - - case Executing: - m_runState = CompletedSuccessfully; - break; - case ExecutingChildren: - if( m_children.empty() || m_children.back()->isComplete() ) - m_runState = CompletedSuccessfully; - break; - - default: - throw std::logic_error( "Unexpected state" ); - } - moveToParent(); - m_ctx.completeCycle(); - } - virtual void fail() CATCH_OVERRIDE { - m_runState = Failed; - if( m_parent ) - m_parent->markAsNeedingAnotherRun(); - moveToParent(); - m_ctx.completeCycle(); - } - virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { - m_runState = NeedsAnotherRun; - } private: - void moveToParent() { - assert( m_parent ); - m_ctx.setCurrentTracker( m_parent ); - } - void moveToThis() { - m_ctx.setCurrentTracker( this ); - } + void moveToParent(); + void moveToThis(); }; class SectionTracker : public TrackerBase { + std::vector m_filters; public: - SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( name, ctx, parent ) - {} - virtual ~SectionTracker(); + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); - virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } + bool isSectionTracker() const override; - static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { - SectionTracker* section = CATCH_NULL; + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); - ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { - assert( childTracker ); - assert( childTracker->isSectionTracker() ); - section = static_cast( childTracker ); - } - else { - section = new SectionTracker( name, ctx, ¤tTracker ); - currentTracker.addChild( section ); - } - if( !ctx.completedCycle() && !section->isComplete() ) { + void tryOpen(); - section->open(); - } - return *section; - } + void addInitialFilters( std::vector const& filters ); + void addNextFilters( std::vector const& filters ); }; class IndexTracker : public TrackerBase { int m_size; - int m_index; + int m_index = -1; public: - IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size ) - : TrackerBase( name, ctx, parent ), - m_size( size ), - m_index( -1 ) - {} - virtual ~IndexTracker(); + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ); - virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } + bool isIndexTracker() const override; + void close() override; - static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { - IndexTracker* tracker = CATCH_NULL; + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ); - ITracker& currentTracker = ctx.currentTracker(); - if( ITracker* childTracker = currentTracker.findChild( name ) ) { - assert( childTracker ); - assert( childTracker->isIndexTracker() ); - tracker = static_cast( childTracker ); - } - else { - tracker = new IndexTracker( name, ctx, ¤tTracker, size ); - currentTracker.addChild( tracker ); - } + int index() const; - if( !ctx.completedCycle() && !tracker->isComplete() ) { - if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) - tracker->moveNext(); - tracker->open(); - } - - return *tracker; - } - - int index() const { return m_index; } - - void moveNext() { - m_index++; - m_children.clear(); - } - - virtual void close() CATCH_OVERRIDE { - TrackerBase::close(); - if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) - m_runState = Executing; - } + void moveNext(); }; - inline ITracker& TrackerContext::startRun() { - m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL ); - m_currentTracker = CATCH_NULL; - m_runState = Executing; - return *m_rootTracker; - } - } // namespace TestCaseTracking using TestCaseTracking::ITracker; @@ -5800,1234 +4464,2179 @@ using TestCaseTracking::IndexTracker; } // namespace Catch -// #included from: catch_fatal_condition.hpp -#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED +// end catch_test_case_tracker.h + +// start catch_leak_detector.h namespace Catch { - // Report the error condition then exit the process - inline void fatal( std::string const& message, int exitCode ) { - IContext& context = Catch::getCurrentContext(); - IResultCapture* resultCapture = context.getResultCapture(); - resultCapture->handleFatalErrorCondition( message ); + struct LeakDetector { + LeakDetector(); + }; - if( Catch::alwaysTrue() ) // avoids "no return" warnings - exit( exitCode ); +} +// end catch_leak_detector.h +// Cpp files will be included in the single-header file here +// start catch_approx.cpp + +#include +#include + +namespace { + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +} + +namespace Catch { +namespace Detail { + + Approx::Approx ( double value ) + : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_margin( 0.0 ), + m_scale( 0.0 ), + m_value( value ) + {} + + Approx Approx::custom() { + return Approx( 0 ); } -} // namespace Catch + std::string Approx::toString() const { + ReusableStringStream rss; + rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; + return rss.str(); + } + + bool Approx::equalityComparisonImpl(const double other) const { + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value))); + } + +} // end namespace Detail + +std::string StringMaker::convert(Catch::Detail::Approx const& value) { + return value.toString(); +} + +} // end namespace Catch +// end catch_approx.cpp +// start catch_assertionhandler.cpp + +// start catch_context.h + +#include + +namespace Catch { + + struct IResultCapture; + struct IRunner; + struct IConfig; + struct IMutableContext; + + using IConfigPtr = std::shared_ptr; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual IConfigPtr const& getConfig() const = 0; + }; + + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( IConfigPtr const& config ) = 0; + + private: + static IMutableContext *currentContext; + friend IMutableContext& getCurrentMutableContext(); + friend void cleanUpContext(); + static void createContext(); + }; + + inline IMutableContext& getCurrentMutableContext() + { + if( !IMutableContext::currentContext ) + IMutableContext::createContext(); + return *IMutableContext::currentContext; + } + + inline IContext& getCurrentContext() + { + return getCurrentMutableContext(); + } + + void cleanUpContext(); +} + +// end catch_context.h +// start catch_debugger.h + +namespace Catch { + bool isDebuggerActive(); +} + +#ifdef CATCH_PLATFORM_MAC + + #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ + +#elif defined(CATCH_PLATFORM_LINUX) + // If we can use inline assembler, do it because this allows us to break + // directly at the location of the failing check instead of breaking inside + // raise() called from it, i.e. one stack frame below. + #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) + #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ + #else // Fall back to the generic way. + #include + + #define CATCH_TRAP() raise(SIGTRAP) + #endif +#elif defined(_MSC_VER) + #define CATCH_TRAP() __debugbreak() +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) void __stdcall DebugBreak(); + #define CATCH_TRAP() DebugBreak() +#endif + +#ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } +#else + #define CATCH_BREAK_INTO_DEBUGGER() (void)0, 0 +#endif + +// end catch_debugger.h +// start catch_run_context.h + +// start catch_fatal_condition.h + +#include #if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// +// start catch_windows_h_proxy.h + + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINED_NOMINMAX +# define NOMINMAX +#endif +#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINED_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + +#endif // defined(CATCH_PLATFORM_WINDOWS) + +// end catch_windows_h_proxy.h + +# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + struct FatalConditionHandler { + void reset(); + }; +} + +# else // CATCH_CONFIG_WINDOWS_SEH is defined namespace Catch { struct FatalConditionHandler { - void reset() {} - }; + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); + FatalConditionHandler(); + static void reset(); + ~FatalConditionHandler(); + + private: + static bool isSet; + static ULONG guaranteeSize; + static PVOID exceptionHandlerHandle; + }; } // namespace Catch +# endif // CATCH_CONFIG_WINDOWS_SEH + #else // Not Windows - assumed to be POSIX compatible ////////////////////////// +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace Catch { + struct FatalConditionHandler { + void reset(); + }; +} + +# else // CATCH_CONFIG_POSIX_SIGNALS is defined + #include namespace Catch { - struct SignalDefs { int id; const char* name; }; - extern SignalDefs signalDefs[]; - SignalDefs signalDefs[] = { - { SIGINT, "SIGINT - Terminal interrupt signal" }, - { SIGILL, "SIGILL - Illegal instruction signal" }, - { SIGFPE, "SIGFPE - Floating point error signal" }, - { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, - { SIGTERM, "SIGTERM - Termination request signal" }, - { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } - }; - struct FatalConditionHandler { - static void handleSignal( int sig ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - if( sig == signalDefs[i].id ) - fatal( signalDefs[i].name, -sig ); - fatal( "", -sig ); - } + static bool isSet; + static struct sigaction oldSigActions[];// [sizeof(signalDefs) / sizeof(SignalDefs)]; + static stack_t oldSigStack; + static char altStackMem[]; - FatalConditionHandler() : m_isSet( true ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - signal( signalDefs[i].id, handleSignal ); - } - ~FatalConditionHandler() { - reset(); - } - void reset() { - if( m_isSet ) { - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) - signal( signalDefs[i].id, SIG_DFL ); - m_isSet = false; - } - } + static void handleSignal( int sig ); - bool m_isSet; + FatalConditionHandler(); + ~FatalConditionHandler(); + static void reset(); }; } // namespace Catch +# endif // CATCH_CONFIG_POSIX_SIGNALS + #endif // not Windows -#include +// end catch_fatal_condition.h #include namespace Catch { - class StreamRedirect { - - public: - StreamRedirect( std::ostream& stream, std::string& targetString ) - : m_stream( stream ), - m_prevBuf( stream.rdbuf() ), - m_targetString( targetString ) - { - stream.rdbuf( m_oss.rdbuf() ); - } - - ~StreamRedirect() { - m_targetString += m_oss.str(); - m_stream.rdbuf( m_prevBuf ); - } - - private: - std::ostream& m_stream; - std::streambuf* m_prevBuf; - std::ostringstream m_oss; - std::string& m_targetString; - }; + struct IMutableContext; /////////////////////////////////////////////////////////////////////////// class RunContext : public IResultCapture, public IRunner { - RunContext( RunContext const& ); - void operator =( RunContext const& ); - public: + RunContext( RunContext const& ) = delete; + RunContext& operator =( RunContext const& ) = delete; - explicit RunContext( Ptr const& _config, Ptr const& reporter ) - : m_runInfo( _config->name() ), - m_context( getCurrentMutableContext() ), - m_activeTestCase( CATCH_NULL ), - m_config( _config ), - m_reporter( reporter ) - { - m_context.setRunner( this ); - m_context.setConfig( m_config ); - m_context.setResultCapture( this ); - m_reporter->testRunStarting( m_runInfo ); - } + explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter ); - virtual ~RunContext() { - m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); - } + ~RunContext() override; - void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { - m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); - } - void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { - m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); - } + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ); + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ); - Totals runTest( TestCase const& testCase ) { - Totals prevTotals = m_totals; + Totals runTest(TestCase const& testCase); - std::string redirectedCout; - std::string redirectedCerr; + IConfigPtr config() const; + IStreamingReporter& reporter() const; - TestCaseInfo testInfo = testCase.getTestCaseInfo(); + public: // IResultCapture - m_reporter->testCaseStarting( testInfo ); + // Assertion handlers + void handleExpr + ( AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction ) override; + void handleMessage + ( AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction ) override; + void handleUnexpectedExceptionNotThrown + ( AssertionInfo const& info, + AssertionReaction& reaction ) override; + void handleUnexpectedInflightException + ( AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction ) override; + void handleIncomplete + ( AssertionInfo const& info ) override; + void handleNonExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction ) override; - m_activeTestCase = &testCase; + bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; - do { - m_trackerContext.startRun(); - do { - m_trackerContext.startCycle(); - m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name ); - runCurrentTest( redirectedCout, redirectedCerr ); - } - while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); - } - // !TBD: deprecated - this will be replaced by indexed trackers - while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); + void sectionEnded( SectionEndInfo const& endInfo ) override; + void sectionEndedEarly( SectionEndInfo const& endInfo ) override; - Totals deltaTotals = m_totals.delta( prevTotals ); - if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) { - deltaTotals.assertions.failed++; - deltaTotals.testCases.passed--; - deltaTotals.testCases.failed++; - } - m_totals.testCases += deltaTotals.testCases; - m_reporter->testCaseEnded( TestCaseStats( testInfo, - deltaTotals, - redirectedCout, - redirectedCerr, - aborting() ) ); + void benchmarkStarting( BenchmarkInfo const& info ) override; + void benchmarkEnded( BenchmarkStats const& stats ) override; - m_activeTestCase = CATCH_NULL; - m_testCaseTracker = CATCH_NULL; + void pushScopedMessage( MessageInfo const& message ) override; + void popScopedMessage( MessageInfo const& message ) override; - return deltaTotals; - } + std::string getCurrentTestName() const override; - Ptr config() const { - return m_config; - } + const AssertionResult* getLastResult() const override; - private: // IResultCapture + void exceptionEarlyReported() override; - virtual void assertionEnded( AssertionResult const& result ) { - if( result.getResultType() == ResultWas::Ok ) { - m_totals.assertions.passed++; - } - else if( !result.isOk() ) { - m_totals.assertions.failed++; - } + void handleFatalErrorCondition( StringRef message ) override; - if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) - m_messages.clear(); + bool lastAssertionPassed() override; - // Reset working state - m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); - m_lastResult = result; - } - - virtual bool sectionStarted ( - SectionInfo const& sectionInfo, - Counts& assertions - ) - { - std::ostringstream oss; - oss << sectionInfo.name << "@" << sectionInfo.lineInfo; - - ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() ); - if( !sectionTracker.isOpen() ) - return false; - m_activeSections.push_back( §ionTracker ); - - m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; - - m_reporter->sectionStarting( sectionInfo ); - - assertions = m_totals.assertions; - - return true; - } - bool testForMissingAssertions( Counts& assertions ) { - if( assertions.total() != 0 ) - return false; - if( !m_config->warnAboutMissingAssertions() ) - return false; - if( m_trackerContext.currentTracker().hasChildren() ) - return false; - m_totals.assertions.failed++; - assertions.failed++; - return true; - } - - virtual void sectionEnded( SectionEndInfo const& endInfo ) { - Counts assertions = m_totals.assertions - endInfo.prevAssertions; - bool missingAssertions = testForMissingAssertions( assertions ); - - if( !m_activeSections.empty() ) { - m_activeSections.back()->close(); - m_activeSections.pop_back(); - } - - m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) ); - m_messages.clear(); - } - - virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) { - if( m_unfinishedSections.empty() ) - m_activeSections.back()->fail(); - else - m_activeSections.back()->close(); - m_activeSections.pop_back(); - - m_unfinishedSections.push_back( endInfo ); - } - - virtual void pushScopedMessage( MessageInfo const& message ) { - m_messages.push_back( message ); - } - - virtual void popScopedMessage( MessageInfo const& message ) { - m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); - } - - virtual std::string getCurrentTestName() const { - return m_activeTestCase - ? m_activeTestCase->getTestCaseInfo().name - : ""; - } - - virtual const AssertionResult* getLastResult() const { - return &m_lastResult; - } - - virtual void handleFatalErrorCondition( std::string const& message ) { - ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); - resultBuilder.setResultType( ResultWas::FatalErrorCondition ); - resultBuilder << message; - resultBuilder.captureExpression(); - - handleUnfinishedSections(); - - // Recreate section for test case (as we will lose the one that was in scope) - TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); - - Counts assertions; - assertions.failed = 1; - SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false ); - m_reporter->sectionEnded( testCaseSectionStats ); - - TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); - - Totals deltaTotals; - deltaTotals.testCases.failed = 1; - m_reporter->testCaseEnded( TestCaseStats( testInfo, - deltaTotals, - "", - "", - false ) ); - m_totals.testCases.failed++; - testGroupEnded( "", m_totals, 1, 1 ); - m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); - } + void assertionPassed() override; public: // !TBD We need to do this another way! - bool aborting() const { - return m_totals.assertions.failed == static_cast( m_config->abortAfter() ); - } + bool aborting() const override; private: - void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { - TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); - m_reporter->sectionStarting( testCaseSection ); - Counts prevAssertions = m_totals.assertions; - double duration = 0; - try { - m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); + void invokeActiveTestCase(); - seedRng( *m_config ); + void resetAssertionInfo(); + bool testForMissingAssertions( Counts& assertions ); - Timer timer; - timer.start(); - if( m_reporter->getPreferences().shouldRedirectStdOut ) { - StreamRedirect coutRedir( Catch::cout(), redirectedCout ); - StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); - invokeActiveTestCase(); - } - else { - invokeActiveTestCase(); - } - duration = timer.getElapsedSeconds(); - } - catch( TestFailureException& ) { - // This just means the test was aborted due to failure - } - catch(...) { - makeUnexpectedResultBuilder().useActiveException(); - } - m_testCaseTracker->close(); - handleUnfinishedSections(); - m_messages.clear(); + void assertionEnded( AssertionResult const& result ); + void reportExpr + ( AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ); - Counts assertions = m_totals.assertions - prevAssertions; - bool missingAssertions = testForMissingAssertions( assertions ); - - if( testCaseInfo.okToFail() ) { - std::swap( assertions.failedButOk, assertions.failed ); - m_totals.assertions.failed -= assertions.failedButOk; - m_totals.assertions.failedButOk += assertions.failedButOk; - } - - SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); - m_reporter->sectionEnded( testCaseSectionStats ); - } - - void invokeActiveTestCase() { - FatalConditionHandler fatalConditionHandler; // Handle signals - m_activeTestCase->invoke(); - fatalConditionHandler.reset(); - } + void populateReaction( AssertionReaction& reaction ); private: - ResultBuilder makeUnexpectedResultBuilder() const { - return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), - m_lastAssertionInfo.lineInfo, - m_lastAssertionInfo.capturedExpression.c_str(), - m_lastAssertionInfo.resultDisposition ); - } - - void handleUnfinishedSections() { - // If sections ended prematurely due to an exception we stored their - // infos here so we can tear them down outside the unwind process. - for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), - itEnd = m_unfinishedSections.rend(); - it != itEnd; - ++it ) - sectionEnded( *it ); - m_unfinishedSections.clear(); - } + void handleUnfinishedSections(); TestRunInfo m_runInfo; IMutableContext& m_context; - TestCase const* m_activeTestCase; + TestCase const* m_activeTestCase = nullptr; ITracker* m_testCaseTracker; - ITracker* m_currentSectionTracker; - AssertionResult m_lastResult; + Option m_lastResult; - Ptr m_config; + IConfigPtr m_config; Totals m_totals; - Ptr m_reporter; + IStreamingReporterPtr m_reporter; std::vector m_messages; AssertionInfo m_lastAssertionInfo; std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; + bool m_lastAssertionPassed = false; + bool m_shouldReportUnexpected = true; + bool m_includeSuccessfulResults; }; - IResultCapture& getResultCapture() { - if( IResultCapture* capture = getCurrentContext().getResultCapture() ) - return *capture; - else - throw std::logic_error( "No result capture instance" ); - } - } // end namespace Catch -// #included from: internal/catch_version.h -#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED +// end catch_run_context.h +namespace Catch { + + auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { + expr.streamReconstructedExpression( os ); + return os; + } + + LazyExpression::LazyExpression( bool isNegated ) + : m_isNegated( isNegated ) + {} + + LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {} + + LazyExpression::operator bool() const { + return m_transientExpression != nullptr; + } + + auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& { + if( lazyExpr.m_isNegated ) + os << "!"; + + if( lazyExpr ) { + if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() ) + os << "(" << *lazyExpr.m_transientExpression << ")"; + else + os << *lazyExpr.m_transientExpression; + } + else { + os << "{** error - unchecked empty expression requested **}"; + } + return os; + } + + AssertionHandler::AssertionHandler + ( StringRef macroName, + SourceLineInfo const& lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition ) + : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, + m_resultCapture( getResultCapture() ) + {} + + void AssertionHandler::handleExpr( ITransientExpression const& expr ) { + m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); + } + void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) { + m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction ); + } + + auto AssertionHandler::allowThrows() const -> bool { + return getCurrentContext().getConfig()->allowThrows(); + } + + void AssertionHandler::complete() { + setCompleted(); + if( m_reaction.shouldDebugBreak ) { + + // If you find your debugger stopping you here then go one level up on the + // call-stack for the code that caused it (typically a failed assertion) + + // (To go back to the test and change execution, jump over the throw, next) + CATCH_BREAK_INTO_DEBUGGER(); + } + if( m_reaction.shouldThrow ) + throw Catch::TestFailureException(); + } + void AssertionHandler::setCompleted() { + m_completed = true; + } + + void AssertionHandler::handleUnexpectedInflightException() { + m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); + } + + void AssertionHandler::handleExceptionThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + void AssertionHandler::handleExceptionNotThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + void AssertionHandler::handleUnexpectedExceptionNotThrown() { + m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction ); + } + + void AssertionHandler::handleThrowingCallSkipped() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + // This is the overload that takes a string and infers the Equals matcher from it + // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ) { + handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); + } + +} // namespace Catch +// end catch_assertionhandler.cpp +// start catch_assertionresult.cpp + +namespace Catch { + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): + lazyExpression(_lazyExpression), + resultType(_resultType) {} + + std::string AssertionResultData::reconstructExpression() const { + + if( reconstructedExpression.empty() ) { + if( lazyExpression ) { + ReusableStringStream rss; + rss << lazyExpression; + reconstructedExpression = rss.str(); + } + } + return reconstructedExpression; + } + + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} + + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } + + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } + + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } + + bool AssertionResult::hasExpression() const { + return m_info.capturedExpression[0] != 0; + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + if( isFalseTest( m_info.resultDisposition ) ) + return "!(" + m_info.capturedExpression + ")"; + else + return m_info.capturedExpression; + } + + std::string AssertionResult::getExpressionInMacro() const { + std::string expr; + if( m_info.macroName[0] == 0 ) + expr = m_info.capturedExpression; + else { + expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); + expr += m_info.macroName.c_str(); + expr += "( "; + expr += m_info.capturedExpression.c_str(); + expr += " )"; + } + return expr; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + std::string expr = m_resultData.reconstructExpression(); + return expr.empty() + ? getExpression() + : expr; + } + + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + StringRef AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + +} // end namespace Catch +// end catch_assertionresult.cpp +// start catch_benchmark.cpp namespace Catch { - // Versioning information - struct Version { - Version( unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - std::string const& _branchName, - unsigned int _buildNumber ); + auto BenchmarkLooper::getResolution() -> uint64_t { + return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple(); + } - unsigned int const majorVersion; - unsigned int const minorVersion; - unsigned int const patchNumber; + void BenchmarkLooper::reportStart() { + getResultCapture().benchmarkStarting( { m_name } ); + } + auto BenchmarkLooper::needsMoreIterations() -> bool { + auto elapsed = m_timer.getElapsedNanoseconds(); - // buildNumber is only used if branchName is not null - std::string const branchName; - unsigned int const buildNumber; + // Exponentially increasing iterations until we're confident in our timer resolution + if( elapsed < m_resolution ) { + m_iterationsToRun *= 10; + return true; + } - friend std::ostream& operator << ( std::ostream& os, Version const& version ); + getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } ); + return false; + } - private: - void operator=( Version const& ); +} // end namespace Catch +// end catch_benchmark.cpp +// start catch_capture_matchers.cpp + +namespace Catch { + + using StringMatcher = Matchers::Impl::MatcherBase; + + // This is the general overload that takes a any string matcher + // There is another overload, in catch_assertinhandler.h/.cpp, that only takes a string and infers + // the Equals matcher (so the header does not mention matchers) + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr expr( exceptionMessage, matcher, matcherString ); + handler.handleExpr( expr ); + } + +} // namespace Catch +// end catch_capture_matchers.cpp +// start catch_commandline.cpp + +// start catch_commandline.h + +// start catch_clara.h + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#endif +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1 + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wshadow" +#endif + +// start clara.hpp +// v1.0-develop.2 +// See https://github.com/philsquared/Clara + + +#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 +#endif + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// This work is licensed under the BSD 2-Clause license. +// See the accompanying LICENSE file, or the one at https://opensource.org/licenses/BSD-2-Clause +// +// This project is hosted at https://github.com/philsquared/textflowcpp + + +#include +#include +#include +#include + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { namespace clara { namespace TextFlow { + + inline auto isWhitespace( char c ) -> bool { + static std::string chars = " \t\n\r"; + return chars.find( c ) != std::string::npos; + } + inline auto isBreakableBefore( char c ) -> bool { + static std::string chars = "[({<|"; + return chars.find( c ) != std::string::npos; + } + inline auto isBreakableAfter( char c ) -> bool { + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find( c ) != std::string::npos; + } + + class Columns; + + class Column { + std::vector m_strings; + size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + + public: + class iterator { + friend Column; + + Column const& m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator( Column const& column, size_t stringIndex ) + : m_column( column ), + m_stringIndex( stringIndex ) + {} + + auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + + auto isBoundary( size_t at ) const -> bool { + assert( at > 0 ); + assert( at <= line().size() ); + + return at == line().size() || + ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) || + isBreakableBefore( line()[at] ) || + isBreakableAfter( line()[at-1] ); + } + + void calcLength() { + assert( m_stringIndex < m_column.m_strings.size() ); + + m_suffix = false; + auto width = m_column.m_width-indent(); + m_end = m_pos; + while( m_end < line().size() && line()[m_end] != '\n' ) + ++m_end; + + if( m_end < m_pos + width ) { + m_len = m_end - m_pos; + } + else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && isWhitespace( line()[m_pos + len - 1] )) + --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + auto addIndentAndSuffix(std::string const &plain) const -> std::string { + return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain); + } + + public: + explicit iterator( Column const& column ) : m_column( column ) { + assert( m_column.m_width > m_column.m_indent ); + assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); + calcLength(); + if( m_len == 0 ) + m_stringIndex++; // Empty string + } + + auto operator *() const -> std::string { + assert( m_stringIndex < m_column.m_strings.size() ); + assert( m_pos <= m_end ); + if( m_pos + m_column.m_width < m_end ) + return addIndentAndSuffix(line().substr(m_pos, m_len)); + else + return addIndentAndSuffix(line().substr(m_pos, m_end - m_pos)); + } + + auto operator ++() -> iterator& { + m_pos += m_len; + if( m_pos < line().size() && line()[m_pos] == '\n' ) + m_pos += 1; + else + while( m_pos < line().size() && isWhitespace( line()[m_pos] ) ) + ++m_pos; + + if( m_pos == line().size() ) { + m_pos = 0; + ++m_stringIndex; + } + if( m_stringIndex < m_column.m_strings.size() ) + calcLength(); + return *this; + } + auto operator ++(int) -> iterator { + iterator prev( *this ); + operator++(); + return prev; + } + + auto operator ==( iterator const& other ) const -> bool { + return + m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + auto operator !=( iterator const& other ) const -> bool { + return !operator==( other ); + } + }; + using const_iterator = iterator; + + explicit Column( std::string const& text ) { m_strings.push_back( text ); } + + auto width( size_t newWidth ) -> Column& { + assert( newWidth > 0 ); + m_width = newWidth; + return *this; + } + auto indent( size_t newIndent ) -> Column& { + m_indent = newIndent; + return *this; + } + auto initialIndent( size_t newIndent ) -> Column& { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + auto begin() const -> iterator { return iterator( *this ); } + auto end() const -> iterator { return { *this, m_strings.size() }; } + + inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) { + bool first = true; + for( auto line : col ) { + if( first ) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto operator + ( Column const& other ) -> Columns; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } }; - extern Version libraryVersion; -} + class Spacer : public Column { -#include -#include -#include - -namespace Catch { - - Ptr createReporter( std::string const& reporterName, Ptr const& config ) { - Ptr reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() ); - if( !reporter ) { - std::ostringstream oss; - oss << "No reporter registered with name: '" << reporterName << "'"; - throw std::domain_error( oss.str() ); + public: + explicit Spacer( size_t spaceWidth ) : Column( "" ) { + width( spaceWidth ); } - return reporter; - } + }; - Ptr makeReporter( Ptr const& config ) { - std::vector reporters = config->getReporterNames(); - if( reporters.empty() ) - reporters.push_back( "console" ); - - Ptr reporter; - for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); - it != itEnd; - ++it ) - reporter = addReporter( reporter, createReporter( *it, config ) ); - return reporter; - } - Ptr addListeners( Ptr const& config, Ptr reporters ) { - IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); - for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); - it != itEnd; - ++it ) - reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) ); - return reporters; - } - - Totals runTests( Ptr const& config ) { - - Ptr iconfig = config.get(); - - Ptr reporter = makeReporter( config ); - reporter = addListeners( iconfig, reporter ); - - RunContext context( iconfig, reporter ); - - Totals totals; - - context.testGroupStarting( config->name(), 1, 1 ); - - TestSpec testSpec = config->testSpec(); - if( !testSpec.hasFilters() ) - testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests - - std::vector const& allTestCases = getAllTestCasesSorted( *iconfig ); - for( std::vector::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); - it != itEnd; - ++it ) { - if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) ) - totals += context.runTest( *it ); - else - reporter->skipTest( *it ); - } - - context.testGroupEnded( iconfig->name(), totals, 1, 1 ); - return totals; - } - - void applyFilenamesAsTags( IConfig const& config ) { - std::vector const& tests = getAllTestCasesSorted( config ); - for(std::size_t i = 0; i < tests.size(); ++i ) { - TestCase& test = const_cast( tests[i] ); - std::set tags = test.tags; - - std::string filename = test.lineInfo.file; - std::string::size_type lastSlash = filename.find_last_of( "\\/" ); - if( lastSlash != std::string::npos ) - filename = filename.substr( lastSlash+1 ); - - std::string::size_type lastDot = filename.find_last_of( "." ); - if( lastDot != std::string::npos ) - filename = filename.substr( 0, lastDot ); - - tags.insert( "#" + filename ); - setTags( test, tags ); - } - } - - class Session : NonCopyable { - static bool alreadyInstantiated; + class Columns { + std::vector m_columns; public: - struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; + class iterator { + friend Columns; + struct EndTag {}; - Session() - : m_cli( makeCommandLineParser() ) { - if( alreadyInstantiated ) { - std::string msg = "Only one instance of Catch::Session can ever be used"; - Catch::cerr() << msg << std::endl; - throw std::logic_error( msg ); - } - alreadyInstantiated = true; - } - ~Session() { - Catch::cleanUp(); - } + std::vector const& m_columns; + std::vector m_iterators; + size_t m_activeIterators; - void showHelp( std::string const& processName ) { - Catch::cout() << "\nCatch v" << libraryVersion << "\n"; - - m_cli.usage( Catch::cout(), processName ); - Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; - } - - int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { - try { - m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); - m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData ); - if( m_configData.showHelp ) - showHelp( m_configData.processName ); - m_config.reset(); - } - catch( std::exception& ex ) { - { - Colour colourGuard( Colour::Red ); - Catch::cerr() - << "\nError(s) in input:\n" - << Text( ex.what(), TextAttributes().setIndent(2) ) - << "\n\n"; - } - m_cli.usage( Catch::cout(), m_configData.processName ); - return (std::numeric_limits::max)(); - } - return 0; - } - - void useConfigData( ConfigData const& _configData ) { - m_configData = _configData; - m_config.reset(); - } - - int run( int argc, char const* const* const argv ) { - - int returnCode = applyCommandLine( argc, argv ); - if( returnCode == 0 ) - returnCode = run(); - return returnCode; - } - - int run() { - if( m_configData.showHelp ) - return 0; - - try + iterator( Columns const& columns, EndTag ) + : m_columns( columns.m_columns ), + m_activeIterators( 0 ) { - config(); // Force config to be constructed + m_iterators.reserve( m_columns.size() ); - seedRng( *m_config ); - - if( m_configData.filenamesAsTags ) - applyFilenamesAsTags( *m_config ); - - // Handle list request - if( Option listed = list( config() ) ) - return static_cast( *listed ); - - return static_cast( runTests( m_config ).assertions.failed ); + for( auto const& col : m_columns ) + m_iterators.push_back( col.end() ); } - catch( std::exception& ex ) { - Catch::cerr() << ex.what() << std::endl; - return (std::numeric_limits::max)(); + + public: + explicit iterator( Columns const& columns ) + : m_columns( columns.m_columns ), + m_activeIterators( m_columns.size() ) + { + m_iterators.reserve( m_columns.size() ); + + for( auto const& col : m_columns ) + m_iterators.push_back( col.begin() ); } + + auto operator ==( iterator const& other ) const -> bool { + return m_iterators == other.m_iterators; + } + auto operator !=( iterator const& other ) const -> bool { + return m_iterators != other.m_iterators; + } + auto operator *() const -> std::string { + std::string row, padding; + + for( size_t i = 0; i < m_columns.size(); ++i ) { + auto width = m_columns[i].width(); + if( m_iterators[i] != m_columns[i].end() ) { + std::string col = *m_iterators[i]; + row += padding + col; + if( col.size() < width ) + padding = std::string( width - col.size(), ' ' ); + else + padding = ""; + } + else { + padding += std::string( width, ' ' ); + } + } + return row; + } + auto operator ++() -> iterator& { + for( size_t i = 0; i < m_columns.size(); ++i ) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; + } + return *this; + } + auto operator ++(int) -> iterator { + iterator prev( *this ); + operator++(); + return prev; + } + }; + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator( *this ); } + auto end() const -> iterator { return { *this, iterator::EndTag() }; } + + auto operator += ( Column const& col ) -> Columns& { + m_columns.push_back( col ); + return *this; + } + auto operator + ( Column const& col ) -> Columns { + Columns combined = *this; + combined += col; + return combined; } - Clara::CommandLine const& cli() const { - return m_cli; + inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) { + + bool first = true; + for( auto line : cols ) { + if( first ) + first = false; + else + os << "\n"; + os << line; + } + return os; } - std::vector const& unusedTokens() const { - return m_unusedTokens; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); } - ConfigData& configData() { - return m_configData; - } - Config& config() { - if( !m_config ) - m_config = new Config( m_configData ); - return *m_config; - } - private: - Clara::CommandLine m_cli; - std::vector m_unusedTokens; - ConfigData m_configData; - Ptr m_config; }; - bool Session::alreadyInstantiated = false; + inline auto Column::operator + ( Column const& other ) -> Columns { + Columns cols; + cols += *this; + cols += other; + return cols; + } +}}} // namespace Catch::clara::TextFlow -} // end namespace Catch +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp -// #included from: catch_registry_hub.hpp -#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED - -// #included from: catch_test_case_registry_impl.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED - -#include +#include #include -#include -#include #include -#ifdef CATCH_CPP14_OR_GREATER -#include +#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) +#define CATCH_PLATFORM_WINDOWS #endif -namespace Catch { +namespace Catch { namespace clara { +namespace detail { - struct RandomNumberGenerator { - typedef int result_type; + // Traits for extracting arg and return type of lambdas (for single argument lambdas) + template + struct UnaryLambdaTraits : UnaryLambdaTraits {}; - result_type operator()( result_type n ) const { return std::rand() % n; } - -#ifdef CATCH_CPP14_OR_GREATER - static constexpr result_type min() { return 0; } - static constexpr result_type max() { return 1000000; } - result_type operator()() const { return std::rand() % max(); } -#endif - template - static void shuffle( V& vector ) { - RandomNumberGenerator rng; -#ifdef CATCH_CPP14_OR_GREATER - std::shuffle( vector.begin(), vector.end(), rng ); -#else - std::random_shuffle( vector.begin(), vector.end(), rng ); -#endif - } + template + struct UnaryLambdaTraits { + static const bool isValid = false; }; - inline std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + template + struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = typename std::remove_const::type>::type;; + using ReturnType = ReturnT; + }; - std::vector sorted = unsortedTestCases; + class TokenStream; - switch( config.runOrder() ) { - case RunTests::InLexicographicalOrder: - std::sort( sorted.begin(), sorted.end() ); - break; - case RunTests::InRandomOrder: - { - seedRng( config ); - RandomNumberGenerator::shuffle( sorted ); - } - break; - case RunTests::InDeclarationOrder: - // already in declaration order - break; - } - return sorted; - } - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { - return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); - } + // Transport for raw args (copied from main args, or supplied via init list for testing) + class Args { + friend TokenStream; + std::string m_exeName; + std::vector m_args; - void enforceNoDuplicateTestCases( std::vector const& functions ) { - std::set seenFunctions; - for( std::vector::const_iterator it = functions.begin(), itEnd = functions.end(); - it != itEnd; - ++it ) { - std::pair::const_iterator, bool> prev = seenFunctions.insert( *it ); - if( !prev.second ) { - std::ostringstream ss; - - ss << Colour( Colour::Red ) - << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" - << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; - - throw std::runtime_error(ss.str()); - } - } - } - - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { - std::vector filtered; - filtered.reserve( testCases.size() ); - for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); - it != itEnd; - ++it ) - if( matchTest( *it, testSpec, config ) ) - filtered.push_back( *it ); - return filtered; - } - std::vector const& getAllTestCasesSorted( IConfig const& config ) { - return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); - } - - class TestRegistry : public ITestCaseRegistry { public: - TestRegistry() - : m_currentSortOrder( RunTests::InDeclarationOrder ), - m_unnamedCount( 0 ) + Args( int argc, char *argv[] ) { + m_exeName = argv[0]; + for( int i = 1; i < argc; ++i ) + m_args.push_back( argv[i] ); + } + + Args( std::initializer_list args ) + : m_exeName( *args.begin() ), + m_args( args.begin()+1, args.end() ) {} - virtual ~TestRegistry(); - virtual void registerTest( TestCase const& testCase ) { - std::string name = testCase.getTestCaseInfo().name; - if( name == "" ) { - std::ostringstream oss; - oss << "Anonymous test case " << ++m_unnamedCount; - return registerTest( testCase.withName( oss.str() ) ); - } - m_functions.push_back( testCase ); + auto exeName() const -> std::string { + return m_exeName; } - - virtual std::vector const& getAllTests() const { - return m_functions; - } - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const { - if( m_sortedFunctions.empty() ) - enforceNoDuplicateTestCases( m_functions ); - - if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { - m_sortedFunctions = sortTests( config, m_functions ); - m_currentSortOrder = config.runOrder(); - } - return m_sortedFunctions; - } - - private: - std::vector m_functions; - mutable RunTests::InWhatOrder m_currentSortOrder; - mutable std::vector m_sortedFunctions; - size_t m_unnamedCount; - std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised }; - /////////////////////////////////////////////////////////////////////////// - - class FreeFunctionTestCase : public SharedImpl { - public: - - FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} - - virtual void invoke() const { - m_fun(); - } - - private: - virtual ~FreeFunctionTestCase(); - - TestFunction m_fun; + // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string + // may encode an option + its argument if the : or = form is used + enum class TokenType { + Option, Argument + }; + struct Token { + TokenType type; + std::string token; }; - inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { - std::string className = classOrQualifiedMethodName; - if( startsWith( className, "&" ) ) - { - std::size_t lastColons = className.rfind( "::" ); - std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); - if( penultimateColons == std::string::npos ) - penultimateColons = 1; - className = className.substr( penultimateColons, lastColons-penultimateColons ); - } - return className; - } - - void registerTestCase - ( ITestCase* testCase, - char const* classOrQualifiedMethodName, - NameAndDesc const& nameAndDesc, - SourceLineInfo const& lineInfo ) { - - getMutableRegistryHub().registerTest - ( makeTestCase - ( testCase, - extractClassName( classOrQualifiedMethodName ), - nameAndDesc.name, - nameAndDesc.description, - lineInfo ) ); - } - void registerTestCaseFunction - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ) { - registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); - } - - /////////////////////////////////////////////////////////////////////////// - - AutoReg::AutoReg - ( TestFunction function, - SourceLineInfo const& lineInfo, - NameAndDesc const& nameAndDesc ) { - registerTestCaseFunction( function, lineInfo, nameAndDesc ); - } - - AutoReg::~AutoReg() {} - -} // end namespace Catch - -// #included from: catch_reporter_registry.hpp -#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED - -#include - -namespace Catch { - - class ReporterRegistry : public IReporterRegistry { - - public: - - virtual ~ReporterRegistry() CATCH_OVERRIDE {} - - virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const CATCH_OVERRIDE { - FactoryMap::const_iterator it = m_factories.find( name ); - if( it == m_factories.end() ) - return CATCH_NULL; - return it->second->create( ReporterConfig( config ) ); - } - - void registerReporter( std::string const& name, Ptr const& factory ) { - m_factories.insert( std::make_pair( name, factory ) ); - } - void registerListener( Ptr const& factory ) { - m_listeners.push_back( factory ); - } - - virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { - return m_factories; - } - virtual Listeners const& getListeners() const CATCH_OVERRIDE { - return m_listeners; - } - - private: - FactoryMap m_factories; - Listeners m_listeners; - }; -} - -// #included from: catch_exception_translator_registry.hpp -#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED - -#ifdef __OBJC__ -#import "Foundation/Foundation.h" + inline auto isOptPrefix( char c ) -> bool { + return c == '-' +#ifdef CATCH_PLATFORM_WINDOWS + || c == '/' #endif + ; + } -namespace Catch { + // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled + class TokenStream { + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + + void loadBuffer() { + m_tokenBuffer.resize( 0 ); + + // Skip any empty strings + while( it != itEnd && it->empty() ) + ++it; + + if( it != itEnd ) { + auto const &next = *it; + if( isOptPrefix( next[0] ) ) { + auto delimiterPos = next.find_first_of( " :=" ); + if( delimiterPos != std::string::npos ) { + m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); + m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); + } else { + if( next[1] != '-' && next.size() > 2 ) { + std::string opt = "- "; + for( size_t i = 1; i < next.size(); ++i ) { + opt[1] = next[i]; + m_tokenBuffer.push_back( { TokenType::Option, opt } ); + } + } else { + m_tokenBuffer.push_back( { TokenType::Option, next } ); + } + } + } else { + m_tokenBuffer.push_back( { TokenType::Argument, next } ); + } + } + } - class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { public: - ~ExceptionTranslatorRegistry() { - deleteAll( m_translators ); + explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} + + TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { + loadBuffer(); } - virtual void registerTranslator( const IExceptionTranslator* translator ) { - m_translators.push_back( translator ); + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; } - virtual std::string translateActiveException() const { - try { -#ifdef __OBJC__ - // In Objective-C try objective-c exceptions first - @try { - return tryTranslators(); - } - @catch (NSException *exception) { - return Catch::toString( [exception description] ); - } -#else - return tryTranslators(); -#endif - } - catch( TestFailureException& ) { - throw; - } - catch( std::exception& ex ) { - return ex.what(); - } - catch( std::string& msg ) { - return msg; - } - catch( const char* msg ) { - return msg; - } - catch(...) { - return "Unknown exception"; - } + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + + auto operator*() const -> Token { + assert( !m_tokenBuffer.empty() ); + return m_tokenBuffer.front(); } - std::string tryTranslators() const { - if( m_translators.empty() ) - throw; - else - return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); + auto operator->() const -> Token const * { + assert( !m_tokenBuffer.empty() ); + return &m_tokenBuffer.front(); } - private: - std::vector m_translators; + auto operator++() -> TokenStream & { + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if( it != itEnd ) + ++it; + loadBuffer(); + } + return *this; + } }; -} -namespace Catch { - - namespace { - - class RegistryHub : public IRegistryHub, public IMutableRegistryHub { - - RegistryHub( RegistryHub const& ); - void operator=( RegistryHub const& ); - - public: // IRegistryHub - RegistryHub() { - } - virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { - return m_reporterRegistry; - } - virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { - return m_testCaseRegistry; - } - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { - return m_exceptionTranslatorRegistry; - } - - public: // IMutableRegistryHub - virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { - m_reporterRegistry.registerReporter( name, factory ); - } - virtual void registerListener( Ptr const& factory ) CATCH_OVERRIDE { - m_reporterRegistry.registerListener( factory ); - } - virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE { - m_testCaseRegistry.registerTest( testInfo ); - } - virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { - m_exceptionTranslatorRegistry.registerTranslator( translator ); - } - - private: - TestRegistry m_testCaseRegistry; - ReporterRegistry m_reporterRegistry; - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + class ResultBase { + public: + enum Type { + Ok, LogicError, RuntimeError }; - // Single, global, instance - inline RegistryHub*& getTheRegistryHub() { - static RegistryHub* theRegistryHub = CATCH_NULL; - if( !theRegistryHub ) - theRegistryHub = new RegistryHub(); - return theRegistryHub; + protected: + ResultBase( Type type ) : m_type( type ) {} + virtual ~ResultBase() = default; + + virtual void enforceOk() const = 0; + + Type m_type; + }; + + template + class ResultValueBase : public ResultBase { + public: + auto value() const -> T const & { + enforceOk(); + return m_value; } + + protected: + ResultValueBase( Type type ) : ResultBase( type ) {} + + ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + } + + ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { + new( &m_value ) T( value ); + } + + auto operator=( ResultValueBase const &other ) -> ResultValueBase & { + if( m_type == ResultBase::Ok ) + m_value.~T(); + ResultBase::operator=(other); + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + return *this; + } + + ~ResultValueBase() { + if( m_type == Ok ) + m_value.~T(); + } + + union { + T m_value; + }; + }; + + template<> + class ResultValueBase : public ResultBase { + protected: + using ResultBase::ResultBase; + }; + + template + class BasicResult : public ResultValueBase { + public: + template + explicit BasicResult( BasicResult const &other ) + : ResultValueBase( other.type() ), + m_errorMessage( other.errorMessage() ) + { + assert( type() != ResultBase::Ok ); + } + + template + static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } + static auto ok() -> BasicResult { return { ResultBase::Ok }; } + static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } + static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } + + explicit operator bool() const { return m_type == ResultBase::Ok; } + auto type() const -> ResultBase::Type { return m_type; } + auto errorMessage() const -> std::string { return m_errorMessage; } + + protected: + virtual void enforceOk() const { + // !TBD: If no exceptions, std::terminate here or something + switch( m_type ) { + case ResultBase::LogicError: + throw std::logic_error( m_errorMessage ); + case ResultBase::RuntimeError: + throw std::runtime_error( m_errorMessage ); + case ResultBase::Ok: + break; + } + } + + std::string m_errorMessage; // Only populated if resultType is an error + + BasicResult( ResultBase::Type type, std::string const &message ) + : ResultValueBase(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); + } + + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; + }; + + enum class ParseResultType { + Matched, NoMatch, ShortCircuitAll, ShortCircuitSame + }; + + class ParseState { + public: + + ParseState( ParseResultType type, TokenStream const &remainingTokens ) + : m_type(type), + m_remainingTokens( remainingTokens ) + {} + + auto type() const -> ParseResultType { return m_type; } + auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; + + using Result = BasicResult; + using ParserResult = BasicResult; + using InternalParseResult = BasicResult; + + struct HelpColumns { + std::string left; + std::string right; + }; + + template + inline auto convertInto( std::string const &source, T& target ) -> ParserResult { + std::stringstream ss; + ss << source; + ss >> target; + if( ss.fail() ) + return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { + target = source; + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { + std::string srcLC = source; + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::tolower(c) ); } ); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") + target = false; + else + return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); + return ParserResult::ok( ParseResultType::Matched ); } - IRegistryHub& getRegistryHub() { - return *getTheRegistryHub(); + struct BoundRefBase { + BoundRefBase() = default; + BoundRefBase( BoundRefBase const & ) = delete; + BoundRefBase( BoundRefBase && ) = delete; + BoundRefBase &operator=( BoundRefBase const & ) = delete; + BoundRefBase &operator=( BoundRefBase && ) = delete; + + virtual ~BoundRefBase() = default; + + virtual auto isFlag() const -> bool = 0; + virtual auto isContainer() const -> bool { return false; } + virtual auto setValue( std::string const &arg ) -> ParserResult = 0; + virtual auto setFlag( bool flag ) -> ParserResult = 0; + }; + + struct BoundValueRefBase : BoundRefBase { + auto isFlag() const -> bool override { return false; } + + auto setFlag( bool ) -> ParserResult override { + return ParserResult::logicError( "Flags can only be set on boolean fields" ); + } + }; + + struct BoundFlagRefBase : BoundRefBase { + auto isFlag() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + bool flag; + auto result = convertInto( arg, flag ); + if( result ) + setFlag( flag ); + return result; + } + }; + + template + struct BoundRef : BoundValueRefBase { + T &m_ref; + + explicit BoundRef( T &ref ) : m_ref( ref ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return convertInto( arg, m_ref ); + } + }; + + template + struct BoundRef> : BoundValueRefBase { + std::vector &m_ref; + + explicit BoundRef( std::vector &ref ) : m_ref( ref ) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + T temp; + auto result = convertInto( arg, temp ); + if( result ) + m_ref.push_back( temp ); + return result; + } + }; + + struct BoundFlagRef : BoundFlagRefBase { + bool &m_ref; + + explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} + + auto setFlag( bool flag ) -> ParserResult override { + m_ref = flag; + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + struct LambdaInvoker { + static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); + + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + return lambda( arg ); + } + }; + + template<> + struct LambdaInvoker { + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { + ArgType temp; + auto result = convertInto( arg, temp ); + return !result + ? result + : LambdaInvoker::ReturnType>::invoke( lambda, temp ); + }; + + template + struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return invokeLambda::ArgType>( m_lambda, arg ); + } + }; + + template + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); + + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); + } + }; + + enum class Optionality { Optional, Required }; + + struct Parser; + + class ParserBase { + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto cardinality() const -> size_t { return 1; } + + auto parse( Args const &args ) const -> InternalParseResult { + return parse( args.exeName(), TokenStream( args ) ); + } + }; + + template + class ComposableParserImpl : public ParserBase { + public: + template + auto operator|( T const &other ) const -> Parser; + }; + + // Common code and state for Args and Opts + template + class ParserRefImpl : public ComposableParserImpl { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} + + public: + template + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint( hint ) + {} + + template + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint(hint) + {} + + auto operator()( std::string const &description ) -> DerivedT & { + m_description = description; + return static_cast( *this ); + } + + auto optional() -> DerivedT & { + m_optionality = Optionality::Optional; + return static_cast( *this ); + }; + + auto required() -> DerivedT & { + m_optionality = Optionality::Required; + return static_cast( *this ); + }; + + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } + + auto cardinality() const -> size_t override { + if( m_ref->isContainer() ) + return 0; + else + return 1; + } + + auto hint() const -> std::string { return m_hint; } + }; + + class ExeName : public ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; + + template + static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { + return std::make_shared>( lambda) ; + } + + public: + ExeName() : m_name( std::make_shared( "" ) ) {} + + explicit ExeName( std::string &ref ) : ExeName() { + m_ref = std::make_shared>( ref ); + } + + template + explicit ExeName( LambdaT const& lambda ) : ExeName() { + m_ref = std::make_shared>( lambda ); + } + + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + } + + auto name() const -> std::string { return *m_name; } + auto set( std::string const& newName ) -> ParserResult { + + auto lastSlash = newName.find_last_of( "\\/" ); + auto filename = ( lastSlash == std::string::npos ) + ? newName + : newName.substr( lastSlash+1 ); + + *m_name = filename; + if( m_ref ) + return m_ref->setValue( filename ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + class Arg : public ParserRefImpl { + public: + using ParserRefImpl::ParserRefImpl; + + auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if( token.type != TokenType::Argument ) + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + + auto result = m_ref->setValue( remainingTokens->token ); + if( !result ) + return InternalParseResult( result ); + else + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + }; + + inline auto normaliseOpt( std::string const &optName ) -> std::string { +#ifdef CATCH_PLATFORM_WINDOWS + if( optName[0] == '/' ) + return "-" + optName.substr( 1 ); + else +#endif + return optName; } - IMutableRegistryHub& getMutableRegistryHub() { - return *getTheRegistryHub(); - } - void cleanUp() { - delete getTheRegistryHub(); - getTheRegistryHub() = CATCH_NULL; - cleanUpContext(); - } - std::string translateActiveException() { - return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + + class Opt : public ParserRefImpl { + protected: + std::vector m_optNames; + + public: + template + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} + + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} + + template + Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + template + Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + auto operator[]( std::string const &optName ) -> Opt & { + m_optNames.push_back( optName ); + return *this; + } + + auto getHelpColumns() const -> std::vector { + std::ostringstream oss; + bool first = true; + for( auto const &opt : m_optNames ) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if( !m_hint.empty() ) + oss << " <" << m_hint << ">"; + return { { oss.str(), m_description } }; + } + + auto isMatch( std::string const &optToken ) const -> bool { + auto normalisedToken = normaliseOpt( optToken ); + for( auto const &name : m_optNames ) { + if( normaliseOpt( name ) == normalisedToken ) + return true; + } + return false; + } + + using ParserBase::parse; + + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + if( remainingTokens && remainingTokens->type == TokenType::Option ) { + auto const &token = *remainingTokens; + if( isMatch(token.token ) ) { + if( m_ref->isFlag() ) { + auto result = m_ref->setFlag( true ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } else { + ++remainingTokens; + if( !remainingTokens ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto const &argToken = *remainingTokens; + if( argToken.type != TokenType::Argument ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto result = m_ref->setValue( argToken.token ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + } + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + } + + auto validate() const -> Result override { + if( m_optNames.empty() ) + return Result::logicError( "No options supplied to Opt" ); + for( auto const &name : m_optNames ) { + if( name.empty() ) + return Result::logicError( "Option name cannot be empty" ); +#ifdef CATCH_PLATFORM_WINDOWS + if( name[0] != '-' && name[0] != '/' ) + return Result::logicError( "Option name must begin with '-' or '/'" ); +#else + if( name[0] != '-' ) + return Result::logicError( "Option name must begin with '-'" ); +#endif + } + return ParserRefImpl::validate(); + } + }; + + struct Help : Opt { + Help( bool &showHelpFlag ) + : Opt([&]( bool flag ) { + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); + }) + { + static_cast( *this ) + ("display usage information") + ["-?"]["-h"]["--help"] + .optional(); + } + }; + + struct Parser : ParserBase { + + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + auto operator|=( ExeName const &exeName ) -> Parser & { + m_exeName = exeName; + return *this; + } + + auto operator|=( Arg const &arg ) -> Parser & { + m_args.push_back(arg); + return *this; + } + + auto operator|=( Opt const &opt ) -> Parser & { + m_options.push_back(opt); + return *this; + } + + auto operator|=( Parser const &other ) -> Parser & { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + template + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; + } + + auto getHelpColumns() const -> std::vector { + std::vector cols; + for (auto const &o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); + } + return cols; + } + + void writeToStream( std::ostream &os ) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" << " " << m_exeName.name() << " "; + bool required = true, first = true; + for( auto const &arg : m_args ) { + if (first) + first = false; + else + os << " "; + if( arg.isOptional() && required ) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if( arg.cardinality() == 0 ) + os << " ... "; + } + if( !required ) + os << "]"; + if( !m_options.empty() ) + os << " options"; + os << "\n\nwhere options are:" << std::endl; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for( auto const &cols : rows ) + optWidth = (std::max)(optWidth, cols.left.size() + 2); + + for( auto const &cols : rows ) { + auto row = + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + + TextFlow::Spacer(4) + + TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); + os << row << std::endl; + } + } + + friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { + parser.writeToStream( os ); + return os; + } + + auto validate() const -> Result override { + for( auto const &opt : m_options ) { + auto result = opt.validate(); + if( !result ) + return result; + } + for( auto const &arg : m_args ) { + auto result = arg.validate(); + if( !result ) + return result; + } + return Result::ok(); + } + + using ParserBase::parse; + + auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { + + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert( totalParsers < 512 ); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const &opt : m_options) parseInfos[i++].parser = &opt; + for (auto const &arg : m_args) parseInfos[i++].parser = &arg; + } + + m_exeName.set( exeName ); + + auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + while( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for( size_t i = 0; i < totalParsers; ++i ) { + auto& parseInfo = parseInfos[i]; + if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) + return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } + + if( result.value().type() == ParseResultType::ShortCircuitAll ) + return result; + if( !tokenParsed ) + return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); + } + // !TBD Check missing required options + return result; + } + }; + + template + template + auto ComposableParserImpl::operator|( T const &other ) const -> Parser { + return Parser() | static_cast( *this ) | other; } +} // namespace detail + +// A Combined parser +using detail::Parser; + +// A parser for options +using detail::Opt; + +// A parser for arguments +using detail::Arg; + +// Wrapper for argc, argv from main() +using detail::Args; + +// Specifies the name of the executable +using detail::ExeName; + +// Convenience wrapper for option parser that specifies the help option +using detail::Help; + +// enum of result types from a parse +using detail::ParseResultType; + +// Result type for parser operation +using detail::ParserResult; + +}} // namespace Catch::clara + +// end clara.hpp +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +// end catch_clara.h +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ); } // end namespace Catch -// #included from: catch_notimplemented_exception.hpp -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED +// end catch_commandline.h +#include +#include +namespace Catch { + + clara::Parser makeCommandLineParser( ConfigData& config ) { + + using namespace clara; + + auto const setWarning = [&]( std::string const& warning ) { + if( warning != "NoAssertions" ) + return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); + config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const loadTestNamesFromFile = [&]( std::string const& filename ) { + std::ifstream f( filename.c_str() ); + if( !f.is_open() ) + return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + config.testsOrTags.push_back( line + ',' ); + } + } + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setTestOrder = [&]( std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = RunTests::InDeclarationOrder; + else if( startsWith( "lexical", order ) ) + config.runOrder = RunTests::InLexicographicalOrder; + else if( startsWith( "random", order ) ) + config.runOrder = RunTests::InRandomOrder; + else + return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setRngSeed = [&]( std::string const& seed ) { + if( seed != "time" ) + return clara::detail::convertInto( seed, config.rngSeed ); + config.rngSeed = static_cast( std::time(nullptr) ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setColourUsage = [&]( std::string const& useColour ) { + auto mode = toLower( useColour ); + + if( mode == "yes" ) + config.useColour = UseColour::Yes; + else if( mode == "no" ) + config.useColour = UseColour::No; + else if( mode == "auto" ) + config.useColour = UseColour::Auto; + else + return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setWaitForKeypress = [&]( std::string const& keypress ) { + auto keypressLc = toLower( keypress ); + if( keypressLc == "start" ) + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if( keypressLc == "exit" ) + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if( keypressLc == "both" ) + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + auto const setVerbosity = [&]( std::string const& verbosity ) { + auto lcVerbosity = toLower( verbosity ); + if( lcVerbosity == "quiet" ) + config.verbosity = Verbosity::Quiet; + else if( lcVerbosity == "normal" ) + config.verbosity = Verbosity::Normal; + else if( lcVerbosity == "high" ) + config.verbosity = Verbosity::High; + else + return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + }; + + auto cli + = ExeName( config.processName ) + | Help( config.showHelp ) + | Opt( config.listTests ) + ["-l"]["--list-tests"] + ( "list all/matching test cases" ) + | Opt( config.listTags ) + ["-t"]["--list-tags"] + ( "list all/matching tags" ) + | Opt( config.showSuccessfulTests ) + ["-s"]["--success"] + ( "include successful tests in output" ) + | Opt( config.shouldDebugBreak ) + ["-b"]["--break"] + ( "break into debugger on failure" ) + | Opt( config.noThrow ) + ["-e"]["--nothrow"] + ( "skip exception tests" ) + | Opt( config.showInvisibles ) + ["-i"]["--invisibles"] + ( "show invisibles (tabs, newlines)" ) + | Opt( config.outputFilename, "filename" ) + ["-o"]["--out"] + ( "output filename" ) + | Opt( config.reporterNames, "name" ) + ["-r"]["--reporter"] + ( "reporter to use (defaults to console)" ) + | Opt( config.name, "name" ) + ["-n"]["--name"] + ( "suite name" ) + | Opt( [&]( bool ){ config.abortAfter = 1; } ) + ["-a"]["--abort"] + ( "abort at first failure" ) + | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) + ["-x"]["--abortx"] + ( "abort after x failures" ) + | Opt( setWarning, "warning name" ) + ["-w"]["--warn"] + ( "enable warnings" ) + | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) + ["-d"]["--durations"] + ( "show test durations" ) + | Opt( loadTestNamesFromFile, "filename" ) + ["-f"]["--input-file"] + ( "load test names to run from a file" ) + | Opt( config.filenamesAsTags ) + ["-#"]["--filenames-as-tags"] + ( "adds a tag for the filename" ) + | Opt( config.sectionsToRun, "section name" ) + ["-c"]["--section"] + ( "specify section to run" ) + | Opt( setVerbosity, "quiet|normal|high" ) + ["-v"]["--verbosity"] + ( "set output verbosity" ) + | Opt( config.listTestNamesOnly ) + ["--list-test-names-only"] + ( "list all/matching test cases names only" ) + | Opt( config.listReporters ) + ["--list-reporters"] + ( "list all reporters" ) + | Opt( setTestOrder, "decl|lex|rand" ) + ["--order"] + ( "test case order (defaults to decl)" ) + | Opt( setRngSeed, "'time'|number" ) + ["--rng-seed"] + ( "set a specific seed for random numbers" ) + | Opt( setColourUsage, "yes|no" ) + ["--use-colour"] + ( "should output be colourised" ) + | Opt( config.libIdentify ) + ["--libidentify"] + ( "report name and version according to libidentify standard" ) + | Opt( setWaitForKeypress, "start|exit|both" ) + ["--wait-for-keypress"] + ( "waits for a keypress before exiting" ) + | Opt( config.benchmarkResolutionMultiple, "multiplier" ) + ["--benchmark-resolution-multiple"] + ( "multiple of clock resolution to run benchmarks" ) + + | Arg( config.testsOrTags, "test name|pattern|tags" ) + ( "which test or tests to use" ); + + return cli; + } + +} // end namespace Catch +// end catch_commandline.cpp +// start catch_common.cpp + +#include #include namespace Catch { - NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) - : m_lineInfo( lineInfo ) { - std::ostringstream oss; - oss << lineInfo << ": function "; - oss << "not implemented"; - m_what = oss.str(); + bool SourceLineInfo::empty() const noexcept { + return file[0] == '\0'; + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + } + bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { + return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); } - const char* NotImplementedException::what() const CATCH_NOEXCEPT { - return m_what.c_str(); + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << '(' << info.line << ')'; +#else + os << info.file << ':' << info.line; +#endif + return os; + } + + std::string StreamEndStop::operator+() const { + return std::string(); + } + + NonCopyable::NonCopyable() = default; + NonCopyable::~NonCopyable() = default; + +} +// end catch_common.cpp +// start catch_config.cpp + +// start catch_enforce.h + +#include +#include + +#define CATCH_PREPARE_EXCEPTION( type, msg ) \ + type( static_cast( Catch::ReusableStringStream().get() << msg ).str() ) +#define CATCH_INTERNAL_ERROR( msg ) \ + throw CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg); +#define CATCH_ERROR( msg ) \ + throw CATCH_PREPARE_EXCEPTION( std::domain_error, msg ) +#define CATCH_ENFORCE( condition, msg ) \ + do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false) + +// end catch_enforce.h +namespace Catch { + + Config::Config( ConfigData const& data ) + : m_data( data ), + m_stream( openStream() ) + { + if( !data.testsOrTags.empty() ) { + TestSpecParser parser( ITagAliasRegistry::get() ); + for( auto const& testOrTags : data.testsOrTags ) + parser.parse( testOrTags ); + m_testSpec = parser.testSpec(); + } + } + + std::string const& Config::getFilename() const { + return m_data.outputFilename ; + } + + bool Config::listTests() const { return m_data.listTests; } + bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool Config::listTags() const { return m_data.listTags; } + bool Config::listReporters() const { return m_data.listReporters; } + + std::string Config::getProcessName() const { return m_data.processName; } + + std::vector const& Config::getReporterNames() const { return m_data.reporterNames; } + std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } + + TestSpec const& Config::testSpec() const { return m_testSpec; } + + bool Config::showHelp() const { return m_data.showHelp; } + + // IConfig interface + bool Config::allowThrows() const { return !m_data.noThrow; } + std::ostream& Config::stream() const { return m_stream->stream(); } + std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + bool Config::warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } + ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } + RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } + unsigned int Config::rngSeed() const { return m_data.rngSeed; } + int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; } + UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } + bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } + int Config::abortAfter() const { return m_data.abortAfter; } + bool Config::showInvisibles() const { return m_data.showInvisibles; } + Verbosity Config::verbosity() const { return m_data.verbosity; } + + IStream const* Config::openStream() { + return Catch::makeStream(m_data.outputFilename); } } // end namespace Catch +// end catch_config.cpp +// start catch_console_colour.cpp -// #included from: catch_context_impl.hpp -#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED - -// #included from: catch_stream.hpp -#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED - -#include -#include -#include - -namespace Catch { - - template - class StreamBufImpl : public StreamBufBase { - char data[bufferSize]; - WriterF m_writer; - - public: - StreamBufImpl() { - setp( data, data + sizeof(data) ); - } - - ~StreamBufImpl() CATCH_NOEXCEPT { - sync(); - } - - private: - int overflow( int c ) { - sync(); - - if( c != EOF ) { - if( pbase() == epptr() ) - m_writer( std::string( 1, static_cast( c ) ) ); - else - sputc( static_cast( c ) ); - } - return 0; - } - - int sync() { - if( pbase() != pptr() ) { - m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); - setp( pbase(), epptr() ); - } - return 0; - } - }; - - /////////////////////////////////////////////////////////////////////////// - - FileStream::FileStream( std::string const& filename ) { - m_ofs.open( filename.c_str() ); - if( m_ofs.fail() ) { - std::ostringstream oss; - oss << "Unable to open file: '" << filename << "'"; - throw std::domain_error( oss.str() ); - } - } - - std::ostream& FileStream::stream() const { - return m_ofs; - } - - struct OutputDebugWriter { - - void operator()( std::string const&str ) { - writeToDebugConsole( str ); - } - }; - - DebugOutStream::DebugOutStream() - : m_streamBuf( new StreamBufImpl() ), - m_os( m_streamBuf.get() ) - {} - - std::ostream& DebugOutStream::stream() const { - return m_os; - } - - // Store the streambuf from cout up-front because - // cout may get redirected when running tests - CoutStream::CoutStream() - : m_os( Catch::cout().rdbuf() ) - {} - - std::ostream& CoutStream::stream() const { - return m_os; - } - -#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions - std::ostream& cout() { - return std::cout; - } - std::ostream& cerr() { - return std::cerr; - } +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" #endif -} + +// start catch_errno_guard.h namespace Catch { - class Context : public IMutableContext { - - Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {} - Context( Context const& ); - void operator=( Context const& ); - - public: // IContext - virtual IResultCapture* getResultCapture() { - return m_resultCapture; - } - virtual IRunner* getRunner() { - return m_runner; - } - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { - return getGeneratorsForCurrentTest() - .getGeneratorInfo( fileInfo, totalSize ) - .getCurrentIndex(); - } - virtual bool advanceGeneratorsForCurrentTest() { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - return generators && generators->moveNext(); - } - - virtual Ptr getConfig() const { - return m_config; - } - - public: // IMutableContext - virtual void setResultCapture( IResultCapture* resultCapture ) { - m_resultCapture = resultCapture; - } - virtual void setRunner( IRunner* runner ) { - m_runner = runner; - } - virtual void setConfig( Ptr const& config ) { - m_config = config; - } - - friend IMutableContext& getCurrentMutableContext(); - + class ErrnoGuard { + public: + ErrnoGuard(); + ~ErrnoGuard(); private: - IGeneratorsForTest* findGeneratorsForCurrentTest() { - std::string testName = getResultCapture()->getCurrentTestName(); - - std::map::const_iterator it = - m_generatorsByTestName.find( testName ); - return it != m_generatorsByTestName.end() - ? it->second - : CATCH_NULL; - } - - IGeneratorsForTest& getGeneratorsForCurrentTest() { - IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); - if( !generators ) { - std::string testName = getResultCapture()->getCurrentTestName(); - generators = createGeneratorsForTest(); - m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); - } - return *generators; - } - - private: - Ptr m_config; - IRunner* m_runner; - IResultCapture* m_resultCapture; - std::map m_generatorsByTestName; + int m_oldErrno; }; - namespace { - Context* currentContext = CATCH_NULL; - } - IMutableContext& getCurrentMutableContext() { - if( !currentContext ) - currentContext = new Context(); - return *currentContext; - } - IContext& getCurrentContext() { - return getCurrentMutableContext(); - } - - void cleanUpContext() { - delete currentContext; - currentContext = CATCH_NULL; - } } -// #included from: catch_console_colour_impl.hpp -#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED +// end catch_errno_guard.h +#include namespace Catch { namespace { struct IColourImpl { - virtual ~IColourImpl() {} + virtual ~IColourImpl() = default; virtual void use( Colour::Code _colourCode ) = 0; }; @@ -7053,16 +6662,6 @@ namespace Catch { #if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#ifdef __AFXDLL -#include -#else -#include -#endif - namespace Catch { namespace { @@ -7076,7 +6675,7 @@ namespace { originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); } - virtual void use( Colour::Code _colourCode ) { + virtual void use( Colour::Code _colourCode ) override { switch( _colourCode ) { case Colour::None: return setTextAttribute( originalForegroundAttributes ); case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); @@ -7092,7 +6691,7 @@ namespace { case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); - case Colour::Bright: throw std::logic_error( "not a colour" ); + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); } } @@ -7108,14 +6707,12 @@ namespace { IColourImpl* platformColourInstance() { static Win32ColourImpl s_instance; - Ptr config = getCurrentContext().getConfig(); + IConfigPtr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; if( colourMode == UseColour::Auto ) - colourMode = !isDebuggerActive() - ? UseColour::Yes - : UseColour::No; + colourMode = UseColour::Yes; return colourMode == UseColour::Yes ? &s_instance : NoColourImpl::instance(); @@ -7137,13 +6734,13 @@ namespace { // https://github.com/philsquared/Catch/pull/131 class PosixColourImpl : public IColourImpl { public: - virtual void use( Colour::Code _colourCode ) { + virtual void use( Colour::Code _colourCode ) override { switch( _colourCode ) { case Colour::None: case Colour::White: return setColour( "[0m" ); case Colour::Red: return setColour( "[0;31m" ); case Colour::Green: return setColour( "[0;32m" ); - case Colour::Blue: return setColour( "[0:34m" ); + case Colour::Blue: return setColour( "[0;34m" ); case Colour::Cyan: return setColour( "[0;36m" ); case Colour::Yellow: return setColour( "[0;33m" ); case Colour::Grey: return setColour( "[1;30m" ); @@ -7153,7 +6750,7 @@ namespace { case Colour::BrightGreen: return setColour( "[1;32m" ); case Colour::BrightWhite: return setColour( "[1;37m" ); - case Colour::Bright: throw std::logic_error( "not a colour" ); + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); } } static IColourImpl* instance() { @@ -7167,13 +6764,21 @@ namespace { } }; + bool useColourOnPlatform() { + return +#ifdef CATCH_PLATFORM_MAC + !isDebuggerActive() && +#endif + isatty(STDOUT_FILENO); + } IColourImpl* platformColourInstance() { - Ptr config = getCurrentContext().getConfig(); + ErrnoGuard guard; + IConfigPtr config = getCurrentContext().getConfig(); UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; if( colourMode == UseColour::Auto ) - colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) ) + colourMode = useColourOnPlatform() ? UseColour::Yes : UseColour::No; return colourMode == UseColour::Yes @@ -7196,8 +6801,17 @@ namespace Catch { namespace Catch { - Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } - Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast( _other ).m_moved = true; } + Colour::Colour( Code _colourCode ) { use( _colourCode ); } + Colour::Colour( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + } + Colour& Colour::operator=( Colour&& rhs ) noexcept { + m_moved = rhs.m_moved; + rhs.m_moved = true; + return *this; + } + Colour::~Colour(){ if( !m_moved ) use( None ); } void Colour::use( Code _colourCode ) { @@ -7205,385 +6819,1136 @@ namespace Catch { impl->use( _colourCode ); } -} // end namespace Catch - -// #included from: catch_generators_impl.hpp -#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED - -#include -#include -#include - -namespace Catch { - - struct GeneratorInfo : IGeneratorInfo { - - GeneratorInfo( std::size_t size ) - : m_size( size ), - m_currentIndex( 0 ) - {} - - bool moveNext() { - if( ++m_currentIndex == m_size ) { - m_currentIndex = 0; - return false; - } - return true; - } - - std::size_t getCurrentIndex() const { - return m_currentIndex; - } - - std::size_t m_size; - std::size_t m_currentIndex; - }; - - /////////////////////////////////////////////////////////////////////////// - - class GeneratorsForTest : public IGeneratorsForTest { - - public: - ~GeneratorsForTest() { - deleteAll( m_generatorsInOrder ); - } - - IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { - std::map::const_iterator it = m_generatorsByName.find( fileInfo ); - if( it == m_generatorsByName.end() ) { - IGeneratorInfo* info = new GeneratorInfo( size ); - m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); - m_generatorsInOrder.push_back( info ); - return *info; - } - return *it->second; - } - - bool moveNext() { - std::vector::const_iterator it = m_generatorsInOrder.begin(); - std::vector::const_iterator itEnd = m_generatorsInOrder.end(); - for(; it != itEnd; ++it ) { - if( (*it)->moveNext() ) - return true; - } - return false; - } - - private: - std::map m_generatorsByName; - std::vector m_generatorsInOrder; - }; - - IGeneratorsForTest* createGeneratorsForTest() - { - return new GeneratorsForTest(); - } - -} // end namespace Catch - -// #included from: catch_assertionresult.hpp -#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED - -namespace Catch { - - AssertionInfo::AssertionInfo( std::string const& _macroName, - SourceLineInfo const& _lineInfo, - std::string const& _capturedExpression, - ResultDisposition::Flags _resultDisposition ) - : macroName( _macroName ), - lineInfo( _lineInfo ), - capturedExpression( _capturedExpression ), - resultDisposition( _resultDisposition ) - {} - - AssertionResult::AssertionResult() {} - - AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) - : m_info( info ), - m_resultData( data ) - {} - - AssertionResult::~AssertionResult() {} - - // Result was a success - bool AssertionResult::succeeded() const { - return Catch::isOk( m_resultData.resultType ); - } - - // Result was a success, or failure is suppressed - bool AssertionResult::isOk() const { - return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); - } - - ResultWas::OfType AssertionResult::getResultType() const { - return m_resultData.resultType; - } - - bool AssertionResult::hasExpression() const { - return !m_info.capturedExpression.empty(); - } - - bool AssertionResult::hasMessage() const { - return !m_resultData.message.empty(); - } - - std::string AssertionResult::getExpression() const { - if( isFalseTest( m_info.resultDisposition ) ) - return "!" + m_info.capturedExpression; - else - return m_info.capturedExpression; - } - std::string AssertionResult::getExpressionInMacro() const { - if( m_info.macroName.empty() ) - return m_info.capturedExpression; - else - return m_info.macroName + "( " + m_info.capturedExpression + " )"; - } - - bool AssertionResult::hasExpandedExpression() const { - return hasExpression() && getExpandedExpression() != getExpression(); - } - - std::string AssertionResult::getExpandedExpression() const { - return m_resultData.reconstructedExpression; - } - - std::string AssertionResult::getMessage() const { - return m_resultData.message; - } - SourceLineInfo AssertionResult::getSourceInfo() const { - return m_info.lineInfo; - } - - std::string AssertionResult::getTestMacroName() const { - return m_info.macroName; - } - -} // end namespace Catch - -// #included from: catch_test_case_info.hpp -#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED - -namespace Catch { - - inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { - if( startsWith( tag, "." ) || - tag == "hide" || - tag == "!hide" ) - return TestCaseInfo::IsHidden; - else if( tag == "!throws" ) - return TestCaseInfo::Throws; - else if( tag == "!shouldfail" ) - return TestCaseInfo::ShouldFail; - else if( tag == "!mayfail" ) - return TestCaseInfo::MayFail; - else - return TestCaseInfo::None; - } - inline bool isReservedTag( std::string const& tag ) { - return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] ); - } - inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { - if( isReservedTag( tag ) ) { - { - Colour colourGuard( Colour::Red ); - Catch::cerr() - << "Tag name [" << tag << "] not allowed.\n" - << "Tag names starting with non alpha-numeric characters are reserved\n"; - } - { - Colour colourGuard( Colour::FileName ); - Catch::cerr() << _lineInfo << std::endl; - } - exit(1); - } - } - - TestCase makeTestCase( ITestCase* _testCase, - std::string const& _className, - std::string const& _name, - std::string const& _descOrTags, - SourceLineInfo const& _lineInfo ) - { - bool isHidden( startsWith( _name, "./" ) ); // Legacy support - - // Parse out tags - std::set tags; - std::string desc, tag; - bool inTag = false; - for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { - char c = _descOrTags[i]; - if( !inTag ) { - if( c == '[' ) - inTag = true; - else - desc += c; - } - else { - if( c == ']' ) { - TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); - if( prop == TestCaseInfo::IsHidden ) - isHidden = true; - else if( prop == TestCaseInfo::None ) - enforceNotReservedTag( tag, _lineInfo ); - - tags.insert( tag ); - tag.clear(); - inTag = false; - } - else - tag += c; - } - } - if( isHidden ) { - tags.insert( "hide" ); - tags.insert( "." ); - } - - TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); - return TestCase( _testCase, info ); - } - - void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ) - { - testCaseInfo.tags = tags; - testCaseInfo.lcaseTags.clear(); - - std::ostringstream oss; - for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { - oss << "[" << *it << "]"; - std::string lcaseTag = toLower( *it ); - testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); - testCaseInfo.lcaseTags.insert( lcaseTag ); - } - testCaseInfo.tagsAsString = oss.str(); - } - - TestCaseInfo::TestCaseInfo( std::string const& _name, - std::string const& _className, - std::string const& _description, - std::set const& _tags, - SourceLineInfo const& _lineInfo ) - : name( _name ), - className( _className ), - description( _description ), - lineInfo( _lineInfo ), - properties( None ) - { - setTags( *this, _tags ); - } - - TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) - : name( other.name ), - className( other.className ), - description( other.description ), - tags( other.tags ), - lcaseTags( other.lcaseTags ), - tagsAsString( other.tagsAsString ), - lineInfo( other.lineInfo ), - properties( other.properties ) - {} - - bool TestCaseInfo::isHidden() const { - return ( properties & IsHidden ) != 0; - } - bool TestCaseInfo::throws() const { - return ( properties & Throws ) != 0; - } - bool TestCaseInfo::okToFail() const { - return ( properties & (ShouldFail | MayFail ) ) != 0; - } - bool TestCaseInfo::expectedToFail() const { - return ( properties & (ShouldFail ) ) != 0; - } - - TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} - - TestCase::TestCase( TestCase const& other ) - : TestCaseInfo( other ), - test( other.test ) - {} - - TestCase TestCase::withName( std::string const& _newName ) const { - TestCase other( *this ); - other.name = _newName; - return other; - } - - void TestCase::swap( TestCase& other ) { - test.swap( other.test ); - name.swap( other.name ); - className.swap( other.className ); - description.swap( other.description ); - tags.swap( other.tags ); - lcaseTags.swap( other.lcaseTags ); - tagsAsString.swap( other.tagsAsString ); - std::swap( TestCaseInfo::properties, static_cast( other ).properties ); - std::swap( lineInfo, other.lineInfo ); - } - - void TestCase::invoke() const { - test->invoke(); - } - - bool TestCase::operator == ( TestCase const& other ) const { - return test.get() == other.test.get() && - name == other.name && - className == other.className; - } - - bool TestCase::operator < ( TestCase const& other ) const { - return name < other.name; - } - TestCase& TestCase::operator = ( TestCase const& other ) { - TestCase temp( other ); - swap( temp ); - return *this; - } - - TestCaseInfo const& TestCase::getTestCaseInfo() const - { - return *this; - } - -} // end namespace Catch - -// #included from: catch_version.hpp -#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED - -namespace Catch { - - Version::Version - ( unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - std::string const& _branchName, - unsigned int _buildNumber ) - : majorVersion( _majorVersion ), - minorVersion( _minorVersion ), - patchNumber( _patchNumber ), - branchName( _branchName ), - buildNumber( _buildNumber ) - {} - - std::ostream& operator << ( std::ostream& os, Version const& version ) { - os << version.majorVersion << "." - << version.minorVersion << "." - << version.patchNumber; - - if( !version.branchName.empty() ) { - os << "-" << version.branchName - << "." << version.buildNumber; - } + std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } - Version libraryVersion( 1, 5, 7, "", 0 ); +} // end namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_console_colour.cpp +// start catch_context.cpp + +namespace Catch { + + class Context : public IMutableContext, NonCopyable { + + public: // IContext + virtual IResultCapture* getResultCapture() override { + return m_resultCapture; + } + virtual IRunner* getRunner() override { + return m_runner; + } + + virtual IConfigPtr const& getConfig() const override { + return m_config; + } + + virtual ~Context() override; + + public: // IMutableContext + virtual void setResultCapture( IResultCapture* resultCapture ) override { + m_resultCapture = resultCapture; + } + virtual void setRunner( IRunner* runner ) override { + m_runner = runner; + } + virtual void setConfig( IConfigPtr const& config ) override { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IConfigPtr m_config; + IRunner* m_runner = nullptr; + IResultCapture* m_resultCapture = nullptr; + }; + + IMutableContext *IMutableContext::currentContext = nullptr; + + void IMutableContext::createContext() + { + currentContext = new Context(); + } + + void cleanUpContext() { + delete IMutableContext::currentContext; + IMutableContext::currentContext = nullptr; + } + IContext::~IContext() = default; + IMutableContext::~IMutableContext() = default; + Context::~Context() = default; +} +// end catch_context.cpp +// start catch_debug_console.cpp + +// start catch_debug_console.h + +#include + +namespace Catch { + void writeToDebugConsole( std::string const& text ); +} + +// end catch_debug_console.h +#ifdef CATCH_PLATFORM_WINDOWS + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } +#else + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } + } +#endif // Platform +// end catch_debug_console.cpp +// start catch_debugger.cpp + +#ifdef CATCH_PLATFORM_MAC + +# include +# include +# include +# include +# include +# include +# include + +namespace Catch { + + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ + + int mib[4]; + struct kinfo_proc info; + std::size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + } // namespace Catch + +#elif defined(CATCH_PLATFORM_LINUX) + #include + #include + + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } + } // namespace Catch +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + bool isDebuggerActive() { return false; } + } +#endif // Platform +// end catch_debugger.cpp +// start catch_decomposer.cpp + +namespace Catch { + + ITransientExpression::~ITransientExpression() = default; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { + if( lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ) + os << lhs << " " << op << " " << rhs; + else + os << lhs << "\n" << op << "\n" << rhs; + } +} +// end catch_decomposer.cpp +// start catch_errno_guard.cpp + +#include + +namespace Catch { + ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} + ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } +} +// end catch_errno_guard.cpp +// start catch_exception_translator_registry.cpp + +// start catch_exception_translator_registry.h + +#include +#include +#include + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry(); + virtual void registerTranslator( const IExceptionTranslator* translator ); + virtual std::string translateActiveException() const override; + std::string tryTranslators() const; + + private: + std::vector> m_translators; + }; +} + +// end catch_exception_translator_registry.h +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { + } + + void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( std::unique_ptr( translator ) ); + } + + std::string ExceptionTranslatorRegistry::translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException *exception) { + return Catch::Detail::stringify( [exception description] ); + } +#else + return tryTranslators(); +#endif + } + catch( TestFailureException& ) { + std::rethrow_exception(std::current_exception()); + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } + } + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + if( m_translators.empty() ) + std::rethrow_exception(std::current_exception()); + else + return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); + } +} +// end catch_exception_translator_registry.cpp +// start catch_fatal_condition.cpp + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +namespace { + // Report the error condition + void reportFatal( char const * const message ) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); + } +} + +#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// + +# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + void FatalConditionHandler::reset() {} +} + +# else // CATCH_CONFIG_WINDOWS_SEH is defined + +namespace Catch { + struct SignalDefs { DWORD id; const char* name; }; + + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + static SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + + LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (auto const& def : signalDefs) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { + reportFatal(def.name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + // 32k seems enough for Catch to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; + exceptionHandlerHandle = nullptr; + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); + } + + void FatalConditionHandler::reset() { + if (isSet) { + // Unregister handler and restore the old guarantee + RemoveVectoredExceptionHandler(exceptionHandlerHandle); + SetThreadStackGuarantee(&guaranteeSize); + exceptionHandlerHandle = nullptr; + isSet = false; + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + +bool FatalConditionHandler::isSet = false; +ULONG FatalConditionHandler::guaranteeSize = 0; +PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; + +} // namespace Catch + +# endif // CATCH_CONFIG_WINDOWS_SEH + +#else // Not Windows - assumed to be POSIX compatible ////////////////////////// + +# if !defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace Catch { + void FatalConditionHandler::reset() {} +} + +# else // CATCH_CONFIG_POSIX_SIGNALS is defined + +#include + +namespace Catch { + + struct SignalDefs { + int id; + const char* name; + }; + static SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, + { SIGFPE, "SIGFPE - Floating point error signal" }, + { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, + { SIGTERM, "SIGTERM - Termination request signal" }, + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; + + void FatalConditionHandler::handleSignal( int sig ) { + char const * name = ""; + for (auto const& def : signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise( sig ); + } + + FatalConditionHandler::FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = SIGSTKSZ; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } + + FatalConditionHandler::~FatalConditionHandler() { + reset(); + } + + void FatalConditionHandler::reset() { + if( isSet ) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + isSet = false; + } + } + + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; + +} // namespace Catch + +# endif // CATCH_CONFIG_POSIX_SIGNALS + +#endif // not Windows + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +// end catch_fatal_condition.cpp +// start catch_interfaces_capture.cpp + +namespace Catch { + IResultCapture::~IResultCapture() = default; +} +// end catch_interfaces_capture.cpp +// start catch_interfaces_config.cpp + +namespace Catch { + IConfig::~IConfig() = default; +} +// end catch_interfaces_config.cpp +// start catch_interfaces_exception.cpp + +namespace Catch { + IExceptionTranslator::~IExceptionTranslator() = default; + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; +} +// end catch_interfaces_exception.cpp +// start catch_interfaces_registry_hub.cpp + +namespace Catch { + IRegistryHub::~IRegistryHub() = default; + IMutableRegistryHub::~IMutableRegistryHub() = default; +} +// end catch_interfaces_registry_hub.cpp +// start catch_interfaces_reporter.cpp + +// start catch_reporter_multi.h + +namespace Catch { + + class MultipleReporters : public IStreamingReporter { + using Reporters = std::vector; + Reporters m_reporters; + + public: + void add( IStreamingReporterPtr&& reporter ); + + public: // IStreamingReporter + + ReporterPreferences getPreferences() const override; + + void noMatchingTestCases( std::string const& spec ) override; + + static std::set getSupportedVerbosities(); + + void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; + void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override; + + void testRunStarting( TestRunInfo const& testRunInfo ) override; + void testGroupStarting( GroupInfo const& groupInfo ) override; + void testCaseStarting( TestCaseInfo const& testInfo ) override; + void sectionStarting( SectionInfo const& sectionInfo ) override; + void assertionStarting( AssertionInfo const& assertionInfo ) override; + + // The return value indicates if the messages buffer should be cleared: + bool assertionEnded( AssertionStats const& assertionStats ) override; + void sectionEnded( SectionStats const& sectionStats ) override; + void testCaseEnded( TestCaseStats const& testCaseStats ) override; + void testGroupEnded( TestGroupStats const& testGroupStats ) override; + void testRunEnded( TestRunStats const& testRunStats ) override; + + void skipTest( TestCaseInfo const& testInfo ) override; + bool isMulti() const override; + + }; + +} // end namespace Catch + +// end catch_reporter_multi.h +namespace Catch { + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& ReporterConfig::stream() const { return *m_stream; } + IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } + + TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {} + + GroupInfo::GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + AssertionStats::AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; + + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); + } + } + + AssertionStats::~AssertionStats() = default; + + SectionStats::SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + + SectionStats::~SectionStats() = default; + + TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + + TestCaseStats::~TestCaseStats() = default; + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + + TestGroupStats::~TestGroupStats() = default; + + TestRunStats::TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + + TestRunStats::~TestRunStats() = default; + + void IStreamingReporter::fatalErrorEncountered( StringRef ) {} + bool IStreamingReporter::isMulti() const { return false; } + + IReporterFactory::~IReporterFactory() = default; + IReporterRegistry::~IReporterRegistry() = default; + + void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ) { + + if( !existingReporter ) { + existingReporter = std::move( additionalReporter ); + return; + } + + MultipleReporters* multi = nullptr; + + if( existingReporter->isMulti() ) { + multi = static_cast( existingReporter.get() ); + } + else { + auto newMulti = std::unique_ptr( new MultipleReporters ); + newMulti->add( std::move( existingReporter ) ); + multi = newMulti.get(); + existingReporter = std::move( newMulti ); + } + multi->add( std::move( additionalReporter ) ); + } + +} // end namespace Catch +// end catch_interfaces_reporter.cpp +// start catch_interfaces_runner.cpp + +namespace Catch { + IRunner::~IRunner() = default; +} +// end catch_interfaces_runner.cpp +// start catch_interfaces_testcase.cpp + +namespace Catch { + ITestInvoker::~ITestInvoker() = default; + ITestCaseRegistry::~ITestCaseRegistry() = default; +} +// end catch_interfaces_testcase.cpp +// start catch_leak_detector.cpp + +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include + +namespace Catch { + + LeakDetector::LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +} + +#else + + Catch::LeakDetector::LeakDetector() {} + +#endif +// end catch_leak_detector.cpp +// start catch_list.cpp + +// start catch_list.h + +#include + +namespace Catch { + + std::size_t listTests( Config const& config ); + + std::size_t listTestsNamesOnly( Config const& config ); + + struct TagInfo { + void add( std::string const& spelling ); + std::string all() const; + + std::set spellings; + std::size_t count = 0; + }; + + std::size_t listTags( Config const& config ); + + std::size_t listReporters( Config const& /*config*/ ); + + Option list( Config const& config ); + +} // end namespace Catch + +// end catch_list.h +// start catch_text.h + +namespace Catch { + using namespace clara::TextFlow; +} + +// end catch_text.h +#include +#include +#include + +namespace Catch { + + std::size_t listTests( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n"; + if( config.verbosity() >= Verbosity::High ) { + Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl; + std::string description = testCaseInfo.description; + if( description.empty() ) + description = "(NO DESCRIPTION)"; + Catch::cout() << Column( description ).indent(4) << std::endl; + } + if( !testCaseInfo.tags.empty() ) + Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; + } + + if( !config.testSpec().hasFilters() ) + Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; + else + Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; + return matchedTestCases.size(); + } + + std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( !config.testSpec().hasFilters() ) + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCaseInfo : matchedTestCases ) { + matchedTests++; + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"'; + else + Catch::cout() << testCaseInfo.name; + if ( config.verbosity() >= Verbosity::High ) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; + } + return matchedTests; + } + + void TagInfo::add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); + } + + std::string TagInfo::all() const { + std::string out; + for( auto const& spelling : spellings ) + out += "[" + spelling + "]"; + return out; + } + + std::size_t listTags( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + std::map tagCounts; + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( auto const& testCase : matchedTestCases ) { + for( auto const& tagName : testCase.getTestCaseInfo().tags ) { + std::string lcaseTagName = toLower( tagName ); + auto countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( auto const& tagCount : tagCounts ) { + ReusableStringStream rss; + rss << " " << std::setw(2) << tagCount.second.count << " "; + auto str = rss.str(); + auto wrapper = Column( tagCount.second.all() ) + .initialIndent( 0 ) + .indent( str.size() ) + .width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); + Catch::cout() << str << wrapper << '\n'; + } + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; + return tagCounts.size(); + } + + std::size_t listReporters( Config const& /*config*/ ) { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + std::size_t maxNameLen = 0; + for( auto const& factoryKvp : factories ) + maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() ); + + for( auto const& factoryKvp : factories ) { + Catch::cout() + << Column( factoryKvp.first + ":" ) + .indent(2) + .width( 5+maxNameLen ) + + Column( factoryKvp.second->getDescription() ) + .initialIndent(0) + .indent(2) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) + << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); + } + + Option list( Config const& config ) { + Option listedCount; + if( config.listTests() ) + listedCount = listedCount.valueOr(0) + listTests( config ); + if( config.listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); + if( config.listTags() ) + listedCount = listedCount.valueOr(0) + listTags( config ); + if( config.listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters( config ); + return listedCount; + } + +} // end namespace Catch +// end catch_list.cpp +// start catch_matchers.cpp + +namespace Catch { +namespace Matchers { + namespace Impl { + + std::string MatcherUntypedBase::toString() const { + if( m_cachedToString.empty() ) + m_cachedToString = describe(); + return m_cachedToString; + } + + MatcherUntypedBase::~MatcherUntypedBase() = default; + + } // namespace Impl +} // namespace Matchers + +using namespace Matchers; +using Matchers::Impl::MatcherBase; + +} // namespace Catch +// end catch_matchers.cpp +// start catch_matchers_floating.cpp + +#include +#include +#include +#include + +namespace Catch { +namespace Matchers { +namespace Floating { +enum class FloatingPointKind : uint8_t { + Float, + Double +}; +} +} +} + +namespace { + +template +struct Converter; + +template <> +struct Converter { + static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); + Converter(float f) { + std::memcpy(&i, &f, sizeof(f)); + } + int32_t i; +}; + +template <> +struct Converter { + static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); + Converter(double d) { + std::memcpy(&i, &d, sizeof(d)); + } + int64_t i; +}; + +template +auto convert(T t) -> Converter { + return Converter(t); +} + +template +bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) { + // Comparison with NaN should always be false. + // This way we can rule it out before getting into the ugly details + if (std::isnan(lhs) || std::isnan(rhs)) { + return false; + } + + auto lc = convert(lhs); + auto rc = convert(rhs); + + if ((lc.i < 0) != (rc.i < 0)) { + // Potentially we can have +0 and -0 + return lhs == rhs; + } + + auto ulpDiff = std::abs(lc.i - rc.i); + return ulpDiff <= maxUlpDiff; +} } -// #included from: catch_message.hpp -#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED +namespace Catch { +namespace Matchers { +namespace Floating { + WithinAbsMatcher::WithinAbsMatcher(double target, double margin) + :m_target{ target }, m_margin{ margin } { + if (m_margin < 0) { + throw std::domain_error("Allowed margin difference has to be >= 0"); + } + } + + // Performs equivalent check of std::fabs(lhs - rhs) <= margin + // But without the subtraction to allow for INFINITY in comparison + bool WithinAbsMatcher::match(double const& matchee) const { + return (matchee + m_margin >= m_target) && (m_target + m_margin >= m_margin); + } + + std::string WithinAbsMatcher::describe() const { + return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); + } + + WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType) + :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { + if (m_ulps < 0) { + throw std::domain_error("Allowed ulp difference has to be >= 0"); + } + } + + bool WithinUlpsMatcher::match(double const& matchee) const { + switch (m_type) { + case FloatingPointKind::Float: + return almostEqualUlps(static_cast(matchee), static_cast(m_target), m_ulps); + case FloatingPointKind::Double: + return almostEqualUlps(matchee, m_target, m_ulps); + default: + throw std::domain_error("Unknown FloatingPointKind value"); + } + } + + std::string WithinUlpsMatcher::describe() const { + return "is within " + std::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : ""); + } + +}// namespace Floating + +Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); +} + +Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); +} + +Floating::WithinAbsMatcher WithinAbs(double target, double margin) { + return Floating::WithinAbsMatcher(target, margin); +} + +} // namespace Matchers +} // namespace Catch + +// end catch_matchers_floating.cpp +// start catch_matchers_string.cpp + +#include + +namespace Catch { +namespace Matchers { + + namespace StdString { + + CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string CasedString::adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; + } + std::string CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : std::string(); + } + + StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) + : m_comparator( comparator ), + m_operation( operation ) { + } + + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} + + bool EqualsMatcher::match( std::string const& source ) const { + return m_comparator.adjustString( source ) == m_comparator.m_str; + } + + ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} + + bool ContainsMatcher::match( std::string const& source ) const { + return contains( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} + + bool StartsWithMatcher::match( std::string const& source ) const { + return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} + + bool EndsWithMatcher::match( std::string const& source ) const { + return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + } + + RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {} + + bool RegexMatcher::match(std::string const& matchee) const { + auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway + if (m_caseSensitivity == CaseSensitive::Choice::No) { + flags |= std::regex::icase; + } + auto reg = std::regex(m_regex, flags); + return std::regex_match(matchee, reg); + } + + std::string RegexMatcher::describe() const { + return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively"); + } + + } // namespace StdString + + StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { + return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + } + + StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) { + return StdString::RegexMatcher(regex, caseSensitivity); + } + +} // namespace Matchers +} // namespace Catch +// end catch_matchers_string.cpp +// start catch_message.cpp namespace Catch { @@ -7596,221 +7961,1449 @@ namespace Catch { sequence( ++globalCount ) {} + bool MessageInfo::operator==( MessageInfo const& other ) const { + return sequence == other.sequence; + } + + bool MessageInfo::operator<( MessageInfo const& other ) const { + return sequence < other.sequence; + } + // This may need protecting if threading support is added unsigned int MessageInfo::globalCount = 0; //////////////////////////////////////////////////////////////////////////// + Catch::MessageBuilder::MessageBuilder( std::string const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + :m_info(macroName, lineInfo, type) {} + + //////////////////////////////////////////////////////////////////////////// + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) : m_info( builder.m_info ) { m_info.message = builder.m_stream.str(); getResultCapture().pushScopedMessage( m_info ); } - ScopedMessage::ScopedMessage( ScopedMessage const& other ) - : m_info( other.m_info ) - {} +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 +#endif ScopedMessage::~ScopedMessage() { - getResultCapture().popScopedMessage( m_info ); + if ( !std::uncaught_exception() ){ + getResultCapture().popScopedMessage(m_info); + } } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +} // end namespace Catch +// end catch_message.cpp +// start catch_random_number_generator.cpp + +// start catch_random_number_generator.h + +#include + +namespace Catch { + + struct IConfig; + + void seedRng( IConfig const& config ); + + unsigned int rngSeed(); + + struct RandomNumberGenerator { + using result_type = unsigned int; + + static constexpr result_type (min)() { return 0; } + static constexpr result_type (max)() { return 1000000; } + + result_type operator()( result_type n ) const; + result_type operator()() const; + + template + static void shuffle( V& vector ) { + RandomNumberGenerator rng; + std::shuffle( vector.begin(), vector.end(), rng ); + } + }; + +} + +// end catch_random_number_generator.h +#include + +namespace Catch { + + void seedRng( IConfig const& config ) { + if( config.rngSeed() != 0 ) + std::srand( config.rngSeed() ); + } + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } + + RandomNumberGenerator::result_type RandomNumberGenerator::operator()( result_type n ) const { + return std::rand() % n; + } + RandomNumberGenerator::result_type RandomNumberGenerator::operator()() const { + return std::rand() % (max)(); + } + +} +// end catch_random_number_generator.cpp +// start catch_registry_hub.cpp + +// start catch_test_case_registry_impl.h + +#include +#include +#include +#include + +namespace Catch { + + class TestCase; + struct IConfig; + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + + void enforceNoDuplicateTestCases( std::vector const& functions ); + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + + class TestRegistry : public ITestCaseRegistry { + public: + virtual ~TestRegistry() = default; + + virtual void registerTest( TestCase const& testCase ); + + std::vector const& getAllTests() const override; + std::vector const& getAllTestsSorted( IConfig const& config ) const override; + + private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; + mutable std::vector m_sortedFunctions; + std::size_t m_unnamedCount = 0; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; + + /////////////////////////////////////////////////////////////////////////// + + class TestInvokerAsFunction : public ITestInvoker { + void(*m_testAsFunction)(); + public: + TestInvokerAsFunction( void(*testAsFunction)() ) noexcept; + + void invoke() const override; + }; + + std::string extractClassName( std::string const& classOrQualifiedMethodName ); + + /////////////////////////////////////////////////////////////////////////// } // end namespace Catch -// #included from: catch_legacy_reporter_adapter.hpp -#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED +// end catch_test_case_registry_impl.h +// start catch_reporter_registry.h -// #included from: catch_legacy_reporter_adapter.h -#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED +#include -namespace Catch -{ - // Deprecated - struct IReporter : IShared { - virtual ~IReporter(); +namespace Catch { - virtual bool shouldRedirectStdout() const = 0; + class ReporterRegistry : public IReporterRegistry { - virtual void StartTesting() = 0; - virtual void EndTesting( Totals const& totals ) = 0; - virtual void StartGroup( std::string const& groupName ) = 0; - virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; - virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; - virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; - virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; - virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; - virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; - virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; - virtual void Aborted() = 0; - virtual void Result( AssertionResult const& result ) = 0; - }; - - class LegacyReporterAdapter : public SharedImpl - { public: - LegacyReporterAdapter( Ptr const& legacyReporter ); - virtual ~LegacyReporterAdapter(); - virtual ReporterPreferences getPreferences() const; - virtual void noMatchingTestCases( std::string const& ); - virtual void testRunStarting( TestRunInfo const& ); - virtual void testGroupStarting( GroupInfo const& groupInfo ); - virtual void testCaseStarting( TestCaseInfo const& testInfo ); - virtual void sectionStarting( SectionInfo const& sectionInfo ); - virtual void assertionStarting( AssertionInfo const& ); - virtual bool assertionEnded( AssertionStats const& assertionStats ); - virtual void sectionEnded( SectionStats const& sectionStats ); - virtual void testCaseEnded( TestCaseStats const& testCaseStats ); - virtual void testGroupEnded( TestGroupStats const& testGroupStats ); - virtual void testRunEnded( TestRunStats const& testRunStats ); - virtual void skipTest( TestCaseInfo const& ); + ~ReporterRegistry() override; + + IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override; + + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ); + void registerListener( IReporterFactoryPtr const& factory ); + + FactoryMap const& getFactories() const override; + Listeners const& getListeners() const override; private: - Ptr m_legacyReporter; + FactoryMap m_factories; + Listeners m_listeners; }; } -namespace Catch -{ - LegacyReporterAdapter::LegacyReporterAdapter( Ptr const& legacyReporter ) - : m_legacyReporter( legacyReporter ) - {} - LegacyReporterAdapter::~LegacyReporterAdapter() {} +// end catch_reporter_registry.h +// start catch_tag_alias_registry.h - ReporterPreferences LegacyReporterAdapter::getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); - return prefs; - } +// start catch_tag_alias.h - void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} - void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { - m_legacyReporter->StartTesting(); - } - void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { - m_legacyReporter->StartGroup( groupInfo.name ); - } - void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { - m_legacyReporter->StartTestCase( testInfo ); - } - void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { - m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); - } - void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { - // Not on legacy interface - } +#include - bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { - if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { - for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { - if( it->type == ResultWas::Info ) { - ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal ); - rb << it->message; - rb.setResultType( ResultWas::Info ); - AssertionResult result = rb.build(); - m_legacyReporter->Result( result ); - } +namespace Catch { + + struct TagAlias { + TagAlias(std::string const& _tag, SourceLineInfo _lineInfo); + + std::string tag; + SourceLineInfo lineInfo; + }; + +} // end namespace Catch + +// end catch_tag_alias.h +#include + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + ~TagAliasRegistry() override; + TagAlias const* find( std::string const& alias ) const override; + std::string expandAliases( std::string const& unexpandedTestSpec ) const override; + void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); + + private: + std::map m_registry; + }; + +} // end namespace Catch + +// end catch_tag_alias_registry.h +// start catch_startup_exception_registry.h + +#include +#include + +namespace Catch { + + class StartupExceptionRegistry { + public: + void add(std::exception_ptr const& exception) noexcept; + std::vector const& getExceptions() const noexcept; + private: + std::vector m_exceptions; + }; + +} // end namespace Catch + +// end catch_startup_exception_registry.h +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub, + private NonCopyable { + + public: // IRegistryHub + RegistryHub() = default; + IReporterRegistry const& getReporterRegistry() const override { + return m_reporterRegistry; } + ITestCaseRegistry const& getTestCaseRegistry() const override { + return m_testCaseRegistry; + } + IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() override { + return m_exceptionTranslatorRegistry; + } + ITagAliasRegistry const& getTagAliasRegistry() const override { + return m_tagAliasRegistry; + } + StartupExceptionRegistry const& getStartupExceptionRegistry() const override { + return m_exceptionRegistry; + } + + public: // IMutableRegistryHub + void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerReporter( name, factory ); + } + void registerListener( IReporterFactoryPtr const& factory ) override { + m_reporterRegistry.registerListener( factory ); + } + void registerTest( TestCase const& testInfo ) override { + m_testCaseRegistry.registerTest( testInfo ); + } + void registerTranslator( const IExceptionTranslator* translator ) override { + m_exceptionTranslatorRegistry.registerTranslator( translator ); + } + void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { + m_tagAliasRegistry.add( alias, tag, lineInfo ); + } + void registerStartupException() noexcept override { + m_exceptionRegistry.add(std::current_exception()); + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; + }; + + // Single, global, instance + RegistryHub*& getTheRegistryHub() { + static RegistryHub* theRegistryHub = nullptr; + if( !theRegistryHub ) + theRegistryHub = new RegistryHub(); + return theRegistryHub; } - m_legacyReporter->Result( assertionStats.assertionResult ); + } + + IRegistryHub& getRegistryHub() { + return *getTheRegistryHub(); + } + IMutableRegistryHub& getMutableRegistryHub() { + return *getTheRegistryHub(); + } + void cleanUp() { + delete getTheRegistryHub(); + getTheRegistryHub() = nullptr; + cleanUpContext(); + ReusableStringStream::cleanup(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } + +} // end namespace Catch +// end catch_registry_hub.cpp +// start catch_reporter_registry.cpp + +namespace Catch { + + ReporterRegistry::~ReporterRegistry() = default; + + IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const { + auto it = m_factories.find( name ); + if( it == m_factories.end() ) + return nullptr; + return it->second->create( ReporterConfig( config ) ); + } + + void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) { + m_factories.emplace(name, factory); + } + void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) { + m_listeners.push_back( factory ); + } + + IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { + return m_factories; + } + IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { + return m_listeners; + } + +} +// end catch_reporter_registry.cpp +// start catch_result_type.cpp + +namespace Catch { + + bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } + + ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | static_cast( rhs ) ); + } + + bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch +// end catch_result_type.cpp +// start catch_run_context.cpp + +#include +#include +#include + +namespace Catch { + + class RedirectedStream { + std::ostream& m_originalStream; + std::ostream& m_redirectionStream; + std::streambuf* m_prevBuf; + + public: + RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) + : m_originalStream( originalStream ), + m_redirectionStream( redirectionStream ), + m_prevBuf( m_originalStream.rdbuf() ) + { + m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); + } + ~RedirectedStream() { + m_originalStream.rdbuf( m_prevBuf ); + } + }; + + class RedirectedStdOut { + ReusableStringStream m_rss; + RedirectedStream m_cout; + public: + RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} + auto str() const -> std::string { return m_rss.str(); } + }; + + // StdErr has two constituent streams in C++, std::cerr and std::clog + // This means that we need to redirect 2 streams into 1 to keep proper + // order of writes + class RedirectedStdErr { + ReusableStringStream m_rss; + RedirectedStream m_cerr; + RedirectedStream m_clog; + public: + RedirectedStdErr() + : m_cerr( Catch::cerr(), m_rss.get() ), + m_clog( Catch::clog(), m_rss.get() ) + {} + auto str() const -> std::string { return m_rss.str(); } + }; + + RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) + : m_runInfo(_config->name()), + m_context(getCurrentMutableContext()), + m_config(_config), + m_reporter(std::move(reporter)), + m_lastAssertionInfo{ "", SourceLineInfo("",0), "", ResultDisposition::Normal }, + m_includeSuccessfulResults( m_config->includeSuccessfulResults() ) + { + m_context.setRunner(this); + m_context.setConfig(m_config); + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + } + + RunContext::~RunContext() { + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } + + void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); + } + + void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); + } + + Totals RunContext::runTest(TestCase const& testCase) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + TestCaseInfo testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting(testInfo); + + m_activeTestCase = &testCase; + + ITracker& rootTracker = m_trackerContext.startRun(); + assert(rootTracker.isSectionTracker()); + static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); + runCurrentTest(redirectedCout, redirectedCerr); + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting())); + + m_activeTestCase = nullptr; + m_testCaseTracker = nullptr; + + return deltaTotals; + } + + IConfigPtr RunContext::config() const { + return m_config; + } + + IStreamingReporter& RunContext::reporter() const { + return *m_reporter; + } + + void RunContext::assertionEnded(AssertionResult const & result) { + if (result.getResultType() == ResultWas::Ok) { + m_totals.assertions.passed++; + m_lastAssertionPassed = true; + } else if (!result.isOk()) { + m_lastAssertionPassed = false; + if( m_activeTestCase->getTestCaseInfo().okToFail() ) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; + } + else { + m_lastAssertionPassed = true; + } + + // We have no use for the return value (whether messages should be cleared), because messages were made scoped + // and should be let to clear themselves out. + static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); + + // Reset working state + resetAssertionInfo(); + m_lastResult = result; + } + void RunContext::resetAssertionInfo() { + m_lastAssertionInfo.macroName = StringRef(); + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + } + + bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { + ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting(sectionInfo); + + assertions = m_totals.assertions; + return true; } - void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { - if( sectionStats.missingAssertions ) - m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); - m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); + + bool RunContext::testForMissingAssertions(Counts& assertions) { + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; } - void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { - m_legacyReporter->EndTestCase - ( testCaseStats.testInfo, - testCaseStats.totals, - testCaseStats.stdOut, - testCaseStats.stdErr ); + + void RunContext::sectionEnded(SectionEndInfo const & endInfo) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); + m_messages.clear(); } - void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { - if( testGroupStats.aborting ) - m_legacyReporter->Aborted(); - m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); + + void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { + if (m_unfinishedSections.empty()) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(endInfo); } - void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { - m_legacyReporter->EndTesting( testRunStats.totals ); + void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { + m_reporter->benchmarkStarting( info ); } - void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) { + void RunContext::benchmarkEnded( BenchmarkStats const& stats ) { + m_reporter->benchmarkEnded( stats ); + } + + void RunContext::pushScopedMessage(MessageInfo const & message) { + m_messages.push_back(message); + } + + void RunContext::popScopedMessage(MessageInfo const & message) { + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + } + + std::string RunContext::getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : std::string(); + } + + const AssertionResult * RunContext::getLastResult() const { + return &(*m_lastResult); + } + + void RunContext::exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + + void RunContext::handleFatalErrorCondition( StringRef message ) { + // First notify reporter that bad things happened + m_reporter->fatalErrorEncountered(message); + + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); + tempResult.message = message; + AssertionResult result(m_lastAssertionInfo, tempResult); + + assertionEnded(result); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); + m_reporter->sectionEnded(testCaseSectionStats); + + auto const& testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + std::string(), + std::string(), + false)); + m_totals.testCases.failed++; + testGroupEnded(std::string(), m_totals, 1, 1); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + } + + bool RunContext::lastAssertionPassed() { + return m_lastAssertionPassed; + } + + void RunContext::assertionPassed() { + m_lastAssertionPassed = true; + ++m_totals.assertions.passed; + resetAssertionInfo(); + } + + bool RunContext::aborting() const { + return m_totals.assertions.failed == static_cast(m_config->abortAfter()); + } + + void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { + auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); + m_reporter->sectionStarting(testCaseSection); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + m_lastAssertionInfo = { "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal }; + + seedRng(*m_config); + + Timer timer; + try { + if (m_reporter->getPreferences().shouldRedirectStdOut) { + RedirectedStdOut redirectedStdOut; + RedirectedStdErr redirectedStdErr; + timer.start(); + invokeActiveTestCase(); + redirectedCout += redirectedStdOut.str(); + redirectedCerr += redirectedStdErr.str(); + + } else { + timer.start(); + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } catch (TestFailureException&) { + // This just means the test was aborted due to failure + } catch (...) { + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if( m_shouldReportUnexpected ) { + AssertionReaction dummyReaction; + handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction ); + } + } + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void RunContext::invokeActiveTestCase() { + FatalConditionHandler fatalConditionHandler; // Handle signals + m_activeTestCase->invoke(); + fatalConditionHandler.reset(); + } + + void RunContext::handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it) + sectionEnded(*it); + m_unfinishedSections.clear(); + } + + void RunContext::handleExpr( + AssertionInfo const& info, + ITransientExpression const& expr, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + bool negated = isFalseTest( info.resultDisposition ); + bool result = expr.getResult() != negated; + + if( result ) { + if (!m_includeSuccessfulResults) { + assertionPassed(); + } + else { + reportExpr(info, ResultWas::Ok, &expr, negated); + } + } + else { + reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); + populateReaction( reaction ); + } + } + void RunContext::reportExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated ) { + + m_lastAssertionInfo = info; + AssertionResultData data( resultType, LazyExpression( negated ) ); + + AssertionResult assertionResult{ info, data }; + assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + + assertionEnded( assertionResult ); + } + + void RunContext::handleMessage( + AssertionInfo const& info, + ResultWas::OfType resultType, + StringRef const& message, + AssertionReaction& reaction + ) { + m_reporter->assertionStarting( info ); + + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ m_lastAssertionInfo, data }; + assertionEnded( assertionResult ); + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + void RunContext::handleUnexpectedExceptionNotThrown( + AssertionInfo const& info, + AssertionReaction& reaction + ) { + handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); + } + + void RunContext::handleUnexpectedInflightException( + AssertionInfo const& info, + std::string const& message, + AssertionReaction& reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = message; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + populateReaction( reaction ); + } + + void RunContext::populateReaction( AssertionReaction& reaction ) { + reaction.shouldDebugBreak = m_config->shouldDebugBreak(); + reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); + } + + void RunContext::handleIncomplete( + AssertionInfo const& info + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); + data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + } + void RunContext::handleNonExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data( resultType, LazyExpression( false ) ); + AssertionResult assertionResult{ info, data }; + assertionEnded( assertionResult ); + + if( !assertionResult.isOk() ) + populateReaction( reaction ); + } + + IResultCapture& getResultCapture() { + if (auto* capture = getCurrentContext().getResultCapture()) + return *capture; + else + CATCH_INTERNAL_ERROR("No result capture instance"); } } +// end catch_run_context.cpp +// start catch_section.cpp -// #included from: catch_timer.hpp +namespace Catch { -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++11-long-long" + Section::Section( SectionInfo const& info ) + : m_info( info ), + m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + m_timer.start(); + } + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17 +#endif + Section::~Section() { + if( m_sectionIncluded ) { + SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); + if( std::uncaught_exception() ) + getResultCapture().sectionEndedEarly( endInfo ); + else + getResultCapture().sectionEnded( endInfo ); + } + } +#if defined(_MSC_VER) +#pragma warning(pop) #endif -#ifdef CATCH_PLATFORM_WINDOWS -#include -#else -#include -#endif + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } + +} // end namespace Catch +// end catch_section.cpp +// start catch_section_info.cpp + +namespace Catch { + + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& _description ) + : name( _name ), + description( _description ), + lineInfo( _lineInfo ) + {} + + SectionEndInfo::SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) + : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) + {} + +} // end namespace Catch +// end catch_section_info.cpp +// start catch_session.cpp + +// start catch_session.h + +#include + +namespace Catch { + + class Session : NonCopyable { + public: + + Session(); + ~Session() override; + + void showHelp() const; + void libIdentify(); + + int applyCommandLine( int argc, char* argv[] ); + + void useConfigData( ConfigData const& configData ); + + int run( int argc, char* argv[] ); + #if defined(WIN32) && defined(UNICODE) + int run( int argc, wchar_t* const argv[] ); + #endif + int run(); + + clara::Parser const& cli() const; + void cli( clara::Parser const& newParser ); + ConfigData& configData(); + Config& config(); + private: + int runInternal(); + + clara::Parser m_cli; + ConfigData m_configData; + std::shared_ptr m_config; + bool m_startupExceptions = false; + }; + +} // end namespace Catch + +// end catch_session.h +// start catch_version.h + +#include + +namespace Catch { + + // Versioning information + struct Version { + Version( Version const& ) = delete; + Version& operator=( Version const& ) = delete; + Version( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ); + + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; + + // buildNumber is only used if branchName is not null + char const * const branchName; + unsigned int const buildNumber; + + friend std::ostream& operator << ( std::ostream& os, Version const& version ); + }; + + Version const& libraryVersion(); +} + +// end catch_version.h +#include +#include namespace Catch { namespace { -#ifdef CATCH_PLATFORM_WINDOWS - uint64_t getCurrentTicks() { - static uint64_t hz=0, hzo=0; - if (!hz) { - QueryPerformanceFrequency( reinterpret_cast( &hz ) ); - QueryPerformanceCounter( reinterpret_cast( &hzo ) ); + const int MaxExitCode = 255; + + IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { + auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); + CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); + + return reporter; + } + +#ifndef CATCH_CONFIG_DEFAULT_REPORTER +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + + IStreamingReporterPtr makeReporter(std::shared_ptr const& config) { + auto const& reporterNames = config->getReporterNames(); + if (reporterNames.empty()) + return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config); + + IStreamingReporterPtr reporter; + for (auto const& name : reporterNames) + addReporter(reporter, createReporter(name, config)); + return reporter; + } + +#undef CATCH_CONFIG_DEFAULT_REPORTER + + void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) { + auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const& listener : listeners) + addReporter(reporters, listener->create(Catch::ReporterConfig(config))); + } + + Catch::Totals runTests(std::shared_ptr const& config) { + IStreamingReporterPtr reporter = makeReporter(config); + addListeners(reporter, config); + + RunContext context(config, std::move(reporter)); + + Totals totals; + + context.testGroupStarting(config->name(), 1, 1); + + TestSpec testSpec = config->testSpec(); + if (!testSpec.hasFilters()) + testSpec = TestSpecParser(ITagAliasRegistry::get()).parse("~[.]").testSpec(); // All not hidden tests + + auto const& allTestCases = getAllTestCasesSorted(*config); + for (auto const& testCase : allTestCases) { + if (!context.aborting() && matchTest(testCase, testSpec, *config)) + totals += context.runTest(testCase); + else + context.reporter().skipTest(testCase); } - uint64_t t; - QueryPerformanceCounter( reinterpret_cast( &t ) ); - return ((t-hzo)*1000000)/hz; + + context.testGroupEnded(config->name(), totals, 1, 1); + return totals; } -#else - uint64_t getCurrentTicks() { - timeval t; - gettimeofday(&t,CATCH_NULL); - return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); + + void applyFilenamesAsTags(Catch::IConfig const& config) { + auto& tests = const_cast&>(getAllTestCasesSorted(config)); + for (auto& testCase : tests) { + auto tags = testCase.tags; + + std::string filename = testCase.lineInfo.file; + auto lastSlash = filename.find_last_of("\\/"); + if (lastSlash != std::string::npos) { + filename.erase(0, lastSlash); + filename[0] = '#'; + } + + auto lastDot = filename.find_last_of('.'); + if (lastDot != std::string::npos) { + filename.erase(lastDot); + } + + tags.push_back(std::move(filename)); + setTags(testCase, tags); + } } + + } // anon namespace + + Session::Session() { + static bool alreadyInstantiated = false; + if( alreadyInstantiated ) { + try { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } + catch(...) { getMutableRegistryHub().registerStartupException(); } + } + + const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); + if ( !exceptions.empty() ) { + m_startupExceptions = true; + Colour colourGuard( Colour::Red ); + Catch::cerr() << "Errors occured during startup!" << '\n'; + // iterate over all exceptions and notify user + for ( const auto& ex_ptr : exceptions ) { + try { + std::rethrow_exception(ex_ptr); + } catch ( std::exception const& ex ) { + Catch::cerr() << Column( ex.what() ).indent(2) << '\n'; + } + } + } + + alreadyInstantiated = true; + m_cli = makeCommandLineParser( m_configData ); + } + Session::~Session() { + Catch::cleanUp(); + } + + void Session::showHelp() const { + Catch::cout() + << "\nCatch v" << libraryVersion() << "\n" + << m_cli << std::endl + << "For more detailed usage please see the project docs\n" << std::endl; + } + void Session::libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; + } + + int Session::applyCommandLine( int argc, char* argv[] ) { + if( m_startupExceptions ) + return 1; + + auto result = m_cli.parse( clara::Args( argc, argv ) ); + if( !result ) { + Catch::cerr() + << Colour( Colour::Red ) + << "\nError(s) in input:\n" + << Column( result.errorMessage() ).indent( 2 ) + << "\n\n"; + Catch::cerr() << "Run with -? for usage\n" << std::endl; + return MaxExitCode; + } + + if( m_configData.showHelp ) + showHelp(); + if( m_configData.libIdentify ) + libIdentify(); + m_config.reset(); + return 0; + } + + void Session::useConfigData( ConfigData const& configData ) { + m_configData = configData; + m_config.reset(); + } + + int Session::run( int argc, char* argv[] ) { + if( m_startupExceptions ) + return 1; + int returnCode = applyCommandLine( argc, argv ); + if( returnCode == 0 ) + returnCode = run(); + return returnCode; + } + +#if defined(WIN32) && defined(UNICODE) + int Session::run( int argc, wchar_t* const argv[] ) { + + char **utf8Argv = new char *[ argc ]; + + for ( int i = 0; i < argc; ++i ) { + int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); + + utf8Argv[ i ] = new char[ bufSize ]; + + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); + } + + int returnCode = run( argc, utf8Argv ); + + for ( int i = 0; i < argc; ++i ) + delete [] utf8Argv[ i ]; + + delete [] utf8Argv; + + return returnCode; + } #endif + int Session::run() { + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before starting" << std::endl; + static_cast(std::getchar()); + } + int exitCode = runInternal(); + if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast(std::getchar()); + } + return exitCode; } - void Timer::start() { - m_ticks = getCurrentTicks(); + clara::Parser const& Session::cli() const { + return m_cli; } - unsigned int Timer::getElapsedMicroseconds() const { - return static_cast(getCurrentTicks() - m_ticks); + void Session::cli( clara::Parser const& newParser ) { + m_cli = newParser; } - unsigned int Timer::getElapsedMilliseconds() const { - return static_cast(getElapsedMicroseconds()/1000); + ConfigData& Session::configData() { + return m_configData; } - double Timer::getElapsedSeconds() const { - return getElapsedMicroseconds()/1000000.0; + Config& Session::config() { + if( !m_config ) + m_config = std::make_shared( m_configData ); + return *m_config; } -} // namespace Catch + int Session::runInternal() { + if( m_startupExceptions ) + return 1; -#ifdef __clang__ -#pragma clang diagnostic pop + if( m_configData.showHelp || m_configData.libIdentify ) + return 0; + + try + { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option listed = list( config() ) ) + return static_cast( *listed ); + + return (std::min)( MaxExitCode, static_cast( runTests( m_config ).assertions.failed ) ); + } + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return MaxExitCode; + } + } + +} // end namespace Catch +// end catch_session.cpp +// start catch_startup_exception_registry.cpp + +namespace Catch { + void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { + try { + m_exceptions.push_back(exception); + } + catch(...) { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); + } + } + + std::vector const& StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } + +} // end namespace Catch +// end catch_startup_exception_registry.cpp +// start catch_stream.cpp + +#include +#include +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" #endif -// #included from: catch_common.hpp -#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED + +namespace Catch { + + Catch::IStream::~IStream() = default; + + namespace detail { namespace { + template + class StreamBufImpl : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() noexcept { + StreamBufImpl::sync(); + } + + private: + int overflow( int c ) override { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast( c ) ) ); + else + sputc( static_cast( c ) ); + } + return 0; + } + + int sync() override { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class FileStream : public IStream { + mutable std::ofstream m_ofs; + public: + FileStream( StringRef filename ) { + m_ofs.open( filename.c_str() ); + CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); + } + ~FileStream() override = default; + public: // IStream + std::ostream& stream() const override { + return m_ofs; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + class CoutStream : public IStream { + mutable std::ostream m_os; + public: + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream() : m_os( Catch::cout().rdbuf() ) {} + ~CoutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + /////////////////////////////////////////////////////////////////////////// + + class DebugOutStream : public IStream { + std::unique_ptr> m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream() + : m_streamBuf( new StreamBufImpl() ), + m_os( m_streamBuf.get() ) + {} + + ~DebugOutStream() override = default; + + public: // IStream + std::ostream& stream() const override { return m_os; } + }; + + }} // namespace anon::detail + + /////////////////////////////////////////////////////////////////////////// + + auto makeStream( StringRef const &filename ) -> IStream const* { + if( filename.empty() ) + return new detail::CoutStream(); + else if( filename[0] == '%' ) { + if( filename == "%debug" ) + return new detail::DebugOutStream(); + else + CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); + } + else + return new detail::FileStream( filename ); + } + + // This class encapsulates the idea of a pool of ostringstreams that can be reused. + struct StringStreams { + std::vector> m_streams; + std::vector m_unused; + std::ostringstream m_referenceStream; // Used for copy state/ flags from + static StringStreams* s_instance; + + auto add() -> std::size_t { + if( m_unused.empty() ) { + m_streams.push_back( std::unique_ptr( new std::ostringstream ) ); + return m_streams.size()-1; + } + else { + auto index = m_unused.back(); + m_unused.pop_back(); + return index; + } + } + + void release( std::size_t index ) { + m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state + m_unused.push_back(index); + } + + // !TBD: put in TLS + static auto instance() -> StringStreams& { + if( !s_instance ) + s_instance = new StringStreams(); + return *s_instance; + } + static void cleanup() { + delete s_instance; + s_instance = nullptr; + } + }; + + StringStreams* StringStreams::s_instance = nullptr; + + void ReusableStringStream::cleanup() { + StringStreams::cleanup(); + } + + ReusableStringStream::ReusableStringStream() + : m_index( StringStreams::instance().add() ), + m_oss( StringStreams::instance().m_streams[m_index].get() ) + {} + + ReusableStringStream::~ReusableStringStream() { + static_cast( m_oss )->str(""); + m_oss->clear(); + StringStreams::instance().release( m_index ); + } + + auto ReusableStringStream::str() const -> std::string { + return static_cast( m_oss )->str(); + } + + /////////////////////////////////////////////////////////////////////////// + +#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions + std::ostream& cout() { return std::cout; } + std::ostream& cerr() { return std::cerr; } + std::ostream& clog() { return std::clog; } +#endif +} + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_stream.cpp +// start catch_string_manip.cpp + +#include +#include +#include +#include namespace Catch { bool startsWith( std::string const& s, std::string const& prefix ) { - return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s[0] == prefix; } bool endsWith( std::string const& s, std::string const& suffix ) { - return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; } bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } + char toLowerCh(char c) { + return static_cast( std::tolower( c ) ); + } void toLowerInPlace( std::string& s ) { - std::transform( s.begin(), s.end(), s.begin(), ::tolower ); + std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); } std::string toLower( std::string const& s ) { std::string lc = s; @@ -7822,7 +9415,7 @@ namespace Catch { std::string::size_type start = str.find_first_not_of( whitespaceChars ); std::string::size_type end = str.find_last_not_of( whitespaceChars ); - return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); } bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { @@ -7845,185 +9438,958 @@ namespace Catch { {} std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { - os << pluraliser.m_count << " " << pluraliser.m_label; + os << pluraliser.m_count << ' ' << pluraliser.m_label; if( pluraliser.m_count != 1 ) - os << "s"; + os << 's'; return os; } - SourceLineInfo::SourceLineInfo() : line( 0 ){} - SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) - : file( _file ), - line( _line ) - {} - SourceLineInfo::SourceLineInfo( SourceLineInfo const& other ) - : file( other.file ), - line( other.line ) - {} - bool SourceLineInfo::empty() const { - return file.empty(); - } - bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { - return line == other.line && file == other.file; - } - bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { - return line < other.line || ( line == other.line && file < other.file ); - } - - void seedRng( IConfig const& config ) { - if( config.rngSeed() != 0 ) - std::srand( config.rngSeed() ); - } - unsigned int rngSeed() { - return getCurrentContext().getConfig()->rngSeed(); - } - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { -#ifndef __GNUG__ - os << info.file << "(" << info.line << ")"; -#else - os << info.file << ":" << info.line; -#endif - return os; - } - - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { - std::ostringstream oss; - oss << locationInfo << ": Internal Catch error: '" << message << "'"; - if( alwaysTrue() ) - throw std::logic_error( oss.str() ); - } } +// end catch_string_manip.cpp +// start catch_stringref.cpp -// #included from: catch_section.hpp -#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +#include +#include + +namespace Catch { + StringRef::StringRef( char const* rawChars ) noexcept + : StringRef( rawChars, static_cast(std::strlen(rawChars) ) ) + {} + + StringRef::operator std::string() const { + return std::string( m_start, m_size ); + } + + void StringRef::swap( StringRef& other ) noexcept { + std::swap( m_start, other.m_start ); + std::swap( m_size, other.m_size ); + std::swap( m_data, other.m_data ); + } + + auto StringRef::c_str() const -> char const* { + if( isSubstring() ) + const_cast( this )->takeOwnership(); + return m_start; + } + auto StringRef::data() const noexcept -> char const* { + return m_start; + } + + auto StringRef::isOwned() const noexcept -> bool { + return m_data != nullptr; + } + auto StringRef::isSubstring() const noexcept -> bool { + return m_start[m_size] != '\0'; + } + + void StringRef::takeOwnership() { + if( !isOwned() ) { + m_data = new char[m_size+1]; + memcpy( m_data, m_start, m_size ); + m_data[m_size] = '\0'; + m_start = m_data; + } + } + auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { + if( start < m_size ) + return StringRef( m_start+start, size ); + else + return StringRef(); + } + auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { + return + size() == other.size() && + (std::strncmp( m_start, other.m_start, size() ) == 0); + } + auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool { + return !operator==( other ); + } + + auto StringRef::operator[](size_type index) const noexcept -> char { + return m_start[index]; + } + + auto StringRef::numberOfCharacters() const noexcept -> size_type { + size_type noChars = m_size; + // Make adjustments for uft encodings + for( size_type i=0; i < m_size; ++i ) { + char c = m_start[i]; + if( ( c & 0b11000000 ) == 0b11000000 ) { + if( ( c & 0b11100000 ) == 0b11000000 ) + noChars--; + else if( ( c & 0b11110000 ) == 0b11100000 ) + noChars-=2; + else if( ( c & 0b11111000 ) == 0b11110000 ) + noChars-=3; + } + } + return noChars; + } + + auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string { + std::string str; + str.reserve( lhs.size() + rhs.size() ); + str += lhs; + str += rhs; + return str; + } + auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string { + return std::string( lhs ) + std::string( rhs ); + } + + auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { + return os << str.c_str(); + } + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_stringref.cpp +// start catch_tag_alias.cpp + +namespace Catch { + TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {} +} +// end catch_tag_alias.cpp +// start catch_tag_alias_autoregistrar.cpp namespace Catch { - SectionInfo::SectionInfo - ( SourceLineInfo const& _lineInfo, - std::string const& _name, - std::string const& _description ) - : name( _name ), - description( _description ), - lineInfo( _lineInfo ) - {} - - Section::Section( SectionInfo const& info ) - : m_info( info ), - m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) - { - m_timer.start(); - } - - Section::~Section() { - if( m_sectionIncluded ) { - SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); - if( std::uncaught_exception() ) - getResultCapture().sectionEndedEarly( endInfo ); - else - getResultCapture().sectionEnded( endInfo ); + RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { + try { + getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); + } catch (...) { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); } } - // This indicates whether the section should be executed or not - Section::operator bool() const { - return m_sectionIncluded; +} +// end catch_tag_alias_autoregistrar.cpp +// start catch_tag_alias_registry.cpp + +#include + +namespace Catch { + + TagAliasRegistry::~TagAliasRegistry() {} + + TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { + auto it = m_registry.find( alias ); + if( it != m_registry.end() ) + return &(it->second); + else + return nullptr; + } + + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( auto const& registryKvp : m_registry ) { + std::size_t pos = expandedTestSpec.find( registryKvp.first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + registryKvp.second.tag + + expandedTestSpec.substr( pos + registryKvp.first.size() ); + } + } + return expandedTestSpec; + } + + void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { + CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), + "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); + + CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, + "error: tag alias, '" << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo ); + } + + ITagAliasRegistry::~ITagAliasRegistry() {} + + ITagAliasRegistry const& ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); } } // end namespace Catch +// end catch_tag_alias_registry.cpp +// start catch_test_case_info.cpp -// #included from: catch_debugger.hpp -#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED +#include +#include +#include +#include -#include +namespace Catch { -#ifdef CATCH_PLATFORM_MAC + TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( startsWith( tag, '.' ) || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; + else if( tag == "!benchmark" ) + return static_cast( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); + else + return TestCaseInfo::None; + } + bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); + } + void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + CATCH_ENFORCE( !isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n" + << _lineInfo ); + } - #include - #include - #include - #include - #include + TestCase makeTestCase( ITestInvoker* _testCase, + std::string const& _className, + std::string const& _name, + std::string const& _descOrTags, + SourceLineInfo const& _lineInfo ) + { + bool isHidden = false; - namespace Catch{ - - // The following function is taken directly from the following technical note: - // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html - - // Returns true if the current process is being debugged (either - // running under the debugger or has a debugger attached post facto). - bool isDebuggerActive(){ - - int mib[4]; - struct kinfo_proc info; - size_t size; - - // Initialize the flags so that, if sysctl fails for some bizarre - // reason, we get a predictable result. - - info.kp_proc.p_flag = 0; - - // Initialize mib, which tells sysctl the info we want, in this case - // we're looking for information about a specific process ID. - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); - - // Call sysctl. - - size = sizeof(info); - if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) { - Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; - return false; + // Parse out tags + std::vector tags; + std::string desc, tag; + bool inTag = false; + for (char c : _descOrTags) { + if( !inTag ) { + if( c == '[' ) + inTag = true; + else + desc += c; } + else { + if( c == ']' ) { + TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); + if( ( prop & TestCaseInfo::IsHidden ) != 0 ) + isHidden = true; + else if( prop == TestCaseInfo::None ) + enforceNotReservedTag( tag, _lineInfo ); - // We're being debugged if the P_TRACED flag is set. - - return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + tags.push_back( tag ); + tag.clear(); + inTag = false; + } + else + tag += c; + } + } + if( isHidden ) { + tags.push_back( "." ); } - } // namespace Catch -#elif defined(_MSC_VER) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - namespace Catch { - bool isDebuggerActive() { - return IsDebuggerPresent() != 0; + TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); + return TestCase( _testCase, info ); + } + + void setTags( TestCaseInfo& testCaseInfo, std::vector tags ) { + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), end(tags)); + testCaseInfo.lcaseTags.clear(); + + for( auto const& tag : tags ) { + std::string lcaseTag = toLower( tag ); + testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); + testCaseInfo.lcaseTags.push_back( lcaseTag ); + } + testCaseInfo.tags = std::move(tags); + } + + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::vector const& _tags, + SourceLineInfo const& _lineInfo ) + : name( _name ), + className( _className ), + description( _description ), + lineInfo( _lineInfo ), + properties( None ) + { + setTags( *this, _tags ); + } + + bool TestCaseInfo::isHidden() const { + return ( properties & IsHidden ) != 0; + } + bool TestCaseInfo::throws() const { + return ( properties & Throws ) != 0; + } + bool TestCaseInfo::okToFail() const { + return ( properties & (ShouldFail | MayFail ) ) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return ( properties & (ShouldFail ) ) != 0; + } + + std::string TestCaseInfo::tagsAsString() const { + std::string ret; + // '[' and ']' per tag + std::size_t full_size = 2 * tags.size(); + for (const auto& tag : tags) { + full_size += tag.size(); + } + ret.reserve(full_size); + for (const auto& tag : tags) { + ret.push_back('['); + ret.append(tag); + ret.push_back(']'); + } + + return ret; + } + + TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} + + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + + TestCaseInfo const& TestCase::getTestCaseInfo() const + { + return *this; + } + +} // end namespace Catch +// end catch_test_case_info.cpp +// start catch_test_case_registry_impl.cpp + +#include + +namespace Catch { + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + + std::vector sorted = unsortedTestCases; + + switch( config.runOrder() ) { + case RunTests::InLexicographicalOrder: + std::sort( sorted.begin(), sorted.end() ); + break; + case RunTests::InRandomOrder: + seedRng( config ); + RandomNumberGenerator::shuffle( sorted ); + break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; + } + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); + } + + void enforceNoDuplicateTestCases( std::vector const& functions ) { + std::set seenFunctions; + for( auto const& function : functions ) { + auto prev = seenFunctions.insert( function ); + CATCH_ENFORCE( prev.second, + "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << function.getTestCaseInfo().lineInfo ); } } -#elif defined(__MINGW32__) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - namespace Catch { - bool isDebuggerActive() { - return IsDebuggerPresent() != 0; - } - } -#else - namespace Catch { - inline bool isDebuggerActive() { return false; } - } -#endif // Platform -#ifdef CATCH_PLATFORM_WINDOWS - extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* ); - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - ::OutputDebugStringA( text.c_str() ); - } + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector filtered; + filtered.reserve( testCases.size() ); + for( auto const& testCase : testCases ) + if( matchTest( testCase, testSpec, config ) ) + filtered.push_back( testCase ); + return filtered; } -#else - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - // !TBD: Need a version for Mac/ XCode and other IDEs - Catch::cout() << text; - } + std::vector const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); } -#endif // Platform -// #included from: catch_tostring.hpp -#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED + void TestRegistry::registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name.empty() ) { + ReusableStringStream rss; + rss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( rss.str() ) ); + } + m_functions.push_back( testCase ); + } + + std::vector const& TestRegistry::getAllTests() const { + return m_functions; + } + std::vector const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + /////////////////////////////////////////////////////////////////////////// + TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {} + + void TestInvokerAsFunction::invoke() const { + m_testAsFunction(); + } + + std::string extractClassName( std::string const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, '&' ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; + } + +} // end namespace Catch +// end catch_test_case_registry_impl.cpp +// start catch_test_case_tracker.cpp + +#include +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +#endif + +namespace Catch { +namespace TestCaseTracking { + + NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + + ITracker::~ITracker() = default; + + TrackerContext& TrackerContext::instance() { + static TrackerContext s_instance; + return s_instance; + } + + ITracker& TrackerContext::startRun() { + m_rootTracker = std::make_shared( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); + m_currentTracker = nullptr; + m_runState = Executing; + return *m_rootTracker; + } + + void TrackerContext::endRun() { + m_rootTracker.reset(); + m_currentTracker = nullptr; + m_runState = NotStarted; + } + + void TrackerContext::startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void TrackerContext::completeCycle() { + m_runState = CompletedCycle; + } + + bool TrackerContext::completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& TrackerContext::currentTracker() { + return *m_currentTracker; + } + void TrackerContext::setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; + } + + TrackerBase::TrackerHasName::TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} + bool TrackerBase::TrackerHasName::operator ()( ITrackerPtr const& tracker ) const { + return + tracker->nameAndLocation().name == m_nameAndLocation.name && + tracker->nameAndLocation().location == m_nameAndLocation.location; + } + + TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), + m_ctx( ctx ), + m_parent( parent ) + {} + + NameAndLocation const& TrackerBase::nameAndLocation() const { + return m_nameAndLocation; + } + bool TrackerBase::isComplete() const { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + bool TrackerBase::isSuccessfullyCompleted() const { + return m_runState == CompletedSuccessfully; + } + bool TrackerBase::isOpen() const { + return m_runState != NotStarted && !isComplete(); + } + bool TrackerBase::hasChildren() const { + return !m_children.empty(); + } + + void TrackerBase::addChild( ITrackerPtr const& child ) { + m_children.push_back( child ); + } + + ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { + auto it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); + return( it != m_children.end() ) + ? *it + : nullptr; + } + ITracker& TrackerBase::parent() { + assert( m_parent ); // Should always be non-null except for root + return *m_parent; + } + + void TrackerBase::openChild() { + if( m_runState != ExecutingChildren ) { + m_runState = ExecutingChildren; + if( m_parent ) + m_parent->openChild(); + } + } + + bool TrackerBase::isSectionTracker() const { return false; } + bool TrackerBase::isIndexTracker() const { return false; } + + void TrackerBase::open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) + m_parent->openChild(); + } + + void TrackerBase::close() { + + // Close any still open children (e.g. generators) + while( &m_ctx.currentTracker() != this ) + m_ctx.currentTracker().close(); + + switch( m_runState ) { + case NeedsAnotherRun: + break; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if( m_children.empty() || m_children.back()->isComplete() ) + m_runState = CompletedSuccessfully; + break; + + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); + + default: + CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); + } + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::fail() { + m_runState = Failed; + if( m_parent ) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + void TrackerBase::markAsNeedingAnotherRun() { + m_runState = NeedsAnotherRun; + } + + void TrackerBase::moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); + } + void TrackerBase::moveToThis() { + m_ctx.setCurrentTracker( this ); + } + + SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } + + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + std::shared_ptr section; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = std::static_pointer_cast( childTracker ); + } + else { + section = std::make_shared( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( section ); + } + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } + + void SectionTracker::tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void SectionTracker::addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); + } + } + void SectionTracker::addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); + } + + IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( nameAndLocation, ctx, parent ), + m_size( size ) + {} + + bool IndexTracker::isIndexTracker() const { return true; } + + IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { + std::shared_ptr tracker; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = std::static_pointer_cast( childTracker ); + } + else { + tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker, size ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) + tracker->moveNext(); + tracker->open(); + } + + return *tracker; + } + + int IndexTracker::index() const { return m_index; } + + void IndexTracker::moveNext() { + m_index++; + m_children.clear(); + } + + void IndexTracker::close() { + TrackerBase::close(); + if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) + m_runState = Executing; + } + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; +using TestCaseTracking::IndexTracker; + +} // namespace Catch + +#if defined(__clang__) +# pragma clang diagnostic pop +#endif +// end catch_test_case_tracker.cpp +// start catch_test_registry.cpp + +namespace Catch { + + auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { + return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); + } + + NameAndTags::NameAndTags( StringRef name_ , StringRef tags_ ) noexcept : name( name_ ), tags( tags_ ) {} + + AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept { + try { + getMutableRegistryHub() + .registerTest( + makeTestCase( + invoker, + extractClassName( classOrMethod ), + nameAndTags.name, + nameAndTags.tags, + lineInfo)); + } catch (...) { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + + AutoReg::~AutoReg() = default; +} +// end catch_test_registry.cpp +// start catch_test_spec.cpp + +#include +#include +#include +#include + +namespace Catch { + + TestSpec::Pattern::~Pattern() = default; + TestSpec::NamePattern::~NamePattern() = default; + TestSpec::TagPattern::~TagPattern() = default; + TestSpec::ExcludedPattern::~ExcludedPattern() = default; + + TestSpec::NamePattern::NamePattern( std::string const& name ) + : m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( toLower( testCase.name ) ); + } + + TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { + return std::find(begin(testCase.lcaseTags), + end(testCase.lcaseTags), + m_tag) != end(testCase.lcaseTags); + } + + TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + + bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { + // All patterns in a filter must match for the filter to be a match + for( auto const& pattern : m_patterns ) { + if( !pattern->matches( testCase ) ) + return false; + } + return true; + } + + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } + bool TestSpec::matches( TestCaseInfo const& testCase ) const { + // A TestSpec matches if any filter matches + for( auto const& filter : m_filters ) + if( filter.matches( testCase ) ) + return true; + return false; + } +} +// end catch_test_spec.cpp +// start catch_test_spec_parser.cpp + +namespace Catch { + + TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& TestSpecParser::parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + visitChar( m_arg[m_pos] ); + if( m_mode == Name ) + addPattern(); + return *this; + } + TestSpec TestSpecParser::testSpec() { + addFilter(); + return m_testSpec; + } + + void TestSpecParser::visitChar( char c ) { + if( m_mode == None ) { + switch( c ) { + case ' ': return; + case '~': m_exclusion = true; return; + case '[': return startNewMode( Tag, ++m_pos ); + case '"': return startNewMode( QuotedName, ++m_pos ); + case '\\': return escape(); + default: startNewMode( Name, m_pos ); break; + } + } + if( m_mode == Name ) { + if( c == ',' ) { + addPattern(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern(); + startNewMode( Tag, ++m_pos ); + } + else if( c == '\\' ) + escape(); + } + else if( m_mode == EscapedName ) + m_mode = Name; + else if( m_mode == QuotedName && c == '"' ) + addPattern(); + else if( m_mode == Tag && c == ']' ) + addPattern(); + } + void TestSpecParser::startNewMode( Mode mode, std::size_t start ) { + m_mode = mode; + m_start = start; + } + void TestSpecParser::escape() { + if( m_mode == None ) + m_start = m_pos; + m_mode = EscapedName; + m_escapeChars.push_back( m_pos ); + } + std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + + void TestSpecParser::addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); + } + } + + TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch +// end catch_test_spec_parser.cpp +// start catch_timer.cpp + +#include + +namespace Catch { + + auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); + } + + auto estimateClockResolution() -> uint64_t { + uint64_t sum = 0; + static const uint64_t iterations = 1000000; + + for( std::size_t i = 0; i < iterations; ++i ) { + + uint64_t ticks; + uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); + do { + ticks = getCurrentNanosecondsSinceEpoch(); + } + while( ticks == baseTicks ); + + auto delta = ticks - baseTicks; + sum += delta; + } + + // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers + // - and potentially do more iterations if there's a high variance. + return sum/iterations; + } + auto getEstimatedClockResolution() -> uint64_t { + static auto s_resolution = estimateClockResolution(); + return s_resolution; + } + + void Timer::start() { + m_nanoseconds = getCurrentNanosecondsSinceEpoch(); + } + auto Timer::getElapsedNanoseconds() const -> uint64_t { + return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; + } + auto Timer::getElapsedMicroseconds() const -> uint64_t { + return getElapsedNanoseconds()/1000; + } + auto Timer::getElapsedMilliseconds() const -> unsigned int { + return static_cast(getElapsedMicroseconds()/1000); + } + auto Timer::getElapsedSeconds() const -> double { + return getElapsedMicroseconds()/1000000.0; + } + +} // namespace Catch +// end catch_timer.cpp +// start catch_tostring.cpp + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wexit-time-destructors" +# pragma clang diagnostic ignored "-Wglobal-constructors" +#endif + +// Enable specific decls locally +#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#endif + +#include +#include namespace Catch { @@ -8049,8 +10415,7 @@ namespace Detail { }; } - std::string rawMemoryToString( const void *object, std::size_t size ) - { + std::string rawMemoryToString( const void *object, std::size_t size ) { // Reverse order for little endian architectures int i = 0, end = static_cast( size ), inc = 1; if( Endianness::which() == Endianness::Little ) { @@ -8059,86 +10424,25 @@ namespace Detail { } unsigned char const *bytes = static_cast(object); - std::ostringstream os; - os << "0x" << std::setfill('0') << std::hex; + ReusableStringStream rss; + rss << "0x" << std::setfill('0') << std::hex; for( ; i != end; i += inc ) - os << std::setw(2) << static_cast(bytes[i]); - return os.str(); + rss << std::setw(2) << static_cast(bytes[i]); + return rss.str(); } } -std::string toString( std::string const& value ) { - std::string s = value; - if( getCurrentContext().getConfig()->showInvisibles() ) { - for(size_t i = 0; i < s.size(); ++i ) { - std::string subs; - switch( s[i] ) { - case '\n': subs = "\\n"; break; - case '\t': subs = "\\t"; break; - default: break; - } - if( !subs.empty() ) { - s = s.substr( 0, i ) + subs + s.substr( i+1 ); - ++i; - } - } - } - return "\"" + s + "\""; -} -std::string toString( std::wstring const& value ) { - - std::string s; - s.reserve( value.size() ); - for(size_t i = 0; i < value.size(); ++i ) - s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; - return Catch::toString( s ); -} - -std::string toString( const char* const value ) { - return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); -} - -std::string toString( char* const value ) { - return Catch::toString( static_cast( value ) ); -} - -std::string toString( const wchar_t* const value ) -{ - return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); -} - -std::string toString( wchar_t* const value ) -{ - return Catch::toString( static_cast( value ) ); -} - -std::string toString( int value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; - return oss.str(); -} - -std::string toString( unsigned long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; - return oss.str(); -} - -std::string toString( unsigned int value ) { - return Catch::toString( static_cast( value ) ); -} - template std::string fpToString( T value, int precision ) { - std::ostringstream oss; - oss << std::setprecision( precision ) + if (std::isnan(value)) { + return "nan"; + } + + ReusableStringStream rss; + rss << std::setprecision( precision ) << std::fixed << value; - std::string d = oss.str(); + std::string d = rss.str(); std::size_t i = d.find_last_not_of( '0' ); if( i != std::string::npos && i != d.size()-1 ) { if( d[i] == '.' ) @@ -8148,2203 +10452,1985 @@ std::string fpToString( T value, int precision ) { return d; } -std::string toString( const double value ) { - return fpToString( value, 10 ); -} -std::string toString( const float value ) { - return fpToString( value, 5 ) + "f"; +//// ======================================================= //// +// +// Out-of-line defs for full specialization of StringMaker +// +//// ======================================================= //// + +std::string StringMaker::convert(const std::string& str) { + if (!getCurrentContext().getConfig()->showInvisibles()) { + return '"' + str + '"'; + } + + std::string s("\""); + for (char c : str) { + switch (c) { + case '\n': + s.append("\\n"); + break; + case '\t': + s.append("\\t"); + break; + default: + s.push_back(c); + break; + } + } + s.append("\""); + return s; } -std::string toString( bool value ) { - return value ? "true" : "false"; +std::string StringMaker::convert(const std::wstring& wstr) { + std::string s; + s.reserve(wstr.size()); + for (auto c : wstr) { + s += (c <= 0xff) ? static_cast(c) : '?'; + } + return ::Catch::Detail::stringify(s); } -std::string toString( char value ) { - return value < ' ' - ? toString( static_cast( value ) ) - : Detail::makeString( value ); +std::string StringMaker::convert(char const* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(char* str) { + if (str) { + return ::Catch::Detail::stringify(std::string{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(wchar_t const * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } +} +std::string StringMaker::convert(wchar_t * str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{ str }); + } else { + return{ "{null string}" }; + } } -std::string toString( signed char value ) { - return toString( static_cast( value ) ); +std::string StringMaker::convert(int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); } -std::string toString( unsigned char value ) { - return toString( static_cast( value ) ); +std::string StringMaker::convert(unsigned int value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long value) { + return ::Catch::Detail::stringify(static_cast(value)); +} +std::string StringMaker::convert(unsigned long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); } -#ifdef CATCH_CONFIG_CPP11_LONG_LONG -std::string toString( long long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; - return oss.str(); +std::string StringMaker::convert(bool b) { + return b ? "true" : "false"; } -std::string toString( unsigned long long value ) { - std::ostringstream oss; - oss << value; - if( value > Detail::hexThreshold ) - oss << " (0x" << std::hex << value << ")"; - return oss.str(); -} -#endif -#ifdef CATCH_CONFIG_CPP11_NULLPTR -std::string toString( std::nullptr_t ) { +std::string StringMaker::convert(char value) { + if (value == '\r') { + return "'\\r'"; + } else if (value == '\f') { + return "'\\f'"; + } else if (value == '\n') { + return "'\\n'"; + } else if (value == '\t') { + return "'\\t'"; + } else if ('\0' <= value && value < ' ') { + return ::Catch::Detail::stringify(static_cast(value)); + } else { + char chstr[] = "' '"; + chstr[1] = value; + return chstr; + } +} +std::string StringMaker::convert(signed char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} +std::string StringMaker::convert(unsigned char c) { + return ::Catch::Detail::stringify(static_cast(c)); +} + +std::string StringMaker::convert(std::nullptr_t) { return "nullptr"; } -#endif -#ifdef __OBJC__ - std::string toString( NSString const * const& nsstring ) { - if( !nsstring ) - return "nil"; - return "@" + toString([nsstring UTF8String]); - } - std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { - if( !nsstring ) - return "nil"; - return "@" + toString([nsstring UTF8String]); - } - std::string toString( NSObject* const& nsObject ) { - return toString( [nsObject description] ); - } -#endif +std::string StringMaker::convert(float value) { + return fpToString(value, 5) + 'f'; +} +std::string StringMaker::convert(double value) { + return fpToString(value, 10); +} + +std::string ratio_string::symbol() { return "a"; } +std::string ratio_string::symbol() { return "f"; } +std::string ratio_string::symbol() { return "p"; } +std::string ratio_string::symbol() { return "n"; } +std::string ratio_string::symbol() { return "u"; } +std::string ratio_string::symbol() { return "m"; } } // end namespace Catch -// #included from: catch_result_builder.hpp -#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED +#if defined(__clang__) +# pragma clang diagnostic pop +#endif + +// end catch_tostring.cpp +// start catch_totals.cpp namespace Catch { - std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) { - return secondArg.empty() || secondArg == "\"\"" - ? capturedExpression - : capturedExpression + ", " + secondArg; + Counts Counts::operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; } - ResultBuilder::ResultBuilder( char const* macroName, - SourceLineInfo const& lineInfo, - char const* capturedExpression, - ResultDisposition::Flags resultDisposition, - char const* secondArg ) - : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), - m_shouldDebugBreak( false ), - m_shouldThrow( false ) + + Counts& Counts::operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } + + std::size_t Counts::total() const { + return passed + failed + failedButOk; + } + bool Counts::allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool Counts::allOk() const { + return failed == 0; + } + + Totals Totals::operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals& Totals::operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Totals Totals::delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } + +} +// end catch_totals.cpp +// start catch_version.cpp + +#include + +namespace Catch { + + Version::Version + ( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const * const _branchName, + unsigned int _buildNumber ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + patchNumber( _patchNumber ), + branchName( _branchName ), + buildNumber( _buildNumber ) {} - ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { - m_data.resultType = result; - return *this; - } - ResultBuilder& ResultBuilder::setResultType( bool result ) { - m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; - return *this; - } - ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) { - m_exprComponents.lhs = lhs; - return *this; - } - ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) { - m_exprComponents.rhs = rhs; - return *this; - } - ResultBuilder& ResultBuilder::setOp( std::string const& op ) { - m_exprComponents.op = op; - return *this; - } - - void ResultBuilder::endExpression() { - m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition ); - captureExpression(); - } - - void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { - m_assertionInfo.resultDisposition = resultDisposition; - m_stream.oss << Catch::translateActiveException(); - captureResult( ResultWas::ThrewException ); - } - - void ResultBuilder::captureResult( ResultWas::OfType resultType ) { - setResultType( resultType ); - captureExpression(); - } - void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { - if( expectedMessage.empty() ) - captureExpectedException( Matchers::Impl::Generic::AllOf() ); - else - captureExpectedException( Matchers::Equals( expectedMessage ) ); - } - - void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher const& matcher ) { - - assert( m_exprComponents.testFalse == false ); - AssertionResultData data = m_data; - data.resultType = ResultWas::Ok; - data.reconstructedExpression = m_assertionInfo.capturedExpression; - - std::string actualMessage = Catch::translateActiveException(); - if( !matcher.match( actualMessage ) ) { - data.resultType = ResultWas::ExpressionFailed; - data.reconstructedExpression = actualMessage; + std::ostream& operator << ( std::ostream& os, Version const& version ) { + os << version.majorVersion << '.' + << version.minorVersion << '.' + << version.patchNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; } - AssertionResult result( m_assertionInfo, data ); - handleResult( result ); + return os; } - void ResultBuilder::captureExpression() { - AssertionResult result = build(); - handleResult( result ); - } - void ResultBuilder::handleResult( AssertionResult const& result ) - { - getResultCapture().assertionEnded( result ); - - if( !result.isOk() ) { - if( getCurrentContext().getConfig()->shouldDebugBreak() ) - m_shouldDebugBreak = true; - if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) ) - m_shouldThrow = true; - } - } - void ResultBuilder::react() { - if( m_shouldThrow ) - throw Catch::TestFailureException(); + Version const& libraryVersion() { + static Version version( 2, 1, 0, "", 0 ); + return version; } - bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } - bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } - - AssertionResult ResultBuilder::build() const - { - assert( m_data.resultType != ResultWas::Unknown ); - - AssertionResultData data = m_data; - - // Flip bool results if testFalse is set - if( m_exprComponents.testFalse ) { - if( data.resultType == ResultWas::Ok ) - data.resultType = ResultWas::ExpressionFailed; - else if( data.resultType == ResultWas::ExpressionFailed ) - data.resultType = ResultWas::Ok; - } - - data.message = m_stream.oss.str(); - data.reconstructedExpression = reconstructExpression(); - if( m_exprComponents.testFalse ) { - if( m_exprComponents.op == "" ) - data.reconstructedExpression = "!" + data.reconstructedExpression; - else - data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; - } - return AssertionResult( m_assertionInfo, data ); - } - std::string ResultBuilder::reconstructExpression() const { - if( m_exprComponents.op == "" ) - return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs; - else if( m_exprComponents.op == "matches" ) - return m_exprComponents.lhs + " " + m_exprComponents.rhs; - else if( m_exprComponents.op != "!" ) { - if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && - m_exprComponents.lhs.find("\n") == std::string::npos && - m_exprComponents.rhs.find("\n") == std::string::npos ) - return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; - else - return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; - } - else - return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}"; - } - -} // end namespace Catch - -// #included from: catch_tag_alias_registry.hpp -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED - -// #included from: catch_tag_alias_registry.h -#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED - -#include - -namespace Catch { - - class TagAliasRegistry : public ITagAliasRegistry { - public: - virtual ~TagAliasRegistry(); - virtual Option find( std::string const& alias ) const; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; - void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - static TagAliasRegistry& get(); - - private: - std::map m_registry; - }; - -} // end namespace Catch - -#include -#include - -namespace Catch { - - TagAliasRegistry::~TagAliasRegistry() {} - - Option TagAliasRegistry::find( std::string const& alias ) const { - std::map::const_iterator it = m_registry.find( alias ); - if( it != m_registry.end() ) - return it->second; - else - return Option(); - } - - std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { - std::string expandedTestSpec = unexpandedTestSpec; - for( std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); - it != itEnd; - ++it ) { - std::size_t pos = expandedTestSpec.find( it->first ); - if( pos != std::string::npos ) { - expandedTestSpec = expandedTestSpec.substr( 0, pos ) + - it->second.tag + - expandedTestSpec.substr( pos + it->first.size() ); - } - } - return expandedTestSpec; - } - - void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { - - if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { - std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; - throw std::domain_error( oss.str().c_str() ); - } - if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { - std::ostringstream oss; - oss << "error: tag alias, \"" << alias << "\" already registered.\n" - << "\tFirst seen at " << find(alias)->lineInfo << "\n" - << "\tRedefined at " << lineInfo; - throw std::domain_error( oss.str().c_str() ); - } - } - - TagAliasRegistry& TagAliasRegistry::get() { - static TagAliasRegistry instance; - return instance; - - } - - ITagAliasRegistry::~ITagAliasRegistry() {} - ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } - - RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { - try { - TagAliasRegistry::get().add( alias, tag, lineInfo ); - } - catch( std::exception& ex ) { - Colour colourGuard( Colour::Red ); - Catch::cerr() << ex.what() << std::endl; - exit(1); - } - } - -} // end namespace Catch - -// #included from: ../reporters/catch_reporter_multi.hpp -#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED - -namespace Catch { - -class MultipleReporters : public SharedImpl { - typedef std::vector > Reporters; - Reporters m_reporters; - -public: - void add( Ptr const& reporter ) { - m_reporters.push_back( reporter ); - } - -public: // IStreamingReporter - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporters[0]->getPreferences(); - } - - virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->noMatchingTestCases( spec ); - } - - virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testRunStarting( testRunInfo ); - } - - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testGroupStarting( groupInfo ); - } - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testCaseStarting( testInfo ); - } - - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->sectionStarting( sectionInfo ); - } - - virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->assertionStarting( assertionInfo ); - } - - // The return value indicates if the messages buffer should be cleared: - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - bool clearBuffer = false; - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - clearBuffer |= (*it)->assertionEnded( assertionStats ); - return clearBuffer; - } - - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->sectionEnded( sectionStats ); - } - - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testCaseEnded( testCaseStats ); - } - - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testGroupEnded( testGroupStats ); - } - - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->testRunEnded( testRunStats ); - } - - virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); - it != itEnd; - ++it ) - (*it)->skipTest( testInfo ); - } - - virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE { - return this; - } - -}; - -Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { - Ptr resultingReporter; - - if( existingReporter ) { - MultipleReporters* multi = existingReporter->tryAsMulti(); - if( !multi ) { - multi = new MultipleReporters; - resultingReporter = Ptr( multi ); - if( existingReporter ) - multi->add( existingReporter ); - } - else - resultingReporter = existingReporter; - multi->add( additionalReporter ); - } - else - resultingReporter = additionalReporter; - - return resultingReporter; } - -} // end namespace Catch - -// #included from: ../reporters/catch_reporter_xml.hpp -#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED - -// #included from: catch_reporter_bases.hpp -#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED - -#include - -namespace Catch { - - struct StreamingReporterBase : SharedImpl { - - StreamingReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - { - m_reporterPrefs.shouldRedirectStdOut = false; - } - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporterPrefs; - } - - virtual ~StreamingReporterBase() CATCH_OVERRIDE; - - virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {} - - virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE { - currentTestRunInfo = _testRunInfo; - } - virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE { - currentGroupInfo = _groupInfo; - } - - virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE { - currentTestCaseInfo = _testInfo; - } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { - m_sectionStack.push_back( _sectionInfo ); - } - - virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE { - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE { - currentTestCaseInfo.reset(); - } - virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE { - currentGroupInfo.reset(); - } - virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE { - currentTestCaseInfo.reset(); - currentGroupInfo.reset(); - currentTestRunInfo.reset(); - } - - virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE { - // Don't do anything with this by default. - // It can optionally be overridden in the derived class. - } - - Ptr m_config; - std::ostream& stream; - - LazyStat currentTestRunInfo; - LazyStat currentGroupInfo; - LazyStat currentTestCaseInfo; - - std::vector m_sectionStack; - ReporterPreferences m_reporterPrefs; - }; - - struct CumulativeReporterBase : SharedImpl { - template - struct Node : SharedImpl<> { - explicit Node( T const& _value ) : value( _value ) {} - virtual ~Node() {} - - typedef std::vector > ChildNodes; - T value; - ChildNodes children; - }; - struct SectionNode : SharedImpl<> { - explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} - virtual ~SectionNode(); - - bool operator == ( SectionNode const& other ) const { - return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; - } - bool operator == ( Ptr const& other ) const { - return operator==( *other ); - } - - SectionStats stats; - typedef std::vector > ChildSections; - typedef std::vector Assertions; - ChildSections childSections; - Assertions assertions; - std::string stdOut; - std::string stdErr; - }; - - struct BySectionInfo { - BySectionInfo( SectionInfo const& other ) : m_other( other ) {} - BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} - bool operator() ( Ptr const& node ) const { - return node->stats.sectionInfo.lineInfo == m_other.lineInfo; - } - private: - void operator=( BySectionInfo const& ); - SectionInfo const& m_other; - }; - - typedef Node TestCaseNode; - typedef Node TestGroupNode; - typedef Node TestRunNode; - - CumulativeReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - { - m_reporterPrefs.shouldRedirectStdOut = false; - } - ~CumulativeReporterBase(); - - virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { - return m_reporterPrefs; - } - - virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {} - virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {} - - virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {} - - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); - Ptr node; - if( m_sectionStack.empty() ) { - if( !m_rootSection ) - m_rootSection = new SectionNode( incompleteStats ); - node = m_rootSection; - } - else { - SectionNode& parentNode = *m_sectionStack.back(); - SectionNode::ChildSections::const_iterator it = - std::find_if( parentNode.childSections.begin(), - parentNode.childSections.end(), - BySectionInfo( sectionInfo ) ); - if( it == parentNode.childSections.end() ) { - node = new SectionNode( incompleteStats ); - parentNode.childSections.push_back( node ); - } - else - node = *it; - } - m_sectionStack.push_back( node ); - m_deepestSection = node; - } - - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} - - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - assert( !m_sectionStack.empty() ); - SectionNode& sectionNode = *m_sectionStack.back(); - sectionNode.assertions.push_back( assertionStats ); - return true; - } - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - assert( !m_sectionStack.empty() ); - SectionNode& node = *m_sectionStack.back(); - node.stats = sectionStats; - m_sectionStack.pop_back(); - } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - Ptr node = new TestCaseNode( testCaseStats ); - assert( m_sectionStack.size() == 0 ); - node->children.push_back( m_rootSection ); - m_testCases.push_back( node ); - m_rootSection.reset(); - - assert( m_deepestSection ); - m_deepestSection->stdOut = testCaseStats.stdOut; - m_deepestSection->stdErr = testCaseStats.stdErr; - } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - Ptr node = new TestGroupNode( testGroupStats ); - node->children.swap( m_testCases ); - m_testGroups.push_back( node ); - } - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - Ptr node = new TestRunNode( testRunStats ); - node->children.swap( m_testGroups ); - m_testRuns.push_back( node ); - testRunEndedCumulative(); - } - virtual void testRunEndedCumulative() = 0; - - virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} - - Ptr m_config; - std::ostream& stream; - std::vector m_assertions; - std::vector > > m_sections; - std::vector > m_testCases; - std::vector > m_testGroups; - - std::vector > m_testRuns; - - Ptr m_rootSection; - Ptr m_deepestSection; - std::vector > m_sectionStack; - ReporterPreferences m_reporterPrefs; - - }; - - template - char const* getLineOfChars() { - static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; - if( !*line ) { - memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); - line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; - } - return line; - } - - struct TestEventListenerBase : StreamingReporterBase { - TestEventListenerBase( ReporterConfig const& _config ) - : StreamingReporterBase( _config ) - {} - - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} - virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE { - return false; - } - }; - -} // end namespace Catch - -// #included from: ../internal/catch_reporter_registrars.hpp -#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED - -namespace Catch { - - template - class LegacyReporterRegistrar { - - class ReporterFactory : public IReporterFactory { - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new LegacyReporterAdapter( new T( config ) ); - } - - virtual std::string getDescription() const { - return T::getDescription(); - } - }; - - public: - - LegacyReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); - } - }; - - template - class ReporterRegistrar { - - class ReporterFactory : public SharedImpl { - - // *** Please Note ***: - // - If you end up here looking at a compiler error because it's trying to register - // your custom reporter class be aware that the native reporter interface has changed - // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via - // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. - // However please consider updating to the new interface as the old one is now - // deprecated and will probably be removed quite soon! - // Please contact me via github if you have any questions at all about this. - // In fact, ideally, please contact me anyway to let me know you've hit this - as I have - // no idea who is actually using custom reporters at all (possibly no-one!). - // The new interface is designed to minimise exposure to interface changes in the future. - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new T( config ); - } - - virtual std::string getDescription() const { - return T::getDescription(); - } - }; - - public: - - ReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); - } - }; - - template - class ListenerRegistrar { - - class ListenerFactory : public SharedImpl { - - virtual IStreamingReporter* create( ReporterConfig const& config ) const { - return new T( config ); - } - virtual std::string getDescription() const { - return ""; - } - }; - - public: - - ListenerRegistrar() { - getMutableRegistryHub().registerListener( new ListenerFactory() ); - } - }; -} - -#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ - namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } - -#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ - namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } - -#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ - namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } - -// #included from: ../internal/catch_xmlwriter.hpp -#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED +// end catch_version.cpp +// start catch_wildcard_pattern.cpp #include -#include -#include + +namespace Catch { + + WildcardPattern::WildcardPattern( std::string const& pattern, + CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_pattern( adjustCase( pattern ) ) + { + if( startsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); + } + } + + bool WildcardPattern::matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == adjustCase( str ); + case WildcardAtStart: + return endsWith( adjustCase( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( adjustCase( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( adjustCase( str ), m_pattern ); + default: + CATCH_INTERNAL_ERROR( "Unknown enum" ); + } + } + + std::string WildcardPattern::adjustCase( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; + } +} +// end catch_wildcard_pattern.cpp +// start catch_xmlwriter.cpp + #include namespace Catch { - class XmlEncode { - public: - enum ForWhat { ForTextNodes, ForAttributes }; + XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) + : m_str( str ), + m_forWhat( forWhat ) + {} - XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) - : m_str( str ), - m_forWhat( forWhat ) - {} + void XmlEncode::encodeTo( std::ostream& os ) const { - void encodeTo( std::ostream& os ) const { + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) - // Apostrophe escaping not necessary if we always use " to write attributes - // (see: http://www.w3.org/TR/xml/#syntax) + for( std::size_t i = 0; i < m_str.size(); ++ i ) { + char c = m_str[i]; + switch( c ) { + case '<': os << "<"; break; + case '&': os << "&"; break; - for( std::size_t i = 0; i < m_str.size(); ++ i ) { - char c = m_str[i]; - switch( c ) { - case '<': os << "<"; break; - case '&': os << "&"; break; + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) + os << ">"; + else + os << c; + break; - case '>': - // See: http://www.w3.org/TR/xml/#syntax - if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) - os << ">"; - else - os << c; - break; + case '\"': + if( m_forWhat == ForAttributes ) + os << """; + else + os << c; + break; - case '\"': - if( m_forWhat == ForAttributes ) - os << """; - else - os << c; - break; - - default: - // Escape control chars - based on contribution by @espenalb in PR #465 and - // by @mrpi PR #588 - if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) - os << "&#x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast( c ) << ';'; - else - os << c; - } + default: + // Escape control chars - based on contribution by @espenalb in PR #465 and + // by @mrpi PR #588 + if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast( c ); + } + else + os << c; } } + } - friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { - xmlEncode.encodeTo( os ); - return os; + std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept + : m_writer( other.m_writer ){ + other.m_writer = nullptr; + } + XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { + if ( m_writer ) { + m_writer->endElement(); } + m_writer = other.m_writer; + other.m_writer = nullptr; + return *this; + } - private: - std::string m_str; - ForWhat m_forWhat; - }; + XmlWriter::ScopedElement::~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } - class XmlWriter { - public: + XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { + m_writer->writeText( text, indent ); + return *this; + } - class ScopedElement { - public: - ScopedElement( XmlWriter* writer ) - : m_writer( writer ) - {} + XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) + { + writeDeclaration(); + } - ScopedElement( ScopedElement const& other ) - : m_writer( other.m_writer ){ - other.m_writer = CATCH_NULL; - } + XmlWriter::~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } - ~ScopedElement() { - if( m_writer ) - m_writer->endElement(); - } + XmlWriter& XmlWriter::startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + m_os << m_indent << '<' << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } - ScopedElement& writeText( std::string const& text, bool indent = true ) { - m_writer->writeText( text, indent ); - return *this; - } + XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } - template - ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { - m_writer->writeAttribute( name, attribute ); - return *this; - } - - private: - mutable XmlWriter* m_writer; - }; - - XmlWriter() - : m_tagIsOpen( false ), - m_needsNewline( false ), - m_os( &Catch::cout() ) - { - // We encode control characters, which requires - // XML 1.1 - // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 - *m_os << "\n"; + XmlWriter& XmlWriter::endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; } - - XmlWriter( std::ostream& os ) - : m_tagIsOpen( false ), - m_needsNewline( false ), - m_os( &os ) - { - *m_os << "\n"; + else { + m_os << m_indent << ""; } + m_os << std::endl; + m_tags.pop_back(); + return *this; + } - ~XmlWriter() { - while( !m_tags.empty() ) - endElement(); - } + XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } - XmlWriter& startElement( std::string const& name ) { + XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + return *this; + } + + XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; ensureTagClosed(); - newlineIfNecessary(); - stream() << m_indent << "<" << name; - m_tags.push_back( name ); - m_indent += " "; - m_tagIsOpen = true; - return *this; - } - - ScopedElement scopedElement( std::string const& name ) { - ScopedElement scoped( this ); - startElement( name ); - return scoped; - } - - XmlWriter& endElement() { - newlineIfNecessary(); - m_indent = m_indent.substr( 0, m_indent.size()-2 ); - if( m_tagIsOpen ) { - stream() << "/>\n"; - m_tagIsOpen = false; - } - else { - stream() << m_indent << "\n"; - } - m_tags.pop_back(); - return *this; - } - - XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { - if( !name.empty() && !attribute.empty() ) - stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\""; - return *this; - } - - XmlWriter& writeAttribute( std::string const& name, bool attribute ) { - stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; - return *this; - } - - template - XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { - std::ostringstream oss; - oss << attribute; - return writeAttribute( name, oss.str() ); - } - - XmlWriter& writeText( std::string const& text, bool indent = true ) { - if( !text.empty() ){ - bool tagWasOpen = m_tagIsOpen; - ensureTagClosed(); - if( tagWasOpen && indent ) - stream() << m_indent; - stream() << XmlEncode( text ); - m_needsNewline = true; - } - return *this; - } - - XmlWriter& writeComment( std::string const& text ) { - ensureTagClosed(); - stream() << m_indent << ""; + if( tagWasOpen && indent ) + m_os << m_indent; + m_os << XmlEncode( text ); m_needsNewline = true; - return *this; } + return *this; + } - XmlWriter& writeBlankLine() { - ensureTagClosed(); - stream() << "\n"; - return *this; + XmlWriter& XmlWriter::writeComment( std::string const& text ) { + ensureTagClosed(); + m_os << m_indent << ""; + m_needsNewline = true; + return *this; + } + + void XmlWriter::writeStylesheetRef( std::string const& url ) { + m_os << "\n"; + } + + XmlWriter& XmlWriter::writeBlankLine() { + ensureTagClosed(); + m_os << '\n'; + return *this; + } + + void XmlWriter::ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; } + } - void setStream( std::ostream& os ) { - m_os = &os; + void XmlWriter::writeDeclaration() { + m_os << "\n"; + } + + void XmlWriter::newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; } - - private: - XmlWriter( XmlWriter const& ); - void operator=( XmlWriter const& ); - - std::ostream& stream() { - return *m_os; - } - - void ensureTagClosed() { - if( m_tagIsOpen ) { - stream() << ">\n"; - m_tagIsOpen = false; - } - } - - void newlineIfNecessary() { - if( m_needsNewline ) { - stream() << "\n"; - m_needsNewline = false; - } - } - - bool m_tagIsOpen; - bool m_needsNewline; - std::vector m_tags; - std::string m_indent; - std::ostream* m_os; - }; - + } } -// #included from: catch_reenable_warnings.h - -#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(pop) -# else -# pragma clang diagnostic pop -# endif -#elif defined __GNUC__ -# pragma GCC diagnostic pop -#endif +// end catch_xmlwriter.cpp +// start catch_reporter_bases.cpp +#include +#include +#include +#include +#include namespace Catch { - class XmlReporter : public StreamingReporterBase { - public: - XmlReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_sectionDepth( 0 ) - { - m_reporterPrefs.shouldRedirectStdOut = true; + void prepareExpandedExpression(AssertionResult& result) { + result.getExpandedExpression(); + } + + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + sprintf_s(buffer, "%.3f", duration); +#else + sprintf(buffer, "%.3f", duration); +#endif + return std::string(buffer); + } + + TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config) + :StreamingReporterBase(_config) {} + + void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} + + bool TestEventListenerBase::assertionEnded(AssertionStats const &) { + return false; + } + +} // end namespace Catch +// end catch_reporter_bases.cpp +// start catch_reporter_compact.cpp + +namespace { + +#ifdef CATCH_PLATFORM_MAC + const char* failedString() { return "FAILED"; } + const char* passedString() { return "PASSED"; } +#else + const char* failedString() { return "failed"; } + const char* passedString() { return "passed"; } +#endif + + // Colour::LightGrey + Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } + + std::string bothOrAll( std::size_t count ) { + return count == 1 ? std::string() : + count == 2 ? "both " : "all " ; + } + +} // anon namespace + +namespace Catch { +namespace { +// Colour, message variants: +// - white: No tests ran. +// - red: Failed [both/all] N test cases, failed [both/all] M assertions. +// - white: Passed [both/all] N test cases (no assertions). +// - red: Failed N tests cases, failed M assertions. +// - green: Passed [both/all] N tests cases with M assertions. +void printTotals(std::ostream& out, const Totals& totals) { + if (totals.testCases.total() == 0) { + out << "No tests ran."; + } else if (totals.testCases.failed == totals.testCases.total()) { + Colour colour(Colour::ResultError); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll(totals.assertions.failed) : std::string(); + out << + "Failed " << bothOrAll(totals.testCases.failed) + << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << qualify_assertions_failed << + pluralise(totals.assertions.failed, "assertion") << '.'; + } else if (totals.assertions.total() == 0) { + out << + "Passed " << bothOrAll(totals.testCases.total()) + << pluralise(totals.testCases.total(), "test case") + << " (no assertions)."; + } else if (totals.assertions.failed) { + Colour colour(Colour::ResultError); + out << + "Failed " << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << pluralise(totals.assertions.failed, "assertion") << '.'; + } else { + Colour colour(Colour::ResultSuccess); + out << + "Passed " << bothOrAll(totals.testCases.passed) + << pluralise(totals.testCases.passed, "test case") << + " with " << pluralise(totals.assertions.passed, "assertion") << '.'; + } +} + +// Implementation of CompactReporter formatting +class AssertionPrinter { +public: + AssertionPrinter& operator= (AssertionPrinter const&) = delete; + AssertionPrinter(AssertionPrinter const&) = delete; + AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream) + , result(_stats.assertionResult) + , messages(_stats.infoMessages) + , itMessage(_stats.infoMessages.begin()) + , printInfoMessages(_printInfoMessages) {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(Colour::ResultSuccess, passedString()); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) + printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); + else + printResultType(Colour::Error, failedString()); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(Colour::Error, failedString()); + printIssue("unexpected exception with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(Colour::Error, failedString()); + printIssue("fatal error condition with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(Colour::Error, failedString()); + printIssue("expected exception, got none"); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType(Colour::None, "info"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType(Colour::None, "warning"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(Colour::Error, failedString()); + printIssue("explicitly"); + printRemainingMessages(Colour::None); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType(Colour::Error, "** internal error **"); + break; } + } - virtual ~XmlReporter() CATCH_OVERRIDE; +private: + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ':'; + } - static std::string getDescription() { - return "Reports test results as an XML document"; - } - - public: // StreamingReporterBase - - virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { - StreamingReporterBase::noMatchingTestCases( s ); - } - - virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { - StreamingReporterBase::testRunStarting( testInfo ); - m_xml.setStream( stream ); - m_xml.startElement( "Catch" ); - if( !m_config->name().empty() ) - m_xml.writeAttribute( "name", m_config->name() ); - } - - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - StreamingReporterBase::testGroupStarting( groupInfo ); - m_xml.startElement( "Group" ) - .writeAttribute( "name", groupInfo.name ); - } - - virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { - StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement( "TestCase" ).writeAttribute( "name", testInfo.name ); - - if ( m_config->showDurations() == ShowDurations::Always ) - m_testCaseTimer.start(); - } - - virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { - StreamingReporterBase::sectionStarting( sectionInfo ); - if( m_sectionDepth++ > 0 ) { - m_xml.startElement( "Section" ) - .writeAttribute( "name", trim( sectionInfo.name ) ) - .writeAttribute( "description", sectionInfo.description ); + void printResultType(Colour::Code colour, std::string const& passOrFail) const { + if (!passOrFail.empty()) { + { + Colour colourGuard(colour); + stream << ' ' << passOrFail; } + stream << ':'; + } + } + + void printIssue(std::string const& issue) const { + stream << ' ' << issue; + } + + void printExpressionWas() { + if (result.hasExpression()) { + stream << ';'; + { + Colour colour(dimColour()); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + { + Colour colour(dimColour()); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } + + void printRemainingMessages(Colour::Code colour = dimColour()) { + if (itMessage == messages.end()) + return; + + // using messages.end() directly yields (or auto) compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast(std::distance(itMessage, itEnd)); + + { + Colour colourGuard(colour); + stream << " with " << pluralise(N, "message") << ':'; } - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } - - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - const AssertionResult& assertionResult = assertionStats.assertionResult; - - // Print any info messages in tags. - if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { - for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); - it != itEnd; - ++it ) { - if( it->type == ResultWas::Info ) { - m_xml.scopedElement( "Info" ) - .writeText( it->message ); - } else if ( it->type == ResultWas::Warning ) { - m_xml.scopedElement( "Warning" ) - .writeText( it->message ); - } + for (; itMessage != itEnd; ) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + stream << " '" << itMessage->message << '\''; + if (++itMessage != itEnd) { + Colour colourGuard(dimColour()); + stream << " and"; } } + } + } - // Drop out if result was successful but we're not printing them. - if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) ) - return true; +private: + std::ostream& stream; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; +}; - // Print the expression if there is one. - if( assertionResult.hasExpression() ) { - m_xml.startElement( "Expression" ) - .writeAttribute( "success", assertionResult.succeeded() ) - .writeAttribute( "type", assertionResult.getTestMacroName() ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ); +} // anon namespace - m_xml.scopedElement( "Original" ) - .writeText( assertionResult.getExpression() ); - m_xml.scopedElement( "Expanded" ) - .writeText( assertionResult.getExpandedExpression() ); + std::string CompactReporter::getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + ReporterPreferences CompactReporter::getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = false; + return prefs; + } + + void CompactReporter::noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } + + void CompactReporter::assertionStarting( AssertionInfo const& ) {} + + bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; } - // And... Print a result applicable to each result type. - switch( assertionResult.getResultType() ) { - case ResultWas::ThrewException: - m_xml.scopedElement( "Exception" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); - break; - case ResultWas::FatalErrorCondition: - m_xml.scopedElement( "Fatal Error Condition" ) - .writeAttribute( "filename", assertionResult.getSourceInfo().file ) - .writeAttribute( "line", assertionResult.getSourceInfo().line ) - .writeText( assertionResult.getMessage() ); - break; - case ResultWas::Info: - m_xml.scopedElement( "Info" ) - .writeText( assertionResult.getMessage() ); - break; - case ResultWas::Warning: - // Warning will already have been written - break; - case ResultWas::ExplicitFailure: - m_xml.scopedElement( "Failure" ) - .writeText( assertionResult.getMessage() ); - break; - default: - break; - } - - if( assertionResult.hasExpression() ) - m_xml.endElement(); + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + stream << std::endl; return true; } - virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { - StreamingReporterBase::sectionEnded( sectionStats ); - if( --m_sectionDepth > 0 ) { - XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); - e.writeAttribute( "successes", sectionStats.assertions.passed ); - e.writeAttribute( "failures", sectionStats.assertions.failed ); - e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); - - if ( m_config->showDurations() == ShowDurations::Always ) - e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); - - m_xml.endElement(); + void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; } } - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - StreamingReporterBase::testCaseEnded( testCaseStats ); - XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); - e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); - - if ( m_config->showDurations() == ShowDurations::Always ) - e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); - - m_xml.endElement(); + void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( stream, _testRunStats.totals ); + stream << '\n' << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); } - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - StreamingReporterBase::testGroupEnded( testGroupStats ); - // TODO: Check testGroupStats.aborting and act accordingly. - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) - .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) - .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); - m_xml.endElement(); - } + CompactReporter::~CompactReporter() {} - virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { - StreamingReporterBase::testRunEnded( testRunStats ); - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", testRunStats.totals.assertions.passed ) - .writeAttribute( "failures", testRunStats.totals.assertions.failed ) - .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); - m_xml.endElement(); - } - - private: - Timer m_testCaseTimer; - XmlWriter m_xml; - int m_sectionDepth; - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter ) + CATCH_REGISTER_REPORTER( "compact", CompactReporter ) } // end namespace Catch +// end catch_reporter_compact.cpp +// start catch_reporter_console.cpp -// #included from: ../reporters/catch_reporter_junit.hpp -#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED +#include +#include -#include +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled +#endif namespace Catch { - class JunitReporter : public CumulativeReporterBase { - public: - JunitReporter( ReporterConfig const& _config ) +namespace { + +// Formatter impl for ConsoleReporter +class ConsoleAssertionPrinter { +public: + ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete; + ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) + : stream(_stream), + stats(_stats), + result(_stats.assertionResult), + colour(Colour::None), + message(result.getMessage()), + messages(_stats.infoMessages), + printInfoMessages(_printInfoMessages) { + switch (result.getResultType()) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if (_stats.infoMessages.size() == 1) + messageLabel = "explicitly with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } + + void print() const { + printSourceInfo(); + if (stats.totals.assertions.total() > 0) { + if (result.isOk()) + stream << '\n'; + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } else { + stream << '\n'; + } + printMessage(); + } + +private: + void printResultType() const { + if (!passOrFail.empty()) { + Colour colourGuard(colour); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if (result.hasExpression()) { + Colour colourGuard(Colour::OriginalExpression); + stream << " "; + stream << result.getExpressionInMacro(); + stream << '\n'; + } + } + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + stream << "with expansion:\n"; + Colour colourGuard(Colour::ReconstructedExpression); + stream << Column(result.getExpandedExpression()).indent(2) << '\n'; + } + } + void printMessage() const { + if (!messageLabel.empty()) + stream << messageLabel << ':' << '\n'; + for (auto const& msg : messages) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || msg.type != ResultWas::Info) + stream << Column(msg.message).indent(2) << '\n'; + } + } + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + bool printInfoMessages; +}; + +std::size_t makeRatio(std::size_t number, std::size_t total) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : ratio; +} + +std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) { + if (i > j && i > k) + return i; + else if (j > k) + return j; + else + return k; +} + +struct ColumnInfo { + enum Justification { Left, Right }; + std::string name; + int width; + Justification justification; +}; +struct ColumnBreak {}; +struct RowBreak {}; + +class Duration { + enum class Unit { + Auto, + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + Minutes + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; + + uint64_t m_inNanoseconds; + Unit m_units; + +public: + explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto) + : m_inNanoseconds(inNanoseconds), + m_units(units) { + if (m_units == Unit::Auto) { + if (m_inNanoseconds < s_nanosecondsInAMicrosecond) + m_units = Unit::Nanoseconds; + else if (m_inNanoseconds < s_nanosecondsInAMillisecond) + m_units = Unit::Microseconds; + else if (m_inNanoseconds < s_nanosecondsInASecond) + m_units = Unit::Milliseconds; + else if (m_inNanoseconds < s_nanosecondsInAMinute) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; + } + + } + + auto value() const -> double { + switch (m_units) { + case Unit::Microseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMicrosecond); + case Unit::Milliseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMillisecond); + case Unit::Seconds: + return m_inNanoseconds / static_cast(s_nanosecondsInASecond); + case Unit::Minutes: + return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); + default: + return static_cast(m_inNanoseconds); + } + } + auto unitsAsString() const -> std::string { + switch (m_units) { + case Unit::Nanoseconds: + return "ns"; + case Unit::Microseconds: + return "µs"; + case Unit::Milliseconds: + return "ms"; + case Unit::Seconds: + return "s"; + case Unit::Minutes: + return "m"; + default: + return "** internal error **"; + } + + } + friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& { + return os << duration.value() << " " << duration.unitsAsString(); + } +}; +} // end anon namespace + +class TablePrinter { + std::ostream& m_os; + std::vector m_columnInfos; + std::ostringstream m_oss; + int m_currentColumn = -1; + bool m_isOpen = false; + +public: + TablePrinter( std::ostream& os, std::vector columnInfos ) + : m_os( os ), + m_columnInfos( std::move( columnInfos ) ) {} + + auto columnInfos() const -> std::vector const& { + return m_columnInfos; + } + + void open() { + if (!m_isOpen) { + m_isOpen = true; + *this << RowBreak(); + for (auto const& info : m_columnInfos) + *this << info.name << ColumnBreak(); + *this << RowBreak(); + m_os << Catch::getLineOfChars<'-'>() << "\n"; + } + } + void close() { + if (m_isOpen) { + *this << RowBreak(); + m_os << std::endl; + m_isOpen = false; + } + } + + template + friend TablePrinter& operator << (TablePrinter& tp, T const& value) { + tp.m_oss << value; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { + auto colStr = tp.m_oss.str(); + // This takes account of utf8 encodings + auto strSize = Catch::StringRef(colStr).numberOfCharacters(); + tp.m_oss.str(""); + tp.open(); + if (tp.m_currentColumn == static_cast(tp.m_columnInfos.size() - 1)) { + tp.m_currentColumn = -1; + tp.m_os << "\n"; + } + tp.m_currentColumn++; + + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; + auto padding = (strSize + 2 < static_cast(colInfo.width)) + ? std::string(colInfo.width - (strSize + 2), ' ') + : std::string(); + if (colInfo.justification == ColumnInfo::Left) + tp.m_os << colStr << padding << " "; + else + tp.m_os << padding << colStr << " "; + return tp; + } + + friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { + if (tp.m_currentColumn > 0) { + tp.m_os << "\n"; + tp.m_currentColumn = -1; + } + return tp; + } +}; + +ConsoleReporter::ConsoleReporter(ReporterConfig const& config) + : StreamingReporterBase(config), + m_tablePrinter(new TablePrinter(config.stream(), + { + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left }, + { "iters", 8, ColumnInfo::Right }, + { "elapsed ns", 14, ColumnInfo::Right }, + { "average", 14, ColumnInfo::Right } + })) {} +ConsoleReporter::~ConsoleReporter() = default; + +std::string ConsoleReporter::getDescription() { + return "Reports test results as plain lines of text"; +} + +void ConsoleReporter::noMatchingTestCases(std::string const& spec) { + stream << "No test cases matched '" << spec << '\'' << std::endl; +} + +void ConsoleReporter::assertionStarting(AssertionInfo const&) {} + +bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + // Drop out if result was successful but we're not printing them. + if (!includeResults && result.getResultType() != ResultWas::Warning) + return false; + + lazyPrint(); + + ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults); + printer.print(); + stream << std::endl; + return true; +} + +void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { + m_headerPrinted = false; + StreamingReporterBase::sectionStarting(_sectionInfo); +} +void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { + m_tablePrinter->close(); + if (_sectionStats.missingAssertions) { + lazyPrint(); + Colour colour(Colour::ResultError); + if (m_sectionStack.size() > 1) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + if (m_config->showDurations() == ShowDurations::Always) { + stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + if (m_headerPrinted) { + m_headerPrinted = false; + } + StreamingReporterBase::sectionEnded(_sectionStats); +} + +void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { + lazyPrintWithoutClosingBenchmarkTable(); + + auto nameCol = Column( info.name ).width( static_cast( m_tablePrinter->columnInfos()[0].width - 2 ) ); + + bool firstLine = true; + for (auto line : nameCol) { + if (!firstLine) + (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); + else + firstLine = false; + + (*m_tablePrinter) << line << ColumnBreak(); + } +} +void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) { + Duration average(stats.elapsedTimeInNanoseconds / stats.iterations); + (*m_tablePrinter) + << stats.iterations << ColumnBreak() + << stats.elapsedTimeInNanoseconds << ColumnBreak() + << average << ColumnBreak(); +} + +void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { + m_tablePrinter->close(); + StreamingReporterBase::testCaseEnded(_testCaseStats); + m_headerPrinted = false; +} +void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) { + if (currentGroupInfo.used) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals(_testGroupStats.totals); + stream << '\n' << std::endl; + } + StreamingReporterBase::testGroupEnded(_testGroupStats); +} +void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { + printTotalsDivider(_testRunStats.totals); + printTotals(_testRunStats.totals); + stream << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); +} + +void ConsoleReporter::lazyPrint() { + + m_tablePrinter->close(); + lazyPrintWithoutClosingBenchmarkTable(); +} + +void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { + + if (!currentTestRunInfo.used) + lazyPrintRunInfo(); + if (!currentGroupInfo.used) + lazyPrintGroupInfo(); + + if (!m_headerPrinted) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } +} +void ConsoleReporter::lazyPrintRunInfo() { + stream << '\n' << getLineOfChars<'~'>() << '\n'; + Colour colour(Colour::SecondaryText); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion() << " host application.\n" + << "Run with -? for options\n\n"; + + if (m_config->rngSeed() != 0) + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + + currentTestRunInfo.used = true; +} +void ConsoleReporter::lazyPrintGroupInfo() { + if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) { + printClosedHeader("Group: " + currentGroupInfo->name); + currentGroupInfo.used = true; + } +} +void ConsoleReporter::printTestCaseAndSectionHeader() { + assert(!m_sectionStack.empty()); + printOpenHeader(currentTestCaseInfo->name); + + if (m_sectionStack.size() > 1) { + Colour colourGuard(Colour::Headers); + + auto + it = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(it->name, 2); + } + + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; + + if (!lineInfo.empty()) { + stream << getLineOfChars<'-'>() << '\n'; + Colour colourGuard(Colour::FileName); + stream << lineInfo << '\n'; + } + stream << getLineOfChars<'.'>() << '\n' << std::endl; +} + +void ConsoleReporter::printClosedHeader(std::string const& _name) { + printOpenHeader(_name); + stream << getLineOfChars<'.'>() << '\n'; +} +void ConsoleReporter::printOpenHeader(std::string const& _name) { + stream << getLineOfChars<'-'>() << '\n'; + { + Colour colourGuard(Colour::Headers); + printHeaderString(_name); + } +} + +// if string has a : in first line will set indent to follow it on +// subsequent lines +void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) { + std::size_t i = _string.find(": "); + if (i != std::string::npos) + i += 2; + else + i = 0; + stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n'; +} + +struct SummaryColumn { + + SummaryColumn( std::string _label, Colour::Code _colour ) + : label( std::move( _label ) ), + colour( _colour ) {} + SummaryColumn addRow( std::size_t count ) { + ReusableStringStream rss; + rss << count; + std::string row = rss.str(); + for (auto& oldRow : rows) { + while (oldRow.size() < row.size()) + oldRow = ' ' + oldRow; + while (oldRow.size() > row.size()) + row = ' ' + row; + } + rows.push_back(row); + return *this; + } + + std::string label; + Colour::Code colour; + std::vector rows; + +}; + +void ConsoleReporter::printTotals( Totals const& totals ) { + if (totals.testCases.total() == 0) { + stream << Colour(Colour::Warning) << "No tests ran\n"; + } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { + stream << Colour(Colour::ResultSuccess) << "All tests passed"; + stream << " (" + << pluralise(totals.assertions.passed, "assertion") << " in " + << pluralise(totals.testCases.passed, "test case") << ')' + << '\n'; + } else { + + std::vector columns; + columns.push_back(SummaryColumn("", Colour::None) + .addRow(totals.testCases.total()) + .addRow(totals.assertions.total())); + columns.push_back(SummaryColumn("passed", Colour::Success) + .addRow(totals.testCases.passed) + .addRow(totals.assertions.passed)); + columns.push_back(SummaryColumn("failed", Colour::ResultError) + .addRow(totals.testCases.failed) + .addRow(totals.assertions.failed)); + columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) + .addRow(totals.testCases.failedButOk) + .addRow(totals.assertions.failedButOk)); + + printSummaryRow("test cases", columns, 0); + printSummaryRow("assertions", columns, 1); + } +} +void ConsoleReporter::printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row) { + for (auto col : cols) { + std::string value = col.rows[row]; + if (col.label.empty()) { + stream << label << ": "; + if (value != "0") + stream << value; + else + stream << Colour(Colour::Warning) << "- none -"; + } else if (value != "0") { + stream << Colour(Colour::LightGrey) << " | "; + stream << Colour(col.colour) + << value << ' ' << col.label; + } + } + stream << '\n'; +} + +void ConsoleReporter::printTotalsDivider(Totals const& totals) { + if (totals.testCases.total() > 0) { + std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); + std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); + std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); + while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)++; + while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)--; + + stream << Colour(Colour::Error) << std::string(failedRatio, '='); + stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); + if (totals.testCases.allPassed()) + stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); + else + stream << Colour(Colour::Success) << std::string(passedRatio, '='); + } else { + stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); + } + stream << '\n'; +} +void ConsoleReporter::printSummaryDivider() { + stream << getLineOfChars<'-'>() << '\n'; +} + +CATCH_REGISTER_REPORTER("console", ConsoleReporter) + +} // end namespace Catch + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_console.cpp +// start catch_reporter_junit.cpp + +#include +#include +#include +#include + +namespace Catch { + + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); +#else + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); +#endif + + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + + std::string fileNameTag(const std::vector &tags) { + auto it = std::find_if(begin(tags), + end(tags), + [] (std::string const& tag) {return tag.front() == '#'; }); + if (it != tags.end()) + return it->substr(1); + return std::string(); + } + } // anonymous namespace + + JunitReporter::JunitReporter( ReporterConfig const& _config ) : CumulativeReporterBase( _config ), xml( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = true; } - virtual ~JunitReporter() CATCH_OVERRIDE; + JunitReporter::~JunitReporter() {}; - static std::string getDescription() { - return "Reports test results in an XML format that looks like Ant's junitreport target"; + std::string JunitReporter::getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {} + + void JunitReporter::testRunStarting( TestRunInfo const& runInfo ) { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + } + + void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) { + suiteTimer.start(); + stdOutForSuite.clear(); + stdErrForSuite.clear(); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); + } + + void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) { + m_okToFail = testCaseInfo.okToFail(); + } + + bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } + + void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + stdOutForSuite += testCaseStats.stdOut; + stdErrForSuite += testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } + + void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } + + void JunitReporter::testRunEndedCumulative() { + xml.endElement(); + } + + void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else + xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); + + // Write test cases + for( auto const& child : groupNode.children ) + writeTestCase( *child ); + + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false ); + } + + void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if( className.empty() ) { + className = fileNameTag(stats.testInfo.tags); + if ( className.empty() ) + className = "global"; } - virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {} + if ( !m_config->name().empty() ) + className = m_config->name() + "." + className; - virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE { - CumulativeReporterBase::testRunStarting( runInfo ); - xml.startElement( "testsuites" ); - } + writeSection( className, "", rootSection ); + } - virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { - suiteTimer.start(); - stdOutForSuite.str(""); - stdErrForSuite.str(""); - unexpectedExceptions = 0; - CumulativeReporterBase::testGroupStarting( groupInfo ); - } - - virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { - if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) - unexpectedExceptions++; - return CumulativeReporterBase::assertionEnded( assertionStats ); - } - - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { - stdOutForSuite << testCaseStats.stdOut; - stdErrForSuite << testCaseStats.stdErr; - CumulativeReporterBase::testCaseEnded( testCaseStats ); - } - - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { - double suiteTime = suiteTimer.getElapsedSeconds(); - CumulativeReporterBase::testGroupEnded( testGroupStats ); - writeGroup( *m_testGroups.back(), suiteTime ); - } - - virtual void testRunEndedCumulative() CATCH_OVERRIDE { - xml.endElement(); - } - - void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); - TestGroupStats const& stats = groupNode.value; - xml.writeAttribute( "name", stats.groupInfo.name ); - xml.writeAttribute( "errors", unexpectedExceptions ); - xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); - xml.writeAttribute( "tests", stats.totals.assertions.total() ); - xml.writeAttribute( "hostname", "tbd" ); // !TBD - if( m_config->showDurations() == ShowDurations::Never ) - xml.writeAttribute( "time", "" ); - else - xml.writeAttribute( "time", suiteTime ); - xml.writeAttribute( "timestamp", "tbd" ); // !TBD - - // Write test cases - for( TestGroupNode::ChildNodes::const_iterator - it = groupNode.children.begin(), itEnd = groupNode.children.end(); - it != itEnd; - ++it ) - writeTestCase( **it ); - - xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); - xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); - } - - void writeTestCase( TestCaseNode const& testCaseNode ) { - TestCaseStats const& stats = testCaseNode.value; - - // All test cases have exactly one section - which represents the - // test case itself. That section may have 0-n nested sections - assert( testCaseNode.children.size() == 1 ); - SectionNode const& rootSection = *testCaseNode.children.front(); - - std::string className = stats.testInfo.className; + void JunitReporter::writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode ) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + '/' + name; + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); if( className.empty() ) { - if( rootSection.childSections.empty() ) - className = "global"; + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); } - writeSection( className, "", rootSection ); - } - - void writeSection( std::string const& className, - std::string const& rootName, - SectionNode const& sectionNode ) { - std::string name = trim( sectionNode.stats.sectionInfo.name ); - if( !rootName.empty() ) - name = rootName + "/" + name; - - if( !sectionNode.assertions.empty() || - !sectionNode.stdOut.empty() || - !sectionNode.stdErr.empty() ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); - if( className.empty() ) { - xml.writeAttribute( "classname", name ); - xml.writeAttribute( "name", "root" ); - } - else { - xml.writeAttribute( "classname", className ); - xml.writeAttribute( "name", name ); - } - xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); - - writeAssertions( sectionNode ); - - if( !sectionNode.stdOut.empty() ) - xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); - if( !sectionNode.stdErr.empty() ) - xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); } - for( SectionNode::ChildSections::const_iterator - it = sectionNode.childSections.begin(), - itEnd = sectionNode.childSections.end(); - it != itEnd; - ++it ) - if( className.empty() ) - writeSection( name, "", **it ); - else - writeSection( className, name, **it ); + xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); } + for( auto const& childNode : sectionNode.childSections ) + if( className.empty() ) + writeSection( name, "", *childNode ); + else + writeSection( className, name, *childNode ); + } - void writeAssertions( SectionNode const& sectionNode ) { - for( SectionNode::Assertions::const_iterator - it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); - it != itEnd; - ++it ) - writeAssertion( *it ); - } - void writeAssertion( AssertionStats const& stats ) { - AssertionResult const& result = stats.assertionResult; - if( !result.isOk() ) { - std::string elementName; - switch( result.getResultType() ) { - case ResultWas::ThrewException: - case ResultWas::FatalErrorCondition: - elementName = "error"; - break; - case ResultWas::ExplicitFailure: - elementName = "failure"; - break; - case ResultWas::ExpressionFailed: - elementName = "failure"; - break; - case ResultWas::DidntThrowException: - elementName = "failure"; - break; + void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { + for( auto const& assertion : sectionNode.assertions ) + writeAssertion( assertion ); + } - // We should never see these here: - case ResultWas::Info: - case ResultWas::Warning: - case ResultWas::Ok: - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - elementName = "internalError"; - break; - } + void JunitReporter::writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; + switch( result.getResultType() ) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; - XmlWriter::ScopedElement e = xml.scopedElement( elementName ); - - xml.writeAttribute( "message", result.getExpandedExpression() ); - xml.writeAttribute( "type", result.getTestMacroName() ); - - std::ostringstream oss; - if( !result.getMessage().empty() ) - oss << result.getMessage() << "\n"; - for( std::vector::const_iterator - it = stats.infoMessages.begin(), - itEnd = stats.infoMessages.end(); - it != itEnd; - ++it ) - if( it->type == ResultWas::Info ) - oss << it->message << "\n"; - - oss << "at " << result.getSourceInfo(); - xml.writeText( oss.str(), false ); + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; } + + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + + xml.writeAttribute( "message", result.getExpandedExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); + + ReusableStringStream rss; + if( !result.getMessage().empty() ) + rss << result.getMessage() << '\n'; + for( auto const& msg : stats.infoMessages ) + if( msg.type == ResultWas::Info ) + rss << msg.message << '\n'; + + rss << "at " << result.getSourceInfo(); + xml.writeText( rss.str(), false ); } + } - XmlWriter xml; - Timer suiteTimer; - std::ostringstream stdOutForSuite; - std::ostringstream stdErrForSuite; - unsigned int unexpectedExceptions; - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + CATCH_REGISTER_REPORTER( "junit", JunitReporter ) } // end namespace Catch - -// #included from: ../reporters/catch_reporter_console.hpp -#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED +// end catch_reporter_junit.cpp +// start catch_reporter_multi.cpp namespace Catch { - struct ConsoleReporter : StreamingReporterBase { - ConsoleReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_headerPrinted( false ) - {} + void MultipleReporters::add( IStreamingReporterPtr&& reporter ) { + m_reporters.push_back( std::move( reporter ) ); + } - virtual ~ConsoleReporter() CATCH_OVERRIDE; - static std::string getDescription() { - return "Reports test results as plain lines of text"; - } + ReporterPreferences MultipleReporters::getPreferences() const { + return m_reporters[0]->getPreferences(); + } - virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { - stream << "No test cases matched '" << spec << "'" << std::endl; - } + std::set MultipleReporters::getSupportedVerbosities() { + return std::set{ }; + } - virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { - } + void MultipleReporters::noMatchingTestCases( std::string const& spec ) { + for( auto const& reporter : m_reporters ) + reporter->noMatchingTestCases( spec ); + } - virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { - AssertionResult const& result = _assertionStats.assertionResult; + void MultipleReporters::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { + for( auto const& reporter : m_reporters ) + reporter->benchmarkStarting( benchmarkInfo ); + } + void MultipleReporters::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { + for( auto const& reporter : m_reporters ) + reporter->benchmarkEnded( benchmarkStats ); + } - bool printInfoMessages = true; + void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) { + for( auto const& reporter : m_reporters ) + reporter->testRunStarting( testRunInfo ); + } - // Drop out if result was successful and we're not printing those - if( !m_config->includeSuccessfulResults() && result.isOk() ) { - if( result.getResultType() != ResultWas::Warning ) - return false; - printInfoMessages = false; - } + void MultipleReporters::testGroupStarting( GroupInfo const& groupInfo ) { + for( auto const& reporter : m_reporters ) + reporter->testGroupStarting( groupInfo ); + } - lazyPrint(); + void MultipleReporters::testCaseStarting( TestCaseInfo const& testInfo ) { + for( auto const& reporter : m_reporters ) + reporter->testCaseStarting( testInfo ); + } - AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); - printer.print(); - stream << std::endl; - return true; - } + void MultipleReporters::sectionStarting( SectionInfo const& sectionInfo ) { + for( auto const& reporter : m_reporters ) + reporter->sectionStarting( sectionInfo ); + } - virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { - m_headerPrinted = false; - StreamingReporterBase::sectionStarting( _sectionInfo ); - } - virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE { - if( _sectionStats.missingAssertions ) { - lazyPrint(); - Colour colour( Colour::ResultError ); - if( m_sectionStack.size() > 1 ) - stream << "\nNo assertions in section"; - else - stream << "\nNo assertions in test case"; - stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; - } - if( m_headerPrinted ) { - if( m_config->showDurations() == ShowDurations::Always ) - stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; - m_headerPrinted = false; - } - else { - if( m_config->showDurations() == ShowDurations::Always ) - stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; - } - StreamingReporterBase::sectionEnded( _sectionStats ); - } + void MultipleReporters::assertionStarting( AssertionInfo const& assertionInfo ) { + for( auto const& reporter : m_reporters ) + reporter->assertionStarting( assertionInfo ); + } - virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE { - StreamingReporterBase::testCaseEnded( _testCaseStats ); - m_headerPrinted = false; - } - virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE { - if( currentGroupInfo.used ) { - printSummaryDivider(); - stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; - printTotals( _testGroupStats.totals ); - stream << "\n" << std::endl; - } - StreamingReporterBase::testGroupEnded( _testGroupStats ); - } - virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE { - printTotalsDivider( _testRunStats.totals ); - printTotals( _testRunStats.totals ); - stream << std::endl; - StreamingReporterBase::testRunEnded( _testRunStats ); - } + // The return value indicates if the messages buffer should be cleared: + bool MultipleReporters::assertionEnded( AssertionStats const& assertionStats ) { + bool clearBuffer = false; + for( auto const& reporter : m_reporters ) + clearBuffer |= reporter->assertionEnded( assertionStats ); + return clearBuffer; + } - private: + void MultipleReporters::sectionEnded( SectionStats const& sectionStats ) { + for( auto const& reporter : m_reporters ) + reporter->sectionEnded( sectionStats ); + } - class AssertionPrinter { - void operator= ( AssertionPrinter const& ); - public: - AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) - : stream( _stream ), - stats( _stats ), - result( _stats.assertionResult ), - colour( Colour::None ), - message( result.getMessage() ), - messages( _stats.infoMessages ), - printInfoMessages( _printInfoMessages ) - { - switch( result.getResultType() ) { - case ResultWas::Ok: - colour = Colour::Success; - passOrFail = "PASSED"; - //if( result.hasMessage() ) - if( _stats.infoMessages.size() == 1 ) - messageLabel = "with message"; - if( _stats.infoMessages.size() > 1 ) - messageLabel = "with messages"; - break; - case ResultWas::ExpressionFailed: - if( result.isOk() ) { - colour = Colour::Success; - passOrFail = "FAILED - but was ok"; - } - else { - colour = Colour::Error; - passOrFail = "FAILED"; - } - if( _stats.infoMessages.size() == 1 ) - messageLabel = "with message"; - if( _stats.infoMessages.size() > 1 ) - messageLabel = "with messages"; - break; - case ResultWas::ThrewException: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "due to unexpected exception with message"; - break; - case ResultWas::FatalErrorCondition: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "due to a fatal error condition"; - break; - case ResultWas::DidntThrowException: - colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "because no exception was thrown where one was expected"; - break; - case ResultWas::Info: - messageLabel = "info"; - break; - case ResultWas::Warning: - messageLabel = "warning"; - break; - case ResultWas::ExplicitFailure: - passOrFail = "FAILED"; - colour = Colour::Error; - if( _stats.infoMessages.size() == 1 ) - messageLabel = "explicitly with message"; - if( _stats.infoMessages.size() > 1 ) - messageLabel = "explicitly with messages"; - break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - passOrFail = "** internal error **"; - colour = Colour::Error; - break; - } - } + void MultipleReporters::testCaseEnded( TestCaseStats const& testCaseStats ) { + for( auto const& reporter : m_reporters ) + reporter->testCaseEnded( testCaseStats ); + } - void print() const { - printSourceInfo(); - if( stats.totals.assertions.total() > 0 ) { - if( result.isOk() ) - stream << "\n"; - printResultType(); - printOriginalExpression(); - printReconstructedExpression(); - } - else { - stream << "\n"; - } - printMessage(); - } + void MultipleReporters::testGroupEnded( TestGroupStats const& testGroupStats ) { + for( auto const& reporter : m_reporters ) + reporter->testGroupEnded( testGroupStats ); + } - private: - void printResultType() const { - if( !passOrFail.empty() ) { - Colour colourGuard( colour ); - stream << passOrFail << ":\n"; - } - } - void printOriginalExpression() const { - if( result.hasExpression() ) { - Colour colourGuard( Colour::OriginalExpression ); - stream << " "; - stream << result.getExpressionInMacro(); - stream << "\n"; - } - } - void printReconstructedExpression() const { - if( result.hasExpandedExpression() ) { - stream << "with expansion:\n"; - Colour colourGuard( Colour::ReconstructedExpression ); - stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n"; - } - } - void printMessage() const { - if( !messageLabel.empty() ) - stream << messageLabel << ":" << "\n"; - for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); - it != itEnd; - ++it ) { - // If this assertion is a warning ignore any INFO messages - if( printInfoMessages || it->type != ResultWas::Info ) - stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; - } - } - void printSourceInfo() const { - Colour colourGuard( Colour::FileName ); - stream << result.getSourceInfo() << ": "; - } + void MultipleReporters::testRunEnded( TestRunStats const& testRunStats ) { + for( auto const& reporter : m_reporters ) + reporter->testRunEnded( testRunStats ); + } - std::ostream& stream; - AssertionStats const& stats; - AssertionResult const& result; - Colour::Code colour; - std::string passOrFail; - std::string messageLabel; - std::string message; - std::vector messages; - bool printInfoMessages; - }; + void MultipleReporters::skipTest( TestCaseInfo const& testInfo ) { + for( auto const& reporter : m_reporters ) + reporter->skipTest( testInfo ); + } - void lazyPrint() { - - if( !currentTestRunInfo.used ) - lazyPrintRunInfo(); - if( !currentGroupInfo.used ) - lazyPrintGroupInfo(); - - if( !m_headerPrinted ) { - printTestCaseAndSectionHeader(); - m_headerPrinted = true; - } - } - void lazyPrintRunInfo() { - stream << "\n" << getLineOfChars<'~'>() << "\n"; - Colour colour( Colour::SecondaryText ); - stream << currentTestRunInfo->name - << " is a Catch v" << libraryVersion << " host application.\n" - << "Run with -? for options\n\n"; - - if( m_config->rngSeed() != 0 ) - stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; - - currentTestRunInfo.used = true; - } - void lazyPrintGroupInfo() { - if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { - printClosedHeader( "Group: " + currentGroupInfo->name ); - currentGroupInfo.used = true; - } - } - void printTestCaseAndSectionHeader() { - assert( !m_sectionStack.empty() ); - printOpenHeader( currentTestCaseInfo->name ); - - if( m_sectionStack.size() > 1 ) { - Colour colourGuard( Colour::Headers ); - - std::vector::const_iterator - it = m_sectionStack.begin()+1, // Skip first section (test case) - itEnd = m_sectionStack.end(); - for( ; it != itEnd; ++it ) - printHeaderString( it->name, 2 ); - } - - SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; - - if( !lineInfo.empty() ){ - stream << getLineOfChars<'-'>() << "\n"; - Colour colourGuard( Colour::FileName ); - stream << lineInfo << "\n"; - } - stream << getLineOfChars<'.'>() << "\n" << std::endl; - } - - void printClosedHeader( std::string const& _name ) { - printOpenHeader( _name ); - stream << getLineOfChars<'.'>() << "\n"; - } - void printOpenHeader( std::string const& _name ) { - stream << getLineOfChars<'-'>() << "\n"; - { - Colour colourGuard( Colour::Headers ); - printHeaderString( _name ); - } - } - - // if string has a : in first line will set indent to follow it on - // subsequent lines - void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { - std::size_t i = _string.find( ": " ); - if( i != std::string::npos ) - i+=2; - else - i = 0; - stream << Text( _string, TextAttributes() - .setIndent( indent+i) - .setInitialIndent( indent ) ) << "\n"; - } - - struct SummaryColumn { - - SummaryColumn( std::string const& _label, Colour::Code _colour ) - : label( _label ), - colour( _colour ) - {} - SummaryColumn addRow( std::size_t count ) { - std::ostringstream oss; - oss << count; - std::string row = oss.str(); - for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { - while( it->size() < row.size() ) - *it = " " + *it; - while( it->size() > row.size() ) - row = " " + row; - } - rows.push_back( row ); - return *this; - } - - std::string label; - Colour::Code colour; - std::vector rows; - - }; - - void printTotals( Totals const& totals ) { - if( totals.testCases.total() == 0 ) { - stream << Colour( Colour::Warning ) << "No tests ran\n"; - } - else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { - stream << Colour( Colour::ResultSuccess ) << "All tests passed"; - stream << " (" - << pluralise( totals.assertions.passed, "assertion" ) << " in " - << pluralise( totals.testCases.passed, "test case" ) << ")" - << "\n"; - } - else { - - std::vector columns; - columns.push_back( SummaryColumn( "", Colour::None ) - .addRow( totals.testCases.total() ) - .addRow( totals.assertions.total() ) ); - columns.push_back( SummaryColumn( "passed", Colour::Success ) - .addRow( totals.testCases.passed ) - .addRow( totals.assertions.passed ) ); - columns.push_back( SummaryColumn( "failed", Colour::ResultError ) - .addRow( totals.testCases.failed ) - .addRow( totals.assertions.failed ) ); - columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) - .addRow( totals.testCases.failedButOk ) - .addRow( totals.assertions.failedButOk ) ); - - printSummaryRow( "test cases", columns, 0 ); - printSummaryRow( "assertions", columns, 1 ); - } - } - void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { - for( std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it ) { - std::string value = it->rows[row]; - if( it->label.empty() ) { - stream << label << ": "; - if( value != "0" ) - stream << value; - else - stream << Colour( Colour::Warning ) << "- none -"; - } - else if( value != "0" ) { - stream << Colour( Colour::LightGrey ) << " | "; - stream << Colour( it->colour ) - << value << " " << it->label; - } - } - stream << "\n"; - } - - static std::size_t makeRatio( std::size_t number, std::size_t total ) { - std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; - return ( ratio == 0 && number > 0 ) ? 1 : ratio; - } - static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { - if( i > j && i > k ) - return i; - else if( j > k ) - return j; - else - return k; - } - - void printTotalsDivider( Totals const& totals ) { - if( totals.testCases.total() > 0 ) { - std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); - std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); - std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); - while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) - findMax( failedRatio, failedButOkRatio, passedRatio )++; - while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) - findMax( failedRatio, failedButOkRatio, passedRatio )--; - - stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); - stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); - if( totals.testCases.allPassed() ) - stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); - else - stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); - } - else { - stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); - } - stream << "\n"; - } - void printSummaryDivider() { - stream << getLineOfChars<'-'>() << "\n"; - } - - private: - bool m_headerPrinted; - }; - - INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) + bool MultipleReporters::isMulti() const { + return true; + } } // end namespace Catch +// end catch_reporter_multi.cpp +// start catch_reporter_xml.cpp -// #included from: ../reporters/catch_reporter_compact.hpp -#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED - -namespace Catch { - - struct CompactReporter : StreamingReporterBase { - - CompactReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ) - {} - - virtual ~CompactReporter(); - - static std::string getDescription() { - return "Reports test results on a single line, suitable for IDEs"; - } - - virtual ReporterPreferences getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = false; - return prefs; - } - - virtual void noMatchingTestCases( std::string const& spec ) { - stream << "No test cases matched '" << spec << "'" << std::endl; - } - - virtual void assertionStarting( AssertionInfo const& ) { - } - - virtual bool assertionEnded( AssertionStats const& _assertionStats ) { - AssertionResult const& result = _assertionStats.assertionResult; - - bool printInfoMessages = true; - - // Drop out if result was successful and we're not printing those - if( !m_config->includeSuccessfulResults() && result.isOk() ) { - if( result.getResultType() != ResultWas::Warning ) - return false; - printInfoMessages = false; - } - - AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); - printer.print(); - - stream << std::endl; - return true; - } - - virtual void testRunEnded( TestRunStats const& _testRunStats ) { - printTotals( _testRunStats.totals ); - stream << "\n" << std::endl; - StreamingReporterBase::testRunEnded( _testRunStats ); - } - - private: - class AssertionPrinter { - void operator= ( AssertionPrinter const& ); - public: - AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) - : stream( _stream ) - , stats( _stats ) - , result( _stats.assertionResult ) - , messages( _stats.infoMessages ) - , itMessage( _stats.infoMessages.begin() ) - , printInfoMessages( _printInfoMessages ) - {} - - void print() { - printSourceInfo(); - - itMessage = messages.begin(); - - switch( result.getResultType() ) { - case ResultWas::Ok: - printResultType( Colour::ResultSuccess, passedString() ); - printOriginalExpression(); - printReconstructedExpression(); - if ( ! result.hasExpression() ) - printRemainingMessages( Colour::None ); - else - printRemainingMessages(); - break; - case ResultWas::ExpressionFailed: - if( result.isOk() ) - printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); - else - printResultType( Colour::Error, failedString() ); - printOriginalExpression(); - printReconstructedExpression(); - printRemainingMessages(); - break; - case ResultWas::ThrewException: - printResultType( Colour::Error, failedString() ); - printIssue( "unexpected exception with message:" ); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::FatalErrorCondition: - printResultType( Colour::Error, failedString() ); - printIssue( "fatal error condition with message:" ); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::DidntThrowException: - printResultType( Colour::Error, failedString() ); - printIssue( "expected exception, got none" ); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::Info: - printResultType( Colour::None, "info" ); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::Warning: - printResultType( Colour::None, "warning" ); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::ExplicitFailure: - printResultType( Colour::Error, failedString() ); - printIssue( "explicitly" ); - printRemainingMessages( Colour::None ); - break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - printResultType( Colour::Error, "** internal error **" ); - break; - } - } - - private: - // Colour::LightGrey - - static Colour::Code dimColour() { return Colour::FileName; } - -#ifdef CATCH_PLATFORM_MAC - static const char* failedString() { return "FAILED"; } - static const char* passedString() { return "PASSED"; } -#else - static const char* failedString() { return "failed"; } - static const char* passedString() { return "passed"; } +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled #endif - void printSourceInfo() const { - Colour colourGuard( Colour::FileName ); - stream << result.getSourceInfo() << ":"; - } +namespace Catch { + XmlReporter::XmlReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_xml(_config.stream()) + { + m_reporterPrefs.shouldRedirectStdOut = true; + } - void printResultType( Colour::Code colour, std::string passOrFail ) const { - if( !passOrFail.empty() ) { - { - Colour colourGuard( colour ); - stream << " " << passOrFail; - } - stream << ":"; + XmlReporter::~XmlReporter() = default; + + std::string XmlReporter::getDescription() { + return "Reports test results as an XML document"; + } + + std::string XmlReporter::getStylesheetRef() const { + return std::string(); + } + + void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) { + m_xml + .writeAttribute( "filename", sourceInfo.file ) + .writeAttribute( "line", sourceInfo.line ); + } + + void XmlReporter::noMatchingTestCases( std::string const& s ) { + StreamingReporterBase::noMatchingTestCases( s ); + } + + void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) { + StreamingReporterBase::testRunStarting( testInfo ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); + m_xml.startElement( "Catch" ); + if( !m_config->name().empty() ) + m_xml.writeAttribute( "name", m_config->name() ); + } + + void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) { + StreamingReporterBase::testGroupStarting( groupInfo ); + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupInfo.name ); + } + + void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name", trim( testInfo.name ) ) + .writeAttribute( "description", testInfo.description ) + .writeAttribute( "tags", testInfo.tagsAsString() ); + + writeSourceInfo( testInfo.lineInfo ); + + if ( m_config->showDurations() == ShowDurations::Always ) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); + } + + void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) { + StreamingReporterBase::sectionStarting( sectionInfo ); + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionInfo.name ) ) + .writeAttribute( "description", sectionInfo.description ); + writeSourceInfo( sectionInfo.lineInfo ); + m_xml.ensureTagClosed(); + } + } + + void XmlReporter::assertionStarting( AssertionInfo const& ) { } + + bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) { + + AssertionResult const& result = assertionStats.assertionResult; + + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + + if( includeResults || result.getResultType() == ResultWas::Warning ) { + // Print any info messages in tags. + for( auto const& msg : assertionStats.infoMessages ) { + if( msg.type == ResultWas::Info && includeResults ) { + m_xml.scopedElement( "Info" ) + .writeText( msg.message ); + } else if ( msg.type == ResultWas::Warning ) { + m_xml.scopedElement( "Warning" ) + .writeText( msg.message ); } } - - void printIssue( std::string issue ) const { - stream << " " << issue; - } - - void printExpressionWas() { - if( result.hasExpression() ) { - stream << ";"; - { - Colour colour( dimColour() ); - stream << " expression was:"; - } - printOriginalExpression(); - } - } - - void printOriginalExpression() const { - if( result.hasExpression() ) { - stream << " " << result.getExpression(); - } - } - - void printReconstructedExpression() const { - if( result.hasExpandedExpression() ) { - { - Colour colour( dimColour() ); - stream << " for: "; - } - stream << result.getExpandedExpression(); - } - } - - void printMessage() { - if ( itMessage != messages.end() ) { - stream << " '" << itMessage->message << "'"; - ++itMessage; - } - } - - void printRemainingMessages( Colour::Code colour = dimColour() ) { - if ( itMessage == messages.end() ) - return; - - // using messages.end() directly yields compilation error: - std::vector::const_iterator itEnd = messages.end(); - const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); - - { - Colour colourGuard( colour ); - stream << " with " << pluralise( N, "message" ) << ":"; - } - - for(; itMessage != itEnd; ) { - // If this assertion is a warning ignore any INFO messages - if( printInfoMessages || itMessage->type != ResultWas::Info ) { - stream << " '" << itMessage->message << "'"; - if ( ++itMessage != itEnd ) { - Colour colourGuard( dimColour() ); - stream << " and"; - } - } - } - } - - private: - std::ostream& stream; - AssertionStats const& stats; - AssertionResult const& result; - std::vector messages; - std::vector::const_iterator itMessage; - bool printInfoMessages; - }; - - // Colour, message variants: - // - white: No tests ran. - // - red: Failed [both/all] N test cases, failed [both/all] M assertions. - // - white: Passed [both/all] N test cases (no assertions). - // - red: Failed N tests cases, failed M assertions. - // - green: Passed [both/all] N tests cases with M assertions. - - std::string bothOrAll( std::size_t count ) const { - return count == 1 ? "" : count == 2 ? "both " : "all " ; } - void printTotals( const Totals& totals ) const { - if( totals.testCases.total() == 0 ) { - stream << "No tests ran."; - } - else if( totals.testCases.failed == totals.testCases.total() ) { - Colour colour( Colour::ResultError ); - const std::string qualify_assertions_failed = - totals.assertions.failed == totals.assertions.total() ? - bothOrAll( totals.assertions.failed ) : ""; - stream << - "Failed " << bothOrAll( totals.testCases.failed ) - << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << qualify_assertions_failed << - pluralise( totals.assertions.failed, "assertion" ) << "."; - } - else if( totals.assertions.total() == 0 ) { - stream << - "Passed " << bothOrAll( totals.testCases.total() ) - << pluralise( totals.testCases.total(), "test case" ) - << " (no assertions)."; - } - else if( totals.assertions.failed ) { - Colour colour( Colour::ResultError ); - stream << - "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " - "failed " << pluralise( totals.assertions.failed, "assertion" ) << "."; - } - else { - Colour colour( Colour::ResultSuccess ); - stream << - "Passed " << bothOrAll( totals.testCases.passed ) - << pluralise( totals.testCases.passed, "test case" ) << - " with " << pluralise( totals.assertions.passed, "assertion" ) << "."; - } - } - }; + // Drop out if result was successful but we're not printing them. + if( !includeResults && result.getResultType() != ResultWas::Warning ) + return true; - INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + // Print the expression if there is one. + if( result.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", result.succeeded() ) + .writeAttribute( "type", result.getTestMacroName() ); + + writeSourceInfo( result.getSourceInfo() ); + + m_xml.scopedElement( "Original" ) + .writeText( result.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( result.getExpandedExpression() ); + } + + // And... Print a result applicable to each result type. + switch( result.getResultType() ) { + case ResultWas::ThrewException: + m_xml.startElement( "Exception" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::FatalErrorCondition: + m_xml.startElement( "FatalErrorCondition" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( result.getMessage() ); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.startElement( "Failure" ); + writeSourceInfo( result.getSourceInfo() ); + m_xml.writeText( result.getMessage() ); + m_xml.endElement(); + break; + default: + break; + } + + if( result.hasExpression() ) + m_xml.endElement(); + + return true; + } + + void XmlReporter::sectionEnded( SectionStats const& sectionStats ) { + StreamingReporterBase::sectionEnded( sectionStats ); + if( --m_sectionDepth > 0 ) { + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); + e.writeAttribute( "successes", sectionStats.assertions.passed ); + e.writeAttribute( "failures", sectionStats.assertions.failed ); + e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); + + m_xml.endElement(); + } + } + + void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { + StreamingReporterBase::testCaseEnded( testCaseStats ); + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); + e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); + + m_xml.endElement(); + } + + void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { + StreamingReporterBase::testGroupEnded( testGroupStats ); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) { + StreamingReporterBase::testRunEnded( testRunStats ); + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + CATCH_REGISTER_REPORTER( "xml", XmlReporter ) } // end namespace Catch +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +// end catch_reporter_xml.cpp + namespace Catch { - // These are all here to avoid warnings about not having any out of line - // virtual methods - NonCopyable::~NonCopyable() {} - IShared::~IShared() {} - IStream::~IStream() CATCH_NOEXCEPT {} - FileStream::~FileStream() CATCH_NOEXCEPT {} - CoutStream::~CoutStream() CATCH_NOEXCEPT {} - DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} - StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} - IContext::~IContext() {} - IResultCapture::~IResultCapture() {} - ITestCase::~ITestCase() {} - ITestCaseRegistry::~ITestCaseRegistry() {} - IRegistryHub::~IRegistryHub() {} - IMutableRegistryHub::~IMutableRegistryHub() {} - IExceptionTranslator::~IExceptionTranslator() {} - IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} - IReporter::~IReporter() {} - IReporterFactory::~IReporterFactory() {} - IReporterRegistry::~IReporterRegistry() {} - IStreamingReporter::~IStreamingReporter() {} - AssertionStats::~AssertionStats() {} - SectionStats::~SectionStats() {} - TestCaseStats::~TestCaseStats() {} - TestGroupStats::~TestGroupStats() {} - TestRunStats::~TestRunStats() {} - CumulativeReporterBase::SectionNode::~SectionNode() {} - CumulativeReporterBase::~CumulativeReporterBase() {} - - StreamingReporterBase::~StreamingReporterBase() {} - ConsoleReporter::~ConsoleReporter() {} - CompactReporter::~CompactReporter() {} - IRunner::~IRunner() {} - IMutableContext::~IMutableContext() {} - IConfig::~IConfig() {} - XmlReporter::~XmlReporter() {} - JunitReporter::~JunitReporter() {} - TestRegistry::~TestRegistry() {} - FreeFunctionTestCase::~FreeFunctionTestCase() {} - IGeneratorInfo::~IGeneratorInfo() {} - IGeneratorsForTest::~IGeneratorsForTest() {} - WildcardPattern::~WildcardPattern() {} - TestSpec::Pattern::~Pattern() {} - TestSpec::NamePattern::~NamePattern() {} - TestSpec::TagPattern::~TagPattern() {} - TestSpec::ExcludedPattern::~ExcludedPattern() {} - - Matchers::Impl::StdString::Equals::~Equals() {} - Matchers::Impl::StdString::Contains::~Contains() {} - Matchers::Impl::StdString::StartsWith::~StartsWith() {} - Matchers::Impl::StdString::EndsWith::~EndsWith() {} - - void Config::dummy() {} - - namespace TestCaseTracking { - ITracker::~ITracker() {} - TrackerBase::~TrackerBase() {} - SectionTracker::~SectionTracker() {} - IndexTracker::~IndexTracker() {} - } + LeakDetector leakDetector; } #ifdef __clang__ #pragma clang diagnostic pop #endif +// end catch_impl.hpp #endif #ifdef CATCH_CONFIG_MAIN -// #included from: internal/catch_default_main.hpp -#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED +// start catch_default_main.hpp #ifndef __OBJC__ +#if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +// Standard C/C++ Win32 Unicode wmain entry point +extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { +#else // Standard C/C++ main entry point int main (int argc, char * argv[]) { +#endif + return Catch::Session().run( argc, argv ); } @@ -10357,7 +12443,7 @@ int main (int argc, char * const argv[]) { #endif Catch::registerTestMethods(); - int result = Catch::Session().run( argc, (char* const*)argv ); + int result = Catch::Session().run( argc, (char**)argv ); #if !CATCH_ARC_ENABLED [pool drain]; @@ -10368,157 +12454,281 @@ int main (int argc, char * const argv[]) { #endif // __OBJC__ +// end catch_default_main.hpp #endif +#if !defined(CATCH_CONFIG_IMPL_ONLY) + #ifdef CLARA_CONFIG_MAIN_NOT_DEFINED # undef CLARA_CONFIG_MAIN #endif +#if !defined(CATCH_CONFIG_DISABLE) ////// - // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL -#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) -#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" ) +#define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) -#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" ) -#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) -#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" ) -#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) +#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", __VA_ARGS__ ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) -#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) -#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" ) -#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) -#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) -#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) +#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) -#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" ) -#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) -#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" ) -#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) +#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", __VA_ARGS__ ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) -#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) -#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) -#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) -#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) -#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) - #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) - #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) - #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) -#else - #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) - #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) - #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) - #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) - #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) -#endif -#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << ::Catch::Detail::stringify(msg) ) -#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) -#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() // "BDD-style" convenience wrappers -#ifdef CATCH_CONFIG_VARIADIC_MACROS #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#else -#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) -#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) -#endif -#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" ) -#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) -#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) -#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" ) -#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) +#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc ) +#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc ) +#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc ) +#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc ) +#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc ) // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else -#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) -#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" ) +#define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) -#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" ) -#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) -#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" ) -#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) +#define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) -#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) -#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" ) -#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) -#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) -#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) +#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) -#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS" ) -#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) -#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" ) -#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) +#define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) -#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) -#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) -#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) -#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) -#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#endif // CATCH_CONFIG_DISABLE_MATCHERS -#ifdef CATCH_CONFIG_VARIADIC_MACROS - #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) - #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) - #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) - #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) -#else - #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) - #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) - #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) - #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) - #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) - #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) - #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) -#endif -#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) +#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) +#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << ::Catch::Detail::stringify(msg) ) -#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) -#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) - -#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) +#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) +#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) +#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() #endif #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) // "BDD-style" convenience wrappers -#ifdef CATCH_CONFIG_VARIADIC_MACROS #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#else -#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) -#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) -#endif -#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" ) -#define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) -#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" ) -#define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" ) -#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" ) + +#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc ) +#define WHEN( desc ) SECTION( std::string(" When: ") + desc ) +#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc ) +#define THEN( desc ) SECTION( std::string(" Then: ") + desc ) +#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc ) using Catch::Detail::Approx; +#else +////// +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( ... ) (void)(0) +#define CATCH_REQUIRE_FALSE( ... ) (void)(0) + +#define CATCH_REQUIRE_THROWS( ... ) (void)(0) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif// CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) + +#define CATCH_CHECK( ... ) (void)(0) +#define CATCH_CHECK_FALSE( ... ) (void)(0) +#define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) +#define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CATCH_CHECK_NOFAIL( ... ) (void)(0) + +#define CATCH_CHECK_THROWS( ... ) (void)(0) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THAT( arg, matcher ) (void)(0) + +#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define CATCH_INFO( msg ) (void)(0) +#define CATCH_WARN( msg ) (void)(0) +#define CATCH_CAPTURE( msg ) (void)(0) + +#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_METHOD_AS_TEST_CASE( method, ... ) +#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define CATCH_SECTION( ... ) +#define CATCH_FAIL( ... ) (void)(0) +#define CATCH_FAIL_CHECK( ... ) (void)(0) +#define CATCH_SUCCEED( ... ) (void)(0) + +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) +#define CATCH_GIVEN( desc ) +#define CATCH_WHEN( desc ) +#define CATCH_AND_WHEN( desc ) +#define CATCH_THEN( desc ) +#define CATCH_AND_THEN( desc ) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( ... ) (void)(0) +#define REQUIRE_FALSE( ... ) (void)(0) + +#define REQUIRE_THROWS( ... ) (void)(0) +#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) +#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define REQUIRE_NOTHROW( ... ) (void)(0) + +#define CHECK( ... ) (void)(0) +#define CHECK_FALSE( ... ) (void)(0) +#define CHECKED_IF( ... ) if (__VA_ARGS__) +#define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) +#define CHECK_NOFAIL( ... ) (void)(0) + +#define CHECK_THROWS( ... ) (void)(0) +#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) +#define CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CHECK_NOTHROW( ... ) (void)(0) + +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CHECK_THAT( arg, matcher ) (void)(0) + +#define REQUIRE_THAT( arg, matcher ) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS + +#define INFO( msg ) (void)(0) +#define WARN( msg ) (void)(0) +#define CAPTURE( msg ) (void)(0) + +#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define METHOD_AS_TEST_CASE( method, ... ) +#define REGISTER_TEST_CASE( Function, ... ) (void)(0) +#define SECTION( ... ) +#define FAIL( ... ) (void)(0) +#define FAIL_CHECK( ... ) (void)(0) +#define SUCCEED( ... ) (void)(0) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// "BDD-style" convenience wrappers +#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) + +#define GIVEN( desc ) +#define WHEN( desc ) +#define AND_WHEN( desc ) +#define THEN( desc ) +#define AND_THEN( desc ) + +using Catch::Detail::Approx; + +#endif + +#endif // ! CATCH_CONFIG_IMPL_ONLY + +// start catch_reenable_warnings.h + + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(pop) +# else +# pragma clang diagnostic pop +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + +// end catch_reenable_warnings.h +// end catch.hpp #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 6e241572..a39c3f8d 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -382,7 +382,7 @@ TEST_CASE("Set and restore chai state") // set state should have reverted the state of the functions and dropped // the 'myfun' - CHECK_THROWS_AS(chai.eval("myfun()"), chaiscript::exception::eval_error &); + CHECK_THROWS_AS(chai.eval("myfun()"), chaiscript::exception::eval_error); // set state should not affect the local variables CHECK(chai.eval("i") == 1); @@ -390,7 +390,7 @@ TEST_CASE("Set and restore chai state") // After resetting the locals we expect the 'i' to be gone chai.set_locals(locals); - CHECK_THROWS_AS(chai.eval("i"), chaiscript::exception::eval_error &); + CHECK_THROWS_AS(chai.eval("i"), chaiscript::exception::eval_error); } @@ -468,8 +468,8 @@ TEST_CASE("Simultaneous ChaiScript tests") CHECK(chai.eval("do_something(" + std::to_string(i) + ")") == i + 2); CHECK(chai2.eval("do_something_else(" + std::to_string(i) + ")") == i * 2); - CHECK_THROWS_AS(chai2.eval("do_something(1)"), chaiscript::exception::eval_error &); - CHECK_THROWS_AS(chai2.eval("i"), chaiscript::exception::eval_error &); + CHECK_THROWS_AS(chai2.eval("do_something(1)"), chaiscript::exception::eval_error); + CHECK_THROWS_AS(chai2.eval("i"), chaiscript::exception::eval_error); CHECK_NOTHROW(chai2.eval("do_something_else(1)")); } } From 695fa0b37141fc7994c1293c060a051adb83689e Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 19 Jan 2018 11:54:19 -0700 Subject: [PATCH 83/97] Proper fix for noexcept/msvc2017 From @StephanTLavavej --- include/chaiscript/dispatchkit/register_function.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/chaiscript/dispatchkit/register_function.hpp b/include/chaiscript/dispatchkit/register_function.hpp index 2206b024..e5dec699 100644 --- a/include/chaiscript/dispatchkit/register_function.hpp +++ b/include/chaiscript/dispatchkit/register_function.hpp @@ -86,7 +86,7 @@ namespace chaiscript // only compile this bit if noexcept is part of the type system // -#if __cpp_noexcept_function_type >= 201510 || (_MSVC_LANG > 201403L && _MSC_VER >= 1912) +#if __cpp_noexcept_function_type >= 201510 || (defined(_NOEXCEPT_TYPES_SUPPORTED) && _MSC_VER >= 1912) template Proxy_Function fun(Ret (*func)(Param...) noexcept) { From cb30a9783217be5623ddd4d24a082e67f4784210 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 19 Jan 2018 12:57:53 -0700 Subject: [PATCH 84/97] Workaround for /permissive- on MSVC Addresses #403 --- CMakeLists.txt | 4 +++- include/chaiscript/language/chaiscript_eval.hpp | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e98ed1d7..2b210467 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,6 +174,8 @@ if(MSVC) add_definitions(/std:c++17) endif() + + if (MSVC_VERSION STREQUAL "1800") # VS2013 doesn't have magic statics add_definitions(/w44640) @@ -182,7 +184,7 @@ if(MSVC) add_definitions(/w34062) endif() - add_definitions(/bigobj) + add_definitions(/bigobj /permissive-) # Note on MSVC compiler flags. # The code base selective disables warnings as necessary when the compiler is complaining too much # about something that is perfectly valid, or there is simply no technical way around it diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index e0933b71..e5b2aa7e 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -327,15 +327,17 @@ namespace chaiscript Boxed_Value fn(this->children[0]->eval(t_ss)); + using ConstFunctionTypePtr = const dispatch::Proxy_Function_Base *; try { - return (*t_ss->boxed_cast(fn))(params, t_ss.conversions()); + return (*t_ss->boxed_cast(fn))(params, t_ss.conversions()); } catch(const exception::dispatch_error &e){ throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'", e.parameters, e.functions, false, *t_ss); } catch(const exception::bad_boxed_cast &){ try { - Const_Proxy_Function f = t_ss->boxed_cast(fn); + using ConstFunctionTypeRef = const Const_Proxy_Function &; + Const_Proxy_Function f = t_ss->boxed_cast(fn); // handle the case where there is only 1 function to try to call and dispatch fails on it throw exception::eval_error("Error calling function '" + this->children[0]->text + "'", params, {f}, false, *t_ss); } catch (const exception::bad_boxed_cast &) { From 0c32c5054c7a1647a94bb4b12bcba1652df9d862 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 19 Jan 2018 13:01:44 -0700 Subject: [PATCH 85/97] Add clarification on use-after-move --- include/chaiscript/language/chaiscript_eval.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index e5b2aa7e..f6472933 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -758,6 +758,8 @@ namespace chaiscript std::vector>(std::make_move_iterator(t_children.begin()), std::make_move_iterator(std::prev(t_children.end(), has_guard(t_children, 1)?2:1))) ), + // This apparent use after move is safe because we are only moving out the specific elements we need + // on each operation. m_body_node(get_body_node(std::move(t_children))), m_guard_node(get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2)) From 6c41ac90d84a37fb0f3c1dfb4c3bf2646644d19c Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 2 Feb 2018 20:35:32 -0700 Subject: [PATCH 86/97] Add `to_int(int)` and similar overloads * This is so that `to_int` `to_char` `to_long` and similar work not only with strings but also with built-in types --- include/chaiscript/dispatchkit/bootstrap.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index 8aedb353..bcaebfca 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -144,6 +144,7 @@ namespace chaiscript construct_pod(name, m); m.add(fun(&parse_string), "to_" + name); + m.add(fun([](const T t){ return t; }), "to_" + name); } From 9be8f3682408023b62a258d20f70b57cd171b0d3 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 2 Feb 2018 20:36:29 -0700 Subject: [PATCH 87/97] Fix some warnings found on g++7 --- include/chaiscript/chaiscript_defines.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index 93d9187e..d3a74d78 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -205,15 +205,15 @@ namespace chaiscript { case '9': if (decimal_place < 10) { t *= 10; - t += c - '0'; + t += static_cast(c - '0'); } else { - t += (c - '0') / decimal_place; + t += static_cast(c - '0') / decimal_place; decimal_place *= 10; } break; default: - return exponent ? base * std::pow(T(10), t * exponent) : t; + return exponent ? base * std::pow(T(10), t * static_cast(exponent)) : t; } } } From 35af4edb3068fba912993247aabd478ac7e66c75 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 2 Feb 2018 21:04:21 -0700 Subject: [PATCH 88/97] Ignore some warnings from clang++ --- CMakeLists.txt | 2 +- include/chaiscript/language/chaiscript_parser.hpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b210467..12ddd56c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,7 +197,7 @@ else() add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -pedantic ${CPP14_FLAG}) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors -Wno-documentation-unknown-command) + add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors -Wno-documentation-unknown-command -Wno-unused-template) else() add_definitions(-Wnoexcept) endif() diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 77eec4e2..e6f362be 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -748,7 +748,9 @@ namespace chaiscript return const_var(static_cast(u)); } else if (!unsigned_ && !longlong_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { return const_var(static_cast(u)); - } else if ((unsigned_ || base != 10) && !longlong_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + } else if ((unsigned_ || base != 10) && !longlong_ + && u >= std::numeric_limits::min() + && u <= std::numeric_limits::max()) { return const_var(static_cast(u)); } else if (!unsigned_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { return const_var(static_cast(u)); From e154e1e380eb7202b5d14581619970af8729bc23 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 2 Feb 2018 21:31:19 -0700 Subject: [PATCH 89/97] Move DYNLIB disabled builds to G++5 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 888922b7..3debce25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ matrix: compiler: gcc - os: linux sudo: false - env: GCC_VER="4.9" CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1 + env: GCC_VER="5" CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1 compiler: gcc - os: linux sudo: false From 55b16a8204b8872957b1aff8cd46d2c67f4f9d0a Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 2 Feb 2018 21:41:49 -0700 Subject: [PATCH 90/97] Enhance tests for execution contexts Closes #387 references #388 --- unittests/execution_context.chai | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/unittests/execution_context.chai b/unittests/execution_context.chai index 2944827d..44a9eb1f 100644 --- a/unittests/execution_context.chai +++ b/unittests/execution_context.chai @@ -40,3 +40,13 @@ assert_equal(res3[2], "member2") assert_true(__FILE__.find("execution_context.chai") != -1) +assert_equal(eval("__FILE__"), "__EVAL__") +assert_equal(eval("__LINE__"), 1) +assert_equal(eval("__FUNC__"), "NOT_IN_FUNCTION") +assert_equal(eval("__CLASS__"), "NOT_IN_CLASS") + + + + + + From 5cd9e94d4a043c642e0c29ac8303c488a0c2b779 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 2 Feb 2018 21:54:08 -0700 Subject: [PATCH 91/97] Third attempt to avoid ICE on travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3debce25..dde394b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ matrix: compiler: gcc - os: linux sudo: false - env: GCC_VER="5" CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1 + env: GCC_VER="6" CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1 compiler: gcc - os: linux sudo: false From 0391a9c71552a697a6adcb95f4760ec65e28cb54 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Fri, 2 Feb 2018 22:19:31 -0700 Subject: [PATCH 92/97] Remove DYNLIB disabled build for linux from travis This is already covered by MacOS anyhow, so we aren't missing anything in our build coverage --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index dde394b4..7333e3b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,10 +24,10 @@ matrix: sudo: false env: GCC_VER="4.9" compiler: gcc - - os: linux - sudo: false - env: GCC_VER="6" CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1 - compiler: gcc + # - os: linux + #sudo: false + #env: GCC_VER="6" CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1 + #compiler: gcc - os: linux sudo: false env: GCC_VER="5" CPPCHECK=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" From 88042c79589f7452bdc2fd23c24f72dec475170d Mon Sep 17 00:00:00 2001 From: arcoRocks <33952704+arcoRocks@users.noreply.github.com> Date: Thu, 15 Feb 2018 14:33:38 +0100 Subject: [PATCH 93/97] Fix for #409 --- include/chaiscript/utility/json.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index d7c632c6..c6988d54 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -392,7 +392,7 @@ class JSON bool skip = true; for( auto &p : *internal.Map ) { if( !skip ) { s += ",\n"; } - s += ( pad + "\"" + p.first + "\" : " + p.second.dump( depth + 1, tab ) ); + s += ( pad + "\"" + json_escape(p.first) + "\" : " + p.second.dump( depth + 1, tab ) ); skip = false; } s += ( "\n" + pad.erase( 0, 2 ) + "}" ) ; From 3e1916a8d5ef59bc9413c71d305ab48963e7a789 Mon Sep 17 00:00:00 2001 From: arcoRocks <33952704+arcoRocks@users.noreply.github.com> Date: Mon, 19 Feb 2018 13:31:38 +0100 Subject: [PATCH 94/97] fix for #413 --- include/chaiscript/utility/json_wrap.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp index 05b9e404..980b54be 100644 --- a/include/chaiscript/utility/json_wrap.hpp +++ b/include/chaiscript/utility/json_wrap.hpp @@ -110,7 +110,7 @@ namespace chaiscript { return json::JSON(bn.get_as()); } else { - return json::JSON(bn.get_as()); + return json::JSON(bn.get_as()); } } catch (const chaiscript::detail::exception::bad_any_cast &) { // not a number From 906e5e2b6f66f61794378f7a5cbc71f4022d5846 Mon Sep 17 00:00:00 2001 From: arcoRocks <33952704+arcoRocks@users.noreply.github.com> Date: Tue, 20 Feb 2018 16:13:17 +0100 Subject: [PATCH 95/97] Update json_15.chai --- unittests/json_15.chai | 1 + 1 file changed, 1 insertion(+) diff --git a/unittests/json_15.chai b/unittests/json_15.chai index 7e8ad652..e15ace05 100644 --- a/unittests/json_15.chai +++ b/unittests/json_15.chai @@ -16,3 +16,4 @@ assert_equal(to_json(from_json("null")), "null") assert_equal(from_json(to_json(["a": 5, "b": "stuff"])), ["a": 5, "b": "stuff"]) auto x = [3.5, true, false, "test", [], Vector(), Map()] assert_equal(from_json(to_json(x)), x) +assert_equal(from_json(to_json(["aa\\zz":"aa\\zz"])), ["aa\\zz": "aa\\zz"]) From e23c2bb04f07b4949fd334932b69fd9e4250eb7b Mon Sep 17 00:00:00 2001 From: arcoRocks <33952704+arcoRocks@users.noreply.github.com> Date: Tue, 20 Feb 2018 16:23:45 +0100 Subject: [PATCH 96/97] fix for #413 --- include/chaiscript/utility/json_wrap.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp index 980b54be..656bf21d 100644 --- a/include/chaiscript/utility/json_wrap.hpp +++ b/include/chaiscript/utility/json_wrap.hpp @@ -110,7 +110,7 @@ namespace chaiscript { return json::JSON(bn.get_as()); } else { - return json::JSON(bn.get_as()); + return json::JSON(bn.get_as()); } } catch (const chaiscript::detail::exception::bad_any_cast &) { // not a number From 33451163c45a12f6046f323eb6416d7e624ec35c Mon Sep 17 00:00:00 2001 From: arcoRocks <33952704+arcoRocks@users.noreply.github.com> Date: Tue, 20 Feb 2018 16:28:23 +0100 Subject: [PATCH 97/97] to_json std::int64_t integer conversion --- unittests/json_3.chai | 1 + 1 file changed, 1 insertion(+) diff --git a/unittests/json_3.chai b/unittests/json_3.chai index 11ce7dcb..1308c492 100644 --- a/unittests/json_3.chai +++ b/unittests/json_3.chai @@ -1,2 +1,3 @@ assert_equal(from_json("100"), 100) assert_equal(from_json("-100"), -100) +assert_equal(to_json(4294967295), "4294967295")