public async Task Connect_Address_Credentials_Succeeds_When_TcpConnection_Succeeds(IPEndPoint endpoint, string username, string password)
        {
            var c = new Mock <IMessageConnection>();

            var factory = new Mock <IConnectionFactory>();

            factory.Setup(m => m.GetServerConnection(
                              It.IsAny <IPEndPoint>(),
                              It.IsAny <EventHandler>(),
                              It.IsAny <EventHandler <ConnectionDisconnectedEventArgs> >(),
                              It.IsAny <EventHandler <MessageReadEventArgs> >(),
                              It.IsAny <ConnectionOptions>(),
                              It.IsAny <ITcpClient>()))
            .Returns(c.Object);

            var key = new WaitKey(MessageCode.Server.Login);

            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.Wait <LoginResponse>(key, It.IsAny <int?>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(new LoginResponse(succeeded: true, string.Empty)));

            using (var s = new SoulseekClient(connectionFactory: factory.Object, waiter: waiter.Object))
            {
                var ex = await Record.ExceptionAsync(async() => await s.ConnectAsync(endpoint.Address.ToString(), endpoint.Port, username, password));

                Assert.Null(ex);
            }

            waiter.Verify(m => m.Wait <LoginResponse>(key, It.IsAny <int?>(), It.IsAny <CancellationToken>()), Times.Once);
        }
Example #2
0
        public async Task LeaveRoomAsync_Uses_Given_CancellationToken(string roomName)
        {
            var cancellationToken = new CancellationToken();

            var conn = new Mock <IMessageConnection>();

            conn.Setup(m => m.State)
            .Returns(ConnectionState.Connected);

            var key    = new WaitKey(MessageCode.Server.LeaveRoom, roomName);
            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.Wait(It.Is <WaitKey>(k => k.Equals(key)), It.IsAny <int?>(), It.IsAny <CancellationToken?>()))
            .Returns(Task.CompletedTask);

            using (var s = new SoulseekClient(serverConnection: conn.Object, waiter: waiter.Object))
            {
                s.SetProperty("State", SoulseekClientStates.Connected | SoulseekClientStates.LoggedIn);

                var ex = await Record.ExceptionAsync(() => s.LeaveRoomAsync(roomName, cancellationToken));

                Assert.Null(ex);
            }

            conn.Verify(m => m.WriteAsync(It.IsAny <byte[]>(), cancellationToken), Times.Once);
        }
Example #3
0
        public void Wait_Dictionary_And_Queue_Are_Collected_After_Last_Wait_Is_Dequeued()
        {
            using (var waiter = new Waiter())
            {
                var waits = waiter.GetProperty <ConcurrentDictionary <WaitKey, ConcurrentQueue <PendingWait> > >("Waits");
                var locks = waiter.GetProperty <ConcurrentDictionary <WaitKey, ReaderWriterLockSlim> >("Locks");

                int CountWaits(WaitKey k)
                {
                    waits.TryGetValue(k, out var queue);
                    return(queue?.Count ?? 0);
                }

                var key = new WaitKey("foo");

                waiter.Wait(key);
                waiter.Wait(key);

                Assert.Equal(2, CountWaits(key));
                Assert.True(waits.TryGetValue(key, out _));
                Assert.True(locks.TryGetValue(key, out _));

                waiter.Complete(key);

                Assert.Equal(1, CountWaits(key));
                Assert.True(waits.TryGetValue(key, out _));
                Assert.True(locks.TryGetValue(key, out _));

                waiter.Complete(key);

                Assert.Equal(0, CountWaits(key));
                Assert.False(waits.TryGetValue(key, out _));
                Assert.False(locks.TryGetValue(key, out _));
            }
        }
Example #4
0
        public void Wait_Throws_And_Is_Dequeued_When_Cancelled()
        {
            using (var tcs = new CancellationTokenSource())
            {
                tcs.CancelAfter(100);

                var key = new WaitKey(MessageCode.ServerLogin);

                using (var waiter = new Waiter(0))
                {
                    Task <object> task   = waiter.Wait <object>(key, 999999, tcs.Token);
                    object        result = null;

                    var ex = Record.Exception(() => result = task.Result);

                    var waits = waiter.GetProperty <ConcurrentDictionary <WaitKey, ConcurrentQueue <PendingWait> > >("Waits");
                    waits.TryGetValue(key, out var queue);
                    queue.TryPeek(out var wait);

                    Assert.NotNull(ex);
                    Assert.IsType <OperationCanceledException>(ex.InnerException);

                    Assert.NotEmpty(waits);
                    Assert.Single(waits);

                    Assert.NotNull(queue);
                    Assert.Empty(queue);
                }
            }
        }
