예제 #1
0
        public async Task SearchInternalAsync_Adds_Search_To_ActiveSearches(string searchText, int token)
        {
            var options = new SearchOptions(searchTimeout: 1000, fileLimit: 1);

            using (var search = new SearchInternal(searchText, token, options)
            {
                State = SearchStates.InProgress,
            })
            {
                var conn = new Mock <IMessageConnection>();
                conn.Setup(m => m.WriteAsync(It.IsAny <byte[]>(), null))
                .Returns(Task.CompletedTask);

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

                        var task = s.SearchAsync(SearchQuery.FromText(searchText), SearchScope.Default, token, options, cts.Token);

                        var active = s.GetProperty <ConcurrentDictionary <int, SearchInternal> >("Searches").ToList();

                        cts.Cancel();

                        await Record.ExceptionAsync(() => task); // swallow the cancellation exception

                        Assert.Single(active);
                        Assert.Contains(active, kvp => kvp.Key == token);
                    }
            }
        }
예제 #2
0
        public async Task SearchAsync_Fires_SearchStateChanged_Event(string searchText, int token)
        {
            var fired   = false;
            var options = new SearchOptions(searchTimeout: 1000, fileLimit: 1);

            using (var search = new SearchInternal(searchText, token, options)
            {
                State = SearchStates.InProgress,
            })
            {
                var conn = new Mock <IMessageConnection>();
                conn.Setup(m => m.WriteAsync(It.IsAny <byte[]>(), null))
                .Returns(Task.CompletedTask);

                using (var s = new SoulseekClient(serverConnection: conn.Object))
                {
                    s.SearchStateChanged += (sender, e) => fired = true;
                    s.SetProperty("State", SoulseekClientStates.Connected | SoulseekClientStates.LoggedIn);

                    var task = s.SearchAsync(SearchQuery.FromText(searchText), SearchScope.Default, token, options, null);

                    await task;

                    Assert.True(fired);
                }
            }
        }
예제 #3
0
        public void Disconnect_Clears_Searches()
        {
            var c = new Mock <IMessageConnection>();

            using (var s = new SoulseekClient(Guid.NewGuid().ToString(), new Random().Next(65535), serverConnection: c.Object))
            {
                s.SetProperty("State", SoulseekClientStates.Connected);

                using (var search1 = new SearchInternal(string.Empty, 0, new SearchOptions()))
                    using (var search2 = new SearchInternal(string.Empty, 1, new SearchOptions()))
                    {
                        var searches = new ConcurrentDictionary <int, SearchInternal>();
                        searches.TryAdd(0, search1);
                        searches.TryAdd(1, search2);

                        s.SetProperty("Searches", searches);

                        var ex = Record.Exception(() => s.Disconnect());

                        Assert.Null(ex);
                        Assert.Equal(SoulseekClientStates.Disconnected, s.State);
                        Assert.Empty(searches);
                    }
            }
        }
        public async Task Disconnect_Cancels_Searches()
        {
            var c = new Mock <IMessageConnection>();

            var p = new Mock <IPeerConnectionManager>();

            using (var search = new SearchInternal("foo", 1))
            {
                var searches = new ConcurrentDictionary <int, SearchInternal>();
                searches.TryAdd(1, search);

                using (var s = new SoulseekClient(serverConnection: c.Object, peerConnectionManager: p.Object))
                {
                    s.SetProperty("State", SoulseekClientStates.Connected);
                    s.SetProperty("Searches", searches);

                    var searchWait = search.WaitForCompletion(CancellationToken.None);

                    s.Disconnect();

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

                    Assert.NotNull(ex);
                    Assert.IsType <OperationCanceledException>(ex);
                }
            }
        }
예제 #5
0
 /// <summary>
 ///     Initializes a new instance of the <see cref="Search"/> class.
 /// </summary>
 /// <param name="searchInternal">The internal instance from which to copy data.</param>
 internal Search(SearchInternal searchInternal)
     : this(
         searchInternal.SearchText,
         searchInternal.Token,
         searchInternal.State,
         searchInternal.ResponseCount,
         searchInternal.FileCount)
 {
 }
예제 #6
0
        public void Disposes_Without_Throwing()
        {
            var s = new SearchInternal("foo", 42);

            var ex = Record.Exception(() => s.Dispose());

            Assert.Null(ex);

            s.Dispose();
        }
예제 #7
0
        public void SearchEventArgs_Instantiates_With_Valid_Search(string searchText, int token, SearchOptions options)
        {
            using (var search = new SearchInternal(searchText, token, options))
            {
                var s = new Search(search);
                var e = new SearchEventArgs(s);

                Assert.Equal(s, e.Search);
            }
        }
