// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Diagnostics; using System.Reactive.Concurrency; using System.Threading; using Microsoft.Reactive.Testing; #if NUNIT using NUnit.Framework; using TestClassAttribute = NUnit.Framework.TestFixtureAttribute; using TestMethodAttribute = NUnit.Framework.TestAttribute; using TestInitializeAttribute = NUnit.Framework.SetUpAttribute; #else using Microsoft.VisualStudio.TestTools.UnitTesting; #endif #if STRESS using ReactiveTests.Stress.Schedulers; #endif namespace ReactiveTests.Tests { [TestClass] public class EventLoopSchedulerTest { [TestMethod] public void EventLoop_ArgumentChecking() { var el = new EventLoopScheduler(); ReactiveAssert.Throws(() => new EventLoopScheduler(null)); ReactiveAssert.Throws(() => el.Schedule(42, default(Func))); ReactiveAssert.Throws(() => el.Schedule(42, DateTimeOffset.Now, default(Func))); ReactiveAssert.Throws(() => el.Schedule(42, TimeSpan.Zero, default(Func))); ReactiveAssert.Throws(() => el.SchedulePeriodic(42, TimeSpan.FromSeconds(1), default(Func))); ReactiveAssert.Throws(() => el.SchedulePeriodic(42, TimeSpan.FromSeconds(-1), _ => _)); } [TestMethod] public void EventLoop_Now() { var res = new EventLoopScheduler().Now - DateTime.Now; Assert.IsTrue(res.Seconds < 1); } [TestMethod] public void EventLoop_ScheduleAction() { var ran = false; var gate = new Semaphore(0, 1); var el = new EventLoopScheduler(); el.Schedule(() => { ran = true; gate.Release(); }); Assert.IsTrue(gate.WaitOne(TimeSpan.FromSeconds(2))); Assert.IsTrue(ran); } [TestMethod] public void EventLoop_DifferentThread() { var id = default(int); var gate = new Semaphore(0, 1); var el = new EventLoopScheduler(); el.Schedule(() => { id = Thread.CurrentThread.ManagedThreadId; gate.Release(); }); Assert.IsTrue(gate.WaitOne(TimeSpan.FromSeconds(2))); Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, id); } [TestMethod] public void EventLoop_ScheduleOrderedActions() { var results = new List(); var gate = new Semaphore(0, 1); var el = new EventLoopScheduler(); el.Schedule(() => results.Add(0)); el.Schedule(() => { results.Add(1); gate.Release(); }); Assert.IsTrue(gate.WaitOne(TimeSpan.FromSeconds(2))); results.AssertEqual(0, 1); } [TestMethod] public void EventLoop_SchedulerDisposed() { var d = 0; var e = new ManualResetEvent(false); var f = new ManualResetEvent(false); var results = new List(); var el = new EventLoopScheduler(); el.Schedule(() => results.Add(0)); el.Schedule(() => { el.Dispose(); e.Set(); results.Add(1); try { el.Schedule(() => { throw new Exception("Should be disposed!"); }); f.Set(); } catch (ObjectDisposedException) { // BREAKING CHANGE v2 > v1.x - New exception behavior. Interlocked.Increment(ref d); f.Set(); } }); e.WaitOne(); try { el.Schedule(() => results.Add(2)); } catch (ObjectDisposedException) { // BREAKING CHANGE v2 > v1.x - New exception behavior. Interlocked.Increment(ref d); } f.WaitOne(); results.AssertEqual(0, 1); Assert.AreEqual(2, d); } [TestMethod] public void EventLoop_ScheduleTimeOrderedActions() { var results = new List(); var gate = new Semaphore(0, 1); var el = new EventLoopScheduler(); el.Schedule(TimeSpan.FromMilliseconds(50), () => results.Add(1)); el.Schedule(TimeSpan.FromMilliseconds(100), () => { results.Add(0); gate.Release(); }); Assert.IsTrue(gate.WaitOne(TimeSpan.FromSeconds(2))); results.AssertEqual(1, 0); } [TestMethod] public void EventLoop_ScheduleOrderedAndTimedActions() { var results = new List(); var gate = new Semaphore(0, 1); var el = new EventLoopScheduler(); el.Schedule(() => results.Add(1)); el.Schedule(TimeSpan.FromMilliseconds(100), () => { results.Add(0); gate.Release(); }); Assert.IsTrue(gate.WaitOne(TimeSpan.FromSeconds(2))); results.AssertEqual(1, 0); } [TestMethod] public void EventLoop_ScheduleTimeOrderedInFlightActions() { var results = new List(); var gate = new Semaphore(0, 1); var el = new EventLoopScheduler(); el.Schedule(TimeSpan.FromMilliseconds(100), () => { results.Add(0); el.Schedule(TimeSpan.FromMilliseconds(50), () => results.Add(1)); el.Schedule(TimeSpan.FromMilliseconds(100), ()=> { results.Add(2); gate.Release(); }); }); Assert.IsTrue(gate.WaitOne(TimeSpan.FromSeconds(2))); results.AssertEqual(0, 1, 2); } [TestMethod] public void EventLoop_ScheduleTimeAndOrderedInFlightActions() { var results = new List(); var gate = new Semaphore(0, 1); var el = new EventLoopScheduler(); el.Schedule(TimeSpan.FromMilliseconds(100), () => { results.Add(0); el.Schedule(() => results.Add(4)); el.Schedule(TimeSpan.FromMilliseconds(50), () => results.Add(1)); el.Schedule(TimeSpan.FromMilliseconds(100), () => { results.Add(2); gate.Release(); }); }); Assert.IsTrue(gate.WaitOne(TimeSpan.FromSeconds(2))); results.AssertEqual(0, 4, 1, 2); } [TestMethod] public void EventLoop_ScheduleActionNested() { var ran = false; var el = new EventLoopScheduler(); var gate = new Semaphore(0, 1); el.Schedule(() => el.Schedule(() => { ran = true; gate.Release(); })); Assert.IsTrue(gate.WaitOne(TimeSpan.FromSeconds(2))); Assert.IsTrue(ran); } #if !SILVERLIGHT [TestMethod] [Ignore] public void EventLoop_ScheduleActionDue() { var ran = false; var el = new EventLoopScheduler(); var sw = new Stopwatch(); var gate = new Semaphore(0, 1); sw.Start(); el.Schedule(TimeSpan.FromSeconds(0.2), () => { ran = true; sw.Stop(); gate.Release(); }); Assert.IsTrue(gate.WaitOne(TimeSpan.FromSeconds(2))); Assert.IsTrue(ran, "ran"); Assert.IsTrue(sw.ElapsedMilliseconds > 180, "due " + sw.ElapsedMilliseconds); } [TestMethod] [Ignore] public void EventLoop_ScheduleActionDueNested() { var ran = false; var el = new EventLoopScheduler(); var gate = new Semaphore(0, 1); var sw = new Stopwatch(); sw.Start(); el.Schedule(TimeSpan.FromSeconds(0.2), () => { sw.Stop(); sw.Start(); el.Schedule(TimeSpan.FromSeconds(0.2), () => { sw.Stop(); ran = true; gate.Release(); }); }); Assert.IsTrue(gate.WaitOne(TimeSpan.FromSeconds(2))); Assert.IsTrue(ran, "ran"); Assert.IsTrue(sw.ElapsedMilliseconds > 380, "due " + sw.ElapsedMilliseconds); } #endif #if !NO_PERF #if !NO_STOPWATCH [TestMethod] public void Stopwatch() { StopwatchTest.Run(new EventLoopScheduler()); } #endif #endif #if !NO_CDS [TestMethod] public void EventLoop_Immediate() { var M = 1000; var N = 4; for (int i = 0; i < N; i++) { for (int j = 1; j <= M; j *= 10) { using (var e = new EventLoopScheduler()) { var cd = new CountdownEvent(j); for (int k = 0; k < j; k++) e.Schedule(() => cd.Signal()); if (!cd.Wait(10000)) Assert.Fail("j = " + j); } } } } [TestMethod] public void EventLoop_TimeCollisions() { var M = 1000; var N = 4; for (int i = 0; i < N; i++) { for (int j = 1; j <= M; j *= 10) { using (var e = new EventLoopScheduler()) { var cd = new CountdownEvent(j); for (int k = 0; k < j; k++) e.Schedule(TimeSpan.FromMilliseconds(100), () => cd.Signal()); if (!cd.Wait(10000)) Assert.Fail("j = " + j); } } } } [TestMethod] public void EventLoop_Spread() { var M = 1000; var N = 4; for (int i = 0; i < N; i++) { for (int j = 1; j <= M; j *= 10) { using (var e = new EventLoopScheduler()) { var cd = new CountdownEvent(j); for (int k = 0; k < j; k++) e.Schedule(TimeSpan.FromMilliseconds(k), () => cd.Signal()); if (!cd.Wait(10000)) Assert.Fail("j = " + j); } } } } #endif [TestMethod] public void EventLoop_Periodic() { var n = 0; using (var s = new EventLoopScheduler()) { var e = new ManualResetEvent(false); var d = s.SchedulePeriodic(TimeSpan.FromMilliseconds(25), () => { if (Interlocked.Increment(ref n) == 10) e.Set(); }); if (!e.WaitOne(10000)) Assert.Fail(); d.Dispose(); } } #if STRESS [TestMethod] public void EventLoop_Stress() { EventLoop.NoSemaphoreFullException(); } #endif } }