Skip to content

Commit

Permalink
another simple unit tests for issue AvaloniaUI#855 for direct and sty…
Browse files Browse the repository at this point in the history
…led properties
  • Loading branch information
donandren committed Feb 16, 2017
1 parent 7f2302c commit 1df3040
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 22 deletions.
4 changes: 4 additions & 0 deletions tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@
<Project>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
<Project>{3E53A01A-B331-47F3-B828-4A5717E77A24}</Project>
<Name>Avalonia.Markup.Xaml</Name>
</ProjectReference>
<ProjectReference Include="..\Avalonia.UnitTests\Avalonia.UnitTests.csproj">
<Project>{88060192-33d5-4932-b0f9-8bd2763e857d}</Project>
<Name>Avalonia.UnitTests</Name>
Expand Down
103 changes: 91 additions & 12 deletions tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Microsoft.Reactive.Testing;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Data;
using Avalonia.Logging;
using Avalonia.UnitTests;
using Xunit;
using System.Threading.Tasks;
using Avalonia.Markup.Xaml.Data;
using Avalonia.Platform;
using System.Threading;
using Moq;
using System.Reactive.Disposables;
using System.Reactive.Concurrency;
using Avalonia.Threading;
using Avalonia.UnitTests;
using Microsoft.Reactive.Testing;
using Moq;
using Xunit;

namespace Avalonia.Base.UnitTests
{
Expand Down Expand Up @@ -363,7 +362,7 @@ public void Bind_Logs_Binding_Error()
Assert.True(called);
}
}

[Fact]
public async void Bind_With_Scheduler_Executes_On_Scheduler()
{
Expand All @@ -384,7 +383,43 @@ public async void Bind_With_Scheduler_Executes_On_Scheduler()

await Task.Run(() => source.OnNext(6.7));
}
}

[Fact]
public void SetValue_Should_Not_Cause_StackOverflow_And_Have_Correct_Values()
{
var viewModel = new TestStackOverflowViewModel()
{
Value = 50
};

var target = new Class1();

//note: if the initialization of the child binding is here target/child binding work fine!!!
//var child = new Class1()
//{
// [~~Class1.DoubleValueProperty] = target[~~Class1.DoubleValueProperty]
//};

target.Bind(Class1.DoubleValueProperty, new Binding("Value") { Mode = BindingMode.TwoWay, Source = viewModel });

var child = new Class1()
{
[~~Class1.DoubleValueProperty] = target[~~Class1.DoubleValueProperty]
};

Assert.Equal(1, viewModel.SetterInvokedCount);

//here in real life stack overflow exception is thrown issue #855 and #824
target.DoubleValue = 51.001;

Assert.Equal(2, viewModel.SetterInvokedCount);

double expected = 51;

Assert.Equal(expected, viewModel.Value);
Assert.Equal(expected, target.DoubleValue);
Assert.Equal(expected, child.DoubleValue);
}

/// <summary>
Expand All @@ -405,6 +440,15 @@ private class Class1 : AvaloniaObject

public static readonly StyledProperty<double> QuxProperty =
AvaloniaProperty.Register<Class1, double>("Qux", 5.6);

public static readonly StyledProperty<double> DoubleValueProperty =
AvaloniaProperty.Register<Class1, double>(nameof(DoubleValue));

public double DoubleValue
{
get { return GetValue(DoubleValueProperty); }
set { SetValue(DoubleValueProperty, value); }
}
}

private class Class2 : Class1
Expand All @@ -431,5 +475,40 @@ public InstancedBinding Initiate(
return new InstancedBinding(_source, BindingMode.OneTime);
}
}

private class TestStackOverflowViewModel : INotifyPropertyChanged
{
public int SetterInvokedCount { get; private set; }

public const int MaxInvokedCount = 1000;

private double _value;

public event PropertyChangedEventHandler PropertyChanged;

public double Value
{
get { return _value; }
set
{
if (_value != value)
{
SetterInvokedCount++;
if (SetterInvokedCount < MaxInvokedCount)
{
_value = (int)value;
if (_value > 75) _value = 75;
if (_value < 25) _value = 25;
}
else
{
_value = value;
}

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
}
}
}
}
}
}
}
106 changes: 96 additions & 10 deletions tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reactive.Subjects;
using Avalonia;
using Avalonia.Data;
using Avalonia.Logging;
using Avalonia.Markup.Xaml.Data;
using Avalonia.UnitTests;
using Xunit;

