blob: fc75706ae9730e7fe22e325299af9fdb60c39dec [file] [log] [blame]
// Copyright 2020 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_multisink/log_queue.h"
#include "gtest/gtest.h"
#include "pw_log/levels.h"
#include "pw_log_proto/log.pwpb.h"
#include "pw_protobuf/decoder.h"
namespace pw::log_rpc {
namespace {
constexpr size_t kEncodeBufferSize = 512;
constexpr const char kTokenizedMessage[] = "msg_token";
constexpr uint32_t kFlags = 0xF;
constexpr uint32_t kLevel = 0b010;
constexpr uint32_t kLine = 0b101011000;
constexpr uint32_t kTokenizedThread = 0xF;
constexpr int64_t kTimestamp = 0;
constexpr size_t kLogBufferSize = kEncodeBufferSize * 3;
void VerifyLogEntry(pw::protobuf::Decoder& log_decoder,
const char* expected_tokenized_message,
const uint32_t expected_flags,
const uint32_t expected_level,
const uint32_t expected_line,
const uint32_t expected_tokenized_thread,
const int64_t expected_timestamp) {
ConstByteSpan log_entry_message;
EXPECT_TRUE(log_decoder.Next().ok()); // preamble
EXPECT_EQ(1U, log_decoder.FieldNumber());
EXPECT_TRUE(log_decoder.ReadBytes(&log_entry_message).ok());
pw::protobuf::Decoder entry_decoder(log_entry_message);
ConstByteSpan tokenized_message;
EXPECT_TRUE(entry_decoder.Next().ok()); // tokenized_message
EXPECT_EQ(1U, entry_decoder.FieldNumber());
EXPECT_TRUE(entry_decoder.ReadBytes(&tokenized_message).ok());
EXPECT_TRUE(std::memcmp(tokenized_message.begin(),
(const void*)expected_tokenized_message,
tokenized_message.size()) == 0);
uint32_t line_level;
EXPECT_TRUE(entry_decoder.Next().ok()); // line_level
EXPECT_EQ(2U, entry_decoder.FieldNumber());
EXPECT_TRUE(entry_decoder.ReadUint32(&line_level).ok());
EXPECT_EQ(expected_level, line_level & PW_LOG_LEVEL_BITMASK);
EXPECT_EQ(expected_line,
(line_level & ~PW_LOG_LEVEL_BITMASK) >> PW_LOG_LEVEL_BITWIDTH);
uint32_t flags;
EXPECT_TRUE(entry_decoder.Next().ok()); // flags
EXPECT_EQ(3U, entry_decoder.FieldNumber());
EXPECT_TRUE(entry_decoder.ReadUint32(&flags).ok());
EXPECT_EQ(expected_flags, flags);
uint32_t tokenized_thread;
EXPECT_TRUE(entry_decoder.Next().ok()); // tokenized_thread
EXPECT_EQ(4U, entry_decoder.FieldNumber());
EXPECT_TRUE(entry_decoder.ReadUint32(&tokenized_thread).ok());
EXPECT_EQ(expected_tokenized_thread, tokenized_thread);
int64_t timestamp;
EXPECT_TRUE(entry_decoder.Next().ok()); // timestamp
EXPECT_EQ(5U, entry_decoder.FieldNumber());
EXPECT_TRUE(entry_decoder.ReadInt64(&timestamp).ok());
EXPECT_EQ(expected_timestamp, timestamp);
}
} // namespace
TEST(LogQueue, SinglePushPopTokenizedMessage) {
std::byte log_buffer[kLogBufferSize];
LogQueueWithEncodeBuffer<kEncodeBufferSize> log_queue(log_buffer);
EXPECT_EQ(OkStatus(),
log_queue.PushTokenizedMessage(
std::as_bytes(std::span(kTokenizedMessage)),
kFlags,
kLevel,
kLine,
kTokenizedThread,
kTimestamp));
std::byte log_entry[kEncodeBufferSize];
Result<LogEntries> pop_result = log_queue.Pop(std::span(log_entry));
EXPECT_TRUE(pop_result.ok());
pw::protobuf::Decoder log_decoder(pop_result.value().entries);
EXPECT_EQ(pop_result.value().entry_count, 1U);
VerifyLogEntry(log_decoder,
kTokenizedMessage,
kFlags,
kLevel,
kLine,
kTokenizedThread,
kTimestamp);
}
TEST(LogQueue, MultiplePushPopTokenizedMessage) {
constexpr size_t kEntryCount = 3;
std::byte log_buffer[1024];
LogQueueWithEncodeBuffer<kEncodeBufferSize> log_queue(log_buffer);
for (size_t i = 0; i < kEntryCount; i++) {
EXPECT_EQ(OkStatus(),
log_queue.PushTokenizedMessage(
std::as_bytes(std::span(kTokenizedMessage)),
kFlags,
kLevel,
kLine + (i << 3),
kTokenizedThread,
kTimestamp + i));
}
std::byte log_entry[kEncodeBufferSize];
for (size_t i = 0; i < kEntryCount; i++) {
Result<LogEntries> pop_result = log_queue.Pop(std::span(log_entry));
EXPECT_TRUE(pop_result.ok());
pw::protobuf::Decoder log_decoder(pop_result.value().entries);
EXPECT_EQ(pop_result.value().entry_count, 1U);
VerifyLogEntry(log_decoder,
kTokenizedMessage,
kFlags,
kLevel,
kLine + (i << 3),
kTokenizedThread,
kTimestamp + i);
}
}
TEST(LogQueue, PopMultiple) {
constexpr size_t kEntryCount = 3;
std::byte log_buffer[kLogBufferSize];
LogQueueWithEncodeBuffer<kEncodeBufferSize> log_queue(log_buffer);
for (size_t i = 0; i < kEntryCount; i++) {
EXPECT_EQ(OkStatus(),
log_queue.PushTokenizedMessage(
std::as_bytes(std::span(kTokenizedMessage)),
kFlags,
kLevel,
kLine + (i << 3),
kTokenizedThread,
kTimestamp + i));
}
std::byte log_entries[kLogBufferSize];
Result<LogEntries> pop_result = log_queue.PopMultiple(log_entries);
EXPECT_TRUE(pop_result.ok());
pw::protobuf::Decoder log_decoder(pop_result.value().entries);
EXPECT_EQ(pop_result.value().entry_count, kEntryCount);
for (size_t i = 0; i < kEntryCount; i++) {
VerifyLogEntry(log_decoder,
kTokenizedMessage,
kFlags,
kLevel,
kLine + (i << 3),
kTokenizedThread,
kTimestamp + i);
}
}
TEST(LogQueue, TooSmallEncodeBuffer) {
constexpr size_t kSmallBuffer = 1;
std::byte log_buffer[kLogBufferSize];
LogQueueWithEncodeBuffer<kSmallBuffer> log_queue(log_buffer);
EXPECT_EQ(Status::Internal(),
log_queue.PushTokenizedMessage(
std::as_bytes(std::span(kTokenizedMessage)),
kFlags,
kLevel,
kLine,
kTokenizedThread,
kTimestamp));
}
TEST(LogQueue, TooSmallLogBuffer) {
constexpr size_t kSmallerThanPreamble = 1;
constexpr size_t kEntryCount = 100;
// Expect OUT_OF_RANGE when the buffer is smaller than a preamble.
std::byte log_buffer[kLogBufferSize];
LogQueueWithEncodeBuffer<kEncodeBufferSize> log_queue_small(
std::span(log_buffer, kSmallerThanPreamble));
EXPECT_EQ(Status::OutOfRange(),
log_queue_small.PushTokenizedMessage(
std::as_bytes(std::span(kTokenizedMessage)),
kFlags,
kLevel,
kLine,
kTokenizedThread,
kTimestamp));
// Expect RESOURCE_EXHAUSTED when there's not enough space for the chunk.
LogQueueWithEncodeBuffer<kEncodeBufferSize> log_queue_medium(log_buffer);
for (size_t i = 0; i < kEntryCount; i++) {
log_queue_medium.PushTokenizedMessage(
std::as_bytes(std::span(kTokenizedMessage)),
kFlags,
kLevel,
kLine,
kTokenizedThread,
kTimestamp);
}
EXPECT_EQ(Status::ResourceExhausted(),
log_queue_medium.PushTokenizedMessage(
std::as_bytes(std::span(kTokenizedMessage)),
kFlags,
kLevel,
kLine,
kTokenizedThread,
kTimestamp));
}
} // namespace pw::log_rpc