• 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_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