Browse Source

Merge branch 'master' into scenegraph

scenegraph-after-breakage
danwalmsley 9 years ago
committed by GitHub
parent
commit
a4518035f7
  1. 38
      src/Avalonia.Base/Data/BindingNotification.cs
  2. 2
      src/Avalonia.Controls/Button.cs
  3. 11
      src/Avalonia.Controls/Control.cs
  4. 11
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  5. 40
      src/Avalonia.Controls/ToolTip.cs
  6. 10
      src/Avalonia.Styling/LogicalTree/ILogical.cs
  7. 2
      src/Markup/Avalonia.Markup.Xaml/Data/MultiBinding.cs
  8. 4
      src/Markup/Avalonia.Markup/Data/BindingExpression.cs
  9. 2
      tests/Avalonia.Base.UnitTests/Properties/AssemblyInfo.cs
  10. 109
      tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs
  11. 28
      tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs
  12. 19
      tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
  13. 3
      tests/Avalonia.Markup.UnitTests/ControlLocatorTests.cs
  14. 133
      tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs
  15. 5
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_AttachedProperty.cs
  16. 3
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_AvaloniaProperty.cs
  17. 9
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_DataValidation.cs
  18. 67
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs
  19. 5
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Lifetime.cs
  20. 31
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Negation.cs
  21. 12
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs
  22. 78
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs
  23. 12
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Task.cs
  24. 2
      tests/Avalonia.Markup.UnitTests/Data/Plugins/ExceptionValidationPluginTests.cs
  25. 3
      tests/Avalonia.Markup.Xaml.UnitTests/Data/MultiBindingTests.cs
  26. 8
      tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs
  27. 5
      tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs
  28. 15
      tests/Avalonia.UnitTests/InvariantCultureFixture.cs

38
src/Avalonia.Base/Data/BindingNotification.cs

@ -44,11 +44,7 @@ namespace Avalonia.Data
public static readonly BindingNotification UnsetValue =
new BindingNotification(AvaloniaProperty.UnsetValue);
// Null cannot be held in WeakReference as it's indistinguishable from an expired value so
// use this value in its place.
private static readonly object NullValue = new object();
private WeakReference<object> _value;
private object _value;
/// <summary>
/// Initializes a new instance of the <see cref="BindingNotification"/> class.
@ -56,7 +52,7 @@ namespace Avalonia.Data
/// <param name="value">The binding value.</param>
public BindingNotification(object value)
{
_value = new WeakReference<object>(value ?? NullValue);
_value = value;
}
/// <summary>
@ -73,6 +69,7 @@ namespace Avalonia.Data
Error = error;
ErrorType = errorType;
_value = AvaloniaProperty.UnsetValue;
}
/// <summary>
@ -84,7 +81,7 @@ namespace Avalonia.Data
public BindingNotification(Exception error, BindingErrorType errorType, object fallbackValue)
: this(error, errorType)
{
_value = new WeakReference<object>(fallbackValue ?? NullValue);
_value = fallbackValue;
}
/// <summary>
@ -95,31 +92,12 @@ namespace Avalonia.Data
/// If this property is read when <see cref="HasValue"/> is false then it will return
/// <see cref="AvaloniaProperty.UnsetValue"/>.
/// </remarks>
public object Value
{
get
{
if (_value != null)
{
object result;
if (_value.TryGetTarget(out result))
{
return result == NullValue ? null : result;
}
}
// There's the possibility of a race condition in that HasValue can return true,
// and then the value is GC'd before Value is read. We should be ok though as
// we return UnsetValue which should be a safe alternative.
return AvaloniaProperty.UnsetValue;
}
}
public object Value => _value;
/// <summary>
/// Gets a value indicating whether <see cref="Value"/> should be pushed to the target.
/// </summary>
public bool HasValue => _value != null;
public bool HasValue => _value != AvaloniaProperty.UnsetValue;
/// <summary>
/// Gets the error that occurred on the source, if any.
@ -248,7 +226,7 @@ namespace Avalonia.Data
/// </summary>
public void ClearValue()
{
_value = null;
_value = AvaloniaProperty.UnsetValue;
}
/// <summary>
@ -256,7 +234,7 @@ namespace Avalonia.Data
/// </summary>
public void SetValue(object value)
{
_value = new WeakReference<object>(value ?? NullValue);
_value = value;
}
/// <inheritdoc/>

2
src/Avalonia.Controls/Button.cs

@ -234,7 +234,7 @@ namespace Avalonia.Controls
PseudoClasses.Remove(":pressed");
e.Handled = true;
if (ClickMode == ClickMode.Release && Classes.Contains(":pointerover"))
if (ClickMode == ClickMode.Release && new Rect(Bounds.Size).Contains(e.GetPosition(this)))
{
RaiseClickEvent();
}

11
src/Avalonia.Controls/Control.cs

