393 lines
12 KiB
C#
Raw Normal View History

// 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<ArgumentNullException>(() => new EventLoopScheduler(null));
ReactiveAssert.Throws<ArgumentNullException>(() => el.Schedule<int>(42, default(Func<IScheduler, int, IDisposable>)));
ReactiveAssert.Throws<ArgumentNullException>(() => el.Schedule<int>(42, DateTimeOffset.Now, default(Func<IScheduler, int, IDisposable>)));
ReactiveAssert.Throws<ArgumentNullException>(() => el.Schedule<int>(42, TimeSpan.Zero, default(Func<IScheduler, int, IDisposable>)));
ReactiveAssert.Throws<ArgumentNullException>(() => el.SchedulePeriodic(42, TimeSpan.FromSeconds(1), default(Func<int, int>)));
ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => 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<int>();
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<int>();
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<int>();
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<int>();
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<int>();
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<int>();
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
}
}