1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
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 <thrift/conformance/cpp2/AnyRegistry.h>
18
19 #include <folly/portability/GTest.h>
20
21 #include <thrift/conformance/cpp2/Any.h>
22 #include <thrift/conformance/cpp2/Object.h>
23 #include <thrift/conformance/cpp2/Testing.h>
24 #include <thrift/lib/cpp2/type/UniversalName.h>
25
26 namespace apache::thrift::conformance {
27 using type::kDisableUniversalHash;
28
29 namespace {
30
TEST(AnyRegistryTest,ShortType)31 TEST(AnyRegistryTest, ShortType) {
32 AnyRegistry registry;
33 FollyToStringSerializer<int> intCodec;
34 EXPECT_TRUE(registry.registerType<int>(shortThriftType(), {&intCodec}));
35
36 // Should use the type uri because it is shorter than the id.
37 Any any = registry.store(1, kFollyToStringProtocol);
38 EXPECT_TRUE(any.type_ref());
39 EXPECT_FALSE(any.typeHashPrefixSha2_256_ref());
40 EXPECT_EQ(registry.load<int>(any), 1);
41 EXPECT_EQ(*registry.tryGetTypeId(any), typeid(int));
42 EXPECT_EQ(registry.getTypeId(any), typeid(int));
43 EXPECT_EQ(registry.getTypeUri(any), shortThriftType().uri_ref().value());
44 }
45
checkLongType(int typeBytes,int expectedOutBytes)46 void checkLongType(int typeBytes, int expectedOutBytes) {
47 AnyRegistry registry;
48 FollyToStringSerializer<int> intCodec;
49 auto longType = longThriftType();
50 if (typeBytes == kTypeHashBytesNotSpecified) {
51 longType.typeHashBytes_ref().reset();
52 } else {
53 longType.set_typeHashBytes(typeBytes);
54 }
55 EXPECT_TRUE(registry.registerType<int>(longType, {&intCodec}));
56
57 // Should use the type id because it is shorter than the uri.
58 Any any = registry.store(1, kFollyToStringProtocol);
59 if (expectedOutBytes == kDisableUniversalHash) {
60 EXPECT_FALSE(any.typeHashPrefixSha2_256_ref());
61 EXPECT_TRUE(any.type_ref());
62 EXPECT_EQ(
63 registry.getSerializerByUri(*any.get_type(), intCodec.getProtocol()),
64 &intCodec);
65 } else {
66 EXPECT_FALSE(any.type_ref());
67 ASSERT_TRUE(any.typeHashPrefixSha2_256_ref());
68 EXPECT_EQ(
69 any.typeHashPrefixSha2_256_ref().value_unchecked().size(),
70 expectedOutBytes);
71 EXPECT_EQ(
72 registry.getSerializerByHash(
73 type::UniversalHashAlgorithm::Sha2_256,
74 *any.get_typeHashPrefixSha2_256(),
75 intCodec.getProtocol()),
76 &intCodec);
77 }
78 EXPECT_EQ(registry.load<int>(any), 1);
79 EXPECT_EQ(*registry.tryGetTypeId(any), typeid(int));
80 EXPECT_EQ(registry.getTypeId(any), typeid(int));
81 EXPECT_EQ(registry.getTypeUri(any), longType.uri_ref().value());
82 }
83
TEST(AnyRegistryTest,LongType)84 TEST(AnyRegistryTest, LongType) {
85 // Disabled is respected.
86 THRIFT_SCOPED_CHECK(
87 checkLongType(kDisableUniversalHash, kDisableUniversalHash));
88
89 // Unset uses default.
90 THRIFT_SCOPED_CHECK(
91 checkLongType(kTypeHashBytesNotSpecified, kDefaultTypeHashBytes));
92
93 // Type can increase bytes used.
94 THRIFT_SCOPED_CHECK(checkLongType(24, 24));
95 THRIFT_SCOPED_CHECK(checkLongType(32, 32));
96 }
97
TEST(AnyRegistryTest,ShortTypeHash)98 TEST(AnyRegistryTest, ShortTypeHash) {
99 AnyRegistry registry;
100 FollyToStringSerializer<int> intCodec;
101 auto longType = longThriftType();
102 EXPECT_TRUE(registry.registerType<int>(longType, {&intCodec}));
103 Any any = registry.store(1, kFollyToStringProtocol);
104 any.set_typeHashPrefixSha2_256(
105 any.typeHashPrefixSha2_256_ref()->substr(0, 7));
106 EXPECT_THROW(registry.load<int>(any), std::out_of_range);
107 }
108
TEST(AnyRegistryTest,TypeNotFound)109 TEST(AnyRegistryTest, TypeNotFound) {
110 AnyRegistry registry;
111 EXPECT_EQ(registry.getTypeUri<int>(), "");
112 EXPECT_EQ((registry.getSerializer<int, StandardProtocol::Binary>()), nullptr);
113
114 EXPECT_THROW(registry.store<StandardProtocol::Binary>(1), std::out_of_range);
115
116 Any any;
117 EXPECT_THROW(registry.load<int>(any), std::invalid_argument);
118 any.set_type(thriftType("int"));
119 EXPECT_THROW(registry.load<int>(any), std::out_of_range);
120 any.set_protocol(StandardProtocol::Binary);
121 EXPECT_THROW(registry.load<int>(any), std::out_of_range);
122
123 FollyToStringSerializer<int> intCodec;
124 EXPECT_THROW(registry.registerSerializer<int>(&intCodec), std::out_of_range);
125 }
126
TEST(AnyRegistryTest,ProtocolNotFound)127 TEST(AnyRegistryTest, ProtocolNotFound) {
128 AnyRegistry registry;
129 EXPECT_TRUE(registry.registerType<int>(testThriftType("int")));
130 EXPECT_EQ(registry.getTypeUri<int>(), thriftType("int"));
131 EXPECT_EQ((registry.getSerializer<int, StandardProtocol::Binary>()), nullptr);
132
133 EXPECT_THROW(registry.store<StandardProtocol::Binary>(1), std::out_of_range);
134
135 Any any;
136 EXPECT_THROW(registry.load<int>(any), std::invalid_argument);
137 any.set_type(thriftType("int"));
138 EXPECT_THROW(registry.load<int>(any), std::out_of_range);
139 any.set_protocol(StandardProtocol::Binary);
140 EXPECT_THROW(registry.load<int>(any), std::out_of_range);
141 }
142
TEST(AnyRegistryTest,TypeHashToShort)143 TEST(AnyRegistryTest, TypeHashToShort) {
144 AnyRegistry registry;
145 FollyToStringSerializer<int> intCodec;
146 auto anyType = longThriftType();
147 anyType.set_typeHashBytes(17);
148 EXPECT_TRUE(registry.registerType<int>(anyType, {&intCodec}));
149 Any any = registry.store(1, intCodec.getProtocol());
150 ASSERT_TRUE(any.typeHashPrefixSha2_256_ref());
151 EXPECT_EQ(any.get_typeHashPrefixSha2_256()->size(), 17);
152 EXPECT_EQ(registry.load<int>(any), 1);
153
154 any.set_typeHashPrefixSha2_256(
155 any.get_typeHashPrefixSha2_256()->substr(0, 8));
156 EXPECT_EQ(registry.load<int>(any), 1);
157
158 any.set_typeHashPrefixSha2_256(
159 any.get_typeHashPrefixSha2_256()->substr(0, 7));
160 EXPECT_THROW(registry.load<int>(any), std::out_of_range);
161 }
162
TEST(AnyRegistryTest,Behavior)163 TEST(AnyRegistryTest, Behavior) {
164 AnyRegistry registry;
165 const AnyRegistry& cregistry = registry;
166 EXPECT_EQ(cregistry.getTypeUri(typeid(int)), "");
167 EXPECT_EQ(cregistry.getSerializer<int>(kFollyToStringProtocol), nullptr);
168
169 FollyToStringSerializer<int> intCodec;
170
171 // Type must be registered first.
172 EXPECT_THROW(registry.registerSerializer<int>(&intCodec), std::out_of_range);
173
174 // Empty string is rejected.
175 EXPECT_THROW(
176 registry.registerType<int>(testThriftType("")), std::invalid_argument);
177
178 EXPECT_TRUE(registry.registerType<int>(testThriftType("int")));
179 EXPECT_EQ(cregistry.getTypeUri(typeid(int)), thriftType("int"));
180 EXPECT_EQ(cregistry.getSerializer<int>(kFollyToStringProtocol), nullptr);
181
182 // Conflicting and duplicate registrations are rejected.
183 EXPECT_FALSE(registry.registerType<int>(testThriftType("int")));
184 EXPECT_FALSE(registry.registerType<int>(testThriftType("other-int")));
185 EXPECT_FALSE(registry.registerType<double>(testThriftType("int")));
186
187 EXPECT_TRUE(registry.registerSerializer<int>(&intCodec));
188 EXPECT_EQ(cregistry.getTypeUri<int>(), thriftType("int"));
189 EXPECT_EQ(cregistry.getSerializer<int>(kFollyToStringProtocol), &intCodec);
190
191 // Duplicate registrations are rejected.
192 EXPECT_FALSE(registry.registerSerializer<int>(&intCodec));
193
194 Number1Serializer number1Codec;
195 EXPECT_TRUE(registry.registerSerializer<int>(&number1Codec));
196
197 EXPECT_TRUE(registry.registerType<double>(testThriftType("double")));
198
199 // nullptr is rejected.
200 EXPECT_FALSE(registry.registerSerializer<double>(nullptr));
201
202 EXPECT_TRUE(registry.registerSerializer<double>(
203 std::make_unique<FollyToStringSerializer<double>>()));
204
205 Any value = cregistry.store(3, kFollyToStringProtocol);
206 EXPECT_EQ(value.type_ref().value_or(""), thriftType("int"));
207 EXPECT_FALSE(value.typeHashPrefixSha2_256_ref().has_value());
208 EXPECT_EQ(toString(*value.data_ref()), "3");
209 EXPECT_TRUE(hasProtocol(value, kFollyToStringProtocol));
210 EXPECT_EQ(std::any_cast<int>(cregistry.load(value)), 3);
211 EXPECT_EQ(cregistry.load<int>(value), 3);
212
213 // Storing an Any does nothing if the protocols match.
214 Any original = value;
215 value = cregistry.store(original, kFollyToStringProtocol);
216 EXPECT_EQ(value.type_ref().value_or(""), thriftType("int"));
217 EXPECT_FALSE(value.typeHashPrefixSha2_256_ref().has_value());
218 EXPECT_EQ(toString(*value.data_ref()), "3");
219 EXPECT_TRUE(hasProtocol(value, kFollyToStringProtocol));
220 value =
221 cregistry.store(std::any(std::move(original)), kFollyToStringProtocol);
222 EXPECT_EQ(value.type_ref().value_or(""), thriftType("int"));
223 EXPECT_FALSE(value.typeHashPrefixSha2_256_ref().has_value());
224 EXPECT_EQ(toString(*value.data_ref()), "3");
225 EXPECT_TRUE(hasProtocol(value, kFollyToStringProtocol));
226
227 // Storing an Any with a different protocol does a conversion.
228 original = value;
229 value = cregistry.store(original, Number1Serializer::kProtocol);
230 EXPECT_EQ(value.type_ref().value_or(""), thriftType("int"));
231 EXPECT_FALSE(value.typeHashPrefixSha2_256_ref().has_value());
232 EXPECT_EQ(toString(*value.data_ref()), "number 1!!");
233 EXPECT_TRUE(hasProtocol(value, Number1Serializer::kProtocol));
234 value = cregistry.store(
235 std::any(std::move(original)), Number1Serializer::kProtocol);
236 EXPECT_EQ(value.type_ref().value_or(""), thriftType("int"));
237 EXPECT_FALSE(value.typeHashPrefixSha2_256_ref().has_value());
238 EXPECT_EQ(toString(*value.data_ref()), "number 1!!");
239 EXPECT_TRUE(hasProtocol(value, Number1Serializer::kProtocol));
240 EXPECT_EQ(std::any_cast<int>(cregistry.load(value)), 1);
241 EXPECT_EQ(cregistry.load<int>(value), 1);
242
243 // Storing an unsupported type is an error.
244 EXPECT_THROW(
245 cregistry.store(2.5f, kFollyToStringProtocol), std::out_of_range);
246 EXPECT_THROW(
247 cregistry.store(std::any(2.5f), kFollyToStringProtocol),
248 std::out_of_range);
249
250 // Storing using an unsupported protocol throws an error
251 EXPECT_THROW(
252 cregistry.store(3, Protocol(StandardProtocol::Binary)),
253 std::out_of_range);
254
255 // Loading an empty Any value throws an error.
256 value = {};
257 EXPECT_FALSE(value.type_ref().has_value());
258 EXPECT_FALSE(value.typeHashPrefixSha2_256_ref().has_value());
259 EXPECT_EQ(toString(*value.data_ref()), "");
260 EXPECT_TRUE(
261 hasProtocol(value, getStandardProtocol<StandardProtocol::Compact>()));
262 EXPECT_THROW(cregistry.load(value), std::invalid_argument);
263 EXPECT_THROW(cregistry.load<float>(value), std::invalid_argument);
264 value.set_type("foo");
265 EXPECT_THROW(cregistry.load(value), std::out_of_range);
266 EXPECT_THROW(cregistry.load<float>(value), std::out_of_range);
267
268 value = cregistry.store(2.5, kFollyToStringProtocol);
269 EXPECT_EQ(*value.type_ref(), thriftType("double"));
270 EXPECT_FALSE(value.typeHashPrefixSha2_256_ref().has_value());
271 EXPECT_EQ(toString(*value.data_ref()), "2.5");
272 EXPECT_TRUE(hasProtocol(value, kFollyToStringProtocol));
273 EXPECT_EQ(std::any_cast<double>(cregistry.load(value)), 2.5);
274 EXPECT_EQ(cregistry.load<double>(value), 2.5);
275 EXPECT_THROW(cregistry.load<int>(value), std::bad_any_cast);
276
277 EXPECT_EQ(
278 cregistry.debugString(),
279 "AnyRegistry[\n"
280 " facebook.com/thrift/double (319b4d9a143e15bbf818e1fe4556f46a578cb58acda43d5f40cf9a22886dc9d8):\n"
281 " facebook.com/thrift/FollyToString,\n"
282 " facebook.com/thrift/int (3fc51d1641587f7d26c7ab0dcb97b69f6ea48f9ea15ea626ec34e6069fc4b136):\n"
283 " facebook.com/thrift/FollyToString,\n"
284 " facebook.com/thrift/Number1,\n"
285 "]");
286 }
287
TEST(AnyRegistryTest,Aliases)288 TEST(AnyRegistryTest, Aliases) {
289 AnyRegistry registry;
290 const AnyRegistry& cregistry = registry;
291 FollyToStringSerializer<int> intCodec;
292 Number1Serializer oneCodec;
293
294 EXPECT_TRUE(registry.registerType<int>(
295 testThriftType({"int", "Int", "Integer"}), {&oneCodec, &intCodec}));
296 EXPECT_EQ(registry.getTypeUri<int>(), thriftType("int"));
297 EXPECT_EQ(
298 registry.getSerializerByUri(thriftType("int"), oneCodec.getProtocol()),
299 &oneCodec);
300 EXPECT_EQ(
301 registry.getSerializerByUri(thriftType("Int"), oneCodec.getProtocol()),
302 &oneCodec);
303 EXPECT_EQ(
304 registry.getSerializerByUri(
305 thriftType("Integer"), oneCodec.getProtocol()),
306 &oneCodec);
307
308 auto any = cregistry.store(1, kFollyToStringProtocol);
309 // Stored under the main type uri.
310 EXPECT_EQ(any.type_ref().value_or(""), thriftType("int"));
311 EXPECT_EQ(cregistry.load<int>(any), 1);
312
313 any.set_type(thriftType("Int"));
314 EXPECT_EQ(cregistry.load<int>(any), 1);
315
316 any.set_type(thriftType("Integer"));
317 EXPECT_EQ(cregistry.load<int>(any), 1);
318
319 any.set_type(thriftType("Unknown"));
320 EXPECT_THROW(cregistry.load<int>(any), std::out_of_range);
321 }
322
TEST(AnyRegistryTest,ForwardCompat_Protocol)323 TEST(AnyRegistryTest, ForwardCompat_Protocol) {
324 AnyRegistry registry;
325 Protocol invalidProtocol("invalid");
326 EXPECT_THROW(validateProtocol(invalidProtocol), std::invalid_argument);
327 // Lookup does not throw.
328 EXPECT_EQ(registry.getSerializer<int>(invalidProtocol), nullptr);
329 }
330
TEST(AnyRegistryTest,ForwardCompat_Any)331 TEST(AnyRegistryTest, ForwardCompat_Any) {
332 AnyRegistry registry;
333 const AnyRegistry& cregistry = registry;
334 FollyToStringSerializer<int> intCodec;
335
336 EXPECT_TRUE(registry.registerType<int>(testThriftType("int")));
337 EXPECT_TRUE(registry.registerSerializer<int>(&intCodec));
338
339 Any any = cregistry.store(1, kFollyToStringProtocol);
340
341 validateAny(any);
342 any.type_ref() = "invalid";
343 EXPECT_THROW(validateAny(any), std::invalid_argument);
344 // Load does not throw std::invalid_argument.
345 EXPECT_THROW(cregistry.load(any), std::out_of_range);
346 }
347
TEST(AnyRegistryTest,StdProtocol)348 TEST(AnyRegistryTest, StdProtocol) {
349 AnyRegistry registry;
350 const AnyRegistry& cregistry = registry;
351 registry
352 .registerType<Value, StandardProtocol::Binary, StandardProtocol::Compact>(
353 testThriftType("Value"));
354
355 auto value = asValueStruct<type::i32_t>(1);
356 auto any = cregistry.store<StandardProtocol::Compact>(value);
357 ASSERT_TRUE(any.type_ref());
358 EXPECT_EQ(any.type_ref().value_unchecked(), thriftType("Value"));
359 EXPECT_EQ(cregistry.load<Value>(any), value);
360 }
361
TEST(AnyRegistryTest,Generated)362 TEST(AnyRegistryTest, Generated) {
363 // Double register fails with a runtime error.
364 EXPECT_THROW(detail::registerGeneratedStruct<Value>(), std::runtime_error);
365
366 auto value = asValueStruct<type::i32_t>(1);
367 auto any = AnyRegistry::generated().store<StandardProtocol::Compact>(value);
368 EXPECT_EQ(AnyRegistry::generated().load<Value>(any), value);
369 EXPECT_THROW(
370 AnyRegistry::generated().store<StandardProtocol::Json>(value),
371 std::out_of_range);
372 }
373
TEST(AnyRegistryTest,ForceRegister)374 TEST(AnyRegistryTest, ForceRegister) {
375 AnyRegistry registry;
376 EXPECT_TRUE(registry.forceRegisterType(typeid(Value), "va"));
377 EXPECT_TRUE(registry.registerSerializer<Value>(
378 &getAnyStandardSerializer<Value, StandardProtocol::Compact>()));
379 Value expected;
380 expected.set_boolValue(true);
381 Any any = registry.store(
382 expected, getStandardProtocol<StandardProtocol::Compact>());
383 EXPECT_EQ(any.type_ref(), "va");
384 Value actual = registry.load<Value>(any);
385 EXPECT_EQ(actual, expected);
386 }
387
388 } // namespace
389 } // namespace apache::thrift::conformance
390