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