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