// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. using System; using System.Reactive; using System.Reactive.Concurrency; using System.Reactive.Linq; 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 namespace ReactiveTests.Tests { [TestClass] public partial class ObservableSafetyTest : ReactiveTest { [TestMethod] public void SubscribeSafe_ArgumentChecking() { ReactiveAssert.Throws(() => ObservableExtensions.SubscribeSafe(default(IObservable), Observer.Create(_ => { }))); ReactiveAssert.Throws(() => ObservableExtensions.SubscribeSafe(Observable.Return(42), default(IObserver))); } [TestMethod] public void Safety_Subscription1() { var ex = new Exception(); var xs = new RogueObservable(ex); var res = xs.Where(x => true).Select(x => x); var err = default(Exception); var d = res.Subscribe(x => { Assert.Fail(); }, ex_ => { err = ex_; }, () => { Assert.Fail(); }); Assert.AreSame(ex, err); d.Dispose(); } [TestMethod] public void Safety_Subscription2() { var ex = new Exception(); var scheduler = new TestScheduler(); var xs = scheduler.CreateHotObservable( OnNext(210, 42), OnNext(220, 43), OnNext(230, 44), OnNext(240, 45), OnCompleted(250) ); var ys = new RogueObservable(ex); var res = scheduler.Start(() => xs.Merge(ys) ); res.Messages.AssertEqual( OnError(200, ex) ); xs.Subscriptions.AssertEqual( Subscribe(200, 200) ); } class RogueObservable : IObservable { private readonly Exception _ex; public RogueObservable(Exception ex) { _ex = ex; } public IDisposable Subscribe(IObserver observer) { throw _ex; } } [TestMethod] public void ObservableBase_ObserverThrows() { var ex = new Exception(); var failed = new ManualResetEvent(false); var disposed = new ManualResetEvent(false); var err = default(Exception); var xs = Observable.Create(observer => { Scheduler.Default.Schedule(() => { try { observer.OnNext(42); } catch (Exception ex_) { err = ex_; failed.Set(); } }); return () => { disposed.Set(); }; }); xs.Subscribe(x => { throw ex; }); // Can't use WaitAll - we're on an STA thread. disposed.WaitOne(); failed.WaitOne(); Assert.AreSame(ex, err); } [TestMethod] public void ObservableBase_ObserverThrows_CustomObserver() { var ex = new Exception(); var failed = new ManualResetEvent(false); var disposed = new ManualResetEvent(false); var err = default(Exception); var xs = Observable.Create(observer => { Scheduler.Default.Schedule(() => { try { observer.OnNext(42); } catch (Exception ex_) { err = ex_; failed.Set(); } }); return () => { disposed.Set(); }; }); xs.Subscribe(new MyObserver(_ => true, ex)); // Can't use WaitAll - we're on an STA thread. disposed.WaitOne(); failed.WaitOne(); Assert.AreSame(ex, err); } [TestMethod] public void Producer_ObserverThrows() { var ex = new Exception(); var scheduler = new TestScheduler(); var xs = scheduler.CreateHotObservable( OnNext(210, 1), OnNext(220, 2), OnNext(230, 3) ); var ys = scheduler.CreateHotObservable( OnNext(215, 1), OnNext(225, 2), OnNext(235, 3) ); var res = xs.CombineLatest(ys, (x, y) => x + y); // This creates a Producer object scheduler.ScheduleAbsolute(200, () => { res.Subscribe(z => { if (z == 4) throw ex; }); }); try { scheduler.Start(); Assert.Fail(); } catch (Exception err) { Assert.AreSame(ex, err); } Assert.AreEqual(225, scheduler.Clock); xs.Subscriptions.AssertEqual( Subscribe(200, 225) ); ys.Subscriptions.AssertEqual( Subscribe(200, 225) ); } [TestMethod] public void Producer_ObserverThrows_CustomObserver() { var ex = new Exception(); var scheduler = new TestScheduler(); var xs = scheduler.CreateHotObservable( OnNext(210, 1), OnNext(220, 2), OnNext(230, 3) ); var ys = scheduler.CreateHotObservable( OnNext(215, 1), OnNext(225, 2), OnNext(235, 3) ); var res = xs.CombineLatest(ys, (x, y) => x + y); // This creates a Producer object scheduler.ScheduleAbsolute(200, () => { res.Subscribe(new MyObserver(x => x == 4, ex)); }); try { scheduler.Start(); Assert.Fail(); } catch (Exception err) { Assert.AreSame(ex, err); } Assert.AreEqual(225, scheduler.Clock); xs.Subscriptions.AssertEqual( Subscribe(200, 225) ); ys.Subscriptions.AssertEqual( Subscribe(200, 225) ); } class MyObserver : ObserverBase { private readonly Func _predicate; private readonly Exception _exception; public MyObserver(Func predicate, Exception exception) { _predicate = predicate; _exception = exception; } protected override void OnNextCore(int value) { if (_predicate(value)) throw _exception; } protected override void OnErrorCore(Exception error) { } protected override void OnCompletedCore() { } } } }