using System; using System.Threading.Tasks; using Xunit.Sdk; using Nito.AsyncEx; using System.Threading; namespace CefSharp.Test { internal static class AssertEx { /// /// Verifies that a event with the exact event args (and not a derived type) is raised /// This method differs from /// in that it waits for the event to be raised before returning (or is cancelled). /// /// The type of the event arguments to expect /// number of miliseconds to wait before the timeout /// Code to attach the event handler /// Code to detach the event handler /// A delegate to the code to be tested /// The event sender and arguments wrapped in an object /// Thrown when the expected event was not raised. public static async Task> RaisesAsync( int cancelAfter, Action> attach, Action> detach, Action testCode) where T : EventArgs { var raisedEvent = await RaisesAsyncInternal(cancelAfter, attach, detach, testCode); if (raisedEvent == null) throw new RaisesException(typeof(T)); if (raisedEvent.Arguments != null && !raisedEvent.Arguments.GetType().Equals(typeof(T))) throw new RaisesException(typeof(T), raisedEvent.Arguments.GetType()); return raisedEvent; } private static async Task> RaisesAsyncInternal( int cancelAfter, Action> attach, Action> detach, Action testCode) where T : EventArgs { GuardArgumentNotNull(nameof(attach), attach); GuardArgumentNotNull(nameof(detach), detach); GuardArgumentNotNull(nameof(testCode), testCode); using var cts = new CancellationTokenSource(); var manualResetEvent = new AsyncManualResetEvent(); cts.CancelAfter(cancelAfter); Xunit.Assert.RaisedEvent raisedEvent = null; attach(Handler); testCode(); await manualResetEvent.WaitAsync(cts.Token); detach(Handler); return raisedEvent; void Handler(object s, T args) { raisedEvent = new Xunit.Assert.RaisedEvent(s, args); manualResetEvent.Set(); } } internal static void GuardArgumentNotNull(string argName, object argValue) { if (argValue == null) { throw new ArgumentNullException(argName); } } } }