From 9281beba3341df0d2b6fe37040688de8d2c02e2f Mon Sep 17 00:00:00 2001 From: Ruben Perez Date: Sun, 25 May 2025 12:29:16 +0200 Subject: [PATCH 1/6] Initial impl --- include/boost/core/lightweight_test.hpp | 146 ++++++++++++++++-- include/boost/core/lightweight_test_trait.hpp | 4 +- 2 files changed, 131 insertions(+), 19 deletions(-) diff --git a/include/boost/core/lightweight_test.hpp b/include/boost/core/lightweight_test.hpp index 43baea05..6a76459f 100644 --- a/include/boost/core/lightweight_test.hpp +++ b/include/boost/core/lightweight_test.hpp @@ -45,11 +45,26 @@ namespace boost namespace detail { +struct lwt_context_frame +{ + static const int max_values = 3; + + struct context_value { + const void* obj; + void (*print) (const void*); + + void do_print() const { print(obj); } + }; + + lwt_context_frame* next; + context_value values [max_values]; +}; + class test_result { public: - test_result(): report_( false ), errors_( 0 ) + test_result(): report_( false ), errors_( 0 ), context_ ( NULL ) { core::detail::lwt_unattended(); } @@ -63,7 +78,7 @@ class test_result } } - int& errors() + int errors() const { return errors_; } @@ -73,10 +88,56 @@ class test_result report_ = true; } + void push_context(lwt_context_frame& frame) + { + frame.next = context_; + context_ = &frame; + } + + void pop_context() + { + context_ = context_->next; + } + + void on_error() + { + ++errors_; + print_context(); + } + private: bool report_; int errors_; + lwt_context_frame* context_; + + void print_context() + { + // If there is no context, do nothing + if (!context_) + return; + + BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Failure happened in the following context:\n"; + + // Go through the linked list + lwt_context_frame* frame = context_; + for (int i = 0; frame && i < 10; ++i, frame = frame->next) + { + // Print the header and the first value, which should always be present + BOOST_LIGHTWEIGHT_TEST_OSTREAM << " #" << i << ": "; + frame->values[0].do_print(); + + // Print any other values, if present + for (int j = 1; j < lwt_context_frame::max_values && frame->values[j].obj; ++j) + { + BOOST_LIGHTWEIGHT_TEST_OSTREAM << ", "; + frame->values[j].do_print(); + } + + // Finish the frame + BOOST_LIGHTWEIGHT_TEST_OSTREAM << '\n'; + } + } }; inline test_result& test_results() @@ -85,11 +146,6 @@ inline test_result& test_results() return instance; } -inline int& test_errors() -{ - return test_results().errors(); -} - inline bool test_impl(char const * expr, char const * file, int line, char const * function, bool v) { if( v ) @@ -102,7 +158,7 @@ inline bool test_impl(char const * expr, char const * file, int line, char const BOOST_LIGHTWEIGHT_TEST_OSTREAM << file << "(" << line << "): test '" << expr << "' failed in function '" << function << "'" << std::endl; - ++test_results().errors(); + test_results().on_error(); return false; } } @@ -112,7 +168,7 @@ inline void error_impl(char const * msg, char const * file, int line, char const BOOST_LIGHTWEIGHT_TEST_OSTREAM << file << "(" << line << "): " << msg << " in function '" << function << "'" << std::endl; - ++test_results().errors(); + test_results().on_error(); } inline void throw_failed_impl(const char* expr, char const * excep, char const * file, int line, char const * function) @@ -120,7 +176,7 @@ inline void throw_failed_impl(const char* expr, char const * excep, char const * BOOST_LIGHTWEIGHT_TEST_OSTREAM << file << "(" << line << "): expression '" << expr << "' did not throw exception '" << excep << "' in function '" << function << "'" << std::endl; - ++test_results().errors(); + test_results().on_error(); } inline void no_throw_failed_impl(const char* expr, const char* file, int line, const char* function) @@ -128,7 +184,7 @@ inline void no_throw_failed_impl(const char* expr, const char* file, int line, c BOOST_LIGHTWEIGHT_TEST_OSTREAM << file << "(" << line << "): expression '" << expr << "' threw an exception in function '" << function << "'" << std::endl; - ++test_results().errors(); + test_results().on_error(); } inline void no_throw_failed_impl(const char* expr, const char* what, const char* file, int line, const char* function) @@ -136,7 +192,7 @@ inline void no_throw_failed_impl(const char* expr, const char* what, const char* BOOST_LIGHTWEIGHT_TEST_OSTREAM << file << "(" << line << "): expression '" << expr << "' threw an exception in function '" << function << "': " << what << std::endl; - ++test_results().errors(); + test_results().on_error(); } // In the comparisons below, it is possible that T and U are signed and unsigned integer types, which generates warnings in some compilers. @@ -303,7 +359,7 @@ inline bool test_with_impl(BinaryPredicate pred, char const * expr1, char const << file << "(" << line << "): test '" << expr1 << " " << lwt_predicate_name(pred) << " " << expr2 << "' ('" << test_output_impl(t) << "' " << lwt_predicate_name(pred) << " '" << test_output_impl(u) << "') failed in function '" << function << "'" << std::endl; - ++test_results().errors(); + test_results().on_error(); return false; } } @@ -321,7 +377,7 @@ inline bool test_cstr_eq_impl( char const * expr1, char const * expr2, BOOST_LIGHTWEIGHT_TEST_OSTREAM << file << "(" << line << "): test '" << expr1 << " == " << expr2 << "' ('" << t << "' == '" << u << "') failed in function '" << function << "'" << std::endl; - ++test_results().errors(); + test_results().on_error(); return false; } } @@ -339,7 +395,7 @@ inline bool test_cstr_ne_impl( char const * expr1, char const * expr2, BOOST_LIGHTWEIGHT_TEST_OSTREAM << file << "(" << line << "): test '" << expr1 << " != " << expr2 << "' ('" << t << "' != '" << u << "') failed in function '" << function << "'" << std::endl; - ++test_results().errors(); + test_results().on_error(); return false; } } @@ -409,7 +465,7 @@ bool test_all_eq_impl(FormattedOutputFunction& output, else { output << std::endl; - ++test_results().errors(); + test_results().on_error(); return false; } } @@ -480,7 +536,7 @@ bool test_all_with_impl(FormattedOutputFunction& output, else { output << std::endl; - ++test_results().errors(); + test_results().on_error(); return false; } } @@ -527,6 +583,62 @@ inline void lwt_init() boost::detail::test_results(); } +class lwt_context +{ + boost::detail::lwt_context_frame frame_; + + template + static void lwt_print_value(const void* value) + { + BOOST_LIGHTWEIGHT_TEST_OSTREAM << boost::detail::test_output_impl(*static_cast(value)); + } + + lwt_context(const lwt_context&) {}; + lwt_context& operator=(const lwt_context&) { return *this; } + +public: + template + explicit lwt_context(const T* value) + { + frame_.values[0].obj = value; + frame_.values[0].print = &lwt_print_value; + frame_.values[1].obj = NULL; + frame_.values[1].print = NULL; + frame_.values[2].obj = NULL; + frame_.values[2].print = NULL; + boost::detail::test_results().push_context(frame_); + } + + template + explicit lwt_context(const T1* value1, const T2* value2) + { + frame_.values[0].obj = value1; + frame_.values[0].print = &lwt_print_value; + frame_.values[1].obj = value2; + frame_.values[1].print = &lwt_print_value; + frame_.values[2].obj = NULL; + frame_.values[2].print = NULL; + boost::detail::test_results().push_context(frame_); + } + + template + explicit lwt_context(const T1* value1, const T2* value2, const T3* value3) + { + frame_.values[0].obj = value1; + frame_.values[0].print = &lwt_print_value; + frame_.values[1].obj = value2; + frame_.values[1].print = &lwt_print_value; + frame_.values[2].obj = value3; + frame_.values[2].print = &lwt_print_value; + boost::detail::test_results().push_context(frame_); + } + + ~lwt_context() + { + boost::detail::test_results().pop_context(); + } +}; + } // namespace core } // namespace boost diff --git a/include/boost/core/lightweight_test_trait.hpp b/include/boost/core/lightweight_test_trait.hpp index eea23717..62761f6c 100644 --- a/include/boost/core/lightweight_test_trait.hpp +++ b/include/boost/core/lightweight_test_trait.hpp @@ -46,7 +46,7 @@ template< class T > inline void test_trait_impl( char const * trait, void (*)( T << "' (should have been " << ( expected? "true": "false" ) << ")" << std::endl; - ++test_results().errors(); + test_results().on_error(); } } @@ -71,7 +71,7 @@ template inline void test_trait_same_impl( char const * type << "' != '" << boost::core::type_name() << "')" << std::endl; - ++test_results().errors(); + test_results().on_error(); } } From bdd1a37f01d8e96b46d5f9a98f31f054a5479e04 Mon Sep 17 00:00:00 2001 From: Ruben Perez Date: Sun, 25 May 2025 12:55:09 +0200 Subject: [PATCH 2/6] Refactor --- include/boost/core/lightweight_test.hpp | 171 ++++++++++++------------ 1 file changed, 85 insertions(+), 86 deletions(-) diff --git a/include/boost/core/lightweight_test.hpp b/include/boost/core/lightweight_test.hpp index 6a76459f..33f12132 100644 --- a/include/boost/core/lightweight_test.hpp +++ b/include/boost/core/lightweight_test.hpp @@ -45,17 +45,83 @@ namespace boost namespace detail { +// specialize test output for char pointers to avoid printing as cstring +template inline const T& test_output_impl(const T& v) { return v; } +inline const void* test_output_impl(const char* v) { return v; } +inline const void* test_output_impl(const unsigned char* v) { return v; } +inline const void* test_output_impl(const signed char* v) { return v; } +inline const void* test_output_impl(char* v) { return v; } +inline const void* test_output_impl(unsigned char* v) { return v; } +inline const void* test_output_impl(signed char* v) { return v; } +template inline const void* test_output_impl(T volatile* v) { return const_cast(v); } + +#if !defined( BOOST_NO_CXX11_NULLPTR ) +inline const void* test_output_impl(std::nullptr_t) { return nullptr; } +#endif + +// print chars as numeric + +inline int test_output_impl( signed char const& v ) { return v; } +inline unsigned test_output_impl( unsigned char const& v ) { return v; } + +// Whether wchar_t is signed is implementation-defined + +template struct lwt_long_type {}; +template<> struct lwt_long_type { typedef long type; }; +template<> struct lwt_long_type { typedef unsigned long type; }; + +inline lwt_long_type<(static_cast(-1) < static_cast(0))>::type test_output_impl( wchar_t const& v ) { return v; } + +#if !defined( BOOST_NO_CXX11_CHAR16_T ) +inline unsigned long test_output_impl( char16_t const& v ) { return v; } +#endif + +#if !defined( BOOST_NO_CXX11_CHAR32_T ) +inline unsigned long test_output_impl( char32_t const& v ) { return v; } +#endif + +inline std::string test_output_impl( char const& v ) +{ + if( std::isprint( static_cast( v ) ) ) + { + return std::string( 1, v ); + } + else + { + static const char char_table[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + char buffer[ 4 ]; + buffer[ 0 ] = '\\'; + buffer[ 1 ] = 'x'; + buffer[ 2 ] = char_table[ (static_cast( v ) >> 4u) & 0x0f ]; + buffer[ 3 ] = char_table[ static_cast( v ) & 0x0f ]; + + return std::string( buffer, 4u ); + } +} + +// Allow associating arbitrary context values to tests that get printed on error struct lwt_context_frame { static const int max_values = 3; + template + static void do_print(const void* value) + { + BOOST_LIGHTWEIGHT_TEST_OSTREAM << boost::detail::test_output_impl(*static_cast(value)); + } + + // A type-erased reference to a streamable value struct context_value { const void* obj; - void (*print) (const void*); + void (*print_fn) (const void*); - void do_print() const { print(obj); } + void print() const { print_fn(obj); } + void set_null() { obj = NULL; print_fn = NULL; } + template void set_value(const T* value) { obj = value; print_fn = &do_print; } }; + // Multiple context frames are allowed, creating a stack (forward linked list). + // It's rooted at test_result, with the last pushed frame first. lwt_context_frame* next; context_value values [max_values]; }; @@ -121,17 +187,17 @@ class test_result // Go through the linked list lwt_context_frame* frame = context_; - for (int i = 0; frame && i < 10; ++i, frame = frame->next) + for (int i = 0; frame; ++i, frame = frame->next) { // Print the header and the first value, which should always be present BOOST_LIGHTWEIGHT_TEST_OSTREAM << " #" << i << ": "; - frame->values[0].do_print(); + frame->values[0].print(); // Print any other values, if present for (int j = 1; j < lwt_context_frame::max_values && frame->values[j].obj; ++j) { BOOST_LIGHTWEIGHT_TEST_OSTREAM << ", "; - frame->values[j].do_print(); + frame->values[j].print(); } // Finish the frame @@ -212,59 +278,6 @@ inline void no_throw_failed_impl(const char* expr, const char* what, const char* # pragma GCC diagnostic ignored "-Wsign-conversion" #endif -// specialize test output for char pointers to avoid printing as cstring -template inline const T& test_output_impl(const T& v) { return v; } -inline const void* test_output_impl(const char* v) { return v; } -inline const void* test_output_impl(const unsigned char* v) { return v; } -inline const void* test_output_impl(const signed char* v) { return v; } -inline const void* test_output_impl(char* v) { return v; } -inline const void* test_output_impl(unsigned char* v) { return v; } -inline const void* test_output_impl(signed char* v) { return v; } -template inline const void* test_output_impl(T volatile* v) { return const_cast(v); } - -#if !defined( BOOST_NO_CXX11_NULLPTR ) -inline const void* test_output_impl(std::nullptr_t) { return nullptr; } -#endif - -// print chars as numeric - -inline int test_output_impl( signed char const& v ) { return v; } -inline unsigned test_output_impl( unsigned char const& v ) { return v; } - -// Whether wchar_t is signed is implementation-defined - -template struct lwt_long_type {}; -template<> struct lwt_long_type { typedef long type; }; -template<> struct lwt_long_type { typedef unsigned long type; }; - -inline lwt_long_type<(static_cast(-1) < static_cast(0))>::type test_output_impl( wchar_t const& v ) { return v; } - -#if !defined( BOOST_NO_CXX11_CHAR16_T ) -inline unsigned long test_output_impl( char16_t const& v ) { return v; } -#endif - -#if !defined( BOOST_NO_CXX11_CHAR32_T ) -inline unsigned long test_output_impl( char32_t const& v ) { return v; } -#endif - -inline std::string test_output_impl( char const& v ) -{ - if( std::isprint( static_cast( v ) ) ) - { - return std::string( 1, v ); - } - else - { - static const char char_table[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - char buffer[ 4 ]; - buffer[ 0 ] = '\\'; - buffer[ 1 ] = 'x'; - buffer[ 2 ] = char_table[ (static_cast( v ) >> 4u) & 0x0f ]; - buffer[ 3 ] = char_table[ static_cast( v ) & 0x0f ]; - - return std::string( buffer, 4u ); - } -} // predicates @@ -587,12 +600,7 @@ class lwt_context { boost::detail::lwt_context_frame frame_; - template - static void lwt_print_value(const void* value) - { - BOOST_LIGHTWEIGHT_TEST_OSTREAM << boost::detail::test_output_impl(*static_cast(value)); - } - + // Disallow copy and movement lwt_context(const lwt_context&) {}; lwt_context& operator=(const lwt_context&) { return *this; } @@ -600,36 +608,27 @@ class lwt_context template explicit lwt_context(const T* value) { - frame_.values[0].obj = value; - frame_.values[0].print = &lwt_print_value; - frame_.values[1].obj = NULL; - frame_.values[1].print = NULL; - frame_.values[2].obj = NULL; - frame_.values[2].print = NULL; + frame_.values[0].set_value(value); + frame_.values[1].set_null(); + frame_.values[2].set_null(); boost::detail::test_results().push_context(frame_); } - template - explicit lwt_context(const T1* value1, const T2* value2) + template + lwt_context(const T0* value0, const T1* value1) { - frame_.values[0].obj = value1; - frame_.values[0].print = &lwt_print_value; - frame_.values[1].obj = value2; - frame_.values[1].print = &lwt_print_value; - frame_.values[2].obj = NULL; - frame_.values[2].print = NULL; + frame_.values[0].set_value(value0); + frame_.values[1].set_value(value1); + frame_.values[2].set_null(); boost::detail::test_results().push_context(frame_); } - template - explicit lwt_context(const T1* value1, const T2* value2, const T3* value3) + template + lwt_context(const T0* value0, const T1* value1, const T2* value2) { - frame_.values[0].obj = value1; - frame_.values[0].print = &lwt_print_value; - frame_.values[1].obj = value2; - frame_.values[1].print = &lwt_print_value; - frame_.values[2].obj = value3; - frame_.values[2].print = &lwt_print_value; + frame_.values[0].set_value(value0); + frame_.values[0].set_value(value1); + frame_.values[0].set_value(value2); boost::detail::test_results().push_context(frame_); } From bf89d7b012a075dd2ab62b8f5c6b0c0a4ca14799 Mon Sep 17 00:00:00 2001 From: Ruben Perez Date: Sun, 15 Jun 2025 19:08:14 +0200 Subject: [PATCH 3/6] Apply PR comments --- include/boost/core/lightweight_test.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/boost/core/lightweight_test.hpp b/include/boost/core/lightweight_test.hpp index 33f12132..7f3fdfd5 100644 --- a/include/boost/core/lightweight_test.hpp +++ b/include/boost/core/lightweight_test.hpp @@ -601,8 +601,8 @@ class lwt_context boost::detail::lwt_context_frame frame_; // Disallow copy and movement - lwt_context(const lwt_context&) {}; - lwt_context& operator=(const lwt_context&) { return *this; } + BOOST_DELETED_FUNCTION(lwt_context(const lwt_context&)) + BOOST_DELETED_FUNCTION(lwt_context& operator=(const lwt_context&)) public: template @@ -627,8 +627,8 @@ class lwt_context lwt_context(const T0* value0, const T1* value1, const T2* value2) { frame_.values[0].set_value(value0); - frame_.values[0].set_value(value1); - frame_.values[0].set_value(value2); + frame_.values[1].set_value(value1); + frame_.values[2].set_value(value2); boost::detail::test_results().push_context(frame_); } From 1321debde967eec9aa6b1db6cb0c1b4f2f0512c3 Mon Sep 17 00:00:00 2001 From: Ruben Perez Date: Sun, 15 Jun 2025 19:09:43 +0200 Subject: [PATCH 4/6] Enclose context values in ' --- include/boost/core/lightweight_test.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/boost/core/lightweight_test.hpp b/include/boost/core/lightweight_test.hpp index 7f3fdfd5..2a1d3af4 100644 --- a/include/boost/core/lightweight_test.hpp +++ b/include/boost/core/lightweight_test.hpp @@ -107,7 +107,8 @@ struct lwt_context_frame template static void do_print(const void* value) { - BOOST_LIGHTWEIGHT_TEST_OSTREAM << boost::detail::test_output_impl(*static_cast(value)); + BOOST_LIGHTWEIGHT_TEST_OSTREAM << '\'' + << boost::detail::test_output_impl(*static_cast(value)) << '\''; } // A type-erased reference to a streamable value From 5fc057ef74edb624f719e09894ea114d4e6df1ac Mon Sep 17 00:00:00 2001 From: Ruben Perez Date: Sun, 15 Jun 2025 19:47:41 +0200 Subject: [PATCH 5/6] Docs --- doc/lightweight_test.qbk | 102 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/doc/lightweight_test.qbk b/doc/lightweight_test.qbk index 8c7fa4dd..afbd95e0 100644 --- a/doc/lightweight_test.qbk +++ b/doc/lightweight_test.qbk @@ -59,6 +59,19 @@ namespace core void lwt_init(); +class lwt_context +{ +public: + template explicit lwt_context(const T* value); + template lwt_context(const T0* value0, const T1* value1); + template lwt_context(const T0* value0, const T1* value1, const T2* value2); + + lwt_context(const lwt_context&) = delete; + lwt_context& operator=(const lwt_context&) = delete; + + ~lwt_context(); +}; + } // namespace core } // namespace boost `` @@ -273,6 +286,59 @@ to e.g. an assertion failure before the first test macro is invoked. [endsect] +[section lwt_context] + +This class can be used to attach context to tests. When an assertion fails, +the values passed to `lwt_context` will be printed. This is useful when +the assertion's source location is not enough by itself to diagnose +the cause of failures. Potential use cases include: + +* Parametric tests: when running the same test function over a range of inputs, + `lwt_context` can be used to print the parameter value that caused the assertion to fail. +* When writing utility functions called from several tests, `lwt_context` can be used to + diagnose which test caused the function to fail. + +The context is arranged as a stack composed of frames. When constructing a `lwt_context` +object, a new context frame is pushed to the stack. When it is destroyed, +the frame is popped. + + +`` +template +explicit lwt_context(const T* value); + +template +lwt_context(const T0* value0, const T1* value1); + +template +lwt_context(const T0* value0, const T1* value1, const T2* value2); +`` + +Pushes a new frame to the top of the context stack. Depending on the chosen constructor, +the frame will contain one, two, or three values. +These values will be printed using `operator<<` to `std::cerr` when an assertion fails. + +The caller retains ownership of the passed values. They should be kept alive until +the context stack frame is popped by `lwt_context::~lwt_context()`. + +`` +lwt_context(const lwt_context&) = delete; +lwt_context& operator=(const lwt_context&) = delete; +`` + +This class is neither copyable not movable. + +`` +~lwt_context(); +`` + +Removes the top frame from the context stack. + + +[endsect] + + + [section Example] `` @@ -294,6 +360,42 @@ int main() [endsect] +[section Parametric test example] + +`` +#include + +int sqr( int x ) +{ + return x * x; +} + +int main() +{ + struct + { + int input; + int expected; + } test_cases [] = { + { 2, 4 }, + { -3, 9 }, + { 0, 0 } + }; + + for (const auto& tc : test_cases) + { + // Print the offending parameter on error + boost::core::lwt_context frame (&input); + + BOOST_TEST_EQ( sqr(tc.input), tc.expected ); + } + + return boost::report_errors(); +} +`` + +[endsect] + [endsect] [section Header ] From e70ad2626d86dbca0d25ad752da3a4841901088a Mon Sep 17 00:00:00 2001 From: Ruben Perez Date: Sun, 15 Jun 2025 23:04:59 +0200 Subject: [PATCH 6/6] First test --- test/Jamfile.v2 | 2 ++ test/lightweight_test_context1.cpp | 57 ++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 test/lightweight_test_context1.cpp diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index c8d302f5..a22a89e8 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -176,6 +176,8 @@ run lightweight_test_with_test.cpp : : : $(pedantic-errors) ; run-fail lightweight_test_with_fail.cpp ; +run lightweight_test_context1.cpp ; + run is_same_test.cpp ; run typeinfo_test.cpp ; diff --git a/test/lightweight_test_context1.cpp b/test/lightweight_test_context1.cpp new file mode 100644 index 00000000..ee0d83cf --- /dev/null +++ b/test/lightweight_test_context1.cpp @@ -0,0 +1,57 @@ +// +// Negative test for BOOST_TEST +// +// Copyright (c) 2014 Peter Dimov +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// + +#include +#include +#include +#include + +static std::stringstream test_log; // Intercept logging + +#define BOOST_LIGHTWEIGHT_TEST_OSTREAM ::test_log +#include + +static bool check_log(const char* expected) +{ + std::string str = test_log.str(); + std::string::size_type pos = str.find('\n'); + std::string substr; + if (pos != std::string::npos) { + substr = str.substr(pos + 1); + } + if (substr != expected) { + std::cerr << "Error: expected the following log:\n" << expected << "\n, got:\n" << substr << std::endl; + return false; + } + return true; +} + +int main() +{ + // Run a test that fails + int x = 42; + boost::core::lwt_context frame (&x); + BOOST_TEST( x == 1 ); + + // Check the log + const char* const expected_log = "Failure happened in the following context:\n #0: '42'\n"; + bool log_ok = check_log(expected_log); + + // Check that we get the number of expected errors + int actual_errors = boost::report_errors(); + const int expected_errors = 1; + if (actual_errors != expected_errors) + { + std::cerr << "Expected " << expected_errors << ", got " << actual_errors << " errors\n"; + return 1; + } + + return log_ok ? 0 : 1; +}