ソースを参照

Added failing tests for #5027.

We shouldn't subscribe to bindings until needed.
Steven Kirk 5 年 前
コミット
34b5f07e96

+ 1 - 0
tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj

@@ -3,6 +3,7 @@
     <TargetFrameworks>netcoreapp3.1;net47</TargetFrameworks>
     <TargetFrameworks>netcoreapp3.1;net47</TargetFrameworks>
     <OutputType>Library</OutputType>
     <OutputType>Library</OutputType>
     <IsTestProject>true</IsTestProject>
     <IsTestProject>true</IsTestProject>
+    <LangVersion>latest</LangVersion>
   </PropertyGroup>
   </PropertyGroup>
   <Import Project="..\..\build\UnitTests.NetCore.targets" />
   <Import Project="..\..\build\UnitTests.NetCore.targets" />
   <Import Project="..\..\build\UnitTests.NetFX.props" />
   <Import Project="..\..\build\UnitTests.NetFX.props" />

+ 96 - 0
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs

@@ -1,4 +1,5 @@
 using System;
 using System;
+using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Controls.Presenters;
 using Avalonia.Controls.Presenters;
@@ -792,6 +793,82 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             Assert.Equal(0xff506070, brush.Color.ToUint32());
             Assert.Equal(0xff506070, brush.Color.ToUint32());
         }
         }
 
 
+        [Fact]
+        public void Resource_In_Non_Matching_Style_Is_Not_Resolved()
+        {
+            using var app = StyledWindow();
+
+            var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
+             xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions'>
+    <Window.Resources>
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+                <local:TrackingResourceProvider/>
+            </ResourceDictionary.MergedDictionaries>
+        </ResourceDictionary>
+    </Window.Resources>
+
+    <Window.Styles>
+        <Style Selector='Border.nomatch'>
+            <Setter Property='Tag' Value='{DynamicResource foo}'/>
+        </Style>
+        <Style Selector='Border'>
+            <Setter Property='Tag' Value='{DynamicResource bar}'/>
+        </Style>
+    </Window.Styles>
+
+    <Border Name='border'/>
+</Window>";
+
+            var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
+            var border = window.FindControl<Border>("border");
+
+            Assert.Equal("bar", border.Tag);
+
+            var resourceProvider = (TrackingResourceProvider)window.Resources.MergedDictionaries[0];
+            Assert.Equal(new[] { "bar" }, resourceProvider.RequestedResources);
+        }
+
+        [Fact]
+        public void Resource_In_Non_Active_Style_Is_Not_Resolved()
+        {
+            using var app = StyledWindow();
+
+            var xaml = @"
+<Window xmlns='https://github.com/avaloniaui'
+             xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
+             xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions'>
+    <Window.Resources>
+        <ResourceDictionary>
+            <ResourceDictionary.MergedDictionaries>
+                <local:TrackingResourceProvider/>
+            </ResourceDictionary.MergedDictionaries>
+        </ResourceDictionary>
+    </Window.Resources>
+
+    <Window.Styles>
+        <Style Selector='Border'>
+            <Setter Property='Tag' Value='{DynamicResource foo}'/>
+        </Style>
+        <Style Selector='Border'>
+            <Setter Property='Tag' Value='{DynamicResource bar}'/>
+        </Style>
+    </Window.Styles>
+
+    <Border Name='border'/>
+</Window>";
+
+            var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
+            var border = window.FindControl<Border>("border");
+
+            Assert.Equal("bar", border.Tag);
+
+            var resourceProvider = (TrackingResourceProvider)window.Resources.MergedDictionaries[0];
+            Assert.Equal(new[] { "bar" }, resourceProvider.RequestedResources);
+        }
+
         private IDisposable StyledWindow(params (string, string)[] assets)
         private IDisposable StyledWindow(params (string, string)[] assets)
         {
         {
             var services = TestServices.StyledWindow.With(
             var services = TestServices.StyledWindow.With(
@@ -822,4 +899,23 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
             };
             };
         }
         }
     }
     }
