1 // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. 2 3 using System; 4 using System.Reactive; 5 6 namespace Microsoft.Reactive.Testing 7 { 8 /// <summary> 9 /// Base class to write unit tests for applications and libraries built using Reactive Extensions. 10 /// </summary> 11 public class ReactiveTest 12 { 13 /// <summary> 14 /// Default virtual time used for creation of observable sequences in <see cref="ReactiveTest"/>-based unit tests. 15 /// </summary> 16 public const long Created = 100; 17 18 /// <summary> 19 /// Default virtual time used to subscribe to observable sequences in <see cref="ReactiveTest"/>-based unit tests. 20 /// </summary> 21 public const long Subscribed = 200; 22 23 /// <summary> 24 /// Default virtual time used to dispose subscriptions in <see cref="ReactiveTest"/>-based unit tests. 25 /// </summary> 26 public const long Disposed = 1000; 27 28 /// <summary> 29 /// Factory method for an OnNext notification record at a given time with a given value. 30 /// </summary> 31 /// <typeparam name="T">The element type for the resulting notification object.</typeparam> 32 /// <param name="ticks">Recorded virtual time the OnNext notification occurs.</param> 33 /// <param name="value">Recorded value stored in the OnNext notification.</param> 34 /// <returns>Recorded OnNext notification.</returns> OnNext(long ticks, T value)35 public static Recorded<Notification<T>> OnNext<T>(long ticks, T value) 36 { 37 return new Recorded<Notification<T>>(ticks, Notification.CreateOnNext<T>(value)); 38 } 39 40 /// <summary> 41 /// Factory method for writing an assert that checks for an OnNext notification record at a given time, using the specified predicate to check the value. 42 /// </summary> 43 /// <typeparam name="T">The element type for the resulting notification object.</typeparam> 44 /// <param name="ticks">Recorded virtual time the OnNext notification occurs.</param> 45 /// <param name="predicate">Predicate function to check the OnNext notification value against an expected value.</param> 46 /// <returns>Recorded OnNext notification with a predicate to assert a given value.</returns> 47 /// <exception cref="ArgumentNullException"><paramref name="predicate"/> is null.</exception> OnNext(long ticks, Func<T, bool> predicate)48 public static Recorded<Notification<T>> OnNext<T>(long ticks, Func<T, bool> predicate) 49 { 50 if (predicate == null) 51 throw new ArgumentNullException("predicate"); 52 53 return new Recorded<Notification<T>>(ticks, new OnNextPredicate<T>(predicate)); 54 } 55 56 /// <summary> 57 /// Factory method for an OnCompleted notification record at a given time. 58 /// </summary> 59 /// <typeparam name="T">The element type for the resulting notification object.</typeparam> 60 /// <param name="ticks">Recorded virtual time the OnCompleted notification occurs.</param> 61 /// <returns>Recorded OnCompleted notification.</returns> OnCompleted(long ticks)62 public static Recorded<Notification<T>> OnCompleted<T>(long ticks) 63 { 64 return new Recorded<Notification<T>>(ticks, Notification.CreateOnCompleted<T>()); 65 } 66 67 /// <summary> 68 /// Factory method for an OnCompleted notification record at a given time, using inference to determine the type of <typeparamref name="T"/>. 69 /// </summary> 70 /// <typeparam name="T">The element type for the resulting notification object.</typeparam> 71 /// <param name="ticks">Recorded virtual time the OnCompleted notification occurs.</param> 72 /// <param name="witness">Object solely used to infer the type of the <typeparamref name="T"/> type parameter. This parameter is typically used when creating a sequence of anonymously typed elements.</param> 73 /// <returns>Recorded OnCompleted notification.</returns> OnCompleted(long ticks, T witness)74 public static Recorded<Notification<T>> OnCompleted<T>(long ticks, T witness) 75 { 76 return new Recorded<Notification<T>>(ticks, Notification.CreateOnCompleted<T>()); 77 } 78 79 /// <summary> 80 /// Factory method for an OnError notification record at a given time with a given error. 81 /// </summary> 82 /// <typeparam name="T">The element type for the resulting notification object.</typeparam> 83 /// <param name="ticks">Recorded virtual time the OnError notification occurs.</param> 84 /// <param name="exception">Recorded exception stored in the OnError notification.</param> 85 /// <returns>Recorded OnError notification.</returns> 86 /// <exception cref="ArgumentNullException"><paramref name="exception"/> is null.</exception> OnError(long ticks, Exception exception)87 public static Recorded<Notification<T>> OnError<T>(long ticks, Exception exception) 88 { 89 if (exception == null) 90 throw new ArgumentNullException("exception"); 91 92 return new Recorded<Notification<T>>(ticks, Notification.CreateOnError<T>(exception)); 93 } 94 95 /// <summary> 96 /// Factory method for writing an assert that checks for an OnError notification record at a given time, using the specified predicate to check the exception. 97 /// </summary> 98 /// <typeparam name="T">The element type for the resulting notification object.</typeparam> 99 /// <param name="ticks">Recorded virtual time the OnError notification occurs.</param> 100 /// <param name="predicate">Predicate function to check the OnError notification value against an expected exception.</param> 101 /// <returns>Recorded OnError notification with a predicate to assert a given exception.</returns> 102 /// <exception cref="ArgumentNullException"><paramref name="predicate"/> is null.</exception> OnError(long ticks, Func<Exception, bool> predicate)103 public static Recorded<Notification<T>> OnError<T>(long ticks, Func<Exception, bool> predicate) 104 { 105 if (predicate == null) 106 throw new ArgumentNullException("predicate"); 107 108 return new Recorded<Notification<T>>(ticks, new OnErrorPredicate<T>(predicate)); 109 } 110 111 /// <summary> 112 /// Factory method for an OnError notification record at a given time with a given error, using inference to determine the type of <typeparamref name="T"/>. 113 /// </summary> 114 /// <typeparam name="T">The element type for the resulting notification object.</typeparam> 115 /// <param name="ticks">Recorded virtual time the OnError notification occurs.</param> 116 /// <param name="exception">Recorded exception stored in the OnError notification.</param> 117 /// <param name="witness">Object solely used to infer the type of the <typeparamref name="T"/> type parameter. This parameter is typically used when creating a sequence of anonymously typed elements.</param> 118 /// <returns>Recorded OnError notification.</returns> 119 /// <exception cref="ArgumentNullException"><paramref name="exception"/> is null.</exception> OnError(long ticks, Exception exception, T witness)120 public static Recorded<Notification<T>> OnError<T>(long ticks, Exception exception, T witness) 121 { 122 if (exception == null) 123 throw new ArgumentNullException("exception"); 124 125 return new Recorded<Notification<T>>(ticks, Notification.CreateOnError<T>(exception)); 126 } 127 128 /// <summary> 129 /// Factory method for writing an assert that checks for an OnError notification record at a given time, using the specified predicate to check the exception and inference to determine the type of <typeparamref name="T"/>. 130 /// </summary> 131 /// <typeparam name="T">The element type for the resulting notification object.</typeparam> 132 /// <param name="ticks">Recorded virtual time the OnError notification occurs.</param> 133 /// <param name="predicate">Predicate function to check the OnError notification value against an expected exception.</param> 134 /// <param name="witness">Object solely used to infer the type of the <typeparamref name="T"/> type parameter. This parameter is typically used when creating a sequence of anonymously typed elements.</param> 135 /// <returns>Recorded OnError notification with a predicate to assert a given exception.</returns> 136 /// <exception cref="ArgumentNullException"><paramref name="predicate"/> is null.</exception> OnError(long ticks, Func<Exception, bool> predicate, T witness)137 public static Recorded<Notification<T>> OnError<T>(long ticks, Func<Exception, bool> predicate, T witness) 138 { 139 if (predicate == null) 140 throw new ArgumentNullException("predicate"); 141 142 return new Recorded<Notification<T>>(ticks, new OnErrorPredicate<T>(predicate)); 143 } 144 145 /// <summary> 146 /// Factory method for a subscription record based on a given subscription and disposal time. 147 /// </summary> 148 /// <param name="start">Virtual time indicating when the subscription was created.</param> 149 /// <param name="end">Virtual time indicating when the subscription was disposed.</param> 150 /// <returns>Subscription object.</returns> Subscribe(long start, long end)151 public static Subscription Subscribe(long start, long end) 152 { 153 return new Subscription(start, end); 154 } 155 156 /// <summary> 157 /// Factory method for a subscription record based on a given subscription time. 158 /// </summary> 159 /// <param name="start">Virtual time indicating when the subscription was created.</param> 160 /// <returns>Subscription object.</returns> Subscribe(long start)161 public static Subscription Subscribe(long start) 162 { 163 return new Subscription(start); 164 } 165 166 #region Predicate-based notification assert helper classes 167 168 class OnNextPredicate<T> : PredicateNotification<T> 169 { 170 private readonly Func<T, bool> _predicate; 171 OnNextPredicate(Func<T, bool> predicate)172 public OnNextPredicate(Func<T, bool> predicate) 173 { 174 _predicate = predicate; 175 } 176 Equals(Notification<T> other)177 public override bool Equals(Notification<T> other) 178 { 179 if (Object.ReferenceEquals(this, other)) 180 return true; 181 if (Object.ReferenceEquals(other, null)) 182 return false; 183 if (other.Kind != NotificationKind.OnNext) 184 return false; 185 186 return _predicate(other.Value); 187 } 188 } 189 190 class OnErrorPredicate<T> : PredicateNotification<T> 191 { 192 private readonly Func<Exception, bool> _predicate; 193 OnErrorPredicate(Func<Exception, bool> predicate)194 public OnErrorPredicate(Func<Exception, bool> predicate) 195 { 196 _predicate = predicate; 197 } 198 Equals(Notification<T> other)199 public override bool Equals(Notification<T> other) 200 { 201 if (Object.ReferenceEquals(this, other)) 202 return true; 203 if (Object.ReferenceEquals(other, null)) 204 return false; 205 if (other.Kind != NotificationKind.OnError) 206 return false; 207 208 return _predicate(other.Exception); 209 } 210 } 211 212 abstract class PredicateNotification<T> : Notification<T> 213 { 214 #region Non-implemented members (by design) 215 216 public override T Value 217 { 218 get { throw new NotSupportedException(); } 219 } 220 221 public override bool HasValue 222 { 223 get { throw new NotSupportedException(); } 224 } 225 226 public override Exception Exception 227 { 228 get { throw new NotSupportedException(); } 229 } 230 231 public override NotificationKind Kind 232 { 233 get { throw new NotSupportedException(); } 234 } 235 Accept(IObserver<T> observer)236 public override void Accept(IObserver<T> observer) 237 { 238 throw new NotSupportedException(); 239 } 240 Accept(IObserver<T, TResult> observer)241 public override TResult Accept<TResult>(IObserver<T, TResult> observer) 242 { 243 throw new NotSupportedException(); 244 } 245 Accept(Action<T> onNext, Action<Exception> onError, Action onCompleted)246 public override void Accept(Action<T> onNext, Action<Exception> onError, Action onCompleted) 247 { 248 throw new NotSupportedException(); 249 } 250 Accept(Func<T, TResult> onNext, Func<Exception, TResult> onError, Func<TResult> onCompleted)251 public override TResult Accept<TResult>(Func<T, TResult> onNext, Func<Exception, TResult> onError, Func<TResult> onCompleted) 252 { 253 throw new NotSupportedException(); 254 } 255 256 #endregion 257 } 258 259 #endregion 260 } 261 } 262