Move other ASSERT_EQUAL_XXX macros to a header too

Prepare for reusing them from other source files.

Note that this requires using test_context from test-assert.h, which now
has to include test-context.h.
This commit is contained in:
Vadim Zeitlin
2024-11-06 15:29:07 +01:00
parent 5b4e49785a
commit b22ae661b4
2 changed files with 74 additions and 69 deletions

View File

@@ -36,8 +36,6 @@
#include <clocale>
#include <cstdint>
#include <cstdlib>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <limits>
#include <string>
@@ -47,7 +45,6 @@
#include "soci-mktime.h"
#include "test-assert.h"
#include "test-context.h"
#include "test-myint.h"
// Although SQL standard mandates right padding CHAR(N) values to their length
@@ -187,71 +184,6 @@ template<> struct type_conversion<PhonebookEntry3>
namespace tests
{
// Compare doubles for approximate equality. This has to be used everywhere
// where we write "3.14" (or "6.28") to the database as a string and then
// compare the value read back with the literal 3.14 floating point constant
// because they are not the same.
//
// It is also used for the backends which currently don't handle doubles
// correctly.
//
// Notice that this function is normally not used directly but rather from the
// macro below.
inline bool are_doubles_approx_equal(double const a, double const b)
{
// The formula taken from CATCH test framework
// https://github.com/philsquared/Catch/
// Thanks to Richard Harris for his help refining this formula
double const epsilon(std::numeric_limits<float>::epsilon() * 100);
double const scale(1.0);
return std::fabs(a - b) < epsilon * (scale + (std::max)(std::fabs(a), std::fabs(b)));
}
// This is a macro to ensure we use the correct line numbers. The weird
// do/while construction is used to make this a statement and the even weirder
// condition in while ensures that the loop is executed exactly once without
// triggering warnings from MSVC about the condition being always false.
#define ASSERT_EQUAL_APPROX(a, b) \
do { \
if (!are_doubles_approx_equal((a), (b))) { \
FAIL( "Approximate equality check failed: " \
<< std::fixed \
<< std::setprecision(std::numeric_limits<double>::digits10 + 1) \
<< (a) << " != " << (b) ); \
} \
} while ( (void)0, 0 )
// Compare two floating point numbers either exactly or approximately depending
// on test_context::has_fp_bug() return value.
inline bool
are_doubles_equal(test_context_base const& tc, double a, double b)
{
return tc.has_fp_bug()
? are_doubles_approx_equal(a, b)
: are_doubles_exactly_equal(a, b);
}
// This macro should be used when where we don't have any problems with string
// literals vs floating point literals mismatches described above and would
// ideally compare the numbers exactly but, unfortunately, currently can't do
// this unconditionally because at least some backends are currently buggy and
// don't handle the floating point values correctly.
//
// This can be only used from inside the common_tests class as it relies on
// having an accessible "tc_" variable to determine whether exact or
// approximate comparison should be used.
#define ASSERT_EQUAL(a, b) \
do { \
if (!are_doubles_equal(tc_, (a), (b))) { \
FAIL( "Equality check failed: " \
<< std::fixed \
<< std::setprecision(std::numeric_limits<double>::digits10 + 1) \
<< (a) << " != " << (b) ); \
} \
} while ( (void)0, 0 )
// Define the test cases in their own namespace to avoid clashes with the test
// cases defined in individual backend tests: as only line number is used for
// building the name of the "anonymous" function by the TEST_CASE macro, we

View File

@@ -10,6 +10,11 @@
#include "soci-compiler.h"
#include <cmath>
#include <iomanip>
#include "test-context.h"
namespace soci
{
@@ -29,11 +34,59 @@ are_doubles_exactly_equal(double a, double b)
SOCI_GCC_WARNING_RESTORE(float-equal)
}
// Compare doubles for approximate equality. This has to be used everywhere
// where we write "3.14" (or "6.28") to the database as a string and then
// compare the value read back with the literal 3.14 floating point constant
// because they are not the same.
//
// It is also used for the backends which currently don't handle doubles
// correctly.
//
// Notice that this function is normally not used directly but rather from the
// macro below.
inline bool are_doubles_approx_equal(double const a, double const b)
{
// The formula taken from CATCH test framework
// https://github.com/philsquared/Catch/
// Thanks to Richard Harris for his help refining this formula
double const epsilon(std::numeric_limits<float>::epsilon() * 100);
double const scale(1.0);
return std::fabs(a - b) < epsilon * (scale + (std::max)(std::fabs(a), std::fabs(b)));
}
// Compare two floating point numbers either exactly or approximately depending
// on test_context::has_fp_bug() return value.
inline bool
are_doubles_equal(test_context_base const& tc, double a, double b)
{
return tc.has_fp_bug()
? are_doubles_approx_equal(a, b)
: are_doubles_exactly_equal(a, b);
}
} // namespace tests
} // namespace soci
// This macro can be only used inside CATCH tests.
// Define macros for comparing floating point numbers.
//
// All of them can only be used inside CATCH test cases.
// This is a macro to ensure we use the correct line numbers. The weird
// do/while construction is used to make this a statement and the even weirder
// condition in while ensures that the loop is executed exactly once without
// triggering warnings from MSVC about the condition being always false.
#define ASSERT_EQUAL_APPROX(a, b) \
do { \
if (!are_doubles_approx_equal((a), (b))) { \
FAIL( "Approximate equality check failed: " \
<< std::fixed \
<< std::setprecision(std::numeric_limits<double>::digits10 + 1) \
<< (a) << " != " << (b) ); \
} \
} while ( (void)0, 0 )
#define ASSERT_EQUAL_EXACT(a, b) \
do { \
if (!are_doubles_exactly_equal((a), (b))) { \
@@ -44,4 +97,24 @@ are_doubles_exactly_equal(double a, double b)
} \
} while ( (void)0, 0 )
// This macro should be used when where we don't have any problems with string
// literals vs floating point literals mismatches described above and would
// ideally compare the numbers exactly but, unfortunately, currently can't do
// this unconditionally because at least some backends are currently buggy and
// don't handle the floating point values correctly.
//
// This can be only used from inside the common_tests class as it relies on
// having an accessible "tc_" variable to determine whether exact or
// approximate comparison should be used.
#define ASSERT_EQUAL(a, b) \
do { \
if (!are_doubles_equal(tc_, (a), (b))) { \
FAIL( "Equality check failed: " \
<< std::fixed \
<< std::setprecision(std::numeric_limits<double>::digits10 + 1) \
<< (a) << " != " << (b) ); \
} \
} while ( (void)0, 0 )
#endif // SOCI_TEST_ASSERT_H_INCLUDED