blob: 272214a71fbaf8720bf64ff0f5d46f0570ec7115 [file] [log] [blame]
#include "aos/logging/implementations.h"
#include <inttypes.h>
#include <chrono>
#include <string>
#include "aos/logging/printf_formats.h"
#include "aos/time/time.h"
#include "gtest/gtest.h"
using ::testing::AssertionFailure;
using ::testing::AssertionResult;
using ::testing::AssertionSuccess;
namespace aos {
namespace logging {
namespace testing {
namespace chrono = ::std::chrono;
class TestLogImplementation : public LogImplementation {
virtual ::aos::monotonic_clock::time_point monotonic_now() const {
return ::aos::monotonic_clock::now();
}
__attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0))) void DoLog(
log_level level, const char *format, va_list ap) override {
internal::FillInMessage(level, monotonic_now(), format, ap, &message_);
if (level == FATAL) {
internal::PrintMessage(stderr, message_);
abort();
}
used_ = true;
}
LogMessage message_;
public:
const LogMessage &message() { return message_; }
bool used() { return used_; }
void reset_used() { used_ = false; }
TestLogImplementation() : used_(false) {}
bool used_;
};
class LoggingTest : public ::testing::Test {
protected:
AssertionResult WasAnythingLogged() {
if (log_implementation->used()) {
return AssertionSuccess() << "read message '"
<< log_implementation->message().message << "'";
}
return AssertionFailure();
}
AssertionResult WasLogged(log_level level, const std::string message) {
if (!log_implementation->used()) {
return AssertionFailure() << "nothing was logged";
}
if (log_implementation->message().level != level) {
return AssertionFailure() << "a message with level "
<< log_str(log_implementation->message().level)
<< " was logged instead of " << log_str(level);
}
internal::Context *context = internal::Context::Get();
if (log_implementation->message().source != context->source) {
AOS_LOG(FATAL, "got a message from %" PRIu32 ", but we're %" PRIu32 "\n",
static_cast<uint32_t>(log_implementation->message().source),
static_cast<uint32_t>(context->source));
}
if (log_implementation->message().name_length != context->name_size ||
memcmp(log_implementation->message().name, context->name,
context->name_size) != 0) {
AOS_LOG(FATAL, "got a message from %.*s, but we're %s\n",
static_cast<int>(log_implementation->message().name_length),
log_implementation->message().name, context->name);
}
if (strstr(log_implementation->message().message, message.c_str()) ==
NULL) {
return AssertionFailure()
<< "got a message of '" << log_implementation->message().message
<< "' but expected it to contain '" << message << "'";
}
return AssertionSuccess() << log_implementation->message().message;
}
private:
void SetUp() override {
static bool first = true;
if (first) {
first = false;
Init();
SetImplementation(log_implementation = new TestLogImplementation());
}
log_implementation->reset_used();
}
void TearDown() override { Cleanup(); }
static TestLogImplementation *log_implementation;
};
TestLogImplementation *LoggingTest::log_implementation(NULL);
typedef LoggingTest LoggingDeathTest;
// Tests both basic logging functionality and that the test setup works
// correctly.
TEST_F(LoggingTest, Basic) {
EXPECT_FALSE(WasAnythingLogged());
AOS_LOG(INFO, "test log 1\n");
EXPECT_TRUE(WasLogged(INFO, "test log 1\n"));
AOS_LOG(INFO, "test log 1.5\n");
// there's a subtle typo on purpose...
EXPECT_FALSE(WasLogged(INFO, "test log 15\n"));
AOS_LOG(ERROR, "test log 2=%d\n", 55);
EXPECT_TRUE(WasLogged(ERROR, "test log 2=55\n"));
AOS_LOG(ERROR, "test log 3\n");
EXPECT_FALSE(WasLogged(WARNING, "test log 3\n"));
AOS_LOG(WARNING, "test log 4\n");
EXPECT_TRUE(WasAnythingLogged());
}
TEST_F(LoggingTest, Cork) {
static const int begin_line = __LINE__;
AOS_LOG_CORK("first part ");
AOS_LOG_CORK("second part (=%d) ", 19);
EXPECT_FALSE(WasAnythingLogged());
AOS_LOG_CORK("third part ");
static const int end_line = __LINE__;
AOS_LOG_UNCORK(WARNING, "last part %d\n", 5);
std::stringstream expected;
expected << "implementations_test.cc: ";
expected << (begin_line + 1);
expected << "-";
expected << (end_line + 1);
expected << ": ";
expected << __func__;
expected << ": first part second part (=19) third part last part 5\n";
EXPECT_TRUE(WasLogged(WARNING, expected.str()));
}
TEST_F(LoggingDeathTest, Fatal) {
ASSERT_EXIT(AOS_LOG(FATAL, "this should crash it\n"),
::testing::KilledBySignal(SIGABRT), "this should crash it");
}
TEST_F(LoggingDeathTest, PCHECK) {
EXPECT_DEATH(AOS_PCHECK(fprintf(stdin, "nope")),
".*fprintf\\(stdin, \"nope\"\\).*failed.*");
}
TEST_F(LoggingTest, PCHECK) { EXPECT_EQ(7, AOS_PCHECK(printf("abc123\n"))); }
TEST_F(LoggingTest, PrintfDirectives) {
AOS_LOG(INFO, "test log %%1 %%d\n");
EXPECT_TRUE(WasLogged(INFO, "test log %1 %d\n"));
AOS_LOG_DYNAMIC(WARNING, "test log %%2 %%f\n");
EXPECT_TRUE(WasLogged(WARNING, "test log %2 %f\n"));
AOS_LOG_CORK("log 3 part %%1 %%d ");
AOS_LOG_UNCORK(DEBUG, "log 3 part %%2 %%f\n");
EXPECT_TRUE(WasLogged(DEBUG, "log 3 part %1 %d log 3 part %2 %f\n"));
}
TEST_F(LoggingTest, Timing) {
// For writing only.
// static const long kTimingCycles = 5000000;
static const long kTimingCycles = 5000;
monotonic_clock::time_point start = monotonic_clock::now();
for (long i = 0; i < kTimingCycles; ++i) {
AOS_LOG(INFO, "a\n");
}
monotonic_clock::time_point end = monotonic_clock::now();
auto diff = end - start;
printf("short message took %" PRId64 " nsec for %ld\n",
static_cast<int64_t>(
chrono::duration_cast<chrono::nanoseconds>(diff).count()),
kTimingCycles);
start = monotonic_clock::now();
for (long i = 0; i < kTimingCycles; ++i) {
AOS_LOG(INFO, "something longer than just \"a\" to log to test timing\n");
}
end = monotonic_clock::now();
diff = end - start;
printf("long message took %" PRId64 " nsec for %ld\n",
static_cast<int64_t>(
chrono::duration_cast<chrono::nanoseconds>(diff).count()),
kTimingCycles);
}
TEST(LoggingPrintFormatTest, Time) {
char buffer[1024];
// Easy ones.
ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
AOS_TIME_ARGS(2, 0)));
EXPECT_EQ("0000000002.000000s", ::std::string(buffer));
ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
AOS_TIME_ARGS(2, 1)));
EXPECT_EQ("0000000002.000000s", ::std::string(buffer));
// This one should be exact.
ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
AOS_TIME_ARGS(2, 1000)));
EXPECT_EQ("0000000002.000001s", ::std::string(buffer));
// Make sure rounding works correctly.
ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
AOS_TIME_ARGS(2, 999)));
EXPECT_EQ("0000000002.000000s", ::std::string(buffer));
ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
AOS_TIME_ARGS(2, 500)));
EXPECT_EQ("0000000002.000000s", ::std::string(buffer));
ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
AOS_TIME_ARGS(2, 499)));
EXPECT_EQ("0000000002.000000s", ::std::string(buffer));
// This used to result in "0000000001.099500s".
ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
AOS_TIME_ARGS(1, 995000000)));
EXPECT_EQ("0000000001.995000s", ::std::string(buffer));
// This used to result in "0000000001.099500s".
ASSERT_EQ(18, snprintf(buffer, sizeof(buffer), AOS_TIME_FORMAT,
AOS_TIME_ARGS(1, 999999999)));
EXPECT_EQ("0000000001.999999s", ::std::string(buffer));
}
TEST(LoggingPrintFormatTest, Base) {
char buffer[1024];
static const ::std::string kExpected1 =
"name(971)(01678): ERROR at 0000000001.995000s: ";
ASSERT_GT(sizeof(buffer), kExpected1.size());
ASSERT_EQ(
kExpected1.size(),
static_cast<size_t>(snprintf(
buffer, sizeof(buffer), AOS_LOGGING_BASE_FORMAT,
AOS_LOGGING_BASE_ARGS(4, "name", 971, 1678, ERROR, 1, 995000000))));
EXPECT_EQ(kExpected1, ::std::string(buffer));
}
TEST(ScopedLogRestorerTest, RestoreTest) {
LogImplementation *curr_impl = GetImplementation();
{
ScopedLogRestorer log_restorer;
logging::RegisterCallbackImplementation([] (const LogMessage&) {});
ASSERT_NE(curr_impl, GetImplementation());
}
ASSERT_EQ(curr_impl, GetImplementation());
}
} // namespace testing
} // namespace logging
} // namespace aos