Browse Source

Implemented scheduling for ExpressionObserver.

pull/237/head
Steven Kirk 11 years ago
parent
commit
b252fdaeb1
  1. 8
      src/Markup/Perspex.Markup/Binding/PropertyAccessorNode.cs
  2. 39
      tests/Perspex.Markup.UnitTests/Binding/ExpressionObserverTests_Observable.cs
  3. 37
      tests/Perspex.Markup.UnitTests/Binding/ExpressionObserverTests_Task.cs
  4. 1
      tests/Perspex.Markup.UnitTests/Perspex.Markup.UnitTests.csproj
  5. 68
      tests/Perspex.Markup.UnitTests/UnitTestSynchronizationContext.cs

8
src/Markup/Perspex.Markup/Binding/PropertyAccessorNode.cs

@ -3,11 +3,10 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Perspex.Threading;
namespace Perspex.Markup.Binding
{
@ -93,6 +92,7 @@ namespace Perspex.Markup.Binding
{
CurrentValue = ExpressionValue.None;
_subscription = observable
.ObserveOn(SynchronizationContext.Current)
.Subscribe(x => CurrentValue = new ExpressionValue(x));
}
else if (task != null)
@ -101,7 +101,9 @@ namespace Perspex.Markup.Binding
if (resultProperty != null)
{
task.ContinueWith(x => CurrentValue = new ExpressionValue(resultProperty.GetValue(task)))
task.ContinueWith(
x => CurrentValue = new ExpressionValue(resultProperty.GetValue(task)),
TaskScheduler.FromCurrentSynchronizationContext())
.ConfigureAwait(false);
}
}

39
tests/Perspex.Markup.UnitTests/Binding/ExpressionObserverTests_Observable.cs

@ -15,32 +15,39 @@ namespace Perspex.Markup.UnitTests.Binding
[Fact]
public void Should_Get_Simple_Observable_Value()
{
var source = new BehaviorSubject<string>("foo");
var data = new { Foo = source };
var target = new ExpressionObserver(data, "Foo");
var result = new List<object>();
using (var sync = UnitTestSynchronizationContext.Begin())
{
var source = new BehaviorSubject<string>("foo");
var data = new { Foo = source };
var target = new ExpressionObserver(data, "Foo");
var result = new List<object>();
var sub = target.Subscribe(x => result.Add(x.Value));
source.OnNext("bar");
var sub = target.Subscribe(x => result.Add(x.Value));
source.OnNext("bar");
sync.ExecutePostedCallbacks();
Assert.Equal(new[] { "foo", "bar" }, result);
Assert.Equal(new[] { null, "foo", "bar" }, result);
}
}
[Fact]
public void Should_Get_Property_Value_From_Observable()
{
var data = new Class1();
var target = new ExpressionObserver(data, "Next.Foo");
var result = new List<object>();
var sub = target.Subscribe(x => result.Add(x.Value));
data.Next.OnNext(new Class2("foo"));
using (var sync = UnitTestSynchronizationContext.Begin())
{
var data = new Class1();
var target = new ExpressionObserver(data, "Next.Foo");
var result = new List<object>();
Assert.Equal(new[] { null, "foo" }, result);
var sub = target.Subscribe(x => result.Add(x.Value));
data.Next.OnNext(new Class2("foo"));
sync.ExecutePostedCallbacks();
sub.Dispose();
Assert.Equal(new[] { null, "foo" }, result);
Assert.Equal(0, data.SubscriptionCount);
sub.Dispose();
Assert.Equal(0, data.SubscriptionCount);
}
}
private class Class1 : NotifyingBase

37
tests/Perspex.Markup.UnitTests/Binding/ExpressionObserverTests_Task.cs

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
using Perspex.Markup.Binding;
using Xunit;
@ -15,29 +16,37 @@ namespace Perspex.Markup.UnitTests.Binding
[Fact]
public void Should_Get_Simple_Task_Value()
{
var tcs = new TaskCompletionSource<string>();
var data = new { Foo = tcs.Task };
var target = new ExpressionObserver(data, "Foo");
var result = new List<object>();
using (var sync = UnitTestSynchronizationContext.Begin())
{
var tcs = new TaskCompletionSource<string>();
var data = new { Foo = tcs.Task };
var target = new ExpressionObserver(data, "Foo");
var result = new List<object>();
var sub = target.Subscribe(x => result.Add(x.Value));
tcs.SetResult("foo");
var sub = target.Subscribe(x => result.Add(x.Value));
tcs.SetResult("foo");
sync.ExecutePostedCallbacks();
Assert.Equal(new object[] { null, "foo" }, result.ToArray());
Assert.Equal(new object[] { null, "foo" }, result.ToArray());
}
}
[Fact]
public void Should_Get_Property_Value_From_Task()
{
var tcs = new TaskCompletionSource<Class2>();
var data = new Class1(tcs.Task);
var target = new ExpressionObserver(data, "Next.Foo");
var result = new List<object>();
using (var sync = UnitTestSynchronizationContext.Begin())
{
var tcs = new TaskCompletionSource<Class2>();
var data = new Class1(tcs.Task);
var target = new ExpressionObserver(data, "Next.Foo");
var result = new List<object>();
var sub = target.Subscribe(x => result.Add(x.Value));
tcs.SetResult(new Class2("foo"));
var sub = target.Subscribe(x => result.Add(x.Value));
tcs.SetResult(new Class2("foo"));
sync.ExecutePostedCallbacks();
Assert.Equal(new object[] { null, "foo" }, result.ToArray());
Assert.Equal(new object[] { null, "foo" }, result.ToArray());
}
}
private class Class1 : NotifyingBase

1
tests/Perspex.Markup.UnitTests/Perspex.Markup.UnitTests.csproj

@ -81,6 +81,7 @@
<Compile Include="Binding\ExpressionNodeBuilderTests.cs" />
<Compile Include="Binding\NotifyingBase.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UnitTestSynchronizationContext.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

68
tests/Perspex.Markup.UnitTests/UnitTestSynchronizationContext.cs

@ -0,0 +1,68 @@
// Copyright (c) The Perspex 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.Disposables;
using System.Threading;
namespace Perspex.Markup.UnitTests
{
internal sealed class UnitTestSynchronizationContext : SynchronizationContext
{
readonly List<Tuple<SendOrPostCallback, object>> _postedCallbacks =
new List<Tuple<SendOrPostCallback, object>>();
public static Scope Begin()
{
var sync = new UnitTestSynchronizationContext();
var old = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(sync);
return new Scope(old, sync);
}
public override void Send(SendOrPostCallback d, object state)
{
d(state);
}
public override void Post(SendOrPostCallback d, object state)
{
lock (_postedCallbacks)
{
_postedCallbacks.Add(Tuple.Create(d, state));
}
}
public void ExecutePostedCallbacks()
{
lock (_postedCallbacks)
{
_postedCallbacks.ForEach(t => t.Item1(t.Item2));
_postedCallbacks.Clear();
}
}
public class Scope : IDisposable
{
private SynchronizationContext _old;
private UnitTestSynchronizationContext _new;
public Scope(SynchronizationContext old, UnitTestSynchronizationContext n)
{
_old = old;
_new = n;
}
public void Dispose()
{
SynchronizationContext.SetSynchronizationContext(_old);
}
public void ExecutePostedCallbacks()
{
_new.ExecutePostedCallbacks();
}
}
}
}
Loading…
Cancel
Save