blob: ddd6ef09542b42e17aa8384f7c44c22560966c7c [file] [log] [blame]
//
// Copyright 2002 Google, Inc.
// All Rights Reserved.
//
// Author: Ray Sidney
#include "utilities.h"
#include <fcntl.h>
#include <glob.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iomanip>
#include <iostream>
#include <queue>
#include <sstream>
#include <string>
#include <vector>
#include "base/commandlineflags.h"
#include "glog/logging.h"
#include "glog/raw_logging.h"
#include "googletest.h"
using namespace std;
using namespace 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 TestLogSinkWaitTillSent();
static void TestCHECK();
static void TestDCHECK();
static void TestSTREQ();
static void TestBasename();
static void TestSymlink();
static void TestExtension();
static void TestWrapper();
static void TestErrno();
static void TestTruncate();
static int x = -1;
static void BM_Check1(int n) {
while (n-- > 0) {
CHECK_GE(n, x);
CHECK_GE(n, x);
CHECK_GE(n, x);
CHECK_GE(n, x);
CHECK_GE(n, x);
CHECK_GE(n, x);
CHECK_GE(n, x);
CHECK_GE(n, x);
}
}
BENCHMARK(BM_Check1);
static void CheckFailure(int a, int b, const char* file, int line, const char* msg);
static void BM_Check3(int n) {
while (n-- > 0) {
if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
if (n < x) CheckFailure(n, x, __FILE__, __LINE__, "n < x");
}
}
BENCHMARK(BM_Check3);
static void BM_Check2(int n) {
if (n == 17) {
x = 5;
}
while (n-- > 0) {
CHECK(n >= x);
CHECK(n >= x);
CHECK(n >= x);
CHECK(n >= x);
CHECK(n >= x);
CHECK(n >= x);
CHECK(n >= x);
CHECK(n >= x);
}
}
BENCHMARK(BM_Check2);
static void CheckFailure(int a, int b, const char* file, int line, const char* msg) {
}
int main(int argc, char **argv) {
// Test some basics before InitGoogleLogging:
CaptureTestStderr();
LogWithLevels(FLAGS_v, FLAGS_stderrthreshold,
FLAGS_logtostderr, FLAGS_alsologtostderr);
LogWithLevels(0, 0, 0, 0); // simulate "before global c-tors"
const string early_stderr = GetCapturedTestStderr();
FLAGS_logtostderr = true;
InitGoogleLogging(argv[0]);
RunSpecifiedBenchmarks();
// 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;
TestLogging(true);
TestRawLogging();
TestLoggingLevels();
TestLogString();
TestLogSink();
TestLogSinkWaitTillSent();
TestCHECK();
TestDCHECK();
TestSTREQ();
// TODO: The golden test portion of this test is very flakey.
EXPECT_TRUE(
MungeAndDiffTestStderr(FLAGS_test_srcdir + "/src/logging_unittest.err"));
FLAGS_logtostderr = false;
TestBasename();
TestSymlink();
TestExtension();
TestWrapper();
TestErrno();
TestTruncate();
LOG(INFO) << "PASS";
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);
LOG(INFO) << string("foo ") << "bar " << 10 << ' ' << 3.4;
for ( int i = 0; i < 10; ++i ) {
int old_errno = errno;
errno = i;
PLOG_EVERY_N(ERROR, 2) << "Plog every 2, iteration " << COUNTER;
errno = old_errno;
LOG_EVERY_N(ERROR, 3) << "Log every 3, iteration " << COUNTER << endl;
LOG_EVERY_N(ERROR, 4) << "Log every 4, iteration " << COUNTER << endl;
LOG_IF_EVERY_N(WARNING, true, 5) << "Log if every 5, iteration " << COUNTER;
LOG_IF_EVERY_N(WARNING, false, 3)
<< "Log if every 3, iteration " << COUNTER;
LOG_IF_EVERY_N(INFO, true, 1) << "Log if every 1, iteration " << COUNTER;
LOG_IF_EVERY_N(ERROR, (i < 3), 2)
<< "Log if less than 3 every 2, iteration " << COUNTER;
}
LOG_IF(WARNING, true) << "log_if this";
LOG_IF(WARNING, false) << "don't log_if this";
char s[] = "array";
LOG(INFO) << s;
const char const_s[] = "const array";
LOG(INFO) << const_s;
int j = 1000;
LOG(ERROR) << string("foo") << ' '<< j << ' ' << setw(10) << j << " "
<< setw(1) << hex << j;
LogMessage("foo", LogMessage::kNoLogPrefix, 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));
}
}
static void NoAllocNewHook() {
CHECK(false) << "unexpected new";
}
struct NewHook {
NewHook() {
g_new_hook = &NoAllocNewHook;
}
~NewHook() {
g_new_hook = NULL;
}
};
TEST(DeathNoAllocNewHook, logging) {
// tests that NewHook used below works
NewHook new_hook;
ASSERT_DEATH({
new int;
}, "unexpected new");
}
void TestRawLogging() {
string* foo = new string("foo ");
string huge_str(50000, 'a');
FlagSaver saver;
// Check that RAW loggging does not use mallocs.
NewHook new_hook;
RAW_LOG(INFO, "%s%s%d%c%f", foo->c_str(), "bar ", 10, ' ', 3.4);
char s[] = "array";
RAW_LOG(WARNING, "%s", s);
const char const_s[] = "const array";
RAW_LOG(INFO, "%s", const_s);
void* p = reinterpret_cast<void*>(0x12345678);
RAW_LOG(INFO, "ptr %p", p);
p = NULL;
RAW_LOG(INFO, "ptr %p", p);
int j = 1000;
RAW_LOG(ERROR, "%s%d%c%010d%s%1x", foo->c_str(), j, ' ', j, " ", j);
RAW_VLOG(0, "foo %d", j);
#ifdef NDEBUG
RAW_LOG(INFO, "foo %d", j); // so that have same stderr to compare
#else
RAW_DLOG(INFO, "foo %d", j); // test RAW_DLOG in debug mode
#endif
// test how long messages are chopped:
RAW_LOG(WARNING, "Huge string: %s", huge_str.c_str());
RAW_VLOG(0, "Huge string: %s", huge_str.c_str());
FLAGS_v = 0;
RAW_LOG(INFO, "log");
RAW_VLOG(0, "vlog 0 on");
RAW_VLOG(1, "vlog 1 off");
RAW_VLOG(2, "vlog 2 off");
RAW_VLOG(3, "vlog 3 off");
FLAGS_v = 2;
RAW_LOG(INFO, "log");
RAW_VLOG(1, "vlog 1 on");
RAW_VLOG(2, "vlog 2 on");
RAW_VLOG(3, "vlog 3 off");
#ifdef NDEBUG
RAW_DCHECK(1 == 2, " RAW_DCHECK's shouldn't be compiled in normal mode");
#endif
RAW_CHECK(1 == 1, "should be ok");
RAW_DCHECK(true, "should be ok");
delete foo;
}
void LogWithLevels(int v, int severity, bool err, bool alsoerr) {
RAW_LOG(INFO,
"Test: v=%d stderrthreshold=%d logtostderr=%d alsologtostderr=%d",
v, severity, err, alsoerr);
FlagSaver saver;
FLAGS_v = v;
FLAGS_stderrthreshold = severity;
FLAGS_logtostderr = err;
FLAGS_alsologtostderr = alsoerr;
RAW_VLOG(-1, "vlog -1");
RAW_VLOG(0, "vlog 0");
RAW_VLOG(1, "vlog 1");
RAW_LOG(INFO, "log info");
RAW_LOG(WARNING, "log warning");
RAW_LOG(ERROR, "log error");
VLOG(-1) << "vlog -1";
VLOG(0) << "vlog 0";
VLOG(1) << "vlog 1";
LOG(INFO) << "log info";
LOG(WARNING) << "log warning";
LOG(ERROR) << "log error";
VLOG_IF(-1, true) << "vlog_if -1";
VLOG_IF(-1, false) << "don't vlog_if -1";
VLOG_IF(0, true) << "vlog_if 0";
VLOG_IF(0, false) << "don't vlog_if 0";
VLOG_IF(1, true) << "vlog_if 1";
VLOG_IF(1, false) << "don't vlog_if 1";
LOG_IF(INFO, true) << "log_if info";
LOG_IF(INFO, false) << "don't log_if info";
LOG_IF(WARNING, true) << "log_if warning";
LOG_IF(WARNING, false) << "don't log_if warning";
LOG_IF(ERROR, true) << "log_if error";
LOG_IF(ERROR, false) << "don't log_if error";
int c;
c = 1; VLOG_IF(100, c -= 2) << "vlog_if 100 expr"; EXPECT_EQ(c, -1);
c = 1; VLOG_IF(0, c -= 2) << "vlog_if 0 expr"; EXPECT_EQ(c, -1);
c = 1; LOG_IF(INFO, c -= 2) << "log_if info expr"; EXPECT_EQ(c, -1);
c = 1; LOG_IF(ERROR, c -= 2) << "log_if error expr"; EXPECT_EQ(c, -1);
c = 2; VLOG_IF(0, c -= 2) << "don't vlog_if 0 expr"; EXPECT_EQ(c, 0);
c = 2; LOG_IF(ERROR, c -= 2) << "don't log_if error expr"; EXPECT_EQ(c, 0);
c = 3; LOG_IF_EVERY_N(INFO, c -= 4, 1) << "log_if info every 1 expr";
EXPECT_EQ(c, -1);
c = 3; LOG_IF_EVERY_N(ERROR, c -= 4, 1) << "log_if error every 1 expr";
EXPECT_EQ(c, -1);
c = 4; LOG_IF_EVERY_N(ERROR, c -= 4, 3) << "don't log_if info every 3 expr";
EXPECT_EQ(c, 0);
c = 4; LOG_IF_EVERY_N(ERROR, c -= 4, 3) << "don't log_if error every 3 expr";
EXPECT_EQ(c, 0);
c = 5; VLOG_IF_EVERY_N(0, c -= 4, 1) << "vlog_if 0 every 1 expr";
EXPECT_EQ(c, 1);
c = 5; VLOG_IF_EVERY_N(100, c -= 4, 3) << "vlog_if 100 every 3 expr";
EXPECT_EQ(c, 1);
c = 6; VLOG_IF_EVERY_N(0, c -= 6, 1) << "don't vlog_if 0 every 1 expr";
EXPECT_EQ(c, 0);
c = 6; VLOG_IF_EVERY_N(100, c -= 6, 3) << "don't vlog_if 100 every 1 expr";
EXPECT_EQ(c, 0);
}
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);
}
TEST(DeathRawCHECK, logging) {
ASSERT_DEATH(RAW_CHECK(false, "failure 1"),
"RAW: Check false failed: failure 1");
ASSERT_DEBUG_DEATH(RAW_DCHECK(1 == 2, "failure 2"),
"RAW: Check 1 == 2 failed: failure 2");
}
void TestLogString() {
vector<string> errors;
vector<string> *no_errors = NULL;
LOG_STRING(INFO, &errors) << "LOG_STRING: " << "collected info";
LOG_STRING(WARNING, &errors) << "LOG_STRING: " << "collected warning";
LOG_STRING(ERROR, &errors) << "LOG_STRING: " << "collected error";
LOG_STRING(INFO, no_errors) << "LOG_STRING: " << "reported info";
LOG_STRING(WARNING, no_errors) << "LOG_STRING: " << "reported warning";
LOG_STRING(ERROR, NULL) << "LOG_STRING: " << "reported error";
for (int i = 0; i < errors.size(); ++i) {
LOG(INFO) << "Captured by LOG_STRING: " << errors[i];
}
}
class TestLogSinkImpl : public LogSink {
public:
vector<string> errors;
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) {
errors.push_back(
ToString(severity, base_filename, line, tm_time, message, message_len));
}
};
void TestLogSink() {
TestLogSinkImpl sink;
LogSink *no_sink = NULL;
LOG_TO_SINK(&sink, INFO) << "LOG_TO_SINK: " << "collected info";
LOG_TO_SINK(&sink, WARNING) << "LOG_TO_SINK: " << "collected warning";
LOG_TO_SINK(&sink, ERROR) << "LOG_TO_SINK: " << "collected error";
LOG_TO_SINK(no_sink, INFO) << "LOG_TO_SINK: " << "reported info";
LOG_TO_SINK(no_sink, WARNING) << "LOG_TO_SINK: " << "reported warning";
LOG_TO_SINK(NULL, ERROR) << "LOG_TO_SINK: " << "reported error";
LOG(INFO) << "Captured by LOG_TO_SINK:";
for (int i = 0; i < sink.errors.size(); ++i) {
LogMessage("foo", LogMessage::kNoLogPrefix, INFO).stream()
<< sink.errors[i];
}
}
// For testing using CHECK*() on anonymous enums.
enum {
CASE_A,
CASE_B
};
void TestCHECK() {
// Tests using CHECK*() on int values.
CHECK(1 == 1);
CHECK_EQ(1, 1);
CHECK_NE(1, 2);
CHECK_GE(1, 1);
CHECK_GE(2, 1);
CHECK_LE(1, 1);
CHECK_LE(1, 2);
CHECK_GT(2, 1);
CHECK_LT(1, 2);
// Tests using CHECK*() on anonymous enums.
// Apple's GCC doesn't like this.
#if !defined(OS_MACOSX)
CHECK_EQ(CASE_A, CASE_A);
CHECK_NE(CASE_A, CASE_B);
CHECK_GE(CASE_A, CASE_A);
CHECK_GE(CASE_B, CASE_A);
CHECK_LE(CASE_A, CASE_A);
CHECK_LE(CASE_A, CASE_B);
CHECK_GT(CASE_B, CASE_A);
CHECK_LT(CASE_A, CASE_B);
#endif
}
void TestDCHECK() {
#ifdef NDEBUG
DCHECK( 1 == 2 ) << " DCHECK's shouldn't be compiled in normal mode";
#endif
DCHECK( 1 == 1 );
DCHECK_EQ(1, 1);
DCHECK_NE(1, 2);
DCHECK_GE(1, 1);
DCHECK_GE(2, 1);
DCHECK_LE(1, 1);
DCHECK_LE(1, 2);
DCHECK_GT(2, 1);
DCHECK_LT(1, 2);
}
void TestSTREQ() {
CHECK_STREQ("this", "this");
CHECK_STREQ(NULL, NULL);
CHECK_STRCASEEQ("this", "tHiS");
CHECK_STRCASEEQ(NULL, NULL);
CHECK_STRNE("this", "tHiS");
CHECK_STRNE("this", NULL);
CHECK_STRCASENE("this", "that");
CHECK_STRCASENE(NULL, "that");
CHECK_STREQ((string("a")+"b").c_str(), "ab");
CHECK_STREQ(string("test").c_str(),
(string("te") + string("st")).c_str());
}
TEST(DeathSTREQ, logging) {
ASSERT_DEATH(CHECK_STREQ(NULL, "this"), "");
ASSERT_DEATH(CHECK_STREQ("this", "siht"), "");
ASSERT_DEATH(CHECK_STRCASEEQ(NULL, "siht"), "");
ASSERT_DEATH(CHECK_STRCASEEQ("this", "siht"), "");
ASSERT_DEATH(CHECK_STRNE(NULL, NULL), "");
ASSERT_DEATH(CHECK_STRNE("this", "this"), "");
ASSERT_DEATH(CHECK_STREQ((string("a")+"b").c_str(), "abc"), "");
}
TEST(CheckNOTNULL, Simple) {
int64 t;
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));
}
TEST(DeathCheckNN, Simple) {
ASSERT_DEATH(CHECK_NOTNULL(static_cast<void *>(NULL)), "");
}
// Get list of file names that match pattern
static void GetFiles(const string& pattern, vector<string>* files) {
files->clear();
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++) {
files->push_back(string(g.gl_pathv[i]));
}
globfree(&g);
}
// Delete files patching pattern
static void DeleteFiles(const string& pattern) {
vector<string> files;
GetFiles(pattern, &files);
for (int i = 0; i < files.size(); i++) {
CHECK(unlink(files[i].c_str()) == 0) << ": " << strerror(errno);
}
}
static void CheckFile(const string& name, const string& expected_string) {
vector<string> files;
GetFiles(name + "*", &files);
CHECK_EQ(files.size(), 1);
FILE* file = fopen(files[0].c_str(), "r");
CHECK(file != NULL) << ": could not open " << files[0];
char buf[1000];
while (fgets(buf, sizeof(buf), file) != NULL) {
if (strstr(buf, expected_string.c_str()) != NULL) {
fclose(file);
return;
}
}
fclose(file);
LOG(FATAL) << "Did not find " << expected_string << " in " << files[0];
}
static void TestBasename() {
fprintf(stderr, "==== Test setting log file basename\n");
string dest = FLAGS_test_tmpdir + "/logging_test_basename";
DeleteFiles(dest + "*");
SetLogDestination(INFO, dest.c_str());
LOG(INFO) << "message to new base";
FlushLogFiles(INFO);
CheckFile(dest, "message to new base");
DeleteFiles(dest + "*");
}
static void TestSymlink() {
fprintf(stderr, "==== Test setting log file symlink\n");
string dest = FLAGS_test_tmpdir + "/logging_test_symlink";
string sym = FLAGS_test_tmpdir + "/symlinkbase";
DeleteFiles(dest + "*");
DeleteFiles(sym + "*");
SetLogSymlink(INFO, "symlinkbase");
SetLogDestination(INFO, dest.c_str());
LOG(INFO) << "message to new symlink";
FlushLogFiles(INFO);
CheckFile(sym, "message to new symlink");
DeleteFiles(dest + "*");
DeleteFiles(sym + "*");
}
static void TestExtension() {
fprintf(stderr, "==== Test setting log file extension\n");
string dest = FLAGS_test_tmpdir + "/logging_test_extension";
DeleteFiles(dest + "*");
SetLogDestination(INFO, dest.c_str());
SetLogFilenameExtension("specialextension");
LOG(INFO) << "message to new extension";
FlushLogFiles(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(strstr(filenames[0].c_str(), "specialextension") != NULL);
DeleteFiles(dest + "*");
}
struct MyLogger : public base::Logger {
string data;
virtual void Write(bool should_flush,
time_t timestamp,
const char* message,
int length) {
data.append(message, length);
}
virtual void Flush() { }
virtual uint32 LogSize() { return data.length(); }
};
static void TestWrapper() {
fprintf(stderr, "==== Test log wrapper\n");
MyLogger my_logger;
base::Logger* old_logger = base::GetLogger(INFO);
base::SetLogger(INFO, &my_logger);
LOG(INFO) << "Send to wrapped logger";
FlushLogFiles(INFO);
base::SetLogger(INFO, old_logger);
CHECK(strstr(my_logger.data.c_str(), "Send to wrapped logger") != NULL);
}
static void TestErrno() {
fprintf(stderr, "==== Test errno preservation\n");
errno = ENOENT;
TestLogging(false);
CHECK_EQ(errno, ENOENT);
}
static void TestOneTruncate(const char *path, int64 limit, int64 keep,
int64 dsize, int64 ksize, int64 expect) {
int fd;
CHECK_ERR(fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0600));
const char *discardstr = "DISCARDME!", *keepstr = "KEEPME!";
// Fill the file with the requested data; first discard data, then kept data
int64 written = 0;
while (written < dsize) {
int bytes = min<int64>(dsize - written, strlen(discardstr));
CHECK_ERR(write(fd, discardstr, bytes));
written += bytes;
}
written = 0;
while (written < ksize) {
int bytes = min<int64>(ksize - written, strlen(keepstr));
CHECK_ERR(write(fd, keepstr, bytes));
written += bytes;
}
TruncateLogFile(path, limit, keep);
// File should now be shorter
struct stat statbuf;
CHECK_ERR(fstat(fd, &statbuf));
CHECK_EQ(statbuf.st_size, expect);
CHECK_ERR(lseek(fd, 0, SEEK_SET));
// File should contain the suffix of the original file
char buf[statbuf.st_size + 1];
memset(buf, 0, sizeof(buf));
CHECK_ERR(read(fd, buf, sizeof(buf)));
const char *p = buf;
int64 checked = 0;
while (checked < expect) {
int bytes = min<int64>(expect - checked, strlen(keepstr));
CHECK(!memcmp(p, keepstr, bytes));
checked += bytes;
}
close(fd);
}
static void TestTruncate() {
fprintf(stderr, "==== Test log truncation\n");
string path = FLAGS_test_tmpdir + "/truncatefile";
// Test on a small file
TestOneTruncate(path.c_str(), 10, 10, 10, 10, 10);
// And a big file (multiple blocks to copy)
TestOneTruncate(path.c_str(), 2<<20, 4<<10, 3<<20, 4<<10, 4<<10);
// Check edge-case limits
TestOneTruncate(path.c_str(), 10, 20, 0, 20, 20);
TestOneTruncate(path.c_str(), 10, 0, 0, 0, 0);
TestOneTruncate(path.c_str(), 10, 50, 0, 10, 10);
TestOneTruncate(path.c_str(), 50, 100, 0, 30, 30);
// MacOSX 10.4 doesn't fail in this case. Let's just ignore this test.
#if !defined(OS_MACOSX)
// Through a symlink should fail to truncate
string linkname = path + ".link";
unlink(linkname.c_str());
CHECK_ERR(symlink(path.c_str(), linkname.c_str()));
TestOneTruncate(linkname.c_str(), 10, 10, 0, 30, 30);
#endif
// The /proc/self path makes sense only for linux.
#if defined(OS_LINUX)
// Through an open fd symlink should work
int fd;
CHECK_ERR(fd = open(path.c_str(), O_APPEND | O_WRONLY));
char fdpath[64];
snprintf(fdpath, sizeof(fdpath), "/proc/self/fd/%d", fd);
TestOneTruncate(fdpath, 10, 10, 10, 10, 10);
#endif
}
_START_GOOGLE_NAMESPACE_
extern // in logging.cc
bool SafeFNMatch_(const char* pattern, size_t patt_len,
const char* str, size_t str_len);
_END_GOOGLE_NAMESPACE_
static bool WrapSafeFNMatch(string pattern, string str) {
pattern += "abc";
str += "defgh";
return SafeFNMatch_(pattern.data(), pattern.size() - 3,
str.data(), str.size() - 5);
}
TEST(SafeFNMatch, logging) {
CHECK(WrapSafeFNMatch("foo", "foo"));
CHECK(!WrapSafeFNMatch("foo", "bar"));
CHECK(!WrapSafeFNMatch("foo", "fo"));
CHECK(!WrapSafeFNMatch("foo", "foo2"));
CHECK(WrapSafeFNMatch("bar/foo.ext", "bar/foo.ext"));
CHECK(WrapSafeFNMatch("*ba*r/fo*o.ext*", "bar/foo.ext"));
CHECK(!WrapSafeFNMatch("bar/foo.ext", "bar/baz.ext"));
CHECK(!WrapSafeFNMatch("bar/foo.ext", "bar/foo"));
CHECK(!WrapSafeFNMatch("bar/foo.ext", "bar/foo.ext.zip"));
CHECK(WrapSafeFNMatch("ba?/*.ext", "bar/foo.ext"));
CHECK(WrapSafeFNMatch("ba?/*.ext", "baZ/FOO.ext"));
CHECK(!WrapSafeFNMatch("ba?/*.ext", "barr/foo.ext"));
CHECK(!WrapSafeFNMatch("ba?/*.ext", "bar/foo.ext2"));
CHECK(WrapSafeFNMatch("ba?/*", "bar/foo.ext2"));
CHECK(WrapSafeFNMatch("ba?/*", "bar/"));
CHECK(!WrapSafeFNMatch("ba?/?", "bar/"));
CHECK(!WrapSafeFNMatch("ba?/*", "bar"));
}
// TestWaitingLogSink will save messages here
// No lock: Accessed only by TestLogSinkWriter thread
// and after its demise by its creator.
static vector<string> global_messages;
// helper for TestWaitingLogSink below.
// Thread that does the logic of TestWaitingLogSink
// It's free to use LOG() itself.
class TestLogSinkWriter : public Thread {
public:
TestLogSinkWriter() : should_exit_(false) {
SetJoinable(true);
Start();
}
// Just buffer it (can't use LOG() here).
void Buffer(const string& message) {
mutex_.Lock();
RAW_LOG(INFO, "Buffering");
messages_.push(message);
mutex_.Unlock();
RAW_LOG(INFO, "Buffered");
}
// Wait for the buffer to clear (can't use LOG() here).
void Wait() {
RAW_LOG(INFO, "Waiting");
mutex_.Lock();
while (!NoWork()) {
mutex_.Unlock();
SleepForMilliseconds(1);
mutex_.Lock();
}
RAW_LOG(INFO, "Waited");
mutex_.Unlock();
}
// Trigger thread exit.
void Stop() {
MutexLock l(&mutex_);
should_exit_ = true;
}
private:
// helpers ---------------
// For creating a "Condition".
bool NoWork() { return messages_.empty(); }
bool HaveWork() { return !messages_.empty() || should_exit_; }
// Thread body; CAN use LOG() here!
virtual void Run() {
while (1) {
mutex_.Lock();
while (!HaveWork()) {
mutex_.Unlock();
SleepForMilliseconds(1);
mutex_.Lock();
}
if (should_exit_ && messages_.empty()) {
mutex_.Unlock();
break;
}
// Give the main thread time to log its message,
// so that we get a reliable log capture to compare to golden file.
// Same for the other sleep below.
SleepForMilliseconds(20);
RAW_LOG(INFO, "Sink got a messages"); // only RAW_LOG under mutex_ here
string message = messages_.front();
messages_.pop();
// Normally this would be some more real/involved logging logic
// where LOG() usage can't be eliminated,
// e.g. pushing the message over with an RPC:
int messages_left = messages_.size();
mutex_.Unlock();
SleepForMilliseconds(20);
// May not use LOG while holding mutex_, because Buffer()
// acquires mutex_, and Buffer is called from LOG(),
// which has its own internal mutex:
// LOG()->LogToSinks()->TestWaitingLogSink::send()->Buffer()
LOG(INFO) << "Sink is sending out a message: " << message;
LOG(INFO) << "Have " << messages_left << " left";
global_messages.push_back(message);
}
}
// data ---------------
Mutex mutex_;
bool should_exit_;
queue<string> messages_; // messages to be logged
};
// A log sink that exercises WaitTillSent:
// it pushes data to a buffer and wakes up another thread to do the logging
// (that other thread can than use LOG() itself),
class TestWaitingLogSink : public LogSink {
public:
TestWaitingLogSink() {
tid_ = pthread_self(); // for thread-specific behavior
AddLogSink(this);
}
~TestWaitingLogSink() {
RemoveLogSink(this);
writer_.Stop();
writer_.Join();
}
// (re)define LogSink interface
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) {
// Push it to Writer thread if we are the original logging thread.
// Note: Something like ThreadLocalLogSink is a better choice
// to do thread-specific LogSink logic for real.
if (pthread_equal(tid_, pthread_self())) {
writer_.Buffer(ToString(severity, base_filename, line,
tm_time, message, message_len));
}
}
virtual void WaitTillSent() {
// Wait for Writer thread if we are the original logging thread.
if (pthread_equal(tid_, pthread_self())) writer_.Wait();
}
private:
pthread_t tid_;
TestLogSinkWriter writer_;
};
// Check that LogSink::WaitTillSent can be used in the advertised way.
// We also do golden-stderr comparison.
static void TestLogSinkWaitTillSent() {
{ TestWaitingLogSink sink;
// Sleeps give the sink threads time to do all their work,
// so that we get a reliable log capture to compare to the golden file.
LOG(INFO) << "Message 1";
SleepForMilliseconds(60);
LOG(ERROR) << "Message 2";
SleepForMilliseconds(60);
LOG(WARNING) << "Message 3";
SleepForMilliseconds(60);
}
for (int i = 0; i < global_messages.size(); ++i) {
LOG(INFO) << "Sink capture: " << global_messages[i];
}
CHECK_EQ(global_messages.size(), 3);
}
TEST(Strerror, logging) {
int errcode = EINTR;
char *msg = strdup(strerror(errcode));
char buf[strlen(msg) + 1];
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, sizeof(buf)), -1);
#if defined(OS_MACOSX) || defined(OS_FREEBSD)
// MacOSX or FreeBSD considers this case is an error since there is
// no enough space.
CHECK_EQ(posix_strerror_r(errcode, buf, 1), -1);
#else
CHECK_EQ(posix_strerror_r(errcode, buf, 1), 0);
#endif
CHECK_STREQ(buf, "");
CHECK_EQ(posix_strerror_r(errcode, buf, sizeof(buf)), 0);
CHECK_STREQ(buf, msg);
free(msg);
}
// Simple routines to look at the sizes of generated code for LOG(FATAL) and
// CHECK(..) via objdump
void MyFatal() {
LOG(FATAL) << "Failed";
}
void MyCheck(bool a, bool b) {
CHECK_EQ(a, b);
}
struct UserDefinedClass {
bool operator==(const UserDefinedClass& rhs) const { return true; }
};
inline ostream& operator<<(ostream& out, const UserDefinedClass& u) {
out << "OK";
return out;
}
TEST(UserDefinedClass, logging) {
UserDefinedClass u;
vector<string> buf;
LOG_STRING(INFO, &buf) << u;
CHECK_EQ(1, buf.size());
CHECK(buf[0].find("OK") != string::npos);
// We must be able to compile this.
CHECK_EQ(u, u);
}