SchedulerTest.cs 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reactive.Concurrency;
  6. using System.Reactive.Disposables;
  7. using System.Reactive.Linq;
  8. using System.Reactive.PlatformServices;
  9. using System.Runtime.CompilerServices;
  10. using System.Threading;
  11. using Microsoft.VisualStudio.TestTools.UnitTesting;
  12. using Microsoft.Reactive.Testing;
  13. #if HAS_WINFORMS
  14. using System.Windows.Forms;
  15. #endif
  16. namespace ReactiveTests.Tests
  17. {
  18. [TestClass]
  19. public class SchedulerTest
  20. {
  21. #region IScheduler
  22. [TestMethod]
  23. public void Scheduler_ArgumentChecks()
  24. {
  25. var ms = new MyScheduler();
  26. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), a => { }));
  27. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), () => { }));
  28. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), 1, (a, s) => { }));
  29. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(ms, default(Action<Action>)));
  30. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(ms, 1, default(Action<int, Action<int>>)));
  31. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), DateTimeOffset.Now, a => { }));
  32. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), DateTimeOffset.Now, () => { }));
  33. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), 1, DateTimeOffset.Now, (a, s) => { }));
  34. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(ms, DateTimeOffset.Now, default(Action<Action<DateTimeOffset>>)));
  35. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(ms, 1, DateTimeOffset.Now, default(Action<int, Action<int, DateTimeOffset>>)));
  36. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), TimeSpan.Zero, a => { }));
  37. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), TimeSpan.Zero, () => { }));
  38. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), 1, TimeSpan.Zero, (a, s) => { }));
  39. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(ms, TimeSpan.Zero, default(Action<Action<TimeSpan>>)));
  40. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(ms, 1, TimeSpan.Zero, default(Action<int, Action<int, TimeSpan>>)));
  41. }
  42. [TestMethod]
  43. public void Schedulers_ArgumentChecks()
  44. {
  45. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.CurrentThread.Schedule(default(Action)));
  46. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.CurrentThread.Schedule(TimeSpan.Zero, default(Action)));
  47. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.CurrentThread.Schedule(DateTimeOffset.MaxValue, default(Action)));
  48. ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherScheduler.Instance.Schedule(default(Action)));
  49. ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherScheduler.Instance.Schedule(TimeSpan.Zero, default(Action)));
  50. ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherScheduler.Instance.Schedule(DateTimeOffset.MaxValue, default(Action)));
  51. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Immediate.Schedule(default(Action)));
  52. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Immediate.Schedule(TimeSpan.Zero, default(Action)));
  53. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Immediate.Schedule(DateTimeOffset.MaxValue, default(Action)));
  54. ReactiveAssert.Throws<ArgumentNullException>(() => NewThreadScheduler.Default.Schedule(default(Action)));
  55. ReactiveAssert.Throws<ArgumentNullException>(() => NewThreadScheduler.Default.Schedule(TimeSpan.Zero, default(Action)));
  56. ReactiveAssert.Throws<ArgumentNullException>(() => NewThreadScheduler.Default.Schedule(DateTimeOffset.MaxValue, default(Action)));
  57. #if !NO_TPL
  58. ReactiveAssert.Throws<ArgumentNullException>(() => TaskPoolScheduler.Default.Schedule(default(Action)));
  59. ReactiveAssert.Throws<ArgumentNullException>(() => TaskPoolScheduler.Default.Schedule(TimeSpan.Zero, default(Action)));
  60. ReactiveAssert.Throws<ArgumentNullException>(() => TaskPoolScheduler.Default.Schedule(DateTimeOffset.MaxValue, default(Action)));
  61. #endif
  62. ReactiveAssert.Throws<ArgumentNullException>(() => DefaultScheduler.Instance.Schedule(default(Action)));
  63. ReactiveAssert.Throws<ArgumentNullException>(() => DefaultScheduler.Instance.Schedule(TimeSpan.Zero, default(Action)));
  64. ReactiveAssert.Throws<ArgumentNullException>(() => DefaultScheduler.Instance.Schedule(DateTimeOffset.MaxValue, default(Action)));
  65. ReactiveAssert.Throws<ArgumentNullException>(() => DefaultScheduler.Instance.SchedulePeriodic(42, TimeSpan.FromSeconds(1), default(Func<int, int>)));
  66. ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => DefaultScheduler.Instance.SchedulePeriodic(42, TimeSpan.FromSeconds(-1), _ => _));
  67. #if HAS_WINFORMS
  68. var lbl = new Label();
  69. ReactiveAssert.Throws<ArgumentNullException>(() => new ControlScheduler(lbl).Schedule(default(Action)));
  70. ReactiveAssert.Throws<ArgumentNullException>(() => new ControlScheduler(lbl).Schedule(TimeSpan.Zero, default(Action)));
  71. ReactiveAssert.Throws<ArgumentNullException>(() => new ControlScheduler(lbl).Schedule(DateTimeOffset.MaxValue, default(Action)));
  72. #endif
  73. var ctx = new SynchronizationContext();
  74. ReactiveAssert.Throws<ArgumentNullException>(() => new SynchronizationContextScheduler(ctx).Schedule(default(Action)));
  75. ReactiveAssert.Throws<ArgumentNullException>(() => new SynchronizationContextScheduler(ctx).Schedule(TimeSpan.Zero, default(Action)));
  76. ReactiveAssert.Throws<ArgumentNullException>(() => new SynchronizationContextScheduler(ctx).Schedule(DateTimeOffset.MaxValue, default(Action)));
  77. }
  78. [TestMethod]
  79. public void Scheduler_ScheduleNonRecursive()
  80. {
  81. var ms = new MyScheduler();
  82. var res = false;
  83. Scheduler.Schedule(ms, a => { res = true; });
  84. Assert.IsTrue(res);
  85. }
  86. [TestMethod]
  87. public void Scheduler_ScheduleRecursive()
  88. {
  89. var ms = new MyScheduler();
  90. var i = 0;
  91. Scheduler.Schedule(ms, a => { if (++i < 10) a(); });
  92. Assert.AreEqual(10, i);
  93. }
  94. [TestMethod]
  95. public void Scheduler_ScheduleWithTimeNonRecursive()
  96. {
  97. var now = DateTimeOffset.Now;
  98. var ms = new MyScheduler(now) { Check = (a, s, t) => { Assert.IsTrue(t == TimeSpan.Zero); } };
  99. var res = false;
  100. Scheduler.Schedule(ms, now, a => { res = true; });
  101. Assert.IsTrue(res);
  102. Assert.IsTrue(ms.WaitCycles == 0);
  103. }
  104. [TestMethod]
  105. public void Scheduler_ScheduleWithTimeRecursive()
  106. {
  107. var now = DateTimeOffset.Now;
  108. var i = 0;
  109. var ms = new MyScheduler(now) { Check = (a, s, t) => { Assert.IsTrue(t == TimeSpan.Zero); } };
  110. Scheduler.Schedule(ms, now, a => { if (++i < 10) a(now); });
  111. Assert.IsTrue(ms.WaitCycles == 0);
  112. Assert.AreEqual(10, i);
  113. }
  114. [TestMethod]
  115. public void Scheduler_ScheduleWithTimeSpanNonRecursive()
  116. {
  117. var now = DateTimeOffset.Now;
  118. var ms = new MyScheduler(now) { Check = (a, s, t) => { Assert.IsTrue(t == TimeSpan.Zero); } };
  119. var res = false;
  120. Scheduler.Schedule(ms, TimeSpan.Zero, a => { res = true; });
  121. Assert.IsTrue(res);
  122. Assert.IsTrue(ms.WaitCycles == 0);
  123. }
  124. [TestMethod]
  125. public void Scheduler_ScheduleWithTimeSpanRecursive()
  126. {
  127. var now = DateTimeOffset.Now;
  128. var ms = new MyScheduler(now) { Check = (a, s, t) => { Assert.IsTrue(t < TimeSpan.FromTicks(10)); } };
  129. var i = 0;
  130. Scheduler.Schedule(ms, TimeSpan.Zero, a => { if (++i < 10) a(TimeSpan.FromTicks(i)); });
  131. Assert.IsTrue(ms.WaitCycles == Enumerable.Range(1, 9).Sum());
  132. Assert.AreEqual(10, i);
  133. }
  134. [TestMethod]
  135. public void Scheduler_StateThreading()
  136. {
  137. var lst = new List<int>();
  138. Scheduler.Immediate.Schedule(0, (i, a) =>
  139. {
  140. lst.Add(i);
  141. if (i < 9)
  142. a(i + 1);
  143. });
  144. Assert.IsTrue(lst.SequenceEqual(Enumerable.Range(0, 10)));
  145. }
  146. [TestMethod]
  147. public void Scheduler_Builtins()
  148. {
  149. // Default
  150. {
  151. var e = new ManualResetEvent(false);
  152. Scheduler.Default.Schedule(() => e.Set());
  153. e.WaitOne();
  154. }
  155. if (!Utils.IsRunningWithPortableLibraryBinaries())
  156. {
  157. Scheduler_Builtins_NoPlib();
  158. }
  159. }
  160. [MethodImpl(MethodImplOptions.NoInlining)]
  161. private void Scheduler_Builtins_NoPlib()
  162. {
  163. #if !PLIB
  164. // ThreadPool
  165. {
  166. var e = new ManualResetEvent(false);
  167. Scheduler.ThreadPool.Schedule(() => e.Set());
  168. e.WaitOne();
  169. }
  170. #endif
  171. #if !NO_THREAD
  172. // NewThread
  173. {
  174. var e = new ManualResetEvent(false);
  175. Scheduler.NewThread.Schedule(() => e.Set());
  176. e.WaitOne();
  177. }
  178. #endif
  179. #if !NO_TPL
  180. // TaskPool
  181. {
  182. var e = new ManualResetEvent(false);
  183. Scheduler.TaskPool.Schedule(() => e.Set());
  184. e.WaitOne();
  185. }
  186. #endif
  187. }
  188. #endregion
  189. #region ISchedulerLongRunning
  190. #if !NO_PERF
  191. [TestMethod]
  192. public void Scheduler_LongRunning_ArgumentChecking()
  193. {
  194. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.ScheduleLongRunning(null, c => { }));
  195. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.ScheduleLongRunning(ThreadPoolScheduler.Instance, default(Action<ICancelable>)));
  196. }
  197. [TestMethod]
  198. public void Scheduler_Periodic_ArgumentChecking()
  199. {
  200. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.SchedulePeriodic(null, TimeSpan.FromSeconds(1), () => { }));
  201. ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Scheduler.SchedulePeriodic(ThreadPoolScheduler.Instance, TimeSpan.FromSeconds(-1), () => { }));
  202. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.SchedulePeriodic(ThreadPoolScheduler.Instance, TimeSpan.FromSeconds(1), default(Action)));
  203. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.SchedulePeriodic<int>(null, 42, TimeSpan.FromSeconds(1), _ => { }));
  204. ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Scheduler.SchedulePeriodic<int>(ThreadPoolScheduler.Instance, 42, TimeSpan.FromSeconds(-1), _ => { }));
  205. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.SchedulePeriodic<int>(ThreadPoolScheduler.Instance, 42, TimeSpan.FromSeconds(1), default(Action<int>)));
  206. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.SchedulePeriodic<int>(null, 42, TimeSpan.FromSeconds(1), _ => _));
  207. ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Scheduler.SchedulePeriodic<int>(ThreadPoolScheduler.Instance, 42, TimeSpan.FromSeconds(-1), _ => _));
  208. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.SchedulePeriodic<int>(ThreadPoolScheduler.Instance, 42, TimeSpan.FromSeconds(1), default(Func<int, int>)));
  209. }
  210. [TestMethod]
  211. public void Scheduler_Stopwatch_Emulation()
  212. {
  213. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.StartStopwatch(null));
  214. }
  215. #if !NO_TPL
  216. [TestMethod]
  217. public void Scheduler_LongRunning1()
  218. {
  219. var s = TaskPoolScheduler.Default;
  220. var x = new ManualResetEvent(false);
  221. var e = new ManualResetEvent(false);
  222. var d = s.ScheduleLongRunning(42, (state, cancel) =>
  223. {
  224. while (!cancel.IsDisposed)
  225. x.Set();
  226. e.Set();
  227. });
  228. x.WaitOne();
  229. d.Dispose();
  230. e.WaitOne();
  231. }
  232. [TestMethod]
  233. public void Scheduler_LongRunning2()
  234. {
  235. var s = TaskPoolScheduler.Default;
  236. var x = new ManualResetEvent(false);
  237. var e = new ManualResetEvent(false);
  238. var d = s.ScheduleLongRunning(cancel =>
  239. {
  240. while (!cancel.IsDisposed)
  241. x.Set();
  242. e.Set();
  243. });
  244. x.WaitOne();
  245. d.Dispose();
  246. e.WaitOne();
  247. }
  248. #endif
  249. #endif
  250. #endregion
  251. #region ISchedulerPeriodic
  252. #if !NO_PERF
  253. [TestMethod]
  254. public void Scheduler_Periodic1()
  255. {
  256. var n = 0;
  257. var e = new ManualResetEvent(false);
  258. var d = ThreadPoolScheduler.Instance.SchedulePeriodic(TimeSpan.FromMilliseconds(50), () =>
  259. {
  260. if (n++ == 10)
  261. e.Set();
  262. });
  263. e.WaitOne();
  264. d.Dispose();
  265. }
  266. [TestMethod]
  267. public void Scheduler_Periodic2()
  268. {
  269. var n = 0;
  270. var e = new ManualResetEvent(false);
  271. var d = ThreadPoolScheduler.Instance.SchedulePeriodic(42, TimeSpan.FromMilliseconds(50), x =>
  272. {
  273. Assert.AreEqual(42, x);
  274. if (n++ == 10)
  275. e.Set();
  276. });
  277. e.WaitOne();
  278. d.Dispose();
  279. }
  280. #if DESKTOPCLR
  281. [TestMethod]
  282. public void Scheduler_Periodic_HostLifecycleManagement()
  283. {
  284. var cur = AppDomain.CurrentDomain.BaseDirectory;
  285. var domain = AppDomain.CreateDomain("HLN", null, new AppDomainSetup { ApplicationBase = cur });
  286. domain.DoCallBack(() =>
  287. {
  288. var pep = PlatformEnlightenmentProvider.Current;
  289. try
  290. {
  291. var hln = new HLN();
  292. PlatformEnlightenmentProvider.Current = new PEP(hln);
  293. var s = ThreadPoolScheduler.Instance.DisableOptimizations(typeof(ISchedulerPeriodic));
  294. var n = 0;
  295. var e = new ManualResetEvent(false);
  296. var d = Observable.Interval(TimeSpan.FromMilliseconds(100), s).Subscribe(_ =>
  297. {
  298. if (n++ == 10)
  299. e.Set();
  300. });
  301. hln.OnSuspending();
  302. hln.OnResuming();
  303. Thread.Sleep(250);
  304. hln.OnSuspending();
  305. Thread.Sleep(150);
  306. hln.OnResuming();
  307. Thread.Sleep(50);
  308. hln.OnSuspending();
  309. hln.OnResuming();
  310. e.WaitOne();
  311. d.Dispose();
  312. }
  313. finally
  314. {
  315. PlatformEnlightenmentProvider.Current = pep;
  316. }
  317. });
  318. }
  319. class PEP : IPlatformEnlightenmentProvider
  320. {
  321. private readonly IPlatformEnlightenmentProvider _old;
  322. private readonly IHostLifecycleNotifications _hln;
  323. public PEP(HLN hln)
  324. {
  325. _old = PlatformEnlightenmentProvider.Current;
  326. _hln = hln;
  327. }
  328. public T GetService<T>(params object[] args) where T : class
  329. {
  330. if (typeof(T) == typeof(IHostLifecycleNotifications))
  331. {
  332. return (T)(object)_hln;
  333. }
  334. return _old.GetService<T>(args);
  335. }
  336. }
  337. class HLN : IHostLifecycleNotifications
  338. {
  339. public event EventHandler<HostSuspendingEventArgs> Suspending;
  340. public event EventHandler<HostResumingEventArgs> Resuming;
  341. public void OnSuspending()
  342. {
  343. var s = Suspending;
  344. if (s != null)
  345. s(this, null);
  346. }
  347. public void OnResuming()
  348. {
  349. var s = Resuming;
  350. if (s != null)
  351. s(this, null);
  352. }
  353. }
  354. #endif
  355. #endif
  356. #endregion
  357. #region DisableOptimizations
  358. #if !NO_PERF
  359. [TestMethod]
  360. public void DisableOptimizations_ArgumentChecking()
  361. {
  362. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.DisableOptimizations(default(IScheduler)));
  363. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.DisableOptimizations(default(IScheduler), new Type[0]));
  364. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.DisableOptimizations(ThreadPoolScheduler.Instance, default(Type[])));
  365. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.DisableOptimizations(Scheduler.Default).Schedule(42, default(Func<IScheduler, int, IDisposable>)));
  366. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.DisableOptimizations(Scheduler.Default).Schedule(42, TimeSpan.FromSeconds(1), default(Func<IScheduler, int, IDisposable>)));
  367. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.DisableOptimizations(Scheduler.Default).Schedule(42, DateTimeOffset.Now, default(Func<IScheduler, int, IDisposable>)));
  368. }
  369. #if !NO_TPL
  370. [TestMethod]
  371. public void DisableOptimizations1()
  372. {
  373. var s = TaskPoolScheduler.Default;
  374. Assert.IsTrue(s is IServiceProvider);
  375. var t = s.DisableOptimizations();
  376. var d = t.Now - s.Now;
  377. Assert.IsTrue(d.TotalSeconds < 1);
  378. var e1 = new ManualResetEvent(false);
  379. t.Schedule(42, (self, state) =>
  380. {
  381. e1.Set();
  382. return Disposable.Empty;
  383. });
  384. e1.WaitOne();
  385. var e2 = new ManualResetEvent(false);
  386. t.Schedule(42, TimeSpan.FromMilliseconds(1), (self, state) =>
  387. {
  388. e2.Set();
  389. return Disposable.Empty;
  390. });
  391. e2.WaitOne();
  392. var e3 = new ManualResetEvent(false);
  393. t.Schedule(42, DateTimeOffset.Now.AddMilliseconds(10), (self, state) =>
  394. {
  395. e3.Set();
  396. return Disposable.Empty;
  397. });
  398. e3.WaitOne();
  399. }
  400. [TestMethod]
  401. public void DisableOptimizations2()
  402. {
  403. var s = TaskPoolScheduler.Default;
  404. Assert.IsTrue(s is IServiceProvider);
  405. var lr1 = ((IServiceProvider)s).GetService(typeof(ISchedulerLongRunning));
  406. Assert.IsNotNull(lr1);
  407. var e1 = new ManualResetEvent(false);
  408. s.Schedule(42, (self, state) =>
  409. {
  410. Assert.IsTrue(self is IServiceProvider);
  411. var lrr1 = ((IServiceProvider)self).GetService(typeof(ISchedulerLongRunning));
  412. Assert.IsNotNull(lrr1);
  413. e1.Set();
  414. return Disposable.Empty;
  415. });
  416. e1.WaitOne();
  417. var t = s.DisableOptimizations();
  418. Assert.IsTrue(t is IServiceProvider);
  419. var lr2 = ((IServiceProvider)t).GetService(typeof(ISchedulerLongRunning));
  420. Assert.IsNull(lr2);
  421. var e2 = new ManualResetEvent(false);
  422. t.Schedule(42, (self, state) =>
  423. {
  424. Assert.IsTrue(self is IServiceProvider);
  425. var lrr2 = ((IServiceProvider)self).GetService(typeof(ISchedulerLongRunning));
  426. Assert.IsNull(lrr2);
  427. e2.Set();
  428. return Disposable.Empty;
  429. });
  430. e2.WaitOne();
  431. }
  432. [TestMethod]
  433. public void DisableOptimizations3()
  434. {
  435. var s = TaskPoolScheduler.Default;
  436. Assert.IsTrue(s is IServiceProvider);
  437. var lr1 = ((IServiceProvider)s).GetService(typeof(ISchedulerLongRunning));
  438. Assert.IsNotNull(lr1);
  439. var p1 = ((IServiceProvider)s).GetService(typeof(ISchedulerPeriodic));
  440. Assert.IsNotNull(p1);
  441. var e1 = new ManualResetEvent(false);
  442. s.Schedule(42, (self, state) =>
  443. {
  444. Assert.IsTrue(self is IServiceProvider);
  445. var lrr1 = ((IServiceProvider)self).GetService(typeof(ISchedulerLongRunning));
  446. Assert.IsNotNull(lrr1);
  447. var pr1 = ((IServiceProvider)self).GetService(typeof(ISchedulerPeriodic));
  448. Assert.IsNotNull(pr1);
  449. e1.Set();
  450. return Disposable.Empty;
  451. });
  452. e1.WaitOne();
  453. var t = s.DisableOptimizations(typeof(ISchedulerLongRunning));
  454. Assert.IsTrue(t is IServiceProvider);
  455. var lr2 = ((IServiceProvider)t).GetService(typeof(ISchedulerLongRunning));
  456. Assert.IsNull(lr2);
  457. var p2 = ((IServiceProvider)t).GetService(typeof(ISchedulerPeriodic));
  458. Assert.IsNotNull(p2);
  459. var e2 = new ManualResetEvent(false);
  460. t.Schedule(42, (self, state) =>
  461. {
  462. Assert.IsTrue(self is IServiceProvider);
  463. var lrr2 = ((IServiceProvider)self).GetService(typeof(ISchedulerLongRunning));
  464. Assert.IsNull(lrr2);
  465. var pr2 = ((IServiceProvider)self).GetService(typeof(ISchedulerPeriodic));
  466. Assert.IsNotNull(pr2);
  467. e2.Set();
  468. return Disposable.Empty;
  469. });
  470. e2.WaitOne();
  471. }
  472. #endif
  473. #endif
  474. [TestMethod]
  475. public void DisableOptimizations_UnknownService()
  476. {
  477. var s = new MyScheduler();
  478. Assert.IsFalse(s is IServiceProvider);
  479. var d = s.DisableOptimizations();
  480. Assert.IsTrue(d is IServiceProvider);
  481. Assert.IsNull(((IServiceProvider)d).GetService(typeof(bool)));
  482. }
  483. class MyScheduler : IScheduler
  484. {
  485. public MyScheduler()
  486. : this(DateTimeOffset.Now)
  487. {
  488. }
  489. public MyScheduler(DateTimeOffset now)
  490. {
  491. Now = now;
  492. }
  493. public DateTimeOffset Now
  494. {
  495. get;
  496. private set;
  497. }
  498. public IDisposable Schedule<T>(T state, Func<IScheduler, T, IDisposable> action)
  499. {
  500. return action(this, state);
  501. }
  502. public Action<Action<object>, object, TimeSpan> Check { get; set; }
  503. public long WaitCycles { get; set; }
  504. public IDisposable Schedule<T>(T state, TimeSpan dueTime, Func<IScheduler, T, IDisposable> action)
  505. {
  506. Check(o => action(this, (T)o), state, dueTime);
  507. WaitCycles += dueTime.Ticks;
  508. return action(this, state);
  509. }
  510. public IDisposable Schedule<T>(T state, DateTimeOffset dueTime, Func<IScheduler, T, IDisposable> action)
  511. {
  512. return Schedule(state, dueTime - Now, action);
  513. }
  514. }
  515. #endregion
  516. #region Catch
  517. [TestMethod]
  518. public void Catch_ArgumentChecking()
  519. {
  520. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Catch<Exception>(default(IScheduler), _ => true));
  521. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Catch<Exception>(Scheduler.Default, default(Func<Exception, bool>)));
  522. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Catch<Exception>(Scheduler.Default, _ => true).Schedule(42, default(Func<IScheduler, int, IDisposable>)));
  523. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Catch<Exception>(Scheduler.Default, _ => true).Schedule(42, TimeSpan.FromSeconds(1), default(Func<IScheduler, int, IDisposable>)));
  524. ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Catch<Exception>(Scheduler.Default, _ => true).Schedule(42, DateTimeOffset.Now, default(Func<IScheduler, int, IDisposable>)));
  525. }
  526. [TestMethod]
  527. public void Catch_Builtin_Swallow_Shallow()
  528. {
  529. var done = new ManualResetEvent(false);
  530. var swallow = Scheduler.Default.Catch<InvalidOperationException>(_ => { done.Set(); return true; });
  531. swallow.Schedule(() =>
  532. {
  533. throw new InvalidOperationException();
  534. });
  535. done.WaitOne();
  536. }
  537. [TestMethod]
  538. public void Catch_Builtin_Swallow_Recursive()
  539. {
  540. var done = new ManualResetEvent(false);
  541. var swallow = Scheduler.Default.Catch<InvalidOperationException>(_ => { done.Set(); return true; });
  542. swallow.Schedule(42, (self, state) =>
  543. {
  544. return self.Schedule(() =>
  545. {
  546. throw new InvalidOperationException();
  547. });
  548. });
  549. done.WaitOne();
  550. }
  551. [TestMethod]
  552. public void Catch_Custom_Unhandled()
  553. {
  554. var err = default(Exception);
  555. var scheduler = new MyExceptionScheduler(ex_ => err = ex_);
  556. scheduler.Catch<InvalidOperationException>(_ => true).Schedule(() =>
  557. {
  558. throw new InvalidOperationException();
  559. });
  560. Assert.IsNull(err);
  561. var ex = new ArgumentException();
  562. scheduler.Catch<InvalidOperationException>(_ => true).Schedule(() =>
  563. {
  564. throw ex;
  565. });
  566. Assert.AreSame(ex, err);
  567. }
  568. [TestMethod]
  569. public void Catch_Custom_Rethrow()
  570. {
  571. var err = default(Exception);
  572. var scheduler = new MyExceptionScheduler(ex_ => err = ex_);
  573. var ex = new InvalidOperationException();
  574. scheduler.Catch<InvalidOperationException>(_ => false).Schedule(() =>
  575. {
  576. throw ex;
  577. });
  578. Assert.AreSame(ex, err);
  579. }
  580. [TestMethod]
  581. public void Catch_Custom_LongRunning_Caught()
  582. {
  583. var err = default(Exception);
  584. var scheduler = new MyExceptionScheduler(ex_ => err = ex_);
  585. var caught = false;
  586. var @catch = scheduler.Catch<InvalidOperationException>(_ => caught = true);
  587. var slr = (ISchedulerLongRunning)((IServiceProvider)@catch).GetService(typeof(ISchedulerLongRunning));
  588. slr.ScheduleLongRunning(cancel =>
  589. {
  590. throw new InvalidOperationException();
  591. });
  592. Assert.IsTrue(caught);
  593. Assert.IsNull(err);
  594. var ex = new ArgumentException();
  595. slr.ScheduleLongRunning(cancel =>
  596. {
  597. throw ex;
  598. });
  599. Assert.AreSame(ex, err);
  600. }
  601. [TestMethod]
  602. public void Catch_Custom_LongRunning_Rethrow()
  603. {
  604. var err = default(Exception);
  605. var scheduler = new MyExceptionScheduler(ex_ => err = ex_);
  606. var @catch = scheduler.Catch<InvalidOperationException>(_ => false);
  607. var slr = (ISchedulerLongRunning)((IServiceProvider)@catch).GetService(typeof(ISchedulerLongRunning));
  608. var done = false;
  609. slr.ScheduleLongRunning(cancel =>
  610. {
  611. done = true;
  612. });
  613. Assert.IsTrue(done);
  614. var ex = new InvalidOperationException();
  615. slr.ScheduleLongRunning(cancel =>
  616. {
  617. throw ex;
  618. });
  619. Assert.AreSame(ex, err);
  620. }
  621. [TestMethod]
  622. public void Catch_Custom_Periodic_Regular()
  623. {
  624. var scheduler = new MyExceptionScheduler(_ => { });
  625. scheduler.PeriodicStopped = new ManualResetEvent(false);
  626. var @catch = scheduler.Catch<InvalidOperationException>(_ => true);
  627. var per = (ISchedulerPeriodic)((IServiceProvider)@catch).GetService(typeof(ISchedulerPeriodic));
  628. var madeProgress = new ManualResetEvent(false);
  629. var d = per.SchedulePeriodic(0, TimeSpan.Zero, x =>
  630. {
  631. if (x > 10)
  632. madeProgress.Set();
  633. return x + 1;
  634. });
  635. madeProgress.WaitOne();
  636. d.Dispose();
  637. scheduler.PeriodicStopped.WaitOne();
  638. }
  639. [TestMethod]
  640. public void Catch_Custom_Periodic_Uncaught1()
  641. {
  642. var err = default(Exception);
  643. var done = new ManualResetEvent(false);
  644. var scheduler = new MyExceptionScheduler(ex_ => { err = ex_; done.Set(); });
  645. scheduler.PeriodicStopped = new ManualResetEvent(false);
  646. var @catch = scheduler.Catch<InvalidOperationException>(_ => true);
  647. var per = (ISchedulerPeriodic)((IServiceProvider)@catch).GetService(typeof(ISchedulerPeriodic));
  648. var ex = new ArgumentException();
  649. per.SchedulePeriodic(42, TimeSpan.Zero, x =>
  650. {
  651. throw ex;
  652. });
  653. scheduler.PeriodicStopped.WaitOne();
  654. done.WaitOne();
  655. Assert.AreSame(ex, err);
  656. }
  657. [TestMethod]
  658. public void Catch_Custom_Periodic_Uncaught2()
  659. {
  660. var err = default(Exception);
  661. var done = new ManualResetEvent(false);
  662. var scheduler = new MyExceptionScheduler(ex_ => { err = ex_; done.Set(); });
  663. scheduler.PeriodicStopped = new ManualResetEvent(false);
  664. var @catch = scheduler.Catch<InvalidOperationException>(_ => false);
  665. var per = (ISchedulerPeriodic)((IServiceProvider)@catch).GetService(typeof(ISchedulerPeriodic));
  666. var ex = new InvalidOperationException();
  667. per.SchedulePeriodic(42, TimeSpan.Zero, x =>
  668. {
  669. throw ex;
  670. });
  671. scheduler.PeriodicStopped.WaitOne();
  672. done.WaitOne();
  673. Assert.AreSame(ex, err);
  674. }
  675. [TestMethod]
  676. public void Catch_Custom_Periodic_Caught()
  677. {
  678. var err = default(Exception);
  679. var scheduler = new MyExceptionScheduler(ex_ => err = ex_);
  680. scheduler.PeriodicStopped = new ManualResetEvent(false);
  681. var caught = new ManualResetEvent(false);
  682. var @catch = scheduler.Catch<InvalidOperationException>(_ => { caught.Set(); return true; });
  683. var per = (ISchedulerPeriodic)((IServiceProvider)@catch).GetService(typeof(ISchedulerPeriodic));
  684. per.SchedulePeriodic(42, TimeSpan.Zero, x =>
  685. {
  686. throw new InvalidOperationException();
  687. });
  688. scheduler.PeriodicStopped.WaitOne();
  689. caught.WaitOne();
  690. Assert.IsNull(err);
  691. }
  692. class MyExceptionScheduler : LocalScheduler, ISchedulerLongRunning, ISchedulerPeriodic
  693. {
  694. private readonly Action<Exception> _onError;
  695. public MyExceptionScheduler(Action<Exception> onError)
  696. {
  697. _onError = onError;
  698. }
  699. public override IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
  700. {
  701. try
  702. {
  703. return action(this, state);
  704. }
  705. catch (Exception exception)
  706. {
  707. _onError(exception);
  708. return Disposable.Empty;
  709. }
  710. }
  711. public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
  712. {
  713. throw new NotImplementedException();
  714. }
  715. public IDisposable ScheduleLongRunning<TState>(TState state, Action<TState, ICancelable> action)
  716. {
  717. var b = new BooleanDisposable();
  718. try
  719. {
  720. action(state, b);
  721. }
  722. catch (Exception exception)
  723. {
  724. _onError(exception);
  725. return Disposable.Empty;
  726. }
  727. return b;
  728. }
  729. public ManualResetEvent PeriodicStopped { get; set; }
  730. public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action)
  731. {
  732. var b = new BooleanDisposable();
  733. Scheduler.Default.Schedule(() =>
  734. {
  735. try
  736. {
  737. var s = state;
  738. for (int i = 0; true; i++)
  739. {
  740. if (i > 100 /* mimic delayed cancellation */ && b.IsDisposed)
  741. break;
  742. s = action(s);
  743. }
  744. }
  745. catch (Exception exception)
  746. {
  747. _onError(exception);
  748. }
  749. finally
  750. {
  751. PeriodicStopped.Set();
  752. }
  753. });
  754. return b;
  755. }
  756. }
  757. #endregion
  758. #region Services
  759. [TestMethod]
  760. public void InvalidService_Null()
  761. {
  762. var s = new MySchedulerWithoutServices();
  763. Assert.IsNull(((IServiceProvider)s).GetService(typeof(IAsyncResult)));
  764. }
  765. class MySchedulerWithoutServices : LocalScheduler
  766. {
  767. public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
  768. {
  769. throw new NotImplementedException();
  770. }
  771. }
  772. [TestMethod]
  773. public void DetectServices_Null_1()
  774. {
  775. var s = new MyDumbScheduler1();
  776. Assert.IsNull(Scheduler.AsLongRunning(s));
  777. Assert.IsNull(Scheduler.AsPeriodic(s));
  778. Assert.IsNull(Scheduler.AsStopwatchProvider(s));
  779. }
  780. class MyDumbScheduler1 : IScheduler
  781. {
  782. public DateTimeOffset Now
  783. {
  784. get { throw new NotImplementedException(); }
  785. }
  786. public IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
  787. {
  788. throw new NotImplementedException();
  789. }
  790. public IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
  791. {
  792. throw new NotImplementedException();
  793. }
  794. public IDisposable Schedule<TState>(TState state, DateTimeOffset dueTime, Func<IScheduler, TState, IDisposable> action)
  795. {
  796. throw new NotImplementedException();
  797. }
  798. }
  799. [TestMethod]
  800. public void DetectServices_Null_2()
  801. {
  802. var s = new MyDumbScheduler2(new Dictionary<Type, object>());
  803. Assert.IsNull(Scheduler.AsLongRunning(s));
  804. Assert.IsNull(Scheduler.AsPeriodic(s));
  805. Assert.IsNull(Scheduler.AsStopwatchProvider(s));
  806. }
  807. [TestMethod]
  808. public void DetectServices_Found()
  809. {
  810. {
  811. var x = new MyLongRunning();
  812. var s = new MyDumbScheduler2(new Dictionary<Type, object>
  813. {
  814. { typeof(ISchedulerLongRunning), x }
  815. });
  816. Assert.AreEqual(x, Scheduler.AsLongRunning(s));
  817. }
  818. {
  819. var x = new MyStopwatchProvider();
  820. var s = new MyDumbScheduler2(new Dictionary<Type, object>
  821. {
  822. { typeof(IStopwatchProvider), x }
  823. });
  824. Assert.AreEqual(x, Scheduler.AsStopwatchProvider(s));
  825. }
  826. {
  827. var x = new MyPeriodic();
  828. var s = new MyDumbScheduler2(new Dictionary<Type, object>
  829. {
  830. { typeof(ISchedulerPeriodic), x }
  831. });
  832. Assert.AreEqual(x, Scheduler.AsPeriodic(s));
  833. }
  834. }
  835. class MyDumbScheduler2 : IScheduler, IServiceProvider
  836. {
  837. private readonly Dictionary<Type, object> _services;
  838. public MyDumbScheduler2(Dictionary<Type, object> services)
  839. {
  840. _services = services;
  841. }
  842. public DateTimeOffset Now
  843. {
  844. get { throw new NotImplementedException(); }
  845. }
  846. public IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
  847. {
  848. throw new NotImplementedException();
  849. }
  850. public IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
  851. {
  852. throw new NotImplementedException();
  853. }
  854. public IDisposable Schedule<TState>(TState state, DateTimeOffset dueTime, Func<IScheduler, TState, IDisposable> action)
  855. {
  856. throw new NotImplementedException();
  857. }
  858. public object GetService(Type serviceType)
  859. {
  860. var res = default(object);
  861. if (_services.TryGetValue(serviceType, out res))
  862. return res;
  863. return null;
  864. }
  865. }
  866. class MyLongRunning : ISchedulerLongRunning
  867. {
  868. public IDisposable ScheduleLongRunning<TState>(TState state, Action<TState, ICancelable> action)
  869. {
  870. throw new NotImplementedException();
  871. }
  872. }
  873. class MyStopwatchProvider : IStopwatchProvider
  874. {
  875. public IStopwatch StartStopwatch()
  876. {
  877. throw new NotImplementedException();
  878. }
  879. }
  880. class MyPeriodic : ISchedulerPeriodic
  881. {
  882. public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action)
  883. {
  884. throw new NotImplementedException();
  885. }
  886. }
  887. #endregion
  888. }
  889. }