• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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_service.h"
16 
17 #include <array>
18 #include <cstdint>
19 #include <limits>
20 
21 #include "pw_bytes/endian.h"
22 #include "pw_log/proto/log.pwpb.h"
23 #include "pw_log_rpc/log_filter.h"
24 #include "pw_log_rpc/log_filter_map.h"
25 #include "pw_protobuf/bytes_utils.h"
26 #include "pw_protobuf/decoder.h"
27 #include "pw_result/result.h"
28 #include "pw_rpc/channel.h"
29 #include "pw_rpc/raw/test_method_context.h"
30 #include "pw_unit_test/framework.h"
31 
32 namespace pw::log_rpc {
33 namespace {
34 
35 namespace FilterRule = ::pw::log::pwpb::FilterRule;
36 namespace GetFilterRequest = ::pw::log::pwpb::GetFilterRequest;
37 namespace SetFilterRequest = ::pw::log::pwpb::SetFilterRequest;
38 
39 class FilterServiceTest : public ::testing::Test {
40  public:
FilterServiceTest()41   FilterServiceTest() : filter_map_(filters_) {}
42 
43  protected:
44   static constexpr size_t kMaxFilterRules = 4;
45   std::array<Filter::Rule, kMaxFilterRules> rules1_;
46   std::array<Filter::Rule, kMaxFilterRules> rules2_;
47   std::array<Filter::Rule, kMaxFilterRules> rules3_;
48   static constexpr std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id1_{
49       std::byte(65), std::byte(66), std::byte(67), std::byte(0)};
50   static constexpr std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id2_{
51       std::byte(68), std::byte(69), std::byte(70), std::byte(0)};
52   static constexpr std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id3_{
53       std::byte(71), std::byte(72), std::byte(73), std::byte(0)};
54   static constexpr size_t kMaxFilters = 3;
55   std::array<Filter, kMaxFilters> filters_ = {
56       Filter(filter_id1_, rules1_),
57       Filter(filter_id2_, rules2_),
58       Filter(filter_id3_, rules3_),
59   };
60   FilterMap filter_map_;
61 };
62 
TEST_F(FilterServiceTest,GetFilterIds)63 TEST_F(FilterServiceTest, GetFilterIds) {
64   PW_RAW_TEST_METHOD_CONTEXT(FilterService, ListFilterIds, 1)
65   context(filter_map_);
66   context.call({});
67   ASSERT_EQ(OkStatus(), context.status());
68   ASSERT_TRUE(context.done());
69   ASSERT_EQ(context.responses().size(), 1u);
70   protobuf::Decoder decoder(context.responses()[0]);
71 
72   for (const auto& filter : filter_map_.filters()) {
73     ASSERT_EQ(decoder.Next(), OkStatus());
74     ASSERT_EQ(decoder.FieldNumber(), 1u);  // filter_id
75     ConstByteSpan filter_id;
76     ASSERT_EQ(decoder.ReadBytes(&filter_id), OkStatus());
77     ASSERT_EQ(filter_id.size(), filter.id().size());
78     EXPECT_EQ(
79         std::memcmp(filter_id.data(), filter.id().data(), filter_id.size()), 0);
80   }
81   EXPECT_FALSE(decoder.Next().ok());
82 
83   // No IDs reported when the filter map is empty.
84   FilterMap empty_filter_map({});
85   PW_RAW_TEST_METHOD_CONTEXT(FilterService, ListFilterIds, 1)
86   no_filter_context(empty_filter_map);
87   no_filter_context.call({});
88   ASSERT_EQ(OkStatus(), no_filter_context.status());
89   ASSERT_TRUE(no_filter_context.done());
90   ASSERT_EQ(no_filter_context.responses().size(), 1u);
91   protobuf::Decoder no_filter_decoder(no_filter_context.responses()[0]);
92   uint32_t filter_count = 0;
93   while (no_filter_decoder.Next().ok()) {
94     EXPECT_EQ(no_filter_decoder.FieldNumber(), 1u);  // filter_id
95     ++filter_count;
96   }
97   EXPECT_EQ(filter_count, 0u);
98 }
99 
EncodeFilterRule(const Filter::Rule & rule,FilterRule::StreamEncoder & encoder)100 Status EncodeFilterRule(const Filter::Rule& rule,
101                         FilterRule::StreamEncoder& encoder) {
102   PW_TRY(
103       encoder.WriteLevelGreaterThanOrEqual(rule.level_greater_than_or_equal));
104   PW_TRY(encoder.WriteModuleEquals(rule.module_equals));
105   PW_TRY(encoder.WriteAnyFlagsSet(rule.any_flags_set));
106   PW_TRY(encoder.WriteThreadEquals(rule.thread_equals));
107   return encoder.WriteAction(static_cast<FilterRule::Action>(rule.action));
108 }
109 
EncodeFilter(const Filter & filter,log::pwpb::Filter::StreamEncoder & encoder)110 Status EncodeFilter(const Filter& filter,
111                     log::pwpb::Filter::StreamEncoder& encoder) {
112   for (auto& rule : filter.rules()) {
113     FilterRule::StreamEncoder rule_encoder = encoder.GetRuleEncoder();
114     PW_TRY(EncodeFilterRule(rule, rule_encoder));
115   }
116   return OkStatus();
117 }
118 
EncodeFilterRequest(const Filter & filter,ByteSpan buffer)119 Result<ConstByteSpan> EncodeFilterRequest(const Filter& filter,
120                                           ByteSpan buffer) {
121   stream::MemoryWriter writer(buffer);
122   std::byte encode_buffer[256];
123   protobuf::StreamEncoder encoder(writer, encode_buffer);
124   PW_TRY(encoder.WriteBytes(
125       static_cast<uint32_t>(SetFilterRequest::Fields::kFilterId), filter.id()));
126   {
127     log::pwpb::Filter::StreamEncoder filter_encoder = encoder.GetNestedEncoder(
128         static_cast<uint32_t>(SetFilterRequest::Fields::kFilter));
129     PW_TRY(EncodeFilter(filter, filter_encoder));
130   }  // Let the StreamEncoder destructor finalize the data.
131   return ConstByteSpan(writer.data(), writer.bytes_written());
132 }
133 
VerifyRule(const Filter::Rule & rule,const Filter::Rule & expected_rule)134 void VerifyRule(const Filter::Rule& rule, const Filter::Rule& expected_rule) {
135   EXPECT_EQ(rule.level_greater_than_or_equal,
136             expected_rule.level_greater_than_or_equal);
137   EXPECT_EQ(rule.module_equals, expected_rule.module_equals);
138   EXPECT_EQ(rule.any_flags_set, expected_rule.any_flags_set);
139   EXPECT_EQ(rule.thread_equals, expected_rule.thread_equals);
140   EXPECT_EQ(rule.action, expected_rule.action);
141 }
142 
TEST_F(FilterServiceTest,SetFilterRules)143 TEST_F(FilterServiceTest, SetFilterRules) {
144   const std::array<Filter::Rule, kMaxFilterRules> new_rules{{
145       {
146           .action = Filter::Rule::Action::kKeep,
147           .level_greater_than_or_equal = FilterRule::Level::DEBUG_LEVEL,
148           .any_flags_set = 0x0f,
149           .module_equals{std::byte(123)},
150           .thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
151       },
152       {
153           .action = Filter::Rule::Action::kInactive,
154           .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
155           .any_flags_set = 0xef,
156           .module_equals{},
157           .thread_equals{},
158       },
159       {
160           .action = Filter::Rule::Action::kKeep,
161           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
162           .any_flags_set = 0x1234,
163           .module_equals{std::byte(99)},
164           .thread_equals{std::byte('P'),
165                          std::byte('O'),
166                          std::byte('W'),
167                          std::byte('E'),
168                          std::byte('R')},
169       },
170       {
171           .action = Filter::Rule::Action::kDrop,
172           .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
173           .any_flags_set = 0,
174           .module_equals{std::byte(4)},
175           .thread_equals{std::byte('P'),
176                          std::byte('O'),
177                          std::byte('W'),
178                          std::byte('E'),
179                          std::byte('R')},
180       },
181   }};
182   const Filter new_filter(
183       filters_[0].id(),
184       const_cast<std::array<Filter::Rule, kMaxFilterRules>&>(new_rules));
185 
186   std::byte request_buffer[512];
187   const auto request = EncodeFilterRequest(new_filter, request_buffer);
188   ASSERT_EQ(request.status(), OkStatus());
189 
190   PW_RAW_TEST_METHOD_CONTEXT(FilterService, SetFilter, 1)
191   context(filter_map_);
192   context.call(request.value());
193   ASSERT_EQ(OkStatus(), context.status());
194 
195   size_t i = 0;
196   for (const auto& rule : filters_[0].rules()) {
197     VerifyRule(rule, new_rules[i++]);
198   }
199 }
200 
TEST_F(FilterServiceTest,SetFilterRulesWhenUsedByDrain)201 TEST_F(FilterServiceTest, SetFilterRulesWhenUsedByDrain) {
202   const std::array<Filter::Rule, kMaxFilterRules> new_filter_rules{{
203       {
204           .action = Filter::Rule::Action::kKeep,
205           .level_greater_than_or_equal = FilterRule::Level::CRITICAL_LEVEL,
206           .any_flags_set = 0xfd,
207           .module_equals{std::byte(543)},
208           .thread_equals{std::byte('M'),
209                          std::byte('0'),
210                          std::byte('L'),
211                          std::byte('O'),
212                          std::byte('G')},
213       },
214       {
215           .action = Filter::Rule::Action::kInactive,
216           .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
217           .any_flags_set = 0xca,
218           .module_equals{},
219           .thread_equals{},
220       },
221       {
222           .action = Filter::Rule::Action::kKeep,
223           .level_greater_than_or_equal = FilterRule::Level::INFO_LEVEL,
224           .any_flags_set = 0xabcd,
225           .module_equals{std::byte(9000)},
226           .thread_equals{std::byte('P'),
227                          std::byte('O'),
228                          std::byte('W'),
229                          std::byte('E'),
230                          std::byte('R')},
231       },
232       {
233           .action = Filter::Rule::Action::kDrop,
234           .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
235           .any_flags_set = 0,
236           .module_equals{std::byte(123)},
237           .thread_equals{std::byte('P'),
238                          std::byte('O'),
239                          std::byte('W'),
240                          std::byte('E'),
241                          std::byte('R')},
242       },
243   }};
244   Filter& filter = filters_[0];
245   const Filter new_filter(
246       filter.id(),
247       const_cast<std::array<Filter::Rule, kMaxFilterRules>&>(new_filter_rules));
248 
249   std::byte request_buffer[256];
250   const auto request = EncodeFilterRequest(new_filter, request_buffer);
251   ASSERT_EQ(request.status(), OkStatus());
252 
253   PW_RAW_TEST_METHOD_CONTEXT(FilterService, SetFilter, 1)
254   context(filter_map_);
255   context.call(request.value());
256   ASSERT_EQ(OkStatus(), context.status());
257 
258   size_t i = 0;
259   for (const auto& rule : filter.rules()) {
260     VerifyRule(rule, new_filter_rules[i++]);
261   }
262 
263   // An empty request should not modify the filter.
264   PW_RAW_TEST_METHOD_CONTEXT(FilterService, SetFilter, 1)
265   context_no_filter(filter_map_);
266   context_no_filter.call({});
267   EXPECT_EQ(Status::OutOfRange(), context_no_filter.status());
268 
269   i = 0;
270   for (const auto& rule : filter.rules()) {
271     VerifyRule(rule, new_filter_rules[i++]);
272   }
273 
274   // A new request for logs with a new filter updates filter.
275   const std::array<Filter::Rule, kMaxFilterRules> second_filter_rules{{
276       {
277           .action = Filter::Rule::Action::kKeep,
278           .level_greater_than_or_equal = FilterRule::Level::DEBUG_LEVEL,
279           .any_flags_set = 0xab,
280           .module_equals{},
281           .thread_equals{},
282       },
283       {
284           .action = Filter::Rule::Action::kDrop,
285           .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
286           .any_flags_set = 0x11,
287           .module_equals{std::byte(34)},
288           .thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
289       },
290       {
291           .action = Filter::Rule::Action::kKeep,
292           .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
293           .any_flags_set = 0xef,
294           .module_equals{std::byte(23)},
295           .thread_equals{std::byte('R'), std::byte('P'), std::byte('C')},
296       },
297       {
298           .action = Filter::Rule::Action::kDrop,
299           .level_greater_than_or_equal = FilterRule::Level::ANY_LEVEL,
300           .any_flags_set = 0x0f,
301           .module_equals{},
302           .thread_equals{std::byte('R'), std::byte('P'), std::byte('C')},
303       },
304   }};
305   const Filter second_filter(
306       filter.id(),
307       const_cast<std::array<Filter::Rule, kMaxFilterRules>&>(
308           second_filter_rules));
309 
310   std::memset(request_buffer, 0, sizeof(request_buffer));
311   const auto second_filter_request =
312       EncodeFilterRequest(second_filter, request_buffer);
313   ASSERT_EQ(second_filter_request.status(), OkStatus());
314   PW_RAW_TEST_METHOD_CONTEXT(FilterService, SetFilter, 1)
315   context_new_filter(filter_map_);
316   context_new_filter.call(second_filter_request.value());
317   ASSERT_EQ(OkStatus(), context.status());
318 
319   i = 0;
320   for (const auto& rule : filter.rules()) {
321     VerifyRule(rule, second_filter_rules[i++]);
322   }
323 }
324 
VerifyFilterRule(protobuf::Decoder & decoder,const Filter::Rule & expected_rule)325 void VerifyFilterRule(protobuf::Decoder& decoder,
326                       const Filter::Rule& expected_rule) {
327   ASSERT_TRUE(decoder.Next().ok());
328   ASSERT_EQ(
329       decoder.FieldNumber(),
330       static_cast<uint32_t>(FilterRule::Fields::kLevelGreaterThanOrEqual));
331   FilterRule::Level level_greater_than_or_equal;
332   ASSERT_EQ(decoder.ReadUint32(
333                 reinterpret_cast<uint32_t*>(&level_greater_than_or_equal)),
334             OkStatus());
335   EXPECT_EQ(level_greater_than_or_equal,
336             expected_rule.level_greater_than_or_equal);
337 
338   ASSERT_TRUE(decoder.Next().ok());
339   ASSERT_EQ(decoder.FieldNumber(),
340             static_cast<uint32_t>(FilterRule::Fields::kModuleEquals));
341   ConstByteSpan module_equals;
342   ASSERT_EQ(decoder.ReadBytes(&module_equals), OkStatus());
343   ASSERT_EQ(module_equals.size(), expected_rule.module_equals.size());
344   EXPECT_EQ(std::memcmp(module_equals.data(),
345                         expected_rule.module_equals.data(),
346                         module_equals.size()),
347             0);
348 
349   ASSERT_TRUE(decoder.Next().ok());
350   ASSERT_EQ(decoder.FieldNumber(),
351             static_cast<uint32_t>(FilterRule::Fields::kAnyFlagsSet));
352   uint32_t any_flags_set;
353   ASSERT_EQ(decoder.ReadUint32(&any_flags_set), OkStatus());
354   EXPECT_EQ(any_flags_set, expected_rule.any_flags_set);
355 
356   ASSERT_TRUE(decoder.Next().ok());
357   ASSERT_EQ(decoder.FieldNumber(),
358             static_cast<uint32_t>(FilterRule::Fields::kAction));
359   Filter::Rule::Action action;
360   ASSERT_EQ(decoder.ReadUint32(reinterpret_cast<uint32_t*>(&action)),
361             OkStatus());
362   EXPECT_EQ(action, expected_rule.action);
363 
364   ASSERT_TRUE(decoder.Next().ok());
365   ASSERT_EQ(decoder.FieldNumber(),
366             static_cast<uint32_t>(FilterRule::Fields::kThreadEquals));
367   ConstByteSpan thread;
368   ASSERT_EQ(decoder.ReadBytes(&thread), OkStatus());
369   ASSERT_EQ(thread.size(), expected_rule.thread_equals.size());
370   EXPECT_EQ(
371       std::memcmp(
372           thread.data(), expected_rule.thread_equals.data(), thread.size()),
373       0);
374 }
375 
VerifyFilterRules(protobuf::Decoder & decoder,span<const Filter::Rule> expected_rules)376 void VerifyFilterRules(protobuf::Decoder& decoder,
377                        span<const Filter::Rule> expected_rules) {
378   size_t rules_found = 0;
379   while (decoder.Next().ok()) {
380     ConstByteSpan rule;
381     EXPECT_TRUE(decoder.ReadBytes(&rule).ok());
382     protobuf::Decoder rule_decoder(rule);
383     if (rules_found >= expected_rules.size()) {
384       break;
385     }
386     VerifyFilterRule(rule_decoder, expected_rules[rules_found]);
387     ++rules_found;
388   }
389   EXPECT_EQ(rules_found, expected_rules.size());
390 }
391 
TEST_F(FilterServiceTest,GetFilterRules)392 TEST_F(FilterServiceTest, GetFilterRules) {
393   PW_RAW_TEST_METHOD_CONTEXT(FilterService, GetFilter, 1)
394   context(filter_map_);
395 
396   std::byte request_buffer[64];
397   GetFilterRequest::MemoryEncoder encoder(request_buffer);
398   ASSERT_EQ(OkStatus(), encoder.WriteFilterId(filter_id1_));
399   const auto request = ConstByteSpan(encoder);
400   context.call(request);
401   ASSERT_EQ(OkStatus(), context.status());
402   ASSERT_TRUE(context.done());
403   ASSERT_EQ(context.responses().size(), 1u);
404 
405   // Verify against empty rules.
406   protobuf::Decoder decoder(context.responses()[0]);
407   VerifyFilterRules(decoder, rules1_);
408 
409   // Partially populate rules.
410   rules1_[0].action = Filter::Rule::Action::kKeep;
411   rules1_[0].level_greater_than_or_equal = FilterRule::Level::DEBUG_LEVEL;
412   rules1_[0].any_flags_set = 0xab;
413   const std::array<std::byte, 2> module1{std::byte(123), std::byte(0xab)};
414   rules1_[0].module_equals.assign(module1.begin(), module1.end());
415   const std::array<std::byte, 4> thread1{
416       std::byte('H'), std::byte('O'), std::byte('S'), std::byte('T')};
417   rules1_[0].thread_equals.assign(thread1.begin(), thread1.end());
418   rules1_[1].action = Filter::Rule::Action::kDrop;
419   rules1_[1].level_greater_than_or_equal = FilterRule::Level::ERROR_LEVEL;
420   rules1_[1].any_flags_set = 0;
421 
422   PW_RAW_TEST_METHOD_CONTEXT(FilterService, GetFilter, 1)
423   context2(filter_map_);
424   context2.call(request);
425   ASSERT_EQ(OkStatus(), context2.status());
426   ASSERT_EQ(context2.responses().size(), 1u);
427   protobuf::Decoder decoder2(context2.responses()[0]);
428   VerifyFilterRules(decoder2, rules1_);
429 
430   // Modify the rest of the filter rules.
431   rules1_[2].action = Filter::Rule::Action::kKeep;
432   rules1_[2].level_greater_than_or_equal = FilterRule::Level::FATAL_LEVEL;
433   rules1_[2].any_flags_set = 0xcd;
434   const std::array<std::byte, 2> module2{std::byte(1), std::byte(2)};
435   rules1_[2].module_equals.assign(module2.begin(), module2.end());
436   const std::array<std::byte, 3> thread2{
437       std::byte('A'), std::byte('P'), std::byte('P')};
438   rules1_[2].thread_equals.assign(thread2.begin(), thread2.end());
439   rules1_[3].action = Filter::Rule::Action::kInactive;
440 
441   PW_RAW_TEST_METHOD_CONTEXT(FilterService, GetFilter, 1)
442   context3(filter_map_);
443   context3.call(request);
444   ASSERT_EQ(OkStatus(), context3.status());
445   ASSERT_EQ(context3.responses().size(), 1u);
446   protobuf::Decoder decoder3(context3.responses()[0]);
447   VerifyFilterRules(decoder3, rules1_);
448 }
449 
450 }  // namespace
451 }  // namespace pw::log_rpc
452