Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
<DefineConstants>ASSEMBLY_PROCESSOR;STRIDE_PLATFORM_DESKTOP;TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
Expand Down
135 changes: 123 additions & 12 deletions sources/core/Stride.Core.AssemblyProcessor.Tests/TestCecilExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,24 @@ namespace Stride.Core.AssemblyProcessor.Tests;
public class TestCecilExtensions
{
class Nested;
class GenericNested<T>;

private readonly BaseAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
private readonly AssemblyDefinition testAssembly;

public TestCecilExtensions()
{
// Add location of current assembly to MonoCecil search path.
assemblyResolver.AddSearchDirectory(Path.GetDirectoryName(typeof(TestCecilExtensions).Assembly.Location));
var assemblyLocation = Path.GetDirectoryName(typeof(TestCecilExtensions).Assembly.Location);
assemblyResolver.AddSearchDirectory(assemblyLocation);

// Load test assembly for Cecil operations
testAssembly = AssemblyDefinition.ReadAssembly(typeof(TestCecilExtensions).Assembly.Location);
}

private string GenerateNameCecil(Type type)
{
var typeReference = type.GenerateTypeCecil(assemblyResolver);

return typeReference.ConvertAssemblyQualifiedName();
}

Expand All @@ -38,31 +43,137 @@ private void CheckGeneratedNames(Type type)
}

[Fact]
public void TestCecilDotNetAssemblyQualifiedNames()
public void TestAssemblyQualifiedNamesPrimitiveTypes()
{
// Primitive value type
CheckGeneratedNames(typeof(bool));

// Primitive class
CheckGeneratedNames(typeof(byte));
CheckGeneratedNames(typeof(sbyte));
CheckGeneratedNames(typeof(short));
CheckGeneratedNames(typeof(ushort));
CheckGeneratedNames(typeof(int));
CheckGeneratedNames(typeof(uint));
CheckGeneratedNames(typeof(long));
CheckGeneratedNames(typeof(ulong));
CheckGeneratedNames(typeof(float));
CheckGeneratedNames(typeof(double));
CheckGeneratedNames(typeof(decimal));
CheckGeneratedNames(typeof(char));
CheckGeneratedNames(typeof(string));
}

// User class
[Fact]
public void TestAssemblyQualifiedNamesUserTypes()
{
CheckGeneratedNames(typeof(TestCecilExtensions));
CheckGeneratedNames(typeof(Nested));
}

[Fact]
public void TestAssemblyQualifiedNamesGenericTypes()
{
// Closed generics
CheckGeneratedNames(typeof(List<int>));
CheckGeneratedNames(typeof(Dictionary<string, object>));
CheckGeneratedNames(typeof(Dictionary<int, List<string>>));

// Open generics
CheckGeneratedNames(typeof(List<>));
CheckGeneratedNames(typeof(Dictionary<,>));
}

// Nested types
CheckGeneratedNames(typeof(Nested));

// Arrays
[Fact]
public void TestAssemblyQualifiedNamesArrayTypes()
{
CheckGeneratedNames(typeof(int[]));
CheckGeneratedNames(typeof(string[]));
CheckGeneratedNames(typeof(Dictionary<string, object>[]));
CheckGeneratedNames(typeof(int[,]));
CheckGeneratedNames(typeof(int[,,]));
}

// Nullable
[Fact]
public void TestAssemblyQualifiedNamesNullableTypes()
{
CheckGeneratedNames(typeof(bool?));
CheckGeneratedNames(typeof(int?));
CheckGeneratedNames(typeof(decimal?));
}

[Fact]
public void TestConvertCSharpTypeName()
{
var intType = testAssembly.MainModule.TypeSystem.Int32;
Assert.Equal("System.Int32", intType.ConvertCSharp(false));

var stringType = testAssembly.MainModule.TypeSystem.String;
Assert.Equal("System.String", stringType.ConvertCSharp(false));
}

[Fact]
public void TestConvertCSharpGenericTypeName()
{
var listType = testAssembly.MainModule.ImportReference(typeof(List<int>));
var result = listType.ConvertCSharp(false);
Assert.Contains("System.Collections.Generic.List<System.Int32>", result);
}

[Fact]
public void TestConvertCSharpEmptyGenericTypeName()
{
var listType = testAssembly.MainModule.ImportReference(typeof(List<>));
var result = listType.ConvertCSharp(true);
Assert.Contains("System.Collections.Generic.List<>", result);
}

[Fact]
public void TestConvertCSharpArrayTypeName()
{
var arrayType = testAssembly.MainModule.ImportReference(typeof(int[]));
var result = arrayType.ConvertCSharp(false);
Assert.Equal("System.Int32[]", result);
}

[Fact]
public void TestIsResolvedValueType()
{
var intType = testAssembly.MainModule.TypeSystem.Int32;
Assert.True(intType.IsResolvedValueType());

var stringType = testAssembly.MainModule.TypeSystem.String;
Assert.False(stringType.IsResolvedValueType());
}

[Fact]
public void TestMakeGenericType()
{
var listTypeDef = testAssembly.MainModule.ImportReference(typeof(List<>)).Resolve();
var intType = testAssembly.MainModule.TypeSystem.Int32;

var genericInstance = listTypeDef.MakeGenericType(intType);

Assert.IsType<GenericInstanceType>(genericInstance);
var git = (GenericInstanceType)genericInstance;
Assert.Single(git.GenericArguments);
Assert.Equal(intType.FullName, git.GenericArguments[0].FullName);
}

[Fact]
public void TestMakeGenericTypeInvalidArgumentCount()
{
var listTypeDef = testAssembly.MainModule.ImportReference(typeof(List<>)).Resolve();
var intType = testAssembly.MainModule.TypeSystem.Int32;
var stringType = testAssembly.MainModule.TypeSystem.String;

// List<T> expects 1 type argument, not 2
Assert.Throws<ArgumentException>(() => listTypeDef.MakeGenericType(intType, stringType));
}

[Fact]
public void TestMakeGenericTypeNoArguments()
{
var listTypeDef = testAssembly.MainModule.ImportReference(typeof(List<>)).Resolve();

// List<T> expects 1 type argument, providing 0 should throw
Assert.Throws<ArgumentException>(() => listTypeDef.MakeGenericType());
}
}
Loading
Loading