package org.limewire.core.impl.search;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.limewire.core.api.search.Search;
import org.limewire.core.api.search.SearchCategory;
import org.limewire.core.api.search.SearchDetails;
import org.limewire.core.api.search.SearchEvent;
import org.limewire.core.api.search.SearchListener;
import org.limewire.core.api.search.SearchResult;
import org.limewire.core.api.search.SearchDetails.SearchType;
import org.limewire.core.api.search.sponsored.SponsoredResult;
import org.limewire.core.api.search.sponsored.SponsoredResultTarget;
import org.limewire.core.impl.library.FriendSearcher;
import org.limewire.core.settings.PromotionSettings;
import org.limewire.geocode.GeocodeInformation;
import org.limewire.io.Address;
import org.limewire.io.GUID;
import org.limewire.io.IpPort;
import org.limewire.listener.EventBroadcaster;
import org.limewire.promotion.PromotionSearcher;
import org.limewire.promotion.PromotionSearcher.PromotionSearchResultsCallback;
import org.limewire.promotion.containers.PromotionMessageContainer;
import org.limewire.promotion.containers.PromotionMessageContainer.PromotionOptions;
import org.limewire.util.AssignParameterAction;
import org.limewire.util.BaseTestCase;
import org.limewire.util.Clock;
import org.limewire.util.ExecuteRunnableAction;
import org.limewire.util.MediaType;
import com.google.inject.Provider;
import com.limegroup.gnutella.RemoteFileDesc;
import com.limegroup.gnutella.SearchServices;
import com.limegroup.gnutella.messages.QueryReply;
public class CoreSearchTest extends BaseTestCase {
public CoreSearchTest(String name) {
super(name);
}
@SuppressWarnings({"unchecked"})
public void testQueryReplySearchListenerResultsAdded() {
Mockery context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
PromotionSettings.PROMOTION_SYSTEM_IS_ENABLED.setValue(false);
final SearchDetails searchDetails = context.mock(SearchDetails.class);
final String searchQuery = "test";
final SearchServices searchServices = context.mock(SearchServices.class);
final QueryReplyListenerList listenerList = context.mock(QueryReplyListenerList.class);
final PromotionSearcher promotionSearcher = context.mock(PromotionSearcher.class);
final FriendSearcher friendSearcher = context.mock(FriendSearcher.class);
final Provider<GeocodeInformation> geoLocation = context.mock(Provider.class);
final ScheduledExecutorService backgroundExecutor = context
.mock(ScheduledExecutorService.class);
final EventBroadcaster<SearchEvent> searchEventBroadcaster = context.mock(EventBroadcaster.class);
final byte[] searchGuid = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
final SearchListener searchListener = context.mock(SearchListener.class);
final AtomicReference<QueryReplyListener> queryReplyListener = new AtomicReference<QueryReplyListener>();
final CoreSearch coreSearch = new CoreSearch(searchDetails, searchServices, listenerList,
promotionSearcher, friendSearcher, geoLocation, backgroundExecutor,
searchEventBroadcaster, null, null, null);
context.checking(new Expectations() {{
allowing(searchDetails).getSearchQuery();
will(returnValue(searchQuery));
allowing(searchDetails).getSearchCategory();
will(returnValue(SearchCategory.ALL));
allowing(searchDetails).getSearchType();
will(returnValue(SearchType.KEYWORD));
allowing(searchDetails);
}});
context.checking(new Expectations() {{
one(searchEventBroadcaster).broadcast(
new SearchEvent(coreSearch, SearchEvent.Type.STARTED));
one(searchServices).newQueryGUID();
will(returnValue(searchGuid));
one(listenerList).addQueryReplyListener(with(equal(searchGuid)),
with(any(QueryReplyListener.class)));
will(new AssignParameterAction<QueryReplyListener>(queryReplyListener, 1));
one(searchServices).query(searchGuid, searchQuery, "",
MediaType.getAnyTypeMediaType());
one(backgroundExecutor).execute(with(any(Runnable.class)));
will(new ExecuteRunnableAction());
one(friendSearcher).doSearch(with(equal(searchDetails)),
with(any(FriendSearchListener.class)));
one(searchListener).searchStarted(coreSearch);
}
});
coreSearch.addSearchListener(searchListener);
coreSearch.start();
try {
coreSearch.start();
fail("Illegal search repeat not handled");
} catch (IllegalStateException e) {
// Expected
}
final RemoteFileDesc remoteFileDesc1 = context.mock(RemoteFileDesc.class);
final QueryReply queryReply1 = context.mock(QueryReply.class);
final Set<IpPort> ipPorts = new HashSet<IpPort>();
final Address address1 = context.mock(Address.class);
final byte[] guid1 = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
final String fileName1 = "remote file name 1.txt";
context.checking(new Expectations() {
{
allowing(remoteFileDesc1).getAddress();
will(returnValue(address1));
allowing(remoteFileDesc1).getClientGUID();
will(returnValue(guid1));
allowing(address1).getAddressDescription();
will(returnValue("address 1 description"));
allowing(remoteFileDesc1).getFileName();
will(returnValue(fileName1));
allowing(remoteFileDesc1).getSize();
will(returnValue(1234L));
allowing(remoteFileDesc1).getXMLDocument();
will(returnValue(null));
allowing(remoteFileDesc1).getCreationTime();
will(returnValue(5678L));
allowing(remoteFileDesc1).getSHA1Urn();
will(returnValue(null));
one(searchListener).handleSearchResult(with(same(coreSearch)),
with(new SearchResultMatcher(fileName1)));
}
});
queryReplyListener.get().handleQueryReply(remoteFileDesc1, queryReply1, ipPorts);
context.assertIsSatisfied();
}
@SuppressWarnings({"unchecked"})
public void testFriendSearchListenerResultsAdded() {
Mockery context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
PromotionSettings.PROMOTION_SYSTEM_IS_ENABLED.setValue(false);
final SearchDetails searchDetails = context.mock(SearchDetails.class);
final String searchQuery = "test";
final SearchServices searchServices = context.mock(SearchServices.class);
final QueryReplyListenerList listenerList = context.mock(QueryReplyListenerList.class);
final PromotionSearcher promotionSearcher = context.mock(PromotionSearcher.class);
final FriendSearcher friendSearcher = context.mock(FriendSearcher.class);
final Provider<GeocodeInformation> geoLocation = context.mock(Provider.class);
final ScheduledExecutorService backgroundExecutor = context
.mock(ScheduledExecutorService.class);
final EventBroadcaster<SearchEvent> searchEventBroadcaster = context.mock(EventBroadcaster.class);
final Clock clock = context.mock(Clock.class);
final byte[] searchGuid = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16 };
final SearchListener searchListener = context.mock(SearchListener.class);
final AtomicReference<FriendSearchListener> friendSearchListener = new AtomicReference<FriendSearchListener>();
final CoreSearch coreSearch = new CoreSearch(searchDetails, searchServices, listenerList,
promotionSearcher, friendSearcher, geoLocation, backgroundExecutor,
searchEventBroadcaster, null, clock, null);
context.checking(new Expectations() {{
allowing(searchDetails).getSearchQuery();
will(returnValue(searchQuery));
allowing(searchDetails).getSearchCategory();
will(returnValue(SearchCategory.ALL));
allowing(searchDetails).getSearchType();
will(returnValue(SearchType.KEYWORD));
allowing(searchDetails);
}});
context.checking(new Expectations() {{
one(searchEventBroadcaster).broadcast(
new SearchEvent(coreSearch, SearchEvent.Type.STARTED));
one(searchServices).newQueryGUID();
will(returnValue(searchGuid));
one(listenerList).addQueryReplyListener(with(equal(searchGuid)),
with(any(QueryReplyListener.class)));
one(searchServices).query(searchGuid, searchQuery, "",
MediaType.getAnyTypeMediaType());
one(backgroundExecutor).execute(with(any(Runnable.class)));
will(new ExecuteRunnableAction());
one(friendSearcher).doSearch(with(equal(searchDetails)),
with(any(FriendSearchListener.class)));
will(new AssignParameterAction<FriendSearchListener>(friendSearchListener, 1));
one(searchListener).searchStarted(coreSearch);
}});
coreSearch.addSearchListener(searchListener);
coreSearch.start();
final SearchResult searchResult1 = context.mock(SearchResult.class);
final String fileName1 = "filename1";
context.checking(new Expectations() {{
allowing(searchResult1).getFileName();
will(returnValue(fileName1));
one(searchListener).handleSearchResults(with(same(coreSearch)),
with(new SearchResultsListMatcher(Collections.singletonList(fileName1))));
}});
friendSearchListener.get().handleFriendResults(Arrays.asList(searchResult1));
context.assertIsSatisfied();
}
@SuppressWarnings({"unchecked"})
public void testQueryReplySearchListenerPromotionsAdded() {
Mockery context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
PromotionSettings.PROMOTION_SYSTEM_IS_ENABLED.setValue(true);
final SearchDetails searchDetails = context.mock(SearchDetails.class);
final String searchQuery = "test";
final SearchServices searchServices = context.mock(SearchServices.class);
final QueryReplyListenerList listenerList = context.mock(QueryReplyListenerList.class);
final PromotionSearcher promotionSearcher = context.mock(PromotionSearcher.class);
final FriendSearcher friendSearcher = context.mock(FriendSearcher.class);
final Provider<GeocodeInformation> geoLocation = context.mock(Provider.class);
final ScheduledExecutorService backgroundExecutor = context
.mock(ScheduledExecutorService.class);
final EventBroadcaster<SearchEvent> searchEventBroadcaster = context.mock(EventBroadcaster.class);
final Clock clock = context.mock(Clock.class);
final byte[] searchGuid = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
final SearchListener searchListener = context.mock(SearchListener.class);
final AtomicReference<QueryReplyListener> queryReplyListener = new AtomicReference<QueryReplyListener>();
final CoreSearch coreSearch = new CoreSearch(searchDetails, searchServices, listenerList,
promotionSearcher, friendSearcher, geoLocation, backgroundExecutor,
searchEventBroadcaster, null, clock, null);
final GeocodeInformation geocodeInformation = null;
final AtomicReference<PromotionSearchResultsCallback> promotionResultCallback = new AtomicReference<PromotionSearchResultsCallback>();
context.checking(new Expectations() {{
allowing(searchDetails).getSearchQuery();
will(returnValue(searchQuery));
allowing(searchDetails).getSearchCategory();
will(returnValue(SearchCategory.ALL));
allowing(searchDetails).getSearchType();
will(returnValue(SearchType.KEYWORD));
allowing(searchDetails);
}});
context.checking(new Expectations() {
{
allowing(promotionSearcher).isEnabled();
will(returnValue(true));
one(searchEventBroadcaster).broadcast(
new SearchEvent(coreSearch, SearchEvent.Type.STARTED));
one(searchServices).newQueryGUID();
will(returnValue(searchGuid));
one(listenerList).addQueryReplyListener(with(equal(searchGuid)),
with(any(QueryReplyListener.class)));
will(new AssignParameterAction<QueryReplyListener>(queryReplyListener, 1));
one(searchServices).query(searchGuid, searchQuery, "",
MediaType.getAnyTypeMediaType());
exactly(2).of(backgroundExecutor).execute(with(any(Runnable.class)));
will(new ExecuteRunnableAction());
one(friendSearcher).doSearch(with(equal(searchDetails)),
with(any(FriendSearchListener.class)));
one(searchListener).searchStarted(coreSearch);
allowing(geoLocation).get();
will(returnValue(geocodeInformation));
one(promotionSearcher).search(with(equal(searchQuery)),
with(any(PromotionSearchResultsCallback.class)),
with(any(GeocodeInformation.class)));
will(new AssignParameterAction<PromotionSearchResultsCallback>(
promotionResultCallback, 1));
}
});
coreSearch.addSearchListener(searchListener);
coreSearch.start();
final PromotionMessageContainer result = context.mock(PromotionMessageContainer.class);
final PromotionOptions options = context.mock(PromotionOptions.class);
final String title = "title";
final String displayUrl = "displayurlrwqd43.com";
final String url = "http://urlasdasr.com`/blahblahblah";
final String description = "description";
final String expectedUrl = PromotionSettings.REDIRECT_URL.get() + "?url=" + url + "&now=52&id=42";
final AtomicReference<List<SponsoredResult>> sponsoredResults = new AtomicReference<List<SponsoredResult>>();
context.checking(new Expectations() {
{
allowing(result).getOptions();
will(returnValue(options));
allowing(options).isOpenInStoreTab();
will(returnValue(true));
allowing(result).getTitle();
will(returnValue(title));
allowing(result).getDisplayUrl();
will(returnValue(displayUrl));
allowing(result).getURL();
will(returnValue(url));
allowing(result).getDescription();
will(returnValue(description));
allowing(result).getUniqueID();
will(returnValue(42L));
allowing(clock).now();
will(returnValue(52000L)); //divs by 1000
one(searchListener).handleSponsoredResults(with(any(Search.class)),
with(new SponsoredResultMatcher(title, expectedUrl, displayUrl)));
will(new AssignParameterAction<List<SponsoredResult>>(sponsoredResults, 1));
}
});
promotionResultCallback.get().process(result);
assertNotEmpty(sponsoredResults.get());
assertEquals(1, sponsoredResults.get().size());
SponsoredResult sponsoredResult = sponsoredResults.get().get(0);
assertEquals(expectedUrl, sponsoredResult.getUrl());
assertEquals(SponsoredResultTarget.STORE, sponsoredResult.getTarget());
assertEquals(description, sponsoredResult.getText());
assertEquals(title, sponsoredResult.getTitle());
assertEquals(displayUrl, sponsoredResult.getVisibleUrl());
context.assertIsSatisfied();
}
private final class SearchResultsListMatcher extends BaseMatcher<Collection<? extends SearchResult>> {
private final List<String> names;
private SearchResultsListMatcher(List<String> names) {
this.names = names;
}
@SuppressWarnings("unchecked")
@Override
public boolean matches(Object item) {
Collection<? extends SearchResult> searchResults = (Collection<? extends SearchResult>)item;
assertEquals(item.toString(), names.size(), searchResults.size());
int i = 0;
for(SearchResult result : searchResults) {
assertEquals(names.get(i++), result.getFileName());
}
return true;
}
@Override
public void describeTo(Description description) {
}
}
private final class SearchResultMatcher extends BaseMatcher<SearchResult> {
private final String fileName1;
private SearchResultMatcher(String fileName1) {
this.fileName1 = fileName1;
}
@Override
public boolean matches(Object item) {
SearchResult searchResult = (SearchResult) item;
assertEquals(fileName1, searchResult.getFileName());
return true;
}
@Override
public void describeTo(Description description) {
}
}
private final class SponsoredResultMatcher extends BaseMatcher<List<SponsoredResult>> {
private final String title;
private final String url;
private final String displayUrl;
private SponsoredResultMatcher(String title, String url, String displayUrl) {
this.title = title;
this.url = url;
this.displayUrl = displayUrl;
}
@SuppressWarnings("unchecked")
@Override
public boolean matches(Object item) {
List<SponsoredResult> sponsoredResult = (List<SponsoredResult>) item;
assertEquals(1, sponsoredResult.size());
SponsoredResult result1 = sponsoredResult.get(0);
assertEquals(title, result1.getTitle());
assertEquals(displayUrl, result1.getVisibleUrl());
assertEquals(url, result1.getUrl());
return true;
}
@Override
public void describeTo(Description description) {
}
}
@SuppressWarnings("unchecked")
public void testStop() {
Mockery context = new Mockery();
final byte[] guid = new byte[] {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
final EventBroadcaster<SearchEvent> searchEventBroadcaster = context.mock(EventBroadcaster.class);
final QueryReplyListenerList listenerList = context.mock(QueryReplyListenerList.class);
final SearchServices searchServices = context.mock(SearchServices.class);
final SearchListener listener = context.mock(SearchListener.class);
final CoreSearch search = new CoreSearch(null, searchServices, listenerList, null, null, null, null, searchEventBroadcaster, null, null, null);
context.checking(new Expectations() {
{
exactly(1).of(searchEventBroadcaster).broadcast(with(any(SearchEvent.class)));
exactly(1).of(listenerList).removeQueryReplyListener(with(same(guid)),
with(any(QueryReplyListener.class)));
exactly(1).of(searchServices).stopQuery(new GUID(guid));
exactly(1).of(listener).searchStopped(search);
}});
search.stop();
search.searchGuid = guid;
search.processingStarted.set(true);
search.addSearchListener(listener);
search.stop();
context.assertIsSatisfied();
}
@SuppressWarnings("unchecked")
public void testRestart() {
Mockery context = new Mockery();
final byte[] guid1 = new byte[] {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
final byte[] guid2 = new byte[] {0,1,2,3,4,5,6,'x','n',9,10,11,12,13,14,15};
final EventBroadcaster<SearchEvent> searchEventBroadcaster = context.mock(EventBroadcaster.class);
final QueryReplyListenerList listenerList = context.mock(QueryReplyListenerList.class);
final SearchServices searchServices = context.mock(SearchServices.class);
final SearchListener listener = context.mock(SearchListener.class);
final SearchDetails details = context.mock(SearchDetails.class);
final CoreSearch search = new CoreSearch(details, searchServices, listenerList, null, null, null, null, searchEventBroadcaster, null, null, null);
context.checking(new Expectations() {
{
exactly(1).of(searchEventBroadcaster).broadcast(new SearchEvent(search, SearchEvent.Type.STOPPED));
exactly(1).of(listenerList).removeQueryReplyListener(with(same(guid1)),
with(any(QueryReplyListener.class)));
exactly(1).of(searchServices).stopQuery(new GUID(guid1));
exactly(1).of(listener).searchStopped(search);
exactly(1).of(listener).searchStarted(with(any(Search.class)));
exactly(1).of(searchEventBroadcaster).broadcast(new SearchEvent(search, SearchEvent.Type.STARTED));
exactly(1).of(listenerList).addQueryReplyListener(with(same(guid2)),
with(any(QueryReplyListener.class)));
exactly(1).of(searchServices).queryWhatIsNew(with(same(guid2)),
with(same(MediaType.getOtherMediaType())));
allowing(searchServices).newQueryGUID();
will(returnValue(guid2));
allowing(details).getSearchType();
will(returnValue(SearchType.WHATS_NEW));
allowing(details).getSearchCategory();
will(returnValue(SearchCategory.OTHER));
}});
try {
search.repeat();
fail("Should not be able to repeat a search that has not started");
} catch (IllegalStateException e) {
// Expected
}
search.searchGuid = guid1;
search.processingStarted.set(true);
search.addSearchListener(listener);
search.repeat();
context.assertIsSatisfied();
}
public void testGetCategory() {
Mockery context = new Mockery();
final SearchDetails searchDetails = context.mock(SearchDetails.class);
final CoreSearch coreSearch = new CoreSearch(searchDetails, null, null, null, null, null,
null, null, null, null, null);
context.checking(new Expectations() {{
one(searchDetails).getSearchCategory();
will(returnValue(SearchCategory.IMAGE));
one(searchDetails).getSearchCategory();
will(returnValue(SearchCategory.DOCUMENT));
allowing(searchDetails);
}});
assertEquals(SearchCategory.IMAGE, coreSearch.getCategory());
assertEquals(SearchCategory.DOCUMENT, coreSearch.getCategory());
}
public void testGetQueryGuid() {
final CoreSearch coreSearch = new CoreSearch(null, null, null, null, null, null,
null, null, null, null, null);
coreSearch.searchGuid = new byte[] {4,3,2,1,'q','x','x','x','x','x','x','x','x','x','x','x'};
assertEquals(new GUID(new byte[] {4,3,2,1,'q','x','x','x','x','x','x','x','x','x','x','x'}),
coreSearch.getQueryGuid());
}
}