/ src / settings-ui / Settings.UI.UnitTests / ModelsTests / BasePTModuleSettingsSerializationTests.cs
BasePTModuleSettingsSerializationTests.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Linq; 7 using System.Reflection; 8 using System.Text.Json; 9 using System.Text.Json.Serialization; 10 using Microsoft.PowerToys.Settings.UI.Library; 11 using Microsoft.VisualStudio.TestTools.UnitTesting; 12 13 namespace CommonLibTest 14 { 15 [TestClass] 16 public class BasePTModuleSettingsSerializationTests 17 { 18 /// <summary> 19 /// Test to verify that all classes derived from BasePTModuleSettings are registered 20 /// in the SettingsSerializationContext for Native AOT compatibility. 21 /// </summary> 22 [TestMethod] 23 public void AllBasePTModuleSettingsClasses_ShouldBeRegisteredInSerializationContext() 24 { 25 // Arrange 26 var assembly = typeof(BasePTModuleSettings).Assembly; 27 var settingsClasses = assembly.GetTypes() 28 .Where(t => typeof(BasePTModuleSettings).IsAssignableFrom(t) && !t.IsAbstract && t != typeof(BasePTModuleSettings)) 29 .OrderBy(t => t.Name) 30 .ToList(); 31 32 Assert.IsTrue(settingsClasses.Count > 0, "No BasePTModuleSettings derived classes found. This test may be broken."); 33 34 var jsonSerializerOptions = new JsonSerializerOptions 35 { 36 TypeInfoResolver = SettingsSerializationContext.Default, 37 }; 38 39 var unregisteredTypes = new System.Collections.Generic.List<string>(); 40 41 // Act & Assert 42 foreach (var settingsType in settingsClasses) 43 { 44 var typeInfo = jsonSerializerOptions.TypeInfoResolver?.GetTypeInfo(settingsType, jsonSerializerOptions); 45 46 if (typeInfo == null) 47 { 48 unregisteredTypes.Add(settingsType.FullName ?? settingsType.Name); 49 } 50 } 51 52 // Assert 53 if (unregisteredTypes.Count > 0) 54 { 55 var errorMessage = $"The following {unregisteredTypes.Count} settings class(es) are NOT registered in SettingsSerializationContext:\n" + 56 $"{string.Join("\n", unregisteredTypes.Select(t => $" - {t}"))}\n\n" + 57 $"Please add [JsonSerializable(typeof(ClassName))] attribute to SettingsSerializationContext.cs for each missing type."; 58 Assert.Fail(errorMessage); 59 } 60 61 // Print success message with count 62 Console.WriteLine($"✓ All {settingsClasses.Count} BasePTModuleSettings derived classes are properly registered in SettingsSerializationContext."); 63 } 64 65 /// <summary> 66 /// Test to verify that calling ToJsonString() on an unregistered type throws InvalidOperationException 67 /// with a helpful error message. 68 /// </summary> 69 [TestMethod] 70 [ExpectedException(typeof(InvalidOperationException))] 71 public void ToJsonString_UnregisteredType_ShouldThrowInvalidOperationException() 72 { 73 // Arrange 74 var unregisteredSettings = new UnregisteredTestSettings 75 { 76 Name = "UnregisteredModule", 77 Version = "1.0.0", 78 }; 79 80 // Act - This should throw InvalidOperationException 81 var jsonString = unregisteredSettings.ToJsonString(); 82 83 // Assert - Exception should be thrown, so this line should never be reached 84 Assert.Fail("Expected InvalidOperationException was not thrown."); 85 } 86 87 /// <summary> 88 /// Test to verify that the error message for unregistered types is helpful and contains 89 /// necessary information for developers. 90 /// </summary> 91 [TestMethod] 92 public void ToJsonString_UnregisteredType_ShouldHaveHelpfulErrorMessage() 93 { 94 // Arrange 95 var unregisteredSettings = new UnregisteredTestSettings 96 { 97 Name = "UnregisteredModule", 98 Version = "1.0.0", 99 }; 100 101 // Act & Assert 102 try 103 { 104 var jsonString = unregisteredSettings.ToJsonString(); 105 Assert.Fail("Expected InvalidOperationException was not thrown."); 106 } 107 catch (InvalidOperationException ex) 108 { 109 // Verify the error message contains helpful information 110 Assert.IsTrue(ex.Message.Contains("UnregisteredTestSettings"), "Error message should contain the type name."); 111 Assert.IsTrue(ex.Message.Contains("SettingsSerializationContext"), "Error message should mention SettingsSerializationContext."); 112 Assert.IsTrue(ex.Message.Contains("JsonSerializable"), "Error message should mention JsonSerializable attribute."); 113 114 Console.WriteLine($"✓ Error message is helpful: {ex.Message}"); 115 } 116 } 117 118 /// <summary> 119 /// Test class that is intentionally NOT registered in SettingsSerializationContext 120 /// to verify error handling for unregistered types. 121 /// </summary> 122 private sealed class UnregisteredTestSettings : BasePTModuleSettings 123 { 124 // Intentionally empty - this class should NOT be registered in SettingsSerializationContext 125 } 126 } 127 }