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