From 3d4b0ae91cba10d4eab8a05d605ce32a89edc84e Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 23 Oct 2022 03:55:31 -0400 Subject: [PATCH] Reenable form factor markup extension with some tests --- .../Pages/PlatformInfoPage.xaml | 18 +- .../Platform/IRuntimePlatform.cs | 13 +- .../AvaloniaXamlIlCompiler.cs | 5 +- .../AvaloniaXamlIlOnFormFactorTransformer.cs | 159 +++++++++ .../AvaloniaXamlIlOnPlatformTransformer.cs | 15 +- .../AvaloniaXamlIlWellKnownTypes.cs | 4 + .../Transformers/OnFormFactorTransformer.cs | 128 -------- .../MarkupExtensions/OnFormFactorExtension.cs | 21 +- .../MarkupExtensions/OnPlatformExtension.cs | 8 +- .../XamlIl/Runtime/XamlIlRuntimeHelpers.cs | 5 + .../OnFormFactorExtensionTests.cs | 309 ++++++++++++++++++ 11 files changed, 526 insertions(+), 159 deletions(-) create mode 100644 src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOnFormFactorTransformer.cs delete mode 100644 src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/OnFormFactorTransformer.cs create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnFormFactorExtensionTests.cs diff --git a/samples/ControlCatalog/Pages/PlatformInfoPage.xaml b/samples/ControlCatalog/Pages/PlatformInfoPage.xaml index 8eb0dfd8dd..09f12e84c6 100644 --- a/samples/ControlCatalog/Pages/PlatformInfoPage.xaml +++ b/samples/ControlCatalog/Pages/PlatformInfoPage.xaml @@ -7,19 +7,17 @@ d:DesignWidth="400" mc:Ignorable="d"> - - - - - - - - - - + + + + + + + + diff --git a/src/Avalonia.Base/Platform/IRuntimePlatform.cs b/src/Avalonia.Base/Platform/IRuntimePlatform.cs index 8ab04f5995..3f8983479f 100644 --- a/src/Avalonia.Base/Platform/IRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/IRuntimePlatform.cs @@ -17,13 +17,16 @@ namespace Avalonia.Platform IntPtr Address { get; } int Size { get; } bool IsDisposed { get; } - + } [Unstable] public struct RuntimePlatformInfo { public OperatingSystemType OperatingSystem { get; set; } + + public FormFactorType FormFactor => IsDesktop ? FormFactorType.Desktop : + IsMobile ? FormFactorType.Mobile : FormFactorType.Unknown; public bool IsDesktop { get; set; } public bool IsMobile { get; set; } public bool IsBrowser { get; set; } @@ -44,4 +47,12 @@ namespace Avalonia.Platform iOS, Browser } + + [Unstable] + public enum FormFactorType + { + Unknown, + Desktop, + Mobile + } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs index 134d8ba908..26c5aed581 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs @@ -57,10 +57,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions new AvaloniaXamlIlResolveByNameMarkupExtensionReplacer() ); InsertAfter( - new AvaloniaXamlIlOnPlatformTransformer()); + new AvaloniaXamlIlOnPlatformTransformer(), + new AvaloniaXamlIlOnFormFactorTransformer()); - InsertBefore( - new OnFormFactorTransformer()); InsertAfter( new XDataTypeTransformer()); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOnFormFactorTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOnFormFactorTransformer.cs new file mode 100644 index 0000000000..1391f7a0aa --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOnFormFactorTransformer.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using XamlX; +using XamlX.Ast; +using XamlX.Emit; +using XamlX.IL; +using XamlX.Transform; +using XamlX.TypeSystem; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; + +internal class AvaloniaXamlIlOnFormFactorTransformer : IXamlAstTransformer +{ + private const string OnFormFactorFqn = "Avalonia.Markup.Xaml:Avalonia.Markup.Xaml.MarkupExtensions.OnFormFactorExtension"; + + public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) + { + if (node is XamlAstXamlPropertyValueNode targetPropertyNode + && targetPropertyNode.Values.OfType().FirstOrDefault() is + { + Value: XamlAstObjectNode { Type: XamlAstClrTypeReference { Type: { } type } } objectNode + } + && type.GetFqn().StartsWith(OnFormFactorFqn)) + { + var typeArgument = type.GenericArguments?.FirstOrDefault(); + + OnFormFactorDefaultNode defaultValue = null; + var values = new List(); + + var directives = objectNode.Children.OfType().ToArray(); + + foreach (var child in objectNode.Arguments.Take(1)) + { + defaultValue = new OnFormFactorDefaultNode(new XamlAstXamlPropertyValueNode(child, targetPropertyNode.Property, child)); + } + + foreach (var extProp in objectNode.Children.OfType()) + { + var propName = extProp.Property.GetClrProperty().Name.Trim(); + var transformed = TransformNode(targetPropertyNode.Property, extProp.Values, + typeArgument, directives, extProp); + if (propName.Equals("DEFAULT", StringComparison.OrdinalIgnoreCase)) + { + defaultValue = new OnFormFactorDefaultNode(transformed); + } + else if (propName != "CONTENT") + { + values.Add(new OnFormFactorBranchNode( + ConvertPlatformNode(propName, extProp), + transformed)); + } + } + + return new AvaloniaXamlIlConditionalNode(defaultValue, values, node); + } + + return node; + + XamlConstantNode ConvertPlatformNode(string propName, IXamlLineInfo li) + { + var enumType = context.GetAvaloniaTypes().FormFactorType; + + if (TypeSystemHelpers.TryGetEnumValueNode(enumType, propName, li, false, out var enumConstantNode)) + { + return enumConstantNode; + } + + throw new XamlParseException($"Unable to parse form factor name: \"{propName}\"", li); + } + + XamlAstXamlPropertyValueNode TransformNode( + IXamlAstPropertyReference property, + IReadOnlyCollection values, + IXamlType suggestedType, + IReadOnlyCollection directives, + IXamlLineInfo line) + { + if (suggestedType is not null) + { + values = values + .Select(v => XamlTransformHelpers + .TryGetCorrectlyTypedValue(context, v, suggestedType, out var converted) + ? converted : v) + .ToArray(); + } + + if (directives.Any()) + { + foreach (var value in values) + { + if (value is XamlAstObjectNode xamlAstObjectNode) + { + xamlAstObjectNode.Children.AddRange(directives); + } + } + } + + return new XamlAstXamlPropertyValueNode(line, property, values); + } + } + + private sealed class OnFormFactorBranchNode : AvaloniaXamlIlConditionalBranchNode + { + private IXamlAstNode _platform; + private IXamlAstNode _value; + + public OnFormFactorBranchNode( + IXamlAstNode platform, + IXamlAstNode value) + : base(value) + { + _platform = platform; + _value = value; + } + + public override void VisitChildren(IXamlAstVisitor visitor) + { + _platform = _platform.Visit(visitor); + _value = _value.Visit(visitor); + } + + public override void EmitCondition(XamlEmitContext context, IXamlILEmitter codeGen) + { + var enumType = context.GetAvaloniaTypes().FormFactorType; + var isOnFormFactorMethod = context.GetAvaloniaTypes().IsOnFormFactorMethod; + codeGen.Ldloc(context.ContextLocal); + context.Emit(_platform, codeGen, enumType); + codeGen.EmitCall(isOnFormFactorMethod); + } + + public override void EmitBody(XamlEmitContext context, IXamlILEmitter codeGen) + { + context.Emit(_value, codeGen, null); + } + } + + private sealed class OnFormFactorDefaultNode : AvaloniaXamlIlConditionalDefaultNode + { + private IXamlAstNode _value; + + public OnFormFactorDefaultNode( + IXamlAstNode value) + : base(value) + { + _value = value; + } + + public override void VisitChildren(IXamlAstVisitor visitor) + { + _value = _value.Visit(visitor); + } + + public override void EmitBody(XamlEmitContext context, IXamlILEmitter codeGen) + { + context.Emit(_value, codeGen, null); + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOnPlatformTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOnPlatformTransformer.cs index 2d2c4043dd..5b6e4c759c 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOnPlatformTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOnPlatformTransformer.cs @@ -25,13 +25,6 @@ internal class AvaloniaXamlIlOnPlatformTransformer : IXamlAstTransformer && type.GetFqn().StartsWith(OnPlatformFqn)) { var typeArgument = type.GenericArguments?.FirstOrDefault(); - var targetType = typeArgument ?? objectNode.Type.GetClrType(); - if (targetType is null) - { - throw new XamlParseException( - "Unable to find OnPlatform property type. Try to set x:TypeArguments on the markup extension.", - node); - } OnPlatformDefaultNode defaultValue = null; var values = new List(); @@ -80,17 +73,17 @@ internal class AvaloniaXamlIlOnPlatformTransformer : IXamlAstTransformer } else { - var platformStr = extProp.Property.GetClrProperty().Name.Trim().ToUpperInvariant(); + var propName = extProp.Property.GetClrProperty().Name.Trim().ToUpperInvariant(); var transformed = TransformNode(targetPropertyNode.Property, extProp.Values, typeArgument, directives, extProp); - if (platformStr.Equals("DEFAULT", StringComparison.OrdinalIgnoreCase)) + if (propName.Equals("DEFAULT", StringComparison.OrdinalIgnoreCase)) { defaultValue = new OnPlatformDefaultNode(transformed); } - else if (platformStr != "CONTENT") + else if (propName != "CONTENT") { values.Add(new OnPlatformBranchNode( - ConvertPlatformNode(platformStr, extProp), + ConvertPlatformNode(propName, extProp), transformed)); } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 1de4b2542a..7730c7448f 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -105,6 +105,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType OperatingSystemType { get; } public IXamlMethod IsOnPlatformMethod { get; } + public IXamlType FormFactorType { get; set; } + public IXamlMethod IsOnFormFactorMethod { get; set; } public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg) { @@ -236,6 +238,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers OperatingSystemType = cfg.TypeSystem.GetType("Avalonia.Platform.OperatingSystemType"); IsOnPlatformMethod = RuntimeHelpers.FindMethod(m => m.IsStatic && m.Parameters.Count == 2 && m.Name == "IsOnPlatform"); + FormFactorType = cfg.TypeSystem.GetType("Avalonia.Platform.FormFactorType"); + IsOnFormFactorMethod = RuntimeHelpers.FindMethod(m => m.IsStatic && m.Parameters.Count == 2 && m.Name == "IsOnFormFactor"); } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/OnFormFactorTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/OnFormFactorTransformer.cs deleted file mode 100644 index 07570d33bd..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/OnFormFactorTransformer.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System; -using System.Linq; -using XamlX.Ast; -using XamlX.Transform; -using XamlX.Transform.Transformers; -using XamlX.TypeSystem; - -namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; - -internal class OnFormFactorTransformer : IXamlAstTransformer -{ - public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) - { - // if (node is XamlAstObjectNode xmlobj - // && xmlobj.Type is XamlAstXmlTypeReference xmlref - // && xmlref.Name.StartsWith("OnFormFactor") - // && !xmlref.GenericArguments.Any()) - // { - // IXamlType propertyType = null; - // - // if (context.ParentNodes().FirstOrDefault() is XamlAstXamlPropertyValueNode parentPropertyValueNode) - // { - // var property = (XamlAstNamePropertyReference)parentPropertyValueNode.Property; - // var declaringType = TypeReferenceResolver.ResolveType(context, (XamlAstXmlTypeReference)property.DeclaringType, context.StrictMode); - // propertyType = declaringType.Type.GetAllProperties().First(p => p.Name == property.Name).PropertyType; - // } - // else if (context.ParentNodes().FirstOrDefault() is XamlAstObjectNode parentNode) - // { - // var parentType = parentNode.Type is XamlAstClrTypeReference clrType - // ? clrType.Type - // : TypeReferenceResolver.ResolveType(context, (XamlAstXmlTypeReference)parentNode.Type, context.StrictMode).Type; - // var contentProperty = context.Configuration.FindContentProperty(parentType); - // propertyType = contentProperty.PropertyType; - // } - // - // if (propertyType is null) - // { - // throw new InvalidOperationException("Unable to find OnFormFactor property type"); - // } - // - // xmlobj.Type = TypeReferenceResolver.ResolveType(context, xmlref.XmlNamespace, xmlref.Name, - // xmlref.IsMarkupExtension, new[] { new XamlAstClrTypeReference(xmlref, propertyType, false) }, - // xmlref, context.StrictMode); - // } - // - // if (node is XamlAstNamePropertyReference xmlprop - // && xmlprop.DeclaringType is XamlAstXmlTypeReference propxmlref - // && propxmlref.Name.StartsWith("OnFormFactor") - // && !propxmlref.GenericArguments.Any()) - // { - // var expectedType = context.ParentNodes().OfType() - // .First(n => n.Type is XamlAstClrTypeReference clrRef && clrRef.Type.Name.StartsWith("OnFormFactor") - // || n.Type is XamlAstXmlTypeReference xmlRef && xmlRef.Name.StartsWith("OnFormFactor")) - // .Type; - // xmlprop.DeclaringType = expectedType; - // xmlprop.TargetType = expectedType; - // } - - // if (node is XamlAstObjectNode onobj - // && onobj.Type is XamlAstXmlTypeReference onref - // && onref.Name == "On") - // { - // var platformStr = (onobj.Children.OfType() - // .FirstOrDefault(v => ((XamlAstNamePropertyReference)v.Property).Name == "Platform")?.Values.Single() as XamlAstTextNode)? - // .Text; - // if (string.IsNullOrWhiteSpace(platformStr)) - // { - // throw new InvalidOperationException("On.Platform string must be set"); - // } - // var content = onobj.Children.OfType().FirstOrDefault(); - // if (content is null) - // { - // throw new InvalidOperationException("On content object must be set"); - // } - // - // var parentOnFormFactorObject = context.ParentNodes().OfType() - // .First(n => n.Type is XamlAstClrTypeReference clrRef && clrRef.Type.Name.StartsWith("OnFormFactor") - // || n.Type is XamlAstXmlTypeReference xmlRef && xmlRef.Name.StartsWith("OnFormFactor")); - // parentOnFormFactorObject.Children.Remove(onobj); - // foreach (var platform in platformStr.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries)) - // { - // var propertyNode = new XamlAstXamlPropertyValueNode(onobj, - // new XamlAstNamePropertyReference(onobj, parentOnFormFactorObject.Type, platform.Trim(), - // parentOnFormFactorObject.Type), content); - // parentOnFormFactorObject.Children.Add(propertyNode); - // } - // - // return parentOnFormFactorObject.Children.Last(); - // } - // if (node is XamlAstXamlPropertyValueNode propNode) - // { - // var type = (propNode.Property as XamlAstNamePropertyReference).TargetType as XamlAstXmlTypeReference; - // - // propNode.VisitChildren(new OnFormFactorGenericTypeVisitor(type)); - // - // return node; - // } - - return node; - } - - private class OnFormFactorGenericTypeVisitor : IXamlAstVisitor - { - private readonly XamlAstXmlTypeReference _type; - public OnFormFactorGenericTypeVisitor(IXamlAstTypeReference type) - { - - } - - public IXamlAstNode Visit(IXamlAstNode node) - { - if (node is XamlAstXmlTypeReference { Name: "OnFormFactor" } xmlref) - { - xmlref.GenericArguments.Add(_type); - } - - return node; - } - - public void Push(IXamlAstNode node) - { - } - - public void Pop() - { - } - } -} diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs index 0d1d350978..db0b5683f7 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs @@ -6,10 +6,27 @@ using Avalonia.Platform; namespace Avalonia.Markup.Xaml.MarkupExtensions; +public class OnFormFactorExtension : OnFormFactorExtension +{ + public OnFormFactorExtension() + { + + } + + public OnFormFactorExtension(object defaultValue) : base(defaultValue) + { + } +} + public class OnFormFactorExtension { private readonly Dictionary _values = new(); + public OnFormFactorExtension() + { + + } + public OnFormFactorExtension(TReturn defaultValue) { Default = defaultValue; @@ -51,7 +68,7 @@ public class OnFormFactorExtension var runtimeInfo = AvaloniaLocator.Current.GetRequiredService().GetRuntimeInfo(); TReturn val; - + if (runtimeInfo.IsDesktop) { if (_values.TryGetValue(nameof(Desktop), out val)) @@ -67,7 +84,7 @@ public class OnFormFactorExtension return (val, true); } } - + if (_values.TryGetValue(nameof(Default), out val)) { return (val, true); diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs index c4c32939fe..66adad7dc9 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs @@ -31,14 +31,14 @@ public class OnPlatformExtension : IAddChild { private readonly Dictionary _values = new(); - public OnPlatformExtension(TReturn defaultValue) + public OnPlatformExtension() { - Default = defaultValue; + } - public OnPlatformExtension() + public OnPlatformExtension(TReturn defaultValue) { - + Default = defaultValue; } public TReturn? Default { get => _values.TryGetValue(nameof(Default), out var value) ? value : default; set { _values[nameof(Default)] = value; } } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs index 3e03be3a47..307896f3ee 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs @@ -205,5 +205,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime { return serviceProvider.GetService().GetRuntimeInfo().OperatingSystem == platform; } + + public static bool IsOnFormFactor(IServiceProvider serviceProvider, FormFactorType formFactor) + { + return serviceProvider.GetService().GetRuntimeInfo().FormFactor == formFactor; + } } } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnFormFactorExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnFormFactorExtensionTests.cs new file mode 100644 index 0000000000..94a78e126e --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnFormFactorExtensionTests.cs @@ -0,0 +1,309 @@ +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.Platform; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions; + +public class OnFormFactorExtensionTests : XamlTestBase +{ + [Fact] + public void Should_Resolve_Default_Value() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(false, false)); + + var xaml = @" + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = (TextBlock)userControl.Content!; + + Assert.Equal("Hello World", textBlock.Text); + } + } + + [Fact] + public void Should_Resolve_Default_Value_From_Ctor() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(false, false)); + + var xaml = @" + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = (TextBlock)userControl.Content!; + + Assert.Equal("Hello World", textBlock.Text); + } + } + + [Theory] + [InlineData(false, true, "Im Mobile")] + [InlineData(true, false, "Im Desktop")] + [InlineData(false, false, "Default value")] + public void Should_Resolve_Expected_Value_Per_Platform(bool isDesktop, bool isMobile, string expectedResult) + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(isDesktop, isMobile)); + + var xaml = @" + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = (TextBlock)userControl.Content!; + + Assert.Equal(expectedResult, textBlock.Text); + } + } + + [Fact] + public void Should_Convert_Bcl_Type() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(true, false)); + + var xaml = @" + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var border = (Border)userControl.Content!; + + Assert.Equal(50.1, border.Height); + } + } + + [Fact] + public void Should_Convert_Avalonia_Type() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(true, false)); + + var xaml = @" + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var border = (Border)userControl.Content!; + + Assert.Equal(new Thickness(10, 8, 10, 8), border.Padding); + } + } + + [Fact] + public void Should_Respect_Custom_TypeArgument() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(true, false)); + + var xaml = @" + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var textBlock = (TextBlock)userControl.Content!; + + Assert.Equal(new Thickness(10, 10, 10, 10), textBlock.DataContext); + } + } + + [Fact] + public void Should_Allow_Nester_Markup_Extensions() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(true, false)); + + var xaml = @" + + + #ff506070 + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var border = (Border)userControl.Content!; + + Assert.Equal(Color.Parse("#ff506070"), ((ISolidColorBrush)border.Background!).Color); + } + } + + [Fact] + public void Should_Support_Xml_Syntax() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(true, false)); + + var xaml = @" + + + + + + + + + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var border = (Border)userControl.Content!; + + Assert.Equal(Color.Parse("#ff506070"), ((ISolidColorBrush)border.Background!).Color); + } + } + + [Fact] + public void Should_Support_Xml_Syntax_With_Custom_TypeArguments() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(true, false)); + + var xaml = @" + + + + + + +"; + + var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml); + var border = (Border)userControl.Content!; + + Assert.Equal(new Thickness(10, 10, 10, 10), border.Tag); + } + } + + [Fact] + public void Should_Support_Control_Inside_Xml_Syntax() + { + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind() + .ToConstant(new TestRuntimePlatform(true, false)); + + var xaml = @" + + + +