+
+    public class TrackingResourceProvider : IResourceProvider
+    {
+        public IResourceHost Owner { get; private set; }
+        public bool HasResources => true;
+        public List<object> RequestedResources { get; } = new List<object>();
+
+        public event EventHandler OwnerChanged;
+
+        public void AddOwner(IResourceHost owner) => Owner = owner;
+        public void RemoveOwner(IResourceHost owner) => Owner = null;
+
+        public bool TryGetResource(object key, out object value)
+        {
+            RequestedResources.Add(key);
+            value = key;
+            return true;
+        }
+    }
 }
 }

+ 82 - 0
tests/Avalonia.Styling.UnitTests/StyleTests.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using Avalonia.Controls;
 using Avalonia.Controls;
+using Avalonia.Controls.Templates;
 using Avalonia.Data;
 using Avalonia.Data;
 using Avalonia.UnitTests;
 using Avalonia.UnitTests;
 using Moq;
 using Moq;
@@ -217,6 +218,78 @@ namespace Avalonia.Styling.UnitTests
             Assert.Equal(new[] { "foodefault", "Bar" }, values);
             Assert.Equal(new[] { "foodefault", "Bar" }, values);
         }
         }
 
 
+        [Fact]
+        public void Template_In_Non_Matching_Style_Is_Not_Built()
+        {
+            var instantiationCount = 0;
+            var template = new FuncTemplate<Class1>(() =>
+            {
+                ++instantiationCount;
+                return new Class1();
+            });
+
+            Styles styles = new Styles
+            {
+                new Style(x => x.OfType<Class1>().Class("foo"))
+                {
+                    Setters =
+                    {
+                        new Setter(Class1.ChildProperty, template),
+                    },
+                },
+
+                new Style(x => x.OfType<Class1>())
+                {
+                    Setters =
+                    {
+                        new Setter(Class1.ChildProperty, template),
+                    },
+                }
+            };
+
+            var target = new Class1();
+            styles.TryAttach(target, null);
+
+            Assert.NotNull(target.Child);
+            Assert.Equal(1, instantiationCount);
+        }
+
+        [Fact]
+        public void Template_In_Inactive_Style_Is_Not_Built()
+        {
+            var instantiationCount = 0;
+            var template = new FuncTemplate<Class1>(() =>
+            {
+                ++instantiationCount;
+                return new Class1();
+            });
+
+            Styles styles = new Styles
+            {
+                new Style(x => x.OfType<Class1>())
+                {
+                    Setters =
+                    {
+                        new Setter(Class1.ChildProperty, template),
+                    },
+                },
+
+                new Style(x => x.OfType<Class1>())
+                {
+                    Setters =
+                    {
+                        new Setter(Class1.ChildProperty, template),
+                    },
+                }
+            };
+
+            var target = new Class1();
+            styles.TryAttach(target, null);
+
+            Assert.NotNull(target.Child);
+            Assert.Equal(1, instantiationCount);
+        }
+
         [Fact]
         [Fact]
         public void Style_Should_Detach_When_Control_Removed_From_Logical_Tree()
         public void Style_Should_Detach_When_Control_Removed_From_Logical_Tree()
         {
         {
@@ -453,12 +526,21 @@ namespace Avalonia.Styling.UnitTests
             public static readonly StyledProperty<string> FooProperty =
             public static readonly StyledProperty<string> FooProperty =
                 AvaloniaProperty.Register<Class1, string>(nameof(Foo), "foodefault");
                 AvaloniaProperty.Register<Class1, string>(nameof(Foo), "foodefault");
 
 
+            public static readonly StyledProperty<Class1> ChildProperty =
+                AvaloniaProperty.Register<Class1, Class1>(nameof(Child));
+
             public string Foo
             public string Foo
             {
             {
                 get { return GetValue(FooProperty); }
                 get { return GetValue(FooProperty); }
                 set { SetValue(FooProperty, value); }
                 set { SetValue(FooProperty, value); }
             }
             }
 
 
+            public Class1 Child
+            {
+                get => GetValue(ChildProperty);
+                set => SetValue(ChildProperty, value);
+            }
+
             protected override Size MeasureOverride(Size availableSize)
             protected override Size MeasureOverride(Size availableSize)
             {
             {
                 throw new NotImplementedException();
                 throw new NotImplementedException();