diff options
Diffstat (limited to 'gnu/llvm/lldb/unittests/Utility/LogTest.cpp')
-rw-r--r-- | gnu/llvm/lldb/unittests/Utility/LogTest.cpp | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/unittests/Utility/LogTest.cpp b/gnu/llvm/lldb/unittests/Utility/LogTest.cpp new file mode 100644 index 00000000000..511724b6a1b --- /dev/null +++ b/gnu/llvm/lldb/unittests/Utility/LogTest.cpp @@ -0,0 +1,313 @@ +//===-- LogTest.cpp ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Threading.h" +#include <thread> + +using namespace lldb; +using namespace lldb_private; + +enum { FOO = 1, BAR = 2 }; +static constexpr Log::Category test_categories[] = { + {{"foo"}, {"log foo"}, FOO}, {{"bar"}, {"log bar"}, BAR}, +}; +static constexpr uint32_t default_flags = FOO; + +static Log::Channel test_channel(test_categories, default_flags); + +// Wrap enable, disable and list functions to make them easier to test. +static bool EnableChannel(std::shared_ptr<llvm::raw_ostream> stream_sp, + uint32_t log_options, llvm::StringRef channel, + llvm::ArrayRef<const char *> categories, + std::string &error) { + error.clear(); + llvm::raw_string_ostream error_stream(error); + return Log::EnableLogChannel(stream_sp, log_options, channel, categories, + error_stream); +} + +static bool DisableChannel(llvm::StringRef channel, + llvm::ArrayRef<const char *> categories, + std::string &error) { + error.clear(); + llvm::raw_string_ostream error_stream(error); + return Log::DisableLogChannel(channel, categories, error_stream); +} + +static bool ListCategories(llvm::StringRef channel, std::string &result) { + result.clear(); + llvm::raw_string_ostream result_stream(result); + return Log::ListChannelCategories(channel, result_stream); +} + +namespace { +// A test fixture which provides tests with a pre-registered channel. +struct LogChannelTest : public ::testing::Test { + void TearDown() override { Log::DisableAllLogChannels(); } + + static void SetUpTestCase() { + Log::Register("chan", test_channel); + } + + static void TearDownTestCase() { + Log::Unregister("chan"); + llvm::llvm_shutdown(); + } +}; + +// A test fixture which provides tests with a pre-registered and pre-enabled +// channel. Additionally, the messages written to that channel are captured and +// made available via getMessage(). +class LogChannelEnabledTest : public LogChannelTest { + llvm::SmallString<0> m_messages; + std::shared_ptr<llvm::raw_svector_ostream> m_stream_sp = + std::make_shared<llvm::raw_svector_ostream>(m_messages); + Log *m_log; + size_t m_consumed_bytes = 0; + +protected: + std::shared_ptr<llvm::raw_ostream> getStream() { return m_stream_sp; } + Log *getLog() { return m_log; } + llvm::StringRef takeOutput(); + llvm::StringRef logAndTakeOutput(llvm::StringRef Message); + +public: + void SetUp() override; +}; +} // end anonymous namespace + +void LogChannelEnabledTest::SetUp() { + LogChannelTest::SetUp(); + + std::string error; + ASSERT_TRUE(EnableChannel(m_stream_sp, 0, "chan", {}, error)); + + m_log = test_channel.GetLogIfAll(FOO); + ASSERT_NE(nullptr, m_log); +} + +llvm::StringRef LogChannelEnabledTest::takeOutput() { + llvm::StringRef result = m_stream_sp->str().drop_front(m_consumed_bytes); + m_consumed_bytes+= result.size(); + return result; +} + +llvm::StringRef LogChannelEnabledTest::logAndTakeOutput(llvm::StringRef Message) { + LLDB_LOG(m_log, "{0}", Message); + return takeOutput(); +} + +TEST(LogTest, LLDB_LOG_nullptr) { + Log *log = nullptr; + LLDB_LOG(log, "{0}", 0); // Shouldn't crash +} + +TEST(LogTest, Register) { + llvm::llvm_shutdown_obj obj; + Log::Register("chan", test_channel); + Log::Unregister("chan"); + Log::Register("chan", test_channel); + Log::Unregister("chan"); +} + +TEST(LogTest, Unregister) { + llvm::llvm_shutdown_obj obj; + Log::Register("chan", test_channel); + EXPECT_EQ(nullptr, test_channel.GetLogIfAny(FOO)); + std::string message; + std::shared_ptr<llvm::raw_string_ostream> stream_sp( + new llvm::raw_string_ostream(message)); + EXPECT_TRUE(Log::EnableLogChannel(stream_sp, 0, "chan", {"foo"}, llvm::nulls())); + EXPECT_NE(nullptr, test_channel.GetLogIfAny(FOO)); + Log::Unregister("chan"); + EXPECT_EQ(nullptr, test_channel.GetLogIfAny(FOO)); +} + +TEST_F(LogChannelTest, Enable) { + EXPECT_EQ(nullptr, test_channel.GetLogIfAll(FOO)); + std::string message; + std::shared_ptr<llvm::raw_string_ostream> stream_sp( + new llvm::raw_string_ostream(message)); + std::string error; + ASSERT_FALSE(EnableChannel(stream_sp, 0, "chanchan", {}, error)); + EXPECT_EQ("Invalid log channel 'chanchan'.\n", error); + + EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {}, error)); + EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO)); + EXPECT_EQ(nullptr, test_channel.GetLogIfAll(BAR)); + + EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {"bar"}, error)); + EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO | BAR)); + + EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {"baz"}, error)); + EXPECT_NE(std::string::npos, error.find("unrecognized log category 'baz'")) + << "error: " << error; + EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO | BAR)); +} + +TEST_F(LogChannelTest, EnableOptions) { + EXPECT_EQ(nullptr, test_channel.GetLogIfAll(FOO)); + std::string message; + std::shared_ptr<llvm::raw_string_ostream> stream_sp( + new llvm::raw_string_ostream(message)); + std::string error; + EXPECT_TRUE( + EnableChannel(stream_sp, LLDB_LOG_OPTION_VERBOSE, "chan", {}, error)); + + Log *log = test_channel.GetLogIfAll(FOO); + ASSERT_NE(nullptr, log); + EXPECT_TRUE(log->GetVerbose()); +} + +TEST_F(LogChannelTest, Disable) { + EXPECT_EQ(nullptr, test_channel.GetLogIfAll(FOO)); + std::string message; + std::shared_ptr<llvm::raw_string_ostream> stream_sp( + new llvm::raw_string_ostream(message)); + std::string error; + EXPECT_TRUE(EnableChannel(stream_sp, 0, "chan", {"foo", "bar"}, error)); + EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO | BAR)); + + EXPECT_TRUE(DisableChannel("chan", {"bar"}, error)); + EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO)); + EXPECT_EQ(nullptr, test_channel.GetLogIfAll(BAR)); + + EXPECT_TRUE(DisableChannel("chan", {"baz"}, error)); + EXPECT_NE(std::string::npos, error.find("unrecognized log category 'baz'")) + << "error: " << error; + EXPECT_NE(nullptr, test_channel.GetLogIfAll(FOO)); + EXPECT_EQ(nullptr, test_channel.GetLogIfAll(BAR)); + + EXPECT_TRUE(DisableChannel("chan", {}, error)); + EXPECT_EQ(nullptr, test_channel.GetLogIfAny(FOO | BAR)); +} + +TEST_F(LogChannelTest, List) { + std::string list; + EXPECT_TRUE(ListCategories("chan", list)); + std::string expected = + R"(Logging categories for 'chan': + all - all available logging categories + default - default set of logging categories + foo - log foo + bar - log bar +)"; + EXPECT_EQ(expected, list); + + EXPECT_FALSE(ListCategories("chanchan", list)); + EXPECT_EQ("Invalid log channel 'chanchan'.\n", list); +} + +TEST_F(LogChannelEnabledTest, log_options) { + std::string Err; + EXPECT_EQ("Hello World\n", logAndTakeOutput("Hello World")); + EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_THREADSAFE, "chan", {}, + Err)); + EXPECT_EQ("Hello World\n", logAndTakeOutput("Hello World")); + + { + EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_PREPEND_SEQUENCE, + "chan", {}, Err)); + llvm::StringRef Msg = logAndTakeOutput("Hello World"); + int seq_no; + EXPECT_EQ(1, sscanf(Msg.str().c_str(), "%d Hello World", &seq_no)); + } + + { + EXPECT_TRUE(EnableChannel(getStream(), LLDB_LOG_OPTION_PREPEND_FILE_FUNCTION, + "chan", {}, Err)); + llvm::StringRef Msg = logAndTakeOutput("Hello World"); + char File[12]; + char Function[17]; + + sscanf(Msg.str().c_str(), "%[^:]:%s Hello World", File, Function); + EXPECT_STRCASEEQ("LogTest.cpp", File); + EXPECT_STREQ("logAndTakeOutput", Function); + } + + EXPECT_TRUE(EnableChannel( + getStream(), LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD, "chan", {}, Err)); + EXPECT_EQ(llvm::formatv("[{0,0+4}/{1,0+4}] Hello World\n", ::getpid(), + llvm::get_threadid()) + .str(), + logAndTakeOutput("Hello World")); +} + +TEST_F(LogChannelEnabledTest, LLDB_LOG_ERROR) { + LLDB_LOG_ERROR(getLog(), llvm::Error::success(), "Foo failed: {0}"); + ASSERT_EQ("", takeOutput()); + + LLDB_LOG_ERROR(getLog(), + llvm::make_error<llvm::StringError>( + "My Error", llvm::inconvertibleErrorCode()), + "Foo failed: {0}"); + ASSERT_EQ("Foo failed: My Error\n", takeOutput()); + + // Doesn't log, but doesn't assert either + LLDB_LOG_ERROR(nullptr, + llvm::make_error<llvm::StringError>( + "My Error", llvm::inconvertibleErrorCode()), + "Foo failed: {0}"); +} + +TEST_F(LogChannelEnabledTest, LogThread) { + // Test that we are able to concurrently write to a log channel and disable + // it. + std::string err; + + // Start logging on one thread. Concurrently, try disabling the log channel. + std::thread log_thread([this] { LLDB_LOG(getLog(), "Hello World"); }); + EXPECT_TRUE(DisableChannel("chan", {}, err)); + log_thread.join(); + + // The log thread either managed to write to the log in time, or it didn't. In + // either case, we should not trip any undefined behavior (run the test under + // TSAN to verify this). + EXPECT_THAT(takeOutput(), testing::AnyOf("", "Hello World\n")); +} + +TEST_F(LogChannelEnabledTest, LogVerboseThread) { + // Test that we are able to concurrently check the verbose flag of a log + // channel and enable it. + std::string err; + + // Start logging on one thread. Concurrently, try enabling the log channel + // (with different log options). + std::thread log_thread([this] { LLDB_LOGV(getLog(), "Hello World"); }); + EXPECT_TRUE( + EnableChannel(getStream(), LLDB_LOG_OPTION_VERBOSE, "chan", {}, err)); + log_thread.join(); + + // The log thread either managed to write to the log, or it didn't. In either + // case, we should not trip any undefined behavior (run the test under TSAN to + // verify this). + EXPECT_THAT(takeOutput(), testing::AnyOf("", "Hello World\n")); +} + +TEST_F(LogChannelEnabledTest, LogGetLogThread) { + // Test that we are able to concurrently get mask of a Log object and disable + // it. + std::string err; + + // Try fetching the log mask on one thread. Concurrently, try disabling the + // log channel. + uint32_t mask; + std::thread log_thread([this, &mask] { mask = getLog()->GetMask().Get(); }); + EXPECT_TRUE(DisableChannel("chan", {}, err)); + log_thread.join(); + + // The mask should be either zero of "FOO". In either case, we should not trip + // any undefined behavior (run the test under TSAN to verify this). + EXPECT_THAT(mask, testing::AnyOf(0, FOO)); +} |