From 07b457d8a0021ada606699529c9ce7c42f234c1c Mon Sep 17 00:00:00 2001 From: Nelson Carrillo Date: Tue, 29 May 2018 22:34:32 -0400 Subject: [PATCH 01/22] Add support for locating custom renderers --- src/Avalonia.Visuals/Media/DrawingContext.cs | 2 +- .../Rendering/IRendererFactory.cs | 20 +++++++++++++++++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 9 ++++++--- 3 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 src/Avalonia.Visuals/Rendering/IRendererFactory.cs diff --git a/src/Avalonia.Visuals/Media/DrawingContext.cs b/src/Avalonia.Visuals/Media/DrawingContext.cs index 1d25224b8d..962f2c1ba8 100644 --- a/src/Avalonia.Visuals/Media/DrawingContext.cs +++ b/src/Avalonia.Visuals/Media/DrawingContext.cs @@ -59,7 +59,7 @@ namespace Avalonia.Media //HACK: This is a temporary hack that is used in the render loop //to update TransformedBounds property [Obsolete("HACK for render loop, don't use")] - internal Matrix CurrentContainerTransform => _currentContainerTransform; + public Matrix CurrentContainerTransform => _currentContainerTransform; /// /// Draws a bitmap image. diff --git a/src/Avalonia.Visuals/Rendering/IRendererFactory.cs b/src/Avalonia.Visuals/Rendering/IRendererFactory.cs new file mode 100644 index 0000000000..0d7044e125 --- /dev/null +++ b/src/Avalonia.Visuals/Rendering/IRendererFactory.cs @@ -0,0 +1,20 @@ +// 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; + +namespace Avalonia.Rendering +{ + /// + /// Defines the interface for a renderer factory. + /// + public interface IRendererFactory + { + /// + /// Creates a renderer. + /// + /// The root visual. + /// The render loop. + IRenderer Create(IRenderRoot root, IRenderLoop renderLoop); + } +} diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 7b9f8ee066..159c8386b6 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -124,9 +124,12 @@ namespace Avalonia.Win32 public IRenderer CreateRenderer(IRenderRoot root) { var loop = AvaloniaLocator.Current.GetService(); - return Win32Platform.UseDeferredRendering ? - (IRenderer)new DeferredRenderer(root, loop) : - new ImmediateRenderer(root); + var customRendererFactory = AvaloniaLocator.Current.GetService(); + + if (customRendererFactory != null) + return customRendererFactory.Create(root, loop); + + return Win32Platform.UseDeferredRendering ? (IRenderer)new DeferredRenderer(root, loop) : new ImmediateRenderer(root); } public void Resize(Size value) From f596cb13bb369dd27ffba3fea74d6f857748f2f7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 31 May 2018 21:45:04 -0500 Subject: [PATCH 02/22] Don't set when value is equal to the last fetched value (invalidated on PropertyChanged event). --- .../Plugins/InpcPropertyAccessorPlugin.cs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs index ba4e60eb74..c03a384eee 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs @@ -52,9 +52,11 @@ namespace Avalonia.Data.Core.Plugins private class Accessor : PropertyAccessorBase, IWeakSubscriber { + private static readonly object CacheInvalid = new object(); private readonly WeakReference _reference; private readonly PropertyInfo _property; private bool _eventRaised; + private object _lastValue = CacheInvalid; public Accessor(WeakReference reference, PropertyInfo property) { @@ -72,7 +74,7 @@ namespace Avalonia.Data.Core.Plugins get { var o = _reference.Target; - return (o != null) ? _property.GetValue(o) : null; + return (_lastValue = (o != null) ? _property.GetValue(o) : null); } } @@ -80,6 +82,11 @@ namespace Avalonia.Data.Core.Plugins { if (_property.CanWrite) { + if (!ShouldSet(value)) + { + return true; + } + _eventRaised = false; _property.SetValue(_reference.Target, value); @@ -94,11 +101,24 @@ namespace Avalonia.Data.Core.Plugins return false; } + private bool ShouldSet(object value) + { + if (PropertyType.IsValueType) + { + return !_lastValue.Equals(value); + } + else + { + return !Object.ReferenceEquals(_lastValue, value); + } + } + void IWeakSubscriber.OnEvent(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == _property.Name || string.IsNullOrEmpty(e.PropertyName)) { _eventRaised = true; + _lastValue = CacheInvalid; SendCurrentValue(); } } From 41b20f63f0db3b3a63a772e70835336ad3f29d67 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 31 May 2018 22:18:44 -0500 Subject: [PATCH 03/22] Move fix up to PropertyAccessorNode from InpcPropertyAccessorPlugin. --- .../Plugins/InpcPropertyAccessorPlugin.cs | 22 +---------- .../Data/Core/PropertyAccessorNode.cs | 37 ++++++++++++++++++- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs index c03a384eee..ba4e60eb74 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs @@ -52,11 +52,9 @@ namespace Avalonia.Data.Core.Plugins private class Accessor : PropertyAccessorBase, IWeakSubscriber { - private static readonly object CacheInvalid = new object(); private readonly WeakReference _reference; private readonly PropertyInfo _property; private bool _eventRaised; - private object _lastValue = CacheInvalid; public Accessor(WeakReference reference, PropertyInfo property) { @@ -74,7 +72,7 @@ namespace Avalonia.Data.Core.Plugins get { var o = _reference.Target; - return (_lastValue = (o != null) ? _property.GetValue(o) : null); + return (o != null) ? _property.GetValue(o) : null; } } @@ -82,11 +80,6 @@ namespace Avalonia.Data.Core.Plugins { if (_property.CanWrite) { - if (!ShouldSet(value)) - { - return true; - } - _eventRaised = false; _property.SetValue(_reference.Target, value); @@ -101,24 +94,11 @@ namespace Avalonia.Data.Core.Plugins return false; } - private bool ShouldSet(object value) - { - if (PropertyType.IsValueType) - { - return !_lastValue.Equals(value); - } - else - { - return !Object.ReferenceEquals(_lastValue, value); - } - } - void IWeakSubscriber.OnEvent(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == _property.Name || string.IsNullOrEmpty(e.PropertyName)) { _eventRaised = true; - _lastValue = CacheInvalid; SendCurrentValue(); } } diff --git a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs index 4dbff4602f..efa5b88303 100644 --- a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs +++ b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs @@ -12,8 +12,10 @@ namespace Avalonia.Data.Core { internal class PropertyAccessorNode : ExpressionNode, ISettableNode { + private static readonly object CacheInvalid = new object(); private readonly bool _enableValidation; private IPropertyAccessor _accessor; + private object _lastValue = CacheInvalid; public PropertyAccessorNode(string propertyName, bool enableValidation) { @@ -29,12 +31,32 @@ namespace Avalonia.Data.Core { if (_accessor != null) { - try { return _accessor.SetValue(value, priority); } catch { } + try + { + if (ShouldNotSet(value)) + { + return true; + } + else + { + return _accessor.SetValue(value, priority); + } + } + catch { } } return false; } + private bool ShouldNotSet(object value) + { + if (PropertyType.IsValueType) + { + return _lastValue.Equals(value); + } + return Object.ReferenceEquals(_lastValue, value); + } + protected override IObservable StartListeningCore(WeakReference reference) { var plugin = ExpressionObserver.PropertyAccessors.FirstOrDefault(x => x.Match(reference.Target, PropertyName)); @@ -58,7 +80,18 @@ namespace Avalonia.Data.Core _accessor = accessor; return Disposable.Create(() => _accessor = null); }, - _ => accessor); + _ => accessor).Select(value => + { + if (value is BindingNotification notification) + { + _lastValue = notification.HasValue ? notification.Value : CacheInvalid; + } + else + { + _lastValue = value; + } + return value; + }); } } } From a37e24dc441f471f8f9d1896b09b5be569fc5028 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sat, 2 Jun 2018 12:30:12 -0500 Subject: [PATCH 04/22] Add unit test. --- .../AvaloniaObjectTests_Binding.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs index 02fb1f11ad..65d37503b6 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs @@ -457,6 +457,17 @@ namespace Avalonia.Base.UnitTests Assert.True(target.IsAnimating(Class1.FooProperty)); } + [Fact] + public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation() + { + var target = new Class1(); + var source = new TestTwoWayBindingViewModel(); + + target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source }); + + Assert.False(source.SetterCalled); + } + /// /// Returns an observable that returns a single value but does not complete. /// @@ -545,5 +556,22 @@ namespace Avalonia.Base.UnitTests } } } + + private class TestTwoWayBindingViewModel + { + private double _value; + + public double Value + { + get => _value; + set + { + _value = value; + SetterCalled = true; + } + } + + public bool SetterCalled { get; private set; } + } } } \ No newline at end of file From 5ea9677cb961bbe3a0a06443eb516daf8fd86aa2 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sat, 2 Jun 2018 12:30:49 -0500 Subject: [PATCH 05/22] Use WeakReferences to make sure we aren't accidentally keeping objects alive in this new cache. --- src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs index efa5b88303..c958cfdb25 100644 --- a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs +++ b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs @@ -15,7 +15,7 @@ namespace Avalonia.Data.Core private static readonly object CacheInvalid = new object(); private readonly bool _enableValidation; private IPropertyAccessor _accessor; - private object _lastValue = CacheInvalid; + private WeakReference _lastValue = null; public PropertyAccessorNode(string propertyName, bool enableValidation) { @@ -52,9 +52,9 @@ namespace Avalonia.Data.Core { if (PropertyType.IsValueType) { - return _lastValue.Equals(value); + return _lastValue?.Target.Equals(value) ?? false; } - return Object.ReferenceEquals(_lastValue, value); + return Object.ReferenceEquals(_lastValue?.Target ?? CacheInvalid, value); } protected override IObservable StartListeningCore(WeakReference reference) @@ -84,11 +84,11 @@ namespace Avalonia.Data.Core { if (value is BindingNotification notification) { - _lastValue = notification.HasValue ? notification.Value : CacheInvalid; + _lastValue = notification.HasValue ? new WeakReference(notification.Value) : null; } else { - _lastValue = value; + _lastValue = new WeakReference(value); } return value; }); From 1c3b714a0ec25166a8f3b15291f432b09da9b883 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sat, 2 Jun 2018 13:02:13 -0500 Subject: [PATCH 06/22] Move fix up to SettableNode and ExpressionNode so all settable node types (i.e. PropertyAccessor and Indexer nodes) can get the fix. --- src/Avalonia.Base/Data/Core/ExpressionNode.cs | 6 +++ .../Data/Core/ExpressionObserver.cs | 4 +- src/Avalonia.Base/Data/Core/ISettableNode.cs | 15 ------- src/Avalonia.Base/Data/Core/IndexerNode.cs | 6 +-- .../Data/Core/PropertyAccessorNode.cs | 44 ++++--------------- src/Avalonia.Base/Data/Core/SettableNode.cs | 38 ++++++++++++++++ .../AvaloniaObjectTests_Binding.cs | 21 +++++++++ 7 files changed, 79 insertions(+), 55 deletions(-) delete mode 100644 src/Avalonia.Base/Data/Core/ISettableNode.cs create mode 100644 src/Avalonia.Base/Data/Core/SettableNode.cs diff --git a/src/Avalonia.Base/Data/Core/ExpressionNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNode.cs index ae70cacdba..ac7e97a4b1 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionNode.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionNode.cs @@ -11,6 +11,7 @@ namespace Avalonia.Data.Core { internal abstract class ExpressionNode : ISubject { + private static readonly object CacheInvalid = new object(); protected static readonly WeakReference UnsetReference = new WeakReference(AvaloniaProperty.UnsetValue); @@ -18,6 +19,8 @@ namespace Avalonia.Data.Core private IDisposable _valueSubscription; private IObserver _observer; + protected WeakReference LastValue { get; private set; } + public abstract string Description { get; } public ExpressionNode Next { get; set; } @@ -61,6 +64,7 @@ namespace Avalonia.Data.Core { _valueSubscription?.Dispose(); _valueSubscription = null; + LastValue = null; nextSubscription?.Dispose(); _observer = null; }); @@ -120,6 +124,7 @@ namespace Avalonia.Data.Core if (notification == null) { + LastValue = new WeakReference(value); if (Next != null) { Next.Target = new WeakReference(value); @@ -131,6 +136,7 @@ namespace Avalonia.Data.Core } else { + LastValue = new WeakReference(notification.Value); if (Next != null) { Next.Target = new WeakReference(notification.Value); diff --git a/src/Avalonia.Base/Data/Core/ExpressionObserver.cs b/src/Avalonia.Base/Data/Core/ExpressionObserver.cs index 7719f93a02..14bc09f5b7 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionObserver.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionObserver.cs @@ -154,7 +154,7 @@ namespace Avalonia.Data.Core /// public bool SetValue(object value, BindingPriority priority = BindingPriority.LocalValue) { - if (Leaf is ISettableNode settable) + if (Leaf is SettableNode settable) { var node = _node; while (node != null) @@ -188,7 +188,7 @@ namespace Avalonia.Data.Core /// Gets the type of the expression result or null if the expression could not be /// evaluated. /// - public Type ResultType => (Leaf as ISettableNode)?.PropertyType; + public Type ResultType => (Leaf as SettableNode)?.PropertyType; /// /// Gets the leaf node. diff --git a/src/Avalonia.Base/Data/Core/ISettableNode.cs b/src/Avalonia.Base/Data/Core/ISettableNode.cs deleted file mode 100644 index 7788407833..0000000000 --- a/src/Avalonia.Base/Data/Core/ISettableNode.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Avalonia.Data; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Avalonia.Data.Core -{ - interface ISettableNode - { - bool SetTargetValue(object value, BindingPriority priority); - Type PropertyType { get; } - } -} diff --git a/src/Avalonia.Base/Data/Core/IndexerNode.cs b/src/Avalonia.Base/Data/Core/IndexerNode.cs index 47e82fa2d3..633d3558ee 100644 --- a/src/Avalonia.Base/Data/Core/IndexerNode.cs +++ b/src/Avalonia.Base/Data/Core/IndexerNode.cs @@ -15,7 +15,7 @@ using Avalonia.Data; namespace Avalonia.Data.Core { - internal class IndexerNode : ExpressionNode, ISettableNode + internal class IndexerNode : SettableNode { public IndexerNode(IList arguments) { @@ -52,7 +52,7 @@ namespace Avalonia.Data.Core return Observable.Merge(inputs).StartWith(GetValue(target)); } - public bool SetTargetValue(object value, BindingPriority priority) + protected override bool SetTargetValueCore(object value, BindingPriority priority) { var typeInfo = Target.Target.GetType().GetTypeInfo(); var list = Target.Target as IList; @@ -154,7 +154,7 @@ namespace Avalonia.Data.Core public IList Arguments { get; } - public Type PropertyType => GetIndexer(Target.Target.GetType().GetTypeInfo())?.PropertyType; + public override Type PropertyType => GetIndexer(Target.Target.GetType().GetTypeInfo())?.PropertyType; private object GetValue(object target) { diff --git a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs index c958cfdb25..9d657b3144 100644 --- a/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs +++ b/src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs @@ -10,12 +10,10 @@ using Avalonia.Data.Core.Plugins; namespace Avalonia.Data.Core { - internal class PropertyAccessorNode : ExpressionNode, ISettableNode + internal class PropertyAccessorNode : SettableNode { - private static readonly object CacheInvalid = new object(); private readonly bool _enableValidation; private IPropertyAccessor _accessor; - private WeakReference _lastValue = null; public PropertyAccessorNode(string propertyName, bool enableValidation) { @@ -25,22 +23,15 @@ namespace Avalonia.Data.Core public override string Description => PropertyName; public string PropertyName { get; } - public Type PropertyType => _accessor?.PropertyType; + public override Type PropertyType => _accessor?.PropertyType; - public bool SetTargetValue(object value, BindingPriority priority) + protected override bool SetTargetValueCore(object value, BindingPriority priority) { if (_accessor != null) { try { - if (ShouldNotSet(value)) - { - return true; - } - else - { - return _accessor.SetValue(value, priority); - } + return _accessor.SetValue(value, priority); } catch { } } @@ -48,15 +39,6 @@ namespace Avalonia.Data.Core return false; } - private bool ShouldNotSet(object value) - { - if (PropertyType.IsValueType) - { - return _lastValue?.Target.Equals(value) ?? false; - } - return Object.ReferenceEquals(_lastValue?.Target ?? CacheInvalid, value); - } - protected override IObservable StartListeningCore(WeakReference reference) { var plugin = ExpressionObserver.PropertyAccessors.FirstOrDefault(x => x.Match(reference.Target, PropertyName)); @@ -78,20 +60,12 @@ namespace Avalonia.Data.Core () => { _accessor = accessor; - return Disposable.Create(() => _accessor = null); - }, - _ => accessor).Select(value => - { - if (value is BindingNotification notification) + return Disposable.Create(() => { - _lastValue = notification.HasValue ? new WeakReference(notification.Value) : null; - } - else - { - _lastValue = new WeakReference(value); - } - return value; - }); + _accessor = null; + }); + }, + _ => accessor); } } } diff --git a/src/Avalonia.Base/Data/Core/SettableNode.cs b/src/Avalonia.Base/Data/Core/SettableNode.cs new file mode 100644 index 0000000000..1f6ec0bff4 --- /dev/null +++ b/src/Avalonia.Base/Data/Core/SettableNode.cs @@ -0,0 +1,38 @@ +using Avalonia.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Data.Core +{ + internal abstract class SettableNode : ExpressionNode + { + public bool SetTargetValue(object value, BindingPriority priority) + { + if (ShouldNotSet(value)) + { + return true; + } + return SetTargetValueCore(value, priority); + } + + private bool ShouldNotSet(object value) + { + if (PropertyType == null) + { + return false; + } + if (PropertyType.IsValueType) + { + return LastValue?.Target.Equals(value) ?? false; + } + return LastValue != null && Object.ReferenceEquals(LastValue?.Target, value); + } + + protected abstract bool SetTargetValueCore(object value, BindingPriority priority); + + public abstract Type PropertyType { get; } + } +} diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs index 65d37503b6..4638aa84a5 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs @@ -468,6 +468,17 @@ namespace Avalonia.Base.UnitTests Assert.False(source.SetterCalled); } + [Fact] + public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation_Indexer() + { + var target = new Class1(); + var source = new TestTwoWayBindingViewModel(); + + target.Bind(Class1.DoubleValueProperty, new Binding("[0]", BindingMode.TwoWay) { Source = source }); + + Assert.False(source.SetterCalled); + } + /// /// Returns an observable that returns a single value but does not complete. /// @@ -571,6 +582,16 @@ namespace Avalonia.Base.UnitTests } } + public double this[int index] + { + get => _value; + set + { + _value = value; + SetterCalled = true; + } + } + public bool SetterCalled { get; private set; } } } From f423dbbb1de22222abf01bdfdcd9f7fec5c20d8c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sat, 2 Jun 2018 15:45:05 -0500 Subject: [PATCH 07/22] Fix codepath where the property is a value type but the weakreference has been collected. --- src/Avalonia.Base/Data/Core/SettableNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Data/Core/SettableNode.cs b/src/Avalonia.Base/Data/Core/SettableNode.cs index 1f6ec0bff4..092cdbe48f 100644 --- a/src/Avalonia.Base/Data/Core/SettableNode.cs +++ b/src/Avalonia.Base/Data/Core/SettableNode.cs @@ -26,7 +26,7 @@ namespace Avalonia.Data.Core } if (PropertyType.IsValueType) { - return LastValue?.Target.Equals(value) ?? false; + return LastValue?.Target != null && LastValue.Target.Equals(value); } return LastValue != null && Object.ReferenceEquals(LastValue?.Target, value); } From 5f67bf13be951b8e42f3aa65243e55bf035b1bc7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 5 Jun 2018 16:18:24 -0500 Subject: [PATCH 08/22] Parallelize build on Windows. --- build.cake | 1 + 1 file changed, 1 insertion(+) diff --git a/build.cake b/build.cake index 1c796e8594..df0e1ed54e 100644 --- a/build.cake +++ b/build.cake @@ -149,6 +149,7 @@ Task("Build") settings.UseToolVersion(MSBuildToolVersion.VS2017); settings.WithProperty("Windows", "True"); settings.SetNodeReuse(false); + settings.SetMaxCpuCount(0); }); } else From 2f1c94ef30358ac4a7f83a8d5616d490aeee189e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 5 Jun 2018 18:15:23 -0500 Subject: [PATCH 09/22] Update to Cake 0.28 --- build.cake | 164 ++++++++++++++++++++++-------------------- parameters.cake | 2 - tools/packages.config | 2 +- 3 files changed, 89 insertions(+), 79 deletions(-) diff --git a/build.cake b/build.cake index df0e1ed54e..efdd62ea32 100644 --- a/build.cake +++ b/build.cake @@ -34,20 +34,31 @@ using NuGet; // PARAMETERS ////////////////////////////////////////////////////////////////////// -Parameters parameters = new Parameters(Context); -Packages packages = new Packages(Context, parameters); +class AvaloniaBuildData +{ + public AvaloniaBuildData(Parameters parameters, Packages packages) + { + Parameters = parameters; + Packages = packages; + } + + public Parameters Parameters { get; } + public Packages Packages { get; } +} /////////////////////////////////////////////////////////////////////////////// // SETUP /////////////////////////////////////////////////////////////////////////////// -Setup(context => +Setup(context => { - Information("Building version {0} of Avalonia ({1}, {2}, {3}) using version {4} of Cake.", + var parameters = new Parameters(context); + var buildContext = new AvaloniaBuildData(parameters, new Packages(context, parameters)); + + Information("Building version {0} of Avalonia ({1}, {2}) using version {3} of Cake.", parameters.Version, parameters.Platform, parameters.Configuration, - parameters.Target, typeof(ICakeContext).Assembly.GetName().Version.ToString()); if (parameters.IsRunningOnAppVeyor) @@ -55,8 +66,7 @@ Setup(context => Information("Repository Name: " + BuildSystem.AppVeyor.Environment.Repository.Name); Information("Repository Branch: " + BuildSystem.AppVeyor.Environment.Repository.Branch); } - - Information("Target: " + parameters.Target); + Information("Target:" + context.TargetTask.Name); Information("Platform: " + parameters.Platform); Information("Configuration: " + parameters.Configuration); Information("IsLocalBuild: " + parameters.IsLocalBuild); @@ -70,13 +80,15 @@ Setup(context => Information("IsReleasable: " + parameters.IsReleasable); Information("IsMyGetRelease: " + parameters.IsMyGetRelease); Information("IsNuGetRelease: " + parameters.IsNuGetRelease); + + return buildContext; }); /////////////////////////////////////////////////////////////////////////////// // TEARDOWN /////////////////////////////////////////////////////////////////////////////// -Teardown(context => +Teardown((context, buildContext) => { Information("Finished running tasks."); }); @@ -86,19 +98,19 @@ Teardown(context => /////////////////////////////////////////////////////////////////////////////// Task("Clean") - .Does(() => + .Does(data => { - CleanDirectories(parameters.BuildDirs); - CleanDirectory(parameters.ArtifactsDir); - CleanDirectory(parameters.NugetRoot); - CleanDirectory(parameters.ZipRoot); - CleanDirectory(parameters.BinRoot); + CleanDirectories(data.Parameters.BuildDirs); + CleanDirectory(data.Parameters.ArtifactsDir); + CleanDirectory(data.Parameters.NugetRoot); + CleanDirectory(data.Parameters.ZipRoot); + CleanDirectory(data.Parameters.BinRoot); }); Task("Restore-NuGet-Packages") .IsDependentOn("Clean") - .WithCriteria(parameters.IsRunningOnWindows) - .Does(() => + .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) + .Does(data => { var maxRetryCount = 5; var toolTimeout = 2d; @@ -115,13 +127,13 @@ Task("Restore-NuGet-Packages") toolTimeout+=0.5; }}) .Execute(()=> { - NuGetRestore(parameters.MSBuildSolution, new NuGetRestoreSettings { + NuGetRestore(data.Parameters.MSBuildSolution, new NuGetRestoreSettings { ToolTimeout = TimeSpan.FromMinutes(toolTimeout) }); }); }); -void DotNetCoreBuild() +void DotNetCoreBuild(Parameters parameters) { var settings = new DotNetCoreBuildSettings { @@ -137,14 +149,14 @@ void DotNetCoreBuild() Task("Build") .IsDependentOn("Restore-NuGet-Packages") - .Does(() => + .Does(data => { - if(parameters.IsRunningOnWindows) + if(data.Parameters.IsRunningOnWindows) { - MSBuild(parameters.MSBuildSolution, settings => { - settings.SetConfiguration(parameters.Configuration); + MSBuild(data.Parameters.MSBuildSolution, settings => { + settings.SetConfiguration(data.Parameters.Configuration); settings.SetVerbosity(Verbosity.Minimal); - settings.WithProperty("Platform", "\"" + parameters.Platform + "\""); + settings.WithProperty("Platform", "\"" + data.Parameters.Platform + "\""); settings.WithProperty("UseRoslynPathHack", "true"); settings.UseToolVersion(MSBuildToolVersion.VS2017); settings.WithProperty("Windows", "True"); @@ -154,7 +166,7 @@ Task("Build") } else { - DotNetCoreBuild(); + DotNetCoreBuild(data.Parameters); } }); @@ -185,66 +197,66 @@ Task("Run-Unit-Tests") .IsDependentOn("Build") .IsDependentOn("Run-Designer-Tests") .IsDependentOn("Run-Render-Tests") - .WithCriteria(() => !parameters.SkipTests) - .Does(() => { - RunCoreTest("./tests/Avalonia.Base.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Controls.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Input.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Interactivity.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Layout.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Markup.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Styling.UnitTests", parameters, false); - RunCoreTest("./tests/Avalonia.Visuals.UnitTests", parameters, false); - if (parameters.IsRunningOnWindows) + .WithCriteria((context, data) => !data.Parameters.SkipTests) + .Does(data => { + RunCoreTest("./tests/Avalonia.Base.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Controls.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Input.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Interactivity.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Layout.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Markup.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Styling.UnitTests", data.Parameters, false); + RunCoreTest("./tests/Avalonia.Visuals.UnitTests", data.Parameters, false); + if (data.Parameters.IsRunningOnWindows) { - RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", parameters, true); + RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", data.Parameters, true); } }); Task("Run-Designer-Tests") .IsDependentOn("Build") - .WithCriteria(() => !parameters.SkipTests) - .Does(() => { - RunCoreTest("./tests/Avalonia.DesignerSupport.Tests", parameters, false); + .WithCriteria((context, data) => !data.Parameters.SkipTests) + .Does(data => { + RunCoreTest("./tests/Avalonia.DesignerSupport.Tests", data.Parameters, false); }); Task("Run-Render-Tests") .IsDependentOn("Build") - .WithCriteria(() => !parameters.SkipTests && parameters.IsRunningOnWindows) - .Does(() => { - RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", parameters, true); - RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", parameters, true); + .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) + .Does(data => { + RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", data.Parameters, true); + RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", data.Parameters, true); }); Task("Copy-Files") .IsDependentOn("Run-Unit-Tests") - .Does(() => + .Does(data => { - CopyFiles(packages.BinFiles, parameters.BinRoot); + CopyFiles(data.Packages.BinFiles, data.Parameters.BinRoot); }); Task("Zip-Files") .IsDependentOn("Copy-Files") - .Does(() => + .Does(data => { - Zip(parameters.BinRoot, parameters.ZipCoreArtifacts); - - Zip(parameters.ZipSourceControlCatalogDesktopDirs, - parameters.ZipTargetControlCatalogDesktopDirs, - GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") + - GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.config") + - GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.so") + - GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dylib") + - GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe")); + Zip(data.Parameters.BinRoot, data.Parameters.ZipCoreArtifacts); + + Zip(data.Parameters.ZipSourceControlCatalogDesktopDirs, + data.Parameters.ZipTargetControlCatalogDesktopDirs, + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") + + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.config") + + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.so") + + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dylib") + + GetFiles(data.Parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe")); }); Task("Create-NuGet-Packages") .IsDependentOn("Run-Unit-Tests") .IsDependentOn("Inspect") - .Does(() => + .Does(data => { - foreach(var nuspec in packages.NuspecNuGetSettings) + foreach(var nuspec in data.Packages.NuspecNuGetSettings) { NuGetPack(nuspec); } @@ -252,12 +264,12 @@ Task("Create-NuGet-Packages") Task("Publish-MyGet") .IsDependentOn("Create-NuGet-Packages") - .WithCriteria(() => !parameters.IsLocalBuild) - .WithCriteria(() => !parameters.IsPullRequest) - .WithCriteria(() => parameters.IsMainRepo) - .WithCriteria(() => parameters.IsMasterBranch) - .WithCriteria(() => parameters.IsMyGetRelease) - .Does(() => + .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) + .WithCriteria((context, data) => !data.Parameters.IsPullRequest) + .WithCriteria((context, data) => data.Parameters.IsMainRepo) + .WithCriteria((context, data) => data.Parameters.IsMasterBranch) + .WithCriteria((context, data) => data.Parameters.IsMyGetRelease) + .Does(data => { var apiKey = EnvironmentVariable("MYGET_API_KEY"); if(string.IsNullOrEmpty(apiKey)) @@ -271,7 +283,7 @@ Task("Publish-MyGet") throw new InvalidOperationException("Could not resolve MyGet API url."); } - foreach(var nupkg in packages.NugetPackages) + foreach(var nupkg in data.Packages.NugetPackages) { NuGetPush(nupkg, new NuGetPushSettings { Source = apiUrl, @@ -286,11 +298,11 @@ Task("Publish-MyGet") Task("Publish-NuGet") .IsDependentOn("Create-NuGet-Packages") - .WithCriteria(() => !parameters.IsLocalBuild) - .WithCriteria(() => !parameters.IsPullRequest) - .WithCriteria(() => parameters.IsMainRepo) - .WithCriteria(() => parameters.IsNuGetRelease) - .Does(() => + .WithCriteria((context, data) => !data.Parameters.IsLocalBuild) + .WithCriteria((context, data) => !data.Parameters.IsPullRequest) + .WithCriteria((context, data) => data.Parameters.IsMainRepo) + .WithCriteria((context, data) => data.Parameters.IsNuGetRelease) + .Does(data => { var apiKey = EnvironmentVariable("NUGET_API_KEY"); if(string.IsNullOrEmpty(apiKey)) @@ -304,7 +316,7 @@ Task("Publish-NuGet") throw new InvalidOperationException("Could not resolve NuGet API url."); } - foreach(var nupkg in packages.NugetPackages) + foreach(var nupkg in data.Packages.NugetPackages) { NuGetPush(nupkg, new NuGetPushSettings { ApiKey = apiKey, @@ -318,7 +330,7 @@ Task("Publish-NuGet") }); Task("Run-Leak-Tests") - .WithCriteria(parameters.IsRunningOnWindows) + .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) .IsDependentOn("Build") .Does(() => { @@ -358,7 +370,7 @@ Task("Run-Leak-Tests") }); Task("Inspect") - .WithCriteria(parameters.IsRunningOnWindows) + .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) .IsDependentOn("Restore-NuGet-Packages") .Does(() => { @@ -396,9 +408,9 @@ Task("Inspect") Task("Package") .IsDependentOn("Create-NuGet-Packages"); -Task("Default").Does(() => +Task("Default").Does(data => { - if(parameters.IsRunningOnWindows) + if(data.Parameters.IsRunningOnWindows) RunTarget("Package"); else RunTarget("Run-Unit-Tests"); @@ -415,4 +427,4 @@ Task("Travis") // EXECUTE /////////////////////////////////////////////////////////////////////////////// -RunTarget(parameters.Target); +RunTarget(Context.Argument("target", "Default")); diff --git a/parameters.cake b/parameters.cake index 759846c658..ffd472cbd4 100644 --- a/parameters.cake +++ b/parameters.cake @@ -1,6 +1,5 @@ public class Parameters { - public string Target { get; private set; } public string Platform { get; private set; } public string Configuration { get; private set; } public bool SkipTests { get; private set; } @@ -43,7 +42,6 @@ public class Parameters var buildSystem = context.BuildSystem(); // ARGUMENTS - Target = context.Argument("target", "Default"); Platform = context.Argument("platform", "Any CPU"); Configuration = context.Argument("configuration", "Release"); SkipTests = context.HasArgument("skip-tests"); diff --git a/tools/packages.config b/tools/packages.config index e52a2c7e98..3c65df896f 100644 --- a/tools/packages.config +++ b/tools/packages.config @@ -1,4 +1,4 @@ - + From 8b36caf88e731b81f7522b6d1c9971f9a1c404ea Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 5 Jun 2018 18:15:57 -0500 Subject: [PATCH 10/22] Disable 'Unused' warning in Gtk/Interop/Native.cs --- src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index 1adaf9f4e1..b018f4db3f 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -1,4 +1,5 @@ -using System; +#pragma warning disable 649 +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; From 4169b8de0cbd6bc7d572d552ac66bbe5fa1a7701 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 6 Jun 2018 00:12:55 -0500 Subject: [PATCH 11/22] Clean up assembly version conflicts when building tests. --- Avalonia.sln | 5 ++--- build/XUnit.props | 17 +---------------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/Avalonia.sln b/Avalonia.sln index 9cf93e8a84..54f6f5e7e7 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -105,9 +105,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Desktop", "s EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{57E0455D-D565-44BB-B069-EE1AA20F8337}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{52F55355-D120-42AC-8116-8410A7D602FA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{52F55355-D120-42AC-8116-8410A7D602FA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DesignerSupport.TestApp", "tests\Avalonia.DesignerSupport.TestApp\Avalonia.DesignerSupport.TestApp.csproj", "{F1381F98-4D24-409A-A6C5-1C5B1E08BB08}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.TestApp", "tests\Avalonia.DesignerSupport.TestApp\Avalonia.DesignerSupport.TestApp.csproj", "{F1381F98-4D24-409A-A6C5-1C5B1E08BB08}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VirtualizationTest", "samples\VirtualizationTest\VirtualizationTest.csproj", "{FBCAF3D0-2808-4934-8E96-3F607594517B}" EndProject @@ -141,7 +141,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props build\Moq.props = build\Moq.props build\NetCore.props = build\NetCore.props - build\NetFX.props = build\NetFX.props build\ReactiveUI.props = build\ReactiveUI.props build\Rx.props = build\Rx.props build\SampleApp.props = build\SampleApp.props diff --git a/build/XUnit.props b/build/XUnit.props index 15412d19e7..079565d184 100644 --- a/build/XUnit.props +++ b/build/XUnit.props @@ -9,21 +9,6 @@ + - - - - - true - - - - - true - - - From df593b3a8084a47e94cb94221485a5f3823f1a1c Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 6 Jun 2018 00:14:15 -0500 Subject: [PATCH 12/22] Get leak tests running again in the build script. They were removed at some point. Also, update the tooling used and clean up the code for running them. --- build.cake | 81 ++++++++----------- cake.config | 15 ++++ .../Avalonia.LeakTests.csproj | 8 +- .../Properties/AssemblyInfo.cs | 8 ++ .../toolproject/tool.csproj | 11 --- .../Avalonia.UnitTests.csproj | 4 +- 6 files changed, 61 insertions(+), 66 deletions(-) create mode 100644 cake.config create mode 100644 tests/Avalonia.LeakTests/Properties/AssemblyInfo.cs delete mode 100644 tests/Avalonia.LeakTests/toolproject/tool.csproj diff --git a/build.cake b/build.cake index efdd62ea32..068b388293 100644 --- a/build.cake +++ b/build.cake @@ -10,7 +10,8 @@ // TOOLS /////////////////////////////////////////////////////////////////////////////// -#tool "nuget:?package=xunit.runner.console&version=2.3.0-beta5-build3769" +#tool "nuget:?package=xunit.runner.console&version=2.3.1" +#tool "nuget:?package=JetBrains.dotMemoryUnit&version=3.0.20171219.105559" /////////////////////////////////////////////////////////////////////////////// // USINGS @@ -195,8 +196,6 @@ void RunCoreTest(string project, Parameters parameters, bool coreOnly = false) Task("Run-Unit-Tests") .IsDependentOn("Build") - .IsDependentOn("Run-Designer-Tests") - .IsDependentOn("Run-Render-Tests") .WithCriteria((context, data) => !data.Parameters.SkipTests) .Does(data => { RunCoreTest("./tests/Avalonia.Base.UnitTests", data.Parameters, false); @@ -229,8 +228,36 @@ Task("Run-Render-Tests") RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", data.Parameters, true); }); -Task("Copy-Files") +Task("Run-Leak-Tests") + .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) + .IsDependentOn("Build") + .Does(() => + { + var dotMemoryUnit = Context.Tools.Resolve("dotMemoryUnit.exe"); + var leakTestsExitCode = StartProcess(dotMemoryUnit, new ProcessSettings + { + Arguments = new ProcessArgumentBuilder() + .Append(Context.Tools.Resolve("xunit.console.x86.exe").FullPath) + .Append("--propagate-exit-code") + .Append("--") + .Append("tests\\Avalonia.LeakTests\\bin\\Release\\net47\\Avalonia.LeakTests.dll"), + Timeout = 120000 + }); + + if (leakTestsExitCode != 0) + { + throw new Exception("Leak Tests failed"); + } + }); + +Task("Run-Tests") .IsDependentOn("Run-Unit-Tests") + .IsDependentOn("Run-Render-Tests") + .IsDependentOn("Run-Designer-Tests") + .IsDependentOn("Run-Leak-Tests"); + +Task("Copy-Files") + .IsDependentOn("Run-Tests") .Does(data => { CopyFiles(data.Packages.BinFiles, data.Parameters.BinRoot); @@ -252,7 +279,7 @@ Task("Zip-Files") }); Task("Create-NuGet-Packages") - .IsDependentOn("Run-Unit-Tests") + .IsDependentOn("Run-Tests") .IsDependentOn("Inspect") .Does(data => { @@ -329,46 +356,6 @@ Task("Publish-NuGet") Information("Publish-NuGet Task failed, but continuing with next Task..."); }); -Task("Run-Leak-Tests") - .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) - .IsDependentOn("Build") - .Does(() => - { - DotNetCoreRestore("tests\\Avalonia.LeakTests\\toolproject\\tool.csproj"); - DotNetBuild("tests\\Avalonia.LeakTests\\toolproject\\tool.csproj", settings => settings.SetConfiguration("Release")); - var report = "tests\\Avalonia.LeakTests\\bin\\Release\\report.xml"; - if(System.IO.File.Exists(report)) - System.IO.File.Delete(report); - - var toolXunitConsoleX86 = Context.Tools.Resolve("xunit.console.x86.exe").FullPath; - var proc = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo - { - FileName="tests\\Avalonia.LeakTests\\toolproject\\bin\\dotMemoryUnit.exe", - Arguments="-targetExecutable=\"" + toolXunitConsoleX86 + "\" -returnTargetExitCode -- tests\\Avalonia.LeakTests\\bin\\Release\\Avalonia.LeakTests.dll -xml tests\\Avalonia.LeakTests\\bin\\Release\\report.xml ", - UseShellExecute = false, - }); - var st = System.Diagnostics.Stopwatch.StartNew(); - while(!proc.HasExited && !System.IO.File.Exists(report)) - { - if(st.Elapsed.TotalSeconds>60) - { - Error("Timed out, probably a bug in dotMemoryUnit"); - proc.Kill(); - throw new Exception("dotMemory issue"); - } - proc.WaitForExit(100); - } - try{ - proc.Kill(); - }catch{} - var doc = System.Xml.Linq.XDocument.Load(report); - if(doc.Root.Descendants("assembly").Any(x=>x.Attribute("failed").Value.ToString() != "0")) - { - throw new Exception("Tests failed"); - } - - }); - Task("Inspect") .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) .IsDependentOn("Restore-NuGet-Packages") @@ -413,7 +400,7 @@ Task("Default").Does(data => if(data.Parameters.IsRunningOnWindows) RunTarget("Package"); else - RunTarget("Run-Unit-Tests"); + RunTarget("Run-Tests"); }); Task("AppVeyor") .IsDependentOn("Zip-Files") @@ -421,7 +408,7 @@ Task("AppVeyor") .IsDependentOn("Publish-NuGet"); Task("Travis") - .IsDependentOn("Run-Unit-Tests"); + .IsDependentOn("Run-Tests"); /////////////////////////////////////////////////////////////////////////////// // EXECUTE diff --git a/cake.config b/cake.config new file mode 100644 index 0000000000..8089cd4084 --- /dev/null +++ b/cake.config @@ -0,0 +1,15 @@ +; This is the default configuration file for Cake. +; This file was downloaded from https://github.com/cake-build/resources + +[Nuget] +Source=https://api.nuget.org/v3/index.json +UseInProcessClient=true +LoadDependencies=false + +[Paths] +Tools=./tools +Addins=./tools/Addins +Modules=./tools/Modules + +[Settings] +SkipVerification=false diff --git a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj index c945db8085..7966cac845 100644 --- a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj +++ b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj @@ -1,13 +1,12 @@  - netcoreapp2.0 + net47 - @@ -19,13 +18,10 @@ - + - - - \ No newline at end of file diff --git a/tests/Avalonia.LeakTests/Properties/AssemblyInfo.cs b/tests/Avalonia.LeakTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..4cc100aa28 --- /dev/null +++ b/tests/Avalonia.LeakTests/Properties/AssemblyInfo.cs @@ -0,0 +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.Reflection; +using Xunit; + +// Don't run tests in parallel. +[assembly: CollectionBehavior(DisableTestParallelization = true)] diff --git a/tests/Avalonia.LeakTests/toolproject/tool.csproj b/tests/Avalonia.LeakTests/toolproject/tool.csproj deleted file mode 100644 index 54dbe6f17e..0000000000 --- a/tests/Avalonia.LeakTests/toolproject/tool.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - $(MSBuildThisFileDirectory)\bin - $(OutputPath) - net461 - Library - - - - - \ No newline at end of file diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj index 6fe61b17f7..d86b27e804 100644 --- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj +++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj @@ -1,6 +1,6 @@  - netcoreapp2.0 + netcoreapp2.0;net461 false Library @@ -16,7 +16,7 @@ - + From 93774af3a445af29a359ac330abb908cee02458e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 6 Jun 2018 00:14:35 -0500 Subject: [PATCH 13/22] Update Android resource files. --- .../Resources/Resource.Designer.cs | 9 ++------- .../Avalonia.Android/Resources/Resource.Designer.cs | 7 ++----- .../Resources/Resource.Designer.cs | 9 ++------- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs index cee3331ba8..96f0e76fd8 100644 --- a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs +++ b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs @@ -28,8 +28,6 @@ namespace ControlCatalog.Android { global::Avalonia.Android.Resource.String.ApplicationName = global::ControlCatalog.Android.Resource.String.ApplicationName; global::Avalonia.Android.Resource.String.Hello = global::ControlCatalog.Android.Resource.String.Hello; - global::Avalonia.Android.Resource.String.library_name = global::ControlCatalog.Android.Resource.String.library_name; - global::Splat.Resource.String.library_name = global::ControlCatalog.Android.Resource.String.library_name; } public partial class Attribute @@ -96,14 +94,11 @@ namespace ControlCatalog.Android public partial class String { - // aapt resource value: 0x7f040002 - public const int ApplicationName = 2130968578; - // aapt resource value: 0x7f040001 - public const int Hello = 2130968577; + public const int ApplicationName = 2130968577; // aapt resource value: 0x7f040000 - public const int library_name = 2130968576; + public const int Hello = 2130968576; static String() { diff --git a/src/Android/Avalonia.Android/Resources/Resource.Designer.cs b/src/Android/Avalonia.Android/Resources/Resource.Designer.cs index e66c2800d3..80cbbc51ec 100644 --- a/src/Android/Avalonia.Android/Resources/Resource.Designer.cs +++ b/src/Android/Avalonia.Android/Resources/Resource.Designer.cs @@ -40,14 +40,11 @@ namespace Avalonia.Android public partial class String { - // aapt resource value: 0x7f020002 - public static int ApplicationName = 2130837506; - // aapt resource value: 0x7f020001 - public static int Hello = 2130837505; + public static int ApplicationName = 2130837505; // aapt resource value: 0x7f020000 - public static int library_name = 2130837504; + public static int Hello = 2130837504; static String() { diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs index 91327cf941..e171dd6162 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs +++ b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs @@ -28,8 +28,6 @@ namespace Avalonia.AndroidTestApplication { global::Avalonia.Android.Resource.String.ApplicationName = global::Avalonia.AndroidTestApplication.Resource.String.ApplicationName; global::Avalonia.Android.Resource.String.Hello = global::Avalonia.AndroidTestApplication.Resource.String.Hello; - global::Avalonia.Android.Resource.String.library_name = global::Avalonia.AndroidTestApplication.Resource.String.library_name; - global::Splat.Resource.String.library_name = global::Avalonia.AndroidTestApplication.Resource.String.library_name; } public partial class Attribute @@ -64,14 +62,11 @@ namespace Avalonia.AndroidTestApplication public partial class String { - // aapt resource value: 0x7f030002 - public const int ApplicationName = 2130903042; - // aapt resource value: 0x7f030001 - public const int Hello = 2130903041; + public const int ApplicationName = 2130903041; // aapt resource value: 0x7f030000 - public const int library_name = 2130903040; + public const int Hello = 2130903040; static String() { From 046322e6b04242e69ef9fdbe7f0b9938117dfd2d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 6 Jun 2018 00:39:38 -0500 Subject: [PATCH 14/22] Skip all tests when -skip-tests passed to build script. --- build.cake | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build.cake b/build.cake index 068b388293..a64fbf0fba 100644 --- a/build.cake +++ b/build.cake @@ -229,7 +229,7 @@ Task("Run-Render-Tests") }); Task("Run-Leak-Tests") - .WithCriteria((context, data) => data.Parameters.IsRunningOnWindows) + .WithCriteria((context, data) => !data.Parameters.SkipTests && data.Parameters.IsRunningOnWindows) .IsDependentOn("Build") .Does(() => { @@ -395,13 +395,6 @@ Task("Inspect") Task("Package") .IsDependentOn("Create-NuGet-Packages"); -Task("Default").Does(data => -{ - if(data.Parameters.IsRunningOnWindows) - RunTarget("Package"); - else - RunTarget("Run-Tests"); -}); Task("AppVeyor") .IsDependentOn("Zip-Files") .IsDependentOn("Publish-MyGet") @@ -414,4 +407,11 @@ Task("Travis") // EXECUTE /////////////////////////////////////////////////////////////////////////////// -RunTarget(Context.Argument("target", "Default")); +var target = Context.Argument("target", "Default"); + +if (target == "Default") +{ + target = Context.IsRunningOnWindows() ? "Package" : "Run-Tests"; +} + +RunTarget(target); From 18f436a2c3e1ba70b573c5a1aade5746431cce8a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 6 Jun 2018 01:34:24 -0500 Subject: [PATCH 15/22] Clean up unused code warnings and malformed doc comments. --- .../interop/Direct3DInteropSample/MainWindow.cs | 1 - .../Specific/Helpers/AndroidTouchEventsHelper.cs | 2 -- src/Avalonia.Animation/AnimatorKeyFrame.cs | 2 +- .../Presenters/CarouselPresenter.cs | 4 ++++ src/Avalonia.Remote.Protocol/TcpTransportBase.cs | 4 +--- src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs | 6 +++--- src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs | 5 +---- .../FramebufferToplevelImpl.cs | 6 +++++- .../PortableXaml/TypeDescriptorExtensions.cs | 14 +++++++------- src/OSX/Avalonia.MonoMac/ClipboardImpl.cs | 3 ++- .../Media/Imaging/D2DBitmapImpl.cs | 1 + .../Media/TransformedGeometryImpl.cs | 1 + .../Avalonia.Win32/Interop/UnmanagedMethods.cs | 4 ++-- src/iOS/Avalonia.iOS/DisplayLinkRenderLoop.cs | 2 +- src/iOS/Avalonia.iOS/TopLevelImpl.cs | 3 +-- .../AvaloniaObjectTests_Threading.cs | 2 ++ .../InteractiveTests.cs | 2 +- tests/Avalonia.RenderTests/TestBase.cs | 2 ++ 18 files changed, 35 insertions(+), 29 deletions(-) diff --git a/samples/interop/Direct3DInteropSample/MainWindow.cs b/samples/interop/Direct3DInteropSample/MainWindow.cs index ffa0de0a36..19c31a3af1 100644 --- a/samples/interop/Direct3DInteropSample/MainWindow.cs +++ b/samples/interop/Direct3DInteropSample/MainWindow.cs @@ -88,7 +88,6 @@ namespace Direct3DInteropSample context.ClearDepthStencilView(depthView, DepthStencilClearFlags.Depth, 1.0f, 0); context.ClearRenderTargetView(renderView, Color.White); - var time = 50; // Update WorldViewProj Matrix var worldViewProj = Matrix.RotationX((float) _model.RotationX) * Matrix.RotationY((float) _model.RotationY) * Matrix.RotationZ((float) _model.RotationZ) diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs index 0f90472bd0..71822a6f47 100644 --- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs +++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs @@ -126,8 +126,6 @@ namespace Avalonia.Android.Platform.Specific.Helpers return e.Action != MotionEventActions.Up; } - private Paint _paint; - public void Dispose() { HandleEvents = false; diff --git a/src/Avalonia.Animation/AnimatorKeyFrame.cs b/src/Avalonia.Animation/AnimatorKeyFrame.cs index 875bf761c4..02457cb9aa 100644 --- a/src/Avalonia.Animation/AnimatorKeyFrame.cs +++ b/src/Avalonia.Animation/AnimatorKeyFrame.cs @@ -9,7 +9,7 @@ namespace Avalonia.Animation { /// /// Defines a KeyFrame that is used for - /// objects. + /// objects. /// public class AnimatorKeyFrame { diff --git a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs index 144174e371..0f9bfe4de0 100644 --- a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs +++ b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs @@ -115,7 +115,9 @@ namespace Avalonia.Controls.Presenters var containers = generator.RemoveRange(e.OldStartingIndex, e.OldItems.Count); Panel.Children.RemoveAll(containers.Select(x => x.ContainerControl)); +#pragma warning disable 4014 MoveToPage(-1, SelectedIndex); +#pragma warning restore 4014 } break; @@ -126,7 +128,9 @@ namespace Avalonia.Controls.Presenters generator.Clear(); Panel.Children.RemoveAll(containers.Select(x => x.ContainerControl)); +#pragma warning disable 4014 MoveToPage(-1, SelectedIndex >= 0 ? SelectedIndex : 0); +#pragma warning restore 4014 } break; } diff --git a/src/Avalonia.Remote.Protocol/TcpTransportBase.cs b/src/Avalonia.Remote.Protocol/TcpTransportBase.cs index 3d2bd09485..562dbdf8f9 100644 --- a/src/Avalonia.Remote.Protocol/TcpTransportBase.cs +++ b/src/Avalonia.Remote.Protocol/TcpTransportBase.cs @@ -48,14 +48,12 @@ namespace Avalonia.Remote.Protocol { var cl = await server.AcceptTcpClientAsync(); AcceptNew(); - Task.Run(async () => + await Task.Run(async () => { var tcs = new TaskCompletionSource(); var t = CreateTransport(_resolver, cl.GetStream(), () => tcs.TrySetResult(0)); cb(t); await tcs.Task; - - }); } catch diff --git a/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs b/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs index c2005c9acb..cb996867c4 100644 --- a/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs +++ b/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs @@ -7,7 +7,7 @@ using System.Linq; namespace Avalonia.Media.Fonts { /// - /// Represents an identifier for a + /// Represents an identifier for a /// public class FontFamilyKey { @@ -33,12 +33,12 @@ namespace Avalonia.Media.Fonts } /// - /// Location of stored font asset that belongs to a + /// Location of stored font asset that belongs to a /// public Uri Location { get; } /// - /// Optional filename for a font asset that belongs to a + /// Optional filename for a font asset that belongs to a /// public string FileName { get; } diff --git a/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs b/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs index 0ab4ef980c..971edb1364 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs @@ -21,20 +21,17 @@ namespace Avalonia.Gtk3.Interop } return true; } - - private static readonly GCHandle PinnedHandle; + private static readonly Native.D.timeout_callback PinnedHandler; static GlibTimeout() { PinnedHandler = Handler; - } public static void Add(int priority, uint interval, Func callback) { var handle = GCHandle.Alloc(callback); - //Native.GTimeoutAdd(interval, PinnedHandler, GCHandle.ToIntPtr(handle)); Native.GTimeoutAddFull(priority, interval, PinnedHandler, GCHandle.ToIntPtr(handle), IntPtr.Zero); } diff --git a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs index 0db622ba13..627b508aa8 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs @@ -70,6 +70,10 @@ namespace Avalonia.LinuxFramebuffer public Action Resized { get; set; } public Action ScalingChanged { get; set; } public Action Closed { get; set; } - public event Action LostFocus; + public event Action LostFocus + { + add {} + remove {} + } } } diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs index 81525fef7d..458b09b66e 100644 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/TypeDescriptorExtensions.cs @@ -14,13 +14,13 @@ namespace Portable.Xaml.ComponentModel /// Gets the service from ITypeDescriptorContext /// usually in TypeConverter in xaml reader context /// examples: - /// context.GetService() - /// context.GetService() - /// context.GetService() - /// context.GetService() - /// context.GetService() - /// context.GetService() - /// context.GetService() + /// context.GetService<IXamlTypeResolver>() + /// context.GetService<IXamlNamespaceResolver>() + /// context.GetService<IXamlNameProvider>() + /// context.GetService<INamespacePrefixLookup>() + /// context.GetService<IXamlSchemaContextProvider>() + /// context.GetService<IRootObjectProvider>() + /// context.GetService<IProvideValueTarget>() /// /// Service Type /// The TypeDescriptor context. diff --git a/src/OSX/Avalonia.MonoMac/ClipboardImpl.cs b/src/OSX/Avalonia.MonoMac/ClipboardImpl.cs index f7b98c0c1f..29eb3720ec 100644 --- a/src/OSX/Avalonia.MonoMac/ClipboardImpl.cs +++ b/src/OSX/Avalonia.MonoMac/ClipboardImpl.cs @@ -22,9 +22,10 @@ namespace Avalonia.MonoMac return Task.CompletedTask; } - public async Task ClearAsync() + public Task ClearAsync() { NSPasteboard.GeneralPasteboard.ClearContents(); + return Task.CompletedTask; } } } diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs index b03e022674..6713cb13be 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs @@ -19,6 +19,7 @@ namespace Avalonia.Direct2D1.Media /// Initialize a new instance of the class /// with a bitmap backed by GPU memory. /// + /// The image factory to use when saving out this bitmap. /// The GPU bitmap. /// /// This bitmap must be either from the same render target, diff --git a/src/Windows/Avalonia.Direct2D1/Media/TransformedGeometryImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TransformedGeometryImpl.cs index e0e9e340bb..c00a826d0c 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/TransformedGeometryImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/TransformedGeometryImpl.cs @@ -11,6 +11,7 @@ namespace Avalonia.Direct2D1.Media /// /// Initializes a new instance of the class. /// + /// The source geometry. /// An existing Direct2D . public TransformedGeometryImpl(TransformedGeometry geometry, GeometryImpl source) : base(geometry) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 86dcec410b..4095447943 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -998,7 +998,7 @@ namespace Avalonia.Win32.Interop public static extern int DragQueryFile(IntPtr hDrop, int iFile, StringBuilder lpszFile, int cch); [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = false)] - public static extern void DoDragDrop(IOleDataObject dataObject, IDropSource dropSource, int allowedEffects, int[] finalEffect); + internal static extern void DoDragDrop(IOleDataObject dataObject, IDropSource dropSource, int allowedEffects, int[] finalEffect); @@ -1408,7 +1408,7 @@ namespace Avalonia.Win32.Interop [ComImport] [Guid("0000010E-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IOleDataObject + internal interface IOleDataObject { void GetData([In] ref FORMATETC format, out STGMEDIUM medium); void GetDataHere([In] ref FORMATETC format, ref STGMEDIUM medium); diff --git a/src/iOS/Avalonia.iOS/DisplayLinkRenderLoop.cs b/src/iOS/Avalonia.iOS/DisplayLinkRenderLoop.cs index ef045f61bf..a6d1bf1670 100644 --- a/src/iOS/Avalonia.iOS/DisplayLinkRenderLoop.cs +++ b/src/iOS/Avalonia.iOS/DisplayLinkRenderLoop.cs @@ -26,7 +26,7 @@ namespace Avalonia.iOS { Tick?.Invoke(this, new EventArgs()); } - catch (Exception e) + catch (Exception) { //TODO: log } diff --git a/src/iOS/Avalonia.iOS/TopLevelImpl.cs b/src/iOS/Avalonia.iOS/TopLevelImpl.cs index 5d0074e6db..3a2d2a33c6 100644 --- a/src/iOS/Avalonia.iOS/TopLevelImpl.cs +++ b/src/iOS/Avalonia.iOS/TopLevelImpl.cs @@ -26,7 +26,6 @@ namespace Avalonia.iOS { private IInputRoot _inputRoot; private readonly KeyboardEventsHelper _keyboardHelper; - private Point _position; public TopLevelImpl() { @@ -52,7 +51,7 @@ namespace Avalonia.iOS public Action Resized { get; set; } public Action ScalingChanged { get; set; } - public IPlatformHandle Handle => null; + public new IPlatformHandle Handle => null; public double Scaling => UIScreen.MainScreen.Scale; diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Threading.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Threading.cs index 78823a370b..e067886742 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Threading.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Threading.cs @@ -160,7 +160,9 @@ namespace Avalonia.Base.UnitTests public bool CurrentThreadIsLoopThread { get; set; } +#pragma warning disable 67 public event Action Signaled; +#pragma warning restore 67 public void RunLoop(CancellationToken cancellationToken) { diff --git a/tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs b/tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs index 067cf85c3c..58ee63cea4 100644 --- a/tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs +++ b/tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs @@ -403,7 +403,7 @@ namespace Avalonia.Interactivity.UnitTests private class TestInteractive : Interactive { public bool ClassHandlerInvoked { get; private set; } - public string Name { get; set; } + public new string Name { get; set; } public IEnumerable Children { diff --git a/tests/Avalonia.RenderTests/TestBase.cs b/tests/Avalonia.RenderTests/TestBase.cs index 321dbc4fbe..19413b32eb 100644 --- a/tests/Avalonia.RenderTests/TestBase.cs +++ b/tests/Avalonia.RenderTests/TestBase.cs @@ -161,7 +161,9 @@ namespace Avalonia.Direct2D1.RenderTests public Thread MainThread { get; set; } +#pragma warning disable 67 public event Action Signaled; +#pragma warning restore 67 public void RunLoop(CancellationToken cancellationToken) { From 38de22cf7563217bbd4ce7644604e46aa77bef48 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 6 Jun 2018 01:46:32 -0500 Subject: [PATCH 16/22] Add empty event implementations for unused events. --- .../Remote/DetachableTransportConnection.cs | 6 +++++- src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs | 6 +++++- .../Remote/RemoteDesignerEntryPoint.cs | 6 +++++- src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs | 6 +++++- src/iOS/Avalonia.iOS/EmbeddableImpl.cs | 6 +++++- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/DetachableTransportConnection.cs b/src/Avalonia.DesignerSupport/Remote/DetachableTransportConnection.cs index 1fb10a3a7c..9c125448a0 100644 --- a/src/Avalonia.DesignerSupport/Remote/DetachableTransportConnection.cs +++ b/src/Avalonia.DesignerSupport/Remote/DetachableTransportConnection.cs @@ -30,6 +30,10 @@ namespace Avalonia.DesignerSupport.Remote public event Action OnMessage; - public event Action OnException; + public event Action OnException + { + add {} + remove {} + } } } \ No newline at end of file diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs index ef16d06b60..61bd9670f2 100644 --- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs +++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs @@ -44,7 +44,11 @@ namespace Avalonia.DesignerSupport.Remote public WindowState WindowState { get; set; } public Action WindowStateChanged { get; set; } public Size MaxClientSize { get; } = new Size(4096, 4096); - public event Action LostFocus; + public event Action LostFocus + { + add {} + remove {} + } protected override void OnMessage(IAvaloniaRemoteTransportConnection transport, object obj) { diff --git a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs index f5893ae69a..2857272ac1 100644 --- a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs +++ b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs @@ -125,7 +125,11 @@ namespace Avalonia.DesignerSupport.Remote class NeverClose : ICloseable { - public event EventHandler Closed; + public event EventHandler Closed + { + add {} + remove {} + } } public static void Main(string[] cmdline) diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index fb308e62b8..3580b4fcb5 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -26,7 +26,11 @@ namespace Avalonia.Markup.Xaml.Styling } /// - public event EventHandler ResourcesChanged; + public event EventHandler ResourcesChanged + { + add {} + remove {} + } /// /// Gets or sets the source URL. diff --git a/src/iOS/Avalonia.iOS/EmbeddableImpl.cs b/src/iOS/Avalonia.iOS/EmbeddableImpl.cs index 3d8bafeca9..7d34cf40f7 100644 --- a/src/iOS/Avalonia.iOS/EmbeddableImpl.cs +++ b/src/iOS/Avalonia.iOS/EmbeddableImpl.cs @@ -27,6 +27,10 @@ namespace Avalonia.iOS { } - public event Action LostFocus; + public event Action LostFocus + { + add {} + remove {} + } } } From 43e6bd2d492cbeff59e06c2080d8e03cbc02171e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 6 Jun 2018 14:46:54 -0500 Subject: [PATCH 17/22] Switch off from deprecated APIs in Avalonia.MonoMac. --- src/OSX/Avalonia.MonoMac/SystemDialogsImpl.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/OSX/Avalonia.MonoMac/SystemDialogsImpl.cs b/src/OSX/Avalonia.MonoMac/SystemDialogsImpl.cs index 4226668119..04db728a2b 100644 --- a/src/OSX/Avalonia.MonoMac/SystemDialogsImpl.cs +++ b/src/OSX/Avalonia.MonoMac/SystemDialogsImpl.cs @@ -7,6 +7,7 @@ using Avalonia.Controls; using Avalonia.Controls.Platform; using Avalonia.Platform; using MonoMac.AppKit; +using MonoMac.Foundation; namespace Avalonia.MonoMac { @@ -24,9 +25,9 @@ namespace Avalonia.MonoMac else { if (panel is NSOpenPanel openPanel) - tcs.SetResult(openPanel.Filenames); + tcs.SetResult(openPanel.Urls.Select(url => url.AbsoluteString).ToArray()); else - tcs.SetResult(new[] { panel.Filename }); + tcs.SetResult(new[] { panel.Url.AbsoluteString }); } panel.OrderOut(panel); keyWindow?.MakeKeyAndOrderFront(keyWindow); @@ -62,7 +63,7 @@ namespace Avalonia.MonoMac panel = new NSSavePanel(); panel.Title = panel.Title; if (dialog.InitialDirectory != null) - panel.Directory = dialog.InitialDirectory; + panel.DirectoryUrl = new NSUrl(dialog.InitialDirectory); if (dialog.InitialFileName != null) panel.NameFieldStringValue = dialog.InitialFileName; if (dialog.Filters?.Count > 0) @@ -84,7 +85,7 @@ namespace Avalonia.MonoMac CanChooseFiles = false }; if (dialog.DefaultDirectory != null) - panel.Directory = dialog.DefaultDirectory; + panel.DirectoryUrl = new NSUrl(dialog.DefaultDirectory); return (await RunPanel(panel, parent))?.FirstOrDefault(); } } From 8d004c5ae817983df64953b230e3400f8f05c3e9 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 6 Jun 2018 16:17:32 -0500 Subject: [PATCH 18/22] Reduce output of Inspect build step. --- build.cake | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/build.cake b/build.cake index a64fbf0fba..632b572e69 100644 --- a/build.cake +++ b/build.cake @@ -366,8 +366,13 @@ Task("Inspect") "src\\markup\\avalonia.markup.xaml\\portablexaml\\portable.xaml.github"}; Information("Running code inspections"); - StartProcess(Context.Tools.Resolve("inspectcode.exe"), - new ProcessSettings{ Arguments = "--output=artifacts\\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln" }); + var exitCode = StartProcess(Context.Tools.Resolve("inspectcode.exe"), + new ProcessSettings + { + Arguments = "--output=artifacts\\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln", + RedirectStandardOutput = true + }); + Information("Analyzing report"); var doc = XDocument.Parse(System.IO.File.ReadAllText("artifacts\\inspectcode.xml")); var failBuild = false; From bc882f5b1137aedd9cc85c1e79480c66528a46fa Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 6 Jun 2018 16:38:11 -0500 Subject: [PATCH 19/22] Add back NetFX.props framework path overriding. --- tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj | 3 ++- tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj index 7966cac845..27f3223c6c 100644 --- a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj +++ b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj @@ -1,11 +1,12 @@  - net47 + net461 + diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj index d86b27e804..24cc6db7f3 100644 --- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj +++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj @@ -35,7 +35,5 @@ - - - + \ No newline at end of file From 08ab688edd4b5115713232b874affe62daede8dd Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 6 Jun 2018 16:52:15 -0500 Subject: [PATCH 20/22] Build Avalonia.DotNetFrameworkRuntime in the NetCoreOnly build. --- Avalonia.sln | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia.sln b/Avalonia.sln index 54f6f5e7e7..86a5411695 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -1813,6 +1813,7 @@ Global {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|NetCoreOnly.ActiveCfg = Debug|Any CPU + {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|NetCoreOnly.Build.0 = Debug|Any CPU {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|x86.ActiveCfg = Debug|Any CPU {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|x86.Build.0 = Debug|Any CPU {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Release|Any CPU.ActiveCfg = Release|Any CPU From f985fc819e57d93dc85e6b17fc65ddcc74b316d6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 6 Jun 2018 17:59:34 -0500 Subject: [PATCH 21/22] Fix LeakTests path --- build.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.cake b/build.cake index 632b572e69..bf3ae41b58 100644 --- a/build.cake +++ b/build.cake @@ -240,7 +240,7 @@ Task("Run-Leak-Tests") .Append(Context.Tools.Resolve("xunit.console.x86.exe").FullPath) .Append("--propagate-exit-code") .Append("--") - .Append("tests\\Avalonia.LeakTests\\bin\\Release\\net47\\Avalonia.LeakTests.dll"), + .Append("tests\\Avalonia.LeakTests\\bin\\Release\\net461\\Avalonia.LeakTests.dll"), Timeout = 120000 }); From 4ec647b870173c545a12def947d92ab44f5be515 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 6 Jun 2018 19:50:32 -0500 Subject: [PATCH 22/22] Make our unit tests support library (Avalonia.UnitTests) target netstandard2.0 so we don't have to worry about targetting .NET Framework on linux. --- Avalonia.sln | 1 - src/Avalonia.Controls/AppBuilderBase.cs | 11 +--------- .../AppBuilderTests.cs | 5 +---- .../Avalonia.DesignerSupport.TestApp.csproj | 2 +- .../DesignerSupportTests.cs | 8 ++++++++ .../FullLayoutTests.cs | 1 + .../Avalonia.UnitTests.csproj | 20 ++----------------- tests/Avalonia.UnitTests/RuntimeInfo.cs | 16 +++++++++++++++ tests/Avalonia.UnitTests/TestServices.cs | 14 +++++++++++++ tests/Avalonia.UnitTests/app.config | 11 ---------- 10 files changed, 44 insertions(+), 45 deletions(-) create mode 100644 tests/Avalonia.UnitTests/RuntimeInfo.cs delete mode 100644 tests/Avalonia.UnitTests/app.config diff --git a/Avalonia.sln b/Avalonia.sln index 86a5411695..54f6f5e7e7 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -1813,7 +1813,6 @@ Global {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|NetCoreOnly.ActiveCfg = Debug|Any CPU - {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|NetCoreOnly.Build.0 = Debug|Any CPU {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|x86.ActiveCfg = Debug|Any CPU {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Debug|x86.Build.0 = Debug|Any CPU {4A1ABB09-9047-4BD5-A4AD-A055E52C5EE0}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index 7af3deef34..51b690ece9 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -209,16 +209,7 @@ namespace Avalonia.Controls public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules()); - private bool CheckSetup { get; set; } = true; - - /// - /// Set this AppBuilder to ignore the setup check. Used for testing purposes. - /// - internal TAppBuilder IgnoreSetupCheck() - { - CheckSetup = false; - return Self; - } + protected virtual bool CheckSetup => true; private void SetupAvaloniaModules() { diff --git a/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs b/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs index 60c53d126c..fae08d37b7 100644 --- a/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs +++ b/tests/Avalonia.Controls.UnitTests/AppBuilderTests.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Xunit; using Avalonia.Controls.UnitTests; using Avalonia.Platform; +using Avalonia.UnitTests; [assembly: ExportAvaloniaModule("DefaultModule", typeof(AppBuilderTests.DefaultModule))] [assembly: ExportAvaloniaModule("RenderingModule", typeof(AppBuilderTests.Direct2DModule), ForRenderingSubsystem = "Direct2D1")] @@ -65,7 +66,6 @@ namespace Avalonia.Controls.UnitTests { ResetModuleLoadStates(); AppBuilder.Configure() - .IgnoreSetupCheck() .UseWindowingSubsystem(() => { }) .UseRenderingSubsystem(() => { }) .UseAvaloniaModules() @@ -82,7 +82,6 @@ namespace Avalonia.Controls.UnitTests { ResetModuleLoadStates(); var builder = AppBuilder.Configure() - .IgnoreSetupCheck() .UseWindowingSubsystem(() => { }) .UseRenderingSubsystem(() => { }, "Direct2D1"); builder.UseAvaloniaModules().SetupWithoutStarting(); @@ -92,7 +91,6 @@ namespace Avalonia.Controls.UnitTests ResetModuleLoadStates(); builder = AppBuilder.Configure() - .IgnoreSetupCheck() .UseWindowingSubsystem(() => { }) .UseRenderingSubsystem(() => { }, "Skia"); builder.UseAvaloniaModules().SetupWithoutStarting(); @@ -109,7 +107,6 @@ namespace Avalonia.Controls.UnitTests { ResetModuleLoadStates(); var builder = AppBuilder.Configure() - .IgnoreSetupCheck() .UseWindowingSubsystem(() => { }) .UseRenderingSubsystem(() => { }, "TBD"); builder.UseAvaloniaModules().SetupWithoutStarting(); diff --git a/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj b/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj index 76017f96bd..dd33ee831d 100644 --- a/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj +++ b/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj @@ -1,7 +1,7 @@  Exe - netcoreapp2.0 + netcoreapp2.0 diff --git a/tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs b/tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs index 5386877876..5220d539d9 100644 --- a/tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs +++ b/tests/Avalonia.DesignerSupport.Tests/DesignerSupportTests.cs @@ -19,6 +19,12 @@ namespace Avalonia.DesignerSupport.Tests public class DesignerSupportTests { private const string DesignerAppPath = "../../../../../src/tools/Avalonia.Designer.HostApp/bin/$BUILD/netcoreapp2.0/Avalonia.Designer.HostApp.dll"; + private readonly Xunit.Abstractions.ITestOutputHelper outputHelper; + + public DesignerSupportTests(Xunit.Abstractions.ITestOutputHelper outputHelper) + { + this.outputHelper = outputHelper; + } [SkippableTheory, InlineData( @@ -73,6 +79,8 @@ namespace Avalonia.DesignerSupport.Tests } else if (msg is UpdateXamlResultMessage result) { + if (result.Error != null) + outputHelper.WriteLine(result.Error); handle = result.Handle != null ? long.Parse(result.Handle) : 0; resultMessageReceivedToken.Cancel(); conn.Dispose(); diff --git a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs index d4df32a4b3..053c77b911 100644 --- a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs +++ b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs @@ -21,6 +21,7 @@ using Xunit; using Avalonia.Media; using System; using System.Collections.Generic; +using Avalonia.UnitTests; namespace Avalonia.Layout.UnitTests { diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj index 24cc6db7f3..c189bbbe66 100644 --- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj +++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj @@ -1,22 +1,10 @@  - netcoreapp2.0;net461 + netstandard2.0 false Library - - - - - - - - - - - - @@ -29,11 +17,7 @@ - - - - - + \ No newline at end of file diff --git a/tests/Avalonia.UnitTests/RuntimeInfo.cs b/tests/Avalonia.UnitTests/RuntimeInfo.cs new file mode 100644 index 0000000000..eb5781a725 --- /dev/null +++ b/tests/Avalonia.UnitTests/RuntimeInfo.cs @@ -0,0 +1,16 @@ +using Avalonia.Platform; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace Avalonia.Shared.PlatformSupport +{ + internal partial class StandardRuntimePlatform : IRuntimePlatform + { + public RuntimePlatformInfo GetRuntimeInfo() + { + return new RuntimePlatformInfo(); + } + } +} diff --git a/tests/Avalonia.UnitTests/TestServices.cs b/tests/Avalonia.UnitTests/TestServices.cs index 394dcbaa22..8414fe7cd5 100644 --- a/tests/Avalonia.UnitTests/TestServices.cs +++ b/tests/Avalonia.UnitTests/TestServices.cs @@ -14,6 +14,8 @@ using Avalonia.Themes.Default; using Avalonia.Rendering; using System.Reactive.Concurrency; using System.Collections.Generic; +using Avalonia.Controls; +using System.Reflection; namespace Avalonia.UnitTests { @@ -178,4 +180,16 @@ namespace Avalonia.UnitTests y => y.Open() == Mock.Of())); } } + + public class AppBuilder : AppBuilderBase + { + public AppBuilder() + : base(new StandardRuntimePlatform(), + builder => StandardRuntimePlatformServices.Register(builder.Instance?.GetType() + ?.GetTypeInfo().Assembly)) + { + } + + protected override bool CheckSetup => false; + } } diff --git a/tests/Avalonia.UnitTests/app.config b/tests/Avalonia.UnitTests/app.config deleted file mode 100644 index fa66e8c206..0000000000 --- a/tests/Avalonia.UnitTests/app.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file