Skip to content

Commit

Permalink
Add more Complex FullName tests and enhance name generation
Browse files Browse the repository at this point in the history
This commit introduces additional FullName tests to validate nested and typed dictionaries. It also updates name generation for types, particularly for generic types, to ensure accurate full names are output. Support for net8 and net9 have also been added.
  • Loading branch information
X39 committed May 11, 2024
1 parent 9486d82 commit a661893
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 37 deletions.
81 changes: 70 additions & 11 deletions X39.Util.Tests/TypeExtensionMethods/FullNameTest.cs
Original file line number Diff line number Diff line change
@@ -1,61 +1,120 @@
using System.Collections.Generic;
using NUnit.Framework;

namespace X39.Util.Tests.TypeExtensionMethods;

[TestFixture]
public class FullNameTest
{

[Test]
public void DictionaryTestUntyped()
{
Assert.AreEqual("System.Collections.Generic.Dictionary<TKey, TValue>",
typeof(System.Collections.Generic.Dictionary<,>).FullNameUncached());
}

[Test]
public void DictionaryTestTyped()
{
Assert.AreEqual("System.Collections.Generic.Dictionary<System.String, System.Int32>",
typeof(System.Collections.Generic.Dictionary<string, int>).FullNameUncached());
}


[Test]
public void ComplexDictionaryTestTyped()
{
Assert.AreEqual(
"System.Collections.Generic.Dictionary<System.String, System.Collections.Generic.Dictionary<System.String, System.Int32>>",
typeof(System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<string, int>>)
.FullNameUncached());
}

[Test]
public void NestedGenericClassTest()
{
Assert.AreEqual(
"X39.Util.Tests.TypeExtensionMethods.Data.GenericClass<System.String, X39.Util.Tests.TypeExtensionMethods.Data.GenericClass<System.String, System.String, System.String>, System.Int32>",
typeof(Data.GenericClass<string, Data.GenericClass<string,string,string>, int>).FullNameUncached());
}

[Test]
public void ComplexNestedGenericInNormalClassTest()
{
Assert.AreEqual(
"X39.Util.Tests.TypeExtensionMethods.Data.NormalClass.SubClassGeneric<System.String, X39.Util.Tests.TypeExtensionMethods.Data.GenericClass<System.String, System.String, System.String>, System.Int32>",
typeof(Data.NormalClass.SubClassGeneric<string, Data.GenericClass<string,string,string>, int>).FullNameUncached());
}

[Test]
public void NormalClassTest()
{
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.NormalClass", typeof(Data.NormalClass).FullNameUncached());
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.NormalClass",
typeof(Data.NormalClass).FullNameUncached());
}

[Test]
public void NormalClassSubClassTest()
{
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.NormalClass.SubClass", typeof(Data.NormalClass.SubClass).FullNameUncached());
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.NormalClass.SubClass",
typeof(Data.NormalClass.SubClass).FullNameUncached());
}

[Test]
public void NormalClassSubClassSubSubClassTest()
{
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.NormalClass.SubClass.SubSubClass", typeof(Data.NormalClass.SubClass.SubSubClass).FullNameUncached());
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.NormalClass.SubClass.SubSubClass",
typeof(Data.NormalClass.SubClass.SubSubClass).FullNameUncached());
}

[Test]
public void NormalClassSubClassGenericTest()
{
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.NormalClass.SubClassGeneric<T1, T2, T3>", typeof(Data.NormalClass.SubClassGeneric<,,>).FullNameUncached());
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.NormalClass.SubClassGeneric<T1, T2, T3>",
typeof(Data.NormalClass.SubClassGeneric<,,>).FullNameUncached());
}

