ArgumentValidationTest.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT License.
  3. // See the LICENSE file in the project root for more information.
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Reactive;
  8. using System.Reactive.Concurrency;
  9. using System.Reactive.Disposables;
  10. using System.Reactive.Joins;
  11. using System.Reactive.Linq;
  12. using System.Reactive.Subjects;
  13. using System.Reflection;
  14. using System.Threading;
  15. using System.Threading.Tasks;
  16. using Microsoft.VisualStudio.TestTools.UnitTesting;
  17. namespace ReactiveTests.Tests
  18. {
  19. /// <summary>
  20. /// Check if the Observable operator methods perform the proper
  21. /// argument validations en-masse with reflective checks.
  22. /// </summary>
  23. [TestClass]
  24. public class ArgumentValidationTest
  25. {
  26. #region + Default values for the generic types +
  27. /// <summary>
  28. /// Contains a map of various types, represented
  29. /// as strings generated via <see cref="TypeNameOf(Type)"/>,
  30. /// mapped to a value.
  31. /// </summary>
  32. private static readonly Dictionary<string, object> _defaultValues;
  33. /// <summary>
  34. /// Prepare the default instances for various types used
  35. /// throughout Rx.NET.
  36. /// </summary>
  37. static ArgumentValidationTest()
  38. {
  39. #pragma warning disable IDE0300 // Simplify collection initialization. We want to be clear about what kinds of collections are in use in these tests.
  40. _defaultValues = new Dictionary<string, object>
  41. {
  42. { "IObservable`1[Object]", Observable.Return(new object()) },
  43. { "IObservable`1[Int32]", Observable.Return(1) },
  44. { "IObservable`1[Task`1[Int32]]", Observable.Return(Task.FromResult(1)) },
  45. { "IObservable`1[Notification`1[Int32]]", Observable.Return(Notification.CreateOnNext(1)) },
  46. { "IObservable`1[Int64]", Observable.Return(1L) },
  47. { "IObservable`1[Double]", Observable.Return(1.0) },
  48. { "IObservable`1[Single]", Observable.Return(1.0f) },
  49. { "IObservable`1[Decimal]", Observable.Return(1.0m) },
  50. { "IObservable`1[Nullable`1[Int32]]", Observable.Return<int?>(1) },
  51. { "IObservable`1[Nullable`1[Int64]]", Observable.Return<long?>(1L) },
  52. { "IObservable`1[Nullable`1[Double]]", Observable.Return<double?>(1.0) },
  53. { "IObservable`1[Nullable`1[Single]]", Observable.Return<float?>(1.0f) },
  54. { "IObservable`1[Nullable`1[Decimal]]", Observable.Return<decimal?>(1.0m) },
  55. { "IObservable`1[IObservable`1[Int32]]", Observable.Return(Observable.Return(1)) },
  56. { "IObservable`1[][Int32]", new[] { Observable.Return(1) } },
  57. { "IConnectableObservable`1[Int32]", Observable.Return(1).Publish() },
  58. { "Int32", 1 },
  59. { "Int64", 1L },
  60. { "IScheduler", Scheduler.Immediate },
  61. { "TimeSpan", TimeSpan.FromMilliseconds(1) },
  62. { "DateTimeOffset", DateTimeOffset.Now },
  63. { "Object", new object() },
  64. { "Exception", new Exception() },
  65. { "String", "String" },
  66. { "Boolean", false },
  67. { "IDictionary`2[Int32, IObservable`1[Int32]]", new Dictionary<int, IObservable<int>>() },
  68. { "Type", typeof(object) },
  69. { "Int32[]", new[] { 1 } },
  70. { "ISubject`1[Int32]", new Subject<int>() },
  71. { "ISubject`2[Int32, Int32]", new Subject<int>() },
  72. { "IEnumerable`1[Int32]", new[] { 1 } },
  73. { "IEnumerable`1[IObservable`1[Int32]]", new[] { Observable.Return(1) } },
  74. { "SynchronizationContext", SynchronizationContext.Current ?? new SynchronizationContext() },
  75. { "IEqualityComparer`1[Int32]", EqualityComparer<int>.Default },
  76. { "IComparer`1[Int32]", Comparer<int>.Default },
  77. { "IObserver`1[Int32]", Observer.Create<int>(v => { }) },
  78. { "CancellationToken", new CancellationToken() },
  79. { "TaskObservationOptions", new TaskObservationOptions(null, false) },
  80. { "Action", new Action(() => { }) },
  81. { "Action`1[Int32]", new Action<int>(v => { }) },
  82. { "Action`1[Exception]", new Action<Exception>(v => { }) },
  83. { "Action`1[IDisposable]", new Action<IDisposable>(v => { }) },
  84. { "Action`1[EventHandler]", new Action<EventHandler>(v => { }) },
  85. { "Action`1[EventHandler`1[Int32]]", new Action<EventHandler<int>>(v => { }) },
  86. { "Action`1[Action`1[Int32]]", new Action<Action<int>>(v => { }) },
  87. { "Action`1[Action]", new Action<Action>(v => { }) },
  88. { "Action`1[IAsyncResult]", new Action<IAsyncResult>(v => { }) },
  89. { "Action`2[Int32, Int32]", new Action<int, int>((v, u) => { }) },
  90. { "Func`1[Boolean]", new Func<bool>(() => true) },
  91. { "Func`1[Int32]", new Func<int>(() => 1) },
  92. { "Func`1[IObservable`1[Int32]]", new Func<IObservable<int>>(() => Observable.Return(1)) },
  93. { "Func`1[ISubject`2[Int32, Int32]]", new Func<ISubject<int, int>>(() => new Subject<int>()) },
  94. { "Func`1[Task`1[IObservable`1[Int32]]]", new Func<Task<IObservable<int>>>(() => Task.FromResult(Observable.Return(1))) },
  95. { "Func`1[IDisposable]", new Func<IDisposable>(() => Disposable.Empty) },
  96. { "Func`1[Task]", new Func<Task>(() => Task.FromResult(1)) },
  97. { "Func`1[Task`1[Int32]]", new Func<Task<int>>(() => Task.FromResult(1)) },
  98. { "Func`1[IEnumerable`1[IObservable`1[Object]]]", new Func<IEnumerable<IObservable<object>>>(() => new[] { Observable.Return((object)1) }) },
  99. { "Func`2[Int32, IObservable`1[Int32]]", new Func<int, IObservable<int>>(v => Observable.Return(v)) },
  100. { "Func`2[Exception, IObservable`1[Int32]]", new Func<Exception, IObservable<int>>(v => Observable.Return(1)) },
  101. { "Func`2[Int32, Task`1[Int32]]", new Func<int, Task<int>>(v => Task.FromResult(v)) },
  102. { "Func`2[Int32, Int32]", new Func<int, int>(v => v) },
  103. { "Func`2[Int32, IEnumerable`1[Int32]]", new Func<int, IEnumerable<int>>(v => new[] { v }) },
  104. { "Func`2[Int32, Boolean]", new Func<int, bool>(v => true) },
  105. { "Func`2[Int32, TimeSpan]", new Func<int, TimeSpan>(v => TimeSpan.FromMilliseconds(1)) },
  106. { "Func`2[Int32, DateTimeOffset]", new Func<int, DateTimeOffset>(v => DateTimeOffset.Now) },
  107. { "Func`2[IList`1[Int32], Int32]", new Func<IList<int>, int>(v => v.Count) },
  108. { "Func`2[Int32, Nullable`1[Double]]", new Func<int, double?>(v => v) },
  109. { "Func`2[Int32, Nullable`1[Single]]", new Func<int, float?>(v => v) },
  110. { "Func`2[Int32, Nullable`1[Int32]]", new Func<int, int?>(v => v) },
  111. { "Func`2[Int32, Nullable`1[Decimal]]", new Func<int, decimal?>(v => v) },
  112. { "Func`2[Int32, Nullable`1[Int64]]", new Func<int, long?>(v => v) },
  113. { "Func`2[Int32, Double]", new Func<int, double>(v => v) },
  114. { "Func`2[Int32, Single]", new Func<int, float>(v => v) },
  115. { "Func`2[Int32, Decimal]", new Func<int, decimal>(v => v) },
  116. { "Func`2[Int32, Int64]", new Func<int, long>(v => v) },
  117. { "Func`2[IObservable`1[Object], IObservable`1[Int32]]", new Func<IObservable<object>, IObservable<int>>(v => v.Select(w => 1)) },
  118. { "Func`2[IObservable`1[Exception], IObservable`1[Int32]]", new Func<IObservable<Exception>, IObservable<int>>(v => v.Select(w => 1)) },
  119. { "Func`2[IGroupedObservable`2[Int32, Int32], IObservable`1[Int32]]", new Func<IGroupedObservable<int, int>, IObservable<int>>(v => v) },
  120. { "Func`2[IObservable`1[Int32], IObservable`1[Int32]]", new Func<IObservable<int>, IObservable<int>>(v => v.Select(w => 1)) },
  121. { "Func`2[CancellationToken, Task`1[IObservable`1[Int32]]]", new Func<CancellationToken, Task<IObservable<int>>>(v => Task.FromResult(Observable.Return(1))) },
  122. { "Func`2[IDisposable, Task`1[IObservable`1[Int32]]]", new Func<IDisposable, Task<IObservable<int>>>(v => Task.FromResult(Observable.Return(1))) },
  123. { "Func`2[IDisposable, IObservable`1[Int32]]", new Func<IDisposable, IObservable<int>>(v => Observable.Return(1)) },
  124. { "Func`2[CancellationToken, Task`1[IDisposable]]", new Func<CancellationToken, Task<IDisposable>>(v => Task.FromResult(Disposable.Empty)) },
  125. { "Func`2[EventHandler`1[Int32], Int32]", new Func<EventHandler<int>, int>(v => 1) },
  126. { "Func`2[Action`1[Int32], Int32]", new Func<Action<int>, int>(v => 1) },
  127. { "Func`2[IObserver`1[Int32], IDisposable]", new Func<IObserver<int>, IDisposable>(v => Disposable.Empty) },
  128. { "Func`2[IObserver`1[Int32], Action]", new Func<IObserver<int>, Action>(v => () => { }) },
  129. { "Func`2[IObserver`1[Int32], Task]", new Func<IObserver<int>, Task>(v => Task.FromResult(1)) },
  130. { "Func`2[IObserver`1[Int32], Task`1[IDisposable]]", new Func<IObserver<int>, Task<IDisposable>>(v => Task.FromResult(Disposable.Empty)) },
  131. { "Func`2[IObserver`1[Int32], Task`1[Action]]", new Func<IObserver<int>, Task<Action>>(v => Task.FromResult<Action>(() => { })) },
  132. { "Func`2[CancellationToken, Task]", new Func<CancellationToken, Task>(v => Task.FromResult(1)) },
  133. { "Func`2[CancellationToken, Task`1[Int32]]", new Func<CancellationToken, Task<int>>(v => Task.FromResult(1)) },
  134. { "Func`2[IAsyncResult, Int32]", new Func<IAsyncResult, int>(v => 1) },
  135. { "Func`2[IObserver`1[Int32], IEnumerable`1[IObservable`1[Object]]]", new Func<IObserver<int>, IEnumerable<IObservable<object>>>(v => new[] { Observable.Return((object)1) }) },
  136. { "Func`2[IObservable`1[Int32], Int32]", new Func<IObservable<int>, int>(v => 1) },
  137. { "Func`3[Int32, Int32, IObservable`1[Int32]]", new Func<int, int, IObservable<int>>((v, u) => Observable.Return(v + u)) },
  138. { "Func`3[Int32, Int32, Task`1[Int32]]", new Func<int, int, Task<int>>((v, u) => Task.FromResult(v + u)) },
  139. { "Func`3[Int32, CancellationToken, Task`1[Int32]]", new Func<int, CancellationToken, Task<int>>((v, u) => Task.FromResult(v)) },
  140. { "Func`3[Int32, Int32, Int32]", new Func<int, int, int>((v, u) => v + u) },
  141. { "Func`3[Int32, Int32, IEnumerable`1[Int32]]", new Func<int, int, IEnumerable<int>>((v, u) => new[] { v, u }) },
  142. { "Func`3[Int32, Int32, Boolean]", new Func<int, int, bool>((v, u) => true) },
  143. { "Func`3[Int32, IObservable`1[Int32], Int32]", new Func<int, IObservable<int>, int>((v, u) => v) },
  144. { "Func`3[IDisposable, CancellationToken, Task`1[IObservable`1[Int32]]]", new Func<IDisposable, CancellationToken, Task<IObservable<int>>>((v, u) => Task.FromResult(Observable.Return(1))) },
  145. { "Func`3[IObserver`1[Int32], CancellationToken, Task]", new Func<IObserver<int>, CancellationToken, Task>((v, w) => Task.FromResult(1)) },
  146. { "Func`3[IObserver`1[Int32], CancellationToken, Task`1[IDisposable]]", new Func<IObserver<int>, CancellationToken, Task<IDisposable>>((v, w) => Task.FromResult(Disposable.Empty)) },
  147. { "Func`3[IObserver`1[Int32], CancellationToken, Task`1[Action]]", new Func<IObserver<int>, CancellationToken, Task<Action>>((v, w) => Task.FromResult<Action>(() => { })) },
  148. { "Func`3[AsyncCallback, Object, IAsyncResult]", new Func<AsyncCallback, object, IAsyncResult>((v, w) => null) },
  149. { "Func`4[Int32, Int32, CancellationToken, Task`1[Int32]]", new Func<int, int, CancellationToken, Task<int>>((v, u, w) => Task.FromResult(v)) },
  150. { "Func`4[Int32, Int32, Int32, Int32]", new Func<int, int, int, int>((v1, v2, v3) => v1 + v2 + v3) },
  151. { "Func`4[Int32, AsyncCallback, Object, IAsyncResult]", new Func<int, AsyncCallback, object, IAsyncResult>((v, w, x) => null) },
  152. { "Func`5[Int32, Int32, Int32, Int32, Int32]", new Func<int, int, int, int, int>((v1, v2, v3, v4) => v1 + v2 + v3 + v4) },
  153. { "Func`6[Int32, Int32, Int32, Int32, Int32, Int32]", new Func<int, int, int, int, int, int>((v1, v2, v3, v4, v5) => v1 + v2 + v3 + v4 + v5) },
  154. { "Func`7[Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Func<int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6) => v1 + v2 + v3 + v4 + v5 + v6) },
  155. { "Func`8[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Func<int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7) => v1 + v2 + v3 + v4 + v5 + v6 + v7) },
  156. { "Func`9[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Func<int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8) => v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8) },
  157. { "Func`10[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Func<int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9) => v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9) },
  158. { "Func`11[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Func<int, int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) => v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10) },
  159. { "Func`12[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Func<int, int, int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) => v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11) },
  160. { "Func`13[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Func<int, int, int, int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) => v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + v12) },
  161. { "Func`14[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) => v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + v12 + v13) },
  162. { "Func`15[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) => v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14) },
  163. { "Func`16[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) => v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15) },
  164. { "Func`17[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) => v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16) },
  165. {
  166. "Plan`1[][Int32]",
  167. new Plan<int>[] {
  168. Observable.Return(1).Then(v => v)
  169. }
  170. },
  171. {
  172. "IEnumerable`1[Plan`1[Int32]]",
  173. new Plan<int>[] {
  174. Observable.Return(1).Then(v => v)
  175. }
  176. },
  177. { "Action`3[Int32, Int32, Int32]", new Action<int, int, int>((v1, v2, v3) => { }) },
  178. { "Action`4[Int32, Int32, Int32, Int32]", new Action<int, int, int, int>((v1, v2, v3, v4) => { }) },
  179. { "Action`5[Int32, Int32, Int32, Int32, Int32]", new Action<int, int, int, int, int>((v1, v2, v3, v4, v5) => { }) },
  180. { "Action`6[Int32, Int32, Int32, Int32, Int32, Int32]", new Action<int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6) => { }) },
  181. { "Action`7[Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Action<int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7) => { }) },
  182. { "Action`8[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Action<int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8) => { }) },
  183. { "Action`9[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Action<int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9) => { }) },
  184. { "Action`10[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Action<int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) => { }) },
  185. { "Action`11[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Action<int, int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) => { }) },
  186. { "Action`12[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Action<int, int, int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) => { }) },
  187. { "Action`13[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Action<int, int, int, int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) => { }) },
  188. { "Action`14[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Action<int, int, int, int, int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) => { }) },
  189. { "Action`15[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Action<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) => { }) },
  190. { "Action`16[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32]", new Action<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) => { }) },
  191. { "Func`5[Int32, Int32, AsyncCallback, Object, IAsyncResult]", new Func<int, int, AsyncCallback, object, IAsyncResult>((v1, v2, v3, v4) => null) },
  192. { "Func`6[Int32, Int32, Int32, AsyncCallback, Object, IAsyncResult]", new Func<int, int, int, AsyncCallback, object, IAsyncResult>((v1, v2, v3, v4, v5) => null) },
  193. { "Func`7[Int32, Int32, Int32, Int32, AsyncCallback, Object, IAsyncResult]", new Func<int, int, int, int, AsyncCallback, object, IAsyncResult>((v1, v2, v3, v4, v5, v6) => null) },
  194. { "Func`8[Int32, Int32, Int32, Int32, Int32, AsyncCallback, Object, IAsyncResult]", new Func<int, int, int, int, int, AsyncCallback, object, IAsyncResult>((v1, v2, v3, v4, v5, v6, v7) => null) },
  195. { "Func`9[Int32, Int32, Int32, Int32, Int32, Int32, AsyncCallback, Object, IAsyncResult]", new Func<int, int, int, int, int, int, AsyncCallback, object, IAsyncResult>((v1, v2, v3, v4, v5, v6, v7, v8) => null) },
  196. { "Func`10[Int32, Int32, Int32, Int32, Int32, Int32, Int32, AsyncCallback, Object, IAsyncResult]", new Func<int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult>((v1, v2, v3, v4, v5, v6, v7, v8, v9) => null) },
  197. { "Func`11[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, AsyncCallback, Object, IAsyncResult]", new Func<int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) => null) },
  198. { "Func`12[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, AsyncCallback, Object, IAsyncResult]", new Func<int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) => null) },
  199. { "Func`13[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, AsyncCallback, Object, IAsyncResult]", new Func<int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) => null) },
  200. { "Func`14[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, AsyncCallback, Object, IAsyncResult]", new Func<int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) => null) },
  201. { "Func`15[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, AsyncCallback, Object, IAsyncResult]", new Func<int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) => null) },
  202. { "Func`16[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, AsyncCallback, Object, IAsyncResult]", new Func<int, int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) => null) },
  203. { "Func`17[Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32, AsyncCallback, Object, IAsyncResult]", new Func<int, int, int, int, int, int, int, int, int, int, int, int, int, int, AsyncCallback, object, IAsyncResult>((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) => null) }
  204. };
  205. #pragma warning restore IDE0300 // Simplify collection initialization
  206. }
  207. #endregion
  208. [TestMethod]
  209. public void Verify_Observable()
  210. {
  211. VerifyClass(typeof(Observable));
  212. }
  213. [TestMethod]
  214. public void Verify_ObservableEx()
  215. {
  216. VerifyClass(typeof(ObservableEx));
  217. }
  218. #region + Verification method +
  219. /// <summary>
  220. /// Verify that public static members of the class
  221. /// check for nulls in their arguments as
  222. /// well as when invoking Subscribe with null.
  223. /// </summary>
  224. /// <param name="type">The type to verify.</param>
  225. private static void VerifyClass(Type type)
  226. {
  227. foreach (var method in type.GetMethods())
  228. {
  229. // public static only (skip methods like Equals)
  230. if (!method.IsPublic || !method.IsStatic)
  231. {
  232. continue;
  233. }
  234. var m = default(MethodInfo);
  235. // Is this a generic method?
  236. if (method.IsGenericMethodDefinition)
  237. {
  238. // we need to specialize it to concrete types
  239. // for the reflective call to work
  240. // get the type arguments
  241. var ga = method.GetGenericArguments();
  242. var targs = new Type[ga.Length];
  243. // fill in the type arguments
  244. for (var k = 0; k < targs.Length; k++)
  245. {
  246. // watch out for type constrains
  247. // the default typeof(int) will not work when
  248. // exception or IDisposable is required at minimum
  249. var gac = ga[k].GetGenericParameterConstraints();
  250. // no type constraints
  251. if (gac.Length == 0)
  252. {
  253. targs[k] = typeof(int);
  254. }
  255. else if (gac[0] == typeof(Exception))
  256. {
  257. targs[k] = typeof(Exception);
  258. }
  259. else if (gac[0] == typeof(IDisposable))
  260. {
  261. targs[k] = typeof(IDisposable);
  262. }
  263. else
  264. {
  265. // If we get here, a new rule should be added above
  266. throw new Exception("Unknown constraint: " + gac + "\r\n" + method);
  267. }
  268. }
  269. // generate a specialized method with the concrete generic arguments
  270. try
  271. {
  272. m = method.MakeGenericMethod(targs);
  273. }
  274. catch (Exception ex)
  275. {
  276. throw new Exception("MakeGenericMethod threw: " + method, ex);
  277. }
  278. }
  279. else
  280. {
  281. // non generic method, we can invoke this directly
  282. m = method;
  283. }
  284. var args = m.GetParameters();
  285. // for each parameter of the (generic) method
  286. for (var i = 0; i < args.Length; i++)
  287. {
  288. // prepare a pattern for the method invocation
  289. var margs = new object[args.Length];
  290. // some arguments can be null, often indicated with a default == null marker
  291. // this tracks this case and forgives for not throwing an ArgumentNullException
  292. var argumentCanBeNull = true;
  293. // for each argument index
  294. // with the loop i, this creates an N x N matrix where in each row, one argument is null
  295. for (var j = 0; j < args.Length; j++)
  296. {
  297. // figure out the type of the argument
  298. var pt = args[j].ParameterType;
  299. // by using some type naming convention as string
  300. var paramTypeName = TypeNameOf(pt);
  301. // classes, interfaces, arrays and abstract markers can be null
  302. // for the diagonal entries of the test matrix
  303. if (j == i && (pt.IsClass || pt.IsInterface || pt.IsArray || pt.IsAbstract))
  304. {
  305. margs[j] = null;
  306. // check if the argument can be actually
  307. argumentCanBeNull = args[j].HasDefaultValue && args[j].DefaultValue == null;
  308. }
  309. else
  310. {
  311. // this argument is not tested for null or is not a null type
  312. // find the default instance for it
  313. if (_defaultValues.ContainsKey(paramTypeName))
  314. {
  315. margs[j] = _defaultValues[paramTypeName];
  316. }
  317. else
  318. {
  319. // default values have to be instantiated in _defaultValues for
  320. // each possible generic type arguments.
  321. // this will indicate what concrete instance value is missing.
  322. throw new Exception("Default instance not found for: " + paramTypeName + "\r\n\r\n" + m);
  323. }
  324. }
  325. }
  326. // assume it threw
  327. var thrown = true;
  328. var obj = default(object);
  329. try
  330. {
  331. obj = m.Invoke(null, margs);
  332. thrown = false;
  333. }
  334. catch (ArgumentNullException)
  335. {
  336. // expected exception, just in case
  337. }
  338. catch (Exception ex)
  339. {
  340. // reflection wraps the actual exception, let's unwrap it
  341. if (ex.InnerException is not ArgumentNullException)
  342. {
  343. throw new Exception("Method threw: " + method + " @ " + i, ex);
  344. }
  345. }
  346. // if the call didn't throw and the argument being tested isn't defaulted to null, throw
  347. if (!thrown && !argumentCanBeNull)
  348. {
  349. throw new Exception("Should have thrown: " + method + " @ " + i);
  350. }
  351. // if the call didn't throw and returned a null object, throw
  352. // no operators should return null
  353. if (obj == null && !thrown)
  354. {
  355. throw new NullReferenceException("null return: " + method + " @ " + i);
  356. }
  357. }
  358. // Now check the same method with valid arguments but
  359. // Subscribe(null) if it returns an IObservable subclass
  360. if (m.ReturnType.Name.Equals("IObservable`1")
  361. || m.ReturnType.Name.Equals("IConnectableObservable`1"))
  362. {
  363. // these will fail other argument validation with the defaults, skip them
  364. if (m.Name.Equals("FromEventPattern"))
  365. {
  366. continue;
  367. }
  368. // prepare method arguments
  369. var margs = new object[args.Length];
  370. for (var j = 0; j < args.Length; j++)
  371. {
  372. var pt = args[j].ParameterType;
  373. var paramTypeName = TypeNameOf(pt);
  374. if (_defaultValues.ContainsKey(paramTypeName))
  375. {
  376. margs[j] = _defaultValues[paramTypeName];
  377. }
  378. else
  379. {
  380. // default values have to be instantiated in _defaultValues for
  381. // each possible generic type arguments.
  382. // this will indicate what concrete instance value is missing.
  383. //
  384. // it may fail independently of the null test above because
  385. // the particular type is non-nullable thus skipped above
  386. // or was the solo argument and it got never tested with a non-null
  387. // value
  388. throw new Exception("Default instance not found (Subscribe(null) check): " + paramTypeName + "\r\n\r\n" + m);
  389. }
  390. }
  391. // Assume it throws
  392. var thrown = true;
  393. try
  394. {
  395. // Should not return null, but would be mistaken for
  396. // throwing because of Subscribe(null)
  397. if (m.Invoke(null, margs) is IObservable<int> o)
  398. {
  399. o.Subscribe(null);
  400. thrown = false;
  401. }
  402. }
  403. catch (ArgumentNullException)
  404. {
  405. // expected
  406. }
  407. catch (Exception ex)
  408. {
  409. // Unexpected exception
  410. // Maybe some other validation failed inside the method call
  411. // Consider skipping this method (set)
  412. //
  413. // Otherwise, the operator may run with the null IObserver
  414. // for a while and crash later.
  415. throw new Exception("Method threw (Subscribe(null) check): " + m, ex);
  416. }
  417. // If it didn't throw, report it
  418. if (!thrown)
  419. {
  420. throw new Exception("Should have thrown (Subscribe(null) check): " + m);
  421. }
  422. }
  423. }
  424. }
  425. /// <summary>
  426. /// Generate a string representation of a possibly generic type
  427. /// that is not verbose (i.e, no "System." everywhere).
  428. /// </summary>
  429. /// <param name="type">The type to get a string representation</param>
  430. /// <returns>The string representation of a possibly generic type</returns>
  431. private static string TypeNameOf(Type type)
  432. {
  433. var ga = type.GetGenericArguments();
  434. if (ga.Length == 0)
  435. {
  436. return type.Name;
  437. }
  438. return type.Name + "[" + string.Join(", ", ga.Select(t => TypeNameOf(t)).ToArray()) + "]";
  439. }
  440. #endregion
  441. }
  442. }