Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 181 additions & 70 deletions include/boost/core/lightweight_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,92 @@ namespace boost
namespace detail
{

// specialize test output for char pointers to avoid printing as cstring
template <class T> 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<class T> inline const void* test_output_impl(T volatile* v) { return const_cast<T*>(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<bool Signed> struct lwt_long_type {};
template<> struct lwt_long_type<true> { typedef long type; };
template<> struct lwt_long_type<false> { typedef unsigned long type; };

inline lwt_long_type<(static_cast<wchar_t>(-1) < static_cast<wchar_t>(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<unsigned char>( 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<unsigned char>( v ) >> 4u) & 0x0f ];
buffer[ 3 ] = char_table[ static_cast<unsigned char>( 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 <class T>
static void do_print(const void* value)
{
BOOST_LIGHTWEIGHT_TEST_OSTREAM << boost::detail::test_output_impl(*static_cast<const T*>(value));
}

// A type-erased reference to a streamable value
struct context_value {
const void* obj;
void (*print_fn) (const void*);

void print() const { print_fn(obj); }
void set_null() { obj = NULL; print_fn = NULL; }
template <class T> void set_value(const T* value) { obj = value; print_fn = &do_print<T>; }
};

// 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];
};

class test_result
{
public:

test_result(): report_( false ), errors_( 0 )
test_result(): report_( false ), errors_( 0 ), context_ ( NULL )
{
core::detail::lwt_unattended();
}
Expand All @@ -63,7 +144,7 @@ class test_result
}
}

int& errors()
int errors() const
{
return errors_;
}
Expand All @@ -73,10 +154,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, frame = frame->next)
{
// Print the header and the first value, which should always be present
BOOST_LIGHTWEIGHT_TEST_OSTREAM << " #" << i << ": ";
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].print();
}

// Finish the frame
BOOST_LIGHTWEIGHT_TEST_OSTREAM << '\n';
}
}
};

inline test_result& test_results()
Expand All @@ -85,11 +212,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 )
Expand All @@ -102,7 +224,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;
}
}
Expand All @@ -112,31 +234,31 @@ 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)
{
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)
{
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)
{
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.
Expand All @@ -156,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 <class T> 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<class T> inline const void* test_output_impl(T volatile* v) { return const_cast<T*>(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<bool Signed> struct lwt_long_type {};
template<> struct lwt_long_type<true> { typedef long type; };
template<> struct lwt_long_type<false> { typedef unsigned long type; };

inline lwt_long_type<(static_cast<wchar_t>(-1) < static_cast<wchar_t>(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<unsigned char>( 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<unsigned char>( v ) >> 4u) & 0x0f ];
buffer[ 3 ] = char_table[ static_cast<unsigned char>( v ) & 0x0f ];

return std::string( buffer, 4u );
}
}

// predicates

Expand Down Expand Up @@ -303,7 +372,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;
}
}
Expand All @@ -321,7 +390,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;
}
}
Expand All @@ -339,7 +408,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;
}
}
Expand Down Expand Up @@ -409,7 +478,7 @@ bool test_all_eq_impl(FormattedOutputFunction& output,
else
{
output << std::endl;
++test_results().errors();
test_results().on_error();
return false;
}
}
Expand Down Expand Up @@ -480,7 +549,7 @@ bool test_all_with_impl(FormattedOutputFunction& output,
else
{
output << std::endl;
++test_results().errors();
test_results().on_error();
return false;
}
}
Expand Down Expand Up @@ -527,6 +596,48 @@ inline void lwt_init()
boost::detail::test_results();
}

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; }

public:
template <class T>
explicit lwt_context(const T* value)
{
frame_.values[0].set_value(value);
frame_.values[1].set_null();
frame_.values[2].set_null();
boost::detail::test_results().push_context(frame_);
}

template <class T0, class T1>
lwt_context(const T0* value0, const T1* value1)
{
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 <class T0, class T1, class T2>
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);
boost::detail::test_results().push_context(frame_);
}

~lwt_context()
{
boost::detail::test_results().pop_context();
}
};

} // namespace core
} // namespace boost

Expand Down
4 changes: 2 additions & 2 deletions include/boost/core/lightweight_test_trait.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}

Expand All @@ -71,7 +71,7 @@ template<class T1, class T2> inline void test_trait_same_impl( char const * type
<< "' != '" << boost::core::type_name<T2>() << "')"
<< std::endl;

++test_results().errors();
test_results().on_error();
}
}

Expand Down
Loading