Expand Down Expand Up @@ -208,7 +209,7 @@ public void ReadOnly_Property_Cannot_Be_Set()
{
var target = new Class1();

Assert.Throws<ArgumentException>(() =>
Assert.Throws<ArgumentException>(() =>
target.SetValue(Class1.BarProperty, "newvalue"));
}

Expand All @@ -217,7 +218,7 @@ public void ReadOnly_Property_Cannot_Be_Set_NonGeneric()
{
var target = new Class1();

Assert.Throws<ArgumentException>(() =>
Assert.Throws<ArgumentException>(() =>
target.SetValue((AvaloniaProperty)Class1.BarProperty, "newvalue"));
}

Expand All @@ -227,7 +228,7 @@ public void ReadOnly_Property_Cannot_Be_Bound()
var target = new Class1();
var source = new Subject<string>();

Assert.Throws<ArgumentException>(() =>
Assert.Throws<ArgumentException>(() =>
target.Bind(Class1.BarProperty, source));
}

Expand Down Expand Up @@ -439,12 +440,49 @@ public void AddOwner_Can_Override_DefaultBindingMode()
Assert.Equal(BindingMode.OneWayToSource, bar.GetMetadata<Class2>().DefaultBindingMode);
}

[Fact]
public void SetValue_Should_Not_Cause_StackOverflow_And_Have_Correct_Values()
{
var viewModel = new TestStackOverflowViewModel()
{
Value = 50
};

var target = new Class1();

//note: if the initialization of the child binding is here there is no stackoverflow!!!
//var child = new Class1()
//{
// [~~Class1.DoubleValueProperty] = target[~~Class1.DoubleValueProperty]
//};

target.Bind(Class1.DoubleValueProperty, new Binding("Value") { Mode = BindingMode.TwoWay, Source = viewModel });

var child = new Class1()
{
[~~Class1.DoubleValueProperty] = target[~~Class1.DoubleValueProperty]
};

Assert.Equal(1, viewModel.SetterInvokedCount);

//here in real life stack overflow exception is thrown issue #855 and #824
target.DoubleValue = 51.001;

Assert.Equal(2, viewModel.SetterInvokedCount);

double expected = 51;

Assert.Equal(expected, viewModel.Value);
Assert.Equal(expected, target.DoubleValue);
Assert.Equal(expected, child.DoubleValue);
}

private class Class1 : AvaloniaObject
{
public static readonly DirectProperty<Class1, string> FooProperty =
AvaloniaProperty.RegisterDirect<Class1, string>(
"Foo",
o => o.Foo,
"Foo",
o => o.Foo,
(o, v) => o.Foo = v,
unsetValue: "unset");

Expand All @@ -453,14 +491,21 @@ private class Class1 : AvaloniaObject

public static readonly DirectProperty<Class1, int> BazProperty =
AvaloniaProperty.RegisterDirect<Class1, int>(
"Bar",
o => o.Baz,
(o,v) => o.Baz = v,
"Bar",
o => o.Baz,
(o, v) => o.Baz = v,
unsetValue: -1);

public static readonly DirectProperty<Class1, double> DoubleValueProperty =
AvaloniaProperty.RegisterDirect<Class1, double>(
nameof(DoubleValue),
o => o.DoubleValue,
(o, v) => o.DoubleValue = v);

private string _foo = "initial";
private readonly string _bar = "bar";
private int _baz = 5;
private double _doubleValue;

public string Foo
{
Expand All @@ -478,6 +523,12 @@ public int Baz
get { return _baz; }
set { SetAndRaise(BazProperty, ref _baz, value); }
}

public double DoubleValue
{
get { return _doubleValue; }
set { SetAndRaise(DoubleValueProperty, ref _doubleValue, value); }
}
}

private class Class2 : AvaloniaObject
Expand All @@ -497,5 +548,40 @@ public string Foo
set { SetAndRaise(FooProperty, ref _foo, value); }
}
}

private class TestStackOverflowViewModel : INotifyPropertyChanged
{
public int SetterInvokedCount { get; private set; }

public const int MaxInvokedCount = 1000;

private double _value;

public event PropertyChangedEventHandler PropertyChanged;

public double Value
{
get { return _value; }
set
{
if (_value != value)
{
SetterInvokedCount++;
if (SetterInvokedCount < MaxInvokedCount)
{
_value = (int)value;
if (_value > 75) _value = 75;
if (_value < 25) _value = 25;
}
else
{
_value = value;
}

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
}
}
}
}
}
}
}

0 comments on commit 1df3040

Please sign in to comment.