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