1 // Copyright 2019 The Pigweed Authors
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
7 // https://www.apache.org/licenses/LICENSE-2.0
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
15 #include "pw_protobuf/encoder.h"
17 #include "gtest/gtest.h"
19 namespace pw::protobuf {
22 // The tests in this file use the following proto message schemas.
24 // message TestProto {
25 // uint32 magic_number = 1;
27 // fixed64 cycles = 3;
29 // string error_message = 5;
30 // NestedProto nested = 6;
33 // message NestedProto {
36 // repeated DoubleNestedProto pair = 3;
39 // message DoubleNestedProto {
45 constexpr uint32_t kTestProtoMagicNumberField = 1;
46 constexpr uint32_t kTestProtoZiggyField = 2;
47 constexpr uint32_t kTestProtoCyclesField = 3;
48 constexpr uint32_t kTestProtoRatioField = 4;
49 constexpr uint32_t kTestProtoErrorMessageField = 5;
50 constexpr uint32_t kTestProtoNestedField = 6;
52 constexpr uint32_t kNestedProtoHelloField = 1;
53 constexpr uint32_t kNestedProtoIdField = 2;
54 constexpr uint32_t kNestedProtoPairField = 3;
56 constexpr uint32_t kDoubleNestedProtoKeyField = 1;
57 constexpr uint32_t kDoubleNestedProtoValueField = 2;
59 TEST(Encoder, EncodePrimitives) {
61 // tp.magic_number = 42;
63 // tp.cycles = 0xdeadbeef8badf00d;
64 // tp.ratio = 1.618034;
65 // tp.error_message = "broken 💩";
67 // Hand-encoded version of the above.
69 constexpr uint8_t encoded_proto[] = {
70 // magic_number [varint k=1]
74 // cycles [fixed64 k=3]
75 0x19, 0x0d, 0xf0, 0xad, 0x8b, 0xef, 0xbe, 0xad, 0xde,
76 // ratio [fixed32 k=4]
77 0x25, 0xbd, 0x1b, 0xcf, 0x3f,
78 // error_message [delimited k=5],
79 0x2a, 0x0b, 'b', 'r', 'o', 'k', 'e', 'n', ' ',
81 0xf0, 0x9f, 0x92, 0xa9,
85 std::byte encode_buffer[32];
86 NestedEncoder encoder(encode_buffer);
88 EXPECT_EQ(encoder.WriteUint32(kTestProtoMagicNumberField, 42), OkStatus());
89 EXPECT_EQ(encoder.WriteSint32(kTestProtoZiggyField, -13), OkStatus());
90 EXPECT_EQ(encoder.WriteFixed64(kTestProtoCyclesField, 0xdeadbeef8badf00d),
92 EXPECT_EQ(encoder.WriteFloat(kTestProtoRatioField, 1.618034), OkStatus());
93 EXPECT_EQ(encoder.WriteString(kTestProtoErrorMessageField, "broken 💩"),
96 Result result = encoder.Encode();
97 ASSERT_EQ(result.status(), OkStatus());
98 EXPECT_EQ(result.value().size(), sizeof(encoded_proto));
100 std::memcmp(result.value().data(), encoded_proto, sizeof(encoded_proto)),
104 TEST(Encoder, EncodeInsufficientSpace) {
105 std::byte encode_buffer[12];
106 NestedEncoder encoder(encode_buffer);
109 EXPECT_EQ(encoder.WriteUint32(kTestProtoMagicNumberField, 42), OkStatus());
111 EXPECT_EQ(encoder.WriteSint32(kTestProtoZiggyField, -13), OkStatus());
112 // 9 bytes; not enough space! The encoder will start writing the field but
113 // should rollback when it realizes it doesn't have enough space.
114 EXPECT_EQ(encoder.WriteFixed64(kTestProtoCyclesField, 0xdeadbeef8badf00d),
115 Status::ResourceExhausted());
116 // Any further write operations should fail.
117 EXPECT_EQ(encoder.WriteFloat(kTestProtoRatioField, 1.618034),
118 Status::ResourceExhausted());
120 ASSERT_EQ(encoder.Encode().status(), Status::ResourceExhausted());
123 TEST(Encoder, EncodeInvalidArguments) {
124 std::byte encode_buffer[12];
125 NestedEncoder encoder(encode_buffer);
127 EXPECT_EQ(encoder.WriteUint32(kTestProtoMagicNumberField, 42), OkStatus());
128 // Invalid proto field numbers.
129 EXPECT_EQ(encoder.WriteUint32(0, 1337), Status::InvalidArgument());
132 EXPECT_EQ(encoder.WriteString(1u << 31, "ha"), Status::InvalidArgument());
135 EXPECT_EQ(encoder.WriteBool(19091, false), Status::InvalidArgument());
136 ASSERT_EQ(encoder.Encode().status(), Status::InvalidArgument());
139 TEST(Encoder, Nested) {
140 std::byte encode_buffer[128];
141 NestedEncoder<5, 5> encoder(encode_buffer);
143 // TestProto test_proto;
144 // test_proto.magic_number = 42;
145 EXPECT_EQ(encoder.WriteUint32(kTestProtoMagicNumberField, 42), OkStatus());
148 // NestedProto& nested_proto = test_proto.nested;
149 EXPECT_EQ(encoder.Push(kTestProtoNestedField), OkStatus());
150 // nested_proto.hello = "world";
151 EXPECT_EQ(encoder.WriteString(kNestedProtoHelloField, "world"), OkStatus());
152 // nested_proto.id = 999;
153 EXPECT_EQ(encoder.WriteUint32(kNestedProtoIdField, 999), OkStatus());
156 // DoubleNestedProto& double_nested_proto = nested_proto.append_pair();
157 EXPECT_EQ(encoder.Push(kNestedProtoPairField), OkStatus());
158 // double_nested_proto.key = "version";
159 EXPECT_EQ(encoder.WriteString(kDoubleNestedProtoKeyField, "version"),
161 // double_nested_proto.value = "2.9.1";
162 EXPECT_EQ(encoder.WriteString(kDoubleNestedProtoValueField, "2.9.1"),
165 EXPECT_EQ(encoder.Pop(), OkStatus());
166 } // end DoubleNestedProto
169 // DoubleNestedProto& double_nested_proto = nested_proto.append_pair();
170 EXPECT_EQ(encoder.Push(kNestedProtoPairField), OkStatus());
171 // double_nested_proto.key = "device";
172 EXPECT_EQ(encoder.WriteString(kDoubleNestedProtoKeyField, "device"),
174 // double_nested_proto.value = "left-soc";
175 EXPECT_EQ(encoder.WriteString(kDoubleNestedProtoValueField, "left-soc"),
178 EXPECT_EQ(encoder.Pop(), OkStatus());
179 } // end DoubleNestedProto
181 EXPECT_EQ(encoder.Pop(), OkStatus());
184 // test_proto.ziggy = -13;
185 EXPECT_EQ(encoder.WriteSint32(kTestProtoZiggyField, -13), OkStatus());
188 constexpr uint8_t encoded_proto[] = {
191 // nested header (key, size)
194 0x0a, 0x05, 'w', 'o', 'r', 'l', 'd',
197 // nested.pair[0] header (key, size)
199 // nested.pair[0].key
200 0x0a, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n',
201 // nested.pair[0].value
202 0x12, 0x05, '2', '.', '9', '.', '1',
203 // nested.pair[1] header (key, size)
205 // nested.pair[1].key
206 0x0a, 0x06, 'd', 'e', 'v', 'i', 'c', 'e',
207 // nested.pair[1].value
208 0x12, 0x08, 'l', 'e', 'f', 't', '-', 's', 'o', 'c',
214 Result result = encoder.Encode();
215 ASSERT_EQ(result.status(), OkStatus());
216 EXPECT_EQ(result.value().size(), sizeof(encoded_proto));
218 std::memcmp(result.value().data(), encoded_proto, sizeof(encoded_proto)),
222 TEST(Encoder, NestedDepthLimit) {
223 std::byte encode_buffer[128];
224 NestedEncoder<2, 2> encoder(encode_buffer);
226 // One level of nesting.
227 EXPECT_EQ(encoder.Push(2), OkStatus());
228 // Two levels of nesting.
229 EXPECT_EQ(encoder.Push(1), OkStatus());
230 // Three levels of nesting: error!
231 EXPECT_EQ(encoder.Push(1), Status::ResourceExhausted());
233 // Further operations should fail.
234 EXPECT_EQ(encoder.Pop(), Status::ResourceExhausted());
235 EXPECT_EQ(encoder.Pop(), Status::ResourceExhausted());
236 EXPECT_EQ(encoder.Pop(), Status::ResourceExhausted());
239 TEST(Encoder, NestedBlobLimit) {
240 std::byte encode_buffer[128];
241 NestedEncoder<3, 3> encoder(encode_buffer);
244 EXPECT_EQ(encoder.Push(1), OkStatus());
245 EXPECT_EQ(encoder.Pop(), OkStatus());
247 // Write second blob.
248 EXPECT_EQ(encoder.Push(2), OkStatus());
250 // Write nested third blob.
251 EXPECT_EQ(encoder.Push(3), OkStatus());
252 EXPECT_EQ(encoder.Pop(), OkStatus());
255 EXPECT_EQ(encoder.Pop(), OkStatus());
257 // Write fourth blob: OK
258 EXPECT_EQ(encoder.Push(4), OkStatus());
259 EXPECT_EQ(encoder.Pop(), OkStatus());
262 TEST(Encoder, RepeatedField) {
263 std::byte encode_buffer[32];
264 NestedEncoder encoder(encode_buffer);
266 // repeated uint32 values = 1;
267 constexpr uint32_t values[] = {0, 50, 100, 150, 200};
268 for (int i = 0; i < 5; ++i) {
269 encoder.WriteUint32(1, values[i]);
272 constexpr uint8_t encoded_proto[] = {
273 0x08, 0x00, 0x08, 0x32, 0x08, 0x64, 0x08, 0x96, 0x01, 0x08, 0xc8, 0x01};
275 Result result = encoder.Encode();
276 ASSERT_EQ(result.status(), OkStatus());
277 EXPECT_EQ(result.value().size(), sizeof(encoded_proto));
279 std::memcmp(result.value().data(), encoded_proto, sizeof(encoded_proto)),
283 TEST(Encoder, PackedVarint) {
284 std::byte encode_buffer[32];
285 NestedEncoder encoder(encode_buffer);
287 // repeated uint32 values = 1;
288 constexpr uint32_t values[] = {0, 50, 100, 150, 200};
289 encoder.WritePackedUint32(1, values);
291 constexpr uint8_t encoded_proto[] = {
292 0x0a, 0x07, 0x00, 0x32, 0x64, 0x96, 0x01, 0xc8, 0x01};
293 // key size v[0] v[1] v[2] v[3] v[4]
295 Result result = encoder.Encode();
296 ASSERT_EQ(result.status(), OkStatus());
297 EXPECT_EQ(result.value().size(), sizeof(encoded_proto));
299 std::memcmp(result.value().data(), encoded_proto, sizeof(encoded_proto)),
303 TEST(Encoder, PackedVarintInsufficientSpace) {
304 std::byte encode_buffer[8];
305 NestedEncoder encoder(encode_buffer);
307 constexpr uint32_t values[] = {0, 50, 100, 150, 200};
308 encoder.WritePackedUint32(1, values);
310 EXPECT_EQ(encoder.Encode().status(), Status::ResourceExhausted());
313 TEST(Encoder, PackedFixed) {
314 std::byte encode_buffer[32];
315 NestedEncoder encoder(encode_buffer);
317 // repeated fixed32 values = 1;
318 constexpr uint32_t values[] = {0, 50, 100, 150, 200};
319 encoder.WritePackedFixed32(1, values);
321 // repeated fixed64 values64 = 2;
322 constexpr uint64_t values64[] = {0x0102030405060708};
323 encoder.WritePackedFixed64(2, values64);
325 constexpr uint8_t encoded_proto[] = {
326 0x0a, 0x14, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x64,
327 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00,
328 0x12, 0x08, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
330 Result result = encoder.Encode();
331 ASSERT_EQ(result.status(), OkStatus());
332 EXPECT_EQ(result.value().size(), sizeof(encoded_proto));
334 std::memcmp(result.value().data(), encoded_proto, sizeof(encoded_proto)),
338 TEST(Encoder, PackedZigzag) {
339 std::byte encode_buffer[32];
340 NestedEncoder encoder(encode_buffer);
342 // repeated sint32 values = 1;
343 constexpr int32_t values[] = {-100, -25, -1, 0, 1, 25, 100};
344 encoder.WritePackedSint32(1, values);
346 constexpr uint8_t encoded_proto[] = {
347 0x0a, 0x09, 0xc7, 0x01, 0x31, 0x01, 0x00, 0x02, 0x32, 0xc8, 0x01};
349 Result result = encoder.Encode();
350 ASSERT_EQ(result.status(), OkStatus());
351 EXPECT_EQ(result.value().size(), sizeof(encoded_proto));
353 std::memcmp(result.value().data(), encoded_proto, sizeof(encoded_proto)),
358 } // namespace pw::protobuf