Example #5
0
        static void Main(string[] args)
        {
            MyClass   t           = new MyClass();
            NonStatic myNonStatic = new NonStatic(t.Y);

            myNonStatic();
            myNonStatic = MyClass.X;
            myNonStatic();

            MyWrite    myDelegate = new MyWrite(WriteLine);
            MyReadLine read       = ReadLine;
            string     str        = read();

            MyWrite write = WriteLine;

            write("myDelegate == WriteLine()");
            write(str);
            write = Write;
            write("myDelegate == Write()");
            write("a");
            write("a");
            write("a");
            write("a");

            WaitKey wait = ReadKey;

            wait();
        }
Example #6
0
        public async Task JoinRoomAsync_Returns_Expected_Response_On_Success(string roomName)
        {
            var conn = new Mock <IMessageConnection>();

            conn.Setup(m => m.State)
            .Returns(ConnectionState.Connected);

            var expectedResponse = new RoomData(roomName, 0, Enumerable.Empty <UserData>(), false, null, null, null);

            var key    = new WaitKey(MessageCode.Server.JoinRoom, roomName);
            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.Wait <RoomData>(It.Is <WaitKey>(k => k.Equals(key)), It.IsAny <int?>(), It.IsAny <CancellationToken?>()))
            .Returns(Task.FromResult(expectedResponse));

            using (var s = new SoulseekClient(serverConnection: conn.Object, waiter: waiter.Object))
            {
                s.SetProperty("State", SoulseekClientStates.Connected | SoulseekClientStates.LoggedIn);

                RoomData response;

                response = await s.JoinRoomAsync(roomName);

                Assert.Equal(expectedResponse, response);
            }
        }
Example #7
0
        public void Wait_Throws_And_Is_Dequeued_When_Cancelled()
        {
            using (var tcs = new CancellationTokenSource())
            {
                tcs.CancelAfter(100);

                var key = new WaitKey(MessageCode.Server.Login);

                using (var waiter = new Waiter(0))
                {
                    Task <object> task   = waiter.Wait <object>(key, 999999, tcs.Token);
                    object        result = null;

                    // stick another wait in the same queue to prevent the disposal logic from removing
                    // the dictionary record before we can inspect it
                    waiter.Wait <object>(key, 999999, CancellationToken.None);

                    var ex = Record.Exception(() => result = task.Result);

                    var waits = waiter.GetProperty <ConcurrentDictionary <WaitKey, ConcurrentQueue <PendingWait> > >("Waits");
                    waits.TryGetValue(key, out var queue);
                    queue.TryPeek(out var wait);

                    Assert.NotNull(ex);
                    Assert.IsType <OperationCanceledException>(ex.InnerException);

                    Assert.NotEmpty(waits);
                    Assert.Single(waits);

                    Assert.NotNull(queue);
                    Assert.Single(queue); // should contain only the dummy wait
                }
            }
        }
Example #8
0
        public async Task GetRoomListAsync_Uses_Given_CancellationToken(IReadOnlyCollection <RoomInfo> rooms, CancellationToken cancellationToken)
        {
            var conn = new Mock <IMessageConnection>();

            conn.Setup(m => m.State)
            .Returns(ConnectionState.Connected);

            var key    = new WaitKey(MessageCode.Server.RoomList);
            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.Wait <IReadOnlyCollection <RoomInfo> >(It.Is <WaitKey>(k => k.Equals(key)), It.IsAny <int?>(), It.IsAny <CancellationToken?>()))
            .Returns(Task.FromResult(rooms));

            using (var s = new SoulseekClient(serverConnection: conn.Object, waiter: waiter.Object))
            {
                s.SetProperty("State", SoulseekClientStates.Connected | SoulseekClientStates.LoggedIn);

                IReadOnlyCollection <RoomInfo> response;

                response = await s.GetRoomListAsync(cancellationToken);

                Assert.Equal(rooms, response);
            }

            conn.Verify(m => m.WriteAsync(It.IsAny <IOutgoingMessage>(), cancellationToken), Times.Once);
        }
