Browse Source

Fix failing tests, add other tests.

Also use logical parent's DataContext as base for DataContext bindings
instead of visual parent's. Was previously in error.
pull/691/head
Steven Kirk 10 years ago
parent
commit
0c2057e458
  1. 15
      src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs
  2. 2
      src/Markup/Avalonia.Markup/Data/ExpressionNode.cs
  3. 7
      src/Markup/Avalonia.Markup/Data/ExpressionObserver.cs
  4. 5
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_DataValidation.cs
  5. 99
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Property.cs
  6. 8
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs

15
src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs

@ -227,10 +227,7 @@ namespace Avalonia.Markup.Xaml.Data
else
{
return new ExpressionObserver(
target.GetObservable(Visual.VisualParentProperty)
.OfType<IAvaloniaObject>()
.Select(x => x.GetObservable(Control.DataContextProperty))
.Switch(),
GetParentDataContext(target),
path,
EnableValidation);
}
@ -272,6 +269,16 @@ namespace Avalonia.Markup.Xaml.Data
return result;
}
private IObservable<object> GetParentDataContext(IAvaloniaObject target)
{
return target.GetObservable(Control.ParentProperty)
.Select(x =>
{
return (x as IAvaloniaObject)?.GetObservable(Control.DataContextProperty) ??
Observable.Return((object)null);
}).Switch();
}
private class PathInfo
{
public string Path { get; set; }

2
src/Markup/Avalonia.Markup/Data/ExpressionNode.cs

@ -178,7 +178,7 @@ namespace Avalonia.Markup.Data
private BindingNotification TargetNullNotification()
{
// TODO: Work out a way to give a more useful error message here.
return new BindingNotification(new NullReferenceException(), BindingErrorType.Error);
return new BindingNotification(new NullReferenceException(), BindingErrorType.Error, AvaloniaProperty.UnsetValue);
}
}
}

7
src/Markup/Avalonia.Markup/Data/ExpressionObserver.cs

@ -66,6 +66,11 @@ namespace Avalonia.Markup.Data
{
Contract.Requires<ArgumentNullException>(expression != null);
if (root == AvaloniaProperty.UnsetValue)
{
root = null;
}
Expression = expression;
_node = Parse(expression, enableDataValidation);
_root = new WeakReference(root);
@ -199,7 +204,7 @@ namespace Avalonia.Markup.Data
if (observable != null)
{
return observable.Subscribe(
x => _node.Target = new WeakReference(x),
x => _node.Target = new WeakReference(x != AvaloniaProperty.UnsetValue ? x : null),
_ => _finished.OnNext(Unit.Default),
() => _finished.OnNext(Unit.Default));
}

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

@ -142,7 +142,10 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal(new[]
{
new BindingNotification(new NullReferenceException(), BindingErrorType.Error),
new BindingNotification(
new NullReferenceException(),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
}, result);
}

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

@ -57,6 +57,66 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal("foo", result);
}
[Fact]
public async void Should_Return_BindingNotification_Error_For_Root_Null()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(default(object), "Foo");
var result = await target.Take(1);
Assert.Equal(
new BindingNotification(
new NullReferenceException(),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
result);
}
[Fact]
public async void Should_Return_BindingNotification_Error_For_Root_UnsetValue()
{
var data = new Class3 { Foo = "foo" };
var target = new ExpressionObserver(AvaloniaProperty.UnsetValue, "Foo");
var result = await target.Take(1);
Assert.Equal(
new BindingNotification(
new NullReferenceException(),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
result);
}
[Fact]
public async void Should_Return_BindingNotification_Error_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(
new BindingNotification(
new NullReferenceException(),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
result);
}
[Fact]
public async void Should_Return_BindingNotification_Error_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(
new BindingNotification(
new NullReferenceException(),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
result);
}
[Fact]
public async void Should_Get_Simple_Property_Chain()
{
@ -87,9 +147,10 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.IsType<BindingNotification>(result);
var error = result as BindingNotification;
Assert.IsType<MissingMemberException>(error.Error);
Assert.Equal("Could not find CLR property 'Baz' on '1'", error.Error.Message);
Assert.Equal(
new BindingNotification(
new MissingMemberException("Could not find CLR property 'Baz' on '1'"), BindingErrorType.Error),
result);
}
[Fact]
@ -101,12 +162,15 @@ namespace Avalonia.Markup.UnitTests.Data
target.Subscribe(x => result.Add(x));
Assert.Equal(1, result.Count);
Assert.IsType<BindingNotification>(result[0]);
var error = result[0] as BindingNotification;
Assert.IsType<NullReferenceException>(error.Error);
Assert.Equal("Object reference not set to an instance of an object.", error.Error.Message);
Assert.Equal(
new[]
{
new BindingNotification(
new NullReferenceException(),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
},
result);
}
[Fact]
@ -219,7 +283,10 @@ namespace Avalonia.Markup.UnitTests.Data
new object[]
{
"bar",
new BindingNotification(new NullReferenceException(), BindingErrorType.Error),
new BindingNotification(
new NullReferenceException(),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
"baz"
},
result);
@ -388,7 +455,12 @@ namespace Avalonia.Markup.UnitTests.Data
var target = new ExpressionObserver((object)null, "Foo");
var result = await target.Take(1);
Assert.Equal(new BindingNotification(new NullReferenceException(), BindingErrorType.Error), result);
Assert.Equal(
new BindingNotification(
new NullReferenceException(),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
result);
}
[Fact]
@ -412,7 +484,10 @@ namespace Avalonia.Markup.UnitTests.Data
{
"foo",
"bar",
new BindingNotification(new NullReferenceException(), BindingErrorType.Error)
new BindingNotification(
new NullReferenceException(),
BindingErrorType.Error,
AvaloniaProperty.UnsetValue),
},
result);

8
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs

@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Markup.Data;
@ -272,7 +274,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
/// </summary>
/// <remarks>
/// - Items is bound to DataContext first, followed by say SelectedIndex
/// - When the ListBox is removed from the visual tree, DataContext becomes null (as it's
/// - When the ListBox is removed from the logical tree, DataContext becomes null (as it's
/// inherited)
/// - This changes Items to null, which changes SelectedIndex to null as there are no
/// longer any items
@ -299,12 +301,12 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
// Bind Foo and Bar to the VM.
target.Bind(OldDataContextTest.FooProperty, fooBinding);
target.Bind(OldDataContextTest.BarProperty, barBinding);
//target.Bind(OldDataContextTest.BarProperty, barBinding);
target.DataContext = vm;
// Make sure the control's Foo and Bar properties are read from the VM
Assert.Equal(1, target.GetValue(OldDataContextTest.FooProperty));
Assert.Equal(2, target.GetValue(OldDataContextTest.BarProperty));
//Assert.Equal(2, target.GetValue(OldDataContextTest.BarProperty));
// Set DataContext to null.
target.DataContext = null;

Loading…
Cancel
Save