blob: fd25efb93f37befe4a97c832453c9bf8a4cd63f7 [file] [log] [blame]
// Copyright 2014 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "android/base/Log.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <gtest/gtest.h>
namespace android {
namespace base {
// Create a severity level which is guaranteed to never generate a log
// message. See LogOnlyEvaluatesArgumentsIfNeeded for usage.
const LogSeverity LOG_INVISIBLE = -10000;
class LogTest : public ::testing::Test, android::base::testing::LogOutput {
public:
LogTest() : mFatal(false) {
mSavedOutput = ::android::base::testing::LogOutput::setNewOutput(this);
mExpected[0] = '\0';
mBuffer[0] = '\x7f';
mBuffer[1] = '\0';
}
~LogTest() {
::android::base::testing::LogOutput::setNewOutput(mSavedOutput);
}
void setExpected(LogSeverity severity, int line, const char* suffix) {
mExpectedParams.file = __FILE__;
mExpectedParams.lineno = line;
mExpectedParams.severity = severity;
snprintf(mExpected, sizeof(mExpected), "%s", suffix);
}
// LogOutput override
void logMessage(const LogParams& params,
const char* message,
size_t messageLen) {
mParams = params;
if (messageLen > sizeof(mBuffer) - 1)
messageLen = sizeof(mBuffer) - 1;
::memcpy(mBuffer, message, messageLen);
mBuffer[messageLen] = '\0';
mFatal = (params.severity >= LOG_FATAL);
}
protected:
::android::base::testing::LogOutput* mSavedOutput;
LogParams mParams;
LogParams mExpectedParams;
char mExpected[1024];
char mBuffer[1024];
bool mFatal;
};
class CheckTest : public LogTest {
};
#if ENABLE_DCHECK != 0
class DCheckEnabledTest : public LogTest {
public:
DCheckEnabledTest() : LogTest() {
// Ensure DCHECKS() always run.
mSavedLevel = setDcheckLevel(true);
}
~DCheckEnabledTest() {
setDcheckLevel(mSavedLevel);
}
private:
bool mSavedLevel;
};
#endif // ENABLE_DCHECK == 0
#if ENABLE_DCHECK != 2
class DCheckDisabledTest : public LogTest {
public:
DCheckDisabledTest() : LogTest() {
mSavedLevel = setDcheckLevel(false);
}
~DCheckDisabledTest() {
setDcheckLevel(mSavedLevel);
}
private:
bool mSavedLevel;
};
#endif // ENABLE_DCHECK != 2
class PLogTest : public LogTest {
public:
PLogTest() : LogTest(), mForcedErrno(-1000) {}
void setForcedErrno(int errnoCode) {
mForcedErrno = errnoCode;
}
void setExpectedErrno(LogSeverity severity,
int line,
int errnoCode,
const char* suffix) {
mExpectedParams.file = __FILE__;
mExpectedParams.lineno = line;
mExpectedParams.severity = severity;
snprintf(mExpected,
sizeof(mExpected),
"%sError message: %s",
suffix,
strerror(errnoCode));
}
void logMessage(const LogParams& params,
const char* message,
size_t messageLen) {
LogTest::logMessage(params, message, messageLen);
if (mForcedErrno != -1000)
errno = mForcedErrno;
}
protected:
int mForcedErrno;
};
#define STRINGIFY(x) STRINGIFY_(x)
#define STRINGIFY_(x) #x
#define EXPECTED_STRING_PREFIX(prefix, line) \
prefix ":" __FILE__ ":" STRINGIFY(line) ": "
#define CHECK_EXPECTATIONS() \
EXPECT_STREQ(mExpectedParams.file, mParams.file); \
EXPECT_EQ(mExpectedParams.lineno, mParams.lineno); \
EXPECT_EQ(mExpectedParams.severity, mParams.severity); \
EXPECT_STREQ(mExpected, mBuffer)
// Helper function used to set a boolean |flag|, then return |string|.
static const char* setFlag(bool* flag, const char* string) {
*flag = true;
return string;
}
TEST(LogString, EmptyString) {
LogString ls("");
EXPECT_STREQ("", ls.string());
}
TEST(LogString, SimpleString) {
LogString ls("Hello");
EXPECT_STREQ("Hello", ls.string());
}
TEST(LogString, FormattedString) {
LogString ls("%d plus %d equals %d", 12, 23, 35);
EXPECT_STREQ("12 plus 23 equals 35", ls.string());
}
TEST_F(LogTest, LogInfoEmpty) {
setExpected(LOG_INFO, __LINE__ + 1, "");
LOG(INFO);
CHECK_EXPECTATIONS();
}
TEST_F(LogTest, LogInfoWithString) {
static const char kString[] = "Hello World!";
setExpected(LOG_INFO, __LINE__ + 1, kString);
LOG(INFO) << kString;
CHECK_EXPECTATIONS();
}
TEST_F(LogTest, LogInfoWithTwoStrings) {
setExpected(LOG_INFO, __LINE__ + 1, "Hello Globe!");
LOG(INFO) << "Hello " << "Globe!";
CHECK_EXPECTATIONS();
}
TEST_F(LogTest, LogInfoWithLogString) {
LogString ls("Hello You!");
setExpected(LOG_INFO, __LINE__ + 1, ls.string());
LOG(INFO) << ls;
CHECK_EXPECTATIONS();
}
TEST_F(LogTest, LogWarning) {
static const char kWarning[] = "Elvis has left the building!";
setExpected(LOG_WARNING, __LINE__ + 1, kWarning);
LOG(WARNING) << kWarning;
CHECK_EXPECTATIONS();
}
TEST_F(LogTest, LogError) {
static const char kError[] = "Bad Bad Robot!";
setExpected(LOG_ERROR, __LINE__ + 1, kError);
LOG(ERROR) << kError;
CHECK_EXPECTATIONS();
}
TEST_F(LogTest, LogFatal) {
static const char kFatalMessage[] = "I'm dying";
setExpected(LOG_FATAL, __LINE__ + 1, kFatalMessage);
LOG(FATAL) << kFatalMessage;
CHECK_EXPECTATIONS();
EXPECT_TRUE(mFatal);
}
TEST_F(LogTest, LogEvaluatesArgumentsIfNeeded) {
// Use LOG_FATAL since it is always active.
bool flag = false;
setExpected(LOG_FATAL, __LINE__ + 1, "PANIC: Flag was set!");
LOG(FATAL) << "PANIC: " << setFlag(&flag, "Flag was set!");
CHECK_EXPECTATIONS();
EXPECT_TRUE(mFatal);
EXPECT_TRUE(flag);
}
TEST_F(LogTest, LogOnlyEvaluatesArgumentsIfNeeded) {
bool flag = false;
LOG(INVISIBLE) << setFlag(&flag, "Flag was set!");
EXPECT_FALSE(flag);
}
// TODO(digit): Convert this to a real death test when this is supported
// by our version of GTest.
TEST_F(CheckTest, CheckFalse) {
setExpected(LOG_FATAL, __LINE__ + 1, "Check failed: false. ");
CHECK(false);
CHECK_EXPECTATIONS();
}
TEST_F(CheckTest, CheckFalseEvaluatesArguments) {
bool flag = false;
setExpected(LOG_FATAL, __LINE__ + 1, "Check failed: false. Flag was set!");
CHECK(false) << setFlag(&flag, "Flag was set!");
EXPECT_TRUE(flag);
CHECK_EXPECTATIONS();
}
TEST_F(CheckTest, CheckTrue) {
CHECK(true);
EXPECT_FALSE(mFatal);
}
TEST_F(CheckTest, CheckTrueDoesNotEvaluateArguments) {
bool flag = false;
CHECK(true) << setFlag(&flag, "Flag was set!");
EXPECT_FALSE(flag);
EXPECT_FALSE(mFatal);
}
#if ENABLE_DCHECK != 0
TEST_F(DCheckEnabledTest, DCheckIsOnReturnsTrue) {
EXPECT_TRUE(DCHECK_IS_ON());
}
TEST_F(DCheckEnabledTest, DCheckFalse) {
bool flag = false;
setExpected(LOG_FATAL, __LINE__ + 1, "Check failed: false. Flag was set!");
DCHECK(false) << setFlag(&flag, "Flag was set!");
CHECK_EXPECTATIONS();
}
TEST_F(DCheckEnabledTest, DCheckTrue) {
bool flag = false;
DCHECK(true) << setFlag(&flag, "Flag was set!");
EXPECT_FALSE(flag);
EXPECT_FALSE(mFatal);
}
#endif // ENABLE_DCHECK != 0
#if ENABLE_DCHECK != 2
TEST_F(DCheckDisabledTest, DCheckIsOnReturnsFalse) {
EXPECT_FALSE(DCHECK_IS_ON());
}
TEST_F(DCheckDisabledTest, DCheckFalse) {
bool flag = false;
DCHECK(false) << setFlag(&flag, "Flag was set!");
EXPECT_FALSE(flag);
EXPECT_FALSE(mFatal);
}
TEST_F(DCheckDisabledTest, DCheckTrue) {
DCHECK(true);
}
#endif // ENABLE_DCHECK != 2
TEST_F(PLogTest, PLogInfoEmpty) {
setExpectedErrno(LOG_INFO, __LINE__ + 2, EINVAL, "");
errno = EINVAL;
PLOG(INFO);
CHECK_EXPECTATIONS();
}
TEST_F(PLogTest, PLogInfoPreservesErrno) {
// Select a value that is unlikely to ever be raised by the logging
// machinery.
const int kErrnoCode = ENOEXEC;
setForcedErrno(EINVAL);
setExpectedErrno(LOG_INFO, __LINE__ + 2, kErrnoCode, "Hi");
errno = kErrnoCode;
PLOG(INFO) << "Hi";
EXPECT_EQ(kErrnoCode, errno);
CHECK_EXPECTATIONS();
}
} // namespace base
} // namespace android