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_protobuf/message.h"
16
17 #include "pw_stream/memory_stream.h"
18 #include "pw_unit_test/framework.h"
19
20 namespace pw::protobuf {
21 namespace {
22
TEST(ProtoHelper,IterateMessage)23 TEST(ProtoHelper, IterateMessage) {
24 // clang-format off
25 constexpr uint8_t encoded_proto[] = {
26 // type=uint32, k=1, v=1
27 0x08, 0x01,
28 // type=uint32, k=2, v=2
29 0x10, 0x02,
30 // type=uint32, k=3, v=3
31 0x18, 0x03,
32 };
33 // clang-format on
34
35 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
36 Message parser = Message(reader, sizeof(encoded_proto));
37
38 uint32_t count = 0;
39 for (Message::Field field : parser) {
40 ++count;
41 EXPECT_EQ(field.field_number(), count);
42 Uint32 value = field.As<Uint32>();
43 PW_TEST_ASSERT_OK(value.status());
44 EXPECT_EQ(value.value(), count);
45 }
46
47 EXPECT_EQ(count, static_cast<uint32_t>(3));
48 }
49
TEST(ProtoHelper,MessageIterator)50 TEST(ProtoHelper, MessageIterator) {
51 // clang-format off
52 std::uint8_t encoded_proto[] = {
53 // key = 1, str = "foo 1"
54 0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
55 // type=uint32, k=2, v=2
56 0x10, 0x02,
57 };
58 // clang-format on
59
60 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
61 Message parser = Message(reader, sizeof(encoded_proto));
62
63 Message::iterator iter = parser.begin();
64
65 Message::iterator first = iter++;
66 ASSERT_EQ(first, first);
67 ASSERT_EQ(first->field_number(), static_cast<uint32_t>(1));
68 String str = first->As<String>();
69 PW_TEST_ASSERT_OK(str.status());
70 Result<bool> cmp = str.Equal("foo 1");
71 PW_TEST_ASSERT_OK(cmp.status());
72 ASSERT_TRUE(cmp.value());
73
74 Message::iterator second = iter++;
75 ASSERT_EQ(second, second);
76 ASSERT_EQ(second->field_number(), static_cast<uint32_t>(2));
77 Uint32 uint32_val = second->As<Uint32>();
78 PW_TEST_ASSERT_OK(uint32_val.status());
79 ASSERT_EQ(uint32_val.value(), static_cast<uint32_t>(2));
80
81 ASSERT_NE(first, second);
82 ASSERT_NE(first, iter);
83 ASSERT_NE(second, iter);
84 ASSERT_EQ(iter, parser.end());
85 }
86
TEST(ProtoHelper,MessageIteratorMalformedProto)87 TEST(ProtoHelper, MessageIteratorMalformedProto) {
88 // clang-format off
89 std::uint8_t encoded_proto[] = {
90 // key = 1, str = "foo 1"
91 0x0a,0x05,'f','o','o',' ','1',
92 // key = 0, str = "foo 2" (invalid)
93 0x02,0x05,'f','o','o',' ','2',
94 // key = 3, str = "bar 1"
95 0x1a,0x05,'b','a','r',' ','1',
96 };
97 // clang-format on
98
99 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
100 Message parser = Message(reader, sizeof(encoded_proto));
101
102 Message::iterator iter = parser.begin();
103 PW_TEST_ASSERT_OK(iter.status());
104
105 // Second field has invalid field number
106 ASSERT_FALSE((++iter).ok());
107
108 // Attempting to increment an invalid iterator result in it being end()
109 ASSERT_EQ((++iter), parser.end());
110
111 // Test the c++ std loop behavior.
112 bool expected_ok_status[] = {true, false};
113 size_t count = 0;
114 for (Message::Field field : parser) {
115 ASSERT_EQ(field.ok(), expected_ok_status[count++]);
116 }
117 // First element ok. Second element invalid. Iteration ends in the next
118 // iteration.
119 ASSERT_EQ(count, 2ULL);
120 }
121
TEST(ProtoHelper,InvalidMessageBeginIterator)122 TEST(ProtoHelper, InvalidMessageBeginIterator) {
123 Message parser(Status::Internal());
124 ASSERT_FALSE(parser.begin().ok());
125 ASSERT_EQ(parser.begin(), parser.end());
126 }
127
TEST(ProtoHelper,AsProtoInteger)128 TEST(ProtoHelper, AsProtoInteger) {
129 // clang-format off
130 std::uint8_t encoded_proto[] = {
131 // type: int32, k = 1, val = -123
132 0x08, 0x85, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01,
133 // type: uint32, k = 2, val = 123
134 0x10, 0x7b,
135 // type: sint32, k = 3, val = -456
136 0x18, 0x8f, 0x07,
137 // type: fixed32, k = 4, val = 268435457
138 0x25, 0x01, 0x00, 0x00, 0x10,
139 // type: sfixed32, k = 5, val = -268435457
140 0x2d, 0xff, 0xff, 0xff, 0xef,
141 // type: int64, k = 6, val = -1099511627776
142 0x30, 0x80, 0x80, 0x80, 0x80, 0x80, 0xe0, 0xff, 0xff, 0xff, 0x01,
143 // type: uint64, k = 7, val = 1099511627776
144 0x38, 0x80, 0x80, 0x80, 0x80, 0x80, 0x20,
145 // type: sint64, k = 8, val = -2199023255552
146 0x40, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
147 // type: fixed64, k = 9, val = 72057594037927937
148 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
149 // type: sfixed64, k = 10, val = -72057594037927937
150 0x51, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
151 // type: float, k = 11, val = 123456.00
152 0x5d, 0x00, 0x20, 0xf1, 0x47,
153 // type: double, k = 12, val = -123456.789
154 0x61, 0xc9, 0x76, 0xbe, 0x9f, 0x0c, 0x24, 0xfe, 0xc0,
155 // type: bool, k = 13, val = true
156 0x68, 0x01,
157 // type: bool, k = 14, val = false
158 0x70, 0x00
159 };
160 // clang-format on
161
162 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
163 Message parser = Message(reader, sizeof(encoded_proto));
164
165 {
166 Int32 value = parser.AsInt32(1);
167 PW_TEST_ASSERT_OK(value.status());
168 ASSERT_EQ(value.value(), static_cast<int32_t>(-123));
169 }
170
171 {
172 Uint32 value = parser.AsUint32(2);
173 PW_TEST_ASSERT_OK(value.status());
174 ASSERT_EQ(value.value(), static_cast<uint32_t>(123));
175 }
176
177 {
178 Sint32 value = parser.AsSint32(3);
179 PW_TEST_ASSERT_OK(value.status());
180 ASSERT_EQ(value.value(), static_cast<int32_t>(-456));
181 }
182
183 {
184 Fixed32 value = parser.AsFixed32(4);
185 PW_TEST_ASSERT_OK(value.status());
186 ASSERT_EQ(value.value(), static_cast<uint32_t>(268435457));
187 }
188
189 {
190 Sfixed32 value = parser.AsSfixed32(5);
191 PW_TEST_ASSERT_OK(value.status());
192 ASSERT_EQ(value.value(), static_cast<int32_t>(-268435457));
193 }
194
195 {
196 Int64 value = parser.AsInt64(6);
197 PW_TEST_ASSERT_OK(value.status());
198 ASSERT_EQ(value.value(), static_cast<int64_t>(-1099511627776));
199 }
200
201 {
202 Uint64 value = parser.AsUint64(7);
203 PW_TEST_ASSERT_OK(value.status());
204 ASSERT_EQ(value.value(), static_cast<uint64_t>(1099511627776));
205 }
206
207 {
208 Sint64 value = parser.AsSint64(8);
209 PW_TEST_ASSERT_OK(value.status());
210 ASSERT_EQ(value.value(), static_cast<int64_t>(-2199023255552));
211 }
212
213 {
214 Fixed64 value = parser.AsFixed64(9);
215 PW_TEST_ASSERT_OK(value.status());
216 ASSERT_EQ(value.value(), static_cast<uint64_t>(72057594037927937));
217 }
218
219 {
220 Sfixed64 value = parser.AsSfixed64(10);
221 PW_TEST_ASSERT_OK(value.status());
222 ASSERT_EQ(value.value(), static_cast<int64_t>(-72057594037927937));
223 }
224
225 {
226 Float value = parser.AsFloat(11);
227 PW_TEST_ASSERT_OK(value.status());
228 ASSERT_EQ(value.value(), static_cast<float>(123456.00));
229 }
230
231 {
232 Double value = parser.AsDouble(12);
233 PW_TEST_ASSERT_OK(value.status());
234 ASSERT_EQ(value.value(), static_cast<double>(-123456.789));
235 }
236
237 {
238 Bool value = parser.AsBool(13);
239 PW_TEST_ASSERT_OK(value.status());
240 ASSERT_EQ(value.value(), static_cast<bool>(true));
241 }
242
243 {
244 Bool value = parser.AsBool(14);
245 PW_TEST_ASSERT_OK(value.status());
246 ASSERT_EQ(value.value(), static_cast<bool>(false));
247 }
248 }
249
TEST(ProtoHelper,AsString)250 TEST(ProtoHelper, AsString) {
251 // message {
252 // string str = 1;
253 // }
254 // clang-format off
255 std::uint8_t encoded_proto[] = {
256 // `str`, k = 1, "string"
257 0x0a, 0x06, 's', 't', 'r', 'i', 'n', 'g',
258 };
259 // clang-format on
260
261 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
262 Message parser = Message(reader, sizeof(encoded_proto));
263
264 constexpr uint32_t kFieldNumber = 1;
265 String value = parser.AsString(kFieldNumber);
266 PW_TEST_ASSERT_OK(value.status());
267 Result<bool> cmp = value.Equal("string");
268 PW_TEST_ASSERT_OK(cmp.status());
269 ASSERT_TRUE(cmp.value());
270
271 cmp = value.Equal("other");
272 PW_TEST_ASSERT_OK(cmp.status());
273 ASSERT_FALSE(cmp.value());
274
275 // The string is a prefix of the target string to compare.
276 cmp = value.Equal("string and more");
277 PW_TEST_ASSERT_OK(cmp.status());
278 ASSERT_FALSE(cmp.value());
279
280 // The target string to compare is a sub prefix of this string
281 cmp = value.Equal("str");
282 PW_TEST_ASSERT_OK(cmp.status());
283 ASSERT_FALSE(cmp.value());
284 }
285
TEST(ProtoHelper,AsRepeatedStrings)286 TEST(ProtoHelper, AsRepeatedStrings) {
287 // Repeated field of string i.e.
288 //
289 // message RepeatedString {
290 // repeated string msg_a = 1;
291 // repeated string msg_b = 2;
292 // }
293 // clang-format off
294 std::uint8_t encoded_proto[] = {
295 // key = 1, str = "foo 1"
296 0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
297 // key = 2, str = "foo 2"
298 0x12, 0x05, 'f', 'o', 'o', ' ', '2',
299 // key = 1, str = "bar 1"
300 0x0a, 0x05, 'b', 'a', 'r', ' ', '1',
301 // key = 2, str = "bar 2"
302 0x12, 0x05, 'b', 'a', 'r', ' ', '2',
303 };
304 // clang-format on
305
306 constexpr uint32_t kMsgAFieldNumber = 1;
307 constexpr uint32_t kMsgBFieldNumber = 2;
308 constexpr uint32_t kNonExistFieldNumber = 3;
309
310 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
311 Message parser = Message(reader, sizeof(encoded_proto));
312
313 // Field 'msg_a'
314 {
315 RepeatedStrings msg = parser.AsRepeatedStrings(kMsgAFieldNumber);
316 std::string_view expected[] = {
317 "foo 1",
318 "bar 1",
319 };
320
321 size_t count = 0;
322 for (String ele : msg) {
323 PW_TEST_ASSERT_OK(ele.status());
324 Result<bool> res = ele.Equal(expected[count++]);
325 PW_TEST_ASSERT_OK(res.status());
326 ASSERT_TRUE(res.value());
327 }
328
329 ASSERT_EQ(count, static_cast<size_t>(2));
330 }
331
332 // Field `msg_b`
333 {
334 RepeatedStrings msg = parser.AsRepeatedStrings(kMsgBFieldNumber);
335 std::string_view expected[] = {
336 "foo 2",
337 "bar 2",
338 };
339
340 size_t count = 0;
341 for (String ele : msg) {
342 PW_TEST_ASSERT_OK(ele.status());
343 Result<bool> res = ele.Equal(expected[count++]);
344 PW_TEST_ASSERT_OK(res.status());
345 ASSERT_TRUE(res.value());
346 }
347
348 ASSERT_EQ(count, static_cast<size_t>(2));
349 }
350
351 // non-existing field
352 {
353 RepeatedStrings msg = parser.AsRepeatedStrings(kNonExistFieldNumber);
354 size_t count = 0;
355 for ([[maybe_unused]] String ele : msg) {
356 count++;
357 }
358
359 ASSERT_EQ(count, static_cast<size_t>(0));
360 }
361 }
362
TEST(ProtoHelper,RepeatedFieldIterator)363 TEST(ProtoHelper, RepeatedFieldIterator) {
364 // Repeated field of string i.e.
365 //
366 // message RepeatedString {
367 // repeated string msg = 1;
368 // }
369 // clang-format off
370 std::uint8_t encoded_proto[] = {
371 // key = 1, str = "foo 1"
372 0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
373 // key = 1, str = "bar 1"
374 0x0a, 0x05, 'b', 'a', 'r', ' ', '1',
375 };
376 // clang-format on
377
378 constexpr uint32_t kFieldNumber = 1;
379 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
380 Message parser = Message(reader, sizeof(encoded_proto));
381 RepeatedStrings repeated_str = parser.AsRepeatedStrings(kFieldNumber);
382
383 RepeatedStrings::iterator iter = repeated_str.begin();
384
385 RepeatedStrings::iterator first = iter++;
386 ASSERT_EQ(first, first);
387 Result<bool> cmp = first->Equal("foo 1");
388 PW_TEST_ASSERT_OK(cmp.status());
389 ASSERT_TRUE(cmp.value());
390
391 RepeatedStrings::iterator second = iter++;
392 ASSERT_EQ(second, second);
393 cmp = second->Equal("bar 1");
394 PW_TEST_ASSERT_OK(cmp.status());
395 ASSERT_TRUE(cmp.value());
396
397 ASSERT_NE(first, second);
398 ASSERT_NE(first, iter);
399 ASSERT_NE(second, iter);
400 ASSERT_EQ(iter, repeated_str.end());
401 }
402
TEST(ProtoHelper,RepeatedFieldIteratorMalformedFieldID)403 TEST(ProtoHelper, RepeatedFieldIteratorMalformedFieldID) {
404 // Repeated field of string i.e.
405 //
406 // message RepeatedString {
407 // repeated string msg = 1;
408 // }
409 // clang-format off
410 std::uint8_t encoded_proto[] = {
411 // key = 1, str = "foo 1"
412 0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
413 // key = 0, str = "foo 1" (invalid)
414 0x02, 0x05, 'f', 'o', 'o', ' ', '1',
415 // key = 1, str = "foo 1"
416 0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
417 };
418 // clang-format on
419
420 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
421 Message parser = Message(reader, sizeof(encoded_proto));
422 RepeatedStrings repeated_str = parser.AsRepeatedStrings(1);
423
424 bool expected_ok[] = {true, false};
425 size_t count = 0;
426 for (String s : repeated_str) {
427 ASSERT_EQ(s.ok(), expected_ok[count++]);
428 }
429 // Iterator becomes invalid in the second iteration. Attempting to increment
430 // causes it to become end(); Therefore, count should be incremented twice.
431 ASSERT_EQ(count, 2ULL);
432 }
433
TEST(ProtoHelper,RepeatedFieldIteratorMalformedFieldIDBeginning)434 TEST(ProtoHelper, RepeatedFieldIteratorMalformedFieldIDBeginning) {
435 // Repeated field of string i.e.
436 //
437 // message RepeatedString {
438 // repeated string msg = 1;
439 // }
440 // clang-format off
441 std::uint8_t encoded_proto[] = {
442 // key = 0, str = "foo 1" (invalid)
443 0x02, 0x05, 'f', 'o', 'o', ' ', '1',
444 // key = 1, str = "foo 1"
445 0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
446 // key = 1, str = "foo 1"
447 0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
448 };
449 // clang-format on
450
451 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
452 Message parser = Message(reader, sizeof(encoded_proto));
453 RepeatedStrings repeated_str = parser.AsRepeatedStrings(1);
454
455 bool expected_ok[] = {false};
456 size_t count = 0;
457 for (String s : repeated_str) {
458 ASSERT_EQ(s.ok(), expected_ok[count++]);
459 }
460 // Iterator becomes invalid in the second iteration. Attempting to increment
461 // causes it to become end(); Therefore, count should be incremented twice.
462 ASSERT_EQ(count, 1ULL);
463 }
464
TEST(ProtoHelper,RepeatedFieldIteratorMalformedDataLoss)465 TEST(ProtoHelper, RepeatedFieldIteratorMalformedDataLoss) {
466 // Repeated field of string i.e.
467 //
468 // message RepeatedString {
469 // repeated string msg = 1;
470 // }
471 // clang-format off
472 std::uint8_t encoded_proto[] = {
473 // key = 1, str = "foo 1"
474 0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
475 // key = 0, str = "foo 1" (invalid)
476 0x0a, 0x10, 'f', 'o', 'o', ' ', '1',
477 };
478 // clang-format on
479
480 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
481 Message parser = Message(reader, sizeof(encoded_proto));
482 RepeatedStrings repeated_str = parser.AsRepeatedStrings(1);
483
484 bool expected_ok[] = {true, false};
485 size_t count = 0;
486 for (String s : repeated_str) {
487 ASSERT_EQ(s.ok(), expected_ok[count++]);
488 }
489 ASSERT_EQ(count, 2ULL);
490 }
491
TEST(ProtoHelper,AsMessage)492 TEST(ProtoHelper, AsMessage) {
493 // A nested message:
494 //
495 // message Contact {
496 // string number = 1;
497 // string email = 2;
498 // }
499 //
500 // message Person {
501 // Contact info = 2;
502 // }
503 // clang-format off
504 std::uint8_t encoded_proto[] = {
505 // Person.info.number = "123456", .email = "[email protected]"
506 0x12, 0x17,
507 0x0a, 0x06, '1', '2', '3', '4', '5', '6',
508 0x12, 0x0d, 'f', 'o', 'o', '@', 'e', 'm', 'a', 'i', 'l', '.', 'c', 'o', 'm',
509 };
510 // clang-format on
511
512 constexpr uint32_t kInfoFieldNumber = 2;
513 constexpr uint32_t kNumberFieldNumber = 1;
514 constexpr uint32_t kEmailFieldNumber = 2;
515
516 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
517 Message parser = Message(reader, sizeof(encoded_proto));
518
519 Message info = parser.AsMessage(kInfoFieldNumber);
520 PW_TEST_ASSERT_OK(info.status());
521
522 String number = info.AsString(kNumberFieldNumber);
523 PW_TEST_ASSERT_OK(number.status());
524 Result<bool> cmp = number.Equal("123456");
525 PW_TEST_ASSERT_OK(cmp.status());
526 ASSERT_TRUE(cmp.value());
527
528 String email = info.AsString(kEmailFieldNumber);
529 PW_TEST_ASSERT_OK(email.status());
530 cmp = email.Equal("[email protected]");
531 PW_TEST_ASSERT_OK(cmp.status());
532 ASSERT_TRUE(cmp.value());
533 }
534
TEST(ProtoHelper,AsRepeatedMessages)535 TEST(ProtoHelper, AsRepeatedMessages) {
536 // message Contact {
537 // string number = 1;
538 // string email = 2;
539 // }
540 //
541 // message Person {
542 // repeated Contact info = 1;
543 // }
544 // clang-format off
545 std::uint8_t encoded_proto[] = {
546 // Person.Contact.number = "12345", .email = "[email protected]"
547 0x0a, 0x16,
548 0x0a, 0x05, '1', '2', '3', '4', '5',
549 0x12, 0x0d, 'f', 'o', 'o', '@', 'e', 'm', 'a', 'i', 'l', '.', 'c', 'o', 'm',
550
551 // Person.Contact.number = "67890", .email = "[email protected]"
552 0x0a, 0x16,
553 0x0a, 0x05, '6', '7', '8', '9', '0',
554 0x12, 0x0d, 'b', 'a', 'r', '@', 'e', 'm', 'a', 'i', 'l', '.', 'c', 'o', 'm',
555 };
556 // clang-format on
557
558 constexpr uint32_t kInfoFieldNumber = 1;
559 constexpr uint32_t kNumberFieldNumber = 1;
560 constexpr uint32_t kEmailFieldNumber = 2;
561
562 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
563 Message parser = Message(reader, sizeof(encoded_proto));
564
565 RepeatedMessages messages = parser.AsRepeatedMessages(kInfoFieldNumber);
566 PW_TEST_ASSERT_OK(messages.status());
567
568 struct {
569 std::string_view number;
570 std::string_view email;
571 } expected[] = {
572 {"12345", "[email protected]"},
573 {"67890", "[email protected]"},
574 };
575
576 size_t count = 0;
577 for (Message message : messages) {
578 String number = message.AsString(kNumberFieldNumber);
579 PW_TEST_ASSERT_OK(number.status());
580 Result<bool> cmp = number.Equal(expected[count].number);
581 PW_TEST_ASSERT_OK(cmp.status());
582 ASSERT_TRUE(cmp.value());
583
584 String email = message.AsString(kEmailFieldNumber);
585 PW_TEST_ASSERT_OK(email.status());
586 cmp = email.Equal(expected[count].email);
587 PW_TEST_ASSERT_OK(cmp.status());
588 ASSERT_TRUE(cmp.value());
589
590 count++;
591 }
592
593 ASSERT_EQ(count, static_cast<size_t>(2));
594 }
595
TEST(ProtoHelper,AsStringToBytesMap)596 TEST(ProtoHelper, AsStringToBytesMap) {
597 // message Maps {
598 // map<string, string> map_a = 1;
599 // map<string, string> map_b = 2;
600 // }
601 // clang-format off
602 std::uint8_t encoded_proto[] = {
603 // map_a["key_bar"] = "bar_a", key = 1
604 0x0a, 0x10,
605 0x0a, 0x07, 'k', 'e', 'y', '_', 'b', 'a', 'r', // map key
606 0x12, 0x05, 'b', 'a', 'r', '_', 'a', // map value
607
608 // map_a["key_foo"] = "foo_a", key = 1
609 0x0a, 0x10,
610 0x0a, 0x07, 'k', 'e', 'y', '_', 'f', 'o', 'o',
611 0x12, 0x05, 'f', 'o', 'o', '_', 'a',
612
613 // map_b["key_foo"] = "foo_b", key = 2
614 0x12, 0x10,
615 0x0a, 0x07, 'k', 'e', 'y', '_', 'f', 'o', 'o',
616 0x12, 0x05, 'f', 'o', 'o', 0x5f, 0x62,
617
618 // map_b["key_bar"] = "bar_b", key = 2
619 0x12, 0x10,
620 0x0a, 0x07, 'k', 'e', 'y', '_', 'b', 'a', 'r',
621 0x12, 0x05, 'b', 'a', 'r', 0x5f, 0x62,
622 };
623 // clang-format on
624
625 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
626 Message parser = Message(reader, sizeof(encoded_proto));
627
628 {
629 // Parse field 'map_a'
630 constexpr uint32_t kFieldNumber = 1;
631 StringMapParser<String> string_map =
632 parser.AsStringToStringMap(kFieldNumber);
633
634 String value = string_map["key_foo"];
635 PW_TEST_ASSERT_OK(value.status());
636 Result<bool> cmp = value.Equal("foo_a");
637 PW_TEST_ASSERT_OK(cmp.status());
638 ASSERT_TRUE(cmp.value());
639
640 value = string_map["key_bar"];
641 PW_TEST_ASSERT_OK(value.status());
642 cmp = value.Equal("bar_a");
643 PW_TEST_ASSERT_OK(cmp.status());
644 ASSERT_TRUE(cmp.value());
645
646 // Non-existing key
647 value = string_map["non-existing"];
648 ASSERT_EQ(value.status(), Status::NotFound());
649 }
650
651 {
652 // Parse field 'map_b'
653 constexpr uint32_t kFieldNumber = 2;
654 StringMapParser<String> string_map =
655 parser.AsStringToStringMap(kFieldNumber);
656
657 String value = string_map["key_foo"];
658 PW_TEST_ASSERT_OK(value.status());
659 Result<bool> cmp = value.Equal("foo_b");
660 PW_TEST_ASSERT_OK(cmp.status());
661 ASSERT_TRUE(cmp.value());
662
663 value = string_map["key_bar"];
664 PW_TEST_ASSERT_OK(value.status());
665 cmp = value.Equal("bar_b");
666 PW_TEST_ASSERT_OK(cmp.status());
667 ASSERT_TRUE(cmp.value());
668
669 // Non-existing key
670 value = string_map["non-existing"];
671 ASSERT_EQ(value.status(), Status::NotFound());
672 }
673 }
674
TEST(ProtoHelper,AsStringToMessageMap)675 TEST(ProtoHelper, AsStringToMessageMap) {
676 // message Contact {
677 // string number = 1;
678 // string email = 2;
679 // }
680 //
681 // message Contacts {
682 // map<string, Contact> staffs = 1;
683 // }
684 // clang-format off
685 std::uint8_t encoded_proto[] = {
686 // staffs['bar'] = {.number = '456, .email = "[email protected]"}
687 0x0a, 0x1b,
688 0x0a, 0x03, 0x62, 0x61, 0x72,
689 0x12, 0x14, 0x0a, 0x03, 0x34, 0x35, 0x36, 0x12, 0x0d, 0x62, 0x61, 0x72, 0x40, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d,
690
691 // staffs['foo'] = {.number = '123', .email = "[email protected]"}
692 0x0a, 0x1b,
693 0x0a, 0x03, 0x66, 0x6f, 0x6f,
694 0x12, 0x14, 0x0a, 0x03, 0x31, 0x32, 0x33, 0x12, 0x0d, 0x66, 0x6f, 0x6f, 0x40, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d,
695 };
696 // clang-format on
697 constexpr uint32_t kStaffsFieldId = 1;
698 constexpr uint32_t kNumberFieldId = 1;
699 constexpr uint32_t kEmailFieldId = 2;
700
701 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
702 Message parser = Message(reader, sizeof(encoded_proto));
703
704 StringMapParser<Message> staffs = parser.AsStringToMessageMap(kStaffsFieldId);
705 PW_TEST_ASSERT_OK(staffs.status());
706
707 Message foo_staff = staffs["foo"];
708 PW_TEST_ASSERT_OK(foo_staff.status());
709 String foo_number = foo_staff.AsString(kNumberFieldId);
710 PW_TEST_ASSERT_OK(foo_number.status());
711 Result<bool> foo_number_cmp = foo_number.Equal("123");
712 PW_TEST_ASSERT_OK(foo_number_cmp.status());
713 ASSERT_TRUE(foo_number_cmp.value());
714 String foo_email = foo_staff.AsString(kEmailFieldId);
715 PW_TEST_ASSERT_OK(foo_email.status());
716 Result<bool> foo_email_cmp = foo_email.Equal("[email protected]");
717 PW_TEST_ASSERT_OK(foo_email_cmp.status());
718 ASSERT_TRUE(foo_email_cmp.value());
719
720 Message bar_staff = staffs["bar"];
721 PW_TEST_ASSERT_OK(bar_staff.status());
722 String bar_number = bar_staff.AsString(kNumberFieldId);
723 PW_TEST_ASSERT_OK(bar_number.status());
724 Result<bool> bar_number_cmp = bar_number.Equal("456");
725 PW_TEST_ASSERT_OK(bar_number_cmp.status());
726 ASSERT_TRUE(bar_number_cmp.value());
727 String bar_email = bar_staff.AsString(kEmailFieldId);
728 PW_TEST_ASSERT_OK(bar_email.status());
729 Result<bool> bar_email_cmp = bar_email.Equal("[email protected]");
730 PW_TEST_ASSERT_OK(bar_email_cmp.status());
731 ASSERT_TRUE(bar_email_cmp.value());
732 }
733
TEST(ProtoHelper,AsStringToBytesMapMalformed)734 TEST(ProtoHelper, AsStringToBytesMapMalformed) {
735 // message Maps {
736 // map<string, string> map_a = 1;
737 // map<string, string> map_b = 2;
738 // }
739 // clang-format off
740 std::uint8_t encoded_proto[] = {
741 // map_a["key_bar"] = "bar_a", key = 1
742 0x0a, 0x10,
743 0x0a, 0x07, 'k', 'e', 'y', '_', 'b', 'a', 'r', // map key
744 0x12, 0x05, 'b', 'a', 'r', '_', 'a', // map value
745
746 // map_a["key_foo"] = "foo_a", key = 0 (invalid)
747 0x02, 0x10,
748 0x0a, 0x07, 'k', 'e', 'y', '_', 'f', 'o', 'o',
749 0x12, 0x05, 'f', 'o', 'o', '_', 'a',
750 };
751 // clang-format on
752
753 stream::MemoryReader reader(as_bytes(span(encoded_proto)));
754 Message parser = Message(reader, sizeof(encoded_proto));
755
756 // Parse field 'map_a'
757 constexpr uint32_t kFieldNumber = 1;
758 StringMapParser<String> string_map = parser.AsStringToStringMap(kFieldNumber);
759
760 bool expected_ok_status[] = {true, false};
761 size_t count = 0;
762 for (StringToStringMapEntry entry : string_map) {
763 ASSERT_EQ(entry.ok(), expected_ok_status[count]);
764 ASSERT_EQ(entry.Key().ok(), expected_ok_status[count]);
765 ASSERT_EQ(entry.Value().ok(), expected_ok_status[count]);
766 count++;
767 }
768 ASSERT_EQ(count, 2ULL);
769 }
770
771 } // namespace
772 } // namespace pw::protobuf
773