| /* |
| * Copyright (C) 2017 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "WTFStringUtilities.h" |
| #include <wtf/Assertions.h> |
| #include <wtf/LoggerHelper.h> |
| #include <wtf/MainThread.h> |
| |
| #define LOG_CHANNEL_PREFIX Test |
| |
| const char* logTestingSubsystem = "com.webkit.testing"; |
| |
| DEFINE_LOG_CHANNEL(Channel1, logTestingSubsystem); |
| DEFINE_LOG_CHANNEL(Channel2, logTestingSubsystem); |
| DEFINE_LOG_CHANNEL(Channel3, logTestingSubsystem); |
| DEFINE_LOG_CHANNEL(Channel4, logTestingSubsystem); |
| |
| static WTFLogChannel* testLogChannels[] = { |
| &TestChannel1, |
| &TestChannel2, |
| &TestChannel3, |
| &TestChannel4, |
| }; |
| static const size_t logChannelCount = sizeof(testLogChannels) / sizeof(testLogChannels[0]); |
| |
| // Define the following to enable all tests. Disabled by default because replacing stderr with a |
| // non-blocking pipe fails on some of the bots. |
| #define TEST_OUTPUT 0 |
| |
| namespace TestWebKitAPI { |
| |
| class LoggingTest : public testing::Test, public LoggerHelper { |
| public: |
| LoggingTest() |
| : m_logger { Logger::create(this) } |
| { |
| } |
| |
| void SetUp() final |
| { |
| WTF::initializeMainThread(); |
| |
| // Replace stderr with a non-blocking pipe that we can read from. |
| pipe(m_descriptors); |
| fcntl(m_descriptors[0], F_SETFL, fcntl(m_descriptors[0], F_GETFL, 0) | O_NONBLOCK); |
| dup2(m_descriptors[1], STDERR_FILENO); |
| close(m_descriptors[1]); |
| |
| m_stderr = fdopen(m_descriptors[0], "r"); |
| |
| WTFInitializeLogChannelStatesFromString(testLogChannels, logChannelCount, "all"); |
| WTFSetLogChannelLevel(&TestChannel1, WTFLogLevel::Error); |
| WTFSetLogChannelLevel(&TestChannel2, WTFLogLevel::Error); |
| WTFSetLogChannelLevel(&TestChannel3, WTFLogLevel::Error); |
| WTFSetLogChannelLevel(&TestChannel4, WTFLogLevel::Error); |
| } |
| |
| void TearDown() override |
| { |
| close(m_descriptors[0]); |
| fclose(m_stderr); |
| } |
| |
| String output() |
| { |
| char buffer[1024]; |
| StringBuilder result; |
| char* line; |
| |
| while ((line = fgets(buffer, sizeof(buffer), m_stderr))) |
| result.append(line); |
| |
| return result.toString(); |
| } |
| |
| const Logger& logger() const final { return m_logger.get(); } |
| const char* logClassName() const final { return "LoggingTest"; } |
| WTFLogChannel& logChannel() const final { return TestChannel1; } |
| const void* logIdentifier() const final { return reinterpret_cast<const void*>(123456789); } |
| |
| private: |
| |
| Ref<Logger> m_logger; |
| int m_descriptors[2]; |
| FILE* m_stderr; |
| }; |
| |
| TEST_F(LoggingTest, Initialization) |
| { |
| EXPECT_EQ(TestChannel1.state, WTFLogChannelState::On); |
| EXPECT_EQ(TestChannel2.state, WTFLogChannelState::On); |
| EXPECT_EQ(TestChannel3.state, WTFLogChannelState::On); |
| EXPECT_EQ(TestChannel4.state, WTFLogChannelState::On); |
| |
| EXPECT_EQ(TestChannel1.level, WTFLogLevel::Error); |
| EXPECT_EQ(TestChannel2.level, WTFLogLevel::Error); |
| EXPECT_EQ(TestChannel3.level, WTFLogLevel::Error); |
| EXPECT_EQ(TestChannel4.level, WTFLogLevel::Error); |
| |
| TestChannel1.state = WTFLogChannelState::Off; |
| WTFInitializeLogChannelStatesFromString(testLogChannels, logChannelCount, "Channel1"); |
| EXPECT_EQ(TestChannel1.level, WTFLogLevel::Error); |
| EXPECT_EQ(TestChannel1.state, WTFLogChannelState::On); |
| |
| TestChannel1.state = WTFLogChannelState::Off; |
| WTFInitializeLogChannelStatesFromString(testLogChannels, logChannelCount, "Channel1=foo"); |
| EXPECT_EQ(TestChannel1.level, WTFLogLevel::Error); |
| #if TEST_OUTPUT |
| EXPECT_TRUE(output().containsIgnoringASCIICase("Unknown logging level: foo")); |
| #endif |
| |
| WTFInitializeLogChannelStatesFromString(testLogChannels, logChannelCount, "Channel1=warning"); |
| EXPECT_EQ(TestChannel1.level, WTFLogLevel::Warning); |
| EXPECT_EQ(TestChannel2.level, WTFLogLevel::Error); |
| EXPECT_EQ(TestChannel3.level, WTFLogLevel::Error); |
| EXPECT_EQ(TestChannel4.level, WTFLogLevel::Error); |
| |
| WTFInitializeLogChannelStatesFromString(testLogChannels, logChannelCount, "Channel4= debug, Channel3 = info,Channel2=error"); |
| EXPECT_EQ(TestChannel1.level, WTFLogLevel::Warning); |
| EXPECT_EQ(TestChannel2.level, WTFLogLevel::Error); |
| EXPECT_EQ(TestChannel3.level, WTFLogLevel::Info); |
| EXPECT_EQ(TestChannel4.level, WTFLogLevel::Debug); |
| |
| WTFInitializeLogChannelStatesFromString(testLogChannels, logChannelCount, "-all"); |
| EXPECT_EQ(TestChannel1.state, WTFLogChannelState::Off); |
| EXPECT_EQ(TestChannel2.state, WTFLogChannelState::Off); |
| EXPECT_EQ(TestChannel3.state, WTFLogChannelState::Off); |
| EXPECT_EQ(TestChannel4.state, WTFLogChannelState::Off); |
| |
| WTFInitializeLogChannelStatesFromString(testLogChannels, logChannelCount, "all"); |
| EXPECT_EQ(TestChannel1.state, WTFLogChannelState::On); |
| EXPECT_EQ(TestChannel2.state, WTFLogChannelState::On); |
| EXPECT_EQ(TestChannel3.state, WTFLogChannelState::On); |
| EXPECT_EQ(TestChannel4.state, WTFLogChannelState::On); |
| } |
| |
| TEST_F(LoggingTest, WTFWillLogWithLevel) |
| { |
| EXPECT_EQ(TestChannel1.state, WTFLogChannelState::On); |
| EXPECT_EQ(TestChannel2.state, WTFLogChannelState::On); |
| EXPECT_EQ(TestChannel3.state, WTFLogChannelState::On); |
| EXPECT_EQ(TestChannel4.state, WTFLogChannelState::On); |
| |
| EXPECT_EQ(TestChannel1.level, WTFLogLevel::Error); |
| EXPECT_EQ(TestChannel2.level, WTFLogLevel::Error); |
| EXPECT_EQ(TestChannel3.level, WTFLogLevel::Error); |
| EXPECT_EQ(TestChannel4.level, WTFLogLevel::Error); |
| |
| EXPECT_TRUE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevel::Error)); |
| EXPECT_TRUE(WTFWillLogWithLevel(&TestChannel2, WTFLogLevel::Error)); |
| EXPECT_TRUE(WTFWillLogWithLevel(&TestChannel3, WTFLogLevel::Error)); |
| EXPECT_TRUE(WTFWillLogWithLevel(&TestChannel4, WTFLogLevel::Error)); |
| |
| EXPECT_FALSE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevel::Info)); |
| EXPECT_FALSE(WTFWillLogWithLevel(&TestChannel2, WTFLogLevel::Info)); |
| EXPECT_FALSE(WTFWillLogWithLevel(&TestChannel3, WTFLogLevel::Info)); |
| EXPECT_FALSE(WTFWillLogWithLevel(&TestChannel4, WTFLogLevel::Info)); |
| |
| TestChannel1.state = WTFLogChannelState::Off; |
| EXPECT_FALSE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevel::Error)); |
| EXPECT_FALSE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevel::Info)); |
| |
| TestChannel1.state = WTFLogChannelState::On; |
| EXPECT_TRUE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevel::Error)); |
| EXPECT_FALSE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevel::Info)); |
| |
| TestChannel1.level = WTFLogLevel::Info; |
| EXPECT_TRUE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevel::Error)); |
| EXPECT_TRUE(WTFWillLogWithLevel(&TestChannel1, WTFLogLevel::Info)); |
| } |
| |
| #if TEST_OUTPUT |
| TEST_F(LoggingTest, LOG) |
| { |
| LOG(Channel1, "Log message."); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("Log Message.")); |
| } |
| |
| TEST_F(LoggingTest, LOG_WITH_LEVEL) |
| { |
| LOG_WITH_LEVEL(Channel1, WTFLogLevel::Error, "Go and boil your bottoms, you sons of a silly person."); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("sons of a silly person.")); |
| |
| LOG_WITH_LEVEL(Channel1, WTFLogLevel::Warning, "You don't frighten us, English pig dogs."); |
| EXPECT_EQ(0u, output().length()); |
| |
| WTFSetLogChannelLevel(&TestChannel1, WTFLogLevel::Info); |
| LOG_WITH_LEVEL(Channel1, WTFLogLevel::Warning, "I'm French. Why do you think I have this outrageous accent, you silly king?"); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("outrageous accent")); |
| |
| LOG_WITH_LEVEL(Channel1, WTFLogLevel::Debug, "You don't frighten us with your silly knees-bent running around advancing behavior!"); |
| EXPECT_EQ(0u, output().length()); |
| |
| WTFSetLogChannelLevel(&TestChannel1, WTFLogLevel::Debug); |
| LOG_WITH_LEVEL(Channel1, WTFLogLevel::Debug, "Go and tell your master that we have been charged by God with a sacred quest."); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("sacred quest")); |
| } |
| |
| TEST_F(LoggingTest, RELEASE_LOG) |
| { |
| RELEASE_LOG(Channel1, "Log message."); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("Log Message.")); |
| } |
| |
| TEST_F(LoggingTest, RELEASE_LOG_IF) |
| { |
| bool enabled = true; |
| RELEASE_LOG_IF(enabled, Channel1, "Your mother was a hamster,"); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("hamster,")); |
| |
| enabled = false; |
| RELEASE_LOG_IF(enabled, Channel1, "and your father smelt of elderberries ..."); |
| EXPECT_EQ(0u, output().length()); |
| } |
| |
| TEST_F(LoggingTest, RELEASE_LOG_WITH_LEVEL) |
| { |
| RELEASE_LOG_WITH_LEVEL(Channel1, WTFLogLevel::Error, "You don't frighten us, English pig dogs."); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("pig dogs.")); |
| |
| RELEASE_LOG_WITH_LEVEL(Channel1, WTFLogLevel::Warning, "Go and boil your bottoms, you sons of a silly person."); |
| EXPECT_EQ(0u, output().length()); |
| |
| WTFSetLogChannelLevel(&TestChannel1, WTFLogLevel::Info); |
| RELEASE_LOG_WITH_LEVEL(Channel1, WTFLogLevel::Warning, "I'm French. Why do you think I have this outrageous accent, you silly king?"); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("outrageous accent")); |
| |
| RELEASE_LOG_WITH_LEVEL(Channel1, WTFLogLevel::Debug, "You don't frighten us with your silly knees-bent running around advancing behavior!"); |
| EXPECT_EQ(0u, output().length()); |
| |
| WTFSetLogChannelLevel(&TestChannel1, WTFLogLevel::Debug); |
| RELEASE_LOG_WITH_LEVEL(Channel1, WTFLogLevel::Debug, "Go and tell your master that we have been charged by God with a sacred quest."); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("sacred quest")); |
| } |
| |
| TEST_F(LoggingTest, RELEASE_LOG_WITH_LEVEL_IF) |
| { |
| bool enabled = true; |
| RELEASE_LOG_WITH_LEVEL_IF(enabled, Channel1, WTFLogLevel::Error, "Is there someone else up there that we can talk to?"); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("someone else")); |
| |
| RELEASE_LOG_WITH_LEVEL_IF(enabled, Channel1, WTFLogLevel::Debug, "No, now go away"); |
| EXPECT_EQ(0u, output().length()); |
| |
| enabled = false; |
| RELEASE_LOG_WITH_LEVEL_IF(enabled, Channel1, WTFLogLevel::Warning, "or I shall taunt you a second time! %i", 12); |
| EXPECT_EQ(0u, output().length()); |
| } |
| |
| TEST_F(LoggingTest, Logger) |
| { |
| Ref<Logger> logger = Logger::create(this); |
| EXPECT_TRUE(logger->enabled()); |
| |
| WTFSetLogChannelLevel(&TestChannel1, WTFLogLevel::Error); |
| EXPECT_TRUE(logger->willLog(TestChannel1, WTFLogLevel::Error)); |
| logger->error(TestChannel1, "You're using coconuts!"); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("You're using coconuts!")); |
| logger->warning(TestChannel1, "You're using coconuts!"); |
| EXPECT_EQ(0u, output().length()); |
| logger->info(TestChannel1, "You're using coconuts!"); |
| EXPECT_EQ(0u, output().length()); |
| logger->debug(TestChannel1, "You're using coconuts!"); |
| EXPECT_EQ(0u, output().length()); |
| |
| logger->error(TestChannel1, Logger::LogSiteIdentifier("LoggingTest::Logger", this) , ": test output"); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("LoggingTest::Logger(")); |
| |
| logger->error(TestChannel1, "What is ", 1, " + " , 12.5F, "?"); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("What is 1 + 12.5?")); |
| |
| logger->error(TestChannel1, "What, ", "ridden on a horse?"); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("What, ridden on a horse?")); |
| |
| logger->setEnabled(this, false); |
| EXPECT_FALSE(logger->enabled()); |
| logger->error(TestChannel1, "You've got two empty halves of coconuts"); |
| EXPECT_EQ(0u, output().length()); |
| |
| logger->setEnabled(this, true); |
| EXPECT_TRUE(logger->enabled()); |
| logger->error(TestChannel1, "You've got ", 2, " empty halves of ", "coconuts!"); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("You've got 2 empty halves of coconuts!")); |
| |
| WTFSetLogChannelLevel(&TestChannel1, WTFLogLevel::Error); |
| logger->logAlways(TestChannel1, "I shall taunt you a second time!"); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("I shall taunt you a second time!")); |
| |
| logger->setEnabled(this, false); |
| EXPECT_FALSE(logger->willLog(TestChannel1, WTFLogLevel::Error)); |
| EXPECT_FALSE(logger->willLog(TestChannel1, WTFLogLevel::Warning)); |
| EXPECT_FALSE(logger->willLog(TestChannel1, WTFLogLevel::Info)); |
| EXPECT_FALSE(logger->willLog(TestChannel1, WTFLogLevel::Debug)); |
| EXPECT_FALSE(logger->enabled()); |
| logger->logAlways(TestChannel1, "You've got two empty halves of coconuts"); |
| EXPECT_EQ(0u, output().length()); |
| |
| logger->setEnabled(this, true); |
| AtomString string1("AtomString"_s); |
| const AtomString string2("const AtomString"_s); |
| logger->logAlways(TestChannel1, string1, " and ", string2); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("AtomString and const AtomString")); |
| |
| String string3("String"); |
| const String string4("const String"); |
| logger->logAlways(TestChannel1, string3, " and ", string4); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("String and const String")); |
| } |
| |
| TEST_F(LoggingTest, LoggerHelper) |
| { |
| EXPECT_TRUE(logger().enabled()); |
| |
| StringBuilder builder; |
| builder.append("LoggingTest::TestBody("); |
| appendUnsigned64AsHex(reinterpret_cast<uintptr_t>(logIdentifier()), builder); |
| builder.append(")"); |
| String signature = builder.toString(); |
| |
| ALWAYS_LOG(LOGIDENTIFIER); |
| EXPECT_TRUE(this->output().containsIgnoringASCIICase(signature)); |
| |
| ALWAYS_LOG(LOGIDENTIFIER, "Welcome back", " my friends", " to the show", " that never ends"); |
| String result = this->output(); |
| EXPECT_TRUE(result.containsIgnoringASCIICase(signature)); |
| EXPECT_TRUE(result.containsIgnoringASCIICase("to the show that never")); |
| |
| WTFSetLogChannelLevel(&TestChannel1, WTFLogLevel::Warning); |
| |
| ERROR_LOG(LOGIDENTIFIER, "We're so glad you could attend"); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("We're so glad you could attend")); |
| |
| WARNING_LOG(LOGIDENTIFIER, "Come inside! ", "Come inside!"); |
| EXPECT_TRUE(output().containsIgnoringASCIICase("Come inside! Come inside!")); |
| |
| INFO_LOG(LOGIDENTIFIER, "be careful as you pass."); |
| EXPECT_EQ(0u, output().length()); |
| |
| DEBUG_LOG(LOGIDENTIFIER, "Move along! Move along!"); |
| EXPECT_EQ(0u, output().length()); |
| } |
| |
| class LogObserver : public Logger::Observer { |
| public: |
| LogObserver() = default; |
| |
| String log() |
| { |
| String log = m_logBuffer.toString(); |
| m_logBuffer.clear(); |
| |
| return log; |
| } |
| |
| WTFLogChannel channel() const { return m_lastChannel; } |
| WTFLogLevel level() const { return m_lastLevel; } |
| |
| private: |
| void didLogMessage(const WTFLogChannel& channel, WTFLogLevel level, Vector<JSONLogValue>&& logMessage) final |
| { |
| m_logBuffer.append(logMessage); |
| m_lastChannel = channel; |
| m_lastLevel = level; |
| } |
| StringBuilder m_logBuffer; |
| WTFLogChannel m_lastChannel; |
| WTFLogLevel m_lastLevel { WTFLogLevel::Error }; |
| }; |
| |
| #if !RELEASE_LOG_DISABLED |
| TEST_F(LoggingTest, LogObserver) |
| { |
| LogObserver observer; |
| |
| EXPECT_TRUE(logger().enabled()); |
| |
| logger().addObserver(observer); |
| ALWAYS_LOG(LOGIDENTIFIER, "testing 1, 2, 3"); |
| EXPECT_TRUE(this->output().containsIgnoringASCIICase("testing 1, 2, 3")); |
| EXPECT_TRUE(observer.log().containsIgnoringASCIICase("testing 1, 2, 3")); |
| EXPECT_STREQ(observer.channel().name, logChannel().name); |
| EXPECT_EQ(static_cast<int>(WTFLogLevel::Always), static_cast<int>(observer.level())); |
| |
| logger().removeObserver(observer); |
| ALWAYS_LOG("testing ", 1, ", ", 2, ", 3"); |
| EXPECT_TRUE(this->output().containsIgnoringASCIICase("testing 1, 2, 3")); |
| EXPECT_EQ(0u, observer.log().length()); |
| } |
| #endif |
| |
| #endif |
| |
| } // namespace TestWebKitAPI |