This is an automated email from the ASF dual-hosted git repository.

rskraba pushed a commit to branch branch-1.11
in repository https://gitbox.apache.org/repos/asf/avro.git

commit 150f91006df8cc32923bebe2bbbef462f749b511
Author: Zoltan Csizmadia <[email protected]>
AuthorDate: Fri Mar 18 13:58:51 2022 -0500

    AVRO-3446: Add avrogen unit tests (#1595)
    
    * Add avrogen unit tests
    
    * Hide dynamic compiler errors
    
    * Add asserts
    
    * Bump MSBUild package versions
    
    * Tweak dotnet 6.0 install
    
    * Add comments
    
    * Use 6.0.2xx channel
    
    * Revert dotnet-install change
    
    * Add codegen tests
    
    * Add test for contextual keyword in namespace
    
    * Add AvroGenHelper
    
    * Update lang/csharp/src/apache/main/CodeGen/CodeGenUtil.cs
    
    Co-authored-by: Martin Grigorov <[email protected]>
    
    * Update lang/csharp/src/apache/test/AvroGen/AvroGenHelper.cs
    
    Co-authored-by: Martin Grigorov <[email protected]>
    
    Co-authored-by: Zoltan Csizmadia <[email protected]>
    Co-authored-by: Martin Grigorov <[email protected]>
---
 lang/csharp/src/apache/codegen/AvroGen.cs          |  17 +-
 lang/csharp/src/apache/main/CodeGen/CodeGenUtil.cs |   6 +-
 lang/csharp/src/apache/test/Avro.test.csproj       |   8 +-
 .../src/apache/test/AvroGen/AvroGenHelper.cs       | 155 +++++
 .../csharp/src/apache/test/AvroGen/AvroGenTests.cs | 664 +++++++++++++++++++++
 .../src/apache/test/AvroGen/AvroGenToolTests.cs    |  75 +++
 lang/csharp/src/apache/test/CodGen/CodeGenTest.cs  | 195 +-----
 lang/csharp/versions.props                         |  12 +-
 8 files changed, 951 insertions(+), 181 deletions(-)

diff --git a/lang/csharp/src/apache/codegen/AvroGen.cs 
b/lang/csharp/src/apache/codegen/AvroGen.cs
index 3499117..cb01671 100644
--- a/lang/csharp/src/apache/codegen/AvroGen.cs
+++ b/lang/csharp/src/apache/codegen/AvroGen.cs
@@ -1,4 +1,4 @@
-/**
+/**
  * 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
@@ -17,13 +17,13 @@
  */
 using System;
 using System.Collections.Generic;
-using System.Text;
+using System.Linq;
 
 namespace Avro
 {
-    class AvroGen
+    public class AvroGenTool
     {
-        static int Main(string[] args)
+        public static int Main(string[] args)
         {
             // Print usage if no arguments provided
             if (args.Length == 0)
@@ -33,7 +33,7 @@ namespace Avro
             }
 
             // Print usage if help requested
-            if (args[0] == "-h" || args[0] == "--help")
+            if (args.Contains("-h") || args.Contains("--help"))
             {
                 Usage();
                 return 0;
@@ -140,9 +140,9 @@ namespace Avro
                 "              The format is 
\"my.avro.namespace:my.csharp.namespace\".\n" +
                 "              May be specified multiple times to map multiple 
namespaces.\n",
                 AppDomain.CurrentDomain.FriendlyName);
-            return;
         }
-        static int GenProtocol(string infile, string outdir,
+
+        public static int GenProtocol(string infile, string outdir,
             IEnumerable<KeyValuePair<string, string>> namespaceMapping)
         {
             try
@@ -167,7 +167,8 @@ namespace Avro
 
             return 0;
         }
-        static int GenSchema(string infile, string outdir,
+
+        public static int GenSchema(string infile, string outdir,
             IEnumerable<KeyValuePair<string, string>> namespaceMapping)
         {
             try
diff --git a/lang/csharp/src/apache/main/CodeGen/CodeGenUtil.cs 
b/lang/csharp/src/apache/main/CodeGen/CodeGenUtil.cs
index 0ed10c1..fd2823d 100644
--- a/lang/csharp/src/apache/main/CodeGen/CodeGenUtil.cs
+++ b/lang/csharp/src/apache/main/CodeGen/CodeGenUtil.cs
@@ -88,6 +88,9 @@ namespace Avro
  
------------------------------------------------------------------------------");
 
             // Visual Studio 2010 
https://msdn.microsoft.com/en-us/library/x53a06bb.aspx
+            // Note:
+            //  1. Contextual keywords are not reserved keywords e.g. value, 
partial
+            //  2. __arglist, __makeref, __reftype, __refvalue are 
undocumented keywords, but recognised by the C# compiler
             ReservedKeywords = new HashSet<string>() {
                 "abstract","as", "base", "bool", "break", "byte", "case", 
"catch", "char", "checked", "class",
                 "const", "continue", "decimal", "default", "delegate", "do", 
"double", "else", "enum", "event",
@@ -96,7 +99,8 @@ namespace Avro
                 "null", "object", "operator", "out", "override", "params", 
"private", "protected", "public",
                 "readonly", "ref", "return", "sbyte", "sealed", "short", 
"sizeof", "stackalloc", "static",
                 "string", "struct", "switch", "this", "throw", "true", "try", 
"typeof", "uint", "ulong",
-                "unchecked", "unsafe", "ushort", "using", "virtual", "void", 
"volatile", "while", "value", "partial" };
+                "unchecked", "unsafe", "ushort", "using", "virtual", "void", 
"volatile", "while",
+                "__arglist", "__makeref", "__reftype", "__refvalue" };
         }
 
         /// <summary>
diff --git a/lang/csharp/src/apache/test/Avro.test.csproj 
b/lang/csharp/src/apache/test/Avro.test.csproj
index 6c359c8..18b5dd8 100644
--- a/lang/csharp/src/apache/test/Avro.test.csproj
+++ b/lang/csharp/src/apache/test/Avro.test.csproj
@@ -32,17 +32,17 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <PackageReference Include="Microsoft.CodeAnalysis" 
Version="$(MicrosoftCodeAnalysisVersion)" />
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" 
Version="$(MicrosoftCodeAnalysisCSharpVersion)" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" 
Version="$(MicrosoftNETTestSdkVersion)" />
     <PackageReference Include="NUnit" Version="$(NUnitVersion)" />
     <PackageReference Include="NUnit3TestAdapter" 
Version="$(NUnit3TestAdapterVersion)" />
     <PackageReference Include="NUnit.ConsoleRunner" 
Version="$(NUnitConsoleRunnerVersion)" />
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" 
Version="$(MicrosoftNETTestSdkVersion)" />
-  </ItemGroup>
-
-  <ItemGroup>
     <ProjectReference Include="..\main\Avro.main.csproj" />
+    <ProjectReference Include="..\codegen\Avro.codegen.csproj" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/lang/csharp/src/apache/test/AvroGen/AvroGenHelper.cs 
b/lang/csharp/src/apache/test/AvroGen/AvroGenHelper.cs
new file mode 100644
index 0000000..53d7d4e
--- /dev/null
+++ b/lang/csharp/src/apache/test/AvroGen/AvroGenHelper.cs
@@ -0,0 +1,155 @@
+/**
+ * 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.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Emit;
+using NUnit.Framework;
+
+namespace Avro.Test.AvroGen
+{
+    class AvroGenToolResult
+    {
+        public int ExitCode { get; set; }
+        public string[] StdOut { get; set; }
+        public string[] StdErr { get; set; }
+    }
+
+    class AvroGenHelper
+    {
+        public static AvroGenToolResult RunAvroGenTool(params string[] args)
+        {
+            // Save stdout and stderr
+            TextWriter conOut = Console.Out;
+            TextWriter conErr = Console.Error;
+
+            try
+            {
+                AvroGenToolResult result = new AvroGenToolResult();
+                StringBuilder strBuilderOut = new StringBuilder();
+                StringBuilder strBuilderErr = new StringBuilder();
+
+                using (StringWriter writerOut = new 
StringWriter(strBuilderOut))
+                using (StringWriter writerErr = new 
StringWriter(strBuilderErr))
+                {
+                    writerOut.NewLine = "\n";
+                    writerErr.NewLine = "\n";
+
+                    // Overwrite stdout and stderr to be able to capture 
console output
+                    Console.SetOut(writerOut);
+                    Console.SetError(writerErr);
+
+                    result.ExitCode = AvroGenTool.Main(args.ToArray());
+
+                    writerOut.Flush();
+                    writerErr.Flush();
+
+                    result.StdOut = strBuilderOut.Length == 0 ? 
Array.Empty<string>() : strBuilderOut.ToString().Split(writerOut.NewLine);
+                    result.StdErr = strBuilderErr.Length == 0 ? 
Array.Empty<string>() : strBuilderErr.ToString().Split(writerErr.NewLine);
+                }
+
+                return result;
+            }
+            finally
+            {
+                // Restore console
+                Console.SetOut(conOut);
+                Console.SetError(conErr);
+            }
+        }
+
+        public static Assembly 
CompileCSharpFilesIntoLibrary(IEnumerable<string> sourceFiles, string 
assemblyName = null, bool loadAssembly = true)
+        {
+            // Create random assembly name if not specified
+            if (assemblyName == null)
+                assemblyName = Path.GetRandomFileName();
+
+            // Base path to assemblies .NET assemblies
+            var assemblyPath = 
Path.GetDirectoryName(typeof(object).Assembly.Location);
+
+            using (var compilerStream = new MemoryStream())
+            {
+                List<string> assemblies = new List<string>()
+                {
+                    typeof(object).Assembly.Location,
+                    typeof(Schema).Assembly.Location,
+                    Path.Combine(assemblyPath, "System.Runtime.dll"),
+                    Path.Combine(assemblyPath, "netstandard.dll")
+                };
+
+                // Create compiler
+                CSharpCompilation compilation = CSharpCompilation
+                    .Create(assemblyName)
+                    .WithOptions(new 
CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
+                    .AddReferences(assemblies.Select(path => 
MetadataReference.CreateFromFile(path)))
+                    .AddSyntaxTrees(sourceFiles.Select(sourceFile =>
+                    {
+                        string sourceText = 
System.IO.File.ReadAllText(sourceFile);
+                        return CSharpSyntaxTree.ParseText(sourceText);
+                    }));
+
+                // Compile
+                EmitResult compilationResult = 
compilation.Emit(compilerStream);
+
+                //Note: Comment the following out to analyze the compiler 
errors if needed
+                //if (!compilationResult.Success)
+                //{
+                //    foreach (Diagnostic diagnostic in 
compilationResult.Diagnostics)
+                //    {
+                //        if (diagnostic.IsWarningAsError || 
diagnostic.Severity == DiagnosticSeverity.Error)
+                //        {
+                //            TestContext.WriteLine($"{diagnostic.Id} - 
{diagnostic.GetMessage()} - {diagnostic.Location}");
+                //        }
+                //    }
+                //}
+
+                Assert.That(compilationResult.Success, Is.True);
+
+                if (!loadAssembly)
+                {
+                    return null;
+                }
+
+                compilerStream.Seek(0, SeekOrigin.Begin);
+                return Assembly.Load(compilerStream.ToArray());
+            }
+        }
+
+        public static string CreateEmptyTemporyFolder(out string uniqueId, 
string path = null)
+        {
+            // Create unique id
+            uniqueId = Guid.NewGuid().ToString();
+
+            // Temporary folder name in working folder or the specified path
+            string tempFolder = Path.Combine(path ?? 
TestContext.CurrentContext.WorkDirectory, uniqueId);
+
+            // Create folder
+            Directory.CreateDirectory(tempFolder);
+
+            // Make sure it is empty
+            Assert.That(new DirectoryInfo(tempFolder), Is.Empty);
+
+            return tempFolder;
+        }
+    }
+}
diff --git a/lang/csharp/src/apache/test/AvroGen/AvroGenTests.cs 
b/lang/csharp/src/apache/test/AvroGen/AvroGenTests.cs
new file mode 100644
index 0000000..f4976d7
--- /dev/null
+++ b/lang/csharp/src/apache/test/AvroGen/AvroGenTests.cs
@@ -0,0 +1,664 @@
+/**
+ * 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.IO;
+using System.Linq;
+using System.Reflection;
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Emit;
+using NUnit.Framework;
+using Avro.Specific;
+
+namespace Avro.Test.AvroGen
+{
+    [TestFixture]
+
+    class AvroGenTests
+    {
+        private const string _customConversionWithLogicalTypes = @"
+{
+  ""namespace"": ""org.apache.avro.codegentest.testdata"",
+  ""type"": ""record"",
+  ""name"": ""CustomConversionWithLogicalTypes"",
+  ""doc"" : ""Test custom conversion and logical types in generated Java 
classes"",
+  ""fields"": [
+    {
+      ""name"": ""customEnum"",
+      ""type"": [""null"", {
+        ""namespace"": ""org.apache.avro.codegentest.testdata"",
+        ""name"": ""CustomAvroEnum"",
+        ""type"": ""enum"",
+        ""logicalType"": ""custom-enum"",
+        ""symbols"": [""ONE"", ""TWO"", ""THREE""]
+    }]
+    }]
+}
+";
+
+        private const string _logicalTypesWithCustomConversion = @"
+{
+""namespace"": ""org.apache.avro.codegentest.testdata"",
+  ""type"": ""record"",
+  ""name"": ""LogicalTypesWithCustomConversion"",
+  ""doc"" : ""Test unions with logical types in generated Java classes"",
+  ""fields"": [
+    {""name"": ""nullableCustomField"",  ""type"": [""null"", {""type"": 
""bytes"", ""logicalType"": ""decimal"", ""precision"": 9, ""scale"": 2}], 
""default"": null},
+    { ""name"": ""nonNullCustomField"",  ""type"": { ""type"": ""bytes"", 
""logicalType"": ""decimal"", ""precision"": 9, ""scale"": 2} },
+    { ""name"": ""nullableFixedSizeString"",  ""type"": [""null"", { ""type"": 
""bytes"", ""logicalType"": ""fixed-size-string"", ""minLength"": 1, 
""maxLength"": 50}], ""default"": null},
+    { ""name"": ""nonNullFixedSizeString"",  ""type"": { ""type"": ""bytes"", 
""logicalType"": ""fixed-size-string"", ""minLength"": 1, ""maxLength"": 50} }
+  ]
+}
+";
+
+        private const string _logicalTypesWithDefaults = @"
+{
+""namespace"": ""org.apache.avro.codegentest.testdata"",
+  ""type"": ""record"",
+  ""name"": ""LogicalTypesWithDefaults"",
+  ""doc"" : ""Test logical types and default values in generated Java 
classes"",
+  ""fields"": [
+    {""name"": ""nullableDate"",  ""type"": [{""type"": ""int"", 
""logicalType"": ""date""}, ""null""], ""default"": 1234},
+    { ""name"": ""nonNullDate"",  ""type"": { ""type"": ""int"", 
""logicalType"": ""date""}, ""default"": 1234}
+  ]
+}";
+
+        private const string _nestedLogicalTypesArray = @"
+{""namespace"": ""org.apache.avro.codegentest.testdata"",
+  ""type"": ""record"",
+  ""name"": ""NestedLogicalTypesArray"",
+  ""doc"" : ""Test nested types with logical types in generated Java classes"",
+  ""fields"": [
+    {
+      ""name"": ""arrayOfRecords"",
+      ""type"": {
+        ""type"": ""array"",
+        ""items"": {
+          ""namespace"": ""org.apache.avro.codegentest.testdata"",
+          ""name"": ""RecordInArray"",
+          ""type"": ""record"",
+          ""fields"": [
+            {
+              ""name"": ""nullableDateField"",
+              ""type"": [""null"", {""type"": ""int"", ""logicalType"": 
""date""}]
+            }
+          ]
+        }
+      }
+    }]
+}
+";
+
+        private const string _nestedLogicalTypesMap = @"
+{""namespace"": ""org.apache.avro.codegentest.testdata"",
+  ""type"": ""record"",
+  ""name"": ""NestedLogicalTypesMap"",
+  ""doc"" : ""Test nested types with logical types in generated Java classes"",
+  ""fields"": [
+    {
+      ""name"": ""mapOfRecords"",
+      ""type"": {
+        ""type"": ""map"",
+        ""values"": {
+          ""namespace"": ""org.apache.avro.codegentest.testdata"",
+          ""name"": ""RecordInMap"",
+          ""type"": ""record"",
+          ""fields"": [
+            {
+              ""name"": ""nullableDateField"",
+              ""type"": [""null"", {""type"": ""int"", ""logicalType"": 
""date""}]
+            }
+          ]
+        }
+      }
+    }]
+}";
+
+        private const string _nestedLogicalTypesRecord = @"
+{""namespace"": ""org.apache.avro.codegentest.testdata"",
+  ""type"": ""record"",
+  ""name"": ""NestedLogicalTypesRecord"",
+  ""doc"" : ""Test nested types with logical types in generated Java classes"",
+  ""fields"": [
+    {
+      ""name"": ""nestedRecord"",
+      ""type"": {
+        ""namespace"": ""org.apache.avro.codegentest.testdata"",
+        ""type"": ""record"",
+        ""name"": ""NestedRecord"",
+        ""fields"": [
+          {
+            ""name"": ""nullableDateField"",
+            ""type"": [""null"", {""type"": ""int"", ""logicalType"": 
""date""}]
+          }
+        ]
+      }
+    }]
+}";
+
+        private const string _nestedLogicalTypesUnionFixedDecimal = @"
+{""namespace"": ""org.apache.avro.codegentest.testdata"",
+  ""type"": ""record"",
+  ""name"": ""NestedLogicalTypesUnionFixedDecimal"",
+  ""doc"" : ""Test nested types with logical types in generated Java classes"",
+  ""fields"": [
+    {
+      ""name"": ""unionOfFixedDecimal"",
+      ""type"": [""null"", {
+        ""namespace"": ""org.apache.avro.codegentest.testdata"",
+        ""name"": ""FixedInUnion"",
+        ""type"": ""fixed"",
+        ""size"": 12,
+        ""logicalType"": ""decimal"",
+        ""precision"": 28,
+        ""scale"": 15
+      }]
+    }]
+}";
+
+        private const string _nestedLogicalTypesUnion = @"
+{""namespace"": ""org.apache.avro.codegentest.testdata"",
+  ""type"": ""record"",
+  ""name"": ""NestedLogicalTypesUnion"",
+  ""doc"" : ""Test nested types with logical types in generated Java classes"",
+  ""fields"": [
+    {
+      ""name"": ""unionOfRecords"",
+      ""type"": [""null"", {
+        ""namespace"": ""org.apache.avro.codegentest.testdata"",
+        ""name"": ""RecordInUnion"",
+        ""type"": ""record"",
+        ""fields"": [
+          {
+            ""name"": ""nullableDateField"",
+            ""type"": [""null"", {""type"": ""int"", ""logicalType"": 
""date""}]
+          }
+        ]
+      }]
+    }]
+}";
+
+        private const string _nestedSomeNamespaceRecord = @"
+{""namespace"": ""org.apache.avro.codegentest.some"",
+  ""type"": ""record"",
+  ""name"": ""NestedSomeNamespaceRecord"",
+  ""doc"" : ""Test nested types with different namespace than the outer type"",
+  ""fields"": [
+    {
+      ""name"": ""nestedRecord"",
+      ""type"": {
+        ""namespace"": ""org.apache.avro.codegentest.other"",
+        ""type"": ""record"",
+        ""name"": ""NestedOtherNamespaceRecord"",
+        ""fields"": [
+          {
+            ""name"": ""someField"",
+            ""type"": ""int""
+          }
+        ]
+      }
+    }]
+}";
+
+        private const string _nullableLogicalTypesArray = @"
+{""namespace"": ""org.apache.avro.codegentest.testdata"",
+  ""type"": ""record"",
+  ""name"": ""NullableLogicalTypesArray"",
+  ""doc"" : ""Test nested types with logical types in generated Java classes"",
+  ""fields"": [
+    {
+      ""name"": ""arrayOfLogicalType"",
+      ""type"": {
+        ""type"": ""array"",
+        ""items"": [""null"", {""type"": ""int"", ""logicalType"": ""date""}]
+      }
+    }]
+}";
+
+        private const string _nullableLogicalTypes = @"
+{""namespace"": ""org.apache.avro.codegentest.testdata"",
+  ""type"": ""record"",
+  ""name"": ""NullableLogicalTypes"",
+  ""doc"" : ""Test unions with logical types in generated Java classes"",
+  ""fields"": [
+    {""name"": ""nullableDate"",  ""type"": [""null"", {""type"": ""int"", 
""logicalType"": ""date""}], ""default"": null}
+  ]
+}";
+
+        private const string _stringLogicalType = @"
+{
+  ""namespace"": ""org.apache.avro.codegentest.testdata"",
+  ""type"": ""record"",
+  ""name"": ""StringLogicalType"",
+  ""doc"": ""Test logical type applied to field of type string"",
+  ""fields"": [
+    {
+      ""name"": ""someIdentifier"",
+      ""type"": {
+        ""type"": ""string"",
+        ""logicalType"": ""uuid""
+      }
+},
+    {
+    ""name"": ""someJavaString"",
+      ""type"": ""string"",
+      ""doc"": ""Just to ensure no one removed <stringType>String</stringType> 
because this is the basis of this test""
+    }
+  ]
+}";
+
+        private Assembly TestSchema(
+            string schema,
+            IEnumerable<string> typeNamesToCheck = null,
+            IEnumerable<KeyValuePair<string, string>> namespaceMapping = null,
+            IEnumerable<string> generatedFilesToCheck = null)
+        {
+            // Create temp folder
+            string outputDir = AvroGenHelper.CreateEmptyTemporyFolder(out 
string uniqueId);
+
+            try
+            {
+                // Save schema
+                string schemaFileName = Path.Combine(outputDir, 
$"{uniqueId}.avsc");
+                System.IO.File.WriteAllText(schemaFileName, schema);
+
+                // Generate from schema file
+                Assert.That(AvroGenTool.GenSchema(schemaFileName, outputDir, 
namespaceMapping ?? new Dictionary<string, string>()), Is.EqualTo(0));
+
+                // Check if all generated files exist
+                if (generatedFilesToCheck != null)
+                {
+                    foreach (string generatedFile in generatedFilesToCheck)
+                    {
+                        Assert.That(new FileInfo(Path.Combine(outputDir, 
generatedFile)), Does.Exist);
+                    }
+                }
+
+                // Compile into netstandard library and load assembly
+                Assembly assembly = 
AvroGenHelper.CompileCSharpFilesIntoLibrary(
+                    new DirectoryInfo(outputDir)
+                        .EnumerateFiles("*.cs", SearchOption.AllDirectories)
+                        .Select(fi => fi.FullName),
+                        uniqueId);
+
+                if (typeNamesToCheck != null)
+                {
+                    // Check if the compiled code has the same number of types 
defined as the check list
+                    Assert.That(typeNamesToCheck.Count(), 
Is.EqualTo(assembly.DefinedTypes.Count()));
+
+                    // Check if types available in compiled assembly
+                    foreach (string typeName in typeNamesToCheck)
+                    {
+                        Type type = assembly.GetType(typeName);
+                        Assert.That(type, Is.Not.Null);
+
+                        // Instantiate
+                        object obj = Activator.CreateInstance(type);
+                        Assert.That(obj, Is.Not.Null);
+                    }
+                }
+
+                return assembly;
+            }
+            finally
+            {
+                Directory.Delete(outputDir, true);
+            }
+        }
+
+        [TestCase(
+            _logicalTypesWithDefaults,
+            new string[]
+            {
+                "org.apache.avro.codegentest.testdata.LogicalTypesWithDefaults"
+            },
+            new string[]
+            {
+                
"org/apache/avro/codegentest/testdata/LogicalTypesWithDefaults.cs"
+            })]
+        [TestCase(
+            _nestedLogicalTypesArray,
+            new string[]
+            {
+                "org.apache.avro.codegentest.testdata.NestedLogicalTypesArray",
+                "org.apache.avro.codegentest.testdata.RecordInArray"
+            },
+            new string[]
+            {
+                
"org/apache/avro/codegentest/testdata/NestedLogicalTypesArray.cs",
+                "org/apache/avro/codegentest/testdata/RecordInArray.cs"
+            })]
+        [TestCase(
+            _nestedLogicalTypesMap,
+            new string[]
+            {
+                "org.apache.avro.codegentest.testdata.NestedLogicalTypesMap",
+                "org.apache.avro.codegentest.testdata.RecordInMap"
+            },
+            new string[]
+            {
+                
"org/apache/avro/codegentest/testdata/NestedLogicalTypesMap.cs",
+                "org/apache/avro/codegentest/testdata/RecordInMap.cs"
+            })]
+        [TestCase(
+            _nestedLogicalTypesRecord,
+            new string[]
+            {
+                
"org.apache.avro.codegentest.testdata.NestedLogicalTypesRecord",
+                "org.apache.avro.codegentest.testdata.NestedRecord"
+            },
+            new string[]
+            {
+                
"org/apache/avro/codegentest/testdata/NestedLogicalTypesRecord.cs",
+                "org/apache/avro/codegentest/testdata/NestedRecord.cs"
+            })]
+        [TestCase(
+            _nestedLogicalTypesUnion,
+            new string[]
+            {
+                "org.apache.avro.codegentest.testdata.NestedLogicalTypesUnion",
+                "org.apache.avro.codegentest.testdata.RecordInUnion"
+            },
+            new string[]
+            {
+                
"org/apache/avro/codegentest/testdata/NestedLogicalTypesUnion.cs",
+                "org/apache/avro/codegentest/testdata/RecordInUnion.cs"
+            })]
+        [TestCase(
+            _nestedSomeNamespaceRecord,
+            new string[]
+            {
+                "org.apache.avro.codegentest.some.NestedSomeNamespaceRecord",
+                "org.apache.avro.codegentest.other.NestedOtherNamespaceRecord"
+            },
+            new string[]
+            {
+                
"org/apache/avro/codegentest/some/NestedSomeNamespaceRecord.cs",
+                
"org/apache/avro/codegentest/other/NestedOtherNamespaceRecord.cs"
+            })]
+        [TestCase(
+            _nullableLogicalTypes,
+            new string[]
+            {
+               "org.apache.avro.codegentest.testdata.NullableLogicalTypes"
+            },
+            new string[]
+            {
+               "org/apache/avro/codegentest/testdata/NullableLogicalTypes.cs"
+            })]
+        [TestCase(
+            _nullableLogicalTypesArray,
+            new string[]
+            {
+                
"org.apache.avro.codegentest.testdata.NullableLogicalTypesArray"
+            },
+            new string[]
+            {
+                
"org/apache/avro/codegentest/testdata/NullableLogicalTypesArray.cs"
+            })]
+        public void GenerateSchema(string schema, IEnumerable<string> 
typeNamesToCheck, IEnumerable<string> generatedFilesToCheck)
+        {
+            TestSchema(schema, typeNamesToCheck, generatedFilesToCheck: 
generatedFilesToCheck);
+        }
+        
+        [TestCase(
+            _nullableLogicalTypesArray,
+            "org.apache.avro.codegentest.testdata", 
"org.apache.csharp.codegentest.testdata",
+            new string[]
+            {
+                
"org.apache.csharp.codegentest.testdata.NullableLogicalTypesArray"
+            },
+            new string[]
+            {
+                
"org/apache/csharp/codegentest/testdata/NullableLogicalTypesArray.cs"
+            })]
+        [TestCase(
+            _nullableLogicalTypesArray,
+            "org.apache.avro.codegentest.testdata", "org.apache.@return.@int", 
// Reserved keywords in namespace
+            new string[]
+            {
+                "org.apache.return.int.NullableLogicalTypesArray"
+            },
+            new string[]
+            {
+                "org/apache/return/int/NullableLogicalTypesArray.cs"
+            })]
+        [TestCase(
+            _nullableLogicalTypesArray,
+            "org.apache.avro.codegentest.testdata", 
"org.apache.value.partial", // Contextual keywords in namespace
+            new string[]
+            {
+                "org.apache.value.partial.NullableLogicalTypesArray"
+            },
+            new string[]
+            {
+                "org/apache/value/partial/NullableLogicalTypesArray.cs"
+            })]
+        [TestCase(@"
+{
+    ""type"": ""fixed"",
+    ""namespace"": ""com.base"",
+    ""name"": ""MD5"",
+    ""size"": 16
+}",
+            "com.base", "SchemaTest",
+            new string[]
+            {
+                "SchemaTest.MD5"
+            },
+            new string[]
+            {
+                "SchemaTest/MD5.cs"
+            })]
+        [TestCase(@"
+{
+    ""type"": ""fixed"",
+    ""namespace"": ""com.base"",
+    ""name"": ""MD5"",
+    ""size"": 16
+}",
+            "miss", "SchemaTest",
+            new string[]
+            {
+                "com.base.MD5"
+            },
+            new string[]
+            {
+                "com/base/MD5.cs"
+            })]
+        public void GenerateSchemaWithNamespaceMapping(
+            string schema,
+            string namespaceMappingFrom,
+            string namespaceMappingTo,
+            IEnumerable<string> typeNamesToCheck,
+            IEnumerable<string> generatedFilesToCheck)
+        {
+            TestSchema(schema, typeNamesToCheck, new Dictionary<string, 
string> { { namespaceMappingFrom, namespaceMappingTo } }, 
generatedFilesToCheck);
+        }
+
+        [TestCase(
+            _nestedLogicalTypesUnion,
+            "org.apache.avro.codegentest.testdata", 
"org.apache.csharp.codegentest.testdata",
+            new string[]
+            {
+                "org.apache.avro.codegentest.testdata.NestedLogicalTypesUnion",
+                "org.apache.avro.codegentest.testdata.RecordInUnion"
+            },
+            new string[]
+            {
+                
"org/apache/csharp/codegentest/testdata/NestedLogicalTypesUnion.cs",
+                "org/apache/csharp/codegentest/testdata/RecordInUnion.cs"
+            })]
+        public void GenerateSchemaWithNamespaceMapping_Bug_AVRO_2883(
+            string schema,
+            string namespaceMappingFrom,
+            string namespaceMappingTo,
+            IEnumerable<string> typeNamesToCheck,
+            IEnumerable<string> generatedFilesToCheck)
+        {
+            // !!! This is a bug which must be fixed
+            // !!! Once it is fixed, this test will fail and this test can be 
removed
+            // https://issues.apache.org/jira/browse/AVRO-2883
+            // https://issues.apache.org/jira/browse/AVRO-3046
+            Assert.Throws<AssertionException>(() => TestSchema(schema, 
typeNamesToCheck, new Dictionary<string, string> { { namespaceMappingFrom, 
namespaceMappingTo } }, generatedFilesToCheck));
+        }
+
+        [TestCase(_logicalTypesWithCustomConversion, 
typeof(AvroTypeException))]
+        [TestCase(_customConversionWithLogicalTypes, 
typeof(SchemaParseException))]
+        [TestCase(_nestedLogicalTypesUnionFixedDecimal, 
typeof(SchemaParseException))]
+        public void NotSupportedSchema(string schema, Type expectedException)
+        {
+            // Create temp folder
+            string outputDir = AvroGenHelper.CreateEmptyTemporyFolder(out 
string uniqueId);
+
+            try
+            {
+                // Save schema
+                string schemaFileName = Path.Combine(outputDir, 
$"{uniqueId}.avsc");
+                System.IO.File.WriteAllText(schemaFileName, schema);
+
+                Assert.That(AvroGenTool.GenSchema(schemaFileName, outputDir, 
new Dictionary<string, string>()), Is.EqualTo(1));
+            }
+            finally
+            {
+                Directory.Delete(outputDir, true);
+            }
+        }
+
+        [TestCase(@"
+{
+    ""type"" : ""record"",
+    ""name"" : ""ClassKeywords"",
+    ""namespace"" : ""com.base"",
+    ""fields"" :
+        [      
+            { ""name"" : ""int"", ""type"" : ""int"" },
+            { ""name"" : ""base"", ""type"" : ""long"" },
+            { ""name"" : ""event"", ""type"" : ""boolean"" },
+            { ""name"" : ""foreach"", ""type"" : ""double"" },
+            { ""name"" : ""bool"", ""type"" : ""float"" },
+            { ""name"" : ""internal"", ""type"" : ""bytes"" },
+            { ""name"" : ""while"", ""type"" : ""string"" },
+            { ""name"" : ""return"", ""type"" : ""null"" },
+            { ""name"" : ""enum"", ""type"" : { ""type"" : ""enum"", ""name"" 
: ""class"", ""symbols"" : [ ""Unknown"", ""A"", ""B"" ], ""default"" : 
""Unknown"" } },
+            { ""name"" : ""string"", ""type"" : { ""type"": ""fixed"", 
""size"": 16, ""name"": ""static"" } }
+        ]
+}",
+            new object[] { "com.base.ClassKeywords", typeof(int), 
typeof(long), typeof(bool), typeof(double), typeof(float), typeof(byte[]), 
typeof(string), typeof(object), "com.base.class", "com.base.static" })]
+        [TestCase(@"
+{
+    ""type"" : ""record"",
+    ""name"" : ""AvroNamespaceType"",
+    ""namespace"" : ""My.Avro"",
+    ""fields"" :
+        [
+            { ""name"" : ""justenum"", ""type"" : { ""type"" : ""enum"", 
""name"" : ""justenumEnum"", ""symbols"" : [ ""One"", ""Two"" ] } },
+        ]
+}",
+            new object[] { "My.Avro.AvroNamespaceType", "My.Avro.justenumEnum" 
})]
+        [TestCase(@"
+{
+    ""type"" : ""record"",
+    ""name"" : ""SchemaObject"",
+    ""namespace"" : ""schematest"",
+    ""fields"" :
+        [      
+            { ""name"" : ""myobject"", ""type"" :
+                [
+                    ""null"",
+                    { ""type"" : ""array"", ""items"" :
+                        [
+                            ""null"",
+                            { ""type"" : ""enum"", ""name"" : ""MyEnum"", 
""symbols"" : [ ""A"", ""B"" ] },
+                            { ""type"": ""fixed"", ""size"": 16, ""name"": 
""MyFixed"" }
+                        ]
+                    }
+                ]
+            }
+        ]
+}",
+            new object[] { "schematest.SchemaObject", typeof(IList<object>) })]
+        [TestCase(@"
+{
+       ""type"" : ""record"",
+       ""name"" : ""LogicalTypes"",
+       ""namespace"" : ""schematest"",
+       ""fields"" :
+               [       
+                       { ""name"" : ""nullibleguid"", ""type"" : [""null"", 
{""type"": ""string"", ""logicalType"": ""uuid"" } ]},
+                       { ""name"" : ""guid"", ""type"" : {""type"": 
""string"", ""logicalType"": ""uuid"" } },
+                       { ""name"" : ""nullibletimestampmillis"", ""type"" : 
[""null"", {""type"": ""long"", ""logicalType"": ""timestamp-millis""}]  },
+                       { ""name"" : ""timestampmillis"", ""type"" : {""type"": 
""long"", ""logicalType"": ""timestamp-millis""} },
+                       { ""name"" : ""nullibiletimestampmicros"", ""type"" : 
[""null"", {""type"": ""long"", ""logicalType"": ""timestamp-micros""}]  },
+                       { ""name"" : ""timestampmicros"", ""type"" : {""type"": 
""long"", ""logicalType"": ""timestamp-micros""} },
+                       { ""name"" : ""nullibiletimemicros"", ""type"" : 
[""null"", {""type"": ""long"", ""logicalType"": ""time-micros""}]  },
+                       { ""name"" : ""timemicros"", ""type"" : {""type"": 
""long"", ""logicalType"": ""time-micros""} },
+                       { ""name"" : ""nullibiletimemillis"", ""type"" : 
[""null"", {""type"": ""int"", ""logicalType"": ""time-millis""}]  },
+                       { ""name"" : ""timemillis"", ""type"" : {""type"": 
""int"", ""logicalType"": ""time-millis""} },
+                       { ""name"" : ""nullibledecimal"", ""type"" : [""null"", 
{""type"": ""bytes"", ""logicalType"": ""decimal"", ""precision"": 4, 
""scale"": 2}]  },
+            { ""name"" : ""decimal"", ""type"" : {""type"": ""bytes"", 
""logicalType"": ""decimal"", ""precision"": 4, ""scale"": 2} }
+               ]
+}",
+            new object[] { "schematest.LogicalTypes", typeof(Guid?), 
typeof(Guid), typeof(DateTime?), typeof(DateTime), typeof(DateTime?), 
typeof(DateTime), typeof(TimeSpan?), typeof(TimeSpan), typeof(TimeSpan?), 
typeof(TimeSpan), typeof(AvroDecimal?), typeof(AvroDecimal) })]
+        public void GenerateSchemaCheckFields(string schema, object[] result)
+        {
+            Assembly assembly = TestSchema(schema);
+
+            // Instantiate object
+            Type type = assembly.GetType((string)result[0]);
+            Assert.That(type, Is.Not.Null);
+
+            ISpecificRecord record = Activator.CreateInstance(type) as 
ISpecificRecord;
+            Assert.IsNotNull(record);
+
+            // test type of each fields
+            for (int i = 1; i < result.Length; ++i)
+            {
+                object field = record.Get(i - 1);
+                Type stype;
+                if (result[i].GetType() == typeof(string))
+                {
+                    Type t = assembly.GetType((string)result[i]);
+                    Assert.That(record, Is.Not.Null);
+
+                    object obj = Activator.CreateInstance(t);
+                    Assert.That(obj, Is.Not.Null);
+                    stype = obj.GetType();
+                }
+                else
+                {
+                    stype = (Type)result[i];
+                }
+                if (!stype.IsValueType)
+                {
+                    Assert.That(field, Is.Null);   // can't test reference 
type, it will be null
+                }
+                else if (stype.IsValueType && field == null)
+                {
+                    Assert.That(field, Is.Null); // nullable value type, so we 
can't get the type using GetType
+                }
+                else
+                {
+                    Assert.That(field, Is.Not.Null);
+                    Assert.That(field.GetType(), Is.EqualTo(stype));
+                }
+            }
+        }
+    }
+}
diff --git a/lang/csharp/src/apache/test/AvroGen/AvroGenToolTests.cs 
b/lang/csharp/src/apache/test/AvroGen/AvroGenToolTests.cs
new file mode 100644
index 0000000..a5a46b4
--- /dev/null
+++ b/lang/csharp/src/apache/test/AvroGen/AvroGenToolTests.cs
@@ -0,0 +1,75 @@
+/**
+ * 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.IO;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+
+namespace Avro.Test.AvroGen
+{
+    [TestFixture]
+
+    class AvroGenToolTests
+    {
+        [Test]
+        public void CommandLineNoArgs()
+        {
+            AvroGenToolResult result = 
AvroGenHelper.RunAvroGenTool(Array.Empty<string>());
+
+            Assert.That(result.ExitCode, Is.EqualTo(1));
+            Assert.That(result.StdOut, Is.Not.Empty);
+            Assert.That(result.StdErr, Is.Empty);
+        }
+
+        [TestCase("-h")]
+        [TestCase("--help")]
+        [TestCase("--help", "-h")]
+        [TestCase("--help", "-s", "whatever.avsc", ".")]
+        [TestCase("-p", "whatever.avpr", ".", "-h")]
+        public void CommandLineHelp(params string[] args)
+        {
+            AvroGenToolResult result = AvroGenHelper.RunAvroGenTool(args);
+
+            Assert.That(result.ExitCode, Is.EqualTo(0));
+            Assert.That(result.StdOut, Is.Not.Empty);
+            Assert.That(result.StdErr, Is.Empty);
+        }
+
+        [TestCase("-p")]
+        [TestCase("-s")]
+        [TestCase("-p", "whatever.avpr")]
+        [TestCase("-p", "whatever.avpr")]
+        [TestCase("-s", "whatever.avsc")]
+        [TestCase("whatever.avsc")]
+        [TestCase("whatever.avsc", ".")]
+        [TestCase(".")]
+        [TestCase("-s", "whatever.avsc", "--namespace")]
+        [TestCase("-s", "whatever.avsc", "--namespace", "org.apache")]
+        [TestCase("-s", "whatever.avsc", "--namespace", "org.apache:")]
+        [TestCase("-s", "whatever.avsc", ".", "whatever")]
+        public void CommandLineInvalidArgs(params string[] args)
+        {
+            AvroGenToolResult result = AvroGenHelper.RunAvroGenTool(args);
+
+            Assert.That(result.ExitCode, Is.EqualTo(1));
+            Assert.That(result.StdOut, Is.Not.Empty);
+            Assert.That(result.StdErr, Is.Not.Empty);
+        }
+    }
+}
diff --git a/lang/csharp/src/apache/test/CodGen/CodeGenTest.cs 
b/lang/csharp/src/apache/test/CodGen/CodeGenTest.cs
index 3d45485..243d93e 100644
--- a/lang/csharp/src/apache/test/CodGen/CodeGenTest.cs
+++ b/lang/csharp/src/apache/test/CodGen/CodeGenTest.cs
@@ -17,194 +17,63 @@
  */
 using System;
 using System.Collections.Generic;
-using System.IO;
-using System.CodeDom.Compiler;
-using Microsoft.CSharp;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp;
 using NUnit.Framework;
-using Avro.Specific;
 
-namespace Avro.Test
+namespace Avro.Test.CodeGen
 {
     [TestFixture]
-
-    class CodeGenTest
+    class CodeGenTests
     {
 
         [Test]
         public void TestGetNullableTypeException()
         {
-            Assert.Throws<ArgumentNullException>(() => 
CodeGen.GetNullableType(null));
+            Assert.Throws<ArgumentNullException>(() => 
Avro.CodeGen.GetNullableType(null));
         }
 
-#if !NETCOREAPP // System.CodeDom compilation not supported in .NET Core: 
https://github.com/dotnet/corefx/issues/12180
-        [TestCase(@"{
-""type"" : ""record"",
-""name"" : ""ClassKeywords"",
-""namespace"" : ""com.base"",
-""fields"" :
-               [       
-                       { ""name"" : ""int"", ""type"" : ""int"" },
-                       { ""name"" : ""base"", ""type"" : ""long"" },
-                       { ""name"" : ""event"", ""type"" : ""boolean"" },
-                       { ""name"" : ""foreach"", ""type"" : ""double"" },
-                       { ""name"" : ""bool"", ""type"" : ""float"" },
-                       { ""name"" : ""internal"", ""type"" : ""bytes"" },
-                       { ""name"" : ""while"", ""type"" : ""string"" },
-                       { ""name"" : ""return"", ""type"" : ""null"" },
-                       { ""name"" : ""enum"", ""type"" : { ""type"" : 
""enum"", ""name"" : ""class"", ""symbols"" : [ ""Unknown"", ""A"", ""B"" ], 
""default"" : ""Unknown"" } },
-                       { ""name"" : ""string"", ""type"" : { ""type"": 
""fixed"", ""size"": 16, ""name"": ""static"" } }
-               ]
-}
-", new object[] {"com.base.ClassKeywords", typeof(int), typeof(long), 
typeof(bool), typeof(double), typeof(float), typeof(byte[]), 
typeof(string),typeof(object),"com.base.class", "com.base.static"}, TestName = 
"TestCodeGen0")]
-        [TestCase(@"{
-""type"" : ""record"",
-""name"" : ""AvroNamespaceType"",
-""namespace"" : ""My.Avro"",
-""fields"" :
-               [
-                       { ""name"" : ""justenum"", ""type"" : { ""type"" : 
""enum"", ""name"" : ""justenumEnum"", ""symbols"" : [ ""One"", ""Two"" ] } },
-               ]
-}
-", new object[] {"My.Avro.AvroNamespaceType", "My.Avro.justenumEnum"}, 
TestName = "TestCodeGen3 - Avro namespace conflict")]
-        [TestCase(@"{
-""type"" : ""record"",
-""name"" : ""SchemaObject"",
-""namespace"" : ""schematest"",
-""fields"" :
-       [       
-               { ""name"" : ""myobject"", ""type"" :
-                       [
-                               ""null"",
-                               {""type"" : ""array"", ""items"" : [ ""null"",
-                                                                               
        { ""type"" : ""enum"", ""name"" : ""MyEnum"", ""symbols"" : [ ""A"", 
""B"" ] },
-                                                                               
        { ""type"": ""fixed"", ""size"": 16, ""name"": ""MyFixed"" }
-                                                                               
        ]
-                               }
-                       ]
-               }
-       ]
-}
-", new object[] { "schematest.SchemaObject", typeof(IList<object>) }, TestName 
= "TestCodeGen1")]
-        [TestCase(@"{
-       ""type"" : ""record"",
-       ""name"" : ""LogicalTypes"",
-       ""namespace"" : ""schematest"",
-       ""fields"" :
-               [       
-                       { ""name"" : ""nullibleguid"", ""type"" : [""null"", 
{""type"": ""string"", ""logicalType"": ""uuid"" } ]},
-                       { ""name"" : ""guid"", ""type"" : {""type"": 
""string"", ""logicalType"": ""uuid"" } },
-                       { ""name"" : ""nullibletimestampmillis"", ""type"" : 
[""null"", {""type"": ""long"", ""logicalType"": ""timestamp-millis""}]  },
-                       { ""name"" : ""timestampmillis"", ""type"" : {""type"": 
""long"", ""logicalType"": ""timestamp-millis""} },
-                       { ""name"" : ""nullibiletimestampmicros"", ""type"" : 
[""null"", {""type"": ""long"", ""logicalType"": ""timestamp-micros""}]  },
-                       { ""name"" : ""timestampmicros"", ""type"" : {""type"": 
""long"", ""logicalType"": ""timestamp-micros""} },
-                       { ""name"" : ""nullibiletimemicros"", ""type"" : 
[""null"", {""type"": ""long"", ""logicalType"": ""time-micros""}]  },
-                       { ""name"" : ""timemicros"", ""type"" : {""type"": 
""long"", ""logicalType"": ""time-micros""} },
-                       { ""name"" : ""nullibiletimemillis"", ""type"" : 
[""null"", {""type"": ""int"", ""logicalType"": ""time-millis""}]  },
-                       { ""name"" : ""timemillis"", ""type"" : {""type"": 
""int"", ""logicalType"": ""time-millis""} },
-                       { ""name"" : ""nullibledecimal"", ""type"" : [""null"", 
{""type"": ""bytes"", ""logicalType"": ""decimal"", ""precision"": 4, 
""scale"": 2}]  },
-            { ""name"" : ""decimal"", ""type"" : {""type"": ""bytes"", 
""logicalType"": ""decimal"", ""precision"": 4, ""scale"": 2} }
-               ]
-}
-", new object[] { "schematest.LogicalTypes", typeof(Guid?), typeof(Guid), 
typeof(DateTime?), typeof(DateTime), typeof(DateTime?), typeof(DateTime), 
typeof(TimeSpan?), typeof(TimeSpan), typeof(TimeSpan?), typeof(TimeSpan), 
typeof(AvroDecimal?), typeof(AvroDecimal) }, TestName = "TestCodeGen2 - Logical 
Types")]
-        public static void TestCodeGen(string str, object[] result)
+        [Test]
+        public void TestReservedKeywords()
         {
-            Schema schema = Schema.Parse(str);
-
-            CompilerResults compres = GenerateSchema(schema);
-
-            // instantiate object
-            ISpecificRecord rec = 
compres.CompiledAssembly.CreateInstance((string)result[0]) as ISpecificRecord;
-            Assert.IsNotNull(rec);
+            // 
https://github.com/dotnet/roslyn/blob/main/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs
 
-            // test type of each fields
-            for (int i = 1; i < result.Length; ++i)
+            // Check if all items in CodeGenUtil.Instance.ReservedKeywords are 
keywords
+            foreach (string keyword in CodeGenUtil.Instance.ReservedKeywords)
             {
-                object field = rec.Get(i - 1);
-                Type stype;
-                if (result[i].GetType() == typeof(string))
-                {
-                    object obj = 
compres.CompiledAssembly.CreateInstance((string)result[i]);
-                    Assert.IsNotNull(obj);
-                    stype = obj.GetType();
-                }
-                else
-                    stype = (Type)result[i];
-                if (!stype.IsValueType)
-                    Assert.IsNull(field);   // can't test reference type, it 
will be null
-                else if (stype.IsValueType && field == null)
-                    Assert.IsNull(field); // nullable value type, so we can't 
get the type using GetType
-                else
-                    Assert.AreEqual(stype, field.GetType());
+                Assert.That(SyntaxFacts.GetKeywordKind(keyword) != 
SyntaxKind.None, Is.True);
             }
-        }
 
-        [TestCase(@"{
-""type"": ""fixed"",
-""namespace"": ""com.base"",
-""name"": ""MD5"",
-""size"": 16
-}", null, null, "com.base")]
-        [TestCase(@"{
-""type"": ""fixed"",
-""namespace"": ""com.base"",
-""name"": ""MD5"",
-""size"": 16
-}", "com.base", "SchemaTest", "SchemaTest")]
-        [TestCase(@"{
-""type"": ""fixed"",
-""namespace"": ""com.base"",
-""name"": ""MD5"",
-""size"": 16
-}", "miss", "SchemaTest", "com.base")]
-        public void TestCodeGenNamespaceMapping(string str, string 
avroNamespace, string csharpNamespace,
-            string expectedNamespace)
-        {
-            Schema schema = Schema.Parse(str);
-
-            var codegen = new CodeGen();
-            codegen.AddSchema(schema);
-
-            if (avroNamespace != null && csharpNamespace != null)
+            // Check if all Roslyn defined keywords are in 
CodeGenUtil.Instance.ReservedKeywords
+            foreach (SyntaxKind keywordKind in 
SyntaxFacts.GetReservedKeywordKinds())
             {
-                codegen.NamespaceMapping[avroNamespace] = csharpNamespace;
+                Assert.That(CodeGenUtil.Instance.ReservedKeywords, 
Does.Contain(SyntaxFacts.GetText(keywordKind)));
             }
 
-            var results = GenerateAssembly(codegen);
-            foreach(var type in results.CompiledAssembly.GetTypes())
-            {
-                Assert.AreEqual(expectedNamespace, type.Namespace);
-            }
+            // If this test fails, CodeGenUtil.ReservedKeywords list must be 
updated.
+            // This might happen if newer version of C# language defines new 
reserved keywords.
         }
 
-        private static CompilerResults GenerateSchema(Schema schema)
+        [TestCase("a", "a")]
+        [TestCase("a.b", "a.b")]
+        [TestCase("a.b.c", "a.b.c")]
+        [TestCase("int", "@int")]
+        [TestCase("a.long.b", "[email protected]")]
+        [TestCase("int.b.c", "@int.b.c")]
+        [TestCase("a.b.int", "a.b.@int")]
+        [TestCase("int.long.while", "@int.@long.@while")] // Reserved keywords
+        [TestCase("a.value.partial", "a.value.partial")] // Contextual keywords
+        [TestCase("a.value.b.int.c.while.longpartial", 
"[email protected][email protected]")] // Rseserved and contextual keywords
+        public void TestMangleUnMangle(string input, string mangled)
         {
-            var codegen = new CodeGen();
-            codegen.AddSchema(schema);
-            return GenerateAssembly(codegen);
+            // Mangle
+            Assert.That(CodeGenUtil.Instance.Mangle(input), 
Is.EqualTo(mangled));
+            // Unmangle
+            Assert.That(CodeGenUtil.Instance.UnMangle(mangled), 
Is.EqualTo(input));
         }
 
-        private static CompilerResults GenerateAssembly(CodeGen schema)
-        {
-            var compileUnit = schema.GenerateCode();
-
-            var comparam = new CompilerParameters(new string[] { 
"netstandard.dll" });
-            comparam.ReferencedAssemblies.Add("System.dll");
-            
comparam.ReferencedAssemblies.Add(Path.Combine(TestContext.CurrentContext.TestDirectory,
 "Avro.dll"));
-            comparam.GenerateInMemory = true;
-            var ccp = new CSharpCodeProvider();
-            var units = new[] { compileUnit };
-            var compres = ccp.CompileAssemblyFromDom(comparam, units);
-            if (compres.Errors.Count > 0)
-            {
-                for (int i = 0; i < compres.Errors.Count; i++)
-                    Console.WriteLine(compres.Errors[i]);
-            }
-            Assert.AreEqual(0, compres.Errors.Count);
-            return compres;
-        }
-#endif
         [TestFixture]
-        public class CodeGenTestClass : CodeGen
+        public class CodeGenTestClass : Avro.CodeGen
         {
             [Test]
             public void TestGenerateNamesException()
diff --git a/lang/csharp/versions.props b/lang/csharp/versions.props
index 15cc410..aea7edb 100644
--- a/lang/csharp/versions.props
+++ b/lang/csharp/versions.props
@@ -55,13 +55,15 @@
   -->
   <PropertyGroup Label="Build, Test, Code Analysis, Benchmark Package 
Versions">
     <BenchmarkDotNetVersion>0.13.1</BenchmarkDotNetVersion>
-    <MicrosoftBuildFrameworkVersion>17.0.0</MicrosoftBuildFrameworkVersion>
-    
<MicrosoftBuildUtilitiesCoreVersion>17.0.0</MicrosoftBuildUtilitiesCoreVersion>
-    
<MicrosoftCodeAnalysisCSharpCodeStyleVersion>4.0.1</MicrosoftCodeAnalysisCSharpCodeStyleVersion>
+    <MicrosoftBuildFrameworkVersion>17.1.0</MicrosoftBuildFrameworkVersion>
+    
<MicrosoftBuildUtilitiesCoreVersion>17.1.0</MicrosoftBuildUtilitiesCoreVersion>
+    <MicrosoftCodeAnalysisVersion>4.1.0</MicrosoftCodeAnalysisVersion>
+    
<MicrosoftCodeAnalysisCSharpVersion>4.1.0</MicrosoftCodeAnalysisCSharpVersion>
+    
<MicrosoftCodeAnalysisCSharpCodeStyleVersion>4.1.0</MicrosoftCodeAnalysisCSharpCodeStyleVersion>
     
<MicrosoftCodeAnalysisNetAnalyzersVersion>6.0.0</MicrosoftCodeAnalysisNetAnalyzersVersion>
-    <MicrosoftNETTestSdkVersion>17.0.0</MicrosoftNETTestSdkVersion>
+    <MicrosoftNETTestSdkVersion>17.1.0</MicrosoftNETTestSdkVersion>
     <NUnitVersion>3.13.2</NUnitVersion>
-    <NUnitConsoleRunnerVersion>3.14.0</NUnitConsoleRunnerVersion>
+    <NUnitConsoleRunnerVersion>3.15.0</NUnitConsoleRunnerVersion>
     <NUnit3TestAdapterVersion>4.2.1</NUnit3TestAdapterVersion>
     <StyleCopAnalyzersVersion>1.1.118</StyleCopAnalyzersVersion>
   </PropertyGroup>

Reply via email to