Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test improvements - Filename and text context #244

Merged
merged 2 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
197 changes: 115 additions & 82 deletions test/boostLocale/test/unit_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define BOOST_LOCALE_UNIT_TEST_HPP

#include <boost/locale/config.hpp>
#include <boost/config/helper_macros.hpp>
#include <cstdlib>
#include <iomanip>
#include <iostream>
Expand All @@ -23,12 +24,20 @@
# include <crtdbg.h>
#endif

#ifndef BOOST_LOCALE_ERROR_LIMIT
# define BOOST_LOCALE_ERROR_LIMIT 20
#endif

#define BOOST_LOCALE_STRINGIZE(x) #x

namespace boost { namespace locale { namespace test {
/// Name/path of current executable
std::string exe_name;

class test_context;

struct test_result {
test_result() : error_counter(0), test_counter(0)
test_result()
{
#if defined(_MSC_VER) && (_MSC_VER > 1310)
// disable message boxes on assert(), abort()
Expand All @@ -40,60 +49,85 @@ namespace boost { namespace locale { namespace test {
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
#endif
}
int error_counter;
int test_counter;
int error_counter = 0;
int test_counter = 0;
const test_context* context = nullptr;
};
inline test_result& results()
{
static test_result instance;
return instance;
}
}}} // namespace boost::locale::test

#ifndef BOOST_LOCALE_ERROR_LIMIT
# define BOOST_LOCALE_ERROR_LIMIT 20
#endif
class test_context {
const test_context* oldCtx_;
const std::string msg_;

#define BOOST_LOCALE_STRINGIZE(x) #x
public:
test_context(std::string ctx) : oldCtx_(results().context), msg_(std::move(ctx)) { results().context = this; }
~test_context() { results().context = oldCtx_; }
friend std::ostream& operator<<(std::ostream& os, const test_context& c)
{
const test_context* current = &c;
os << "CONTEXT: ";
std::string indent = "\n\t";
do {
os << indent << current->msg_;
indent += '\t';
} while((current = current->oldCtx_) != nullptr);
return os;
}
};

#define THROW_IF_TOO_BIG(X) \
if((X) > BOOST_LOCALE_ERROR_LIMIT) \
throw std::runtime_error("Error limits reached, stopping unit test")

#define TEST(X) \
do { \
boost::locale::test::results().test_counter++; \
if(X) \
break; \
std::cerr << "Error in line:" << __LINE__ << " " #X << std::endl; \
THROW_IF_TOO_BIG(boost::locale::test::results().error_counter++); \
BOOST_LOCALE_START_CONST_CONDITION \
inline void report_error(const char* expr, const char* file, int line)
{
std::cerr << "Error at " << file << '#' << line << ": " << expr << std::endl;
const auto* context = results().context;
if(context)
std::cerr << ' ' << *context << std::endl;
if(++boost::locale::test::results().error_counter > BOOST_LOCALE_ERROR_LIMIT)
throw std::runtime_error("Error limits reached, stopping unit test");
}
}}} // namespace boost::locale::test

#define BOOST_LOCALE_TEST_REPORT_ERROR(expr) boost::locale::test::report_error(expr, __FILE__, __LINE__)

#define TEST(X) \
do { \
boost::locale::test::results().test_counter++; \
if(X) \
break; \
BOOST_LOCALE_TEST_REPORT_ERROR(#X); \
BOOST_LOCALE_START_CONST_CONDITION \
} while(0) BOOST_LOCALE_END_CONST_CONDITION

#define TEST_REQUIRE(X) \
do { \
boost::locale::test::results().test_counter++; \
if(X) \
break; \
std::cerr << "Error in line " << __LINE__ << ": " #X << std::endl; \
throw std::runtime_error("Critical test " #X " failed"); \
BOOST_LOCALE_START_CONST_CONDITION \
#define TEST_REQUIRE(X) \
do { \
boost::locale::test::results().test_counter++; \
if(X) \
break; \
BOOST_LOCALE_TEST_REPORT_ERROR(#X); \
throw std::runtime_error("Critical test " #X " failed"); \
BOOST_LOCALE_START_CONST_CONDITION \
} while(0) BOOST_LOCALE_END_CONST_CONDITION

#define TEST_THROWS(X, E) \
do { \
boost::locale::test::results().test_counter++; \
try { \
X; \
} catch(E const& /*e*/) { \
break; \
} catch(...) { \
} \
std::cerr << "Error in line " << __LINE__ << ": " #X << std::endl; \
THROW_IF_TOO_BIG(boost::locale::test::results().error_counter++); \
BOOST_LOCALE_START_CONST_CONDITION \
#define TEST_THROWS(X, E) \
do { \
boost::locale::test::results().test_counter++; \
try { \
X; \
} catch(E const& /*e*/) { \
break; \
} catch(...) { \
} \
BOOST_LOCALE_TEST_REPORT_ERROR(#X); \
BOOST_LOCALE_START_CONST_CONDITION \
} while(0) BOOST_LOCALE_END_CONST_CONDITION

#define TEST_CONTEXT(expr) \
boost::locale::test::test_context BOOST_JOIN(test_context_, __COUNTER__)( \
static_cast<const std::stringstream&>(std::stringstream{} << expr).str())

void test_main(int argc, char** argv);

int main(int argc, char** argv)
Expand Down Expand Up @@ -138,6 +172,11 @@ const std::string& to_string(const std::string& s)
return s;
}

std::string to_string(std::nullptr_t)
{
return "<nullptr>";
}

template<typename T>
std::string to_string(const std::vector<T>& v)
{
Expand Down Expand Up @@ -223,58 +262,52 @@ std::string to_string(const char32_t c)
#endif

template<typename T, typename U>
void test_impl(bool success, T const& l, U const& r, const char* expr, const char* fail_expr, int line)
void test_impl(bool success,
T const& l,
U const& r,
const char* expr,
const char* fail_expr,
const char* file,
int line)
{
boost::locale::test::results().test_counter++;
if(!success) {
std::cerr << "Error in line " << line << ": " << expr << std::endl;
std::cerr << "---- [" << to_string(l) << "] " << fail_expr << " [" << to_string(r) << "]" << std::endl;
THROW_IF_TOO_BIG(boost::locale::test::results().error_counter++);
if(fail_expr) {
std::ostringstream s;
s << expr << '\n' << "---- [" << to_string(l) << "] " << fail_expr << " [" << to_string(r) << "]";
boost::locale::test::report_error(s.str().c_str(), file, line);
} else
boost::locale::test::report_error(expr, file, line);
}
}

template<typename T, typename U>
void test_eq_impl(T const& l, U const& r, const char* expr, int line)
{
test_impl(l == r, l, r, expr, "!=", line);
}

template<typename T, typename U>
void test_ne_impl(T const& l, U const& r, const char* expr, int line)
{
test_impl(l != r, l, r, expr, "==", line);
}

template<typename T, typename U>
void test_le_impl(T const& l, U const& r, const char* expr, int line)
{
test_impl(l <= r, l, r, expr, ">", line);
}

template<typename T, typename U>
void test_lt_impl(T const& l, U const& r, const char* expr, int line)
void test_impl(bool success, const char* reason, const char* file, int line)
{
test_impl(l < r, l, r, expr, ">=", line);
test_impl(success, nullptr, nullptr, reason, nullptr, file, line);
}

template<typename T, typename U>
void test_ge_impl(T const& l, U const& r, const char* expr, int line)
{
test_impl(l >= r, l, r, expr, "<", line);
}

template<typename T, typename U>
void test_gt_impl(T const& l, U const& r, const char* expr, int line)
{
test_impl(l > r, l, r, expr, "<=", line);
}
#define BOOST_LOCALE_TEST_OP_IMPL(name, test_op, fail_op) \
template<typename T, typename U> \
void test_##name##_impl(T const& l, U const& r, const char* expr, const char* file, int line) \
{ \
test_impl(l test_op r, l, r, expr, #fail_op, file, line); \
}

#define TEST_EQ(x, y) test_eq_impl(x, y, BOOST_LOCALE_STRINGIZE(x == y), __LINE__)
#define TEST_NE(x, y) test_ne_impl(x, y, BOOST_LOCALE_STRINGIZE(x != y), __LINE__)
#define TEST_LE(x, y) test_le_impl(x, y, BOOST_LOCALE_STRINGIZE(x <= y), __LINE__)
#define TEST_LT(x, y) test_lt_impl(x, y, BOOST_LOCALE_STRINGIZE(x < y), __LINE__)
#define TEST_GE(x, y) test_ge_impl(x, y, BOOST_LOCALE_STRINGIZE(x >= y), __LINE__)
#define TEST_GT(x, y) test_gt_impl(x, y, BOOST_LOCALE_STRINGIZE(x > y), __LINE__)
BOOST_LOCALE_TEST_OP_IMPL(eq, ==, !=)
BOOST_LOCALE_TEST_OP_IMPL(ne, !=, ==)
BOOST_LOCALE_TEST_OP_IMPL(le, <=, >)
BOOST_LOCALE_TEST_OP_IMPL(lt, <, >=)
BOOST_LOCALE_TEST_OP_IMPL(ge, >=, <)
BOOST_LOCALE_TEST_OP_IMPL(gt, >, <=)

#undef BOOST_LOCALE_TEST_OP_IMPL

#define TEST_EQ(x, y) test_eq_impl(x, y, BOOST_LOCALE_STRINGIZE(x == y), __FILE__, __LINE__)
#define TEST_NE(x, y) test_ne_impl(x, y, BOOST_LOCALE_STRINGIZE(x != y), __FILE__, __LINE__)
#define TEST_LE(x, y) test_le_impl(x, y, BOOST_LOCALE_STRINGIZE(x <= y), __FILE__, __LINE__)
#define TEST_LT(x, y) test_lt_impl(x, y, BOOST_LOCALE_STRINGIZE(x < y), __FILE__, __LINE__)
#define TEST_GE(x, y) test_ge_impl(x, y, BOOST_LOCALE_STRINGIZE(x >= y), __FILE__, __LINE__)
#define TEST_GT(x, y) test_gt_impl(x, y, BOOST_LOCALE_STRINGIZE(x > y), __FILE__, __LINE__)

#if BOOST_LOCALE_SPACESHIP_NULLPTR_WARNING
# pragma clang diagnostic pop
Expand Down
8 changes: 3 additions & 5 deletions test/test_catalog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,8 @@ void test_plural_expr_rand(const T& ref, const char* expr)
const auto n = getRandValue(minVal, maxVal);
const auto result = ptr(n);
const auto refResult = ref(n);
if(result != refResult) {
std::cerr << "Expression: " << expr << "; n=" << n << '\n'; // LCOV_EXCL_LINE
TEST_EQ(result, refResult); // LCOV_EXCL_LINE
}
TEST_CONTEXT("Expression: " << expr << "; n=" << n);
TEST_EQ(result, refResult);
}
}

Expand All @@ -47,7 +45,7 @@ void test_plural_expr()
TEST(ptr); \
return ptr; \
}()
#define TEST_EQ_EXPR(expr, rhs) test_eq_impl(COMPILE_PLURAL_EXPR(expr)(0), rhs, expr, __LINE__)
#define TEST_EQ_EXPR(expr, rhs) test_eq_impl(COMPILE_PLURAL_EXPR(expr)(0), rhs, expr, __FILE__, __LINE__)
// Number only
TEST_EQ_EXPR("0", 0);
TEST_EQ_EXPR("42", 42);
Expand Down
4 changes: 1 addition & 3 deletions test/test_codecvt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@ void test_codecvt_in_n_m(const cvt_type& cvt, int n, int m)
std::mbstate_t mb2 = mb;
std::codecvt_base::result r = cvt.in(mb, from, end, from_next, to, to_end, to_next);

int count = cvt.length(mb2, from, end, to_end - to);
const int count = cvt.length(mb2, from, end, to_end - to);
TEST_EQ(memcmp(&mb, &mb2, sizeof(mb)), 0);
if(count != from_next - from)
std::cout << count << " " << from_next - from << std::endl; // LCOV_EXCL_LINE
TEST_EQ(count, from_next - from);

if(r == cvt_type::partial) {
Expand Down
2 changes: 1 addition & 1 deletion test/test_date_time.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

#define TEST_EQ_FMT(t, X) \
empty_stream(ss) << (t); \
test_eq_impl(ss.str(), X, #t "==" #X, __LINE__)
test_eq_impl(ss.str(), X, #t "==" #X, __FILE__, __LINE__)

// Very simple container for a part of the tests. Counts its instances
struct mock_calendar : public boost::locale::abstract_calendar {
Expand Down
14 changes: 6 additions & 8 deletions test/test_encoding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -832,10 +832,9 @@ void test_simple_encodings()
const auto encodings = get_simple_encodings();
for(auto it = encodings.begin(), end = encodings.end(); it != end; ++it) {
TEST_EQ(normalize_encoding(*it), *it); // Must be normalized
const auto it2 = std::find(it + 1, end, *it);
TEST(it2 == end);
if(it2 != end)
std::cerr << "Duplicate entry: " << *it << '\n'; // LCOV_EXCL_LINE
TEST_CONTEXT("Entry: " << *it);
// Must be unique
TEST(std::find(it + 1, end, *it) == end);
}
const auto it = std::is_sorted_until(encodings.begin(), encodings.end());
TEST(it == encodings.end());
Expand All @@ -852,10 +851,9 @@ void test_win_codepages()
auto is_same_win_codepage = [&it](const windows_encoding& rhs) -> bool {
return it->codepage == rhs.codepage && std::strcmp(it->name, rhs.name) == 0;
};
const auto* it2 = std::find_if(it + 1, end, is_same_win_codepage);
TEST(it2 == end);
if(it2 != end)
std::cerr << "Duplicate entry: " << it->name << ':' << it->codepage << '\n'; // LCOV_EXCL_LINE
TEST_CONTEXT("Entry: " << it->name << ':' << it->codepage);
// Must be unique
TEST(std::find_if(it + 1, end, is_same_win_codepage) == end);
}
const auto cmp = [](const windows_encoding& rhs, const windows_encoding& lhs) -> bool { return rhs < lhs.name; };
const auto* it = std::is_sorted_until(all_windows_encodings, std::end(all_windows_encodings), cmp);
Expand Down
Loading
Loading