Example #9
0
        internal void Non_Generic_Wait_Invocation_Creates_Valid_Wait(MessageCode.Server code, string token, int?timeout)
        {
            var key = new WaitKey(code, token);

            using (var waiter = new Waiter())
            {
                Task task = waiter.Wait(key, timeout);

                var waits = waiter.GetProperty <ConcurrentDictionary <WaitKey, ConcurrentQueue <PendingWait> > >("Waits");
                waits.TryGetValue(key, out var queue);
                queue.TryPeek(out var wait);

                Assert.IsType <Task <object> >(task);
                Assert.NotNull(task);
                Assert.Equal(TaskStatus.WaitingForActivation, task.Status);

                Assert.NotEmpty(waits);
                Assert.Single(waits);

                Assert.NotNull(queue);
                Assert.Single(queue);
                Assert.NotEqual(new DateTime(), wait.DateTime);

                if (timeout != null)
                {
                    Assert.Equal(timeout, wait.TimeoutAfter);
                }
            }
        }
Example #10
0
        public void Wait_For_Subsequent_MessageCode_Enqueues_Wait()
        {
            using (var waiter = new Waiter())
            {
                var task1 = waiter.Wait <object>(new WaitKey(MessageCode.Server.Login));
                var task2 = waiter.Wait <object>(new WaitKey(MessageCode.Server.Login));

                var key = new WaitKey(MessageCode.Server.Login);

                var waits = waiter.GetProperty <ConcurrentDictionary <WaitKey, ConcurrentQueue <PendingWait> > >("Waits");
                waits.TryGetValue(key, out var queue);

                Assert.IsType <Task <object> >(task1);
                Assert.NotNull(task1);
                Assert.Equal(TaskStatus.WaitingForActivation, task1.Status);

                Assert.IsType <Task <object> >(task2);
                Assert.NotNull(task2);
                Assert.Equal(TaskStatus.WaitingForActivation, task2.Status);

                Assert.NotEmpty(waits);
                Assert.Single(waits);

                Assert.NotNull(queue);
                Assert.Equal(2, queue.Count);
            }
        }
Example #11
0
        public void Completes_Solicited_Distributed_Connection_On_Distributed_PierceFirewall(IPAddress ip, string username, int token)
        {
            var(handler, mocks) = GetFixture(ip);

            var message      = new PierceFirewall(token);
            var messageBytes = message.ToByteArray().AsSpan().Slice(4).ToArray();

            mocks.Connection.Setup(m => m.ReadAsync(4, It.IsAny <CancellationToken?>()))
            .Returns(Task.FromResult(BitConverter.GetBytes(messageBytes.Length)));
            mocks.Connection.Setup(m => m.ReadAsync(messageBytes.Length, It.IsAny <CancellationToken?>()))
            .Returns(Task.FromResult(messageBytes));

            var dict = new ConcurrentDictionary <int, string>();

            dict.TryAdd(token, username);

            mocks.DistributedConnectionManager.Setup(m => m.PendingSolicitations)
            .Returns(dict);

            handler.HandleConnection(null, mocks.Connection.Object);

            var expectedKey = new WaitKey(Constants.WaitKey.SolicitedDistributedConnection, username, token);

            mocks.Waiter.Verify(m => m.Complete(expectedKey, mocks.Connection.Object), Times.Once);
        }
        public void Sets_BranchRoot_On_Message_From_Parent(string parent, IPAddress ip, int port, string root)
        {
            var(handler, mocks) = GetFixture();

            var conn = new Mock <IMessageConnection>();

            conn.Setup(m => m.Context).Returns(null);
            conn.Setup(m => m.IPAddress).Returns(ip);
            conn.Setup(m => m.Port).Returns(port);
            conn.Setup(m => m.Username).Returns(parent);
            conn.Setup(m => m.Key).Returns(new ConnectionKey(ip, port));

            var key = new WaitKey(Constants.WaitKey.BranchRootMessage, conn.Object.Context, conn.Object.Key);

            mocks.DistributedConnectionManager.Setup(m => m.Parent).Returns((parent, ip, port));

            var message = new MessageBuilder()
                          .WriteCode(MessageCode.Distributed.BranchRoot)
                          .WriteString(root)
                          .Build();

            handler.HandleMessage(conn.Object, message);

            mocks.Waiter.Verify(m => m.Complete <string>(key, root), Times.Once);
            mocks.DistributedConnectionManager.Verify(m => m.SetBranchRoot(root), Times.Once);
        }
