// 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.Disposables; namespace Microsoft.Reactive.Testing { /// /// Virtual time scheduler used for testing applications and libraries built using Reactive Extensions. /// public class TestScheduler : VirtualTimeScheduler { /// /// Schedules an action to be executed at the specified virtual time. /// /// The type of the state passed to the scheduled action. /// State passed to the action to be executed. /// Action to be executed. /// Absolute virtual time at which to execute the action. /// Disposable object used to cancel the scheduled action (best effort). /// is null. public override IDisposable ScheduleAbsolute(TState state, long dueTime, Func action) { if (dueTime <= Clock) dueTime = Clock + 1; return base.ScheduleAbsolute(state, dueTime, action); } /// /// Adds a relative virtual time to an absolute virtual time value. /// /// Absolute virtual time value. /// Relative virtual time value to add. /// Resulting absolute virtual time sum value. protected override long Add(long absolute, long relative) { return absolute + relative; } /// /// Converts the absolute virtual time value to a DateTimeOffset value. /// /// Absolute virtual time value to convert. /// Corresponding DateTimeOffset value. protected override DateTimeOffset ToDateTimeOffset(long absolute) { return new DateTimeOffset(absolute, TimeSpan.Zero); } /// /// Converts the TimeSpan value to a relative virtual time value. /// /// TimeSpan value to convert. /// Corresponding relative virtual time value. protected override long ToRelative(TimeSpan timeSpan) { return timeSpan.Ticks; } /// /// Starts the test scheduler and uses the specified virtual times to invoke the factory function, subscribe to the resulting sequence, and dispose the subscription. /// /// The element type of the observable sequence being tested. /// Factory method to create an observable sequence. /// Virtual time at which to invoke the factory to create an observable sequence. /// Virtual time at which to subscribe to the created observable sequence. /// Virtual time at which to dispose the subscription. /// Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active. /// is null. public ITestableObserver Start(Func> create, long created, long subscribed, long disposed) { if (create == null) throw new ArgumentNullException("create"); var source = default(IObservable); var subscription = default(IDisposable); var observer = CreateObserver(); ScheduleAbsolute(default(object), created, (scheduler, state) => { source = create(); return Disposable.Empty; }); ScheduleAbsolute(default(object), subscribed, (scheduler, state) => { subscription = source.Subscribe(observer); return Disposable.Empty; }); ScheduleAbsolute(default(object), disposed, (scheduler, state) => { subscription.Dispose(); return Disposable.Empty; }); Start(); return observer; } /// /// Starts the test scheduler and uses the specified virtual time to dispose the subscription to the sequence obtained through the factory function. /// Default virtual times are used for factory invocation and sequence subscription. /// /// The element type of the observable sequence being tested. /// Factory method to create an observable sequence. /// Virtual time at which to dispose the subscription. /// Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active. /// is null. public ITestableObserver Start(Func> create, long disposed) { if (create == null) throw new ArgumentNullException("create"); return Start(create, ReactiveTest.Created, ReactiveTest.Subscribed, disposed); } /// /// Starts the test scheduler and uses default virtual times to invoke the factory function, to subscribe to the resulting sequence, and to dispose the subscription. /// /// The element type of the observable sequence being tested. /// Factory method to create an observable sequence. /// Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active. /// is null. public ITestableObserver Start(Func> create) { if (create == null) throw new ArgumentNullException("create"); return Start(create, ReactiveTest.Created, ReactiveTest.Subscribed, ReactiveTest.Disposed); } /// /// Creates a hot observable using the specified timestamped notification messages. /// /// The element type of the observable sequence being created. /// Notifications to surface through the created sequence at their specified absolute virtual times. /// Hot observable sequence that can be used to assert the timing of subscriptions and notifications. /// is null. public ITestableObservable CreateHotObservable(params Recorded>[] messages) { if (messages == null) throw new ArgumentNullException("messages"); return new HotObservable(this, messages); } /// /// Creates a cold observable using the specified timestamped notification messages. /// /// The element type of the observable sequence being created. /// Notifications to surface through the created sequence at their specified virtual time offsets from the sequence subscription time. /// Cold observable sequence that can be used to assert the timing of subscriptions and notifications. /// is null. public ITestableObservable CreateColdObservable(params Recorded>[] messages) { if (messages == null) throw new ArgumentNullException("messages"); return new ColdObservable(this, messages); } /// /// Creates an observer that records received notification messages and timestamps those. /// /// The element type of the observer being created. /// Observer that can be used to assert the timing of received notifications. public ITestableObserver CreateObserver() { return new MockObserver(this); } } }