Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 140 additions & 11 deletions lang/csharp/src/apache/test/AvroGen/AvroGenHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using NUnit.Framework;
using Avro.Specific;

namespace Avro.Test.AvroGen
{
Expand Down Expand Up @@ -112,17 +113,18 @@ public static Assembly CompileCSharpFilesIntoLibrary(IEnumerable<string> sourceF
// 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}");
// }
// }
//}
#if DEBUG
if (!compilationResult.Success)
{
foreach (Diagnostic diagnostic in compilationResult.Diagnostics)
{
if (diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error)
{
TestContext.WriteLine($"{diagnostic.Id} - {diagnostic.GetMessage()} - {diagnostic.Location}");
}
}
}
#endif

Assert.That(compilationResult.Success, Is.True);

Expand All @@ -131,6 +133,7 @@ public static Assembly CompileCSharpFilesIntoLibrary(IEnumerable<string> sourceF
return null;
}

// Load assembly from stream
compilerStream.Seek(0, SeekOrigin.Begin);
return Assembly.Load(compilerStream.ToArray());
}
Expand All @@ -152,5 +155,131 @@ public static string CreateEmptyTemporyFolder(out string uniqueId, string path =

return tempFolder;
}

public static Assembly CompileCSharpFilesAndCheckTypes(
string outputDir,
string assemblyName,
IEnumerable<string> typeNamesToCheck = null,
IEnumerable<string> generatedFilesToCheck = null)
{
// 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 = CompileCSharpFilesIntoLibrary(
new DirectoryInfo(outputDir)
.EnumerateFiles("*.cs", SearchOption.AllDirectories)
.Select(fi => fi.FullName),
assemblyName);

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);

// Protocols are abstract and cannot be instantiated
if (typeof(ISpecificProtocol).IsAssignableFrom(type))
{
Assert.That(type.IsAbstract, Is.True);

// If directly inherited from ISpecificProtocol, use reflection to read static private field
// holding the protocol. Callback objects are not directly inherited from ISpecificProtocol,
// so private fields in the base class cannot be accessed
if (type.BaseType.Equals(typeof(ISpecificProtocol)))
{
// Use reflection to read static field, holding the protocol
FieldInfo protocolField = type.GetField("protocol", BindingFlags.NonPublic | BindingFlags.Static);
Protocol protocol = protocolField.GetValue(null) as Protocol;

Assert.That(protocol, Is.Not.Null);
}
}
else
{
Assert.That(type.IsClass || type.IsEnum, Is.True);

// Instantiate object
object obj = Activator.CreateInstance(type);
Assert.That(obj, Is.Not.Null);

// If ISpecificRecord, call its member for sanity check
if (obj is ISpecificRecord record)
{
// Read record's schema object
Assert.That(record.Schema, Is.Not.Null);
// Force exception by reading/writing invalid field
Assert.Throws<AvroRuntimeException>(() => record.Get(-1));
Assert.Throws<AvroRuntimeException>(() => record.Put(-1, null));
}
}
}
}

return assembly;
}

public static Assembly TestSchema(
string schema,
IEnumerable<string> typeNamesToCheck = null,
IEnumerable<KeyValuePair<string, string>> namespaceMapping = null,
IEnumerable<string> generatedFilesToCheck = null)
{
// Create temp folder
string outputDir = 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));

return CompileCSharpFilesAndCheckTypes(outputDir, uniqueId, typeNamesToCheck, generatedFilesToCheck);
}
finally
{
Directory.Delete(outputDir, true);
}
}

public static Assembly TestProtocol(
string protocol,
IEnumerable<string> typeNamesToCheck = null,
IEnumerable<KeyValuePair<string, string>> namespaceMapping = null,
IEnumerable<string> generatedFilesToCheck = null)
{
// Create temp folder
string outputDir = CreateEmptyTemporyFolder(out string uniqueId);

try
{
// Save protocol
string schemaFileName = Path.Combine(outputDir, $"{uniqueId}.avpr");
System.IO.File.WriteAllText(schemaFileName, protocol);

// Generate from protocol file
Assert.That(AvroGenTool.GenProtocol(schemaFileName, outputDir, namespaceMapping ?? new Dictionary<string, string>()), Is.EqualTo(0));

return CompileCSharpFilesAndCheckTypes(outputDir, uniqueId, typeNamesToCheck, generatedFilesToCheck);
}
finally
{
Directory.Delete(outputDir, true);
}
}
}
}
Loading