|  | // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/bind.h" | 
|  | #include "base/callback.h" | 
|  | #include "base/compiler_specific.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/strings/string_piece.h" | 
|  | #include "base/test/scoped_feature_list.h" | 
|  | #include "build/build_config.h" | 
|  |  | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | #if defined(OS_POSIX) | 
|  | #include <signal.h> | 
|  | #include <unistd.h> | 
|  | #include "base/posix/eintr_wrapper.h" | 
|  | #endif  // OS_POSIX | 
|  |  | 
|  | #if defined(OS_LINUX) || defined(OS_ANDROID) | 
|  | #include <ucontext.h> | 
|  | #endif | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | #include <excpt.h> | 
|  | #include <windows.h> | 
|  | #endif  // OS_WIN | 
|  |  | 
|  | #if defined(OS_FUCHSIA) | 
|  | #include "base/fuchsia/fuchsia_logging.h" | 
|  | #endif | 
|  |  | 
|  | namespace logging { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | using ::testing::Return; | 
|  | using ::testing::_; | 
|  |  | 
|  | // Needs to be global since log assert handlers can't maintain state. | 
|  | int g_log_sink_call_count = 0; | 
|  |  | 
|  | #if !defined(OFFICIAL_BUILD) || defined(DCHECK_ALWAYS_ON) || !defined(NDEBUG) | 
|  | void LogSink(const char* file, | 
|  | int line, | 
|  | const base::StringPiece message, | 
|  | const base::StringPiece stack_trace) { | 
|  | ++g_log_sink_call_count; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | // Class to make sure any manipulations we do to the min log level are | 
|  | // contained (i.e., do not affect other unit tests). | 
|  | class LogStateSaver { | 
|  | public: | 
|  | LogStateSaver() : old_min_log_level_(GetMinLogLevel()) {} | 
|  |  | 
|  | ~LogStateSaver() { | 
|  | SetMinLogLevel(old_min_log_level_); | 
|  | g_log_sink_call_count = 0; | 
|  | } | 
|  |  | 
|  | private: | 
|  | int old_min_log_level_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(LogStateSaver); | 
|  | }; | 
|  |  | 
|  | class LoggingTest : public testing::Test { | 
|  | private: | 
|  | LogStateSaver log_state_saver_; | 
|  | }; | 
|  |  | 
|  | class MockLogSource { | 
|  | public: | 
|  | MOCK_METHOD0(Log, const char*()); | 
|  | }; | 
|  |  | 
|  | class MockLogAssertHandler { | 
|  | public: | 
|  | MOCK_METHOD4( | 
|  | HandleLogAssert, | 
|  | void(const char*, int, const base::StringPiece, const base::StringPiece)); | 
|  | }; | 
|  |  | 
|  | TEST_F(LoggingTest, BasicLogging) { | 
|  | MockLogSource mock_log_source; | 
|  | EXPECT_CALL(mock_log_source, Log()) | 
|  | .Times(DCHECK_IS_ON() ? 16 : 8) | 
|  | .WillRepeatedly(Return("log message")); | 
|  |  | 
|  | SetMinLogLevel(LOG_INFO); | 
|  |  | 
|  | EXPECT_TRUE(LOG_IS_ON(INFO)); | 
|  | EXPECT_TRUE((DCHECK_IS_ON() != 0) == DLOG_IS_ON(INFO)); | 
|  | EXPECT_TRUE(VLOG_IS_ON(0)); | 
|  |  | 
|  | LOG(INFO) << mock_log_source.Log(); | 
|  | LOG_IF(INFO, true) << mock_log_source.Log(); | 
|  | PLOG(INFO) << mock_log_source.Log(); | 
|  | PLOG_IF(INFO, true) << mock_log_source.Log(); | 
|  | VLOG(0) << mock_log_source.Log(); | 
|  | VLOG_IF(0, true) << mock_log_source.Log(); | 
|  | VPLOG(0) << mock_log_source.Log(); | 
|  | VPLOG_IF(0, true) << mock_log_source.Log(); | 
|  |  | 
|  | DLOG(INFO) << mock_log_source.Log(); | 
|  | DLOG_IF(INFO, true) << mock_log_source.Log(); | 
|  | DPLOG(INFO) << mock_log_source.Log(); | 
|  | DPLOG_IF(INFO, true) << mock_log_source.Log(); | 
|  | DVLOG(0) << mock_log_source.Log(); | 
|  | DVLOG_IF(0, true) << mock_log_source.Log(); | 
|  | DVPLOG(0) << mock_log_source.Log(); | 
|  | DVPLOG_IF(0, true) << mock_log_source.Log(); | 
|  | } | 
|  |  | 
|  | TEST_F(LoggingTest, LogIsOn) { | 
|  | #if defined(NDEBUG) | 
|  | const bool kDfatalIsFatal = false; | 
|  | #else  // defined(NDEBUG) | 
|  | const bool kDfatalIsFatal = true; | 
|  | #endif  // defined(NDEBUG) | 
|  |  | 
|  | SetMinLogLevel(LOG_INFO); | 
|  | EXPECT_TRUE(LOG_IS_ON(INFO)); | 
|  | EXPECT_TRUE(LOG_IS_ON(WARNING)); | 
|  | EXPECT_TRUE(LOG_IS_ON(ERROR)); | 
|  | EXPECT_TRUE(LOG_IS_ON(FATAL)); | 
|  | EXPECT_TRUE(LOG_IS_ON(DFATAL)); | 
|  |  | 
|  | SetMinLogLevel(LOG_WARNING); | 
|  | EXPECT_FALSE(LOG_IS_ON(INFO)); | 
|  | EXPECT_TRUE(LOG_IS_ON(WARNING)); | 
|  | EXPECT_TRUE(LOG_IS_ON(ERROR)); | 
|  | EXPECT_TRUE(LOG_IS_ON(FATAL)); | 
|  | EXPECT_TRUE(LOG_IS_ON(DFATAL)); | 
|  |  | 
|  | SetMinLogLevel(LOG_ERROR); | 
|  | EXPECT_FALSE(LOG_IS_ON(INFO)); | 
|  | EXPECT_FALSE(LOG_IS_ON(WARNING)); | 
|  | EXPECT_TRUE(LOG_IS_ON(ERROR)); | 
|  | EXPECT_TRUE(LOG_IS_ON(FATAL)); | 
|  | EXPECT_TRUE(LOG_IS_ON(DFATAL)); | 
|  |  | 
|  | // LOG_IS_ON(FATAL) should always be true. | 
|  | SetMinLogLevel(LOG_FATAL + 1); | 
|  | EXPECT_FALSE(LOG_IS_ON(INFO)); | 
|  | EXPECT_FALSE(LOG_IS_ON(WARNING)); | 
|  | EXPECT_FALSE(LOG_IS_ON(ERROR)); | 
|  | EXPECT_TRUE(LOG_IS_ON(FATAL)); | 
|  | EXPECT_EQ(kDfatalIsFatal, LOG_IS_ON(DFATAL)); | 
|  | } | 
|  |  | 
|  | TEST_F(LoggingTest, LoggingIsLazyBySeverity) { | 
|  | MockLogSource mock_log_source; | 
|  | EXPECT_CALL(mock_log_source, Log()).Times(0); | 
|  |  | 
|  | SetMinLogLevel(LOG_WARNING); | 
|  |  | 
|  | EXPECT_FALSE(LOG_IS_ON(INFO)); | 
|  | EXPECT_FALSE(DLOG_IS_ON(INFO)); | 
|  | EXPECT_FALSE(VLOG_IS_ON(1)); | 
|  |  | 
|  | LOG(INFO) << mock_log_source.Log(); | 
|  | LOG_IF(INFO, false) << mock_log_source.Log(); | 
|  | PLOG(INFO) << mock_log_source.Log(); | 
|  | PLOG_IF(INFO, false) << mock_log_source.Log(); | 
|  | VLOG(1) << mock_log_source.Log(); | 
|  | VLOG_IF(1, true) << mock_log_source.Log(); | 
|  | VPLOG(1) << mock_log_source.Log(); | 
|  | VPLOG_IF(1, true) << mock_log_source.Log(); | 
|  |  | 
|  | DLOG(INFO) << mock_log_source.Log(); | 
|  | DLOG_IF(INFO, true) << mock_log_source.Log(); | 
|  | DPLOG(INFO) << mock_log_source.Log(); | 
|  | DPLOG_IF(INFO, true) << mock_log_source.Log(); | 
|  | DVLOG(1) << mock_log_source.Log(); | 
|  | DVLOG_IF(1, true) << mock_log_source.Log(); | 
|  | DVPLOG(1) << mock_log_source.Log(); | 
|  | DVPLOG_IF(1, true) << mock_log_source.Log(); | 
|  | } | 
|  |  | 
|  | TEST_F(LoggingTest, LoggingIsLazyByDestination) { | 
|  | MockLogSource mock_log_source; | 
|  | MockLogSource mock_log_source_error; | 
|  | EXPECT_CALL(mock_log_source, Log()).Times(0); | 
|  |  | 
|  | // Severity >= ERROR is always printed to stderr. | 
|  | EXPECT_CALL(mock_log_source_error, Log()).Times(1). | 
|  | WillRepeatedly(Return("log message")); | 
|  |  | 
|  | LoggingSettings settings; | 
|  | settings.logging_dest = LOG_NONE; | 
|  | InitLogging(settings); | 
|  |  | 
|  | LOG(INFO) << mock_log_source.Log(); | 
|  | LOG(WARNING) << mock_log_source.Log(); | 
|  | LOG(ERROR) << mock_log_source_error.Log(); | 
|  | } | 
|  |  | 
|  | // Official builds have CHECKs directly call BreakDebugger. | 
|  | #if !defined(OFFICIAL_BUILD) | 
|  |  | 
|  | // https://crbug.com/709067 tracks test flakiness on iOS. | 
|  | #if defined(OS_IOS) | 
|  | #define MAYBE_CheckStreamsAreLazy DISABLED_CheckStreamsAreLazy | 
|  | #else | 
|  | #define MAYBE_CheckStreamsAreLazy CheckStreamsAreLazy | 
|  | #endif | 
|  | TEST_F(LoggingTest, MAYBE_CheckStreamsAreLazy) { | 
|  | MockLogSource mock_log_source, uncalled_mock_log_source; | 
|  | EXPECT_CALL(mock_log_source, Log()).Times(8). | 
|  | WillRepeatedly(Return("check message")); | 
|  | EXPECT_CALL(uncalled_mock_log_source, Log()).Times(0); | 
|  |  | 
|  | ScopedLogAssertHandler scoped_assert_handler(base::Bind(LogSink)); | 
|  |  | 
|  | CHECK(mock_log_source.Log()) << uncalled_mock_log_source.Log(); | 
|  | PCHECK(!mock_log_source.Log()) << mock_log_source.Log(); | 
|  | CHECK_EQ(mock_log_source.Log(), mock_log_source.Log()) | 
|  | << uncalled_mock_log_source.Log(); | 
|  | CHECK_NE(mock_log_source.Log(), mock_log_source.Log()) | 
|  | << mock_log_source.Log(); | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | #if defined(OFFICIAL_BUILD) && defined(OS_WIN) | 
|  | NOINLINE void CheckContainingFunc(int death_location) { | 
|  | CHECK(death_location != 1); | 
|  | CHECK(death_location != 2); | 
|  | CHECK(death_location != 3); | 
|  | } | 
|  |  | 
|  | int GetCheckExceptionData(EXCEPTION_POINTERS* p, DWORD* code, void** addr) { | 
|  | *code = p->ExceptionRecord->ExceptionCode; | 
|  | *addr = p->ExceptionRecord->ExceptionAddress; | 
|  | return EXCEPTION_EXECUTE_HANDLER; | 
|  | } | 
|  |  | 
|  | TEST_F(LoggingTest, CheckCausesDistinctBreakpoints) { | 
|  | DWORD code1 = 0; | 
|  | DWORD code2 = 0; | 
|  | DWORD code3 = 0; | 
|  | void* addr1 = nullptr; | 
|  | void* addr2 = nullptr; | 
|  | void* addr3 = nullptr; | 
|  |  | 
|  | // Record the exception code and addresses. | 
|  | __try { | 
|  | CheckContainingFunc(1); | 
|  | } __except ( | 
|  | GetCheckExceptionData(GetExceptionInformation(), &code1, &addr1)) { | 
|  | } | 
|  |  | 
|  | __try { | 
|  | CheckContainingFunc(2); | 
|  | } __except ( | 
|  | GetCheckExceptionData(GetExceptionInformation(), &code2, &addr2)) { | 
|  | } | 
|  |  | 
|  | __try { | 
|  | CheckContainingFunc(3); | 
|  | } __except ( | 
|  | GetCheckExceptionData(GetExceptionInformation(), &code3, &addr3)) { | 
|  | } | 
|  |  | 
|  | // Ensure that the exception codes are correct (in particular, breakpoints, | 
|  | // not access violations). | 
|  | EXPECT_EQ(STATUS_BREAKPOINT, code1); | 
|  | EXPECT_EQ(STATUS_BREAKPOINT, code2); | 
|  | EXPECT_EQ(STATUS_BREAKPOINT, code3); | 
|  |  | 
|  | // Ensure that none of the CHECKs are colocated. | 
|  | EXPECT_NE(addr1, addr2); | 
|  | EXPECT_NE(addr1, addr3); | 
|  | EXPECT_NE(addr2, addr3); | 
|  | } | 
|  |  | 
|  | #elif defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_IOS) && \ | 
|  | !defined(OS_FUCHSIA) &&                                         \ | 
|  | (defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY)) | 
|  |  | 
|  | int g_child_crash_pipe; | 
|  |  | 
|  | void CheckCrashTestSighandler(int, siginfo_t* info, void* context_ptr) { | 
|  | // Conversely to what clearly stated in "man 2 sigaction", some Linux kernels | 
|  | // do NOT populate the |info->si_addr| in the case of a SIGTRAP. Hence we | 
|  | // need the arch-specific boilerplate below, which is inspired by breakpad. | 
|  | // At the same time, on OSX, ucontext.h is deprecated but si_addr works fine. | 
|  | uintptr_t crash_addr = 0; | 
|  | #if defined(OS_MACOSX) | 
|  | crash_addr = reinterpret_cast<uintptr_t>(info->si_addr); | 
|  | #else  // OS_POSIX && !OS_MACOSX | 
|  | ucontext_t* context = reinterpret_cast<ucontext_t*>(context_ptr); | 
|  | #if defined(ARCH_CPU_X86) | 
|  | crash_addr = static_cast<uintptr_t>(context->uc_mcontext.gregs[REG_EIP]); | 
|  | #elif defined(ARCH_CPU_X86_64) | 
|  | crash_addr = static_cast<uintptr_t>(context->uc_mcontext.gregs[REG_RIP]); | 
|  | #elif defined(ARCH_CPU_ARMEL) | 
|  | crash_addr = static_cast<uintptr_t>(context->uc_mcontext.arm_pc); | 
|  | #elif defined(ARCH_CPU_ARM64) | 
|  | crash_addr = static_cast<uintptr_t>(context->uc_mcontext.pc); | 
|  | #endif  // ARCH_* | 
|  | #endif  // OS_POSIX && !OS_MACOSX | 
|  | HANDLE_EINTR(write(g_child_crash_pipe, &crash_addr, sizeof(uintptr_t))); | 
|  | _exit(0); | 
|  | } | 
|  |  | 
|  | // CHECK causes a direct crash (without jumping to another function) only in | 
|  | // official builds. Unfortunately, continuous test coverage on official builds | 
|  | // is lower. DO_CHECK here falls back on a home-brewed implementation in | 
|  | // non-official builds, to catch regressions earlier in the CQ. | 
|  | #if defined(OFFICIAL_BUILD) | 
|  | #define DO_CHECK CHECK | 
|  | #else | 
|  | #define DO_CHECK(cond) \ | 
|  | if (!(cond))         \ | 
|  | IMMEDIATE_CRASH() | 
|  | #endif | 
|  |  | 
|  | void CrashChildMain(int death_location) { | 
|  | struct sigaction act = {}; | 
|  | act.sa_sigaction = CheckCrashTestSighandler; | 
|  | act.sa_flags = SA_SIGINFO; | 
|  | ASSERT_EQ(0, sigaction(SIGTRAP, &act, nullptr)); | 
|  | ASSERT_EQ(0, sigaction(SIGBUS, &act, nullptr)); | 
|  | ASSERT_EQ(0, sigaction(SIGILL, &act, nullptr)); | 
|  | DO_CHECK(death_location != 1); | 
|  | DO_CHECK(death_location != 2); | 
|  | printf("\n"); | 
|  | DO_CHECK(death_location != 3); | 
|  |  | 
|  | // Should never reach this point. | 
|  | const uintptr_t failed = 0; | 
|  | HANDLE_EINTR(write(g_child_crash_pipe, &failed, sizeof(uintptr_t))); | 
|  | }; | 
|  |  | 
|  | void SpawnChildAndCrash(int death_location, uintptr_t* child_crash_addr) { | 
|  | int pipefd[2]; | 
|  | ASSERT_EQ(0, pipe(pipefd)); | 
|  |  | 
|  | int pid = fork(); | 
|  | ASSERT_GE(pid, 0); | 
|  |  | 
|  | if (pid == 0) {      // child process. | 
|  | close(pipefd[0]);  // Close reader (parent) end. | 
|  | g_child_crash_pipe = pipefd[1]; | 
|  | CrashChildMain(death_location); | 
|  | FAIL() << "The child process was supposed to crash. It didn't."; | 
|  | } | 
|  |  | 
|  | close(pipefd[1]);  // Close writer (child) end. | 
|  | DCHECK(child_crash_addr); | 
|  | int res = HANDLE_EINTR(read(pipefd[0], child_crash_addr, sizeof(uintptr_t))); | 
|  | ASSERT_EQ(static_cast<int>(sizeof(uintptr_t)), res); | 
|  | } | 
|  |  | 
|  | TEST_F(LoggingTest, CheckCausesDistinctBreakpoints) { | 
|  | uintptr_t child_crash_addr_1 = 0; | 
|  | uintptr_t child_crash_addr_2 = 0; | 
|  | uintptr_t child_crash_addr_3 = 0; | 
|  |  | 
|  | SpawnChildAndCrash(1, &child_crash_addr_1); | 
|  | SpawnChildAndCrash(2, &child_crash_addr_2); | 
|  | SpawnChildAndCrash(3, &child_crash_addr_3); | 
|  |  | 
|  | ASSERT_NE(0u, child_crash_addr_1); | 
|  | ASSERT_NE(0u, child_crash_addr_2); | 
|  | ASSERT_NE(0u, child_crash_addr_3); | 
|  | ASSERT_NE(child_crash_addr_1, child_crash_addr_2); | 
|  | ASSERT_NE(child_crash_addr_1, child_crash_addr_3); | 
|  | ASSERT_NE(child_crash_addr_2, child_crash_addr_3); | 
|  | } | 
|  | #endif  // OS_POSIX | 
|  |  | 
|  | TEST_F(LoggingTest, DebugLoggingReleaseBehavior) { | 
|  | #if DCHECK_IS_ON() | 
|  | int debug_only_variable = 1; | 
|  | #endif | 
|  | // These should avoid emitting references to |debug_only_variable| | 
|  | // in release mode. | 
|  | DLOG_IF(INFO, debug_only_variable) << "test"; | 
|  | DLOG_ASSERT(debug_only_variable) << "test"; | 
|  | DPLOG_IF(INFO, debug_only_variable) << "test"; | 
|  | DVLOG_IF(1, debug_only_variable) << "test"; | 
|  | } | 
|  |  | 
|  | TEST_F(LoggingTest, DcheckStreamsAreLazy) { | 
|  | MockLogSource mock_log_source; | 
|  | EXPECT_CALL(mock_log_source, Log()).Times(0); | 
|  | #if DCHECK_IS_ON() | 
|  | DCHECK(true) << mock_log_source.Log(); | 
|  | DCHECK_EQ(0, 0) << mock_log_source.Log(); | 
|  | #else | 
|  | DCHECK(mock_log_source.Log()) << mock_log_source.Log(); | 
|  | DPCHECK(mock_log_source.Log()) << mock_log_source.Log(); | 
|  | DCHECK_EQ(0, 0) << mock_log_source.Log(); | 
|  | DCHECK_EQ(mock_log_source.Log(), static_cast<const char*>(nullptr)) | 
|  | << mock_log_source.Log(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void DcheckEmptyFunction1() { | 
|  | // Provide a body so that Release builds do not cause the compiler to | 
|  | // optimize DcheckEmptyFunction1 and DcheckEmptyFunction2 as a single | 
|  | // function, which breaks the Dcheck tests below. | 
|  | LOG(INFO) << "DcheckEmptyFunction1"; | 
|  | } | 
|  | void DcheckEmptyFunction2() {} | 
|  |  | 
|  | #if DCHECK_IS_CONFIGURABLE | 
|  | class ScopedDcheckSeverity { | 
|  | public: | 
|  | ScopedDcheckSeverity(LogSeverity new_severity) : old_severity_(LOG_DCHECK) { | 
|  | LOG_DCHECK = new_severity; | 
|  | } | 
|  |  | 
|  | ~ScopedDcheckSeverity() { LOG_DCHECK = old_severity_; } | 
|  |  | 
|  | private: | 
|  | LogSeverity old_severity_; | 
|  | }; | 
|  | #endif  // DCHECK_IS_CONFIGURABLE | 
|  |  | 
|  | // https://crbug.com/709067 tracks test flakiness on iOS. | 
|  | #if defined(OS_IOS) | 
|  | #define MAYBE_Dcheck DISABLED_Dcheck | 
|  | #else | 
|  | #define MAYBE_Dcheck Dcheck | 
|  | #endif | 
|  | TEST_F(LoggingTest, MAYBE_Dcheck) { | 
|  | #if DCHECK_IS_CONFIGURABLE | 
|  | // DCHECKs are enabled, and LOG_DCHECK is mutable, but defaults to non-fatal. | 
|  | // Set it to LOG_FATAL to get the expected behavior from the rest of this | 
|  | // test. | 
|  | ScopedDcheckSeverity dcheck_severity(LOG_FATAL); | 
|  | #endif  // DCHECK_IS_CONFIGURABLE | 
|  |  | 
|  | #if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) | 
|  | // Release build. | 
|  | EXPECT_FALSE(DCHECK_IS_ON()); | 
|  | EXPECT_FALSE(DLOG_IS_ON(DCHECK)); | 
|  | #elif defined(NDEBUG) && defined(DCHECK_ALWAYS_ON) | 
|  | // Release build with real DCHECKS. | 
|  | ScopedLogAssertHandler scoped_assert_handler(base::Bind(LogSink)); | 
|  | EXPECT_TRUE(DCHECK_IS_ON()); | 
|  | EXPECT_TRUE(DLOG_IS_ON(DCHECK)); | 
|  | #else | 
|  | // Debug build. | 
|  | ScopedLogAssertHandler scoped_assert_handler(base::Bind(LogSink)); | 
|  | EXPECT_TRUE(DCHECK_IS_ON()); | 
|  | EXPECT_TRUE(DLOG_IS_ON(DCHECK)); | 
|  | #endif | 
|  |  | 
|  | // DCHECKs are fatal iff they're compiled in DCHECK_IS_ON() and the DCHECK | 
|  | // log level is set to fatal. | 
|  | const bool dchecks_are_fatal = DCHECK_IS_ON() && LOG_DCHECK == LOG_FATAL; | 
|  | EXPECT_EQ(0, g_log_sink_call_count); | 
|  | DCHECK(false); | 
|  | EXPECT_EQ(dchecks_are_fatal ? 1 : 0, g_log_sink_call_count); | 
|  | DPCHECK(false); | 
|  | EXPECT_EQ(dchecks_are_fatal ? 2 : 0, g_log_sink_call_count); | 
|  | DCHECK_EQ(0, 1); | 
|  | EXPECT_EQ(dchecks_are_fatal ? 3 : 0, g_log_sink_call_count); | 
|  |  | 
|  | // Test DCHECK on std::nullptr_t | 
|  | g_log_sink_call_count = 0; | 
|  | const void* p_null = nullptr; | 
|  | const void* p_not_null = &p_null; | 
|  | DCHECK_EQ(p_null, nullptr); | 
|  | DCHECK_EQ(nullptr, p_null); | 
|  | DCHECK_NE(p_not_null, nullptr); | 
|  | DCHECK_NE(nullptr, p_not_null); | 
|  | EXPECT_EQ(0, g_log_sink_call_count); | 
|  |  | 
|  | // Test DCHECK on a scoped enum. | 
|  | enum class Animal { DOG, CAT }; | 
|  | DCHECK_EQ(Animal::DOG, Animal::DOG); | 
|  | EXPECT_EQ(0, g_log_sink_call_count); | 
|  | DCHECK_EQ(Animal::DOG, Animal::CAT); | 
|  | EXPECT_EQ(dchecks_are_fatal ? 1 : 0, g_log_sink_call_count); | 
|  |  | 
|  | // Test DCHECK on functions and function pointers. | 
|  | g_log_sink_call_count = 0; | 
|  | struct MemberFunctions { | 
|  | void MemberFunction1() { | 
|  | // See the comment in DcheckEmptyFunction1(). | 
|  | LOG(INFO) << "Do not merge with MemberFunction2."; | 
|  | } | 
|  | void MemberFunction2() {} | 
|  | }; | 
|  | void (MemberFunctions::*mp1)() = &MemberFunctions::MemberFunction1; | 
|  | void (MemberFunctions::*mp2)() = &MemberFunctions::MemberFunction2; | 
|  | void (*fp1)() = DcheckEmptyFunction1; | 
|  | void (*fp2)() = DcheckEmptyFunction2; | 
|  | void (*fp3)() = DcheckEmptyFunction1; | 
|  | DCHECK_EQ(fp1, fp3); | 
|  | EXPECT_EQ(0, g_log_sink_call_count); | 
|  | DCHECK_EQ(mp1, &MemberFunctions::MemberFunction1); | 
|  | EXPECT_EQ(0, g_log_sink_call_count); | 
|  | DCHECK_EQ(mp2, &MemberFunctions::MemberFunction2); | 
|  | EXPECT_EQ(0, g_log_sink_call_count); | 
|  | DCHECK_EQ(fp1, fp2); | 
|  | EXPECT_EQ(dchecks_are_fatal ? 1 : 0, g_log_sink_call_count); | 
|  | DCHECK_EQ(mp2, &MemberFunctions::MemberFunction1); | 
|  | EXPECT_EQ(dchecks_are_fatal ? 2 : 0, g_log_sink_call_count); | 
|  | } | 
|  |  | 
|  | TEST_F(LoggingTest, DcheckReleaseBehavior) { | 
|  | int some_variable = 1; | 
|  | // These should still reference |some_variable| so we don't get | 
|  | // unused variable warnings. | 
|  | DCHECK(some_variable) << "test"; | 
|  | DPCHECK(some_variable) << "test"; | 
|  | DCHECK_EQ(some_variable, 1) << "test"; | 
|  | } | 
|  |  | 
|  | TEST_F(LoggingTest, DCheckEqStatements) { | 
|  | bool reached = false; | 
|  | if (false) | 
|  | DCHECK_EQ(false, true);           // Unreached. | 
|  | else | 
|  | DCHECK_EQ(true, reached = true);  // Reached, passed. | 
|  | ASSERT_EQ(DCHECK_IS_ON() ? true : false, reached); | 
|  |  | 
|  | if (false) | 
|  | DCHECK_EQ(false, true);           // Unreached. | 
|  | } | 
|  |  | 
|  | TEST_F(LoggingTest, CheckEqStatements) { | 
|  | bool reached = false; | 
|  | if (false) | 
|  | CHECK_EQ(false, true);           // Unreached. | 
|  | else | 
|  | CHECK_EQ(true, reached = true);  // Reached, passed. | 
|  | ASSERT_TRUE(reached); | 
|  |  | 
|  | if (false) | 
|  | CHECK_EQ(false, true);           // Unreached. | 
|  | } | 
|  |  | 
|  | TEST_F(LoggingTest, NestedLogAssertHandlers) { | 
|  | ::testing::InSequence dummy; | 
|  | ::testing::StrictMock<MockLogAssertHandler> handler_a, handler_b; | 
|  |  | 
|  | EXPECT_CALL( | 
|  | handler_a, | 
|  | HandleLogAssert( | 
|  | _, _, base::StringPiece("First assert must be caught by handler_a"), | 
|  | _)); | 
|  | EXPECT_CALL( | 
|  | handler_b, | 
|  | HandleLogAssert( | 
|  | _, _, base::StringPiece("Second assert must be caught by handler_b"), | 
|  | _)); | 
|  | EXPECT_CALL( | 
|  | handler_a, | 
|  | HandleLogAssert( | 
|  | _, _, | 
|  | base::StringPiece("Last assert must be caught by handler_a again"), | 
|  | _)); | 
|  |  | 
|  | logging::ScopedLogAssertHandler scoped_handler_a(base::Bind( | 
|  | &MockLogAssertHandler::HandleLogAssert, base::Unretained(&handler_a))); | 
|  |  | 
|  | // Using LOG(FATAL) rather than CHECK(false) here since log messages aren't | 
|  | // preserved for CHECKs in official builds. | 
|  | LOG(FATAL) << "First assert must be caught by handler_a"; | 
|  |  | 
|  | { | 
|  | logging::ScopedLogAssertHandler scoped_handler_b(base::Bind( | 
|  | &MockLogAssertHandler::HandleLogAssert, base::Unretained(&handler_b))); | 
|  | LOG(FATAL) << "Second assert must be caught by handler_b"; | 
|  | } | 
|  |  | 
|  | LOG(FATAL) << "Last assert must be caught by handler_a again"; | 
|  | } | 
|  |  | 
|  | // Test that defining an operator<< for a type in a namespace doesn't prevent | 
|  | // other code in that namespace from calling the operator<<(ostream, wstring) | 
|  | // defined by logging.h. This can fail if operator<<(ostream, wstring) can't be | 
|  | // found by ADL, since defining another operator<< prevents name lookup from | 
|  | // looking in the global namespace. | 
|  | namespace nested_test { | 
|  | class Streamable {}; | 
|  | ALLOW_UNUSED_TYPE std::ostream& operator<<(std::ostream& out, | 
|  | const Streamable&) { | 
|  | return out << "Streamable"; | 
|  | } | 
|  | TEST_F(LoggingTest, StreamingWstringFindsCorrectOperator) { | 
|  | std::wstring wstr = L"Hello World"; | 
|  | std::ostringstream ostr; | 
|  | ostr << wstr; | 
|  | EXPECT_EQ("Hello World", ostr.str()); | 
|  | } | 
|  | }  // namespace nested_test | 
|  |  | 
|  | #if DCHECK_IS_CONFIGURABLE | 
|  | TEST_F(LoggingTest, ConfigurableDCheck) { | 
|  | // Verify that DCHECKs default to non-fatal in configurable-DCHECK builds. | 
|  | // Note that we require only that DCHECK is non-fatal by default, rather | 
|  | // than requiring that it be exactly INFO, ERROR, etc level. | 
|  | EXPECT_LT(LOG_DCHECK, LOG_FATAL); | 
|  | DCHECK(false); | 
|  |  | 
|  | // Verify that DCHECK* aren't hard-wired to crash on failure. | 
|  | LOG_DCHECK = LOG_INFO; | 
|  | DCHECK(false); | 
|  | DCHECK_EQ(1, 2); | 
|  |  | 
|  | // Verify that DCHECK does crash if LOG_DCHECK is set to LOG_FATAL. | 
|  | LOG_DCHECK = LOG_FATAL; | 
|  |  | 
|  | ::testing::StrictMock<MockLogAssertHandler> handler; | 
|  | EXPECT_CALL(handler, HandleLogAssert(_, _, _, _)).Times(2); | 
|  | { | 
|  | logging::ScopedLogAssertHandler scoped_handler_b(base::Bind( | 
|  | &MockLogAssertHandler::HandleLogAssert, base::Unretained(&handler))); | 
|  | DCHECK(false); | 
|  | DCHECK_EQ(1, 2); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(LoggingTest, ConfigurableDCheckFeature) { | 
|  | // Initialize FeatureList with and without DcheckIsFatal, and verify the | 
|  | // value of LOG_DCHECK. Note that we don't require that DCHECK take a | 
|  | // specific value when the feature is off, only that it is non-fatal. | 
|  |  | 
|  | { | 
|  | base::test::ScopedFeatureList feature_list; | 
|  | feature_list.InitFromCommandLine("DcheckIsFatal", ""); | 
|  | EXPECT_EQ(LOG_DCHECK, LOG_FATAL); | 
|  | } | 
|  |  | 
|  | { | 
|  | base::test::ScopedFeatureList feature_list; | 
|  | feature_list.InitFromCommandLine("", "DcheckIsFatal"); | 
|  | EXPECT_LT(LOG_DCHECK, LOG_FATAL); | 
|  | } | 
|  |  | 
|  | // The default case is last, so we leave LOG_DCHECK in the default state. | 
|  | { | 
|  | base::test::ScopedFeatureList feature_list; | 
|  | feature_list.InitFromCommandLine("", ""); | 
|  | EXPECT_LT(LOG_DCHECK, LOG_FATAL); | 
|  | } | 
|  | } | 
|  | #endif  // DCHECK_IS_CONFIGURABLE | 
|  |  | 
|  | #if defined(OS_FUCHSIA) | 
|  | TEST_F(LoggingTest, FuchsiaLogging) { | 
|  | MockLogSource mock_log_source; | 
|  | EXPECT_CALL(mock_log_source, Log()) | 
|  | .Times(DCHECK_IS_ON() ? 2 : 1) | 
|  | .WillRepeatedly(Return("log message")); | 
|  |  | 
|  | SetMinLogLevel(LOG_INFO); | 
|  |  | 
|  | EXPECT_TRUE(LOG_IS_ON(INFO)); | 
|  | EXPECT_TRUE((DCHECK_IS_ON() != 0) == DLOG_IS_ON(INFO)); | 
|  |  | 
|  | ZX_LOG(INFO, ZX_ERR_INTERNAL) << mock_log_source.Log(); | 
|  | ZX_DLOG(INFO, ZX_ERR_INTERNAL) << mock_log_source.Log(); | 
|  |  | 
|  | ZX_CHECK(true, ZX_ERR_INTERNAL); | 
|  | ZX_DCHECK(true, ZX_ERR_INTERNAL); | 
|  | } | 
|  | #endif  // defined(OS_FUCHSIA) | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | }  // namespace logging |