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 "pw_protobuf/find.h"
16
17 #include <string_view>
18
19 #include "pw_bytes/array.h"
20 #include "pw_status/status.h"
21 #include "pw_stream/memory_stream.h"
22 #include "pw_string/string.h"
23 #include "pw_unit_test/framework.h"
24
25 namespace pw::protobuf {
26 namespace {
27
28 constexpr auto kEncodedProto = bytes::Array< // clang-format off
29 // type=int32, k=1, v=42
30 0x08, 0x2a, // 0-1
31 // type=sint32, k=2, v=-13
32 0x10, 0x19, // 2-3
33 // type=bool, k=3, v=false
34 0x18, 0x00, // 4-5
35 // type=double, k=4, v=3.14159
36 0x21, 0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x09, 0x40, // 6-14
37 // type=fixed32, k=5, v=0xdeadbeef
38 0x2d, 0xef, 0xbe, 0xad, 0xde, // 15-19
39 // type=string, k=6, v="Hello world"
40 0x32, 0x0b, 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', // 20-32
41
42 // type=message, k=7, len=2
43 0x3a, 0x02, // 33-34
44 // (nested) type=uint32, k=1, v=3
45 0x08, 0x03 // 35-36
46 >(); // clang-format on
47
48 static_assert(kEncodedProto.size() == 37);
49
TEST(Find,PresentField)50 TEST(Find, PresentField) {
51 EXPECT_EQ(FindInt32(kEncodedProto, 1).value(), 42);
52 EXPECT_EQ(FindSint32(kEncodedProto, 2).value(), -13);
53 EXPECT_EQ(FindBool(kEncodedProto, 3).value(), false);
54 EXPECT_EQ(FindDouble(kEncodedProto, 4).value(), 3.14159);
55 EXPECT_EQ(FindFixed32(kEncodedProto, 5).value(), 0xdeadbeef);
56
57 Result<std::string_view> result = FindString(kEncodedProto, 6);
58 ASSERT_EQ(result.status(), OkStatus());
59 InlineString<32> str(*result);
60 EXPECT_STREQ(str.c_str(), "Hello world");
61 }
62
TEST(Find,MissingField)63 TEST(Find, MissingField) {
64 EXPECT_EQ(FindUint32(kEncodedProto, 8).status(), Status::NotFound());
65 EXPECT_EQ(FindUint32(kEncodedProto, 66).status(), Status::NotFound());
66 EXPECT_EQ(FindUint32(kEncodedProto, 123456789).status(), Status::NotFound());
67 EXPECT_EQ(FindRaw(kEncodedProto, 123456789).status(), Status::NotFound());
68 }
69
TEST(Find,InvalidFieldNumber)70 TEST(Find, InvalidFieldNumber) {
71 EXPECT_EQ(FindUint32(kEncodedProto, 0).status(), Status::InvalidArgument());
72 EXPECT_EQ(FindUint32(kEncodedProto, uint32_t(-1)).status(),
73 Status::InvalidArgument());
74 }
75
TEST(Find,WrongWireType)76 TEST(Find, WrongWireType) {
77 // Field 5 is a fixed32, but we request a uint32 (varint).
78 EXPECT_EQ(FindUint32(kEncodedProto, 5).status(),
79 Status::FailedPrecondition());
80 }
81
TEST(Find,MultiLevel)82 TEST(Find, MultiLevel) {
83 Result<ConstByteSpan> submessage = FindSubmessage(kEncodedProto, 7);
84 ASSERT_EQ(submessage.status(), OkStatus());
85 EXPECT_EQ(submessage->size(), 2u);
86
87 // Read a field from the submessage.
88 EXPECT_EQ(FindUint32(*submessage, 1).value(), 3u);
89 }
90
TEST(FindStream,PresentField)91 TEST(FindStream, PresentField) {
92 stream::MemoryReader reader(kEncodedProto);
93
94 EXPECT_EQ(FindInt32(reader, 1).value(), 42);
95 EXPECT_EQ(FindSint32(reader, 2).value(), -13);
96 EXPECT_EQ(FindBool(reader, 3).value(), false);
97 EXPECT_EQ(FindDouble(kEncodedProto, 4).value(), 3.14159);
98
99 EXPECT_EQ(FindFixed32(reader, 5).value(), 0xdeadbeef);
100
101 char str[32];
102 StatusWithSize sws = FindString(reader, 6, str);
103 ASSERT_EQ(sws.status(), OkStatus());
104 ASSERT_EQ(sws.size(), 11u);
105 str[sws.size()] = '\0';
106 EXPECT_STREQ(str, "Hello world");
107 }
108
TEST(FindStream,MissingField)109 TEST(FindStream, MissingField) {
110 stream::MemoryReader reader(kEncodedProto);
111 EXPECT_EQ(FindUint32(reader, 8).status(), Status::NotFound());
112
113 reader = stream::MemoryReader(kEncodedProto);
114 EXPECT_EQ(FindUint32(reader, 66).status(), Status::NotFound());
115
116 reader = stream::MemoryReader(kEncodedProto);
117 EXPECT_EQ(FindUint32(reader, 123456789).status(), Status::NotFound());
118 }
119
TEST(FindStream,InvalidFieldNumber)120 TEST(FindStream, InvalidFieldNumber) {
121 stream::MemoryReader reader(kEncodedProto);
122 EXPECT_EQ(FindUint32(reader, 0).status(), Status::InvalidArgument());
123
124 reader = stream::MemoryReader(kEncodedProto);
125 EXPECT_EQ(FindUint32(reader, uint32_t(-1)).status(),
126 Status::InvalidArgument());
127 }
128
TEST(FindStream,WrongWireType)129 TEST(FindStream, WrongWireType) {
130 stream::MemoryReader reader(kEncodedProto);
131
132 // Field 5 is a fixed32, but we request a uint32 (varint).
133 EXPECT_EQ(FindUint32(reader, 5).status(), Status::FailedPrecondition());
134 }
135
136 enum class Fields : uint32_t {
137 kField1 = 1,
138 kField2 = 2,
139 kField3 = 3,
140 kField4 = 4,
141 kField5 = 5,
142 kField6 = 6,
143 kField7 = 7,
144 };
145
TEST(FindEnum,PresentField)146 TEST(FindEnum, PresentField) {
147 EXPECT_EQ(FindInt32(kEncodedProto, Fields::kField1).value(), 42);
148 EXPECT_EQ(FindSint32(kEncodedProto, Fields::kField2).value(), -13);
149 EXPECT_EQ(FindBool(kEncodedProto, Fields::kField3).value(), false);
150 EXPECT_EQ(FindDouble(kEncodedProto, Fields::kField4).value(), 3.14159);
151 EXPECT_EQ(FindFixed32(kEncodedProto, Fields::kField5).value(), 0xdeadbeef);
152
153 stream::MemoryReader reader(kEncodedProto);
154 InlineString<32> str;
155 StatusWithSize result = FindString(reader, Fields::kField6, str);
156 ASSERT_EQ(result.status(), OkStatus());
157 EXPECT_STREQ(str.c_str(), "Hello world");
158 }
159
TEST(FindRaw,PresentField)160 TEST(FindRaw, PresentField) {
161 ConstByteSpan field1 = FindRaw(kEncodedProto, Fields::kField1).value();
162 EXPECT_EQ(field1.data(), kEncodedProto.data() + 1);
163 EXPECT_EQ(field1.size(), 1u);
164
165 ConstByteSpan field2 = FindRaw(kEncodedProto, Fields::kField2).value();
166 EXPECT_EQ(field2.data(), kEncodedProto.data() + 3);
167 EXPECT_EQ(field2.size(), 1u);
168
169 ConstByteSpan field3 = FindRaw(kEncodedProto, Fields::kField3).value();
170 EXPECT_EQ(field3.data(), kEncodedProto.data() + 5);
171 EXPECT_EQ(field3.size(), 1u);
172
173 ConstByteSpan field4 = FindRaw(kEncodedProto, Fields::kField4).value();
174 EXPECT_EQ(field4.data(), kEncodedProto.data() + 7);
175 EXPECT_EQ(field4.size(), sizeof(double));
176
177 ConstByteSpan field5 = FindRaw(kEncodedProto, Fields::kField5).value();
178 EXPECT_EQ(field5.data(), kEncodedProto.data() + 16);
179 EXPECT_EQ(field5.size(), sizeof(uint32_t));
180
181 ConstByteSpan field6 = FindRaw(kEncodedProto, Fields::kField6).value();
182 EXPECT_EQ(field6.data(), kEncodedProto.data() + 22);
183 EXPECT_EQ(field6.size(), sizeof("Hello world") - 1 /* null */);
184
185 ConstByteSpan field7 = FindRaw(kEncodedProto, Fields::kField7).value();
186 EXPECT_EQ(field7.data(), kEncodedProto.data() + 35);
187 EXPECT_EQ(field7.size(), 2u);
188 }
189
190 enum class Boolean {
191 kTrue = 0,
192 kFalse = 1,
193 kFileNotFound = 2,
194 };
195
196 constexpr auto kEncodedRepeatedProto = bytes::Array< // clang-format off
197 // type=int32, k=1, v=42
198 0x08, 0x2a,
199 // type=int32, k=1, v=32
200 0x08, 0x20,
201 // type=int32, k=1, v=16
202 0x08, 0x10,
203 // type=int32, k=1, v=0
204 0x08, 0x00,
205 // type=uint32, k=2, v=1
206 0x10, 0x1,
207 // type=uint32, k=2, v=2
208 0x10, 0x2,
209 // type=string, k=6, v="Hello, "
210 0x32, 0x07, 'H', 'e', 'l', 'l', 'o', ',', ' ',
211 // type=string, k=6, v="world"
212 0x32, 0x05, 'w', 'o', 'r', 'l', 'd',
213 // type=string, k=6, v="!"
214 0x32, 0x01, '!'
215 >(); // clang-format on
216
TEST(Finder,RepeatedField)217 TEST(Finder, RepeatedField) {
218 StringFinder finder(kEncodedRepeatedProto, 6);
219 Result<std::string_view> result = finder.Next();
220 EXPECT_EQ(result.status(), OkStatus());
221 EXPECT_EQ(result.value(), std::string_view("Hello, "));
222 result = finder.Next();
223 EXPECT_EQ(result.status(), OkStatus());
224 EXPECT_EQ(result.value(), std::string_view("world"));
225 result = finder.Next();
226 EXPECT_EQ(result.status(), OkStatus());
227 EXPECT_EQ(result.value(), std::string_view("!"));
228 result = finder.Next();
229 EXPECT_EQ(result.status(), Status::NotFound());
230 }
231
TEST(StreamFinder,RepeatedField)232 TEST(StreamFinder, RepeatedField) {
233 stream::MemoryReader reader(kEncodedRepeatedProto);
234 Int32StreamFinder finder(reader, 1);
235 Result<int32_t> result = finder.Next();
236 EXPECT_EQ(result.status(), OkStatus());
237 EXPECT_EQ(result.value(), 42);
238 result = finder.Next();
239 EXPECT_EQ(result.status(), OkStatus());
240 EXPECT_EQ(result.value(), 32);
241 result = finder.Next();
242 EXPECT_EQ(result.status(), OkStatus());
243 EXPECT_EQ(result.value(), 16);
244 result = finder.Next();
245 EXPECT_EQ(result.status(), OkStatus());
246 EXPECT_EQ(result.value(), 0);
247 result = finder.Next();
248 EXPECT_EQ(result.status(), Status::NotFound());
249 }
250
TEST(EnumFinder,RepeatedField)251 TEST(EnumFinder, RepeatedField) {
252 EnumFinder<Boolean> finder(kEncodedRepeatedProto, 2);
253 Result<Boolean> result = finder.Next();
254 EXPECT_EQ(result.status(), OkStatus());
255 EXPECT_EQ(result.value(), Boolean::kFalse);
256 result = finder.Next();
257 EXPECT_EQ(result.status(), OkStatus());
258 EXPECT_EQ(result.value(), Boolean::kFileNotFound);
259 result = finder.Next();
260 EXPECT_EQ(result.status(), Status::NotFound());
261 }
262
263 } // namespace
264 } // namespace pw::protobuf
265