@ -118,6 +118,7 @@ namespace Avalonia.Controls
public Control()
{
_nameScope = this as INameScope;
_isAttachedToLogicalTree = this is IStyleRoot;
}
/// <summary>
@ -369,6 +370,12 @@ namespace Avalonia.Controls
}
}
/// <inheritdoc/>
void ILogical.NotifyAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
this.OnAttachedToLogicalTreeCore(e);
}
/// <inheritdoc/>
void ILogical.NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
@ -418,7 +425,7 @@ namespace Avalonia.Controls
if (_isAttachedToLogicalTree)
{
var oldRoot = FindStyleRoot(old);
var oldRoot = FindStyleRoot(old) ?? this as IStyleRoot;
if (oldRoot == null)
{
@ -436,7 +443,7 @@ namespace Avalonia.Controls
_parent = (IControl)parent;
if (_parent is IStyleRoot || _parent?.IsAttachedToLogicalTree == true)
if (_parent is IStyleRoot || _parent?.IsAttachedToLogicalTree == true || this is IStyleRoot)
{
var newRoot = FindStyleRoot(this);

11
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@ -285,6 +285,17 @@ namespace Avalonia.Controls.Primitives
return this;
}
/// <inheritdoc/>
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
if (VisualChildren.Count > 0)
{
((ILogical)VisualChildren[0]).NotifyAttachedToLogicalTree(e);
}
base.OnAttachedToLogicalTree(e);
}
/// <inheritdoc/>
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{

40
src/Avalonia.Controls/ToolTip.cs

@ -105,16 +105,21 @@ namespace Avalonia.Controls
{
if (control != null && control.IsVisible && control.GetVisualRoot() != null)
{
if (s_popup != null)
{
throw new AvaloniaInternalException("Previous ToolTip not disposed.");
}
var cp = (control.GetVisualRoot() as IInputRoot)?.MouseDevice?.GetPosition(control);
var position = control.PointToScreen(cp ?? new Point(0, 0)) + new Vector(0, 22);
s_popup = new PopupRoot();
if (s_popup == null)
{
s_popup = new PopupRoot();
s_popup.Content = new ToolTip();
}
else
{
((ISetLogicalParent)s_popup).SetParent(null);
}
((ISetLogicalParent)s_popup).SetParent(control);
s_popup.Content = new ToolTip { Content = GetTip(control) };
((ToolTip)s_popup.Content).Content = GetTip(control);
s_popup.Position = position;
s_popup.Show();
@ -146,16 +151,23 @@ namespace Avalonia.Controls
{
if (s_popup != null)
{
// Clear the ToolTip's Content in case it has control content: this will
// reset its visual parent allowing it to be used again.
((ToolTip)s_popup.Content).Content = null;
// Dispose of the popup.
s_popup.Dispose();
s_popup = null;
DisposeTooltip();
s_show.OnNext(null);
}
}
}
private static void DisposeTooltip()
{
if (s_popup != null)
{
// Clear the ToolTip's Content in case it has control content: this will
// reset its visual parent allowing it to be used again.
((ToolTip)s_popup.Content).Content = null;
s_show.OnNext(null);
// Dispose of the popup.
s_popup.Dispose();
s_popup = null;
}
}
}

10
src/Avalonia.Styling/LogicalTree/ILogical.cs

@ -36,6 +36,16 @@ namespace Avalonia.LogicalTree
/// </summary>
IAvaloniaReadOnlyList<ILogical> LogicalChildren { get; }
/// <summary>
/// Notifies the control that it is being attached to a rooted logical tree.
/// </summary>
/// <param name="e">The event args.</param>
/// <remarks>
/// This method will be called automatically by the framework, you should not need to call
/// this method yourself.
/// </remarks>
void NotifyAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e);
/// <summary>
/// Notifies the control that it is being detached from a rooted logical tree.
/// </summary>

2
src/Markup/Avalonia.Markup.Xaml/Data/MultiBinding.cs

@ -102,7 +102,7 @@ namespace Avalonia.Markup.Xaml.Data
private object ConvertValue(IList<object> values, Type targetType)
{
var converted = Converter.Convert(values, targetType, null, CultureInfo.CurrentUICulture);
var converted = Converter.Convert(values, targetType, null, CultureInfo.CurrentCulture);
if (converted == AvaloniaProperty.UnsetValue && FallbackValue != null)
{

4
src/Markup/Avalonia.Markup/Data/BindingExpression.cs

@ -122,7 +122,7 @@ namespace Avalonia.Markup.Data
value,
type,
ConverterParameter,
CultureInfo.CurrentUICulture);
CultureInfo.CurrentCulture);
if (converted == AvaloniaProperty.UnsetValue)
{
@ -186,7 +186,7 @@ namespace Avalonia.Markup.Data
value,
_targetType,
ConverterParameter,
CultureInfo.CurrentUICulture);
CultureInfo.CurrentCulture);
notification = converted as BindingNotification;

2
tests/Avalonia.Base.UnitTests/Properties/AssemblyInfo.cs

@ -7,4 +7,4 @@ using Xunit;
[assembly: AssemblyTitle("Avalonia.UnitTests")]
// Don't run tests in parallel.
[assembly: CollectionBehavior(MaxParallelThreads = 1)]
[assembly: CollectionBehavior(DisableTestParallelization = true)]

109
tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs

@ -0,0 +1,109 @@
// 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 Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.LogicalTree;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.UnitTests.Primitives
{
public class PopupRootTests
{
[Fact]
public void PopupRoot_IsAttachedToLogicalTree_Is_True()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var target = CreateTarget();
Assert.True(((ILogical)target).IsAttachedToLogicalTree);
}
}
[Fact]
public void Templated_Child_IsAttachedToLogicalTree_Is_True()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var target = CreateTarget();
Assert.True(target.Presenter.IsAttachedToLogicalTree);
}
}
[Fact]
public void Attaching_PopupRoot_To_Parent_Logical_Tree_Raises_DetachedFromLogicalTree_And_AttachedToLogicalTree()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var child = new Decorator();
var target = CreateTarget();
var window = new Window();
var detachedCount = 0;
var attachedCount = 0;
target.Content = child;
target.DetachedFromLogicalTree += (s, e) => ++detachedCount;
child.DetachedFromLogicalTree += (s, e) => ++detachedCount;
target.AttachedToLogicalTree += (s, e) => ++attachedCount;
child.AttachedToLogicalTree += (s, e) => ++attachedCount;
((ISetLogicalParent)target).SetParent(window);
Assert.Equal(2, detachedCount);
Assert.Equal(2, attachedCount);
}
}
[Fact]
public void Detaching_PopupRoot_From_Parent_Logical_Tree_Raises_DetachedFromLogicalTree_And_AttachedToLogicalTree()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var child = new Decorator();
var target = CreateTarget();
var window = new Window();
var detachedCount = 0;
var attachedCount = 0;
target.Content = child;
((ISetLogicalParent)target).SetParent(window);
target.DetachedFromLogicalTree += (s, e) => ++detachedCount;
child.DetachedFromLogicalTree += (s, e) => ++detachedCount;
target.AttachedToLogicalTree += (s, e) => ++attachedCount;
child.AttachedToLogicalTree += (s, e) => ++attachedCount;
((ISetLogicalParent)target).SetParent(null);
// Despite being detached from the parent logical tree, we're still attached to a
// logical tree as PopupRoot itself is a logical tree root.
Assert.True(((ILogical)target).IsAttachedToLogicalTree);
Assert.True(((ILogical)child).IsAttachedToLogicalTree);
Assert.Equal(2, detachedCount);
Assert.Equal(2, attachedCount);
}
}
private PopupRoot CreateTarget()
{
var result = new PopupRoot
{
Template = new FuncControlTemplate<PopupRoot>(_ =>
new ContentPresenter
{
Name = "PART_ContentPresenter",
}),
};
result.ApplyTemplate();
return result;
}
}
}

