23 changed files with 959 additions and 379 deletions
@ -0,0 +1,165 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reactive.Linq; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Diagnostics; |
|||
using Avalonia.Data.Core; |
|||
using Xunit; |
|||
using Avalonia.Markup.Parsers; |
|||
|
|||
namespace Avalonia.Markup.UnitTests.Parsers |
|||
{ |
|||
public class ExpressionObserverBuilderTests_AttachedProperty |
|||
{ |
|||
private readonly Func<string, string, Type> _typeResolver; |
|||
|
|||
public ExpressionObserverBuilderTests_AttachedProperty() |
|||
{ |
|||
var foo = Owner.FooProperty; |
|||
_typeResolver = (_, name) => name == "Owner" ? typeof(Owner) : null; |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Get_Attached_Property_Value() |
|||
{ |
|||
var data = new Class1(); |
|||
var target = ExpressionObserverBuilder.Build(data, "(Owner.Foo)", typeResolver: _typeResolver); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal("foo", result); |
|||
|
|||
Assert.Null(((IAvaloniaObjectDebug)data).GetPropertyChangedSubscribers()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Get_Attached_Property_Value_With_Namespace() |
|||
{ |
|||
var data = new Class1(); |
|||
var target = ExpressionObserverBuilder.Build( |
|||
data, |
|||
"(NS:Owner.Foo)", |
|||
typeResolver: (ns, name) => ns == "NS" && name == "Owner" ? typeof(Owner) : null); |
|||
var result = await target.Take(1); |
|||
Assert.Equal("foo", result); |
|||
Assert.Null(((IAvaloniaObjectDebug)data).GetPropertyChangedSubscribers()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Get_Chained_Attached_Property_Value() |
|||
{ |
|||
var data = new Class1 |
|||
{ |
|||
Next = new Class1 |
|||
{ |
|||
[Owner.FooProperty] = "bar", |
|||
} |
|||
}; |
|||
|
|||
var target = ExpressionObserverBuilder.Build(data, "Next.(Owner.Foo)", typeResolver: _typeResolver); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal("bar", result); |
|||
|
|||
Assert.Null(((IAvaloniaObjectDebug)data).GetPropertyChangedSubscribers()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Track_Simple_Attached_Value() |
|||
{ |
|||
var data = new Class1(); |
|||
var target = ExpressionObserverBuilder.Build(data, "(Owner.Foo)", typeResolver: _typeResolver); |
|||
var result = new List<object>(); |
|||
|
|||
var sub = target.Subscribe(x => result.Add(x)); |
|||
data.SetValue(Owner.FooProperty, "bar"); |
|||
|
|||
Assert.Equal(new[] { "foo", "bar" }, result); |
|||
|
|||
sub.Dispose(); |
|||
|
|||
Assert.Null(((IAvaloniaObjectDebug)data).GetPropertyChangedSubscribers()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Track_Chained_Attached_Value() |
|||
{ |
|||
var data = new Class1 |
|||
{ |
|||
Next = new Class1 |
|||
{ |
|||
[Owner.FooProperty] = "foo", |
|||
} |
|||
}; |
|||
|
|||
var target = ExpressionObserverBuilder.Build(data, "Next.(Owner.Foo)", typeResolver: _typeResolver); |
|||
var result = new List<object>(); |
|||
|
|||
var sub = target.Subscribe(x => result.Add(x)); |
|||
data.Next.SetValue(Owner.FooProperty, "bar"); |
|||
|
|||
Assert.Equal(new[] { "foo", "bar" }, result); |
|||
|
|||
sub.Dispose(); |
|||
|
|||
Assert.Null(((IAvaloniaObjectDebug)data).GetPropertyChangedSubscribers()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Not_Keep_Source_Alive() |
|||
{ |
|||
Func<Tuple<ExpressionObserver, WeakReference>> run = () => |
|||
{ |
|||
var source = new Class1(); |
|||
var target = ExpressionObserverBuilder.Build(source, "(Owner.Foo)", typeResolver: _typeResolver); |
|||
return Tuple.Create(target, new WeakReference(source)); |
|||
}; |
|||
|
|||
var result = run(); |
|||
result.Item1.Subscribe(x => { }); |
|||
|
|||
GC.Collect(); |
|||
|
|||
Assert.Null(result.Item2.Target); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Fail_With_Attached_Property_With_Only_1_Part() |
|||
{ |
|||
var data = new Class1(); |
|||
|
|||
Assert.Throws<ExpressionParseException>(() => ExpressionObserverBuilder.Build(data, "(Owner)", typeResolver: _typeResolver)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Fail_With_Attached_Property_With_More_Than_2_Parts() |
|||
{ |
|||
var data = new Class1(); |
|||
|
|||
Assert.Throws<ExpressionParseException>(() => ExpressionObserverBuilder.Build(data, "(Owner.Foo.Bar)", typeResolver: _typeResolver)); |
|||
} |
|||
|
|||
private static class Owner |
|||
{ |
|||
public static readonly AttachedProperty<string> FooProperty = |
|||
AvaloniaProperty.RegisterAttached<Class1, string>( |
|||
"Foo", |
|||
typeof(Owner), |
|||
defaultValue: "foo"); |
|||
} |
|||
|
|||
private class Class1 : AvaloniaObject |
|||
{ |
|||
public static readonly StyledProperty<Class1> NextProperty = |
|||
AvaloniaProperty.Register<Class1, Class1>(nameof(Next)); |
|||
|
|||
public Class1 Next |
|||
{ |
|||
get { return GetValue(NextProperty); } |
|||
set { SetValue(NextProperty, value); } |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reactive.Linq; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Diagnostics; |
|||
using Avalonia.Data.Core; |
|||
using Xunit; |
|||
using Avalonia.Markup.Parsers; |
|||
|
|||
namespace Avalonia.Markup.UnitTests.Parsers |
|||
{ |
|||
public class ExpressionObserverBuilderTests_AvaloniaProperty |
|||
{ |
|||
public ExpressionObserverBuilderTests_AvaloniaProperty() |
|||
{ |
|||
var foo = Class1.FooProperty; |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Get_AvaloniaProperty_By_Name() |
|||
{ |
|||
var data = new Class1(); |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal("foo", result); |
|||
|
|||
Assert.Null(((IAvaloniaObjectDebug)data).GetPropertyChangedSubscribers()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Track_AvaloniaProperty_By_Name() |
|||
{ |
|||
var data = new Class1(); |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo"); |
|||
var result = new List<object>(); |
|||
|
|||
var sub = target.Subscribe(x => result.Add(x)); |
|||
data.SetValue(Class1.FooProperty, "bar"); |
|||
|
|||
Assert.Equal(new[] { "foo", "bar" }, result); |
|||
|
|||
sub.Dispose(); |
|||
|
|||
Assert.Null(((IAvaloniaObjectDebug)data).GetPropertyChangedSubscribers()); |
|||
} |
|||
|
|||
private class Class1 : AvaloniaObject |
|||
{ |
|||
public static readonly StyledProperty<string> FooProperty = |
|||
AvaloniaProperty.Register<Class1, string>("Foo", defaultValue: "foo"); |
|||
|
|||
public string ClrProperty { get; } = "clr-property"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,371 @@ |
|||
using Avalonia.Collections; |
|||
using Avalonia.Data.Core; |
|||
using Avalonia.Diagnostics; |
|||
using Avalonia.Markup.Parsers; |
|||
using Avalonia.Markup.Parsers.Nodes; |
|||
using Avalonia.UnitTests; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.ObjectModel; |
|||
using System.Reactive.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.Markup.UnitTests.Parsers |
|||
{ |
|||
public class ExpressionObserverBuilderTests_Indexer |
|||
{ |
|||
[Fact] |
|||
public async Task Should_Get_Array_Value() |
|||
{ |
|||
var data = new { Foo = new[] { "foo", "bar" } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[1]"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal("bar", result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Get_UnsetValue_For_Invalid_Array_Index() |
|||
{ |
|||
var data = new { Foo = new[] { "foo", "bar" } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[invalid]"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal(AvaloniaProperty.UnsetValue, result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Get_UnsetValue_For_Invalid_Dictionary_Index() |
|||
{ |
|||
var data = new { Foo = new Dictionary<int, string> { { 1, "foo" } } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[invalid]"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal(AvaloniaProperty.UnsetValue, result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Get_UnsetValue_For_Object_Without_Indexer() |
|||
{ |
|||
var data = new { Foo = 5 }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[noindexer]"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal(AvaloniaProperty.UnsetValue, result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Get_MultiDimensional_Array_Value() |
|||
{ |
|||
var data = new { Foo = new[,] { { "foo", "bar" }, { "baz", "qux" } } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[1, 1]"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal("qux", result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Get_Value_For_String_Indexer() |
|||
{ |
|||
var data = new { Foo = new Dictionary<string, string> { { "foo", "bar" }, { "baz", "qux" } } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[foo]"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal("bar", result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
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 = ExpressionObserverBuilder.Build(data, "Foo[1.0]"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal("bar", result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Array_Out_Of_Bounds_Should_Return_UnsetValue() |
|||
{ |
|||
var data = new { Foo = new[] { "foo", "bar" } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[2]"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal(AvaloniaProperty.UnsetValue, result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Array_With_Wrong_Dimensions_Should_Return_UnsetValue() |
|||
{ |
|||
var data = new { Foo = new[] { "foo", "bar" } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[1,2]"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal(AvaloniaProperty.UnsetValue, result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task List_Out_Of_Bounds_Should_Return_UnsetValue() |
|||
{ |
|||
var data = new { Foo = new List<string> { "foo", "bar" } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[2]"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal(AvaloniaProperty.UnsetValue, result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Get_List_Value() |
|||
{ |
|||
var data = new { Foo = new List<string> { "foo", "bar" } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[1]"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal("bar", result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Track_INCC_Add() |
|||
{ |
|||
var data = new { Foo = new AvaloniaList<string> { "foo", "bar" } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[2]"); |
|||
var result = new List<object>(); |
|||
|
|||
using (var sub = target.Subscribe(x => result.Add(x))) |
|||
{ |
|||
data.Foo.Add("baz"); |
|||
} |
|||
|
|||
Assert.Equal(new[] { AvaloniaProperty.UnsetValue, "baz" }, result); |
|||
Assert.Null(((INotifyCollectionChangedDebug)data.Foo).GetCollectionChangedSubscribers()); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Track_INCC_Remove() |
|||
{ |
|||
var data = new { Foo = new AvaloniaList<string> { "foo", "bar" } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[0]"); |
|||
var result = new List<object>(); |
|||
|
|||
using (var sub = target.Subscribe(x => result.Add(x))) |
|||
{ |
|||
data.Foo.RemoveAt(0); |
|||
} |
|||
|
|||
Assert.Equal(new[] { "foo", "bar" }, result); |
|||
Assert.Null(((INotifyCollectionChangedDebug)data.Foo).GetCollectionChangedSubscribers()); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Track_INCC_Replace() |
|||
{ |
|||
var data = new { Foo = new AvaloniaList<string> { "foo", "bar" } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[1]"); |
|||
var result = new List<object>(); |
|||
|
|||
using (var sub = target.Subscribe(x => result.Add(x))) |
|||
{ |
|||
data.Foo[1] = "baz"; |
|||
} |
|||
|
|||
Assert.Equal(new[] { "bar", "baz" }, result); |
|||
Assert.Null(((INotifyCollectionChangedDebug)data.Foo).GetCollectionChangedSubscribers()); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Track_INCC_Move() |
|||
{ |
|||
// Using ObservableCollection here because AvaloniaList does not yet have a Move
|
|||
// method, but even if it did we need to test with ObservableCollection as well
|
|||
// as AvaloniaList as it implements PropertyChanged as an explicit interface event.
|
|||
var data = new { Foo = new ObservableCollection<string> { "foo", "bar" } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[1]"); |
|||
var result = new List<object>(); |
|||
|
|||
var sub = target.Subscribe(x => result.Add(x)); |
|||
data.Foo.Move(0, 1); |
|||
|
|||
Assert.Equal(new[] { "bar", "foo" }, result); |
|||
|
|||
GC.KeepAlive(sub); |
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Track_INCC_Reset() |
|||
{ |
|||
var data = new { Foo = new AvaloniaList<string> { "foo", "bar" } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[1]"); |
|||
var result = new List<object>(); |
|||
|
|||
var sub = target.Subscribe(x => result.Add(x)); |
|||
data.Foo.Clear(); |
|||
|
|||
Assert.Equal(new[] { "bar", AvaloniaProperty.UnsetValue }, result); |
|||
|
|||
GC.KeepAlive(sub); |
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Track_NonIntegerIndexer() |
|||
{ |
|||
var data = new { Foo = new NonIntegerIndexer() }; |
|||
data.Foo["foo"] = "bar"; |
|||
data.Foo["baz"] = "qux"; |
|||
|
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[foo]"); |
|||
var result = new List<object>(); |
|||
|
|||
using (var sub = target.Subscribe(x => result.Add(x))) |
|||
{ |
|||
data.Foo["foo"] = "bar2"; |
|||
} |
|||
|
|||
var expected = new[] { "bar", "bar2" }; |
|||
Assert.Equal(expected, result); |
|||
Assert.Equal(0, data.Foo.PropertyChangedSubscriptionCount); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_SetArrayIndex() |
|||
{ |
|||
var data = new { Foo = new[] { "foo", "bar" } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[1]"); |
|||
|
|||
using (target.Subscribe(_ => { })) |
|||
{ |
|||
Assert.True(target.SetValue("baz")); |
|||
} |
|||
|
|||
Assert.Equal("baz", data.Foo[1]); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Set_ExistingDictionaryEntry() |
|||
{ |
|||
var data = new |
|||
{ |
|||
Foo = new Dictionary<string, int> |
|||
{ |
|||
{"foo", 1 } |
|||
} |
|||
}; |
|||
|
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[foo]"); |
|||
using (target.Subscribe(_ => { })) |
|||
{ |
|||
Assert.True(target.SetValue(4)); |
|||
} |
|||
|
|||
Assert.Equal(4, data.Foo["foo"]); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Add_NewDictionaryEntry() |
|||
{ |
|||
var data = new |
|||
{ |
|||
Foo = new Dictionary<string, int> |
|||
{ |
|||
{"foo", 1 } |
|||
} |
|||
}; |
|||
|
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[bar]"); |
|||
using (target.Subscribe(_ => { })) |
|||
{ |
|||
Assert.True(target.SetValue(4)); |
|||
} |
|||
|
|||
Assert.Equal(4, data.Foo["bar"]); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Set_NonIntegerIndexer() |
|||
{ |
|||
var data = new { Foo = new NonIntegerIndexer() }; |
|||
data.Foo["foo"] = "bar"; |
|||
data.Foo["baz"] = "qux"; |
|||
|
|||
var target = ExpressionObserverBuilder.Build(data, "Foo[foo]"); |
|||
|
|||
using (target.Subscribe(_ => { })) |
|||
{ |
|||
Assert.True(target.SetValue("bar2")); |
|||
} |
|||
|
|||
Assert.Equal("bar2", data.Foo["foo"]); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Indexer_Only_Binding_Works() |
|||
{ |
|||
var data = new[] { 1, 2, 3 }; |
|||
|
|||
var target = ExpressionObserverBuilder.Build(data, "[1]"); |
|||
|
|||
var value = await target.Take(1); |
|||
|
|||
Assert.Equal(data[1], value); |
|||
} |
|||
|
|||
private class NonIntegerIndexer : NotifyingBase |
|||
{ |
|||
private readonly Dictionary<string, string> _storage = new Dictionary<string, string>(); |
|||
|
|||
public string this[string key] |
|||
{ |
|||
get |
|||
{ |
|||
return _storage[key]; |
|||
} |
|||
set |
|||
{ |
|||
_storage[key] = value; |
|||
RaisePropertyChanged(CommonPropertyNames.IndexerName); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,112 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Reactive.Linq; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Data; |
|||
using Avalonia.Markup.Parsers; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.Markup.UnitTests.Parsers |
|||
{ |
|||
public class ExpressionObserverBuilderTests_Negation |
|||
{ |
|||
[Fact] |
|||
public async Task Should_Negate_0() |
|||
{ |
|||
var data = new { Foo = 0 }; |
|||
var target = ExpressionObserverBuilder.Build(data, "!Foo"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.True((bool)result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Negate_1() |
|||
{ |
|||
var data = new { Foo = 1 }; |
|||
var target = ExpressionObserverBuilder.Build(data, "!Foo"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.False((bool)result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Negate_False_String() |
|||
{ |
|||
var data = new { Foo = "false" }; |
|||
var target = ExpressionObserverBuilder.Build(data, "!Foo"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.True((bool)result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Negate_True_String() |
|||
{ |
|||
var data = new { Foo = "True" }; |
|||
var target = ExpressionObserverBuilder.Build(data, "!Foo"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.False((bool)result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Return_BindingNotification_For_String_Not_Convertible_To_Boolean() |
|||
{ |
|||
var data = new { Foo = "foo" }; |
|||
var target = ExpressionObserverBuilder.Build(data, "!Foo"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal( |
|||
new BindingNotification( |
|||
new InvalidCastException($"Unable to convert 'foo' to bool."), |
|||
BindingErrorType.Error), |
|||
result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Return_BindingNotification_For_Value_Not_Convertible_To_Boolean() |
|||
{ |
|||
var data = new { Foo = new object() }; |
|||
var target = ExpressionObserverBuilder.Build(data, "!Foo"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.Equal( |
|||
new BindingNotification( |
|||
new InvalidCastException($"Unable to convert 'System.Object' to bool."), |
|||
BindingErrorType.Error), |
|||
result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void SetValue_Should_Return_False_For_Invalid_Value() |
|||
{ |
|||
var data = new { Foo = "foo" }; |
|||
var target = ExpressionObserverBuilder.Build(data, "!Foo"); |
|||
target.Subscribe(_ => { }); |
|||
|
|||
Assert.False(target.SetValue("bar")); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
private class Test |
|||
{ |
|||
public bool Foo { get; set; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
using Avalonia.Data; |
|||
using Avalonia.Markup.Parsers; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reactive.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Xunit; |
|||
|
|||
namespace Avalonia.Markup.UnitTests.Parsers |
|||
{ |
|||
public class ExpressionObserverBuilderTests_Property |
|||
{ |
|||
[Fact] |
|||
public async Task Should_Return_BindingNotification_Error_For_Broken_Chain() |
|||
{ |
|||
var data = new { Foo = new { Bar = 1 } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo.Bar.Baz"); |
|||
var result = await target.Take(1); |
|||
|
|||
Assert.IsType<BindingNotification>(result); |
|||
|
|||
Assert.Equal( |
|||
new BindingNotification( |
|||
new MissingMemberException("Could not find CLR property 'Baz' on '1'"), BindingErrorType.Error), |
|||
result); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Have_Null_ResultType_For_Broken_Chain() |
|||
{ |
|||
var data = new { Foo = new { Bar = 1 } }; |
|||
var target = ExpressionObserverBuilder.Build(data, "Foo.Bar.Baz"); |
|||
|
|||
Assert.Null(target.ResultType); |
|||
|
|||
GC.KeepAlive(data); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue