// 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(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(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(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(log::LogEntry::Fields::TIMESTAMP) || entry_decoder.FieldNumber() == static_cast(log::LogEntry::Fields::TIME_SINCE_LAST_ENTRY)); EXPECT_TRUE(entry_decoder.ReadInt64(×tamp).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(log::LogEntry::Fields::MODULE)); const Result 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(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(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(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(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(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(log::LogEntry::Fields::TIMESTAMP) || entry_decoder.FieldNumber() == static_cast(log::LogEntry::Fields::TIME_SINCE_LAST_ENTRY)); EXPECT_TRUE(entry_decoder.ReadInt64(×tamp).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(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(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(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 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(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 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 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 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 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 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 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 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 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 result = EncodeLog(kExpectedLevel, kExpectedFlags, kExpectedModule, kExpectedThread, kExpectedFile, kExpectedLine, kExpectedTimestamp, kExpectedMessage, encode_buffer); EXPECT_TRUE(result.status().IsResourceExhausted()); } } // namespace pw::log