#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 to to support serialization of the new property type. /// [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}'."); } } } } } }