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