pw_unit_test: Support *_NEAR, *_FLOAT_EQ, *_DOUBLE_EQ
Add implementation of the _NEAR checks using existing infrastructure.
The same method is used for comparing floats and double.
Error messaging was shortened compared to gTest's to save on buffer
space.
Change-Id: I8b5d490d48f756c61a6b7008e221c522f747fcae
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/179770
Reviewed-by: Alexei Frolov <frolv@google.com>
Commit-Queue: Yuval Peress <peress@google.com>
diff --git a/pw_unit_test/framework_test.cc b/pw_unit_test/framework_test.cc
index 2e1fe90..211c665 100644
--- a/pw_unit_test/framework_test.cc
+++ b/pw_unit_test/framework_test.cc
@@ -57,6 +57,46 @@
ASSERT_LE(-2, -2);
}
+TEST(PigweedTest, ExpectNearComparisons) {
+ EXPECT_NEAR(1, 2, 1);
+ ASSERT_NEAR(1, 2, 1);
+
+ EXPECT_NEAR(-5, 5, 10);
+ ASSERT_NEAR(-5, 5, 10);
+
+ int x = 17;
+ int epsilon = 5;
+
+ EXPECT_NEAR(x, 15, epsilon);
+ ASSERT_NEAR(x, 15, epsilon);
+}
+
+TEST(PigweedTest, ExpectFloatComparisons) {
+ EXPECT_FLOAT_EQ(5.0f, 10.0f / 2);
+ ASSERT_FLOAT_EQ(5.0f, 10.0f / 2);
+
+ EXPECT_FLOAT_EQ(-0.5f, -5.0f / 10);
+ ASSERT_FLOAT_EQ(-0.5f, -5.0f / 10);
+
+ float x = 17.0f / 20.0f;
+
+ EXPECT_FLOAT_EQ(x, 17.0f / 20.0f);
+ ASSERT_FLOAT_EQ(x, 17.0f / 20.0f);
+}
+
+TEST(PigweedTest, ExpectDoubleComparisons) {
+ EXPECT_DOUBLE_EQ(5.0, 10.0 / 2);
+ ASSERT_DOUBLE_EQ(5.0, 10.0 / 2);
+
+ EXPECT_DOUBLE_EQ(-0.5, -5.0 / 10);
+ ASSERT_DOUBLE_EQ(-0.5, -5.0 / 10);
+
+ double x = 17.0 / 20.0;
+
+ EXPECT_DOUBLE_EQ(x, 17.0 / 20.0);
+ ASSERT_DOUBLE_EQ(x, 17.0 / 20.0);
+}
+
TEST(PigweedTest, ExpectStringEquality) {
EXPECT_STREQ("", "");
EXPECT_STREQ("Yes", "Yes");
diff --git a/pw_unit_test/public/pw_unit_test/internal/framework.h b/pw_unit_test/public/pw_unit_test/internal/framework.h
index fce2293..78a5933 100644
--- a/pw_unit_test/public/pw_unit_test/internal/framework.h
+++ b/pw_unit_test/public/pw_unit_test/internal/framework.h
@@ -60,6 +60,14 @@
#define EXPECT_GE(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, >=))
#define EXPECT_LT(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, <))
#define EXPECT_LE(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_OP(lhs, rhs, <=))
+#define EXPECT_NEAR(lhs, rhs, epsilon) \
+ _PW_TEST_EXPECT(_PW_TEST_NEAR(lhs, rhs, epsilon))
+#define EXPECT_FLOAT_EQ(lhs, rhs) \
+ _PW_TEST_EXPECT( \
+ _PW_TEST_NEAR(lhs, rhs, 4 * std::numeric_limits<float>::epsilon()))
+#define EXPECT_DOUBLE_EQ(lhs, rhs) \
+ _PW_TEST_EXPECT( \
+ _PW_TEST_NEAR(lhs, rhs, 4 * std::numeric_limits<double>::epsilon()))
#define EXPECT_STREQ(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_C_STR(lhs, rhs, ==))
#define EXPECT_STRNE(lhs, rhs) _PW_TEST_EXPECT(_PW_TEST_C_STR(lhs, rhs, !=))
@@ -71,6 +79,14 @@
#define ASSERT_GE(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, >=))
#define ASSERT_LT(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, <))
#define ASSERT_LE(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_OP(lhs, rhs, <=))
+#define ASSERT_NEAR(lhs, rhs, epsilon) \
+ _PW_TEST_ASSERT(_PW_TEST_NEAR(lhs, rhs, epsilon))
+#define ASSERT_FLOAT_EQ(lhs, rhs) \
+ _PW_TEST_ASSERT( \
+ _PW_TEST_NEAR(lhs, rhs, 4 * std::numeric_limits<float>::epsilon()))
+#define ASSERT_DOUBLE_EQ(lhs, rhs) \
+ _PW_TEST_ASSERT( \
+ _PW_TEST_NEAR(lhs, rhs, 4 * std::numeric_limits<double>::epsilon()))
#define ASSERT_STREQ(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_C_STR(lhs, rhs, ==))
#define ASSERT_STRNE(lhs, rhs) _PW_TEST_ASSERT(_PW_TEST_C_STR(lhs, rhs, !=))
@@ -309,6 +325,37 @@
framework.EndCurrentTest();
}
+ template <typename Expectation, typename Lhs, typename Rhs, typename Epsilon>
+ bool CurrentTestExpect(Expectation expectation,
+ const Lhs& lhs,
+ const Rhs& rhs,
+ const Epsilon& epsilon,
+ const char* expression,
+ int line) {
+ // Size of the buffer into which to write the string with the evaluated
+ // version of the arguments. This buffer is allocated on the unit test's
+ // stack, so it shouldn't be too large.
+ // TODO(hepler): Make this configurable.
+ [[maybe_unused]] constexpr size_t kExpectationBufferSizeBytes = 192;
+
+ const bool success = expectation(lhs, rhs, epsilon);
+ CurrentTestExpectSimple(
+ expression,
+#if PW_CXX_STANDARD_IS_SUPPORTED(17)
+ MakeString<kExpectationBufferSizeBytes>(ConvertForPrint(lhs),
+ " within ",
+ ConvertForPrint(epsilon),
+ " of ",
+ ConvertForPrint(rhs))
+ .c_str(),
+#else
+ "(evaluation requires C++17)",
+#endif // PW_CXX_STANDARD_IS_SUPPORTED(17)
+ line,
+ success);
+ return success;
+ }
+
// Runs an expectation function for the currently active test case.
template <typename Expectation, typename Lhs, typename Rhs>
bool CurrentTestExpect(Expectation expectation,
@@ -625,6 +672,17 @@
#lhs " " #op " " #rhs, \
__LINE__)
+#define _PW_TEST_NEAR(lhs, rhs, epsilon) \
+ ::pw::unit_test::internal::Framework::Get().CurrentTestExpect( \
+ [](const auto& _pw_lhs, const auto& _pw_rhs, const auto& _pw_epsilon) { \
+ return std::abs(_pw_lhs - _pw_rhs) <= _pw_epsilon; \
+ }, \
+ (lhs), \
+ (rhs), \
+ (epsilon), \
+ #lhs " within " #epsilon " of " #rhs, \
+ __LINE__)
+
#define _PW_TEST_C_STR(lhs, rhs, op) \
::pw::unit_test::internal::Framework::Get().CurrentTestExpect( \
[](const auto& _pw_lhs, const auto& _pw_rhs) { \