[Test]
public void NormalClassSubClassGenericSubSubClassGenericTest()
{
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.NormalClass.SubClassGeneric<T1, T2, T3>.SubSubClassGeneric<T1>", typeof(Data.NormalClass.SubClassGeneric<,,>.SubSubClassGeneric<>).FullNameUncached());
Assert.AreEqual(
"X39.Util.Tests.TypeExtensionMethods.Data.NormalClass.SubClassGeneric<T1, T2, T3>.SubSubClassGeneric<T1>",
typeof(Data.NormalClass.SubClassGeneric<,,>.SubSubClassGeneric<>).FullNameUncached());
}

[Test]
public void GenericClassTest()
{
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.GenericClass<T1, T2, T3>", typeof(Data.GenericClass<,,>).FullNameUncached());
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.GenericClass<T1, T2, T3>",
typeof(Data.GenericClass<,,>).FullNameUncached());
}

[Test]
public void GenericClassTestSubClass()
{
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.GenericClass<T1, T2, T3>", typeof(Data.GenericClass<,,>.SubClass).FullNameUncached());
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.GenericClass<T1, T2, T3>.SubClass",
typeof(Data.GenericClass<,,>.SubClass).FullNameUncached());
}

[Test]
public void GenericClassTestSubClassSubSubClass()
{
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.GenericClass<T1, T2, T3>", typeof(Data.GenericClass<,,>.SubClass.SubSubClass).FullNameUncached());
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.GenericClass<T1, T2, T3>.SubClass.SubSubClass",
typeof(Data.GenericClass<,,>.SubClass.SubSubClass).FullNameUncached());
}

[Test]
public void GenericClassTestSubClassGenericTest()
{
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.NormalClass.SubClassGeneric<T1, T2, T3>", typeof(Data.GenericClass<,,>.SubClassGeneric<,,>).FullNameUncached());
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.GenericClass<T1, T2, T3>.SubClassGeneric<T1, T2, T3>",
typeof(Data.GenericClass<,,>.SubClassGeneric<,,>).FullNameUncached());
}

[Test]
public void GenericClassTestSubClassGenericSubSubClassGenericTest()
{
Assert.AreEqual("X39.Util.Tests.TypeExtensionMethods.Data.NormalClass.SubClassGeneric<T1, T2, T3>.SubSubClassGeneric<T1>", typeof(Data.GenericClass<,,>.SubClassGeneric<,,>.SubSubClassGeneric<>).FullNameUncached());
Assert.AreEqual(
"X39.Util.Tests.TypeExtensionMethods.Data.GenericClass<T1, T2, T3>.SubClassGeneric<T1, T2, T3>.SubSubClassGeneric<T1>",
typeof(Data.GenericClass<,,>.SubClassGeneric<,,>.SubSubClassGeneric<>).FullNameUncached());
}
}
4 changes: 3 additions & 1 deletion X39.Util.Tests/X39.Util.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net7.0;net6.0;netstandard2.0;netstandard2.1;net46;net461;net462;net47;net471;net472;net48</TargetFrameworks>