예제 #8
0
        internal void Instantiates_With_Expected_Data_Given_SearchInternal(SearchInternal i)
        {
            var s = new Search(i);

            Assert.Equal(i.SearchText, s.SearchText);
            Assert.Equal(i.Token, s.Token);
            Assert.Equal(i.State, s.State);
            Assert.Equal(i.ResponseCount, s.ResponseCount);
            Assert.Equal(i.FileCount, s.FileCount);
        }
예제 #9
0
        public void Complete_Sets_State()
        {
            var s = new SearchInternal("foo", 42);

            s.Complete(SearchStates.Cancelled);

            Assert.True(s.State.HasFlag(SearchStates.Completed));
            Assert.True(s.State.HasFlag(SearchStates.Cancelled));

            s.Dispose();
        }
예제 #10
0
        public void Response_Filter_Respects_MinimumResponseFileCount_Option(int actual, int option, bool expected)
        {
            var s        = new SearchInternal("foo", 42, new SearchOptions(filterResponses: true, minimumResponseFileCount: option));
            var response = new SearchResponseSlim("u", 1, actual, 1, 1, 1, null);

            var filter = s.InvokeMethod <bool>("SlimResponseMeetsOptionCriteria", response);

            Assert.Equal(expected, filter);

            s.Dispose();
        }
예제 #11
0
        public void Response_Filter_Returns_True_When_FilterResponses_Option_Is_False()
        {
            var s        = new SearchInternal("foo", 42, new SearchOptions(filterResponses: false));
            var response = new SearchResponseSlim("u", 1, 1, 1, 1, 1, null);

            var filter = s.InvokeMethod <bool>("SlimResponseMeetsOptionCriteria", response);

            Assert.True(filter);

            s.Dispose();
        }
예제 #12
0
        public void Instantiates_With_Expected_Data(string searchText, int token, SearchOptions options)
        {
            var s = new SearchInternal(searchText, token, options);

            Assert.Equal(searchText, s.SearchText);
            Assert.Equal(token, s.Token);
            Assert.Equal(options, s.Options);

            Assert.Equal(SearchStates.None, s.State);

            s.Dispose();
        }
예제 #13
0
        public async Task Cancel_Cancels()
        {
            using (var s = new SearchInternal("foo", 1))
            {
                s.Cancel();

                var ex = await Record.ExceptionAsync(() => s.WaitForCompletion(CancellationToken.None));

                Assert.NotNull(ex);
                Assert.IsType <OperationCanceledException>(ex);
            }
        }
예제 #14
0
        public void Response_Filter_Respects_MinimumResponseFileCount_Option(int actual, int option, bool expected)
        {
            var fixture = new Fixture();
            var file    = fixture.Create <File>();

            var s        = new SearchInternal("foo", 42, new SearchOptions(filterResponses: true, minimumResponseFileCount: option));
            var response = new SearchResponse("u", 1, 1, 1, 1, DuplicateFile(file, actual));

            var filter = s.InvokeMethod <bool>("ResponseMeetsOptionCriteria", response);

            Assert.Equal(expected, filter);

            s.Dispose();
        }
예제 #15
0
        public void SearchResponseReceivedEventArgs_Instantiates_With_Valid_Search_And_SearchResponse()
        {
            var searchText = Guid.NewGuid().ToString();
            var token      = new Random().Next();

            using (var search = new SearchInternal(searchText, token, new SearchOptions()))
            {
                var response = new SearchResponse("foo", 1, 1, 1, 1, new List <File>());

                var s = new Search(search);
                var e = new SearchResponseReceivedEventArgs(response, s);

                Assert.Equal(s, e.Search);
                Assert.Equal(response, e.Response);
            }
        }
