+// Copyright (c) 2002, Google Inc.
+// All rights reserved.
//
-// Copyright 2002 Google, 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:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * 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.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+// OWNER OR 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.
//
// Author: Ray Sidney
#include <iomanip>
#include <iostream>
+#include <memory>
#include <queue>
#include <sstream>
#include <string>
#include <vector>
+#include <stdio.h>
+#include <stdlib.h>
+
#include "base/commandlineflags.h"
#include "glog/logging.h"
#include "glog/raw_logging.h"
#include "googletest.h"
+DECLARE_string(log_backtrace_at); // logging.cc
+
+#ifdef HAVE_LIB_GFLAGS
+#include <gflags/gflags.h>
+using namespace GFLAGS_NAMESPACE;
+#endif
+
+#ifdef HAVE_LIB_GMOCK
+#include <gmock/gmock.h>
+#include "mock-log.h"
+// Introduce several symbols from gmock.
+using testing::_;
+using testing::AnyNumber;
+using testing::HasSubstr;
+using testing::AllOf;
+using testing::StrNe;
+using testing::StrictMock;
+using testing::InitGoogleMock;
+using GOOGLE_NAMESPACE::glog_testing::ScopedMockLog;
+#endif
+
using namespace std;
using namespace GOOGLE_NAMESPACE;
+// Some non-advertised functions that we want to test or use.
+_START_GOOGLE_NAMESPACE_
+namespace base {
+namespace internal {
+bool GetExitOnDFatal();
+void SetExitOnDFatal(bool value);
+} // namespace internal
+} // namespace base
+_END_GOOGLE_NAMESPACE_
+
static void TestLogging(bool check_counts);
static void TestRawLogging();
static void LogWithLevels(int v, int severity, bool err, bool alsoerr);
static void TestLoggingLevels();
static void TestLogString();
static void TestLogSink();
+static void TestLogToString();
static void TestLogSinkWaitTillSent();
static void TestCHECK();
static void TestDCHECK();
}
BENCHMARK(BM_Check2);
-static void CheckFailure(int a, int b, const char* file, int line, const char* msg) {
+static void CheckFailure(int, int, const char* /* file */, int /* line */,
+ const char* /* msg */) {
+}
+
+static void BM_logspeed(int n) {
+ while (n-- > 0) {
+ LOG(INFO) << "test message";
+ }
+}
+BENCHMARK(BM_logspeed);
+
+static void BM_vlog(int n) {
+ while (n-- > 0) {
+ VLOG(1) << "test message";
+ }
}
+BENCHMARK(BM_vlog);
int main(int argc, char **argv) {
+ FLAGS_colorlogtostderr = false;
+#ifdef HAVE_LIB_GFLAGS
+ ParseCommandLineFlags(&argc, &argv, true);
+#endif
+ // Make sure stderr is not buffered as stderr seems to be buffered
+ // on recent windows.
+ setbuf(stderr, NULL);
+
// Test some basics before InitGoogleLogging:
CaptureTestStderr();
LogWithLevels(FLAGS_v, FLAGS_stderrthreshold,
LogWithLevels(0, 0, 0, 0); // simulate "before global c-tors"
const string early_stderr = GetCapturedTestStderr();
- FLAGS_logtostderr = true;
-
InitGoogleLogging(argv[0]);
RunSpecifiedBenchmarks();
+ FLAGS_logtostderr = true;
+
InitGoogleTest(&argc, argv);
+#ifdef HAVE_LIB_GMOCK
+ InitGoogleMock(&argc, argv);
+#endif
// so that death tests run before we use threads
CHECK_EQ(RUN_ALL_TESTS(), 0);
CaptureTestStderr();
// re-emit early_stderr
- LogMessage("dummy", LogMessage::kNoLogPrefix, INFO).stream() << early_stderr;
+ LogMessage("dummy", LogMessage::kNoLogPrefix, GLOG_INFO).stream() << early_stderr;
TestLogging(true);
TestRawLogging();
TestLoggingLevels();
TestLogString();
TestLogSink();
+ TestLogToString();
TestLogSinkWaitTillSent();
TestCHECK();
TestDCHECK();
TestErrno();
TestTruncate();
- LOG(INFO) << "PASS";
+ ShutdownGoogleLogging();
+
+ fprintf(stdout, "PASS\n");
return 0;
}
void TestLogging(bool check_counts) {
- int64 base_num_infos = LogMessage::num_messages(INFO);
- int64 base_num_warning = LogMessage::num_messages(WARNING);
- int64 base_num_errors = LogMessage::num_messages(ERROR);
+ int64 base_num_infos = LogMessage::num_messages(GLOG_INFO);
+ int64 base_num_warning = LogMessage::num_messages(GLOG_WARNING);
+ int64 base_num_errors = LogMessage::num_messages(GLOG_ERROR);
LOG(INFO) << string("foo ") << "bar " << 10 << ' ' << 3.4;
for ( int i = 0; i < 10; ++i ) {
LOG(ERROR) << string("foo") << ' '<< j << ' ' << setw(10) << j << " "
<< setw(1) << hex << j;
- LogMessage("foo", LogMessage::kNoLogPrefix, INFO).stream() << "no prefix";
+ LogMessage("foo", LogMessage::kNoLogPrefix, GLOG_INFO).stream() << "no prefix";
if (check_counts) {
- CHECK_EQ(base_num_infos + 14, LogMessage::num_messages(INFO));
- CHECK_EQ(base_num_warning + 3, LogMessage::num_messages(WARNING));
- CHECK_EQ(base_num_errors + 15, LogMessage::num_messages(ERROR));
+ CHECK_EQ(base_num_infos + 14, LogMessage::num_messages(GLOG_INFO));
+ CHECK_EQ(base_num_warning + 3, LogMessage::num_messages(GLOG_WARNING));
+ CHECK_EQ(base_num_errors + 15, LogMessage::num_messages(GLOG_ERROR));
}
}
static void NoAllocNewHook() {
- CHECK(false) << "unexpected new";
+ LOG(FATAL) << "unexpected new";
}
struct NewHook {
}
void TestLoggingLevels() {
- LogWithLevels(0, INFO, false, false);
- LogWithLevels(1, INFO, false, false);
- LogWithLevels(-1, INFO, false, false);
- LogWithLevels(0, WARNING, false, false);
- LogWithLevels(0, ERROR, false, false);
- LogWithLevels(0, FATAL, false, false);
- LogWithLevels(0, FATAL, true, false);
- LogWithLevels(0, FATAL, false, true);
- LogWithLevels(1, WARNING, false, false);
- LogWithLevels(1, FATAL, false, true);
+ LogWithLevels(0, GLOG_INFO, false, false);
+ LogWithLevels(1, GLOG_INFO, false, false);
+ LogWithLevels(-1, GLOG_INFO, false, false);
+ LogWithLevels(0, GLOG_WARNING, false, false);
+ LogWithLevels(0, GLOG_ERROR, false, false);
+ LogWithLevels(0, GLOG_FATAL, false, false);
+ LogWithLevels(0, GLOG_FATAL, true, false);
+ LogWithLevels(0, GLOG_FATAL, false, true);
+ LogWithLevels(1, GLOG_WARNING, false, false);
+ LogWithLevels(1, GLOG_FATAL, false, true);
}
TEST(DeathRawCHECK, logging) {
}
}
+void TestLogToString() {
+ string error;
+ string* no_error = NULL;
+
+ LOG_TO_STRING(INFO, &error) << "LOG_TO_STRING: " << "collected info";
+ LOG(INFO) << "Captured by LOG_TO_STRING: " << error;
+ LOG_TO_STRING(WARNING, &error) << "LOG_TO_STRING: " << "collected warning";
+ LOG(INFO) << "Captured by LOG_TO_STRING: " << error;
+ LOG_TO_STRING(ERROR, &error) << "LOG_TO_STRING: " << "collected error";
+ LOG(INFO) << "Captured by LOG_TO_STRING: " << error;
+
+ LOG_TO_STRING(INFO, no_error) << "LOG_TO_STRING: " << "reported info";
+ LOG_TO_STRING(WARNING, no_error) << "LOG_TO_STRING: " << "reported warning";
+ LOG_TO_STRING(ERROR, NULL) << "LOG_TO_STRING: " << "reported error";
+}
+
class TestLogSinkImpl : public LogSink {
public:
vector<string> errors;
- virtual void send(LogSeverity severity, const char* full_filename,
+ virtual void send(LogSeverity severity, const char* /* full_filename */,
const char* base_filename, int line,
const struct tm* tm_time,
const char* message, size_t message_len) {
LOG_TO_SINK(no_sink, WARNING) << "LOG_TO_SINK: " << "reported warning";
LOG_TO_SINK(NULL, ERROR) << "LOG_TO_SINK: " << "reported error";
+ LOG_TO_SINK_BUT_NOT_TO_LOGFILE(&sink, INFO)
+ << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " << "collected info";
+ LOG_TO_SINK_BUT_NOT_TO_LOGFILE(&sink, WARNING)
+ << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " << "collected warning";
+ LOG_TO_SINK_BUT_NOT_TO_LOGFILE(&sink, ERROR)
+ << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " << "collected error";
+
+ LOG_TO_SINK_BUT_NOT_TO_LOGFILE(no_sink, INFO)
+ << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " << "thrashed info";
+ LOG_TO_SINK_BUT_NOT_TO_LOGFILE(no_sink, WARNING)
+ << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " << "thrashed warning";
+ LOG_TO_SINK_BUT_NOT_TO_LOGFILE(NULL, ERROR)
+ << "LOG_TO_SINK_BUT_NOT_TO_LOGFILE: " << "thrashed error";
+
LOG(INFO) << "Captured by LOG_TO_SINK:";
for (size_t i = 0; i < sink.errors.size(); ++i) {
- LogMessage("foo", LogMessage::kNoLogPrefix, INFO).stream()
+ LogMessage("foo", LogMessage::kNoLogPrefix, GLOG_INFO).stream()
<< sink.errors[i];
}
}
DCHECK_LE(1, 2);
DCHECK_GT(2, 1);
DCHECK_LT(1, 2);
+
+ auto_ptr<int64> sptr(new int64);
+ int64* ptr = DCHECK_NOTNULL(sptr.get());
+ CHECK_EQ(ptr, sptr.get());
}
void TestSTREQ() {
void *ptr = static_cast<void *>(&t);
void *ref = CHECK_NOTNULL(ptr);
EXPECT_EQ(ptr, ref);
- CHECK_NOTNULL(reinterpret_cast<char *>(&t));
- CHECK_NOTNULL(reinterpret_cast<unsigned char *>(&t));
- CHECK_NOTNULL(reinterpret_cast<int *>(&t));
- CHECK_NOTNULL(reinterpret_cast<int64 *>(&t));
+ CHECK_NOTNULL(reinterpret_cast<char *>(ptr));
+ CHECK_NOTNULL(reinterpret_cast<unsigned char *>(ptr));
+ CHECK_NOTNULL(reinterpret_cast<int *>(ptr));
+ CHECK_NOTNULL(reinterpret_cast<int64 *>(ptr));
}
TEST(DeathCheckNN, Simple) {
glob_t g;
const int r = glob(pattern.c_str(), 0, NULL, &g);
CHECK((r == 0) || (r == GLOB_NOMATCH)) << ": error matching " << pattern;
- for (int i = 0; i < g.gl_pathc; i++) {
+ for (size_t i = 0; i < g.gl_pathc; i++) {
files->push_back(string(g.gl_pathv[i]));
}
globfree(&g);
LOG(FATAL) << "No directory separator.";
}
const string dirname = pattern.substr(0, index + 1);
- if (FAILED(handle)) {
+ if (handle == INVALID_HANDLE_VALUE) {
// Finding no files is OK.
return;
}
do {
files->push_back(dirname + data.cFileName);
} while (FindNextFileA(handle, &data));
- LOG_SYSRESULT(FindClose(handle));
+ BOOL result = FindClose(handle);
+ LOG_SYSRESULT(result);
#else
# error There is no way to do glob.
#endif
static void CheckFile(const string& name, const string& expected_string) {
vector<string> files;
GetFiles(name + "*", &files);
- CHECK_EQ(files.size(), 1);
+ CHECK_EQ(files.size(), 1UL);
FILE* file = fopen(files[0].c_str(), "r");
CHECK(file != NULL) << ": could not open " << files[0];
const string dest = FLAGS_test_tmpdir + "/logging_test_basename";
DeleteFiles(dest + "*");
- SetLogDestination(INFO, dest.c_str());
+ SetLogDestination(GLOG_INFO, dest.c_str());
LOG(INFO) << "message to new base";
- FlushLogFiles(INFO);
+ FlushLogFiles(GLOG_INFO);
CheckFile(dest, "message to new base");
DeleteFiles(dest + "*");
DeleteFiles(sym + "*");
- SetLogSymlink(INFO, "symlinkbase");
- SetLogDestination(INFO, dest.c_str());
+ SetLogSymlink(GLOG_INFO, "symlinkbase");
+ SetLogDestination(GLOG_INFO, dest.c_str());
LOG(INFO) << "message to new symlink";
- FlushLogFiles(INFO);
+ FlushLogFiles(GLOG_INFO);
CheckFile(sym, "message to new symlink");
DeleteFiles(dest + "*");
string dest = FLAGS_test_tmpdir + "/logging_test_extension";
DeleteFiles(dest + "*");
- SetLogDestination(INFO, dest.c_str());
+ SetLogDestination(GLOG_INFO, dest.c_str());
SetLogFilenameExtension("specialextension");
LOG(INFO) << "message to new extension";
- FlushLogFiles(INFO);
+ FlushLogFiles(GLOG_INFO);
CheckFile(dest, "message to new extension");
// Check that file name ends with extension
vector<string> filenames;
GetFiles(dest + "*", &filenames);
- CHECK_EQ(filenames.size(), 1);
+ CHECK_EQ(filenames.size(), 1UL);
CHECK(strstr(filenames[0].c_str(), "specialextension") != NULL);
// Release file handle for the destination file to unlock the file in Windows.
struct MyLogger : public base::Logger {
string data;
- virtual void Write(bool should_flush,
- time_t timestamp,
+ virtual void Write(bool /* should_flush */,
+ time_t /* timestamp */,
const char* message,
int length) {
data.append(message, length);
fprintf(stderr, "==== Test log wrapper\n");
MyLogger my_logger;
- base::Logger* old_logger = base::GetLogger(INFO);
- base::SetLogger(INFO, &my_logger);
+ base::Logger* old_logger = base::GetLogger(GLOG_INFO);
+ base::SetLogger(GLOG_INFO, &my_logger);
LOG(INFO) << "Send to wrapped logger";
- FlushLogFiles(INFO);
- base::SetLogger(INFO, old_logger);
+ FlushLogFiles(GLOG_INFO);
+ base::SetLogger(GLOG_INFO, old_logger);
CHECK(strstr(my_logger.data.c_str(), "Send to wrapped logger") != NULL);
}
CHECK_ERR(lseek(fd, 0, SEEK_SET));
// File should contain the suffix of the original file
- int buf_size = statbuf.st_size + 1;
+ const size_t buf_size = statbuf.st_size + 1;
char* buf = new char[buf_size];
- memset(buf, 0, sizeof(buf));
+ memset(buf, 0, buf_size);
CHECK_ERR(read(fd, buf, buf_size));
const char *p = buf;
// (re)define LogSink interface
- virtual void send(LogSeverity severity, const char* full_filename,
+ virtual void send(LogSeverity severity, const char* /* full_filename */,
const char* base_filename, int line,
const struct tm* tm_time,
const char* message, size_t message_len) {
for (size_t i = 0; i < global_messages.size(); ++i) {
LOG(INFO) << "Sink capture: " << global_messages[i];
}
- CHECK_EQ(global_messages.size(), 3);
+ CHECK_EQ(global_messages.size(), 3UL);
}
TEST(Strerror, logging) {
int errcode = EINTR;
char *msg = strdup(strerror(errcode));
- int buf_size = strlen(msg) + 1;
+ const size_t buf_size = strlen(msg) + 1;
char *buf = new char[buf_size];
CHECK_EQ(posix_strerror_r(errcode, NULL, 0), -1);
buf[0] = 'A';
CHECK_EQ(posix_strerror_r(errcode, buf, 0), -1);
CHECK_EQ(buf[0], 'A');
CHECK_EQ(posix_strerror_r(errcode, NULL, buf_size), -1);
-#if defined(OS_MACOSX) || defined(OS_FREEBSD)
+#if defined(OS_MACOSX) || defined(OS_FREEBSD) || defined(OS_OPENBSD)
// MacOSX or FreeBSD considers this case is an error since there is
// no enough space.
CHECK_EQ(posix_strerror_r(errcode, buf, 1), -1);
CHECK_STREQ(buf, "");
CHECK_EQ(posix_strerror_r(errcode, buf, buf_size), 0);
CHECK_STREQ(buf, msg);
- free(msg);
delete[] buf;
+ CHECK_EQ(msg, StrError(errcode));
+ free(msg);
}
// Simple routines to look at the sizes of generated code for LOG(FATAL) and
CHECK_EQ(a, b);
}
+#ifdef HAVE_LIB_GMOCK
+
+TEST(DVLog, Basic) {
+ ScopedMockLog log;
+
+#if NDEBUG
+ // We are expecting that nothing is logged.
+ EXPECT_CALL(log, Log(_, _, _)).Times(0);
+#else
+ EXPECT_CALL(log, Log(INFO, __FILE__, "debug log"));
+#endif
+
+ FLAGS_v = 1;
+ DVLOG(1) << "debug log";
+}
+
+TEST(DVLog, V0) {
+ ScopedMockLog log;
+
+ // We are expecting that nothing is logged.
+ EXPECT_CALL(log, Log(_, _, _)).Times(0);
+
+ FLAGS_v = 0;
+ DVLOG(1) << "debug log";
+}
+
+TEST(LogAtLevel, Basic) {
+ ScopedMockLog log;
+
+ // The function version outputs "logging.h" as a file name.
+ EXPECT_CALL(log, Log(WARNING, StrNe(__FILE__), "function version"));
+ EXPECT_CALL(log, Log(INFO, __FILE__, "macro version"));
+
+ int severity = WARNING;
+ LogAtLevel(severity, "function version");
+
+ severity = INFO;
+ // We can use the macro version as a C++ stream.
+ LOG_AT_LEVEL(severity) << "macro" << ' ' << "version";
+}
+
+TEST(TestExitOnDFatal, ToBeOrNotToBe) {
+ // Check the default setting...
+ EXPECT_TRUE(base::internal::GetExitOnDFatal());
+
+ // Turn off...
+ base::internal::SetExitOnDFatal(false);
+ EXPECT_FALSE(base::internal::GetExitOnDFatal());
+
+ // We don't die.
+ {
+ ScopedMockLog log;
+ //EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
+ // LOG(DFATAL) has severity FATAL if debugging, but is
+ // downgraded to ERROR if not debugging.
+ const LogSeverity severity =
+#ifdef NDEBUG
+ ERROR;
+#else
+ FATAL;
+#endif
+ EXPECT_CALL(log, Log(severity, __FILE__, "This should not be fatal"));
+ LOG(DFATAL) << "This should not be fatal";
+ }
+
+ // Turn back on...
+ base::internal::SetExitOnDFatal(true);
+ EXPECT_TRUE(base::internal::GetExitOnDFatal());
+
+#ifdef GTEST_HAS_DEATH_TEST
+ // Death comes on little cats' feet.
+ EXPECT_DEBUG_DEATH({
+ LOG(DFATAL) << "This should be fatal in debug mode";
+ }, "This should be fatal in debug mode");
+#endif
+}
+
+#ifdef HAVE_STACKTRACE
+
+static void BacktraceAtHelper() {
+ LOG(INFO) << "Not me";
+
+// The vertical spacing of the next 3 lines is significant.
+ LOG(INFO) << "Backtrace me";
+}
+static int kBacktraceAtLine = __LINE__ - 2; // The line of the LOG(INFO) above
+
+TEST(LogBacktraceAt, DoesNotBacktraceWhenDisabled) {
+ StrictMock<ScopedMockLog> log;
+
+ FLAGS_log_backtrace_at = "";
+
+ EXPECT_CALL(log, Log(_, _, "Backtrace me"));
+ EXPECT_CALL(log, Log(_, _, "Not me"));
+
+ BacktraceAtHelper();
+}
+
+TEST(LogBacktraceAt, DoesBacktraceAtRightLineWhenEnabled) {
+ StrictMock<ScopedMockLog> log;
+
+ char where[100];
+ snprintf(where, 100, "%s:%d", const_basename(__FILE__), kBacktraceAtLine);
+ FLAGS_log_backtrace_at = where;
+
+ // The LOG at the specified line should include a stacktrace which includes
+ // the name of the containing function, followed by the log message.
+ // We use HasSubstr()s instead of ContainsRegex() for environments
+ // which don't have regexp.
+ EXPECT_CALL(log, Log(_, _, AllOf(HasSubstr("stacktrace:"),
+ HasSubstr("BacktraceAtHelper"),
+ HasSubstr("main"),
+ HasSubstr("Backtrace me"))));
+ // Other LOGs should not include a backtrace.
+ EXPECT_CALL(log, Log(_, _, "Not me"));
+
+ BacktraceAtHelper();
+}
+
+#endif // HAVE_STACKTRACE
+
+#endif // HAVE_LIB_GMOCK
+
struct UserDefinedClass {
- bool operator==(const UserDefinedClass& rhs) const { return true; }
+ bool operator==(const UserDefinedClass&) const { return true; }
};
-inline ostream& operator<<(ostream& out, const UserDefinedClass& u) {
+inline ostream& operator<<(ostream& out, const UserDefinedClass&) {
out << "OK";
return out;
}
UserDefinedClass u;
vector<string> buf;
LOG_STRING(INFO, &buf) << u;
- CHECK_EQ(1, buf.size());
+ CHECK_EQ(1UL, buf.size());
CHECK(buf[0].find("OK") != string::npos);
// We must be able to compile this.