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