<!--<TargetFramework>net7.0</TargetFramework>-->
<TargetFrameworks>net46;net461;net462;net47;net471;net472;net48;netstandard2.0;netstandard2.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<IsPackable>false</IsPackable>
Expand Down
92 changes: 68 additions & 24 deletions X39.Util/TypeExtensionMethods.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable enable
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq.Expressions;
using System.Numerics;
using System.Reflection;
Expand Down Expand Up @@ -108,7 +109,7 @@ public override int GetHashCode()
{
return Arguments.Select((q) => q.GetHashCode()).Aggregate(
Type.GetHashCode(),
(l, r) => (int)(r + 0x9e3779b9 + (l << 6) + (l >> 2)));
(l, r) => (int) (r + 0x9e3779b9 + (l << 6) + (l >> 2)));
}
#endif
}
Expand Down Expand Up @@ -172,44 +173,87 @@ public static string FullName(this Type t)
/// <returns></returns>
public static string FullNameUncached(this Type t)
{
if (t.IsGenericParameter)
/*
X39.Util.Tests.TypeExtensionMethods.Data.GenericClass`3+SubClassGeneric`3
X39.Util.Tests.TypeExtensionMethods.Data.GenericClass`3
X39.Util.Tests.TypeExtensionMethods.Data.GenericClass`3+SubClassGeneric`3[[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[X39.Util.Tests.TypeExtensionMethods.Data.GenericClass`3+SubClassGeneric`3[[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], X39.Util.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
"System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[X39.Util.Tests.TypeExtensionMethods.Data.GenericClass`3[[System.Int32, System.Private.CoreLib, Version=7.0.…"
"System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=7.0.0.0, Culture=ne…"
"System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]"
"System.Collections.Generic.KeyValuePair`2[[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]"
System.String
*/
if (t.FullName is null)
return t.Name;
if (!t.IsGenericType)
return t.FullName?.Replace('+', '.') ?? "NULL";
if (t.IsGenericParameter)
return t.FullName;
var builder = new StringBuilder();
builder.Append(t.Namespace);
builder.Append('.');
#if NET5_0_OR_GREATER
builder.Append(t.Name[..t.Name.IndexOf('`')].Replace('+', '.'));
#else
builder.Append(t.Name.Substring(0, t.Name.IndexOf('`')).Replace('+', '-'));
#endif
builder.Append('<');
builder.Append(string.Join(", ", t.GetGenericArguments().Select(FullName)));
builder.Append('>');
return FullNameCache[t] = builder.ToString();
var fullName = t.FullName;
var argumentsIndex = fullName.IndexOf('[');
var typePart = argumentsIndex == -1 ? fullName : fullName.Substring(0, argumentsIndex);
var plusSplit = typePart.Split('+');
var genericArguments = t.GetGenericArguments();
var index = 0;
bool ran = false;
foreach (var str in plusSplit)
{
if (ran)
builder.Append('.');
ran = true;
var gravisIndex = str.IndexOf('`');
if (gravisIndex == -1)
builder.Append(str);
else
{
var left = str.Substring(0, gravisIndex);
builder.Append(left);

var right = str.Substring(gravisIndex + 1);
var num = int.Parse(right, NumberStyles.Integer, CultureInfo.InvariantCulture);
builder.Append('<');
builder.Append(string.Join(", ", genericArguments.Skip(index).Take(num).Select(FullNameUncached)));
builder.Append('>');
index += num;
}
}

return builder.ToString();
}

/// <summary>
/// Generates a valid C#-Code name from any type, including generics.
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
/// <param name="t">The type for which to generate the name.</param>
/// <returns>The generated C# code name.</returns>
public static string Name(this Type t)
{
if (NameCache.TryGetValue(t, out var name))
return name;
if (!t.IsGenericType) return t.Name;

name = NameUncached(t);
return NameCache[t] = name;
}

/// <summary>
/// Returns the uncached name of the specified <paramref name="t"/> type.
/// If the type is a generic type, the generic arguments are included in the name.
/// </summary>
/// <param name="t">The <see cref="Type"/> to get the name of.</param>
/// <returns>The name of the specified type.</returns>
public static string NameUncached(this Type t)
{
if (!t.IsGenericType)
return t.Name;
var builder = new StringBuilder();
#if NET5_0_OR_GREATER
builder.Append(t.Name[..t.Name.IndexOf('`')]);
#else
builder.Append(t.Name.Substring(0, t.Name.IndexOf('`')));
#endif
var gravisIndex = t.Name.IndexOf('`');
if (gravisIndex == -1)
gravisIndex = t.Name.Length;
builder.Append(t.Name.Substring(0, gravisIndex));
builder.Append('<');
builder.Append(string.Join(", ", t.GetGenericArguments().Select(FullName)));
builder.Append('>');
return NameCache[t] = builder.ToString();
return builder.ToString();
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion X39.Util/X39.Util.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net46;net461;net462;net47;net471;net472;net48;net5.0;net6.0;net7.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<TargetFrameworks>net46;net461;net462;net47;net471;net472;net48;netstandard2.0;netstandard2.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
Expand Down

0 comments on commit a661893

Please sign in to comment.