blob: 7e2b827310840e7cf26447a6d7c31b5703da8067 [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_rpc/log_filter.h"
#include <array>
#include <cstddef>
#include <cstring>
#include "gtest/gtest.h"
#include "pw_bytes/endian.h"
#include "pw_log/levels.h"
#include "pw_log/log.h"
#include "pw_log/proto/log.pwpb.h"
#include "pw_log/proto_utils.h"
#include "pw_log_rpc/log_filter_map.h"
#include "pw_log_tokenized/metadata.h"
#include "pw_result/result.h"
#include "pw_status/status.h"
#include "pw_status/try.h"
namespace pw::log_rpc {
namespace {
constexpr uint32_t kSampleModule = 0x1234;
constexpr uint32_t kSampleFlags = 0x3;
const std::array<std::byte, cfg::kMaxThreadNameBytes - 7> kSampleThread = {
std::byte('R'), std::byte('P'), std::byte('C')};
constexpr char kSampleMessage[] = "message";
constexpr auto kSampleModuleLittleEndian =
bytes::CopyInOrder<uint32_t>(endian::little, kSampleModule);
// Creates and encodes a log entry in the provided buffer.
template <uintptr_t log_level, uintptr_t module, uintptr_t flags>
Result<ConstByteSpan> EncodeLogEntry(std::string_view message,
ByteSpan buffer,
ConstByteSpan thread) {
auto metadata = log_tokenized::Metadata::Set<log_level, module, flags, 0>();
return log::EncodeTokenizedLog(metadata,
as_bytes(span(message)),
/*ticks_since_epoch=*/0,
thread,
buffer);
}
Status EncodeFilterRule(const Filter::Rule& rule,
log::FilterRule::StreamEncoder& encoder) {
PW_TRY(
encoder.WriteLevelGreaterThanOrEqual(rule.level_greater_than_or_equal));
PW_TRY(encoder.WriteModuleEquals(rule.module_equals));
PW_TRY(encoder.WriteAnyFlagsSet(rule.any_flags_set));
PW_TRY(encoder.WriteThreadEquals(rule.thread_equals));
return encoder.WriteAction(static_cast<log::FilterRule::Action>(rule.action));
}
Result<ConstByteSpan> EncodeFilter(const Filter& filter, ByteSpan buffer) {
log::Filter::MemoryEncoder encoder(buffer);
for (auto& rule : filter.rules()) {
log::FilterRule::StreamEncoder rule_encoder = encoder.GetRuleEncoder();
PW_TRY(EncodeFilterRule(rule, rule_encoder));
}
return ConstByteSpan(encoder);
}
void VerifyRule(const Filter::Rule& rule, const Filter::Rule& expected_rule) {
EXPECT_EQ(rule.level_greater_than_or_equal,
expected_rule.level_greater_than_or_equal);
EXPECT_EQ(rule.module_equals, expected_rule.module_equals);
EXPECT_EQ(rule.any_flags_set, expected_rule.any_flags_set);
EXPECT_EQ(rule.thread_equals, expected_rule.thread_equals);
EXPECT_EQ(rule.action, expected_rule.action);
}
TEST(FilterMap, RetrieveFiltersById) {
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id1{
std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id2{
std::byte(0xca), std::byte(0xfe), std::byte(0xc0), std::byte(0xc0)};
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id3{
std::byte(0xfa), std::byte(0xfe), std::byte(0xf1), std::byte(0xf0)};
std::array<Filter, 3> filters = {
Filter(filter_id1, {}),
Filter(filter_id2, {}),
Filter(filter_id3, {}),
};
FilterMap filter_map(filters);
// Check that each filters() element points to the same object provided.
span<const Filter> filter_list = filter_map.filters();
ASSERT_EQ(filter_list.size(), filters.size());
size_t i = 0;
for (auto& filter : filter_list) {
EXPECT_EQ(&filter, &filters[i++]);
}
auto filter_result = filter_map.GetFilterFromId(filter_id3);
ASSERT_TRUE(filter_result.ok());
EXPECT_EQ(filter_result.value(), &filters[2]);
filter_result = filter_map.GetFilterFromId(filter_id2);
ASSERT_TRUE(filter_result.ok());
EXPECT_EQ(filter_result.value(), &filters[1]);
filter_result = filter_map.GetFilterFromId(filter_id1);
ASSERT_TRUE(filter_result.ok());
EXPECT_EQ(filter_result.value(), &filters[0]);
const std::array<std::byte, cfg::kMaxFilterIdBytes> invalid_id{
std::byte(0xd0), std::byte(0x1c), std::byte(0xe7), std::byte(0xea)};
filter_result = filter_map.GetFilterFromId(invalid_id);
ASSERT_EQ(filter_result.status(), Status::NotFound());
}
TEST(Filter, UpdateFilterRules) {
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
std::array<Filter::Rule, 4> rules;
const std::array<Filter::Rule, 4> new_rules{{
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::DEBUG_LEVEL,
.any_flags_set = 0x0f,
.module_equals{std::byte(123)},
.thread_equals{},
},
{
.action = Filter::Rule::Action::kInactive,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0xef,
.module_equals{},
.thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
},
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = 0x1234,
.module_equals{std::byte(99)},
.thread_equals{},
},
{
.action = Filter::Rule::Action::kDrop,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0,
.module_equals{std::byte(4)},
.thread_equals{std::byte('P'),
std::byte('O'),
std::byte('W'),
std::byte('E'),
std::byte('R')},
},
}};
Filter filter(filter_id, rules);
const Filter new_filter(filter_id,
const_cast<std::array<Filter::Rule, 4>&>(new_rules));
std::byte buffer[256];
auto encode_result = EncodeFilter(new_filter, buffer);
ASSERT_EQ(encode_result.status(), OkStatus());
EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
size_t i = 0;
for (const auto& rule : filter.rules()) {
VerifyRule(rule, new_rules[i++]);
}
// A new filter with no rules should clear filter.
const Filter empty_filter(filter_id, {});
std::memset(buffer, 0, sizeof(buffer));
encode_result = EncodeFilter(empty_filter, buffer);
ASSERT_EQ(encode_result.status(), OkStatus());
EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
const Filter::Rule empty_rule{
.action = Filter::Rule::Action::kInactive,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0,
.module_equals{},
.thread_equals{},
};
for (const auto& rule : filter.rules()) {
VerifyRule(rule, empty_rule);
}
EXPECT_TRUE(empty_filter.rules().empty());
// Passing a new filter with less rules.
const std::array<Filter::Rule, 2> few_rules{{
{
.action = Filter::Rule::Action::kInactive,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0xef,
.module_equals{},
.thread_equals{},
},
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = 0x1234,
.module_equals{std::byte(99)},
.thread_equals{std::byte('P'),
std::byte('O'),
std::byte('W'),
std::byte('E'),
std::byte('R')},
},
}};
const Filter filter_few_rules(
filter_id, const_cast<std::array<Filter::Rule, 2>&>(few_rules));
std::memset(buffer, 0, sizeof(buffer));
encode_result = EncodeFilter(filter_few_rules, buffer);
ASSERT_EQ(encode_result.status(), OkStatus());
EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
i = 0;
for (const auto& rule : filter.rules()) {
if (i >= few_rules.size()) {
VerifyRule(rule, empty_rule);
} else {
VerifyRule(rule, few_rules[i++]);
}
}
// Passing a new filter with extra rules.
const std::array<Filter::Rule, 6> extra_rules{{
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::DEBUG_LEVEL,
.any_flags_set = 0x0f,
.module_equals{std::byte(123)},
.thread_equals{std::byte('P'),
std::byte('O'),
std::byte('W'),
std::byte('E'),
std::byte('R')},
},
{
.action = Filter::Rule::Action::kInactive,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0xef,
.module_equals{},
.thread_equals{},
},
{
.action = Filter::Rule::Action::kInactive,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0xef,
.module_equals{},
.thread_equals{},
},
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = 0x1234,
.module_equals{std::byte(99)},
.thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
},
{
.action = Filter::Rule::Action::kDrop,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0,
.module_equals{std::byte(4)},
.thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
},
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = 0x1234,
.module_equals{std::byte('M'),
std::byte('0'),
std::byte('L'),
std::byte('O'),
std::byte('G')},
.thread_equals{},
},
}};
const Filter filter_extra_rules(
filter_id, const_cast<std::array<Filter::Rule, 6>&>(extra_rules));
std::memset(buffer, 0, sizeof(buffer));
encode_result = EncodeFilter(filter_extra_rules, buffer);
ASSERT_EQ(encode_result.status(), OkStatus());
EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
i = 0;
for (const auto& rule : filter.rules()) {
VerifyRule(rule, extra_rules[i++]);
}
// A filter with no rules buffer cannot get rules updated.
Filter filter_no_rules(filter_id, {});
EXPECT_EQ(filter_no_rules.UpdateRulesFromProto(encode_result.value()),
Status::FailedPrecondition());
}
TEST(FilterTest, FilterLogsRuleDefaultDrop) {
const std::array<Filter::Rule, 2> rules{{
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = kSampleFlags,
.module_equals{kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
.thread_equals{kSampleThread.begin(), kSampleThread.end()},
},
// This rule catches all logs.
{
.action = Filter::Rule::Action::kDrop,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0,
.module_equals = {},
.thread_equals{},
},
}};
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
const Filter filter(filter_id,
const_cast<std::array<Filter::Rule, 2>&>(rules));
std::array<std::byte, 50> buffer;
const Result<ConstByteSpan> log_entry_info =
EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_info.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_debug =
EncodeLogEntry<PW_LOG_LEVEL_DEBUG, kSampleModule, kSampleFlags>(
kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_debug.status(), OkStatus());
EXPECT_TRUE(filter.ShouldDropLog(log_entry_debug.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_warn =
EncodeLogEntry<PW_LOG_LEVEL_WARN, kSampleModule, kSampleFlags>(
kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_warn.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_warn.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_error =
EncodeLogEntry<PW_LOG_LEVEL_ERROR, kSampleModule, kSampleFlags>(
kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_error.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_error.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_info_different =
EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_info_different.status(), OkStatus());
EXPECT_TRUE(filter.ShouldDropLog(log_entry_info_different.value()));
// Because the last rule catches all logs, the filter default action is not
// applied.
const Filter filter_default_drop(
filter_id, const_cast<std::array<Filter::Rule, 2>&>(rules));
EXPECT_TRUE(
filter_default_drop.ShouldDropLog(log_entry_info_different.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_flags =
EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_same_flags.status(), OkStatus());
EXPECT_TRUE(filter.ShouldDropLog(log_entry_same_flags.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_module =
EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_same_module.status(), OkStatus());
EXPECT_TRUE(filter.ShouldDropLog(log_entry_same_module.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_thread =
EncodeLogEntry<0, 0, 0>(kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_same_thread.status(), OkStatus());
EXPECT_TRUE(filter.ShouldDropLog(log_entry_same_thread.value()));
}
TEST(FilterTest, FilterLogsKeepLogsWhenNoRuleMatches) {
// There is no rule that catches all logs.
const std::array<Filter::Rule, 1> rules{{
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
.thread_equals = {kSampleThread.begin(), kSampleThread.end()},
},
}};
// Filters should not share rules if they are mutable, to avoid race
// conditions.
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
const Filter filter(filter_id,
const_cast<std::array<Filter::Rule, 1>&>(rules));
std::array<std::byte, 50> buffer;
const Result<ConstByteSpan> log_entry_info =
EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_info.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_debug =
EncodeLogEntry<PW_LOG_LEVEL_DEBUG, kSampleModule, kSampleFlags>(
kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_debug.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_debug.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_warn =
EncodeLogEntry<PW_LOG_LEVEL_WARN, kSampleModule, kSampleFlags>(
kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_warn.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_warn.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_error =
EncodeLogEntry<PW_LOG_LEVEL_ERROR, kSampleModule, kSampleFlags>(
kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_error.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_error.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_info_different =
EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_info_different.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_info_different.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_flags =
EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_same_flags.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_flags.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_module =
EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_same_module.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_module.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_thread =
EncodeLogEntry<0, 0, 0>(kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_same_thread.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_thread.value()));
}
TEST(FilterTest, FilterLogsKeepLogsWhenRulesEmpty) {
// Filters should not share rules if they are mutable, to avoid race
// conditions.
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
const Filter filter(filter_id, {});
std::array<std::byte, 50> buffer;
const Result<ConstByteSpan> log_entry_info =
EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_info.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_debug =
EncodeLogEntry<PW_LOG_LEVEL_DEBUG, kSampleModule, kSampleFlags>(
kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_debug.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_debug.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_warn =
EncodeLogEntry<PW_LOG_LEVEL_WARN, kSampleModule, kSampleFlags>(
kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_warn.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_warn.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_error =
EncodeLogEntry<PW_LOG_LEVEL_ERROR, kSampleModule, kSampleFlags>(
kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_error.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_error.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_info_different =
EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_info_different.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_info_different.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_flags =
EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_same_flags.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_flags.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_module =
EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_same_module.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_module.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_thread =
EncodeLogEntry<0, 0, 0>(kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_same_thread.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_thread.value()));
}
TEST(FilterTest, FilterLogsFirstRuleWins) {
const std::array<Filter::Rule, 2> rules{{
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
.thread_equals = {kSampleThread.begin(), kSampleThread.end()},
},
{
.action = Filter::Rule::Action::kDrop,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
.thread_equals = {kSampleThread.begin(), kSampleThread.end()},
},
}};
const std::array<Filter::Rule, 2> rules_reversed{{
{
.action = Filter::Rule::Action::kDrop,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
.thread_equals = {kSampleThread.begin(), kSampleThread.end()},
},
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
.thread_equals = {kSampleThread.begin(), kSampleThread.end()},
},
}};
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id1{
std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id2{
std::byte(0), std::byte(0), std::byte(0), std::byte(2)};
const Filter filter(filter_id1,
const_cast<std::array<Filter::Rule, 2>&>(rules));
const Filter filter_reverse_rules(
filter_id2, const_cast<std::array<Filter::Rule, 2>&>(rules_reversed));
std::array<std::byte, 50> buffer;
const Result<ConstByteSpan> log_entry_info =
EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_info.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
EXPECT_TRUE(filter_reverse_rules.ShouldDropLog(log_entry_info.value()));
}
TEST(FilterTest, DropFilterRuleDueToThreadName) {
const std::array<std::byte, cfg::kMaxThreadNameBytes - 7> kDropThread = {
std::byte('L'), std::byte('O'), std::byte('G')};
const std::array<Filter::Rule, 2> rules{{
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
.thread_equals = {kDropThread.begin(), kDropThread.end()},
},
{
.action = Filter::Rule::Action::kDrop,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
.thread_equals = {kSampleThread.begin(), kSampleThread.end()},
},
}};
const std::array<Filter::Rule, 2> drop_rule{{
{
.action = Filter::Rule::Action::kDrop,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
.thread_equals = {kDropThread.begin(), kDropThread.end()},
},
}};
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id1{
std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
// A filter's thread_equals name that does and does not match the log's thread
// name.
const Filter filter(filter_id1,
const_cast<std::array<Filter::Rule, 2>&>(rules));
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id2{
std::byte(0), std::byte(0), std::byte(0), std::byte(2)};
// A filter's thread_equals name that does not match the log's thread name.
const Filter filter_with_unregistered_filter_rule(
filter_id2, const_cast<std::array<Filter::Rule, 2>&>(drop_rule));
std::array<std::byte, 50> buffer;
const Result<ConstByteSpan> log_entry_thread =
EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_thread.status(), OkStatus());
// Set filter rules to kDrop to showcase the output difference.
// Drop_rule not being dropped, while rules is dropped successfully.
EXPECT_TRUE(filter.ShouldDropLog(log_entry_thread.value()));
EXPECT_FALSE(filter_with_unregistered_filter_rule.ShouldDropLog(
log_entry_thread.value()));
}
TEST(FilterTest, UpdateFilterWithLargeThreadNamePasses) {
// Threads are limited to a size of kMaxThreadNameBytes.
// However, the excess bytes will not be in the updated rules.
const std::array<std::byte, cfg::kMaxThreadNameBytes + 1>
kThreadNameLongerThanAllowed = {
std::byte('L'),
std::byte('O'),
std::byte('C'),
std::byte('A'),
std::byte('L'),
std::byte('E'),
std::byte('G'),
std::byte('R'),
std::byte('E'),
std::byte('S'),
std::byte('S'),
};
const std::array<Filter::Rule, 2> rule{{
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
.thread_equals = {kThreadNameLongerThanAllowed.begin(),
kThreadNameLongerThanAllowed.end()},
},
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
.thread_equals = {kSampleThread.begin(), kSampleThread.end()},
},
}};
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
Filter filter(filter_id, const_cast<std::array<Filter::Rule, 2>&>(rule));
std::byte buffer[256];
auto encode_result = EncodeFilter(filter, buffer);
ASSERT_EQ(encode_result.status(), OkStatus());
EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
size_t i = 0;
for (const auto& rules : filter.rules()) {
VerifyRule(rules, rule[i++]);
}
}
TEST(FilterTest, UpdateFilterWithLargeThreadNameFails) {
const std::array<Filter::Rule, 1> rule_with_more_than_ten_bytes{{{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
.thread_equals = {kSampleThread.begin(), kSampleThread.end()},
}}};
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
Filter filter(
filter_id,
const_cast<std::array<Filter::Rule, 1>&>(rule_with_more_than_ten_bytes));
std::byte buffer[256];
log::Filter::MemoryEncoder encoder(buffer);
{
std::array<const std::byte, cfg::kMaxThreadNameBytes + 1>
kThreadNameLongerThanAllowed = {
std::byte('L'),
std::byte('O'),
std::byte('C'),
std::byte('A'),
std::byte('L'),
std::byte('E'),
std::byte('G'),
std::byte('R'),
std::byte('E'),
std::byte('S'),
std::byte('S'),
};
// Stream encoder writes to the buffer when it goes out of scope.
log::FilterRule::StreamEncoder rule_encoder = encoder.GetRuleEncoder();
ASSERT_EQ(rule_encoder.WriteThreadEquals(kThreadNameLongerThanAllowed),
OkStatus());
}
EXPECT_EQ(filter.UpdateRulesFromProto(ConstByteSpan(encoder)),
Status::InvalidArgument());
}
} // namespace
} // namespace pw::log_rpc