This is an automated email from the ASF dual-hosted git repository.
RyanSkraba pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/avro.git
The following commit(s) were added to refs/heads/main by this push:
new c5f66c7cb5 AVRO-2825: [csharp] Resolve: C# Logical Types throw
exception on unknown (#3727)
c5f66c7cb5 is described below
commit c5f66c7cb5f522f7c6fa7ce68e640e3833525d8a
Author: RawScape <[email protected]>
AuthorDate: Sun May 10 14:23:20 2026 +0200
AVRO-2825: [csharp] Resolve: C# Logical Types throw exception on unknown
(#3727)
* AVRO-2825: [csharp] Resolve: C# Logical Types throw exception on unknown
logical type
(cherry picked from commit ad61af02da435652ba0662d34205f60fa88537cb)
* [AVRO-3941] CSharp Resolve missing namespace issue
(cherry picked from commit e5a7dedf9959687637f621bfdecbe478a5a89cc4)
* Resolve requested changes on PR 2751
(cherry picked from commit 89095295036d0dbd180fa4265205f0d666b3c0d9)
* uncomment testcase after testing
(cherry picked from commit 235d329118764005c2ace77bc7b26e472bcd5e14)
* savepoint remove local setting files
(cherry picked from commit f7b4872a347938d090193a55beba3ff2a31bcefa)
* backout chags for a different pr, adjust unit test
(cherry picked from commit 60463b5da7c885ce4e90ff9cf2d81c7682d59b0b)
* changes from code review
(cherry picked from commit 42b0fbd0a665cf409b85e329151f5b5d3e2a07e8)
* Resolve remaining feedback for AVRO-2825 and fix complex logical type
parsing
* Fix CodeQL warning: Use pattern matching to assert logicalSchema type
safely
* Remove accidentally tracked launchSettings.json
---------
Co-authored-by: Tom Bruns <[email protected]>
---
.../csharp/src/apache/main/Schema/LogicalSchema.cs | 18 +-
.../src/apache/main/Util/LogicalTypeFactory.cs | 20 +--
.../src/apache/main/Util/UnknownLogicalType.cs | 145 ++++++++++++++++
.../src/apache/test/AvroGen/AvroGenSchemaTests.cs | 10 +-
lang/csharp/src/apache/test/File/FileTests.cs | 33 ++++
lang/csharp/src/apache/test/Schema/SchemaTests.cs | 73 +++++++-
.../apache/test/Util/UnknownLogicalTypeTests.cs | 188 +++++++++++++++++++++
7 files changed, 469 insertions(+), 18 deletions(-)
diff --git a/lang/csharp/src/apache/main/Schema/LogicalSchema.cs
b/lang/csharp/src/apache/main/Schema/LogicalSchema.cs
index 181260f2ca..49d04875d5 100644
--- a/lang/csharp/src/apache/main/Schema/LogicalSchema.cs
+++ b/lang/csharp/src/apache/main/Schema/LogicalSchema.cs
@@ -47,14 +47,27 @@ namespace Avro
JToken jtype = jtok["type"];
if (null == jtype) throw new AvroTypeException("Logical Type does
not have 'type'");
- return new LogicalSchema(Schema.ParseJson(jtype, names, encspace),
JsonHelper.GetRequiredString(jtok, "logicalType"), props);
+ JToken baseSchemaToken = jtype;
+
+ if (jtok is JObject jo && jtype.Type == JTokenType.String)
+ {
+ string typeStr = (string)jtype;
+ if (typeStr == "record" || typeStr == "enum" || typeStr ==
"array" || typeStr == "map" || typeStr == "fixed")
+ {
+ var clone = (JObject)jo.DeepClone();
+ clone.Property("logicalType")?.Remove();
+ baseSchemaToken = clone;
+ }
+ }
+
+ return new LogicalSchema(Schema.ParseJson(baseSchemaToken, names,
encspace), JsonHelper.GetRequiredString(jtok, "logicalType"), props);
}
private LogicalSchema(Schema baseSchema, string logicalTypeName,
PropertyMap props) : base(Type.Logical, props)
{
BaseSchema = baseSchema ?? throw new
ArgumentNullException(nameof(baseSchema));
LogicalTypeName = logicalTypeName;
- LogicalType =
LogicalTypeFactory.Instance.GetFromLogicalSchema(this);
+ LogicalType =
LogicalTypeFactory.Instance.GetFromLogicalSchema(this, true);
}
/// <summary>
@@ -97,6 +110,7 @@ namespace Avro
if (writerSchema.Tag != Tag) return false;
LogicalSchema that = writerSchema as LogicalSchema;
+
return BaseSchema.CanRead(that.BaseSchema);
}
diff --git a/lang/csharp/src/apache/main/Util/LogicalTypeFactory.cs
b/lang/csharp/src/apache/main/Util/LogicalTypeFactory.cs
index f4086ab5a2..e92d48ff7a 100644
--- a/lang/csharp/src/apache/main/Util/LogicalTypeFactory.cs
+++ b/lang/csharp/src/apache/main/Util/LogicalTypeFactory.cs
@@ -67,22 +67,22 @@ namespace Avro.Util
/// <returns>A <see cref="LogicalType" />.</returns>
public LogicalType GetFromLogicalSchema(LogicalSchema schema, bool
ignoreInvalidOrUnknown = false)
{
- try
- {
- if (!_logicalTypes.TryGetValue(schema.LogicalTypeName, out
LogicalType logicalType))
- throw new AvroTypeException("Logical type '" +
schema.LogicalTypeName + "' is not supported.");
+ LogicalType logicalType = null;
+ if (_logicalTypes.TryGetValue(schema.LogicalTypeName, out
logicalType))
+ {
logicalType.ValidateSchema(schema);
-
- return logicalType;
}
- catch (AvroTypeException)
+ else if (ignoreInvalidOrUnknown)
+ {
+ logicalType = new UnknownLogicalType(schema);
+ }
+ else
{
- if (!ignoreInvalidOrUnknown)
- throw;
+ throw new AvroTypeException("Logical type '" +
schema.LogicalTypeName + "' is not supported.");
}
- return null;
+ return logicalType;
}
}
}
diff --git a/lang/csharp/src/apache/main/Util/UnknownLogicalType.cs
b/lang/csharp/src/apache/main/Util/UnknownLogicalType.cs
new file mode 100644
index 0000000000..5cc03fe289
--- /dev/null
+++ b/lang/csharp/src/apache/main/Util/UnknownLogicalType.cs
@@ -0,0 +1,145 @@
+/*
+ * 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.Generic;
+using System.Text;
+
+namespace Avro.Util
+{
+ /// <summary>
+ /// Class UnknownLogicalType.
+ /// Implements the <see cref="Avro.Util.LogicalType" />
+ /// </summary>
+ /// <seealso cref="Avro.Util.LogicalType" />
+ public class UnknownLogicalType : LogicalType
+ {
+ /// <summary>
+ /// Gets the schema.
+ /// </summary>
+ /// <value>The schema.</value>
+ public LogicalSchema Schema { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UnknownLogicalType"/>
class.
+ /// </summary>
+ /// <param name="schema">The schema.</param>
+ public UnknownLogicalType(LogicalSchema schema) :
base(schema.LogicalTypeName)
+ {
+ this.Schema = schema;
+ }
+
+ /// <summary>
+ /// Converts a logical value to an instance of its base type.
+ /// </summary>
+ /// <param name="logicalValue">The logical value to convert.</param>
+ /// <param name="schema">The schema that represents the target of the
conversion.</param>
+ /// <returns>An object representing the encoded value of the base
type.</returns>
+ public override object ConvertToBaseValue(object logicalValue,
LogicalSchema schema)
+ {
+ return logicalValue;
+ }
+
+ /// <summary>
+ /// Converts a base value to an instance of the logical type.
+ /// </summary>
+ /// <param name="baseValue">The base value to convert.</param>
+ /// <param name="schema">The schema that represents the target of the
conversion.</param>
+ /// <returns>An object representing the encoded value of the logical
type.</returns>
+ public override object ConvertToLogicalValue(object baseValue,
LogicalSchema schema)
+ {
+ switch (schema.Name)
+ {
+ case @"string":
+ return (System.String)baseValue;
+ case @"boolean":
+ return (System.Boolean)baseValue;
+ case @"int":
+ return (System.Int32)baseValue;
+ case @"long":
+ return (System.Int64)baseValue;
+ case @"float":
+ return (System.Single)baseValue;
+ case @"double":
+ return (System.Double)baseValue;
+ case @"bytes":
+ return (System.Byte[])baseValue;
+ default:
+ return baseValue;
+ }
+ }
+
+ /// <summary>
+ /// Retrieve the .NET type that is represented by the logical type
implementation.
+ /// </summary>
+ /// <param name="nullible">A flag indicating whether it should be
nullible.</param>
+ /// <returns>Type.</returns>
+ public override Type GetCSharpType(bool nullible)
+ {
+ // handle all Primitive Types
+ switch (this.Schema.BaseSchema.Name)
+ {
+ case @"string":
+ return typeof(System.String);
+ case @"boolean":
+ return nullible ? typeof(System.Boolean?) :
typeof(System.Boolean);
+ case @"int":
+ return nullible ? typeof(System.Int32?) :
typeof(System.Int32);
+ case @"long":
+ return nullible ? typeof(System.Int64?) :
typeof(System.Int64);
+ case @"float":
+ return nullible ? typeof(System.Single?) :
typeof(System.Single);
+ case @"double":
+ return nullible ? typeof(System.Double?) :
typeof(System.Double);
+ case @"bytes":
+ return typeof(System.Byte[]);
+ default:
+ return typeof(System.Object);
+ }
+ }
+
+ /// <summary>
+ /// Determines if a given object is an instance of the logical type.
+ /// </summary>
+ /// <param name="logicalValue">The logical value to test.</param>
+ /// <returns><c>true</c> if [is instance of logical type] [the
specified logical value]; otherwise, <c>false</c>.</returns>
+ public override bool IsInstanceOfLogicalType(object logicalValue)
+ {
+ // handle all Primitive Types
+ switch (this.Schema.BaseSchema.Name)
+ {
+ case @"string":
+ return logicalValue is System.String;
+ case @"boolean":
+ return logicalValue is System.Boolean;
+ case @"int":
+ return logicalValue is System.Int32;
+ case @"long":
+ return logicalValue is System.Int64;
+ case @"float":
+ return logicalValue is System.Single;
+ case @"double":
+ return logicalValue is System.Double;
+ case @"bytes":
+ return logicalValue is System.Byte[];
+ default:
+ return true;
+ }
+ }
+
+ }
+}
diff --git a/lang/csharp/src/apache/test/AvroGen/AvroGenSchemaTests.cs
b/lang/csharp/src/apache/test/AvroGen/AvroGenSchemaTests.cs
index 807acbda92..2763318c87 100644
--- a/lang/csharp/src/apache/test/AvroGen/AvroGenSchemaTests.cs
+++ b/lang/csharp/src/apache/test/AvroGen/AvroGenSchemaTests.cs
@@ -606,9 +606,9 @@ namespace Avro.Test.AvroGen
AvroGenHelper.TestSchema(schema, typeNamesToCheck, new
Dictionary<string, string> { { namespaceMappingFrom, namespaceMappingTo } },
generatedFilesToCheck);
}
- [TestCase(_logicalTypesWithCustomConversion,
typeof(AvroTypeException))]
- [TestCase(_customConversionWithLogicalTypes,
typeof(SchemaParseException))]
- public void NotSupportedSchema(string schema, Type expectedException)
+ [TestCase(_logicalTypesWithCustomConversion)]
+ [TestCase(_customConversionWithLogicalTypes)]
+ public void UnknownLogicalTypesFallbackToBaseType(string schema)
{
// Create temp folder
string outputDir = AvroGenHelper.CreateEmptyTemporaryFolder(out
string uniqueId);
@@ -619,7 +619,9 @@ namespace Avro.Test.AvroGen
string schemaFileName = Path.Combine(outputDir,
$"{uniqueId}.avsc");
System.IO.File.WriteAllText(schemaFileName, schema);
- Assert.That(AvroGenTool.GenSchema(schemaFileName, outputDir,
new Dictionary<string, string>(), false), Is.EqualTo(1));
+ // Assert that the generator successfully runs (exit code 0)
+ // by ignoring the unknown logical types and using the
underlying base types
+ Assert.That(AvroGenTool.GenSchema(schemaFileName, outputDir,
new Dictionary<string, string>(), false), Is.EqualTo(0));
}
finally
{
diff --git a/lang/csharp/src/apache/test/File/FileTests.cs
b/lang/csharp/src/apache/test/File/FileTests.cs
index 0ef81c9766..abb3f9c607 100644
--- a/lang/csharp/src/apache/test/File/FileTests.cs
+++ b/lang/csharp/src/apache/test/File/FileTests.cs
@@ -35,6 +35,34 @@ namespace Avro.Test.File
const string specificSchema =
"{\"type\":\"record\",\"name\":\"Foo\",\"namespace\":\"Avro.Test.File\",\"fields\":"
+
"[{\"name\":\"name\",\"type\":[\"null\",\"string\"]},{\"name\":\"age\",\"type\":\"int\"}]}";
+ /// <summary>
+ /// This test case added to confirm standalone serialization /
deserialization behavior of new type UnknownLogicalType
+ /// </summary>
+ const string unknowLogicalTypeSchema = @"
+{
+ ""type"" : ""record"",
+ ""name"" : ""Foo"",
+ ""namespace"" : ""Avro.Test.File"",
+ ""fields"": [
+ {
+ ""name"" :""name"",
+ ""type"": [
+ ""null"",
+ {
+ ""logicalType"": ""varchar"",
+ ""maxLength"": 65,
+ ""type"": ""string""
+ }
+ ]
+ },
+ {
+ ""name"" : ""age"",
+ ""type"" : ""int""
+ }
+ ]
+}
+";
+
private static IEnumerable<TestCaseData> TestSpecificDataSource()
{
foreach (Codec.Type codecType in
Enum.GetValues(typeof(Codec.Type)))
@@ -100,6 +128,11 @@ namespace Avro.Test.File
new object[] { "Bob", 9 },
new object[] { null, 48 }
}, codecType).SetName("{m}(Case3,{2})");
+
+ yield return new TestCaseData(unknowLogicalTypeSchema, new
object[]
+ {
+ new object[] { "John", 23 }
+ }, codecType).SetName("{m}(Case4,{2})");
}
}
diff --git a/lang/csharp/src/apache/test/Schema/SchemaTests.cs
b/lang/csharp/src/apache/test/Schema/SchemaTests.cs
index 221f34eec0..309ecf4d60 100644
--- a/lang/csharp/src/apache/test/Schema/SchemaTests.cs
+++ b/lang/csharp/src/apache/test/Schema/SchemaTests.cs
@@ -19,6 +19,7 @@ using System;
using System.Collections.Generic;
using NUnit.Framework;
using System.Linq;
+using Avro.Util;
namespace Avro.Test
{
@@ -567,12 +568,80 @@ namespace Avro.Test
testToString(sc);
}
+ // Make sure unknown type is carried thru to LogicalTypeName
[TestCase("{\"type\": \"int\", \"logicalType\": \"unknown\"}",
"unknown")]
public void TestUnknownLogical(string s, string unknownType)
{
- var err = Assert.Throws<AvroTypeException>(() => Schema.Parse(s));
+ var schema = Schema.Parse(s);
+ Assert.IsNotNull(schema);
+ Assert.IsInstanceOf(typeof(LogicalSchema), schema);
- Assert.AreEqual("Logical type '" + unknownType + "' is not
supported.", err.Message);
+ if (schema is LogicalSchema logicalSchema)
+ {
+ Assert.IsInstanceOf(typeof(UnknownLogicalType),
logicalSchema.LogicalType);
+ Assert.AreEqual(logicalSchema.LogicalTypeName, unknownType);
+ }
+ else
+ {
+ Assert.Fail("Parsed schema was not a LogicalSchema");
+ }
+ }
+
+ /*
+ {
+ "fields": [
+ {
+ "default": 0,
+ "name": "firstField",
+ "type": "int"
+ },
+ {
+ "default": null,
+ "name": "secondField",
+ "type": [
+ "null",
+ {
+ "logicalType": "varchar",
+ "maxLength": 65,
+ "type": "string"
+ }
+ ]
+ }
+ ],
+ "name": "sample_schema",
+ "type": "record"
+ }
+ */
+
+ // Before Change will throw Avro.AvroTypeException: 'Logical type
'varchar' is not supported.'
+ // Per AVRO Spec (v1.8.0 - v1.11.1) ... Logical Types Section
+ // Language implementations must ignore unknown logical types when
reading, and should use the underlying Avro type.
+ [TestCase("{\"fields\": [{\"default\": 0,\"name\":
\"firstField\",\"type\": \"int\"},{\"default\": null,\"name\":
\"secondField\",\"type\": [\"null\",{\"logicalType\":
\"varchar\",\"maxLength\": 65,\"type\": \"string\"}]}],\"name\":
\"sample_schema\",\"type\": \"record\"}")]
+ public void TestUnknownLogicalType(string schemaText)
+ {
+ var schema = Avro.Schema.Parse(schemaText);
+ Assert.IsNotNull(schema);
+
+ var secondField = ((RecordSchema)schema).Fields.FirstOrDefault(f
=> f.Name == @"secondField");
+ Assert.IsNotNull(secondField);
+
+ var secondFieldSchema = (secondField).Schema;
+ Assert.IsNotNull(secondFieldSchema);
+
+ var secondFieldUnionSchema = (UnionSchema)secondFieldSchema;
+ Assert.IsNotNull(secondFieldUnionSchema);
+
+ var props = secondFieldUnionSchema.Schemas.Where(s => s.Props !=
null).ToList();
+ Assert.IsNotNull(props);
+ Assert.IsTrue(props.Count == 1);
+
+ var prop = props[0];
+ // Confirm that the unknown logical type is ignored and the
underlying AVRO type is used
+ Assert.IsTrue(prop.Name == @"string");
+ var logicalSchema = prop as LogicalSchema;
+ Assert.IsInstanceOf(typeof(UnknownLogicalType),
logicalSchema.LogicalType);
+
+ Assert.AreEqual(logicalSchema.LogicalTypeName, @"varchar");
}
[TestCase("{\"type\": \"map\", \"values\": \"long\"}", "long")]
diff --git a/lang/csharp/src/apache/test/Util/UnknownLogicalTypeTests.cs
b/lang/csharp/src/apache/test/Util/UnknownLogicalTypeTests.cs
new file mode 100644
index 0000000000..43cc2f3a46
--- /dev/null
+++ b/lang/csharp/src/apache/test/Util/UnknownLogicalTypeTests.cs
@@ -0,0 +1,188 @@
+/*
+ * 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.Data.SqlTypes;
+using System.Globalization;
+using System.Net.Sockets;
+using System.Numerics;
+using Avro.Util;
+using NUnit.Framework;
+using NUnit.Framework.Constraints;
+
+namespace Avro.test.Util
+{
+ /// <summary>
+ /// This tests added to confirm standalone operation of new type
UnknownLogicalType that implements LogicalType
+ /// </summary>
+ [TestFixture]
+ class UnknownLogicalTypeTests
+ {
+ [TestCase(typeof(System.String), "", "{\"type\": \"string\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Boolean), true, "{\"type\": \"boolean\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int32), Int32.MinValue, "{\"type\": \"int\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int64), Int64.MinValue, "{\"type\": \"long\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Single), Single.MinValue, "{\"type\":
\"float\", \"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Double), Double.MinValue, "{\"type\":
\"double\", \"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Byte[]), new byte[] { }, "{\"type\":
\"bytes\", \"logicalType\": \"unknown\"}")]
+ public void TestConvertToBaseValue_IsTrue(Type baseType, object
logicalValue, string schemaText)
+ {
+ var schema = (LogicalSchema)Schema.Parse(schemaText);
+
+ var logicalType = new UnknownLogicalType(schema);
+
+ var baseValue = logicalType.ConvertToBaseValue(logicalValue,
schema);
+
+ Assert.AreEqual(baseValue, Convert.ChangeType(logicalValue,
baseType));
+ }
+
+ [TestCase(typeof(System.Byte[]), "", "{\"type\": \"string\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Double), true, "{\"type\": \"boolean\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Single), Int32.MinValue, "{\"type\": \"int\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Boolean), Int64.MinValue, "{\"type\":
\"long\", \"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int32), Single.MinValue, "{\"type\":
\"float\", \"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int64), Double.MinValue, "{\"type\":
\"double\", \"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.String), new byte[] { }, "{\"type\":
\"bytes\", \"logicalType\": \"unknown\"}")]
+ public void TestConvertToBaseValue_IsFalse(Type baseType, object
logicalValue, string schemaText)
+ {
+ var schema = (LogicalSchema)Schema.Parse(schemaText);
+
+ var logicalType = new UnknownLogicalType(schema);
+
+ var baseValue = logicalType.ConvertToBaseValue(logicalValue,
schema);
+
+ Assert.AreNotEqual(baseValue.GetType(), baseType);
+ }
+
+ [TestCase(typeof(System.String), "", "{\"type\": \"string\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Boolean), true, "{\"type\": \"boolean\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int32), Int32.MinValue, "{\"type\": \"int\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int64), Int64.MinValue, "{\"type\": \"long\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Single), Single.MinValue, "{\"type\":
\"float\", \"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Double), Double.MinValue, "{\"type\":
\"double\", \"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Byte[]), new byte[] { }, "{\"type\":
\"bytes\", \"logicalType\": \"unknown\"}")]
+ public void TestConvertToLogicalValue_IsTrue(Type baseType, object
logicalValue, string schemaText)
+ {
+ var schema = (LogicalSchema)Schema.Parse(schemaText);
+
+ var logicalType = new UnknownLogicalType(schema);
+
+ var baseValue = logicalType.ConvertToLogicalValue(logicalValue,
schema);
+
+ Assert.AreEqual(baseValue, Convert.ChangeType(logicalValue,
baseType));
+ }
+
+ [TestCase(typeof(System.Byte[]), "", "{\"type\": \"string\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Double), true, "{\"type\": \"boolean\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Single), Int32.MinValue, "{\"type\": \"int\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Boolean), Int64.MinValue, "{\"type\":
\"long\", \"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int32), Single.MinValue, "{\"type\":
\"float\", \"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int64), Double.MinValue, "{\"type\":
\"double\", \"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.String), new byte[] { }, "{\"type\":
\"bytes\", \"logicalType\": \"unknown\"}")]
+ public void TestConvertToLogicalValue_IsFalse(Type baseType, object
logicalValue, string schemaText)
+ {
+ var schema = (LogicalSchema)Schema.Parse(schemaText);
+
+ var logicalType = new UnknownLogicalType(schema);
+
+ var baseValue = logicalType.ConvertToLogicalValue(logicalValue,
schema);
+
+ Assert.AreNotEqual(baseValue.GetType(), baseType);
+ }
+
+ [TestCase(typeof(System.String), false, "{\"type\": \"string\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Boolean), false, "{\"type\": \"boolean\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int32), false, "{\"type\": \"int\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int64), false, "{\"type\": \"long\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Single), false, "{\"type\": \"float\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Double), false, "{\"type\": \"double\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Byte[]), false, "{\"type\": \"bytes\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.String), true, "{\"type\": \"string\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Boolean?), true, "{\"type\": \"boolean\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int32?), true, "{\"type\": \"int\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int64?), true, "{\"type\": \"long\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Single?), true, "{\"type\": \"float\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Double?), true, "{\"type\": \"double\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Byte[]), true, "{\"type\": \"bytes\",
\"logicalType\": \"unknown\"}")]
+ public void TestGetCSharpType_IsTrue(Type type, bool isNullable,
string schemaText)
+ {
+ var schema = (LogicalSchema)Schema.Parse(schemaText);
+
+ var logicalType = new UnknownLogicalType(schema);
+
+ Assert.AreEqual(logicalType.GetCSharpType(isNullable), type);
+ }
+
+ //[TestCase(typeof(System.String), true, "{\"type\": \"string\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Boolean), true, "{\"type\": \"boolean\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int32), true, "{\"type\": \"int\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int64), true, "{\"type\": \"long\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Single), true, "{\"type\": \"float\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Double), true, "{\"type\": \"double\",
\"logicalType\": \"unknown\"}")]
+ //[TestCase(typeof(System.Byte[]), true, "{\"type\": \"bytes\",
\"logicalType\": \"unknown\"}")]
+ //[TestCase(typeof(System.String), false, "{\"type\": \"string\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Boolean?), false, "{\"type\": \"boolean\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int32?), false, "{\"type\": \"int\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Int64?), false, "{\"type\": \"long\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Single?), false, "{\"type\": \"float\",
\"logicalType\": \"unknown\"}")]
+ [TestCase(typeof(System.Double?), false, "{\"type\": \"double\",
\"logicalType\": \"unknown\"}")]
+ //[TestCase(typeof(System.Byte?[]), false, "{\"type\": \"bytes\",
\"logicalType\": \"unknown\"}")]
+ public void TestGetCSharpType_IsFalse(Type type, bool isNullable,
string schemaText)
+ {
+ var schema = (LogicalSchema)Schema.Parse(schemaText);
+
+ var logicalType = new UnknownLogicalType(schema);
+
+ Assert.AreNotEqual(logicalType.GetCSharpType(isNullable), type);
+ }
+
+ [TestCase("", "{\"type\": \"string\", \"logicalType\": \"unknown\"}")]
+ [TestCase(true, "{\"type\": \"boolean\", \"logicalType\":
\"unknown\"}")]
+ [TestCase(Int32.MinValue, "{\"type\": \"int\", \"logicalType\":
\"unknown\"}")]
+ [TestCase(Int64.MinValue, "{\"type\": \"long\", \"logicalType\":
\"unknown\"}")]
+ [TestCase(Single.MinValue, "{\"type\": \"float\", \"logicalType\":
\"unknown\"}")]
+ [TestCase(Double.MinValue, "{\"type\": \"double\", \"logicalType\":
\"unknown\"}")]
+ [TestCase(new byte[] { } , "{\"type\": \"bytes\", \"logicalType\":
\"unknown\"}")]
+ public void TestIsInstanceOfLogicalType_IsTrue(object logicalValue,
string schemaText)
+ {
+ var schema = (LogicalSchema)Schema.Parse(schemaText);
+
+ var logicalType = new UnknownLogicalType(schema);
+
+ Assert.IsTrue(logicalType.IsInstanceOfLogicalType(logicalValue));
+ }
+
+ [TestCase(Int32.MinValue, "{\"type\": \"string\", \"logicalType\":
\"unknown\"}")]
+ [TestCase(new byte[] { }, "{\"type\": \"boolean\", \"logicalType\":
\"unknown\"}")]
+ [TestCase(Int64.MinValue, "{\"type\": \"int\", \"logicalType\":
\"unknown\"}")]
+ [TestCase(Single.MinValue, "{\"type\": \"long\", \"logicalType\":
\"unknown\"}")]
+ [TestCase(Double.MinValue, "{\"type\": \"float\", \"logicalType\":
\"unknown\"}")]
+ [TestCase(new byte[] { }, "{\"type\": \"double\", \"logicalType\":
\"unknown\"}")]
+ [TestCase("", "{\"type\": \"bytes\", \"logicalType\": \"unknown\"}")]
+ public void TestIsInstanceOfLogicalType_IsFalse(object logicalValue,
string schemaText)
+ {
+ var schema = (LogicalSchema)Schema.Parse(schemaText);
+
+ var logicalType = new UnknownLogicalType(schema);
+
+ Assert.IsFalse(logicalType.IsInstanceOfLogicalType(logicalValue));
+ }
+
+ // See also a new test in Avro.Tests.File in TestSpecificDataSource
using unknowLogicalTypeSchema
+ }
+}