934d1de9创建于 2025年1月10日历史提交
#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