// 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.ComponentModel; using System.Diagnostics; using System.Linq; using System.Reactive; using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reflection; using System.Threading; 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 !NO_TPL using System.Threading.Tasks; #endif namespace ReactiveTests.Tests { [TestClass] public partial class ObservableTest : ReactiveTest { #region - Create - [TestMethod] public void Create_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Create(default(Func, Action>))); // // BREAKING CHANGE v2.0 > v1.x - Returning null from Subscribe means "nothing to do upon unsubscription" // all null-coalesces to Disposable.Empty. // //ReactiveAssert.Throws(() => Observable.Create(o => default(Action)).Subscribe(DummyObserver.Instance)); ReactiveAssert.Throws(() => Observable.Create(o => () => { }).Subscribe(null)); ReactiveAssert.Throws(() => Observable.Create(o => { o.OnError(null); return () => { }; }).Subscribe(null)); } [TestMethod] public void Create_NullCoalescingAction() { var xs = Observable.Create(o => { o.OnNext(42); return default(Action); }); var lst = new List(); var d = xs.Subscribe(lst.Add); d.Dispose(); Assert.IsTrue(lst.SequenceEqual(new[] {42})); } [TestMethod] public void Create_Next() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Create(o => { o.OnNext(1); o.OnNext(2); return () => { }; }) ); res.Messages.AssertEqual( OnNext(200, 1), OnNext(200, 2) ); } [TestMethod] public void Create_Completed() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Create(o => { o.OnCompleted(); o.OnNext(100); o.OnError(new Exception()); o.OnCompleted(); return () => { }; }) ); res.Messages.AssertEqual( OnCompleted(200) ); } [TestMethod] public void Create_Error() { var scheduler = new TestScheduler(); var ex = new Exception(); var res = scheduler.Start(() => Observable.Create(o => { o.OnError(ex); o.OnNext(100); o.OnError(new Exception()); o.OnCompleted(); return () => { }; }) ); res.Messages.AssertEqual( OnError(200, ex) ); } [TestMethod] public void Create_Exception() { ReactiveAssert.Throws(() => Observable.Create(new Func, Action>(o => { throw new InvalidOperationException(); })).Subscribe()); } [TestMethod] public void Create_Dispose() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Create(o => { var stopped = false; o.OnNext(1); o.OnNext(2); scheduler.Schedule(TimeSpan.FromTicks(600), () => { if (!stopped) o.OnNext(3); }); scheduler.Schedule(TimeSpan.FromTicks(700), () => { if (!stopped) o.OnNext(4); }); scheduler.Schedule(TimeSpan.FromTicks(900), () => { if (!stopped) o.OnNext(5); }); scheduler.Schedule(TimeSpan.FromTicks(1100), () => { if (!stopped) o.OnNext(6); }); return () => { stopped = true; }; }) ); res.Messages.AssertEqual( OnNext(200, 1), OnNext(200, 2), OnNext(800, 3), OnNext(900, 4) ); } [TestMethod] public void Create_ObserverThrows() { ReactiveAssert.Throws(() => Observable.Create(o => { o.OnNext(1); return () => { }; }).Subscribe(x => { throw new InvalidOperationException(); })); ReactiveAssert.Throws(() => Observable.Create(o => { o.OnError(new Exception()); return () => { }; }).Subscribe(x => { }, ex => { throw new InvalidOperationException(); })); ReactiveAssert.Throws(() => Observable.Create(o => { o.OnCompleted(); return () => { }; }).Subscribe(x => { }, ex => { }, () => { throw new InvalidOperationException(); })); } [TestMethod] public void CreateWithDisposable_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Create(default(Func, IDisposable>))); ReactiveAssert.Throws(() => Observable.Create(o => DummyDisposable.Instance).Subscribe(null)); ReactiveAssert.Throws(() => Observable.Create(o => { o.OnError(null); return DummyDisposable.Instance; }).Subscribe(null)); } [TestMethod] public void CreateWithDisposable_NullCoalescingAction() { var xs = Observable.Create(o => { o.OnNext(42); return default(IDisposable); }); var lst = new List(); var d = xs.Subscribe(lst.Add); d.Dispose(); Assert.IsTrue(lst.SequenceEqual(new[] { 42 })); } [TestMethod] public void CreateWithDisposable_Next() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Create(o => { o.OnNext(1); o.OnNext(2); return Disposable.Empty; }) ); res.Messages.AssertEqual( OnNext(200, 1), OnNext(200, 2) ); } [TestMethod] public void CreateWithDisposable_Completed() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Create(o => { o.OnCompleted(); o.OnNext(100); o.OnError(new Exception()); o.OnCompleted(); return Disposable.Empty; }) ); res.Messages.AssertEqual( OnCompleted(200) ); } [TestMethod] public void CreateWithDisposable_Error() { var scheduler = new TestScheduler(); var ex = new Exception(); var res = scheduler.Start(() => Observable.Create(o => { o.OnError(ex); o.OnNext(100); o.OnError(new Exception()); o.OnCompleted(); return Disposable.Empty; }) ); res.Messages.AssertEqual( OnError(200, ex) ); } [TestMethod] public void CreateWithDisposable_Exception() { ReactiveAssert.Throws(() => Observable.Create(new Func, IDisposable>(o => { throw new InvalidOperationException(); })).Subscribe()); } [TestMethod] public void CreateWithDisposable_Dispose() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Create(o => { var d = new BooleanDisposable(); o.OnNext(1); o.OnNext(2); scheduler.Schedule(TimeSpan.FromTicks(600), () => { if (!d.IsDisposed) o.OnNext(3); }); scheduler.Schedule(TimeSpan.FromTicks(700), () => { if (!d.IsDisposed) o.OnNext(4); }); scheduler.Schedule(TimeSpan.FromTicks(900), () => { if (!d.IsDisposed) o.OnNext(5); }); scheduler.Schedule(TimeSpan.FromTicks(1100), () => { if (!d.IsDisposed) o.OnNext(6); }); return d; }) ); res.Messages.AssertEqual( OnNext(200, 1), OnNext(200, 2), OnNext(800, 3), OnNext(900, 4) ); } [TestMethod] public void CreateWithDisposable_ObserverThrows() { ReactiveAssert.Throws(() => Observable.Create(o => { o.OnNext(1); return Disposable.Empty; }).Subscribe(x => { throw new InvalidOperationException(); })); ReactiveAssert.Throws(() => Observable.Create(o => { o.OnError(new Exception()); return Disposable.Empty; }).Subscribe(x => { }, ex => { throw new InvalidOperationException(); })); ReactiveAssert.Throws(() => Observable.Create(o => { o.OnCompleted(); return Disposable.Empty; }).Subscribe(x => { }, ex => { }, () => { throw new InvalidOperationException(); })); } #endregion #region - CreateAsync - #if !NO_TPL [TestMethod] public void CreateAsync_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Create(default(Func, Task>))); ReactiveAssert.Throws(() => Observable.Create(default(Func, CancellationToken, Task>))); ReactiveAssert.Throws(() => Observable.Create(default(Func, Task>))); ReactiveAssert.Throws(() => Observable.Create(default(Func, CancellationToken, Task>))); ReactiveAssert.Throws(() => Observable.Create(default(Func, Task>))); ReactiveAssert.Throws(() => Observable.Create(default(Func, CancellationToken, Task>))); } [TestMethod] public void CreateAsync_NullCoalescingAction1() { var xs = Observable.Create(o => { o.OnNext(42); return Task.Factory.StartNew(() => default(Action)); }); var lst = new List(); var d = xs.Subscribe(lst.Add); d.Dispose(); Assert.IsTrue(lst.SequenceEqual(new[] { 42 })); } [TestMethod] public void CreateAsync_NullCoalescingAction2() { var xs = Observable.Create((o, ct) => { o.OnNext(42); return Task.Factory.StartNew(() => default(Action)); }); var lst = new List(); var d = xs.Subscribe(lst.Add); d.Dispose(); Assert.IsTrue(lst.SequenceEqual(new[] { 42 })); } [TestMethod] public void CreateAsync_NullCoalescingDisposable1() { var xs = Observable.Create(o => { o.OnNext(42); return Task.Factory.StartNew(() => default(IDisposable)); }); var lst = new List(); var d = xs.Subscribe(lst.Add); d.Dispose(); Assert.IsTrue(lst.SequenceEqual(new[] { 42 })); } [TestMethod] public void CreateAsync_NullCoalescingDisposable2() { var xs = Observable.Create((o, ct) => { o.OnNext(42); return Task.Factory.StartNew(() => default(IDisposable)); }); var lst = new List(); var d = xs.Subscribe(lst.Add); d.Dispose(); Assert.IsTrue(lst.SequenceEqual(new[] { 42 })); } Task Producer1(IObserver results, CancellationToken token, IScheduler scheduler) { var tcs = new TaskCompletionSource(); var x = 0; var d = scheduler.Schedule(TimeSpan.FromTicks(100), self => { results.OnNext(++x); self(TimeSpan.FromTicks(100)); }); token.Register(d.Dispose); return tcs.Task; } [TestMethod] public void CreateAsync_Never() { RunSynchronously(() => { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Create((observer, token) => Producer1(observer, token, scheduler)) ); res.Messages.AssertEqual( OnNext(300, 1), OnNext(400, 2), OnNext(500, 3), OnNext(600, 4), OnNext(700, 5), OnNext(800, 6), OnNext(900, 7) ); }); } Task Producer2(IObserver results, CancellationToken token, IScheduler scheduler) { var tcs = new TaskCompletionSource(); var x = 0; var d = scheduler.Schedule(TimeSpan.FromTicks(100), self => { if (x == 4) { tcs.SetResult(null); } results.OnNext(++x); self(TimeSpan.FromTicks(100)); }); token.Register(d.Dispose); return tcs.Task; } [TestMethod] public void CreateAsync_Completed1() { RunSynchronously(() => { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Create((observer, token) => Producer2(observer, token, scheduler)) ); res.Messages.AssertEqual( OnNext(300, 1), OnNext(400, 2), OnNext(500, 3), OnNext(600, 4), OnCompleted(700) ); }); } Task Producer3(IObserver results, CancellationToken token, IScheduler scheduler) { var tcs = new TaskCompletionSource(); var x = 0; var d = scheduler.Schedule(TimeSpan.FromTicks(100), self => { if (x == 4) { results.OnCompleted(); } results.OnNext(++x); self(TimeSpan.FromTicks(100)); }); token.Register(d.Dispose); return tcs.Task; } [TestMethod] public void CreateAsync_Completed2() { RunSynchronously(() => { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Create((observer, token) => Producer3(observer, token, scheduler)) ); res.Messages.AssertEqual( OnNext(300, 1), OnNext(400, 2), OnNext(500, 3), OnNext(600, 4), OnCompleted(700) ); }); } Task Producer4(IObserver results, CancellationToken token, IScheduler scheduler, Exception exception) { var tcs = new TaskCompletionSource(); var x = 0; var d = scheduler.Schedule(TimeSpan.FromTicks(100), self => { if (x == 4) { results.OnError(exception); } results.OnNext(++x); self(TimeSpan.FromTicks(100)); }); token.Register(d.Dispose); return tcs.Task; } [TestMethod] public void CreateAsync_Error1() { RunSynchronously(() => { var scheduler = new TestScheduler(); var exception = new Exception(); var res = scheduler.Start(() => Observable.Create((observer, token) => Producer4(observer, token, scheduler, exception)) ); res.Messages.AssertEqual( OnNext(300, 1), OnNext(400, 2), OnNext(500, 3), OnNext(600, 4), OnError(700, exception) ); }); } Task Producer5(IObserver results, CancellationToken token, IScheduler scheduler, Exception exception) { var tcs = new TaskCompletionSource(); var x = 0; var d = scheduler.Schedule(TimeSpan.FromTicks(100), self => { if (x == 4) { tcs.SetException(exception); } results.OnNext(++x); self(TimeSpan.FromTicks(100)); }); token.Register(d.Dispose); return tcs.Task; } [TestMethod] public void CreateAsync_Error2() { RunSynchronously(() => { var scheduler = new TestScheduler(); var exception = new Exception(); var res = scheduler.Start(() => Observable.Create((observer, token) => Producer5(observer, token, scheduler, exception)) ); res.Messages.AssertEqual( OnNext(300, 1), OnNext(400, 2), OnNext(500, 3), OnNext(600, 4), OnError(700, exception) ); }); } Task Producer6(IObserver results, CancellationToken token, Exception exception) { throw exception; } [TestMethod] public void CreateAsync_Error3() { RunSynchronously(() => { var scheduler = new TestScheduler(); var exception = new InvalidOperationException(); var res = scheduler.Start(() => Observable.Create((observer, token) => Producer6(observer, token, exception)) ); res.Messages.AssertEqual( OnError(200, exception) ); }); } Task Producer7(IObserver results, CancellationToken token, IScheduler scheduler) { var tcs = new TaskCompletionSource(); var x = 0; var d = scheduler.Schedule(TimeSpan.FromTicks(100), self => { if (x == 4) { tcs.SetResult(null); } results.OnNext(++x); self(TimeSpan.FromTicks(100)); }); token.Register(d.Dispose); return tcs.Task; } [TestMethod] public void CreateAsync_Cancel1() { RunSynchronously(() => { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Create((observer, token) => Producer7(observer, token, scheduler)), 650 ); res.Messages.AssertEqual( OnNext(300, 1), OnNext(400, 2), OnNext(500, 3), OnNext(600, 4) ); }); } Task Producer8(IObserver results, CancellationToken token, IScheduler scheduler) { var tcs = new TaskCompletionSource(); var x = 0; var d = scheduler.Schedule(TimeSpan.FromTicks(100), self => { if (x == 4) { results.OnCompleted(); } results.OnNext(++x); self(TimeSpan.FromTicks(100)); }); token.Register(d.Dispose); return tcs.Task; } [TestMethod] public void CreateAsync_Cancel2() { RunSynchronously(() => { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Create((observer, token) => Producer8(observer, token, scheduler)), 650 ); res.Messages.AssertEqual( OnNext(300, 1), OnNext(400, 2), OnNext(500, 3), OnNext(600, 4) ); }); } Task Producer9(IObserver results, CancellationToken token, IScheduler scheduler) { var tcs = new TaskCompletionSource(); var x = 0; var d = scheduler.Schedule(TimeSpan.FromTicks(100), self => { if (x == 4) { results.OnCompleted(); } results.OnNext(++x); self(TimeSpan.FromTicks(100)); }); token.Register(d.Dispose); return tcs.Task; } [TestMethod] public void CreateAsync_Cancel3() { RunSynchronously(() => { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Create((observer, token) => Producer9(observer, token, scheduler)), 750 ); res.Messages.AssertEqual( OnNext(300, 1), OnNext(400, 2), OnNext(500, 3), OnNext(600, 4), OnCompleted(700) ); }); } Task Producer10(IObserver results, CancellationToken token, IScheduler scheduler) { var tcs = new TaskCompletionSource(); var x = 0; var d = scheduler.Schedule(TimeSpan.FromTicks(100), self => { if (x == 4) { tcs.SetCanceled(); } results.OnNext(++x); self(TimeSpan.FromTicks(100)); }); token.Register(d.Dispose); return tcs.Task; } [TestMethod] public void CreateAsync_Cancel4() { RunSynchronously(() => { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Create((observer, token) => Producer10(observer, token, scheduler)) ); res.Messages.Take(4).AssertEqual( OnNext(300, 1), OnNext(400, 2), OnNext(500, 3), OnNext(600, 4) ); Assert.AreEqual(5, res.Messages.Count); Assert.AreEqual(700, res.Messages[4].Time); Assert.AreEqual(NotificationKind.OnError, res.Messages[4].Value.Kind); Assert.IsTrue(res.Messages[4].Value.Exception is OperationCanceledException); }); } void RunSynchronously(Action action) { var t = new Task(action); t.RunSynchronously(new SynchronousScheduler()); t.Wait(); } class SynchronousScheduler : TaskScheduler { protected override IEnumerable GetScheduledTasks() { throw new NotImplementedException(); } protected override void QueueTask(Task task) { TryExecuteTask(task); } protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { return TryExecuteTask(task); } } [TestMethod] public void CreateAsync_Task_Simple() { var xs = Observable.Create(observer => { return Task.Factory.StartNew(() => { observer.OnNext(42); observer.OnCompleted(); }); }); var lst = new List(); xs.ForEach(lst.Add); Assert.IsTrue(new[] { 42 }.SequenceEqual(lst)); } [TestMethod] public void CreateAsync_Task_Token() { var e = new ManualResetEvent(false); var xs = Observable.Create((observer, ct) => { return Task.Factory.StartNew(() => { var i = 0; while (!ct.IsCancellationRequested) { if (i++ == 10) e.Set(); observer.OnNext(42); } }); }); var lst = new List(); var d = xs.Subscribe(lst.Add); e.WaitOne(); d.Dispose(); Assert.IsTrue(lst.Take(10).SequenceEqual(Enumerable.Repeat(42, 10))); } [TestMethod] public void CreateAsync_IDisposable_Simple() { var stopped = new ManualResetEvent(false); var s = Disposable.Create(() => stopped.Set()); var xs = Observable.Create(observer => { return Task.Factory.StartNew(() => { observer.OnNext(42); observer.OnCompleted(); return s; }); }); var lst = new List(); xs.ForEach(lst.Add); stopped.WaitOne(); Assert.IsTrue(new[] { 42 }.SequenceEqual(lst)); } [TestMethod] public void CreateAsync_IDisposable_Token() { var stopped = new ManualResetEvent(false); var s = Disposable.Create(() => stopped.Set()); var e = new ManualResetEvent(false); var xs = Observable.Create((observer, ct) => { return Task.Factory.StartNew(() => { var i = 0; while (!ct.IsCancellationRequested) { if (i++ == 10) e.Set(); observer.OnNext(42); } return s; }); }); var lst = new List(); var d = xs.Subscribe(lst.Add); e.WaitOne(); d.Dispose(); stopped.WaitOne(); Assert.IsTrue(lst.Take(10).SequenceEqual(Enumerable.Repeat(42, 10))); } [TestMethod] public void CreateAsync_Action_Simple() { var stopped = new ManualResetEvent(false); var s = new Action(() => stopped.Set()); var xs = Observable.Create(observer => { return Task.Factory.StartNew(() => { observer.OnNext(42); observer.OnCompleted(); return s; }); }); var lst = new List(); xs.ForEach(lst.Add); stopped.WaitOne(); Assert.IsTrue(new[] { 42 }.SequenceEqual(lst)); } [TestMethod] public void CreateAsync_Action_Token() { var stopped = new ManualResetEvent(false); var s = new Action(() => stopped.Set()); var e = new ManualResetEvent(false); var xs = Observable.Create((observer, ct) => { return Task.Factory.StartNew(() => { var i = 0; while (!ct.IsCancellationRequested) { if (i++ == 10) e.Set(); observer.OnNext(42); } return s; }); }); var lst = new List(); var d = xs.Subscribe(lst.Add); e.WaitOne(); d.Dispose(); stopped.WaitOne(); Assert.IsTrue(lst.Take(10).SequenceEqual(Enumerable.Repeat(42, 10))); } #endif #endregion #region + Defer + [TestMethod] public void Defer_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Defer(default(Func>))); ReactiveAssert.Throws(() => Observable.Defer(() => DummyObservable.Instance).Subscribe(null)); ReactiveAssert.Throws(() => Observable.Defer(() => default(IObservable)).Subscribe()); } [TestMethod] public void Defer_Complete() { var scheduler = new TestScheduler(); var invoked = 0; var xs = default(ITestableObservable); var res = scheduler.Start(() => Observable.Defer(() => { invoked++; xs = scheduler.CreateColdObservable( OnNext(100, scheduler.Clock), OnCompleted(200)); return xs; }) ); res.Messages.AssertEqual( OnNext(300, 200L), OnCompleted(400) ); Assert.AreEqual(1, invoked); xs.Subscriptions.AssertEqual( Subscribe(200, 400) ); } [TestMethod] public void Defer_Error() { var scheduler = new TestScheduler(); var invoked = 0; var xs = default(ITestableObservable); var ex = new Exception(); var res = scheduler.Start(() => Observable.Defer(() => { invoked++; xs = scheduler.CreateColdObservable( OnNext(100, scheduler.Clock), OnError(200, ex)); return xs; }) ); res.Messages.AssertEqual( OnNext(300, 200L), OnError(400, ex) ); Assert.AreEqual(1, invoked); xs.Subscriptions.AssertEqual( Subscribe(200, 400) ); } [TestMethod] public void Defer_Dispose() { var scheduler = new TestScheduler(); var invoked = 0; var xs = default(ITestableObservable); var res = scheduler.Start(() => Observable.Defer(() => { invoked++; xs = scheduler.CreateColdObservable( OnNext(100, scheduler.Clock), OnNext(200, invoked), OnNext(1100, 1000)); return xs; }) ); res.Messages.AssertEqual( OnNext(300, 200L), OnNext(400, 1L) ); Assert.AreEqual(1, invoked); xs.Subscriptions.AssertEqual( Subscribe(200, 1000) ); } [TestMethod] public void Defer_Throw() { var scheduler = new TestScheduler(); var invoked = 0; var ex = new Exception(); var res = scheduler.Start(() => Observable.Defer(new Func>(() => { invoked++; throw ex; })) ); res.Messages.AssertEqual( OnError(200, ex) ); Assert.AreEqual(1, invoked); } #endregion #region - DeferAsync - #if !NO_TPL [TestMethod] public void DeferAsync_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Defer(default(Func>>))); ReactiveAssert.Throws(() => Observable.DeferAsync(default(Func>>))); } [TestMethod] public void DeferAsync_Simple() { var xs = Observable.Defer(() => Task.Factory.StartNew(() => Observable.Return(42))); var res = xs.ToEnumerable().ToList(); Assert.IsTrue(new[] { 42 }.SequenceEqual(res)); } [TestMethod] public void DeferAsync_WithCancel_Simple() { var xs = Observable.DeferAsync(ct => Task.Factory.StartNew(() => Observable.Return(42))); var res = xs.ToEnumerable().ToList(); Assert.IsTrue(new[] { 42 }.SequenceEqual(res)); } [TestMethod] public void DeferAsync_WithCancel_Cancel() { var N = 10;// 0000; for (int i = 0; i < N; i++) { var e = new ManualResetEvent(false); var called = false; var xs = Observable.DeferAsync(ct => Task.Factory.StartNew(() => { e.Set(); while (!ct.IsCancellationRequested) ; return Observable.Defer(() => { called = true; return Observable.Return(42); }); })); var d = xs.Subscribe(_ => { }); e.WaitOne(); d.Dispose(); Assert.IsFalse(called); } } #endif #endregion #region + Empty + [TestMethod] public void Empty_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Empty(null)); ReactiveAssert.Throws(() => Observable.Empty(null, 42)); ReactiveAssert.Throws(() => Observable.Empty(DummyScheduler.Instance).Subscribe(null)); } [TestMethod] public void Empty_Basic() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Empty(scheduler) ); res.Messages.AssertEqual( OnCompleted(201) ); } [TestMethod] public void Empty_Disposed() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Empty(scheduler), 200 ); res.Messages.AssertEqual( ); } [TestMethod] public void Empty_ObserverThrows() { var scheduler1 = new TestScheduler(); var xs = Observable.Empty(scheduler1); xs.Subscribe(x => { }, exception => { }, () => { throw new InvalidOperationException(); }); ReactiveAssert.Throws(() => scheduler1.Start()); } [TestMethod] public void Empty_DefaultScheduler() { Observable.Empty().AssertEqual(Observable.Empty(DefaultScheduler.Instance)); } [TestMethod] public void Empty_Basic_Witness() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Empty(scheduler, 42) ); res.Messages.AssertEqual( OnCompleted(201) ); } #endregion #region + Generate + [TestMethod] public void Generate_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Generate(0, DummyFunc.Instance, DummyFunc.Instance, DummyFunc.Instance, (IScheduler)null)); ReactiveAssert.Throws(() => Observable.Generate(0, (Func)null, DummyFunc.Instance, DummyFunc.Instance, DummyScheduler.Instance)); ReactiveAssert.Throws(() => Observable.Generate(0, DummyFunc.Instance, DummyFunc.Instance, (Func)null, DummyScheduler.Instance)); ReactiveAssert.Throws(() => Observable.Generate(0, DummyFunc.Instance, (Func)null, DummyFunc.Instance, DummyScheduler.Instance)); ReactiveAssert.Throws(() => Observable.Generate(0, DummyFunc.Instance, DummyFunc.Instance, DummyFunc.Instance, DummyScheduler.Instance).Subscribe(null)); } [TestMethod] public void Generate_Finite() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Generate(0, x => x <= 3, x => x + 1, x => x, scheduler) ); res.Messages.AssertEqual( OnNext(201, 0), OnNext(202, 1), OnNext(203, 2), OnNext(204, 3), OnCompleted(205) ); } [TestMethod] public void Generate_Throw_Condition() { var scheduler = new TestScheduler(); var ex = new Exception(); var res = scheduler.Start(() => Observable.Generate(0, new Func(x => { throw ex; }), x => x + 1, x => x, scheduler) ); res.Messages.AssertEqual( OnError(201, ex) ); } [TestMethod] public void Generate_Throw_ResultSelector() { var scheduler = new TestScheduler(); var ex = new Exception(); var res = scheduler.Start(() => Observable.Generate(0, x => true, x => x + 1, new Func(x => { throw ex; }), scheduler) ); res.Messages.AssertEqual( OnError(201, ex) ); } [TestMethod] public void Generate_Throw_Iterate() { var scheduler = new TestScheduler(); var ex = new Exception(); var res = scheduler.Start(() => Observable.Generate(0, x => true, new Func(x => { throw ex; }), x => x, scheduler) ); res.Messages.AssertEqual( OnNext(201, 0), OnError(202, ex) ); } [TestMethod] public void Generate_Dispose() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Generate(0, x => true, x => x + 1, x => x, scheduler), 203 ); res.Messages.AssertEqual( OnNext(201, 0), OnNext(202, 1) ); } [TestMethod] public void Generate_DefaultScheduler_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Generate(0, (Func)null, DummyFunc.Instance, DummyFunc.Instance)); ReactiveAssert.Throws(() => Observable.Generate(0, DummyFunc.Instance, DummyFunc.Instance, (Func)null)); ReactiveAssert.Throws(() => Observable.Generate(0, DummyFunc.Instance, (Func)null, DummyFunc.Instance)); ReactiveAssert.Throws(() => Observable.Generate(0, DummyFunc.Instance, DummyFunc.Instance, DummyFunc.Instance).Subscribe(null)); } [TestMethod] public void Generate_DefaultScheduler() { Observable.Generate(0, x => x < 10, x => x + 1, x => x).AssertEqual(Observable.Generate(0, x => x < 10, x => x + 1, x => x, DefaultScheduler.Instance)); } #if !NO_PERF [TestMethod] public void Generate_LongRunning1() { var start = default(ManualResetEvent); var end = default(ManualResetEvent); var s = new TestLongRunningScheduler(x => start = x, x => end = x); var xs = Observable.Generate(0, x => x < 100, x => x + 1, x => x, s); var lst = new List(); var done = false; xs.Subscribe(x => { lst.Add(x); }, () => done = true); end.WaitOne(); Assert.IsTrue(lst.SequenceEqual(Enumerable.Range(0, 100))); Assert.IsTrue(done); } [TestMethod] public void Generate_LongRunning2() { var start = default(ManualResetEvent); var end = default(ManualResetEvent); var s = new TestLongRunningScheduler(x => start = x, x => end = x); var xs = Observable.Generate(0, _ => true, x => x + 1, x => x, s); var lst = new List(); var d = xs.Subscribe(x => { lst.Add(x); }); start.WaitOne(); while (lst.Count < 100) ; d.Dispose(); end.WaitOne(); Assert.IsTrue(lst.Take(100).SequenceEqual(Enumerable.Range(0, 100))); } [TestMethod] public void Generate_LongRunning_Throw() { var start = default(ManualResetEvent); var end = default(ManualResetEvent); var s = new TestLongRunningScheduler(x => start = x, x => end = x); var ex = new Exception(); var xs = Observable.Generate(0, x => { if (x < 100) return true; throw ex; }, x => x + 1, x => x, s); var lst = new List(); var e = default(Exception); var done = false; xs.Subscribe(x => { lst.Add(x); }, e_ => e = e_, () => done = true); end.WaitOne(); Assert.IsTrue(lst.SequenceEqual(Enumerable.Range(0, 100))); Assert.AreSame(ex, e); Assert.IsFalse(done); } #endif #endregion #region + Never + [TestMethod] public void Never_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Never().Subscribe(null)); ReactiveAssert.Throws(() => Observable.Never(42).Subscribe(null)); } [TestMethod] public void Never_Basic() { var scheduler = new TestScheduler(); var xs = Observable.Never(); var res = scheduler.CreateObserver(); xs.Subscribe(res); scheduler.Start(); res.Messages.AssertEqual( ); } [TestMethod] public void Never_Basic_Witness() { var scheduler = new TestScheduler(); var xs = Observable.Never(42); var res = scheduler.CreateObserver(); xs.Subscribe(res); scheduler.Start(); res.Messages.AssertEqual( ); } #endregion #region + Range + [TestMethod] public void Range_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Range(0, 0, null)); ReactiveAssert.Throws(() => Observable.Range(0, -1, DummyScheduler.Instance)); ReactiveAssert.Throws(() => Observable.Range(int.MaxValue, 2, DummyScheduler.Instance)); } [TestMethod] public void Range_Zero() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Range(0, 0, scheduler) ); res.Messages.AssertEqual( OnCompleted(201) ); } [TestMethod] public void Range_One() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Range(0, 1, scheduler) ); res.Messages.AssertEqual( OnNext(201, 0), OnCompleted(202) ); } [TestMethod] public void Range_Five() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Range(10, 5, scheduler) ); res.Messages.AssertEqual( OnNext(201, 10), OnNext(202, 11), OnNext(203, 12), OnNext(204, 13), OnNext(205, 14), OnCompleted(206) ); } [TestMethod] public void Range_Boundaries() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Range(int.MaxValue, 1, scheduler) ); res.Messages.AssertEqual( OnNext(201, int.MaxValue), OnCompleted(202) ); } [TestMethod] public void Range_Dispose() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Range(-10, 5, scheduler), 204 ); res.Messages.AssertEqual( OnNext(201, -10), OnNext(202, -9), OnNext(203, -8) ); } [TestMethod] public void Range_Default_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Range(0, -1)); ReactiveAssert.Throws(() => Observable.Range(int.MaxValue, 2)); } [TestMethod] public void Range_Default() { for (int i = 0; i < 100; i++) Observable.Range(100, 100).AssertEqual(Observable.Range(100, 100, DefaultScheduler.Instance)); } #if !NO_PERF [TestMethod] public void Range_LongRunning1() { var start = default(ManualResetEvent); var end = default(ManualResetEvent); var s = new TestLongRunningScheduler(x => start = x, x => end = x); var xs = Observable.Range(0, 100, s); var lst = new List(); var done = false; xs.Subscribe(x => { lst.Add(x); }, () => done = true); end.WaitOne(); Assert.IsTrue(lst.SequenceEqual(Enumerable.Range(0, 100))); Assert.IsTrue(done); } [TestMethod] public void Range_LongRunning2() { var start = default(ManualResetEvent); var end = default(ManualResetEvent); var s = new TestLongRunningScheduler(x => start = x, x => end = x); var xs = Observable.Range(0, int.MaxValue, s); var lst = new List(); var d = xs.Subscribe(x => { lst.Add(x); }); start.WaitOne(); while (lst.Count < 100) ; d.Dispose(); end.WaitOne(); Assert.IsTrue(true); } [TestMethod] public void Range_LongRunning_Empty() { var start = default(ManualResetEvent); var end = default(ManualResetEvent); var scheduler = new TestLongRunningScheduler(x => start = x, x => end = x); var xs = Observable.Range(5, 0, scheduler); var lst = new List(); xs.ForEach(lst.Add); Assert.IsTrue(lst.SequenceEqual(Enumerable.Range(5, 0))); } [TestMethod] public void Range_LongRunning_Regular() { var start = default(ManualResetEvent); var end = default(ManualResetEvent); var scheduler = new TestLongRunningScheduler(x => start = x, x => end = x); var xs = Observable.Range(5, 17, scheduler); var lst = new List(); xs.ForEach(lst.Add); Assert.IsTrue(lst.SequenceEqual(Enumerable.Range(5, 17))); } [TestMethod] public void Range_LongRunning_Boundaries() { var start = default(ManualResetEvent); var end = default(ManualResetEvent); var scheduler = new TestLongRunningScheduler(x => start = x, x => end = x); var xs = Observable.Range(int.MaxValue, 1, scheduler); var lst = new List(); xs.ForEach(lst.Add); Assert.IsTrue(lst.SequenceEqual(Enumerable.Range(int.MaxValue, 1))); } #endif #endregion #region + Repeat + [TestMethod] public void Repeat_Value_Count_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Repeat(1, 0, default(IScheduler))); ReactiveAssert.Throws(() => Observable.Repeat(1, -1, DummyScheduler.Instance)); ReactiveAssert.Throws(() => Observable.Repeat(1, 1, DummyScheduler.Instance).Subscribe(null)); } [TestMethod] public void Repeat_Value_Count_Zero() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Repeat(42, 0, scheduler) ); #if !NO_PERF res.Messages.AssertEqual( OnCompleted(201) ); #else res.Messages.AssertEqual( OnCompleted(200) ); #endif } [TestMethod] public void Repeat_Value_Count_One() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Repeat(42, 1, scheduler) ); res.Messages.AssertEqual( OnNext(201, 42), OnCompleted(201) ); } [TestMethod] public void Repeat_Value_Count_Ten() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Repeat(42, 10, scheduler) ); res.Messages.AssertEqual( OnNext(201, 42), OnNext(202, 42), OnNext(203, 42), OnNext(204, 42), OnNext(205, 42), OnNext(206, 42), OnNext(207, 42), OnNext(208, 42), OnNext(209, 42), OnNext(210, 42), OnCompleted(210) ); } [TestMethod] public void Repeat_Value_Count_Dispose() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Repeat(42, 10, scheduler), 207 ); res.Messages.AssertEqual( OnNext(201, 42), OnNext(202, 42), OnNext(203, 42), OnNext(204, 42), OnNext(205, 42), OnNext(206, 42) ); } [TestMethod] public void Repeat_Value_Count_Default_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Repeat(1, -1)); ReactiveAssert.Throws(() => Observable.Repeat(1, 1).Subscribe(null)); } [TestMethod] public void Repeat_Value_Count_Default() { Observable.Repeat(42, 10).AssertEqual(Observable.Repeat(42, 10, DefaultScheduler.Instance)); } [TestMethod] public void Repeat_Value_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Repeat(1, (IScheduler)null)); ReactiveAssert.Throws(() => Observable.Repeat(DummyScheduler.Instance, 1).Subscribe(null)); } [TestMethod] public void Repeat_Value() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Repeat(42, scheduler), 207 ); res.Messages.AssertEqual( OnNext(201, 42), OnNext(202, 42), OnNext(203, 42), OnNext(204, 42), OnNext(205, 42), OnNext(206, 42) ); } [TestMethod] public void Repeat_Value_Default_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Repeat(1).Subscribe(null)); } [TestMethod] public void Repeat_Value_Default() { Observable.Repeat(42).Take(100).AssertEqual(Observable.Repeat(42, DefaultScheduler.Instance).Take(100)); } #if !NO_PERF [TestMethod] public void Repeat_Count_LongRunning1() { var start = default(ManualResetEvent); var end = default(ManualResetEvent); var s = new TestLongRunningScheduler(x => start = x, x => end = x); var xs = Observable.Repeat(42, 100, s); var lst = new List(); var done = false; xs.Subscribe(x => { lst.Add(x); }, () => done = true); end.WaitOne(); Assert.IsTrue(lst.SequenceEqual(Enumerable.Repeat(42, 100))); Assert.IsTrue(done); } [TestMethod] public void Repeat_Count_LongRunning2() { var start = default(ManualResetEvent); var end = default(ManualResetEvent); var s = new TestLongRunningScheduler(x => start = x, x => end = x); var xs = Observable.Repeat(42, int.MaxValue, s); var lst = new List(); var d = xs.Subscribe(x => { lst.Add(x); }); start.WaitOne(); while (lst.Count < 100) ; d.Dispose(); end.WaitOne(); Assert.IsTrue(true); } [TestMethod] public void Repeat_Inf_LongRunning() { var start = default(ManualResetEvent); var end = default(ManualResetEvent); var s = new TestLongRunningScheduler(x => start = x, x => end = x); var xs = Observable.Repeat(42, s); var lst = new List(); var d = xs.Subscribe(x => { lst.Add(x); }); start.WaitOne(); while (lst.Count < 100) ; d.Dispose(); end.WaitOne(); Assert.IsTrue(true); } #endif #endregion #region + Return + [TestMethod] public void Return_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Return(0, null)); ReactiveAssert.Throws(() => Observable.Return(0, DummyScheduler.Instance).Subscribe(null)); } [TestMethod] public void Return_Basic() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Return(42, scheduler) ); res.Messages.AssertEqual( OnNext(201, 42), OnCompleted(201) ); } [TestMethod] public void Return_Disposed() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Return(42, scheduler), 200 ); res.Messages.AssertEqual( ); } [TestMethod] public void Return_DisposedAfterNext() { var scheduler = new TestScheduler(); var d = new SerialDisposable(); var xs = Observable.Return(42, scheduler); var res = scheduler.CreateObserver(); scheduler.ScheduleAbsolute(100, () => d.Disposable = xs.Subscribe( x => { d.Dispose(); res.OnNext(x); }, res.OnError, res.OnCompleted ) ); scheduler.Start(); res.Messages.AssertEqual( OnNext(101, 42) ); } [TestMethod] public void Return_ObserverThrows() { var scheduler1 = new TestScheduler(); var xs = Observable.Return(1, scheduler1); xs.Subscribe(x => { throw new InvalidOperationException(); }); ReactiveAssert.Throws(() => scheduler1.Start()); var scheduler2 = new TestScheduler(); var ys = Observable.Return(1, scheduler2); ys.Subscribe(x => { }, ex => { }, () => { throw new InvalidOperationException(); }); ReactiveAssert.Throws(() => scheduler2.Start()); } [TestMethod] public void Return_DefaultScheduler() { Observable.Return(42).AssertEqual(Observable.Return(42, DefaultScheduler.Instance)); } #endregion #region + Throw + [TestMethod] public void Throw_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Throw(null)); ReactiveAssert.Throws(() => Observable.Throw(null, 42)); ReactiveAssert.Throws(() => Observable.Throw(new Exception(), null)); ReactiveAssert.Throws(() => Observable.Throw(new Exception(), null, 42)); ReactiveAssert.Throws(() => Observable.Throw(null, DummyScheduler.Instance)); ReactiveAssert.Throws(() => Observable.Throw(null, DummyScheduler.Instance, 42)); ReactiveAssert.Throws(() => Observable.Throw(new Exception(), DummyScheduler.Instance).Subscribe(null)); } [TestMethod] public void Throw_Basic() { var scheduler = new TestScheduler(); var ex = new Exception(); var res = scheduler.Start(() => Observable.Throw(ex, scheduler) ); res.Messages.AssertEqual( OnError(201, ex) ); } [TestMethod] public void Throw_Disposed() { var scheduler = new TestScheduler(); var res = scheduler.Start(() => Observable.Throw(new Exception(), scheduler), 200 ); res.Messages.AssertEqual( ); } [TestMethod] public void Throw_ObserverThrows() { var scheduler1 = new TestScheduler(); var xs = Observable.Throw(new Exception(), scheduler1); xs.Subscribe(x => { }, ex => { throw new InvalidOperationException(); }, () => { }); ReactiveAssert.Throws(() => scheduler1.Start()); } [TestMethod] public void Throw_DefaultScheduler() { var ex = new Exception(); Observable.Throw(ex).AssertEqual(Observable.Throw(ex, DefaultScheduler.Instance)); } [TestMethod] public void Throw_Witness_Basic() { var scheduler = new TestScheduler(); var ex = new Exception(); var res = scheduler.Start(() => Observable.Throw(ex, scheduler, 42) ); res.Messages.AssertEqual( OnError(201, ex) ); } #endregion #region + Using + [TestMethod] public void Using_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Using((Func)null, DummyFunc>.Instance)); ReactiveAssert.Throws(() => Observable.Using(DummyFunc.Instance, (Func>)null)); ReactiveAssert.Throws(() => Observable.Using(() => DummyDisposable.Instance, d => default(IObservable)).Subscribe()); ReactiveAssert.Throws(() => Observable.Using(() => DummyDisposable.Instance, d => DummyObservable.Instance).Subscribe(null)); } [TestMethod] public void Using_Null() { var scheduler = new TestScheduler(); var disposeInvoked = 0L; var createInvoked = 0L; var xs = default(ITestableObservable); var disposable = default(MockDisposable); var _d = default(MockDisposable); var res = scheduler.Start(() => Observable.Using( () => { disposeInvoked++; disposable = default(MockDisposable); return disposable; }, d => { _d = d; createInvoked++; xs = scheduler.CreateColdObservable( OnNext(100, scheduler.Clock), OnCompleted(200)); return xs; } ) ); Assert.AreSame(disposable, _d); res.Messages.AssertEqual( OnNext(300, 200L), OnCompleted(400) ); Assert.AreEqual(1, createInvoked); Assert.AreEqual(1, disposeInvoked); xs.Subscriptions.AssertEqual( Subscribe(200, 400) ); Assert.IsNull(disposable); } [TestMethod] public void Using_Complete() { var scheduler = new TestScheduler(); var disposeInvoked = 0; var createInvoked = 0; var xs = default(ITestableObservable); var disposable = default(MockDisposable); var _d = default(MockDisposable); var res = scheduler.Start(() => Observable.Using( () => { disposeInvoked++; disposable = new MockDisposable(scheduler); return disposable; }, d => { _d = d; createInvoked++; xs = scheduler.CreateColdObservable( OnNext(100, scheduler.Clock), OnCompleted(200)); return xs; } ) ); Assert.AreSame(disposable, _d); res.Messages.AssertEqual( OnNext(300, 200L), OnCompleted(400) ); Assert.AreEqual(1, createInvoked); Assert.AreEqual(1, disposeInvoked); xs.Subscriptions.AssertEqual( Subscribe(200, 400) ); disposable.AssertEqual( 200, 400 ); } [TestMethod] public void Using_Error() { var scheduler = new TestScheduler(); var disposeInvoked = 0; var createInvoked = 0; var xs = default(ITestableObservable); var disposable = default(MockDisposable); var _d = default(MockDisposable); var ex = new Exception(); var res = scheduler.Start(() => Observable.Using( () => { disposeInvoked++; disposable = new MockDisposable(scheduler); return disposable; }, d => { _d = d; createInvoked++; xs = scheduler.CreateColdObservable( OnNext(100, scheduler.Clock), OnError(200, ex)); return xs; } ) ); Assert.AreSame(disposable, _d); res.Messages.AssertEqual( OnNext(300, 200L), OnError(400, ex) ); Assert.AreEqual(1, createInvoked); Assert.AreEqual(1, disposeInvoked); xs.Subscriptions.AssertEqual( Subscribe(200, 400) ); disposable.AssertEqual( 200, 400 ); } [TestMethod] public void Using_Dispose() { var scheduler = new TestScheduler(); var disposeInvoked = 0; var createInvoked = 0; var xs = default(ITestableObservable); var disposable = default(MockDisposable); var _d = default(MockDisposable); var res = scheduler.Start(() => Observable.Using( () => { disposeInvoked++; disposable = new MockDisposable(scheduler); return disposable; }, d => { _d = d; createInvoked++; xs = scheduler.CreateColdObservable( OnNext(100, scheduler.Clock), OnNext(1000, scheduler.Clock + 1)); return xs; } ) ); Assert.AreSame(disposable, _d); res.Messages.AssertEqual( OnNext(300, 200L) ); Assert.AreEqual(1, createInvoked); Assert.AreEqual(1, disposeInvoked); xs.Subscriptions.AssertEqual( Subscribe(200, 1000) ); disposable.AssertEqual( 200, 1000 ); } [TestMethod] public void Using_ThrowResourceSelector() { var scheduler = new TestScheduler(); var disposeInvoked = 0; var createInvoked = 0; var ex = new Exception(); var res = scheduler.Start(() => Observable.Using( () => { disposeInvoked++; throw ex; }, d => { createInvoked++; return Observable.Never(); } ) ); res.Messages.AssertEqual( OnError(200, ex) ); Assert.AreEqual(0, createInvoked); Assert.AreEqual(1, disposeInvoked); } [TestMethod] public void Using_ThrowResourceUsage() { var scheduler = new TestScheduler(); var ex = new Exception(); var disposeInvoked = 0; var createInvoked = 0; var disposable = default(MockDisposable); var res = scheduler.Start(() => Observable.Using( () => { disposeInvoked++; disposable = new MockDisposable(scheduler); return disposable; }, d => { createInvoked++; throw ex; } ) ); res.Messages.AssertEqual( OnError(200, ex) ); Assert.AreEqual(1, createInvoked); Assert.AreEqual(1, disposeInvoked); disposable.AssertEqual( 200, 200 ); } #endregion #region - UsingAsync - #if !NO_TPL [TestMethod] public void UsingAsync_ArgumentChecking() { ReactiveAssert.Throws(() => Observable.Using(null, (res, ct) => null)); ReactiveAssert.Throws(() => Observable.Using(ct => null, null)); } [TestMethod] public void UsingAsync_Simple() { var done = false; var xs = Observable.Using( ct => Task.Factory.StartNew(() => Disposable.Create(() => done = true)), (_, ct) => Task.Factory.StartNew>(() => Observable.Return(42)) ); var res = xs.ToEnumerable().ToList(); Assert.IsTrue(new[] { 42 }.SequenceEqual(res)); Assert.IsTrue(done); } [TestMethod] public void UsingAsync_CancelResource() { var N = 10;// 0000; for (int i = 0; i < N; i++) { var called = false; var s = new ManualResetEvent(false); var e = new ManualResetEvent(false); var x = new ManualResetEvent(false); var xs = Observable.Using( ct => Task.Factory.StartNew(() => { s.Set(); e.WaitOne(); while (!ct.IsCancellationRequested) ; x.Set(); return Disposable.Empty; }), (_, ct) => { called = true; return Task.Factory.StartNew>(() => Observable.Return(42) ); } ); var d = xs.Subscribe(_ => { }); s.WaitOne(); d.Dispose(); e.Set(); x.WaitOne(); Assert.IsFalse(called); } } [TestMethod] public void UsingAsync_CancelFactory() { var N = 10;// 0000; for (int i = 0; i < N; i++) { var gate = new object(); var disposed = false; var called = false; var s = new ManualResetEvent(false); var e = new ManualResetEvent(false); var x = new ManualResetEvent(false); var xs = Observable.Using( ct => Task.Factory.StartNew(() => Disposable.Create(() => { lock (gate) disposed = true; }) ), (_, ct) => Task.Factory.StartNew>(() => { s.Set(); e.WaitOne(); while (!ct.IsCancellationRequested) ; x.Set(); return Observable.Defer(() => { called = true; return Observable.Return(42); }); }) ); var d = xs.Subscribe(_ => { }); s.WaitOne(); // // This will *eventually* set the CancellationToken. There's a fundamental race between observing the CancellationToken // and returning the IDisposable that will set the CancellationTokenSource. Notice this is reflected in the code above, // by looping until the CancellationToken is set. // d.Dispose(); e.Set(); x.WaitOne(); while (true) { lock (gate) if (disposed) break; } Assert.IsFalse(called, i.ToString()); } } #endif #endregion } }