Add features to test framework

* Make binary comparison macros variadic, allowing you
  to use them with initializer lists.
  eg. EXPECT_EQ(..., std::vector<std::string>{"a", "b"})
* Implement a two-argument form of EXPECT/ASSERT_SUCCESS(result, expected)
  that asserts that Result<T> is Ok(expected).
* Implement both 1 and 2-argument forms of EXPECT_FAILURE
  * 1-argument form expects that there was an error
  * 2-argument form expects that there was an error with the message
    containing a specific string
* Implement a pretty-printer for Err and Result, allowing you to print
  things like:
  * "Expected failure but got Ok(1)"
  * Expected success but got Err("error message")

Change-Id: I16c8bc2eb6761ff1aa3daa04a9cf85446a6a6964
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/25280
Reviewed-by: Takuto Ikuta <tikuta@google.com>
Commit-Queue: Matt Stark <msta@google.com>
diff --git a/build/gen.py b/build/gen.py
index 94fdcb8..1415058 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -689,6 +689,8 @@
         # Enable __cplusplus macro to report the correct C++ standard version,
         # otherwise it defaults to C++98.
         '/Zc:__cplusplus',
+        # Enable use of __VA_OPT__ in macros.
+        '/Zc:preprocessor',
         '/GR-',
         '/D_HAS_EXCEPTIONS=0',
     ])
diff --git a/src/util/test/gn_test.cc b/src/util/test/gn_test.cc
index 717c2d2..0ec1d30 100644
--- a/src/util/test/gn_test.cc
+++ b/src/util/test/gn_test.cc
@@ -44,6 +44,13 @@
   return Pretty(std::string(value));
 }
 
+std::string Pretty(const Err& err) {
+  if (!err.has_error()) {
+    return "no error";
+  }
+  return err.message();
+}
+
 std::string Indent(std::string_view value) {
   std::stringstream ss;
   ss << "  ";
diff --git a/src/util/test/test.h b/src/util/test/test.h
index 6945660..cc2261d 100644
--- a/src/util/test/test.h
+++ b/src/util/test/test.h
@@ -18,6 +18,7 @@
 #include <vector>
 
 #include "base/strings/stringprintf.h"
+#include "gn/err.h"
 
 // This is a minimal googletest-like testing framework. It's originally derived
 // from Ninja's src/test.h. You might prefer that one if you have different
@@ -94,6 +95,7 @@
 
 std::string Pretty(bool value);
 std::string Pretty(const char* value);
+std::string Pretty(const Err& err);
 
 // Explicitly write this for enum, because otherwise it tries to cast enums
 // to bools.
@@ -175,6 +177,17 @@
   return ss.str();
 }
 
+template <typename T>
+std::string Pretty(const Result<T>& result) {
+  if (!result.has_value()) {
+    return "Err(" + Pretty(result.error()) + ")";
+  }
+  if constexpr (requires { Pretty(*result); }) {
+    return "Ok(" + Pretty(*result) + ")";
+  }
+  return "Ok(<unprintable value>)";
+}
+
 template <typename T, typename U>
 std::string TryDiffStrings(const T& expected, const U& actual) {
   if constexpr (requires {
@@ -193,6 +206,38 @@
   }
 }
 
+inline std::optional<std::string> CheckSuccess(const auto& err) {
+  if (err.has_error()) {
+    return "Expected success but got error:\n" + err.message();
+  }
+  return std::nullopt;
+}
+
+template <typename T, typename U>
+inline std::optional<std::string> CheckSuccess(const Result<T>& result,
+                                               const U& expected) {
+  if (auto msg = CheckSuccess(result); msg.has_value()) {
+    return msg;
+  }
+  if (*result != expected) {
+    return ::testing::TryDiffStrings(*result, expected);
+  }
+  return std::nullopt;
+}
+
+inline std::optional<std::string> CheckFailure(
+    auto&& result,
+    std::string_view expected_msg = "") {
+  if (!result.has_error()) {
+    return "Expected failure but got " + Pretty(result);
+  }
+  if (!expected_msg.empty() && !result.message().contains(expected_msg)) {
+    return "Expected error message to contain \"" + std::string(expected_msg) +
+           "\", but actual was:\n" + result.message();
+  }
+  return std::nullopt;
+}
+
 }  // namespace testing
 
 void RegisterTest(testing::Test* (*)(), const char*);
