Beispiel #1
0
        public async Task BrowseInternalAsync_Returns_Expected_Response_On_Success(string username, IPAddress ip, int port, string localUsername, List <Directory> directories)
        {
            var response = new BrowseResponse(directories.Count, directories);

            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.WaitIndefinitely <BrowseResponse>(It.IsAny <WaitKey>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(response));
            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>();

            conn.Setup(m => m.State)
            .Returns(ConnectionState.Connected);
            conn.Setup(m => m.WriteMessageAsync(It.IsAny <Message>(), It.IsAny <CancellationToken>()))
            .Returns(Task.CompletedTask);

            var connManager = new Mock <IConnectionManager>();

            connManager.Setup(m => m.GetOrAddUnsolicitedConnectionAsync(It.IsAny <ConnectionKey>(), localUsername, It.IsAny <EventHandler <Message> >(), It.IsAny <ConnectionOptions>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(conn.Object));

            var s = new SoulseekClient("127.0.0.1", 1, waiter: waiter.Object, serverConnection: conn.Object, connectionManager: connManager.Object);

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

            var result = await s.BrowseAsync(username);

            Assert.Equal(response, result);
        }
Beispiel #2
0
        public async Task BrowseAsync_Throws_UserOfflineException_On_User_Not_Found(string username, IPEndPoint endpoint, string localUsername)
        {
            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.Wait <UserAddressResponse>(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromException <UserAddressResponse>(new UserOfflineException()));

            var conn = new Mock <IMessageConnection>();

            var connManager = new Mock <IPeerConnectionManager>();

            connManager.Setup(m => m.GetOrAddMessageConnectionAsync(username, endpoint, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(conn.Object));

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

                var ex = await Record.ExceptionAsync(() => s.BrowseAsync(username));

                Assert.NotNull(ex);
                Assert.IsType <UserOfflineException>(ex);
            }
        }
Beispiel #3
0
        public async Task BrowseAsync_Throws_TimeoutException_On_Timeout(string username, IPEndPoint endpoint, string localUsername)
        {
            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.WaitIndefinitely <BrowseResponse>(It.IsAny <WaitKey>(), It.IsAny <CancellationToken>()));
            waiter.Setup(m => m.Wait <UserAddressResponse>(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(new UserAddressResponse(username, endpoint.Address, endpoint.Port)));

            var conn = new Mock <IMessageConnection>();

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

            var connManager = new Mock <IPeerConnectionManager>();

            connManager.Setup(m => m.GetOrAddMessageConnectionAsync(username, endpoint, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(conn.Object));

            waiter.Setup(m => m.Wait <(MessageReceivedEventArgs, IMessageConnection)>(It.IsAny <WaitKey>(), It.IsAny <int?>(), It.IsAny <CancellationToken?>()))
            .Returns(Task.FromException <(MessageReceivedEventArgs, IMessageConnection)>(new TimeoutException()));

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

                var ex = await Record.ExceptionAsync(() => s.BrowseAsync(username));

                Assert.NotNull(ex);
                Assert.IsType <TimeoutException>(ex);
            }
        }
Beispiel #4
0
        public async Task BrowseAsync_Throws_BrowseException_On_Write_Exception(string username, IPEndPoint endpoint, string localUsername)
        {
            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.Wait <UserAddressResponse>(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(new UserAddressResponse(username, endpoint.Address, endpoint.Port)));

            var conn = new Mock <IMessageConnection>();

            conn.Setup(m => m.WriteAsync(It.Is <byte[]>(n => new MessageReader <MessageCode.Peer>(n).ReadCode() == MessageCode.Peer.BrowseRequest), It.IsAny <CancellationToken>()))
            .Throws(new ConnectionWriteException());

            var connManager = new Mock <IPeerConnectionManager>();

            connManager.Setup(m => m.GetOrAddMessageConnectionAsync(username, endpoint, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(conn.Object));

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

                var ex = await Record.ExceptionAsync(() => s.BrowseAsync(username));

                Assert.NotNull(ex);
                Assert.IsType <BrowseException>(ex);
                Assert.IsType <ConnectionWriteException>(ex.InnerException);
            }
        }
Beispiel #5
0
        public async Task BrowseAsync_Throws_ArgumentException_Given_Bad_Username(string username)
        {
            var s = new SoulseekClient();

            var ex = await Record.ExceptionAsync(async() => await s.BrowseAsync(username));

            Assert.NotNull(ex);
            Assert.IsType <ArgumentException>(ex);
        }
Beispiel #6
0
        public async Task BrowseAsync_Throws_InvalidOperationException_When_Not_Connected()
        {
            var s = new SoulseekClient();

            var ex = await Record.ExceptionAsync(async() => await s.BrowseAsync("foo"));

            Assert.NotNull(ex);
            Assert.IsType <InvalidOperationException>(ex);
            Assert.Contains("Connected", ex.Message, StringComparison.InvariantCultureIgnoreCase);
        }
Beispiel #7
0
        public async Task BrowseAsync_Throws_InvalidOperationException_When_Not_Logged_In()
        {
            using (var s = new SoulseekClient())
            {
                s.SetProperty("State", SoulseekClientStates.Connected);

                var ex = await Record.ExceptionAsync(async() => await s.BrowseAsync("foo"));

                Assert.NotNull(ex);
                Assert.IsType <InvalidOperationException>(ex);
                Assert.Contains("logged in", ex.Message, StringComparison.InvariantCultureIgnoreCase);
            }
        }
Beispiel #8
0
        public async Task BrowseAsync_Throws_ConnectionException_On_Unexpected_Disconnect(string username, IPEndPoint endpoint, string localUsername)
        {
            var tcs = new TaskCompletionSource <BrowseResponse>();

            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.WaitIndefinitely <BrowseResponse>(It.IsAny <WaitKey>(), It.IsAny <CancellationToken>()))
            .Returns(tcs.Task);
            waiter.Setup(m => m.Wait <UserAddressResponse>(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(new UserAddressResponse(username, endpoint.Address, endpoint.Port)));

            var conn = new Mock <IMessageConnection>();

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

            var connManager = new Mock <IPeerConnectionManager>();

            connManager.Setup(m => m.GetOrAddMessageConnectionAsync(username, endpoint, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(conn.Object));

            waiter.Setup(m => m.Wait <(MessageReceivedEventArgs, IMessageConnection)>(It.IsAny <WaitKey>(), It.IsAny <int?>(), It.IsAny <CancellationToken?>()))
            .Returns(Task.FromResult((new MessageReceivedEventArgs(1, new byte[] { 0x0 }), conn.Object)));
            waiter.Setup(m => m.Throw(It.IsAny <WaitKey>(), It.IsAny <Exception>()))
            .Callback <WaitKey, Exception>((key, ex) => tcs.SetException(ex));

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

                var task = s.BrowseAsync(username);

                conn.Raise(m => m.Disconnected += null, new ConnectionDisconnectedEventArgs("foo"));

                var ex = await Record.ExceptionAsync(() => task);

                Assert.NotNull(ex);
                Assert.IsType <BrowseException>(ex);
                Assert.IsType <ConnectionException>(ex.InnerException);
            }
        }
Beispiel #9
0
        public async Task BrowseAsync_Raises_BrowseProgressUpdated_Event_At_Least_Twice(string username, IPEndPoint endpoint, string localUsername, List <Directory> directories, int length)
        {
            var response = new BrowseResponse(directories.Count, directories);

            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.WaitIndefinitely <BrowseResponse>(It.IsAny <WaitKey>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(response));
            waiter.Setup(m => m.Wait <UserAddressResponse>(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(new UserAddressResponse(username, endpoint.Address, endpoint.Port)));

            var conn = new Mock <IMessageConnection>();

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

            var connManager = new Mock <IPeerConnectionManager>();

            connManager.Setup(m => m.GetOrAddMessageConnectionAsync(username, endpoint, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(conn.Object));

            waiter.Setup(m => m.Wait <(MessageReceivedEventArgs, IMessageConnection)>(It.IsAny <WaitKey>(), It.IsAny <int?>(), It.IsAny <CancellationToken?>()))
            .Returns(Task.FromResult((new MessageReceivedEventArgs(length, new byte[] { 0x0 }), conn.Object)));

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

                var events = new List <BrowseProgressUpdatedEventArgs>();
                s.BrowseProgressUpdated += (sender, args) => events.Add(args);

                await s.BrowseAsync(username);

                Assert.NotEmpty(events);
                Assert.Equal(2, events.Count);
                Assert.Equal(0, events[0].PercentComplete);
                Assert.Equal(100, events[1].PercentComplete);
            }
        }
Beispiel #10
0
        public async Task BrowseAsync_Returns_Expected_Response_On_Success(string username, IPEndPoint endpoint, string localUsername, List <Directory> directories)
        {
            var response = new BrowseResponse(directories.Count, directories);

            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.WaitIndefinitely <BrowseResponse>(It.IsAny <WaitKey>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(response));
            waiter.Setup(m => m.Wait <UserAddressResponse>(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(new UserAddressResponse(username, endpoint.Address, endpoint.Port)));

            var conn = new Mock <IMessageConnection>();

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

            var connManager = new Mock <IPeerConnectionManager>();

            connManager.Setup(m => m.GetOrAddMessageConnectionAsync(username, endpoint, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(conn.Object));

            waiter.Setup(m => m.Wait <(MessageReceivedEventArgs, IMessageConnection)>(It.IsAny <WaitKey>(), It.IsAny <int?>(), It.IsAny <CancellationToken?>()))
            .Returns(Task.FromResult((new MessageReceivedEventArgs(1, new byte[] { 0x0 }), conn.Object)));

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

                var result = await s.BrowseAsync(username);

                Assert.Equal(response.Directories, result);
            }
        }
Beispiel #11
0
        public async Task BrowseAsync_Throws_BrowseException_On_Disconnect(string username, IPEndPoint endpoint, string localUsername)
        {
            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.Wait <UserAddressResponse>(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(new UserAddressResponse(username, endpoint.Address, endpoint.Port)));
            waiter.Setup(m => m.WaitIndefinitely <BrowseResponse>(It.IsAny <WaitKey>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromException <BrowseResponse>(new ConnectionException("disconnected unexpectedly")));

            var conn = new Mock <IMessageConnection>();

            conn.Setup(m => m.WriteAsync(It.IsAny <byte[]>(), It.IsAny <CancellationToken>()))
            .Returns(Task.CompletedTask)
            .Raises(m => m.Disconnected += null, conn.Object, new ConnectionDisconnectedEventArgs(string.Empty));

            var connManager = new Mock <IPeerConnectionManager>();

            connManager.Setup(m => m.GetOrAddMessageConnectionAsync(username, endpoint, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(conn.Object));

            waiter.Setup(m => m.Wait <(MessageReceivedEventArgs, IMessageConnection)>(It.IsAny <WaitKey>(), It.IsAny <int?>(), It.IsAny <CancellationToken?>()))
            .Returns(Task.FromResult((new MessageReceivedEventArgs(1, new byte[] { 0x0 }), conn.Object)));

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

                var ex = await Record.ExceptionAsync(() => s.BrowseAsync(username));

                Assert.NotNull(ex);
                Assert.IsType <BrowseException>(ex);
                Assert.IsType <ConnectionException>(ex.InnerException);
                Assert.Contains("disconnected unexpectedly", ex.InnerException.Message, StringComparison.InvariantCultureIgnoreCase);
            }
        }
Beispiel #12
0
        public async Task BrowseAsync_Uses_Given_CancellationToken(string username, IPEndPoint endpoint, string localUsername, List <Directory> directories, CancellationToken cancellationToken)
        {
            var response = new BrowseResponse(directories);

            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.WaitIndefinitely <BrowseResponse>(It.IsAny <WaitKey>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(response));
            waiter.Setup(m => m.Wait <UserAddressResponse>(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(new UserAddressResponse(username, endpoint.Address, endpoint.Port)));

            var conn = new Mock <IMessageConnection>();

            conn.Setup(m => m.State)
            .Returns(ConnectionState.Connected);
            conn.Setup(m => m.WriteAsync(It.IsAny <IOutgoingMessage>(), It.IsAny <CancellationToken>()))
            .Returns(Task.CompletedTask);

            var connManager = new Mock <IPeerConnectionManager>();

            connManager.Setup(m => m.GetOrAddMessageConnectionAsync(username, endpoint, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(conn.Object));

            waiter.Setup(m => m.Wait <(MessageReceivedEventArgs, IMessageConnection)>(It.IsAny <WaitKey>(), It.IsAny <int?>(), It.IsAny <CancellationToken?>()))
            .Returns(Task.FromResult((new MessageReceivedEventArgs(1, new byte[] { 0x0 }), conn.Object)));

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

                await s.BrowseAsync(username, cancellationToken : cancellationToken);
            }

            conn.Verify(m => m.WriteAsync(It.IsAny <IOutgoingMessage>(), cancellationToken), Times.AtLeastOnce);
        }
Beispiel #13
0
        public async Task BrowseInternalAsync_Throws_BrowseException_On_Disconnect(string username, IPAddress ip, int port, string localUsername)
        {
            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.Wait <GetPeerAddressResponse>(It.IsAny <WaitKey>(), null, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(new GetPeerAddressResponse(username, ip, port)));
            waiter.Setup(m => m.WaitIndefinitely <BrowseResponse>(It.IsAny <WaitKey>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromException <BrowseResponse>(new ConnectionException("disconnected unexpectedly")));

            var conn = new Mock <IMessageConnection>();

            conn.Setup(m => m.WriteMessageAsync(It.IsAny <Message>(), It.IsAny <CancellationToken>()))
            .Returns(Task.CompletedTask)
            .Raises(m => m.Disconnected += null, conn.Object, string.Empty);

            var connManager = new Mock <IConnectionManager>();

            connManager.Setup(m => m.GetOrAddUnsolicitedConnectionAsync(It.IsAny <ConnectionKey>(), localUsername, It.IsAny <EventHandler <Message> >(), It.IsAny <ConnectionOptions>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(conn.Object));

            var s = new SoulseekClient("127.0.0.1", 1, waiter: waiter.Object, serverConnection: conn.Object, connectionManager: connManager.Object);

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

            BrowseResponse result = null;
            var            ex     = await Record.ExceptionAsync(async() => result = await s.BrowseAsync(username));

            Assert.NotNull(ex);
            Assert.IsType <BrowseException>(ex);
            Assert.IsType <ConnectionException>(ex.InnerException);
            Assert.Contains("disconnected unexpectedly", ex.InnerException.Message, StringComparison.InvariantCultureIgnoreCase);
        }
Beispiel #14
0
        public async Task BrowseInternalAsync_Throws_BrowseException_On_Write_Exception(string username, IPAddress ip, int port, string localUsername)
        {
            var waiter = new Mock <IWaiter>();

            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>();

            conn.Setup(m => m.WriteMessageAsync(It.IsAny <Message>(), It.IsAny <CancellationToken>()))
            .Throws(new ConnectionWriteException());

            var connManager = new Mock <IConnectionManager>();

            connManager.Setup(m => m.GetOrAddUnsolicitedConnectionAsync(It.IsAny <ConnectionKey>(), localUsername, It.IsAny <EventHandler <Message> >(), It.IsAny <ConnectionOptions>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(conn.Object));

            var s = new SoulseekClient("127.0.0.1", 1, waiter: waiter.Object, serverConnection: conn.Object, connectionManager: connManager.Object);

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

            BrowseResponse result = null;
            var            ex     = await Record.ExceptionAsync(async() => result = await s.BrowseAsync(username));

            Assert.NotNull(ex);
            Assert.IsType <BrowseException>(ex);
            Assert.IsType <ConnectionWriteException>(ex.InnerException);
        }
Beispiel #15
0
        public async Task BrowseInternalAsync_Throws_BrowseException_On_Cancellation(string username, IPAddress ip, int port, string localUsername)
        {
            var waiter = new Mock <IWaiter>();

            waiter.Setup(m => m.WaitIndefinitely <BrowseResponse>(It.IsAny <WaitKey>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromException <BrowseResponse>(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>();

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

            var connManager = new Mock <IPeerConnectionManager>();

            connManager.Setup(m => m.GetOrAddMessageConnectionAsync(username, ip, port, It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(conn.Object));

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

                BrowseResponse result = null;
                var            ex     = await Record.ExceptionAsync(async() => result = await s.BrowseAsync(username));

                Assert.NotNull(ex);
                Assert.IsType <BrowseException>(ex);
                Assert.IsType <OperationCanceledException>(ex.InnerException);
            }
        }
Beispiel #16
0
        static async Task Main(string[] args)
        {
            using (var client = new SoulseekClient(new SoulseekClientOptions(minimumDiagnosticLevel: DiagnosticLevel.Debug)))
            {
                client.StateChanged            += Client_ServerStateChanged;
                client.SearchResponseReceived  += Client_SearchResponseReceived;
                client.SearchStateChanged      += Client_SearchStateChanged;
                client.DownloadProgressUpdated += Client_DownloadProgress;
                client.DownloadStateChanged    += Client_DownloadStateChanged;
                client.DiagnosticGenerated     += Client_DiagnosticMessageGenerated;
                client.PrivateMessageReceived  += Client_PrivateMessageReceived;

                await client.ConnectAsync();

                Console.WriteLine("Enter username and password:"******"disconnect")
                    {
                        client.Disconnect();
                        return;
                    }
                    else if (cmd.StartsWith("msg"))
                    {
                        var arr = cmd.Split(' ');

                        var peer    = arr.Skip(1).Take(1).FirstOrDefault();
                        var message = arr.Skip(2).Take(999);

                        await client.SendPrivateMessageAsync(peer, string.Join(' ', message));
                    }
                    else if (cmd.StartsWith("browse"))
                    {
                        var peer   = cmd.Split(' ').Skip(1).FirstOrDefault();
                        var result = await client.BrowseAsync(peer);

                        Console.WriteLine(JsonConvert.SerializeObject(result));
                        continue;
                    }
                    else if (cmd.StartsWith("search"))
                    {
                        using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(300)))
                        {
                            var search = string.Join(' ', cmd.Split(' ').Skip(1));
                            var token  = new Random().Next();
                            var result = await client.SearchAsync(search, token, new SearchOptions(
                                                                      filterFiles : false,
                                                                      filterResponses : false,
                                                                      fileLimit : 10000), cts.Token);

                            Console.WriteLine(JsonConvert.SerializeObject(result));
                            continue;
                        }
                    }
                    else if (cmd.StartsWith("download-folder"))
                    {
                        var peer = cmd.Split(' ').Skip(1).FirstOrDefault();

                        var files = new[]
                        {
                            @"@@djpnk\\Bootlegs\\Fear Is Your Only God\\01 - Bulls On Parade.mp3",
                            @"@@djpnk\\Bootlegs\\Fear Is Your Only God\\02 - Down Rodeo.mp3",
                            @"@@djpnk\\Bootlegs\\Fear Is Your Only God\\03 - People Of The Sun.mp3",
                            @"@@djpnk\\Bootlegs\\Fear Is Your Only God\\04 - Revolver.mp3",
                            @"@@djpnk\\Bootlegs\\Fear Is Your Only God\\05 - Roll Right.mp3",
                            @"@@djpnk\\Bootlegs\\Fear Is Your Only God\\06 - Snakecharmer.mp3",
                            @"@@djpnk\\Bootlegs\\Fear Is Your Only God\\07 - Tire Me.mp3",
                            @"@@djpnk\\Bootlegs\\Fear Is Your Only God\\08 - Vietnow.mp3",
                            @"@@djpnk\\Bootlegs\\Fear Is Your Only God\\09 - Wind Below.mp3",
                            @"@@djpnk\\Bootlegs\\Fear Is Your Only God\\10 - Without A Face.mp3",
                            @"@@djpnk\\Bootlegs\\Fear Is Your Only God\\11 - Year Of The Boomerang.mp3",
                            @"@@djpnk\\Bootlegs\\Fear Is Your Only God\\Thumbs.db",
                            @"@@djpnk\\Bootlegs\\Fear Is Your Only God\\album.nfo",
                        };

                        var task = Task.Run(() =>
                        {
                            var random = new Random();

                            Parallel.ForEach(files, async(file) =>
                            {
                                Console.WriteLine($"Attempting to download {file}");
                                var bytes    = await client.DownloadAsync(peer, file, random.Next());
                                var filename = $@"C:\tmp\{Path.GetFileName(file)}";

                                Console.WriteLine($"Bytes received: {bytes.Length}; writing to file {filename}...");
                                System.IO.File.WriteAllBytes(filename, bytes);
                                Console.WriteLine("Download complete!");
                            });
                        });

                        await task;

                        Console.WriteLine($"All files complete.");
                    }
                    else if (cmd.StartsWith("download"))
                    {
                        var peer = cmd.Split(' ').Skip(1).FirstOrDefault();
                        var file = string.Join(' ', cmd.Split(' ').Skip(2));

                        var bytes = await client.DownloadAsync(peer, file, new Random().Next());

                        var filename = $@"C:\tmp\{Path.GetFileName(file)}";

                        Console.WriteLine($"Bytes received: {bytes.Length}; writing to file {filename}...");
                        System.IO.File.WriteAllBytes(filename, bytes);
                        Console.WriteLine("Download complete!");
                    }
                    else
                    {
                        try
                        {
                            await client.LoginAsync(cmd.Split(' ')[0], cmd.Split(' ')[1]);

                            Console.WriteLine($"Logged in.");
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"Login failed: {ex.Message}");
                        }
                    }
                }
            }
        }