#nullable enable using System.Reflection; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; namespace UnitTests_Parallelizable.ConfigurationTests; public class ConfigurationPropertyAttributeTests { /// /// If this test fails, you need to add a new property to to support serialization of the new property type. /// [Fact] public void Verify_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 [ConfigurationProperty] attribute var properties = new List (); foreach (var type in types) { properties.AddRange (type.GetProperties ().Where (p => p.GetCustomAttributes (typeof (ConfigurationPropertyAttribute), 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); } } [Fact] public void OmitClassName_Omits () { // Color.Schemes is serialized as "Schemes", not "Colors.Schemes" PropertyInfo pi = typeof (SchemeManager).GetProperty ("Schemes")!; var scp = (ConfigurationPropertyAttribute)pi!.GetCustomAttribute (typeof (ConfigurationPropertyAttribute))!; Assert.True (scp!.Scope == typeof (ThemeScope)); Assert.True (scp.OmitClassName); } 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}'."); } } } } } }