예제 #16
0
        public void TryAddResponse_Adds_Response(string username, int token, byte code, string filename, int size, string extension)
        {
            var s = new SearchInternal("foo", token, new SearchOptions(filterResponses: true, minimumResponseFileCount: 1))
            {
                State = SearchStates.InProgress,
            };

            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.SearchResponse)
                      .WriteString(username)
                      .WriteInteger(token)                           // token
                      .WriteInteger(1)                               // file count
                      .WriteByte(code)                               // code
                      .WriteString(filename)                         // filename
                      .WriteLong(size)                               // size
                      .WriteString(extension)                        // extension
                      .WriteInteger(1)                               // attribute count
                      .WriteInteger((int)FileAttributeType.BitDepth) // attribute[0].type
                      .WriteInteger(4)                               // attribute[0].value
                      .WriteByte(1)                                  // free upload slots
                      .WriteInteger(1)                               // upload speed
                      .WriteLong(0)                                  // queue length
                      .WriteBytes(new byte[4])                       // unknown 4 bytes
                      .Build();

            var reader = new MessageReader <MessageCode.Peer>(msg);

            reader.Seek(username.Length + 12); // seek to the start of the file list

            var responses = new List <SearchResponse>();

            s.ResponseReceived = (r) => responses.Add(r);

            s.TryAddResponse(new SearchResponseSlim(username, token, 1, 1, 1, 1, reader));

            Assert.Single(responses);

            var response = responses[0];
            var files    = response.Files.ToList();

            Assert.Equal(1, response.FileCount);
            Assert.Equal(username, response.Username);
            Assert.Equal(filename, files[0].Filename);
            Assert.Equal(size, files[0].Size);

            s.Dispose();
        }
예제 #17
0
        public void TryAddResponse_Ignores_Response_When_Response_Criteria_Not_Met()
        {
            var s = new SearchInternal("foo", 42, new SearchOptions(filterResponses: true, minimumResponseFileCount: 1))
            {
                State = SearchStates.InProgress,
            };

            s.TryAddResponse(new SearchResponseSlim("bar", 42, 0, 1, 1, 1, null));

            var invoked = false;

            s.ResponseReceived = (r) => invoked = true;

            Assert.False(invoked);

            s.Dispose();
        }
예제 #18
0
        public void TryAddResponse_Ignores_Response_When_Search_Is_Not_In_Progress()
        {
            var s = new SearchInternal("foo", 42)
            {
                State = SearchStates.Completed,
            };

            s.TryAddResponse(new SearchResponseSlim("bar", 42, 1, 1, 1, 1, null));

            var invoked = false;

            s.ResponseReceived = (r) => invoked = true;

            Assert.False(invoked);

            s.Dispose();
        }
예제 #19
0
        public async Task TryAddResponse_Completes_Search_And_Invokes_Completed_Event_When_Response_Limit_Reached(string username, int token, byte code, string filename, int size, string extension)
        {
            var options = new SearchOptions(
                filterResponses: false,
                minimumResponseFileCount: 1,
                responseLimit: 1,
                fileLimit: 10000000);

            var s = new SearchInternal("foo", token, options)
            {
                State = SearchStates.InProgress,
            };

            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.SearchResponse)
                      .WriteString(username)
                      .WriteInteger(token)                           // token
                      .WriteInteger(1)                               // file count
                      .WriteByte(code)                               // code
                      .WriteString(filename)                         // filename
                      .WriteLong(size)                               // size
                      .WriteString(extension)                        // extension
                      .WriteInteger(1)                               // attribute count
                      .WriteInteger((int)FileAttributeType.BitDepth) // attribute[0].type
                      .WriteInteger(4)                               // attribute[0].value
                      .WriteByte(1)                                  // free upload slots
                      .WriteInteger(1)                               // upload speed
                      .WriteLong(0)                                  // queue length
                      .WriteBytes(new byte[4])                       // unknown 4 bytes
                      .Build();

            var reader = new MessageReader <MessageCode.Peer>(msg);

            reader.Seek(username.Length + 12); // seek to the start of the file lists

            var task = s.WaitForCompletion(CancellationToken.None);

            s.TryAddResponse(new SearchResponseSlim(username, token, 1, 1, 1, 1, reader));

            await task;

            Assert.True(s.State.HasFlag(SearchStates.Completed));
            Assert.True(s.State.HasFlag(SearchStates.ResponseLimitReached));

            s.Dispose();
        }
예제 #20
0
        public void SearchStateChangedEventArgs_Instantiates_With_Valid_Search()
        {
            var searchText = Guid.NewGuid().ToString();
            var token      = new Random().Next();

            using (var search = new SearchInternal(searchText, token, new SearchOptions()))
            {
                search.SetProperty("State", SearchStates.Completed);

                var s = new Search(search);
                var e = new SearchStateChangedEventArgs(SearchStates.None, s);

                Assert.Equal(s, e.Search);
                Assert.Equal(SearchStates.None, e.PreviousState);
                Assert.Equal(SearchStates.Completed, e.Search.State);
            }
        }