Example #13
0
        public void Wait_Throws_And_Is_Dequeued_When_Timing_out()
        {
            var key = new WaitKey(MessageCode.Server.Login);

            using (var waiter = new Waiter(0))
            {
                Task <object> task   = waiter.Wait <object>(key);
                object        result = null;

                // stick another wait in the same queue to prevent the disposal logic from removing
                // the dictionary record before we can inspect it
                waiter.Wait <object>(key, timeout: 99999);

                var ex = Record.Exception(() => result = task.Result);

                var waits = waiter.GetProperty <ConcurrentDictionary <WaitKey, ConcurrentQueue <PendingWait> > >("Waits");
                waits.TryGetValue(key, out var queue);
                queue.TryPeek(out var wait);

                Assert.NotNull(ex);
                Assert.IsType <TimeoutException>(ex.InnerException);

                Assert.NotEmpty(waits);
                Assert.Single(waits);

                Assert.NotNull(queue);
                Assert.Single(queue);
            }
        }
Example #14
0
        public void Expiration_Ignores_Non_Timed_Out_Waits()
        {
            var key = new WaitKey(MessageCode.Server.Login);

            using (var waiter = new Waiter(0))
            {
                Task <object> task = waiter.Wait <object>(key);
                waiter.Wait <object>(key, 30);

                object result = null;

                var ex = Record.Exception(() => result = task.Result);

                var waits = waiter.GetProperty <ConcurrentDictionary <WaitKey, ConcurrentQueue <PendingWait> > >("Waits");
                waits.TryGetValue(key, out var queue);
                queue.TryPeek(out var wait);

                Assert.NotNull(ex);
                Assert.IsType <TimeoutException>(ex.InnerException);

                Assert.NotEmpty(waits);
                Assert.Single(waits);

                Assert.NotNull(queue);
                Assert.Single(queue);
            }
        }
