Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

1141 lines
35 KiB
C#

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Threading;
#if !NO_WINDOWS_THREADING
using System.Windows.Threading;
#endif
using Microsoft.Reactive.Testing;
#if NUNIT
using NUnit.Framework;
using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
using TestMethodAttribute = NUnit.Framework.TestAttribute;
using TestInitializeAttribute = NUnit.Framework.SetUpAttribute;
#else
using Microsoft.VisualStudio.TestTools.UnitTesting;
#endif
using ReactiveTests.Dummies;
#if HAS_WINFORMS
using System.Windows.Forms;
#endif
#if SILVERLIGHT && !SILVERLIGHTM7
using Microsoft.Silverlight.Testing;
#endif
namespace ReactiveTests.Tests
{
[TestClass]
public partial class ObservableConcurrencyTest : TestBase
{
#region + ObserveOn +
[TestMethod]
public void ObserveOn_ArgumentChecking()
{
var someObservable = Observable.Empty<int>();
#if HAS_WINFORMS
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ObserveOn<int>(default(IObservable<int>), new ControlScheduler(new Label())));
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ObserveOn<int>(someObservable, default(ControlScheduler)));
ReactiveAssert.Throws<ArgumentNullException>(() => ControlObservable.ObserveOn<int>(default(IObservable<int>), new Label()));
ReactiveAssert.Throws<ArgumentNullException>(() => ControlObservable.ObserveOn<int>(someObservable, default(Label)));
#endif
#if !NO_WINDOWS_THREADING
#if USE_SL_DISPATCHER
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ObserveOn<int>(default(IObservable<int>), new DispatcherScheduler(System.Windows.Deployment.Current.Dispatcher)));
#else
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ObserveOn<int>(default(IObservable<int>), new DispatcherScheduler(Dispatcher.CurrentDispatcher)));
#endif
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ObserveOn<int>(someObservable, default(DispatcherScheduler)));
#if USE_SL_DISPATCHER
ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherObservable.ObserveOn<int>(default(IObservable<int>), System.Windows.Deployment.Current.Dispatcher));
#else
ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherObservable.ObserveOn<int>(default(IObservable<int>), Dispatcher.CurrentDispatcher));
#endif
ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherObservable.ObserveOn<int>(someObservable, default(Dispatcher)));
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ObserveOn<int>(default(IObservable<int>), new SynchronizationContext()));
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ObserveOn<int>(someObservable, default(SynchronizationContext)));
ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherObservable.ObserveOnDispatcher<int>(default(IObservable<int>)));
#endif
}
#if HAS_WINFORMS
[TestMethod]
public void ObserveOn_Control()
{
var lbl = CreateLabel();
var evt = new ManualResetEvent(false);
bool okay = true;
Observable.Range(0, 10, NewThreadScheduler.Default).ObserveOn(lbl).Subscribe(x =>
{
lbl.Text = x.ToString();
okay &= (SynchronizationContext.Current is System.Windows.Forms.WindowsFormsSynchronizationContext);
}, () => evt.Set());
evt.WaitOne();
Application.Exit();
Assert.IsTrue(okay);
}
[TestMethod]
public void ObserveOn_ControlScheduler()
{
var lbl = CreateLabel();
var evt = new ManualResetEvent(false);
bool okay = true;
Observable.Range(0, 10, NewThreadScheduler.Default).ObserveOn(new ControlScheduler(lbl)).Subscribe(x =>
{
lbl.Text = x.ToString();
okay &= (SynchronizationContext.Current is System.Windows.Forms.WindowsFormsSynchronizationContext);
}, () => evt.Set());
evt.WaitOne();
Application.Exit();
Assert.IsTrue(okay);
}
private Label CreateLabel()
{
var loaded = new ManualResetEvent(false);
var lbl = default(Label);
var t = new Thread(() =>
{
lbl = new Label();
var frm = new Form { Controls = { lbl }, Width = 0, Height = 0, FormBorderStyle = FormBorderStyle.None, ShowInTaskbar = false };
frm.Load += (_, __) =>
{
loaded.Set();
};
Application.Run(frm);
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
loaded.WaitOne();
return lbl;
}
#endif
#if !NO_WINDOWS_THREADING
[TestMethod]
[Asynchronous]
public void ObserveOn_Dispatcher()
{
var dispatcher = DispatcherHelpers.EnsureDispatcher();
RunAsync(evt =>
{
bool okay = true;
Observable.Range(0, 10, NewThreadScheduler.Default).ObserveOn(dispatcher).Subscribe(x =>
{
okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
}, () =>
{
Assert.IsTrue(okay);
dispatcher.InvokeShutdown();
evt.Set();
});
});
}
[TestMethod]
[Asynchronous]
public void ObserveOn_DispatcherScheduler()
{
var dispatcher = DispatcherHelpers.EnsureDispatcher();
RunAsync(evt =>
{
bool okay = true;
Observable.Range(0, 10, NewThreadScheduler.Default).ObserveOn(new DispatcherScheduler(dispatcher)).Subscribe(x =>
{
okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
}, () =>
{
Assert.IsTrue(okay);
dispatcher.InvokeShutdown();
evt.Set();
});
});
}
[TestMethod]
[Asynchronous]
public void ObserveOn_CurrentDispatcher()
{
var dispatcher = DispatcherHelpers.EnsureDispatcher();
RunAsync(evt =>
{
bool okay = true;
dispatcher.BeginInvoke(new Action(() =>
{
Observable.Range(0, 10, NewThreadScheduler.Default).ObserveOnDispatcher().Subscribe(x =>
{
okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
}, () =>
{
Assert.IsTrue(okay);
dispatcher.InvokeShutdown();
evt.Set();
});
}));
});
}
[TestMethod]
[Asynchronous]
public void ObserveOn_Error()
{
var dispatcher = DispatcherHelpers.EnsureDispatcher();
RunAsync(evt =>
{
var ex = new Exception();
bool okay = true;
dispatcher.BeginInvoke(new Action(() =>
{
Observable.Throw<int>(ex).ObserveOnDispatcher().Subscribe(x =>
{
okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
},
e =>
{
Assert.IsTrue(okay);
Assert.AreSame(ex, e);
dispatcher.InvokeShutdown();
evt.Set();
},
() =>
{
Assert.Fail();
dispatcher.InvokeShutdown();
evt.Set();
});
}));
});
}
#endif
#endregion
#region SubscribeOn
[TestMethod]
public void SubscribeOn_ArgumentChecking()
{
var someObservable = Observable.Empty<int>();
#if HAS_WINFORMS
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn<int>(default(IObservable<int>), new ControlScheduler(new Label())));
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn<int>(someObservable, default(ControlScheduler)));
ReactiveAssert.Throws<ArgumentNullException>(() => ControlObservable.SubscribeOn<int>(default(IObservable<int>), new Label()));
ReactiveAssert.Throws<ArgumentNullException>(() => ControlObservable.SubscribeOn<int>(someObservable, default(Label)));
#endif
#if !NO_WINDOWS_THREADING
#if USE_SL_DISPATCHER
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn<int>(default(IObservable<int>), new DispatcherScheduler(System.Windows.Deployment.Current.Dispatcher)));
#else
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn<int>(default(IObservable<int>), new DispatcherScheduler(Dispatcher.CurrentDispatcher)));
#endif
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn<int>(someObservable, default(DispatcherScheduler)));
#if USE_SL_DISPATCHER
ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherObservable.SubscribeOn<int>(default(IObservable<int>), System.Windows.Deployment.Current.Dispatcher));
#else
ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherObservable.SubscribeOn<int>(default(IObservable<int>), Dispatcher.CurrentDispatcher));
#endif
ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherObservable.SubscribeOn<int>(someObservable, default(Dispatcher)));
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn<int>(default(IObservable<int>), new SynchronizationContext()));
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn<int>(someObservable, default(SynchronizationContext)));
ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherObservable.SubscribeOnDispatcher<int>(default(IObservable<int>)));
#endif
}
#if HAS_WINFORMS
[TestMethod]
public void SubscribeOn_Control()
{
var lbl = CreateLabel();
var evt2 = new ManualResetEvent(false);
var evt = new ManualResetEvent(false);
bool okay = true;
var d = Observable.Create<int>(obs =>
{
lbl.Text = "Subscribe";
okay &= (SynchronizationContext.Current is System.Windows.Forms.WindowsFormsSynchronizationContext);
evt2.Set();
return () =>
{
lbl.Text = "Unsubscribe";
okay &= (SynchronizationContext.Current is System.Windows.Forms.WindowsFormsSynchronizationContext);
evt.Set();
};
})
.SubscribeOn(lbl)
.Subscribe(_ => {});
evt2.WaitOne();
d.Dispose();
evt.WaitOne();
Application.Exit();
Assert.IsTrue(okay);
}
[TestMethod]
public void SubscribeOn_ControlScheduler()
{
var lbl = CreateLabel();
var evt2 = new ManualResetEvent(false);
var evt = new ManualResetEvent(false);
bool okay = true;
var d = Observable.Create<int>(obs =>
{
lbl.Text = "Subscribe";
okay &= (SynchronizationContext.Current is System.Windows.Forms.WindowsFormsSynchronizationContext);
evt2.Set();
return () =>
{
lbl.Text = "Unsubscribe";
okay &= (SynchronizationContext.Current is System.Windows.Forms.WindowsFormsSynchronizationContext);
evt.Set();
};
})
.SubscribeOn(new ControlScheduler(lbl))
.Subscribe(_ => { });
evt2.WaitOne();
d.Dispose();
evt.WaitOne();
Application.Exit();
Assert.IsTrue(okay);
}
#endif
#if !NO_WINDOWS_THREADING
[TestMethod]
[Asynchronous]
public void SubscribeOn_Dispatcher()
{
var dispatcher = DispatcherHelpers.EnsureDispatcher();
RunAsync(evt =>
{
var s = new AsyncSubject<Unit>();
bool okay = true;
var d = Observable.Create<int>(obs =>
{
okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
s.OnNext(Unit.Default);
s.OnCompleted();
return () =>
{
okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
Assert.IsTrue(okay);
dispatcher.InvokeShutdown();
evt.Set();
};
})
.SubscribeOn(dispatcher)
.Subscribe(_ => { });
s.Subscribe(_ => d.Dispose());
});
}
[TestMethod]
[Asynchronous]
public void SubscribeOn_DispatcherScheduler()
{
var dispatcher = DispatcherHelpers.EnsureDispatcher();
RunAsync(evt =>
{
var s = new AsyncSubject<Unit>();
bool okay = true;
var d = Observable.Create<int>(obs =>
{
okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
s.OnNext(Unit.Default);
s.OnCompleted();
return () =>
{
okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
Assert.IsTrue(okay);
dispatcher.InvokeShutdown();
evt.Set();
};
})
.SubscribeOn(new DispatcherScheduler(dispatcher))
.Subscribe(_ => { });
s.Subscribe(_ => d.Dispose());
});
}
[TestMethod]
[Asynchronous]
public void SubscribeOn_CurrentDispatcher()
{
var dispatcher = DispatcherHelpers.EnsureDispatcher();
RunAsync(evt =>
{
var s = new AsyncSubject<Unit>();
bool okay = true;
dispatcher.BeginInvoke(new Action(() =>
{
var d = Observable.Create<int>(obs =>
{
okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
s.OnNext(Unit.Default);
s.OnCompleted();
return () =>
{
okay &= (SynchronizationContext.Current is System.Windows.Threading.DispatcherSynchronizationContext);
Assert.IsTrue(okay);
dispatcher.InvokeShutdown();
evt.Set();
};
})
.SubscribeOnDispatcher()
.Subscribe(_ => { });
s.Subscribe(_ => d.Dispose());
}));
});
}
#endif
#endregion
#region + Synchronize +
[TestMethod]
public void Synchronize_ArgumentChecking()
{
var someObservable = Observable.Empty<int>();
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Synchronize<int>(default(IObservable<int>)));
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Synchronize<int>(default(IObservable<int>), new object()));
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.Synchronize<int>(someObservable, null));
}
[TestMethod]
public void Synchronize_Range()
{
int i = 0;
bool outsideLock = true;
var gate = new object();
lock (gate)
{
outsideLock = false;
Observable.Range(0, 100, NewThreadScheduler.Default).Synchronize(gate).Subscribe(x => i++, () => { Assert.IsTrue(outsideLock); });
Thread.Sleep(100);
Assert.AreEqual(0, i);
outsideLock = true;
}
while (i < 100)
{
Thread.Sleep(10);
lock (gate)
{
int start = i;
Thread.Sleep(100);
Assert.AreEqual(start, i);
}
}
}
[TestMethod]
public void Synchronize_Throw()
{
var ex = new Exception();
var resLock = new object();
var e = default(Exception);
bool outsideLock = true;
var gate = new object();
lock (gate)
{
outsideLock = false;
Observable.Throw<int>(ex, NewThreadScheduler.Default).Synchronize(gate).Subscribe(x => { Assert.Fail(); }, err => { lock (resLock) { e = err; } }, () => { Assert.IsTrue(outsideLock); });
Thread.Sleep(100);
Assert.IsNull(e);
outsideLock = true;
}
while (true)
{
lock (resLock)
{
if (e != null)
break;
}
}
Assert.AreSame(ex, e);
}
[TestMethod]
public void Synchronize_BadObservable()
{
var o = Observable.Create<int>(obs =>
{
var t1 = new Thread(() =>
{
for (int i = 0; i < 100; i++)
{
obs.OnNext(i);
}
});
new Thread(() =>
{
t1.Start();
for (int i = 100; i < 200; i++)
{
obs.OnNext(i);
}
t1.Join();
obs.OnCompleted();
}).Start();
return () => { };
});
var evt = new ManualResetEvent(false);
int sum = 0;
o.Synchronize().Subscribe(x => sum += x, () => { evt.Set(); });
evt.WaitOne();
Assert.AreEqual(Enumerable.Range(0, 200).Sum(), sum);
}
#endregion
}
[TestClass]
public class ObservableConcurrencyReactiveTest : ReactiveTest
{
#region + ObserveOn +
[TestMethod]
public void ObserveOn_Scheduler_ArgumentChecking()
{
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ObserveOn(default(IObservable<int>), DummyScheduler.Instance));
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.ObserveOn(DummyObservable<int>.Instance, default(IScheduler)));
}
[TestMethod]
public void ObserveOn_Scheduler_Completed()
{
var scheduler = new TestScheduler();
var xs = scheduler.CreateHotObservable(
OnNext( 90, 1),
OnNext(120, 2),
OnNext(230, 3),
OnNext(240, 4),
OnNext(310, 5),
OnNext(470, 6),
OnCompleted<int>(530)
);
var results = scheduler.Start(() =>
xs.ObserveOn(scheduler)
);
results.Messages.AssertEqual(
OnNext(231, 3),
OnNext(241, 4),
OnNext(311, 5),
OnNext(471, 6),
OnCompleted<int>(531)
);
#if !NO_PERF && !NO_CDS
// BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
xs.Subscriptions.AssertEqual(
Subscribe(200, 531)
);
#else
//
// TODO: Check platform discrepancies
//
//xs.Subscriptions.AssertEqual(
// Subscribe(200, 1000)
//);
#endif
}
[TestMethod]
public void ObserveOn_Scheduler_Error()
{
var scheduler = new TestScheduler();
var ex = new Exception();
var xs = scheduler.CreateHotObservable(
OnNext(90, 1),
OnNext(120, 2),
OnNext(230, 3),
OnNext(240, 4),
OnNext(310, 5),
OnNext(470, 6),
OnError<int>(530, ex)
);
var results = scheduler.Start(() =>
xs.ObserveOn(scheduler)
);
results.Messages.AssertEqual(
OnNext(231, 3),
OnNext(241, 4),
OnNext(311, 5),
OnNext(471, 6),
OnError<int>(531, ex)
);
#if !NO_PERF && !NO_CDS
// BREAKING CHANGE v2 > v1.x -> More aggressive disposal behavior
xs.Subscriptions.AssertEqual(
Subscribe(200, 531)
);
#else
//
// TODO: Check platform discrepancies
//
//xs.Subscriptions.AssertEqual(
// Subscribe(200, 1000)
//);
#endif
}
[TestMethod]
public void ObserveOn_Scheduler_Dispose()
{
var scheduler = new TestScheduler();
var xs = scheduler.CreateHotObservable(
OnNext(90, 1),
OnNext(120, 2),
OnNext(230, 3),
OnNext(240, 4),
OnNext(310, 5),
OnNext(470, 6)
);
var results = scheduler.Start(() =>
xs.ObserveOn(scheduler)
);
results.Messages.AssertEqual(
OnNext(231, 3),
OnNext(241, 4),
OnNext(311, 5),
OnNext(471, 6)
);
xs.Subscriptions.AssertEqual(
Subscribe(200, 1000)
);
}
[TestMethod]
public void ObserveOn_Scheduler_SameTime()
{
var scheduler = new TestScheduler();
var xs = scheduler.CreateHotObservable(
OnNext(210, 1),
OnNext(210, 2)
);
var results = scheduler.Start(() =>
xs.ObserveOn(scheduler)
);
results.Messages.AssertEqual(
OnNext(211, 1),
OnNext(212, 2)
);
xs.Subscriptions.AssertEqual(
Subscribe(200, 1000)
);
}
[TestMethod]
public void ObserveOn_Scheduler_OnNextThrows()
{
var e = new ManualResetEvent(false);
var scheduler = new MyScheduler(e);
Observable.Range(0, 10, Scheduler.Default).ObserveOn(scheduler).Subscribe(
x =>
{
if (x == 5)
throw new Exception();
}
);
e.WaitOne();
Assert.IsNotNull(scheduler._exception);
}
class MyScheduler : IScheduler
{
internal Exception _exception;
private ManualResetEvent _evt;
public MyScheduler(ManualResetEvent e)
{
_evt = e;
}
public DateTimeOffset Now
{
get { throw new NotImplementedException(); }
}
public IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
{
try
{
return action(this, state);
}
catch (Exception ex)
{
_exception = ex;
_evt.Set();
return Disposable.Empty;
}
}
public IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
{
throw new NotImplementedException();
}
public IDisposable Schedule<TState>(TState state, DateTimeOffset dueTime, Func<IScheduler, TState, IDisposable> action)
{
throw new NotImplementedException();
}
}
#if !NO_PERF && !NO_CDS
[TestMethod]
public void ObserveOn_LongRunning_Simple()
{
var started = default(ManualResetEvent);
var stopped = default(ManualResetEvent);
var scheduler = new TestLongRunningScheduler(e => started = e, e => stopped = e);
var s = new Subject<int>();
var end = new ManualResetEvent(false);
var lst = new List<int>();
s.ObserveOn(scheduler).Subscribe(lst.Add, () => end.Set());
s.OnNext(1);
s.OnNext(2);
s.OnNext(3);
s.OnCompleted();
end.WaitOne();
Assert.IsTrue(lst.SequenceEqual(new[] { 1, 2, 3 }));
}
[TestMethod]
public void ObserveOn_LongRunning_Error()
{
var started = default(ManualResetEvent);
var stopped = default(ManualResetEvent);
var scheduler = new TestLongRunningScheduler(e => started = e, e => stopped = e);
var s = new Subject<int>();
var end = new ManualResetEvent(false);
var err = default(Exception);
s.ObserveOn(scheduler).Subscribe(_ => { }, ex => { err = ex; end.Set(); });
s.OnNext(1);
s.OnNext(2);
s.OnNext(3);
var ex_ = new Exception();
s.OnError(ex_);
end.WaitOne();
Assert.AreSame(ex_, err);
}
[TestMethod]
public void ObserveOn_LongRunning_TimeVariance()
{
var started = default(ManualResetEvent);
var stopped = default(ManualResetEvent);
var scheduler = new TestLongRunningScheduler(e => started = e, e => stopped = e);
var s = new Subject<int>();
var end = new ManualResetEvent(false);
s.ObserveOn(scheduler).Subscribe(_ => { }, () => end.Set());
s.OnNext(1); // Ensure active
started.WaitOne();
Thread.Sleep(100); // Try to enter the dispatcher event wait state
for (int i = 0; i < 1000; i++)
{
if (i % 100 == 0)
Thread.Sleep(10);
s.OnNext(i);
}
s.OnCompleted();
end.WaitOne();
}
[TestMethod]
public void ObserveOn_LongRunning_HoldUpDuringDispatchAndFail()
{
var started = default(ManualResetEvent);
var stopped = default(ManualResetEvent);
var scheduler = new TestLongRunningScheduler(e => started = e, e => stopped = e);
var s = new Subject<int>();
var onNext = new ManualResetEvent(false);
var resume = new ManualResetEvent(false);
var lst = new List<int>();
var err = default(Exception);
var end = new ManualResetEvent(false);
s.ObserveOn(scheduler).Subscribe(x => { lst.Add(x); onNext.Set(); resume.WaitOne(); }, ex_ => { err = ex_; end.Set(); });
s.OnNext(1);
onNext.WaitOne();
s.OnNext(2);
s.OnNext(3);
var ex = new Exception();
s.OnError(ex);
resume.Set();
end.WaitOne();
Assert.IsTrue(lst.SequenceEqual(new[] { 1, 2, 3 }));
Assert.AreSame(ex, err);
}
[TestMethod]
public void ObserveOn_LongRunning_Cancel()
{
var started = default(ManualResetEvent);
var stopped = default(ManualResetEvent);
var scheduler = new TestLongRunningScheduler(e => started = e, e => stopped = e);
var s = new Subject<int>();
var lst = new List<int>();
var end = new ManualResetEvent(false);
var running = new ManualResetEvent(false);
var d = s.ObserveOn(scheduler).Subscribe(x => { lst.Add(x); running.Set(); });
s.OnNext(0);
started.WaitOne();
s.OnNext(1);
s.OnNext(2);
s.OnNext(3);
running.WaitOne();
d.Dispose();
stopped.WaitOne();
s.OnNext(4);
Assert.IsTrue(lst.Count > 0 && !lst.Contains(4));
}
[TestMethod]
public void ObserveOn_LongRunning_OnNextThrows()
{
var started = default(ManualResetEvent);
var stopped = default(ManualResetEvent);
var exception = default(Exception);
var scheduler = new TestLongRunningScheduler(e => started = e, e => stopped = e, ex => exception = ex);
var s = new Subject<int>();
var lst = new List<int>();
var end = new ManualResetEvent(false);
var running = new ManualResetEvent(false);
var d = s.ObserveOn(scheduler).Subscribe(x => { lst.Add(x); running.Set(); if (x == 3) throw new Exception(); });
s.OnNext(0);
started.WaitOne();
s.OnNext(1);
s.OnNext(2);
s.OnNext(3);
running.WaitOne();
s.OnNext(4);
stopped.WaitOne();
Assert.IsNotNull(exception);
}
#endif
#if !NO_SYNCCTX
[TestMethod]
public void ObserveOn_SynchronizationContext_Simple()
{
var scheduler = new TestScheduler();
var xs = scheduler.CreateHotObservable(
OnNext(90, 1),
OnNext(120, 2),
OnNext(230, 3),
OnNext(240, 4),
OnNext(310, 5),
OnNext(470, 6),
OnCompleted<int>(530)
);
var results = scheduler.Start(() =>
xs.ObserveOn(new MyCtx(scheduler))
);
results.Messages.AssertEqual(
OnNext(231, 3),
OnNext(241, 4),
OnNext(311, 5),
OnNext(471, 6),
OnCompleted<int>(531)
);
xs.Subscriptions.AssertEqual(
Subscribe(200, 531)
);
}
#endif
#endregion
#region SubscribeOn
[TestMethod]
public void SubscribeOn_Scheduler_ArgumentChecking()
{
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn(default(IObservable<int>), DummyScheduler.Instance));
ReactiveAssert.Throws<ArgumentNullException>(() => Observable.SubscribeOn(DummyObservable<int>.Instance, default(IScheduler)));
}
[TestMethod]
public void SubscribeOn_Scheduler_Sleep()
{
var scheduler = new TestScheduler();
var s = 0L;
var d = 0L;
var xs = Observable.Create<long>(observer =>
{
s = scheduler.Clock;
return () => d = scheduler.Clock;
});
var results = scheduler.Start(() =>
xs.SubscribeOn(scheduler)
);
results.Messages.AssertEqual(
);
Assert.AreEqual(201, s);
Assert.AreEqual(1001, d);
}
[TestMethod]
public void SubscribeOn_Scheduler_Completed()
{
var scheduler = new TestScheduler();
var xs = scheduler.CreateHotObservable(
OnCompleted<long>(300)
);
var results = scheduler.Start(() =>
xs.SubscribeOn(scheduler)
);
results.Messages.AssertEqual(
OnCompleted<long>(300)
);
xs.Subscriptions.AssertEqual(
Subscribe(201, 301)
);
}
[TestMethod]
public void SubscribeOn_Scheduler_Error()
{
var scheduler = new TestScheduler();
var ex = new Exception();
var xs = scheduler.CreateHotObservable(
OnError<int>(300, ex)
);
var results = scheduler.Start(() =>
xs.SubscribeOn(scheduler)
);
results.Messages.AssertEqual(
OnError<int>(300, ex)
);
xs.Subscriptions.AssertEqual(
Subscribe(201, 301)
);
}
[TestMethod]
public void SubscribeOn_Scheduler_Dispose()
{
var scheduler = new TestScheduler();
var xs = scheduler.CreateHotObservable<int>(
);
var results = scheduler.Start(() =>
xs.SubscribeOn(scheduler)
);
results.Messages.AssertEqual(
);
xs.Subscriptions.AssertEqual(
Subscribe(201, 1001)
);
}
#if !NO_SYNCCTX
[TestMethod]
public void SubscribeOn_SynchronizationContext_Simple()
{
var scheduler = new TestScheduler();
var xs = scheduler.CreateHotObservable(
OnNext(90, 1),
OnNext(120, 2),
OnNext(230, 3),
OnNext(240, 4),
OnNext(310, 5),
OnNext(470, 6),
OnCompleted<int>(530)
);
var results = scheduler.Start(() =>
xs.SubscribeOn(new MyCtx(scheduler))
);
results.Messages.AssertEqual(
OnNext(230, 3),
OnNext(240, 4),
OnNext(310, 5),
OnNext(470, 6),
OnCompleted<int>(530)
);
xs.Subscriptions.AssertEqual(
Subscribe(201, 531)
);
}
#endif
#endregion
#region |> Helpers <|
#if !NO_SYNCCTX
class MyCtx : SynchronizationContext
{
private IScheduler scheduler;
public MyCtx(IScheduler scheduler)
{
this.scheduler = scheduler;
}
public override void Post(SendOrPostCallback d, object state)
{
scheduler.Schedule(state, (self, s) => { d(s); return Disposable.Empty; });
}
}
#endif
#endregion
}
}