1090 lines
39 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.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.PlatformServices;
using System.Runtime.CompilerServices;
using System.Threading;
#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
using Microsoft.Reactive.Testing;
#if HAS_WINFORMS
using System.Windows.Forms;
#endif
namespace ReactiveTests.Tests
{
[TestClass]
public class SchedulerTest
{
#region IScheduler
[TestMethod]
public void Scheduler_ArgumentChecks()
{
var ms = new MyScheduler();
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), a => { }));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), () => { }));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), 1, (a, s) => { }));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(ms, default(Action<Action>)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(ms, 1, default(Action<int, Action<int>>)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), DateTimeOffset.Now, a => { }));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), DateTimeOffset.Now, () => { }));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), 1, DateTimeOffset.Now, (a, s) => { }));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(ms, DateTimeOffset.Now, default(Action<Action<DateTimeOffset>>)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(ms, 1, DateTimeOffset.Now, default(Action<int, Action<int, DateTimeOffset>>)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), TimeSpan.Zero, a => { }));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), TimeSpan.Zero, () => { }));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(default(IScheduler), 1, TimeSpan.Zero, (a, s) => { }));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(ms, TimeSpan.Zero, default(Action<Action<TimeSpan>>)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Schedule(ms, 1, TimeSpan.Zero, default(Action<int, Action<int, TimeSpan>>)));
}
[TestMethod]
public void Schedulers_ArgumentChecks()
{
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.CurrentThread.Schedule(default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.CurrentThread.Schedule(TimeSpan.Zero, default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.CurrentThread.Schedule(DateTimeOffset.MaxValue, default(Action)));
#if !NO_WINDOWS_THREADING
ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherScheduler.Instance.Schedule(default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherScheduler.Instance.Schedule(TimeSpan.Zero, default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => DispatcherScheduler.Instance.Schedule(DateTimeOffset.MaxValue, default(Action)));
#endif
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Immediate.Schedule(default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Immediate.Schedule(TimeSpan.Zero, default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Immediate.Schedule(DateTimeOffset.MaxValue, default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => NewThreadScheduler.Default.Schedule(default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => NewThreadScheduler.Default.Schedule(TimeSpan.Zero, default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => NewThreadScheduler.Default.Schedule(DateTimeOffset.MaxValue, default(Action)));
#if !NO_TPL
ReactiveAssert.Throws<ArgumentNullException>(() => TaskPoolScheduler.Default.Schedule(default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => TaskPoolScheduler.Default.Schedule(TimeSpan.Zero, default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => TaskPoolScheduler.Default.Schedule(DateTimeOffset.MaxValue, default(Action)));
#endif
ReactiveAssert.Throws<ArgumentNullException>(() => DefaultScheduler.Instance.Schedule(default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => DefaultScheduler.Instance.Schedule(TimeSpan.Zero, default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => DefaultScheduler.Instance.Schedule(DateTimeOffset.MaxValue, default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => DefaultScheduler.Instance.SchedulePeriodic(42, TimeSpan.FromSeconds(1), default(Func<int, int>)));
ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => DefaultScheduler.Instance.SchedulePeriodic(42, TimeSpan.FromSeconds(-1), _ => _));
#if HAS_WINFORMS
var lbl = new Label();
ReactiveAssert.Throws<ArgumentNullException>(() => new ControlScheduler(lbl).Schedule(default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => new ControlScheduler(lbl).Schedule(TimeSpan.Zero, default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => new ControlScheduler(lbl).Schedule(DateTimeOffset.MaxValue, default(Action)));
#endif
var ctx = new SynchronizationContext();
ReactiveAssert.Throws<ArgumentNullException>(() => new SynchronizationContextScheduler(ctx).Schedule(default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => new SynchronizationContextScheduler(ctx).Schedule(TimeSpan.Zero, default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => new SynchronizationContextScheduler(ctx).Schedule(DateTimeOffset.MaxValue, default(Action)));
}
[TestMethod]
public void Scheduler_ScheduleNonRecursive()
{
var ms = new MyScheduler();
var res = false;
Scheduler.Schedule(ms, a => { res = true; });
Assert.IsTrue(res);
}
[TestMethod]
public void Scheduler_ScheduleRecursive()
{
var ms = new MyScheduler();
var i = 0;
Scheduler.Schedule(ms, a => { if (++i < 10) a(); });
Assert.AreEqual(10, i);
}
[TestMethod]
public void Scheduler_ScheduleWithTimeNonRecursive()
{
var now = DateTimeOffset.Now;
var ms = new MyScheduler(now) { Check = (a, s, t) => { Assert.IsTrue(t == TimeSpan.Zero); } };
var res = false;
Scheduler.Schedule(ms, now, a => { res = true; });
Assert.IsTrue(res);
Assert.IsTrue(ms.WaitCycles == 0);
}
[TestMethod]
public void Scheduler_ScheduleWithTimeRecursive()
{
var now = DateTimeOffset.Now;
var i = 0;
var ms = new MyScheduler(now) { Check = (a, s, t) => { Assert.IsTrue(t == TimeSpan.Zero); } };
Scheduler.Schedule(ms, now, a => { if (++i < 10) a(now); });
Assert.IsTrue(ms.WaitCycles == 0);
Assert.AreEqual(10, i);
}
[TestMethod]
public void Scheduler_ScheduleWithTimeSpanNonRecursive()
{
var now = DateTimeOffset.Now;
var ms = new MyScheduler(now) { Check = (a, s, t) => { Assert.IsTrue(t == TimeSpan.Zero); } };
var res = false;
Scheduler.Schedule(ms, TimeSpan.Zero, a => { res = true; });
Assert.IsTrue(res);
Assert.IsTrue(ms.WaitCycles == 0);
}
[TestMethod]
public void Scheduler_ScheduleWithTimeSpanRecursive()
{
var now = DateTimeOffset.Now;
var ms = new MyScheduler(now) { Check = (a, s, t) => { Assert.IsTrue(t < TimeSpan.FromTicks(10)); } };
var i = 0;
Scheduler.Schedule(ms, TimeSpan.Zero, a => { if (++i < 10) a(TimeSpan.FromTicks(i)); });
Assert.IsTrue(ms.WaitCycles == Enumerable.Range(1, 9).Sum());
Assert.AreEqual(10, i);
}
[TestMethod]
public void Scheduler_StateThreading()
{
var lst = new List<int>();
Scheduler.Immediate.Schedule(0, (i, a) =>
{
lst.Add(i);
if (i < 9)
a(i + 1);
});
Assert.IsTrue(lst.SequenceEqual(Enumerable.Range(0, 10)));
}
[TestMethod]
public void Scheduler_Builtins()
{
// Default
{
var e = new ManualResetEvent(false);
Scheduler.Default.Schedule(() => e.Set());
e.WaitOne();
}
if (!Utils.IsRunningWithPortableLibraryBinaries())
{
Scheduler_Builtins_NoPlib();
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void Scheduler_Builtins_NoPlib()
{
#if !PLIB
// ThreadPool
{
var e = new ManualResetEvent(false);
Scheduler.ThreadPool.Schedule(() => e.Set());
e.WaitOne();
}
#endif
#if !NO_THREAD
// NewThread
{
var e = new ManualResetEvent(false);
Scheduler.NewThread.Schedule(() => e.Set());
e.WaitOne();
}
#endif
#if !NO_TPL
// TaskPool
{
var e = new ManualResetEvent(false);
Scheduler.TaskPool.Schedule(() => e.Set());
e.WaitOne();
}
#endif
}
#endregion
#region ISchedulerLongRunning
#if !NO_PERF
[TestMethod]
public void Scheduler_LongRunning_ArgumentChecking()
{
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.ScheduleLongRunning(null, c => { }));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.ScheduleLongRunning(ThreadPoolScheduler.Instance, default(Action<ICancelable>)));
}
[TestMethod]
public void Scheduler_Periodic_ArgumentChecking()
{
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.SchedulePeriodic(null, TimeSpan.FromSeconds(1), () => { }));
ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Scheduler.SchedulePeriodic(ThreadPoolScheduler.Instance, TimeSpan.FromSeconds(-1), () => { }));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.SchedulePeriodic(ThreadPoolScheduler.Instance, TimeSpan.FromSeconds(1), default(Action)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.SchedulePeriodic<int>(null, 42, TimeSpan.FromSeconds(1), _ => { }));
ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Scheduler.SchedulePeriodic<int>(ThreadPoolScheduler.Instance, 42, TimeSpan.FromSeconds(-1), _ => { }));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.SchedulePeriodic<int>(ThreadPoolScheduler.Instance, 42, TimeSpan.FromSeconds(1), default(Action<int>)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.SchedulePeriodic<int>(null, 42, TimeSpan.FromSeconds(1), _ => _));
ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => Scheduler.SchedulePeriodic<int>(ThreadPoolScheduler.Instance, 42, TimeSpan.FromSeconds(-1), _ => _));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.SchedulePeriodic<int>(ThreadPoolScheduler.Instance, 42, TimeSpan.FromSeconds(1), default(Func<int, int>)));
}
[TestMethod]
public void Scheduler_Stopwatch_Emulation()
{
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.StartStopwatch(null));
}
#if !NO_TPL
[TestMethod]
public void Scheduler_LongRunning1()
{
var s = TaskPoolScheduler.Default;
var x = new ManualResetEvent(false);
var e = new ManualResetEvent(false);
var d = s.ScheduleLongRunning(42, (state, cancel) =>
{
while (!cancel.IsDisposed)
x.Set();
e.Set();
});
x.WaitOne();
d.Dispose();
e.WaitOne();
}
[TestMethod]
public void Scheduler_LongRunning2()
{
var s = TaskPoolScheduler.Default;
var x = new ManualResetEvent(false);
var e = new ManualResetEvent(false);
var d = s.ScheduleLongRunning(cancel =>
{
while (!cancel.IsDisposed)
x.Set();
e.Set();
});
x.WaitOne();
d.Dispose();
e.WaitOne();
}
#endif
#endif
#endregion
#region ISchedulerPeriodic
#if !NO_PERF
[TestMethod]
public void Scheduler_Periodic1()
{
var n = 0;
var e = new ManualResetEvent(false);
var d = ThreadPoolScheduler.Instance.SchedulePeriodic(TimeSpan.FromMilliseconds(50), () =>
{
if (n++ == 10)
e.Set();
});
e.WaitOne();
d.Dispose();
}
[TestMethod]
public void Scheduler_Periodic2()
{
var n = 0;
var e = new ManualResetEvent(false);
var d = ThreadPoolScheduler.Instance.SchedulePeriodic(42, TimeSpan.FromMilliseconds(50), x =>
{
Assert.AreEqual(42, x);
if (n++ == 10)
e.Set();
});
e.WaitOne();
d.Dispose();
}
#if DESKTOPCLR
[TestMethod]
public void Scheduler_Periodic_HostLifecycleManagement()
{
var cur = Utils.GetTestBaseDirectory();
var domain = AppDomain.CreateDomain("HLN", null, new AppDomainSetup { ApplicationBase = cur });
domain.DoCallBack(() =>
{
var pep = PlatformEnlightenmentProvider.Current;
try
{
var hln = new HLN();
PlatformEnlightenmentProvider.Current = new PEP(hln);
var s = ThreadPoolScheduler.Instance.DisableOptimizations(typeof(ISchedulerPeriodic));
var n = 0;
var e = new ManualResetEvent(false);
var d = Observable.Interval(TimeSpan.FromMilliseconds(100), s).Subscribe(_ =>
{
if (n++ == 10)
e.Set();
});
hln.OnSuspending();
hln.OnResuming();
Thread.Sleep(250);
hln.OnSuspending();
Thread.Sleep(150);
hln.OnResuming();
Thread.Sleep(50);
hln.OnSuspending();
hln.OnResuming();
e.WaitOne();
d.Dispose();
}
finally
{
PlatformEnlightenmentProvider.Current = pep;
}
});
}
class PEP : IPlatformEnlightenmentProvider
{
private readonly IPlatformEnlightenmentProvider _old;
private readonly IHostLifecycleNotifications _hln;
public PEP(HLN hln)
{
_old = PlatformEnlightenmentProvider.Current;
_hln = hln;
}
public T GetService<T>(params object[] args) where T : class
{
if (typeof(T) == typeof(IHostLifecycleNotifications))
{
return (T)(object)_hln;
}
return _old.GetService<T>(args);
}
}
class HLN : IHostLifecycleNotifications
{
public event EventHandler<HostSuspendingEventArgs> Suspending;
public event EventHandler<HostResumingEventArgs> Resuming;
public void OnSuspending()
{
var s = Suspending;
if (s != null)
s(this, null);
}
public void OnResuming()
{
var s = Resuming;
if (s != null)
s(this, null);
}
}
#endif
#endif
#endregion
#region DisableOptimizations
#if !NO_PERF
[TestMethod]
public void DisableOptimizations_ArgumentChecking()
{
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.DisableOptimizations(default(IScheduler)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.DisableOptimizations(default(IScheduler), new Type[0]));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.DisableOptimizations(ThreadPoolScheduler.Instance, default(Type[])));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.DisableOptimizations(Scheduler.Default).Schedule(42, default(Func<IScheduler, int, IDisposable>)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.DisableOptimizations(Scheduler.Default).Schedule(42, TimeSpan.FromSeconds(1), default(Func<IScheduler, int, IDisposable>)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.DisableOptimizations(Scheduler.Default).Schedule(42, DateTimeOffset.Now, default(Func<IScheduler, int, IDisposable>)));
}
#if !NO_TPL
[TestMethod]
public void DisableOptimizations1()
{
var s = TaskPoolScheduler.Default;
Assert.IsTrue(s is IServiceProvider);
var t = s.DisableOptimizations();
var d = t.Now - s.Now;
Assert.IsTrue(d.TotalSeconds < 1);
var e1 = new ManualResetEvent(false);
t.Schedule(42, (self, state) =>
{
e1.Set();
return Disposable.Empty;
});
e1.WaitOne();
var e2 = new ManualResetEvent(false);
t.Schedule(42, TimeSpan.FromMilliseconds(1), (self, state) =>
{
e2.Set();
return Disposable.Empty;
});
e2.WaitOne();
var e3 = new ManualResetEvent(false);
t.Schedule(42, DateTimeOffset.Now.AddMilliseconds(10), (self, state) =>
{
e3.Set();
return Disposable.Empty;
});
e3.WaitOne();
}
[TestMethod]
public void DisableOptimizations2()
{
var s = TaskPoolScheduler.Default;
Assert.IsTrue(s is IServiceProvider);
var lr1 = ((IServiceProvider)s).GetService(typeof(ISchedulerLongRunning));
Assert.IsNotNull(lr1);
var e1 = new ManualResetEvent(false);
s.Schedule(42, (self, state) =>
{
Assert.IsTrue(self is IServiceProvider);
var lrr1 = ((IServiceProvider)self).GetService(typeof(ISchedulerLongRunning));
Assert.IsNotNull(lrr1);
e1.Set();
return Disposable.Empty;
});
e1.WaitOne();
var t = s.DisableOptimizations();
Assert.IsTrue(t is IServiceProvider);
var lr2 = ((IServiceProvider)t).GetService(typeof(ISchedulerLongRunning));
Assert.IsNull(lr2);
var e2 = new ManualResetEvent(false);
t.Schedule(42, (self, state) =>
{
Assert.IsTrue(self is IServiceProvider);
var lrr2 = ((IServiceProvider)self).GetService(typeof(ISchedulerLongRunning));
Assert.IsNull(lrr2);
e2.Set();
return Disposable.Empty;
});
e2.WaitOne();
}
[TestMethod]
public void DisableOptimizations3()
{
var s = TaskPoolScheduler.Default;
Assert.IsTrue(s is IServiceProvider);
var lr1 = ((IServiceProvider)s).GetService(typeof(ISchedulerLongRunning));
Assert.IsNotNull(lr1);
var p1 = ((IServiceProvider)s).GetService(typeof(ISchedulerPeriodic));
Assert.IsNotNull(p1);
var e1 = new ManualResetEvent(false);
s.Schedule(42, (self, state) =>
{
Assert.IsTrue(self is IServiceProvider);
var lrr1 = ((IServiceProvider)self).GetService(typeof(ISchedulerLongRunning));
Assert.IsNotNull(lrr1);
var pr1 = ((IServiceProvider)self).GetService(typeof(ISchedulerPeriodic));
Assert.IsNotNull(pr1);
e1.Set();
return Disposable.Empty;
});
e1.WaitOne();
var t = s.DisableOptimizations(typeof(ISchedulerLongRunning));
Assert.IsTrue(t is IServiceProvider);
var lr2 = ((IServiceProvider)t).GetService(typeof(ISchedulerLongRunning));
Assert.IsNull(lr2);
var p2 = ((IServiceProvider)t).GetService(typeof(ISchedulerPeriodic));
Assert.IsNotNull(p2);
var e2 = new ManualResetEvent(false);
t.Schedule(42, (self, state) =>
{
Assert.IsTrue(self is IServiceProvider);
var lrr2 = ((IServiceProvider)self).GetService(typeof(ISchedulerLongRunning));
Assert.IsNull(lrr2);
var pr2 = ((IServiceProvider)self).GetService(typeof(ISchedulerPeriodic));
Assert.IsNotNull(pr2);
e2.Set();
return Disposable.Empty;
});
e2.WaitOne();
}
#endif
#endif
[TestMethod]
public void DisableOptimizations_UnknownService()
{
var s = new MyScheduler();
Assert.IsFalse(s is IServiceProvider);
var d = s.DisableOptimizations();
Assert.IsTrue(d is IServiceProvider);
Assert.IsNull(((IServiceProvider)d).GetService(typeof(bool)));
}
class MyScheduler : IScheduler
{
public MyScheduler()
: this(DateTimeOffset.Now)
{
}
public MyScheduler(DateTimeOffset now)
{
Now = now;
}
public DateTimeOffset Now
{
get;
private set;
}
public IDisposable Schedule<T>(T state, Func<IScheduler, T, IDisposable> action)
{
return action(this, state);
}
public Action<Action<object>, object, TimeSpan> Check { get; set; }
public long WaitCycles { get; set; }
public IDisposable Schedule<T>(T state, TimeSpan dueTime, Func<IScheduler, T, IDisposable> action)
{
Check(o => action(this, (T)o), state, dueTime);
WaitCycles += dueTime.Ticks;
return action(this, state);
}
public IDisposable Schedule<T>(T state, DateTimeOffset dueTime, Func<IScheduler, T, IDisposable> action)
{
return Schedule(state, dueTime - Now, action);
}
}
#endregion
#region Catch
[TestMethod]
public void Catch_ArgumentChecking()
{
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Catch<Exception>(default(IScheduler), _ => true));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Catch<Exception>(Scheduler.Default, default(Func<Exception, bool>)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Catch<Exception>(Scheduler.Default, _ => true).Schedule(42, default(Func<IScheduler, int, IDisposable>)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Catch<Exception>(Scheduler.Default, _ => true).Schedule(42, TimeSpan.FromSeconds(1), default(Func<IScheduler, int, IDisposable>)));
ReactiveAssert.Throws<ArgumentNullException>(() => Scheduler.Catch<Exception>(Scheduler.Default, _ => true).Schedule(42, DateTimeOffset.Now, default(Func<IScheduler, int, IDisposable>)));
}
[TestMethod]
public void Catch_Builtin_Swallow_Shallow()
{
var done = new ManualResetEvent(false);
var swallow = Scheduler.Default.Catch<InvalidOperationException>(_ => { done.Set(); return true; });
swallow.Schedule(() =>
{
throw new InvalidOperationException();
});
done.WaitOne();
}
[TestMethod]
public void Catch_Builtin_Swallow_Recursive()
{
var done = new ManualResetEvent(false);
var swallow = Scheduler.Default.Catch<InvalidOperationException>(_ => { done.Set(); return true; });
swallow.Schedule(42, (self, state) =>
{
return self.Schedule(() =>
{
throw new InvalidOperationException();
});
});
done.WaitOne();
}
[TestMethod]
public void Catch_Custom_Unhandled()
{
var err = default(Exception);
var scheduler = new MyExceptionScheduler(ex_ => err = ex_);
scheduler.Catch<InvalidOperationException>(_ => true).Schedule(() =>
{
throw new InvalidOperationException();
});
Assert.IsNull(err);
var ex = new ArgumentException();
scheduler.Catch<InvalidOperationException>(_ => true).Schedule(() =>
{
throw ex;
});
Assert.AreSame(ex, err);
}
[TestMethod]
public void Catch_Custom_Rethrow()
{
var err = default(Exception);
var scheduler = new MyExceptionScheduler(ex_ => err = ex_);
var ex = new InvalidOperationException();
scheduler.Catch<InvalidOperationException>(_ => false).Schedule(() =>
{
throw ex;
});
Assert.AreSame(ex, err);
}
[TestMethod]
public void Catch_Custom_LongRunning_Caught()
{
var err = default(Exception);
var scheduler = new MyExceptionScheduler(ex_ => err = ex_);
var caught = false;
var @catch = scheduler.Catch<InvalidOperationException>(_ => caught = true);
var slr = (ISchedulerLongRunning)((IServiceProvider)@catch).GetService(typeof(ISchedulerLongRunning));
slr.ScheduleLongRunning(cancel =>
{
throw new InvalidOperationException();
});
Assert.IsTrue(caught);
Assert.IsNull(err);
var ex = new ArgumentException();
slr.ScheduleLongRunning(cancel =>
{
throw ex;
});
Assert.AreSame(ex, err);
}
[TestMethod]
public void Catch_Custom_LongRunning_Rethrow()
{
var err = default(Exception);
var scheduler = new MyExceptionScheduler(ex_ => err = ex_);
var @catch = scheduler.Catch<InvalidOperationException>(_ => false);
var slr = (ISchedulerLongRunning)((IServiceProvider)@catch).GetService(typeof(ISchedulerLongRunning));
var done = false;
slr.ScheduleLongRunning(cancel =>
{
done = true;
});
Assert.IsTrue(done);
var ex = new InvalidOperationException();
slr.ScheduleLongRunning(cancel =>
{
throw ex;
});
Assert.AreSame(ex, err);
}
[TestMethod]
public void Catch_Custom_Periodic_Regular()
{
var scheduler = new MyExceptionScheduler(_ => { });
scheduler.PeriodicStopped = new ManualResetEvent(false);
var @catch = scheduler.Catch<InvalidOperationException>(_ => true);
var per = (ISchedulerPeriodic)((IServiceProvider)@catch).GetService(typeof(ISchedulerPeriodic));
var madeProgress = new ManualResetEvent(false);
var d = per.SchedulePeriodic(0, TimeSpan.Zero, x =>
{
if (x > 10)
madeProgress.Set();
return x + 1;
});
madeProgress.WaitOne();
d.Dispose();
scheduler.PeriodicStopped.WaitOne();
}
[TestMethod]
public void Catch_Custom_Periodic_Uncaught1()
{
var err = default(Exception);
var done = new ManualResetEvent(false);
var scheduler = new MyExceptionScheduler(ex_ => { err = ex_; done.Set(); });
scheduler.PeriodicStopped = new ManualResetEvent(false);
var @catch = scheduler.Catch<InvalidOperationException>(_ => true);
var per = (ISchedulerPeriodic)((IServiceProvider)@catch).GetService(typeof(ISchedulerPeriodic));
var ex = new ArgumentException();
per.SchedulePeriodic(42, TimeSpan.Zero, x =>
{
throw ex;
});
scheduler.PeriodicStopped.WaitOne();
done.WaitOne();
Assert.AreSame(ex, err);
}
[TestMethod]
public void Catch_Custom_Periodic_Uncaught2()
{
var err = default(Exception);
var done = new ManualResetEvent(false);
var scheduler = new MyExceptionScheduler(ex_ => { err = ex_; done.Set(); });
scheduler.PeriodicStopped = new ManualResetEvent(false);
var @catch = scheduler.Catch<InvalidOperationException>(_ => false);
var per = (ISchedulerPeriodic)((IServiceProvider)@catch).GetService(typeof(ISchedulerPeriodic));
var ex = new InvalidOperationException();
per.SchedulePeriodic(42, TimeSpan.Zero, x =>
{
throw ex;
});
scheduler.PeriodicStopped.WaitOne();
done.WaitOne();
Assert.AreSame(ex, err);
}
[TestMethod]
public void Catch_Custom_Periodic_Caught()
{
var err = default(Exception);
var scheduler = new MyExceptionScheduler(ex_ => err = ex_);
scheduler.PeriodicStopped = new ManualResetEvent(false);
var caught = new ManualResetEvent(false);
var @catch = scheduler.Catch<InvalidOperationException>(_ => { caught.Set(); return true; });
var per = (ISchedulerPeriodic)((IServiceProvider)@catch).GetService(typeof(ISchedulerPeriodic));
per.SchedulePeriodic(42, TimeSpan.Zero, x =>
{
throw new InvalidOperationException();
});
scheduler.PeriodicStopped.WaitOne();
caught.WaitOne();
Assert.IsNull(err);
}
class MyExceptionScheduler : LocalScheduler, ISchedulerLongRunning, ISchedulerPeriodic
{
private readonly Action<Exception> _onError;
public MyExceptionScheduler(Action<Exception> onError)
{
_onError = onError;
}
public override IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
{
try
{
return action(this, state);
}
catch (Exception exception)
{
_onError(exception);
return Disposable.Empty;
}
}
public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
{
throw new NotImplementedException();
}
public IDisposable ScheduleLongRunning<TState>(TState state, Action<TState, ICancelable> action)
{
var b = new BooleanDisposable();
try
{
action(state, b);
}
catch (Exception exception)
{
_onError(exception);
return Disposable.Empty;
}
return b;
}
public ManualResetEvent PeriodicStopped { get; set; }
public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action)
{
var b = new BooleanDisposable();
Scheduler.Default.Schedule(() =>
{
try
{
var s = state;
for (int i = 0; true; i++)
{
if (i > 100 /* mimic delayed cancellation */ && b.IsDisposed)
break;
s = action(s);
}
}
catch (Exception exception)
{
_onError(exception);
}
finally
{
PeriodicStopped.Set();
}
});
return b;
}
}
#endregion
#region Services
[TestMethod]
public void InvalidService_Null()
{
var s = new MySchedulerWithoutServices();
Assert.IsNull(((IServiceProvider)s).GetService(typeof(IAsyncResult)));
}
class MySchedulerWithoutServices : LocalScheduler
{
public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
{
throw new NotImplementedException();
}
}
[TestMethod]
public void DetectServices_Null_1()
{
var s = new MyDumbScheduler1();
Assert.IsNull(Scheduler.AsLongRunning(s));
Assert.IsNull(Scheduler.AsPeriodic(s));
Assert.IsNull(Scheduler.AsStopwatchProvider(s));
}
class MyDumbScheduler1 : IScheduler
{
public DateTimeOffset Now
{
get { throw new NotImplementedException(); }
}
public IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
{
throw new NotImplementedException();
}
public IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
{
throw new NotImplementedException();
}
public IDisposable Schedule<TState>(TState state, DateTimeOffset dueTime, Func<IScheduler, TState, IDisposable> action)
{
throw new NotImplementedException();
}
}
[TestMethod]
public void DetectServices_Null_2()
{
var s = new MyDumbScheduler2(new Dictionary<Type, object>());
Assert.IsNull(Scheduler.AsLongRunning(s));
Assert.IsNull(Scheduler.AsPeriodic(s));
Assert.IsNull(Scheduler.AsStopwatchProvider(s));
}
[TestMethod]
public void DetectServices_Found()
{
{
var x = new MyLongRunning();
var s = new MyDumbScheduler2(new Dictionary<Type, object>
{
{ typeof(ISchedulerLongRunning), x }
});
Assert.AreEqual(x, Scheduler.AsLongRunning(s));
}
{
var x = new MyStopwatchProvider();
var s = new MyDumbScheduler2(new Dictionary<Type, object>
{
{ typeof(IStopwatchProvider), x }
});
Assert.AreEqual(x, Scheduler.AsStopwatchProvider(s));
}
{
var x = new MyPeriodic();
var s = new MyDumbScheduler2(new Dictionary<Type, object>
{
{ typeof(ISchedulerPeriodic), x }
});
Assert.AreEqual(x, Scheduler.AsPeriodic(s));
}
}
class MyDumbScheduler2 : IScheduler, IServiceProvider
{
private readonly Dictionary<Type, object> _services;
public MyDumbScheduler2(Dictionary<Type, object> services)
{
_services = services;
}
public DateTimeOffset Now
{
get { throw new NotImplementedException(); }
}
public IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
{
throw new NotImplementedException();
}
public IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
{
throw new NotImplementedException();
}
public IDisposable Schedule<TState>(TState state, DateTimeOffset dueTime, Func<IScheduler, TState, IDisposable> action)
{
throw new NotImplementedException();
}
public object GetService(Type serviceType)
{
var res = default(object);
if (_services.TryGetValue(serviceType, out res))
return res;
return null;
}
}
class MyLongRunning : ISchedulerLongRunning
{
public IDisposable ScheduleLongRunning<TState>(TState state, Action<TState, ICancelable> action)
{
throw new NotImplementedException();
}
}
class MyStopwatchProvider : IStopwatchProvider
{
public IStopwatch StartStopwatch()
{
throw new NotImplementedException();
}
}
class MyPeriodic : ISchedulerPeriodic
{
public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action)
{
throw new NotImplementedException();
}
}
#endregion
}
}