Example #15
0
        public async Task DownloadInternalAsync_Throws_DownloadException_On_PeerTransferRequest_Cancellation(string username, IPAddress ip, int port, string filename, int token, int size)
        {
            var options = new SoulseekClientOptions(messageTimeout: 5);

            var response        = new PeerTransferResponse(token, false, size, string.Empty);
            var responseWaitKey = new WaitKey(MessageCode.PeerTransferResponse, username, token);

            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.Wait <PeerTransferResponse>(It.Is <WaitKey>(w => w.Equals(responseWaitKey)), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(response));
            waiter.Setup(m => m.WaitIndefinitely <PeerTransferRequest>(It.IsAny <WaitKey>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromException <PeerTransferRequest>(new OperationCanceledException()));
            waiter.Setup(m => m.Wait <GetPeerAddressResponse>(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(new GetPeerAddressResponse(username, ip, port)));

            var conn        = new Mock <IMessageConnection>();
            var connFactory = new Mock <IMessageConnectionFactory>();

            connFactory.Setup(m => m.GetMessageConnection(It.IsAny <MessageConnectionType>(), It.IsAny <string>(), It.IsAny <IPAddress>(), It.IsAny <int>(), It.IsAny <ConnectionOptions>()))
            .Returns(conn.Object);

            var s = new SoulseekClient("127.0.0.1", 1, options, messageWaiter: waiter.Object, messageConnectionFactory: connFactory.Object);

            s.SetProperty("State", SoulseekClientStates.Connected | SoulseekClientStates.LoggedIn);

            var ex = await Record.ExceptionAsync(async() => await s.InvokeMethod <Task <byte[]> >("DownloadInternalAsync", username, filename, token, null));

            Assert.NotNull(ex);
            Assert.IsType <DownloadException>(ex);
            Assert.IsType <OperationCanceledException>(ex.InnerException);
        }
Example #16
0
        public void TokenParts_Returns_Empty_Given_Null_Parts()
        {
            WaitKey k = new WaitKey();

            Assert.NotNull(k.TokenParts);
            Assert.Empty(k.TokenParts);
        }
Example #17
0
        public void GetHashCode_Differs_Given_Different_Key()
        {
            var k1 = new WaitKey(1, "test", 5m);
            var k2 = new WaitKey(2, "foo", 2.5m);

            Assert.NotEqual(k1.GetHashCode(), k2.GetHashCode());
        }
Example #18
0
        public void Non_Generic_WaitIndefinitely_Invocation_Creates_Wait_With_Max_Timeout()
        {
            var key = new WaitKey(MessageCode.Server.Login);

            using (var waiter = new Waiter())
            {
                Task task = waiter.WaitIndefinitely(key);

                var waits = waiter.GetProperty <ConcurrentDictionary <WaitKey, ConcurrentQueue <PendingWait> > >("Waits");
                waits.TryGetValue(key, out var queue);
                queue.TryPeek(out var wait);

                Assert.IsType <Task <object> >(task);
                Assert.NotNull(task);
                Assert.Equal(TaskStatus.WaitingForActivation, task.Status);

                Assert.NotEmpty(waits);
                Assert.Single(waits);

                Assert.NotNull(queue);
                Assert.Single(queue);
                Assert.NotEqual(new DateTime(), wait.DateTime);
                Assert.Equal(int.MaxValue, wait.TimeoutAfter);
            }
        }
Example #19
0
        public void GetHashCode_Matches_Given_Identical_Key()
        {
            var k1 = new WaitKey(1, "test", 5m);
            var k2 = new WaitKey(1, "test", 5m);

            Assert.Equal(k1.GetHashCode(), k2.GetHashCode());
        }
Example #20
0
        public void Wait_Throws_And_Is_Dequeued_When_Thrown()
        {
            var key = new WaitKey(MessageCode.Server.Login);

            using (var waiter = new Waiter(0))
            {
                Task <object> task   = waiter.Wait <object>(key, 999999);
                object        result = null;

                waiter.Throw(key, new InvalidOperationException("error"));

                var ex = Record.Exception(() => result = task.Result);

                var waits = waiter.GetProperty <ConcurrentDictionary <WaitKey, ConcurrentQueue <PendingWait> > >("Waits");
                waits.TryGetValue(key, out var queue);
                queue.TryPeek(out var wait);

                Assert.NotNull(ex);
                Assert.IsType <InvalidOperationException>(ex.InnerException);
                Assert.Equal("error", ex.InnerException.Message);

                Assert.NotEmpty(waits);
                Assert.Single(waits);

                Assert.NotNull(queue);
                Assert.Empty(queue);
            }
        }
Example #21
0
        public void Wait_Dictionary_And_Queue_Are_Collected_After_Last_Wait_Is_Dequeued()
        {
            using (var tcs = new CancellationTokenSource())
            {
                tcs.CancelAfter(100);

                var key = new WaitKey(MessageCode.Server.Login);

                using (var waiter = new Waiter(0))
                {
                    Task <object> task   = waiter.Wait <object>(key, 999999, tcs.Token);
                    object        result = null;

                    var ex = Record.Exception(() => result = task.Result);
                    waiter.InvokeMethod("MonitorWaits", null, null); // force clean up.  normally this is on a timer.

                    var waits  = waiter.GetProperty <ConcurrentDictionary <WaitKey, ConcurrentQueue <PendingWait> > >("Waits");
                    var exists = waits.TryGetValue(key, out var record);

                    Assert.NotNull(ex);
                    Assert.IsType <OperationCanceledException>(ex.InnerException);

                    Assert.Empty(waits);
                    Assert.False(exists);
                }
            }
        }
Example #22
0
        public void DoubleEqualOperator_Returns_True_When_Equal()
        {
            var k1 = new WaitKey("foo", 2);
            var k2 = new WaitKey("foo", 2);

            Assert.True(k1 == k2);
            Assert.True(k2 == k1);
        }
Example #23
0
        public void TokenParts_Returns_Parts()
        {
            WaitKey k = new WaitKey(1, 2);

            Assert.Equal(2, k.TokenParts.Length);
            Assert.Contains(1, k.TokenParts);
            Assert.Contains(2, k.TokenParts);
        }
Example #24
0
        public void Instantiates_With_Null_Parts()
        {
            WaitKey k  = null;
            var     ex = Record.Exception(() => k = new WaitKey());

            Assert.Null(ex);
            Assert.NotNull(k);
        }
Example #25
0
        public void Equals_Handles_Boxed_Instances()
        {
            var k1 = new WaitKey("foo", 2);
            var k2 = new WaitKey("foo", 2);

            Assert.True(k1.Equals((object)k2));
            Assert.True(k2.Equals((object)k1));
        }
Example #26
0
        public void Equals_Returns_False_When_Different_Type()
        {
            var k1 = new WaitKey("foo", 2);
            var k2 = "bar";

            Assert.False(k1.Equals(k2));
            Assert.False(k2.Equals(k1));
        }
Example #27
0
        public void Equals_Returns_False_When_Not_Equal()
        {
            var k1 = new WaitKey("foo", 2);
            var k2 = new WaitKey("bar", 3);

            Assert.False(k1.Equals(k2));
            Assert.False(k2.Equals(k1));
        }
Example #28
0
        public void Equals_Returns_True_When_Equal()
        {
            var k1 = new WaitKey("foo", 2);
            var k2 = new WaitKey("foo", 2);

            Assert.True(k1.Equals(k2));
            Assert.True(k2.Equals(k1));
        }
Example #29
0
        public void NotEqualOperator_Returns_False_When_Not_Equal()
        {
            var k1 = new WaitKey("foo", 2);
            var k2 = new WaitKey("bar", 3);

            Assert.True(k1 != k2);
            Assert.True(k2 != k1);
        }
Example #30
0
        public async Task DownloadInternalAsync_Raises_Expected_Final_Event_On_Cancellation(string username, IPAddress ip, int port, string filename, int token, int size)
        {
            var options = new SoulseekClientOptions(messageTimeout: 5);

            var response        = new PeerTransferResponse(token, false, size, string.Empty);
            var responseWaitKey = new WaitKey(MessageCode.PeerTransferResponse, username, token);

            var request = new PeerTransferRequest(TransferDirection.Download, token, filename, size);

            var transferConn = new Mock <IConnection>();

            transferConn.Setup(m => m.WriteAsync(It.IsAny <byte[]>(), It.IsAny <CancellationToken>()))
            .Returns(Task.CompletedTask);

            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.Wait <PeerTransferResponse>(It.Is <WaitKey>(w => w.Equals(responseWaitKey)), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(response));
            waiter.Setup(m => m.WaitIndefinitely <PeerTransferRequest>(It.IsAny <WaitKey>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(request));
            waiter.Setup(m => m.Wait(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.CompletedTask);
            waiter.Setup(m => m.WaitIndefinitely <byte[]>(It.IsAny <WaitKey>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromException <byte[]>(new OperationCanceledException()));
            waiter.Setup(m => m.Wait <IConnection>(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(transferConn.Object));
            waiter.Setup(m => m.Wait <GetPeerAddressResponse>(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(new GetPeerAddressResponse(username, ip, port)));

            var conn = new Mock <IMessageConnection>();

            var connFactory = new Mock <IMessageConnectionFactory>();

            connFactory.Setup(m => m.GetMessageConnection(It.IsAny <MessageConnectionType>(), It.IsAny <string>(), It.IsAny <IPAddress>(), It.IsAny <int>(), It.IsAny <ConnectionOptions>()))
            .Returns(conn.Object);

            var s = new SoulseekClient("127.0.0.1", 1, options, messageWaiter: waiter.Object, messageConnectionFactory: connFactory.Object);

            s.SetProperty("State", SoulseekClientStates.Connected | SoulseekClientStates.LoggedIn);

            var events = new List <DownloadStateChangedEventArgs>();

            s.DownloadStateChanged += (sender, e) =>
            {
                events.Add(e);
            };

            var ex = await Record.ExceptionAsync(async() => await s.InvokeMethod <Task <byte[]> >("DownloadInternalAsync", username, filename, token, null));

            Assert.NotNull(ex);
            Assert.IsType <DownloadException>(ex);
            Assert.IsType <OperationCanceledException>(ex.InnerException);

            Assert.Equal(DownloadStates.Cancelled, events[events.Count - 1].PreviousState);
            Assert.Equal(DownloadStates.Completed | DownloadStates.Cancelled, events[events.Count - 1].State);
        }