blob: 7b018006d56aaf11a4f4adbf5d0306a405102e1e [file] [log] [blame]
// Copyright 2021 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_log/proto_utils.h"
#include "gtest/gtest.h"
#include "pw_bytes/span.h"
#include "pw_log/levels.h"
#include "pw_log/proto/log.pwpb.h"
#include "pw_protobuf/bytes_utils.h"
#include "pw_protobuf/decoder.h"
namespace pw::log {
void VerifyTokenizedLogEntry(pw::protobuf::Decoder& entry_decoder,
pw::log_tokenized::Metadata expected_metadata,
ConstByteSpan expected_tokenized_data,
const int64_t expected_timestamp,
ConstByteSpan expected_thread_name) {
ConstByteSpan tokenized_data;
EXPECT_TRUE(entry_decoder.Next().ok()); // message [tokenized]
EXPECT_EQ(entry_decoder.FieldNumber(),
static_cast<uint32_t>(log::LogEntry::Fields::MESSAGE));
EXPECT_TRUE(entry_decoder.ReadBytes(&tokenized_data).ok());
EXPECT_TRUE(std::memcmp(tokenized_data.begin(),
expected_tokenized_data.begin(),
expected_tokenized_data.size()) == 0);
uint32_t line_level;
EXPECT_TRUE(entry_decoder.Next().ok()); // line_level
EXPECT_EQ(entry_decoder.FieldNumber(),
static_cast<uint32_t>(log::LogEntry::Fields::LINE_LEVEL));
EXPECT_TRUE(entry_decoder.ReadUint32(&line_level).ok());
uint32_t line_number;
uint8_t level;
std::tie(line_number, level) = UnpackLineLevel(line_level);
EXPECT_EQ(expected_metadata.level(), level);
EXPECT_EQ(expected_metadata.line_number(), line_number);
if (expected_metadata.flags() != 0) {
uint32_t flags;
EXPECT_TRUE(entry_decoder.Next().ok()); // flags
EXPECT_EQ(entry_decoder.FieldNumber(),
static_cast<uint32_t>(log::LogEntry::Fields::FLAGS));
EXPECT_TRUE(entry_decoder.ReadUint32(&flags).ok());
EXPECT_EQ(expected_metadata.flags(), flags);
}
int64_t timestamp;
EXPECT_TRUE(entry_decoder.Next().ok()); // timestamp
EXPECT_TRUE(
entry_decoder.FieldNumber() ==
static_cast<uint32_t>(log::LogEntry::Fields::TIMESTAMP) ||
entry_decoder.FieldNumber() ==
static_cast<uint32_t>(log::LogEntry::Fields::TIME_SINCE_LAST_ENTRY));
EXPECT_TRUE(entry_decoder.ReadInt64(&timestamp).ok());
EXPECT_EQ(expected_timestamp, timestamp);
if (expected_metadata.module() != 0) {
EXPECT_TRUE(entry_decoder.Next().ok()); // module name
EXPECT_EQ(entry_decoder.FieldNumber(),
static_cast<uint32_t>(log::LogEntry::Fields::MODULE));
const Result<uint32_t> module =
protobuf::DecodeBytesToUint32(entry_decoder);
ASSERT_TRUE(module.ok());
EXPECT_EQ(expected_metadata.module(), module.value());
}
if (!expected_thread_name.empty()) {
ConstByteSpan tokenized_thread_name;
EXPECT_TRUE(entry_decoder.Next().ok()); // thread [tokenized]
EXPECT_EQ(entry_decoder.FieldNumber(),
static_cast<uint32_t>(log::LogEntry::Fields::THREAD));
EXPECT_TRUE(entry_decoder.ReadBytes(&tokenized_thread_name).ok());
EXPECT_TRUE(std::memcmp(tokenized_thread_name.begin(),
expected_thread_name.begin(),
expected_thread_name.size()) == 0);
}
}
void VerifyLogEntry(pw::protobuf::Decoder& entry_decoder,
int expected_level,
unsigned int expected_flags,
std::string_view expected_module,
std::string_view expected_thread_name,
std::string_view expected_file_name,
int expected_line_number,
int64_t expected_ticks_since_epoch,
std::string_view expected_message) {
std::string_view message;
EXPECT_TRUE(entry_decoder.Next().ok()); // message
EXPECT_EQ(entry_decoder.FieldNumber(),
static_cast<uint32_t>(log::LogEntry::Fields::MESSAGE));
EXPECT_TRUE(entry_decoder.ReadString(&message).ok());
EXPECT_TRUE(std::equal(message.begin(),
message.end(),
expected_message.begin(),
expected_message.end()));
uint32_t line_level;
EXPECT_TRUE(entry_decoder.Next().ok()); // line_level
EXPECT_EQ(entry_decoder.FieldNumber(),
static_cast<uint32_t>(log::LogEntry::Fields::LINE_LEVEL));
EXPECT_TRUE(entry_decoder.ReadUint32(&line_level).ok());
uint32_t line_number;
uint8_t level;
std::tie(line_number, level) = UnpackLineLevel(line_level);
EXPECT_EQ(static_cast<unsigned int>(expected_line_number), line_number);
EXPECT_EQ(expected_level, level);
if (expected_flags != 0) {
uint32_t flags;
EXPECT_TRUE(entry_decoder.Next().ok()); // flags
EXPECT_EQ(entry_decoder.FieldNumber(),
static_cast<uint32_t>(log::LogEntry::Fields::FLAGS));
EXPECT_TRUE(entry_decoder.ReadUint32(&flags).ok());
EXPECT_EQ(expected_flags, flags);
}
int64_t timestamp;
EXPECT_TRUE(entry_decoder.Next().ok()); // timestamp
EXPECT_TRUE(
entry_decoder.FieldNumber() ==
static_cast<uint32_t>(log::LogEntry::Fields::TIMESTAMP) ||
entry_decoder.FieldNumber() ==
static_cast<uint32_t>(log::LogEntry::Fields::TIME_SINCE_LAST_ENTRY));
EXPECT_TRUE(entry_decoder.ReadInt64(&timestamp).ok());
EXPECT_EQ(expected_ticks_since_epoch, timestamp);
if (!expected_module.empty()) {
std::string_view module_name;
EXPECT_TRUE(entry_decoder.Next().ok()); // module
EXPECT_EQ(entry_decoder.FieldNumber(),
static_cast<uint32_t>(log::LogEntry::Fields::MODULE));
EXPECT_TRUE(entry_decoder.ReadString(&module_name).ok());
EXPECT_TRUE(std::equal(module_name.begin(),
module_name.end(),
expected_module.begin(),
expected_module.end()));
}
if (!expected_file_name.empty()) {
std::string_view file_name;
EXPECT_TRUE(entry_decoder.Next().ok()); // file
EXPECT_EQ(entry_decoder.FieldNumber(),
static_cast<uint32_t>(log::LogEntry::Fields::FILE));
EXPECT_TRUE(entry_decoder.ReadString(&file_name).ok());
EXPECT_TRUE(std::equal(file_name.begin(),
file_name.end(),
expected_file_name.begin(),
expected_file_name.end()));
}
if (!expected_thread_name.empty()) {
std::string_view thread_name;
EXPECT_TRUE(entry_decoder.Next().ok()); // file
EXPECT_EQ(entry_decoder.FieldNumber(),
static_cast<uint32_t>(log::LogEntry::Fields::THREAD));
EXPECT_TRUE(entry_decoder.ReadString(&thread_name).ok());
EXPECT_TRUE(std::equal(thread_name.begin(),
thread_name.end(),
expected_thread_name.begin(),
expected_thread_name.end()));
}
}
TEST(UtilsTest, LineLevelPacking) {
constexpr uint8_t kExpectedLevel = PW_LOG_LEVEL_ERROR;
constexpr uint32_t kExpectedLine = 1234567;
constexpr uint32_t kExpectedLineLevel =
(kExpectedLine << PW_LOG_LEVEL_BITS) |
(kExpectedLevel & PW_LOG_LEVEL_BITMASK);
EXPECT_EQ(kExpectedLineLevel, PackLineLevel(kExpectedLine, kExpectedLevel));
}
TEST(UtilsTest, LineLevelUnpacking) {
constexpr uint8_t kExpectedLevel = PW_LOG_LEVEL_ERROR;
constexpr uint32_t kExpectedLine = 1234567;
constexpr uint32_t kExpectedLineLevel =
(kExpectedLine << PW_LOG_LEVEL_BITS) |
(kExpectedLevel & PW_LOG_LEVEL_BITMASK);
uint32_t line_number;
uint8_t level;
std::tie(line_number, level) = UnpackLineLevel(kExpectedLineLevel);
EXPECT_EQ(kExpectedLine, line_number);
EXPECT_EQ(kExpectedLevel, level);
}
TEST(UtilsTest, LineLevelPackAndUnpack) {
constexpr uint8_t kExpectedLevel = PW_LOG_LEVEL_ERROR;
constexpr uint32_t kExpectedLine = 1234567;
uint32_t line_number;
uint8_t level;
std::tie(line_number, level) =
UnpackLineLevel(PackLineLevel(kExpectedLine, kExpectedLevel));
EXPECT_EQ(kExpectedLine, line_number);
EXPECT_EQ(kExpectedLevel, level);
}
TEST(UtilsTest, EncodeTokenizedLog) {
constexpr std::byte kTokenizedData[1] = {std::byte(0x01)};
constexpr int64_t kExpectedTimestamp = 1;
constexpr std::byte kExpectedThreadName[1] = {std::byte(0x02)};
std::byte encode_buffer[32];
pw::log_tokenized::Metadata metadata =
pw::log_tokenized::Metadata::Set<1, 2, 3, 4>();
Result<ConstByteSpan> result = EncodeTokenizedLog(metadata,
kTokenizedData,
kExpectedTimestamp,
kExpectedThreadName,
encode_buffer);
EXPECT_TRUE(result.ok());
pw::protobuf::Decoder log_decoder(result.value());
VerifyTokenizedLogEntry(log_decoder,
metadata,
kTokenizedData,
kExpectedTimestamp,
kExpectedThreadName);
result = EncodeTokenizedLog(metadata,
reinterpret_cast<const uint8_t*>(kTokenizedData),
sizeof(kTokenizedData),
kExpectedTimestamp,
kExpectedThreadName,
encode_buffer);
EXPECT_TRUE(result.ok());
log_decoder.Reset(result.value());
VerifyTokenizedLogEntry(log_decoder,
metadata,
kTokenizedData,
kExpectedTimestamp,
kExpectedThreadName);
}
TEST(UtilsTest, EncodeTokenizedLog_EmptyFlags) {
constexpr std::byte kTokenizedData[1] = {std::byte(0x01)};
constexpr int64_t kExpectedTimestamp = 1;
constexpr std::byte kExpectedThreadName[1] = {std::byte(0x02)};
std::byte encode_buffer[32];
// Create an empty flags set.
pw::log_tokenized::Metadata metadata =
pw::log_tokenized::Metadata::Set<1, 2, 0, 4>();
Result<ConstByteSpan> result = EncodeTokenizedLog(metadata,
kTokenizedData,
kExpectedTimestamp,
kExpectedThreadName,
encode_buffer);
EXPECT_TRUE(result.ok());
pw::protobuf::Decoder log_decoder(result.value());
VerifyTokenizedLogEntry(log_decoder,
metadata,
kTokenizedData,
kExpectedTimestamp,
kExpectedThreadName);
}
TEST(UtilsTest, EncodeTokenizedLog_InsufficientSpace) {
constexpr std::byte kTokenizedData[1] = {std::byte(0x01)};
constexpr int64_t kExpectedTimestamp = 1;
constexpr std::byte kExpectedThreadName[1] = {std::byte(0x02)};
std::byte encode_buffer[1];
pw::log_tokenized::Metadata metadata =
pw::log_tokenized::Metadata::Set<1, 2, 3, 4>();
Result<ConstByteSpan> result = EncodeTokenizedLog(metadata,
kTokenizedData,
kExpectedTimestamp,
kExpectedThreadName,
encode_buffer);
EXPECT_TRUE(result.status().IsResourceExhausted());
}
TEST(UtilsTest, EncodeLog) {
constexpr int kExpectedLevel = PW_LOG_LEVEL_INFO;
constexpr unsigned int kExpectedFlags = 2;
constexpr std::string_view kExpectedModule("TST");
constexpr std::string_view kExpectedThread("thread");
constexpr std::string_view kExpectedFile("proto_test.cc");
constexpr int kExpectedLine = 14;
constexpr int64_t kExpectedTimestamp = 1;
constexpr std::string_view kExpectedMessage("msg");
std::byte encode_buffer[64];
Result<ConstByteSpan> result = EncodeLog(kExpectedLevel,
kExpectedFlags,
kExpectedModule,
kExpectedThread,
kExpectedFile,
kExpectedLine,
kExpectedTimestamp,
kExpectedMessage,
encode_buffer);
EXPECT_TRUE(result.ok());
pw::protobuf::Decoder log_decoder(result.value());
VerifyLogEntry(log_decoder,
kExpectedLevel,
kExpectedFlags,
kExpectedModule,
kExpectedThread,
kExpectedFile,
kExpectedLine,
kExpectedTimestamp,
kExpectedMessage);
}
TEST(UtilsTest, EncodeLog_EmptyFlags) {
constexpr int kExpectedLevel = PW_LOG_LEVEL_INFO;
constexpr unsigned int kExpectedFlags = 0;
constexpr std::string_view kExpectedModule("TST");
constexpr std::string_view kExpectedThread("thread");
constexpr std::string_view kExpectedFile("proto_test.cc");
constexpr int kExpectedLine = 14;
constexpr int64_t kExpectedTimestamp = 1;
constexpr std::string_view kExpectedMessage("msg");
std::byte encode_buffer[64];
Result<ConstByteSpan> result = EncodeLog(kExpectedLevel,
kExpectedFlags,
kExpectedModule,
kExpectedThread,
kExpectedFile,
kExpectedLine,
kExpectedTimestamp,
kExpectedMessage,
encode_buffer);
EXPECT_TRUE(result.ok());
pw::protobuf::Decoder log_decoder(result.value());
VerifyLogEntry(log_decoder,
kExpectedLevel,
kExpectedFlags,
kExpectedModule,
kExpectedThread,
kExpectedFile,
kExpectedLine,
kExpectedTimestamp,
kExpectedMessage);
}
TEST(UtilsTest, EncodeLog_EmptyFile) {
constexpr int kExpectedLevel = PW_LOG_LEVEL_INFO;
constexpr unsigned int kExpectedFlags = 0;
constexpr std::string_view kExpectedModule("TST");
constexpr std::string_view kExpectedThread("thread");
constexpr std::string_view kExpectedFile;
constexpr int kExpectedLine = 14;
constexpr int64_t kExpectedTimestamp = 1;
constexpr std::string_view kExpectedMessage("msg");
std::byte encode_buffer[64];
Result<ConstByteSpan> result = EncodeLog(kExpectedLevel,
kExpectedFlags,
kExpectedModule,
kExpectedThread,
kExpectedFile,
kExpectedLine,
kExpectedTimestamp,
kExpectedMessage,
encode_buffer);
EXPECT_TRUE(result.ok());
pw::protobuf::Decoder log_decoder(result.value());
VerifyLogEntry(log_decoder,
kExpectedLevel,
kExpectedFlags,
kExpectedModule,
kExpectedThread,
kExpectedFile,
kExpectedLine,
kExpectedTimestamp,
kExpectedMessage);
}
TEST(UtilsTest, EncodeLog_EmptyModule) {
constexpr int kExpectedLevel = PW_LOG_LEVEL_INFO;
constexpr unsigned int kExpectedFlags = 3;
constexpr std::string_view kExpectedModule;
constexpr std::string_view kExpectedThread("thread");
constexpr std::string_view kExpectedFile("test.cc");
constexpr int kExpectedLine = 14;
constexpr int64_t kExpectedTimestamp = 1;
constexpr std::string_view kExpectedMessage("msg");
std::byte encode_buffer[64];
Result<ConstByteSpan> result = EncodeLog(kExpectedLevel,
kExpectedFlags,
kExpectedModule,
kExpectedThread,
kExpectedFile,
kExpectedLine,
kExpectedTimestamp,
kExpectedMessage,
encode_buffer);
EXPECT_TRUE(result.ok());
pw::protobuf::Decoder log_decoder(result.value());
VerifyLogEntry(log_decoder,
kExpectedLevel,
kExpectedFlags,
kExpectedModule,
kExpectedThread,
kExpectedFile,
kExpectedLine,
kExpectedTimestamp,
kExpectedMessage);
}
TEST(UtilsTest, EncodeLog_EmptyThread) {
constexpr int kExpectedLevel = PW_LOG_LEVEL_INFO;
constexpr unsigned int kExpectedFlags = 2;
constexpr std::string_view kExpectedModule("TST");
constexpr std::string_view kExpectedThread;
constexpr std::string_view kExpectedFile("proto_test.cc");
constexpr int kExpectedLine = 14;
constexpr int64_t kExpectedTimestamp = 1;
constexpr std::string_view kExpectedMessage("msg");
std::byte encode_buffer[64];
Result<ConstByteSpan> result = EncodeLog(kExpectedLevel,
kExpectedFlags,
kExpectedModule,
kExpectedThread,
kExpectedFile,
kExpectedLine,
kExpectedTimestamp,
kExpectedMessage,
encode_buffer);
EXPECT_TRUE(result.ok());
pw::protobuf::Decoder log_decoder(result.value());
VerifyLogEntry(log_decoder,
kExpectedLevel,
kExpectedFlags,
kExpectedModule,
kExpectedThread,
kExpectedFile,
kExpectedLine,
kExpectedTimestamp,
kExpectedMessage);
}
TEST(UtilsTest, EncodeLog_EmptyMessage) {
constexpr int kExpectedLevel = PW_LOG_LEVEL_INFO;
constexpr unsigned int kExpectedFlags = 0;
constexpr std::string_view kExpectedModule;
constexpr std::string_view kExpectedThread;
constexpr std::string_view kExpectedFile;
constexpr int kExpectedLine = 14;
constexpr int64_t kExpectedTimestamp = 1;
constexpr std::string_view kExpectedMessage;
std::byte encode_buffer[64];
Result<ConstByteSpan> result = EncodeLog(kExpectedLevel,
kExpectedFlags,
kExpectedModule,
kExpectedThread,
kExpectedFile,
kExpectedLine,
kExpectedTimestamp,
kExpectedMessage,
encode_buffer);
EXPECT_TRUE(result.status().IsInvalidArgument());
}
TEST(UtilsTest, EncodeLog_InsufficientSpace) {
constexpr int kExpectedLevel = PW_LOG_LEVEL_INFO;
constexpr unsigned int kExpectedFlags = 0;
constexpr std::string_view kExpectedModule;
constexpr std::string_view kExpectedThread;
constexpr std::string_view kExpectedFile;
constexpr int kExpectedLine = 14;
constexpr int64_t kExpectedTimestamp = 1;
constexpr std::string_view kExpectedMessage("msg");
std::byte encode_buffer[1];
Result<ConstByteSpan> result = EncodeLog(kExpectedLevel,
kExpectedFlags,
kExpectedModule,
kExpectedThread,
kExpectedFile,
kExpectedLine,
kExpectedTimestamp,
kExpectedMessage,
encode_buffer);
EXPECT_TRUE(result.status().IsResourceExhausted());
}
} // namespace pw::log