28
tests/Avalonia.Controls.UnitTests/Primitives/TemplatedControlTests.cs

@ -527,6 +527,34 @@ namespace Avalonia.Controls.UnitTests.Primitives
}
}
[Fact]
public void Moving_To_New_LogicalTree_Should_Detach_Attach_Template_Child()
{
using (UnitTestApplication.Start(TestServices.RealStyler))
{
TestTemplatedControl target;
var root = new TestRoot
{
Child = target = new TestTemplatedControl
{
Template = new FuncControlTemplate(_ => new Decorator()),
}
};
Assert.NotNull(target.Template);
target.ApplyTemplate();
var templateChild = (ILogical)target.GetVisualChildren().Single();
Assert.True(templateChild.IsAttachedToLogicalTree);
root.Child = null;
Assert.False(templateChild.IsAttachedToLogicalTree);
var newRoot = new TestRoot { Child = target };
Assert.True(templateChild.IsAttachedToLogicalTree);
}
}
private static IControl ScrollingContentControlTemplate(ContentControl control)
{
return new Border

19
tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

@ -2,24 +2,33 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Reactive;
using System.Reactive.Subjects;
using Moq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Moq;
using Xunit;
namespace Avalonia.Controls.UnitTests
{
public class TopLevelTests
{
[Fact]
public void IsAttachedToLogicalTree_Is_True()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var impl = new Mock<ITopLevelImpl>();
var target = new TestTopLevel(impl.Object);
Assert.True(((ILogical)target).IsAttachedToLogicalTree);
}
}
[Fact]
public void ClientSize_Should_Be_Set_On_Construction()
{

3
tests/Avalonia.Markup.UnitTests/ControlLocatorTests.cs

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.UnitTests;
using Xunit;
@ -13,7 +14,7 @@ namespace Avalonia.Markup.UnitTests
public class ControlLocatorTests
{
[Fact]
public async void Track_By_Name_Should_Find_Control_Added_Earlier()
public async Task Track_By_Name_Should_Find_Control_Added_Earlier()
{
TextBlock target;
TextBlock relativeTo;

133
tests/Avalonia.Markup.UnitTests/Data/BindingExpressionTests.cs

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Data;
using Avalonia.Markup.Data;
using Avalonia.UnitTests;
@ -17,13 +18,15 @@ namespace Avalonia.Markup.UnitTests.Data
public class BindingExpressionTests : IClassFixture<InvariantCultureFixture>
{
[Fact]
public async void Should_Get_Simple_Property_Value()
public async Task Should_Get_Simple_Property_Value()
{
var data = new Class1 { StringValue = "foo" };
var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(string));
var result = await target.Take(1);
Assert.Equal("foo", result);
GC.KeepAlive(data);
}
[Fact]
@ -35,6 +38,8 @@ namespace Avalonia.Markup.UnitTests.Data
target.OnNext("bar");
Assert.Equal("bar", data.StringValue);
GC.KeepAlive(data);
}
[Fact]
@ -46,102 +51,87 @@ namespace Avalonia.Markup.UnitTests.Data
target.OnNext("bar");
Assert.Equal("bar", data.Foo[0]);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Convert_Get_String_To_Double()
public async Task Should_Convert_Get_String_To_Double()
{
#if NET461
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
#else
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
#endif
var data = new Class1 { StringValue = "5.6" };
var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double));
var result = await target.Take(1);
Assert.Equal(5.6, result);
GC.KeepAlive(data);
}
[Fact]
public async void Getting_Invalid_Double_String_Should_Return_BindingError()
public async Task Getting_Invalid_Double_String_Should_Return_BindingError()
{
var data = new Class1 { StringValue = "foo" };
var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double));
var result = await target.Take(1);
Assert.IsType<BindingNotification>(result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Coerce_Get_Null_Double_String_To_UnsetValue()
public async Task Should_Coerce_Get_Null_Double_String_To_UnsetValue()
{
var data = new Class1 { StringValue = null };
var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double));
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
GC.KeepAlive(data);
}
[Fact]
public void Should_Convert_Set_String_To_Double()
{
#if NET461
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
#else
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
#endif
var data = new Class1 { StringValue = (5.6).ToString() };
var target = new BindingExpression(new ExpressionObserver(data, "StringValue"), typeof(double));
target.OnNext(6.7);
Assert.Equal((6.7).ToString(), data.StringValue);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Convert_Get_Double_To_String()
public async Task Should_Convert_Get_Double_To_String()
{
#if NET461
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
#else
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
#endif
var data = new Class1 { DoubleValue = 5.6 };
var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string));
var result = await target.Take(1);
Assert.Equal((5.6).ToString(), result);
GC.KeepAlive(data);
}
[Fact]
public void Should_Convert_Set_Double_To_String()
{
#if NET461
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
#else
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
#endif
var data = new Class1 { DoubleValue = 5.6 };
var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue"), typeof(string));
target.OnNext("6.7");
Assert.Equal(6.7, data.DoubleValue);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Return_BindingNotification_With_FallbackValue_For_NonConvertibe_Target_Value()
public async Task Should_Return_BindingNotification_With_FallbackValue_For_NonConvertibe_Target_Value()
{
#if NET461
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
#else
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
#endif
var data = new Class1 { StringValue = "foo" };
var target = new BindingExpression(
new ExpressionObserver(data, "StringValue"),
@ -156,17 +146,13 @@ namespace Avalonia.Markup.UnitTests.Data
BindingErrorType.Error,
42),
result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Return_BindingNotification_With_FallbackValue_For_NonConvertibe_Target_Value_With_Data_Validation()
public async Task Should_Return_BindingNotification_With_FallbackValue_For_NonConvertibe_Target_Value_With_Data_Validation()
{
#if NET461
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
#else
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
#endif
var data = new Class1 { StringValue = "foo" };
var target = new BindingExpression(
new ExpressionObserver(data, "StringValue", true),
@ -181,17 +167,13 @@ namespace Avalonia.Markup.UnitTests.Data
BindingErrorType.Error,
42),
result);
GC.KeepAlive(data);
}
[Fact(Skip="Result is not always AggregateException.")]
public async void Should_Return_BindingNotification_For_Invalid_FallbackValue()
[Fact]
public async Task Should_Return_BindingNotification_For_Invalid_FallbackValue()
{
#if NET461
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
#else
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
#endif
var data = new Class1 { StringValue = "foo" };
var target = new BindingExpression(
new ExpressionObserver(data, "StringValue"),
@ -207,17 +189,13 @@ namespace Avalonia.Markup.UnitTests.Data
new InvalidCastException("Could not convert FallbackValue 'bar' to 'System.Int32'")),
BindingErrorType.Error),
result);
GC.KeepAlive(data);
}
[Fact(Skip="Result is not always AggregateException.")]
public async void Should_Return_BindingNotification_For_Invalid_FallbackValue_With_Data_Validation()
[Fact]
public async Task Should_Return_BindingNotification_For_Invalid_FallbackValue_With_Data_Validation()
{
#if NET461
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
#else
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
#endif
var data = new Class1 { StringValue = "foo" };
var target = new BindingExpression(
new ExpressionObserver(data, "StringValue", true),
@ -233,6 +211,8 @@ namespace Avalonia.Markup.UnitTests.Data
new InvalidCastException("Could not convert FallbackValue 'bar' to 'System.Int32'")),
BindingErrorType.Error),
result);
GC.KeepAlive(data);
}
[Fact]
@ -244,6 +224,8 @@ namespace Avalonia.Markup.UnitTests.Data
target.OnNext("foo");
Assert.Equal(5.6, data.DoubleValue);
GC.KeepAlive(data);
}
[Fact]
@ -259,6 +241,8 @@ namespace Avalonia.Markup.UnitTests.Data
target.OnNext("foo");
Assert.Equal(9.8, data.DoubleValue);
GC.KeepAlive(data);
}
[Fact]
@ -270,6 +254,8 @@ namespace Avalonia.Markup.UnitTests.Data
target.OnNext(null);
Assert.Equal(0, data.DoubleValue);
GC.KeepAlive(data);
}
[Fact]
@ -281,19 +267,16 @@ namespace Avalonia.Markup.UnitTests.Data
target.OnNext(AvaloniaProperty.UnsetValue);
Assert.Equal(0, data.DoubleValue);
GC.KeepAlive(data);
}
[Fact]
public void Should_Pass_ConverterParameter_To_Convert()
{
#if NET461
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
#else
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
#endif
var data = new Class1 { DoubleValue = 5.6 };
var converter = new Mock<IValueConverter>();
var target = new BindingExpression(
new ExpressionObserver(data, "DoubleValue"),
typeof(string),
@ -302,18 +285,14 @@ namespace Avalonia.Markup.UnitTests.Data
target.Subscribe(_ => { });
converter.Verify(x => x.Convert(5.6, typeof(string), "foo", CultureInfo.InvariantCulture));
converter.Verify(x => x.Convert(5.6, typeof(string), "foo", CultureInfo.CurrentCulture));
GC.KeepAlive(data);
}
[Fact]
public void Should_Pass_ConverterParameter_To_ConvertBack()
{
#if NET461
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
#else
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
#endif
var data = new Class1 { DoubleValue = 5.6 };
var converter = new Mock<IValueConverter>();
var target = new BindingExpression(
@ -324,18 +303,14 @@ namespace Avalonia.Markup.UnitTests.Data
target.OnNext("bar");
converter.Verify(x => x.ConvertBack("bar", typeof(double), "foo", CultureInfo.InvariantCulture));
converter.Verify(x => x.ConvertBack("bar", typeof(double), "foo", CultureInfo.CurrentCulture));
GC.KeepAlive(data);
}
[Fact]
public void Should_Handle_DataValidation()
{
#if NET461
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
#else
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
#endif
var data = new Class1 { DoubleValue = 5.6 };
var converter = new Mock<IValueConverter>();
var target = new BindingExpression(new ExpressionObserver(data, "DoubleValue", true), typeof(string));
@ -357,6 +332,8 @@ namespace Avalonia.Markup.UnitTests.Data
BindingErrorType.Error)
},
result);
GC.KeepAlive(data);
}
private class Class1 : NotifyingBase

