| #include "quiche/common/bug_utils.h" | 
 |  | 
 | #include <string.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <memory> | 
 | #include <string> | 
 |  | 
 | #include "absl/base/log_severity.h" | 
 | #include "absl/strings/string_view.h" | 
 | #include "quiche/common/bug_utils_test_helper.h" | 
 | #include "quiche/common/platform/api/quiche_test.h" | 
 |  | 
 | namespace quiche { | 
 | namespace internal { | 
 | namespace { | 
 |  | 
 | using ::testing::_; | 
 | using ::testing::EndsWith; | 
 | using ::testing::InSequence; | 
 |  | 
 | class BugHandler { | 
 |  public: | 
 |   virtual ~BugHandler() = default; | 
 |   virtual void OnBug(absl::string_view file, int line, | 
 |                      absl::string_view message) = 0; | 
 | }; | 
 |  | 
 | // This class provides a convenient way to write expectations for the bug | 
 | // override function. | 
 | class MockBugHandler : public BugHandler { | 
 |  public: | 
 |   MockBugHandler() = default; | 
 |  | 
 |   MOCK_METHOD(void, OnBug, | 
 |               (absl::string_view file, int line, absl::string_view message), | 
 |               (override)); | 
 | }; | 
 |  | 
 | MockBugHandler* mock_bug_handler = nullptr; | 
 |  | 
 | MockBugHandler* GetInstance() { | 
 |   if (mock_bug_handler == nullptr) { | 
 |     mock_bug_handler = new MockBugHandler; | 
 |   } | 
 |   return mock_bug_handler; | 
 | } | 
 |  | 
 | void ResetInstance() { | 
 |   delete mock_bug_handler; | 
 |   mock_bug_handler = nullptr; | 
 | } | 
 |  | 
 | class BugUtilsTest : public ::quiche::test::QuicheTest { | 
 |  public: | 
 |   void SetUp() override { | 
 |     fn_ = GenericBugStreamHandler::GetOverrideFunction(); | 
 |     GenericBugStreamHandler::SetOverrideFunction( | 
 |         [](absl::LogSeverity /*severity*/, const char* file, int line, | 
 |            absl::string_view log_message) { | 
 |           GetInstance()->OnBug(absl::string_view(file, strlen(file)), line, | 
 |                                log_message); | 
 |         }); | 
 |   } | 
 |  | 
 |   void TearDown() override { | 
 |     GenericBugStreamHandler::SetOverrideFunction(fn_); | 
 |     ResetInstance(); | 
 |   } | 
 |  | 
 |   inline static GenericBugStreamHandler::OverrideFunction fn_ = nullptr; | 
 | }; | 
 |  | 
 | // Tests several permutations. | 
 | TEST_F(BugUtilsTest, TestsEverythingUsing23And26) { | 
 |   InSequence seq; | 
 |   EXPECT_CALL(*GetInstance(), OnBug(EndsWith("bug_utils_test_helper.h"), 23, | 
 |                                     EndsWith("Here on line 23"))); | 
 |   LogBugLine23(); | 
 |  | 
 |   EXPECT_CALL(*GetInstance(), OnBug(EndsWith("bug_utils_test_helper.h"), 23, | 
 |                                     EndsWith("Here on line 23"))); | 
 |   LogBugLine23(); | 
 |  | 
 |   EXPECT_CALL(*GetInstance(), OnBug(EndsWith("bug_utils_test_helper.h"), 26, | 
 |                                     EndsWith("Here on line 26"))); | 
 |   EXPECT_CALL(*GetInstance(), OnBug(EndsWith("bug_utils_test_helper.h"), 27, | 
 |                                     EndsWith("And 27!"))); | 
 |   LogBugLine26(); | 
 | } | 
 |  | 
 | TEST_F(BugUtilsTest, TestBugIf) { | 
 |   InSequence seq; | 
 |  | 
 |   // Verify that we don't invoke the function for a false condition. | 
 |   LogIfBugLine31(false); | 
 |  | 
 |   // The first true should trigger an invocation. | 
 |   EXPECT_CALL(*GetInstance(), OnBug(EndsWith("bug_utils_test_helper.h"), 31, | 
 |                                     EndsWith("Here on line 31"))); | 
 |   LogIfBugLine31(true); | 
 |  | 
 |   // It's always a no-op if the condition is false. | 
 |   LogIfBugLine31(false);  // no-op | 
 |   LogIfBugLine31(false);  // no-op | 
 | } | 
 |  | 
 | TEST_F(BugUtilsTest, TestBugIfMessage) { | 
 |   int i; | 
 |  | 
 |   // Check success | 
 |   LogIfBugNullCheckLine35(&i); | 
 |  | 
 |   // Check failure | 
 |   EXPECT_CALL( | 
 |       *GetInstance(), | 
 |       OnBug( | 
 |           EndsWith("bug_utils_test_helper.h"), 35, | 
 |           EndsWith( | 
 |               "QUICHE_TEST_BUG_IF(Bug 35, ptr == nullptr): Here on line 35"))); | 
 |   LogIfBugNullCheckLine35(nullptr); | 
 | } | 
 |  | 
 | // Don't actually need to crash, just cause a side effect the test can assert | 
 | // on. | 
 | int num_times_called = 0; | 
 | bool BadCondition() { | 
 |   ++num_times_called; | 
 |   return true; | 
 | } | 
 |  | 
 | TEST_F(BugUtilsTest, BadCondition) { | 
 |   InSequence seq; | 
 |  | 
 |   EXPECT_EQ(num_times_called, 0); | 
 |  | 
 |   EXPECT_CALL(*GetInstance(), OnBug(_, _, EndsWith("Called BadCondition"))); | 
 |   QUICHE_TEST_BUG_IF(id, BadCondition()) << "Called BadCondition"; | 
 |   EXPECT_EQ(num_times_called, 1); | 
 | } | 
 |  | 
 | TEST_F(BugUtilsTest, NoDanglingElse) { | 
 |   auto unexpected_bug_message = [] { | 
 |     ADD_FAILURE() << "This should not be called"; | 
 |     return "bad"; | 
 |   }; | 
 |  | 
 |   if (false) QUICHE_TEST_BUG(dangling_else) << unexpected_bug_message(); | 
 |  | 
 |   bool expected_else_reached = false; | 
 |   if (false) | 
 |     QUICHE_TEST_BUG(dangling_else_2) << unexpected_bug_message(); | 
 |   else | 
 |     expected_else_reached = true; | 
 |  | 
 |   EXPECT_TRUE(expected_else_reached); | 
 | } | 
 |  | 
 | TEST_F(BugUtilsTest, BugListener) { | 
 |   class TestListener : public GenericBugListener { | 
 |    public: | 
 |     explicit TestListener(bool expect_log_message) | 
 |         : expect_log_message_(expect_log_message) {} | 
 |  | 
 |     ~TestListener() override { EXPECT_EQ(hit_count_, 1); } | 
 |  | 
 |     void OnBug(const char* bug_id, const char* file, int line, | 
 |                absl::string_view bug_message) override { | 
 |       ++hit_count_; | 
 |       EXPECT_EQ(bug_id, "bug_listener_test"); | 
 |       EXPECT_EQ(file, __FILE__); | 
 |       EXPECT_GT(line, 0); | 
 |       if (expect_log_message_) { | 
 |         EXPECT_EQ(bug_message, "TEST_BUG(bug_listener_test): Bug listener msg"); | 
 |       } else { | 
 |         EXPECT_EQ(bug_message, ""); | 
 |       } | 
 |     } | 
 |  | 
 |     TestListener* self() { return this; } | 
 |  | 
 |    private: | 
 |     int hit_count_ = 0; | 
 |     const bool expect_log_message_; | 
 |   }; | 
 |  | 
 |   GENERIC_BUG_IMPL("TEST_BUG", bug_listener_test, /*skip_log_condition=*/false, | 
 |                    QUICHE_TEST_BUG_OPTIONS().SetBugListener( | 
 |                        TestListener(/*expect_log_message=*/true).self())) | 
 |       << "Bug listener msg"; | 
 |  | 
 |   GENERIC_BUG_IMPL("TEST_BUG", bug_listener_test, /*skip_log_condition=*/true, | 
 |                    QUICHE_TEST_BUG_OPTIONS().SetBugListener( | 
 |                        TestListener(/*expect_log_message=*/false).self())) | 
 |       << "Bug listener msg"; | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace internal | 
 | }  // namespace quiche |