Source code
Revision control
Copy as Markdown
Other Tools
//
// Copyright 2022 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/log/log_sink.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/attributes.h"
#include "absl/log/internal/test_actions.h"
#include "absl/log/internal/test_helpers.h"
#include "absl/log/internal/test_matchers.h"
#include "absl/log/log.h"
#include "absl/log/log_sink_registry.h"
#include "absl/log/scoped_mock_log.h"
#include "absl/strings/string_view.h"
namespace {
using ::absl::log_internal::DeathTestExpectedLogging;
using ::absl::log_internal::DeathTestUnexpectedLogging;
using ::absl::log_internal::DeathTestValidateExpectations;
using ::absl::log_internal::DiedOfFatal;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::HasSubstr;
using ::testing::InSequence;
auto* test_env ABSL_ATTRIBUTE_UNUSED = ::testing::AddGlobalTestEnvironment(
new absl::log_internal::LogTestEnvironment);
// Tests for global log sink registration.
// ---------------------------------------
TEST(LogSinkRegistryTest, AddLogSink) {
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
InSequence s;
EXPECT_CALL(test_sink, Log(_, _, "hello world")).Times(0);
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kInfo, __FILE__, "Test : 42"));
EXPECT_CALL(test_sink,
Log(absl::LogSeverity::kWarning, __FILE__, "Danger ahead"));
EXPECT_CALL(test_sink,
Log(absl::LogSeverity::kError, __FILE__, "This is an error"));
LOG(INFO) << "hello world";
test_sink.StartCapturingLogs();
LOG(INFO) << "Test : " << 42;
LOG(WARNING) << "Danger" << ' ' << "ahead";
LOG(ERROR) << "This is an error";
test_sink.StopCapturingLogs();
LOG(INFO) << "Goodby world";
}
TEST(LogSinkRegistryTest, MultipleLogSinks) {
absl::ScopedMockLog test_sink1(absl::MockLogDefault::kDisallowUnexpected);
absl::ScopedMockLog test_sink2(absl::MockLogDefault::kDisallowUnexpected);
::testing::InSequence seq;
EXPECT_CALL(test_sink1, Log(absl::LogSeverity::kInfo, _, "First")).Times(1);
EXPECT_CALL(test_sink2, Log(absl::LogSeverity::kInfo, _, "First")).Times(0);
EXPECT_CALL(test_sink1, Log(absl::LogSeverity::kInfo, _, "Second")).Times(1);
EXPECT_CALL(test_sink2, Log(absl::LogSeverity::kInfo, _, "Second")).Times(1);
EXPECT_CALL(test_sink1, Log(absl::LogSeverity::kInfo, _, "Third")).Times(0);
EXPECT_CALL(test_sink2, Log(absl::LogSeverity::kInfo, _, "Third")).Times(1);
LOG(INFO) << "Before first";
test_sink1.StartCapturingLogs();
LOG(INFO) << "First";
test_sink2.StartCapturingLogs();
LOG(INFO) << "Second";
test_sink1.StopCapturingLogs();
LOG(INFO) << "Third";
test_sink2.StopCapturingLogs();
LOG(INFO) << "Fourth";
}
TEST(LogSinkRegistrationDeathTest, DuplicateSinkRegistration) {
ASSERT_DEATH_IF_SUPPORTED(
{
absl::ScopedMockLog sink;
sink.StartCapturingLogs();
absl::AddLogSink(&sink.UseAsLocalSink());
},
HasSubstr("Duplicate log sinks"));
}
TEST(LogSinkRegistrationDeathTest, MismatchSinkRemoval) {
ASSERT_DEATH_IF_SUPPORTED(
{
absl::ScopedMockLog sink;
absl::RemoveLogSink(&sink.UseAsLocalSink());
},
HasSubstr("Mismatched log sink"));
}
// Tests for log sink semantic.
// ---------------------------------------
TEST(LogSinkTest, FlushSinks) {
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
EXPECT_CALL(test_sink, Flush()).Times(2);
test_sink.StartCapturingLogs();
absl::FlushLogSinks();
absl::FlushLogSinks();
}
TEST(LogSinkDeathTest, DeathInSend) {
class FatalSendSink : public absl::LogSink {
public:
void Send(const absl::LogEntry&) override { LOG(FATAL) << "goodbye world"; }
};
FatalSendSink sink;
EXPECT_EXIT({ LOG(INFO).ToSinkAlso(&sink) << "hello world"; }, DiedOfFatal,
_);
}
// Tests for explicit log sink redirection.
// ---------------------------------------
TEST(LogSinkTest, ToSinkAlso) {
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
absl::ScopedMockLog another_sink(absl::MockLogDefault::kDisallowUnexpected);
EXPECT_CALL(test_sink, Log(_, _, "hello world"));
EXPECT_CALL(another_sink, Log(_, _, "hello world"));
test_sink.StartCapturingLogs();
LOG(INFO).ToSinkAlso(&another_sink.UseAsLocalSink()) << "hello world";
}
TEST(LogSinkTest, ToSinkOnly) {
absl::ScopedMockLog another_sink(absl::MockLogDefault::kDisallowUnexpected);
EXPECT_CALL(another_sink, Log(_, _, "hello world"));
LOG(INFO).ToSinkOnly(&another_sink.UseAsLocalSink()) << "hello world";
}
TEST(LogSinkTest, ToManySinks) {
absl::ScopedMockLog sink1(absl::MockLogDefault::kDisallowUnexpected);
absl::ScopedMockLog sink2(absl::MockLogDefault::kDisallowUnexpected);
absl::ScopedMockLog sink3(absl::MockLogDefault::kDisallowUnexpected);
absl::ScopedMockLog sink4(absl::MockLogDefault::kDisallowUnexpected);
absl::ScopedMockLog sink5(absl::MockLogDefault::kDisallowUnexpected);
EXPECT_CALL(sink3, Log(_, _, "hello world"));
EXPECT_CALL(sink4, Log(_, _, "hello world"));
EXPECT_CALL(sink5, Log(_, _, "hello world"));
LOG(INFO)
.ToSinkAlso(&sink1.UseAsLocalSink())
.ToSinkAlso(&sink2.UseAsLocalSink())
.ToSinkOnly(&sink3.UseAsLocalSink())
.ToSinkAlso(&sink4.UseAsLocalSink())
.ToSinkAlso(&sink5.UseAsLocalSink())
<< "hello world";
}
class ReentrancyTest : public ::testing::Test {
protected:
ReentrancyTest() = default;
enum class LogMode : int { kNormal, kToSinkAlso, kToSinkOnly };
class ReentrantSendLogSink : public absl::LogSink {
public:
explicit ReentrantSendLogSink(absl::LogSeverity severity,
absl::LogSink* sink, LogMode mode)
: severity_(severity), sink_(sink), mode_(mode) {}
explicit ReentrantSendLogSink(absl::LogSeverity severity)
: ReentrantSendLogSink(severity, nullptr, LogMode::kNormal) {}
void Send(const absl::LogEntry&) override {
switch (mode_) {
case LogMode::kNormal:
LOG(LEVEL(severity_)) << "The log is coming from *inside the sink*.";
break;
case LogMode::kToSinkAlso:
LOG(LEVEL(severity_)).ToSinkAlso(sink_)
<< "The log is coming from *inside the sink*.";
break;
case LogMode::kToSinkOnly:
LOG(LEVEL(severity_)).ToSinkOnly(sink_)
<< "The log is coming from *inside the sink*.";
break;
default:
LOG(FATAL) << "Invalid mode " << static_cast<int>(mode_);
}
}
private:
absl::LogSeverity severity_;
absl::LogSink* sink_;
LogMode mode_;
};
static absl::string_view LogAndReturn(absl::LogSeverity severity,
absl::string_view to_log,
absl::string_view to_return) {
LOG(LEVEL(severity)) << to_log;
return to_return;
}
};
TEST_F(ReentrancyTest, LogFunctionThatLogs) {
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
InSequence seq;
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kInfo, _, "hello"));
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kInfo, _, "world"));
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kWarning, _, "danger"));
EXPECT_CALL(test_sink, Log(absl::LogSeverity::kInfo, _, "here"));
test_sink.StartCapturingLogs();
LOG(INFO) << LogAndReturn(absl::LogSeverity::kInfo, "hello", "world");
LOG(INFO) << LogAndReturn(absl::LogSeverity::kWarning, "danger", "here");
}
TEST_F(ReentrancyTest, RegisteredLogSinkThatLogsInSend) {
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
ReentrantSendLogSink renentrant_sink(absl::LogSeverity::kInfo);
EXPECT_CALL(test_sink, Log(_, _, "hello world"));
test_sink.StartCapturingLogs();
absl::AddLogSink(&renentrant_sink);
LOG(INFO) << "hello world";
absl::RemoveLogSink(&renentrant_sink);
}
TEST_F(ReentrancyTest, AlsoLogSinkThatLogsInSend) {
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kInfo);
EXPECT_CALL(test_sink, Log(_, _, "hello world"));
EXPECT_CALL(test_sink,
Log(_, _, "The log is coming from *inside the sink*."));
test_sink.StartCapturingLogs();
LOG(INFO).ToSinkAlso(&reentrant_sink) << "hello world";
}
TEST_F(ReentrancyTest, RegisteredAlsoLogSinkThatLogsInSend) {
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kInfo);
EXPECT_CALL(test_sink, Log(_, _, "hello world"));
// We only call into the test_log sink once with this message, since the
// second time log statement is run we are in "ThreadIsLogging" mode and all
// the log statements are redirected into stderr.
EXPECT_CALL(test_sink,
Log(_, _, "The log is coming from *inside the sink*."));
test_sink.StartCapturingLogs();
absl::AddLogSink(&reentrant_sink);
LOG(INFO).ToSinkAlso(&reentrant_sink) << "hello world";
absl::RemoveLogSink(&reentrant_sink);
}
TEST_F(ReentrancyTest, OnlyLogSinkThatLogsInSend) {
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kInfo);
EXPECT_CALL(test_sink,
Log(_, _, "The log is coming from *inside the sink*."));
test_sink.StartCapturingLogs();
LOG(INFO).ToSinkOnly(&reentrant_sink) << "hello world";
}
TEST_F(ReentrancyTest, RegisteredOnlyLogSinkThatLogsInSend) {
absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kInfo);
EXPECT_CALL(test_sink,
Log(_, _, "The log is coming from *inside the sink*."));
test_sink.StartCapturingLogs();
absl::AddLogSink(&reentrant_sink);
LOG(INFO).ToSinkOnly(&reentrant_sink) << "hello world";
absl::RemoveLogSink(&reentrant_sink);
}
using ReentrancyDeathTest = ReentrancyTest;
TEST_F(ReentrancyDeathTest, LogFunctionThatLogsFatal) {
EXPECT_EXIT(
{
absl::ScopedMockLog test_sink;
EXPECT_CALL(test_sink, Log)
.Times(AnyNumber())
.WillRepeatedly(DeathTestUnexpectedLogging());
EXPECT_CALL(test_sink, Log(_, _, "hello"))
.WillOnce(DeathTestExpectedLogging());
test_sink.StartCapturingLogs();
LOG(INFO) << LogAndReturn(absl::LogSeverity::kFatal, "hello", "world");
},
DiedOfFatal, DeathTestValidateExpectations());
}
TEST_F(ReentrancyDeathTest, RegisteredLogSinkThatLogsFatalInSend) {
EXPECT_EXIT(
{
absl::ScopedMockLog test_sink;
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kFatal);
EXPECT_CALL(test_sink, Log)
.Times(AnyNumber())
.WillRepeatedly(DeathTestUnexpectedLogging());
EXPECT_CALL(test_sink, Log(_, _, "hello world"))
.WillOnce(DeathTestExpectedLogging());
test_sink.StartCapturingLogs();
absl::AddLogSink(&reentrant_sink);
LOG(INFO) << "hello world";
// No need to call RemoveLogSink - process is dead at this point.
},
DiedOfFatal, DeathTestValidateExpectations());
}
TEST_F(ReentrancyDeathTest, AlsoLogSinkThatLogsFatalInSend) {
EXPECT_EXIT(
{
absl::ScopedMockLog test_sink;
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kFatal);
EXPECT_CALL(test_sink, Log)
.Times(AnyNumber())
.WillRepeatedly(DeathTestUnexpectedLogging());
EXPECT_CALL(test_sink, Log(_, _, "hello world"))
.WillOnce(DeathTestExpectedLogging());
EXPECT_CALL(test_sink,
Log(_, _, "The log is coming from *inside the sink*."))
.WillOnce(DeathTestExpectedLogging());
test_sink.StartCapturingLogs();
LOG(INFO).ToSinkAlso(&reentrant_sink) << "hello world";
},
DiedOfFatal, DeathTestValidateExpectations());
}
TEST_F(ReentrancyDeathTest, RegisteredAlsoLogSinkThatLogsFatalInSend) {
EXPECT_EXIT(
{
absl::ScopedMockLog test_sink;
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kFatal);
EXPECT_CALL(test_sink, Log)
.Times(AnyNumber())
.WillRepeatedly(DeathTestUnexpectedLogging());
EXPECT_CALL(test_sink, Log(_, _, "hello world"))
.WillOnce(DeathTestExpectedLogging());
EXPECT_CALL(test_sink,
Log(_, _, "The log is coming from *inside the sink*."))
.WillOnce(DeathTestExpectedLogging());
test_sink.StartCapturingLogs();
absl::AddLogSink(&reentrant_sink);
LOG(INFO).ToSinkAlso(&reentrant_sink) << "hello world";
// No need to call RemoveLogSink - process is dead at this point.
},
DiedOfFatal, DeathTestValidateExpectations());
}
TEST_F(ReentrancyDeathTest, OnlyLogSinkThatLogsFatalInSend) {
EXPECT_EXIT(
{
absl::ScopedMockLog test_sink;
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kFatal);
EXPECT_CALL(test_sink, Log)
.Times(AnyNumber())
.WillRepeatedly(DeathTestUnexpectedLogging());
EXPECT_CALL(test_sink,
Log(_, _, "The log is coming from *inside the sink*."))
.WillOnce(DeathTestExpectedLogging());
test_sink.StartCapturingLogs();
LOG(INFO).ToSinkOnly(&reentrant_sink) << "hello world";
},
DiedOfFatal, DeathTestValidateExpectations());
}
TEST_F(ReentrancyDeathTest, RegisteredOnlyLogSinkThatLogsFatalInSend) {
EXPECT_EXIT(
{
absl::ScopedMockLog test_sink;
ReentrantSendLogSink reentrant_sink(absl::LogSeverity::kFatal);
EXPECT_CALL(test_sink, Log)
.Times(AnyNumber())
.WillRepeatedly(DeathTestUnexpectedLogging());
EXPECT_CALL(test_sink,
Log(_, _, "The log is coming from *inside the sink*."))
.WillOnce(DeathTestExpectedLogging());
test_sink.StartCapturingLogs();
absl::AddLogSink(&reentrant_sink);
LOG(INFO).ToSinkOnly(&reentrant_sink) << "hello world";
// No need to call RemoveLogSink - process is dead at this point.
},
DiedOfFatal, DeathTestValidateExpectations());
}
} // namespace