5
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_AttachedProperty.cs

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Diagnostics;
using Avalonia.Markup.Data;
using Xunit;
@ -18,7 +19,7 @@ namespace Avalonia.Markup.UnitTests.Data
}
[Fact]
public async void Should_Get_Attached_Property_Value()
public async Task Should_Get_Attached_Property_Value()
{
var data = new Class1();
var target = new ExpressionObserver(data, "(Owner.Foo)");
@ -30,7 +31,7 @@ namespace Avalonia.Markup.UnitTests.Data
}
[Fact]
public async void Should_Get_Chained_Attached_Property_Value()
public async Task Should_Get_Chained_Attached_Property_Value()
{
var data = new Class1
{

3
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_AvaloniaProperty.cs

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Diagnostics;
using Avalonia.Markup.Data;
using Xunit;
@ -18,7 +19,7 @@ namespace Avalonia.Markup.UnitTests.Data
}
[Fact]
public async void Should_Get_Simple_Property_Value()
public async Task Should_Get_Simple_Property_Value()
{
var data = new Class1();
var target = new ExpressionObserver(data, "Foo");

9
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_DataValidation.cs

@ -28,6 +28,8 @@ namespace Avalonia.Markup.UnitTests.Data
observer.SetValue(-5);
Assert.False(validationMessageFound);
GC.KeepAlive(data);
}
[Fact]
@ -43,6 +45,8 @@ namespace Avalonia.Markup.UnitTests.Data
observer.SetValue(-5);
Assert.True(validationMessageFound);
GC.KeepAlive(data);
}
[Fact]
@ -102,6 +106,8 @@ namespace Avalonia.Markup.UnitTests.Data
new BindingNotification(new Exception("Must be positive"), BindingErrorType.DataValidationError, 5),
new BindingNotification(5),
}, result);
GC.KeepAlive(data);
}
[Fact]
@ -147,6 +153,9 @@ namespace Avalonia.Markup.UnitTests.Data
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
}, result);
GC.KeepAlive(container);
GC.KeepAlive(inner);
}
public class ExceptionTest : NotifyingBase

