• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 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 <array>
16 #include <string_view>
17 #include <tuple>
18 
19 #include "gtest/gtest.h"
20 #include "pw_preprocessor/compiler.h"
21 #include "pw_protobuf/internal/codegen.h"
22 #include "pw_span/span.h"
23 #include "pw_status/status.h"
24 #include "pw_status/status_with_size.h"
25 #include "pw_stream/memory_stream.h"
26 
27 // These header files contain the code generated by the pw_protobuf plugin.
28 // They are re-generated every time the tests are built and are used by the
29 // tests to ensure that the interface remains consistent.
30 //
31 // The purpose of the tests in this file is primarily to verify that the
32 // generated C++ interface is valid rather than the correctness of the
33 // low-level encoder.
34 #include "pw_protobuf_test_protos/full_test.pwpb.h"
35 #include "pw_protobuf_test_protos/importer.pwpb.h"
36 #include "pw_protobuf_test_protos/optional.pwpb.h"
37 #include "pw_protobuf_test_protos/repeated.pwpb.h"
38 
39 namespace pw::protobuf {
40 namespace {
41 
42 using namespace ::pw::protobuf::test::pwpb;
43 
44 PW_MODIFY_DIAGNOSTICS_PUSH();
45 PW_MODIFY_DIAGNOSTIC(ignored, "-Wmissing-field-initializers");
46 
TEST(CodegenMessage,Equality)47 TEST(CodegenMessage, Equality) {
48   const Pigweed::Message one{
49       .magic_number = 0x49u,
50       .ziggy = -111,
51       .cycles = 0x40302010fecaaddeu,
52       .ratio = -1.42f,
53       .error_message = "not a typewriter",
54       .pigweed = {.status = Bool::FILE_NOT_FOUND},
55       .bin = Pigweed::Protobuf::Binary::ZERO,
56       .proto = {.bin = Proto::Binary::OFF,
57                 .pigweed_pigweed_bin = Pigweed::Pigweed::Binary::ZERO,
58                 .pigweed_protobuf_bin = Pigweed::Protobuf::Binary::ZERO,
59                 .meta =
60                     {
61                         .file_name = "/etc/passwd",
62                         .status = Pigweed::Protobuf::Compiler::Status::FUBAR,
63                         .protobuf_bin = Pigweed::Protobuf::Binary::ONE,
64                         .pigweed_bin = Pigweed::Pigweed::Binary::ONE,
65                     }},
66       .data = {std::byte{0x10},
67                std::byte{0x20},
68                std::byte{0x30},
69                std::byte{0x40},
70                std::byte{0x50},
71                std::byte{0x60},
72                std::byte{0x70},
73                std::byte{0x80}},
74       .bungle = -111,
75   };
76 
77   const Pigweed::Message two{
78       .magic_number = 0x49u,
79       .ziggy = -111,
80       .cycles = 0x40302010fecaaddeu,
81       .ratio = -1.42f,
82       .error_message = "not a typewriter",
83       .pigweed = {.status = Bool::FILE_NOT_FOUND},
84       .bin = Pigweed::Protobuf::Binary::ZERO,
85       .proto = {.bin = Proto::Binary::OFF,
86                 .pigweed_pigweed_bin = Pigweed::Pigweed::Binary::ZERO,
87                 .pigweed_protobuf_bin = Pigweed::Protobuf::Binary::ZERO,
88                 .meta =
89                     {
90                         .file_name = "/etc/passwd",
91                         .status = Pigweed::Protobuf::Compiler::Status::FUBAR,
92                         .protobuf_bin = Pigweed::Protobuf::Binary::ONE,
93                         .pigweed_bin = Pigweed::Pigweed::Binary::ONE,
94                     }},
95       .data = {std::byte{0x10},
96                std::byte{0x20},
97                std::byte{0x30},
98                std::byte{0x40},
99                std::byte{0x50},
100                std::byte{0x60},
101                std::byte{0x70},
102                std::byte{0x80}},
103       .bungle = -111,
104   };
105 
106   EXPECT_TRUE(one == two);
107 }
108 
TEST(CodegenMessage,CopyEquality)109 TEST(CodegenMessage, CopyEquality) {
110   Pigweed::Message one{
111       .magic_number = 0x49u,
112       .ziggy = -111,
113       .cycles = 0x40302010fecaaddeu,
114       .ratio = -1.42f,
115       .error_message = "not a typewriter",
116       .pigweed = {.status = Bool::FILE_NOT_FOUND},
117       .bin = Pigweed::Protobuf::Binary::ZERO,
118       .proto = {.bin = Proto::Binary::OFF,
119                 .pigweed_pigweed_bin = Pigweed::Pigweed::Binary::ZERO,
120                 .pigweed_protobuf_bin = Pigweed::Protobuf::Binary::ZERO,
121                 .meta =
122                     {
123                         .file_name = "/etc/passwd",
124                         .status = Pigweed::Protobuf::Compiler::Status::FUBAR,
125                         .protobuf_bin = Pigweed::Protobuf::Binary::ONE,
126                         .pigweed_bin = Pigweed::Pigweed::Binary::ONE,
127                     }},
128       .data = {std::byte{0x10},
129                std::byte{0x20},
130                std::byte{0x30},
131                std::byte{0x40},
132                std::byte{0x50},
133                std::byte{0x60},
134                std::byte{0x70},
135                std::byte{0x80}},
136       .bungle = -111,
137   };
138   Pigweed::Message two = one;
139 
140   EXPECT_TRUE(one == two);
141 }
142 
TEST(CodegenMessage,EmptyEquality)143 TEST(CodegenMessage, EmptyEquality) {
144   const Pigweed::Message one{};
145   const Pigweed::Message two{};
146 
147   EXPECT_TRUE(one == two);
148 }
149 
TEST(CodegenMessage,Inequality)150 TEST(CodegenMessage, Inequality) {
151   const Pigweed::Message one{
152       .magic_number = 0x49u,
153       .ziggy = -111,
154       .cycles = 0x40302010fecaaddeu,
155       .ratio = -1.42f,
156       .error_message = "not a typewriter",
157       .pigweed = {.status = Bool::FILE_NOT_FOUND},
158       .bin = Pigweed::Protobuf::Binary::ZERO,
159       .proto = {.bin = Proto::Binary::OFF,
160                 .pigweed_pigweed_bin = Pigweed::Pigweed::Binary::ZERO,
161                 .pigweed_protobuf_bin = Pigweed::Protobuf::Binary::ZERO,
162                 .meta =
163                     {
164                         .file_name = "/etc/passwd",
165                         .status = Pigweed::Protobuf::Compiler::Status::FUBAR,
166                         .protobuf_bin = Pigweed::Protobuf::Binary::ONE,
167                         .pigweed_bin = Pigweed::Pigweed::Binary::ONE,
168                     }},
169       .data = {std::byte{0x10},
170                std::byte{0x20},
171                std::byte{0x30},
172                std::byte{0x40},
173                std::byte{0x50},
174                std::byte{0x60},
175                std::byte{0x70},
176                std::byte{0x80}},
177       .bungle = -111,
178   };
179 
180   const Pigweed::Message two{
181       .magic_number = 0x43u,
182       .ziggy = 128,
183       .ratio = -1.42f,
184       .error_message = "not a typewriter",
185       .pigweed = {.status = Bool::TRUE},
186       .bin = Pigweed::Protobuf::Binary::ZERO,
187       .proto = {.bin = Proto::Binary::OFF,
188                 .pigweed_pigweed_bin = Pigweed::Pigweed::Binary::ZERO,
189                 .pigweed_protobuf_bin = Pigweed::Protobuf::Binary::ONE,
190                 .meta =
191                     {
192                         .file_name = "/etc/passwd",
193                         .status = Pigweed::Protobuf::Compiler::Status::FUBAR,
194                         .protobuf_bin = Pigweed::Protobuf::Binary::ONE,
195                         .pigweed_bin = Pigweed::Pigweed::Binary::ONE,
196                     }},
197       .data = {std::byte{0x20},
198                std::byte{0x30},
199                std::byte{0x40},
200                std::byte{0x50},
201                std::byte{0x60},
202                std::byte{0x70},
203                std::byte{0x80},
204                std::byte{0x90}},
205   };
206 
207   EXPECT_FALSE(one == two);
208 }
209 
TEST(CodegenMessage,TriviallyComparable)210 TEST(CodegenMessage, TriviallyComparable) {
211   static_assert(IsTriviallyComparable<IntegerMetadata::Message>());
212   static_assert(IsTriviallyComparable<KeyValuePair::Message>());
213   static_assert(!IsTriviallyComparable<Pigweed::Message>());
214 }
215 
TEST(CodegenMessage,ConstCopyable)216 TEST(CodegenMessage, ConstCopyable) {
217   const Pigweed::Message one{
218       .magic_number = 0x49u,
219       .ziggy = -111,
220       .cycles = 0x40302010fecaaddeu,
221       .ratio = -1.42f,
222       .error_message = "not a typewriter",
223       .pigweed = {.status = Bool::FILE_NOT_FOUND},
224       .bin = Pigweed::Protobuf::Binary::ZERO,
225       .proto = {.bin = Proto::Binary::OFF,
226                 .pigweed_pigweed_bin = Pigweed::Pigweed::Binary::ZERO,
227                 .pigweed_protobuf_bin = Pigweed::Protobuf::Binary::ZERO,
228                 .meta =
229                     {
230                         .file_name = "/etc/passwd",
231                         .status = Pigweed::Protobuf::Compiler::Status::FUBAR,
232                         .protobuf_bin = Pigweed::Protobuf::Binary::ONE,
233                         .pigweed_bin = Pigweed::Pigweed::Binary::ONE,
234                     }},
235       .data = {std::byte{0x10},
236                std::byte{0x20},
237                std::byte{0x30},
238                std::byte{0x40},
239                std::byte{0x50},
240                std::byte{0x60},
241                std::byte{0x70},
242                std::byte{0x80}},
243       .bungle = -111,
244   };
245   Pigweed::Message two = one;
246 
247   EXPECT_TRUE(one == two);
248 }
249 
TEST(CodegenMessage,FixReservedIdentifiers)250 TEST(CodegenMessage, FixReservedIdentifiers) {
251   // This test checks that the code was generated as expected, so it will simply
252   // fail to compile if its expectations are not met.
253 
254   // Make sure that the `signed` field was renamed to `signed_`.
255   std::ignore = IntegerMetadata::Message{
256       .bits = 32,
257       .signed_ = true,
258       .null = false,
259   };
260 
261   // Make sure that the internal enum describing the struct's fields was
262   // generated as expected:
263   // - `BITS` doesn't need an underscore.
264   // - `SIGNED_` has an underscore to match the corresponding `signed_` field.
265   // - `NULL_` has an underscore to avoid a collision with `NULL` (even though
266   //   the field `null` doesn't have or need an underscore).
267   std::ignore = IntegerMetadata::Fields::kBits;
268   std::ignore = IntegerMetadata::Fields::kSigned;
269   std::ignore = IntegerMetadata::Fields::kNull;
270 
271   // Make sure that the `ReservedWord` enum values were renamed as expected.
272   // Specifically, only enum-value names that are reserved in UPPER_SNAKE_CASE
273   // should be modified. Names that are only reserved in lower_snake_case should
274   // be left alone since they'll never appear in that form in the generated
275   // code.
276   std::ignore = ReservedWord::NULL_;    // Add underscore since NULL is a macro.
277   std::ignore = ReservedWord::kNull;    // No underscore necessary.
278   std::ignore = ReservedWord::INT;      // No underscore necessary.
279   std::ignore = ReservedWord::kInt;     // No underscore necessary.
280   std::ignore = ReservedWord::RETURN;   // No underscore necessary.
281   std::ignore = ReservedWord::kReturn;  // No underscore necessary.
282   std::ignore = ReservedWord::BREAK;    // No underscore necessary.
283   std::ignore = ReservedWord::kBreak;   // No underscore necessary.
284   std::ignore = ReservedWord::FOR;      // No underscore necessary.
285   std::ignore = ReservedWord::kFor;     // No underscore necessary.
286   std::ignore = ReservedWord::DO;       // No underscore necessary.
287   std::ignore = ReservedWord::kDo;      // No underscore necessary.
288 
289   // Instantiate an extremely degenerately named set of nested types in order to
290   // make sure that name conflicts with the codegen internals are properly
291   // prevented.
292   std::ignore = Function::Message{
293       .description =
294           Function::Message_::Message{
295               .content = "multiplication (mod 5)",
296           },
297       .domain_field = Function::Fields_::INTEGERS_MOD_5,
298       .codomain_field = Function::Fields_::INTEGERS_MOD_5,
299   };
300 
301   // Check for expected values of `enum class Function::Fields`:
302   std::ignore = Function::Fields::kDescription;
303   std::ignore = Function::Fields::kDomainField;
304   std::ignore = Function::Fields::kCodomainField;
305 
306   // Check for expected values of `enum class Function::Message_::Fields`:
307   std::ignore = Function::Message_::Fields::kContent;
308 
309   // Check for expected values of `enum class Function::Fields_`:
310   std::ignore = Function::Fields_::NONE;
311   std::ignore = Function::Fields_::kNone;
312   std::ignore = Function::Fields_::COMPLEX_NUMBERS;
313   std::ignore = Function::Fields_::kComplexNumbers;
314   std::ignore = Function::Fields_::INTEGERS_MOD_5;
315   std::ignore = Function::Fields_::kIntegersMod5;
316   std::ignore = Function::Fields_::MEROMORPHIC_FUNCTIONS_ON_COMPLEX_PLANE;
317   std::ignore = Function::Fields_::kMeromorphicFunctionsOnComplexPlane;
318   std::ignore = Function::Fields_::OTHER;
319   std::ignore = Function::Fields_::kOther;
320 }
321 
322 PW_MODIFY_DIAGNOSTICS_POP();
323 
TEST(CodegenMessage,Read)324 TEST(CodegenMessage, Read) {
325   // clang-format off
326   constexpr uint8_t proto_data[] = {
327     // pigweed.magic_number
328     0x08, 0x49,
329     // pigweed.ziggy
330     0x10, 0xdd, 0x01,
331     // pigweed.cycles
332     0x19, 0xde, 0xad, 0xca, 0xfe, 0x10, 0x20, 0x30, 0x40,
333     // pigweed.ratio
334     0x25, 0x8f, 0xc2, 0xb5, 0xbf,
335     // pigweed.error_message
336     0x2a, 0x10, 'n', 'o', 't', ' ', 'a', ' ',
337     't', 'y', 'p', 'e', 'w', 'r', 'i', 't', 'e', 'r',
338     // pigweed.bin
339     0x40, 0x01,
340     // pigweed.bungle
341     0x70, 0x91, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01,
342   };
343   // clang-format on
344 
345   stream::MemoryReader reader(as_bytes(span(proto_data)));
346   Pigweed::StreamDecoder pigweed(reader);
347 
348   Pigweed::Message message{};
349   const auto status = pigweed.Read(message);
350   ASSERT_EQ(status, OkStatus());
351 
352   constexpr std::string_view kExpectedErrorMessage{"not a typewriter"};
353 
354   EXPECT_EQ(message.magic_number, 0x49u);
355   EXPECT_EQ(message.ziggy, -111);
356   EXPECT_EQ(message.cycles, 0x40302010fecaaddeu);
357   EXPECT_EQ(message.ratio, -1.42f);
358   EXPECT_EQ(message.error_message.size(), kExpectedErrorMessage.size());
359   EXPECT_EQ(std::memcmp(message.error_message.data(),
360                         kExpectedErrorMessage.data(),
361                         kExpectedErrorMessage.size()),
362             0);
363   EXPECT_EQ(message.bin, Pigweed::Protobuf::Binary::ZERO);
364   EXPECT_EQ(message.bungle, -111);
365 }
366 
TEST(CodegenMessage,ReadNonPackedScalar)367 TEST(CodegenMessage, ReadNonPackedScalar) {
368   // clang-format off
369   constexpr uint8_t proto_data[] = {
370     // uint32s[], v={0, 16, 32, 48}
371     0x08, 0x00,
372     0x08, 0x10,
373     0x08, 0x20,
374     0x08, 0x30,
375     // fixed32s[]. v={0, 16, 32, 48}
376     0x35, 0x00, 0x00, 0x00, 0x00,
377     0x35, 0x10, 0x00, 0x00, 0x00,
378     0x35, 0x20, 0x00, 0x00, 0x00,
379     0x35, 0x30, 0x00, 0x00, 0x00,
380   };
381   // clang-format on
382 
383   stream::MemoryReader reader(as_bytes(span(proto_data)));
384   RepeatedTest::StreamDecoder repeated_test(reader);
385 
386   RepeatedTest::Message message{};
387   const auto status = repeated_test.Read(message);
388   ASSERT_EQ(status, OkStatus());
389 
390   ASSERT_EQ(message.uint32s.size(), 4u);
391   for (int i = 0; i < 4; ++i) {
392     EXPECT_EQ(message.uint32s[i], i * 16u);
393   }
394 
395   ASSERT_EQ(message.fixed32s.size(), 4u);
396   for (int i = 0; i < 4; ++i) {
397     EXPECT_EQ(message.fixed32s[i], i * 16u);
398   }
399 }
400 
TEST(CodegenMessage,ReadPackedScalar)401 TEST(CodegenMessage, ReadPackedScalar) {
402   // clang-format off
403   constexpr uint8_t proto_data[] = {
404     // uint32s[], v={0, 16, 32, 48}
405     0x0a, 0x04,
406     0x00,
407     0x10,
408     0x20,
409     0x30,
410     // fixed32s[]. v={0, 16, 32, 48}
411     0x32, 0x10,
412     0x00, 0x00, 0x00, 0x00,
413     0x10, 0x00, 0x00, 0x00,
414     0x20, 0x00, 0x00, 0x00,
415     0x30, 0x00, 0x00, 0x00,
416   };
417   // clang-format on
418 
419   stream::MemoryReader reader(as_bytes(span(proto_data)));
420   RepeatedTest::StreamDecoder repeated_test(reader);
421 
422   RepeatedTest::Message message{};
423   const auto status = repeated_test.Read(message);
424   ASSERT_EQ(status, OkStatus());
425 
426   ASSERT_EQ(message.uint32s.size(), 4u);
427   for (int i = 0; i < 4; ++i) {
428     EXPECT_EQ(message.uint32s[i], i * 16u);
429   }
430 
431   ASSERT_EQ(message.fixed32s.size(), 4u);
432   for (int i = 0; i < 4; ++i) {
433     EXPECT_EQ(message.fixed32s[i], i * 16u);
434   }
435 }
436 
TEST(CodegenMessage,ReadPackedScalarRepeated)437 TEST(CodegenMessage, ReadPackedScalarRepeated) {
438   // clang-format off
439   constexpr uint8_t proto_data[] = {
440     // uint32s[], v={0, 16, 32, 48}
441     0x0a, 0x04,
442     0x00,
443     0x10,
444     0x20,
445     0x30,
446     // uint32s[], v={64, 80, 96, 112}
447     0x0a, 0x04,
448     0x40,
449     0x50,
450     0x60,
451     0x70,
452     // fixed32s[]. v={0, 16, 32, 48}
453     0x32, 0x10,
454     0x00, 0x00, 0x00, 0x00,
455     0x10, 0x00, 0x00, 0x00,
456     0x20, 0x00, 0x00, 0x00,
457     0x30, 0x00, 0x00, 0x00,
458     // fixed32s[]. v={64, 80, 96, 112}
459     0x32, 0x10,
460     0x40, 0x00, 0x00, 0x00,
461     0x50, 0x00, 0x00, 0x00,
462     0x60, 0x00, 0x00, 0x00,
463     0x70, 0x00, 0x00, 0x00,
464   };
465   // clang-format on
466 
467   stream::MemoryReader reader(as_bytes(span(proto_data)));
468   RepeatedTest::StreamDecoder repeated_test(reader);
469 
470   RepeatedTest::Message message{};
471   const auto status = repeated_test.Read(message);
472   ASSERT_EQ(status, OkStatus());
473 
474   ASSERT_EQ(message.uint32s.size(), 8u);
475   for (int i = 0; i < 8; ++i) {
476     EXPECT_EQ(message.uint32s[i], i * 16u);
477   }
478 
479   ASSERT_EQ(message.fixed32s.size(), 8u);
480   for (int i = 0; i < 8; ++i) {
481     EXPECT_EQ(message.fixed32s[i], i * 16u);
482   }
483 }
484 
TEST(CodegenMessage,ReadPackedScalarExhausted)485 TEST(CodegenMessage, ReadPackedScalarExhausted) {
486   // clang-format off
487   constexpr uint8_t proto_data[] = {
488     // uint32s[], v={0, 16, 32, 48, 64, 80, 96, 112, 128}
489     0x0a, 0x09,
490     0x00,
491     0x10,
492     0x20,
493     0x30,
494     0x40,
495     0x50,
496     0x60,
497     0x70,
498     0x80,
499   };
500   // clang-format on
501 
502   stream::MemoryReader reader(as_bytes(span(proto_data)));
503   RepeatedTest::StreamDecoder repeated_test(reader);
504 
505   // uint32s has max_size=8, so this will exhaust the vector.
506   RepeatedTest::Message message{};
507   const auto status = repeated_test.Read(message);
508   ASSERT_EQ(status, Status::ResourceExhausted());
509 }
510 
TEST(CodegenMessage,ReadPackedScalarCallback)511 TEST(CodegenMessage, ReadPackedScalarCallback) {
512   // clang-format off
513   constexpr uint8_t proto_data[] = {
514     // sint32s[], v={-25, -1, 0, 1, 25}
515     0x12, 0x05,
516     0x31,
517     0x01,
518     0x00,
519     0x02,
520     0x32,
521   };
522   // clang-format on
523 
524   stream::MemoryReader reader(as_bytes(span(proto_data)));
525   RepeatedTest::StreamDecoder repeated_test(reader);
526 
527   // sint32s is a repeated field declared without max_count, so requirses a
528   // callback to be decoded.
529   RepeatedTest::Message message{};
530   message.sint32s.SetDecoder([](RepeatedTest::StreamDecoder& decoder) {
531     EXPECT_EQ(decoder.Field().value(), RepeatedTest::Fields::kSint32s);
532 
533     pw::Vector<int32_t, 8> sint32s{};
534     const auto status = decoder.ReadSint32s(sint32s);
535     EXPECT_EQ(status, OkStatus());
536 
537     EXPECT_EQ(sint32s.size(), 5u);
538     EXPECT_EQ(sint32s[0], -25);
539     EXPECT_EQ(sint32s[1], -1);
540     EXPECT_EQ(sint32s[2], 0);
541     EXPECT_EQ(sint32s[3], 1);
542     EXPECT_EQ(sint32s[4], 25);
543 
544     return status;
545   });
546 
547   const auto status = repeated_test.Read(message);
548   ASSERT_EQ(status, OkStatus());
549 }
550 
TEST(CodegenMessage,ReadPackedScalarFixedLength)551 TEST(CodegenMessage, ReadPackedScalarFixedLength) {
552   // clang-format off
553   constexpr uint8_t proto_data[] = {
554     // uint64s[], v={1000, 2000, 3000, 4000}
555     0x42, 0x08, 0xe8, 0x07, 0xd0, 0x0f, 0xb8, 0x17, 0xa0, 0x1f,
556     // doubles[], v={3.14159, 2.71828}
557     0x22, 0x10,
558     0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x09, 0x40,
559     0x90, 0xf7, 0xaa, 0x95, 0x09, 0xbf, 0x05, 0x40,
560   };
561   // clang-format on
562 
563   stream::MemoryReader reader(as_bytes(span(proto_data)));
564   RepeatedTest::StreamDecoder repeated_test(reader);
565 
566   RepeatedTest::Message message{};
567   const auto status = repeated_test.Read(message);
568   ASSERT_EQ(status, OkStatus());
569 
570   EXPECT_EQ(message.uint64s[0], 1000u);
571   EXPECT_EQ(message.uint64s[1], 2000u);
572   EXPECT_EQ(message.uint64s[2], 3000u);
573   EXPECT_EQ(message.uint64s[3], 4000u);
574 
575   EXPECT_EQ(message.doubles[0], 3.14159);
576   EXPECT_EQ(message.doubles[1], 2.71828);
577 }
578 
TEST(CodegenMessage,ReadPackedScalarFixedLengthShort)579 TEST(CodegenMessage, ReadPackedScalarFixedLengthShort) {
580   // clang-format off
581   constexpr uint8_t proto_data[] = {
582     // uint64s[], v={1000, 2000}
583     0x42, 0x04, 0xe8, 0x07, 0xd0, 0x0f,
584     // doubles[], v={3.14159}
585     0x22, 0x08,
586     0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x09, 0x40,
587   };
588   // clang-format on
589 
590   stream::MemoryReader reader(as_bytes(span(proto_data)));
591   RepeatedTest::StreamDecoder repeated_test(reader);
592 
593   RepeatedTest::Message message{};
594   const auto status = repeated_test.Read(message);
595   ASSERT_EQ(status, OkStatus());
596 
597   EXPECT_EQ(message.uint64s[0], 1000u);
598   EXPECT_EQ(message.uint64s[1], 2000u);
599   EXPECT_EQ(message.uint64s[2], 0u);
600   EXPECT_EQ(message.uint64s[3], 0u);
601 
602   EXPECT_EQ(message.doubles[0], 3.14159);
603   EXPECT_EQ(message.doubles[1], 0);
604 }
605 
TEST(CodegenMessage,ReadPackedScalarVarintFixedLengthExhausted)606 TEST(CodegenMessage, ReadPackedScalarVarintFixedLengthExhausted) {
607   // clang-format off
608   constexpr uint8_t proto_data[] = {
609     // uint64s[], v={0, 1000, 2000, 3000, 4000}
610     0x42, 0x09, 0x08, 0xe8, 0x07, 0xd0, 0x0f, 0xb8, 0x17, 0xa0, 0x1f,
611   };
612   // clang-format on
613 
614   stream::MemoryReader reader(as_bytes(span(proto_data)));
615   RepeatedTest::StreamDecoder repeated_test(reader);
616 
617   RepeatedTest::Message message{};
618   const auto status = repeated_test.Read(message);
619   ASSERT_EQ(status, Status::ResourceExhausted());
620 }
621 
TEST(CodegenMessage,ReadPackedScalarFixedLengthExhausted)622 TEST(CodegenMessage, ReadPackedScalarFixedLengthExhausted) {
623   // clang-format off
624   constexpr uint8_t proto_data[] = {
625     // doubles[], v={3.14159, 2.71828, 1.41429, 1.73205}
626     0x22, 0x20,
627     0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x09, 0x40,
628     0x90, 0xf7, 0xaa, 0x95, 0x09, 0xbf, 0x05, 0x40,
629     0x1b, 0xf5, 0x10, 0x8d, 0xee, 0xa0, 0xf6, 0x3f,
630     0xbc, 0x96, 0x90, 0x0f, 0x7a, 0xb6, 0xfb, 0x3f,
631   };
632   // clang-format on
633 
634   stream::MemoryReader reader(as_bytes(span(proto_data)));
635   RepeatedTest::StreamDecoder repeated_test(reader);
636 
637   RepeatedTest::Message message{};
638   const auto status = repeated_test.Read(message);
639   ASSERT_EQ(status, Status::ResourceExhausted());
640 }
641 
TEST(CodegenMessage,ReadPackedEnum)642 TEST(CodegenMessage, ReadPackedEnum) {
643   // clang-format off
644   constexpr uint8_t proto_data[] = {
645     // enums[], v={RED, GREEN, AMBER, RED}
646     0x4a, 0x04, 0x00, 0x02, 0x01, 0x00,
647   };
648   // clang-format on
649 
650   stream::MemoryReader reader(as_bytes(span(proto_data)));
651   RepeatedTest::StreamDecoder repeated_test(reader);
652 
653   RepeatedTest::Message message{};
654   const auto status = repeated_test.Read(message);
655   ASSERT_EQ(status, OkStatus());
656 
657   ASSERT_EQ(message.enums.size(), 4u);
658   for (int i = 0; i < 4; ++i) {
659     EXPECT_TRUE(IsValidEnum(message.enums[i]));
660   }
661 
662   EXPECT_EQ(message.enums[0], Enum::RED);
663   EXPECT_EQ(message.enums[1], Enum::GREEN);
664   EXPECT_EQ(message.enums[2], Enum::AMBER);
665   EXPECT_EQ(message.enums[3], Enum::RED);
666 }
667 
TEST(CodegenMessage,ReadStringExhausted)668 TEST(CodegenMessage, ReadStringExhausted) {
669   // clang-format off
670   constexpr uint8_t proto_data[] = {
671     // pigweed.error_message
672     0x2a, 0xd3, 0x01, 'T', 'h', 'i', 's', ' ', 'l', 'a', 'b', 'e', 'l', ' ', 'i',
673     's', ' ', 't', 'h', 'e', ' ', 't', 'a', 'r', 'g', 'e', 't', ' ', 'o', 'f',
674     ' ', 'a', ' ', 'g', 'o', 't', 'o', ' ', 'f', 'r', 'o', 'm', ' ', 'o', 'u',
675     't', 's', 'i', 'd', 'e', ' ', 'o', 'f', ' ', 't', 'h', 'e', ' ', 'b', 'l',
676     'o', 'c', 'k', ' ', 'c', 'o', 'n', 't', 'a', 'i', 'n', 'i', 'n', 'g', ' ',
677     't', 'h', 'i', 's', ' ', 'l', 'a', 'b', 'e', 'l', ' ', 'A', 'N', 'D', ' ',
678     't', 'h', 'i', 's', ' ', 'b', 'l', 'o', 'c', 'k', ' ', 'h', 'a', 's', ' ',
679     'a', 'n', ' ', 'a', 'u', 't', 'o', 'm', 'a', 't', 'i', 'c', ' ', 'v', 'a',
680     'r', 'i', 'a', 'b', 'l', 'e', ' ', 'w', 'i', 't', 'h', ' ', 'a', 'n', ' ',
681     'i', 'n', 'i', 't', 'i', 'a', 'l', 'i', 'z', 'e', 'r', ' ', 'A', 'N', 'D',
682     ' ', 'y', 'o', 'u', 'r', ' ', 'w', 'i', 'n', 'd', 'o', 'w', ' ', 'w', 'a',
683     's', 'n', '\'', 't', ' ', 'w', 'i', 'd', 'e', ' ', 'e', 'n', 'o', 'u', 'g',
684     'h', ' ', 't', 'o', ' ', 'r', 'e', 'a', 'd', ' ', 't', 'h', 'i', 's', ' ',
685     'w', 'h', 'o', 'l', 'e', ' ', 'e', 'r', 'r', 'o', 'r', ' ', 'm', 'e', 's',
686     's', 'a', 'g', 'e'
687   };
688   // clang-format on
689 
690   stream::MemoryReader reader(as_bytes(span(proto_data)));
691   Pigweed::StreamDecoder pigweed(reader);
692 
693   Pigweed::Message message{};
694   const auto status = pigweed.Read(message);
695   ASSERT_EQ(status, Status::ResourceExhausted());
696 }
697 
TEST(CodegenMessage,ReadStringCallback)698 TEST(CodegenMessage, ReadStringCallback) {
699   // clang-format off
700   constexpr uint8_t proto_data[] = {
701     // pigweed.description
702     0x62, 0x5c, 'a', 'n', ' ', 'o', 'p', 'e', 'n', ' ', 's', 'o', 'u', 'r', 'c',
703     'e', ' ', 'c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n', ' ', 'o', 'f',
704     ' ', 'e', 'm', 'b', 'e', 'd', 'd', 'e', 'd', '-', 't', 'a', 'r', 'g', 'e',
705     't', 'e', 'd', ' ', 'l', 'i', 'b', 'r', 'a', 'r', 'i', 'e', 's', '-', 'o',
706     'r', ' ', 'a', 's', ' ', 'w', 'e', ' ', 'l', 'i', 'k', 'e', ' ', 't', 'o',
707     ' ', 'c', 'a', 'l', 'l', ' ', 't', 'h', 'e', 'm', ',', ' ', 'm', 'o', 'd',
708     'u', 'l', 'e', 's'
709   };
710   // clang-format on
711 
712   stream::MemoryReader reader(as_bytes(span(proto_data)));
713   Pigweed::StreamDecoder pigweed(reader);
714 
715   // pigweed.description has no max_size specified so a callback must be
716   // set to read the value if present.
717   Pigweed::Message message{};
718   message.description.SetDecoder([](Pigweed::StreamDecoder& decoder) {
719     EXPECT_EQ(decoder.Field().value(), Pigweed::Fields::kDescription);
720 
721     constexpr std::string_view kExpectedDescription{
722         "an open source collection of embedded-targeted libraries-or as we "
723         "like to call them, modules"};
724 
725     std::array<char, 128> description{};
726     const auto sws = decoder.ReadDescription(description);
727     EXPECT_EQ(sws.status(), OkStatus());
728     EXPECT_EQ(sws.size(), kExpectedDescription.size());
729     EXPECT_EQ(std::memcmp(description.data(),
730                           kExpectedDescription.data(),
731                           kExpectedDescription.size()),
732               0);
733 
734     return sws.status();
735   });
736 
737   const auto status = pigweed.Read(message);
738   ASSERT_EQ(status, OkStatus());
739 }
740 
TEST(CodegenMessage,ReadMultipleString)741 TEST(CodegenMessage, ReadMultipleString) {
742   // clang-format off
743   constexpr uint8_t proto_data[] = {
744     // pigweed.error_message
745     0x2a, 0x10, 'n', 'o', 't', ' ', 'a', ' ',
746     't', 'y', 'p', 'e', 'w', 'r', 'i', 't', 'e', 'r',
747     // pigweed.error_message
748     0x02a, 0x07, 'o', 'n', ' ', 'f', 'i', 'r', 'e'
749   };
750   // clang-format on
751 
752   stream::MemoryReader reader(as_bytes(span(proto_data)));
753   Pigweed::StreamDecoder pigweed(reader);
754 
755   Pigweed::Message message{};
756   const auto status = pigweed.Read(message);
757   ASSERT_EQ(status, OkStatus());
758 
759   constexpr std::string_view kExpectedErrorMessage{"on fire"};
760 
761   EXPECT_EQ(message.error_message.size(), kExpectedErrorMessage.size());
762   EXPECT_EQ(std::memcmp(message.error_message.data(),
763                         kExpectedErrorMessage.data(),
764                         kExpectedErrorMessage.size()),
765             0);
766 }
767 
TEST(CodegenMessage,ReadRepeatedStrings)768 TEST(CodegenMessage, ReadRepeatedStrings) {
769   // clang-format off
770   constexpr uint8_t proto_data[] = {
771     // repeated.strings
772     0x1a, 0x25, 'i', 'f', ' ', 'm', 'u', 's', 'i', 'c', ' ', 'b', 'e', ' ',
773     't', 'h', 'e', ' ', 'f', 'o', 'o', 'd', ' ', 'o', 'f', ' ',
774     'l', 'o', 'v', 'e', ',', ' ', 'p', 'l', 'a', 'y', ' ', 'o', 'n',
775     // repeated.strings
776     0x1a, 0x26, 'g', 'i', 'v', 'e', ' ', 'm', 'e', ' ', 'e', 'x', 'c', 'e',
777     's', 's', ' ', 'o', 'f', ' ', 'i', 't', ',', ' ', 't', 'h', 'a', 't', ',',
778     ' ', 's', 'u', 'r', 'f', 'e', 'i', 't', 'i', 'n', 'g',
779     // repeated.strings
780     0x1a, 0x23, 't', 'h', 'e', ' ', 'a', 'p', 'p', 'e', 't', 'i', 't', 'e', ' ',
781     'm', 'a', 'y', ' ', 's', 'i', 'c', 'k', 'e', 'n', ',', ' ', 'a', 'n', 'd',
782     ' ', 's', 'o', ' ', 'd', 'i', 'e',
783   };
784   // clang-format on
785 
786   stream::MemoryReader reader(as_bytes(span(proto_data)));
787   RepeatedTest::StreamDecoder repeated_test(reader);
788 
789   // Repeated strings require a callback to avoid forcing multi-dimensional
790   // arrays upon the caller.
791   RepeatedTest::Message message{};
792   int i = 0;
793   message.strings.SetDecoder([&i](RepeatedTest::StreamDecoder& decoder) {
794     EXPECT_EQ(decoder.Field().value(), RepeatedTest::Fields::kStrings);
795 
796     constexpr std::string_view kExpectedStrings[] = {
797         {"if music be the food of love, play on"},
798         {"give me excess of it, that, surfeiting"},
799         {"the appetite may sicken, and so die"}};
800 
801     std::array<char, 40> strings{};
802     const StatusWithSize sws = decoder.ReadStrings(strings);
803     EXPECT_EQ(sws.status(), OkStatus());
804     EXPECT_EQ(sws.size(), kExpectedStrings[i].size());
805     EXPECT_EQ(std::memcmp(strings.data(),
806                           kExpectedStrings[i].data(),
807                           kExpectedStrings[i].size()),
808               0);
809 
810     ++i;
811     return sws.status();
812   });
813 
814   const auto status = repeated_test.Read(message);
815   ASSERT_EQ(status, OkStatus());
816 }
817 
TEST(CodegenMessage,ReadForcedCallback)818 TEST(CodegenMessage, ReadForcedCallback) {
819   // clang-format off
820   constexpr uint8_t proto_data[] = {
821     // pigweed.special_property
822     0x68, 0x2a,
823   };
824   // clang-format on
825 
826   stream::MemoryReader reader(as_bytes(span(proto_data)));
827   Pigweed::StreamDecoder pigweed(reader);
828 
829   // pigweed.special_property has use_callback=true to force the use of a
830   // callback even though it's a simple scalar.
831   Pigweed::Message message{};
832   message.special_property.SetDecoder([](Pigweed::StreamDecoder& decoder) {
833     EXPECT_EQ(decoder.Field().value(), Pigweed::Fields::kSpecialProperty);
834 
835     pw::Result<uint32_t> result = decoder.ReadSpecialProperty();
836     EXPECT_EQ(result.status(), OkStatus());
837     EXPECT_EQ(result.value(), 42u);
838 
839     return result.status();
840   });
841   const auto status = pigweed.Read(message);
842   ASSERT_EQ(status, OkStatus());
843 }
844 
TEST(CodegenMessage,ReadMissingCallback)845 TEST(CodegenMessage, ReadMissingCallback) {
846   // clang-format off
847   constexpr uint8_t proto_data[] = {
848     // repeated.strings
849     0x1a, 0x25, 'i', 'f', ' ', 'm', 'u', 's', 'i', 'c', ' ', 'b', 'e', ' ',
850     't', 'h', 'e', ' ', 'f', 'o', 'o', 'd', ' ', 'o', 'f', ' ',
851     'l', 'o', 'v', 'e', ',', ' ', 'p', 'l', 'a', 'y', ' ', 'o', 'n',
852     // repeated.strings
853     0x1a, 0x26, 'g', 'i', 'v', 'e', ' ', 'm', 'e', ' ', 'e', 'x', 'c', 'e',
854     's', 's', ' ', 'o', 'f', ' ', 'i', 't', ',', ' ', 't', 'h', 'a', 't', ',',
855     ' ', 's', 'u', 'r', 'f', 'e', 'i', 't', 'i', 'n', 'g',
856     // repeated.strings
857     0x1a, 0x23, 't', 'h', 'e', ' ', 'a', 'p', 'p', 'e', 't', 'i', 't', 'e', ' ',
858     'm', 'a', 'y', ' ', 's', 'i', 'c', 'k', 'e', 'n', ',', ' ', 'a', 'n', 'd',
859     ' ', 's', 'o', ' ', 'd', 'i', 'e',
860   };
861   // clang-format on
862 
863   stream::MemoryReader reader(as_bytes(span(proto_data)));
864   RepeatedTest::StreamDecoder repeated_test(reader);
865 
866   // Failing to set a callback will give a DataLoss error if that field is
867   // present in the decoded data.
868   RepeatedTest::Message message{};
869   const auto status = repeated_test.Read(message);
870   ASSERT_EQ(status, Status::DataLoss());
871 }
872 
TEST(CodegenMessage,ReadFixedLength)873 TEST(CodegenMessage, ReadFixedLength) {
874   // clang-format off
875   constexpr uint8_t proto_data[] = {
876     // pigweed.data
877     0x5a, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
878   };
879   // clang-format on
880 
881   stream::MemoryReader reader(as_bytes(span(proto_data)));
882   Pigweed::StreamDecoder pigweed(reader);
883 
884   Pigweed::Message message{};
885   const auto status = pigweed.Read(message);
886   ASSERT_EQ(status, OkStatus());
887 
888   EXPECT_EQ(message.data[0], std::byte{0x01});
889   EXPECT_EQ(message.data[1], std::byte{0x02});
890   EXPECT_EQ(message.data[2], std::byte{0x03});
891   EXPECT_EQ(message.data[3], std::byte{0x04});
892   EXPECT_EQ(message.data[4], std::byte{0x05});
893   EXPECT_EQ(message.data[5], std::byte{0x06});
894   EXPECT_EQ(message.data[6], std::byte{0x07});
895   EXPECT_EQ(message.data[7], std::byte{0x08});
896 }
897 
TEST(CodegenMessage,ReadFixedLengthShort)898 TEST(CodegenMessage, ReadFixedLengthShort) {
899   // clang-format off
900   constexpr uint8_t proto_data[] = {
901     // pigweed.data
902     0x5a, 0x04, 0x01, 0x02, 0x03, 0x04
903   };
904   // clang-format on
905 
906   stream::MemoryReader reader(as_bytes(span(proto_data)));
907   Pigweed::StreamDecoder pigweed(reader);
908 
909   Pigweed::Message message{};
910   const auto status = pigweed.Read(message);
911   ASSERT_EQ(status, OkStatus());
912 
913   EXPECT_EQ(message.data[0], std::byte{0x01});
914   EXPECT_EQ(message.data[1], std::byte{0x02});
915   EXPECT_EQ(message.data[2], std::byte{0x03});
916   EXPECT_EQ(message.data[3], std::byte{0x04});
917   // Remaining bytes are whatever you initialized them to.
918   EXPECT_EQ(message.data[4], std::byte{0x00});
919   EXPECT_EQ(message.data[5], std::byte{0x00});
920   EXPECT_EQ(message.data[6], std::byte{0x00});
921   EXPECT_EQ(message.data[7], std::byte{0x00});
922 }
923 
TEST(CodegenMessage,ReadFixedLengthExhausted)924 TEST(CodegenMessage, ReadFixedLengthExhausted) {
925   // clang-format off
926   constexpr uint8_t proto_data[] = {
927     // pigweed.data
928     0x5a, 0x0c, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
929     0x09, 0x0a, 0x0b, 0x0c
930   };
931   // clang-format on
932 
933   stream::MemoryReader reader(as_bytes(span(proto_data)));
934   Pigweed::StreamDecoder pigweed(reader);
935 
936   Pigweed::Message message{};
937   const auto status = pigweed.Read(message);
938   ASSERT_EQ(status, Status::ResourceExhausted());
939 }
940 
TEST(CodegenMessage,ReadNested)941 TEST(CodegenMessage, ReadNested) {
942   // clang-format off
943   constexpr uint8_t proto_data[] = {
944     // pigweed.magic_number
945     0x08, 0x49,
946     // pigweed.pigweed
947     0x3a, 0x02,
948     // pigweed.pigweed.status
949     0x08, 0x02,
950     // pigweed.ziggy
951     0x10, 0xdd, 0x01,
952   };
953   // clang-format on
954 
955   stream::MemoryReader reader(as_bytes(span(proto_data)));
956   Pigweed::StreamDecoder pigweed(reader);
957 
958   Pigweed::Message message{};
959   const auto status = pigweed.Read(message);
960   ASSERT_EQ(status, OkStatus());
961 
962   EXPECT_EQ(message.magic_number, 0x49u);
963   EXPECT_EQ(message.pigweed.status, Bool::FILE_NOT_FOUND);
964   EXPECT_EQ(message.ziggy, -111);
965 }
966 
TEST(CodegenMessage,ReadNestedImported)967 TEST(CodegenMessage, ReadNestedImported) {
968   // clang-format off
969   constexpr uint8_t proto_data[] = {
970     // period.start
971     0x0a, 0x08,
972     // period.start.seconds v=1517949900
973     0x08, 0xcc, 0xa7, 0xe8, 0xd3, 0x05,
974     // period.start.nanoseconds v=0
975     0x10, 0x00,
976     // period.end
977     0x12, 0x08,
978     // period.end.seconds, v=1517950378
979     0x08, 0xaa, 0xab, 0xe8, 0xd3, 0x05,
980     // period.end.nanoseconds, v=0
981     0x10, 0x00,
982   };
983   // clang-format on
984 
985   stream::MemoryReader reader(as_bytes(span(proto_data)));
986   Period::StreamDecoder period(reader);
987 
988   // Messages imported from another file can be directly embedded in a message.
989   Period::Message message{};
990   const auto status = period.Read(message);
991   ASSERT_EQ(status, OkStatus());
992 
993   EXPECT_EQ(message.start.seconds, 1517949900u);
994   EXPECT_EQ(message.start.nanoseconds, 0u);
995   EXPECT_EQ(message.end.seconds, 1517950378u);
996   EXPECT_EQ(message.end.nanoseconds, 0u);
997 }
998 
TEST(CodegenMessage,ReadNestedRepeated)999 TEST(CodegenMessage, ReadNestedRepeated) {
1000   // clang-format off
1001   constexpr uint8_t proto_data[] = {
1002     // repeated.structs
1003     0x2a, 0x04,
1004     // repeated.structs.one v=16
1005     0x08, 0x10,
1006     // repeated.structs.two v=32
1007     0x10, 0x20,
1008     // repeated.structs
1009     0x2a, 0x04,
1010     // repeated.structs.one v=48
1011     0x08, 0x30,
1012     // repeated.structs.two v=64
1013     0x10, 0x40,
1014   };
1015   // clang-format on
1016 
1017   stream::MemoryReader reader(as_bytes(span(proto_data)));
1018   RepeatedTest::StreamDecoder repeated_test(reader);
1019 
1020   // Repeated nested messages require a callback since there would otherwise be
1021   // no way to set callbacks on the nested message.
1022   RepeatedTest::Message message{};
1023   int i = 0;
1024   message.structs.SetDecoder([&i](RepeatedTest::StreamDecoder& decoder) {
1025     EXPECT_EQ(decoder.Field().value(), RepeatedTest::Fields::kStructs);
1026 
1027     Struct::Message structs_message{};
1028     auto structs_decoder = decoder.GetStructsDecoder();
1029     const auto status = structs_decoder.Read(structs_message);
1030     EXPECT_EQ(status, OkStatus());
1031 
1032     EXPECT_LT(i, 2);
1033     EXPECT_EQ(structs_message.one, i * 32 + 16u);
1034     EXPECT_EQ(structs_message.two, i * 32 + 32u);
1035     ++i;
1036 
1037     return status;
1038   });
1039 
1040   const auto status = repeated_test.Read(message);
1041   ASSERT_EQ(status, OkStatus());
1042 }
1043 
TEST(CodegenMessage,ReadNestedForcedCallback)1044 TEST(CodegenMessage, ReadNestedForcedCallback) {
1045   // clang-format off
1046   constexpr uint8_t proto_data[] = {
1047     // pigweed.device_info
1048     0x32, 0x0e,
1049     // pigweed.device_info.device_name
1050     0x0a, 0x05, 'p', 'i', 'x', 'e', 'l',
1051     // pigweed.device_info.device_id
1052     0x15, 0x08, 0x08, 0x08, 0x08,
1053     // pigweed.device_info.status
1054     0x18, 0x00,
1055       };
1056   // clang-format on
1057 
1058   stream::MemoryReader reader(as_bytes(span(proto_data)));
1059   Pigweed::StreamDecoder pigweed(reader);
1060 
1061   // pigweed.device_info has use_callback=true to force the use of a callback.
1062   Pigweed::Message message{};
1063   message.device_info.SetDecoder([](Pigweed::StreamDecoder& decoder) {
1064     EXPECT_EQ(decoder.Field().value(), Pigweed::Fields::kDeviceInfo);
1065 
1066     DeviceInfo::Message device_info{};
1067     DeviceInfo::StreamDecoder device_info_decoder =
1068         decoder.GetDeviceInfoDecoder();
1069     const auto status = device_info_decoder.Read(device_info);
1070     EXPECT_EQ(status, OkStatus());
1071 
1072     constexpr std::string_view kExpectedDeviceName{"pixel"};
1073 
1074     EXPECT_EQ(device_info.device_name.size(), kExpectedDeviceName.size());
1075     EXPECT_EQ(std::memcmp(device_info.device_name.data(),
1076                           kExpectedDeviceName.data(),
1077                           kExpectedDeviceName.size()),
1078               0);
1079     EXPECT_EQ(device_info.device_id, 0x08080808u);
1080     EXPECT_EQ(device_info.status, DeviceInfo::DeviceStatus::OK);
1081 
1082     return status;
1083   });
1084   const auto status = pigweed.Read(message);
1085   ASSERT_EQ(status, OkStatus());
1086 }
1087 
TEST(CodegenMessage,ReadOptionalPresent)1088 TEST(CodegenMessage, ReadOptionalPresent) {
1089   // clang-format off
1090   constexpr uint8_t proto_data[] = {
1091     // optional.sometimes_present_fixed
1092     0x0d, 0x2a, 0x00, 0x00, 0x00,
1093     // optional.sometimes_present_varint
1094     0x10, 0x2a,
1095     // optional.explicitly_present_fixed
1096     0x1d, 0x45, 0x00, 0x00, 0x00,
1097     // optional.explicitly_present_varint
1098     0x20, 0x45,
1099     // optional.sometimes_empty_fixed
1100     0x2a, 0x04, 0x63, 0x00, 0x00, 0x00,
1101     // optional.sometimes_empty_varint
1102     0x32, 0x01, 0x63,
1103   };
1104   // clang-format on
1105 
1106   stream::MemoryReader reader(as_bytes(span(proto_data)));
1107   OptionalTest::StreamDecoder optional_test(reader);
1108 
1109   OptionalTest::Message message{};
1110   const auto status = optional_test.Read(message);
1111   ASSERT_EQ(status, OkStatus());
1112 
1113   EXPECT_EQ(message.sometimes_present_fixed, 0x2a);
1114   EXPECT_EQ(message.sometimes_present_varint, 0x2a);
1115   EXPECT_TRUE(message.explicitly_present_fixed);
1116   EXPECT_EQ(*message.explicitly_present_fixed, 0x45);
1117   EXPECT_TRUE(message.explicitly_present_varint);
1118   EXPECT_EQ(*message.explicitly_present_varint, 0x45);
1119   EXPECT_FALSE(message.sometimes_empty_fixed.empty());
1120   EXPECT_EQ(message.sometimes_empty_fixed.size(), 1u);
1121   EXPECT_EQ(message.sometimes_empty_fixed[0], 0x63);
1122   EXPECT_FALSE(message.sometimes_empty_varint.empty());
1123   EXPECT_EQ(message.sometimes_empty_varint.size(), 1u);
1124   EXPECT_EQ(message.sometimes_empty_varint[0], 0x63);
1125 }
1126 
TEST(CodegenMessage,ReadOptionalNotPresent)1127 TEST(CodegenMessage, ReadOptionalNotPresent) {
1128   constexpr std::array<std::byte, 0> proto_data{};
1129 
1130   stream::MemoryReader reader(proto_data);
1131   OptionalTest::StreamDecoder optional_test(reader);
1132 
1133   OptionalTest::Message message{};
1134   const auto status = optional_test.Read(message);
1135   ASSERT_EQ(status, OkStatus());
1136 
1137   // Non-optional fields have their default value.
1138   EXPECT_EQ(message.sometimes_present_fixed, 0);
1139   EXPECT_EQ(message.sometimes_present_varint, 0);
1140   EXPECT_TRUE(message.sometimes_empty_fixed.empty());
1141   EXPECT_TRUE(message.sometimes_empty_varint.empty());
1142 
1143   // Optional fields are explicitly not present.
1144   EXPECT_FALSE(message.explicitly_present_fixed);
1145   EXPECT_FALSE(message.explicitly_present_varint);
1146 }
1147 
TEST(CodegenMessage,ReadOptionalPresentDefaults)1148 TEST(CodegenMessage, ReadOptionalPresentDefaults) {
1149   // clang-format off
1150   constexpr uint8_t proto_data[] = {
1151     // optional.sometimes_present_fixed
1152     0x0d, 0x00, 0x00, 0x00, 0x00,
1153     // optional.sometimes_present_varint
1154     0x10, 0x00,
1155     // optional.explicitly_present_fixed
1156     0x1d, 0x00, 0x00, 0x00, 0x00,
1157     // optional.explicitly_present_varint
1158     0x20, 0x00,
1159     // optional.sometimes_empty_fixed
1160     0x2a, 0x04, 0x00, 0x00, 0x00, 0x00,
1161     // optional.sometimes_empty_varint
1162     0x32, 0x01, 0x00,
1163   };
1164   // clang-format on
1165 
1166   stream::MemoryReader reader(as_bytes(span(proto_data)));
1167   OptionalTest::StreamDecoder optional_test(reader);
1168 
1169   OptionalTest::Message message{};
1170   const auto status = optional_test.Read(message);
1171   ASSERT_EQ(status, OkStatus());
1172 
1173   // Non-optional fields have their default value and aren't meaningfully
1174   // different from missing.
1175   EXPECT_EQ(message.sometimes_present_fixed, 0x00);
1176   EXPECT_EQ(message.sometimes_present_varint, 0x00);
1177 
1178   // Optional fields are explicitly present with a default value.
1179   EXPECT_TRUE(message.explicitly_present_fixed);
1180   EXPECT_EQ(*message.explicitly_present_fixed, 0x00);
1181   EXPECT_TRUE(message.explicitly_present_varint);
1182   EXPECT_EQ(*message.explicitly_present_varint, 0x00);
1183 
1184   // Repeated fields with a default value are meaningfully non-empty.
1185   EXPECT_FALSE(message.sometimes_empty_fixed.empty());
1186   EXPECT_EQ(message.sometimes_empty_fixed.size(), 1u);
1187   EXPECT_EQ(message.sometimes_empty_fixed[0], 0x00);
1188   EXPECT_FALSE(message.sometimes_empty_varint.empty());
1189   EXPECT_EQ(message.sometimes_empty_varint.size(), 1u);
1190   EXPECT_EQ(message.sometimes_empty_varint[0], 0x00);
1191 }
1192 
TEST(CodegenMessage,ReadImportedOptions)1193 TEST(CodegenMessage, ReadImportedOptions) {
1194   // clang-format off
1195   constexpr uint8_t proto_data[] = {
1196     // notice
1197     0x0a, 0x0f,
1198     // notice.message
1199     0x0a, 0x0d, 'P', 'r', 'e', 's', 's', ' ', 'a', 'n', 'y', ' ', 'k', 'e', 'y'
1200   };
1201   // clang-format on
1202 
1203   stream::MemoryReader reader(as_bytes(span(proto_data)));
1204   TestMessage::StreamDecoder test_message(reader);
1205 
1206   // The options file for the imported proto is applied, making the string
1207   // field a vector rather than requiring a callback.
1208   TestMessage::Message message{};
1209   const auto status = test_message.Read(message);
1210   ASSERT_EQ(status, OkStatus());
1211 
1212   constexpr std::string_view kExpectedMessage{"Press any key"};
1213 
1214   EXPECT_EQ(message.notice.message.size(), kExpectedMessage.size());
1215   EXPECT_EQ(std::memcmp(message.notice.message.data(),
1216                         kExpectedMessage.data(),
1217                         kExpectedMessage.size()),
1218             0);
1219 }
1220 
TEST(CodegenMessage,ReadImportedFromDepsOptions)1221 TEST(CodegenMessage, ReadImportedFromDepsOptions) {
1222   // clang-format off
1223   constexpr uint8_t proto_data[] = {
1224     // debug
1225     0x12, 0x0f,
1226     // debug.message
1227     0x0a, 0x0d, 'P', 'r', 'e', 's', 's', ' ', 'a', 'n', 'y', ' ', 'k', 'e', 'y'
1228   };
1229   // clang-format on
1230 
1231   stream::MemoryReader reader(as_bytes(span(proto_data)));
1232   TestMessage::StreamDecoder test_message(reader);
1233 
1234   // The options file for the imported proto is applied, making the string
1235   // field a vector rather than requiring a callback.
1236   TestMessage::Message message{};
1237   const auto status = test_message.Read(message);
1238   ASSERT_EQ(status, OkStatus());
1239 
1240   constexpr std::string_view kExpectedMessage{"Press any key"};
1241 
1242   EXPECT_EQ(message.debug.message.size(), kExpectedMessage.size());
1243   EXPECT_EQ(std::memcmp(message.debug.message.data(),
1244                         kExpectedMessage.data(),
1245                         kExpectedMessage.size()),
1246             0);
1247 }
1248 
1249 class BreakableDecoder : public KeyValuePair::StreamDecoder {
1250  public:
BreakableDecoder(stream::Reader & reader)1251   constexpr BreakableDecoder(stream::Reader& reader) : StreamDecoder(reader) {}
1252 
Read(KeyValuePair::Message & message,span<const internal::MessageField> table)1253   Status Read(KeyValuePair::Message& message,
1254               span<const internal::MessageField> table) {
1255     return ::pw::protobuf::StreamDecoder::Read(
1256         as_writable_bytes(span(&message, 1)), table);
1257   }
1258 };
1259 
TEST(CodegenMessage,DISABLED_ReadDoesNotOverrun)1260 TEST(CodegenMessage, DISABLED_ReadDoesNotOverrun) {
1261   // Deliberately construct a message table that attempts to violate the bounds
1262   // of the structure. We're not testing that a developer can't do this, rather
1263   // that the protobuf decoder can't be exploited in this way.
1264   constexpr internal::MessageField kMessageFields[] = {
1265       {1,
1266        WireType::kDelimited,
1267        sizeof(std::byte),
1268        static_cast<internal::VarintType>(0),
1269        false,
1270        false,
1271        false,
1272        false,
1273        false,
1274        0,
1275        sizeof(KeyValuePair::Message) * 2,
1276        {}},
1277   };
1278 
1279   // clang-format off
1280   constexpr uint8_t proto_data[] = {
1281     // id=1, len=9,
1282     0x0a, 0x08, 'd', 'o', 'n', 't', 'e', 'a', 't', 'm', 'e',
1283   };
1284   // clang-format on
1285 
1286   stream::MemoryReader reader(as_bytes(span(proto_data)));
1287   BreakableDecoder decoder(reader);
1288 
1289   KeyValuePair::Message message{};
1290   // ASSERT_CRASH
1291   std::ignore = decoder.Read(message, kMessageFields);
1292 }
1293 
TEST(CodegenMessage,Write)1294 TEST(CodegenMessage, Write) {
1295   constexpr uint8_t pigweed_data[] = {
1296       0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
1297 
1298   Pigweed::Message message{};
1299   message.magic_number = 0x49u;
1300   message.ziggy = -111;
1301   message.cycles = 0x40302010fecaaddeu;
1302   message.ratio = -1.42f;
1303   message.error_message = "not a typewriter";
1304   message.pigweed.status = Bool::FILE_NOT_FOUND;
1305   message.bin = Pigweed::Protobuf::Binary::ZERO;
1306   message.bungle = -111;
1307   message.proto.bin = Proto::Binary::OFF;
1308   message.proto.pigweed_pigweed_bin = Pigweed::Pigweed::Binary::ZERO;
1309   message.proto.pigweed_protobuf_bin = Pigweed::Protobuf::Binary::ZERO;
1310   message.proto.meta.file_name = "/etc/passwd";
1311   message.proto.meta.status = Pigweed::Protobuf::Compiler::Status::FUBAR;
1312   message.proto.meta.protobuf_bin = Pigweed::Protobuf::Binary::ONE;
1313   message.proto.meta.pigweed_bin = Pigweed::Pigweed::Binary::ONE;
1314   std::memcpy(message.data.data(), pigweed_data, sizeof(pigweed_data));
1315 
1316   std::byte encode_buffer[Pigweed::kMaxEncodedSizeBytes];
1317   std::byte temp_buffer[Pigweed::kScratchBufferSizeBytes];
1318 
1319   stream::MemoryWriter writer(encode_buffer);
1320   Pigweed::StreamEncoder pigweed(writer, temp_buffer);
1321 
1322   const auto status = pigweed.Write(message);
1323   ASSERT_EQ(status, OkStatus());
1324 
1325   // clang-format off
1326   constexpr uint8_t expected_proto[] = {
1327     // pigweed.magic_number
1328     0x08, 0x49,
1329     // pigweed.ziggy
1330     0x10, 0xdd, 0x01,
1331     // pigweed.cycles
1332     0x19, 0xde, 0xad, 0xca, 0xfe, 0x10, 0x20, 0x30, 0x40,
1333     // pigweed.ratio
1334     0x25, 0x8f, 0xc2, 0xb5, 0xbf,
1335     // pigweed.error_message
1336     0x2a, 0x10, 'n', 'o', 't', ' ', 'a', ' ',
1337     't', 'y', 'p', 'e', 'w', 'r', 'i', 't', 'e', 'r',
1338     // pigweed.pigweed
1339     0x3a, 0x02,
1340     // pigweed.pigweed.status
1341     0x08, 0x02,
1342     // pigweed.bin
1343     0x40, 0x01,
1344     // pigweed.proto
1345     0x4a, 0x15,
1346     // pigweed.proto.pigweed_protobuf_bin
1347     0x20, 0x01,
1348     // pigweed.proto.meta
1349     0x2a, 0x11,
1350     // pigweed.proto.meta.file_name
1351     0x0a, 0x0b, '/', 'e', 't', 'c', '/', 'p', 'a', 's', 's', 'w', 'd',
1352     // pigweed.proto.meta.status
1353     0x10, 0x02,
1354     // pigweed.proto.meta.pigweed_bin
1355     0x20, 0x01,
1356     // pigweed.bytes
1357     0x5a, 0x08, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
1358     // pigweed.bungle
1359     0x70, 0x91, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01,
1360   };
1361   // clang-format on
1362 
1363   ConstByteSpan result = writer.WrittenData();
1364   EXPECT_EQ(result.size(), sizeof(expected_proto));
1365   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1366             0);
1367 }
1368 
TEST(CodegenMessage,WriteDefaults)1369 TEST(CodegenMessage, WriteDefaults) {
1370   Pigweed::Message message{};
1371 
1372   std::byte encode_buffer[Pigweed::kMaxEncodedSizeBytes];
1373   std::byte temp_buffer[Pigweed::kScratchBufferSizeBytes];
1374 
1375   stream::MemoryWriter writer(encode_buffer);
1376   Pigweed::StreamEncoder pigweed(writer, temp_buffer);
1377 
1378   const auto status = pigweed.Write(message);
1379   ASSERT_EQ(status, OkStatus());
1380 
1381   // Since all fields are at their default, the output should be zero sized.
1382   ConstByteSpan result = writer.WrittenData();
1383   EXPECT_EQ(result.size(), 0u);
1384 }
1385 
TEST(CodegenMessage,WritePackedScalar)1386 TEST(CodegenMessage, WritePackedScalar) {
1387   RepeatedTest::Message message{};
1388   for (int i = 0; i < 4; ++i) {
1389     message.uint32s.push_back(i * 16u);
1390     message.fixed32s.push_back(i * 16u);
1391   }
1392 
1393   std::byte encode_buffer[RepeatedTest::kMaxEncodedSizeBytes];
1394 
1395   stream::MemoryWriter writer(encode_buffer);
1396   RepeatedTest::StreamEncoder repeated_test(writer, ByteSpan());
1397 
1398   const auto status = repeated_test.Write(message);
1399   ASSERT_EQ(status, OkStatus());
1400 
1401   // clang-format off
1402   constexpr uint8_t expected_proto[] = {
1403     // uint32s[], v={0, 16, 32, 48}
1404     0x0a, 0x04,
1405     0x00,
1406     0x10,
1407     0x20,
1408     0x30,
1409     // fixed32s[]. v={0, 16, 32, 48}
1410     0x32, 0x10,
1411     0x00, 0x00, 0x00, 0x00,
1412     0x10, 0x00, 0x00, 0x00,
1413     0x20, 0x00, 0x00, 0x00,
1414     0x30, 0x00, 0x00, 0x00,
1415   };
1416   // clang-format on
1417 
1418   ConstByteSpan result = writer.WrittenData();
1419   EXPECT_EQ(result.size(), sizeof(expected_proto));
1420   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1421             0);
1422 }
1423 
TEST(CodegenMessage,WritePackedScalarFixedLength)1424 TEST(CodegenMessage, WritePackedScalarFixedLength) {
1425   RepeatedTest::Message message{};
1426   for (int i = 0; i < 4; ++i) {
1427     message.uint64s[i] = (i + 1) * 1000u;
1428   }
1429   message.doubles[0] = 3.14159;
1430   message.doubles[1] = 2.71828;
1431 
1432   std::byte encode_buffer[RepeatedTest::kMaxEncodedSizeBytes];
1433 
1434   stream::MemoryWriter writer(encode_buffer);
1435   RepeatedTest::StreamEncoder repeated_test(writer, ByteSpan());
1436 
1437   const auto status = repeated_test.Write(message);
1438   ASSERT_EQ(status, OkStatus());
1439 
1440   // clang-format off
1441   constexpr uint8_t expected_proto[] = {
1442     // doubles[], v={3.14159, 2.71828}
1443     0x22, 0x10,
1444     0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x09, 0x40,
1445     0x90, 0xf7, 0xaa, 0x95, 0x09, 0xbf, 0x05, 0x40,
1446     // uint64s[], v={1000, 2000, 3000, 4000}
1447     0x42, 0x08, 0xe8, 0x07, 0xd0, 0x0f, 0xb8, 0x17, 0xa0, 0x1f,
1448   };
1449   // clang-format on
1450 
1451   ConstByteSpan result = writer.WrittenData();
1452   EXPECT_EQ(result.size(), sizeof(expected_proto));
1453   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1454             0);
1455 }
1456 
TEST(CodegenMessage,WritePackedScalarCallback)1457 TEST(CodegenMessage, WritePackedScalarCallback) {
1458   RepeatedTest::Message message{};
1459   message.sint32s.SetEncoder([](RepeatedTest::StreamEncoder& encoder) {
1460     constexpr int32_t sint32s[] = {-25, -1, 0, 1, 25};
1461     return encoder.WriteSint32s(sint32s);
1462   });
1463 
1464   std::byte encode_buffer[RepeatedTest::kMaxEncodedSizeBytes +
1465                           varint::kMaxVarint32SizeBytes * 5];
1466 
1467   stream::MemoryWriter writer(encode_buffer);
1468   RepeatedTest::StreamEncoder repeated_test(writer, ByteSpan());
1469 
1470   const auto status = repeated_test.Write(message);
1471   ASSERT_EQ(status, OkStatus());
1472 
1473   // clang-format off
1474   constexpr uint8_t expected_proto[] = {
1475     // sint32s[], v={-25, -1, 0, 1, 25}
1476     0x12, 0x05,
1477     0x31,
1478     0x01,
1479     0x00,
1480     0x02,
1481     0x32,
1482   };
1483   // clang-format on
1484 
1485   ConstByteSpan result = writer.WrittenData();
1486   EXPECT_EQ(result.size(), sizeof(expected_proto));
1487   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1488             0);
1489 }
1490 
TEST(CodegenMessage,WritePackedEnum)1491 TEST(CodegenMessage, WritePackedEnum) {
1492   RepeatedTest::Message message{};
1493   message.enums.push_back(Enum::RED);
1494   message.enums.push_back(Enum::GREEN);
1495   message.enums.push_back(Enum::AMBER);
1496   message.enums.push_back(Enum::RED);
1497 
1498   std::byte encode_buffer[RepeatedTest::kMaxEncodedSizeBytes];
1499 
1500   stream::MemoryWriter writer(encode_buffer);
1501   RepeatedTest::StreamEncoder repeated_test(writer, ByteSpan());
1502 
1503   const auto status = repeated_test.Write(message);
1504   ASSERT_EQ(status, OkStatus());
1505 
1506   // clang-format off
1507   constexpr uint8_t expected_proto[] = {
1508     // enums[], v={RED, GREEN, AMBER, RED}
1509     0x4a, 0x04, 0x00, 0x02, 0x01, 0x00,
1510   };
1511   // clang-format on
1512 
1513   ConstByteSpan result = writer.WrittenData();
1514   EXPECT_EQ(result.size(), sizeof(expected_proto));
1515   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1516             0);
1517 }
1518 
TEST(CodegenMessage,WriteStringCallback)1519 TEST(CodegenMessage, WriteStringCallback) {
1520   Pigweed::Message message{};
1521   // pigweed.description has no max_size specified so a callback must be
1522   // set to write the value.
1523   message.description.SetEncoder([](Pigweed::StreamEncoder& encoder) {
1524     return encoder.WriteDescription(
1525         "an open source collection of embedded-targeted "
1526         "libraries-or as we like to call them, modules");
1527   });
1528 
1529   std::byte encode_buffer[Pigweed::kMaxEncodedSizeBytes + 92];
1530   std::byte temp_buffer[Pigweed::kScratchBufferSizeBytes];
1531 
1532   stream::MemoryWriter writer(encode_buffer);
1533   Pigweed::StreamEncoder pigweed(writer, temp_buffer);
1534 
1535   const auto status = pigweed.Write(message);
1536   ASSERT_EQ(status, OkStatus());
1537 
1538   // clang-format off
1539   constexpr uint8_t expected_proto[] = {
1540     // pigweed.description
1541     0x62, 0x5c, 'a', 'n', ' ', 'o', 'p', 'e', 'n', ' ', 's', 'o', 'u', 'r', 'c',
1542     'e', ' ', 'c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n', ' ', 'o', 'f',
1543     ' ', 'e', 'm', 'b', 'e', 'd', 'd', 'e', 'd', '-', 't', 'a', 'r', 'g', 'e',
1544     't', 'e', 'd', ' ', 'l', 'i', 'b', 'r', 'a', 'r', 'i', 'e', 's', '-', 'o',
1545     'r', ' ', 'a', 's', ' ', 'w', 'e', ' ', 'l', 'i', 'k', 'e', ' ', 't', 'o',
1546     ' ', 'c', 'a', 'l', 'l', ' ', 't', 'h', 'e', 'm', ',', ' ', 'm', 'o', 'd',
1547     'u', 'l', 'e', 's',
1548   };
1549   // clang-format on
1550 
1551   ConstByteSpan result = writer.WrittenData();
1552   EXPECT_EQ(result.size(), sizeof(expected_proto));
1553   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1554             0);
1555 }
1556 
TEST(CodegenMessage,WriteForcedCallback)1557 TEST(CodegenMessage, WriteForcedCallback) {
1558   Pigweed::Message message{};
1559   // pigweed.special_property has use_callback=true to force the use of a
1560   // callback even though it's a simple scalar.
1561   message.special_property.SetEncoder([](Pigweed::StreamEncoder& encoder) {
1562     return encoder.WriteSpecialProperty(42u);
1563   });
1564 
1565   std::byte encode_buffer[Pigweed::kMaxEncodedSizeBytes];
1566   std::byte temp_buffer[Pigweed::kScratchBufferSizeBytes];
1567 
1568   stream::MemoryWriter writer(encode_buffer);
1569   Pigweed::StreamEncoder pigweed(writer, temp_buffer);
1570 
1571   const auto status = pigweed.Write(message);
1572   ASSERT_EQ(status, OkStatus());
1573 
1574   // clang-format off
1575   constexpr uint8_t expected_proto[] = {
1576     // pigweed.special_property
1577     0x68, 0x2a,
1578   };
1579   // clang-format on
1580 
1581   ConstByteSpan result = writer.WrittenData();
1582   EXPECT_EQ(result.size(), sizeof(expected_proto));
1583   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1584             0);
1585 }
1586 
TEST(CodegenMessage,WriteNestedImported)1587 TEST(CodegenMessage, WriteNestedImported) {
1588   Period::Message message{};
1589   message.start.seconds = 1517949900u;
1590   message.end.seconds = 1517950378u;
1591 
1592   std::byte encode_buffer[Period::kMaxEncodedSizeBytes];
1593   std::byte temp_buffer[Period::kScratchBufferSizeBytes];
1594 
1595   stream::MemoryWriter writer(encode_buffer);
1596   Period::StreamEncoder period(writer, temp_buffer);
1597 
1598   const auto status = period.Write(message);
1599   ASSERT_EQ(status, OkStatus());
1600 
1601   // clang-format off
1602   constexpr uint8_t expected_proto[] = {
1603     // period.start
1604     0x0a, 0x06,
1605     // period.start.seconds v=1517949900
1606     0x08, 0xcc, 0xa7, 0xe8, 0xd3, 0x05,
1607     // period.end
1608     0x12, 0x06,
1609     // period.end.seconds, v=1517950378
1610     0x08, 0xaa, 0xab, 0xe8, 0xd3, 0x05,
1611   };
1612   // clang-format on
1613 
1614   ConstByteSpan result = writer.WrittenData();
1615   EXPECT_EQ(result.size(), sizeof(expected_proto));
1616   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1617             0);
1618 }
1619 
TEST(CodegenMessage,WriteNestedRepeated)1620 TEST(CodegenMessage, WriteNestedRepeated) {
1621   RepeatedTest::Message message{};
1622   // Repeated nested messages require a callback since there would otherwise be
1623   // no way to set callbacks on the nested message.
1624   message.structs.SetEncoder([](RepeatedTest::StreamEncoder& encoder) {
1625     for (int i = 0; i < 2; ++i) {
1626       Struct::Message struct_message{};
1627       struct_message.one = i * 32 + 16u;
1628       struct_message.two = i * 32 + 32u;
1629 
1630       const auto status = encoder.GetStructsEncoder().Write(struct_message);
1631       EXPECT_EQ(status, OkStatus());
1632     }
1633     return OkStatus();
1634   });
1635 
1636   std::byte encode_buffer[RepeatedTest::kMaxEncodedSizeBytes +
1637                           Struct::kMaxEncodedSizeBytes * 2];
1638   std::byte temp_buffer[RepeatedTest::kScratchBufferSizeBytes +
1639                         Struct::kMaxEncodedSizeBytes];
1640 
1641   stream::MemoryWriter writer(encode_buffer);
1642   RepeatedTest::StreamEncoder repeated_test(writer, temp_buffer);
1643 
1644   const auto status = repeated_test.Write(message);
1645   ASSERT_EQ(status, OkStatus());
1646 
1647   // clang-format off
1648   constexpr uint8_t expected_proto[] = {
1649     // repeated.structs
1650     0x2a, 0x04,
1651     // repeated.structs.one v=16
1652     0x08, 0x10,
1653     // repeated.structs.two v=32
1654     0x10, 0x20,
1655     // repeated.structs
1656     0x2a, 0x04,
1657     // repeated.structs.one v=48
1658     0x08, 0x30,
1659     // repeated.structs.two v=64
1660     0x10, 0x40,
1661   };
1662   // clang-format on
1663 
1664   ConstByteSpan result = writer.WrittenData();
1665   EXPECT_EQ(result.size(), sizeof(expected_proto));
1666   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1667             0);
1668 }
1669 
TEST(CodegenMessage,WriteNestedForcedCallback)1670 TEST(CodegenMessage, WriteNestedForcedCallback) {
1671   Pigweed::Message message{};
1672   // pigweed.device_info has use_callback=true to force the use of a callback.
1673   message.device_info.SetEncoder([](Pigweed::StreamEncoder& encoder) {
1674     DeviceInfo::Message device_info{};
1675     device_info.device_name = "pixel";
1676     device_info.device_id = 0x08080808u;
1677     device_info.status = DeviceInfo::DeviceStatus::OK;
1678 
1679     // Use the callback to set nested callbacks.
1680     device_info.attributes.SetEncoder(
1681         [](DeviceInfo::StreamEncoder& device_info_encoder) {
1682           KeyValuePair::Message attribute{};
1683 
1684           attribute.key = "version";
1685           attribute.value = "5.3.1";
1686           PW_TRY(device_info_encoder.GetAttributesEncoder().Write(attribute));
1687 
1688           attribute.key = "chip";
1689           attribute.value = "left-soc";
1690           PW_TRY(device_info_encoder.GetAttributesEncoder().Write(attribute));
1691 
1692           return OkStatus();
1693         });
1694 
1695     return encoder.GetDeviceInfoEncoder().Write(device_info);
1696   });
1697 
1698   std::byte encode_buffer[Pigweed::kMaxEncodedSizeBytes +
1699                           DeviceInfo::kMaxEncodedSizeBytes];
1700   std::byte temp_buffer[Pigweed::kScratchBufferSizeBytes +
1701                         DeviceInfo::kMaxEncodedSizeBytes];
1702 
1703   stream::MemoryWriter writer(encode_buffer);
1704   Pigweed::StreamEncoder pigweed(writer, temp_buffer);
1705 
1706   const auto status = pigweed.Write(message);
1707   ASSERT_EQ(status, OkStatus());
1708 
1709   // clang-format off
1710   constexpr uint8_t expected_proto[] = {
1711     // pigweed.device_info
1712     0x32, 0x30,
1713     // pigweed.device_info.device_name
1714     0x0a, 0x05, 'p', 'i', 'x', 'e', 'l',
1715     // pigweed.device_info.device_id
1716     0x15, 0x08, 0x08, 0x08, 0x08,
1717     // pigweed.device_info.attributes[0]
1718     0x22, 0x10,
1719     // pigweed.device_info.attributes[0].key
1720     0x0a, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n',
1721     // pigweed.device_info.attributes[0].value
1722     0x12, 0x05, '5', '.', '3', '.', '1',
1723     // pigweed.device_info.attributes[1]
1724     0x22, 0x10,
1725     // pigweed.device_info.attributes[1].key
1726     0x0a, 0x04, 'c', 'h', 'i', 'p',
1727     // pigweed.device_info.attributes[1].value
1728     0x12, 0x08, 'l', 'e', 'f', 't', '-', 's', 'o', 'c',
1729   };
1730   // clang-format on
1731 
1732   ConstByteSpan result = writer.WrittenData();
1733   EXPECT_EQ(result.size(), sizeof(expected_proto));
1734   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1735             0);
1736 }
1737 
TEST(CodegenMessage,EnumAliases)1738 TEST(CodegenMessage, EnumAliases) {
1739   // Unprefixed enum.
1740   EXPECT_EQ(Bool::kTrue, Bool::TRUE);
1741   EXPECT_EQ(Bool::kFalse, Bool::FALSE);
1742   EXPECT_EQ(Bool::kFileNotFound, Bool::FILE_NOT_FOUND);
1743 
1744   // Prefixed enum has the prefix removed.
1745   EXPECT_EQ(Error::kNone, Error::ERROR_NONE);
1746   EXPECT_EQ(Error::kNotFound, Error::ERROR_NOT_FOUND);
1747   EXPECT_EQ(Error::kUnknown, Error::ERROR_UNKNOWN);
1748 
1749   // Single-value enum.
1750   EXPECT_EQ(AlwaysBlue::kBlue, AlwaysBlue::BLUE);
1751 }
1752 
TEST(CodegenMessage,WriteOptionalPresent)1753 TEST(CodegenMessage, WriteOptionalPresent) {
1754   OptionalTest::Message message{};
1755   message.sometimes_present_fixed = 0x2a;
1756   message.sometimes_present_varint = 0x2a;
1757   message.explicitly_present_fixed = 0x45;
1758   message.explicitly_present_varint = 0x45;
1759   message.sometimes_empty_fixed.push_back(0x63);
1760   message.sometimes_empty_varint.push_back(0x63);
1761 
1762   std::byte encode_buffer[512];
1763 
1764   stream::MemoryWriter writer(encode_buffer);
1765   OptionalTest::StreamEncoder optional_test(writer, ByteSpan());
1766 
1767   const auto status = optional_test.Write(message);
1768   ASSERT_EQ(status, OkStatus());
1769 
1770   // clang-format off
1771   constexpr uint8_t expected_proto[] = {
1772     // optional.sometimes_present_fixed
1773     0x0d, 0x2a, 0x00, 0x00, 0x00,
1774     // optional.sometimes_present_varint
1775     0x10, 0x2a,
1776     // optional.explicitly_present_fixed
1777     0x1d, 0x45, 0x00, 0x00, 0x00,
1778     // optional.explicitly_present_varint
1779     0x20, 0x45,
1780     // optional.sometimes_empty_fixed
1781     0x2a, 0x04, 0x63, 0x00, 0x00, 0x00,
1782     // optional.sometimes_empty_varint
1783     0x32, 0x01, 0x63,
1784   };
1785   // clang-format on
1786 
1787   ConstByteSpan result = writer.WrittenData();
1788   EXPECT_EQ(result.size(), sizeof(expected_proto));
1789   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1790             0);
1791 }
1792 
TEST(CodegenMessage,WriteOptionalNotPresent)1793 TEST(CodegenMessage, WriteOptionalNotPresent) {
1794   OptionalTest::Message message{};
1795 
1796   std::byte encode_buffer[512];
1797 
1798   stream::MemoryWriter writer(encode_buffer);
1799   OptionalTest::StreamEncoder optional_test(writer, ByteSpan());
1800 
1801   const auto status = optional_test.Write(message);
1802   ASSERT_EQ(status, OkStatus());
1803 
1804   // The expected proto is empty; no bytes should be written.
1805 
1806   ConstByteSpan result = writer.WrittenData();
1807   EXPECT_TRUE(result.empty());
1808 }
1809 
TEST(CodegenMessage,WriteOptionalPresentDefaults)1810 TEST(CodegenMessage, WriteOptionalPresentDefaults) {
1811   OptionalTest::Message message{};
1812   // Non-optional fields with a default value are not explicitly encoded, so
1813   // aren't meaningfully different from one that's just ommitted.
1814   message.sometimes_present_fixed = 0x00;
1815   message.sometimes_present_varint = 0x00;
1816   // Optional fields, even with a default value, are explicitly encoded.
1817   message.explicitly_present_fixed = 0x00;
1818   message.explicitly_present_varint = 0x00;
1819   // Repeated fields with a default value are meaningfully non-empty.
1820   message.sometimes_empty_fixed.push_back(0x00);
1821   message.sometimes_empty_varint.push_back(0x00);
1822 
1823   std::byte encode_buffer[512];
1824 
1825   stream::MemoryWriter writer(encode_buffer);
1826   OptionalTest::StreamEncoder optional_test(writer, ByteSpan());
1827 
1828   const auto status = optional_test.Write(message);
1829   ASSERT_EQ(status, OkStatus());
1830 
1831   // clang-format off
1832   constexpr uint8_t expected_proto[] = {
1833     // optional.explicitly_present_fixed
1834     0x1d, 0x00, 0x00, 0x00, 0x00,
1835     // optional.explicitly_present_varint
1836     0x20, 0x00,
1837     // optional.sometimes_empty_fixed
1838     0x2a, 0x04, 0x00, 0x00, 0x00, 0x00,
1839     // optional.sometimes_empty_varint
1840     0x32, 0x01, 0x00,
1841   };
1842   // clang-format on
1843 
1844   ConstByteSpan result = writer.WrittenData();
1845   EXPECT_EQ(result.size(), sizeof(expected_proto));
1846   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1847             0);
1848 }
1849 
1850 class BreakableEncoder : public KeyValuePair::MemoryEncoder {
1851  public:
BreakableEncoder(ByteSpan buffer)1852   constexpr BreakableEncoder(ByteSpan buffer)
1853       : KeyValuePair::MemoryEncoder(buffer) {}
1854 
Write(const KeyValuePair::Message & message,span<const internal::MessageField> table)1855   Status Write(const KeyValuePair::Message& message,
1856                span<const internal::MessageField> table) {
1857     return ::pw::protobuf::StreamEncoder::Write(as_bytes(span(&message, 1)),
1858                                                 table);
1859   }
1860 };
1861 
TEST(CodegenMessage,DISABLED_WriteDoesNotOverrun)1862 TEST(CodegenMessage, DISABLED_WriteDoesNotOverrun) {
1863   // Deliberately construct a message table that attempts to violate the bounds
1864   // of the structure. We're not testing that a developer can't do this, rather
1865   // that the protobuf encoder can't be exploited in this way.
1866   constexpr internal::MessageField kMessageFields[] = {
1867       {1,
1868        WireType::kDelimited,
1869        sizeof(std::byte),
1870        static_cast<internal::VarintType>(0),
1871        false,
1872        false,
1873        false,
1874        false,
1875        false,
1876        0,
1877        sizeof(KeyValuePair::Message) * 2,
1878        {}},
1879   };
1880 
1881   std::byte encode_buffer[64];
1882 
1883   BreakableEncoder encoder(encode_buffer);
1884   KeyValuePair::Message message{};
1885   // ASSERT_CRASH
1886   std::ignore = encoder.Write(message, kMessageFields);
1887 }
1888 
1889 // The following tests cover using the codegen struct Message and callbacks in
1890 // different ways.
1891 
1892 // Check that the callback function object is large enough to implement a
1893 // "call a function on this" lambda.
1894 class StringChecker {
1895  public:
1896   StringChecker() = default;
1897   ~StringChecker() = default;
1898 
Check(RepeatedTest::StreamDecoder & repeated_test)1899   Status Check(RepeatedTest::StreamDecoder& repeated_test) {
1900     RepeatedTest::Message message{};
1901     message.strings.SetDecoder([this](RepeatedTest::StreamDecoder& decoder) {
1902       return this->CheckOne(decoder);
1903     });
1904     return repeated_test.Read(message);
1905   }
1906 
1907  private:
CheckOne(RepeatedTest::StreamDecoder & decoder)1908   Status CheckOne(RepeatedTest::StreamDecoder& decoder) {
1909     EXPECT_EQ(decoder.Field().value(), RepeatedTest::Fields::kStrings);
1910 
1911     std::array<char, 40> strings{};
1912     const StatusWithSize sws = decoder.ReadStrings(strings);
1913     EXPECT_EQ(sws.status(), OkStatus());
1914     EXPECT_EQ(sws.size(), kExpectedStrings[i_].size());
1915     EXPECT_EQ(std::memcmp(strings.data(),
1916                           kExpectedStrings[i_].data(),
1917                           kExpectedStrings[i_].size()),
1918               0);
1919 
1920     ++i_;
1921     return sws.status();
1922   }
1923 
1924   int i_ = 0;
1925   constexpr static std::string_view kExpectedStrings[] = {
1926       {"if music be the food of love, play on"},
1927       {"give me excess of it, that, surfeiting"},
1928       {"the appetite may sicken, and so die"}};
1929 };
1930 
TEST(CodegenMessage,CallbackInClass)1931 TEST(CodegenMessage, CallbackInClass) {
1932   // clang-format off
1933   constexpr uint8_t proto_data[] = {
1934     // repeated.strings
1935     0x1a, 0x25, 'i', 'f', ' ', 'm', 'u', 's', 'i', 'c', ' ', 'b', 'e', ' ',
1936     't', 'h', 'e', ' ', 'f', 'o', 'o', 'd', ' ', 'o', 'f', ' ',
1937     'l', 'o', 'v', 'e', ',', ' ', 'p', 'l', 'a', 'y', ' ', 'o', 'n',
1938     // repeated.strings
1939     0x1a, 0x26, 'g', 'i', 'v', 'e', ' ', 'm', 'e', ' ', 'e', 'x', 'c', 'e',
1940     's', 's', ' ', 'o', 'f', ' ', 'i', 't', ',', ' ', 't', 'h', 'a', 't', ',',
1941     ' ', 's', 'u', 'r', 'f', 'e', 'i', 't', 'i', 'n', 'g',
1942     // repeated.strings
1943     0x1a, 0x23, 't', 'h', 'e', ' ', 'a', 'p', 'p', 'e', 't', 'i', 't', 'e', ' ',
1944     'm', 'a', 'y', ' ', 's', 'i', 'c', 'k', 'e', 'n', ',', ' ', 'a', 'n', 'd',
1945     ' ', 's', 'o', ' ', 'd', 'i', 'e',
1946   };
1947   // clang-format on
1948 
1949   stream::MemoryReader reader(as_bytes(span(proto_data)));
1950   RepeatedTest::StreamDecoder repeated_test(reader);
1951 
1952   StringChecker checker{};
1953   const auto status = checker.Check(repeated_test);
1954   ASSERT_EQ(status, OkStatus());
1955 }
1956 
1957 // Check that we can create a custom subclass of the message struct that sets
1958 // its own callbacks to member functions that populate fields added in the
1959 // subclass.
1960 struct CustomMessage : RepeatedTest::Message {
CustomMessagepw::protobuf::__anon0bc28c7c0111::CustomMessage1961   CustomMessage() : RepeatedTest::Message() {
1962     strings.SetDecoder([this](RepeatedTest::StreamDecoder& decoder) {
1963       return this->ParseStrings(decoder);
1964     });
1965   }
1966 
1967   pw::Vector<std::array<char, 40>, 8> all_strings{};
1968 
1969  private:
ParseStringspw::protobuf::__anon0bc28c7c0111::CustomMessage1970   Status ParseStrings(RepeatedTest::StreamDecoder& decoder) {
1971     PW_ASSERT(decoder.Field().value() == RepeatedTest::Fields::kStrings);
1972 
1973     std::array<char, 40> one_strings{};
1974     const auto sws = decoder.ReadStrings(one_strings);
1975     if (!sws.ok()) {
1976       return sws.status();
1977     }
1978 
1979     one_strings[sws.size()] = '\0';
1980     all_strings.push_back(one_strings);
1981 
1982     return OkStatus();
1983   }
1984 };
1985 
TEST(CodegenMessage,CallbackInSubclass)1986 TEST(CodegenMessage, CallbackInSubclass) {
1987   // clang-format off
1988   constexpr uint8_t proto_data[] = {
1989     // repeated.strings
1990     0x1a, 0x25, 'i', 'f', ' ', 'm', 'u', 's', 'i', 'c', ' ', 'b', 'e', ' ',
1991     't', 'h', 'e', ' ', 'f', 'o', 'o', 'd', ' ', 'o', 'f', ' ',
1992     'l', 'o', 'v', 'e', ',', ' ', 'p', 'l', 'a', 'y', ' ', 'o', 'n',
1993     // repeated.strings
1994     0x1a, 0x26, 'g', 'i', 'v', 'e', ' ', 'm', 'e', ' ', 'e', 'x', 'c', 'e',
1995     's', 's', ' ', 'o', 'f', ' ', 'i', 't', ',', ' ', 't', 'h', 'a', 't', ',',
1996     ' ', 's', 'u', 'r', 'f', 'e', 'i', 't', 'i', 'n', 'g',
1997     // repeated.strings
1998     0x1a, 0x23, 't', 'h', 'e', ' ', 'a', 'p', 'p', 'e', 't', 'i', 't', 'e', ' ',
1999     'm', 'a', 'y', ' ', 's', 'i', 'c', 'k', 'e', 'n', ',', ' ', 'a', 'n', 'd',
2000     ' ', 's', 'o', ' ', 'd', 'i', 'e',
2001   };
2002   // clang-format on
2003 
2004   stream::MemoryReader reader(as_bytes(span(proto_data)));
2005   RepeatedTest::StreamDecoder repeated_test(reader);
2006 
2007   CustomMessage message{};
2008   const auto status = repeated_test.Read(message);
2009   ASSERT_EQ(status, OkStatus());
2010 
2011   constexpr static std::string_view kExpectedStrings[] = {
2012       {"if music be the food of love, play on"},
2013       {"give me excess of it, that, surfeiting"},
2014       {"the appetite may sicken, and so die"}};
2015 
2016   EXPECT_EQ(message.all_strings.size(), 3u);
2017   for (int i = 0; i < 3; ++i) {
2018     EXPECT_EQ(std::memcmp(message.all_strings[i].data(),
2019                           kExpectedStrings[i].data(),
2020                           kExpectedStrings[i].size()),
2021               0);
2022     EXPECT_EQ(message.all_strings[i].data()[kExpectedStrings[i].size()], '\0');
2023   }
2024 }
2025 
2026 }  // namespace
2027 }  // namespace pw::protobuf
2028