Added: avro/trunk/lang/c++/test/CodecTests.cc URL: http://svn.apache.org/viewvc/avro/trunk/lang/c%2B%2B/test/CodecTests.cc?rev=1072677&view=auto ============================================================================== --- avro/trunk/lang/c++/test/CodecTests.cc (added) +++ avro/trunk/lang/c++/test/CodecTests.cc Sun Feb 20 18:23:18 2011 @@ -0,0 +1,1279 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <iostream> + +#include "Encoder.hh" +#include "Decoder.hh" +#include "Compiler.hh" +#include "ValidSchema.hh" + +#include <stdint.h> +#include <vector> +#include <stack> +#include <string> +#include <functional> +#include <boost/bind.hpp> + +#include <boost/test/included/unit_test_framework.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/test/parameterized_test.hpp> +#include <boost/random/mersenne_twister.hpp> + +namespace avro { +namespace parsing { + +static const unsigned int count = 10; + +/** + * A bunch of tests that share quite a lot of infrastructure between them. + * The basic idea is to generate avro data for according to a schema and + * then read back and compare the data with the original. But quite a few + * variations are possible: + * 1. While reading back, one can skip different data elements + * 2. While reading resolve against a reader's schema. The resolver may + * promote data type, convert from union to plain data type and vice versa, + * insert or remove fields in records or reorder fields in a record. + * + * To test Json encoder and decoder, we use the same technqiue with only + * one difference - we use JsonEncoder and JsonDecoder. + * + * For most tests, the data is generated at random. + */ + +using std::string; +using std::vector; +using std::stack; +using std::pair; +using std::make_pair; +using std::istringstream; +using std::ostringstream; +using std::back_inserter; +using std::copy; +using std::auto_ptr; + +template <typename T> +T from_string(const std::string& s) +{ + istringstream iss(s); + T result; + iss >> result; + return result; +} + +template <> +vector<uint8_t> from_string(const std::string& s) +{ + vector<uint8_t> result; + result.reserve(s.size()); + copy(s.begin(), s.end(), back_inserter(result)); + return result; +} + +template <typename T> +std::string to_string(const T& t) +{ + ostringstream oss; + oss << t; + return oss.str(); +} + +template <> +std::string to_string(const vector<uint8_t>& t) +{ + string result; + copy(t.begin(), t.end(), back_inserter(result)); + return result; +} + +class Scanner { + const char *p; + const char * const end; +public: + Scanner(const char* calls) : p(calls), end(calls + strlen(calls)) { } + Scanner(const char* calls, size_t len) : p(calls), end(calls + len) { } + char advance() { + return *p++; + } + + int extractInt() { + int result = 0; + while (p < end) { + if (isdigit(*p)) { + result *= 10; + result += *p++ - '0'; + } else { + break; + } + } + return result; + } + + bool isDone() const { return p == end; } +}; + +boost::mt19937 rnd; + +static string randomString(size_t len) +{ + std::string result; + result.reserve(len + 1); + for (size_t i = 0; i < len; ++i) { + char c = static_cast<char>(rnd()) & 0x7f; + if (c == '\0') { + c = '\x7f'; + } + result.push_back(c); + } + return result; +} + +static vector<uint8_t> randomBytes(size_t len) +{ + vector<uint8_t> result; + result.reserve(len); + for (size_t i = 0; i < len; ++i) { + result.push_back(rnd()); + } + return result; +} + +static vector<string> randomValues(const char* calls) +{ + Scanner sc(calls); + vector<string> result; + while (! sc.isDone()) { + char c = sc.advance(); + switch (c) { + case 'B': + result.push_back(to_string(rnd() % 2 == 0)); + break; + case 'I': + result.push_back(to_string(static_cast<int32_t>(rnd()))); + break; + case 'L': + result.push_back(to_string(rnd() | static_cast<int64_t>(rnd()) << 32)); + break; + case 'F': + result.push_back( + to_string(static_cast<float>(rnd()) / static_cast<float>(rnd()))); + break; + case 'D': + result.push_back( + to_string(static_cast<double>(rnd()) / static_cast<double>(rnd()))); + break; + case 'S': + case 'K': + result.push_back(to_string(randomString(sc.extractInt()))); + break; + case 'b': + case 'f': + result.push_back(to_string(randomBytes(sc.extractInt()))); + break; + case 'e': + case 'c': + case 'U': + sc.extractInt(); + break; + case 'N': + case '[': + case ']': + case '{': + case '}': + case 's': + break; + default: + BOOST_FAIL("Unknown mnemonic: " << c); + } + } + return result; +} + +static auto_ptr<OutputStream> generate(Encoder& e, const char* calls, + const vector<string>& values) +{ + Scanner sc(calls); + vector<string>::const_iterator it = values.begin(); + auto_ptr<OutputStream> ob = memoryOutputStream(); + e.init(*ob); + + while (! sc.isDone()) { + char c = sc.advance(); + + switch (c) { + case 'N': + e.encodeNull(); + break; + case 'B': + e.encodeBool(from_string<bool>(*it++)); + break; + case 'I': + e.encodeInt(from_string<int32_t>(*it++)); + break; + case 'L': + e.encodeLong(from_string<int64_t>(*it++)); + break; + case 'F': + e.encodeFloat(from_string<float>(*it++)); + break; + case 'D': + e.encodeDouble(from_string<double>(*it++)); + break; + case 'S': + case 'K': + sc.extractInt(); + e.encodeString(from_string<string>(*it++)); + break; + case 'b': + sc.extractInt(); + e.encodeBytes(from_string<vector<uint8_t> >(*it++)); + break; + case 'f': + sc.extractInt(); + e.encodeFixed(from_string<vector<uint8_t> >(*it++)); + break; + case 'e': + e.encodeEnum(sc.extractInt()); + break; + case '[': + e.arrayStart(); + break; + case ']': + e.arrayEnd(); + break; + case '{': + e.mapStart(); + break; + case '}': + e.mapEnd(); + break; + case 'c': + e.setItemCount(sc.extractInt()); + break; + case 's': + e.startItem(); + break; + case 'U': + e.encodeUnionIndex(sc.extractInt()); + break; + default: + BOOST_FAIL("Unknown mnemonic: " << c); + } + } + e.flush(); + return ob; +} + +namespace { +struct StackElement { + size_t size; + size_t count; + bool isArray; + StackElement(size_t s, bool a) : size(s), count(0), isArray(a) { } +}; +} + +static vector<string>::const_iterator skipCalls(Scanner& sc, Decoder& d, + vector<string>::const_iterator it, bool isArray) +{ + char end = isArray ? ']' : '}'; + int level = 0; + while (! sc.isDone()) { + char c = sc.advance(); + switch (c) { + case '[': + case '{': + ++level; + break; + case ']': + case '}': + if (c == end && level == 0) { + return it; + } + --level; + break; + case 'B': + case 'I': + case 'L': + case 'F': + case 'D': + ++it; + break; + case 'S': + case 'K': + case 'b': + case 'f': + case 'e': + ++it; // Fall through. + case 'c': + case 'U': + sc.extractInt(); + break; + case 's': + case 'N': + break; + default: + BOOST_FAIL("Don't know how to skip: " << c); + } + } + BOOST_FAIL("End reached while trying to skip"); +} + +static void check(Decoder& d, unsigned int skipLevel, + const char* calls, const vector<string>& values) +{ + Scanner sc(calls); + stack<StackElement> containerStack; + vector<string>::const_iterator it = values.begin(); + while (! sc.isDone()) { + char c = sc.advance(); + switch (c) { + case 'N': + d.decodeNull(); + break; + case 'B': + { + bool b1 = d.decodeBool(); + bool b2 = from_string<bool>(*it++); + BOOST_CHECK_EQUAL(b1, b2); + } + break; + case 'I': + { + int32_t b1 = d.decodeInt(); + int32_t b2 = from_string<int32_t>(*it++); + BOOST_CHECK_EQUAL(b1, b2); + } + break; + case 'L': + { + int64_t b1 = d.decodeLong(); + int64_t b2 = from_string<int64_t>(*it++); + BOOST_CHECK_EQUAL(b1, b2); + } + break; + case 'F': + { + float b1 = d.decodeFloat(); + float b2 = from_string<float>(*it++); + BOOST_CHECK_CLOSE(b1, b2, 0.001); + } + break; + case 'D': + { + double b1 = d.decodeDouble(); + double b2 = from_string<double>(*it++); + BOOST_CHECK_CLOSE(b1, b2, 0.001); + } + break; + case 'S': + case 'K': + sc.extractInt(); + if (containerStack.size() >= skipLevel) { + d.skipString(); + } else { + string b1 = d.decodeString(); + string b2 = from_string<string>(*it); + BOOST_CHECK_EQUAL(b1, b2); + } + ++it; + break; + case 'b': + sc.extractInt(); + if (containerStack.size() >= skipLevel) { + d.skipBytes(); + } else { + vector<uint8_t> b1 = d.decodeBytes(); + vector<uint8_t> b2 = from_string<vector<uint8_t> >(*it); + BOOST_CHECK_EQUAL_COLLECTIONS(b1.begin(), b1.end(), + b2.begin(), b2.end()); + } + ++it; + break; + case 'f': + { + size_t len = sc.extractInt(); + if (containerStack.size() >= skipLevel) { + d.skipFixed(len); + } else { + vector<uint8_t> b1 = d.decodeFixed(len); + vector<uint8_t> b2 = from_string<vector<uint8_t> >(*it); + BOOST_CHECK_EQUAL_COLLECTIONS(b1.begin(), b1.end(), + b2.begin(), b2.end()); + } + } + ++it; + break; + case 'e': + { + size_t b1 = d.decodeEnum(); + size_t b2 = sc.extractInt(); + BOOST_CHECK_EQUAL(b1, b2); + } + break; + case '[': + if (containerStack.size() >= skipLevel) { + size_t n = d.skipArray(); + if (n == 0) { + it = skipCalls(sc, d, it, true); + } else { + containerStack.push(StackElement(n, true)); + } + } else { + containerStack.push(StackElement(d.arrayStart(), true)); + } + break; + case '{': + if (containerStack.size() >= skipLevel) { + size_t n = d.skipMap(); + if (n == 0) { + it = skipCalls(sc, d, it, false); + } else { + containerStack.push(StackElement(n, false)); + } + } else { + containerStack.push(StackElement(d.mapStart(), false)); + } + break; + case ']': + { + const StackElement& se = containerStack.top(); + BOOST_CHECK_EQUAL(se.size, se.count); + if (se.size != 0) { + BOOST_CHECK_EQUAL(0, d.arrayNext()); + } + containerStack.pop(); + } + break; + case '}': + { + const StackElement& se = containerStack.top(); + BOOST_CHECK_EQUAL(se.size, se.count); + if (se.size != 0) { + BOOST_CHECK_EQUAL(0, d.mapNext()); + } + containerStack.pop(); + } + break; + case 's': + { + StackElement& se = containerStack.top(); + if (se.size == se.count) { + se.size += (se.isArray ? + d.arrayNext() : d.mapNext()); + } + ++se.count; + } + break; + case 'c': + sc.extractInt(); + break; + case 'U': + { + size_t idx = sc.extractInt(); + BOOST_CHECK_EQUAL(idx, d.decodeUnionIndex()); + } + break; + case 'R': + static_cast<ResolvingDecoder&>(d).fieldOrder(); + continue; + default: + BOOST_FAIL("Unknown mnemonic: " << c); + } + } + BOOST_CHECK(it == values.end()); +} + +ValidSchema makeValidSchema(const char* schema) +{ + istringstream iss(schema); + ValidSchema vs; + compileJsonSchema(iss, vs); + return ValidSchema(vs); +} + +void testEncoder(const EncoderPtr& e, const char* writerCalls, + vector<string>& v, auto_ptr<OutputStream>& p) +{ + v = randomValues(writerCalls); + p = generate(*e, writerCalls, v); +} + +static void testDecoder(const DecoderPtr& d, + const vector<string>& values, InputStream& data, + const char* readerCalls, unsigned int skipLevel) +{ + d->init(data); + check(*d, skipLevel, readerCalls, values); +} + +/** + * The first member is a schema. + * The second one is a sequence of (single character) mnemonics: + * N null + * B boolean + * I int + * L long + * F float + * D double + * K followed by integer - key-name (and its length) in a map + * S followed by integer - string and its length + * b followed by integer - bytes and length + * f followed by integer - fixed and length + * c Number of items to follow in an array/map. + * U followed by integer - Union and its branch + * e followed by integer - Enum and its value + * [ Start array + * ] End array + * { Start map + * } End map + * s start item + * R Start of record in resolving situations. Client may call fieldOrder() + */ + +struct TestData { + const char* schema; + const char* calls; + unsigned int depth; +}; + +struct TestData2 { + const char* schema; + const char* correctCalls; + const char* incorrectCalls; + unsigned int depth; +}; + +struct TestData3 { + const char* writerSchema; + const char* writerCalls; + const char* readerSchema; + const char* readerCalls; + unsigned int depth; +}; + +struct TestData4 { + const char* writerSchema; + const char* writerCalls; + const char* writerValues[100]; + const char* readerSchema; + const char* readerCalls; + const char* readerValues[100]; + unsigned int depth; +}; + +/* +static void dump(const OutputStream& os) +{ + std::auto_ptr<InputStream> in = memoryInputStream(os); + const char *b; + size_t n; + std::cout << os.byteCount() << std::endl; + while (in->next(reinterpret_cast<const uint8_t**>(&b), &n)) { + std::cout << std::string(b, n); + } + std::cout << std::endl; +} +*/ + +template<typename CodecFactory> +void testCodec(const TestData& td) { + static int testNo = 0; + testNo++; + + ValidSchema vs = makeValidSchema(td.schema); + + for (unsigned int i = 0; i < count; ++i) { + vector<string> v; + auto_ptr<OutputStream> p; + testEncoder(CodecFactory::newEncoder(vs), td.calls, v, p); + // dump(*p); + + for (unsigned int i = 0; i <= td.depth; ++i) { + unsigned int skipLevel = td.depth - i; + /* + std::cout << "Test: " << testNo << ' ' + << " schema: " << td.schema + << " calls: " << td.calls + << " skip-level: " << skipLevel << std::endl; + */ + BOOST_TEST_CHECKPOINT("Test: " << testNo << ' ' + << " schema: " << td.schema + << " calls: " << td.calls + << " skip-level: " << skipLevel); + auto_ptr<InputStream> in = memoryInputStream(*p); + testDecoder(CodecFactory::newDecoder(vs), v, *in, + td.calls, skipLevel); + } + } +} + +template<typename CodecFactory> +void testCodecResolving(const TestData3& td) { + static int testNo = 0; + testNo++; + + BOOST_TEST_CHECKPOINT("Test: " << testNo << ' ' + << " writer schema: " << td.writerSchema + << " writer calls: " << td.writerCalls + << " reader schema: " << td.readerSchema + << " reader calls: " << td.readerCalls); + + ValidSchema vs = makeValidSchema(td.writerSchema); + + for (unsigned int i = 0; i < count; ++i) { + vector<string> v; + auto_ptr<OutputStream> p; + testEncoder(CodecFactory::newEncoder(vs), td.writerCalls, v, p); + // dump(*p); + + ValidSchema rvs = makeValidSchema(td.readerSchema); + for (unsigned int i = 0; i <= td.depth; ++i) { + unsigned int skipLevel = td.depth - i; + BOOST_TEST_CHECKPOINT("Test: " << testNo << ' ' + << " writer schema: " << td.writerSchema + << " writer calls: " << td.writerCalls + << " reader schema: " << td.readerSchema + << " reader calls: " << td.readerCalls + << " skip-level: " << skipLevel); + auto_ptr<InputStream> in = memoryInputStream(*p); + testDecoder(CodecFactory::newDecoder(vs, rvs), v, *in, + td.readerCalls, skipLevel); + } + } +} + +static vector<string> mkValues(const char* const values[]) +{ + vector<string> result; + for (const char* const* p = values; *p; ++p) { + result.push_back(*p); + } + return result; +} + +template<typename CodecFactory> +void testCodecResolving2(const TestData4& td) { + static int testNo = 0; + testNo++; + + BOOST_TEST_CHECKPOINT("Test: " << testNo << ' ' + << " writer schema: " << td.writerSchema + << " writer calls: " << td.writerCalls + << " reader schema: " << td.readerSchema + << " reader calls: " << td.readerCalls); + + ValidSchema vs = makeValidSchema(td.writerSchema); + + vector<string> wd = mkValues(td.writerValues); + auto_ptr<OutputStream> p = + generate(*CodecFactory::newEncoder(vs), td.writerCalls, wd); + // dump(*p); + + ValidSchema rvs = makeValidSchema(td.readerSchema); + vector<string> rd = mkValues(td.readerValues); + for (unsigned int i = 0; i <= td.depth; ++i) { + unsigned int skipLevel = td.depth - i; + BOOST_TEST_CHECKPOINT("Test: " << testNo << ' ' + << " writer schema: " << td.writerSchema + << " writer calls: " << td.writerCalls + << " reader schema: " << td.readerSchema + << " reader calls: " << td.readerCalls + << " skip-level: " << skipLevel); + auto_ptr<InputStream> in = memoryInputStream(*p); + testDecoder(CodecFactory::newDecoder(vs, rvs), rd, *in, + td.readerCalls, skipLevel); + } +} + +template<typename CodecFactory> +void testReaderFail(const TestData2& td) { + static int testNo = 0; + testNo++; + BOOST_TEST_CHECKPOINT("Test: " << testNo << ' ' + << " schema: " << td.schema + << " correctCalls: " << td.correctCalls + << " incorrectCalls: " << td.incorrectCalls + << " skip-level: " << td.depth); + ValidSchema vs = makeValidSchema(td.schema); + + vector<string> v; + auto_ptr<OutputStream> p; + testEncoder(CodecFactory::newEncoder(vs), td.correctCalls, v, p); + auto_ptr<InputStream> in = memoryInputStream(*p); + BOOST_CHECK_THROW( + testDecoder(CodecFactory::newDecoder(vs), v, *in, + td.incorrectCalls, td.depth), Exception); +} + +template<typename CodecFactory> +void testWriterFail(const TestData2& td) { + static int testNo = 0; + testNo++; + BOOST_TEST_CHECKPOINT("Test: " << testNo << ' ' + << " schema: " << td.schema + << " incorrectCalls: " << td.incorrectCalls); + ValidSchema vs = makeValidSchema(td.schema); + + vector<string> v; + auto_ptr<OutputStream> p; + BOOST_CHECK_THROW(testEncoder(CodecFactory::newEncoder(vs), + td.incorrectCalls, v, p), Exception); +} + +static const TestData data[] = { + { "\"null\"", "N", 1 }, + { "\"boolean\"", "B", 1 }, + { "\"int\"", "I", 1 }, + { "\"long\"", "L", 1 }, + { "\"float\"", "F", 1 }, + { "\"double\"", "D", 1 }, + { "\"string\"", "S0", 1 }, + { "\"string\"", "S10", 1 }, + { "\"bytes\"", "b0", 1 }, + { "\"bytes\"", "b10", 1 }, + + { "{\"type\":\"fixed\", \"name\":\"fi\", \"size\": 1}", "f1", 1 }, + { "{\"type\":\"fixed\", \"name\":\"fi\", \"size\": 10}", "f10", 1 }, + { "{\"type\":\"enum\", \"name\":\"en\", \"symbols\":[\"v1\", \"v2\"]}", + "e1", 1 }, + + { "{\"type\":\"array\", \"items\": \"boolean\"}", "[]", 2 }, + { "{\"type\":\"array\", \"items\": \"int\"}", "[]", 2 }, + { "{\"type\":\"array\", \"items\": \"long\"}", "[]", 2 }, + { "{\"type\":\"array\", \"items\": \"float\"}", "[]", 2 }, + { "{\"type\":\"array\", \"items\": \"double\"}", "[]", 2 }, + { "{\"type\":\"array\", \"items\": \"string\"}", "[]", 2 }, + { "{\"type\":\"array\", \"items\": \"bytes\"}", "[]", 2 }, + { "{\"type\":\"array\", \"items\":{\"type\":\"fixed\", " + "\"name\":\"fi\", \"size\": 10}}", "[]", 2 }, + + { "{\"type\":\"array\", \"items\": \"boolean\"}", "[c1sB]", 2 }, + { "{\"type\":\"array\", \"items\": \"int\"}", "[c1sI]", 2 }, + { "{\"type\":\"array\", \"items\": \"long\"}", "[c1sL]", 2 }, + { "{\"type\":\"array\", \"items\": \"float\"}", "[c1sF]", 2 }, + { "{\"type\":\"array\", \"items\": \"double\"}", "[c1sD]", 2 }, + { "{\"type\":\"array\", \"items\": \"string\"}", "[c1sS10]", 2 }, + { "{\"type\":\"array\", \"items\": \"bytes\"}", "[c1sb10]", 2 }, + { "{\"type\":\"array\", \"items\": \"int\"}", "[c1sIc1sI]", 2 }, + { "{\"type\":\"array\", \"items\": \"int\"}", "[c2sIsI]", 2 }, + { "{\"type\":\"array\", \"items\":{\"type\":\"fixed\", " + "\"name\":\"fi\", \"size\": 10}}", "[c2sf10sf10]", 2 }, + + { "{\"type\":\"map\", \"values\": \"boolean\"}", "{}", 2 }, + { "{\"type\":\"map\", \"values\": \"int\"}", "{}", 2 }, + { "{\"type\":\"map\", \"values\": \"long\"}", "{}", 2 }, + { "{\"type\":\"map\", \"values\": \"float\"}", "{}", 2 }, + { "{\"type\":\"map\", \"values\": \"double\"}", "{}", 2 }, + { "{\"type\":\"map\", \"values\": \"string\"}", "{}", 2 }, + { "{\"type\":\"map\", \"values\": \"bytes\"}", "{}", 2 }, + { "{\"type\":\"map\", \"values\": " + "{\"type\":\"array\", \"items\":\"int\"}}", "{}", 2 }, + + { "{\"type\":\"map\", \"values\": \"boolean\"}", "{c1sK5B}", 2 }, + { "{\"type\":\"map\", \"values\": \"int\"}", "{c1sK5I}", 2 }, + { "{\"type\":\"map\", \"values\": \"long\"}", "{c1sK5L}", 2 }, + { "{\"type\":\"map\", \"values\": \"float\"}", "{c1sK5F}", 2 }, + { "{\"type\":\"map\", \"values\": \"double\"}", "{c1sK5D}", 2 }, + { "{\"type\":\"map\", \"values\": \"string\"}", "{c1sK5S10}", 2 }, + { "{\"type\":\"map\", \"values\": \"bytes\"}", "{c1sK5b10}", 2 }, + { "{\"type\":\"map\", \"values\": " + "{\"type\":\"array\", \"items\":\"int\"}}", "{c1sK5[c3sIsIsI]}", 2 }, + + { "{\"type\":\"map\", \"values\": \"boolean\"}", + "{c1sK5Bc2sK5BsK5B}", 2 }, + + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f\", \"type\":\"boolean\"}]}", "B", 1 }, + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f\", \"type\":\"int\"}]}", "I", 1 }, + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f\", \"type\":\"long\"}]}", "L", 1 }, + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f\", \"type\":\"float\"}]}", "F", 1 }, + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f\", \"type\":\"double\"}]}", "D", 1 }, + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f\", \"type\":\"string\"}]}", "S10", 1 }, + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f\", \"type\":\"bytes\"}]}", "b10", 1 }, + + // multi-field records + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"int\"}," + "{\"name\":\"f2\", \"type\":\"double\"}," + "{\"name\":\"f3\", \"type\":\"string\"}]}", "IDS10", 1 }, + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f0\", \"type\":\"null\"}," + "{\"name\":\"f1\", \"type\":\"boolean\"}," + "{\"name\":\"f2\", \"type\":\"int\"}," + "{\"name\":\"f3\", \"type\":\"long\"}," + "{\"name\":\"f4\", \"type\":\"float\"}," + "{\"name\":\"f5\", \"type\":\"double\"}," + "{\"name\":\"f6\", \"type\":\"string\"}," + "{\"name\":\"f7\", \"type\":\"bytes\"}]}", + "NBILFDS10b25", 1 }, + + // record of records + { "{\"type\":\"record\",\"name\":\"outer\",\"fields\":[" + "{\"name\":\"f1\", \"type\":{\"type\":\"record\", " + "\"name\":\"inner\", \"fields\":[" + "{\"name\":\"g1\", \"type\":\"int\"}, {\"name\":\"g2\", " + "\"type\":\"double\"}]}}," + "{\"name\":\"f2\", \"type\":\"string\"}," + "{\"name\":\"f3\", \"type\":\"inner\"}]}", + "IDS10ID", 1 }, + + // record with name references + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":{\"type\":\"fixed\", " + "\"name\":\"f\", \"size\":10 }}," + "{\"name\":\"f2\", \"type\":\"f\"}," + "{\"name\":\"f3\", \"type\":\"f\"}]}", + "f10f10f10", 1 }, + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":{\"type\":\"enum\", " + "\"name\": \"e\", \"symbols\":[\"s1\", \"s2\"] }}," + "{\"name\":\"f2\", \"type\":\"e\"}," + "{\"name\":\"f3\", \"type\":\"e\"}]}", + "e1e0e1", 1 }, + + // record with array + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"long\"}," + "{\"name\":\"f2\", " + "\"type\":{\"type\":\"array\", \"items\":\"int\"}}]}", + "L[c1sI]", 2 }, + + // record with map + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"long\"}," + "{\"name\":\"f2\", " + "\"type\":{\"type\":\"map\", \"values\":\"int\"}}]}", + "L{c1sK5I}", 2 }, + + // array of records + { "{\"type\":\"array\", \"items\":" + "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"long\"}," + "{\"name\":\"f2\", \"type\":\"null\"}]}}", + "[c2sLNsLN]", 2 }, + + + { "{\"type\":\"array\", \"items\":" + "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"long\"}," + "{\"name\":\"f2\", " + "\"type\":{\"type\":\"array\", \"items\":\"int\"}}]}}", + "[c2sL[c1sI]sL[c2sIsI]]", 3 }, + { "{\"type\":\"array\", \"items\":" + "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"long\"}," + "{\"name\":\"f2\", " + "\"type\":{\"type\":\"map\", \"values\":\"int\"}}]}}", + "[c2sL{c1sK5I}sL{c2sK5IsK5I}]", 3 }, + { "{\"type\":\"array\", \"items\":" + "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"long\"}," + "{\"name\":\"f2\", " + "\"type\":[\"null\", \"int\"]}]}}", + "[c2sLU0NsLU1I]", 2 }, + + { "[\"boolean\", \"null\" ]", "U0B", 1 }, + { "[\"int\", \"null\" ]", "U0I", 1 }, + { "[\"long\", \"null\" ]", "U0L", 1 }, + { "[\"float\", \"null\" ]", "U0F", 1 }, + { "[\"double\", \"null\" ]", "U0D", 1 }, + { "[\"string\", \"null\" ]", "U0S10", 1 }, + { "[\"bytes\", \"null\" ]", "U0b10", 1 }, + + { "[\"null\", \"int\"]", "U0N", 1 }, + { "[\"boolean\", \"int\"]", "U0B", 1 }, + { "[\"boolean\", \"int\"]", "U1I", 1 }, + { "[\"boolean\", {\"type\":\"array\", \"items\":\"int\"} ]", + "U0B", 1 }, + + { "[\"boolean\", {\"type\":\"array\", \"items\":\"int\"} ]", + "U1[c1sI]", 2 }, + + // Recursion + { "{\"type\": \"record\", \"name\": \"Node\", \"fields\": [" + "{\"name\":\"label\", \"type\":\"string\"}," + "{\"name\":\"children\", \"type\":" + "{\"type\": \"array\", \"items\": \"Node\" }}]}", + "S10[c1sS10[]]", 3 }, + + { "{\"type\": \"record\", \"name\": \"Lisp\", \"fields\": [" + "{\"name\":\"value\", \"type\":[\"null\", \"string\"," + "{\"type\": \"record\", \"name\": \"Cons\", \"fields\": [" + "{\"name\":\"car\", \"type\":\"Lisp\"}," + "{\"name\":\"cdr\", \"type\":\"Lisp\"}]}]}]}", + "U0N", 1 }, + { "{\"type\": \"record\", \"name\": \"Lisp\", \"fields\": [" + "{\"name\":\"value\", \"type\":[\"null\", \"string\"," + "{\"type\": \"record\", \"name\": \"Cons\", \"fields\": [" + "{\"name\":\"car\", \"type\":\"Lisp\"}," + "{\"name\":\"cdr\", \"type\":\"Lisp\"}]}]}]}", + "U1S10", 1}, + { "{\"type\": \"record\", \"name\": \"Lisp\", \"fields\": [" + "{\"name\":\"value\", \"type\":[\"null\", \"string\"," + "{\"type\": \"record\", \"name\": \"Cons\", \"fields\": [" + "{\"name\":\"car\", \"type\":\"Lisp\"}," + "{\"name\":\"cdr\", \"type\":\"Lisp\"}]}]}]}", + "U2U1S10U0N", 1}, +}; + +static const TestData2 data2[] = { + { "\"int\"", "I", "B", 1 }, + { "\"boolean\"", "B", "I", 1 }, + { "\"boolean\"", "B", "L", 1 }, + { "\"boolean\"", "B", "F", 1 }, + { "\"boolean\"", "B", "D", 1 }, + { "\"boolean\"", "B", "S10", 1 }, + { "\"boolean\"", "B", "b10", 1 }, + { "\"boolean\"", "B", "[]", 1 }, + { "\"boolean\"", "B", "{}", 1 }, + { "\"boolean\"", "B", "U0", 1 }, + { "{\"type\":\"fixed\", \"name\":\"fi\", \"size\": 1}", "f1", "f2", 1 }, +}; + +static const TestData3 data3[] = { + { "\"int\"", "I", "\"float\"", "F", 1 }, + { "\"int\"", "I", "\"double\"", "D", 1 }, + { "\"int\"", "I", "\"long\"", "L", 1 }, + { "\"long\"", "L", "\"float\"", "F", 1 }, + { "\"long\"", "L", "\"double\"", "D", 1 }, + { "\"float\"", "F", "\"double\"", "D", 1 }, + + { "{\"type\":\"array\", \"items\": \"int\"}", "[]", + "{\"type\":\"array\", \"items\": \"long\"}", "[]", 2 }, + { "{\"type\":\"array\", \"items\": \"int\"}", "[]", + "{\"type\":\"array\", \"items\": \"double\"}", "[]", 2 }, + { "{\"type\":\"array\", \"items\": \"long\"}", "[]", + "{\"type\":\"array\", \"items\": \"double\"}", "[]", 2 }, + { "{\"type\":\"array\", \"items\": \"float\"}", "[]", + "{\"type\":\"array\", \"items\": \"double\"}", "[]", 2 }, + + { "{\"type\":\"array\", \"items\": \"int\"}", "[c1sI]", + "{\"type\":\"array\", \"items\": \"long\"}", "[c1sL]", 2 }, + { "{\"type\":\"array\", \"items\": \"int\"}", "[c1sI]", + "{\"type\":\"array\", \"items\": \"double\"}", "[c1sD]", 2 }, + { "{\"type\":\"array\", \"items\": \"long\"}", "[c1sL]", + "{\"type\":\"array\", \"items\": \"double\"}", "[c1sD]", 2 }, + { "{\"type\":\"array\", \"items\": \"float\"}", "[c1sF]", + "{\"type\":\"array\", \"items\": \"double\"}", "[c1sD]", 2 }, + + { "{\"type\":\"map\", \"values\": \"int\"}", "{}", + "{\"type\":\"map\", \"values\": \"long\"}", "{}", 2 }, + { "{\"type\":\"map\", \"values\": \"int\"}", "{}", + "{\"type\":\"map\", \"values\": \"double\"}", "{}", 2 }, + { "{\"type\":\"map\", \"values\": \"long\"}", "{}", + "{\"type\":\"map\", \"values\": \"double\"}", "{}", 2 }, + { "{\"type\":\"map\", \"values\": \"float\"}", "{}", + "{\"type\":\"map\", \"values\": \"double\"}", "{}", 2 }, + + { "{\"type\":\"map\", \"values\": \"int\"}", "{c1sK5I}", + "{\"type\":\"map\", \"values\": \"long\"}", "{c1sK5L}", 2 }, + { "{\"type\":\"map\", \"values\": \"int\"}", "{c1sK5I}", + "{\"type\":\"map\", \"values\": \"double\"}", "{c1sK5D}", 2 }, + { "{\"type\":\"map\", \"values\": \"long\"}", "{c1sK5L}", + "{\"type\":\"map\", \"values\": \"double\"}", "{c1sK5D}", 2 }, + { "{\"type\":\"map\", \"values\": \"float\"}", "{c1sK5F}", + "{\"type\":\"map\", \"values\": \"double\"}", "{c1sK5D}", 2 }, + + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f\", \"type\":\"int\"}]}", "I", + "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f\", \"type\":\"long\"}]}", "L", 1 }, + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f\", \"type\":\"int\"}]}", "I", + "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f\", \"type\":\"double\"}]}", "D", 1 }, + + // multi-field record with promotions + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f0\", \"type\":\"boolean\"}," + "{\"name\":\"f1\", \"type\":\"int\"}," + "{\"name\":\"f2\", \"type\":\"float\"}," + "{\"name\":\"f3\", \"type\":\"string\"}]}", "BIFS", + "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f0\", \"type\":\"boolean\"}," + "{\"name\":\"f1\", \"type\":\"long\"}," + "{\"name\":\"f2\", \"type\":\"double\"}," + "{\"name\":\"f3\", \"type\":\"string\"}]}", "BLDS", 1 }, + + { "[\"int\", \"long\"]", "U0I", "[\"long\", \"string\"]", "U0L", 1 }, + { "[\"int\", \"long\"]", "U0I", "[\"double\", \"string\"]", "U0D", 1 }, + { "[\"long\", \"double\"]", "U0L", "[\"double\", \"string\"]", "U0D", 1 }, + { "[\"float\", \"double\"]", "U0F", "[\"double\", \"string\"]", "U0D", 1 }, + + { "\"int\"", "I", "[\"int\", \"string\"]", "U0I", 1 }, + + { "[\"int\", \"double\"]", "U0I", "\"int\"", "I", 1 }, + { "[\"int\", \"double\"]", "U0I", "\"long\"", "L", 1 }, + + { "[\"boolean\", \"int\"]", "U1I", "[\"boolean\", \"long\"]", "U1L", 1 }, + { "[\"boolean\", \"int\"]", "U1I", "[\"long\", \"boolean\"]", "U0L", 1 }, +}; + +static const TestData4 data4[] = { + // Projection + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"string\"}," + "{\"name\":\"f2\", \"type\":\"string\"}," + "{\"name\":\"f3\", \"type\":\"int\"}]}", "S10S10IS10S10I", + { "s1", "s2", "100", "t1", "t2", "200", NULL }, + "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"string\" }," + "{\"name\":\"f2\", \"type\":\"string\"}]}", "RS10S10RS10S10", + { "s1", "s2", "t1", "t2", NULL }, 1 }, + + // Reordered fields + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"int\"}," + "{\"name\":\"f2\", \"type\":\"string\"}]}", "IS10", + { "10", "hello", NULL }, + "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f2\", \"type\":\"string\" }," + "{\"name\":\"f1\", \"type\":\"long\"}]}", "RLS10", + { "10", "hello", NULL }, 1 }, + + /* + // Default values + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[]}", "", + { NULL }, + "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f\", \"type\":\"int\", \"default\": 100}]}", "RI", + { "100", NULL }, 1 }, + + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f2\", \"type\":\"int\"}]}", "I", + { "10", NULL }, + "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"int\", \"default\": 101}," + "{\"name\":\"f2\", \"type\":\"int\"}]}", "RII", + { "10", "101", NULL }, 1 }, + + { "{\"type\":\"record\",\"name\":\"outer\",\"fields\":[" + "{\"name\": \"g1\", " + "\"type\":{\"type\":\"record\",\"name\":\"inner\",\"fields\":[" + "{\"name\":\"f2\", \"type\":\"int\"}]}}, " + "{\"name\": \"g2\", \"type\": \"long\"}]}", "IL", + { "10", "11", NULL }, + "{\"type\":\"record\",\"name\":\"outer\",\"fields\":[" + "{\"name\": \"g1\", " + "\"type\":{\"type\":\"record\",\"name\":\"inner\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"int\", \"default\": 101}," + "{\"name\":\"f2\", \"type\":\"int\"}]}}, " + "{\"name\": \"g2\", \"type\": \"long\"}]}}", "RRIIL", + { "10", "101", "11", NULL }, 1 }, + + // Default value for a record. + { "{\"type\":\"record\",\"name\":\"outer\",\"fields\":[" + "{\"name\": \"g2\", \"type\": \"long\"}]}", "L", + { "11", NULL }, + "{\"type\":\"record\",\"name\":\"outer\",\"fields\":[" + "{\"name\": \"g1\", " + "\"type\":{\"type\":\"record\",\"name\":\"inner\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"int\" }," + "{\"name\":\"f2\", \"type\":\"int\"}] }, " + "\"default\": { \"f1\": 10, \"f2\": 101 } }, " + "{\"name\": \"g2\", \"type\": \"long\"}]}", "RLRII", + { "11", "10", "101", NULL}, 1 }, + + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[]}", "", + { NULL }, + "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f\", \"type\":{ \"type\": \"array\", \"items\": \"int\" }," + "\"default\": [100]}]}", "[c1sI]", + { "100", NULL }, 1 }, + + { "{ \"type\": \"array\", \"items\": {\"type\":\"record\"," + "\"name\":\"r\",\"fields\":[]} }", "[c1s]", + { NULL }, + "{ \"type\": \"array\", \"items\": {\"type\":\"record\"," + "\"name\":\"r\",\"fields\":[" + "{\"name\":\"f\", \"type\":\"int\", \"default\": 100}]} }", + "[c1sI]", + { "100", NULL }, 1 }, + */ + + // Enum resolution + { "{\"type\":\"enum\",\"name\":\"e\",\"symbols\":[\"x\",\"y\",\"z\"]}", + "e2", + { NULL }, + "{\"type\":\"enum\",\"name\":\"e\",\"symbols\":[ \"y\", \"z\" ]}", + "e1", + { NULL }, 1 }, + + { "{\"type\":\"enum\",\"name\":\"e\",\"symbols\":[ \"x\", \"y\" ]}", + "e1", + { NULL }, + "{\"type\":\"enum\",\"name\":\"e\",\"symbols\":[ \"y\", \"z\" ]}", + "e0", + { NULL }, 1 }, + + + // Union + { "\"int\"", "I", { "100", NULL }, + "[ \"long\", \"int\"]", "U1I", { "100", NULL }, 1 }, + + { "[ \"long\", \"int\"]", "U1I", { "100", NULL } , + "\"int\"", "I", { "100", NULL }, 1 }, + + // Arrray of unions + { "{\"type\":\"array\", \"items\":[ \"long\", \"int\"]}", + "[c2sU1IsU1I]", { "100", "100", NULL } , + "{\"type\":\"array\", \"items\": \"int\"}", + "[c2sIsI]", { "100", "100", NULL }, 2 }, + { "{\"type\":\"array\", \"items\":[ \"long\", \"int\"]}", + "[c1sU1Ic1sU1I]", { "100", "100", NULL } , + "{\"type\":\"array\", \"items\": \"int\"}", + "[c1sIc1sI]", { "100", "100", NULL }, 2 }, + + // Map of unions + { "{\"type\":\"map\", \"values\":[ \"long\", \"int\"]}", + "{c2sS10U1IsS10U1I}", { "k1", "100", "k2", "100", NULL } , + "{\"type\":\"map\", \"values\": \"int\"}", + "{c2sS10IsS10I}", { "k1", "100", "k2", "100", NULL }, 2 }, + { "{\"type\":\"map\", \"values\":[ \"long\", \"int\"]}", + "{c1sS10U1Ic1sS10U1I}", { "k1", "100", "k2", "100", NULL } , + "{\"type\":\"map\", \"values\": \"int\"}", + "{c1sS10Ic1sS10I}", { "k1", "100", "k2", "100", NULL }, 2 }, + + // Union + promotion + { "\"int\"", "I", { "100", NULL }, + "[ \"long\", \"string\"]", "U0L", { "100", NULL }, 1 }, + + { "[ \"int\", \"string\"]", "U0I", { "100", NULL }, + "\"long\"", "L", { "100", NULL }, 1 }, + + // Record where union field is skipped. + { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f0\", \"type\":\"boolean\"}," + "{\"name\":\"f1\", \"type\":\"int\"}," + "{\"name\":\"f2\", \"type\":[\"int\", \"long\"]}," + "{\"name\":\"f3\", \"type\":\"float\"}" + "]}", "BIU0IF", + { "1", "100", "121", "10.75", NULL }, + "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f0\", \"type\":\"boolean\"}," + "{\"name\":\"f1\", \"type\":\"long\"}," + "{\"name\":\"f3\", \"type\":\"double\"}]}", "BLD", + { "1", "100", "10.75", NULL }, 1 }, +}; + +#define COUNTOF(x) sizeof(x) / sizeof(x[0]) + +#define ADD_TESTS(testSuite, Factory, testFunc, data) \ +testSuite.add(BOOST_PARAM_TEST_CASE(&testFunc<Factory>, \ + data, data + COUNTOF(data))) + +struct BinaryEncoderFactory { + static EncoderPtr newEncoder(const ValidSchema& schema) { + return binaryEncoder(); + } +}; + +struct BinaryDecoderFactory { + static DecoderPtr newDecoder(const ValidSchema& schema) { + return binaryDecoder(); + } +}; + +struct BinaryCodecFactory : public BinaryEncoderFactory, + public BinaryDecoderFactory { }; + +struct ValidatingEncoderFactory { + static EncoderPtr newEncoder(const ValidSchema& schema) { + return validatingEncoder(schema, binaryEncoder()); + } +}; + +struct ValidatingDecoderFactory { + static DecoderPtr newDecoder(const ValidSchema& schema) { + return validatingDecoder(schema, binaryDecoder()); + } +}; + +struct ValidatingCodecFactory : public ValidatingEncoderFactory, + public ValidatingDecoderFactory { }; + +struct JsonCodec { + static EncoderPtr newEncoder(const ValidSchema& schema) { + return jsonEncoder(schema); + } + static DecoderPtr newDecoder(const ValidSchema& schema) { + return jsonDecoder(schema); + } +}; + +struct BinaryEncoderResolvingDecoderFactory : public BinaryEncoderFactory { + static DecoderPtr newDecoder(const ValidSchema& schema) { + return resolvingDecoder(schema, schema, binaryDecoder()); + } + + static DecoderPtr newDecoder(const ValidSchema& writer, + const ValidSchema& reader) { + return resolvingDecoder(writer, reader, binaryDecoder()); + } +}; + +struct ValidatingEncoderResolvingDecoderFactory : + public ValidatingEncoderFactory { + static DecoderPtr newDecoder(const ValidSchema& schema) { + return resolvingDecoder(schema, schema, + validatingDecoder(schema, binaryDecoder())); + } + + static DecoderPtr newDecoder(const ValidSchema& writer, + const ValidSchema& reader) { + return resolvingDecoder(writer, reader, + validatingDecoder(writer, binaryDecoder())); + } +}; + +void add_tests(boost::unit_test::test_suite& ts) +{ + ADD_TESTS(ts, BinaryCodecFactory, testCodec, data); + ADD_TESTS(ts, ValidatingCodecFactory, testCodec, data); + ADD_TESTS(ts, JsonCodec, testCodec, data); + ADD_TESTS(ts, BinaryEncoderResolvingDecoderFactory, testCodec, data); + ADD_TESTS(ts, ValidatingCodecFactory, testReaderFail, data2); + ADD_TESTS(ts, ValidatingCodecFactory, testWriterFail, data2); + ADD_TESTS(ts, BinaryEncoderResolvingDecoderFactory, + testCodecResolving, data3); + ADD_TESTS(ts, BinaryEncoderResolvingDecoderFactory, + testCodecResolving2, data4); + ADD_TESTS(ts, ValidatingEncoderResolvingDecoderFactory, + testCodecResolving2, data4); +} + +} // namespace parsing +} // namespace avro + +boost::unit_test::test_suite* +init_unit_test_suite( int argc, char* argv[] ) +{ + using namespace boost::unit_test; + + test_suite* ts= BOOST_TEST_SUITE("Avro C++ unit test suite"); + avro::parsing::add_tests(*ts); + + return ts; +}
Added: avro/trunk/lang/c++/test/StreamTests.cc URL: http://svn.apache.org/viewvc/avro/trunk/lang/c%2B%2B/test/StreamTests.cc?rev=1072677&view=auto ============================================================================== --- avro/trunk/lang/c++/test/StreamTests.cc (added) +++ avro/trunk/lang/c++/test/StreamTests.cc Sun Feb 20 18:23:18 2011 @@ -0,0 +1,238 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "boost/filesystem.hpp" +#include "Stream.hh" +#include "Exception.hh" +#include <boost/test/included/unit_test_framework.hpp> +#include <boost/test/parameterized_test.hpp> + +namespace avro { +namespace stream { + +struct CheckEmpty1 { + void operator()(InputStream& is) { + const uint8_t* d; + size_t n; + BOOST_CHECK(! is.next(&d, &n)); + } +}; + +struct CheckEmpty2 { + void operator()(InputStream& is) { + StreamReader r; + r.reset(is); + BOOST_CHECK_THROW(r.read(), Exception); + } +}; + +struct TestData { + size_t chunkSize; + size_t dataSize; +}; + +struct Fill1 { + void operator()(OutputStream& os, size_t len) { + StreamWriter w; + w.reset(os); + for (int i = 0; i < len; ++i) { + w.write(i % 10 + '0'); + } + w.flush(); + } +}; + +struct Fill2 { + void operator()(OutputStream& os, size_t len) { + for (int i = 0; i < len;) { + uint8_t *b; + size_t n; + os.next(&b, &n); + size_t j = 0; + for (; i < len && j < n; ++j, ++i, ++b) { + *b = i % 10 + '0'; + } + if (i == len) { + os.backup(n - j); + } + } + os.flush(); + } +}; + +struct Verify1 { + void operator()(InputStream& is, size_t dataSize) { + StreamReader r; + r.reset(is); + for (int i = 0; i < dataSize; ++i) { + BOOST_CHECK_EQUAL(i % 10 + '0', r.read()); + } + BOOST_CHECK_THROW(r.read(), Exception); + } +}; + +struct Verify2 { + void operator()(InputStream& is, size_t len) { + const uint8_t *b; + size_t n; + + for (int i = 0; i < len;) { + BOOST_REQUIRE(is.next(&b, &n)); + size_t j = 0; + for (; i < len && j < n; ++j, ++i, ++b) { + BOOST_CHECK_EQUAL(*b, i % 10 + '0'); + } + BOOST_CHECK_EQUAL(j, n); + } + BOOST_CHECK(! is.next(&b, &n)); + } +}; + +template <typename V> +void testEmpty_memoryStream() { + std::auto_ptr<OutputStream> os = memoryOutputStream(); + std::auto_ptr<InputStream> is = memoryInputStream(*os); + V()(*is); +} + +template <typename F, typename V> +void testNonEmpty_memoryStream(const TestData& td) +{ + std::auto_ptr<OutputStream> os = memoryOutputStream(td.chunkSize); + F()(*os, td.dataSize); + + std::auto_ptr<InputStream> is = memoryInputStream(*os); + V()(*is, td.dataSize); +} + +void testNonEmpty2(const TestData& td) { + std::vector<uint8_t> v; + for (int i = 0; i < td.dataSize; ++i) { + v.push_back(i % 10 + '0'); + } + + std::auto_ptr<InputStream> is = memoryInputStream(&v[0], v.size()); + Verify1()(*is, td.dataSize); +} + +static const char filename[] = "test_str.bin"; + +struct FileRemover { + const boost::filesystem::path file; + FileRemover(const char* filename) : file(filename) { } + ~FileRemover() { boost::filesystem::remove(file); } +}; + +template <typename V> +void testEmpty_fileStream() { + FileRemover fr(filename); + { + std::auto_ptr<OutputStream> os = fileOutputStream(filename); + } + std::auto_ptr<InputStream> is = fileInputStream(filename); + V()(*is); +} + +template <typename F, typename V> +void testNonEmpty_fileStream(const TestData& td) +{ + FileRemover fr(filename); + { + std::auto_ptr<OutputStream> os = fileOutputStream(filename, + td.chunkSize); + F()(*os, td.dataSize); + } + + std::auto_ptr<InputStream> is = fileInputStream(filename, td.chunkSize); + V()(*is, td.dataSize); +} + +TestData data[] = { + { 100, 0 }, + { 100, 1 }, + { 100, 10 }, + { 100, 100 }, + { 100, 101 }, + { 100, 1000 }, + { 100, 1024 } +}; + +} // namespace stream + +} // namespace + +boost::unit_test::test_suite* +init_unit_test_suite( int argc, char* argv[] ) +{ + boost::unit_test::test_suite* ts = + BOOST_TEST_SUITE("Avro C++ unit test suite for streams"); + + ts->add(BOOST_TEST_CASE( + &avro::stream::testEmpty_memoryStream<avro::stream::CheckEmpty1>)); + ts->add(BOOST_TEST_CASE( + &avro::stream::testEmpty_memoryStream<avro::stream::CheckEmpty2>)); + + ts->add(BOOST_PARAM_TEST_CASE( + (&avro::stream::testNonEmpty_memoryStream<avro::stream::Fill1, + avro::stream::Verify1>), + avro::stream::data, + avro::stream::data + + sizeof(avro::stream::data) / sizeof(avro::stream::data[0]))); + ts->add(BOOST_PARAM_TEST_CASE( + (&avro::stream::testNonEmpty_memoryStream<avro::stream::Fill2, + avro::stream::Verify1>), + avro::stream::data, + avro::stream::data + + sizeof(avro::stream::data) / sizeof(avro::stream::data[0]))); + ts->add(BOOST_PARAM_TEST_CASE( + (&avro::stream::testNonEmpty_memoryStream<avro::stream::Fill2, + avro::stream::Verify2>), + avro::stream::data, + avro::stream::data + + sizeof(avro::stream::data) / sizeof(avro::stream::data[0]))); + + ts->add(BOOST_PARAM_TEST_CASE(&avro::stream::testNonEmpty2, + avro::stream::data, + avro::stream::data + + sizeof(avro::stream::data) / sizeof(avro::stream::data[0]))); + + ts->add(BOOST_TEST_CASE( + &avro::stream::testEmpty_fileStream<avro::stream::CheckEmpty1>)); + ts->add(BOOST_TEST_CASE( + &avro::stream::testEmpty_fileStream<avro::stream::CheckEmpty2>)); + + ts->add(BOOST_PARAM_TEST_CASE( + (&avro::stream::testNonEmpty_fileStream<avro::stream::Fill1, + avro::stream::Verify1>), + avro::stream::data, + avro::stream::data + + sizeof(avro::stream::data) / sizeof(avro::stream::data[0]))); + ts->add(BOOST_PARAM_TEST_CASE( + (&avro::stream::testNonEmpty_fileStream<avro::stream::Fill2, + avro::stream::Verify1>), + avro::stream::data, + avro::stream::data + + sizeof(avro::stream::data) / sizeof(avro::stream::data[0]))); + ts->add(BOOST_PARAM_TEST_CASE( + (&avro::stream::testNonEmpty_fileStream<avro::stream::Fill2, + avro::stream::Verify2>), + avro::stream::data, + avro::stream::data + + sizeof(avro::stream::data) / sizeof(avro::stream::data[0]))); + return ts; +}