67
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Collections;
using Avalonia.Diagnostics;
using Avalonia.Markup.Data;
@ -16,113 +17,135 @@ namespace Avalonia.Markup.UnitTests.Data
public class ExpressionObserverTests_Indexer
{
[Fact]
public async void Should_Get_Array_Value()
public async Task Should_Get_Array_Value()
{
var data = new { Foo = new [] { "foo", "bar" } };
var target = new ExpressionObserver(data, "Foo[1]");
var result = await target.Take(1);
Assert.Equal("bar", result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Get_UnsetValue_For_Invalid_Array_Index()
public async Task Should_Get_UnsetValue_For_Invalid_Array_Index()
{
var data = new { Foo = new[] { "foo", "bar" } };
var target = new ExpressionObserver(data, "Foo[invalid]");
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Get_UnsetValue_For_Invalid_Dictionary_Index()
public async Task Should_Get_UnsetValue_For_Invalid_Dictionary_Index()
{
var data = new { Foo = new Dictionary<int, string> { { 1, "foo" } } };
var target = new ExpressionObserver(data, "Foo[invalid]");
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Get_UnsetValue_For_Object_Without_Indexer()
public async Task Should_Get_UnsetValue_For_Object_Without_Indexer()
{
var data = new { Foo = 5 };
var target = new ExpressionObserver(data, "Foo[noindexer]");
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Get_MultiDimensional_Array_Value()
public async Task Should_Get_MultiDimensional_Array_Value()
{
var data = new { Foo = new[,] { { "foo", "bar" }, { "baz", "qux" } } };
var target = new ExpressionObserver(data, "Foo[1, 1]");
var result = await target.Take(1);
Assert.Equal("qux", result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Get_Value_For_String_Indexer()
public async Task Should_Get_Value_For_String_Indexer()
{
var data = new { Foo = new Dictionary<string, string> { { "foo", "bar" }, { "baz", "qux" } } };
var target = new ExpressionObserver(data, "Foo[foo]");
var result = await target.Take(1);
Assert.Equal("bar", result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Get_Value_For_Non_String_Indexer()
public async Task Should_Get_Value_For_Non_String_Indexer()
{
var data = new { Foo = new Dictionary<double, string> { { 1.0, "bar" }, { 2.0, "qux" } } };
var target = new ExpressionObserver(data, "Foo[1.0]");
var result = await target.Take(1);
Assert.Equal("bar", result);
GC.KeepAlive(data);
}
[Fact]
public async void Array_Out_Of_Bounds_Should_Return_UnsetValue()
public async Task Array_Out_Of_Bounds_Should_Return_UnsetValue()
{
var data = new { Foo = new[] { "foo", "bar" } };
var target = new ExpressionObserver(data, "Foo[2]");
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
GC.KeepAlive(data);
}
[Fact]
public async void Array_With_Wrong_Dimensions_Should_Return_UnsetValue()
public async Task Array_With_Wrong_Dimensions_Should_Return_UnsetValue()
{
var data = new { Foo = new[] { "foo", "bar" } };
var target = new ExpressionObserver(data, "Foo[1,2]");
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
GC.KeepAlive(data);
}
[Fact]
public async void List_Out_Of_Bounds_Should_Return_UnsetValue()
public async Task List_Out_Of_Bounds_Should_Return_UnsetValue()
{
var data = new { Foo = new List<string> { "foo", "bar" } };
var target = new ExpressionObserver(data, "Foo[2]");
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Get_List_Value()
public async Task Should_Get_List_Value()
{
var data = new { Foo = new List<string> { "foo", "bar" } };
var target = new ExpressionObserver(data, "Foo[1]");
var result = await target.Take(1);
Assert.Equal("bar", result);
GC.KeepAlive(data);
}
[Fact]
@ -139,6 +162,8 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(new[] { AvaloniaProperty.UnsetValue, "baz" }, result);
Assert.Null(((INotifyCollectionChangedDebug)data.Foo).GetCollectionChangedSubscribers());
GC.KeepAlive(data);
}
[Fact]
@ -155,6 +180,8 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(new[] { "foo", "bar" }, result);
Assert.Null(((INotifyCollectionChangedDebug)data.Foo).GetCollectionChangedSubscribers());
GC.KeepAlive(data);
}
[Fact]
@ -171,6 +198,8 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(new[] { "bar", "baz" }, result);
Assert.Null(((INotifyCollectionChangedDebug)data.Foo).GetCollectionChangedSubscribers());
GC.KeepAlive(data);
}
[Fact]
@ -187,6 +216,9 @@ namespace Avalonia.Markup.UnitTests.Data
data.Foo.Move(0, 1);
Assert.Equal(new[] { "bar", "foo" }, result);
GC.KeepAlive(sub);
GC.KeepAlive(data);
}
[Fact]
@ -200,6 +232,9 @@ namespace Avalonia.Markup.UnitTests.Data
data.Foo.Clear();
Assert.Equal(new[] { "bar", AvaloniaProperty.UnsetValue }, result);
GC.KeepAlive(sub);
GC.KeepAlive(data);
}
[Fact]
@ -220,6 +255,8 @@ namespace Avalonia.Markup.UnitTests.Data
var expected = new[] { "bar", "bar2" };
Assert.Equal(expected, result);
Assert.Equal(0, data.Foo.PropertyChangedSubscriptionCount);
GC.KeepAlive(data);
}
[Fact]
@ -234,6 +271,8 @@ namespace Avalonia.Markup.UnitTests.Data
}
Assert.Equal("baz", data.Foo[1]);
GC.KeepAlive(data);
}
[Fact]
@ -254,6 +293,8 @@ namespace Avalonia.Markup.UnitTests.Data
}
Assert.Equal(4, data.Foo["foo"]);
GC.KeepAlive(data);
}
[Fact]
@ -274,6 +315,8 @@ namespace Avalonia.Markup.UnitTests.Data
}
Assert.Equal(4, data.Foo["bar"]);
GC.KeepAlive(data);
}
[Fact]
@ -291,6 +334,8 @@ namespace Avalonia.Markup.UnitTests.Data
}
Assert.Equal("bar2", data.Foo["foo"]);
GC.KeepAlive(data);
}
private class NonIntegerIndexer : NotifyingBase

