Reflection for RapidJSON  0.0.7
Reflection for serializing/deserializing with RapidJSON
binaryreflector.cpp
Go to the documentation of this file.
1 #include "../binary/reflector-chronoutilities.h"
2 #include "../binary/reflector.h"
3 #include "../binary/serializable.h"
4 
5 #include <c++utilities/conversion/stringbuilder.h>
6 #include <c++utilities/conversion/stringconversion.h>
7 #include <c++utilities/io/misc.h>
8 #include <c++utilities/tests/testutils.h>
9 
10 using TestUtilities::operator<<; // must be visible prior to the call site
11 #include <cppunit/TestFixture.h>
12 #include <cppunit/extensions/HelperMacros.h>
13 
14 #include <iostream>
15 #include <limits>
16 #include <map>
17 #include <sstream>
18 #include <string>
19 #include <tuple>
20 #include <unordered_map>
21 #include <vector>
22 
23 using namespace std;
24 using namespace CPPUNIT_NS;
25 using namespace IoUtilities;
26 using namespace ChronoUtilities;
27 using namespace ConversionUtilities;
28 using namespace TestUtilities;
29 using namespace TestUtilities::Literals;
30 using namespace ReflectiveRapidJSON;
31 
33 
34 // define some enums and structs for testing serialization
35 enum SomeEnum {
36  SomeEnumItem1,
37  SomeEnumItem2,
38  SomeEnumItem3,
39 };
40 
41 enum class SomeEnumClass : uint16 {
42  Item1,
43  Item2,
44  Item3,
45 };
46 
47 struct TestObjectBinary : public BinarySerializable<TestObjectBinary> {
48  int number;
49  double number2;
50  vector<int> numbers;
51  string text;
52  bool boolean;
53  map<string, int> someMap;
54  unordered_map<string, bool> someHash;
55  set<string> someSet;
56  multiset<string> someMultiset;
57  unordered_set<string> someUnorderedSet;
58  unordered_multiset<string> someUnorderedMultiset;
59  SomeEnum someEnum;
60  SomeEnumClass someEnumClass;
61  TimeSpan timeSpan;
62  DateTime dateTime;
63 };
64 
65 struct NestingArrayBinary : public BinarySerializable<NestingArrayBinary> {
66  string name;
67  vector<TestObjectBinary> testObjects;
68 };
69 
70 // pretend serialization code for structs has been generated
71 namespace ReflectiveRapidJSON {
72 namespace BinaryReflector {
73 
74 template <> void readCustomType<TestObjectBinary>(BinaryDeserializer &deserializer, TestObjectBinary &customType)
75 {
76  deserializer.read(customType.number);
77  deserializer.read(customType.number2);
78  deserializer.read(customType.numbers);
79  deserializer.read(customType.text);
80  deserializer.read(customType.boolean);
81  deserializer.read(customType.someMap);
82  deserializer.read(customType.someHash);
83  deserializer.read(customType.someSet);
84  deserializer.read(customType.someMultiset);
85  deserializer.read(customType.someUnorderedSet);
86  deserializer.read(customType.someUnorderedMultiset);
87  deserializer.read(customType.someEnum);
88  deserializer.read(customType.someEnumClass);
89  deserializer.read(customType.timeSpan);
90  deserializer.read(customType.dateTime);
91 }
92 
93 template <> void writeCustomType<TestObjectBinary>(BinarySerializer &serializer, const TestObjectBinary &customType)
94 {
95  serializer.write(customType.number);
96  serializer.write(customType.number2);
97  serializer.write(customType.numbers);
98  serializer.write(customType.text);
99  serializer.write(customType.boolean);
100  serializer.write(customType.someMap);
101  serializer.write(customType.someHash);
102  serializer.write(customType.someSet);
103  serializer.write(customType.someMultiset);
104  serializer.write(customType.someUnorderedSet);
105  serializer.write(customType.someUnorderedMultiset);
106  serializer.write(customType.someEnum);
107  serializer.write(customType.someEnumClass);
108  serializer.write(customType.timeSpan);
109  serializer.write(customType.dateTime);
110 }
111 
112 template <> void readCustomType<NestingArrayBinary>(BinaryDeserializer &deserializer, NestingArrayBinary &customType)
113 {
114  deserializer.read(customType.name);
115  deserializer.read(customType.testObjects);
116 }
117 
118 template <> void writeCustomType<NestingArrayBinary>(BinarySerializer &serializer, const NestingArrayBinary &customType)
119 {
120  serializer.write(customType.name);
121  serializer.write(customType.testObjects);
122 }
123 
124 } // namespace BinaryReflector
125 
126 // namespace BinaryReflector
127 } // namespace ReflectiveRapidJSON
128 
130 
135 class BinaryReflectorTests : public TestFixture {
136  CPPUNIT_TEST_SUITE(BinaryReflectorTests);
137  CPPUNIT_TEST(testSerializeSimpleStruct);
138  CPPUNIT_TEST(testDeserializeSimpleStruct);
139  CPPUNIT_TEST(testSerializeNestedStruct);
140  CPPUNIT_TEST(testDeserializeNestedStruct);
141  CPPUNIT_TEST(testSmallSharedPointer);
142  CPPUNIT_TEST(testBigSharedPointer);
143  CPPUNIT_TEST_SUITE_END();
144 
145 public:
147 
148  void setUp();
149  void tearDown();
150 
151  void testSerializeSimpleStruct();
152  void testDeserializeSimpleStruct();
153  void testSerializeNestedStruct();
154  void testDeserializeNestedStruct();
155  void assertTestObject(const TestObjectBinary &deserialized);
156  void testSharedPointer(std::uintptr_t fakePointer);
157  void testSmallSharedPointer();
158  void testBigSharedPointer();
159 
160 private:
161  vector<unsigned char> m_buffer;
162  TestObjectBinary m_testObj;
163  NestingArrayBinary m_nestedTestObj;
164  vector<unsigned char> m_expectedTestObj;
165  vector<unsigned char> m_expectedNestedTestObj;
166 };
167 
169 
170 // clang-format off
172  : m_buffer()
173  , m_testObj()
174  , m_nestedTestObj()
175  , m_expectedTestObj({
176  0x00, 0x00, 0x00, 0x05,
177  0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
178  0x85,
179  0x00, 0x00, 0x00, 0x01,
180  0x00, 0x00, 0x00, 0x02,
181  0x00, 0x00, 0x00, 0x03,
182  0x00, 0x00, 0x00, 0x04,
183  0x00, 0x00, 0x00, 0x05,
184  0x89,
185  0x73, 0x6F, 0x6D, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74,
186  0x01,
187  0x82,
188  0x83, 0x62, 0x61, 0x72, 0x00, 0x00, 0x00, 0x13,
189  0x83, 0x66, 0x6f, 0x6f, 0x00, 0x00, 0x00, 0x11,
190  0x80,
191  0x83,
192  0x81, 0x31,
193  0x81, 0x32,
194  0x81, 0x33,
195  0x84,
196  0x81, 0x31,
197  0x81, 0x32,
198  0x81, 0x32,
199  0x81, 0x33,
200  0x80,
201  0x80,
202  0x00, 0x00, 0x00, 0x01,
203  0x00, 0x02,
204  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xCD,
205  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xAB,
206  })
207  , m_expectedNestedTestObj({
208  0x93, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67,
209  0x82,
210  })
211 {
212 }
213 // clang-format on
214 
216 {
217  m_testObj.number = 5;
218  m_testObj.number2 = 2.5;
219  m_testObj.numbers = { 1, 2, 3, 4, 5 };
220  m_testObj.text = "some text";
221  m_testObj.boolean = true;
222  m_testObj.someMap = {
223  { "foo", 17 },
224  { "bar", 19 },
225  };
226  m_testObj.someSet = { "1", "2", "3", "2" };
227  m_testObj.someMultiset = { "1", "2", "3", "2" };
228  m_testObj.someEnum = SomeEnumItem2;
229  m_testObj.someEnumClass = SomeEnumClass::Item3;
230  m_testObj.timeSpan = TimeSpan(0xABCD);
231  m_testObj.dateTime = DateTime(0xEFAB);
232  m_nestedTestObj.name = "struct with nesting";
233  m_expectedNestedTestObj.reserve(m_expectedNestedTestObj.size() + 2 * m_expectedTestObj.size());
234  m_expectedNestedTestObj.insert(m_expectedNestedTestObj.end(), m_expectedTestObj.cbegin(), m_expectedTestObj.cend());
235  m_expectedNestedTestObj.insert(m_expectedNestedTestObj.end(), m_expectedTestObj.cbegin(), m_expectedTestObj.cend());
236  m_nestedTestObj.testObjects.insert(m_nestedTestObj.testObjects.end(), 2, m_testObj);
237 }
238 
240 {
241 }
242 
244 {
245  stringstream stream(ios_base::out | ios_base::binary);
246  stream.exceptions(ios_base::failbit | ios_base::badbit);
247  m_buffer.resize(m_expectedTestObj.size());
248  stream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(m_buffer.data()), static_cast<streamsize>(m_buffer.size()));
249  m_testObj.toBinary(stream);
250 
251  CPPUNIT_ASSERT_EQUAL(m_expectedTestObj, m_buffer);
252 }
253 
255 {
256  stringstream stream(ios_base::in | ios_base::binary);
257  stream.exceptions(ios_base::failbit | ios_base::badbit);
258  stream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(m_expectedTestObj.data()), static_cast<streamsize>(m_expectedTestObj.size()));
259  const auto deserialized(TestObjectBinary::fromBinary(stream));
260  assertTestObject(deserialized);
261 }
262 
264 {
265  stringstream stream(ios_base::out | ios_base::binary);
266  stream.exceptions(ios_base::failbit | ios_base::badbit);
267  m_buffer.resize(m_expectedNestedTestObj.size());
268  stream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(m_buffer.data()), static_cast<streamsize>(m_buffer.size()));
269  m_nestedTestObj.toBinary(stream);
270 
271  CPPUNIT_ASSERT_EQUAL(m_expectedNestedTestObj, m_buffer);
272 }
273 
275 {
276  stringstream stream(ios_base::in | ios_base::binary);
277  stream.exceptions(ios_base::failbit | ios_base::badbit);
278  stream.rdbuf()->pubsetbuf(reinterpret_cast<char *>(m_expectedNestedTestObj.data()), static_cast<streamsize>(m_expectedNestedTestObj.size()));
279 
280  const auto deserialized(NestingArrayBinary::fromBinary(stream));
281  CPPUNIT_ASSERT_EQUAL(m_nestedTestObj.name, deserialized.name);
282  for (const auto &testObj : deserialized.testObjects) {
283  assertTestObject(testObj);
284  }
285 }
286 
287 void BinaryReflectorTests::assertTestObject(const TestObjectBinary &deserialized)
288 {
289  CPPUNIT_ASSERT_EQUAL(m_testObj.number, deserialized.number);
290  CPPUNIT_ASSERT_EQUAL(m_testObj.number2, deserialized.number2);
291  CPPUNIT_ASSERT_EQUAL(m_testObj.numbers, deserialized.numbers);
292  CPPUNIT_ASSERT_EQUAL(m_testObj.text, deserialized.text);
293  CPPUNIT_ASSERT_EQUAL(m_testObj.boolean, deserialized.boolean);
294  CPPUNIT_ASSERT_EQUAL(m_testObj.someMap, deserialized.someMap);
295  CPPUNIT_ASSERT_EQUAL(m_testObj.someHash, deserialized.someHash);
296  CPPUNIT_ASSERT_EQUAL(m_testObj.someSet, deserialized.someSet);
297  CPPUNIT_ASSERT_EQUAL(m_testObj.someMultiset, deserialized.someMultiset);
298  CPPUNIT_ASSERT_EQUAL(m_testObj.someUnorderedSet, deserialized.someUnorderedSet);
299  CPPUNIT_ASSERT_EQUAL(m_testObj.someUnorderedMultiset, deserialized.someUnorderedMultiset);
300 }
301 
302 void BinaryReflectorTests::testSharedPointer(uintptr_t fakePointer)
303 {
304  // create a shared pointer for the fake pointer ensuring that it is not actually deleted
305  shared_ptr<int> sharedPointer(reinterpret_cast<int *>(fakePointer), [](int *) {});
306 
307  // setup stream
308  stringstream stream(ios_base::in | ios_base::out | ios_base::binary);
309  stream.exceptions(ios_base::failbit | ios_base::badbit);
310 
311  // serialize the shared pointer assuming its contents have been written before (to prevent actually dereferencing it)
312  BinaryReflector::BinarySerializer serializer(&stream);
313  serializer.m_pointer[fakePointer] = true;
314  serializer.write(sharedPointer);
315 
316  // deserialize the shared pointer assuming it has already been read and the type does not match
317  BinaryReflector::BinaryDeserializer deserializer(&stream);
318  shared_ptr<int> readPtr;
319  deserializer.m_pointer[fakePointer] = "foo";
320  CPPUNIT_ASSERT_THROW(deserializer.read(readPtr), ConversionUtilities::ConversionException);
321  CPPUNIT_ASSERT(readPtr == nullptr);
322 
323  // deserialize the shared pointer assuming it has already been read and the type matches
324  stream.seekg(0);
325  deserializer.m_pointer[fakePointer] = make_shared<int>(42);
326  deserializer.read(readPtr);
327  CPPUNIT_ASSERT(readPtr != nullptr);
328  CPPUNIT_ASSERT_EQUAL(42, *readPtr);
329 }
330 
332 {
333  testSharedPointer(std::numeric_limits<std::uintptr_t>::min() + 1);
334 }
335 
337 {
338  testSharedPointer(std::numeric_limits<std::uintptr_t>::max());
339 }
void testSharedPointer(std::uintptr_t fakePointer)
CPPUNIT_TEST_SUITE_REGISTRATION(BinaryReflectorTests)
std::unordered_map< uint64, bool > m_pointer
Definition: reflector.h:84
void assertTestObject(const TestObjectBinary &deserialized)
The BinaryReflectorTests class tests the (de)serializer.
std::unordered_map< uint64, std::any > m_pointer
Definition: reflector.h:69
The BinarySerializable class provides the CRTP-base for (de)serializable objects.
Definition: reflector.h:33