| /** |
| * 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.Buffers; |
| using NUnit.Framework; |
| using System.IO; |
| using System.Linq; |
| using Avro.IO; |
| |
| namespace Avro.Test |
| { |
| using Decoder = Avro.IO.Decoder; |
| using Encoder = Avro.IO.Encoder; |
| delegate T Decode<T>(Decoder d); |
| delegate void Skip<T>(Decoder d); |
| delegate void Encode<T>(Encoder e, T t); |
| |
| /// <summary> |
| /// Tests the BinaryEncoder and BinaryDecoder. This is pertty general set of test cases and hence |
| /// can be used for any encoder and its corresponding decoder. |
| /// </summary> |
| [TestFixture] |
| public class BinaryCodecTests |
| { |
| |
| /// <summary> |
| /// Writes an avro type T with value t into a stream using the encode method e |
| /// and reads it back using the decode method d and verifies that |
| /// the value read back is the same as the one written in. |
| /// </summary> |
| /// <typeparam name="T">Avro type to test</typeparam> |
| /// <param name="t">Value for the Avro type to test.</param> |
| /// <param name="r">The decode method</param> |
| /// <param name="w">The encode method</param> |
| private void TestRead<T>(T t, Decode<T> r, Encode<T> w, int size) |
| { |
| MemoryStream iostr = new MemoryStream(); |
| Encoder e = new BinaryEncoder(iostr); |
| w(e, t); |
| iostr.Flush(); |
| Assert.AreEqual(size, iostr.Length); |
| iostr.Position = 0; |
| Decoder d = new BinaryDecoder(iostr); |
| T actual = r(d); |
| Assert.AreEqual(t, actual); |
| Assert.AreEqual(-1, iostr.ReadByte()); |
| iostr.Close(); |
| } |
| |
| /// <summary> |
| /// Writes an avro type T with value t into a stream using the encode method e |
| /// and reads it back using the decode method d and verifies that |
| /// the value read back is the same as the one written in. |
| /// </summary> |
| /// <typeparam name="T">Avro type to test</typeparam> |
| /// <param name="t">Value for the Avro type to test.</param> |
| /// <param name="r">The skip method</param> |
| /// <param name="w">The encode method</param> |
| private void TestSkip<T>(T t, Skip<T> s, Encode<T> w, int size) |
| { |
| MemoryStream iostr = new MemoryStream(); |
| Encoder e = new BinaryEncoder(iostr); |
| w(e, t); |
| iostr.Flush(); |
| Assert.AreEqual(size, iostr.Length); |
| iostr.Position = 0; |
| Decoder d = new BinaryDecoder(iostr); |
| s(d); |
| Assert.AreEqual(-1, iostr.ReadByte()); |
| iostr.Close(); |
| } |
| |
| |
| [TestCase(true)] |
| [TestCase(false)] |
| public void TestBoolean(bool b) |
| { |
| TestRead(b, (Decoder d) => d.ReadBoolean(), (Encoder e, bool t) => e.WriteBoolean(t), 1); |
| TestSkip(b, (Decoder d) => d.SkipBoolean(), (Encoder e, bool t) => e.WriteBoolean(t), 1); |
| } |
| |
| [TestCase(0, 1)] |
| [TestCase(1, 1)] |
| [TestCase(63, 1)] |
| [TestCase(64, 2)] |
| [TestCase(8191, 2)] |
| [TestCase(8192, 3)] |
| [TestCase(1048575, 3)] |
| [TestCase(1048576, 4)] |
| [TestCase(134217727, 4)] |
| [TestCase(134217728, 5)] |
| [TestCase(2147483647, 5)] |
| [TestCase(-1, 1)] |
| [TestCase(-64, 1)] |
| [TestCase(-65, 2)] |
| [TestCase(-8192, 2)] |
| [TestCase(-8193, 3)] |
| [TestCase(-1048576, 3)] |
| [TestCase(-1048577, 4)] |
| [TestCase(-134217728, 4)] |
| [TestCase(-134217729, 5)] |
| [TestCase(-2147483648, 5)] |
| public void TestInt(int n, int size) |
| { |
| TestRead(n, (Decoder d) => d.ReadInt(), (Encoder e, int t) => e.WriteInt(t), size); |
| TestSkip(n, (Decoder d) => d.SkipInt(), (Encoder e, int t) => e.WriteInt(t), size); |
| } |
| |
| [TestCase(0, 1)] |
| [TestCase(1, 1)] |
| [TestCase(63, 1)] |
| [TestCase(64, 2)] |
| [TestCase(8191, 2)] |
| [TestCase(8192, 3)] |
| [TestCase(1048575, 3)] |
| [TestCase(1048576, 4)] |
| [TestCase(134217727, 4)] |
| [TestCase(134217728, 5)] |
| [TestCase(17179869183L, 5)] |
| [TestCase(17179869184L, 6)] |
| [TestCase(2199023255551L, 6)] |
| [TestCase(2199023255552L, 7)] |
| [TestCase(281474976710655L, 7)] |
| [TestCase(281474976710656L, 8)] |
| [TestCase(36028797018963967L, 8)] |
| [TestCase(36028797018963968L, 9)] |
| [TestCase(4611686018427387903L, 9)] |
| [TestCase(4611686018427387904L, 10)] |
| [TestCase(9223372036854775807L, 10)] |
| [TestCase(-1, 1)] |
| [TestCase(-64, 1)] |
| [TestCase(-65, 2)] |
| [TestCase(-8192, 2)] |
| [TestCase(-8193, 3)] |
| [TestCase(-1048576, 3)] |
| [TestCase(-1048577, 4)] |
| [TestCase(-134217728, 4)] |
| [TestCase(-134217729, 5)] |
| [TestCase(-17179869184L, 5)] |
| [TestCase(-17179869185L, 6)] |
| [TestCase(-2199023255552L, 6)] |
| [TestCase(-2199023255553L, 7)] |
| [TestCase(-281474976710656L, 7)] |
| [TestCase(-281474976710657L, 8)] |
| [TestCase(-36028797018963968L, 8)] |
| [TestCase(-36028797018963969L, 9)] |
| [TestCase(-4611686018427387904L, 9)] |
| [TestCase(-4611686018427387905L, 10)] |
| [TestCase(-9223372036854775808L, 10)] |
| public void TestLong(long n, int size) |
| { |
| TestRead(n, (Decoder d) => d.ReadLong(), (Encoder e, long t) => e.WriteLong(t), size); |
| TestSkip(n, (Decoder d) => d.SkipLong(), (Encoder e, long t) => e.WriteLong(t), size); |
| } |
| |
| [TestCase(0.0f)] |
| [TestCase(Single.MaxValue, Description = "Max value")] |
| [TestCase(1.17549435E-38f, Description = "Min 'normal' value")] |
| [TestCase(1.4e-45f, Description = "Min value")] |
| public void TestFloat(float n) |
| { |
| TestRead(n, (Decoder d) => d.ReadFloat(), (Encoder e, float t) => e.WriteFloat(t), 4); |
| TestSkip(n, (Decoder d) => d.SkipFloat(), (Encoder e, float t) => e.WriteFloat(t), 4); |
| } |
| |
| [TestCase(0.0)] |
| [TestCase(1.7976931348623157e+308, Description = "Max value")] |
| [TestCase(2.2250738585072014E-308, Description = "Min 'normal' value")] |
| [TestCase(4.9e-324, Description = "Min value")] |
| public void TestDouble(double n) |
| { |
| TestRead(n, (Decoder d) => d.ReadDouble(), (Encoder e, double t) => e.WriteDouble(t), 8); |
| TestSkip(n, (Decoder d) => d.SkipDouble(), (Encoder e, double t) => e.WriteDouble(t), 8); |
| } |
| |
| |
| [TestCase(0, 1)] |
| [TestCase(5, 1)] |
| [TestCase(63, 1)] |
| [TestCase(64, 2)] |
| [TestCase(8191, 2)] |
| [TestCase(8192, 3)] |
| public void TestBytes(int length, int overhead) |
| { |
| Random r = new Random(); |
| byte[] b = new byte[length]; |
| r.NextBytes(b); |
| TestRead(b, (Decoder d) => d.ReadBytes(), (Encoder e, byte[] t) => e.WriteBytes(t), overhead + b.Length); |
| TestSkip(b, (Decoder d) => d.SkipBytes(), (Encoder e, byte[] t) => e.WriteBytes(t), overhead + b.Length); |
| } |
| |
| [TestCase("", 1)] |
| [TestCase("hello", 1)] |
| [TestCase("1234567890123456789012345678901234567890123456789012345678901234", 2)] |
| [TestCase("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456", 2)] |
| public void TestString(string n, int overhead) |
| { |
| TestRead(n, (Decoder d) => d.ReadString(), (Encoder e, string t) => e.WriteString(t), overhead + n.Length); |
| TestSkip(n, (Decoder d) => d.SkipString(), (Encoder e, string t) => e.WriteString(t), overhead + n.Length); |
| } |
| |
| #if NETCOREAPP3_1 |
| [Test] |
| public void TestLargeString() |
| { |
| // Create a 16KB buffer in the Array Pool |
| var largeBufferToSeedPool = ArrayPool<byte>.Shared.Rent(2 << 14); |
| ArrayPool<byte>.Shared.Return(largeBufferToSeedPool); |
| |
| // Create a slightly less than 16KB buffer, which will use the 16KB buffer in the pool |
| var n = string.Concat(Enumerable.Repeat("1234567890", 1600)); |
| var overhead = 3; |
| |
| TestRead(n, (Decoder d) => d.ReadString(), (Encoder e, string t) => e.WriteString(t), overhead + n.Length); |
| TestSkip(n, (Decoder d) => d.SkipString(), (Encoder e, string t) => e.WriteString(t), overhead + n.Length); |
| } |
| #endif |
| |
| [TestCase(0, 1)] |
| [TestCase(1, 1)] |
| [TestCase(64, 2)] |
| public void TestEnum(int n, int size) |
| { |
| TestRead(n, (Decoder d) => d.ReadEnum(), (Encoder e, int t) => e.WriteEnum(t), size); |
| TestSkip(n, (Decoder d) => d.SkipEnum(), (Encoder e, int t) => e.WriteEnum(t), size); |
| } |
| |
| [TestCase(1, new int[] { })] |
| [TestCase(3, new int[] { 0 })] |
| [TestCase(4, new int[] { 64 })] |
| public void TestArray(int size, int[] entries) |
| { |
| TestRead(entries, (Decoder d) => |
| { |
| int[] t = new int[entries.Length]; |
| int j = 0; |
| for (long n = d.ReadArrayStart(); n != 0; n = d.ReadArrayNext()) |
| { |
| for (int i = 0; i < n; i++) { t[j++] = d.ReadInt(); } |
| |
| } |
| return t; |
| }, |
| (Encoder e, int[] t) => |
| { |
| e.WriteArrayStart(); |
| e.SetItemCount(t.Length); |
| foreach (int i in t) { e.StartItem(); e.WriteInt(i); } e.WriteArrayEnd(); |
| }, size); |
| |
| TestSkip(entries, (Decoder d) => |
| { |
| for (long n = d.ReadArrayStart(); n != 0; n = d.ReadArrayNext()) |
| { |
| for (int i = 0; i < n; i++) { d.SkipInt(); } |
| |
| } |
| }, |
| (Encoder e, int[] t) => |
| { |
| e.WriteArrayStart(); |
| e.SetItemCount(t.Length); |
| foreach (int i in t) { e.StartItem(); e.WriteInt(i); } e.WriteArrayEnd(); |
| }, size); |
| } |
| |
| [TestCase(1, new string[] { })] |
| [TestCase(6, new string[] { "a", "b" })] |
| [TestCase(9, new string[] { "a", "b", "c", "" })] |
| public void TestMap(int size, string[] entries) |
| { |
| TestRead(entries, (Decoder d) => |
| { |
| string[] t = new string[entries.Length]; |
| int j = 0; |
| for (long n = d.ReadArrayStart(); n != 0; n = d.ReadArrayNext()) |
| { |
| for (int i = 0; i < n; i++) { t[j++] = d.ReadString(); t[j++] = d.ReadString(); } |
| |
| } |
| return t; |
| }, |
| (Encoder e, string[] t) => |
| { |
| e.WriteArrayStart(); |
| e.SetItemCount(t.Length / 2); |
| for (int i = 0; i < t.Length; i += 2) |
| { |
| e.StartItem(); e.WriteString(t[i]); e.WriteString(t[i + 1]); |
| } |
| e.WriteArrayEnd(); |
| }, size); |
| |
| TestSkip(entries, (Decoder d) => |
| { |
| for (long n = d.ReadArrayStart(); n != 0; n = d.ReadArrayNext()) |
| { |
| for (int i = 0; i < n; i++) { d.SkipString(); d.SkipString(); } |
| |
| } |
| }, |
| (Encoder e, string[] t) => |
| { |
| e.WriteArrayStart(); |
| e.SetItemCount(t.Length / 2); |
| for (int i = 0; i < t.Length; i += 2) |
| { |
| e.StartItem(); e.WriteString(t[i]); e.WriteString(t[i + 1]); |
| } |
| e.WriteArrayEnd(); |
| }, size); |
| } |
| |
| [TestCase(0, 1)] |
| [TestCase(1, 1)] |
| [TestCase(64, 2)] |
| public void TestUnionIndex(int n, int size) |
| { |
| TestRead(n, (Decoder d) => d.ReadUnionIndex(), (Encoder e, int t) => e.WriteUnionIndex(t), size); |
| TestSkip(n, (Decoder d) => d.SkipUnionIndex(), (Encoder e, int t) => e.WriteUnionIndex(t), size); |
| } |
| |
| [TestCase(0)] |
| [TestCase(1)] |
| [TestCase(64)] |
| public void TestFixed(int size) |
| { |
| byte[] b = new byte[size]; |
| new Random().NextBytes(b); |
| TestRead(b, (Decoder d) => { byte[] t = new byte[size]; d.ReadFixed(t); return t; }, |
| (Encoder e, byte[] t) => e.WriteFixed(t), size); |
| TestSkip(b, (Decoder d) => d.SkipFixed(size), |
| (Encoder e, byte[] t) => e.WriteFixed(t), size); |
| } |
| } |
| } |