5
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Lifetime.cs

@ -90,7 +90,8 @@ namespace Avalonia.Markup.UnitTests.Data
{
var scheduler = new TestScheduler();
var update = scheduler.CreateColdObservable<Unit>();
var target = new ExpressionObserver(() => new { Foo = "foo" }, "Foo", update);
var data = new { Foo = "foo" };
var target = new ExpressionObserver(() => data, "Foo", update);
var result = new List<object>();
using (target.Subscribe(x => result.Add(x)))
@ -101,6 +102,8 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(new[] { "foo" }, result);
Assert.All(update.Subscriptions, x => Assert.NotEqual(Subscription.Infinite, x.Unsubscribe));
GC.KeepAlive(data);
}
private Recorded<Notification<object>> OnNext(long time, object value)

31
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Negation.cs

@ -3,6 +3,7 @@
using System;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Data;
using Avalonia.Markup.Data;
using Xunit;
@ -12,57 +13,67 @@ namespace Avalonia.Markup.UnitTests.Data
public class ExpressionObserverTests_Negation
{
[Fact]
public async void Should_Negate_Boolean_Value()
public async Task Should_Negate_Boolean_Value()
{
var data = new { Foo = true };
var target = new ExpressionObserver(data, "!Foo");
var result = await target.Take(1);
Assert.Equal(false, result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Negate_0()
public async Task Should_Negate_0()
{
var data = new { Foo = 0 };
var target = new ExpressionObserver(data, "!Foo");
var result = await target.Take(1);
Assert.Equal(true, result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Negate_1()
public async Task Should_Negate_1()
{
var data = new { Foo = 1 };
var target = new ExpressionObserver(data, "!Foo");
var result = await target.Take(1);
Assert.Equal(false, result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Negate_False_String()
public async Task Should_Negate_False_String()
{
var data = new { Foo = "false" };
var target = new ExpressionObserver(data, "!Foo");
var result = await target.Take(1);
Assert.Equal(true, result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Negate_True_String()
public async Task Should_Negate_True_String()
{
var data = new { Foo = "True" };
var target = new ExpressionObserver(data, "!Foo");
var result = await target.Take(1);
Assert.Equal(false, result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Return_BindingNotification_For_String_Not_Convertible_To_Boolean()
public async Task Should_Return_BindingNotification_For_String_Not_Convertible_To_Boolean()
{
var data = new { Foo = "foo" };
var target = new ExpressionObserver(data, "!Foo");
@ -73,10 +84,12 @@ namespace Avalonia.Markup.UnitTests.Data
new InvalidCastException($"Unable to convert 'foo' to bool."),
BindingErrorType.Error),
result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Return_BindingNotification_For_Value_Not_Convertible_To_Boolean()
public async Task Should_Return_BindingNotification_For_Value_Not_Convertible_To_Boolean()
{
var data = new { Foo = new object() };
var target = new ExpressionObserver(data, "!Foo");
@ -87,6 +100,8 @@ namespace Avalonia.Markup.UnitTests.Data
new InvalidCastException($"Unable to convert 'System.Object' to bool."),
BindingErrorType.Error),
result);
GC.KeepAlive(data);
}
[Fact]
@ -96,6 +111,8 @@ namespace Avalonia.Markup.UnitTests.Data
var target = new ExpressionObserver(data, "!Foo");
Assert.False(target.SetValue("bar"));
GC.KeepAlive(data);
}
}
}

12
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs

@ -29,6 +29,8 @@ namespace Avalonia.Markup.UnitTests.Data
sync.ExecutePostedCallbacks();
Assert.Equal(new[] { source }, result);
GC.KeepAlive(data);
}
}
@ -47,6 +49,8 @@ namespace Avalonia.Markup.UnitTests.Data
sync.ExecutePostedCallbacks();
Assert.Equal(new[] { "foo", "bar" }, result);
GC.KeepAlive(data);
}
}
@ -67,6 +71,8 @@ namespace Avalonia.Markup.UnitTests.Data
sub.Dispose();
Assert.Equal(0, data.PropertyChangedSubscriptionCount);
GC.KeepAlive(data);
}
}
@ -87,6 +93,8 @@ namespace Avalonia.Markup.UnitTests.Data
// What does it mean to have data validation on an observable? Without a use-case
// it's hard to know what to do here so for the moment the value is returned.
Assert.Equal(new[] { "foo", "bar" }, result);
GC.KeepAlive(data);
}
}
@ -107,6 +115,8 @@ namespace Avalonia.Markup.UnitTests.Data
sub.Dispose();
Assert.Equal(0, data.PropertyChangedSubscriptionCount);
GC.KeepAlive(data);
}
}
@ -132,6 +142,8 @@ namespace Avalonia.Markup.UnitTests.Data
result);
sub.Dispose();
GC.KeepAlive(data);
}
}

