package org.limewire.core.impl.library;
import java.io.IOException;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.limewire.core.api.browse.Browse;
import org.limewire.core.api.browse.BrowseFactory;
import org.limewire.core.api.browse.BrowseListener;
import org.limewire.core.api.library.FriendLibrary;
import org.limewire.core.api.library.LibraryState;
import org.limewire.core.api.library.PresenceLibrary;
import org.limewire.core.api.library.RemoteLibraryManager;
import org.limewire.core.impl.search.RemoteFileDescAdapter;
import org.limewire.friend.api.Friend;
import org.limewire.friend.api.FriendPresence;
import org.limewire.friend.api.LibraryChanged;
import org.limewire.friend.api.LibraryChangedEvent;
import org.limewire.friend.api.feature.AddressFeature;
import org.limewire.io.Address;
import org.limewire.listener.EventListener;
import org.limewire.listener.ListenerSupport;
import org.limewire.net.ConnectivityChangeEvent;
import org.limewire.net.SocketsManager;
import org.limewire.net.address.AddressResolutionObserver;
import org.limewire.util.BaseTestCase;
import org.limewire.util.MatchAndCopy;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
import ca.odell.glazedlists.util.concurrent.Lock;
import ca.odell.glazedlists.util.concurrent.ReadWriteLock;
/**
* Tests the methods and classes contained in PresenceLibraryBrowse with various levels of
* internal integration.
*/
public class PresenceLibraryBrowserTest extends BaseTestCase {
public PresenceLibraryBrowserTest(String name) {
super(name);
}
/**
* Goes through the register phase and ensures all listeners are correctly connected.
*/
@SuppressWarnings("unchecked")
public void testListeners() {
Mockery context = new Mockery();
final ListenerSupport<LibraryChangedEvent> listenerSupport = context.mock(ListenerSupport.class);
final RemoteLibraryManager remoteLibraryManager = context.mock(RemoteLibraryManager.class);
final SocketsManager socketsManager = context.mock(SocketsManager.class);
final FriendPresence presence = context.mock(FriendPresence.class);
final EventList<FriendLibrary> friendLibraryList = context.mock(EventList.class);
final PresenceLibraryBrowser presenceLibraryBrowser
= new PresenceLibraryBrowser(null, remoteLibraryManager, socketsManager, null);
context.checking(new Expectations() {{
allowing(remoteLibraryManager).getFriendLibraryList();
will(returnValue(friendLibraryList));
exactly(1).of(listenerSupport).addListener(presenceLibraryBrowser);
exactly(1).of(socketsManager).addListener(with(any(EventListener.class)));
exactly(1).of(friendLibraryList).addListEventListener(with(any(ListEventListener.class)));
exactly(1).of(remoteLibraryManager).getPresenceLibrary(presence);
exactly(1).of(remoteLibraryManager).addPresenceLibrary(presence);
}});
presenceLibraryBrowser.register(listenerSupport);
presenceLibraryBrowser.registerToRemoteLibraryManager();
presenceLibraryBrowser.registerToSocksManager();
presenceLibraryBrowser.handleEvent(new LibraryChangedEvent(presence, LibraryChanged.LIBRARY_CHANGED));
context.assertIsSatisfied();
}
/**
* Force fire a connectivity change event and ensure a browse is attempted on a
* a queued PresenceLibrary. This simulates the retry of an earlier failed browse.
*/
@SuppressWarnings("unchecked")
public void testRetryBrowse() {
Mockery context = new Mockery();
final SocketsManager socketsManager = context.mock(SocketsManager.class);
final PresenceLibrary presenceLibrary = context.mock(PresenceLibrary.class);
final PresenceLibraryBrowser presenceLibraryBrowser
= new PresenceLibraryBrowser(null, null, socketsManager, null);
final MatchAndCopy<EventListener> socketsListenerCollector = new MatchAndCopy<EventListener>(EventListener.class);
context.checking(new Expectations() {{
// Assertions
exactly(1).of(socketsManager).addListener(with(socketsListenerCollector));
exactly(1).of(presenceLibrary).setState(LibraryState.LOADING);
// Allow the browse to unfold minimally as it must
allowing(presenceLibrary);
}});
// Register the required listener and collect it for probing
presenceLibraryBrowser.registerToSocksManager();
// Fire a connectivity change with no PresenceLibrary instances queued
socketsListenerCollector.getLastMatch().handleEvent(new ConnectivityChangeEvent());
// Queue a PresenceLibrary for rebrowse
presenceLibraryBrowser.librariesToBrowse.add(presenceLibrary);
// Fire a connectivity change again
socketsListenerCollector.getLastMatch().handleEvent(new ConnectivityChangeEvent());
context.assertIsSatisfied();
}
/**
* Fires events for presence library changes but ones that should not spawn new browses.
*/
@SuppressWarnings("unchecked")
public void testBrowseNotInitiated() {
Mockery context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
final RemoteLibraryManager remoteLibraryManager = context.mock(RemoteLibraryManager.class);
final EventList<FriendLibrary> friendLibraryList = context.mock(EventList.class);
final MatchAndCopy<ListEventListener> friendListenerCollector
= new MatchAndCopy<ListEventListener>(ListEventListener.class);
final ListEvent<FriendLibrary> listEventBlank = context.mock(ListEvent.class);
final PresenceLibraryBrowser presenceLibraryBrowser
= new PresenceLibraryBrowser(null, remoteLibraryManager, null, null);
context.checking(new Expectations() {{
allowing(remoteLibraryManager).getFriendLibraryList();
will(returnValue(friendLibraryList));
one(listEventBlank).next();
will(returnValue(true));
one(listEventBlank).getType();
will(returnValue(ListEvent.UPDATE));
one(listEventBlank).next();
will(returnValue(true));
one(listEventBlank).getType();
will(returnValue(ListEvent.DELETE));
one(listEventBlank).next();
will(returnValue(false));
exactly(1).of(friendLibraryList).addListEventListener(with(friendListenerCollector));
}});
presenceLibraryBrowser.registerToRemoteLibraryManager();
ListEventListener<FriendLibrary> listener = friendListenerCollector.getLastMatch();
listener.listChanged(listEventBlank);
context.assertIsSatisfied();
}
/**
* Fires events that should spawn a normal friend browse. When the browse is
* complete makes sure the listener handles removing failed browses correctly.
*
* <p>Full internal integration test.
*/
@SuppressWarnings("unchecked")
public void testBrowseIntegration() {
final Mockery context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
final RemoteLibraryManager remoteLibraryManager = context.mock(RemoteLibraryManager.class);
final SocketsManager socketsManager = context.mock(SocketsManager.class);
final BrowseFactory browseFactory = context.mock(BrowseFactory.class);
final MatchAndCopy<ListEventListener> listenerCollector
= new MatchAndCopy<ListEventListener>(ListEventListener.class);
final MatchAndCopy<BrowseListener> browseListenerCollector
= new MatchAndCopy<BrowseListener>(BrowseListener.class);
final ListEvent<FriendLibrary> listEvent = context.mock(ListEvent.class);
final EventList<FriendLibrary> friendLibraryEventList = context.mock(EventList.class);
final FriendLibrary friendLibrary = context.mock(FriendLibrary.class);
final EventList<PresenceLibrary> presenceLibraryEventList = context.mock(EventList.class);
final ReadWriteLock dummyRWLock = context.mock(ReadWriteLock.class);
final Lock dummyLock = context.mock(Lock.class);
final ListEvent<PresenceLibrary> addEvent = context.mock(ListEvent.class);
final PresenceLibrary presenceLibrary = context.mock(PresenceLibrary.class);
final ListEvent<PresenceLibrary> updateAndRemoveEvent = context.mock(ListEvent.class);
final PresenceLibraryBrowser presenceLibraryBrowser
= new PresenceLibraryBrowser(browseFactory, remoteLibraryManager, socketsManager, null);
context.checking(new Expectations() {{
allowing(remoteLibraryManager).getFriendLibraryList();
will(returnValue(friendLibraryEventList));
one(listEvent).next();
will(returnValue(true));
one(listEvent).getType();
will(returnValue(ListEvent.INSERT));
one(listEvent).next();
will(returnValue(false));
allowing(listEvent).getSourceList();
will(returnValue(friendLibraryEventList));
allowing(listEvent).getIndex();
will(returnValue(777));
allowing(friendLibraryEventList).get(777);
will(returnValue(friendLibrary));
allowing(friendLibrary).getPresenceLibraryList();
will(returnValue(presenceLibraryEventList));
allowing(presenceLibraryEventList).getReadWriteLock();
will(returnValue(dummyRWLock));
allowing(presenceLibraryEventList).toArray();
will(returnValue(new Object[0]));
allowing(dummyRWLock).readLock();
will(returnValue(dummyLock));
allowing(dummyLock).lock();
allowing(dummyLock).unlock();
one(addEvent).next();
will(returnValue(true));
one(addEvent).getType();
will(returnValue(ListEvent.INSERT));
one(addEvent).next();
will(returnValue(false));
one(updateAndRemoveEvent).next();
will(returnValue(true));
one(updateAndRemoveEvent).getType();
will(returnValue(ListEvent.UPDATE));
one(updateAndRemoveEvent).next();
will(returnValue(true));
one(updateAndRemoveEvent).getType();
will(returnValue(ListEvent.DELETE));
one(updateAndRemoveEvent).next();
will(returnValue(false));
allowing(addEvent).getSourceList();
will(returnValue(presenceLibraryEventList));
allowing(presenceLibraryEventList).get(0);
will(returnValue(presenceLibrary));
allowing(addEvent).getIndex();
will(returnValue(0));
allowing(updateAndRemoveEvent).getSourceList();
will(returnValue(presenceLibraryEventList));
allowing(updateAndRemoveEvent).getIndex();
will(returnValue(0));
// TODO: why two?
exactly(2).of(presenceLibrary).setState(LibraryState.LOADING);
FriendPresence friendPresence = context.mock(FriendPresence.class);
allowing(presenceLibrary).getPresence();
will(returnValue(friendPresence));
AddressFeature addressFeature = context.mock(AddressFeature.class);
allowing(friendPresence).getFeature(AddressFeature.ID);
will(returnValue(addressFeature));
Address address = context.mock(Address.class);
allowing(addressFeature).getFeature();
will(returnValue(address));
allowing(socketsManager).canResolve(address);
will(returnValue(false));
allowing(socketsManager).canConnect(address);
will(returnValue(true));
allowing(friendPresence).getPresenceId();
Browse browse = context.mock(Browse.class);
allowing(browseFactory).createBrowse(friendPresence);
will(returnValue(browse));
Friend friend = context.mock(Friend.class);
allowing(friendPresence).getFriend();
will(returnValue(friend));
allowing(friend).isAnonymous();
will(returnValue(true));
// Assertions
exactly(1).of(friendLibraryEventList).addListEventListener(with(listenerCollector));
exactly(1).of(presenceLibraryEventList).addListEventListener(with(listenerCollector));
exactly(1).of(browse).start(with(browseListenerCollector));
exactly(1).of(presenceLibrary).size();
exactly(1).of(presenceLibrary).setState(LibraryState.LOADED);
}});
// Register the initial listener to listen for new remote libraries to manage browsing
presenceLibraryBrowser.registerToRemoteLibraryManager();
// Force fire a friend being added to the browse list
ListEventListener<FriendLibrary> friendListener = listenerCollector.getLastMatch();
friendListener.listChanged(listEvent);
// Force fire a presence coming available and thus initiate the browse
assertEquals("A listener was not installed on the presence library",
2, listenerCollector.getMatches().size());
ListEventListener<PresenceLibrary> presenceListener = listenerCollector.getLastMatch();
presenceListener.listChanged(addEvent);
// Complete the browse
BrowseListener browseListener = browseListenerCollector.getLastMatch();
browseListener.browseFinished(true);
// PresenceLibrary should not be added to the rebrowse list because the browse was
// successful
assertNotContains(presenceLibraryBrowser.librariesToBrowse, presenceLibrary);
// Test removal of simulated killed browse
presenceLibraryBrowser.librariesToBrowse.add(presenceLibrary);
presenceListener.listChanged(updateAndRemoveEvent);
assertEmpty(presenceLibraryBrowser.librariesToBrowse);
context.assertIsSatisfied();
}
/**
* Simulates a browse with a resolvable address.
*
* <p>Skips initial integration steps and tests tryToResolveAndBrowse() and browse().
*/
public void testBrowseWithGoodResolve() {
final Mockery context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
final RemoteLibraryManager remoteLibraryManager = context.mock(RemoteLibraryManager.class);
final SocketsManager socketsManager = context.mock(SocketsManager.class);
final BrowseFactory browseFactory = context.mock(BrowseFactory.class);
final PresenceLibrary presenceLibrary = context.mock(PresenceLibrary.class);
final Address resolvedAddress = context.mock(Address.class);
final PresenceLibraryBrowser presenceLibraryBrowser
= new PresenceLibraryBrowser(browseFactory, remoteLibraryManager, socketsManager, null);
final MatchAndCopy<AddressResolutionObserver> observerCollector
= new MatchAndCopy<AddressResolutionObserver>(AddressResolutionObserver.class);
final MatchAndCopy<BrowseListener> browseListenerCollector
= new MatchAndCopy<BrowseListener>(BrowseListener.class);
context.checking(new Expectations() {{
allowing(presenceLibrary).setState(with(any(LibraryState.class)));
FriendPresence presence = context.mock(FriendPresence.class);
allowing(presenceLibrary).getPresence();
will(returnValue(presence));
AddressFeature addressFeature = context.mock(AddressFeature.class);
allowing(presence).getFeature(AddressFeature.ID);
will(returnValue(addressFeature));
Address address = context.mock(Address.class);
allowing(addressFeature).getFeature();
will(returnValue(address));
allowing(socketsManager).canResolve(address);
will(returnValue(true));
allowing(socketsManager).canConnect(resolvedAddress);
will(returnValue(true));
allowing(presence).getPresenceId();
will(returnValue("12321321 abajeƱa 12342323523452"));
Browse browse = context.mock(Browse.class);
allowing(browseFactory).createBrowse(presence);
will(returnValue(browse));
Friend friend = context.mock(Friend.class);
allowing(presence).getFriend();
will(returnValue(friend));
allowing(friend).isAnonymous();
will(returnValue(true));
// Assertions
exactly(1).of(socketsManager).resolve(with(same(address)), with(observerCollector));
exactly(1).of(browse).start(with(browseListenerCollector));
exactly(1).of(presenceLibrary).size();
}});
// Kick off the browse
presenceLibraryBrowser.tryToResolveAndBrowse(presenceLibrary, 0);
// Signal resolution and complete the browse
observerCollector.getLastMatch().resolved(resolvedAddress);
// Signal browse failure
browseListenerCollector.getLastMatch().browseFinished(false);
// Signal browse success
browseListenerCollector.getLastMatch().browseFinished(true);
context.assertIsSatisfied();
}
/**
* Attempt a browse on a presence without an address feature. Ensure
* graceful fail and the presence is added to the retry queue.
*
* <p> Tests tryToResolveAndBrowse() and handleFailedResolution().
*/
public void testBrowseWithoutAddressFeature() {
final Mockery context = new Mockery();
final PresenceLibrary presenceLibrary = context.mock(PresenceLibrary.class);
final PresenceLibraryBrowser presenceLibraryBrowser
= new PresenceLibraryBrowser(null, null, null, null);
context.checking(new Expectations() {{
FriendPresence presence = context.mock(FriendPresence.class);
allowing(presenceLibrary).getPresence();
will(returnValue(presence));
allowing(presence).hasFeatures(AddressFeature.ID);
will(returnValue(false));
allowing(presence).getFeature(AddressFeature.ID);
will(returnValue(null));
allowing(presenceLibrary);
allowing(presence);
}});
presenceLibraryBrowser.tryToResolveAndBrowse(presenceLibrary, 0);
assertContains(presenceLibraryBrowser.librariesToBrowse, presenceLibrary);
context.assertIsSatisfied();
}
/**
* Attempt a browse with a currently non connectable or resolvable address. Ensure
* graceful fail and the PresenceLibrary is immediately retried. On the second failing
* check to make sure the PresenceLibrary is added to the retry queue.
*
* <p> Tests tryToResolveAndBrowse() and handleFailedResolution().
*/
public void testBrowseWhenCantResolveAndCantConnect() {
final Mockery context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
final SocketsManager socketsManager = context.mock(SocketsManager.class);
final PresenceLibrary presenceLibrary = context.mock(PresenceLibrary.class);
final PresenceLibraryBrowser presenceLibraryBrowser
= new PresenceLibraryBrowser(null, null, socketsManager, null);
context.checking(new Expectations() {{
FriendPresence presence = context.mock(FriendPresence.class);
allowing(presenceLibrary).getPresence();
will(returnValue(presence));
allowing(presence).hasFeatures(AddressFeature.ID);
will(returnValue(false));
AddressFeature addressFeature = context.mock(AddressFeature.class);
allowing(presence).getFeature(AddressFeature.ID);
will(returnValue(addressFeature));
Address address = context.mock(Address.class);
allowing(addressFeature).getFeature();
will(returnValue(address));
// Can't resolve, can't connect, twice because immediate retry
atLeast(2).of(socketsManager).canConnect(address);
will(returnValue(false));
atLeast(2).of(socketsManager).canResolve(address);
will(returnValue(false));
// General behaviour
allowing(presenceLibrary);
allowing(presence);
}});
// Revision = -1 so an immediate retry will be attempted
presenceLibraryBrowser.tryToResolveAndBrowse(presenceLibrary, -1);
assertContains(presenceLibraryBrowser.librariesToBrowse, presenceLibrary);
context.assertIsSatisfied();
}
/**
* Tests the edge functionality of the can resolve case. First resolve but with a
* non connectable address. Next force fire an IOE event to the then non relevant resolve handler
* and make sure things are handled accordingly.
*
* <p> Tests tryToResolveAndBrowse() and handleFailedResolution().
*/
public void testBrowseWhenCanResolveButCantConnectPlusIOE() {
final Mockery context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
final SocketsManager socketsManager = context.mock(SocketsManager.class);
final PresenceLibrary presenceLibrary = context.mock(PresenceLibrary.class);
final Address resolvedAddress = context.mock(Address.class);
final PresenceLibraryBrowser presenceLibraryBrowser
= new PresenceLibraryBrowser(null, null, socketsManager, null);
final MatchAndCopy<AddressResolutionObserver> observerCollector
= new MatchAndCopy<AddressResolutionObserver>(AddressResolutionObserver.class);
context.checking(new Expectations() {{
FriendPresence presence = context.mock(FriendPresence.class);
allowing(presenceLibrary).getPresence();
will(returnValue(presence));
allowing(presence).hasFeatures(AddressFeature.ID);
will(returnValue(false));
AddressFeature addressFeature = context.mock(AddressFeature.class);
allowing(presence).getFeature(AddressFeature.ID);
will(returnValue(addressFeature));
Address address = context.mock(Address.class);
allowing(addressFeature).getFeature();
will(returnValue(address));
// Can resolve, can't connect
allowing(socketsManager).canResolve(address);
will(returnValue(true));
allowing(socketsManager).canConnect(resolvedAddress);
will(returnValue(false));
// General behaviour
allowing(presenceLibrary);
allowing(presence);
// Assertions
exactly(1).of(socketsManager).resolve(with(same(address)), with(observerCollector));
}});
// Kick off browse process
presenceLibraryBrowser.tryToResolveAndBrowse(presenceLibrary, 0);
// Resolve
observerCollector.getLastMatch().resolved(resolvedAddress);
// Make sure the failed PresenceLibrary is in the rebrowse list
assertContains(presenceLibraryBrowser.librariesToBrowse, presenceLibrary);
// Remove the failed presence library so when simulating the IOE we can readd it
presenceLibraryBrowser.librariesToBrowse.remove(presenceLibrary);
// Force IOE
observerCollector.getLastMatch().handleIOException(new IOException("forced"));
// Should still have the PresenceLibrary in the rebrowse list
assertContains(presenceLibraryBrowser.librariesToBrowse, presenceLibrary);
// "Shutdown" the observer
observerCollector.getLastMatch().shutdown();
context.assertIsSatisfied();
}
/**
* Test a browse sequence where the address feature is lost between resolving and browsing.
*
* <p>NOTE: This simulates a browse upon shutdown.
*
* <p>Tests tryToResolveAndBrowse and browse().
*/
public void testBrowseWhenAfterResolveFeatureIsLost() {
final Mockery context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
final SocketsManager socketsManager = context.mock(SocketsManager.class);
final PresenceLibrary presenceLibrary = context.mock(PresenceLibrary.class);
final Address resolvedAddress = context.mock(Address.class);
final PresenceLibraryBrowser presenceLibraryBrowser
= new PresenceLibraryBrowser(null, null, socketsManager, null);
final MatchAndCopy<AddressResolutionObserver> observerCollector
= new MatchAndCopy<AddressResolutionObserver>(AddressResolutionObserver.class);
context.checking(new Expectations() {{
FriendPresence presence = context.mock(FriendPresence.class);
allowing(presenceLibrary).getPresence();
will(returnValue(presence));
AddressFeature addressFeature = context.mock(AddressFeature.class);
// Only return the feature the first time since
// after the resolve this should fail
atMost(1).of(presence).hasFeatures(AddressFeature.ID);
will(returnValue(true));
allowing(presence).hasFeatures(AddressFeature.ID);
will(returnValue(false));
atMost(1).of(presence).getFeature(AddressFeature.ID);
will(returnValue(addressFeature));
allowing(presence).getFeature(AddressFeature.ID);
will(returnValue(null));
Address address = context.mock(Address.class);
allowing(addressFeature).getFeature();
will(returnValue(address));
// Can resolve, can connect
allowing(socketsManager).canResolve(address);
will(returnValue(true));
allowing(socketsManager).canConnect(resolvedAddress);
will(returnValue(true));
// General behaviour
allowing(presenceLibrary);
allowing(presence);
// Assertions
exactly(1).of(socketsManager).resolve(with(same(address)), with(observerCollector));
}});
// Kick off browse process
presenceLibraryBrowser.tryToResolveAndBrowse(presenceLibrary, 0);
// Resolve
observerCollector.getLastMatch().resolved(resolvedAddress);
// PresenceLibrary should not be added to the rebrowse list in this case
// because this fault indicates the shutdown sequence is in progress.
assertNotContains(presenceLibraryBrowser.librariesToBrowse, presenceLibrary);
context.assertIsSatisfied();
}
/**
* Do a full normal browse on an anonymous friend.
*
* Only tests browse().
*/
public void testBrowseWithAnonymousFriend() {
final Mockery context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
final SocketsManager socketsManager = context.mock(SocketsManager.class);
final BrowseFactory browseFactory = context.mock(BrowseFactory.class);
final PresenceLibrary presenceLibrary = context.mock(PresenceLibrary.class);
final RemoteFileDescAdapter searchResult = context.mock(RemoteFileDescAdapter.class);
final PresenceLibraryBrowser presenceLibraryBrowser
= new PresenceLibraryBrowser(browseFactory, null, socketsManager, null);
final MatchAndCopy<BrowseListener> listenerCollector
= new MatchAndCopy<BrowseListener>(BrowseListener.class);
context.checking(new Expectations() {{
FriendPresence presence = context.mock(FriendPresence.class);
allowing(presenceLibrary).getPresence();
will(returnValue(presence));
AddressFeature addressFeature = context.mock(AddressFeature.class);
allowing(presence).hasFeatures(AddressFeature.ID);
will(returnValue(true));
allowing(presence).getFeature(AddressFeature.ID);
will(returnValue(addressFeature));
Friend friend = context.mock(Friend.class);
allowing(presence).getFriend();
will(returnValue(friend));
allowing(friend).isAnonymous();
will(returnValue(true));
Address address = context.mock(Address.class);
allowing(addressFeature).getFeature();
will(returnValue(address));
// Browse creation
Browse browse = context.mock(Browse.class);
allowing(browseFactory).createBrowse(presence);
will(returnValue(browse));
// General behaviour
allowing(presenceLibrary);
allowing(presence);
allowing(searchResult);
// Assertions
exactly(1).of(browse).start(with(listenerCollector));
}});
// Kick off browse process
presenceLibraryBrowser.browse(presenceLibrary);
listenerCollector.getLastMatch().handleBrowseResult(searchResult);
// PresenceLibrary should not be added to the rebrowse list because the browse was
// successful
assertNotContains(presenceLibraryBrowser.librariesToBrowse, presenceLibrary);
context.assertIsSatisfied();
}
}