using System; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Input.Raw; using Avalonia.Layout; using Avalonia.LogicalTree; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Rendering.Composition; using Avalonia.Styling; using Avalonia.UnitTests; using Moq; using Xunit; using static Avalonia.Controls.UnitTests.MaskedTextBoxTests; namespace Avalonia.Controls.UnitTests { public class TopLevelTests : ScopedTestBase { [Fact] public void IsAttachedToLogicalTree_Is_True() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var impl = CreateMockTopLevelImpl(); var target = new TestTopLevel(impl.Object); Assert.True(((ILogical)target).IsAttachedToLogicalTree); } } [Fact] public void ClientSize_Should_Be_Set_On_Construction() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var impl = CreateMockTopLevelImpl(); impl.Setup(x => x.ClientSize).Returns(new Size(123, 456)); var target = new TestTopLevel(impl.Object); Assert.Equal(new Size(123, 456), target.ClientSize); } } [Fact] public void Width_Should_Not_Be_Set_On_Construction() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var impl = CreateMockTopLevelImpl(); impl.Setup(x => x.ClientSize).Returns(new Size(123, 456)); var target = new TestTopLevel(impl.Object); Assert.Equal(double.NaN, target.Width); } } [Fact] public void Height_Should_Not_Be_Set_On_Construction() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var impl = CreateMockTopLevelImpl(); impl.Setup(x => x.ClientSize).Returns(new Size(123, 456)); var target = new TestTopLevel(impl.Object); Assert.Equal(double.NaN, target.Height); } } [Fact] public void Layout_Pass_Should_Not_Be_Automatically_Scheduled() { var services = TestServices.StyledWindow; using (UnitTestApplication.Start(services)) { var impl = CreateMockTopLevelImpl(); var target = new TestTopLevel(impl.Object, Mock.Of()); // The layout pass should be scheduled by the derived class. var layoutManagerMock = Mock.Get(target.LayoutManager); layoutManagerMock.Verify(x => x.ExecuteLayoutPass(), Times.Never); } } [Fact] public void Bounds_Should_Be_Set_After_Layout_Pass() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var impl = CreateMockTopLevelImpl(); impl.SetupProperty(x => x.Resized); impl.SetupGet(x => x.RenderScaling).Returns(1); var target = new TestTopLevel(impl.Object) { IsVisible = true, Template = CreateTemplate(), Content = new TextBlock { Width = 321, Height = 432, } }; target.LayoutManager.ExecuteInitialLayoutPass(); Assert.Equal(new Rect(0, 0, 321, 432), target.Bounds); } } [Fact] public void Width_And_Height_Should_Not_Be_Set_After_Layout_Pass() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var impl = CreateMockTopLevelImpl(); impl.Setup(x => x.ClientSize).Returns(new Size(123, 456)); var target = new TestTopLevel(impl.Object); target.LayoutManager.ExecuteLayoutPass(); Assert.Equal(double.NaN, target.Width); Assert.Equal(double.NaN, target.Height); } } [Fact] public void Width_And_Height_Should_Be_Set_After_Window_Resize_Notification() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var impl = CreateMockTopLevelImpl(true); impl.Setup(x => x.ClientSize).Returns(new Size(123, 456)); // The user has resized the window, so we can no longer auto-size. var target = new TestTopLevel(impl.Object); impl.Object.Resized(new Size(100, 200), WindowResizeReason.Unspecified); Assert.Equal(100, target.Width); Assert.Equal(200, target.Height); } } [Fact] public void Impl_Close_Should_Call_Raise_Closed_Event() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var impl = CreateMockTopLevelImpl(true); bool raised = false; var target = new TestTopLevel(impl.Object); target.Closed += (s, e) => raised = true; impl.Object.Closed(); Assert.True(raised); } } [Fact] public void Impl_Close_Should_Raise_DetachedFromLogicalTree_Event() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var impl = CreateMockTopLevelImpl(true); var target = new TestTopLevel(impl.Object); var raised = 0; target.DetachedFromLogicalTree += (s, e) => { Assert.Same(target, e.Root); Assert.Same(target, e.Source); Assert.Null(e.Parent); ++raised; }; impl.Object.Closed(); Assert.Equal(1, raised); } } [Fact] public void Impl_Input_Should_Pass_Input_To_InputManager() { var inputManagerMock = new Mock(); inputManagerMock.DefaultValue = DefaultValue.Mock; inputManagerMock.SetupAllProperties(); var services = TestServices.StyledWindow.With(inputManager: inputManagerMock.Object); using (UnitTestApplication.Start(services)) { var impl = CreateMockTopLevelImpl(true); var target = new TestTopLevel(impl.Object); var input = new RawKeyEventArgs( new Mock().Object, 0, target, RawKeyEventType.KeyDown, Key.A, RawInputModifiers.None, PhysicalKey.A, "a"); impl.Object.Input(input); inputManagerMock.Verify(x => x.ProcessInput(input)); } } [Fact] public void Adding_Top_Level_As_Child_Should_Throw_Exception() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var impl = CreateMockTopLevelImpl(true); var target = new TestTopLevel(impl.Object); var child = new TestTopLevel(impl.Object); target.Template = CreateTemplate(); target.Content = child; target.ApplyTemplate(); Assert.Throws(() => target.Presenter.ApplyTemplate()); } } [Fact] public void Adding_Resource_To_Application_Should_Raise_ResourcesChanged() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var impl = CreateMockTopLevelImpl(true); var target = new TestTopLevel(impl.Object); var raised = false; target.ResourcesChanged += (_, __) => raised = true; Application.Current.Resources.Add("foo", "bar"); Assert.True(raised); } } [Fact] public void Close_Should_Dispose_LayoutManager() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var impl = CreateMockTopLevelImpl(true); var layoutManager = new Mock(); var target = new TestTopLevel(impl.Object, layoutManager.Object); impl.Object.Closed(); layoutManager.Verify(x => x.Dispose()); } } [Fact] public void Reacts_To_Changes_In_Global_Styles() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { var impl = CreateMockTopLevelImpl(); impl.SetupGet(x => x.RenderScaling).Returns(1); var child = new Border { Classes = { "foo" } }; var target = new TestTopLevel(impl.Object) { Template = CreateTemplate(), Content = child, }; target.LayoutManager.ExecuteInitialLayoutPass(); Assert.Equal(new Thickness(0), child.BorderThickness); var style = new Style(x => x.OfType().Class("foo")) { Setters = { new Setter(Border.BorderThicknessProperty, new Thickness(2)) } }; Application.Current.Styles.Add(style); target.LayoutManager.ExecuteInitialLayoutPass(); Assert.Equal(new Thickness(2), child.BorderThickness); Application.Current.Styles.Remove(style); Assert.Equal(new Thickness(0), child.BorderThickness); } } private static FuncControlTemplate CreateTemplate() { return new FuncControlTemplate((x, scope) => new ContentPresenter { Name = "PART_ContentPresenter", [!ContentPresenter.ContentProperty] = x[!ContentControl.ContentProperty], }.RegisterInNameScope(scope)); } private static Mock CreateMockTopLevelImpl(bool setupProperties = false) { var topLevel = new Mock(); if (setupProperties) topLevel.SetupAllProperties(); topLevel.Setup(x => x.RenderScaling).Returns(1); topLevel.Setup(x => x.Compositor).Returns(RendererMocks.CreateDummyCompositor()); return topLevel; } private class TestTopLevel : TopLevel { private readonly ILayoutManager _layoutManager; public bool IsClosed { get; private set; } public TestTopLevel(ITopLevelImpl impl, ILayoutManager layoutManager = null) : base(impl) { _layoutManager = layoutManager ?? new LayoutManager(this); } private protected override ILayoutManager CreateLayoutManager() => _layoutManager; } } }