#nullable enable
using System.Reflection;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
namespace Terminal.Gui.ConfigurationTests;
public class SerializableConfigurationPropertyTests
{
///
/// If this test fails, you need to add a new property with the SerializableConfigurationProperty attribute.
///
[Fact]
public void Test_SerializableConfigurationProperty_Types_Added_To_JsonSerializerContext ()
{
// The assembly containing the types to inspect
var assembly = Assembly.GetAssembly (typeof (SourceGenerationContext));
// Get all types from the assembly
var types = assembly!.GetTypes ();
// Find all properties with the SerializableConfigurationProperty attribute
var properties = new List ();
foreach (var type in types)
{
properties.AddRange (type.GetProperties ().Where (p =>
p.GetCustomAttributes (typeof (SerializableConfigurationProperty), false).Any ()));
}
// Get the types of the properties
var propertyTypes = properties.Select (p => p.PropertyType).Distinct ();
// Get the types registered in the JsonSerializerContext derived class
var contextType = typeof (SourceGenerationContext);
var contextTypes = GetRegisteredTypes (contextType);
// Ensure all property types are included in the JsonSerializerContext derived class
IEnumerable collection = contextTypes as Type [] ?? contextTypes.ToArray ();
foreach (var type in propertyTypes)
{
Assert.Contains (type, collection);
}
// Ensure no property has the generic JsonStringEnumConverter<>
EnsureNoSpecifiedConverters (properties, new [] { typeof (JsonStringEnumConverter<>) });
// Ensure no property has the type RuneJsonConverter
EnsureNoSpecifiedConverters (properties, new [] { typeof (RuneJsonConverter) });
// Ensure no property has the type KeyJsonConverter
EnsureNoSpecifiedConverters (properties, new [] { typeof (KeyJsonConverter) });
// Find all classes with the JsonConverter attribute of type ScopeJsonConverter<>
var classesWithScopeJsonConverter = types.Where (t =>
t.GetCustomAttributes (typeof (JsonConverterAttribute), false)
.Any (attr => ((JsonConverterAttribute)attr).ConverterType!.IsGenericType &&
((JsonConverterAttribute)attr).ConverterType!.GetGenericTypeDefinition () == typeof (ScopeJsonConverter<>)));
// Ensure all these classes are included in the JsonSerializerContext derived class
foreach (var type in classesWithScopeJsonConverter)
{
Assert.Contains (type, collection);
}
}
private static IEnumerable GetRegisteredTypes (Type contextType)
{
// Use reflection to find which types are registered in the JsonSerializerContext
var registeredTypes = new List ();
var properties = contextType.GetProperties (BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
foreach (var property in properties)
{
if (property.PropertyType.IsGenericType &&
property.PropertyType.GetGenericTypeDefinition () == typeof (JsonTypeInfo<>))
{
registeredTypes.Add (property.PropertyType.GetGenericArguments () [0]);
}
}
return registeredTypes.Distinct ();
}
private static void EnsureNoSpecifiedConverters (List properties, IEnumerable converterTypes)
{
// Ensure no property has any of the specified converter types
foreach (var property in properties)
{
var jsonConverterAttributes = property.GetCustomAttributes (typeof (JsonConverterAttribute), false)
.Cast ();
foreach (var attribute in jsonConverterAttributes)
{
foreach (var converterType in converterTypes)
{
if (attribute.ConverterType!.IsGenericType &&
attribute.ConverterType.GetGenericTypeDefinition () == converterType)
{
Assert.Fail ($"Property '{property.Name}' should not use the converter '{converterType.Name}'.");
}
if (!attribute.ConverterType!.IsGenericType &&
attribute.ConverterType == converterType)
{
Assert.Fail ($"Property '{property.Name}' should not use the converter '{converterType.Name}'.");
}
}
}
}
}
}