Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 1 | /* |
Lalit Maganti | c6bccda | 2018-06-25 11:05:24 +0100 | [diff] [blame] | 2 | * Copyright (C) 2018 The Android Open Source Project |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "perfetto/protozero/proto_decoder.h" |
| 18 | |
Primiano Tucci | 2c5488f | 2019-06-01 03:27:28 +0100 | [diff] [blame] | 19 | #include "perfetto/ext/base/utils.h" |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 20 | #include "perfetto/protozero/message.h" |
| 21 | #include "perfetto/protozero/proto_utils.h" |
Eric Seckler | 7631120 | 2019-01-07 12:20:25 +0000 | [diff] [blame] | 22 | #include "perfetto/protozero/scattered_heap_buffer.h" |
Primiano Tucci | 2cfc7fd | 2019-11-28 16:07:27 +0000 | [diff] [blame] | 23 | #include "perfetto/protozero/static_buffer.h" |
Primiano Tucci | 919ca1e | 2019-08-21 20:26:58 +0200 | [diff] [blame] | 24 | #include "test/gtest_and_gmock.h" |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 25 | |
Ryan | cbd1615 | 2019-09-04 16:54:45 +0100 | [diff] [blame] | 26 | #include "src/protozero/test/example_proto/test_messages.pb.h" |
| 27 | #include "src/protozero/test/example_proto/test_messages.pbzero.h" |
| 28 | |
Primiano Tucci | ba784e5 | 2019-11-13 07:04:52 -0800 | [diff] [blame] | 29 | // Generated by the protozero plugin. |
| 30 | namespace pbtest = protozero::test::protos::pbzero; |
| 31 | |
| 32 | // Generated by the official protobuf compiler. |
| 33 | namespace pbgold = protozero::test::protos; |
Ryan | cbd1615 | 2019-09-04 16:54:45 +0100 | [diff] [blame] | 34 | |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 35 | namespace protozero { |
| 36 | namespace { |
| 37 | |
| 38 | using ::testing::_; |
| 39 | using ::testing::InSequence; |
| 40 | using ::testing::Invoke; |
Isabelle Taylor | aa45fe0 | 2018-12-16 21:02:20 +0000 | [diff] [blame] | 41 | using namespace proto_utils; |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 42 | |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 43 | TEST(ProtoDecoderTest, ReadString) { |
Primiano Tucci | a2d75e9 | 2019-05-17 12:06:30 +0100 | [diff] [blame] | 44 | HeapBuffered<Message> message; |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 45 | |
| 46 | static constexpr char kTestString[] = "test"; |
Primiano Tucci | a2d75e9 | 2019-05-17 12:06:30 +0100 | [diff] [blame] | 47 | message->AppendString(1, kTestString); |
| 48 | std::vector<uint8_t> proto = message.SerializeAsArray(); |
| 49 | TypedProtoDecoder<32, false> decoder(proto.data(), proto.size()); |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 50 | |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 51 | const auto& field = decoder.Get(1); |
| 52 | ASSERT_EQ(field.type(), ProtoWireType::kLengthDelimited); |
| 53 | ASSERT_EQ(field.size(), sizeof(kTestString) - 1); |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 54 | for (size_t i = 0; i < sizeof(kTestString) - 1; i++) { |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 55 | ASSERT_EQ(field.data()[i], kTestString[i]); |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 56 | } |
| 57 | } |
| 58 | |
Primiano Tucci | 2cfc7fd | 2019-11-28 16:07:27 +0000 | [diff] [blame] | 59 | TEST(ProtoDecoderTest, SkipVeryLargeFields) { |
| 60 | const size_t kPayloadSize = 257 * 1024 * 1024; |
| 61 | const uint64_t data_size = 4096 + kPayloadSize; |
Hector Dearman | cf4ef4b | 2019-02-27 19:10:21 +0000 | [diff] [blame] | 62 | std::unique_ptr<uint8_t, perfetto::base::FreeDeleter> data( |
Primiano Tucci | 2cfc7fd | 2019-11-28 16:07:27 +0000 | [diff] [blame] | 63 | static_cast<uint8_t*>(malloc(data_size))); |
Hector Dearman | cf4ef4b | 2019-02-27 19:10:21 +0000 | [diff] [blame] | 64 | |
Primiano Tucci | 2cfc7fd | 2019-11-28 16:07:27 +0000 | [diff] [blame] | 65 | StaticBufferDelegate delegate(data.get(), data_size); |
| 66 | ScatteredStreamWriter writer(&delegate); |
| 67 | Message message; |
| 68 | message.Reset(&writer); |
Hector Dearman | cf4ef4b | 2019-02-27 19:10:21 +0000 | [diff] [blame] | 69 | |
Primiano Tucci | 2cfc7fd | 2019-11-28 16:07:27 +0000 | [diff] [blame] | 70 | // Append a valid field. |
| 71 | message.AppendVarInt(/*field_id=*/1, 11); |
| 72 | |
| 73 | // Append a very large field that will be skipped. |
| 74 | uint8_t raw[10]; |
| 75 | uint8_t* wptr = raw; |
| 76 | wptr = WriteVarInt(MakeTagLengthDelimited(2), wptr); |
| 77 | wptr = WriteVarInt(kPayloadSize, wptr); |
| 78 | message.AppendRawProtoBytes(raw, static_cast<size_t>(wptr - raw)); |
| 79 | const uint8_t padding[1024 * 128]{}; |
| 80 | for (size_t i = 0; i < kPayloadSize / sizeof(padding); i++) |
| 81 | message.AppendRawProtoBytes(padding, sizeof(padding)); |
| 82 | |
| 83 | // Append another valid field. |
| 84 | message.AppendVarInt(/*field_id=*/3, 13); |
| 85 | |
| 86 | ProtoDecoder decoder(data.get(), static_cast<size_t>(writer.written())); |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 87 | Field field = decoder.ReadField(); |
| 88 | ASSERT_EQ(1u, field.id()); |
Primiano Tucci | 2cfc7fd | 2019-11-28 16:07:27 +0000 | [diff] [blame] | 89 | ASSERT_EQ(11, field.as_int32()); |
| 90 | |
| 91 | field = decoder.ReadField(); |
| 92 | ASSERT_EQ(3u, field.id()); |
| 93 | ASSERT_EQ(13, field.as_int32()); |
| 94 | |
| 95 | field = decoder.ReadField(); |
| 96 | ASSERT_FALSE(field.valid()); |
Hector Dearman | cf4ef4b | 2019-02-27 19:10:21 +0000 | [diff] [blame] | 97 | } |
| 98 | |
Hector Dearman | c7c267a | 2019-03-25 13:06:21 +0000 | [diff] [blame] | 99 | TEST(ProtoDecoderTest, SingleRepeatedField) { |
| 100 | Message message; |
| 101 | ScatteredHeapBuffer delegate(512, 512); |
| 102 | ScatteredStreamWriter writer(&delegate); |
| 103 | delegate.set_writer(&writer); |
| 104 | message.Reset(&writer); |
| 105 | message.AppendVarInt(/*field_id=*/2, 10); |
| 106 | delegate.AdjustUsedSizeOfCurrentSlice(); |
| 107 | auto used_range = delegate.slices()[0].GetUsedRange(); |
| 108 | |
| 109 | TypedProtoDecoder<2, true> tpd(used_range.begin, used_range.size()); |
Primiano Tucci | 8f7359c | 2019-10-17 12:59:33 +0100 | [diff] [blame] | 110 | auto it = tpd.GetRepeated<int32_t>(/*field_id=*/2); |
Hector Dearman | c7c267a | 2019-03-25 13:06:21 +0000 | [diff] [blame] | 111 | EXPECT_TRUE(it); |
Primiano Tucci | 8f7359c | 2019-10-17 12:59:33 +0100 | [diff] [blame] | 112 | EXPECT_EQ(it.field().as_int32(), 10); |
| 113 | EXPECT_EQ(*it, 10); |
Hector Dearman | c7c267a | 2019-03-25 13:06:21 +0000 | [diff] [blame] | 114 | EXPECT_FALSE(++it); |
| 115 | } |
| 116 | |
Sami Kyostila | 8a48e02 | 2019-11-12 23:46:18 +0000 | [diff] [blame] | 117 | TEST(ProtoDecoderTest, RepeatedVariableLengthField) { |
| 118 | HeapBuffered<Message> message; |
| 119 | |
| 120 | static constexpr char kTestString[] = "test"; |
| 121 | static constexpr char kTestString2[] = "honk honk"; |
| 122 | message->AppendString(1, kTestString); |
| 123 | message->AppendString(1, kTestString2); |
| 124 | std::vector<uint8_t> proto = message.SerializeAsArray(); |
| 125 | TypedProtoDecoder<32, false> decoder(proto.data(), proto.size()); |
| 126 | |
| 127 | auto it = decoder.GetRepeated<ConstChars>(1); |
| 128 | ASSERT_EQ(it->type(), ProtoWireType::kLengthDelimited); |
| 129 | ASSERT_EQ(it->size(), sizeof(kTestString) - 1); |
| 130 | ASSERT_EQ(it->as_std_string(), std::string(kTestString)); |
| 131 | ASSERT_EQ((*it).ToStdString(), std::string(kTestString)); |
| 132 | ++it; |
| 133 | ASSERT_EQ(it->type(), ProtoWireType::kLengthDelimited); |
| 134 | ASSERT_EQ(it->size(), sizeof(kTestString2) - 1); |
| 135 | ASSERT_EQ(it->as_std_string(), std::string(kTestString2)); |
| 136 | ASSERT_EQ((*it).ToStdString(), std::string(kTestString2)); |
| 137 | } |
| 138 | |
Hector Dearman | c7c267a | 2019-03-25 13:06:21 +0000 | [diff] [blame] | 139 | TEST(ProtoDecoderTest, SingleRepeatedFieldWithExpansion) { |
| 140 | Message message; |
| 141 | ScatteredHeapBuffer delegate(512, 512); |
| 142 | ScatteredStreamWriter writer(&delegate); |
| 143 | delegate.set_writer(&writer); |
| 144 | message.Reset(&writer); |
| 145 | for (int i = 0; i < 2000; i++) { |
| 146 | message.AppendVarInt(/*field_id=*/2, i); |
| 147 | } |
| 148 | std::vector<uint8_t> data = delegate.StitchSlices(); |
| 149 | |
| 150 | TypedProtoDecoder<2, true> tpd(data.data(), data.size()); |
Primiano Tucci | 8f7359c | 2019-10-17 12:59:33 +0100 | [diff] [blame] | 151 | auto it = tpd.GetRepeated<int32_t>(/*field_id=*/2); |
Hector Dearman | c7c267a | 2019-03-25 13:06:21 +0000 | [diff] [blame] | 152 | for (int i = 0; i < 2000; i++) { |
| 153 | EXPECT_TRUE(it); |
Primiano Tucci | 8f7359c | 2019-10-17 12:59:33 +0100 | [diff] [blame] | 154 | EXPECT_EQ(*it, i); |
Hector Dearman | c7c267a | 2019-03-25 13:06:21 +0000 | [diff] [blame] | 155 | ++it; |
| 156 | } |
| 157 | EXPECT_FALSE(it); |
| 158 | } |
| 159 | |
| 160 | TEST(ProtoDecoderTest, NoRepeatedField) { |
| 161 | uint8_t buf[] = {0x01}; |
| 162 | TypedProtoDecoder<2, true> tpd(buf, 1); |
Primiano Tucci | 8f7359c | 2019-10-17 12:59:33 +0100 | [diff] [blame] | 163 | auto it = tpd.GetRepeated<int32_t>(/*field_id=*/1); |
Hector Dearman | c7c267a | 2019-03-25 13:06:21 +0000 | [diff] [blame] | 164 | EXPECT_FALSE(it); |
| 165 | EXPECT_FALSE(tpd.Get(2).valid()); |
| 166 | } |
| 167 | |
| 168 | TEST(ProtoDecoderTest, RepeatedFields) { |
| 169 | Message message; |
| 170 | ScatteredHeapBuffer delegate(512, 512); |
| 171 | ScatteredStreamWriter writer(&delegate); |
| 172 | delegate.set_writer(&writer); |
| 173 | message.Reset(&writer); |
| 174 | |
| 175 | message.AppendVarInt(1, 10); |
| 176 | message.AppendVarInt(2, 20); |
| 177 | message.AppendVarInt(3, 30); |
| 178 | |
| 179 | message.AppendVarInt(1, 11); |
| 180 | message.AppendVarInt(2, 21); |
| 181 | message.AppendVarInt(2, 22); |
| 182 | |
| 183 | delegate.AdjustUsedSizeOfCurrentSlice(); |
| 184 | auto used_range = delegate.slices()[0].GetUsedRange(); |
| 185 | |
| 186 | // When iterating with the simple decoder we should just see fields in parsing |
| 187 | // order. |
| 188 | ProtoDecoder decoder(used_range.begin, used_range.size()); |
| 189 | std::string fields_seen; |
| 190 | for (auto fld = decoder.ReadField(); fld.valid(); fld = decoder.ReadField()) { |
| 191 | fields_seen += |
| 192 | std::to_string(fld.id()) + ":" + std::to_string(fld.as_int32()) + ";"; |
| 193 | } |
| 194 | EXPECT_EQ(fields_seen, "1:10;2:20;3:30;1:11;2:21;2:22;"); |
| 195 | |
| 196 | TypedProtoDecoder<4, true> tpd(used_range.begin, used_range.size()); |
| 197 | |
| 198 | // When parsing with the one-shot decoder and querying the single field id, we |
| 199 | // should see the last value for each of them, not the first one. This is the |
| 200 | // current behavior of Google protobuf's parser. |
| 201 | EXPECT_EQ(tpd.Get(1).as_int32(), 11); |
| 202 | EXPECT_EQ(tpd.Get(2).as_int32(), 22); |
| 203 | EXPECT_EQ(tpd.Get(3).as_int32(), 30); |
| 204 | |
| 205 | // But when iterating we should see values in the original order. |
Primiano Tucci | 8f7359c | 2019-10-17 12:59:33 +0100 | [diff] [blame] | 206 | auto it = tpd.GetRepeated<int32_t>(1); |
| 207 | EXPECT_EQ(*it, 10); |
| 208 | EXPECT_EQ(*++it, 11); |
Hector Dearman | c7c267a | 2019-03-25 13:06:21 +0000 | [diff] [blame] | 209 | EXPECT_FALSE(++it); |
| 210 | |
Primiano Tucci | 8f7359c | 2019-10-17 12:59:33 +0100 | [diff] [blame] | 211 | it = tpd.GetRepeated<int32_t>(2); |
| 212 | EXPECT_EQ(*it++, 20); |
| 213 | EXPECT_EQ(*it++, 21); |
| 214 | EXPECT_EQ(*it++, 22); |
Sami Kyostila | 5b50920 | 2019-07-24 07:15:50 +0100 | [diff] [blame] | 215 | EXPECT_FALSE(it); |
Hector Dearman | c7c267a | 2019-03-25 13:06:21 +0000 | [diff] [blame] | 216 | |
Primiano Tucci | 8f7359c | 2019-10-17 12:59:33 +0100 | [diff] [blame] | 217 | it = tpd.GetRepeated<int32_t>(3); |
| 218 | EXPECT_EQ(*it, 30); |
Hector Dearman | c7c267a | 2019-03-25 13:06:21 +0000 | [diff] [blame] | 219 | EXPECT_FALSE(++it); |
| 220 | } |
| 221 | |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 222 | TEST(ProtoDecoderTest, FixedData) { |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 223 | struct FieldExpectation { |
| 224 | const char* encoded; |
| 225 | size_t encoded_size; |
| 226 | uint32_t id; |
Isabelle Taylor | aa45fe0 | 2018-12-16 21:02:20 +0000 | [diff] [blame] | 227 | ProtoWireType type; |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 228 | uint64_t int_value; |
| 229 | }; |
| 230 | |
| 231 | const FieldExpectation kFieldExpectations[] = { |
Isabelle Taylor | aa45fe0 | 2018-12-16 21:02:20 +0000 | [diff] [blame] | 232 | {"\x08\x00", 2, 1, ProtoWireType::kVarInt, 0}, |
Hector Dearman | dd63979 | 2019-01-31 15:04:59 +0000 | [diff] [blame] | 233 | {"\x08\x01", 2, 1, ProtoWireType::kVarInt, 1}, |
Isabelle Taylor | aa45fe0 | 2018-12-16 21:02:20 +0000 | [diff] [blame] | 234 | {"\x08\x42", 2, 1, ProtoWireType::kVarInt, 0x42}, |
| 235 | {"\xF8\x07\x42", 3, 127, ProtoWireType::kVarInt, 0x42}, |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 236 | {"\xB8\x3E\xFF\xFF\xFF\xFF\x0F", 7, 999, ProtoWireType::kVarInt, |
Isabelle Taylor | aa45fe0 | 2018-12-16 21:02:20 +0000 | [diff] [blame] | 237 | 0xFFFFFFFF}, |
| 238 | {"\x7D\x42\x00\x00\x00", 5, 15, ProtoWireType::kFixed32, 0x42}, |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 239 | {"\xBD\x3E\x78\x56\x34\x12", 6, 999, ProtoWireType::kFixed32, 0x12345678}, |
Isabelle Taylor | aa45fe0 | 2018-12-16 21:02:20 +0000 | [diff] [blame] | 240 | {"\x79\x42\x00\x00\x00\x00\x00\x00\x00", 9, 15, ProtoWireType::kFixed64, |
| 241 | 0x42}, |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 242 | {"\xB9\x3E\x08\x07\x06\x05\x04\x03\x02\x01", 10, 999, |
Isabelle Taylor | aa45fe0 | 2018-12-16 21:02:20 +0000 | [diff] [blame] | 243 | ProtoWireType::kFixed64, 0x0102030405060708}, |
| 244 | {"\x0A\x00", 2, 1, ProtoWireType::kLengthDelimited, 0}, |
| 245 | {"\x0A\x04|abc", 6, 1, ProtoWireType::kLengthDelimited, 4}, |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 246 | {"\xBA\x3E\x04|abc", 7, 999, ProtoWireType::kLengthDelimited, 4}, |
| 247 | {"\xBA\x3E\x83\x01|abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab" |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 248 | "cdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu" |
| 249 | "vwx", |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 250 | 135, 999, ProtoWireType::kLengthDelimited, 131}, |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 251 | }; |
| 252 | |
| 253 | for (size_t i = 0; i < perfetto::base::ArraySize(kFieldExpectations); ++i) { |
| 254 | const FieldExpectation& exp = kFieldExpectations[i]; |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 255 | TypedProtoDecoder<999, 0> decoder( |
| 256 | reinterpret_cast<const uint8_t*>(exp.encoded), exp.encoded_size); |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 257 | |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 258 | auto& field = decoder.Get(exp.id); |
| 259 | ASSERT_EQ(exp.type, field.type()); |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 260 | |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 261 | if (field.type() == ProtoWireType::kLengthDelimited) { |
| 262 | ASSERT_EQ(exp.int_value, field.size()); |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 263 | } else { |
Oystein Eftevaag | f159a21 | 2019-03-22 15:39:08 -0700 | [diff] [blame] | 264 | ASSERT_EQ(int64_t(exp.int_value), field.as_int64()); |
Hector Dearman | dd63979 | 2019-01-31 15:04:59 +0000 | [diff] [blame] | 265 | // Proto encodes booleans as varints of 0 or 1. |
| 266 | if (exp.int_value == 0 || exp.int_value == 1) { |
Florian Mayer | 15aac0c | 2019-03-22 11:15:30 +0000 | [diff] [blame] | 267 | ASSERT_EQ(int64_t(exp.int_value), field.as_bool()); |
Hector Dearman | dd63979 | 2019-01-31 15:04:59 +0000 | [diff] [blame] | 268 | } |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 269 | } |
| 270 | } |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 271 | |
| 272 | // Test float and doubles decoding. |
| 273 | const char buf[] = "\x0d\x00\x00\xa0\x3f\x11\x00\x00\x00\x00\x00\x42\x8f\xc0"; |
Ryan | cbd1615 | 2019-09-04 16:54:45 +0100 | [diff] [blame] | 274 | TypedProtoDecoder<2, false> decoder(reinterpret_cast<const uint8_t*>(buf), |
| 275 | sizeof(buf)); |
Primiano Tucci | c167887 | 2019-03-20 11:30:54 +0000 | [diff] [blame] | 276 | EXPECT_FLOAT_EQ(decoder.Get(1).as_float(), 1.25f); |
| 277 | EXPECT_DOUBLE_EQ(decoder.Get(2).as_double(), -1000.25); |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 278 | } |
| 279 | |
Eric Seckler | d012597 | 2019-04-29 09:49:22 +0000 | [diff] [blame] | 280 | TEST(ProtoDecoderTest, FindField) { |
| 281 | uint8_t buf[] = {0x08, 0x00}; // field_id 1, varint value 0. |
| 282 | ProtoDecoder pd(buf, 2); |
| 283 | |
| 284 | auto field = pd.FindField(1); |
| 285 | ASSERT_TRUE(field); |
| 286 | EXPECT_EQ(field.as_int64(), 0); |
| 287 | |
| 288 | auto field2 = pd.FindField(2); |
| 289 | EXPECT_FALSE(field2); |
| 290 | } |
| 291 | |
Sami Kyostila | c45e945 | 2019-07-26 23:42:28 +0100 | [diff] [blame] | 292 | TEST(ProtoDecoderTest, MoveTypedDecoder) { |
| 293 | HeapBuffered<Message> message; |
| 294 | message->AppendVarInt(/*field_id=*/1, 10); |
| 295 | std::vector<uint8_t> proto = message.SerializeAsArray(); |
| 296 | |
| 297 | // Construct a decoder that uses inline storage (i.e., the fields are stored |
| 298 | // within the object itself). |
| 299 | using Decoder = TypedProtoDecoder<32, false>; |
| 300 | std::unique_ptr<Decoder> decoder(new Decoder(proto.data(), proto.size())); |
| 301 | ASSERT_GE(reinterpret_cast<uintptr_t>(&decoder->at<1>()), |
| 302 | reinterpret_cast<uintptr_t>(decoder.get())); |
| 303 | ASSERT_LT(reinterpret_cast<uintptr_t>(&decoder->at<1>()), |
| 304 | reinterpret_cast<uintptr_t>(decoder.get()) + sizeof(Decoder)); |
| 305 | |
| 306 | // Move the decoder into another object and deallocate the original object. |
| 307 | Decoder decoder2(std::move(*decoder)); |
| 308 | decoder.reset(); |
| 309 | |
| 310 | // Check that the contents got moved correctly. |
| 311 | EXPECT_EQ(decoder2.Get(1).as_int32(), 10); |
| 312 | ASSERT_GE(reinterpret_cast<uintptr_t>(&decoder2.at<1>()), |
| 313 | reinterpret_cast<uintptr_t>(&decoder2)); |
| 314 | ASSERT_LT(reinterpret_cast<uintptr_t>(&decoder2.at<1>()), |
| 315 | reinterpret_cast<uintptr_t>(&decoder2) + sizeof(Decoder)); |
| 316 | } |
| 317 | |
Ryan | cbd1615 | 2019-09-04 16:54:45 +0100 | [diff] [blame] | 318 | TEST(ProtoDecoderTest, PackedRepeatedVarint) { |
| 319 | std::vector<int32_t> values = {42, 255, 0, -1}; |
| 320 | |
| 321 | // serialize using protobuf library |
| 322 | pbgold::PackedRepeatedFields msg; |
| 323 | for (auto v : values) |
| 324 | msg.add_field_int32(v); |
| 325 | std::string serialized = msg.SerializeAsString(); |
| 326 | |
| 327 | // decode using TypedProtoDecoder directly |
| 328 | { |
| 329 | constexpr int kFieldId = |
| 330 | pbtest::PackedRepeatedFields::kFieldInt32FieldNumber; |
| 331 | TypedProtoDecoder<kFieldId, false> decoder( |
| 332 | reinterpret_cast<const uint8_t*>(serialized.data()), serialized.size()); |
| 333 | ASSERT_TRUE(decoder.at<kFieldId>().valid()); |
| 334 | bool parse_error = false; |
| 335 | auto packed_it = |
| 336 | decoder.GetPackedRepeated<proto_utils::ProtoWireType::kVarInt, int32_t>( |
| 337 | kFieldId, &parse_error); |
| 338 | |
| 339 | std::vector<int32_t> decoded_values; |
| 340 | for (; packed_it; ++packed_it) { |
| 341 | auto v = *packed_it; |
| 342 | decoded_values.push_back(v); |
| 343 | } |
| 344 | ASSERT_EQ(values, decoded_values); |
| 345 | ASSERT_FALSE(parse_error); |
| 346 | } |
| 347 | |
| 348 | // decode using plugin-generated accessor |
| 349 | { |
| 350 | auto decoder = pbtest::PackedRepeatedFields::Decoder(serialized); |
| 351 | ASSERT_TRUE(decoder.has_field_int32()); |
| 352 | |
| 353 | bool parse_error = false; |
| 354 | std::vector<int32_t> decoded_values; |
| 355 | for (auto packed_it = decoder.field_int32(&parse_error); packed_it; |
| 356 | ++packed_it) { |
| 357 | auto v = *packed_it; |
| 358 | decoded_values.push_back(v); |
| 359 | } |
| 360 | ASSERT_EQ(values, decoded_values); |
| 361 | ASSERT_FALSE(parse_error); |
| 362 | } |
| 363 | |
| 364 | // unset field case |
| 365 | pbgold::PackedRepeatedFields empty_msg; |
| 366 | std::string empty_serialized = empty_msg.SerializeAsString(); |
| 367 | auto decoder = pbtest::PackedRepeatedFields::Decoder(empty_serialized); |
| 368 | ASSERT_FALSE(decoder.has_field_int32()); |
| 369 | bool parse_error = false; |
| 370 | auto packed_it = decoder.field_int32(&parse_error); |
| 371 | ASSERT_FALSE(bool(packed_it)); |
| 372 | ASSERT_FALSE(parse_error); |
| 373 | } |
| 374 | |
| 375 | TEST(ProtoDecoderTest, PackedRepeatedFixed32) { |
| 376 | std::vector<uint32_t> values = {42, 255, 0, 1}; |
| 377 | |
| 378 | // serialize using protobuf library |
| 379 | pbgold::PackedRepeatedFields msg; |
| 380 | for (auto v : values) |
| 381 | msg.add_field_fixed32(v); |
| 382 | std::string serialized = msg.SerializeAsString(); |
| 383 | |
| 384 | // decode using TypedProtoDecoder directly |
| 385 | { |
| 386 | constexpr int kFieldId = |
| 387 | pbtest::PackedRepeatedFields::kFieldFixed32FieldNumber; |
| 388 | TypedProtoDecoder<kFieldId, false> decoder( |
| 389 | reinterpret_cast<const uint8_t*>(serialized.data()), serialized.size()); |
| 390 | bool parse_error = false; |
| 391 | auto packed_it = |
| 392 | decoder |
| 393 | .GetPackedRepeated<proto_utils::ProtoWireType::kFixed32, uint32_t>( |
| 394 | kFieldId, &parse_error); |
| 395 | |
| 396 | std::vector<uint32_t> decoded_values; |
| 397 | for (; packed_it; ++packed_it) { |
| 398 | auto v = *packed_it; |
| 399 | decoded_values.push_back(v); |
| 400 | } |
| 401 | ASSERT_EQ(values, decoded_values); |
| 402 | ASSERT_FALSE(parse_error); |
| 403 | } |
| 404 | |
| 405 | // decode using plugin-generated accessor |
| 406 | { |
| 407 | auto decoder = pbtest::PackedRepeatedFields::Decoder(serialized); |
| 408 | ASSERT_TRUE(decoder.has_field_fixed32()); |
| 409 | |
| 410 | bool parse_error = false; |
| 411 | std::vector<uint32_t> decoded_values; |
| 412 | for (auto packed_it = decoder.field_fixed32(&parse_error); packed_it; |
| 413 | packed_it++) { |
| 414 | auto v = *packed_it; |
| 415 | decoded_values.push_back(v); |
| 416 | } |
| 417 | ASSERT_EQ(values, decoded_values); |
| 418 | ASSERT_FALSE(parse_error); |
| 419 | } |
| 420 | |
| 421 | // unset field case |
| 422 | pbgold::PackedRepeatedFields empty_msg; |
| 423 | std::string empty_serialized = empty_msg.SerializeAsString(); |
| 424 | auto decoder = pbtest::PackedRepeatedFields::Decoder(empty_serialized); |
| 425 | ASSERT_FALSE(decoder.has_field_fixed32()); |
| 426 | bool parse_error = false; |
| 427 | auto packed_it = decoder.field_fixed32(&parse_error); |
| 428 | ASSERT_FALSE(bool(packed_it)); |
| 429 | ASSERT_FALSE(parse_error); |
| 430 | } |
| 431 | |
| 432 | TEST(ProtoDecoderTest, PackedRepeatedFixed64) { |
| 433 | std::vector<int64_t> values = {42, 255, 0, -1}; |
| 434 | |
| 435 | // serialize using protobuf library |
| 436 | pbgold::PackedRepeatedFields msg; |
| 437 | for (auto v : values) |
| 438 | msg.add_field_sfixed64(v); |
| 439 | std::string serialized = msg.SerializeAsString(); |
| 440 | |
| 441 | // decode using TypedProtoDecoder directly |
| 442 | { |
| 443 | constexpr int kFieldId = |
| 444 | pbtest::PackedRepeatedFields::kFieldSfixed64FieldNumber; |
| 445 | TypedProtoDecoder<kFieldId, false> decoder( |
| 446 | reinterpret_cast<const uint8_t*>(serialized.data()), serialized.size()); |
| 447 | bool parse_error = false; |
| 448 | auto packed_it = |
| 449 | decoder |
| 450 | .GetPackedRepeated<proto_utils::ProtoWireType::kFixed64, int64_t>( |
| 451 | kFieldId, &parse_error); |
| 452 | |
| 453 | std::vector<int64_t> decoded_values; |
| 454 | for (; packed_it; ++packed_it) { |
| 455 | auto v = *packed_it; |
| 456 | decoded_values.push_back(v); |
| 457 | } |
| 458 | ASSERT_EQ(values, decoded_values); |
| 459 | ASSERT_FALSE(parse_error); |
| 460 | } |
| 461 | |
| 462 | // decode using plugin-generated accessor |
| 463 | { |
| 464 | auto decoder = pbtest::PackedRepeatedFields::Decoder(serialized); |
| 465 | ASSERT_TRUE(decoder.has_field_sfixed64()); |
| 466 | |
| 467 | bool parse_error = false; |
| 468 | std::vector<int64_t> decoded_values; |
| 469 | for (auto packed_it = decoder.field_sfixed64(&parse_error); packed_it; |
| 470 | packed_it++) { |
| 471 | auto v = *packed_it; |
| 472 | decoded_values.push_back(v); |
| 473 | } |
| 474 | ASSERT_EQ(values, decoded_values); |
| 475 | ASSERT_FALSE(parse_error); |
| 476 | } |
| 477 | |
| 478 | // unset field case |
| 479 | pbgold::PackedRepeatedFields empty_msg; |
| 480 | std::string empty_serialized = empty_msg.SerializeAsString(); |
| 481 | auto decoder = pbtest::PackedRepeatedFields::Decoder(empty_serialized); |
| 482 | ASSERT_FALSE(decoder.has_field_sfixed64()); |
| 483 | bool parse_error = false; |
| 484 | auto packed_it = decoder.field_sfixed64(&parse_error); |
| 485 | ASSERT_FALSE(bool(packed_it)); |
| 486 | ASSERT_FALSE(parse_error); |
| 487 | } |
| 488 | |
| 489 | TEST(ProtoDecoderTest, ZeroLengthPackedRepeatedField) { |
| 490 | HeapBuffered<pbtest::PackedRepeatedFields> msg; |
Primiano Tucci | 03ac833 | 2019-11-06 13:30:36 +0000 | [diff] [blame] | 491 | PackedVarInt buf; |
Ryan | cbd1615 | 2019-09-04 16:54:45 +0100 | [diff] [blame] | 492 | msg->set_field_int32(buf); |
| 493 | std::string serialized = msg.SerializeAsString(); |
| 494 | |
| 495 | // Encoded as 2 bytes: tag/field, and a length of zero. |
| 496 | EXPECT_EQ(2u, serialized.size()); |
| 497 | |
| 498 | // Appears empty when decoded. |
| 499 | auto decoder = pbtest::PackedRepeatedFields::Decoder(serialized); |
| 500 | ASSERT_TRUE(decoder.has_field_int32()); |
| 501 | bool parse_error = false; |
| 502 | auto packed_it = decoder.field_int32(&parse_error); |
| 503 | ASSERT_FALSE(bool(packed_it)); |
| 504 | ASSERT_FALSE(parse_error); |
| 505 | } |
| 506 | |
| 507 | TEST(ProtoDecoderTest, MalformedPackedFixedBuffer) { |
| 508 | // Encode a fixed32 field where the length is not a multiple of 4 bytes. |
| 509 | HeapBuffered<pbtest::PackedRepeatedFields> msg; |
Primiano Tucci | 03ac833 | 2019-11-06 13:30:36 +0000 | [diff] [blame] | 510 | PackedFixedSizeInt<uint32_t> buf; |
Ryan | cbd1615 | 2019-09-04 16:54:45 +0100 | [diff] [blame] | 511 | buf.Append(1); |
| 512 | buf.Append(2); |
| 513 | buf.Append(3); |
| 514 | const uint8_t* data = buf.data(); |
| 515 | size_t size = buf.size(); |
| 516 | size_t invalid_size = size - 2; |
| 517 | constexpr int kFieldId = |
| 518 | pbtest::PackedRepeatedFields::kFieldFixed32FieldNumber; |
| 519 | msg->AppendBytes(kFieldId, data, invalid_size); |
| 520 | std::string serialized = msg.SerializeAsString(); |
| 521 | |
| 522 | // Iterator indicates parse error. |
| 523 | auto decoder = pbtest::PackedRepeatedFields::Decoder(serialized); |
| 524 | ASSERT_TRUE(decoder.has_field_fixed32()); |
| 525 | bool parse_error = false; |
| 526 | for (auto packed_it = decoder.field_fixed32(&parse_error); packed_it; |
| 527 | packed_it++) { |
| 528 | } |
| 529 | ASSERT_TRUE(parse_error); |
| 530 | } |
| 531 | |
| 532 | TEST(ProtoDecoderTest, MalformedPackedVarIntBuffer) { |
| 533 | // Encode a varint field with the last varint chopped off partway. |
| 534 | HeapBuffered<pbtest::PackedRepeatedFields> msg; |
Primiano Tucci | 03ac833 | 2019-11-06 13:30:36 +0000 | [diff] [blame] | 535 | PackedVarInt buf; |
Ryan | cbd1615 | 2019-09-04 16:54:45 +0100 | [diff] [blame] | 536 | buf.Append(1024); |
| 537 | buf.Append(2048); |
| 538 | buf.Append(4096); |
| 539 | const uint8_t* data = buf.data(); |
| 540 | size_t size = buf.size(); |
| 541 | size_t invalid_size = size - 1; |
| 542 | constexpr int kFieldId = pbtest::PackedRepeatedFields::kFieldInt32FieldNumber; |
| 543 | msg->AppendBytes(kFieldId, data, invalid_size); |
| 544 | std::string serialized = msg.SerializeAsString(); |
| 545 | |
| 546 | // Iterator indicates parse error. |
| 547 | auto decoder = pbtest::PackedRepeatedFields::Decoder(serialized); |
| 548 | ASSERT_TRUE(decoder.has_field_int32()); |
| 549 | bool parse_error = false; |
| 550 | for (auto packed_it = decoder.field_int32(&parse_error); packed_it; |
| 551 | packed_it++) { |
| 552 | } |
| 553 | ASSERT_TRUE(parse_error); |
| 554 | } |
| 555 | |
Primiano Tucci | 2cfc7fd | 2019-11-28 16:07:27 +0000 | [diff] [blame] | 556 | // Tests that big field ids (> 0xffff) are just skipped but don't fail parsing. |
| 557 | // This is a regression test for b/145339282 (DataSourceConfig.for_testing |
| 558 | // having a very large ID == 268435455 until Android R). |
| 559 | TEST(ProtoDecoderTest, SkipBigFieldIds) { |
| 560 | Message message; |
| 561 | ScatteredHeapBuffer delegate(512, 512); |
| 562 | ScatteredStreamWriter writer(&delegate); |
| 563 | delegate.set_writer(&writer); |
| 564 | message.Reset(&writer); |
| 565 | message.AppendVarInt(/*field_id=*/1, 11); |
| 566 | message.AppendVarInt(/*field_id=*/1000000, 0); // Will be skipped |
| 567 | message.AppendVarInt(/*field_id=*/65535, 99); |
| 568 | message.AppendVarInt(/*field_id=*/268435455, 0); // Will be skipped |
| 569 | message.AppendVarInt(/*field_id=*/2, 12); |
| 570 | message.AppendVarInt(/*field_id=*/2000000, 0); // Will be skipped |
| 571 | std::vector<uint8_t> data = delegate.StitchSlices(); |
| 572 | |
| 573 | // Check the iterator-based ProtoDecoder. |
| 574 | { |
| 575 | ProtoDecoder decoder(data.data(), data.size()); |
| 576 | Field field = decoder.ReadField(); |
| 577 | ASSERT_TRUE(field.valid()); |
| 578 | ASSERT_EQ(field.id(), 1u); |
| 579 | ASSERT_EQ(field.as_int32(), 11); |
| 580 | |
| 581 | field = decoder.ReadField(); |
| 582 | ASSERT_TRUE(field.valid()); |
| 583 | ASSERT_EQ(field.id(), 65535u); |
| 584 | ASSERT_EQ(field.as_int32(), 99); |
| 585 | |
| 586 | field = decoder.ReadField(); |
| 587 | ASSERT_TRUE(field.valid()); |
| 588 | ASSERT_EQ(field.id(), 2u); |
| 589 | ASSERT_EQ(field.as_int32(), 12); |
| 590 | |
| 591 | field = decoder.ReadField(); |
| 592 | ASSERT_FALSE(field.valid()); |
| 593 | } |
| 594 | |
| 595 | // Test the one-shot-read TypedProtoDecoder. |
| 596 | // Note: field 65535 will be also skipped because this TypedProtoDecoder has |
| 597 | // a cap on MAX_FIELD_ID = 3. |
| 598 | { |
| 599 | TypedProtoDecoder<3, true> tpd(data.data(), data.size()); |
| 600 | EXPECT_EQ(tpd.Get(1).as_int32(), 11); |
| 601 | EXPECT_EQ(tpd.Get(2).as_int32(), 12); |
| 602 | } |
| 603 | } |
| 604 | |
| 605 | // Edge case for SkipBigFieldIds, the message contains only one field with a |
| 606 | // very big id. Test that we skip it and return an invalid field, instead of |
| 607 | // geetting stuck in some loop. |
| 608 | TEST(ProtoDecoderTest, OneBigFieldIdOnly) { |
| 609 | Message message; |
| 610 | ScatteredHeapBuffer delegate(512, 512); |
| 611 | ScatteredStreamWriter writer(&delegate); |
| 612 | delegate.set_writer(&writer); |
| 613 | message.Reset(&writer); |
| 614 | message.AppendVarInt(/*field_id=*/268435455, 0); |
| 615 | std::vector<uint8_t> data = delegate.StitchSlices(); |
| 616 | |
| 617 | // Check the iterator-based ProtoDecoder. |
| 618 | ProtoDecoder decoder(data.data(), data.size()); |
| 619 | Field field = decoder.ReadField(); |
| 620 | ASSERT_FALSE(field.valid()); |
| 621 | } |
| 622 | |
Lalit Maganti | 45172a8 | 2018-06-01 03:04:43 +0100 | [diff] [blame] | 623 | } // namespace |
| 624 | } // namespace protozero |