Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Address flaky UI framework tests #1379

Merged
merged 2 commits into from
Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 36 additions & 8 deletions Rx.NET/Source/tests/Tests.System.Reactive/DispatcherHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT License.
// See the LICENSE file in the project root for more information.

using System.Reactive.Disposables;

#if NETCOREAPP2_1 || NET472 || NETCOREAPP3_1 || CSWINRT
using System.Threading;
#endif
Expand All @@ -15,8 +17,12 @@ namespace ReactiveTests
#if HAS_DISPATCHER
static class DispatcherHelpers
{
public static DispatcherWrapper EnsureDispatcher()
private static readonly Semaphore s_oneDispatcher = new Semaphore(1, 1);

public static IDisposable RunTest(out DispatcherWrapper wrapper)
{
s_oneDispatcher.WaitOne();

#if DESKTOPCLR
var dispatcher = new Thread(Dispatcher.Run);
dispatcher.IsBackground = true;
Expand All @@ -29,11 +35,38 @@ public static DispatcherWrapper EnsureDispatcher()

while (d.BeginInvoke(new Action(() => { })).Status == DispatcherOperationStatus.Aborted) ;

return new DispatcherWrapper(d);
wrapper = new DispatcherWrapper(d);

return new DispatcherTest(dispatcher);
#else
return new DispatcherWrapper(Dispatcher.CurrentDispatcher);
wrapper = new DispatcherWrapper(Dispatcher.CurrentDispatcher);

return Disposable.Empty; // REVIEW: Anything to shut down?
#endif
}

#if DESKTOPCLR
private sealed class DispatcherTest : IDisposable
{
private readonly Thread _t;

public DispatcherTest(Thread t)
{
_t = t;
}

public void Dispose()
{
var d = Dispatcher.FromThread(_t);

d.BeginInvoke(new Action(() => d.InvokeShutdown()));

_t.Join();

s_oneDispatcher.Release();
}
}
#endif
}

class DispatcherWrapper
Expand All @@ -47,11 +80,6 @@ public DispatcherWrapper(Dispatcher dispatcher)

public Dispatcher Dispatcher { get { return _dispatcher; } }

public void InvokeShutdown()
{
_dispatcher.InvokeShutdown();
}

public static implicit operator Dispatcher(DispatcherWrapper wrapper)
{
return wrapper._dispatcher;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@ public void Ctor_ArgumentChecking()
[Fact]
public void Current()
{
var d = DispatcherHelpers.EnsureDispatcher();
var e = new ManualResetEvent(false);

d.BeginInvoke(() =>
using (DispatcherHelpers.RunTest(out var d))
{
var c = DispatcherScheduler.Current;
c.Schedule(() => { e.Set(); });
});
var e = new ManualResetEvent(false);

d.BeginInvoke(() =>
{
var c = DispatcherScheduler.Current;
c.Schedule(() => { e.Set(); });
});

e.WaitOne();
e.WaitOne();
}
}

[Fact]
Expand Down Expand Up @@ -64,69 +66,76 @@ public void Current_None()
[Fact]
public void Dispatcher()
{
var disp = DispatcherHelpers.EnsureDispatcher();
Assert.Same(disp.Dispatcher, new DispatcherScheduler(disp).Dispatcher);
using (DispatcherHelpers.RunTest(out var disp))
{
Assert.Same(disp.Dispatcher, new DispatcherScheduler(disp).Dispatcher);
}
}

[Fact]
public void Now()
{
var disp = DispatcherHelpers.EnsureDispatcher();
var res = new DispatcherScheduler(disp).Now - DateTime.Now;
Assert.True(res.Seconds < 1);
using (DispatcherHelpers.RunTest(out var disp))
{
var res = new DispatcherScheduler(disp).Now - DateTime.Now;
Assert.True(res.Seconds < 1);
}
}

