1 // Copyright 2020 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 #include <span>
15
16 #include "gtest/gtest.h"
17 #include "pw_protobuf/encoder.h"
18 #include "pw_stream/memory_stream.h"
19
20 // These header files contain the code generated by the pw_protobuf plugin.
21 // They are re-generated every time the tests are built and are used by the
22 // tests to ensure that the interface remains consistent.
23 //
24 // The purpose of the tests in this file is primarily to verify that the
25 // generated C++ interface is valid rather than the correctness of the
26 // low-level encoder.
27 #include "pw_protobuf_test_protos/full_test.pwpb.h"
28 #include "pw_protobuf_test_protos/importer.pwpb.h"
29 #include "pw_protobuf_test_protos/non_pw_package.pwpb.h"
30 #include "pw_protobuf_test_protos/proto2.pwpb.h"
31 #include "pw_protobuf_test_protos/repeated.pwpb.h"
32
33 namespace pw::protobuf {
34 namespace {
35
36 using namespace pw::protobuf::test;
37
TEST(Codegen,Codegen)38 TEST(Codegen, Codegen) {
39 std::byte encode_buffer[512];
40 std::byte temp_buffer[512];
41 stream::MemoryWriter writer(encode_buffer);
42
43 Pigweed::StreamEncoder pigweed(writer, temp_buffer);
44 pigweed.WriteMagicNumber(73)
45 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
46 pigweed.WriteZiggy(-111)
47 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
48 pigweed.WriteErrorMessage("not a typewriter")
49 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
50 pigweed.WriteBin(Pigweed::Protobuf::Binary::ZERO)
51 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
52
53 {
54 Pigweed::Pigweed::StreamEncoder pigweed_pigweed =
55 pigweed.GetPigweedEncoder();
56 pigweed_pigweed.WriteStatus(Bool::FILE_NOT_FOUND)
57 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
58
59 ASSERT_EQ(pigweed_pigweed.status(), OkStatus());
60 }
61
62 {
63 Proto::StreamEncoder proto = pigweed.GetProtoEncoder();
64 proto.WriteBin(Proto::Binary::OFF)
65 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
66 proto.WritePigweedPigweedBin(Pigweed::Pigweed::Binary::ZERO)
67 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
68 proto.WritePigweedProtobufBin(Pigweed::Protobuf::Binary::ZERO)
69 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
70
71 {
72 Pigweed::Protobuf::Compiler::StreamEncoder meta = proto.GetMetaEncoder();
73 meta.WriteFileName("/etc/passwd")
74 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
75 meta.WriteStatus(Pigweed::Protobuf::Compiler::Status::FUBAR)
76 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
77 }
78
79 {
80 Pigweed::StreamEncoder nested_pigweed = proto.GetPigweedEncoder();
81 nested_pigweed.WriteErrorMessage("here we go again")
82 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
83 nested_pigweed.WriteMagicNumber(616)
84 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
85
86 {
87 DeviceInfo::StreamEncoder device_info =
88 nested_pigweed.GetDeviceInfoEncoder();
89
90 {
91 KeyValuePair::StreamEncoder attributes =
92 device_info.GetAttributesEncoder();
93 attributes.WriteKey("version")
94 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
95 attributes.WriteValue("5.3.1")
96 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
97 }
98
99 {
100 KeyValuePair::StreamEncoder attributes =
101 device_info.GetAttributesEncoder();
102 attributes.WriteKey("chip")
103 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
104 attributes.WriteValue("left-soc")
105 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
106 }
107
108 device_info.WriteStatus(DeviceInfo::DeviceStatus::PANIC)
109 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
110 }
111 }
112 }
113
114 for (int i = 0; i < 5; ++i) {
115 Proto::ID::StreamEncoder id = pigweed.GetIdEncoder();
116 id.WriteId(5 * i * i + 3 * i + 49)
117 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
118 }
119
120 // clang-format off
121 constexpr uint8_t expected_proto[] = {
122 // pigweed.magic_number
123 0x08, 0x49,
124 // pigweed.ziggy
125 0x10, 0xdd, 0x01,
126 // pigweed.error_message
127 0x2a, 0x10, 'n', 'o', 't', ' ', 'a', ' ',
128 't', 'y', 'p', 'e', 'w', 'r', 'i', 't', 'e', 'r',
129 // pigweed.bin
130 0x40, 0x01,
131 // pigweed.pigweed
132 0x3a, 0x02,
133 // pigweed.pigweed.status
134 0x08, 0x02,
135 // pigweed.proto
136 0x4a, 0x56,
137 // pigweed.proto.bin
138 0x10, 0x00,
139 // pigweed.proto.pigweed_pigweed_bin
140 0x18, 0x00,
141 // pigweed.proto.pigweed_protobuf_bin
142 0x20, 0x01,
143 // pigweed.proto.meta
144 0x2a, 0x0f,
145 // pigweed.proto.meta.file_name
146 0x0a, 0x0b, '/', 'e', 't', 'c', '/', 'p', 'a', 's', 's', 'w', 'd',
147 // pigweed.proto.meta.status
148 0x10, 0x02,
149 // pigweed.proto.nested_pigweed
150 0x0a, 0x3d,
151 // pigweed.proto.nested_pigweed.error_message
152 0x2a, 0x10, 'h', 'e', 'r', 'e', ' ', 'w', 'e', ' ',
153 'g', 'o', ' ', 'a', 'g', 'a', 'i', 'n',
154 // pigweed.proto.nested_pigweed.magic_number
155 0x08, 0xe8, 0x04,
156 // pigweed.proto.nested_pigweed.device_info
157 0x32, 0x26,
158 // pigweed.proto.nested_pigweed.device_info.attributes[0]
159 0x22, 0x10,
160 // pigweed.proto.nested_pigweed.device_info.attributes[0].key
161 0x0a, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n',
162 // pigweed.proto.nested_pigweed.device_info.attributes[0].value
163 0x12, 0x05, '5', '.', '3', '.', '1',
164 // pigweed.proto.nested_pigweed.device_info.attributes[1]
165 0x22, 0x10,
166 // pigweed.proto.nested_pigweed.device_info.attributes[1].key
167 0x0a, 0x04, 'c', 'h', 'i', 'p',
168 // pigweed.proto.nested_pigweed.device_info.attributes[1].value
169 0x12, 0x08, 'l', 'e', 'f', 't', '-', 's', 'o', 'c',
170 // pigweed.proto.nested_pigweed.device_info.status
171 0x18, 0x03,
172 // pigweed.id[0]
173 0x52, 0x02,
174 // pigweed.id[0].id
175 0x08, 0x31,
176 // pigweed.id[1]
177 0x52, 0x02,
178 // pigweed.id[1].id
179 0x08, 0x39,
180 // pigweed.id[2]
181 0x52, 0x02,
182 // pigweed.id[2].id
183 0x08, 0x4b,
184 // pigweed.id[3]
185 0x52, 0x02,
186 // pigweed.id[3].id
187 0x08, 0x67,
188 // pigweed.id[4]
189 0x52, 0x03,
190 // pigweed.id[4].id
191 0x08, 0x8d, 0x01
192 };
193 // clang-format on
194
195 ConstByteSpan result = writer.WrittenData();
196 ASSERT_EQ(pigweed.status(), OkStatus());
197 EXPECT_EQ(result.size(), sizeof(expected_proto));
198 EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
199 0);
200 }
201
TEST(Codegen,RecursiveSubmessage)202 TEST(Codegen, RecursiveSubmessage) {
203 std::byte encode_buffer[512];
204
205 Crate::MemoryEncoder biggest_crate(encode_buffer);
206 biggest_crate.WriteName("Huge crate")
207 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
208
209 {
210 Crate::StreamEncoder medium_crate = biggest_crate.GetSmallerCratesEncoder();
211 medium_crate.WriteName("Medium crate")
212 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
213 {
214 Crate::StreamEncoder small_crate = medium_crate.GetSmallerCratesEncoder();
215 small_crate.WriteName("Small crate")
216 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
217 }
218 {
219 Crate::StreamEncoder tiny_crate = medium_crate.GetSmallerCratesEncoder();
220 tiny_crate.WriteName("Tiny crate")
221 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
222 }
223 }
224
225 // clang-format off
226 constexpr uint8_t expected_proto[] = {
227 // crate.name
228 0x0a, 0x0a, 'H', 'u', 'g', 'e', ' ', 'c', 'r', 'a', 't', 'e',
229 // crate.smaller_crate[0]
230 0x12, 0x2b,
231 // crate.smaller_crate[0].name
232 0x0a, 0x0c, 'M', 'e', 'd', 'i', 'u', 'm', ' ', 'c', 'r', 'a', 't', 'e',
233 // crate.smaller_crate[0].smaller_crate[0]
234 0x12, 0x0d,
235 // crate.smaller_crate[0].smaller_crate[0].name
236 0x0a, 0x0b, 'S', 'm', 'a', 'l', 'l', ' ', 'c', 'r', 'a', 't', 'e',
237 // crate.smaller_crate[0].smaller_crate[1]
238 0x12, 0x0c,
239 // crate.smaller_crate[0].smaller_crate[1].name
240 0x0a, 0x0a, 'T', 'i', 'n', 'y', ' ', 'c', 'r', 'a', 't', 'e',
241 };
242 // clang-format on
243
244 ConstByteSpan result(biggest_crate);
245 ASSERT_EQ(biggest_crate.status(), OkStatus());
246 EXPECT_EQ(result.size(), sizeof(expected_proto));
247 EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
248 0);
249 }
250
TEST(CodegenRepeated,NonPackedScalar)251 TEST(CodegenRepeated, NonPackedScalar) {
252 std::byte encode_buffer[32];
253
254 stream::MemoryWriter writer(encode_buffer);
255 RepeatedTest::StreamEncoder repeated_test(writer, ByteSpan());
256 for (int i = 0; i < 4; ++i) {
257 repeated_test.WriteUint32s(i * 16)
258 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
259 }
260
261 for (int i = 0; i < 4; ++i) {
262 repeated_test.WriteFixed32s(i * 16)
263 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
264 }
265
266 // clang-format off
267 constexpr uint8_t expected_proto[] = {
268 // uint32s[], v={0, 16, 32, 48}
269 0x08, 0x00,
270 0x08, 0x10,
271 0x08, 0x20,
272 0x08, 0x30,
273 // fixed32s[]. v={0, 16, 32, 48}
274 0x35, 0x00, 0x00, 0x00, 0x00,
275 0x35, 0x10, 0x00, 0x00, 0x00,
276 0x35, 0x20, 0x00, 0x00, 0x00,
277 0x35, 0x30, 0x00, 0x00, 0x00,
278 };
279 // clang-format on
280
281 ConstByteSpan result = writer.WrittenData();
282 ASSERT_EQ(repeated_test.status(), OkStatus());
283 EXPECT_EQ(result.size(), sizeof(expected_proto));
284 EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
285 0);
286 }
287
TEST(CodegenRepeated,PackedScalar)288 TEST(CodegenRepeated, PackedScalar) {
289 std::byte encode_buffer[32];
290
291 stream::MemoryWriter writer(encode_buffer);
292 RepeatedTest::StreamEncoder repeated_test(writer, ByteSpan());
293 constexpr uint32_t values[] = {0, 16, 32, 48};
294 repeated_test.WriteUint32s(values)
295 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
296 repeated_test.WriteFixed32s(values)
297 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
298
299 // clang-format off
300 constexpr uint8_t expected_proto[] = {
301 // uint32s[], v={0, 16, 32, 48}
302 0x0a, 0x04,
303 0x00,
304 0x10,
305 0x20,
306 0x30,
307 // fixed32s[]. v={0, 16, 32, 48}
308 0x32, 0x10,
309 0x00, 0x00, 0x00, 0x00,
310 0x10, 0x00, 0x00, 0x00,
311 0x20, 0x00, 0x00, 0x00,
312 0x30, 0x00, 0x00, 0x00,
313 };
314 // clang-format on
315
316 ConstByteSpan result = writer.WrittenData();
317 ASSERT_EQ(repeated_test.status(), OkStatus());
318 EXPECT_EQ(result.size(), sizeof(expected_proto));
319 EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
320 0);
321 }
322
TEST(CodegenRepeated,PackedBool)323 TEST(CodegenRepeated, PackedBool) {
324 std::byte encode_buffer[32];
325
326 stream::MemoryWriter writer(encode_buffer);
327 RepeatedTest::StreamEncoder repeated_test(writer, ByteSpan());
328 constexpr bool values[] = {true, false, true, true, false};
329 repeated_test.WriteBools(std::span(values))
330 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
331
332 // clang-format off
333 constexpr uint8_t expected_proto[] = {
334 // bools[], v={true, false, true, true, false}
335 0x3a, 0x05, 0x01, 0x00, 0x01, 0x01, 0x00,
336 };
337 // clang-format on
338
339 ConstByteSpan result = writer.WrittenData();
340 ASSERT_EQ(repeated_test.status(), OkStatus());
341 EXPECT_EQ(result.size(), sizeof(expected_proto));
342 EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
343 0);
344 }
345
TEST(CodegenRepeated,PackedScalarVector)346 TEST(CodegenRepeated, PackedScalarVector) {
347 std::byte encode_buffer[32];
348
349 stream::MemoryWriter writer(encode_buffer);
350 RepeatedTest::StreamEncoder repeated_test(writer, ByteSpan());
351 const pw::Vector<uint32_t, 4> values = {0, 16, 32, 48};
352 repeated_test.WriteUint32s(values)
353 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
354 repeated_test.WriteFixed32s(values)
355 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
356
357 // clang-format off
358 constexpr uint8_t expected_proto[] = {
359 // uint32s[], v={0, 16, 32, 48}
360 0x0a, 0x04,
361 0x00,
362 0x10,
363 0x20,
364 0x30,
365 // fixed32s[]. v={0, 16, 32, 48}
366 0x32, 0x10,
367 0x00, 0x00, 0x00, 0x00,
368 0x10, 0x00, 0x00, 0x00,
369 0x20, 0x00, 0x00, 0x00,
370 0x30, 0x00, 0x00, 0x00,
371 };
372 // clang-format on
373
374 ConstByteSpan result = writer.WrittenData();
375 ASSERT_EQ(repeated_test.status(), OkStatus());
376 EXPECT_EQ(result.size(), sizeof(expected_proto));
377 EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
378 0);
379 }
380
TEST(CodegenRepeated,NonScalar)381 TEST(CodegenRepeated, NonScalar) {
382 std::byte encode_buffer[32];
383
384 stream::MemoryWriter writer(encode_buffer);
385 RepeatedTest::StreamEncoder repeated_test(writer, ByteSpan());
386 constexpr const char* strings[] = {"the", "quick", "brown", "fox"};
387 for (const char* s : strings) {
388 repeated_test.WriteStrings(s)
389 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
390 }
391
392 constexpr uint8_t expected_proto[] = {
393 0x1a, 0x03, 't', 'h', 'e', 0x1a, 0x5, 'q', 'u', 'i', 'c', 'k',
394 0x1a, 0x5, 'b', 'r', 'o', 'w', 'n', 0x1a, 0x3, 'f', 'o', 'x'};
395 ConstByteSpan result = writer.WrittenData();
396 ASSERT_EQ(repeated_test.status(), OkStatus());
397 EXPECT_EQ(result.size(), sizeof(expected_proto));
398 EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
399 0);
400 }
401
TEST(CodegenRepeated,Message)402 TEST(CodegenRepeated, Message) {
403 std::byte encode_buffer[64];
404
405 RepeatedTest::MemoryEncoder repeated_test(encode_buffer);
406 for (int i = 0; i < 3; ++i) {
407 auto structs = repeated_test.GetStructsEncoder();
408 structs.WriteOne(i * 1)
409 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
410 structs.WriteTwo(i * 2)
411 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
412 }
413
414 // clang-format off
415 constexpr uint8_t expected_proto[] = {
416 0x2a, 0x04, 0x08, 0x00, 0x10, 0x00, 0x2a, 0x04, 0x08,
417 0x01, 0x10, 0x02, 0x2a, 0x04, 0x08, 0x02, 0x10, 0x04};
418 // clang-format on
419
420 ConstByteSpan result(repeated_test);
421 ASSERT_EQ(repeated_test.status(), OkStatus());
422 EXPECT_EQ(result.size(), sizeof(expected_proto));
423 EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
424 0);
425 }
426
TEST(Codegen,Proto2)427 TEST(Codegen, Proto2) {
428 std::byte encode_buffer[64];
429
430 Foo::MemoryEncoder foo(encode_buffer);
431 foo.WriteInteger(3).IgnoreError(); // TODO(pwbug/387): Handle Status properly
432
433 {
434 constexpr std::byte data[] = {
435 std::byte(0xde), std::byte(0xad), std::byte(0xbe), std::byte(0xef)};
436 Bar::StreamEncoder bar = foo.GetBarEncoder();
437 bar.WriteData(data)
438 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
439 }
440
441 constexpr uint8_t expected_proto[] = {
442 0x08, 0x03, 0x1a, 0x06, 0x0a, 0x04, 0xde, 0xad, 0xbe, 0xef};
443
444 ConstByteSpan result(foo);
445 ASSERT_EQ(foo.status(), OkStatus());
446 EXPECT_EQ(result.size(), sizeof(expected_proto));
447 EXPECT_EQ(std::memcmp(result.data(), expected_proto, sizeof(expected_proto)),
448 0);
449 }
450
TEST(Codegen,Import)451 TEST(Codegen, Import) {
452 std::byte encode_buffer[64];
453
454 Period::MemoryEncoder period(encode_buffer);
455 {
456 imported::Timestamp::StreamEncoder start = period.GetStartEncoder();
457 start.WriteSeconds(1589501793)
458 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
459 start.WriteNanoseconds(511613110)
460 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
461 }
462
463 {
464 imported::Timestamp::StreamEncoder end = period.GetEndEncoder();
465 end.WriteSeconds(1589501841)
466 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
467 end.WriteNanoseconds(490367432)
468 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
469 }
470
471 EXPECT_EQ(period.status(), OkStatus());
472 }
473
TEST(Codegen,NonPigweedPackage)474 TEST(Codegen, NonPigweedPackage) {
475 using namespace non::pigweed::package::name;
476 std::byte encode_buffer[64];
477 std::array<const int64_t, 2> repeated = {0, 1};
478 stream::MemoryWriter writer(encode_buffer);
479 Packed::StreamEncoder packed(writer, ByteSpan());
480 packed.WriteRep(std::span<const int64_t>(repeated))
481 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
482 packed.WritePacked("packed")
483 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
484
485 EXPECT_EQ(packed.status(), OkStatus());
486 }
487
488 } // namespace
489 } // namespace pw::protobuf
490