@@ -238,34 +283,39 @@
   return ::testing::AssertHelper(__FILE__, __LINE__, message) = \
              ::testing::Message()
 
-#define EXPECT_EQ(a, b)                                        \
-  TEST_AMBIGUOUS_ELSE_BLOCKER_                                 \
-  if (const ::testing::TestResult test_result =                \
-          ::testing::TestResult(a == b, #a " == " #b))         \
-    ;                                                          \
-  else                                                         \
-    ::testing::AssertHelper(__FILE__, __LINE__, test_result) = \
-        ::testing::Message() << ::testing::TryDiffStrings(a, b)
+#define EXPECT_EQ(a, ...)                                                    \
+  TEST_AMBIGUOUS_ELSE_BLOCKER_                                               \
+  if (const ::testing::TestResult test_result =                              \
+          ::testing::TestResult(a == (__VA_ARGS__), #a " == " #__VA_ARGS__)) \
+    ;                                                                        \
+  else                                                                       \
+    ::testing::AssertHelper(__FILE__, __LINE__, test_result) =               \
+        ::testing::Message() << ::testing::TryDiffStrings(a, (__VA_ARGS__))
 
-#define EXPECT_NE(a, b)                                     \
-  TEST_ASSERT_(::testing::TestResult(a != b, #a " != " #b), \
-               TEST_NONFATAL_FAILURE_)
+#define EXPECT_NE(a, ...)                                                \
+  TEST_ASSERT_(                                                          \
+      ::testing::TestResult(a != (__VA_ARGS__), #a " != " #__VA_ARGS__), \
+      TEST_NONFATAL_FAILURE_)
 
-#define EXPECT_LT(a, b)                                   \
-  TEST_ASSERT_(::testing::TestResult(a < b, #a " < " #b), \
-               TEST_NONFATAL_FAILURE_)
+#define EXPECT_LT(a, ...)                                              \
+  TEST_ASSERT_(                                                        \
+      ::testing::TestResult(a < (__VA_ARGS__), #a " < " #__VA_ARGS__), \
+      TEST_NONFATAL_FAILURE_)
 
-#define EXPECT_GT(a, b)                                   \
-  TEST_ASSERT_(::testing::TestResult(a > b, #a " > " #b), \
-               TEST_NONFATAL_FAILURE_)
+#define EXPECT_GT(a, ...)                                              \
+  TEST_ASSERT_(                                                        \
+      ::testing::TestResult(a > (__VA_ARGS__), #a " > " #__VA_ARGS__), \
+      TEST_NONFATAL_FAILURE_)
 
-#define EXPECT_LE(a, b)                                     \
-  TEST_ASSERT_(::testing::TestResult(a <= b, #a " <= " #b), \
-               TEST_NONFATAL_FAILURE_)
+#define EXPECT_LE(a, ...)                                                \
+  TEST_ASSERT_(                                                          \
+      ::testing::TestResult(a <= (__VA_ARGS__), #a " <= " #__VA_ARGS__), \
+      TEST_NONFATAL_FAILURE_)
 
-#define EXPECT_GE(a, b)                                     \
-  TEST_ASSERT_(::testing::TestResult(a >= b, #a " >= " #b), \
-               TEST_NONFATAL_FAILURE_)
+#define EXPECT_GE(a, ...)                                                \
+  TEST_ASSERT_(                                                          \
+      ::testing::TestResult(a >= (__VA_ARGS__), #a " >= " #__VA_ARGS__), \
+      TEST_NONFATAL_FAILURE_)
 
 #define EXPECT_TRUE(a)                                          \
   TEST_ASSERT_(::testing::TestResult(static_cast<bool>(a), #a), \
@@ -275,33 +325,45 @@
   TEST_ASSERT_(::testing::TestResult(!static_cast<bool>(a), #a), \
                TEST_NONFATAL_FAILURE_)
 
-#define EXPECT_STREQ(a, b)                                                \
-  TEST_ASSERT_(::testing::TestResult(strcmp(a, b) == 0, #a " str== " #b), \
+#define EXPECT_STREQ(a, ...)                                        \
+  TEST_ASSERT_(::testing::TestResult(strcmp(a, (__VA_ARGS__)) == 0, \
+                                     #a " str== " #__VA_ARGS__),    \
                TEST_NONFATAL_FAILURE_)
 
-#define ASSERT_EQ(a, b)                                               \
-  TEST_AMBIGUOUS_ELSE_BLOCKER_                                        \
-  if (const ::testing::TestResult test_result =                       \
-          ::testing::TestResult(a == b, #a " == " #b))                \
-    ;                                                                 \
-  else                                                                \
-    return ::testing::AssertHelper(__FILE__, __LINE__, test_result) = \
-               ::testing::Message() << ::testing::TryDiffStrings(a, b)
+#define ASSERT_EQ(a, ...)                                                    \
+  TEST_AMBIGUOUS_ELSE_BLOCKER_                                               \
+  if (const ::testing::TestResult test_result =                              \
+          ::testing::TestResult(a == (__VA_ARGS__), #a " == " #__VA_ARGS__)) \
+    ;                                                                        \
+  else                                                                       \
+    return ::testing::AssertHelper(__FILE__, __LINE__, test_result) =        \
+               ::testing::Message()                                          \
+               << ::testing::TryDiffStrings(a, (__VA_ARGS__))
 
-#define ASSERT_NE(a, b) \
-  TEST_ASSERT_(::testing::TestResult(a != b, #a " != " #b), TEST_FATAL_FAILURE_)
+#define ASSERT_NE(a, ...)                                                \
+  TEST_ASSERT_(                                                          \
+      ::testing::TestResult(a != (__VA_ARGS__), #a " != " #__VA_ARGS__), \
+      TEST_FATAL_FAILURE_)
 
-#define ASSERT_LT(a, b) \
-  TEST_ASSERT_(::testing::TestResult(a < b, #a " < " #b), TEST_FATAL_FAILURE_)
+#define ASSERT_LT(a, ...)                                              \
+  TEST_ASSERT_(                                                        \
+      ::testing::TestResult(a < (__VA_ARGS__), #a " < " #__VA_ARGS__), \
+      TEST_FATAL_FAILURE_)
 
-#define ASSERT_GT(a, b) \
-  TEST_ASSERT_(::testing::TestResult(a > b, #a " > " #b), TEST_FATAL_FAILURE_)
+#define ASSERT_GT(a, ...)                                              \
+  TEST_ASSERT_(                                                        \
+      ::testing::TestResult(a > (__VA_ARGS__), #a " > " #__VA_ARGS__), \
+      TEST_FATAL_FAILURE_)
 
-#define ASSERT_LE(a, b) \
-  TEST_ASSERT_(::testing::TestResult(a <= b, #a " <= " #b), TEST_FATAL_FAILURE_)
+#define ASSERT_LE(a, ...)                                                \
+  TEST_ASSERT_(                                                          \
+      ::testing::TestResult(a <= (__VA_ARGS__), #a " <= " #__VA_ARGS__), \
+      TEST_FATAL_FAILURE_)
 
-#define ASSERT_GE(a, b) \
-  TEST_ASSERT_(::testing::TestResult(a >= b, #a " >= " #b), TEST_FATAL_FAILURE_)
+#define ASSERT_GE(a, ...)                                                \
+  TEST_ASSERT_(                                                          \
+      ::testing::TestResult(a >= (__VA_ARGS__), #a " >= " #__VA_ARGS__), \
+      TEST_FATAL_FAILURE_)
 
 #define ASSERT_TRUE(a)                                          \
   TEST_ASSERT_(::testing::TestResult(static_cast<bool>(a), #a), \
@@ -311,26 +373,53 @@
   TEST_ASSERT_(::testing::TestResult(!static_cast<bool>(a), #a), \
                TEST_FATAL_FAILURE_)
 
-#define ASSERT_STREQ(a, b)                                                \
-  TEST_ASSERT_(::testing::TestResult(strcmp(a, b) == 0, #a " str== " #b), \
+#define ASSERT_STREQ(a, ...)                                        \
+  TEST_ASSERT_(::testing::TestResult(strcmp(a, (__VA_ARGS__)) == 0, \
+                                     #a " str== " #__VA_ARGS__),    \
                TEST_FATAL_FAILURE_)
 
-#define EXPECT_SUCCESS(err)                                       \
-  TEST_AMBIGUOUS_ELSE_BLOCKER_                                    \
-  if (const auto& test_err = (err); !test_err.has_error())        \
-    ;                                                             \
-  else                                                            \
-    TEST_NONFATAL_FAILURE_(                                       \
-        ::testing::TestResult(false, "EXPECT_SUCCESS(" #err ")")) \
-        << test_err.message()
+#define EXPECT_SUCCESS(err, ...)                                          \
+  TEST_AMBIGUOUS_ELSE_BLOCKER_                                            \
+  if (const std::optional<std::string> test_err_msg =                     \
+          ::testing::CheckSuccess((err)__VA_OPT__(, (__VA_ARGS__)));      \
+      !test_err_msg.has_value())                                          \
+    ;                                                                     \
+  else                                                                    \
+    TEST_NONFATAL_FAILURE_(::testing::TestResult(                         \
+        false, "EXPECT_SUCCESS(" #err __VA_OPT__(", " #__VA_ARGS__) ")")) \
+        << *test_err_msg
 
-#define ASSERT_SUCCESS(err)                                       \
-  TEST_AMBIGUOUS_ELSE_BLOCKER_                                    \
-  if (const auto& test_err = (err); !test_err.has_error())        \
-    ;                                                             \
-  else                                                            \
-    TEST_FATAL_FAILURE_(                                          \
-        ::testing::TestResult(false, "ASSERT_SUCCESS(" #err ")")) \
-        << test_err.message()
+#define ASSERT_SUCCESS(err, ...)                                          \
+  TEST_AMBIGUOUS_ELSE_BLOCKER_                                            \
+  if (const std::optional<std::string> test_err_msg =                     \
+          ::testing::CheckSuccess((err)__VA_OPT__(, (__VA_ARGS__)));      \
+      !test_err_msg.has_value())                                          \
+    ;                                                                     \
+  else                                                                    \
+    TEST_FATAL_FAILURE_(::testing::TestResult(                            \
+        false, "ASSERT_SUCCESS(" #err __VA_OPT__(", " #__VA_ARGS__) ")")) \
+        << *test_err_msg
+
+#define EXPECT_FAILURE(err, ...)                                          \
+  TEST_AMBIGUOUS_ELSE_BLOCKER_                                            \
+  if (const std::optional<std::string> test_err_msg =                     \
+          ::testing::CheckFailure((err)__VA_OPT__(, (__VA_ARGS__)));      \
+      !test_err_msg.has_value())                                          \
+    ;                                                                     \
+  else                                                                    \
+    TEST_NONFATAL_FAILURE_(::testing::TestResult(                         \
+        false, "EXPECT_FAILURE(" #err __VA_OPT__(", " #__VA_ARGS__) ")")) \
+        << *test_err_msg
+
+#define ASSERT_FAILURE(err, ...)                                          \
+  TEST_AMBIGUOUS_ELSE_BLOCKER_                                            \
+  if (const std::optional<std::string> test_err_msg =                     \
+          ::testing::CheckFailure((err)__VA_OPT__(, (__VA_ARGS__)));      \
+      !test_err_msg.has_value())                                          \
+    ;                                                                     \
+  else                                                                    \
+    TEST_FATAL_FAILURE_(::testing::TestResult(                            \
+        false, "ASSERT_FAILURE(" #err __VA_OPT__(", " #__VA_ARGS__) ")")) \
+        << *test_err_msg
 
 #endif  // UTIL_TEST_TEST_H_