78
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs

@ -11,19 +11,22 @@ using Avalonia.Data;
using Avalonia.Markup.Data;
using Avalonia.UnitTests;
using Xunit;
using System.Threading.Tasks;
namespace Avalonia.Markup.UnitTests.Data
{
public class ExpressionObserverTests_Property
{
[Fact]
public async void Should_Get_Simple_Property_Value()
public async Task Should_Get_Simple_Property_Value()
{
var data = new { Foo = "foo" };
var target = new ExpressionObserver(data, "Foo");
var result = await target.Take(1);
Assert.Equal("foo", result);
GC.KeepAlive(data);
}
[Fact]
@ -35,76 +38,92 @@ namespace Avalonia.Markup.UnitTests.Data
target.Subscribe(_ => { });
Assert.Equal(typeof(string), target.ResultType);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Get_Simple_Property_Value_Null()
public async Task Should_Get_Simple_Property_Value_Null()
{
var data = new { Foo = (string)null };
var target = new ExpressionObserver(data, "Foo");
var result = await target.Take(1);
Assert.Null(result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Get_Simple_Property_From_Base_Class()
public async Task Should_Get_Simple_Property_From_Base_Class()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(data, "Foo");
var result = await target.Take(1);
Assert.Equal("foo", result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Return_UnsetValue_For_Root_Null()
public async Task Should_Return_UnsetValue_For_Root_Null()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(default(object), "Foo");
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Return_UnsetValue_For_Root_UnsetValue()
public async Task Should_Return_UnsetValue_For_Root_UnsetValue()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(AvaloniaProperty.UnsetValue, "Foo");
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Return_UnsetValue_For_Observable_Root_Null()
public async Task Should_Return_UnsetValue_For_Observable_Root_Null()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(Observable.Return(default(object)), "Foo");
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Return_UnsetValue_For_Observable_Root_UnsetValue()
public async Task Should_Return_UnsetValue_For_Observable_Root_UnsetValue()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(Observable.Return(AvaloniaProperty.UnsetValue), "Foo");
var result = await target.Take(1);
Assert.Equal(AvaloniaProperty.UnsetValue, result);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Get_Simple_Property_Chain()
public async Task Should_Get_Simple_Property_Chain()
{
var data = new { Foo = new { Bar = new { Baz = "baz" } } };
var target = new ExpressionObserver(data, "Foo.Bar.Baz");
var result = await target.Take(1);
Assert.Equal("baz", result);
GC.KeepAlive(data);
}
[Fact]
@ -116,10 +135,12 @@ namespace Avalonia.Markup.UnitTests.Data
target.Subscribe(_ => { });
Assert.Equal(typeof(string), target.ResultType);
GC.KeepAlive(data);
}
[Fact]
public async void Should_Return_BindingNotification_Error_For_Broken_Chain()
public async Task Should_Return_BindingNotification_Error_For_Broken_Chain()
{
var data = new { Foo = new { Bar = 1 } };
var target = new ExpressionObserver(data, "Foo.Bar.Baz");
@ -131,6 +152,8 @@ namespace Avalonia.Markup.UnitTests.Data
new BindingNotification(
new MissingMemberException("Could not find CLR property 'Baz' on '1'"), BindingErrorType.Error),
result);
GC.KeepAlive(data);
}
[Fact]
@ -151,6 +174,8 @@ namespace Avalonia.Markup.UnitTests.Data
AvaloniaProperty.UnsetValue),
},
result);
GC.KeepAlive(data);
}
[Fact]
@ -160,6 +185,8 @@ namespace Avalonia.Markup.UnitTests.Data
var target = new ExpressionObserver(data, "Foo.Bar.Baz");
Assert.Null(target.ResultType);
GC.KeepAlive(data);
}
[Fact]
@ -177,6 +204,8 @@ namespace Avalonia.Markup.UnitTests.Data
sub.Dispose();
Assert.Equal(0, data.PropertyChangedSubscriptionCount);
GC.KeepAlive(data);
}
[Fact]
@ -205,6 +234,8 @@ namespace Avalonia.Markup.UnitTests.Data
sub.Dispose();
Assert.Equal(0, data.PropertyChangedSubscriptionCount);
GC.KeepAlive(data);
}
[Fact]
@ -224,6 +255,8 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(0, data.PropertyChangedSubscriptionCount);
Assert.Equal(0, data.Next.PropertyChangedSubscriptionCount);
GC.KeepAlive(data);
}
[Fact]
@ -245,6 +278,8 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(0, data.PropertyChangedSubscriptionCount);
Assert.Equal(0, data.Next.PropertyChangedSubscriptionCount);
Assert.Equal(0, old.PropertyChangedSubscriptionCount);
GC.KeepAlive(data);
}
[Fact]
@ -286,6 +321,8 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(0, data.PropertyChangedSubscriptionCount);
Assert.Equal(0, data.Next.PropertyChangedSubscriptionCount);
Assert.Equal(0, old.PropertyChangedSubscriptionCount);
GC.KeepAlive(data);
}
[Fact]
@ -318,6 +355,8 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(0, data.Next.PropertyChangedSubscriptionCount);
Assert.Equal(0, breaking.PropertyChangedSubscriptionCount);
Assert.Equal(0, old.PropertyChangedSubscriptionCount);
GC.KeepAlive(data);
}
[Fact]
@ -334,6 +373,8 @@ namespace Avalonia.Markup.UnitTests.Data
update.OnNext(Unit.Default);
Assert.Equal(new[] { "foo", "bar" }, result);
GC.KeepAlive(data);
}
[Fact]
@ -374,6 +415,8 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(new[] { "foo", "bar" }, result1);
Assert.Equal(new[] { "foo", "bar" }, result2);
Assert.Equal(new[] { "bar" }, result3);
GC.KeepAlive(data);
}
[Fact]
@ -391,6 +434,8 @@ namespace Avalonia.Markup.UnitTests.Data
sub2.Dispose();
Assert.Equal(0, data.PropertyChangedSubscriptionCount);
GC.KeepAlive(data);
}
[Fact]
@ -405,6 +450,8 @@ namespace Avalonia.Markup.UnitTests.Data
}
Assert.Equal("bar", data.Foo);
GC.KeepAlive(data);
}
[Fact]
@ -419,6 +466,8 @@ namespace Avalonia.Markup.UnitTests.Data
}
Assert.Equal("baz", ((Class2)data.Next).Bar);
GC.KeepAlive(data);
}
[Fact]
@ -431,6 +480,8 @@ namespace Avalonia.Markup.UnitTests.Data
{
Assert.False(target.SetValue("baz"));
}
GC.KeepAlive(data);
}
[Fact]
@ -444,6 +495,8 @@ namespace Avalonia.Markup.UnitTests.Data
target.SetValue("bar");
Assert.Equal(new[] { null, "bar" }, result);
GC.KeepAlive(data);
}
[Fact]
@ -457,6 +510,8 @@ namespace Avalonia.Markup.UnitTests.Data
target.SetValue("bar");
Assert.Equal(new[] { null, "bar" }, result);
GC.KeepAlive(data);
}
[Fact]
@ -469,6 +524,8 @@ namespace Avalonia.Markup.UnitTests.Data
{
Assert.False(target.SetValue("baz"));
}
GC.KeepAlive(data);
}
[Fact]
@ -498,6 +555,9 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(0, first.PropertyChangedSubscriptionCount);
Assert.Equal(0, second.PropertyChangedSubscriptionCount);
GC.KeepAlive(first);
GC.KeepAlive(second);
}
[Fact]

