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 // Utilities for unit testing conformance implementions.
18
19 #pragma once
20
21 #include <initializer_list>
22 #include <string>
23 #include <string_view>
24
25 #include <folly/Conv.h>
26 #include <folly/io/Cursor.h>
27 #include <folly/io/IOBuf.h>
28 #include <folly/portability/GTest.h>
29 #include <thrift/conformance/cpp2/AnySerializer.h>
30 #include <thrift/conformance/cpp2/AnyStructSerializer.h>
31 #include <thrift/conformance/cpp2/Protocol.h>
32 #include <thrift/conformance/cpp2/ThriftTypeInfo.h>
33 #include <thrift/conformance/if/gen-cpp2/any_types_custom_protocol.h>
34 #include <thrift/conformance/if/gen-cpp2/object_types_custom_protocol.h>
35
36 // Helper so gtest prints out the line number when running the given check.
37 #define THRIFT_SCOPED_CHECK(check) \
38 { \
39 SCOPED_TRACE(#check); \
40 check; \
41 }
42
43 namespace apache::thrift::conformance {
44
45 constexpr StandardProtocol kUnknownStdProtocol =
46 static_cast<StandardProtocol>(1000);
47
UnknownProtocol()48 inline const Protocol& UnknownProtocol() {
49 return getStandardProtocol<kUnknownStdProtocol>();
50 }
51
52 // Creates an ThriftTypeInfo for tests using the given name(s).
53 ThriftTypeInfo testThriftType(const std::string& shortName);
54 ThriftTypeInfo testThriftType(std::initializer_list<const char*> names);
55
56 // Creates an ThriftTypeInfo that is smaller than the associated type id.
57 ThriftTypeInfo shortThriftType(int ordinal = 1);
58 // Creates an ThriftTypeInfo that is larger than the associated type id.
59 ThriftTypeInfo longThriftType(int ordinal = 1);
60
61 // Returns the full thrift type name, for the given type.
62 std::string thriftType(std::string_view type);
63
64 std::string toString(const folly::IOBuf& buf);
65
66 // Checks round trips using the given serializer.
67 template <typename S, typename T>
checkRoundTrip(const S & seralizer,const T & expected)68 void checkRoundTrip(const S& seralizer, const T& expected) {
69 folly::IOBufQueue queue;
70 seralizer.encode(expected, folly::io::QueueAppender{&queue, 2 << 4});
71 folly::io::Cursor cursor(queue.front());
72 T actual = seralizer.template decode<T>(cursor);
73 EXPECT_EQ(actual, expected);
74 }
75
76 // Checks round trips for all standard protocols
77 template <typename T>
checkRoundTrip(const T & value)78 void checkRoundTrip(const T& value) {
79 THRIFT_SCOPED_CHECK(checkRoundTrip(
80 getAnyStandardSerializer<T, StandardProtocol::Binary>(), value));
81 THRIFT_SCOPED_CHECK(checkRoundTrip(
82 getAnyStandardSerializer<T, StandardProtocol::Compact>(), value));
83 THRIFT_SCOPED_CHECK(checkRoundTrip(
84 getAnyStandardSerializer<T, StandardProtocol::Json>(), value));
85 THRIFT_SCOPED_CHECK(checkRoundTrip(
86 getAnyStandardSerializer<T, StandardProtocol::SimpleJson>(), value));
87 }
88
89 // Always serializes integers to the number 1.
90 class Number1Serializer
91 : public BaseTypedAnySerializer<int, Number1Serializer> {
92 using Base = BaseTypedAnySerializer<int, Number1Serializer>;
93
94 public:
95 static const Protocol kProtocol;
96
getProtocol()97 const Protocol& getProtocol() const override { return kProtocol; }
98
99 using Base::encode;
encode(const int &,folly::io::QueueAppender && appender)100 void encode(const int&, folly::io::QueueAppender&& appender) const {
101 std::string data = "number 1!!";
102 appender.push(reinterpret_cast<const uint8_t*>(data.data()), data.size());
103 }
104
105 using Base::decode;
decode(folly::io::Cursor & cursor,int & value)106 void decode(folly::io::Cursor& cursor, int& value) const {
107 cursor.readFixedString(10);
108 value = 1;
109 }
110 };
111
112 extern const Protocol kFollyToStringProtocol;
113
114 // A serializer based on `folly::to<std::string>`.
115 template <typename T>
116 class FollyToStringSerializer
117 : public BaseTypedAnySerializer<T, FollyToStringSerializer<T>> {
118 using Base = BaseTypedAnySerializer<T, FollyToStringSerializer<T>>;
119
120 public:
getProtocol()121 const Protocol& getProtocol() const override {
122 return kFollyToStringProtocol;
123 }
124 using Base::encode;
encode(const T & value,folly::io::QueueAppender && appender)125 void encode(const T& value, folly::io::QueueAppender&& appender) const {
126 std::string data = folly::to<std::string>(value);
127 appender.push(reinterpret_cast<const uint8_t*>(data.data()), data.size());
128 }
129 using Base::decode;
decode(folly::io::Cursor & cursor,T & value)130 void decode(folly::io::Cursor& cursor, T& value) const {
131 value = folly::to<T>(cursor.readFixedString(cursor.totalLength()));
132 }
133 };
134
135 class MultiSerializer : public AnySerializer {
136 using Base = AnySerializer;
137
138 public:
139 mutable size_t intEncCount = 0;
140 mutable size_t dblEncCount = 0;
141
142 mutable size_t intDecCount = 0;
143 mutable size_t dblDecCount = 0;
144 mutable size_t anyDecCount = 0;
145
146 using Base::decode;
147 using Base::encode;
148
getProtocol()149 const Protocol& getProtocol() const override {
150 return kFollyToStringProtocol;
151 }
152 void encode(
153 any_ref value, folly::io::QueueAppender&& appender) const override;
154 void decode(
155 const std::type_info& typeInfo,
156 folly::io::Cursor& cursor,
157 any_ref value) const override;
158
159 // Helper functions to check the statis
160 void checkAndResetInt(size_t enc, size_t dec) const;
161 void checkAndResetDbl(size_t enc, size_t dec) const;
162 void checkAndResetAny(size_t dec) const;
163 void checkAndResetAll() const;
164 void checkAnyDec() const;
165 void checkIntEnc() const;
166 void checkIntDec() const;
167 void checkDblEnc() const;
168 void checkDblDec() const;
169 void checkAnyIntDec() const;
170 void checkAnyDblDec() const;
171 };
172
173 // The different types of ctors/dtor/assignment.
174 //
175 // Order from most to least versatile, in terms of what
176 // contexts they can be used in.
177 //
178 // TODO(afuller): Use the option pattern instead. See Boost.Intrusive.
179 enum class CtorType {
180 Trivial, // Implies also NoThrow.
181 NoThrow,
182 Throw,
183 Delete,
184 };
185
186 // Scope for genericly named classes/helpers.
187 namespace test {
188
189 // If the given CtorType can be used in a throw context.
190 template <CtorType Type>
191 inline constexpr bool is_throw_compat_v = Type <= CtorType::Throw;
192
193 // If the given CtorType can be used in a nothrow context.
194 template <CtorType Type>
195 inline constexpr bool is_nothrow_compat_v = Type <= CtorType::NoThrow;
196
197 // If the given CtorType can be used in a trivial context.
198 template <CtorType Type>
199 inline constexpr bool is_trivial_compat_v = Type <= CtorType::Trivial;
200
201 // Constructors may have to call destructors, so the requirements
202 // combine.
203 template <CtorType Ctor, CtorType Dtor>
204 inline constexpr CtorType ctor_type_v =
205 Ctor == CtorType::Delete ? CtorType::Delete
206 : Ctor > Dtor ? Ctor
207 : Dtor;
208
209 // An empty struct with the given traits.
210 template <
211 CtorType DefaultType = CtorType::Trivial,
212 CtorType CopyType = CtorType::Trivial,
213 CtorType MoveType = CtorType::Trivial,
214 CtorType DestructType = CtorType::Trivial>
215 struct EmptyStruct;
216
217 // A few helpful 'presets'.
218 using Copyable =
219 EmptyStruct<CtorType::Trivial, CtorType::Throw, CtorType::Delete>;
220
221 using Moveable =
222 EmptyStruct<CtorType::Trivial, CtorType::Delete, CtorType::NoThrow>;
223
224 using MoveableThrow =
225 EmptyStruct<CtorType::Trivial, CtorType::Delete, CtorType::Throw>;
226
227 using NonMoveable =
228 EmptyStruct<CtorType::Trivial, CtorType::Delete, CtorType::Delete>;
229
230 using NoDefault = EmptyStruct<CtorType::Delete>;
231
232 using DestructorThrow = EmptyStruct<
233 CtorType::Trivial,
234 CtorType::Trivial,
235 CtorType::Trivial,
236 CtorType::Throw>;
237
238 // An empty struct derived from the given base class.
239 template <typename Base = Copyable>
240 struct Derived : Base {
241 using Base::Base;
242 };
243
244 } // namespace test
245
246 // Statically asserts that T has the required properties.
247 template <typename T, CtorType DefaultType>
staticAssertDefaultConstructible()248 constexpr void staticAssertDefaultConstructible() {
249 sizeof(T); // Fails if the type is not defined.
250 static_assert(
251 std::is_default_constructible_v<T> ==
252 test::is_throw_compat_v<DefaultType>,
253 "");
254 static_assert( // is_nothrow_default_constructible_v
255 std::is_nothrow_default_constructible_v<T> ==
256 test::is_nothrow_compat_v<DefaultType>,
257 "");
258 static_assert( // is_trivially_default_constructible_v
259 std::is_trivially_default_constructible_v<T> ==
260 test::is_trivial_compat_v<DefaultType>,
261 "");
262 }
263
264 // Statically asserts that T has the required properties.
265 template <typename T, CtorType CopyType>
staticAssertCopyConstructible()266 constexpr void staticAssertCopyConstructible() {
267 sizeof(T); // Fails if the type is not defined.
268 static_assert(
269 std::is_copy_constructible_v<T> == test::is_throw_compat_v<CopyType>, "");
270 static_assert( // is_nothrow_copy_constructible_v
271 std::is_nothrow_copy_constructible_v<T> ==
272 test::is_nothrow_compat_v<CopyType>,
273 "");
274 static_assert( // is_trivially_copy_constructible_v
275 std::is_trivially_copy_constructible_v<T> ==
276 test::is_trivial_compat_v<CopyType>,
277 "");
278 }
279
280 // Statically asserts that T has the required properties.
281 template <typename T, CtorType CopyType>
staticAssertCopyAssignable()282 constexpr void staticAssertCopyAssignable() {
283 sizeof(T); // Fails if the type is not defined.
284 static_assert(
285 std::is_copy_assignable_v<T> == test::is_throw_compat_v<CopyType>, "");
286 static_assert( // is_nothrow_copy_assignable_v
287 std::is_nothrow_copy_assignable_v<T> ==
288 test::is_nothrow_compat_v<CopyType>,
289 "");
290 static_assert( // is_trivially_copy_assignable_v
291 std::is_trivially_copy_assignable_v<T> ==
292 test::is_trivial_compat_v<CopyType>,
293 "");
294 }
295
296 // Statically asserts that T has the required properties.
297 template <typename T, CtorType MoveType>
staticAssertMoveConstructible()298 constexpr void staticAssertMoveConstructible() {
299 sizeof(T); // Fails if the type is not defined.
300 static_assert(
301 std::is_move_constructible_v<T> == test::is_throw_compat_v<MoveType>, "");
302 static_assert( // is_nothrow_move_constructible_v
303 std::is_nothrow_move_constructible_v<T> ==
304 test::is_nothrow_compat_v<MoveType>,
305 "");
306 static_assert( // is_trivially_move_constructible_v
307 std::is_trivially_move_constructible_v<T> ==
308 test::is_trivial_compat_v<MoveType>,
309 "");
310 }
311
312 // Statically asserts that T has the required properties.
313 template <typename T, CtorType MoveType>
staticAssertMoveAssignable()314 constexpr void staticAssertMoveAssignable() {
315 sizeof(T); // Fails if the type is not defined.
316 static_assert(
317 std::is_move_assignable_v<T> == test::is_throw_compat_v<MoveType>, "");
318 static_assert( // is_nothrow_move_assignable_v
319 std::is_nothrow_move_assignable_v<T> ==
320 test::is_nothrow_compat_v<MoveType>,
321 "");
322 static_assert( // is_trivially_move_assignable_v
323 std::is_trivially_move_assignable_v<T> ==
324 test::is_trivial_compat_v<MoveType>,
325 "");
326 }
327
328 // Statically asserts that T has the required properties.
329 template <typename T, CtorType DtorType>
staticAssertDestructible()330 constexpr void staticAssertDestructible() {
331 sizeof(T); // Fails if the type is not defined.
332 static_assert(DtorType != CtorType::Delete, "can't delete destructor.");
333 static_assert( // is_nothrow_destructible_v
334 std::is_nothrow_destructible_v<T> == test::is_nothrow_compat_v<DtorType>,
335 "");
336 static_assert( // is_trivially_destructible_v
337 std::is_trivially_destructible_v<T> ==
338 test::is_trivial_compat_v<DtorType>,
339 "");
340 }
341
342 template <CtorType Ctor, CtorType Dtor>
ValidateTestCase()343 constexpr void ValidateTestCase() {
344 static_assert(
345 test::ctor_type_v<Ctor, Dtor> == Ctor,
346 "Bad test: Constructors cannot have lower requirements than destructors.");
347 }
348
349 // Statically asserts that T has the required properties.
350 template <
351 typename T,
352 CtorType DefaultType,
353 CtorType CopyType,
354 CtorType MoveType,
355 CtorType DtorType>
staticAssertConstructible()356 constexpr void staticAssertConstructible() {
357 ValidateTestCase<DefaultType, DtorType>();
358 ValidateTestCase<CopyType, DtorType>();
359 ValidateTestCase<MoveType, DtorType>();
360 staticAssertDefaultConstructible<T, DefaultType>();
361 staticAssertCopyConstructible<T, CopyType>();
362 staticAssertMoveConstructible<T, MoveType>();
363 staticAssertDestructible<T, DtorType>();
364 }
365
366 // Statically asserts that T has the required properties.
367 template <typename T, CtorType CopyType, CtorType MoveType>
staticAssertAssignable()368 constexpr void staticAssertAssignable() {
369 staticAssertCopyAssignable<T, CopyType>();
370 staticAssertMoveAssignable<T, MoveType>();
371 }
372
373 // Statically asserts that T has the required properties.
374 template <
375 typename T,
376 CtorType DefaultType,
377 CtorType CopyType,
378 CtorType MoveType,
379 CtorType DtorType>
staticAssertCtors()380 constexpr void staticAssertCtors() {
381 staticAssertConstructible<T, DefaultType, CopyType, MoveType, DtorType>();
382 staticAssertAssignable<T, CopyType, MoveType>();
383 }
384
385 // Implementation.
386
387 namespace test {
388
389 // Generate all specializtions for EmptyStruct.
390 #define _THRIFT_CON_Delete = delete;
391 #define _THRIFT_CON_Trivial noexcept(true) = default;
392 #define _THRIFT_CON_NoThrow \
393 noexcept(true) {}
394 #define _THRIFT_CON_Throw \
395 noexcept(false) {}
396 #define _THRIFT_ASSIGN_Delete _THRIFT_CON_Delete
397 #define _THRIFT_ASSIGN_Trivial _THRIFT_CON_Trivial
398 #define _THRIFT_ASSIGN_NoThrow \
399 noexcept(true) { return *this; }
400 #define _THRIFT_ASSIGN_Throw \
401 noexcept(false) { return *this; }
402
403 #define _THRIFT_CON(type) _THRIFT_CON_##type
404 #define _THRIFT_ASSIGN(type) _THRIFT_ASSIGN_##type
405
406 // clang-format off
407 #define _THRIFT_INSTANCE(default, dtor, copy, move) \
408 template <> \
409 struct EmptyStruct< \
410 CtorType::default, \
411 CtorType::copy, \
412 CtorType::move, \
413 CtorType::dtor> { \
414 EmptyStruct() _THRIFT_CON(default) \
415 ~EmptyStruct() _THRIFT_CON(dtor) \
416 \
417 EmptyStruct(const EmptyStruct&) _THRIFT_CON(copy) \
418 EmptyStruct& operator=(const EmptyStruct&) _THRIFT_ASSIGN(copy) \
419 \
420 EmptyStruct(EmptyStruct&&) _THRIFT_CON(move) \
421 EmptyStruct& operator=(EmptyStruct&&) _THRIFT_ASSIGN(move) \
422 }
423 // clang-format on
424
425 #define _THRIFT_ALL_MOVE(default, dtor, copy) \
426 _THRIFT_INSTANCE(default, dtor, copy, Delete); \
427 _THRIFT_INSTANCE(default, dtor, copy, Trivial); \
428 _THRIFT_INSTANCE(default, dtor, copy, NoThrow); \
429 _THRIFT_INSTANCE(default, dtor, copy, Throw)
430
431 #define _THRIFT_ALL_COPY(default, dtor) \
432 _THRIFT_ALL_MOVE(default, dtor, Delete); \
433 _THRIFT_ALL_MOVE(default, dtor, Trivial); \
434 _THRIFT_ALL_MOVE(default, dtor, NoThrow); \
435 _THRIFT_ALL_MOVE(default, dtor, Throw)
436
437 #define _THRIFT_ALL(dtor) \
438 _THRIFT_ALL_COPY(Delete, dtor); \
439 _THRIFT_ALL_COPY(Trivial, dtor); \
440 _THRIFT_ALL_COPY(NoThrow, dtor); \
441 _THRIFT_ALL_COPY(Throw, dtor)
442
443 _THRIFT_ALL(NoThrow);
444 _THRIFT_ALL(Trivial);
445 _THRIFT_ALL(Throw);
446
447 #undef _THRIFT_CTOR_Delete
448 #undef _THRIFT_CTOR_Trivial
449 #undef _THRIFT_CTOR_NoThrow
450 #undef _THRIFT_CTOR_Throw
451 #undef _THRIFT_ASSIGN_Delete
452 #undef _THRIFT_ASSIGN_Trivial
453 #undef _THRIFT_ASSIGN_NoThrow
454 #undef _THRIFT_ASSIGN_Throw
455 #undef _THRIFT_CTOR
456 #undef _THRIFT_ASSIGN
457 #undef _THRIFT_INSTANCE
458 #undef _THRIFT_ALL_MOVE
459 #undef _THRIFT_ALL_COPY
460 #undef _THRIFT_ALL
461
462 } // namespace test
463
464 } // namespace apache::thrift::conformance
465