package org.limewire.core.impl.friend;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.Sequence;
import org.jmock.lib.legacy.ClassImposteriser;
import org.limewire.core.api.browse.server.BrowseTracker;
import org.limewire.core.impl.friend.FriendShareListRefresher;
import org.limewire.core.impl.library.FileViewStub;
import org.limewire.friend.api.Friend;
import org.limewire.friend.api.FriendEvent;
import org.limewire.friend.api.feature.FeatureTransport;
import org.limewire.friend.api.feature.LibraryChangedNotifier;
import org.limewire.friend.api.feature.LibraryChangedNotifierFeature;
import org.limewire.listener.EventListener;
import org.limewire.listener.ListenerSupport;
import org.limewire.util.BaseTestCase;
import org.limewire.util.MatchAndCopy;
import com.limegroup.gnutella.ClockStub;
import com.limegroup.gnutella.MockFriend;
import com.limegroup.gnutella.MockFriendPresence;
import com.limegroup.gnutella.library.FileDescStub;
import com.limegroup.gnutella.library.FileViewChangeEvent;
import com.limegroup.gnutella.library.FileViewManager;
import com.limegroup.gnutella.library.Library;
import com.limegroup.gnutella.library.LibraryStatusEvent;
public class FriendShareListRefresherTest extends BaseTestCase {
public FriendShareListRefresherTest(String name) {
super(name);
}
@SuppressWarnings("unchecked")
public void testRegister() {
Mockery context = new Mockery();
final Library library = context.mock(Library.class);
final FileViewManager viewManager = context.mock(FileViewManager.class);
final ListenerSupport friendSupport = context.mock(ListenerSupport.class);
FriendShareListRefresher refresher = new FriendShareListRefresher(null, null, null, null);
context.checking(new Expectations() {{
exactly(1).of(library).addManagedListStatusListener(with(any(EventListener.class)));
exactly(1).of(viewManager).addListener(with(any(EventListener.class)));
exactly(1).of(friendSupport).addListener(with(any(EventListener.class)));
}});
refresher.register(library);
refresher.register(viewManager, friendSupport);
context.assertIsSatisfied();
}
@SuppressWarnings("unchecked")
public void testHandleEventBeforeLibraryLoad() {
Mockery context = new Mockery();
final Map friendMap = context.mock(Map.class);
final ScheduledExecutorService executor = context.mock(ScheduledExecutorService.class);
final FileViewManager viewManager = context.mock(FileViewManager.class);
final ListenerSupport friendSupport = context.mock(ListenerSupport.class);
final MatchAndCopy<EventListener> viewMatcher = new MatchAndCopy<EventListener>(EventListener.class);
final MatchAndCopy<EventListener> friendMatcher = new MatchAndCopy<EventListener>(EventListener.class);
FriendShareListRefresher refresher = new FriendShareListRefresher(null, null, executor, friendMap);
// Events do nothing because library isn't loaded.
context.checking(new Expectations() {{
exactly(1).of(viewManager).addListener(with(viewMatcher));
exactly(1).of(friendSupport).addListener(with(friendMatcher));
}});
refresher.register(viewManager, friendSupport);
viewMatcher.getLastMatch().handleEvent(new FileViewChangeEvent(new FileViewStub("test id"), FileViewChangeEvent.Type.FILE_ADDED, new FileDescStub()));
friendMatcher.getLastMatch().handleEvent(new FriendEvent(new MockFriend("test id"), FriendEvent.Type.REMOVED));
context.assertIsSatisfied();
}
@SuppressWarnings("unchecked")
public void testHandleEventAfterLibraryLoad() {
Mockery context = new Mockery();
final Map friendMap = context.mock(Map.class);
final ScheduledExecutorService executor = context.mock(ScheduledExecutorService.class);
final FileViewManager viewManager = context.mock(FileViewManager.class);
final ListenerSupport friendSupport = context.mock(ListenerSupport.class);
final Friend friend = new MockFriend("test id");
final MatchAndCopy<EventListener> viewMatcher = new MatchAndCopy<EventListener>(EventListener.class);
final MatchAndCopy<EventListener> friendMatcher = new MatchAndCopy<EventListener>(EventListener.class);
FriendShareListRefresher refresher = new FriendShareListRefresher(null, null, executor, friendMap);
refresher.fileManagerLoaded.set(true);
final Sequence resultSequence = context.sequence("resultSequence");
// Initial setup.
context.checking(new Expectations() {{
exactly(1).of(viewManager).addListener(with(viewMatcher));
exactly(1).of(friendSupport).addListener(with(friendMatcher));
}});
refresher.register(viewManager, friendSupport);
context.assertIsSatisfied();
// Event triggers friend retrieval & scheduling.
context.checking(new Expectations() {{
exactly(1).of(friendMap).get(friend.getId());
will(returnValue(friend));
inSequence(resultSequence);
exactly(1).of(executor).schedule(with(any(Runnable.class)), with(equal(5000000000L)), with(equal(TimeUnit.NANOSECONDS)));
inSequence(resultSequence);
}});
viewMatcher.getLastMatch().handleEvent(new FileViewChangeEvent(new FileViewStub(friend.getId()), FileViewChangeEvent.Type.FILE_ADDED, new FileDescStub()));
context.assertIsSatisfied();
// Another modification event will not trigger a new runnable to be scheduled,
// because one is already scheduled.
context.checking(new Expectations() {{
exactly(1).of(friendMap).get(friend.getId());
will(returnValue(friend));
inSequence(resultSequence);
}});
viewMatcher.getLastMatch().handleEvent(new FileViewChangeEvent(new FileViewStub(friend.getId()), FileViewChangeEvent.Type.FILE_ADDED, new FileDescStub()));
context.assertIsSatisfied();
// But now, if we remove the known friend, a later modification will again trigger a new runnable.
context.checking(new Expectations() {{
exactly(1).of(friendMap).get(friend.getId());
will(returnValue(friend));
inSequence(resultSequence);
exactly(1).of(executor).schedule(with(any(Runnable.class)), with(equal(5000000000L)), with(equal(TimeUnit.NANOSECONDS)));
inSequence(resultSequence);
}});
friendMatcher.getLastMatch().handleEvent(new FriendEvent(friend, FriendEvent.Type.REMOVED));
viewMatcher.getLastMatch().handleEvent(new FileViewChangeEvent(new FileViewStub(friend.getId()), FileViewChangeEvent.Type.FILE_ADDED, new FileDescStub()));
context.assertIsSatisfied();
}
@SuppressWarnings("unchecked")
public void testFinishedLoadingListenerWithUnrelatedEventsDoesNothing() {
Mockery context = new Mockery();
final BrowseTracker tracker = context.mock(BrowseTracker.class);
final ScheduledExecutorService scheduledExecutorService = context.mock(ScheduledExecutorService.class);
final Library library = context.mock(Library.class);
FriendShareListRefresher refresher = new FriendShareListRefresher(null, tracker, scheduledExecutorService, null);
final MatchAndCopy<EventListener> libraryMatcher = new MatchAndCopy<EventListener>(EventListener.class);
context.checking(new Expectations() {{
exactly(1).of(library).addManagedListStatusListener(with(libraryMatcher));
}});
refresher.register(library);
libraryMatcher.getLastMatch().handleEvent(new LibraryStatusEvent(library, LibraryStatusEvent.Type.SAVE));
libraryMatcher.getLastMatch().handleEvent(new LibraryStatusEvent(library, LibraryStatusEvent.Type.LOAD_FINISHING));
context.assertIsSatisfied();
}
@SuppressWarnings("unchecked")
public void testFinishedLoadingListenerWithLoadCompleteSendsNotifications() throws Exception {
Mockery context = new Mockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
final BrowseTracker tracker = context.mock(BrowseTracker.class);
final ScheduledExecutorService scheduledExecutorService = context.mock(ScheduledExecutorService.class);
final Library library = context.mock(Library.class);
final Map<String, Friend> friendMap = new HashMap<String, Friend>();
final MockFriend friend1 = new MockFriend("friend 1");
final MockFriend friend2 = new MockFriend("friend 2");
final MockFriendPresence friend1Presence1 = new MockFriendPresence(friend1, "a");
final MockFriendPresence friend1Presence2 = new MockFriendPresence(friend1, "b");
final MockFriendPresence friend2Presence1 = new MockFriendPresence(friend2, "c");
final MockFriendPresence friend2Presence2 = new MockFriendPresence(friend2, "d");
friend1.addPresence(friend1Presence1);
friend1.addPresence(friend1Presence2);
friend2.addPresence(friend2Presence1);
friend2.addPresence(friend2Presence2);
friendMap.put(friend1.getId(), friend1);
friendMap.put(friend2.getId(), friend2);
FriendShareListRefresher refresher = new FriendShareListRefresher(null, tracker, scheduledExecutorService, friendMap);
final LibraryChangedNotifier notifierA = context.mock(LibraryChangedNotifier.class);
final LibraryChangedNotifier notifierB = context.mock(LibraryChangedNotifier.class);
final LibraryChangedNotifier notifierC = context.mock(LibraryChangedNotifier.class);
friend1Presence1.addFeature(new LibraryChangedNotifierFeature(notifierA));
friend1Presence2.addFeature(new LibraryChangedNotifierFeature(notifierB));
friend2Presence1.addFeature(new LibraryChangedNotifierFeature(notifierC));
// Do not add a feature for f2p2.
final MatchAndCopy<EventListener> libraryMatcher = new MatchAndCopy<EventListener>(EventListener.class);
context.checking(new Expectations() {{
exactly(1).of(library).addManagedListStatusListener(with(libraryMatcher));
}});
refresher.register(library);
context.assertIsSatisfied();
final FeatureTransport<LibraryChangedNotifier> transport11 = context.mock(FeatureTransport.class);
friend1Presence1.addTransport(LibraryChangedNotifierFeature.class, transport11);
final FeatureTransport<LibraryChangedNotifier> transport12 = context.mock(FeatureTransport.class);
friend1Presence2.addTransport(LibraryChangedNotifierFeature.class, transport12);
final FeatureTransport<LibraryChangedNotifier> transport21 = context.mock(FeatureTransport.class);
friend2Presence1.addTransport(LibraryChangedNotifierFeature.class, transport21);
context.checking(new Expectations() {{
exactly(1).of(tracker).sentRefresh(friend1.getId());
exactly(1).of(tracker).sentRefresh(friend2.getId());
one(transport11).sendFeature(friend1Presence1, notifierA);
one(transport12).sendFeature(friend1Presence2, notifierB);
one(transport21).sendFeature(friend2Presence1, notifierC);
}});
assertFalse(refresher.fileManagerLoaded.get());
libraryMatcher.getLastMatch().handleEvent(new LibraryStatusEvent(library, LibraryStatusEvent.Type.LOAD_COMPLETE));
assertTrue(refresher.fileManagerLoaded.get());
context.assertIsSatisfied();
}
@SuppressWarnings("unchecked")
public void testScheduledLibraryRefreshSenderWithNoRefreshNeeded() {
Mockery context = new Mockery();
final ClockStub clock = new ClockStub();
final BrowseTracker tracker = context.mock(BrowseTracker.class);
final ScheduledExecutorService executor = context.mock(ScheduledExecutorService.class);
final FileViewManager viewManager = context.mock(FileViewManager.class);
final ListenerSupport friendSupport = context.mock(ListenerSupport.class);
final Map<String, Friend> friendMap = new HashMap<String, Friend>();
final Friend friend = new MockFriend("anId");
friendMap.put(friend.getId(), friend);
final MatchAndCopy<EventListener> viewMatcher = new MatchAndCopy<EventListener>(EventListener.class);
final MatchAndCopy<EventListener> friendMatcher = new MatchAndCopy<EventListener>(EventListener.class);
FriendShareListRefresher refresher = new FriendShareListRefresher(clock, tracker, executor, friendMap);
refresher.fileManagerLoaded.set(true);
// Initial setup.
context.checking(new Expectations() {{
exactly(1).of(viewManager).addListener(with(viewMatcher));
exactly(1).of(friendSupport).addListener(with(friendMatcher));
}});
refresher.register(viewManager, friendSupport);
context.assertIsSatisfied();
final MatchAndCopy<Runnable> runnableMatcher = new MatchAndCopy<Runnable>(Runnable.class);
// Event triggers friend retrieval & scheduling.
context.checking(new Expectations() {{
exactly(1).of(executor).schedule(with(runnableMatcher), with(equal(5000000000L)), with(equal(TimeUnit.NANOSECONDS)));
}});
clock.setNanoTime(System.nanoTime());
viewMatcher.getLastMatch().handleEvent(new FileViewChangeEvent(new FileViewStub(friend.getId()), FileViewChangeEvent.Type.FILE_ADDED, new FileDescStub()));
context.assertIsSatisfied();
context.checking(new Expectations() {{
// Browse times should at least be compared
exactly(1).of(tracker).lastBrowseTime(friend.getId());
will(returnValue(new Date(50)));
exactly(1).of(tracker).lastRefreshTime(friend.getId());
will(returnValue(new Date(100)));
}});
clock.setNanoTime(clock.nanoTime() + TimeUnit.SECONDS.toNanos(6));
runnableMatcher.getLastMatch().run();
context.assertIsSatisfied();
}
/**
* Force fire a refresh action with the last browse time after the
* last refresh time, therefore with notifications necessary. Also,
* force one of the feature lookups to return null and make sure the
* failure is handled correctly.
* <p>
* Ensure that the browse tracker and friend presence notifications are made.
*/
@SuppressWarnings("unchecked")
public void testScheduledLibraryRefreshSender() throws Exception {
Mockery context = new Mockery();
final ClockStub clock = new ClockStub();
final BrowseTracker tracker = context.mock(BrowseTracker.class);
final ScheduledExecutorService executor = context.mock(ScheduledExecutorService.class);
final FileViewManager viewManager = context.mock(FileViewManager.class);
final ListenerSupport friendSupport = context.mock(ListenerSupport.class);
final Map<String, Friend> friendMap = new HashMap<String, Friend>();
final MockFriend friend = new MockFriend("anId");
final MockFriendPresence presence1 = new MockFriendPresence(friend, "a");
final MockFriendPresence presence2 = new MockFriendPresence(friend, "b");
friend.addPresence(presence1);
friend.addPresence(presence2);
friendMap.put(friend.getId(), friend);
final MatchAndCopy<EventListener> viewMatcher = new MatchAndCopy<EventListener>(EventListener.class);
final MatchAndCopy<EventListener> friendMatcher = new MatchAndCopy<EventListener>(EventListener.class);
FriendShareListRefresher refresher = new FriendShareListRefresher(clock, tracker, executor, friendMap);
refresher.fileManagerLoaded.set(true);
// Initial setup.
context.checking(new Expectations() {{
exactly(1).of(viewManager).addListener(with(viewMatcher));
exactly(1).of(friendSupport).addListener(with(friendMatcher));
}});
refresher.register(viewManager, friendSupport);
context.assertIsSatisfied();
final MatchAndCopy<Runnable> runnableMatcher = new MatchAndCopy<Runnable>(Runnable.class);
// Event triggers friend retrieval & scheduling.
context.checking(new Expectations() {{
exactly(1).of(executor).schedule(with(runnableMatcher), with(equal(5000000000L)), with(equal(TimeUnit.NANOSECONDS)));
}});
clock.setNanoTime(System.nanoTime());
viewMatcher.getLastMatch().handleEvent(new FileViewChangeEvent(new FileViewStub(friend.getId()), FileViewChangeEvent.Type.FILE_ADDED, new FileDescStub()));
context.assertIsSatisfied();
final LibraryChangedNotifier notifierA = context.mock(LibraryChangedNotifier.class);
presence1.addFeature(new LibraryChangedNotifierFeature(notifierA));
final FeatureTransport<LibraryChangedNotifier> transport = context.mock(FeatureTransport.class);
presence1.addTransport(LibraryChangedNotifierFeature.class, transport);
context.checking(new Expectations() {{
// Browse times should at least be compared
exactly(1).of(tracker).lastBrowseTime(friend.getId());
will(returnValue(new Date(100)));
exactly(1).of(tracker).lastRefreshTime(friend.getId());
will(returnValue(new Date(50)));
exactly(1).of(tracker).sentRefresh(friend.getId());
one(transport).sendFeature(presence1, notifierA);
}});
clock.setNanoTime(clock.nanoTime() + TimeUnit.SECONDS.toNanos(6));
runnableMatcher.getLastMatch().run();
context.assertIsSatisfied();
}
}