1 // Copyright 2021 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_protobuf/encoder.h"
16
17 #include <span>
18
19 #include "gtest/gtest.h"
20 #include "pw_bytes/span.h"
21 #include "pw_stream/memory_stream.h"
22
23 namespace pw::protobuf {
24 namespace {
25
26 using stream::MemoryWriter;
27
28 // The tests in this file use the following proto message schemas.
29 //
30 // message TestProto {
31 // uint32 magic_number = 1;
32 // sint32 ziggy = 2;
33 // fixed64 cycles = 3;
34 // float ratio = 4;
35 // string error_message = 5;
36 // NestedProto nested = 6;
37 // }
38 //
39 // message NestedProto {
40 // string hello = 1;
41 // uint32 id = 2;
42 // repeated DoubleNestedProto pair = 3;
43 // }
44 //
45 // message DoubleNestedProto {
46 // string key = 1;
47 // string value = 2;
48 // }
49 //
50
51 constexpr uint32_t kTestProtoMagicNumberField = 1;
52 constexpr uint32_t kTestProtoZiggyField = 2;
53 constexpr uint32_t kTestProtoCyclesField = 3;
54 constexpr uint32_t kTestProtoRatioField = 4;
55 constexpr uint32_t kTestProtoErrorMessageField = 5;
56 constexpr uint32_t kTestProtoNestedField = 6;
57 constexpr uint32_t kTestProtoPayloadFromStreamField = 7;
58
59 constexpr uint32_t kNestedProtoHelloField = 1;
60 constexpr uint32_t kNestedProtoIdField = 2;
61 constexpr uint32_t kNestedProtoPairField = 3;
62
63 constexpr uint32_t kDoubleNestedProtoKeyField = 1;
64 constexpr uint32_t kDoubleNestedProtoValueField = 2;
65
TEST(StreamEncoder,EncodePrimitives)66 TEST(StreamEncoder, EncodePrimitives) {
67 // TestProto tp;
68 // tp.magic_number = 42;
69 // tp.ziggy = -13;
70 // tp.cycles = 0xdeadbeef8badf00d;
71 // tp.ratio = 1.618034;
72 // tp.error_message = "broken ";
73 // tp.payload_from_stream = "byreader"
74
75 // Hand-encoded version of the above.
76 // clang-format off
77 constexpr uint8_t encoded_proto[] = {
78 // magic_number [varint k=1]
79 0x08, 0x2a,
80 // ziggy [varint k=2]
81 0x10, 0x19,
82 // cycles [fixed64 k=3]
83 0x19, 0x0d, 0xf0, 0xad, 0x8b, 0xef, 0xbe, 0xad, 0xde,
84 // ratio [fixed32 k=4]
85 0x25, 0xbd, 0x1b, 0xcf, 0x3f,
86 // error_message [delimited k=5],
87 0x2a, 0x0b, 'b', 'r', 'o', 'k', 'e', 'n', ' ',
88 // poop!
89 0xf0, 0x9f, 0x92, 0xa9,
90 // payload_from_stream [delimited k=7]
91 0x3a, 0x08, 'b', 'y', 'r', 'e', 'a', 'd', 'e', 'r',
92 };
93 // clang-format on
94 std::byte encode_buffer[64];
95 std::byte dest_buffer[64];
96 // This writer isn't necessary, it's just the most testable way to exercise
97 // a stream interface. Use a MemoryEncoder when encoding a proto directly to
98 // an in-memory buffer.
99 MemoryWriter writer(dest_buffer);
100 StreamEncoder encoder(writer, encode_buffer);
101
102 EXPECT_EQ(encoder.WriteUint32(kTestProtoMagicNumberField, 42), OkStatus());
103 EXPECT_EQ(writer.bytes_written(), 2u);
104 EXPECT_EQ(encoder.WriteSint32(kTestProtoZiggyField, -13), OkStatus());
105 EXPECT_EQ(encoder.WriteFixed64(kTestProtoCyclesField, 0xdeadbeef8badf00d),
106 OkStatus());
107 EXPECT_EQ(encoder.WriteFloat(kTestProtoRatioField, 1.618034), OkStatus());
108 EXPECT_EQ(encoder.WriteString(kTestProtoErrorMessageField, "broken "),
109 OkStatus());
110
111 const std::string_view kReaderMessage = "byreader";
112 stream::MemoryReader msg_reader(std::as_bytes(std::span(kReaderMessage)));
113 std::byte stream_pipe_buffer[1];
114 EXPECT_EQ(encoder.WriteStringFromStream(kTestProtoPayloadFromStreamField,
115 msg_reader,
116 kReaderMessage.size(),
117 stream_pipe_buffer),
118 OkStatus());
119
120 ASSERT_EQ(encoder.status(), OkStatus());
121 ConstByteSpan result = writer.WrittenData();
122 EXPECT_EQ(result.size(), sizeof(encoded_proto));
123 EXPECT_EQ(std::memcmp(result.data(), encoded_proto, sizeof(encoded_proto)),
124 0);
125 }
126
TEST(StreamEncoder,EncodeInsufficientSpace)127 TEST(StreamEncoder, EncodeInsufficientSpace) {
128 std::byte encode_buffer[12];
129 MemoryEncoder encoder(encode_buffer);
130
131 // 2 bytes.
132 EXPECT_EQ(encoder.WriteUint32(kTestProtoMagicNumberField, 42), OkStatus());
133 // 2 bytes.
134 EXPECT_EQ(encoder.WriteSint32(kTestProtoZiggyField, -13), OkStatus());
135 // 9 bytes; not enough space! The encoder will start writing the field but
136 // should rollback when it realizes it doesn't have enough space.
137 EXPECT_EQ(encoder.WriteFixed64(kTestProtoCyclesField, 0xdeadbeef8badf00d),
138 Status::ResourceExhausted());
139 // Any further write operations should fail.
140 EXPECT_EQ(encoder.WriteFloat(kTestProtoRatioField, 1.618034),
141 Status::ResourceExhausted());
142
143 ASSERT_EQ(encoder.status(), Status::ResourceExhausted());
144 }
145
TEST(StreamEncoder,EncodeInvalidArguments)146 TEST(StreamEncoder, EncodeInvalidArguments) {
147 std::byte encode_buffer[12];
148 MemoryEncoder encoder(encode_buffer);
149
150 EXPECT_EQ(encoder.WriteUint32(kTestProtoMagicNumberField, 42), OkStatus());
151 // Invalid proto field numbers.
152 EXPECT_EQ(encoder.WriteUint32(0, 1337), Status::InvalidArgument());
153
154 // TODO(amontanez): Does it make sense to support this?
155 // encoder.Clear();
156
157 EXPECT_EQ(encoder.WriteString(1u << 31, "ha"), Status::InvalidArgument());
158
159 // TODO(amontanez): Does it make sense to support this?
160 // encoder.Clear();
161
162 EXPECT_EQ(encoder.WriteBool(19091, false), Status::InvalidArgument());
163 ASSERT_EQ(encoder.status(), Status::InvalidArgument());
164 }
165
TEST(StreamEncoder,Nested)166 TEST(StreamEncoder, Nested) {
167 // This is the largest complete submessage in this test.
168 constexpr size_t kLargestSubmessageSize = 0x30;
169 constexpr size_t kScratchBufferSize =
170 MaxScratchBufferSize(kLargestSubmessageSize, 2);
171 std::byte encode_buffer[kScratchBufferSize];
172 std::byte dest_buffer[128];
173 MemoryWriter writer(dest_buffer);
174 StreamEncoder encoder(writer, encode_buffer);
175
176 // TestProto test_proto;
177 // test_proto.magic_number = 42;
178 EXPECT_EQ(encoder.WriteUint32(kTestProtoMagicNumberField, 42), OkStatus());
179
180 {
181 // NestedProto& nested_proto = test_proto.nested;
182 StreamEncoder nested_proto =
183 encoder.GetNestedEncoder(kTestProtoNestedField);
184 // nested_proto.hello = "world";
185 EXPECT_EQ(nested_proto.WriteString(kNestedProtoHelloField, "world"),
186 OkStatus());
187
188 {
189 // DoubleNestedProto& double_nested_proto = nested_proto.append_pair();
190 StreamEncoder double_nested_proto =
191 nested_proto.GetNestedEncoder(kNestedProtoPairField);
192 // double_nested_proto.key = "version";
193 EXPECT_EQ(double_nested_proto.WriteString(kDoubleNestedProtoKeyField,
194 "version"),
195 OkStatus());
196 // double_nested_proto.value = "2.9.1";
197 EXPECT_EQ(double_nested_proto.WriteString(kDoubleNestedProtoValueField,
198 "2.9.1"),
199 OkStatus());
200 } // end DoubleNestedProto
201
202 // nested_proto.id = 999;
203 EXPECT_EQ(nested_proto.WriteUint32(kNestedProtoIdField, 999), OkStatus());
204
205 {
206 // DoubleNestedProto& double_nested_proto = nested_proto.append_pair();
207 StreamEncoder double_nested_proto =
208 nested_proto.GetNestedEncoder(kNestedProtoPairField);
209 // double_nested_proto.key = "device";
210 EXPECT_EQ(
211 double_nested_proto.WriteString(kDoubleNestedProtoKeyField, "device"),
212 OkStatus());
213 // double_nested_proto.value = "left-soc";
214 EXPECT_EQ(double_nested_proto.WriteString(kDoubleNestedProtoValueField,
215 "left-soc"),
216 OkStatus());
217 // Rely on destructor for finalization.
218 } // end DoubleNestedProto
219 } // end NestedProto
220
221 // test_proto.ziggy = -13;
222 EXPECT_EQ(encoder.WriteSint32(kTestProtoZiggyField, -13), OkStatus());
223
224 // clang-format off
225 constexpr uint8_t encoded_proto[] = {
226 // magic_number
227 0x08, 0x2a,
228 // nested header (key, size)
229 0x32, 0x30,
230 // nested.hello
231 0x0a, 0x05, 'w', 'o', 'r', 'l', 'd',
232 // nested.pair[0] header (key, size)
233 0x1a, 0x10,
234 // nested.pair[0].key
235 0x0a, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n',
236 // nested.pair[0].value
237 0x12, 0x05, '2', '.', '9', '.', '1',
238 // nested.id
239 0x10, 0xe7, 0x07,
240 // nested.pair[1] header (key, size)
241 0x1a, 0x12,
242 // nested.pair[1].key
243 0x0a, 0x06, 'd', 'e', 'v', 'i', 'c', 'e',
244 // nested.pair[1].value
245 0x12, 0x08, 'l', 'e', 'f', 't', '-', 's', 'o', 'c',
246 // ziggy
247 0x10, 0x19
248 };
249 // clang-format on
250
251 ASSERT_EQ(encoder.status(), OkStatus());
252 ConstByteSpan result = ConstByteSpan(writer.data(), writer.bytes_written());
253 EXPECT_EQ(result.size(), sizeof(encoded_proto));
254 EXPECT_EQ(std::memcmp(result.data(), encoded_proto, sizeof(encoded_proto)),
255 0);
256 }
257
TEST(StreamEncoder,RepeatedField)258 TEST(StreamEncoder, RepeatedField) {
259 std::byte encode_buffer[32];
260 MemoryEncoder encoder(encode_buffer);
261
262 // repeated uint32 values = 1;
263 constexpr uint32_t values[] = {0, 50, 100, 150, 200};
264 for (int i = 0; i < 5; ++i) {
265 encoder.WriteUint32(1, values[i])
266 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
267 }
268
269 constexpr uint8_t encoded_proto[] = {
270 0x08, 0x00, 0x08, 0x32, 0x08, 0x64, 0x08, 0x96, 0x01, 0x08, 0xc8, 0x01};
271
272 ASSERT_EQ(encoder.status(), OkStatus());
273 ConstByteSpan result(encoder);
274 EXPECT_EQ(result.size(), sizeof(encoded_proto));
275 EXPECT_EQ(std::memcmp(result.data(), encoded_proto, sizeof(encoded_proto)),
276 0);
277 }
278
TEST(StreamEncoder,PackedVarint)279 TEST(StreamEncoder, PackedVarint) {
280 std::byte encode_buffer[32];
281 MemoryEncoder encoder(encode_buffer);
282
283 // repeated uint32 values = 1;
284 constexpr uint32_t values[] = {0, 50, 100, 150, 200};
285 encoder.WritePackedUint32(1, values)
286 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
287
288 constexpr uint8_t encoded_proto[] = {
289 0x0a, 0x07, 0x00, 0x32, 0x64, 0x96, 0x01, 0xc8, 0x01};
290 // key size v[0] v[1] v[2] v[3] v[4]
291
292 ASSERT_EQ(encoder.status(), OkStatus());
293 ConstByteSpan result(encoder);
294 EXPECT_EQ(result.size(), sizeof(encoded_proto));
295 EXPECT_EQ(std::memcmp(result.data(), encoded_proto, sizeof(encoded_proto)),
296 0);
297 }
298
TEST(StreamEncoder,PackedVarintInsufficientSpace)299 TEST(StreamEncoder, PackedVarintInsufficientSpace) {
300 std::byte encode_buffer[8];
301 MemoryEncoder encoder(encode_buffer);
302
303 constexpr uint32_t values[] = {0, 50, 100, 150, 200};
304 encoder.WritePackedUint32(1, values)
305 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
306
307 EXPECT_EQ(encoder.status(), Status::ResourceExhausted());
308 }
309
TEST(StreamEncoder,PackedVarintVector)310 TEST(StreamEncoder, PackedVarintVector) {
311 std::byte encode_buffer[32];
312 MemoryEncoder encoder(encode_buffer);
313
314 // repeated uint32 values = 1;
315 const pw::Vector<uint32_t, 5> values = {0, 50, 100, 150, 200};
316 encoder.WriteRepeatedUint32(1, values)
317 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
318
319 constexpr uint8_t encoded_proto[] = {
320 0x0a, 0x07, 0x00, 0x32, 0x64, 0x96, 0x01, 0xc8, 0x01};
321 // key size v[0] v[1] v[2] v[3] v[4]
322
323 ASSERT_EQ(encoder.status(), OkStatus());
324 ConstByteSpan result(encoder);
325 EXPECT_EQ(result.size(), sizeof(encoded_proto));
326 EXPECT_EQ(std::memcmp(result.data(), encoded_proto, sizeof(encoded_proto)),
327 0);
328 }
329
TEST(StreamEncoder,PackedVarintVectorInsufficientSpace)330 TEST(StreamEncoder, PackedVarintVectorInsufficientSpace) {
331 std::byte encode_buffer[8];
332 MemoryEncoder encoder(encode_buffer);
333
334 const pw::Vector<uint32_t, 5> values = {0, 50, 100, 150, 200};
335 encoder.WriteRepeatedUint32(1, values)
336 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
337
338 EXPECT_EQ(encoder.status(), Status::ResourceExhausted());
339 }
340
TEST(StreamEncoder,PackedBool)341 TEST(StreamEncoder, PackedBool) {
342 std::byte encode_buffer[32];
343 MemoryEncoder encoder(encode_buffer);
344
345 // repeated bool values = 1;
346 constexpr bool values[] = {true, false, true, true, false};
347 encoder.WritePackedBool(1, values)
348 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
349
350 constexpr uint8_t encoded_proto[] = {
351 0x0a, 0x05, 0x01, 0x00, 0x01, 0x01, 0x00};
352 // key size v[0] v[1] v[2] v[3] v[4]
353
354 ASSERT_EQ(encoder.status(), OkStatus());
355 ConstByteSpan result(encoder);
356 EXPECT_EQ(result.size(), sizeof(encoded_proto));
357 EXPECT_EQ(std::memcmp(result.data(), encoded_proto, sizeof(encoded_proto)),
358 0);
359 }
360
TEST(StreamEncoder,PackedFixed)361 TEST(StreamEncoder, PackedFixed) {
362 std::byte encode_buffer[32];
363 MemoryEncoder encoder(encode_buffer);
364
365 // repeated fixed32 values = 1;
366 constexpr uint32_t values[] = {0, 50, 100, 150, 200};
367 encoder.WritePackedFixed32(1, values)
368 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
369
370 // repeated fixed64 values64 = 2;
371 constexpr uint64_t values64[] = {0x0102030405060708};
372 encoder.WritePackedFixed64(2, values64)
373 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
374
375 constexpr uint8_t encoded_proto[] = {
376 0x0a, 0x14, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x64,
377 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00,
378 0x12, 0x08, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
379
380 ASSERT_EQ(encoder.status(), OkStatus());
381 ConstByteSpan result(encoder);
382 EXPECT_EQ(result.size(), sizeof(encoded_proto));
383 EXPECT_EQ(std::memcmp(result.data(), encoded_proto, sizeof(encoded_proto)),
384 0);
385 }
386
TEST(StreamEncoder,PackedFixedVector)387 TEST(StreamEncoder, PackedFixedVector) {
388 std::byte encode_buffer[32];
389 MemoryEncoder encoder(encode_buffer);
390
391 // repeated fixed32 values = 1;
392 const pw::Vector<uint32_t, 5> values = {0, 50, 100, 150, 200};
393 encoder.WriteRepeatedFixed32(1, values)
394 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
395
396 // repeated fixed64 values64 = 2;
397 const pw::Vector<uint64_t, 1> values64 = {0x0102030405060708};
398 encoder.WriteRepeatedFixed64(2, values64)
399 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
400
401 constexpr uint8_t encoded_proto[] = {
402 0x0a, 0x14, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x64,
403 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00,
404 0x12, 0x08, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
405
406 ASSERT_EQ(encoder.status(), OkStatus());
407 ConstByteSpan result(encoder);
408 EXPECT_EQ(result.size(), sizeof(encoded_proto));
409 EXPECT_EQ(std::memcmp(result.data(), encoded_proto, sizeof(encoded_proto)),
410 0);
411 }
412
TEST(StreamEncoder,PackedZigzag)413 TEST(StreamEncoder, PackedZigzag) {
414 std::byte encode_buffer[32];
415 MemoryEncoder encoder(encode_buffer);
416
417 // repeated sint32 values = 1;
418 constexpr int32_t values[] = {-100, -25, -1, 0, 1, 25, 100};
419 encoder.WritePackedSint32(1, values)
420 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
421
422 constexpr uint8_t encoded_proto[] = {
423 0x0a, 0x09, 0xc7, 0x01, 0x31, 0x01, 0x00, 0x02, 0x32, 0xc8, 0x01};
424
425 ASSERT_EQ(encoder.status(), OkStatus());
426 ConstByteSpan result(encoder);
427 EXPECT_EQ(result.size(), sizeof(encoded_proto));
428 EXPECT_EQ(std::memcmp(result.data(), encoded_proto, sizeof(encoded_proto)),
429 0);
430 }
431
TEST(StreamEncoder,PackedZigzagVector)432 TEST(StreamEncoder, PackedZigzagVector) {
433 std::byte encode_buffer[32];
434 MemoryEncoder encoder(encode_buffer);
435
436 // repeated sint32 values = 1;
437 const pw::Vector<int32_t, 7> values = {-100, -25, -1, 0, 1, 25, 100};
438 encoder.WriteRepeatedSint32(1, values)
439 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
440
441 constexpr uint8_t encoded_proto[] = {
442 0x0a, 0x09, 0xc7, 0x01, 0x31, 0x01, 0x00, 0x02, 0x32, 0xc8, 0x01};
443
444 ASSERT_EQ(encoder.status(), OkStatus());
445 ConstByteSpan result(encoder);
446 EXPECT_EQ(result.size(), sizeof(encoded_proto));
447 EXPECT_EQ(std::memcmp(result.data(), encoded_proto, sizeof(encoded_proto)),
448 0);
449 }
450
TEST(StreamEncoder,ParentUnavailable)451 TEST(StreamEncoder, ParentUnavailable) {
452 std::byte encode_buffer[32];
453 MemoryEncoder parent(encode_buffer);
454 {
455 StreamEncoder child = parent.GetNestedEncoder(kTestProtoNestedField);
456 ASSERT_EQ(child.status(), OkStatus());
457 }
458 ASSERT_EQ(parent.status(), OkStatus());
459 }
460
TEST(StreamEncoder,NestedEncoderRequiresBuffer)461 TEST(StreamEncoder, NestedEncoderRequiresBuffer) {
462 MemoryEncoder parent((ByteSpan()));
463 {
464 StreamEncoder child = parent.GetNestedEncoder(kTestProtoNestedField);
465
466 ASSERT_EQ(child.status(), Status::ResourceExhausted());
467 }
468 ASSERT_EQ(parent.status(), Status::ResourceExhausted());
469 }
470
TEST(StreamEncoder,WriteTooBig)471 TEST(StreamEncoder, WriteTooBig) {
472 constexpr size_t kTempBufferSize = 32;
473 constexpr size_t kWriteSize = 2;
474 std::byte encode_buffer[32];
475 MemoryEncoder encoder(encode_buffer);
476 // Each write is 2 bytes. Ensure we can write 16 times.
477 for (size_t i = 0; i < kTempBufferSize; i += kWriteSize) {
478 ASSERT_EQ(encoder.WriteUint32(1, 12), OkStatus());
479 }
480 ASSERT_EQ(encoder.size(), kTempBufferSize);
481 ASSERT_EQ(encoder.WriteUint32(1, 12), Status::ResourceExhausted());
482 }
483
TEST(StreamEncoder,EmptyChildWrites)484 TEST(StreamEncoder, EmptyChildWrites) {
485 std::byte encode_buffer[32];
486 MemoryEncoder parent(encode_buffer);
487 { StreamEncoder child = parent.GetNestedEncoder(kTestProtoNestedField); }
488 ASSERT_EQ(parent.status(), OkStatus());
489 const size_t kExpectedSize =
490 varint::EncodedSize(
491 FieldKey(kTestProtoNestedField, WireType::kDelimited)) +
492 varint::EncodedSize(0);
493 ASSERT_EQ(parent.size(), kExpectedSize);
494 }
495
TEST(StreamEncoder,NestedStatusPropagates)496 TEST(StreamEncoder, NestedStatusPropagates) {
497 std::byte encode_buffer[32];
498 MemoryEncoder parent(encode_buffer);
499 {
500 StreamEncoder child = parent.GetNestedEncoder(kTestProtoNestedField);
501 ASSERT_EQ(child.WriteUint32(0, 0), Status::InvalidArgument());
502 }
503 ASSERT_EQ(parent.status(), Status::InvalidArgument());
504 }
505
506 } // namespace
507 } // namespace pw::protobuf
508