xref: /aosp_15_r20/external/pigweed/pw_protobuf/codegen_message_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 "pw_preprocessor/compiler.h"
20 #include "pw_protobuf/internal/codegen.h"
21 #include "pw_span/span.h"
22 #include "pw_status/status.h"
23 #include "pw_status/status_with_size.h"
24 #include "pw_stream/memory_stream.h"
25 #include "pw_unit_test/framework.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,SetEncoder)324 TEST(CodegenMessage, SetEncoder) {
325   Pigweed::Message msg{};
326 
327   EXPECT_FALSE(msg.id);
328   msg.id.SetEncoder(
329       [](Pigweed::StreamEncoder&) -> Status { return OkStatus(); });
330   EXPECT_TRUE(msg.id);
331 }
332 
TEST(CodegenMessage,SetDecoder)333 TEST(CodegenMessage, SetDecoder) {
334   Pigweed::Message msg{};
335 
336   EXPECT_FALSE(msg.id);
337   msg.id.SetDecoder(
338       [](Pigweed::StreamDecoder&) -> Status { return OkStatus(); });
339   EXPECT_TRUE(msg.id);
340 }
341 
TEST(CodegenMessage,SetEncoderAndDecoder)342 TEST(CodegenMessage, SetEncoderAndDecoder) {
343   Pigweed::Message msg{};
344 
345   EXPECT_FALSE(msg.id);
346   msg.id.SetEncoder(
347       [](Pigweed::StreamEncoder&) -> Status { return OkStatus(); });
348   msg.id.SetDecoder(
349       [](Pigweed::StreamDecoder&) -> Status { return OkStatus(); });
350   EXPECT_TRUE(msg.id);
351 }
352 
TEST(CodegenMessage,Read)353 TEST(CodegenMessage, Read) {
354   // clang-format off
355   constexpr uint8_t proto_data[] = {
356     // pigweed.magic_number
357     0x08, 0x49,
358     // pigweed.ziggy
359     0x10, 0xdd, 0x01,
360     // pigweed.cycles
361     0x19, 0xde, 0xad, 0xca, 0xfe, 0x10, 0x20, 0x30, 0x40,
362     // pigweed.ratio
363     0x25, 0x8f, 0xc2, 0xb5, 0xbf,
364     // pigweed.error_message
365     0x2a, 0x10, 'n', 'o', 't', ' ', 'a', ' ',
366     't', 'y', 'p', 'e', 'w', 'r', 'i', 't', 'e', 'r',
367     // pigweed.bin
368     0x40, 0x01,
369     // pigweed.bungle
370     0x70, 0x91, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01,
371   };
372   // clang-format on
373 
374   stream::MemoryReader reader(as_bytes(span(proto_data)));
375   Pigweed::StreamDecoder pigweed(reader);
376 
377   Pigweed::Message message{};
378   const auto status = pigweed.Read(message);
379   ASSERT_EQ(status, OkStatus());
380 
381   constexpr std::string_view kExpectedErrorMessage{"not a typewriter"};
382 
383   EXPECT_EQ(message.magic_number, 0x49u);
384   EXPECT_EQ(message.ziggy, -111);
385   EXPECT_EQ(message.cycles, 0x40302010fecaaddeu);
386   EXPECT_EQ(message.ratio, -1.42f);
387   EXPECT_EQ(message.error_message.size(), kExpectedErrorMessage.size());
388   EXPECT_EQ(std::memcmp(message.error_message.data(),
389                         kExpectedErrorMessage.data(),
390                         kExpectedErrorMessage.size()),
391             0);
392   EXPECT_EQ(message.bin, Pigweed::Protobuf::Binary::ZERO);
393   EXPECT_EQ(message.bungle, -111);
394 }
395 
TEST(CodegenMessage,ReadNonPackedScalar)396 TEST(CodegenMessage, ReadNonPackedScalar) {
397   // clang-format off
398   constexpr uint8_t proto_data[] = {
399     // uint32s[], v={0, 16, 32, 48}
400     0x08, 0x00,
401     0x08, 0x10,
402     0x08, 0x20,
403     0x08, 0x30,
404     // fixed32s[]. v={0, 16, 32, 48}
405     0x35, 0x00, 0x00, 0x00, 0x00,
406     0x35, 0x10, 0x00, 0x00, 0x00,
407     0x35, 0x20, 0x00, 0x00, 0x00,
408     0x35, 0x30, 0x00, 0x00, 0x00,
409   };
410   // clang-format on
411 
412   stream::MemoryReader reader(as_bytes(span(proto_data)));
413   RepeatedTest::StreamDecoder repeated_test(reader);
414 
415   RepeatedTest::Message message{};
416   const auto status = repeated_test.Read(message);
417   ASSERT_EQ(status, OkStatus());
418 
419   ASSERT_EQ(message.uint32s.size(), 4u);
420   for (unsigned short i = 0; i < 4; ++i) {
421     EXPECT_EQ(message.uint32s[i], i * 16u);
422   }
423 
424   ASSERT_EQ(message.fixed32s.size(), 4u);
425   for (unsigned short i = 0; i < 4; ++i) {
426     EXPECT_EQ(message.fixed32s[i], i * 16u);
427   }
428 }
429 
TEST(CodegenMessage,ReadPackedScalar)430 TEST(CodegenMessage, ReadPackedScalar) {
431   // clang-format off
432   constexpr uint8_t proto_data[] = {
433     // uint32s[], v={0, 16, 32, 48}
434     0x0a, 0x04,
435     0x00,
436     0x10,
437     0x20,
438     0x30,
439     // fixed32s[]. v={0, 16, 32, 48}
440     0x32, 0x10,
441     0x00, 0x00, 0x00, 0x00,
442     0x10, 0x00, 0x00, 0x00,
443     0x20, 0x00, 0x00, 0x00,
444     0x30, 0x00, 0x00, 0x00,
445   };
446   // clang-format on
447 
448   stream::MemoryReader reader(as_bytes(span(proto_data)));
449   RepeatedTest::StreamDecoder repeated_test(reader);
450 
451   RepeatedTest::Message message{};
452   const auto status = repeated_test.Read(message);
453   ASSERT_EQ(status, OkStatus());
454 
455   ASSERT_EQ(message.uint32s.size(), 4u);
456   for (unsigned short i = 0; i < 4; ++i) {
457     EXPECT_EQ(message.uint32s[i], i * 16u);
458   }
459 
460   ASSERT_EQ(message.fixed32s.size(), 4u);
461   for (unsigned short i = 0; i < 4; ++i) {
462     EXPECT_EQ(message.fixed32s[i], i * 16u);
463   }
464 }
465 
TEST(CodegenMessage,ReadPackedScalarRepeated)466 TEST(CodegenMessage, ReadPackedScalarRepeated) {
467   // clang-format off
468   constexpr uint8_t proto_data[] = {
469     // uint32s[], v={0, 16, 32, 48}
470     0x0a, 0x04,
471     0x00,
472     0x10,
473     0x20,
474     0x30,
475     // uint32s[], v={64, 80, 96, 112}
476     0x0a, 0x04,
477     0x40,
478     0x50,
479     0x60,
480     0x70,
481     // fixed32s[]. v={0, 16, 32, 48}
482     0x32, 0x10,
483     0x00, 0x00, 0x00, 0x00,
484     0x10, 0x00, 0x00, 0x00,
485     0x20, 0x00, 0x00, 0x00,
486     0x30, 0x00, 0x00, 0x00,
487     // fixed32s[]. v={64, 80, 96, 112}
488     0x32, 0x10,
489     0x40, 0x00, 0x00, 0x00,
490     0x50, 0x00, 0x00, 0x00,
491     0x60, 0x00, 0x00, 0x00,
492     0x70, 0x00, 0x00, 0x00,
493   };
494   // clang-format on
495 
496   stream::MemoryReader reader(as_bytes(span(proto_data)));
497   RepeatedTest::StreamDecoder repeated_test(reader);
498 
499   RepeatedTest::Message message{};
500   const auto status = repeated_test.Read(message);
501   ASSERT_EQ(status, OkStatus());
502 
503   ASSERT_EQ(message.uint32s.size(), 8u);
504   for (unsigned short i = 0; i < 8; ++i) {
505     EXPECT_EQ(message.uint32s[i], i * 16u);
506   }
507 
508   ASSERT_EQ(message.fixed32s.size(), 8u);
509   for (unsigned short i = 0; i < 8; ++i) {
510     EXPECT_EQ(message.fixed32s[i], i * 16u);
511   }
512 }
513 
TEST(CodegenMessage,ReadPackedScalarExhausted)514 TEST(CodegenMessage, ReadPackedScalarExhausted) {
515   // clang-format off
516   constexpr uint8_t proto_data[] = {
517     // uint32s[], v={0, 16, 32, 48, 64, 80, 96, 112, 128}
518     0x0a, 0x09,
519     0x00,
520     0x10,
521     0x20,
522     0x30,
523     0x40,
524     0x50,
525     0x60,
526     0x70,
527     0x80,
528   };
529   // clang-format on
530 
531   stream::MemoryReader reader(as_bytes(span(proto_data)));
532   RepeatedTest::StreamDecoder repeated_test(reader);
533 
534   // uint32s has max_size=8, so this will exhaust the vector.
535   RepeatedTest::Message message{};
536   const auto status = repeated_test.Read(message);
537   ASSERT_EQ(status, Status::ResourceExhausted());
538 }
539 
TEST(CodegenMessage,ReadPackedScalarCallback)540 TEST(CodegenMessage, ReadPackedScalarCallback) {
541   // clang-format off
542   constexpr uint8_t proto_data[] = {
543     // sint32s[], v={-25, -1, 0, 1, 25}
544     0x12, 0x05,
545     0x31,
546     0x01,
547     0x00,
548     0x02,
549     0x32,
550   };
551   // clang-format on
552 
553   stream::MemoryReader reader(as_bytes(span(proto_data)));
554   RepeatedTest::StreamDecoder repeated_test(reader);
555 
556   // sint32s is a repeated field declared without max_count, so requirses a
557   // callback to be decoded.
558   RepeatedTest::Message message{};
559   message.sint32s.SetDecoder([](RepeatedTest::StreamDecoder& decoder) {
560     EXPECT_EQ(decoder.Field().value(), RepeatedTest::Fields::kSint32s);
561 
562     pw::Vector<int32_t, 8> sint32s{};
563     const auto status = decoder.ReadSint32s(sint32s);
564     EXPECT_EQ(status, OkStatus());
565 
566     EXPECT_EQ(sint32s.size(), 5u);
567     EXPECT_EQ(sint32s[0], -25);
568     EXPECT_EQ(sint32s[1], -1);
569     EXPECT_EQ(sint32s[2], 0);
570     EXPECT_EQ(sint32s[3], 1);
571     EXPECT_EQ(sint32s[4], 25);
572 
573     return status;
574   });
575 
576   const auto status = repeated_test.Read(message);
577   ASSERT_EQ(status, OkStatus());
578 }
579 
TEST(CodegenMessage,ReadPackedScalarFixedLength)580 TEST(CodegenMessage, ReadPackedScalarFixedLength) {
581   // clang-format off
582   constexpr uint8_t proto_data[] = {
583     // uint64s[], v={1000, 2000, 3000, 4000}
584     0x42, 0x08, 0xe8, 0x07, 0xd0, 0x0f, 0xb8, 0x17, 0xa0, 0x1f,
585     // doubles[], v={3.14159, 2.71828}
586     0x22, 0x10,
587     0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x09, 0x40,
588     0x90, 0xf7, 0xaa, 0x95, 0x09, 0xbf, 0x05, 0x40,
589   };
590   // clang-format on
591 
592   stream::MemoryReader reader(as_bytes(span(proto_data)));
593   RepeatedTest::StreamDecoder repeated_test(reader);
594 
595   RepeatedTest::Message message{};
596   const auto status = repeated_test.Read(message);
597   ASSERT_EQ(status, OkStatus());
598 
599   EXPECT_EQ(message.uint64s[0], 1000u);
600   EXPECT_EQ(message.uint64s[1], 2000u);
601   EXPECT_EQ(message.uint64s[2], 3000u);
602   EXPECT_EQ(message.uint64s[3], 4000u);
603 
604   EXPECT_EQ(message.doubles[0], 3.14159);
605   EXPECT_EQ(message.doubles[1], 2.71828);
606 }
607 
TEST(CodegenMessage,ReadPackedScalarFixedLengthShort)608 TEST(CodegenMessage, ReadPackedScalarFixedLengthShort) {
609   // clang-format off
610   constexpr uint8_t proto_data[] = {
611     // uint64s[], v={1000, 2000}
612     0x42, 0x04, 0xe8, 0x07, 0xd0, 0x0f,
613     // doubles[], v={3.14159}
614     0x22, 0x08,
615     0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x09, 0x40,
616   };
617   // clang-format on
618 
619   stream::MemoryReader reader(as_bytes(span(proto_data)));
620   RepeatedTest::StreamDecoder repeated_test(reader);
621 
622   RepeatedTest::Message message{};
623   const auto status = repeated_test.Read(message);
624   ASSERT_EQ(status, OkStatus());
625 
626   EXPECT_EQ(message.uint64s[0], 1000u);
627   EXPECT_EQ(message.uint64s[1], 2000u);
628   EXPECT_EQ(message.uint64s[2], 0u);
629   EXPECT_EQ(message.uint64s[3], 0u);
630 
631   EXPECT_EQ(message.doubles[0], 3.14159);
632   EXPECT_EQ(message.doubles[1], 0);
633 }
634 
TEST(CodegenMessage,ReadPackedScalarVarintFixedLengthExhausted)635 TEST(CodegenMessage, ReadPackedScalarVarintFixedLengthExhausted) {
636   // clang-format off
637   constexpr uint8_t proto_data[] = {
638     // uint64s[], v={0, 1000, 2000, 3000, 4000}
639     0x42, 0x09, 0x08, 0xe8, 0x07, 0xd0, 0x0f, 0xb8, 0x17, 0xa0, 0x1f,
640   };
641   // clang-format on
642 
643   stream::MemoryReader reader(as_bytes(span(proto_data)));
644   RepeatedTest::StreamDecoder repeated_test(reader);
645 
646   RepeatedTest::Message message{};
647   const auto status = repeated_test.Read(message);
648   ASSERT_EQ(status, Status::ResourceExhausted());
649 }
650 
TEST(CodegenMessage,ReadPackedScalarFixedLengthExhausted)651 TEST(CodegenMessage, ReadPackedScalarFixedLengthExhausted) {
652   // clang-format off
653   constexpr uint8_t proto_data[] = {
654     // doubles[], v={3.14159, 2.71828, 1.41429, 1.73205}
655     0x22, 0x20,
656     0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x09, 0x40,
657     0x90, 0xf7, 0xaa, 0x95, 0x09, 0xbf, 0x05, 0x40,
658     0x1b, 0xf5, 0x10, 0x8d, 0xee, 0xa0, 0xf6, 0x3f,
659     0xbc, 0x96, 0x90, 0x0f, 0x7a, 0xb6, 0xfb, 0x3f,
660   };
661   // clang-format on
662 
663   stream::MemoryReader reader(as_bytes(span(proto_data)));
664   RepeatedTest::StreamDecoder repeated_test(reader);
665 
666   RepeatedTest::Message message{};
667   const auto status = repeated_test.Read(message);
668   ASSERT_EQ(status, Status::ResourceExhausted());
669 }
670 
TEST(CodegenMessage,ReadPackedEnum)671 TEST(CodegenMessage, ReadPackedEnum) {
672   // clang-format off
673   constexpr uint8_t proto_data[] = {
674     // enums[], v={RED, GREEN, AMBER, RED}
675     0x4a, 0x04, 0x00, 0x02, 0x01, 0x00,
676   };
677   // clang-format on
678 
679   stream::MemoryReader reader(as_bytes(span(proto_data)));
680   RepeatedTest::StreamDecoder repeated_test(reader);
681 
682   RepeatedTest::Message message{};
683   const auto status = repeated_test.Read(message);
684   ASSERT_EQ(status, OkStatus());
685 
686   ASSERT_EQ(message.enums.size(), 4u);
687   for (unsigned short i = 0; i < 4; ++i) {
688     EXPECT_TRUE(IsValidEnum(message.enums[i]));
689   }
690 
691   EXPECT_EQ(message.enums[0], Enum::RED);
692   EXPECT_EQ(message.enums[1], Enum::GREEN);
693   EXPECT_EQ(message.enums[2], Enum::AMBER);
694   EXPECT_EQ(message.enums[3], Enum::RED);
695 }
696 
TEST(CodegenMessage,ReadStringExhausted)697 TEST(CodegenMessage, ReadStringExhausted) {
698   // clang-format off
699   constexpr uint8_t proto_data[] = {
700     // pigweed.error_message
701     0x2a, 0xd3, 0x01, 'T', 'h', 'i', 's', ' ', 'l', 'a', 'b', 'e', 'l', ' ', 'i',
702     's', ' ', 't', 'h', 'e', ' ', 't', 'a', 'r', 'g', 'e', 't', ' ', 'o', 'f',
703     ' ', 'a', ' ', 'g', 'o', 't', 'o', ' ', 'f', 'r', 'o', 'm', ' ', 'o', 'u',
704     't', 's', 'i', 'd', 'e', ' ', 'o', 'f', ' ', 't', 'h', 'e', ' ', 'b', 'l',
705     'o', 'c', 'k', ' ', 'c', 'o', 'n', 't', 'a', 'i', 'n', 'i', 'n', 'g', ' ',
706     't', 'h', 'i', 's', ' ', 'l', 'a', 'b', 'e', 'l', ' ', 'A', 'N', 'D', ' ',
707     't', 'h', 'i', 's', ' ', 'b', 'l', 'o', 'c', 'k', ' ', 'h', 'a', 's', ' ',
708     'a', 'n', ' ', 'a', 'u', 't', 'o', 'm', 'a', 't', 'i', 'c', ' ', 'v', 'a',
709     'r', 'i', 'a', 'b', 'l', 'e', ' ', 'w', 'i', 't', 'h', ' ', 'a', 'n', ' ',
710     'i', 'n', 'i', 't', 'i', 'a', 'l', 'i', 'z', 'e', 'r', ' ', 'A', 'N', 'D',
711     ' ', 'y', 'o', 'u', 'r', ' ', 'w', 'i', 'n', 'd', 'o', 'w', ' ', 'w', 'a',
712     's', 'n', '\'', 't', ' ', 'w', 'i', 'd', 'e', ' ', 'e', 'n', 'o', 'u', 'g',
713     'h', ' ', 't', 'o', ' ', 'r', 'e', 'a', 'd', ' ', 't', 'h', 'i', 's', ' ',
714     'w', 'h', 'o', 'l', 'e', ' ', 'e', 'r', 'r', 'o', 'r', ' ', 'm', 'e', 's',
715     's', 'a', 'g', 'e'
716   };
717   // clang-format on
718 
719   stream::MemoryReader reader(as_bytes(span(proto_data)));
720   Pigweed::StreamDecoder pigweed(reader);
721 
722   Pigweed::Message message{};
723   const auto status = pigweed.Read(message);
724   ASSERT_EQ(status, Status::ResourceExhausted());
725 }
726 
TEST(CodegenMessage,ReadStringCallback)727 TEST(CodegenMessage, ReadStringCallback) {
728   // clang-format off
729   constexpr uint8_t proto_data[] = {
730     // pigweed.description
731     0x62, 0x5c, 'a', 'n', ' ', 'o', 'p', 'e', 'n', ' ', 's', 'o', 'u', 'r', 'c',
732     'e', ' ', 'c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n', ' ', 'o', 'f',
733     ' ', 'e', 'm', 'b', 'e', 'd', 'd', 'e', 'd', '-', 't', 'a', 'r', 'g', 'e',
734     't', 'e', 'd', ' ', 'l', 'i', 'b', 'r', 'a', 'r', 'i', 'e', 's', '-', 'o',
735     'r', ' ', 'a', 's', ' ', 'w', 'e', ' ', 'l', 'i', 'k', 'e', ' ', 't', 'o',
736     ' ', 'c', 'a', 'l', 'l', ' ', 't', 'h', 'e', 'm', ',', ' ', 'm', 'o', 'd',
737     'u', 'l', 'e', 's'
738   };
739   // clang-format on
740 
741   stream::MemoryReader reader(as_bytes(span(proto_data)));
742   Pigweed::StreamDecoder pigweed(reader);
743 
744   // pigweed.description has no max_size specified so a callback must be
745   // set to read the value if present.
746   Pigweed::Message message{};
747   message.description.SetDecoder([](Pigweed::StreamDecoder& decoder) {
748     EXPECT_EQ(decoder.Field().value(), Pigweed::Fields::kDescription);
749 
750     constexpr std::string_view kExpectedDescription{
751         "an open source collection of embedded-targeted libraries-or as we "
752         "like to call them, modules"};
753 
754     std::array<char, 128> description{};
755     const auto sws = decoder.ReadDescription(description);
756     EXPECT_EQ(sws.status(), OkStatus());
757     EXPECT_EQ(sws.size(), kExpectedDescription.size());
758     EXPECT_EQ(std::memcmp(description.data(),
759                           kExpectedDescription.data(),
760                           kExpectedDescription.size()),
761               0);
762 
763     return sws.status();
764   });
765 
766   const auto status = pigweed.Read(message);
767   ASSERT_EQ(status, OkStatus());
768 }
769 
TEST(CodegenMessage,ReadMultipleString)770 TEST(CodegenMessage, ReadMultipleString) {
771   // clang-format off
772   constexpr uint8_t proto_data[] = {
773     // pigweed.error_message
774     0x2a, 0x10, 'n', 'o', 't', ' ', 'a', ' ',
775     't', 'y', 'p', 'e', 'w', 'r', 'i', 't', 'e', 'r',
776     // pigweed.error_message
777     0x02a, 0x07, 'o', 'n', ' ', 'f', 'i', 'r', 'e'
778   };
779   // clang-format on
780 
781   stream::MemoryReader reader(as_bytes(span(proto_data)));
782   Pigweed::StreamDecoder pigweed(reader);
783 
784   Pigweed::Message message{};
785   const auto status = pigweed.Read(message);
786   ASSERT_EQ(status, OkStatus());
787 
788   constexpr std::string_view kExpectedErrorMessage{"on fire"};
789 
790   EXPECT_EQ(message.error_message.size(), kExpectedErrorMessage.size());
791   EXPECT_EQ(std::memcmp(message.error_message.data(),
792                         kExpectedErrorMessage.data(),
793                         kExpectedErrorMessage.size()),
794             0);
795 }
796 
TEST(CodegenMessage,ReadRepeatedStrings)797 TEST(CodegenMessage, ReadRepeatedStrings) {
798   // clang-format off
799   constexpr uint8_t proto_data[] = {
800     // repeated.strings
801     0x1a, 0x25, 'i', 'f', ' ', 'm', 'u', 's', 'i', 'c', ' ', 'b', 'e', ' ',
802     't', 'h', 'e', ' ', 'f', 'o', 'o', 'd', ' ', 'o', 'f', ' ',
803     'l', 'o', 'v', 'e', ',', ' ', 'p', 'l', 'a', 'y', ' ', 'o', 'n',
804     // repeated.strings
805     0x1a, 0x26, 'g', 'i', 'v', 'e', ' ', 'm', 'e', ' ', 'e', 'x', 'c', 'e',
806     's', 's', ' ', 'o', 'f', ' ', 'i', 't', ',', ' ', 't', 'h', 'a', 't', ',',
807     ' ', 's', 'u', 'r', 'f', 'e', 'i', 't', 'i', 'n', 'g',
808     // repeated.strings
809     0x1a, 0x23, 't', 'h', 'e', ' ', 'a', 'p', 'p', 'e', 't', 'i', 't', 'e', ' ',
810     'm', 'a', 'y', ' ', 's', 'i', 'c', 'k', 'e', 'n', ',', ' ', 'a', 'n', 'd',
811     ' ', 's', 'o', ' ', 'd', 'i', 'e',
812   };
813   // clang-format on
814 
815   stream::MemoryReader reader(as_bytes(span(proto_data)));
816   RepeatedTest::StreamDecoder repeated_test(reader);
817 
818   // Repeated strings require a callback to avoid forcing multi-dimensional
819   // arrays upon the caller.
820   RepeatedTest::Message message{};
821   int i = 0;
822   message.strings.SetDecoder([&i](RepeatedTest::StreamDecoder& decoder) {
823     EXPECT_EQ(decoder.Field().value(), RepeatedTest::Fields::kStrings);
824 
825     constexpr std::string_view kExpectedStrings[] = {
826         {"if music be the food of love, play on"},
827         {"give me excess of it, that, surfeiting"},
828         {"the appetite may sicken, and so die"}};
829 
830     std::array<char, 40> strings{};
831     const StatusWithSize sws = decoder.ReadStrings(strings);
832     EXPECT_EQ(sws.status(), OkStatus());
833     EXPECT_EQ(sws.size(), kExpectedStrings[i].size());
834     EXPECT_EQ(std::memcmp(strings.data(),
835                           kExpectedStrings[i].data(),
836                           kExpectedStrings[i].size()),
837               0);
838 
839     ++i;
840     return sws.status();
841   });
842 
843   const auto status = repeated_test.Read(message);
844   ASSERT_EQ(status, OkStatus());
845 }
846 
TEST(CodegenMessage,ReadForcedCallback)847 TEST(CodegenMessage, ReadForcedCallback) {
848   // clang-format off
849   constexpr uint8_t proto_data[] = {
850     // pigweed.special_property
851     0x68, 0x2a,
852   };
853   // clang-format on
854 
855   stream::MemoryReader reader(as_bytes(span(proto_data)));
856   Pigweed::StreamDecoder pigweed(reader);
857 
858   // pigweed.special_property has use_callback=true to force the use of a
859   // callback even though it's a simple scalar.
860   Pigweed::Message message{};
861   message.special_property.SetDecoder([](Pigweed::StreamDecoder& decoder) {
862     EXPECT_EQ(decoder.Field().value(), Pigweed::Fields::kSpecialProperty);
863 
864     pw::Result<uint32_t> result = decoder.ReadSpecialProperty();
865     EXPECT_EQ(result.status(), OkStatus());
866     EXPECT_EQ(result.value(), 42u);
867 
868     return result.status();
869   });
870   const auto status = pigweed.Read(message);
871   ASSERT_EQ(status, OkStatus());
872 }
873 
TEST(CodegenMessage,ReadMissingCallback)874 TEST(CodegenMessage, ReadMissingCallback) {
875   // clang-format off
876   constexpr uint8_t proto_data[] = {
877     // repeated.strings
878     0x1a, 0x25, 'i', 'f', ' ', 'm', 'u', 's', 'i', 'c', ' ', 'b', 'e', ' ',
879     't', 'h', 'e', ' ', 'f', 'o', 'o', 'd', ' ', 'o', 'f', ' ',
880     'l', 'o', 'v', 'e', ',', ' ', 'p', 'l', 'a', 'y', ' ', 'o', 'n',
881     // repeated.strings
882     0x1a, 0x26, 'g', 'i', 'v', 'e', ' ', 'm', 'e', ' ', 'e', 'x', 'c', 'e',
883     's', 's', ' ', 'o', 'f', ' ', 'i', 't', ',', ' ', 't', 'h', 'a', 't', ',',
884     ' ', 's', 'u', 'r', 'f', 'e', 'i', 't', 'i', 'n', 'g',
885     // repeated.strings
886     0x1a, 0x23, 't', 'h', 'e', ' ', 'a', 'p', 'p', 'e', 't', 'i', 't', 'e', ' ',
887     'm', 'a', 'y', ' ', 's', 'i', 'c', 'k', 'e', 'n', ',', ' ', 'a', 'n', 'd',
888     ' ', 's', 'o', ' ', 'd', 'i', 'e',
889   };
890   // clang-format on
891 
892   stream::MemoryReader reader(as_bytes(span(proto_data)));
893   RepeatedTest::StreamDecoder repeated_test(reader);
894 
895   // Failing to set a callback will give a DataLoss error if that field is
896   // present in the decoded data.
897   RepeatedTest::Message message{};
898   const auto status = repeated_test.Read(message);
899   ASSERT_EQ(status, Status::DataLoss());
900 }
901 
TEST(CodegenMessage,ReadFixedLength)902 TEST(CodegenMessage, ReadFixedLength) {
903   // clang-format off
904   constexpr uint8_t proto_data[] = {
905     // pigweed.data
906     0x5a, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
907   };
908   // clang-format on
909 
910   stream::MemoryReader reader(as_bytes(span(proto_data)));
911   Pigweed::StreamDecoder pigweed(reader);
912 
913   Pigweed::Message message{};
914   const auto status = pigweed.Read(message);
915   ASSERT_EQ(status, OkStatus());
916 
917   EXPECT_EQ(message.data[0], std::byte{0x01});
918   EXPECT_EQ(message.data[1], std::byte{0x02});
919   EXPECT_EQ(message.data[2], std::byte{0x03});
920   EXPECT_EQ(message.data[3], std::byte{0x04});
921   EXPECT_EQ(message.data[4], std::byte{0x05});
922   EXPECT_EQ(message.data[5], std::byte{0x06});
923   EXPECT_EQ(message.data[6], std::byte{0x07});
924   EXPECT_EQ(message.data[7], std::byte{0x08});
925 }
926 
TEST(CodegenMessage,ReadFixedLengthShort)927 TEST(CodegenMessage, ReadFixedLengthShort) {
928   // clang-format off
929   constexpr uint8_t proto_data[] = {
930     // pigweed.data
931     0x5a, 0x04, 0x01, 0x02, 0x03, 0x04
932   };
933   // clang-format on
934 
935   stream::MemoryReader reader(as_bytes(span(proto_data)));
936   Pigweed::StreamDecoder pigweed(reader);
937 
938   Pigweed::Message message{};
939   const auto status = pigweed.Read(message);
940   ASSERT_EQ(status, OkStatus());
941 
942   EXPECT_EQ(message.data[0], std::byte{0x01});
943   EXPECT_EQ(message.data[1], std::byte{0x02});
944   EXPECT_EQ(message.data[2], std::byte{0x03});
945   EXPECT_EQ(message.data[3], std::byte{0x04});
946   // Remaining bytes are whatever you initialized them to.
947   EXPECT_EQ(message.data[4], std::byte{0x00});
948   EXPECT_EQ(message.data[5], std::byte{0x00});
949   EXPECT_EQ(message.data[6], std::byte{0x00});
950   EXPECT_EQ(message.data[7], std::byte{0x00});
951 }
952 
TEST(CodegenMessage,ReadFixedLengthExhausted)953 TEST(CodegenMessage, ReadFixedLengthExhausted) {
954   // clang-format off
955   constexpr uint8_t proto_data[] = {
956     // pigweed.data
957     0x5a, 0x0c, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
958     0x09, 0x0a, 0x0b, 0x0c
959   };
960   // clang-format on
961 
962   stream::MemoryReader reader(as_bytes(span(proto_data)));
963   Pigweed::StreamDecoder pigweed(reader);
964 
965   Pigweed::Message message{};
966   const auto status = pigweed.Read(message);
967   ASSERT_EQ(status, Status::ResourceExhausted());
968 }
969 
TEST(CodegenMessage,ReadNested)970 TEST(CodegenMessage, ReadNested) {
971   // clang-format off
972   constexpr uint8_t proto_data[] = {
973     // pigweed.magic_number
974     0x08, 0x49,
975     // pigweed.pigweed
976     0x3a, 0x02,
977     // pigweed.pigweed.status
978     0x08, 0x02,
979     // pigweed.ziggy
980     0x10, 0xdd, 0x01,
981   };
982   // clang-format on
983 
984   stream::MemoryReader reader(as_bytes(span(proto_data)));
985   Pigweed::StreamDecoder pigweed(reader);
986 
987   Pigweed::Message message{};
988   const auto status = pigweed.Read(message);
989   ASSERT_EQ(status, OkStatus());
990 
991   EXPECT_EQ(message.magic_number, 0x49u);
992   EXPECT_EQ(message.pigweed.status, Bool::FILE_NOT_FOUND);
993   EXPECT_EQ(message.ziggy, -111);
994 }
995 
TEST(CodegenMessage,ReadNestedImported)996 TEST(CodegenMessage, ReadNestedImported) {
997   // clang-format off
998   constexpr uint8_t proto_data[] = {
999     // period.start
1000     0x0a, 0x08,
1001     // period.start.seconds v=1517949900
1002     0x08, 0xcc, 0xa7, 0xe8, 0xd3, 0x05,
1003     // period.start.nanoseconds v=0
1004     0x10, 0x00,
1005     // period.end
1006     0x12, 0x08,
1007     // period.end.seconds, v=1517950378
1008     0x08, 0xaa, 0xab, 0xe8, 0xd3, 0x05,
1009     // period.end.nanoseconds, v=0
1010     0x10, 0x00,
1011   };
1012   // clang-format on
1013 
1014   stream::MemoryReader reader(as_bytes(span(proto_data)));
1015   Period::StreamDecoder period(reader);
1016 
1017   // Messages imported from another file can be directly embedded in a message.
1018   Period::Message message{};
1019   const auto status = period.Read(message);
1020   ASSERT_EQ(status, OkStatus());
1021 
1022   EXPECT_EQ(message.start.seconds, 1517949900u);
1023   EXPECT_EQ(message.start.nanoseconds, 0u);
1024   EXPECT_EQ(message.end.seconds, 1517950378u);
1025   EXPECT_EQ(message.end.nanoseconds, 0u);
1026 }
1027 
TEST(CodegenMessage,ReadNestedRepeated)1028 TEST(CodegenMessage, ReadNestedRepeated) {
1029   // clang-format off
1030   constexpr uint8_t proto_data[] = {
1031     // repeated.structs
1032     0x2a, 0x04,
1033     // repeated.structs.one v=16
1034     0x08, 0x10,
1035     // repeated.structs.two v=32
1036     0x10, 0x20,
1037     // repeated.structs
1038     0x2a, 0x04,
1039     // repeated.structs.one v=48
1040     0x08, 0x30,
1041     // repeated.structs.two v=64
1042     0x10, 0x40,
1043   };
1044   // clang-format on
1045 
1046   stream::MemoryReader reader(as_bytes(span(proto_data)));
1047   RepeatedTest::StreamDecoder repeated_test(reader);
1048 
1049   // Repeated nested messages require a callback since there would otherwise be
1050   // no way to set callbacks on the nested message.
1051   RepeatedTest::Message message{};
1052   unsigned i = 0;
1053   message.structs.SetDecoder([&i](RepeatedTest::StreamDecoder& decoder) {
1054     EXPECT_EQ(decoder.Field().value(), RepeatedTest::Fields::kStructs);
1055 
1056     Struct::Message structs_message{};
1057     auto structs_decoder = decoder.GetStructsDecoder();
1058     const auto status = structs_decoder.Read(structs_message);
1059     EXPECT_EQ(status, OkStatus());
1060 
1061     EXPECT_LT(i, 2u);
1062     EXPECT_EQ(structs_message.one, i * 32 + 16u);
1063     EXPECT_EQ(structs_message.two, i * 32 + 32u);
1064     ++i;
1065 
1066     return status;
1067   });
1068 
1069   const auto status = repeated_test.Read(message);
1070   ASSERT_EQ(status, OkStatus());
1071 }
1072 
TEST(CodegenMessage,ReadNestedForcedCallback)1073 TEST(CodegenMessage, ReadNestedForcedCallback) {
1074   // clang-format off
1075   constexpr uint8_t proto_data[] = {
1076     // pigweed.device_info
1077     0x32, 0x0e,
1078     // pigweed.device_info.device_name
1079     0x0a, 0x05, 'p', 'i', 'x', 'e', 'l',
1080     // pigweed.device_info.device_id
1081     0x15, 0x08, 0x08, 0x08, 0x08,
1082     // pigweed.device_info.status
1083     0x18, 0x00,
1084       };
1085   // clang-format on
1086 
1087   stream::MemoryReader reader(as_bytes(span(proto_data)));
1088   Pigweed::StreamDecoder pigweed(reader);
1089 
1090   // pigweed.device_info has use_callback=true to force the use of a callback.
1091   Pigweed::Message message{};
1092   message.device_info.SetDecoder([](Pigweed::StreamDecoder& decoder) {
1093     EXPECT_EQ(decoder.Field().value(), Pigweed::Fields::kDeviceInfo);
1094 
1095     DeviceInfo::Message device_info{};
1096     DeviceInfo::StreamDecoder device_info_decoder =
1097         decoder.GetDeviceInfoDecoder();
1098     const auto status = device_info_decoder.Read(device_info);
1099     EXPECT_EQ(status, OkStatus());
1100 
1101     constexpr std::string_view kExpectedDeviceName{"pixel"};
1102 
1103     EXPECT_EQ(device_info.device_name.size(), kExpectedDeviceName.size());
1104     EXPECT_EQ(std::memcmp(device_info.device_name.data(),
1105                           kExpectedDeviceName.data(),
1106                           kExpectedDeviceName.size()),
1107               0);
1108     EXPECT_EQ(device_info.device_id, 0x08080808u);
1109     EXPECT_EQ(device_info.status, DeviceInfo::DeviceStatus::OK);
1110 
1111     return status;
1112   });
1113   const auto status = pigweed.Read(message);
1114   ASSERT_EQ(status, OkStatus());
1115 }
1116 
TEST(CodegenMessage,ReadOptionalPresent)1117 TEST(CodegenMessage, ReadOptionalPresent) {
1118   // clang-format off
1119   constexpr uint8_t proto_data[] = {
1120     // optional.sometimes_present_fixed
1121     0x0d, 0x2a, 0x00, 0x00, 0x00,
1122     // optional.sometimes_present_varint
1123     0x10, 0x2a,
1124     // optional.explicitly_present_fixed
1125     0x1d, 0x45, 0x00, 0x00, 0x00,
1126     // optional.explicitly_present_varint
1127     0x20, 0x45,
1128     // optional.sometimes_empty_fixed
1129     0x2a, 0x04, 0x63, 0x00, 0x00, 0x00,
1130     // optional.sometimes_empty_varint
1131     0x32, 0x01, 0x63,
1132   };
1133   // clang-format on
1134 
1135   stream::MemoryReader reader(as_bytes(span(proto_data)));
1136   OptionalTest::StreamDecoder optional_test(reader);
1137 
1138   OptionalTest::Message message{};
1139   const auto status = optional_test.Read(message);
1140   ASSERT_EQ(status, OkStatus());
1141 
1142   EXPECT_EQ(message.sometimes_present_fixed, 0x2a);
1143   EXPECT_EQ(message.sometimes_present_varint, 0x2a);
1144   EXPECT_TRUE(message.explicitly_present_fixed);
1145   EXPECT_EQ(*message.explicitly_present_fixed, 0x45);
1146   EXPECT_TRUE(message.explicitly_present_varint);
1147   EXPECT_EQ(*message.explicitly_present_varint, 0x45);
1148   EXPECT_FALSE(message.sometimes_empty_fixed.empty());
1149   EXPECT_EQ(message.sometimes_empty_fixed.size(), 1u);
1150   EXPECT_EQ(message.sometimes_empty_fixed[0], 0x63);
1151   EXPECT_FALSE(message.sometimes_empty_varint.empty());
1152   EXPECT_EQ(message.sometimes_empty_varint.size(), 1u);
1153   EXPECT_EQ(message.sometimes_empty_varint[0], 0x63);
1154 }
1155 
TEST(CodegenMessage,ReadOptionalNotPresent)1156 TEST(CodegenMessage, ReadOptionalNotPresent) {
1157   constexpr std::array<std::byte, 0> proto_data{};
1158 
1159   stream::MemoryReader reader(proto_data);
1160   OptionalTest::StreamDecoder optional_test(reader);
1161 
1162   OptionalTest::Message message{};
1163   const auto status = optional_test.Read(message);
1164   ASSERT_EQ(status, OkStatus());
1165 
1166   // Non-optional fields have their default value.
1167   EXPECT_EQ(message.sometimes_present_fixed, 0);
1168   EXPECT_EQ(message.sometimes_present_varint, 0);
1169   EXPECT_TRUE(message.sometimes_empty_fixed.empty());
1170   EXPECT_TRUE(message.sometimes_empty_varint.empty());
1171 
1172   // Optional fields are explicitly not present.
1173   EXPECT_FALSE(message.explicitly_present_fixed);
1174   EXPECT_FALSE(message.explicitly_present_varint);
1175 }
1176 
TEST(CodegenMessage,ReadOptionalPresentDefaults)1177 TEST(CodegenMessage, ReadOptionalPresentDefaults) {
1178   // clang-format off
1179   constexpr uint8_t proto_data[] = {
1180     // optional.sometimes_present_fixed
1181     0x0d, 0x00, 0x00, 0x00, 0x00,
1182     // optional.sometimes_present_varint
1183     0x10, 0x00,
1184     // optional.explicitly_present_fixed
1185     0x1d, 0x00, 0x00, 0x00, 0x00,
1186     // optional.explicitly_present_varint
1187     0x20, 0x00,
1188     // optional.sometimes_empty_fixed
1189     0x2a, 0x04, 0x00, 0x00, 0x00, 0x00,
1190     // optional.sometimes_empty_varint
1191     0x32, 0x01, 0x00,
1192   };
1193   // clang-format on
1194 
1195   stream::MemoryReader reader(as_bytes(span(proto_data)));
1196   OptionalTest::StreamDecoder optional_test(reader);
1197 
1198   OptionalTest::Message message{};
1199   const auto status = optional_test.Read(message);
1200   ASSERT_EQ(status, OkStatus());
1201 
1202   // Non-optional fields have their default value and aren't meaningfully
1203   // different from missing.
1204   EXPECT_EQ(message.sometimes_present_fixed, 0x00);
1205   EXPECT_EQ(message.sometimes_present_varint, 0x00);
1206 
1207   // Optional fields are explicitly present with a default value.
1208   EXPECT_TRUE(message.explicitly_present_fixed);
1209   EXPECT_EQ(*message.explicitly_present_fixed, 0x00);
1210   EXPECT_TRUE(message.explicitly_present_varint);
1211   EXPECT_EQ(*message.explicitly_present_varint, 0x00);
1212 
1213   // Repeated fields with a default value are meaningfully non-empty.
1214   EXPECT_FALSE(message.sometimes_empty_fixed.empty());
1215   EXPECT_EQ(message.sometimes_empty_fixed.size(), 1u);
1216   EXPECT_EQ(message.sometimes_empty_fixed[0], 0x00);
1217   EXPECT_FALSE(message.sometimes_empty_varint.empty());
1218   EXPECT_EQ(message.sometimes_empty_varint.size(), 1u);
1219   EXPECT_EQ(message.sometimes_empty_varint[0], 0x00);
1220 }
1221 
TEST(CodegenMessage,ReadImportedOptions)1222 TEST(CodegenMessage, ReadImportedOptions) {
1223   // clang-format off
1224   constexpr uint8_t proto_data[] = {
1225     // notice
1226     0x0a, 0x0f,
1227     // notice.message
1228     0x0a, 0x0d, 'P', 'r', 'e', 's', 's', ' ', 'a', 'n', 'y', ' ', 'k', 'e', 'y'
1229   };
1230   // clang-format on
1231 
1232   stream::MemoryReader reader(as_bytes(span(proto_data)));
1233   TestMessage::StreamDecoder test_message(reader);
1234 
1235   // The options file for the imported proto is applied, making the string
1236   // field a vector rather than requiring a callback.
1237   TestMessage::Message message{};
1238   const auto status = test_message.Read(message);
1239   ASSERT_EQ(status, OkStatus());
1240 
1241   constexpr std::string_view kExpectedMessage{"Press any key"};
1242 
1243   EXPECT_EQ(message.notice.message.size(), kExpectedMessage.size());
1244   EXPECT_EQ(std::memcmp(message.notice.message.data(),
1245                         kExpectedMessage.data(),
1246                         kExpectedMessage.size()),
1247             0);
1248 }
1249 
TEST(CodegenMessage,ReadImportedFromDepsOptions)1250 TEST(CodegenMessage, ReadImportedFromDepsOptions) {
1251   // clang-format off
1252   constexpr uint8_t proto_data[] = {
1253     // debug
1254     0x12, 0x0f,
1255     // debug.message
1256     0x0a, 0x0d, 'P', 'r', 'e', 's', 's', ' ', 'a', 'n', 'y', ' ', 'k', 'e', 'y'
1257   };
1258   // clang-format on
1259 
1260   stream::MemoryReader reader(as_bytes(span(proto_data)));
1261   TestMessage::StreamDecoder test_message(reader);
1262 
1263   // The options file for the imported proto is applied, making the string
1264   // fields a vector rather than requiring a callback. This will not compile if
1265   // the .pwpb_options files aren't applied correctly.
1266   TestMessage::Message message{};
1267   const auto status = test_message.Read(message);
1268   ASSERT_EQ(status, OkStatus());
1269 
1270   constexpr std::string_view kExpectedMessage{"Press any key"};
1271 
1272   EXPECT_EQ(message.debug.message.size(), kExpectedMessage.size());
1273   EXPECT_EQ(std::memcmp(message.debug.message.data(),
1274                         kExpectedMessage.data(),
1275                         kExpectedMessage.size()),
1276             0);
1277   EXPECT_EQ(message.prefix_debug.message.size(), 0U);
1278 }
1279 
1280 class BreakableDecoder : public KeyValuePair::StreamDecoder {
1281  public:
BreakableDecoder(stream::Reader & reader)1282   constexpr BreakableDecoder(stream::Reader& reader) : StreamDecoder(reader) {}
1283 
Read(KeyValuePair::Message & message,span<const internal::MessageField> table)1284   Status Read(KeyValuePair::Message& message,
1285               span<const internal::MessageField> table) {
1286     return ::pw::protobuf::StreamDecoder::Read(
1287         as_writable_bytes(span(&message, 1)), table);
1288   }
1289 };
1290 
TEST(CodegenMessage,DISABLED_ReadDoesNotOverrun)1291 TEST(CodegenMessage, DISABLED_ReadDoesNotOverrun) {
1292   // Deliberately construct a message table that attempts to violate the bounds
1293   // of the structure. We're not testing that a developer can't do this, rather
1294   // that the protobuf decoder can't be exploited in this way.
1295   constexpr internal::MessageField kMessageFields[] = {
1296       {1,
1297        WireType::kDelimited,
1298        sizeof(std::byte),
1299        static_cast<internal::VarintType>(0),
1300        false,
1301        false,
1302        false,
1303        false,
1304        internal::CallbackType::kNone,
1305        0,
1306        sizeof(KeyValuePair::Message) * 2,
1307        {}},
1308   };
1309 
1310   // clang-format off
1311   constexpr uint8_t proto_data[] = {
1312     // id=1, len=9,
1313     0x0a, 0x08, 'd', 'o', 'n', 't', 'e', 'a', 't', 'm', 'e',
1314   };
1315   // clang-format on
1316 
1317   stream::MemoryReader reader(as_bytes(span(proto_data)));
1318   BreakableDecoder decoder(reader);
1319 
1320   KeyValuePair::Message message{};
1321   // ASSERT_CRASH
1322   std::ignore = decoder.Read(message, kMessageFields);
1323 }
1324 
TEST(CodegenMessage,Write)1325 TEST(CodegenMessage, Write) {
1326   constexpr uint8_t pigweed_data[] = {
1327       0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
1328 
1329   Pigweed::Message message{};
1330   message.magic_number = 0x49u;
1331   message.ziggy = -111;
1332   message.cycles = 0x40302010fecaaddeu;
1333   message.ratio = -1.42f;
1334   message.error_message = "not a typewriter";
1335   message.pigweed.status = Bool::FILE_NOT_FOUND;
1336   message.bin = Pigweed::Protobuf::Binary::ZERO;
1337   message.bungle = -111;
1338   message.proto.bin = Proto::Binary::OFF;
1339   message.proto.pigweed_pigweed_bin = Pigweed::Pigweed::Binary::ZERO;
1340   message.proto.pigweed_protobuf_bin = Pigweed::Protobuf::Binary::ZERO;
1341   message.proto.meta.file_name = "/etc/passwd";
1342   message.proto.meta.status = Pigweed::Protobuf::Compiler::Status::FUBAR;
1343   message.proto.meta.protobuf_bin = Pigweed::Protobuf::Binary::ONE;
1344   message.proto.meta.pigweed_bin = Pigweed::Pigweed::Binary::ONE;
1345   std::memcpy(message.data.data(), pigweed_data, sizeof(pigweed_data));
1346 
1347   std::byte encode_buffer[Pigweed::kMaxEncodedSizeBytes];
1348   std::byte temp_buffer[Pigweed::kScratchBufferSizeBytes];
1349 
1350   stream::MemoryWriter writer(encode_buffer);
1351   Pigweed::StreamEncoder pigweed(writer, temp_buffer);
1352 
1353   const auto status = pigweed.Write(message);
1354   ASSERT_EQ(status, OkStatus());
1355 
1356   // clang-format off
1357   constexpr uint8_t expected_proto[] = {
1358     // pigweed.magic_number
1359     0x08, 0x49,
1360     // pigweed.ziggy
1361     0x10, 0xdd, 0x01,
1362     // pigweed.cycles
1363     0x19, 0xde, 0xad, 0xca, 0xfe, 0x10, 0x20, 0x30, 0x40,
1364     // pigweed.ratio
1365     0x25, 0x8f, 0xc2, 0xb5, 0xbf,
1366     // pigweed.error_message
1367     0x2a, 0x10, 'n', 'o', 't', ' ', 'a', ' ',
1368     't', 'y', 'p', 'e', 'w', 'r', 'i', 't', 'e', 'r',
1369     // pigweed.pigweed
1370     0x3a, 0x02,
1371     // pigweed.pigweed.status
1372     0x08, 0x02,
1373     // pigweed.bin
1374     0x40, 0x01,
1375     // pigweed.proto
1376     0x4a, 0x15,
1377     // pigweed.proto.pigweed_protobuf_bin
1378     0x20, 0x01,
1379     // pigweed.proto.meta
1380     0x2a, 0x11,
1381     // pigweed.proto.meta.file_name
1382     0x0a, 0x0b, '/', 'e', 't', 'c', '/', 'p', 'a', 's', 's', 'w', 'd',
1383     // pigweed.proto.meta.status
1384     0x10, 0x02,
1385     // pigweed.proto.meta.pigweed_bin
1386     0x20, 0x01,
1387     // pigweed.bytes
1388     0x5a, 0x08, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
1389     // pigweed.bungle
1390     0x70, 0x91, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01,
1391   };
1392   // clang-format on
1393 
1394   ConstByteSpan result = writer.WrittenData();
1395   EXPECT_EQ(result.size(), sizeof(expected_proto));
1396   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1397             0);
1398 }
1399 
TEST(CodegenMessage,WriteDefaults)1400 TEST(CodegenMessage, WriteDefaults) {
1401   Pigweed::Message message{};
1402 
1403   std::byte encode_buffer[Pigweed::kMaxEncodedSizeBytes];
1404   std::byte temp_buffer[Pigweed::kScratchBufferSizeBytes];
1405 
1406   stream::MemoryWriter writer(encode_buffer);
1407   Pigweed::StreamEncoder pigweed(writer, temp_buffer);
1408 
1409   const auto status = pigweed.Write(message);
1410   ASSERT_EQ(status, OkStatus());
1411 
1412   // Since all fields are at their default, the output should be zero sized.
1413   ConstByteSpan result = writer.WrittenData();
1414   EXPECT_EQ(result.size(), 0u);
1415 }
1416 
TEST(CodegenMessage,WritePackedScalar)1417 TEST(CodegenMessage, WritePackedScalar) {
1418   RepeatedTest::Message message{};
1419   for (unsigned i = 0; i < 4; ++i) {
1420     message.uint32s.push_back(i * 16u);
1421     message.fixed32s.push_back(i * 16u);
1422   }
1423 
1424   std::byte encode_buffer[RepeatedTest::kMaxEncodedSizeBytes];
1425 
1426   stream::MemoryWriter writer(encode_buffer);
1427   RepeatedTest::StreamEncoder repeated_test(writer, ByteSpan());
1428 
1429   const auto status = repeated_test.Write(message);
1430   ASSERT_EQ(status, OkStatus());
1431 
1432   // clang-format off
1433   constexpr uint8_t expected_proto[] = {
1434     // uint32s[], v={0, 16, 32, 48}
1435     0x0a, 0x04,
1436     0x00,
1437     0x10,
1438     0x20,
1439     0x30,
1440     // fixed32s[]. v={0, 16, 32, 48}
1441     0x32, 0x10,
1442     0x00, 0x00, 0x00, 0x00,
1443     0x10, 0x00, 0x00, 0x00,
1444     0x20, 0x00, 0x00, 0x00,
1445     0x30, 0x00, 0x00, 0x00,
1446   };
1447   // clang-format on
1448 
1449   ConstByteSpan result = writer.WrittenData();
1450   EXPECT_EQ(result.size(), sizeof(expected_proto));
1451   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1452             0);
1453 }
1454 
TEST(CodegenMessage,WritePackedScalarFixedLength)1455 TEST(CodegenMessage, WritePackedScalarFixedLength) {
1456   RepeatedTest::Message message{};
1457   for (unsigned i = 0; i < 4; ++i) {
1458     message.uint64s[i] = (i + 1) * 1000u;
1459   }
1460   message.doubles[0] = 3.14159;
1461   message.doubles[1] = 2.71828;
1462 
1463   std::byte encode_buffer[RepeatedTest::kMaxEncodedSizeBytes];
1464 
1465   stream::MemoryWriter writer(encode_buffer);
1466   RepeatedTest::StreamEncoder repeated_test(writer, ByteSpan());
1467 
1468   const auto status = repeated_test.Write(message);
1469   ASSERT_EQ(status, OkStatus());
1470 
1471   // clang-format off
1472   constexpr uint8_t expected_proto[] = {
1473     // doubles[], v={3.14159, 2.71828}
1474     0x22, 0x10,
1475     0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x09, 0x40,
1476     0x90, 0xf7, 0xaa, 0x95, 0x09, 0xbf, 0x05, 0x40,
1477     // uint64s[], v={1000, 2000, 3000, 4000}
1478     0x42, 0x08, 0xe8, 0x07, 0xd0, 0x0f, 0xb8, 0x17, 0xa0, 0x1f,
1479   };
1480   // clang-format on
1481 
1482   ConstByteSpan result = writer.WrittenData();
1483   EXPECT_EQ(result.size(), sizeof(expected_proto));
1484   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1485             0);
1486 }
1487 
TEST(CodegenMessage,WritePackedScalarCallback)1488 TEST(CodegenMessage, WritePackedScalarCallback) {
1489   RepeatedTest::Message message{};
1490   message.sint32s.SetEncoder([](RepeatedTest::StreamEncoder& encoder) {
1491     constexpr int32_t sint32s[] = {-25, -1, 0, 1, 25};
1492     return encoder.WriteSint32s(sint32s);
1493   });
1494 
1495   std::byte encode_buffer[RepeatedTest::kMaxEncodedSizeBytes +
1496                           varint::kMaxVarint32SizeBytes * 5];
1497 
1498   stream::MemoryWriter writer(encode_buffer);
1499   RepeatedTest::StreamEncoder repeated_test(writer, ByteSpan());
1500 
1501   const auto status = repeated_test.Write(message);
1502   ASSERT_EQ(status, OkStatus());
1503 
1504   // clang-format off
1505   constexpr uint8_t expected_proto[] = {
1506     // sint32s[], v={-25, -1, 0, 1, 25}
1507     0x12, 0x05,
1508     0x31,
1509     0x01,
1510     0x00,
1511     0x02,
1512     0x32,
1513   };
1514   // clang-format on
1515 
1516   ConstByteSpan result = writer.WrittenData();
1517   EXPECT_EQ(result.size(), sizeof(expected_proto));
1518   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1519             0);
1520 }
1521 
TEST(CodegenMessage,WritePackedEnum)1522 TEST(CodegenMessage, WritePackedEnum) {
1523   RepeatedTest::Message message{};
1524   message.enums.push_back(Enum::RED);
1525   message.enums.push_back(Enum::GREEN);
1526   message.enums.push_back(Enum::AMBER);
1527   message.enums.push_back(Enum::RED);
1528 
1529   std::byte encode_buffer[RepeatedTest::kMaxEncodedSizeBytes];
1530 
1531   stream::MemoryWriter writer(encode_buffer);
1532   RepeatedTest::StreamEncoder repeated_test(writer, ByteSpan());
1533 
1534   const auto status = repeated_test.Write(message);
1535   ASSERT_EQ(status, OkStatus());
1536 
1537   // clang-format off
1538   constexpr uint8_t expected_proto[] = {
1539     // enums[], v={RED, GREEN, AMBER, RED}
1540     0x4a, 0x04, 0x00, 0x02, 0x01, 0x00,
1541   };
1542   // clang-format on
1543 
1544   ConstByteSpan result = writer.WrittenData();
1545   EXPECT_EQ(result.size(), sizeof(expected_proto));
1546   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1547             0);
1548 }
1549 
TEST(CodegenMessage,WriteStringCallback)1550 TEST(CodegenMessage, WriteStringCallback) {
1551   Pigweed::Message message{};
1552   // pigweed.description has no max_size specified so a callback must be
1553   // set to write the value.
1554   message.description.SetEncoder([](Pigweed::StreamEncoder& encoder) {
1555     return encoder.WriteDescription(
1556         "an open source collection of embedded-targeted "
1557         "libraries-or as we like to call them, modules");
1558   });
1559 
1560   std::byte encode_buffer[Pigweed::kMaxEncodedSizeBytes + 92];
1561   std::byte temp_buffer[Pigweed::kScratchBufferSizeBytes];
1562 
1563   stream::MemoryWriter writer(encode_buffer);
1564   Pigweed::StreamEncoder pigweed(writer, temp_buffer);
1565 
1566   const auto status = pigweed.Write(message);
1567   ASSERT_EQ(status, OkStatus());
1568 
1569   // clang-format off
1570   constexpr uint8_t expected_proto[] = {
1571     // pigweed.description
1572     0x62, 0x5c, 'a', 'n', ' ', 'o', 'p', 'e', 'n', ' ', 's', 'o', 'u', 'r', 'c',
1573     'e', ' ', 'c', 'o', 'l', 'l', 'e', 'c', 't', 'i', 'o', 'n', ' ', 'o', 'f',
1574     ' ', 'e', 'm', 'b', 'e', 'd', 'd', 'e', 'd', '-', 't', 'a', 'r', 'g', 'e',
1575     't', 'e', 'd', ' ', 'l', 'i', 'b', 'r', 'a', 'r', 'i', 'e', 's', '-', 'o',
1576     'r', ' ', 'a', 's', ' ', 'w', 'e', ' ', 'l', 'i', 'k', 'e', ' ', 't', 'o',
1577     ' ', 'c', 'a', 'l', 'l', ' ', 't', 'h', 'e', 'm', ',', ' ', 'm', 'o', 'd',
1578     'u', 'l', 'e', 's',
1579   };
1580   // clang-format on
1581 
1582   ConstByteSpan result = writer.WrittenData();
1583   EXPECT_EQ(result.size(), sizeof(expected_proto));
1584   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1585             0);
1586 }
1587 
TEST(CodegenMessage,WriteForcedCallback)1588 TEST(CodegenMessage, WriteForcedCallback) {
1589   Pigweed::Message message{};
1590   // pigweed.special_property has use_callback=true to force the use of a
1591   // callback even though it's a simple scalar.
1592   message.special_property.SetEncoder([](Pigweed::StreamEncoder& encoder) {
1593     return encoder.WriteSpecialProperty(42u);
1594   });
1595 
1596   std::byte encode_buffer[Pigweed::kMaxEncodedSizeBytes];
1597   std::byte temp_buffer[Pigweed::kScratchBufferSizeBytes];
1598 
1599   stream::MemoryWriter writer(encode_buffer);
1600   Pigweed::StreamEncoder pigweed(writer, temp_buffer);
1601 
1602   const auto status = pigweed.Write(message);
1603   ASSERT_EQ(status, OkStatus());
1604 
1605   // clang-format off
1606   constexpr uint8_t expected_proto[] = {
1607     // pigweed.special_property
1608     0x68, 0x2a,
1609   };
1610   // clang-format on
1611 
1612   ConstByteSpan result = writer.WrittenData();
1613   EXPECT_EQ(result.size(), sizeof(expected_proto));
1614   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1615             0);
1616 }
1617 
TEST(CodegenMessage,WriteNestedImported)1618 TEST(CodegenMessage, WriteNestedImported) {
1619   Period::Message message{};
1620   message.start.seconds = 1517949900u;
1621   message.end.seconds = 1517950378u;
1622 
1623   std::byte encode_buffer[Period::kMaxEncodedSizeBytes];
1624   std::byte temp_buffer[Period::kScratchBufferSizeBytes];
1625 
1626   stream::MemoryWriter writer(encode_buffer);
1627   Period::StreamEncoder period(writer, temp_buffer);
1628 
1629   const auto status = period.Write(message);
1630   ASSERT_EQ(status, OkStatus());
1631 
1632   // clang-format off
1633   constexpr uint8_t expected_proto[] = {
1634     // period.start
1635     0x0a, 0x06,
1636     // period.start.seconds v=1517949900
1637     0x08, 0xcc, 0xa7, 0xe8, 0xd3, 0x05,
1638     // period.end
1639     0x12, 0x06,
1640     // period.end.seconds, v=1517950378
1641     0x08, 0xaa, 0xab, 0xe8, 0xd3, 0x05,
1642   };
1643   // clang-format on
1644 
1645   ConstByteSpan result = writer.WrittenData();
1646   EXPECT_EQ(result.size(), sizeof(expected_proto));
1647   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1648             0);
1649 }
1650 
TEST(CodegenMessage,WriteNestedRepeated)1651 TEST(CodegenMessage, WriteNestedRepeated) {
1652   RepeatedTest::Message message{};
1653   // Repeated nested messages require a callback since there would otherwise be
1654   // no way to set callbacks on the nested message.
1655   message.structs.SetEncoder([](RepeatedTest::StreamEncoder& encoder) {
1656     for (uint32_t i = 0; i < 2; ++i) {
1657       Struct::Message struct_message{};
1658       struct_message.one = i * 32 + 16u;
1659       struct_message.two = i * 32 + 32u;
1660 
1661       const auto status = encoder.GetStructsEncoder().Write(struct_message);
1662       EXPECT_EQ(status, OkStatus());
1663     }
1664     return OkStatus();
1665   });
1666 
1667   std::byte encode_buffer[RepeatedTest::kMaxEncodedSizeBytes +
1668                           Struct::kMaxEncodedSizeBytes * 2];
1669   std::byte temp_buffer[RepeatedTest::kScratchBufferSizeBytes +
1670                         Struct::kMaxEncodedSizeBytes];
1671 
1672   stream::MemoryWriter writer(encode_buffer);
1673   RepeatedTest::StreamEncoder repeated_test(writer, temp_buffer);
1674 
1675   const auto status = repeated_test.Write(message);
1676   ASSERT_EQ(status, OkStatus());
1677 
1678   // clang-format off
1679   constexpr uint8_t expected_proto[] = {
1680     // repeated.structs
1681     0x2a, 0x04,
1682     // repeated.structs.one v=16
1683     0x08, 0x10,
1684     // repeated.structs.two v=32
1685     0x10, 0x20,
1686     // repeated.structs
1687     0x2a, 0x04,
1688     // repeated.structs.one v=48
1689     0x08, 0x30,
1690     // repeated.structs.two v=64
1691     0x10, 0x40,
1692   };
1693   // clang-format on
1694 
1695   ConstByteSpan result = writer.WrittenData();
1696   EXPECT_EQ(result.size(), sizeof(expected_proto));
1697   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1698             0);
1699 }
1700 
TEST(CodegenMessage,WriteNestedForcedCallback)1701 TEST(CodegenMessage, WriteNestedForcedCallback) {
1702   Pigweed::Message message{};
1703   // pigweed.device_info has use_callback=true to force the use of a callback.
1704   message.device_info.SetEncoder([](Pigweed::StreamEncoder& encoder) {
1705     DeviceInfo::Message device_info{};
1706     device_info.device_name = "pixel";
1707     device_info.device_id = 0x08080808u;
1708     device_info.status = DeviceInfo::DeviceStatus::OK;
1709 
1710     // Use the callback to set nested callbacks.
1711     device_info.attributes.SetEncoder(
1712         [](DeviceInfo::StreamEncoder& device_info_encoder) {
1713           KeyValuePair::Message attribute{};
1714 
1715           attribute.key = "version";
1716           attribute.value = "5.3.1";
1717           PW_TRY(device_info_encoder.GetAttributesEncoder().Write(attribute));
1718 
1719           attribute.key = "chip";
1720           attribute.value = "left-soc";
1721           PW_TRY(device_info_encoder.GetAttributesEncoder().Write(attribute));
1722 
1723           return OkStatus();
1724         });
1725 
1726     return encoder.GetDeviceInfoEncoder().Write(device_info);
1727   });
1728 
1729   std::byte encode_buffer[Pigweed::kMaxEncodedSizeBytes +
1730                           DeviceInfo::kMaxEncodedSizeBytes];
1731   std::byte temp_buffer[Pigweed::kScratchBufferSizeBytes +
1732                         DeviceInfo::kMaxEncodedSizeBytes];
1733 
1734   stream::MemoryWriter writer(encode_buffer);
1735   Pigweed::StreamEncoder pigweed(writer, temp_buffer);
1736 
1737   const auto status = pigweed.Write(message);
1738   ASSERT_EQ(status, OkStatus());
1739 
1740   // clang-format off
1741   constexpr uint8_t expected_proto[] = {
1742     // pigweed.device_info
1743     0x32, 0x30,
1744     // pigweed.device_info.device_name
1745     0x0a, 0x05, 'p', 'i', 'x', 'e', 'l',
1746     // pigweed.device_info.device_id
1747     0x15, 0x08, 0x08, 0x08, 0x08,
1748     // pigweed.device_info.attributes[0]
1749     0x22, 0x10,
1750     // pigweed.device_info.attributes[0].key
1751     0x0a, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n',
1752     // pigweed.device_info.attributes[0].value
1753     0x12, 0x05, '5', '.', '3', '.', '1',
1754     // pigweed.device_info.attributes[1]
1755     0x22, 0x10,
1756     // pigweed.device_info.attributes[1].key
1757     0x0a, 0x04, 'c', 'h', 'i', 'p',
1758     // pigweed.device_info.attributes[1].value
1759     0x12, 0x08, 'l', 'e', 'f', 't', '-', 's', 'o', 'c',
1760   };
1761   // clang-format on
1762 
1763   ConstByteSpan result = writer.WrittenData();
1764   EXPECT_EQ(result.size(), sizeof(expected_proto));
1765   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1766             0);
1767 }
1768 
TEST(CodegenMessage,EnumAliases)1769 TEST(CodegenMessage, EnumAliases) {
1770   // Unprefixed enum.
1771   EXPECT_EQ(Bool::kTrue, Bool::TRUE);
1772   EXPECT_EQ(Bool::kFalse, Bool::FALSE);
1773   EXPECT_EQ(Bool::kFileNotFound, Bool::FILE_NOT_FOUND);
1774 
1775   // Prefixed enum has the prefix removed.
1776   EXPECT_EQ(Error::kNone, Error::ERROR_NONE);
1777   EXPECT_EQ(Error::kNotFound, Error::ERROR_NOT_FOUND);
1778   EXPECT_EQ(Error::kUnknown, Error::ERROR_UNKNOWN);
1779 
1780   // Single-value enum.
1781   EXPECT_EQ(AlwaysBlue::kBlue, AlwaysBlue::BLUE);
1782 }
1783 
TEST(CodegenMessage,WriteOptionalPresent)1784 TEST(CodegenMessage, WriteOptionalPresent) {
1785   OptionalTest::Message message{};
1786   message.sometimes_present_fixed = 0x2a;
1787   message.sometimes_present_varint = 0x2a;
1788   message.explicitly_present_fixed = 0x45;
1789   message.explicitly_present_varint = 0x45;
1790   message.sometimes_empty_fixed.push_back(0x63);
1791   message.sometimes_empty_varint.push_back(0x63);
1792 
1793   std::byte encode_buffer[512];
1794 
1795   stream::MemoryWriter writer(encode_buffer);
1796   OptionalTest::StreamEncoder optional_test(writer, ByteSpan());
1797 
1798   const auto status = optional_test.Write(message);
1799   ASSERT_EQ(status, OkStatus());
1800 
1801   // clang-format off
1802   constexpr uint8_t expected_proto[] = {
1803     // optional.sometimes_present_fixed
1804     0x0d, 0x2a, 0x00, 0x00, 0x00,
1805     // optional.sometimes_present_varint
1806     0x10, 0x2a,
1807     // optional.explicitly_present_fixed
1808     0x1d, 0x45, 0x00, 0x00, 0x00,
1809     // optional.explicitly_present_varint
1810     0x20, 0x45,
1811     // optional.sometimes_empty_fixed
1812     0x2a, 0x04, 0x63, 0x00, 0x00, 0x00,
1813     // optional.sometimes_empty_varint
1814     0x32, 0x01, 0x63,
1815   };
1816   // clang-format on
1817 
1818   ConstByteSpan result = writer.WrittenData();
1819   EXPECT_EQ(result.size(), sizeof(expected_proto));
1820   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1821             0);
1822 }
1823 
TEST(CodegenMessage,WriteOptionalNotPresent)1824 TEST(CodegenMessage, WriteOptionalNotPresent) {
1825   OptionalTest::Message message{};
1826 
1827   std::byte encode_buffer[512];
1828 
1829   stream::MemoryWriter writer(encode_buffer);
1830   OptionalTest::StreamEncoder optional_test(writer, ByteSpan());
1831 
1832   const auto status = optional_test.Write(message);
1833   ASSERT_EQ(status, OkStatus());
1834 
1835   // The expected proto is empty; no bytes should be written.
1836 
1837   ConstByteSpan result = writer.WrittenData();
1838   EXPECT_TRUE(result.empty());
1839 }
1840 
TEST(CodegenMessage,WriteOptionalPresentDefaults)1841 TEST(CodegenMessage, WriteOptionalPresentDefaults) {
1842   OptionalTest::Message message{};
1843   // Non-optional fields with a default value are not explicitly encoded, so
1844   // aren't meaningfully different from one that's just ommitted.
1845   message.sometimes_present_fixed = 0x00;
1846   message.sometimes_present_varint = 0x00;
1847   // Optional fields, even with a default value, are explicitly encoded.
1848   message.explicitly_present_fixed = 0x00;
1849   message.explicitly_present_varint = 0x00;
1850   // Repeated fields with a default value are meaningfully non-empty.
1851   message.sometimes_empty_fixed.push_back(0x00);
1852   message.sometimes_empty_varint.push_back(0x00);
1853 
1854   std::byte encode_buffer[512];
1855 
1856   stream::MemoryWriter writer(encode_buffer);
1857   OptionalTest::StreamEncoder optional_test(writer, ByteSpan());
1858 
1859   const auto status = optional_test.Write(message);
1860   ASSERT_EQ(status, OkStatus());
1861 
1862   // clang-format off
1863   constexpr uint8_t expected_proto[] = {
1864     // optional.explicitly_present_fixed
1865     0x1d, 0x00, 0x00, 0x00, 0x00,
1866     // optional.explicitly_present_varint
1867     0x20, 0x00,
1868     // optional.sometimes_empty_fixed
1869     0x2a, 0x04, 0x00, 0x00, 0x00, 0x00,
1870     // optional.sometimes_empty_varint
1871     0x32, 0x01, 0x00,
1872   };
1873   // clang-format on
1874 
1875   ConstByteSpan result = writer.WrittenData();
1876   EXPECT_EQ(result.size(), sizeof(expected_proto));
1877   EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
1878             0);
1879 }
1880 
1881 class BreakableEncoder : public KeyValuePair::MemoryEncoder {
1882  public:
BreakableEncoder(ByteSpan buffer)1883   constexpr BreakableEncoder(ByteSpan buffer)
1884       : KeyValuePair::MemoryEncoder(buffer) {}
1885 
Write(const KeyValuePair::Message & message,span<const internal::MessageField> table)1886   Status Write(const KeyValuePair::Message& message,
1887                span<const internal::MessageField> table) {
1888     return ::pw::protobuf::StreamEncoder::Write(as_bytes(span(&message, 1)),
1889                                                 table);
1890   }
1891 };
1892 
TEST(CodegenMessage,DISABLED_WriteDoesNotOverrun)1893 TEST(CodegenMessage, DISABLED_WriteDoesNotOverrun) {
1894   // Deliberately construct a message table that attempts to violate the bounds
1895   // of the structure. We're not testing that a developer can't do this, rather
1896   // that the protobuf encoder can't be exploited in this way.
1897   constexpr internal::MessageField kMessageFields[] = {
1898       {1,
1899        WireType::kDelimited,
1900        sizeof(std::byte),
1901        static_cast<internal::VarintType>(0),
1902        false,
1903        false,
1904        false,
1905        false,
1906        internal::CallbackType::kNone,
1907        0,
1908        sizeof(KeyValuePair::Message) * 2,
1909        {}},
1910   };
1911 
1912   std::byte encode_buffer[64];
1913 
1914   BreakableEncoder encoder(encode_buffer);
1915   KeyValuePair::Message message{};
1916   // ASSERT_CRASH
1917   std::ignore = encoder.Write(message, kMessageFields);
1918 }
1919 
1920 // The following tests cover using the codegen struct Message and callbacks in
1921 // different ways.
1922 
1923 // Check that the callback function object is large enough to implement a
1924 // "call a function on this" lambda.
1925 class StringChecker {
1926  public:
1927   StringChecker() = default;
1928   ~StringChecker() = default;
1929 
Check(RepeatedTest::StreamDecoder & repeated_test)1930   Status Check(RepeatedTest::StreamDecoder& repeated_test) {
1931     RepeatedTest::Message message{};
1932     message.strings.SetDecoder([this](RepeatedTest::StreamDecoder& decoder) {
1933       return this->CheckOne(decoder);
1934     });
1935     return repeated_test.Read(message);
1936   }
1937 
1938  private:
CheckOne(RepeatedTest::StreamDecoder & decoder)1939   Status CheckOne(RepeatedTest::StreamDecoder& decoder) {
1940     EXPECT_EQ(decoder.Field().value(), RepeatedTest::Fields::kStrings);
1941 
1942     std::array<char, 40> strings{};
1943     const StatusWithSize sws = decoder.ReadStrings(strings);
1944     EXPECT_EQ(sws.status(), OkStatus());
1945     EXPECT_EQ(sws.size(), kExpectedStrings[i_].size());
1946     EXPECT_EQ(std::memcmp(strings.data(),
1947                           kExpectedStrings[i_].data(),
1948                           kExpectedStrings[i_].size()),
1949               0);
1950 
1951     ++i_;
1952     return sws.status();
1953   }
1954 
1955   int i_ = 0;
1956   constexpr static std::string_view kExpectedStrings[] = {
1957       {"if music be the food of love, play on"},
1958       {"give me excess of it, that, surfeiting"},
1959       {"the appetite may sicken, and so die"}};
1960 };
1961 
TEST(CodegenMessage,CallbackInClass)1962 TEST(CodegenMessage, CallbackInClass) {
1963   // clang-format off
1964   constexpr uint8_t proto_data[] = {
1965     // repeated.strings
1966     0x1a, 0x25, 'i', 'f', ' ', 'm', 'u', 's', 'i', 'c', ' ', 'b', 'e', ' ',
1967     't', 'h', 'e', ' ', 'f', 'o', 'o', 'd', ' ', 'o', 'f', ' ',
1968     'l', 'o', 'v', 'e', ',', ' ', 'p', 'l', 'a', 'y', ' ', 'o', 'n',
1969     // repeated.strings
1970     0x1a, 0x26, 'g', 'i', 'v', 'e', ' ', 'm', 'e', ' ', 'e', 'x', 'c', 'e',
1971     's', 's', ' ', 'o', 'f', ' ', 'i', 't', ',', ' ', 't', 'h', 'a', 't', ',',
1972     ' ', 's', 'u', 'r', 'f', 'e', 'i', 't', 'i', 'n', 'g',
1973     // repeated.strings
1974     0x1a, 0x23, 't', 'h', 'e', ' ', 'a', 'p', 'p', 'e', 't', 'i', 't', 'e', ' ',
1975     'm', 'a', 'y', ' ', 's', 'i', 'c', 'k', 'e', 'n', ',', ' ', 'a', 'n', 'd',
1976     ' ', 's', 'o', ' ', 'd', 'i', 'e',
1977   };
1978   // clang-format on
1979 
1980   stream::MemoryReader reader(as_bytes(span(proto_data)));
1981   RepeatedTest::StreamDecoder repeated_test(reader);
1982 
1983   StringChecker checker{};
1984   const auto status = checker.Check(repeated_test);
1985   ASSERT_EQ(status, OkStatus());
1986 }
1987 
1988 // Check that we can create a custom subclass of the message struct that sets
1989 // its own callbacks to member functions that populate fields added in the
1990 // subclass.
1991 struct CustomMessage : RepeatedTest::Message {
CustomMessagepw::protobuf::__anon3b63cbad0111::CustomMessage1992   CustomMessage() : RepeatedTest::Message() {
1993     strings.SetDecoder([this](RepeatedTest::StreamDecoder& decoder) {
1994       return this->ParseStrings(decoder);
1995     });
1996   }
1997 
1998   pw::Vector<std::array<char, 40>, 8> all_strings{};
1999 
2000  private:
ParseStringspw::protobuf::__anon3b63cbad0111::CustomMessage2001   Status ParseStrings(RepeatedTest::StreamDecoder& decoder) {
2002     PW_ASSERT(decoder.Field().value() == RepeatedTest::Fields::kStrings);
2003 
2004     std::array<char, 40> one_strings{};
2005     const auto sws = decoder.ReadStrings(one_strings);
2006     if (!sws.ok()) {
2007       return sws.status();
2008     }
2009 
2010     one_strings[sws.size()] = '\0';
2011     all_strings.push_back(one_strings);
2012 
2013     return OkStatus();
2014   }
2015 };
2016 
TEST(CodegenMessage,CallbackInSubclass)2017 TEST(CodegenMessage, CallbackInSubclass) {
2018   // clang-format off
2019   constexpr uint8_t proto_data[] = {
2020     // repeated.strings
2021     0x1a, 0x25, 'i', 'f', ' ', 'm', 'u', 's', 'i', 'c', ' ', 'b', 'e', ' ',
2022     't', 'h', 'e', ' ', 'f', 'o', 'o', 'd', ' ', 'o', 'f', ' ',
2023     'l', 'o', 'v', 'e', ',', ' ', 'p', 'l', 'a', 'y', ' ', 'o', 'n',
2024     // repeated.strings
2025     0x1a, 0x26, 'g', 'i', 'v', 'e', ' ', 'm', 'e', ' ', 'e', 'x', 'c', 'e',
2026     's', 's', ' ', 'o', 'f', ' ', 'i', 't', ',', ' ', 't', 'h', 'a', 't', ',',
2027     ' ', 's', 'u', 'r', 'f', 'e', 'i', 't', 'i', 'n', 'g',
2028     // repeated.strings
2029     0x1a, 0x23, 't', 'h', 'e', ' ', 'a', 'p', 'p', 'e', 't', 'i', 't', 'e', ' ',
2030     'm', 'a', 'y', ' ', 's', 'i', 'c', 'k', 'e', 'n', ',', ' ', 'a', 'n', 'd',
2031     ' ', 's', 'o', ' ', 'd', 'i', 'e',
2032   };
2033   // clang-format on
2034 
2035   stream::MemoryReader reader(as_bytes(span(proto_data)));
2036   RepeatedTest::StreamDecoder repeated_test(reader);
2037 
2038   CustomMessage message{};
2039   const auto status = repeated_test.Read(message);
2040   ASSERT_EQ(status, OkStatus());
2041 
2042   constexpr static std::string_view kExpectedStrings[] = {
2043       {"if music be the food of love, play on"},
2044       {"give me excess of it, that, surfeiting"},
2045       {"the appetite may sicken, and so die"}};
2046 
2047   EXPECT_EQ(message.all_strings.size(), 3u);
2048   for (unsigned short i = 0; i < 3; ++i) {
2049     EXPECT_EQ(std::memcmp(message.all_strings[i].data(),
2050                           kExpectedStrings[i].data(),
2051                           kExpectedStrings[i].size()),
2052               0);
2053     EXPECT_EQ(message.all_strings[i].data()[kExpectedStrings[i].size()], '\0');
2054   }
2055 }
2056 
TEST(CodegenMessage,MaxSize)2057 TEST(CodegenMessage, MaxSize) {
2058   // Verify constants generated from max_size options in full_test.pwpb_options
2059   static_assert(Pigweed::kErrorMessageMaxSize == 64);
2060   static_assert(Pigweed::kDataMaxSize == 8);
2061 
2062   Pigweed::Message size_message;
2063   EXPECT_EQ(size_message.error_message.max_size(),
2064             Pigweed::kErrorMessageMaxSize);
2065   EXPECT_EQ(size_message.data.max_size(), Pigweed::kDataMaxSize);
2066 
2067   // Verify constants generated from max_count options in repeated.pwpb_options
2068   static_assert(RepeatedTest::kUint32sMaxSize == 8);
2069   static_assert(RepeatedTest::kFixed32sMaxSize == 8);
2070   static_assert(RepeatedTest::kDoublesMaxSize == 2);
2071   static_assert(RepeatedTest::kUint64sMaxSize == 4);
2072   static_assert(RepeatedTest::kEnumsMaxSize == 4);
2073 
2074   RepeatedTest::Message count_message;
2075   EXPECT_EQ(count_message.uint32s.max_size(), RepeatedTest::kUint32sMaxSize);
2076   EXPECT_EQ(count_message.fixed32s.max_size(), RepeatedTest::kFixed32sMaxSize);
2077   EXPECT_EQ(count_message.doubles.max_size(), RepeatedTest::kDoublesMaxSize);
2078   EXPECT_EQ(count_message.uint64s.max_size(), RepeatedTest::kUint64sMaxSize);
2079   EXPECT_EQ(count_message.enums.max_size(), RepeatedTest::kEnumsMaxSize);
2080 }
2081 
TEST(CodegenMessage,OneOf_Encode)2082 TEST(CodegenMessage, OneOf_Encode) {
2083   OneOfTest::Message message;
2084 
2085   int invocations = 0;
2086   message.type.SetEncoder([&invocations](OneOfTest::StreamEncoder& encoder) {
2087     invocations++;
2088     return encoder.WriteAnInt(32);
2089   });
2090 
2091   // clang-format off
2092   constexpr uint8_t expected_proto[] = {
2093     // type.an_int
2094     0x08, 0x20,
2095   };
2096   // clang-format on
2097 
2098   std::array<std::byte, 8> buffer;
2099   OneOfTest::MemoryEncoder oneof_test(buffer);
2100 
2101   EXPECT_EQ(oneof_test.Write(message), OkStatus());
2102   EXPECT_EQ(invocations, 1);
2103 
2104   EXPECT_EQ(oneof_test.size(), sizeof(expected_proto));
2105   EXPECT_EQ(
2106       std::memcmp(oneof_test.data(), expected_proto, sizeof(expected_proto)),
2107       0);
2108 }
2109 
TEST(CodegenMessage,OneOf_Encode_MultipleTimes)2110 TEST(CodegenMessage, OneOf_Encode_MultipleTimes) {
2111   OneOfTest::Message message;
2112 
2113   int invocations = 0;
2114   message.type.SetEncoder([&invocations](OneOfTest::StreamEncoder& encoder) {
2115     invocations++;
2116     return encoder.WriteAString("oneof");
2117   });
2118 
2119   // clang-format off
2120   constexpr uint8_t expected_proto[] = {
2121     // type.a_string
2122     0x12, 0x05, 'o', 'n', 'e', 'o', 'f'
2123   };
2124   // clang-format on
2125 
2126   // Write the same message struct to two different buffers. Even though its
2127   // internal state is modified during the write, it should be logically const
2128   // with both writes successfully producing the same output.
2129 
2130   std::array<std::byte, 8> buffer_1;
2131   std::array<std::byte, 8> buffer_2;
2132   OneOfTest::MemoryEncoder oneof_test_1(buffer_1);
2133   OneOfTest::MemoryEncoder oneof_test_2(buffer_2);
2134 
2135   EXPECT_EQ(oneof_test_1.Write(message), OkStatus());
2136   EXPECT_EQ(invocations, 1);
2137   EXPECT_EQ(oneof_test_1.size(), sizeof(expected_proto));
2138   EXPECT_EQ(
2139       std::memcmp(oneof_test_1.data(), expected_proto, sizeof(expected_proto)),
2140       0);
2141 
2142   EXPECT_EQ(oneof_test_2.Write(message), OkStatus());
2143   EXPECT_EQ(invocations, 2);
2144   EXPECT_EQ(oneof_test_2.size(), sizeof(expected_proto));
2145   EXPECT_EQ(
2146       std::memcmp(oneof_test_2.data(), expected_proto, sizeof(expected_proto)),
2147       0);
2148 }
2149 
TEST(CodegenMessage,OneOf_Encode_UnsetEncoderIsIgnored)2150 TEST(CodegenMessage, OneOf_Encode_UnsetEncoderIsIgnored) {
2151   OneOfTest::Message message;
2152   std::array<std::byte, 8> buffer;
2153   OneOfTest::MemoryEncoder oneof_test(buffer);
2154   EXPECT_EQ(oneof_test.Write(message), OkStatus());
2155 }
2156 
TEST(CodegenMessage,OneOf_Decode)2157 TEST(CodegenMessage, OneOf_Decode) {
2158   // clang-format off
2159   constexpr uint8_t proto_data[] = {
2160     // type.a_message
2161     0x1a, 0x02, 0x08, 0x01,
2162   };
2163   // clang-format on
2164 
2165   stream::MemoryReader reader(as_bytes(span(proto_data)));
2166   OneOfTest::StreamDecoder stream_decoder(reader);
2167 
2168   struct {
2169     OneOfTest::Fields field;
2170     OneOfTest::AMessage::Message submessage;
2171     int invocations = 0;
2172   } result;
2173 
2174   OneOfTest::Message message;
2175   message.type.SetDecoder(
2176       [&result](OneOfTest::Fields field, OneOfTest::StreamDecoder& decoder) {
2177         result.field = field;
2178         result.invocations++;
2179         if (field == OneOfTest::Fields::kAMessage) {
2180           return decoder.GetAMessageDecoder().Read(result.submessage);
2181         }
2182         return Status::InvalidArgument();
2183       });
2184 
2185   EXPECT_EQ(stream_decoder.Read(message), OkStatus());
2186   EXPECT_EQ(result.field, OneOfTest::Fields::kAMessage);
2187   EXPECT_EQ(result.invocations, 1);
2188   EXPECT_EQ(result.submessage.a_bool, true);
2189 }
2190 
TEST(CodegenMessage,OneOf_Decode_MultipleOneOfFieldsFails)2191 TEST(CodegenMessage, OneOf_Decode_MultipleOneOfFieldsFails) {
2192   // clang-format off
2193   constexpr uint8_t proto_data[] = {
2194     // type.an_int
2195     0x08, 0x20,
2196     // type.a_message
2197     0x1a, 0x02, 0x08, 0x01,
2198   };
2199   // clang-format on
2200 
2201   stream::MemoryReader reader(as_bytes(span(proto_data)));
2202   OneOfTest::StreamDecoder stream_decoder(reader);
2203 
2204   OneOfTest::Message message;
2205   message.type.SetDecoder(
2206       [](OneOfTest::Fields, OneOfTest::StreamDecoder&) { return OkStatus(); });
2207 
2208   EXPECT_EQ(stream_decoder.Read(message), Status::DataLoss());
2209 }
2210 
TEST(CodegenMessage,OneOf_Decode_UnsetDecoderIsIgnored)2211 TEST(CodegenMessage, OneOf_Decode_UnsetDecoderIsIgnored) {
2212   // clang-format off
2213   constexpr uint8_t proto_data[] = {
2214     // type.an_int
2215     0x08, 0x20,
2216   };
2217   // clang-format on
2218 
2219   stream::MemoryReader reader(as_bytes(span(proto_data)));
2220   OneOfTest::StreamDecoder stream_decoder(reader);
2221   OneOfTest::Message message;
2222   EXPECT_EQ(stream_decoder.Read(message), OkStatus());
2223 }
2224 
2225 }  // namespace
2226 }  // namespace pw::protobuf
2227