Fixes #3109. AOT support with .Net 8. (#3638)

* Add a native AOT project.

* Fixes Text.Json to work with native AOT.

* Fix silent errors on unit tests when testing the Red color which has a length of 3.

* Allowing test custom configuration without the config.json file match the unit tests configurations.

* Fix unit test if tested alone.

* Add native project into solution.

* Fix merge errors.

* Setting ConfigurationManager.ThrowOnJsonErrors as true to throw any serialization issue when published file runs.

* Remove unnecessary using's.

* Added unit test to ensure all serialization is properly configured.

* Fix warnings.

* Remove ThrowOnJsonErrors.

* Fix warnings.

---------

Co-authored-by: Tig <tig@users.noreply.github.com>
This commit is contained in:
BDisp
2024-08-06 19:05:36 +01:00
committed by GitHub
parent 1b973eed5a
commit 63e75b7413
27 changed files with 443 additions and 82 deletions

View File

@@ -38,7 +38,7 @@ public class ConfigurationManagerTests
}
// act
Settings ["Application.QuitKey"].PropertyValue = Key.Q;
Settings! ["Application.QuitKey"].PropertyValue = Key.Q;
Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
@@ -130,7 +130,7 @@ public class ConfigurationManagerTests
{ "Disabled", new Attribute (Color.White) }, { "Normal", new Attribute (Color.Blue) }
};
dictCopy = (Dictionary<string, Attribute>)DeepMemberWiseCopy (dictSrc, dictDest);
Assert.Equal (2, dictCopy.Count);
Assert.Equal (2, dictCopy!.Count);
Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]);
Assert.Equal (dictSrc ["Normal"], dictCopy ["Normal"]);
@@ -141,7 +141,7 @@ public class ConfigurationManagerTests
};
dictSrc = new Dictionary<string, Attribute> { { "Disabled", new Attribute (Color.White) } };
dictCopy = (Dictionary<string, Attribute>)DeepMemberWiseCopy (dictSrc, dictDest);
Assert.Equal (2, dictCopy.Count);
Assert.Equal (2, dictCopy!.Count);
Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]);
Assert.Equal (dictDest ["Normal"], dictCopy ["Normal"]);
}
@@ -151,7 +151,7 @@ public class ConfigurationManagerTests
{
Reset ();
Settings ["Application.QuitKey"].PropertyValue = Key.Q;
Settings! ["Application.QuitKey"].PropertyValue = Key.Q;
Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
@@ -163,16 +163,16 @@ public class ConfigurationManagerTests
fired = true;
// assert
Assert.Equal (Key.Esc, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode);
Assert.Equal (Key.Esc, (((Key)Settings! ["Application.QuitKey"].PropertyValue)!).KeyCode);
Assert.Equal (
KeyCode.F6,
((Key)Settings ["Application.NextTabGroupKey"].PropertyValue).KeyCode
(((Key)Settings ["Application.NextTabGroupKey"].PropertyValue)!).KeyCode
);
Assert.Equal (
KeyCode.F6 | KeyCode.ShiftMask,
((Key)Settings ["Application.PrevTabGroupKey"].PropertyValue).KeyCode
(((Key)Settings ["Application.PrevTabGroupKey"].PropertyValue)!).KeyCode
);
}
@@ -228,7 +228,7 @@ public class ConfigurationManagerTests
// arrange
Reset ();
Settings ["Application.QuitKey"].PropertyValue = Key.Q;
Settings! ["Application.QuitKey"].PropertyValue = Key.Q;
Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
Settings.Apply ();
@@ -242,7 +242,7 @@ public class ConfigurationManagerTests
Reset ();
// assert
Assert.NotEmpty (Themes);
Assert.NotEmpty (Themes!);
Assert.Equal ("Default", Themes.Theme);
Assert.Equal (Key.Esc, Application.QuitKey);
Assert.Equal (Key.F6, Application.NextTabGroupKey);
@@ -274,7 +274,7 @@ public class ConfigurationManagerTests
{
Locations = ConfigLocations.DefaultOnly;
Reset ();
Assert.NotEmpty (Themes);
Assert.NotEmpty (Themes!);
Assert.Equal ("Default", Themes.Theme);
}
@@ -367,7 +367,7 @@ public class ConfigurationManagerTests
// Serialize to a JSON string
string json = ToJson ();
// Write the JSON string to the file
// Write the JSON string to the file
File.WriteAllText ("config.json", json);
}
@@ -377,23 +377,23 @@ public class ConfigurationManagerTests
Locations = ConfigLocations.All;
Reset ();
Assert.NotEmpty (Settings);
Assert.NotEmpty (Settings!);
// test that all ConfigProperties have our attribute
Assert.All (
Settings,
item => Assert.NotEmpty (
item.Value.PropertyInfo.CustomAttributes.Where (
a => a.AttributeType == typeof (SerializableConfigurationProperty)
)
item.Value.PropertyInfo!.CustomAttributes.Where (
a => a.AttributeType == typeof (SerializableConfigurationProperty)
)
)
);
Assert.Empty (
Settings.Where (
cp => cp.Value.PropertyInfo.GetCustomAttribute (
typeof (SerializableConfigurationProperty)
)
cp => cp.Value.PropertyInfo!.GetCustomAttribute (
typeof (SerializableConfigurationProperty)
)
== null
)
);
@@ -401,12 +401,12 @@ public class ConfigurationManagerTests
// Application is a static class
PropertyInfo pi = typeof (Application).GetProperty ("QuitKey");
Assert.Equal (pi, Settings ["Application.QuitKey"].PropertyInfo);
// FrameView is not a static class and DefaultBorderStyle is Scope.Scheme
pi = typeof (FrameView).GetProperty ("DefaultBorderStyle");
Assert.False (Settings.ContainsKey ("FrameView.DefaultBorderStyle"));
Assert.True (Themes ["Default"].ContainsKey ("FrameView.DefaultBorderStyle"));
Assert.True (Themes! ["Default"].ContainsKey ("FrameView.DefaultBorderStyle"));
Assert.Equal (pi, Themes! ["Default"] ["FrameView.DefaultBorderStyle"].PropertyInfo);
}
[Fact]
@@ -414,31 +414,31 @@ public class ConfigurationManagerTests
{
// Color.ColorSchemes is serialized as "ColorSchemes", not "Colors.ColorSchemes"
PropertyInfo pi = typeof (Colors).GetProperty ("ColorSchemes");
var scp = (SerializableConfigurationProperty)pi.GetCustomAttribute (typeof (SerializableConfigurationProperty));
Assert.True (scp.Scope == typeof (ThemeScope));
var scp = (SerializableConfigurationProperty)pi!.GetCustomAttribute (typeof (SerializableConfigurationProperty));
Assert.True (scp!.Scope == typeof (ThemeScope));
Assert.True (scp.OmitClassName);
Reset ();
Assert.Equal (pi, Themes ["Default"] ["ColorSchemes"].PropertyInfo);
Assert.Equal (pi, Themes! ["Default"] ["ColorSchemes"].PropertyInfo);
}
[Fact]
[AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
public void TestConfigurationManagerInitDriver ()
{
Assert.Equal ("Default", Themes.Theme);
Assert.Equal ("Default", Themes!.Theme);
Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground);
Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"]!.Normal.Foreground);
Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background);
// Change Base
Stream json = ToStream ();
Settings.Update (json, "TestConfigurationManagerInitDriver");
Settings!.Update (json, "TestConfigurationManagerInitDriver");
Dictionary<string, ColorScheme> colorSchemes =
(Dictionary<string, ColorScheme>)Themes [Themes.Theme] ["ColorSchemes"].PropertyValue;
Assert.Equal (Colors.ColorSchemes ["Base"], colorSchemes ["Base"]);
Assert.Equal (Colors.ColorSchemes ["Base"], colorSchemes! ["Base"]);
Assert.Equal (Colors.ColorSchemes ["TopLevel"], colorSchemes ["TopLevel"]);
Assert.Equal (Colors.ColorSchemes ["Error"], colorSchemes ["Error"]);
Assert.Equal (Colors.ColorSchemes ["Dialog"], colorSchemes ["Dialog"]);
@@ -489,7 +489,7 @@ public class ConfigurationManagerTests
}
}";
Settings.Update (json, "test");
Settings!.Update (json, "test");
// AbNormal is not a ColorScheme attribute
json = @"
@@ -514,7 +514,7 @@ public class ConfigurationManagerTests
Settings.Update (json, "test");
// Modify hotNormal background only
// Modify hotNormal background only
json = @"
{
""Themes"" : [
@@ -572,7 +572,7 @@ public class ConfigurationManagerTests
]
}";
var jsonException = Assert.Throws<JsonException> (() => Settings.Update (json, "test"));
var jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test"));
Assert.Equal ("Unexpected color name: brown.", jsonException.Message);
// AbNormal is not a ColorScheme attribute
@@ -596,10 +596,10 @@ public class ConfigurationManagerTests
]
}";
jsonException = Assert.Throws<JsonException> (() => Settings.Update (json, "test"));
jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test"));
Assert.Equal ("Unrecognized ColorScheme Attribute name: AbNormal.", jsonException.Message);
// Modify hotNormal background only
// Modify hotNormal background only
json = @"
{
""Themes"" : [
@@ -619,7 +619,7 @@ public class ConfigurationManagerTests
]
}";
jsonException = Assert.Throws<JsonException> (() => Settings.Update (json, "test"));
jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test"));
Assert.Equal ("Both Foreground and Background colors must be provided.", jsonException.Message);
// Unknown property
@@ -628,7 +628,7 @@ public class ConfigurationManagerTests
""Unknown"" : ""Not known""
}";
jsonException = Assert.Throws<JsonException> (() => Settings.Update (json, "test"));
jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test"));
Assert.StartsWith ("Unknown property", jsonException.Message);
Assert.Equal (0, _jsonErrors.Length);
@@ -644,7 +644,7 @@ public class ConfigurationManagerTests
GetHardCodedDefaults ();
Stream stream = ToStream ();
Settings.Update (stream, "TestConfigurationManagerToJson");
Settings!.Update (stream, "TestConfigurationManagerToJson");
}
[Fact]
@@ -790,19 +790,19 @@ public class ConfigurationManagerTests
Reset ();
ThrowOnJsonErrors = true;
Settings.Update (json, "TestConfigurationManagerUpdateFromJson");
Settings!.Update (json, "TestConfigurationManagerUpdateFromJson");
Assert.Equal (KeyCode.Esc, Application.QuitKey.KeyCode);
Assert.Equal (KeyCode.Z | KeyCode.AltMask, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode);
Assert.Equal (KeyCode.Z | KeyCode.AltMask, ((Key)Settings ["Application.QuitKey"].PropertyValue)!.KeyCode);
Assert.Equal ("Default", Themes.Theme);
Assert.Equal ("Default", Themes!.Theme);
Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground);
Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"]!.Normal.Foreground);
Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background);
Dictionary<string, ColorScheme> colorSchemes =
(Dictionary<string, ColorScheme>)Themes.First ().Value ["ColorSchemes"].PropertyValue;
Assert.Equal (new Color (Color.White), colorSchemes ["Base"].Normal.Foreground);
Assert.Equal (new Color (Color.White), colorSchemes! ["Base"].Normal.Foreground);
Assert.Equal (new Color (Color.Blue), colorSchemes ["Base"].Normal.Background);
// Now re-apply