[Fact]
public void Schedule_ArgumentChecking()
{
var disp = DispatcherHelpers.EnsureDispatcher();
var s = new DispatcherScheduler(disp);
ReactiveAssert.Throws<ArgumentNullException>(() => s.Schedule(42, default(Func<IScheduler, int, IDisposable>)));
ReactiveAssert.Throws<ArgumentNullException>(() => s.Schedule(42, TimeSpan.FromSeconds(1), default(Func<IScheduler, int, IDisposable>)));
ReactiveAssert.Throws<ArgumentNullException>(() => s.Schedule(42, DateTimeOffset.Now, default(Func<IScheduler, int, IDisposable>)));
using (DispatcherHelpers.RunTest(out var disp))
{
var s = new DispatcherScheduler(disp);
ReactiveAssert.Throws<ArgumentNullException>(() => s.Schedule(42, default(Func<IScheduler, int, IDisposable>)));
ReactiveAssert.Throws<ArgumentNullException>(() => s.Schedule(42, TimeSpan.FromSeconds(1), default(Func<IScheduler, int, IDisposable>)));
ReactiveAssert.Throws<ArgumentNullException>(() => s.Schedule(42, DateTimeOffset.Now, default(Func<IScheduler, int, IDisposable>)));
}
}

[Fact]
[Asynchronous]
public void Schedule()
{
var disp = DispatcherHelpers.EnsureDispatcher();

RunAsync(evt =>
using (DispatcherHelpers.RunTest(out var disp))
{
var id = Thread.CurrentThread.ManagedThreadId;
var sch = new DispatcherScheduler(disp);
sch.Schedule(() =>
RunAsync(evt =>
{
Assert.NotEqual(id, Thread.CurrentThread.ManagedThreadId);
disp.InvokeShutdown();
evt.Set();
var id = Thread.CurrentThread.ManagedThreadId;
var sch = new DispatcherScheduler(disp);
sch.Schedule(() =>
{
Assert.NotEqual(id, Thread.CurrentThread.ManagedThreadId);
evt.Set();
});
});
});
}
}

[Fact]
public void ScheduleError()
{
var ex = new Exception();
using (DispatcherHelpers.RunTest(out var disp))
{
var ex = new Exception();

var id = Thread.CurrentThread.ManagedThreadId;
var disp = DispatcherHelpers.EnsureDispatcher();
var evt = new ManualResetEvent(false);
var id = Thread.CurrentThread.ManagedThreadId;
var evt = new ManualResetEvent(false);

Exception thrownEx = null;
disp.UnhandledException += (o, e) =>
{
thrownEx = e.Exception;
evt.Set();
e.Handled = true;
};
var sch = new DispatcherScheduler(disp);
sch.Schedule(() => { throw ex; });
evt.WaitOne();
disp.InvokeShutdown();

Assert.Same(ex, thrownEx);
Exception thrownEx = null;
disp.UnhandledException += (o, e) =>
{
thrownEx = e.Exception;
evt.Set();
e.Handled = true;
};
var sch = new DispatcherScheduler(disp);
sch.Schedule(() => { throw ex; });
evt.WaitOne();

Assert.Same(ex, thrownEx);
}
}

[Fact]
Expand All @@ -143,111 +152,116 @@ public void ScheduleRelative_Zero()

private void ScheduleRelative_(TimeSpan delay)
{
var evt = new ManualResetEvent(false);

var id = Thread.CurrentThread.ManagedThreadId;
using (DispatcherHelpers.RunTest(out var disp))
{
var evt = new ManualResetEvent(false);

var disp = DispatcherHelpers.EnsureDispatcher();
var sch = new DispatcherScheduler(disp);
var id = Thread.CurrentThread.ManagedThreadId;

sch.Schedule(delay, () =>
{
Assert.NotEqual(id, Thread.CurrentThread.ManagedThreadId);
var sch = new DispatcherScheduler(disp);

sch.Schedule(delay, () =>
{
Assert.NotEqual(id, Thread.CurrentThread.ManagedThreadId);
evt.Set();

sch.Schedule(delay, () =>
{
Assert.NotEqual(id, Thread.CurrentThread.ManagedThreadId);
evt.Set();
});
});
});

evt.WaitOne();
disp.InvokeShutdown();
evt.WaitOne();
}
}

