• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_log_rpc/log_filter.h"
16 
17 #include <array>
18 #include <cstddef>
19 #include <cstring>
20 
21 #include "pw_bytes/endian.h"
22 #include "pw_log/levels.h"
23 #include "pw_log/log.h"
24 #include "pw_log/proto/log.pwpb.h"
25 #include "pw_log/proto_utils.h"
26 #include "pw_log_rpc/log_filter_map.h"
27 #include "pw_log_tokenized/metadata.h"
28 #include "pw_result/result.h"
29 #include "pw_status/status.h"
30 #include "pw_status/try.h"
31 #include "pw_unit_test/framework.h"
32 
33 namespace pw::log_rpc {
34 namespace {
35 
36 namespace FilterRule = ::pw::log::pwpb::FilterRule;
37 
38 constexpr uint32_t kSampleModule = 0x1234;
39 constexpr uint32_t kSampleFlags = 0x3;
40 const std::array<std::byte, cfg::kMaxThreadNameBytes - 7> kSampleThread = {
41     std::byte('R'), std::byte('P'), std::byte('C')};
42 constexpr char kSampleMessage[] = "message";
43 constexpr auto kSampleModuleLittleEndian =
44     bytes::CopyInOrder<uint32_t>(endian::little, kSampleModule);
45 
46 // Creates and encodes a log entry in the provided buffer.
47 template <uintptr_t log_level, uintptr_t module, uintptr_t flags>
EncodeLogEntry(std::string_view message,ByteSpan buffer,ConstByteSpan thread)48 Result<ConstByteSpan> EncodeLogEntry(std::string_view message,
49                                      ByteSpan buffer,
50                                      ConstByteSpan thread) {
51   auto metadata = log_tokenized::Metadata::Set<log_level, module, flags, 0>();
52   return log::EncodeTokenizedLog(metadata,
53                                  as_bytes(span<const char>(message)),
54                                  /*ticks_since_epoch=*/0,
55                                  thread,
56                                  buffer);
57 }
58 
EncodeFilterRule(const Filter::Rule & rule,FilterRule::StreamEncoder & encoder)59 Status EncodeFilterRule(const Filter::Rule& rule,
60                         FilterRule::StreamEncoder& encoder) {
61   PW_TRY(
62       encoder.WriteLevelGreaterThanOrEqual(rule.level_greater_than_or_equal));
63   PW_TRY(encoder.WriteModuleEquals(rule.module_equals));
64   PW_TRY(encoder.WriteAnyFlagsSet(rule.any_flags_set));
65   PW_TRY(encoder.WriteThreadEquals(rule.thread_equals));
66   return encoder.WriteAction(static_cast<FilterRule::Action>(rule.action));
67 }
68 
EncodeFilter(const Filter & filter,ByteSpan buffer)69 Result<ConstByteSpan> EncodeFilter(const Filter& filter, ByteSpan buffer) {
70   log::pwpb::Filter::MemoryEncoder encoder(buffer);
71   for (auto& rule : filter.rules()) {
72     FilterRule::StreamEncoder rule_encoder = encoder.GetRuleEncoder();
73     PW_TRY(EncodeFilterRule(rule, rule_encoder));
74   }
75   return ConstByteSpan(encoder);
76 }
77 
VerifyRule(const Filter::Rule & rule,const Filter::Rule & expected_rule)78 void VerifyRule(const Filter::Rule& rule, const Filter::Rule& expected_rule) {
79   EXPECT_EQ(rule.level_greater_than_or_equal,
80             expected_rule.level_greater_than_or_equal);
81   EXPECT_EQ(rule.module_equals, expected_rule.module_equals);
82   EXPECT_EQ(rule.any_flags_set, expected_rule.any_flags_set);
83   EXPECT_EQ(rule.thread_equals, expected_rule.thread_equals);
84   EXPECT_EQ(rule.action, expected_rule.action);
85 }
86 
TEST(FilterMap,RetrieveFiltersById)87 TEST(FilterMap, RetrieveFiltersById) {
88   const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id1{
89       std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
90   const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id2{
91       std::byte(0xca), std::byte(0xfe), std::byte(0xc0), std::byte(0xc0)};
92   const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id3{
93       std::byte(0xfa), std::byte(0xfe), std::byte(0xf1), std::byte(0xf0)};
94   std::array<Filter, 3> filters = {
95       Filter(filter_id1, {}),
96       Filter(filter_id2, {}),
97       Filter(filter_id3, {}),
98   };
99 
100   FilterMap filter_map(filters);
101 
102   // Check that each filters() element points to the same object provided.
103   span<const Filter> filter_list = filter_map.filters();
104   ASSERT_EQ(filter_list.size(), filters.size());
105   size_t i = 0;
106   for (auto& filter : filter_list) {
107     EXPECT_EQ(&filter, &filters[i++]);
108   }
109 
110   auto filter_result = filter_map.GetFilterFromId(filter_id3);
111   ASSERT_TRUE(filter_result.ok());
112   EXPECT_EQ(filter_result.value(), &filters[2]);
113 
114   filter_result = filter_map.GetFilterFromId(filter_id2);
115   ASSERT_TRUE(filter_result.ok());
116   EXPECT_EQ(filter_result.value(), &filters[1]);
117 
118   filter_result = filter_map.GetFilterFromId(filter_id1);
119   ASSERT_TRUE(filter_result.ok());
120   EXPECT_EQ(filter_result.value(), &filters[0]);
121 
122   const std::array<std::byte, cfg::kMaxFilterIdBytes> invalid_id{
123       std::byte(0xd0), std::byte(0x1c), std::byte(0xe7), std::byte(0xea)};
124   filter_result = filter_map.GetFilterFromId(invalid_id);
125   ASSERT_EQ(filter_result.status(), Status::NotFound());
126 }
127 
TEST(Filter,UpdateFilterRules)128 TEST(Filter, UpdateFilterRules) {
129   const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
130       std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
131   std::array<Filter::Rule, 4> rules;
132   const std::array<Filter::Rule, 4> new_rules{{
133       {
134           .action = Filter::Rule::Action::kKeep,
135           .level_greater_than_or_equal = FilterRule::Level::DEBUG_LEVEL,
136           .any_flags_set = 0x0f,
137           .module_equals{std::byte(123)},
138           .thread_equals{},
139       },
140       {
141           .action = Filter::Rule::Action::kInactive,
142           .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
143           .any_flags_set = 0xef,
144           .module_equals{},
145           .thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
146       },
147       {
148           .action = Filter::Rule::Action::kKeep,
149           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
150           .any_flags_set = 0x1234,
151           .module_equals{std::byte(99)},
152           .thread_equals{},
153       },
154       {
155           .action = Filter::Rule::Action::kDrop,
156           .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
157           .any_flags_set = 0,
158           .module_equals{std::byte(4)},
159           .thread_equals{std::byte('P'),
160                          std::byte('O'),
161                          std::byte('W'),
162                          std::byte('E'),
163                          std::byte('R')},
164       },
165   }};
166 
167   Filter filter(filter_id, rules);
168   const Filter new_filter(filter_id,
169                           const_cast<std::array<Filter::Rule, 4>&>(new_rules));
170   std::byte buffer[256];
171   auto encode_result = EncodeFilter(new_filter, buffer);
172   ASSERT_EQ(encode_result.status(), OkStatus());
173   EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
174 
175   size_t i = 0;
176   for (const auto& rule : filter.rules()) {
177     VerifyRule(rule, new_rules[i++]);
178   }
179 
180   // A new filter with no rules should clear filter.
181   const Filter empty_filter(filter_id, {});
182   std::memset(buffer, 0, sizeof(buffer));
183   encode_result = EncodeFilter(empty_filter, buffer);
184   ASSERT_EQ(encode_result.status(), OkStatus());
185   EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
186   const Filter::Rule empty_rule{
187       .action = Filter::Rule::Action::kInactive,
188       .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
189       .any_flags_set = 0,
190       .module_equals{},
191       .thread_equals{},
192   };
193   for (const auto& rule : filter.rules()) {
194     VerifyRule(rule, empty_rule);
195   }
196   EXPECT_TRUE(empty_filter.rules().empty());
197 
198   // Passing a new filter with less rules.
199   const std::array<Filter::Rule, 2> few_rules{{
200       {
201           .action = Filter::Rule::Action::kInactive,
202           .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
203           .any_flags_set = 0xef,
204           .module_equals{},
205           .thread_equals{},
206       },
207       {
208           .action = Filter::Rule::Action::kKeep,
209           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
210           .any_flags_set = 0x1234,
211           .module_equals{std::byte(99)},
212           .thread_equals{std::byte('P'),
213                          std::byte('O'),
214                          std::byte('W'),
215                          std::byte('E'),
216                          std::byte('R')},
217       },
218   }};
219   const Filter filter_few_rules(
220       filter_id, const_cast<std::array<Filter::Rule, 2>&>(few_rules));
221   std::memset(buffer, 0, sizeof(buffer));
222   encode_result = EncodeFilter(filter_few_rules, buffer);
223   ASSERT_EQ(encode_result.status(), OkStatus());
224   EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
225   i = 0;
226   for (const auto& rule : filter.rules()) {
227     if (i >= few_rules.size()) {
228       VerifyRule(rule, empty_rule);
229     } else {
230       VerifyRule(rule, few_rules[i++]);
231     }
232   }
233 
234   // Passing a new filter with extra rules.
235   const std::array<Filter::Rule, 6> extra_rules{{
236       {
237           .action = Filter::Rule::Action::kKeep,
238           .level_greater_than_or_equal = FilterRule::Level::DEBUG_LEVEL,
239           .any_flags_set = 0x0f,
240           .module_equals{std::byte(123)},
241           .thread_equals{std::byte('P'),
242                          std::byte('O'),
243                          std::byte('W'),
244                          std::byte('E'),
245                          std::byte('R')},
246       },
247       {
248           .action = Filter::Rule::Action::kInactive,
249           .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
250           .any_flags_set = 0xef,
251           .module_equals{},
252           .thread_equals{},
253       },
254       {
255           .action = Filter::Rule::Action::kInactive,
256           .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
257           .any_flags_set = 0xef,
258           .module_equals{},
259           .thread_equals{},
260       },
261       {
262           .action = Filter::Rule::Action::kKeep,
263           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
264           .any_flags_set = 0x1234,
265           .module_equals{std::byte(99)},
266           .thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
267       },
268       {
269           .action = Filter::Rule::Action::kDrop,
270           .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
271           .any_flags_set = 0,
272           .module_equals{std::byte(4)},
273           .thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
274       },
275       {
276           .action = Filter::Rule::Action::kKeep,
277           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
278           .any_flags_set = 0x1234,
279           .module_equals{std::byte('M'),
280                          std::byte('0'),
281                          std::byte('L'),
282                          std::byte('O'),
283                          std::byte('G')},
284           .thread_equals{},
285       },
286   }};
287   const Filter filter_extra_rules(
288       filter_id, const_cast<std::array<Filter::Rule, 6>&>(extra_rules));
289   std::memset(buffer, 0, sizeof(buffer));
290   encode_result = EncodeFilter(filter_extra_rules, buffer);
291   ASSERT_EQ(encode_result.status(), OkStatus());
292   EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
293   i = 0;
294   for (const auto& rule : filter.rules()) {
295     VerifyRule(rule, extra_rules[i++]);
296   }
297 
298   // A filter with no rules buffer cannot get rules updated.
299   Filter filter_no_rules(filter_id, {});
300   EXPECT_EQ(filter_no_rules.UpdateRulesFromProto(encode_result.value()),
301             Status::FailedPrecondition());
302 }
303 
TEST(FilterTest,FilterLogsRuleDefaultDrop)304 TEST(FilterTest, FilterLogsRuleDefaultDrop) {
305   const std::array<Filter::Rule, 2> rules{{
306       {
307           .action = Filter::Rule::Action::kKeep,
308           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
309           .any_flags_set = kSampleFlags,
310           .module_equals{kSampleModuleLittleEndian.begin(),
311                          kSampleModuleLittleEndian.end()},
312           .thread_equals{kSampleThread.begin(), kSampleThread.end()},
313       },
314       // This rule catches all logs.
315       {
316           .action = Filter::Rule::Action::kDrop,
317           .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
318           .any_flags_set = 0,
319           .module_equals = {},
320           .thread_equals{},
321       },
322   }};
323   const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
324       std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
325   const Filter filter(filter_id,
326                       const_cast<std::array<Filter::Rule, 2>&>(rules));
327 
328   std::array<std::byte, 50> buffer;
329   const Result<ConstByteSpan> log_entry_info =
330       EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
331           kSampleMessage, buffer, kSampleThread);
332   ASSERT_EQ(log_entry_info.status(), OkStatus());
333   EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
334 
335   buffer.fill(std::byte(0));
336   const Result<ConstByteSpan> log_entry_debug =
337       EncodeLogEntry<PW_LOG_LEVEL_DEBUG, kSampleModule, kSampleFlags>(
338           kSampleMessage, buffer, kSampleThread);
339   ASSERT_EQ(log_entry_debug.status(), OkStatus());
340   EXPECT_TRUE(filter.ShouldDropLog(log_entry_debug.value()));
341 
342   buffer.fill(std::byte(0));
343   const Result<ConstByteSpan> log_entry_warn =
344       EncodeLogEntry<PW_LOG_LEVEL_WARN, kSampleModule, kSampleFlags>(
345           kSampleMessage, buffer, kSampleThread);
346   ASSERT_EQ(log_entry_warn.status(), OkStatus());
347   EXPECT_FALSE(filter.ShouldDropLog(log_entry_warn.value()));
348 
349   buffer.fill(std::byte(0));
350   const Result<ConstByteSpan> log_entry_error =
351       EncodeLogEntry<PW_LOG_LEVEL_ERROR, kSampleModule, kSampleFlags>(
352           kSampleMessage, buffer, kSampleThread);
353   ASSERT_EQ(log_entry_error.status(), OkStatus());
354   EXPECT_FALSE(filter.ShouldDropLog(log_entry_error.value()));
355 
356   buffer.fill(std::byte(0));
357   const Result<ConstByteSpan> log_entry_info_different =
358       EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer, {});
359   ASSERT_EQ(log_entry_info_different.status(), OkStatus());
360   EXPECT_TRUE(filter.ShouldDropLog(log_entry_info_different.value()));
361   // Because the last rule catches all logs, the filter default action is not
362   // applied.
363   const Filter filter_default_drop(
364       filter_id, const_cast<std::array<Filter::Rule, 2>&>(rules));
365   EXPECT_TRUE(
366       filter_default_drop.ShouldDropLog(log_entry_info_different.value()));
367 
368   buffer.fill(std::byte(0));
369   const Result<ConstByteSpan> log_entry_same_flags =
370       EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer, {});
371   ASSERT_EQ(log_entry_same_flags.status(), OkStatus());
372   EXPECT_TRUE(filter.ShouldDropLog(log_entry_same_flags.value()));
373 
374   buffer.fill(std::byte(0));
375   const Result<ConstByteSpan> log_entry_same_module =
376       EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer, {});
377   ASSERT_EQ(log_entry_same_module.status(), OkStatus());
378   EXPECT_TRUE(filter.ShouldDropLog(log_entry_same_module.value()));
379 
380   buffer.fill(std::byte(0));
381   const Result<ConstByteSpan> log_entry_same_thread =
382       EncodeLogEntry<0, 0, 0>(kSampleMessage, buffer, kSampleThread);
383   ASSERT_EQ(log_entry_same_thread.status(), OkStatus());
384   EXPECT_TRUE(filter.ShouldDropLog(log_entry_same_thread.value()));
385 }
386 
TEST(FilterTest,FilterLogsKeepLogsWhenNoRuleMatches)387 TEST(FilterTest, FilterLogsKeepLogsWhenNoRuleMatches) {
388   // There is no rule that catches all logs.
389   const std::array<Filter::Rule, 1> rules{{
390       {
391           .action = Filter::Rule::Action::kKeep,
392           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
393           .any_flags_set = kSampleFlags,
394           .module_equals = {kSampleModuleLittleEndian.begin(),
395                             kSampleModuleLittleEndian.end()},
396           .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
397       },
398   }};
399 
400   // Filters should not share rules if they are mutable, to avoid race
401   // conditions.
402   const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
403       std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
404   const Filter filter(filter_id,
405                       const_cast<std::array<Filter::Rule, 1>&>(rules));
406 
407   std::array<std::byte, 50> buffer;
408   const Result<ConstByteSpan> log_entry_info =
409       EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
410           kSampleMessage, buffer, kSampleThread);
411   ASSERT_EQ(log_entry_info.status(), OkStatus());
412   EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
413 
414   buffer.fill(std::byte(0));
415   const Result<ConstByteSpan> log_entry_debug =
416       EncodeLogEntry<PW_LOG_LEVEL_DEBUG, kSampleModule, kSampleFlags>(
417           kSampleMessage, buffer, kSampleThread);
418   ASSERT_EQ(log_entry_debug.status(), OkStatus());
419   EXPECT_FALSE(filter.ShouldDropLog(log_entry_debug.value()));
420 
421   buffer.fill(std::byte(0));
422   const Result<ConstByteSpan> log_entry_warn =
423       EncodeLogEntry<PW_LOG_LEVEL_WARN, kSampleModule, kSampleFlags>(
424           kSampleMessage, buffer, kSampleThread);
425   ASSERT_EQ(log_entry_warn.status(), OkStatus());
426   EXPECT_FALSE(filter.ShouldDropLog(log_entry_warn.value()));
427 
428   buffer.fill(std::byte(0));
429   const Result<ConstByteSpan> log_entry_error =
430       EncodeLogEntry<PW_LOG_LEVEL_ERROR, kSampleModule, kSampleFlags>(
431           kSampleMessage, buffer, kSampleThread);
432   ASSERT_EQ(log_entry_error.status(), OkStatus());
433   EXPECT_FALSE(filter.ShouldDropLog(log_entry_error.value()));
434 
435   buffer.fill(std::byte(0));
436   const Result<ConstByteSpan> log_entry_info_different =
437       EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer, {});
438   ASSERT_EQ(log_entry_info_different.status(), OkStatus());
439   EXPECT_FALSE(filter.ShouldDropLog(log_entry_info_different.value()));
440 
441   buffer.fill(std::byte(0));
442   const Result<ConstByteSpan> log_entry_same_flags =
443       EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer, {});
444   ASSERT_EQ(log_entry_same_flags.status(), OkStatus());
445   EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_flags.value()));
446 
447   buffer.fill(std::byte(0));
448   const Result<ConstByteSpan> log_entry_same_module =
449       EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer, {});
450   ASSERT_EQ(log_entry_same_module.status(), OkStatus());
451   EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_module.value()));
452 
453   buffer.fill(std::byte(0));
454   const Result<ConstByteSpan> log_entry_same_thread =
455       EncodeLogEntry<0, 0, 0>(kSampleMessage, buffer, kSampleThread);
456   ASSERT_EQ(log_entry_same_thread.status(), OkStatus());
457   EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_thread.value()));
458 }
459 
TEST(FilterTest,FilterLogsKeepLogsWhenRulesEmpty)460 TEST(FilterTest, FilterLogsKeepLogsWhenRulesEmpty) {
461   // Filters should not share rules if they are mutable, to avoid race
462   // conditions.
463   const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
464       std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
465   const Filter filter(filter_id, {});
466 
467   std::array<std::byte, 50> buffer;
468   const Result<ConstByteSpan> log_entry_info =
469       EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
470           kSampleMessage, buffer, kSampleThread);
471   ASSERT_EQ(log_entry_info.status(), OkStatus());
472   EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
473 
474   buffer.fill(std::byte(0));
475   const Result<ConstByteSpan> log_entry_debug =
476       EncodeLogEntry<PW_LOG_LEVEL_DEBUG, kSampleModule, kSampleFlags>(
477           kSampleMessage, buffer, kSampleThread);
478   ASSERT_EQ(log_entry_debug.status(), OkStatus());
479   EXPECT_FALSE(filter.ShouldDropLog(log_entry_debug.value()));
480 
481   buffer.fill(std::byte(0));
482   const Result<ConstByteSpan> log_entry_warn =
483       EncodeLogEntry<PW_LOG_LEVEL_WARN, kSampleModule, kSampleFlags>(
484           kSampleMessage, buffer, kSampleThread);
485   ASSERT_EQ(log_entry_warn.status(), OkStatus());
486   EXPECT_FALSE(filter.ShouldDropLog(log_entry_warn.value()));
487 
488   buffer.fill(std::byte(0));
489   const Result<ConstByteSpan> log_entry_error =
490       EncodeLogEntry<PW_LOG_LEVEL_ERROR, kSampleModule, kSampleFlags>(
491           kSampleMessage, buffer, kSampleThread);
492   ASSERT_EQ(log_entry_error.status(), OkStatus());
493   EXPECT_FALSE(filter.ShouldDropLog(log_entry_error.value()));
494 
495   buffer.fill(std::byte(0));
496   const Result<ConstByteSpan> log_entry_info_different =
497       EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer, {});
498   ASSERT_EQ(log_entry_info_different.status(), OkStatus());
499   EXPECT_FALSE(filter.ShouldDropLog(log_entry_info_different.value()));
500 
501   buffer.fill(std::byte(0));
502   const Result<ConstByteSpan> log_entry_same_flags =
503       EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer, {});
504   ASSERT_EQ(log_entry_same_flags.status(), OkStatus());
505   EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_flags.value()));
506 
507   buffer.fill(std::byte(0));
508   const Result<ConstByteSpan> log_entry_same_module =
509       EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer, {});
510   ASSERT_EQ(log_entry_same_module.status(), OkStatus());
511   EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_module.value()));
512 
513   buffer.fill(std::byte(0));
514   const Result<ConstByteSpan> log_entry_same_thread =
515       EncodeLogEntry<0, 0, 0>(kSampleMessage, buffer, kSampleThread);
516   ASSERT_EQ(log_entry_same_thread.status(), OkStatus());
517   EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_thread.value()));
518 }
519 
TEST(FilterTest,FilterLogsFirstRuleWins)520 TEST(FilterTest, FilterLogsFirstRuleWins) {
521   const std::array<Filter::Rule, 2> rules{{
522       {
523           .action = Filter::Rule::Action::kKeep,
524           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
525           .any_flags_set = kSampleFlags,
526           .module_equals = {kSampleModuleLittleEndian.begin(),
527                             kSampleModuleLittleEndian.end()},
528           .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
529       },
530       {
531           .action = Filter::Rule::Action::kDrop,
532           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
533           .any_flags_set = kSampleFlags,
534           .module_equals = {kSampleModuleLittleEndian.begin(),
535                             kSampleModuleLittleEndian.end()},
536           .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
537       },
538   }};
539   const std::array<Filter::Rule, 2> rules_reversed{{
540       {
541           .action = Filter::Rule::Action::kDrop,
542           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
543           .any_flags_set = kSampleFlags,
544           .module_equals = {kSampleModuleLittleEndian.begin(),
545                             kSampleModuleLittleEndian.end()},
546           .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
547       },
548       {
549           .action = Filter::Rule::Action::kKeep,
550           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
551           .any_flags_set = kSampleFlags,
552           .module_equals = {kSampleModuleLittleEndian.begin(),
553                             kSampleModuleLittleEndian.end()},
554           .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
555       },
556   }};
557   const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id1{
558       std::byte(0xfe), std::byte(0xed), std::byte(0xba), std::byte(0xb1)};
559   const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id2{
560       std::byte(0), std::byte(0), std::byte(0), std::byte(2)};
561   const Filter filter(filter_id1,
562                       const_cast<std::array<Filter::Rule, 2>&>(rules));
563   const Filter filter_reverse_rules(
564       filter_id2, const_cast<std::array<Filter::Rule, 2>&>(rules_reversed));
565 
566   std::array<std::byte, 50> buffer;
567   const Result<ConstByteSpan> log_entry_info =
568       EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
569           kSampleMessage, buffer, kSampleThread);
570   ASSERT_EQ(log_entry_info.status(), OkStatus());
571   EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
572   EXPECT_TRUE(filter_reverse_rules.ShouldDropLog(log_entry_info.value()));
573 }
574 
TEST(FilterTest,DropFilterRuleDueToThreadName)575 TEST(FilterTest, DropFilterRuleDueToThreadName) {
576   const std::array<std::byte, cfg::kMaxThreadNameBytes - 7> kDropThread = {
577       std::byte('L'), std::byte('O'), std::byte('G')};
578   const std::array<Filter::Rule, 2> rules{{
579       {
580           .action = Filter::Rule::Action::kKeep,
581           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
582           .any_flags_set = kSampleFlags,
583           .module_equals = {kSampleModuleLittleEndian.begin(),
584                             kSampleModuleLittleEndian.end()},
585           .thread_equals = {kDropThread.begin(), kDropThread.end()},
586       },
587       {
588           .action = Filter::Rule::Action::kDrop,
589           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
590           .any_flags_set = kSampleFlags,
591           .module_equals = {kSampleModuleLittleEndian.begin(),
592                             kSampleModuleLittleEndian.end()},
593           .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
594       },
595   }};
596 
597   const std::array<Filter::Rule, 2> drop_rule{{
598       {
599           .action = Filter::Rule::Action::kDrop,
600           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
601           .any_flags_set = kSampleFlags,
602           .module_equals = {kSampleModuleLittleEndian.begin(),
603                             kSampleModuleLittleEndian.end()},
604           .thread_equals = {kDropThread.begin(), kDropThread.end()},
605       },
606   }};
607 
608   const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id1{
609       std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
610   // A filter's thread_equals name that does and does not match the log's thread
611   // name.
612   const Filter filter(filter_id1,
613                       const_cast<std::array<Filter::Rule, 2>&>(rules));
614   const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id2{
615       std::byte(0), std::byte(0), std::byte(0), std::byte(2)};
616   // A filter's thread_equals name that does not match the log's thread name.
617   const Filter filter_with_unregistered_filter_rule(
618       filter_id2, const_cast<std::array<Filter::Rule, 2>&>(drop_rule));
619   std::array<std::byte, 50> buffer;
620   const Result<ConstByteSpan> log_entry_thread =
621       EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
622           kSampleMessage, buffer, kSampleThread);
623   ASSERT_EQ(log_entry_thread.status(), OkStatus());
624   // Set filter rules to kDrop to showcase the output difference.
625   // Drop_rule not being dropped, while rules is dropped successfully.
626   EXPECT_TRUE(filter.ShouldDropLog(log_entry_thread.value()));
627   EXPECT_FALSE(filter_with_unregistered_filter_rule.ShouldDropLog(
628       log_entry_thread.value()));
629 }
630 
TEST(FilterTest,UpdateFilterWithLargeThreadNamePasses)631 TEST(FilterTest, UpdateFilterWithLargeThreadNamePasses) {
632   // Threads are limited to a size of kMaxThreadNameBytes.
633   // However, the excess bytes will not be in the updated rules.
634   const std::array<std::byte, cfg::kMaxThreadNameBytes + 1>
635       kThreadNameLongerThanAllowed = {
636           std::byte('L'),
637           std::byte('O'),
638           std::byte('C'),
639           std::byte('A'),
640           std::byte('L'),
641           std::byte('E'),
642           std::byte('G'),
643           std::byte('R'),
644           std::byte('E'),
645           std::byte('S'),
646           std::byte('S'),
647       };
648 
649   const std::array<Filter::Rule, 2> rule{{
650       {
651           .action = Filter::Rule::Action::kKeep,
652           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
653           .any_flags_set = kSampleFlags,
654           .module_equals = {kSampleModuleLittleEndian.begin(),
655                             kSampleModuleLittleEndian.end()},
656           .thread_equals = {kThreadNameLongerThanAllowed.begin(),
657                             kThreadNameLongerThanAllowed.end()},
658       },
659       {
660           .action = Filter::Rule::Action::kKeep,
661           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
662           .any_flags_set = kSampleFlags,
663           .module_equals = {kSampleModuleLittleEndian.begin(),
664                             kSampleModuleLittleEndian.end()},
665           .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
666       },
667   }};
668 
669   const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
670       std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
671   Filter filter(filter_id, const_cast<std::array<Filter::Rule, 2>&>(rule));
672   std::byte buffer[256];
673   auto encode_result = EncodeFilter(filter, buffer);
674   ASSERT_EQ(encode_result.status(), OkStatus());
675   EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
676   size_t i = 0;
677   for (const auto& rules : filter.rules()) {
678     VerifyRule(rules, rule[i++]);
679   }
680 }
681 
TEST(FilterTest,UpdateFilterWithLargeThreadNameFails)682 TEST(FilterTest, UpdateFilterWithLargeThreadNameFails) {
683   const std::array<Filter::Rule, 1> rule_with_more_than_ten_bytes{{{
684       .action = Filter::Rule::Action::kKeep,
685       .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
686       .any_flags_set = kSampleFlags,
687       .module_equals = {kSampleModuleLittleEndian.begin(),
688                         kSampleModuleLittleEndian.end()},
689       .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
690   }}};
691   const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
692       std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
693   Filter filter(
694       filter_id,
695       const_cast<std::array<Filter::Rule, 1>&>(rule_with_more_than_ten_bytes));
696   std::byte buffer[256];
697   log::pwpb::Filter::MemoryEncoder encoder(buffer);
698   {
699     std::array<const std::byte, cfg::kMaxThreadNameBytes + 1>
700         kThreadNameLongerThanAllowed = {
701             std::byte('L'),
702             std::byte('O'),
703             std::byte('C'),
704             std::byte('A'),
705             std::byte('L'),
706             std::byte('E'),
707             std::byte('G'),
708             std::byte('R'),
709             std::byte('E'),
710             std::byte('S'),
711             std::byte('S'),
712         };
713     // Stream encoder writes to the buffer when it goes out of scope.
714     FilterRule::StreamEncoder rule_encoder = encoder.GetRuleEncoder();
715     ASSERT_EQ(rule_encoder.WriteThreadEquals(kThreadNameLongerThanAllowed),
716               OkStatus());
717   }
718   EXPECT_EQ(filter.UpdateRulesFromProto(ConstByteSpan(encoder)),
719             Status::InvalidArgument());
720 }
721 }  // namespace
722 }  // namespace pw::log_rpc
723