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) {             \