View File

@@ -0,0 +1,86 @@
#nullable enable
using System.Reflection;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
namespace Terminal.Gui.ConfigurationTests;
public class SerializableConfigurationPropertyTests
{
[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<PropertyInfo> ();
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<Type> collection = contextTypes as Type [] ?? contextTypes.ToArray ();
foreach (var type in propertyTypes)
{
Assert.Contains (type, collection);
}
// Ensure no property has the generic JsonStringEnumConverter<>
foreach (var property in properties)
{
var jsonConverterAttributes = property.GetCustomAttributes (typeof (JsonConverterAttribute), false)
.Cast<JsonConverterAttribute> ();
foreach (var attribute in jsonConverterAttributes)
{
Assert.False (attribute.ConverterType!.IsGenericType &&
attribute.ConverterType.GetGenericTypeDefinition () == typeof (JsonStringEnumConverter<>));
}
}
// 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 IEnumerable<Type> GetRegisteredTypes (Type contextType)
{
// Use reflection to find which types are registered in the JsonSerializerContext
var registeredTypes = new List<Type> ();
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 ();
}
}

View File

@@ -29,13 +29,18 @@ public class ThemeScopeTests
{
Reset ();
Assert.NotEmpty (Themes);
Assert.Equal (Alignment.End, Dialog.DefaultButtonAlignment);
Alignment savedValue = Dialog.DefaultButtonAlignment;
Alignment newValue = Alignment.Center != savedValue ? Alignment.Center : Alignment.Start;
Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = Alignment.Center;
Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = newValue;
ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply ();
Assert.Equal (Alignment.Center, Dialog.DefaultButtonAlignment);
Reset ();
Assert.Equal (newValue, Dialog.DefaultButtonAlignment);
// Replace with the savedValue to avoid failures on other unit tests that rely on the default value
Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = savedValue;
ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply ();
Assert.Equal (savedValue, Dialog.DefaultButtonAlignment);
}
[Fact]

View File

@@ -76,6 +76,9 @@ public class ThemeTests
[Fact]
public void TestSerialize_RoundTrip ()
{
// This is needed to test only this alone
Reset ();
var theme = new ThemeScope ();
theme ["Dialog.DefaultButtonAlignment"].PropertyValue = Alignment.End;