avro-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From th...@apache.org
Subject svn commit: r1072677 [3/3] - in /avro/trunk: ./ lang/c++/ lang/c++/api/ lang/c++/api/buffer/detail/ lang/c++/impl/ lang/c++/impl/parsing/ lang/c++/parser/ lang/c++/test/
Date Sun, 20 Feb 2011 18:23:19 GMT
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;
+}



Mime
View raw message