xref: /aosp_15_r20/external/pigweed/pw_protobuf/message_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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