SystemClockTest.cs 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127
  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2. #if !NO_REMOTING
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Reactive;
  6. using System.Reactive.Concurrency;
  7. using System.Reactive.Disposables;
  8. using System.Reactive.PlatformServices;
  9. using Microsoft.VisualStudio.TestTools.UnitTesting;
  10. namespace ReactiveTests.Tests
  11. {
  12. static class Exts
  13. {
  14. public static T Deq<T>(this List<T> l)
  15. {
  16. var t = l[0];
  17. l.RemoveAt(0);
  18. return t;
  19. }
  20. }
  21. [TestClass]
  22. public class SystemClockTest
  23. {
  24. private void Run(CrossAppDomainDelegate a)
  25. {
  26. var domain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null, new AppDomainSetup { ApplicationBase = AppDomain.CurrentDomain.BaseDirectory });
  27. domain.DoCallBack(a);
  28. AppDomain.Unload(domain);
  29. }
  30. [TestMethod]
  31. public void PastWork()
  32. {
  33. Run(PastWork_Callback);
  34. }
  35. private static void PastWork_Callback()
  36. {
  37. var provider = new MyPlatformEnlightenmentProvider();
  38. PlatformEnlightenmentProvider.Current = provider;
  39. var cal = provider._cal;
  40. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  41. var due = now - TimeSpan.FromMinutes(1);
  42. var s = new MyScheduler();
  43. s.SetTime(now);
  44. var done = false;
  45. s.Schedule(due, () => { done = true; });
  46. Assert.AreEqual(1, s._queue.Count);
  47. var next = s._queue.Deq();
  48. Assert.IsTrue(s.Now + next.DueTime == now);
  49. s.SetTime(due);
  50. next.Invoke();
  51. Assert.IsTrue(done);
  52. }
  53. [TestMethod]
  54. public void ImmediateWork()
  55. {
  56. Run(ImmediateWork_Callback);
  57. }
  58. private static void ImmediateWork_Callback()
  59. {
  60. var provider = new MyPlatformEnlightenmentProvider();
  61. PlatformEnlightenmentProvider.Current = provider;
  62. var cal = provider._cal;
  63. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  64. var due = now;
  65. var s = new MyScheduler();
  66. s.SetTime(now);
  67. var done = false;
  68. s.Schedule(due, () => { done = true; });
  69. Assert.AreEqual(1, s._queue.Count);
  70. var next = s._queue.Deq();
  71. Assert.IsTrue(s.Now + next.DueTime == due);
  72. s.SetTime(due);
  73. next.Invoke();
  74. Assert.IsTrue(done);
  75. }
  76. [TestMethod]
  77. public void ShortTermWork()
  78. {
  79. Run(ShortTermWork_Callback);
  80. }
  81. private static void ShortTermWork_Callback()
  82. {
  83. var provider = new MyPlatformEnlightenmentProvider();
  84. PlatformEnlightenmentProvider.Current = provider;
  85. var cal = provider._cal;
  86. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  87. var rel = TimeSpan.FromSeconds(1) /* rel <= SHORTTERM */;
  88. var due = now + rel;
  89. var s = new MyScheduler();
  90. s.SetTime(now);
  91. var done = false;
  92. s.Schedule(due, () => { done = true; });
  93. Assert.AreEqual(1, s._queue.Count);
  94. var next = s._queue.Deq();
  95. Assert.IsTrue(s.Now + next.DueTime == due);
  96. s.SetTime(due);
  97. next.Invoke();
  98. Assert.IsTrue(done);
  99. }
  100. [TestMethod]
  101. public void ShortTermWork_Dispose()
  102. {
  103. Run(ShortTermWork_Dispose_Callback);
  104. }
  105. private static void ShortTermWork_Dispose_Callback()
  106. {
  107. var provider = new MyPlatformEnlightenmentProvider();
  108. PlatformEnlightenmentProvider.Current = provider;
  109. var cal = provider._cal;
  110. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  111. var rel = TimeSpan.FromSeconds(1) /* rel <= SHORTTERM */;
  112. var due = now + rel;
  113. var s = new MyScheduler();
  114. s.SetTime(now);
  115. var done = false;
  116. var d = s.Schedule(due, () => { done = true; });
  117. Assert.AreEqual(1, s._queue.Count);
  118. var next = s._queue.Deq();
  119. Assert.IsTrue(s.Now + next.DueTime == due);
  120. d.Dispose();
  121. s.SetTime(due);
  122. next.Invoke();
  123. Assert.IsFalse(done);
  124. }
  125. [TestMethod]
  126. public void ShortTermWork_InaccurateClock()
  127. {
  128. Run(ShortTermWork_InaccurateClock_Callback);
  129. }
  130. private static void ShortTermWork_InaccurateClock_Callback()
  131. {
  132. var provider = new MyPlatformEnlightenmentProvider();
  133. PlatformEnlightenmentProvider.Current = provider;
  134. var cal = provider._cal;
  135. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  136. var rel = TimeSpan.FromSeconds(1);
  137. var due = now + rel;
  138. var s = new MyScheduler();
  139. s.SetTime(now);
  140. var done = false;
  141. s.Schedule(due, () => { done = true; });
  142. Assert.AreEqual(1, s._queue.Count);
  143. var nxt1 = s._queue.Deq();
  144. Assert.IsTrue(s.Now + nxt1.DueTime == due);
  145. s.SetTime(due - TimeSpan.FromMilliseconds(500) /* > RETRYSHORT */);
  146. nxt1.Invoke();
  147. Assert.AreEqual(1, s._queue.Count);
  148. var nxt2 = s._queue.Deq();
  149. Assert.IsTrue(s.Now + nxt2.DueTime == due);
  150. s.SetTime(due);
  151. nxt2.Invoke();
  152. Assert.IsTrue(done);
  153. }
  154. [TestMethod]
  155. public void LongTermWork1()
  156. {
  157. Run(LongTermWork1_Callback);
  158. }
  159. private static void LongTermWork1_Callback()
  160. {
  161. var provider = new MyPlatformEnlightenmentProvider();
  162. PlatformEnlightenmentProvider.Current = provider;
  163. var cal = provider._cal;
  164. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  165. var rel = TimeSpan.FromMinutes(1) /* rel > SHORTTERM */;
  166. var due = now + rel;
  167. var s = new MyScheduler();
  168. s.SetTime(now);
  169. var done = false;
  170. s.Schedule(due, () => { done = true; });
  171. Assert.AreEqual(1, cal._queue.Count);
  172. var work = cal._queue.Deq();
  173. Assert.IsTrue(work.Interval < rel);
  174. s.SetTime(s.Now + work.Interval);
  175. work.Value._action(work.Value._state);
  176. Assert.AreEqual(1, s._queue.Count);
  177. var next = s._queue.Deq();
  178. Assert.IsTrue(s.Now + next.DueTime == due);
  179. s.SetTime(due);
  180. next.Invoke();
  181. Assert.IsTrue(done);
  182. }
  183. [TestMethod]
  184. public void LongTermWork2()
  185. {
  186. Run(LongTermWork2_Callback);
  187. }
  188. private static void LongTermWork2_Callback()
  189. {
  190. var provider = new MyPlatformEnlightenmentProvider();
  191. PlatformEnlightenmentProvider.Current = provider;
  192. var cal = provider._cal;
  193. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  194. var rel = TimeSpan.FromDays(1) /* rel > SHORTTERM and rel * MAXERRORRATIO > SHORTTERM */;
  195. var due = now + rel;
  196. var s = new MyScheduler();
  197. s.SetTime(now);
  198. var done = false;
  199. s.Schedule(due, () => { done = true; });
  200. Assert.AreEqual(1, cal._queue.Count);
  201. var wrk1 = cal._queue.Deq();
  202. Assert.IsTrue(wrk1.Interval < rel);
  203. s.SetTime(s.Now + wrk1.Interval);
  204. wrk1.Value._action(wrk1.Value._state);
  205. // Begin of second long term scheduling
  206. Assert.AreEqual(1, cal._queue.Count);
  207. var wrk2 = cal._queue.Deq();
  208. Assert.IsTrue(wrk2.Interval < rel);
  209. s.SetTime(s.Now + wrk2.Interval);
  210. wrk2.Value._action(wrk2.Value._state);
  211. // End of second long term scheduling
  212. Assert.AreEqual(1, s._queue.Count);
  213. var next = s._queue.Deq();
  214. Assert.IsTrue(s.Now + next.DueTime == due);
  215. s.SetTime(due);
  216. next.Invoke();
  217. Assert.IsTrue(done);
  218. }
  219. [TestMethod]
  220. public void LongTerm_Multiple()
  221. {
  222. Run(LongTerm_Multiple_Callback);
  223. }
  224. private static void LongTerm_Multiple_Callback()
  225. {
  226. var provider = new MyPlatformEnlightenmentProvider();
  227. PlatformEnlightenmentProvider.Current = provider;
  228. var cal = provider._cal;
  229. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  230. var s = new MyScheduler();
  231. s.SetTime(now);
  232. var due1 = now + TimeSpan.FromMinutes(10);
  233. var due2 = now + TimeSpan.FromMinutes(30);
  234. var due3 = now + TimeSpan.FromMinutes(60);
  235. var done1 = false;
  236. var done2 = false;
  237. var done3 = false;
  238. s.Schedule(due2, () => { done2 = true; });
  239. s.Schedule(due1, () => { done1 = true; });
  240. s.Schedule(due3, () => { done3 = true; });
  241. // First CHK
  242. Assert.AreEqual(1, cal._queue.Count);
  243. var wrk1 = cal._queue.Deq();
  244. var fst = s.Now + wrk1.Interval;
  245. Assert.IsTrue(fst < due1);
  246. // First TRN
  247. s.SetTime(fst);
  248. wrk1.Value._action(wrk1.Value._state);
  249. // First SHT
  250. Assert.AreEqual(1, s._queue.Count);
  251. var sh1 = s._queue.Deq();
  252. // Second CHK
  253. Assert.AreEqual(1, cal._queue.Count);
  254. var wrk2 = cal._queue.Deq();
  255. var snd = s.Now + wrk2.Interval;
  256. Assert.IsTrue(snd < due2);
  257. // First RUN
  258. s.SetTime(due1);
  259. sh1.Invoke();
  260. Assert.IsTrue(done1);
  261. // Second TRN
  262. s.SetTime(snd);
  263. wrk2.Value._action(wrk2.Value._state);
  264. // Second SHT
  265. Assert.AreEqual(1, s._queue.Count);
  266. var sh2 = s._queue.Deq();
  267. // Third CHK
  268. Assert.AreEqual(1, cal._queue.Count);
  269. var wrk3 = cal._queue.Deq();
  270. var trd = s.Now + wrk3.Interval;
  271. Assert.IsTrue(trd < due3);
  272. // Second RUN
  273. s.SetTime(due2);
  274. sh2.Invoke();
  275. Assert.IsTrue(done2);
  276. // Third TRN
  277. s.SetTime(trd);
  278. wrk3.Value._action(wrk3.Value._state);
  279. // Third SHT
  280. Assert.AreEqual(1, s._queue.Count);
  281. var sh3 = s._queue.Deq();
  282. // Third RUN
  283. s.SetTime(due3);
  284. sh3.Invoke();
  285. Assert.IsTrue(done3);
  286. }
  287. [TestMethod]
  288. public void LongTerm_Multiple_Dispose()
  289. {
  290. Run(LongTerm_Multiple_Dispose_Callback);
  291. }
  292. private static void LongTerm_Multiple_Dispose_Callback()
  293. {
  294. var provider = new MyPlatformEnlightenmentProvider();
  295. PlatformEnlightenmentProvider.Current = provider;
  296. var cal = provider._cal;
  297. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  298. var s = new MyScheduler();
  299. s.SetTime(now);
  300. var due1 = now + TimeSpan.FromMinutes(10);
  301. var due2 = now + TimeSpan.FromMinutes(30);
  302. var due3 = now + TimeSpan.FromMinutes(60);
  303. var done1 = false;
  304. var done2 = false;
  305. var done3 = false;
  306. var d2 = s.Schedule(due2, () => { done2 = true; });
  307. var d1 = s.Schedule(due1, () => { done1 = true; });
  308. var d3 = s.Schedule(due3, () => { done3 = true; });
  309. // First CHK
  310. Assert.AreEqual(1, cal._queue.Count);
  311. var wrk1 = cal._queue.Deq();
  312. var fst = s.Now + wrk1.Interval;
  313. Assert.IsTrue(fst < due1);
  314. // First TRN
  315. s.SetTime(fst);
  316. wrk1.Value._action(wrk1.Value._state);
  317. // First DIS
  318. d1.Dispose();
  319. // First SHT
  320. Assert.AreEqual(1, s._queue.Count);
  321. var sh1 = s._queue.Deq();
  322. // Second CHK
  323. Assert.AreEqual(1, cal._queue.Count);
  324. var wrk2 = cal._queue.Deq();
  325. var snd = s.Now + wrk2.Interval;
  326. Assert.IsTrue(snd < due2);
  327. // First RUN
  328. s.SetTime(due1);
  329. sh1.Invoke();
  330. Assert.IsFalse(done1);
  331. // Second DIS
  332. // Third DIS
  333. d2.Dispose();
  334. d3.Dispose();
  335. // Second TRN
  336. s.SetTime(snd);
  337. wrk2.Value._action(wrk2.Value._state);
  338. // Second SHT
  339. Assert.AreEqual(1, s._queue.Count);
  340. var sh2 = s._queue.Deq();
  341. // Third CHK
  342. Assert.AreEqual(1, cal._queue.Count);
  343. var wrk3 = cal._queue.Deq();
  344. var trd = s.Now + wrk3.Interval;
  345. Assert.IsTrue(trd < due3);
  346. // Second RUN
  347. s.SetTime(due2);
  348. sh2.Invoke();
  349. Assert.IsFalse(done2);
  350. // Third TRN
  351. s.SetTime(trd);
  352. wrk3.Value._action(wrk3.Value._state);
  353. // Third SHT
  354. Assert.AreEqual(1, s._queue.Count);
  355. var sh3 = s._queue.Deq();
  356. // Third RUN
  357. s.SetTime(due3);
  358. sh3.Invoke();
  359. Assert.IsFalse(done3);
  360. }
  361. [TestMethod]
  362. public void ClockChanged_FalsePositive()
  363. {
  364. Run(ClockChanged_FalsePositive_Callback);
  365. }
  366. private static void ClockChanged_FalsePositive_Callback()
  367. {
  368. var provider = new MyPlatformEnlightenmentProvider();
  369. PlatformEnlightenmentProvider.Current = provider;
  370. var scm = (ClockChanged)provider.GetService<INotifySystemClockChanged>();
  371. var cal = provider._cal;
  372. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  373. var rel = TimeSpan.FromMinutes(1);
  374. var due = now + rel;
  375. var s = new MyScheduler();
  376. s.SetTime(now);
  377. var done = false;
  378. s.Schedule(due, () => { done = true; });
  379. Assert.AreEqual(1, cal._queue.Count);
  380. s.SetTime(now);
  381. scm.OnSystemClockChanged();
  382. var work = cal._queue.Deq();
  383. Assert.IsTrue(work.Interval < rel);
  384. s.SetTime(s.Now + work.Interval);
  385. work.Value._action(work.Value._state);
  386. Assert.AreEqual(1, s._queue.Count);
  387. var next = s._queue.Deq();
  388. Assert.IsTrue(s.Now + next.DueTime == due);
  389. s.SetTime(due);
  390. next.Invoke();
  391. Assert.IsTrue(done);
  392. }
  393. [TestMethod]
  394. public void ClockChanged_Forward1()
  395. {
  396. Run(ClockChanged_Forward1_Callback);
  397. }
  398. private static void ClockChanged_Forward1_Callback()
  399. {
  400. var provider = new MyPlatformEnlightenmentProvider();
  401. PlatformEnlightenmentProvider.Current = provider;
  402. var scm = (ClockChanged)provider.GetService<INotifySystemClockChanged>();
  403. var cal = provider._cal;
  404. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  405. var rel = TimeSpan.FromMinutes(1);
  406. var due = now + rel;
  407. var err = TimeSpan.FromMinutes(1);
  408. var s = new MyScheduler();
  409. s.SetTime(now);
  410. var done = false;
  411. s.Schedule(due, () => { done = true; });
  412. Assert.AreEqual(1, cal._queue.Count);
  413. Assert.AreEqual(0, s._queue.Count);
  414. s.SetTime(due + err);
  415. scm.OnSystemClockChanged();
  416. Assert.AreEqual(1, s._queue.Count);
  417. var next = s._queue.Deq();
  418. Assert.IsTrue(next.DueTime == TimeSpan.Zero);
  419. next.Invoke();
  420. Assert.IsTrue(done);
  421. var tmr = cal._queue.Deq();
  422. tmr.Value._action(tmr.Value._state);
  423. Assert.AreEqual(0, cal._queue.Count);
  424. Assert.AreEqual(0, s._queue.Count);
  425. }
  426. [TestMethod]
  427. public void ClockChanged_Forward2()
  428. {
  429. Run(ClockChanged_Forward2_Callback);
  430. }
  431. private static void ClockChanged_Forward2_Callback()
  432. {
  433. var provider = new MyPlatformEnlightenmentProvider();
  434. PlatformEnlightenmentProvider.Current = provider;
  435. var scm = (ClockChanged)provider.GetService<INotifySystemClockChanged>();
  436. var cal = provider._cal;
  437. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  438. var rel = TimeSpan.FromSeconds(1);
  439. var due = now + rel;
  440. var err = TimeSpan.FromMinutes(1);
  441. var s = new MyScheduler();
  442. s.SetTime(now);
  443. var n = 0;
  444. s.Schedule(due, () => { n++; });
  445. Assert.AreEqual(1, s._queue.Count);
  446. var wrk = s._queue.Deq();
  447. Assert.IsTrue(wrk.DueTime == rel);
  448. s.SetTime(due + err);
  449. scm.OnSystemClockChanged();
  450. Assert.AreEqual(1, s._queue.Count);
  451. var next = s._queue.Deq();
  452. Assert.IsTrue(next.DueTime == TimeSpan.Zero);
  453. next.Invoke();
  454. Assert.AreEqual(1, n);
  455. wrk.Invoke(); // Bad schedulers may not grant cancellation immediately.
  456. Assert.AreEqual(1, n); // Invoke shouldn't cause double execution of the work.
  457. }
  458. [TestMethod]
  459. public void ClockChanged_Backward1()
  460. {
  461. Run(ClockChanged_Backward1_Callback);
  462. }
  463. private static void ClockChanged_Backward1_Callback()
  464. {
  465. var provider = new MyPlatformEnlightenmentProvider();
  466. PlatformEnlightenmentProvider.Current = provider;
  467. var scm = (ClockChanged)provider.GetService<INotifySystemClockChanged>();
  468. var cal = provider._cal;
  469. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  470. var rel = TimeSpan.FromMinutes(1);
  471. var due = now + rel;
  472. var err = TimeSpan.FromMinutes(-2);
  473. var s = new MyScheduler();
  474. s.SetTime(now);
  475. var done = false;
  476. s.Schedule(due, () => { done = true; });
  477. Assert.AreEqual(1, cal._queue.Count);
  478. Assert.IsTrue(cal._queue[0].Interval < rel);
  479. Assert.AreEqual(0, s._queue.Count);
  480. s.SetTime(due + err);
  481. scm.OnSystemClockChanged();
  482. Assert.AreEqual(1, cal._queue.Count);
  483. var tmr = cal._queue.Deq();
  484. Assert.IsTrue(tmr.Interval > rel);
  485. Assert.IsTrue(tmr.Interval < -err);
  486. s.SetTime(s.Now + tmr.Interval);
  487. tmr.Value._action(tmr.Value._state);
  488. Assert.IsFalse(done);
  489. Assert.AreEqual(0, cal._queue.Count);
  490. Assert.AreEqual(1, s._queue.Count);
  491. s.SetTime(due);
  492. s._queue.Deq().Invoke();
  493. Assert.IsTrue(done);
  494. }
  495. [TestMethod]
  496. public void ClockChanged_Backward2()
  497. {
  498. Run(ClockChanged_Backward2_Callback);
  499. }
  500. private static void ClockChanged_Backward2_Callback()
  501. {
  502. var provider = new MyPlatformEnlightenmentProvider();
  503. PlatformEnlightenmentProvider.Current = provider;
  504. var scm = (ClockChanged)provider.GetService<INotifySystemClockChanged>();
  505. var cal = provider._cal;
  506. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  507. var rel = TimeSpan.FromSeconds(1);
  508. var due = now + rel;
  509. var err = TimeSpan.FromMinutes(-1);
  510. var s = new MyScheduler();
  511. s.SetTime(now);
  512. var n = 0;
  513. s.Schedule(due, () => { n++; });
  514. Assert.AreEqual(0, cal._queue.Count);
  515. Assert.AreEqual(1, s._queue.Count);
  516. var wrk = s._queue[0];
  517. Assert.IsTrue(wrk.DueTime == rel);
  518. s.SetTime(due + err);
  519. scm.OnSystemClockChanged();
  520. Assert.AreEqual(1, cal._queue.Count);
  521. var tmr = cal._queue.Deq();
  522. Assert.IsTrue(tmr.Interval > rel);
  523. Assert.IsTrue(tmr.Interval < -err);
  524. s.SetTime(s.Now + tmr.Interval);
  525. tmr.Value._action(tmr.Value._state);
  526. Assert.AreEqual(0, n);
  527. Assert.AreEqual(0, cal._queue.Count);
  528. Assert.AreEqual(1, s._queue.Count);
  529. s.SetTime(due);
  530. s._queue.Deq().Invoke();
  531. Assert.AreEqual(1, n);
  532. wrk.Invoke(); // Bad schedulers may not grant cancellation immediately.
  533. Assert.AreEqual(1, n); // Invoke shouldn't cause double execution of the work.
  534. }
  535. [TestMethod]
  536. public void PeriodicSystemClockChangeMonitor()
  537. {
  538. Run(PeriodicSystemClockChangeMonitor_Callback);
  539. }
  540. private static void PeriodicSystemClockChangeMonitor_Callback()
  541. {
  542. var provider = new FakeClockPlatformEnlightenmentProvider();
  543. PlatformEnlightenmentProvider.Current = provider;
  544. var clock = (FakeClock)provider.GetService<ISystemClock>();
  545. clock._now = new DateTimeOffset(2012, 4, 26, 12, 0, 0, TimeSpan.Zero);
  546. var cal = (FakeClockCAL)provider.GetService<IConcurrencyAbstractionLayer>();
  547. var period = TimeSpan.FromSeconds(1);
  548. var ptscm = new PeriodicTimerSystemClockMonitor(period);
  549. var delta = TimeSpan.Zero;
  550. var n = 0;
  551. var h = new EventHandler<SystemClockChangedEventArgs>((o, e) =>
  552. {
  553. delta = e.NewTime - e.OldTime;
  554. n++;
  555. });
  556. ptscm.SystemClockChanged += h;
  557. Assert.IsNotNull(cal._action);
  558. Assert.IsTrue(cal._period == period);
  559. Assert.AreEqual(0, n);
  560. clock._now += period;
  561. cal._action();
  562. Assert.AreEqual(0, n);
  563. clock._now += period;
  564. cal._action();
  565. Assert.AreEqual(0, n);
  566. var diff1 = TimeSpan.FromSeconds(3);
  567. clock._now += period + diff1;
  568. cal._action();
  569. Assert.AreEqual(1, n);
  570. Assert.IsTrue(delta == diff1);
  571. clock._now += period;
  572. cal._action();
  573. Assert.AreEqual(1, n);
  574. clock._now += period;
  575. cal._action();
  576. Assert.AreEqual(1, n);
  577. var diff2 = TimeSpan.FromSeconds(-5);
  578. clock._now += period + diff2;
  579. cal._action();
  580. Assert.AreEqual(2, n);
  581. Assert.IsTrue(delta == diff2);
  582. clock._now += period;
  583. cal._action();
  584. Assert.AreEqual(2, n);
  585. ptscm.SystemClockChanged -= h;
  586. Assert.IsNull(cal._action);
  587. }
  588. [TestMethod]
  589. public void ClockChanged_RefCounting()
  590. {
  591. Run(ClockChanged_RefCounting_Callback);
  592. }
  593. private static void ClockChanged_RefCounting_Callback()
  594. {
  595. var provider = new MyPlatformEnlightenmentProvider();
  596. PlatformEnlightenmentProvider.Current = provider;
  597. var scm = (ClockChanged)provider.GetService<INotifySystemClockChanged>();
  598. var cal = provider._cal;
  599. var now = new DateTimeOffset(2012, 4, 25, 12, 0, 0, TimeSpan.Zero);
  600. var s = new MyScheduler();
  601. s.SetTime(now);
  602. var due1 = now + TimeSpan.FromSeconds(5);
  603. var due2 = now + TimeSpan.FromSeconds(8);
  604. var due3 = now + TimeSpan.FromMinutes(1);
  605. var due4 = now + TimeSpan.FromMinutes(2);
  606. var due5 = now + TimeSpan.FromMinutes(3);
  607. var due6 = now + TimeSpan.FromMinutes(3) + TimeSpan.FromSeconds(2);
  608. var done1 = false;
  609. var done2 = false;
  610. var done3 = false;
  611. var done4 = false;
  612. var done5 = false;
  613. var done6 = false;
  614. var d1 = s.Schedule(due1, () => { done1 = true; });
  615. var d5 = s.Schedule(due5, () => { done5 = true; });
  616. var d3 = s.Schedule(due3, () => { done3 = true; throw new Exception(); });
  617. var d2 = s.Schedule(due2, () => { done2 = true; });
  618. var d4 = s.Schedule(due4, () => { done4 = true; });
  619. d2.Dispose();
  620. d4.Dispose();
  621. Assert.AreEqual(1, scm.n);
  622. s.SetTime(due1);
  623. var i1 = s._queue.Deq();
  624. i1.Invoke();
  625. Assert.IsTrue(done1);
  626. Assert.AreEqual(1, scm.n);
  627. s.SetTime(due2);
  628. var i2 = s._queue.Deq();
  629. i2.Invoke();
  630. Assert.IsFalse(done2);
  631. Assert.AreEqual(1, scm.n);
  632. var l1 = cal._queue.Deq();
  633. var l1d = now + l1.Interval;
  634. s.SetTime(l1d);
  635. l1.Value._action(l1.Value._state);
  636. s.SetTime(due3);
  637. var i3 = s._queue.Deq();
  638. try
  639. {
  640. i3.Invoke();
  641. Assert.Fail();
  642. }
  643. catch { }
  644. Assert.IsTrue(done3);
  645. Assert.AreEqual(1, scm.n);
  646. var l2 = cal._queue.Deq();
  647. var l2d = l1d + l2.Interval;
  648. s.SetTime(l2d);
  649. l2.Value._action(l2.Value._state);
  650. s.SetTime(due4);
  651. var i4 = s._queue.Deq();
  652. i4.Invoke();
  653. Assert.IsFalse(done4);
  654. Assert.AreEqual(1, scm.n);
  655. var l3 = cal._queue.Deq();
  656. var l3d = l2d + l3.Interval;
  657. s.SetTime(l3d);
  658. l3.Value._action(l3.Value._state);
  659. s.SetTime(due5);
  660. var i5 = s._queue.Deq();
  661. i5.Invoke();
  662. Assert.IsTrue(done5);
  663. Assert.AreEqual(0, scm.n);
  664. var d6 = s.Schedule(due6, () => { done6 = true; });
  665. Assert.AreEqual(1, scm.n);
  666. s.SetTime(due6);
  667. var i6 = s._queue.Deq();
  668. i6.Invoke();
  669. Assert.IsTrue(done6);
  670. Assert.AreEqual(0, scm.n);
  671. }
  672. class MyScheduler : LocalScheduler
  673. {
  674. internal List<ScheduledItem<TimeSpan>> _queue = new List<ScheduledItem<TimeSpan>>();
  675. private DateTimeOffset _now;
  676. public void SetTime(DateTimeOffset now)
  677. {
  678. _now = now;
  679. }
  680. public override DateTimeOffset Now
  681. {
  682. get { return _now; }
  683. }
  684. public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
  685. {
  686. var s = new ScheduledItem<TimeSpan, TState>(this, state, action, dueTime);
  687. _queue.Add(s);
  688. return Disposable.Create(() => _queue.Remove(s));
  689. }
  690. }
  691. class MyPlatformEnlightenmentProvider : IPlatformEnlightenmentProvider
  692. {
  693. internal MyCAL _cal;
  694. public MyPlatformEnlightenmentProvider()
  695. {
  696. _cal = new MyCAL();
  697. }
  698. public T GetService<T>(params object[] args) where T : class
  699. {
  700. if (typeof(T) == typeof(IConcurrencyAbstractionLayer))
  701. {
  702. return (T)(object)_cal;
  703. }
  704. else if (typeof(T) == typeof(INotifySystemClockChanged))
  705. {
  706. return (T)(object)ClockChanged.Instance;
  707. }
  708. return null;
  709. }
  710. }
  711. class FakeClockPlatformEnlightenmentProvider : IPlatformEnlightenmentProvider
  712. {
  713. internal FakeClockCAL _cal;
  714. internal FakeClock _clock;
  715. public FakeClockPlatformEnlightenmentProvider()
  716. {
  717. _cal = new FakeClockCAL();
  718. _clock = new FakeClock();
  719. }
  720. public T GetService<T>(params object[] args) where T : class
  721. {
  722. if (typeof(T) == typeof(IConcurrencyAbstractionLayer))
  723. {
  724. return (T)(object)_cal;
  725. }
  726. else if (typeof(T) == typeof(ISystemClock))
  727. {
  728. return (T)(object)_clock;
  729. }
  730. return null;
  731. }
  732. }
  733. class Work
  734. {
  735. internal readonly Action<object> _action;
  736. internal readonly object _state;
  737. public Work(Action<object> action, object state)
  738. {
  739. _action = action;
  740. _state = state;
  741. }
  742. }
  743. class MyCAL : IConcurrencyAbstractionLayer
  744. {
  745. internal List<TimeInterval<Work>> _queue = new List<TimeInterval<Work>>();
  746. public IDisposable StartTimer(Action<object> action, object state, TimeSpan dueTime)
  747. {
  748. var t = new TimeInterval<Work>(new Work(action, state), dueTime);
  749. _queue.Add(t);
  750. return Disposable.Create(() => _queue.Remove(t));
  751. }
  752. public IDisposable StartPeriodicTimer(Action action, TimeSpan period)
  753. {
  754. throw new NotImplementedException();
  755. }
  756. public IDisposable QueueUserWorkItem(Action<object> action, object state)
  757. {
  758. throw new NotImplementedException();
  759. }
  760. public void Sleep(TimeSpan timeout)
  761. {
  762. throw new NotImplementedException();
  763. }
  764. public IStopwatch StartStopwatch()
  765. {
  766. throw new NotImplementedException();
  767. }
  768. public bool SupportsLongRunning
  769. {
  770. get { throw new NotImplementedException(); }
  771. }
  772. public void StartThread(Action<object> action, object state)
  773. {
  774. throw new NotImplementedException();
  775. }
  776. }
  777. class FakeClockCAL : IConcurrencyAbstractionLayer
  778. {
  779. internal Action _action;
  780. internal TimeSpan _period;
  781. public IDisposable StartTimer(Action<object> action, object state, TimeSpan dueTime)
  782. {
  783. throw new NotImplementedException();
  784. }
  785. public IDisposable StartPeriodicTimer(Action action, TimeSpan period)
  786. {
  787. _action = action;
  788. _period = period;
  789. return Disposable.Create(() => _action = null);
  790. }
  791. public IDisposable QueueUserWorkItem(Action<object> action, object state)
  792. {
  793. throw new NotImplementedException();
  794. }
  795. public void Sleep(TimeSpan timeout)
  796. {
  797. throw new NotImplementedException();
  798. }
  799. public IStopwatch StartStopwatch()
  800. {
  801. throw new NotImplementedException();
  802. }
  803. public bool SupportsLongRunning
  804. {
  805. get { throw new NotImplementedException(); }
  806. }
  807. public void StartThread(Action<object> action, object state)
  808. {
  809. throw new NotImplementedException();
  810. }
  811. }
  812. class FakeClock : ISystemClock
  813. {
  814. internal DateTimeOffset _now;
  815. public DateTimeOffset UtcNow
  816. {
  817. get { return _now; }
  818. }
  819. }
  820. class ClockChanged : INotifySystemClockChanged
  821. {
  822. private static ClockChanged s_instance = new ClockChanged();
  823. private EventHandler<SystemClockChangedEventArgs> _systemClockChanged;
  824. internal int n = 0;
  825. public event EventHandler<SystemClockChangedEventArgs> SystemClockChanged
  826. {
  827. add
  828. {
  829. _systemClockChanged += value;
  830. n++;
  831. }
  832. remove
  833. {
  834. _systemClockChanged -= value;
  835. n--;
  836. }
  837. }
  838. public static ClockChanged Instance
  839. {
  840. get
  841. {
  842. return s_instance;
  843. }
  844. }
  845. public void OnSystemClockChanged()
  846. {
  847. var scc = _systemClockChanged;
  848. if (scc != null)
  849. scc(this, new SystemClockChangedEventArgs());
  850. }
  851. }
  852. }
  853. }
  854. #endif