1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #include <string.h>
20 #include <stdlib.h>
21 #include <fstream>
22 #include <sstream>
23 #include <boost/test/included/unit_test_framework.hpp>
24
25 #include "testgen.hh" // < generated header
26 #include "testgen2.hh" // < generated header
27
28 #include "Serializer.hh"
29 #include "Writer.hh"
30 #include "Reader.hh"
31 #include "Node.hh"
32 #include "ValidSchema.hh"
33 #include "Compiler.hh"
34 #include "ResolvingReader.hh"
35 #include "ResolverSchema.hh"
36 #include "buffer/BufferPrint.hh"
37
38 std::string gWriter("jsonschemas/bigrecord");
39 std::string gReader("jsonschemas/bigrecord2");
40
printRecord(testgen::RootRecord & record)41 void printRecord(testgen::RootRecord &record) {
42 using namespace testgen;
43 std::cout << "mylong " << record.mylong << '\n';
44 std::cout << "inval1 " << record.nestedrecord.inval1 << '\n';
45 std::cout << "inval2 " << record.nestedrecord.inval2 << '\n';
46 std::cout << "inval3 " << record.nestedrecord.inval3 << '\n';
47
48 Map_of_int::MapType::const_iterator mapiter = record.mymap.value.begin();
49 while (mapiter != record.mymap.value.end()) {
50 std::cout << "mymap " << mapiter->first << " " << mapiter->second << '\n';
51 ++mapiter;
52 }
53
54 Array_of_double::ArrayType::iterator arrayiter = record.myarray.value.begin();
55 while (arrayiter != record.myarray.value.end()) {
56 std::cout << "myarray " << *arrayiter << '\n';
57 ++arrayiter;
58 }
59
60 std::cout << "myeum = " << record.myenum.value << '\n';
61
62 if (record.myunion.choice == 1) {
63 const Map_of_int &theMap = record.myunion.getValue<Map_of_int>();
64 mapiter = theMap.value.begin();
65 while (mapiter != theMap.value.end()) {
66 std::cout << "unionmap " << mapiter->first << " " << mapiter->second
67 << '\n';
68 ++mapiter;
69 }
70 }
71
72 if (record.anotherunion.choice == 0) {
73 std::cout << "unionbytes ";
74 const std::vector<uint8_t> &val =
75 record.anotherunion.getValue<std::vector<uint8_t> >();
76 for (size_t i = 0; i < val.size(); ++i) {
77 std::cout << i << ":" << static_cast<int>(val[i]) << " ";
78 }
79 std::cout << '\n';
80 }
81
82 std::cout << "mybool " << record.mybool << '\n';
83 std::cout << "inval1 " << record.anothernested.inval1 << '\n';
84 std::cout << "inval2 " << record.anothernested.inval2 << '\n';
85 std::cout << "inval3 " << record.anothernested.inval3 << '\n';
86
87 std::cout << "fixed ";
88 for (size_t i = 0; i < record.myfixed.fixedSize; ++i) {
89 std::cout << i << ":" << static_cast<int>(record.myfixed.value[i]) << " ";
90 }
91 std::cout << '\n';
92
93 std::cout << "anotherint " << record.anotherint << '\n';
94
95 std::cout << "bytes ";
96 for (size_t i = 0; i < record.bytes.size(); ++i) {
97 std::cout << i << ":" << static_cast<int>(record.bytes[i]) << " ";
98 }
99 std::cout << '\n';
100 }
101
printRecord(testgen2::RootRecord & record)102 void printRecord(testgen2::RootRecord &record) {
103 using namespace testgen2;
104 std::cout << "mylong " << record.mylong << '\n';
105 std::cout << "inval1 " << record.nestedrecord.inval1 << '\n';
106 std::cout << "inval2 " << record.nestedrecord.inval2 << '\n';
107 std::cout << "inval3 " << record.nestedrecord.inval3 << '\n';
108
109 Map_of_long::MapType::const_iterator mapiter = record.mymap.value.begin();
110 while (mapiter != record.mymap.value.end()) {
111 std::cout << "mymap " << mapiter->first << " " << mapiter->second << '\n';
112 ++mapiter;
113 }
114
115 Array_of_double::ArrayType::iterator arrayiter = record.myarray.value.begin();
116 while (arrayiter != record.myarray.value.end()) {
117 std::cout << "myarray " << *arrayiter << '\n';
118 ++arrayiter;
119 }
120
121 std::cout << "myeum = " << record.myenum.value << '\n';
122
123 if (record.myunion.choice == 1) {
124 const Map_of_float &theMap = record.myunion.getValue<Map_of_float>();
125 Map_of_float::MapType::const_iterator mapiter = theMap.value.begin();
126 while (mapiter != theMap.value.end()) {
127 std::cout << "unionmap " << mapiter->first << " " << mapiter->second
128 << '\n';
129 ++mapiter;
130 }
131 }
132
133 std::cout << "unionbytes ";
134 const std::vector<uint8_t> &val = record.anotherunion;
135 for (size_t i = 0; i < val.size(); ++i) {
136 std::cout << i << ":" << static_cast<int>(val[i]) << " ";
137 }
138 std::cout << '\n';
139
140 std::cout << "inval1 " << record.anothernested.inval1 << '\n';
141 std::cout << "inval2 " << record.anothernested.inval2 << '\n';
142 std::cout << "inval3 " << record.anothernested.inval3 << '\n';
143
144 if (record.myfixed.choice == 1) {
145 const md5 &myfixed = record.myfixed.getValue<md5>();
146 std::cout << "fixed ";
147 for (size_t i = 0; i < myfixed.fixedSize; ++i) {
148 std::cout << i << ":" << static_cast<int>(myfixed.value[i]) << " ";
149 }
150 std::cout << '\n';
151 }
152
153 std::cout << "anotherint " << record.anotherint << '\n';
154
155 std::cout << "bytes ";
156 for (size_t i = 0; i < record.bytes.size(); ++i) {
157 std::cout << i << ":" << static_cast<int>(record.bytes[i]) << " ";
158 }
159 std::cout << '\n';
160 std::cout << "newbool " << record.newbool << '\n';
161 }
162
setRecord(testgen::RootRecord & myRecord)163 void setRecord(testgen::RootRecord &myRecord) {
164 using namespace testgen;
165
166 uint8_t fixed[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
167
168 myRecord.mylong = 212;
169 myRecord.nestedrecord.inval1 = std::numeric_limits<double>::min();
170 myRecord.nestedrecord.inval2 = "hello world";
171 myRecord.nestedrecord.inval3 = std::numeric_limits<int32_t>::max();
172
173 Map_of_int::GenericSetter setter = myRecord.mymap.genericSetter;
174 Map_of_int::ValueType *val = setter(&myRecord.mymap, "one");
175 *val = 100;
176 val = setter(&myRecord.mymap, "two");
177 *val = 200;
178
179 myRecord.myarray.addValue(3434.9);
180 myRecord.myarray.addValue(7343.9);
181 myRecord.myarray.addValue(-63445.9);
182 myRecord.myenum.value = testgen::ExampleEnum::one;
183 testgen::Map_of_int map;
184 map.addValue("one", 1);
185 map.addValue("two", 2);
186 myRecord.myunion.set_Map_of_int(map);
187 std::vector<uint8_t> vec;
188 vec.push_back(1);
189 vec.push_back(2);
190 myRecord.anotherunion.set_bytes(vec);
191 myRecord.mybool = true;
192 myRecord.anothernested.inval1 = std::numeric_limits<double>::max();
193 myRecord.anothernested.inval2 = "goodbye world";
194 myRecord.anothernested.inval3 = std::numeric_limits<int32_t>::min();
195 memcpy(myRecord.myfixed.value, fixed, testgen::md5::fixedSize);
196 myRecord.anotherint = 4534;
197 myRecord.bytes.push_back(10);
198 myRecord.bytes.push_back(20);
199 }
200
201 struct TestCodeGenerator {
202
serializeToScreenTestCodeGenerator203 void serializeToScreen() {
204 std::cout << "Serialize:\n";
205 internal_avro::Writer writer;
206
207 internal_avro::serialize(writer, myRecord_);
208 std::cout << writer.buffer();
209 std::cout << "end Serialize\n";
210 }
211
serializeToScreenValidTestCodeGenerator212 void serializeToScreenValid() {
213 std::cout << "Validated Serialize:\n";
214 internal_avro::ValidatingWriter writer(schema_);
215
216 internal_avro::serialize(writer, myRecord_);
217 std::cout << writer.buffer();
218 std::cout << "end Validated Serialize\n";
219 }
220
checkArrayTestCodeGenerator221 void checkArray(const testgen::Array_of_double &a1,
222 const testgen::Array_of_double &a2) {
223 BOOST_CHECK_EQUAL(a1.value.size(), 3U);
224 BOOST_CHECK_EQUAL(a1.value.size(), a2.value.size());
225 for (size_t i = 0; i < a1.value.size(); ++i) {
226 BOOST_CHECK_EQUAL(a1.value[i], a2.value[i]);
227 }
228 }
229
checkMapTestCodeGenerator230 void checkMap(const testgen::Map_of_int &map1,
231 const testgen::Map_of_int &map2) {
232 BOOST_CHECK_EQUAL(map1.value.size(), map2.value.size());
233 testgen::Map_of_int::MapType::const_iterator iter1 = map1.value.begin();
234 testgen::Map_of_int::MapType::const_iterator end = map1.value.end();
235 testgen::Map_of_int::MapType::const_iterator iter2 = map2.value.begin();
236
237 while (iter1 != end) {
238 BOOST_CHECK_EQUAL(iter1->first, iter2->first);
239 BOOST_CHECK_EQUAL(iter1->second, iter2->second);
240 ++iter1;
241 ++iter2;
242 }
243 }
244
checkBytesTestCodeGenerator245 void checkBytes(const std::vector<uint8_t> &v1,
246 const std::vector<uint8_t> &v2) {
247 BOOST_CHECK_EQUAL(v1.size(), 2U);
248 BOOST_CHECK_EQUAL(v1.size(), v2.size());
249 for (size_t i = 0; i < v1.size(); ++i) {
250 BOOST_CHECK_EQUAL(v1[i], v2[i]);
251 }
252 }
253
checkNestedTestCodeGenerator254 void checkNested(const testgen::Nested &rec1, const testgen::Nested &rec2) {
255 BOOST_CHECK_EQUAL(rec1.inval1, rec2.inval1);
256 BOOST_CHECK_EQUAL(rec1.inval2, rec2.inval2);
257 BOOST_CHECK_EQUAL(rec1.inval3, rec2.inval3);
258 }
259
checkOkTestCodeGenerator260 void checkOk(const testgen::RootRecord &rec1,
261 const testgen::RootRecord &rec2) {
262 BOOST_CHECK_EQUAL(rec1.mylong, rec1.mylong);
263
264 checkNested(rec1.nestedrecord, rec2.nestedrecord);
265 checkMap(rec1.mymap, rec2.mymap);
266 checkArray(rec1.myarray, rec2.myarray);
267
268 BOOST_CHECK_EQUAL(rec1.myenum.value, rec2.myenum.value);
269
270 BOOST_CHECK_EQUAL(rec1.myunion.choice, rec2.myunion.choice);
271 // in this test I know choice was 1
272 {
273 BOOST_CHECK_EQUAL(rec1.myunion.choice, 1);
274 checkMap(rec1.myunion.getValue<testgen::Map_of_int>(),
275 rec2.myunion.getValue<testgen::Map_of_int>());
276 }
277
278 BOOST_CHECK_EQUAL(rec1.anotherunion.choice, rec2.anotherunion.choice);
279 // in this test I know choice was 0
280 {
281 BOOST_CHECK_EQUAL(rec1.anotherunion.choice, 0);
282 typedef std::vector<uint8_t> mytype;
283 checkBytes(
284 rec1.anotherunion.getValue<mytype>(),
285 rec2.anotherunion.getValue<testgen::Union_of_bytes_null::T0>());
286 }
287
288 checkNested(rec1.anothernested, rec2.anothernested);
289
290 BOOST_CHECK_EQUAL(rec1.mybool, rec2.mybool);
291
292 for (int i = 0; i < static_cast<int>(testgen::md5::fixedSize); ++i) {
293 BOOST_CHECK_EQUAL(rec1.myfixed.value[i], rec2.myfixed.value[i]);
294 }
295 BOOST_CHECK_EQUAL(rec1.anotherint, rec2.anotherint);
296
297 checkBytes(rec1.bytes, rec2.bytes);
298 }
299
testParserTestCodeGenerator300 void testParser() {
301 internal_avro::Writer s;
302
303 internal_avro::serialize(s, myRecord_);
304
305 testgen::RootRecord inRecord;
306 internal_avro::Reader p(s.buffer());
307 internal_avro::parse(p, inRecord);
308
309 checkOk(myRecord_, inRecord);
310 }
311
testParserValidTestCodeGenerator312 void testParserValid() {
313 internal_avro::ValidatingWriter s(schema_);
314
315 internal_avro::serialize(s, myRecord_);
316
317 testgen::RootRecord inRecord;
318 internal_avro::ValidatingReader p(schema_, s.buffer());
319 internal_avro::parse(p, inRecord);
320
321 checkOk(myRecord_, inRecord);
322 }
323
testNameIndexTestCodeGenerator324 void testNameIndex() {
325 const internal_avro::NodePtr &node = schema_.root();
326 size_t index = 0;
327 bool found = node->nameIndex("anothernested", index);
328 BOOST_CHECK_EQUAL(found, true);
329 BOOST_CHECK_EQUAL(index, 8U);
330
331 found = node->nameIndex("myenum", index);
332 BOOST_CHECK_EQUAL(found, true);
333 BOOST_CHECK_EQUAL(index, 4U);
334
335 const internal_avro::NodePtr &enumNode = node->leafAt(index);
336 found = enumNode->nameIndex("one", index);
337 BOOST_CHECK_EQUAL(found, true);
338 BOOST_CHECK_EQUAL(index, 1U);
339 }
340
testTestCodeGenerator341 void test() {
342 std::cout << "Running code generation tests\n";
343
344 testNameIndex();
345
346 serializeToScreen();
347 serializeToScreenValid();
348
349 testParser();
350 testParserValid();
351
352 std::cout << "Finished code generation tests\n";
353 }
354
TestCodeGeneratorTestCodeGenerator355 TestCodeGenerator() {
356 setRecord(myRecord_);
357 std::ifstream in(gWriter.c_str());
358 internal_avro::compileJsonSchema(in, schema_);
359 }
360
361 testgen::RootRecord myRecord_;
362 internal_avro::ValidSchema schema_;
363 };
364
365 struct TestSchemaResolving {
366
checkArrayTestSchemaResolving367 void checkArray(const testgen::Array_of_double &a1,
368 const testgen2::Array_of_double &a2) {
369 BOOST_CHECK_EQUAL(a1.value.size(), 3U);
370 BOOST_CHECK_EQUAL(a1.value.size(), a2.value.size());
371 for (size_t i = 0; i < a1.value.size(); ++i) {
372 BOOST_CHECK_EQUAL(a1.value[i], a2.value[i]);
373 }
374 }
375
checkMapTestSchemaResolving376 void checkMap(const testgen::Map_of_int &map1,
377 const testgen2::Map_of_long &map2) {
378 BOOST_CHECK_EQUAL(map1.value.size(), map2.value.size());
379 testgen::Map_of_int::MapType::const_iterator iter1 = map1.value.begin();
380 testgen::Map_of_int::MapType::const_iterator end = map1.value.end();
381 testgen2::Map_of_long::MapType::const_iterator iter2 = map2.value.begin();
382
383 while (iter1 != end) {
384 BOOST_CHECK_EQUAL(iter1->first, iter2->first);
385 BOOST_CHECK_EQUAL(static_cast<float>(iter1->second), iter2->second);
386 ++iter1;
387 ++iter2;
388 }
389 }
390
checkMapTestSchemaResolving391 void checkMap(const testgen::Map_of_int &map1,
392 const testgen2::Map_of_float &map2) {
393 BOOST_CHECK_EQUAL(map1.value.size(), map2.value.size());
394 testgen::Map_of_int::MapType::const_iterator iter1 = map1.value.begin();
395 testgen::Map_of_int::MapType::const_iterator end = map1.value.end();
396 testgen2::Map_of_float::MapType::const_iterator iter2 = map2.value.begin();
397
398 while (iter1 != end) {
399 BOOST_CHECK_EQUAL(iter1->first, iter2->first);
400 BOOST_CHECK_EQUAL(static_cast<int64_t>(iter1->second), iter2->second);
401 ++iter1;
402 ++iter2;
403 }
404 }
405
checkBytesTestSchemaResolving406 void checkBytes(const std::vector<uint8_t> &v1,
407 const std::vector<uint8_t> &v2) {
408 BOOST_CHECK_EQUAL(v1.size(), 2U);
409 BOOST_CHECK_EQUAL(v1.size(), v2.size());
410 for (size_t i = 0; i < v1.size(); ++i) {
411 BOOST_CHECK_EQUAL(v1[i], v2[i]);
412 }
413 }
414
checkNestedTestSchemaResolving415 void checkNested(const testgen::Nested &rec1, const testgen2::Nested &rec2) {
416 BOOST_CHECK_EQUAL(rec1.inval1, rec2.inval1);
417 BOOST_CHECK_EQUAL(rec1.inval2, rec2.inval2);
418 BOOST_CHECK_EQUAL(rec1.inval3, rec2.inval3);
419 }
420
checkOkTestSchemaResolving421 void checkOk(const testgen::RootRecord &rec1,
422 const testgen2::RootRecord &rec2) {
423 BOOST_CHECK_EQUAL(rec1.mylong, rec1.mylong);
424
425 checkNested(rec1.nestedrecord, rec2.nestedrecord);
426 checkMap(rec1.mymap, rec2.mymap);
427 checkArray(rec1.myarray, rec2.myarray);
428
429 // enum was remapped from 1 to 2
430 BOOST_CHECK_EQUAL(rec1.myenum.value, 1);
431 BOOST_CHECK_EQUAL(rec2.myenum.value, 2);
432
433 // in this test I know choice was 1
434 {
435 BOOST_CHECK_EQUAL(rec1.myunion.choice, 1);
436 BOOST_CHECK_EQUAL(rec2.myunion.choice, 2);
437 checkMap(rec1.myunion.getValue<testgen::Map_of_int>(),
438 rec2.myunion.getValue<testgen2::Map_of_float>());
439 }
440
441 {
442 BOOST_CHECK_EQUAL(rec1.anotherunion.choice, 0);
443 typedef std::vector<uint8_t> mytype;
444 checkBytes(rec1.anotherunion.getValue<mytype>(), rec2.anotherunion);
445 }
446
447 checkNested(rec1.anothernested, rec2.anothernested);
448
449 BOOST_CHECK_EQUAL(rec2.newbool, false);
450
451 BOOST_CHECK_EQUAL(rec2.myfixed.choice, 1);
452 {
453 const testgen2::md5 &myfixed2 = rec2.myfixed.getValue<testgen2::md5>();
454 for (int i = 0; i < static_cast<int>(testgen::md5::fixedSize); ++i) {
455 BOOST_CHECK_EQUAL(rec1.myfixed.value[i], myfixed2.value[i]);
456 }
457 }
458 }
459
serializeWriteRecordToBufferTestSchemaResolving460 internal_avro::InputBuffer serializeWriteRecordToBuffer() {
461 std::ostringstream ostring;
462 internal_avro::Writer s;
463 internal_avro::serialize(s, writeRecord_);
464 return s.buffer();
465 }
466
parseDataTestSchemaResolving467 void parseData(const internal_avro::InputBuffer &buf,
468 internal_avro::ResolverSchema &xSchema) {
469 internal_avro::ResolvingReader r(xSchema, buf);
470
471 internal_avro::parse(r, readRecord_);
472 }
473
testTestSchemaResolving474 void test() {
475 std::cout << "Running schema resolution tests\n";
476 testgen2::RootRecord_Layout layout;
477
478 internal_avro::ResolverSchema xSchema(writerSchema_, readerSchema_, layout);
479
480 printRecord(writeRecord_);
481
482 internal_avro::InputBuffer buffer = serializeWriteRecordToBuffer();
483 parseData(buffer, xSchema);
484
485 printRecord(readRecord_);
486
487 checkOk(writeRecord_, readRecord_);
488 std::cout << "Finished schema resolution tests\n";
489 }
490
TestSchemaResolvingTestSchemaResolving491 TestSchemaResolving() {
492 setRecord(writeRecord_);
493 std::ifstream win(gWriter.c_str());
494 internal_avro::compileJsonSchema(win, writerSchema_);
495
496 std::ifstream rin(gReader.c_str());
497 internal_avro::compileJsonSchema(rin, readerSchema_);
498 }
499
500 testgen::RootRecord writeRecord_;
501 internal_avro::ValidSchema writerSchema_;
502
503 testgen2::RootRecord readRecord_;
504 internal_avro::ValidSchema readerSchema_;
505 };
506
507 template <typename T>
addTestCase(boost::unit_test::test_suite & test)508 void addTestCase(boost::unit_test::test_suite &test) {
509 boost::shared_ptr<T> newtest(new T);
510 test.add(BOOST_CLASS_TEST_CASE(&T::test, newtest));
511 }
512
init_unit_test_suite(int argc,char * argv[])513 boost::unit_test::test_suite *init_unit_test_suite(int argc, char *argv[]) {
514 using namespace boost::unit_test;
515
516 const char *srcPath = getenv("top_srcdir");
517
518 if (srcPath) {
519 std::string srcPathStr(srcPath);
520 gWriter = srcPathStr + '/' + gWriter;
521 gReader = srcPathStr + '/' + gReader;
522 } else {
523 if (argc > 1) {
524 gWriter = argv[1];
525 }
526
527 if (argc > 2) {
528 gReader = argv[2];
529 }
530 }
531 std::cout << "Using writer schema " << gWriter << std::endl;
532 std::cout << "Using reader schema " << gReader << std::endl;
533
534 test_suite *test = BOOST_TEST_SUITE("Avro C++ unit test suite");
535
536 addTestCase<TestCodeGenerator>(*test);
537 addTestCase<TestSchemaResolving>(*test);
538
539 return test;
540 }
541