From 9e369e96bec2dd1b961c2f176658fd5a1ea10696 Mon Sep 17 00:00:00 2001 From: danwalmsley Date: Wed, 19 Oct 2016 10:13:53 +0100 Subject: [PATCH 01/21] fix text height --- src/Skia/Avalonia.Skia/FormattedTextImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs index e24b6053b0..c203bf10bf 100644 --- a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs +++ b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs @@ -590,7 +590,7 @@ namespace Avalonia.Skia else { var lastLine = _skiaLines[_skiaLines.Count - 1]; - _size = new Size(maxX, lastLine.Top + lastLine.Height + lastLineDescent); + _size = new Size(lastLine.Width, lastLine.Height); } } From a46b9a97669188ab84ec7f0503f482ddfbb4f3c7 Mon Sep 17 00:00:00 2001 From: danwalmsley Date: Wed, 19 Oct 2016 10:14:27 +0100 Subject: [PATCH 02/21] fix text vertical offset. --- src/Skia/Avalonia.Skia/FormattedTextImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs index c203bf10bf..3d402df01f 100644 --- a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs +++ b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs @@ -522,7 +522,7 @@ namespace Avalonia.Skia _lineHeight = mDescent - mAscent; // Rendering is relative to baseline - _lineOffset = -metrics.Top; + _lineOffset = (-metrics.Ascent); string subString; From 6bde43372431ee06b68407b36c7d65291a2a7e42 Mon Sep 17 00:00:00 2001 From: danwalmsley Date: Thu, 20 Oct 2016 16:49:54 +0100 Subject: [PATCH 03/21] correctly setting size to cope with text wrapping. --- src/Skia/Avalonia.Skia/FormattedTextImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs index 3d402df01f..51f4b2b724 100644 --- a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs +++ b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs @@ -590,7 +590,7 @@ namespace Avalonia.Skia else { var lastLine = _skiaLines[_skiaLines.Count - 1]; - _size = new Size(lastLine.Width, lastLine.Height); + _size = new Size(maxX, lastLine.Top + lastLine.Height); } } From 01a1608a8d3d078e69d5e9473390c7cbd93581d5 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 28 Oct 2016 22:51:14 +0200 Subject: [PATCH 04/21] Call SetFocus in ShowWindow. Dialogs weren't receiving text input (WM_CHAR) after being shown (though they were receiving key down/up messages). Fixes #785. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 65e10cafda..5db3f69f85 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -682,6 +682,7 @@ namespace Avalonia.Win32 } UnmanagedMethods.ShowWindow(_hwnd, command); + UnmanagedMethods.SetFocus(_hwnd); } public void SetIcon(IWindowIconImpl icon) From b28fffc3f8aae18a90b0dd21676e36da72831172 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 30 Oct 2016 20:51:47 +0100 Subject: [PATCH 05/21] Coerce TextBox.CaretIndex when setting Text. Fixes #728 . --- src/Avalonia.Controls/TextBox.cs | 6 ++-- .../TextBoxTests.cs | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 7a271e8615..719e888dd4 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -202,6 +202,7 @@ namespace Avalonia.Controls { if (!_ignoreTextChanges) { + CaretIndex = CoerceCaretIndex(CaretIndex, value?.Length ?? 0); SetAndRaise(TextProperty, ref _text, value); } } @@ -558,10 +559,11 @@ namespace Avalonia.Controls return null; } - private int CoerceCaretIndex(int value) + private int CoerceCaretIndex(int value) => CoerceCaretIndex(value, Text?.Length ?? 0); + + private int CoerceCaretIndex(int value, int length) { var text = Text; - var length = text?.Length ?? 0; if (value < 0) { diff --git a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs index cff49bc32e..26fc2a2461 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBoxTests.cs @@ -1,6 +1,8 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System; +using System.Reactive.Linq; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; using Avalonia.Data; @@ -200,6 +202,33 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Setting_Text_Updates_CaretPosition() + { + using (UnitTestApplication.Start(Services)) + { + var target = new TextBox + { + Text = "Initial Text", + CaretIndex = 11 + }; + + var invoked = false; + + target.GetObservable(TextBox.TextProperty).Skip(1).Subscribe(_ => + { + // Caret index should be set before Text changed notification, as we don't want + // to notify with an invalid CaretIndex. + Assert.Equal(7, target.CaretIndex); + invoked = true; + }); + + target.Text = "Changed"; + + Assert.True(invoked); + } + } + private static TestServices Services => TestServices.MockThreadingInterface.With( standardCursorFactory: Mock.Of()); From 428466ced3d97b37b737139505e8568c021f5599 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sun, 30 Oct 2016 16:21:16 -0500 Subject: [PATCH 06/21] Fixed bug in scheduler and runtime platform. --- src/Avalonia.Base/Threading/AvaloniaScheduler.cs | 8 +------- src/Shared/PlatformSupport/StandardRuntimePlatform.cs | 5 +---- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs index de32057a00..43815b4ebc 100644 --- a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs +++ b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs @@ -26,13 +26,7 @@ namespace Avalonia.Threading /// public override IDisposable Schedule(TState state, TimeSpan dueTime, Func action) { - return DispatcherTimer.Run( - () => - { - action(this, state); - return false; - }, - dueTime); + return DispatcherTimer.RunOnce(() => action(this, state), dueTime); } } } diff --git a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs index e5ede1c6b4..5c02be7d5b 100644 --- a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs +++ b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs @@ -16,10 +16,7 @@ namespace Avalonia.Shared.PlatformSupport public void PostThreadPoolItem(Action cb) => ThreadPool.UnsafeQueueUserWorkItem(_ => cb(), null); public IDisposable StartSystemTimer(TimeSpan interval, Action tick) { - var timer = new Timer(delegate - { - - }, null, interval, interval); + var timer = new Timer(_ => tick(), null, interval, interval); return Disposable.Create(() => timer.Dispose()); } From 298fa627f8d1fdd8d01f71362afaf19008ce2759 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sun, 30 Oct 2016 16:55:47 -0500 Subject: [PATCH 07/21] Implemented binding scheduling via service injection. --- src/Avalonia.Base/AvaloniaObject.cs | 7 +++++++ src/Avalonia.Controls/Application.cs | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 48e937d6b2..5609fde7e9 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -12,6 +12,7 @@ using Avalonia.Diagnostics; using Avalonia.Logging; using Avalonia.Threading; using Avalonia.Utilities; +using System.Reactive.Concurrency; namespace Avalonia { @@ -304,6 +305,12 @@ namespace Avalonia VerifyAccess(); + var scheduler = AvaloniaLocator.Current.GetService(); + if (scheduler != null) + { + source = source.ObserveOn(scheduler); + } + if (property.IsDirect) { if (property.IsReadOnly) diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 326556f629..3d13608226 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -11,6 +11,7 @@ using Avalonia.Layout; using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.Threading; +using System.Reactive.Concurrency; namespace Avalonia { @@ -175,7 +176,8 @@ namespace Avalonia .Bind().ToTransient() .Bind().ToConstant(_styler) .Bind().ToSingleton() - .Bind().ToConstant(this); + .Bind().ToConstant(this) + .Bind().ToConstant(AvaloniaScheduler.Instance); } } } From 6da1d620c9377d4624d888fa362a7b26311bd63b Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sun, 30 Oct 2016 22:54:32 -0500 Subject: [PATCH 08/21] Added test to ensure that the IObservable subscription is on the AvaloniaScheduler when the AvaloniaScheduler is bound in the AvaloniaLocator. --- .../AvaloniaObjectTests_Binding.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs index 66fe3c7767..c72432029a 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs @@ -11,6 +11,13 @@ using Avalonia.Data; using Avalonia.Logging; using Avalonia.UnitTests; using Xunit; +using System.Threading.Tasks; +using Avalonia.Platform; +using System.Threading; +using Moq; +using System.Reactive.Disposables; +using System.Reactive.Concurrency; +using Avalonia.Threading; namespace Avalonia.Base.UnitTests { @@ -356,6 +363,39 @@ namespace Avalonia.Base.UnitTests Assert.True(called); } } + + [Fact] + public async void Bind_With_Scheduler_Executes_On_Scheduler() + { + var target = new Class1(); + var source = new Subject(); + var currentThreadId = Thread.CurrentThread.ManagedThreadId; + var calledThreadingInterface = false; + + var threadingInterfaceMock = new Mock(); + threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread) + .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId); + threadingInterfaceMock.Setup(mock => mock.StartTimer(TimeSpan.Zero, It.IsAny())) + .Returns((ts, act) => + { + act(); + calledThreadingInterface = true; + return Disposable.Empty; + }); + + using (AvaloniaLocator.EnterScope()) + { + AvaloniaLocator.CurrentMutable.Bind().ToConstant(threadingInterfaceMock.Object); + AvaloniaLocator.CurrentMutable.Bind().ToConstant(AvaloniaScheduler.Instance); + + target.Bind(Class1.QuxProperty, source); + + await Task.Run(() => source.OnNext(6.7)); + + Assert.True(calledThreadingInterface); + } + + } /// /// Returns an observable that returns a single value but does not complete. From 428229e9b0e4c17b26083ef0442666be312bf800 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 1 Nov 2016 23:34:27 -0500 Subject: [PATCH 09/21] Optimized the "zero wait time" path in the scheduler and updated the code to always use a scheduler, either the ImmediateScheduler (same as the old default behavior) or the registered scheduler. --- src/Avalonia.Base/AvaloniaObject.cs | 12 ++++++----- .../Threading/AvaloniaScheduler.cs | 20 ++++++++++++++++++- .../AvaloniaObjectTests_Binding.cs | 10 ---------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 5609fde7e9..72ff5187fa 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -305,10 +305,12 @@ namespace Avalonia VerifyAccess(); - var scheduler = AvaloniaLocator.Current.GetService(); - if (scheduler != null) + var description = GetDescription(source); + + if (priority == BindingPriority.LocalValue) { - source = source.ObserveOn(scheduler); + var scheduler = AvaloniaLocator.Current.GetService() ?? ImmediateScheduler.Instance; + source = source.ObserveOn(scheduler); } if (property.IsDirect) @@ -323,7 +325,7 @@ namespace Avalonia this, "Bound {Property} to {Binding} with priority LocalValue", property, - GetDescription(source)); + description); IDisposable subscription = null; @@ -365,7 +367,7 @@ namespace Avalonia this, "Bound {Property} to {Binding} with priority {Priority}", property, - GetDescription(source), + description, priority); return v.Add(source, (int)priority); diff --git a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs index 43815b4ebc..d9361fe7f0 100644 --- a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs +++ b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs @@ -3,6 +3,7 @@ using System; using System.Reactive.Concurrency; +using System.Reactive.Disposables; namespace Avalonia.Threading { @@ -26,7 +27,24 @@ namespace Avalonia.Threading /// public override IDisposable Schedule(TState state, TimeSpan dueTime, Func action) { - return DispatcherTimer.RunOnce(() => action(this, state), dueTime); + var composite = new CompositeDisposable(2); + if (dueTime == TimeSpan.Zero) + { + var cancellation = new CancellationDisposable(); + Dispatcher.UIThread.InvokeAsync(() => + { + if (!cancellation.Token.IsCancellationRequested) + { + composite.Add(action(this, state)); + } + }, DispatcherPriority.DataBind); + composite.Add(cancellation); + } + else + { + composite.Add(DispatcherTimer.RunOnce(() => composite.Add(action(this, state)), dueTime)); + } + return composite; } } } diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs index c72432029a..5e286305d2 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs @@ -370,18 +370,10 @@ namespace Avalonia.Base.UnitTests var target = new Class1(); var source = new Subject(); var currentThreadId = Thread.CurrentThread.ManagedThreadId; - var calledThreadingInterface = false; var threadingInterfaceMock = new Mock(); threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread) .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId); - threadingInterfaceMock.Setup(mock => mock.StartTimer(TimeSpan.Zero, It.IsAny())) - .Returns((ts, act) => - { - act(); - calledThreadingInterface = true; - return Disposable.Empty; - }); using (AvaloniaLocator.EnterScope()) { @@ -391,8 +383,6 @@ namespace Avalonia.Base.UnitTests target.Bind(Class1.QuxProperty, source); await Task.Run(() => source.OnNext(6.7)); - - Assert.True(calledThreadingInterface); } } From 4a8470d8a6886b277594ccd73a26fecb4edd5615 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 2 Nov 2016 14:06:47 -0500 Subject: [PATCH 10/21] Updated fast-path of scheduler to call directly if updates are on the UI thread instead of scheduling on the Dispatcher. --- .../Threading/AvaloniaScheduler.cs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs index d9361fe7f0..f9d67470c1 100644 --- a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs +++ b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs @@ -30,15 +30,22 @@ namespace Avalonia.Threading var composite = new CompositeDisposable(2); if (dueTime == TimeSpan.Zero) { - var cancellation = new CancellationDisposable(); - Dispatcher.UIThread.InvokeAsync(() => + if (!Dispatcher.UIThread.CheckAccess()) { - if (!cancellation.Token.IsCancellationRequested) + var cancellation = new CancellationDisposable(); + Dispatcher.UIThread.InvokeAsync(() => { - composite.Add(action(this, state)); - } - }, DispatcherPriority.DataBind); - composite.Add(cancellation); + if (!cancellation.Token.IsCancellationRequested) + { + composite.Add(action(this, state)); + } + }, DispatcherPriority.DataBind); + composite.Add(cancellation); + } + else + { + return action(this, state); + } } else { From c86e750281480fd960d3b5a47d9df11a4969d3b0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 19 Sep 2016 17:01:13 -0500 Subject: [PATCH 11/21] Add OpenCover coverage testing for Windows builds. --- build.cake | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/build.cake b/build.cake index d97d17218c..6192e0f88d 100644 --- a/build.cake +++ b/build.cake @@ -10,6 +10,7 @@ /////////////////////////////////////////////////////////////////////////////// #tool "nuget:?package=xunit.runner.console&version=2.1.0" +#tool "nuget:?package=OpenCover" /////////////////////////////////////////////////////////////////////////////// // USINGS @@ -670,18 +671,35 @@ Task("Run-Unit-Tests") "./tools/xunit.runner.console/tools/xunit.console.x86.exe" : "./tools/xunit.runner.console/tools/xunit.console.exe"; - var settings = new XUnit2Settings + var xUnitSettings = new XUnit2Settings { ToolPath = toolPath, - Parallelism = ParallelismOption.None + Parallelism = ParallelismOption.None, + ShadowCopy = false }; - settings.NoAppDomain = !isRunningOnWindows; + xUnitSettings.NoAppDomain = !isRunningOnWindows; + + var openCoverOutput = artifactsDir.GetFilePath(new FilePath("./result.xml")); + var openCoverSettings = new OpenCoverSettings + { + ArgumentCustomization = openCoverArgs => openCoverArgs.AppendSwitch("-mergeoutput", "") + } + .WithFilter("+[Avalonia.*]* -[*Test*]*");; foreach (var file in unitTests) { Information("Running test " + file.GetFilenameWithoutExtension()); - XUnit2(file.FullPath, settings); + if(isRunningOnWindows) + { + OpenCover(context => { + context.XUnit2(file.FullPath, xUnitSettings); + }, openCoverOutput, openCoverSettings); + } + else + { + XUnit2(file.FullPath, xUnitSettings); + } } }); From 5edb20fdfed4db6f1fff2b5708cb505c60a74d14 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 21 Sep 2016 11:48:48 -0500 Subject: [PATCH 12/21] Added CodeCov uploading. --- appveyor.yml | 5 +++++ build.cake | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 2909da65b7..db7d129dd1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,6 +20,11 @@ build_script: - ps: .\build.ps1 -Target "AppVeyor" -Platform "$env:platform" -Configuration "$env:configuration" after_build: - .\packages\JetBrains.dotMemoryUnit.2.1.20150828.125449\tools\dotMemoryUnit.exe -targetExecutable="%xunit20%\xunit.console.x86.exe" -returnTargetExitCode --"tests\Avalonia.LeakTests\bin\Release\Avalonia.LeakTests.dll" + +- "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%" +- pip install codecov +- codecov -f "./artifacts/coverage.xml" + test: off artifacts: - path: artifacts\nuget\*.nupkg diff --git a/build.cake b/build.cake index 6192e0f88d..2e63e83d61 100644 --- a/build.cake +++ b/build.cake @@ -680,7 +680,7 @@ Task("Run-Unit-Tests") xUnitSettings.NoAppDomain = !isRunningOnWindows; - var openCoverOutput = artifactsDir.GetFilePath(new FilePath("./result.xml")); + var openCoverOutput = artifactsDir.GetFilePath(new FilePath("./coverage.xml")); var openCoverSettings = new OpenCoverSettings { ArgumentCustomization = openCoverArgs => openCoverArgs.AppendSwitch("-mergeoutput", "") From b6e062ed8aaff7e6b06db99a71d6bacaaa240819 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 22 Sep 2016 23:14:23 -0500 Subject: [PATCH 13/21] Add initial Coveralls.io publishing --- build.cake | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/build.cake b/build.cake index 2e63e83d61..0315a36df2 100644 --- a/build.cake +++ b/build.cake @@ -4,6 +4,7 @@ #addin "nuget:?package=Polly&version=4.2.0" #addin "nuget:?package=NuGet.Core&version=2.12.0" +#addin Cake.Coveralls /////////////////////////////////////////////////////////////////////////////// // TOOLS @@ -11,6 +12,7 @@ #tool "nuget:?package=xunit.runner.console&version=2.1.0" #tool "nuget:?package=OpenCover" +#tool coveralls.net /////////////////////////////////////////////////////////////////////////////// // USINGS @@ -800,6 +802,14 @@ Task("Publish-NuGet") Information("Publish-NuGet Task failed, but continuing with next Task..."); }); +Task("Publish-Coverage") + .IsDependentOn("Run-Unit-Tests") + .WithCriteria(() => isRunningOnAppVeyor) + .Does(() => +{ + CoverallsNet(artifactsDir.GetFilePath(new FilePath("./coverage.xml")), CoverallsNetReportType.OpenCover); +}); + /////////////////////////////////////////////////////////////////////////////// // TARGETS /////////////////////////////////////////////////////////////////////////////// @@ -813,7 +823,8 @@ Task("Default") Task("AppVeyor") .IsDependentOn("Zip-Files") .IsDependentOn("Publish-MyGet") - .IsDependentOn("Publish-NuGet"); + .IsDependentOn("Publish-NuGet") + .IsDependentOn("Publish-Coverage"); Task("Travis") .IsDependentOn("Run-Unit-Tests"); From 57750c8cfe726587cc04b4225e074885f8b8accb Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 22 Sep 2016 23:26:50 -0500 Subject: [PATCH 14/21] Added Coveralls credentials and settings --- build.cake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.cake b/build.cake index 0315a36df2..d735489d5c 100644 --- a/build.cake +++ b/build.cake @@ -807,7 +807,8 @@ Task("Publish-Coverage") .WithCriteria(() => isRunningOnAppVeyor) .Does(() => { - CoverallsNet(artifactsDir.GetFilePath(new FilePath("./coverage.xml")), CoverallsNetReportType.OpenCover); + CoverallsNet(artifactsDir.GetFilePath(new FilePath("./coverage.xml")), CoverallsNetReportType.OpenCover, + new CoverallsNetSettings {RepoToken = "jKaVvndtCVJ2y7pNwQtYeHPn2jAxUpuyh", UseRelativePaths = true, ServiceName = "AppVeyor"}); }); /////////////////////////////////////////////////////////////////////////////// From 22b89ed8971924d9e24a1383fc39fbba1ce341c0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sun, 2 Oct 2016 16:14:51 -0500 Subject: [PATCH 15/21] Copy all tests to a test directory under artifacts to prevent collecting coverage for multiple copies of the same assembly in multiple different areas. (Massively shrinks the coverage.xml file). --- build.cake | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/build.cake b/build.cake index d735489d5c..269b17271e 100644 --- a/build.cake +++ b/build.cake @@ -95,6 +95,7 @@ var artifactsDir = (DirectoryPath)Directory("./artifacts"); var nugetRoot = artifactsDir.Combine("nuget"); var zipRoot = artifactsDir.Combine("zip"); var binRoot = artifactsDir.Combine("bin"); +var testsRoot = artifactsDir.Combine("tests"); var dirSuffix = configuration; var dirSuffixSkia = (isPlatformAnyCPU ? "x86" : platform) + "/" + configuration; @@ -588,6 +589,7 @@ Task("Clean") CleanDirectory(nugetRoot); CleanDirectory(zipRoot); CleanDirectory(binRoot); + CleanDirectory(testsRoot); }); Task("Restore-NuGet-Packages") @@ -683,25 +685,25 @@ Task("Run-Unit-Tests") xUnitSettings.NoAppDomain = !isRunningOnWindows; var openCoverOutput = artifactsDir.GetFilePath(new FilePath("./coverage.xml")); - var openCoverSettings = new OpenCoverSettings - { - ArgumentCustomization = openCoverArgs => openCoverArgs.AppendSwitch("-mergeoutput", "") - } - .WithFilter("+[Avalonia.*]* -[*Test*]*");; + var openCoverSettings = new OpenCoverSettings() + .WithFilter("+[Avalonia.*]* -[*Test*]* -[ControlCatalog*]*") + .WithFilter("-[Avalonia.*]OmniXaml.* -[Avalonia.*]Glass.*") + .WithFilter("-[Avalonia.HtmlRenderer]TheArtOfDev.HtmlRenderer.* +[Avalonia.HtmlRenderer]TheArtOfDev.HtmlRenderer.Avalonia.* -[Avalonia.ReactiveUI]*"); + + foreach(var test in unitTests) + { + CopyDirectory(test.GetDirectory(), testsRoot); + } - foreach (var file in unitTests) + if(isRunningOnWindows) { - Information("Running test " + file.GetFilenameWithoutExtension()); - if(isRunningOnWindows) - { - OpenCover(context => { - context.XUnit2(file.FullPath, xUnitSettings); - }, openCoverOutput, openCoverSettings); - } - else - { - XUnit2(file.FullPath, xUnitSettings); - } + OpenCover(context => { + context.XUnit2(unitTests.Select(test => testsRoot.GetFilePath(test).FullPath), xUnitSettings); + }, openCoverOutput, openCoverSettings); + } + else + { + XUnit2(unitTests.Select(test => test.FullPath), xUnitSettings); } }); From 33df423ef531501ca6b92562e59cc92ab8a09f10 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 2 Nov 2016 15:16:00 -0500 Subject: [PATCH 16/21] Remove Coveralls.io coverage support since we are using CodeCov instead. --- build.cake | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/build.cake b/build.cake index 269b17271e..82fa0b83f3 100644 --- a/build.cake +++ b/build.cake @@ -4,7 +4,6 @@ #addin "nuget:?package=Polly&version=4.2.0" #addin "nuget:?package=NuGet.Core&version=2.12.0" -#addin Cake.Coveralls /////////////////////////////////////////////////////////////////////////////// // TOOLS @@ -12,7 +11,6 @@ #tool "nuget:?package=xunit.runner.console&version=2.1.0" #tool "nuget:?package=OpenCover" -#tool coveralls.net /////////////////////////////////////////////////////////////////////////////// // USINGS @@ -804,15 +802,6 @@ Task("Publish-NuGet") Information("Publish-NuGet Task failed, but continuing with next Task..."); }); -Task("Publish-Coverage") - .IsDependentOn("Run-Unit-Tests") - .WithCriteria(() => isRunningOnAppVeyor) - .Does(() => -{ - CoverallsNet(artifactsDir.GetFilePath(new FilePath("./coverage.xml")), CoverallsNetReportType.OpenCover, - new CoverallsNetSettings {RepoToken = "jKaVvndtCVJ2y7pNwQtYeHPn2jAxUpuyh", UseRelativePaths = true, ServiceName = "AppVeyor"}); -}); - /////////////////////////////////////////////////////////////////////////////// // TARGETS /////////////////////////////////////////////////////////////////////////////// @@ -826,8 +815,7 @@ Task("Default") Task("AppVeyor") .IsDependentOn("Zip-Files") .IsDependentOn("Publish-MyGet") - .IsDependentOn("Publish-NuGet") - .IsDependentOn("Publish-Coverage"); + .IsDependentOn("Publish-NuGet"); Task("Travis") .IsDependentOn("Run-Unit-Tests"); From 26e60f9935511fbd46db63a92e746d504d59248d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 2 Nov 2016 15:35:39 -0500 Subject: [PATCH 17/21] Moved failing test into LeakTests because it depends on runtime implementation details to pass. --- .../Avalonia.LeakTests.csproj | 1 + .../Avalonia.LeakTests/MemberSelectorTests.cs | 47 +++++++++++++++++++ .../Templates/MemberSelectorTests.cs | 26 ---------- 3 files changed, 48 insertions(+), 26 deletions(-) create mode 100644 tests/Avalonia.LeakTests/MemberSelectorTests.cs diff --git a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj index 9aef304beb..7fed908927 100644 --- a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj +++ b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj @@ -99,6 +99,7 @@ + diff --git a/tests/Avalonia.LeakTests/MemberSelectorTests.cs b/tests/Avalonia.LeakTests/MemberSelectorTests.cs new file mode 100644 index 0000000000..d794e788fd --- /dev/null +++ b/tests/Avalonia.LeakTests/MemberSelectorTests.cs @@ -0,0 +1,47 @@ +using Avalonia.Markup.Xaml.Templates; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Avalonia.LeakTests +{ + public class MemberSelectorTests + { + [Fact] + public void Should_Not_Hold_Reference_To_Object() + { + WeakReference dataRef = null; + + var selector = new MemberSelector() { MemberName = "Child.StringValue" }; + + Action run = () => + { + var data = new Item() + { + Child = new Item() { StringValue = "Value1" } + }; + + Assert.Same("Value1", selector.Select(data)); + + dataRef = new WeakReference(data); + }; + + run(); + + GC.Collect(); + + Assert.False(dataRef.IsAlive); + } + + private class Item + { + public Item Child { get; set; } + public int IntValue { get; set; } + + public string StringValue { get; set; } + } + } +} diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Templates/MemberSelectorTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Templates/MemberSelectorTests.cs index 7082863c0d..49a88e8fae 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Templates/MemberSelectorTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Templates/MemberSelectorTests.cs @@ -9,32 +9,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.Templates { public class MemberSelectorTests { - [Fact] - public void Should_Not_Hold_Reference_To_Object() - { - WeakReference dataRef = null; - - var selector = new MemberSelector() { MemberName = "Child.StringValue" }; - - Action run = () => - { - var data = new Item() - { - Child = new Item() { StringValue = "Value1" } - }; - - Assert.Same("Value1", selector.Select(data)); - - dataRef = new WeakReference(data); - }; - - run(); - - GC.Collect(); - - Assert.False(dataRef.IsAlive); - } - [Fact] public void Should_Select_Child_Property_Value() { From c5bb77f3e3a07857b4f615fe5fdbc92dad444c11 Mon Sep 17 00:00:00 2001 From: donandren Date: Thu, 3 Nov 2016 18:20:35 +0200 Subject: [PATCH 18/21] PointToClient fixed --- src/Avalonia.Visuals/VisualExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/VisualExtensions.cs b/src/Avalonia.Visuals/VisualExtensions.cs index 6e55070702..226e616369 100644 --- a/src/Avalonia.Visuals/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualExtensions.cs @@ -30,7 +30,7 @@ namespace Avalonia public static Point PointToClient(this IVisual visual, Point point) { var p = GetRootAndPosition(visual); - return p.Item1.PointToClient(point + p.Item2); + return p.Item1.PointToClient(point - p.Item2); } /// From 80ce8412c052676141f8dd3b42d616983e18f2af Mon Sep 17 00:00:00 2001 From: donandren Date: Thu, 3 Nov 2016 18:31:33 +0200 Subject: [PATCH 19/21] added usefull TransalePoint extension method to IVisual --- src/Avalonia.Visuals/VisualExtensions.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Avalonia.Visuals/VisualExtensions.cs b/src/Avalonia.Visuals/VisualExtensions.cs index 226e616369..d69785cc59 100644 --- a/src/Avalonia.Visuals/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualExtensions.cs @@ -45,6 +45,22 @@ namespace Avalonia return p.Item1.PointToScreen(point + p.Item2); } + /// + /// Translates a point relative to this visual to coordinates that are relative to the specified visual. + /// The visual and relativeTo should be descendants of the same root window + /// + /// The visual. + /// The point value, as relative to this visual. + /// The visual to translate the given point into. + /// A point value, now relative to the target visual rather than this source element. + public static Point TranslatePoint(this IVisual visual, Point point, IVisual relativeTo) + { + var pos = GetRootAndPosition(visual); + var relToPos = GetRootAndPosition(relativeTo); + + return point - (relToPos.Item2 - pos.Item2); + } + /// /// Gets the root of the control's visual tree and the position of the control /// in the root's coordinate space. From 4c179634c1765fd3c23bd4862da6be00f7a4fd7e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 3 Nov 2016 21:10:16 +0100 Subject: [PATCH 20/21] Added a test for scheduler bindings. Added a property which is updated on a b/g thead to BindingTest. --- samples/BindingTest/MainWindow.xaml | 4 ++++ .../ViewModels/MainWindowViewModel.cs | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/samples/BindingTest/MainWindow.xaml b/samples/BindingTest/MainWindow.xaml index 02c364346d..95f671fd84 100644 --- a/samples/BindingTest/MainWindow.xaml +++ b/samples/BindingTest/MainWindow.xaml @@ -41,6 +41,10 @@ + + + + diff --git a/samples/BindingTest/ViewModels/MainWindowViewModel.cs b/samples/BindingTest/ViewModels/MainWindowViewModel.cs index 94f7ff595a..4b58bf2279 100644 --- a/samples/BindingTest/ViewModels/MainWindowViewModel.cs +++ b/samples/BindingTest/ViewModels/MainWindowViewModel.cs @@ -3,6 +3,8 @@ using System.Collections.ObjectModel; using System.Linq; using ReactiveUI; using System.Reactive.Linq; +using System.Threading.Tasks; +using System.Threading; namespace BindingTest.ViewModels { @@ -12,6 +14,7 @@ namespace BindingTest.ViewModels private double _doubleValue = 5.0; private string _stringValue = "Simple Binding"; private bool _booleanFlag = false; + private string _currentTime; public MainWindowViewModel() { @@ -37,6 +40,15 @@ namespace BindingTest.ViewModels BooleanFlag = !BooleanFlag; StringValue = param.ToString(); }); + + Task.Run(() => + { + while (true) + { + CurrentTime = DateTimeOffset.Now.ToString(); + Thread.Sleep(1000); + } + }); } public ObservableCollection Items { get; } @@ -67,6 +79,12 @@ namespace BindingTest.ViewModels set { this.RaiseAndSetIfChanged(ref _booleanFlag, value); } } + public string CurrentTime + { + get { return _currentTime; } + private set { this.RaiseAndSetIfChanged(ref _currentTime, value); } + } + public ReactiveCommand StringValueCommand { get; } public DataAnnotationsErrorViewModel DataAnnotationsValidation { get; } = new DataAnnotationsErrorViewModel(); From 104e0eb0789b50c0001e5bb7e217b493c17469e0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 3 Nov 2016 15:29:25 -0500 Subject: [PATCH 21/21] Removed the priority condition for using the scheduler. --- src/Avalonia.Base/AvaloniaObject.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 72ff5187fa..409abfe8fa 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -307,11 +307,8 @@ namespace Avalonia var description = GetDescription(source); - if (priority == BindingPriority.LocalValue) - { - var scheduler = AvaloniaLocator.Current.GetService() ?? ImmediateScheduler.Instance; - source = source.ObserveOn(scheduler); - } + var scheduler = AvaloniaLocator.Current.GetService() ?? ImmediateScheduler.Instance; + source = source.ObserveOn(scheduler); if (property.IsDirect) {