예제 #21
0
        public void TryAddResponse_Ignores_Response_When_Token_Does_Not_Match()
        {
            var s = new SearchInternal("foo", 42)
            {
                State = SearchStates.InProgress,
            };

            s.TryAddResponse(new SearchResponseSlim("bar", 24, 1, 1, 1, 1, null));

            var invoked = false;

            s.ResponseReceived = (r) => invoked = true;

            Assert.False(invoked);

            s.Dispose();
        }
예제 #22
0
        public void TryAddResponse_Ignores_Response_When_All_Files_Are_Filtered_And_Response_Filtering_Is_Enabled(string username, int token, byte code, string filename, int size, string extension)
        {
            var options = new SearchOptions(
                filterResponses: true,
                minimumResponseFileCount: 1,
                fileFilter: (f) => false);

            var s = new SearchInternal("foo", token, options)
            {
                State = SearchStates.InProgress,
            };

            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.SearchResponse)
                      .WriteString(username)
                      .WriteInteger(token)                           // token
                      .WriteInteger(1)                               // file count
                      .WriteByte(code)                               // code
                      .WriteString(filename)                         // filename
                      .WriteLong(size)                               // size
                      .WriteString(extension)                        // extension
                      .WriteInteger(1)                               // attribute count
                      .WriteInteger((int)FileAttributeType.BitDepth) // attribute[0].type
                      .WriteInteger(4)                               // attribute[0].value
                      .WriteByte(1)                                  // free upload slots
                      .WriteInteger(1)                               // upload speed
                      .WriteLong(0)                                  // queue length
                      .WriteBytes(new byte[4])                       // unknown 4 bytes
                      .Build();

            var reader = new MessageReader <MessageCode.Peer>(msg);

            reader.Seek(username.Length + 12); // seek to the start of the file list

            s.TryAddResponse(new SearchResponseSlim(username, token, 1, 1, 1, 1, reader));

            var invoked = false;

            s.ResponseReceived = (r) => invoked = true;

            Assert.False(invoked);

            s.Dispose();
        }
예제 #23
0
        public void TryAddResponse_Ignores_Response_When_Custom_Response_Filter_Returns_False()
        {
            bool Filter(SearchResponse response) => false;

            var s = new SearchInternal("foo", 42, new SearchOptions(filterResponses: true, minimumResponseFileCount: 0, responseFilter: Filter))
            {
                State = SearchStates.InProgress,
            };

            s.TryAddResponse(new SearchResponse("bar", 42, 1, 1, 1, null));

            var invoked = false;

            s.ResponseReceived = (r) => invoked = true;

            Assert.False(invoked);

            s.Dispose();
        }
예제 #24
0
        public async Task SearchAsync_Delegate_Throws_DuplicateTokenException_Given_A_Token_In_Use(string text, int token)
        {
            using (var search = new SearchInternal(text, token, new SearchOptions()))
            {
                var dict = new ConcurrentDictionary <int, SearchInternal>();
                dict.TryAdd(token, search);

                using (var s = new SoulseekClient())
                {
                    s.SetProperty("State", SoulseekClientStates.Connected | SoulseekClientStates.LoggedIn);
                    s.SetProperty("Searches", dict);

                    var ex = await Record.ExceptionAsync(() => s.SearchAsync(SearchQuery.FromText(text), (r) => { }, token: token));

                    Assert.NotNull(ex);
                    Assert.IsType <DuplicateTokenException>(ex);
                }
            }
        }
예제 #25
0
        public async Task TryAddResponse_Completes_Search_And_Invokes_Completed_Event_When_File_Limit_Reached(string username, int token, File file)
        {
            var options = new SearchOptions(
                filterResponses: false,
                minimumResponseFileCount: 1,
                fileLimit: 1);

            var s = new SearchInternal("foo", token, options)
            {
                State = SearchStates.InProgress,
            };

            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.SearchResponse)
                      .WriteString(username)
                      .WriteInteger(token)     // token
                      .WriteInteger(1)         // file count
                      .WriteFile(file)
                      .WriteByte(1)            // free upload slots
                      .WriteInteger(1)         // upload speed
                      .WriteLong(0)            // queue length
                      .WriteBytes(new byte[4]) // unknown 4 bytes
                      .Build();

            var reader = new MessageReader <MessageCode.Peer>(msg);

            reader.Seek(username.Length + 12); // seek to the start of the file lists

            var task = s.WaitForCompletion(CancellationToken.None);

            s.TryAddResponse(new SearchResponse(username, token, 1, 1, 1, new List <File>()
            {
                file
            }));

            await task;

            Assert.True(s.State.HasFlag(SearchStates.Completed));
            Assert.True(s.State.HasFlag(SearchStates.FileLimitReached));

            s.Dispose();
        }