12
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Task.cs

@ -30,6 +30,8 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(1, result.Count);
Assert.IsType<Task<string>>(result[0]);
GC.KeepAlive(data);
}
}
@ -45,6 +47,8 @@ namespace Avalonia.Markup.UnitTests.Data
var sub = target.Subscribe(x => result.Add(x));
Assert.Equal(new[] { "foo" }, result);
GC.KeepAlive(data);
}
}
@ -63,6 +67,8 @@ namespace Avalonia.Markup.UnitTests.Data
sync.ExecutePostedCallbacks();
Assert.Equal(new[] { "foo" }, result);
GC.KeepAlive(data);
}
}
@ -88,6 +94,8 @@ namespace Avalonia.Markup.UnitTests.Data
BindingErrorType.Error)
},
result);
GC.KeepAlive(data);
}
}
@ -110,6 +118,8 @@ namespace Avalonia.Markup.UnitTests.Data
BindingErrorType.Error)
},
result);
GC.KeepAlive(data);
}
}
@ -130,6 +140,8 @@ namespace Avalonia.Markup.UnitTests.Data
// What does it mean to have data validation on a Task? Without a use-case it's
// hard to know what to do here so for the moment the value is returned.
Assert.Equal(new [] { "foo" }, result);
GC.KeepAlive(data);
}
}

2
tests/Avalonia.Markup.UnitTests/Data/Plugins/ExceptionValidationPluginTests.cs

@ -35,6 +35,8 @@ namespace Avalonia.Markup.UnitTests.Data.Plugins
new BindingNotification(new ArgumentOutOfRangeException("value"), BindingErrorType.DataValidationError),
new BindingNotification(6),
}, result);
GC.KeepAlive(data);
}
public class Data : NotifyingBase

3
tests/Avalonia.Markup.Xaml.UnitTests/Data/MultiBindingTests.cs

@ -10,13 +10,14 @@ using Moq;
using Avalonia.Controls;
using Avalonia.Markup.Xaml.Data;
using Xunit;
using System.Threading.Tasks;
namespace Avalonia.Markup.Xaml.UnitTests.Data
{
public class MultiBindingTests
{
[Fact]
public async void OneWay_Binding_Should_Be_Set_Up()
public async Task OneWay_Binding_Should_Be_Set_Up()
{
var source = new { A = 1, B = 2, C = 3 };
var binding = new MultiBinding

8
tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Data;
@ -45,7 +46,7 @@ namespace Avalonia.Styling.UnitTests
}
[Fact]
public async void Child_Matches_Control_When_It_Is_Child_OfType_And_Class()
public async Task Child_Matches_Control_When_It_Is_Child_OfType_And_Class()
{
var parent = new TestLogical1();
var child = new TestLogical2();
@ -144,6 +145,11 @@ namespace Avalonia.Styling.UnitTests
throw new NotImplementedException();
}
public void NotifyAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
throw new NotImplementedException();
}
public void NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
throw new NotImplementedException();

5
tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs

@ -175,6 +175,11 @@ namespace Avalonia.Styling.UnitTests
throw new NotImplementedException();
}
public void NotifyAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
throw new NotImplementedException();
}
public void NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
throw new NotImplementedException();

15
tests/Avalonia.UnitTests/InvariantCultureFixture.cs

@ -20,13 +20,22 @@ namespace Avalonia.UnitTests
public InvariantCultureFixture()
{
_restore = CultureInfo.CurrentUICulture;
CultureInfo.CurrentUICulture = CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
#if NET461
_restore = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
#else
_restore = CultureInfo.CurrentCulture;
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
#endif
}
public void Dispose()
{
CultureInfo.CurrentUICulture = CultureInfo.CurrentCulture = _restore;
#if NET461
Thread.CurrentThread.CurrentCulture = _restore;
#else
CultureInfo.CurrentCulture = _restore;
#endif
}
}
}

Loading…
Cancel
Save