[Fact]
public void ScheduleRelative_Cancel()
{
var evt = new ManualResetEvent(false);

var id = Thread.CurrentThread.ManagedThreadId;

var disp = DispatcherHelpers.EnsureDispatcher();
var sch = new DispatcherScheduler(disp);

sch.Schedule(TimeSpan.FromSeconds(0.1), () =>
using (DispatcherHelpers.RunTest(out var disp))
{
Assert.NotEqual(id, Thread.CurrentThread.ManagedThreadId);
var evt = new ManualResetEvent(false);

var id = Thread.CurrentThread.ManagedThreadId;

var d = sch.Schedule(TimeSpan.FromSeconds(0.1), () =>
var sch = new DispatcherScheduler(disp);

sch.Schedule(TimeSpan.FromSeconds(0.1), () =>
{
Assert.True(false);
evt.Set();
});
Assert.NotEqual(id, Thread.CurrentThread.ManagedThreadId);

sch.Schedule(() =>
{
d.Dispose();
});
var d = sch.Schedule(TimeSpan.FromSeconds(0.1), () =>
{
Assert.True(false);
evt.Set();
});

sch.Schedule(TimeSpan.FromSeconds(0.2), () =>
{
Assert.NotEqual(id, Thread.CurrentThread.ManagedThreadId);
evt.Set();
sch.Schedule(() =>
{
d.Dispose();
});

sch.Schedule(TimeSpan.FromSeconds(0.2), () =>
{
Assert.NotEqual(id, Thread.CurrentThread.ManagedThreadId);
evt.Set();
});
});
});

evt.WaitOne();
disp.InvokeShutdown();
evt.WaitOne();
}
}

[Fact]
public void SchedulePeriodic_ArgumentChecking()
{
var disp = DispatcherHelpers.EnsureDispatcher();
var s = new DispatcherScheduler(disp);
using (DispatcherHelpers.RunTest(out var disp))
{
var s = new DispatcherScheduler(disp);

ReactiveAssert.Throws<ArgumentNullException>(() => s.SchedulePeriodic(42, TimeSpan.FromSeconds(1), default(Func<int, int>)));
ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => s.SchedulePeriodic(42, TimeSpan.FromSeconds(-1), x => x));
ReactiveAssert.Throws<ArgumentNullException>(() => s.SchedulePeriodic(42, TimeSpan.FromSeconds(1), default(Func<int, int>)));
ReactiveAssert.Throws<ArgumentOutOfRangeException>(() => s.SchedulePeriodic(42, TimeSpan.FromSeconds(-1), x => x));
}
}

[Fact]
public void SchedulePeriodic()
{
var evt = new ManualResetEvent(false);

var id = Thread.CurrentThread.ManagedThreadId;
using (DispatcherHelpers.RunTest(out var disp))
{
var evt = new ManualResetEvent(false);

var disp = DispatcherHelpers.EnsureDispatcher();
var sch = new DispatcherScheduler(disp);
var id = Thread.CurrentThread.ManagedThreadId;

var d = new SingleAssignmentDisposable();
var sch = new DispatcherScheduler(disp);

d.Disposable = sch.SchedulePeriodic(1, TimeSpan.FromSeconds(0.1), n =>
{
Assert.NotEqual(id, Thread.CurrentThread.ManagedThreadId);
var d = new SingleAssignmentDisposable();

if (n == 3)
d.Disposable = sch.SchedulePeriodic(1, TimeSpan.FromSeconds(0.1), n =>
{
d.Dispose();
Assert.NotEqual(id, Thread.CurrentThread.ManagedThreadId);

sch.Schedule(TimeSpan.FromSeconds(0.2), () =>
if (n == 3)
{
Assert.NotEqual(id, Thread.CurrentThread.ManagedThreadId);
evt.Set();
});
}
d.Dispose();

if (n > 3)
{
Assert.True(false);
}
sch.Schedule(TimeSpan.FromSeconds(0.2), () =>
{
Assert.NotEqual(id, Thread.CurrentThread.ManagedThreadId);
evt.Set();
});
}

return n + 1;
});
if (n > 3)
{
Assert.True(false);
}

return n + 1;
});

evt.WaitOne();
disp.InvokeShutdown();
evt.WaitOne();
}
}
}
}
Expand Down
Loading