/**
* 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
*
* https://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.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using Avro.File;
using Avro.Generic;
using Avro.Specific;
using NUnit.Framework;
namespace Avro.Test.File
{
[TestFixture]
public class FileTests
{
const string specificSchema = "{\"type\":\"record\",\"name\":\"Foo\",\"namespace\":\"Avro.Test.File\",\"fields\":"
+ "[{\"name\":\"name\",\"type\":[\"null\",\"string\"]},{\"name\":\"age\",\"type\":\"int\"}]}";
///
/// Reading & writing of specific (custom) record objects
///
///
///
///
[TestCase(specificSchema, new object[] { new object[] { "John", 23 } }, Codec.Type.Deflate, TestName = "TestSpecificData0")]
[TestCase(specificSchema, new object[] { new object[] { "Jane", 23 } }, Codec.Type.Deflate, TestName = "TestSpecificData1")]
[TestCase(specificSchema, new object[] { new object[] { "John", 23 }, new object[] { "Jane", 99 }, new object[] { "Jeff", 88 } }, Codec.Type.Deflate, TestName = "TestSpecificData2")]
[TestCase(specificSchema, new object[] { new object[] {"John", 23}, new object[] { "Jane", 99 }, new object[] { "Jeff", 88 },
new object[] {"James", 13}, new object[] { "June", 109 }, new object[] { "Lloyd", 18 },
new object[] {"Jenny", 3}, new object[] { "Bob", 9 }, new object[] { null, 48 }}, Codec.Type.Deflate, TestName = "TestSpecificData3")]
[TestCase(specificSchema, new object[] { new object[] { "John", 23 } }, Codec.Type.Null, TestName = "TestSpecificData4")]
[TestCase(specificSchema, new object[] { new object[] { "Jane", 23 } }, Codec.Type.Null, TestName = "TestSpecificData5")]
[TestCase(specificSchema, new object[] { new object[] { "John", 23 }, new object[] { "Jane", 99 }, new object[] { "Jeff", 88 } }, Codec.Type.Null, TestName = "TestSpecificData6")]
[TestCase(specificSchema, new object[] { new object[] {"John", 23}, new object[] { "Jane", 99 }, new object[] { "Jeff", 88 },
new object[] {"James", 13}, new object[] { "June", 109 }, new object[] { "Lloyd", 18 },
new object[] {"Jamie", 53}, new object[] { "Fanessa", 101 }, new object[] { "Kan", 18 },
new object[] {"Janey", 33}, new object[] { "Deva", 102 }, new object[] { "Gavin", 28 },
new object[] {"Lochy", 113}, new object[] { "Nickie", 10 }, new object[] { "Liddia", 38 },
new object[] {"Fred", 3}, new object[] { "April", 17 }, new object[] { "Novac", 48 },
new object[] {"Idan", 33}, new object[] { "Jolyon", 76 }, new object[] { "Ant", 68 },
new object[] {"Ernie", 43}, new object[] { "Joel", 99 }, new object[] { "Dan", 78 },
new object[] {"Dave", 103}, new object[] { "Hillary", 79 }, new object[] { "Grant", 88 },
new object[] {"JJ", 14}, new object[] { "Bill", 90 }, new object[] { "Larry", 4 },
new object[] {"Jenny", 3}, new object[] { "Bob", 9 }, new object[] { null, 48 }}, Codec.Type.Null, TestName = "TestSpecificData7")]
public void TestSpecificData(string schemaStr, object[] recs, Codec.Type codecType)
{
// create and write out
IList records = MakeRecords(recs);
foreach(var rwFactory in SpecificOptions())
{
MemoryStream dataFileOutputStream = new MemoryStream();
Schema schema = Schema.Parse(schemaStr);
using (IFileWriter dataFileWriter = rwFactory.CreateWriter(dataFileOutputStream, schema, Codec.CreateCodec(codecType)))
{
foreach (Foo rec in records)
dataFileWriter.Append(rec);
}
MemoryStream dataFileInputStream = new MemoryStream(dataFileOutputStream.ToArray());
// read back
IList readRecords = new List();
using (IFileReader reader = rwFactory.CreateReader(dataFileInputStream, null))
{
foreach (Foo rec in reader.NextEntries)
readRecords.Add(rec);
}
// compare objects via Json
Assert.AreEqual(records.Count, readRecords.Count);
for (int i = 0; i < records.Count; i++)
{
Assert.AreEqual(records[i].ToString(), readRecords[i].ToString());
}
}
}
///
/// Test appending of specific (custom) record objects
///
/// schema
/// initial records
/// append records
/// initial compression codec type
[TestCase(specificSchema, new object[] { new object[] { "John", 23 } }, new object[] { new object[] { "Jane", 21 } }, Codec.Type.Deflate, TestName = "TestAppendSpecificData0")]
[TestCase(specificSchema, new object[] { new object[] { "John", 23 } }, new object[] { new object[] { "Jane", 21 } }, Codec.Type.Null, TestName = "TestAppendSpecificData1")]
[TestCase(specificSchema, new object[] { new object[] {"John", 23}, new object[] { "Jane", 99 }, new object[] { "Jeff", 88 },
new object[] {"James", 13}, new object[] { "June", 109 }, new object[] { "Lloyd", 18 },
new object[] {"Jenny", 3}, new object[] { "Bob", 9 }, new object[] { null, 48 }},
new object[] { new object[] { "Hillary", 79 },
new object[] { "Grant", 88 } }, Codec.Type.Deflate, TestName = "TestAppendSpecificData2")]
[TestCase(specificSchema, new object[] { new object[] {"John", 23}, new object[] { "Jane", 99 }, new object[] { "Jeff", 88 },
new object[] {"James", 13}, new object[] { "June", 109 }, new object[] { "Lloyd", 18 },
new object[] {"Jenny", 3}, new object[] { "Bob", 9 }, new object[] { null, 48 }},
new object[] { new object[] { "Hillary", 79 },
new object[] { "Grant", 88 } }, Codec.Type.Null, TestName = "TestAppendSpecificData3")]
public void TestAppendSpecificData(string schemaStr, object[] recs, object[] appendRecs, Codec.Type codecType)
{
IList records = MakeRecords(recs);
IList appendRecords = MakeRecords(appendRecs);
IList allRecords = records.Concat(appendRecords).ToList();
foreach (var rwFactory in SpecificOptions())
{
// create and write out
MemoryStream dataFileOutputStream = new MemoryStream();
Schema schema = Schema.Parse(schemaStr);
using (IFileWriter dataFileWriter = rwFactory.CreateWriter(dataFileOutputStream, schema, Codec.CreateCodec(codecType)))
{
foreach (Foo rec in records)
dataFileWriter.Append(rec);
}
// append records
byte[] outputData = dataFileOutputStream.ToArray();
MemoryStream dataFileAppendInputStream = new MemoryStream(dataFileOutputStream.ToArray());
MemoryStream dataFileAppendStream = new MemoryStream(); // MemoryStream is not expandable
dataFileAppendStream.Write(outputData, 0, outputData.Length);
using (IFileWriter appendFileWriter = rwFactory.CreateAppendWriter(dataFileAppendInputStream, dataFileAppendStream, schema))
{
foreach (Foo rec in appendRecords)
appendFileWriter.Append(rec);
}
MemoryStream dataFileInputStream = new MemoryStream(dataFileAppendStream.ToArray());
// read back
IList readRecords = new List();
using (IFileReader reader = rwFactory.CreateReader(dataFileInputStream, null))
{
foreach (Foo rec in reader.NextEntries)
readRecords.Add(rec);
}
// compare objects via Json
Assert.AreEqual(allRecords.Count, readRecords.Count);
for (int i = 0; i < allRecords.Count; i++)
Assert.AreEqual(allRecords[i].ToString(), readRecords[i].ToString());
}
}
///
/// Reading & writing of generic record objects
///
///
///
///
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"null\"}]}",
new object[] { "f1", null }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"boolean\"}]}",
new object[] { "f1", true }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"boolean\"}]}",
new object[] { "f1", false }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"int\"}]}",
new object[] { "f1", 101 }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"long\"}]}",
new object[] { "f1", 101L }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"float\"}]}",
new object[] { "f1", 101.78f }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"double\"}]}",
new object[] { "f1", 101.78 }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"string\"}]}",
new object[] { "f1", "A" }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"bytes\"}]}",
new object[] { "f1", new byte[] { 0, 1 } }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":" +
"[{\"name\":\"f1\", \"type\":{\"type\": \"enum\", \"name\": \"e\", \"symbols\":[\"s1\", \"s2\"]}}]}",
new object[] { "f1", "s2" }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":" +
"[{\"name\":\"f1\", \"type\":{\"type\": \"array\", \"items\": \"int\"}}]}",
new object[] { "f1", new object[] { 0, 1, 101 } }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":" +
"[{\"name\":\"f1\", \"type\":{\"type\": \"array\", \"items\": \"int\"}}]}",
new object[] { "f1", new int[] { 0, 1, 101 } }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":" +
"[{\"name\":\"f1\", \"type\":[\"int\", \"long\"]}]}",
new object[] { "f1", 100 }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":" +
"[{\"name\":\"f1\", \"type\":[\"int\", \"long\"]}]}",
new object[] { "f1", 100L }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":" +
"[{\"name\":\"f1\", \"type\":{\"type\": \"fixed\", \"name\": \"f\", \"size\": 2}}]}",
new object[] { "f1", new byte[] { 1, 2 } }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"null\"}]}",
new object[] { "f1", null }, Codec.Type.Null)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"boolean\"}]}",
new object[] { "f1", true }, Codec.Type.Null)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"boolean\"}]}",
new object[] { "f1", false }, Codec.Type.Null)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"int\"}]}",
new object[] { "f1", 101 }, Codec.Type.Null)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"long\"}]}",
new object[] { "f1", 101L }, Codec.Type.Null)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"float\"}]}",
new object[] { "f1", 101.78f }, Codec.Type.Null)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"double\"}]}",
new object[] { "f1", 101.78 }, Codec.Type.Null)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"string\"}]}",
new object[] { "f1", "A" }, Codec.Type.Null)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"bytes\"}]}",
new object[] { "f1", new byte[] { 0, 1 } }, Codec.Type.Null)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":" +
"[{\"name\":\"f1\", \"type\":{\"type\": \"enum\", \"name\": \"e\", \"symbols\":[\"s1\", \"s2\"]}}]}",
new object[] { "f1", "s2" }, Codec.Type.Null)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":" +
"[{\"name\":\"f1\", \"type\":{\"type\": \"array\", \"items\": \"int\"}}]}",
new object[] { "f1", new object[] { 0, 1, 101 } }, Codec.Type.Null)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":" +
"[{\"name\":\"f1\", \"type\":{\"type\": \"array\", \"items\": \"int\"}}]}",
new object[] { "f1", new int[] { 0, 1, 101 } }, Codec.Type.Null)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":" +
"[{\"name\":\"f1\", \"type\":[\"int\", \"long\"]}]}",
new object[] { "f1", 100 }, Codec.Type.Null)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":" +
"[{\"name\":\"f1\", \"type\":[\"int\", \"long\"]}]}",
new object[] { "f1", 100L }, Codec.Type.Null)]
public void TestGenericData(string schemaStr, object[] value, Codec.Type codecType)
{
foreach(var rwFactory in GenericOptions())
{
// Create and write out
MemoryStream dataFileOutputStream = new MemoryStream();
using (var writer = rwFactory.CreateWriter(dataFileOutputStream, Schema.Parse(schemaStr), Codec.CreateCodec(codecType)))
{
writer.Append(mkRecord(value, Schema.Parse(schemaStr) as RecordSchema));
}
MemoryStream dataFileInputStream = new MemoryStream(dataFileOutputStream.ToArray());
// Read back
IList readFoos = new List();
using (IFileReader reader = rwFactory.CreateReader(dataFileInputStream,null))
{
foreach (GenericRecord foo in reader.NextEntries)
{
readFoos.Add(foo);
}
}
Assert.IsTrue((readFoos != null && readFoos.Count > 0),
string.Format(@"Generic object: {0} did not serialise/deserialise correctly", readFoos));
}
}
///
/// Test appending of generic record objects
///
/// schema
/// initial records
/// append records
/// innitial compression codec type
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"boolean\"}]}",
new object[] { "f1", true }, new object[] { "f1", false }, Codec.Type.Deflate)]
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":[{\"name\":\"f1\", \"type\":\"int\"}]}",
new object[] { "f1", 1 }, new object[] { "f1", 2 }, Codec.Type.Null)]
public void TestAppendGenericData(string schemaStr, object[] recs, object[] appendRecs, Codec.Type codecType)
{
foreach (var rwFactory in GenericOptions())
{
// Create and write out
MemoryStream dataFileOutputStream = new MemoryStream();
using (var writer = rwFactory.CreateWriter(dataFileOutputStream, Schema.Parse(schemaStr), Codec.CreateCodec(codecType)))
{
writer.Append(mkRecord(recs, Schema.Parse(schemaStr) as RecordSchema));
}
// append records
byte[] outputData = dataFileOutputStream.ToArray();
MemoryStream dataFileAppendInputStream = new MemoryStream(dataFileOutputStream.ToArray());
MemoryStream dataFileAppendStream = new MemoryStream(); // MemoryStream is not expandable
dataFileAppendStream.Write(outputData, 0, outputData.Length);
using (var appendFileWriter = rwFactory.CreateAppendWriter(dataFileAppendInputStream, dataFileAppendStream, Schema.Parse(schemaStr)))
{
appendFileWriter.Append(mkRecord(appendRecs, Schema.Parse(schemaStr) as RecordSchema));
}
MemoryStream dataFileInputStream = new MemoryStream(dataFileAppendStream.ToArray());
// Read back
IList readFoos = new List();
using (IFileReader reader = rwFactory.CreateReader(dataFileInputStream, null))
{
foreach (GenericRecord foo in reader.NextEntries)
{
readFoos.Add(foo);
}
}
Assert.NotNull(readFoos);
Assert.AreEqual((recs.Length + appendRecs.Length) / 2, readFoos.Count,
$"Generic object: {readFoos} did not serialise/deserialise correctly");
}
}
[Test]
public void OpenAppendWriter_IncorrectInStream_Throws()
{
MemoryStream compressedStream = new MemoryStream();
// using here a DeflateStream as it is a standard non-seekable stream, so if it works for this one,
// it should also works with any standard non-seekable stream (ie: NetworkStreams)
DeflateStream dataFileInputStream = new DeflateStream(compressedStream, CompressionMode.Compress);
var action = new TestDelegate(() => DataFileWriter.OpenAppendWriter(null, dataFileInputStream, null));
var ex = Assert.Throws(typeof(AvroRuntimeException), action);
}
[Test]
public void OpenAppendWriter_IncorrectOutStream_Throws()
{
MemoryStream inStream = new MemoryStream();
MemoryStream outStream = new MemoryStream();
outStream.Close();
var action = new TestDelegate(() => DataFileWriter.OpenAppendWriter(null, inStream, outStream));
Assert.Throws(typeof(AvroRuntimeException), action);
}
///
/// This test is a single test case of
/// but introduces a
/// DeflateStream as it is a standard non-seekable Stream that has the same behavior as the
/// NetworkStream, which we should handle.
///
[TestCase("{\"type\":\"record\", \"name\":\"n\", \"fields\":" +
"[{\"name\":\"f1\", \"type\":[\"int\", \"long\"]}]}",
new object[] { "f1", 100L }, Codec.Type.Null)]
public void TestNonSeekableStream(string schemaStr, object[] value, Codec.Type codecType)
{
foreach (var rwFactory in GenericOptions())
{
// Create and write out
MemoryStream compressedStream = new MemoryStream();
// using here a DeflateStream as it is a standard non-seekable stream, so if it works for this one,
// it should also works with any standard non-seekable stream (ie: NetworkStreams)
DeflateStream dataFileOutputStream = new DeflateStream(compressedStream, CompressionMode.Compress);
using (var writer = rwFactory.CreateWriter(dataFileOutputStream, Schema.Parse(schemaStr), Codec.CreateCodec(codecType)))
{
writer.Append(mkRecord(value, Schema.Parse(schemaStr) as RecordSchema));
// The Sync method is not supported for non-seekable streams.
Assert.Throws(() => writer.Sync());
}
DeflateStream dataFileInputStream = new DeflateStream(new MemoryStream(compressedStream.ToArray()), CompressionMode.Decompress);
// Read back
IList readFoos = new List();
using (IFileReader reader = rwFactory.CreateReader(dataFileInputStream, null))
{
foreach (GenericRecord foo in reader.NextEntries)
{
readFoos.Add(foo);
}
// These methods are not supported for non-seekable streams.
Assert.Throws(() => reader.Seek(0));
Assert.Throws(() => reader.PreviousSync());
}
Assert.IsTrue((readFoos != null && readFoos.Count > 0),
string.Format(@"Generic object: {0} did not serialise/deserialise correctly", readFoos));
}
}
///
/// Reading & writing of primitive objects
///
///
///
///
[TestCase("{\"type\": \"boolean\"}", true, Codec.Type.Deflate)]
[TestCase("{\"type\": \"boolean\"}", false, Codec.Type.Deflate)]
[TestCase("{\"type\": \"boolean\"}", true, Codec.Type.Null)]
[TestCase("{\"type\": \"boolean\"}", false, Codec.Type.Null)]
[TestCase("[\"boolean\", \"null\"]", null, Codec.Type.Deflate)]
[TestCase("[\"boolean\", \"null\"]", true, Codec.Type.Deflate)]
[TestCase("[\"int\", \"long\"]", 100, Codec.Type.Deflate)]
[TestCase("[\"int\", \"long\"]", 100L, Codec.Type.Deflate)]
[TestCase("[\"float\", \"double\"]", 100.75, Codec.Type.Deflate)]
[TestCase("[\"float\", \"double\"]", 23.67f, Codec.Type.Deflate)]
[TestCase("[{\"type\": \"array\", \"items\": \"float\"}, \"double\"]", new float[] { 23.67f, 22.78f }, Codec.Type.Deflate)]
[TestCase("[{\"type\": \"array\", \"items\": \"float\"}, \"double\"]", 100.89, Codec.Type.Deflate)]
[TestCase("[{\"type\": \"array\", \"items\": \"string\"}, \"string\"]", "a", Codec.Type.Deflate)]
[TestCase("[{\"type\": \"array\", \"items\": \"string\"}, \"string\"]", new string[] { "a", "b" }, Codec.Type.Deflate)]
[TestCase("[{\"type\": \"array\", \"items\": \"bytes\"}, \"bytes\"]", new byte[] { 1, 2, 3 }, Codec.Type.Deflate)]
[TestCase("[{\"type\": \"array\", \"items\": \"bytes\"}, \"bytes\"]", new object[] { new byte[] { 1, 2 }, new byte[] { 3, 4 } }, Codec.Type.Deflate)]
[TestCase("[{\"type\": \"enum\", \"symbols\": [\"s1\", \"s2\"], \"name\": \"e\"}, \"string\"]", "h1", Codec.Type.Deflate)]
[TestCase("{\"type\":\"string\"}", "John", Codec.Type.Deflate)]
[TestCase("{\"type\":[\"null\",\"string\"]}", null, Codec.Type.Deflate)]
[TestCase("{\"type\":\"int\"}", 1, Codec.Type.Deflate)]
[TestCase("{\"type\":\"boolean\"}", false, Codec.Type.Deflate)]
[TestCase("{\"type\":\"long\"}", 12312313123L, Codec.Type.Deflate)]
[TestCase("{\"type\":\"float\"}", 0.0f, Codec.Type.Deflate)]
[TestCase("{\"type\":\"double\"}", 0.0, Codec.Type.Deflate)]
[TestCase("[\"boolean\", \"null\"]", null, Codec.Type.Null)]
[TestCase("[\"boolean\", \"null\"]", true, Codec.Type.Null)]
[TestCase("[\"int\", \"long\"]", 100, Codec.Type.Null)]
[TestCase("[\"int\", \"long\"]", 100L, Codec.Type.Null)]
[TestCase("[\"float\", \"double\"]", 100.75, Codec.Type.Null)]
[TestCase("[\"float\", \"double\"]", 23.67f, Codec.Type.Null)]
[TestCase("[{\"type\": \"array\", \"items\": \"float\"}, \"double\"]", new float[] { 23.67f, 22.78f }, Codec.Type.Null)]
[TestCase("[{\"type\": \"array\", \"items\": \"float\"}, \"double\"]", 100.89, Codec.Type.Null)]
[TestCase("[{\"type\": \"array\", \"items\": \"string\"}, \"string\"]", "a", Codec.Type.Null)]
[TestCase("[{\"type\": \"array\", \"items\": \"string\"}, \"string\"]", new string[] { "a", "b" }, Codec.Type.Null)]
[TestCase("[{\"type\": \"array\", \"items\": \"bytes\"}, \"bytes\"]", new byte[] { 1, 2, 3 }, Codec.Type.Null)]
[TestCase("[{\"type\": \"array\", \"items\": \"bytes\"}, \"bytes\"]", new object[] { new byte[] { 1, 2 }, new byte[] { 3, 4 } }, Codec.Type.Null)]
[TestCase("[{\"type\": \"enum\", \"symbols\": [\"s1\", \"s2\"], \"name\": \"e\"}, \"string\"]", "h1", Codec.Type.Null)]
[TestCase("{\"type\":\"string\"}", "John", Codec.Type.Null)]
[TestCase("{\"type\":[\"null\",\"string\"]}", null, Codec.Type.Null)]
[TestCase("{\"type\":\"int\"}", 1, Codec.Type.Null)]
[TestCase("{\"type\":\"boolean\"}", false, Codec.Type.Null)]
[TestCase("{\"type\":\"long\"}", 12312313123L, Codec.Type.Null)]
[TestCase("{\"type\":\"float\"}", 0.0f, Codec.Type.Null)]
[TestCase("{\"type\":\"double\"}", 0.0, Codec.Type.Null)]
[TestCase("{\"type\":\"string\"}", "test", Codec.Type.Null)]
public void TestPrimitiveData(string schemaStr, object value, Codec.Type codecType)
{
foreach(var rwFactory in GenericOptions