예제 #26
0
        public void TryAddResponse_Ignores_Response_When_Custom_File_Filter_Removes_Locked_Files()
        {
            bool Filter(File file) => false;

            var s = new SearchInternal("foo", 42, new SearchOptions(filterResponses: true, minimumResponseFileCount: 1, fileFilter: Filter))
            {
                State = SearchStates.InProgress,
            };

            s.TryAddResponse(new SearchResponse("bar", 42, 1, 1, 1, null, lockedFileList: new List <File>()
            {
                new File(1, "a", 1, "b")
            }));

            var invoked = false;

            s.ResponseReceived = (r) => invoked = true;

            Assert.False(invoked);

            s.Dispose();
        }
        public void Appends_Active_Search_Response(string username, IPEndPoint endpoint, int token, byte freeUploadSlots, int uploadSpeed, int queueLength)
        {
            var(handler, mocks) = GetFixture(username, endpoint);

            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.SearchResponse)
                      .WriteString(username)
                      .WriteInteger(token)
                      .WriteInteger(1)                               // file count
                      .WriteByte(0x2)                                // code
                      .WriteString("filename")                       // filename
                      .WriteLong(3)                                  // size
                      .WriteString("ext")                            // extension
                      .WriteInteger(1)                               // attribute count
                      .WriteInteger((int)FileAttributeType.BitDepth) // attribute[0].type
                      .WriteInteger(4)                               // attribute[0].value
                      .WriteByte(freeUploadSlots)
                      .WriteInteger(uploadSpeed)
                      .WriteLong(queueLength)
                      .WriteBytes(new byte[4]) // unknown 4 bytes
                      .Compress()
                      .Build();

            var responses = new List <SearchResponse>();

            using (var search = new SearchInternal("foo", token)
            {
                State = SearchStates.InProgress,
                ResponseReceived = (r) => responses.Add(r),
            })
            {
                mocks.Searches.TryAdd(token, search);

                handler.HandleMessageRead(mocks.PeerConnection.Object, msg);

                Assert.Single(responses);
                Assert.Contains(responses, r => r.Username == username && r.Token == token);
            }
        }
예제 #28
0
        public void TryAddResponse_Swallows_Exceptions(string username, int token, File file)
        {
            var s = new SearchInternal("foo", token, new SearchOptions(filterResponses: true, minimumResponseFileCount: 1))
            {
                State = SearchStates.InProgress,
            };

            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.SearchResponse)
                      .WriteString(username)
                      .WriteInteger(token)     // token
                      .WriteInteger(1)         // file count
                      .WriteFile(file)
                      .WriteByte(1)            // free upload slots
                      .WriteInteger(1)         // upload speed
                      .WriteLong(0)            // queue length
                      .WriteBytes(new byte[4]) // unknown 4 bytes
                      .Build();

            var reader = new MessageReader <MessageCode.Peer>(msg);

            reader.Seek(username.Length + 12); // seek to the start of the file list

            var invoked = false;

            s.ResponseReceived += (r) => invoked = true;

            var ex = Record.Exception(() => s.TryAddResponse(new SearchResponse(username, token, 1, 1, 1, new List <File>()
            {
                file
            })));

            Assert.Null(ex);
            Assert.True(invoked);

            s.Dispose();
        }
예제 #29
0
        public void TryAddResponse_Invokes_Response_Received_Event_Handler(string username, int token, File file)
        {
            SearchResponse addResponse = null;

            var s = new SearchInternal("foo", token, new SearchOptions(filterResponses: true, minimumResponseFileCount: 1))
            {
                State = SearchStates.InProgress,
            };

            s.ResponseReceived += (response) => addResponse = response;

            var msg = new MessageBuilder()
                      .WriteCode(MessageCode.Peer.SearchResponse)
                      .WriteString(username)
                      .WriteInteger(token)     // token
                      .WriteInteger(1)         // file count
                      .WriteFile(file)
                      .WriteByte(1)            // free upload slots
                      .WriteInteger(1)         // upload speed
                      .WriteLong(0)            // queue length
                      .WriteBytes(new byte[4]) // unknown 4 bytes
                      .Build();

            var reader = new MessageReader <MessageCode.Peer>(msg);

            reader.Seek(username.Length + 12); // seek to the start of the file list

            s.TryAddResponse(new SearchResponse(username, token, 1, 1, 1, new List <File>()
            {
                file
            }));

            Assert.NotNull(addResponse);
            Assert.Equal(file.Filename, addResponse.Files.ToList()[0].Filename);

            s.Dispose();
        }