blob: 41e7184441a280e97f14b9f4488369c09c67ff13 [file] [log] [blame] [edit]
#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