package org.limewire.core.impl; import java.io.File; import java.io.IOException; import java.util.HashSet; import java.util.Set; import org.jmock.Expectations; import org.jmock.Mockery; import org.jmock.lib.legacy.ClassImposteriser; import org.limewire.bittorrent.Torrent; import org.limewire.core.api.callback.GuiCallback; import org.limewire.core.api.download.DownloadAction; import org.limewire.core.api.download.DownloadException; import org.limewire.core.api.magnet.MagnetLink; import org.limewire.core.impl.download.DownloadListener; import org.limewire.core.impl.monitor.IncomingSearchListener; import org.limewire.core.impl.search.QueryReplyListener; import org.limewire.core.impl.upload.UploadListener; import org.limewire.io.GUID; import org.limewire.io.IpPort; import org.limewire.service.ErrorCallback; import org.limewire.service.ErrorService; import org.limewire.service.MessageCallback; import org.limewire.service.MessageService; import org.limewire.util.BaseTestCase; import org.limewire.util.MatchAndCopy; import com.limegroup.gnutella.DownloadManager; import com.limegroup.gnutella.Downloader; import com.limegroup.gnutella.RemoteFileDesc; import com.limegroup.gnutella.Uploader; import com.limegroup.gnutella.browser.MagnetOptions; import com.limegroup.gnutella.messages.QueryReply; import com.limegroup.gnutella.messages.QueryRequest; /** * Tests methods in {@link GlueActivityCallback} for functionality and linkage. */ public class GlueActivityCallbackTest extends BaseTestCase { public GlueActivityCallbackTest(String name) { super(name); } /** * Adds and removes an {@link UploadListener} ensures events are and are not fired * according to the situation. */ public void testUploadListenerLinkage() { Mockery context = new Mockery(); final UploadListener listener1 = context.mock(UploadListener.class); final UploadListener listener2 = context.mock(UploadListener.class); final UploadListener listener3 = context.mock(UploadListener.class); final Uploader uploaderA = context.mock(Uploader.class); GlueActivityCallback activityCallback = new GlueActivityCallback(null); context.checking(new Expectations() {{ exactly(1).of(listener1).uploadAdded(uploaderA); exactly(1).of(listener2).uploadAdded(uploaderA); exactly(1).of(listener3).uploadAdded(uploaderA); exactly(1).of(listener1).uploadRemoved(uploaderA); exactly(1).of(listener1).uploadsCompleted(); exactly(1).of(listener2).uploadsCompleted(); }}); activityCallback.addUploadListener(listener1); activityCallback.addUploadListener(listener2); activityCallback.addUploadListener(listener3); activityCallback.addUpload(uploaderA); activityCallback.removeUploadListener(listener2); activityCallback.removeUploadListener(listener3); activityCallback.removeUpload(uploaderA); activityCallback.addUploadListener(listener2); activityCallback.uploadsComplete(); context.assertIsSatisfied(); } /** * Adds and removes an {@link DownloadListener} ensures events are and are not fired * according to the situation. */ public void testDownloadListenerLinkage() { Mockery context = new Mockery(); final DownloadListener listener1 = context.mock(DownloadListener.class); final DownloadListener listener2 = context.mock(DownloadListener.class); final DownloadListener listener3 = context.mock(DownloadListener.class); final Downloader downloaderA = context.mock(Downloader.class); GlueActivityCallback activityCallback = new GlueActivityCallback(null); context.checking(new Expectations() {{ exactly(1).of(listener1).downloadAdded(downloaderA); exactly(1).of(listener2).downloadAdded(downloaderA); exactly(1).of(listener3).downloadAdded(downloaderA); exactly(1).of(listener1).downloadRemoved(downloaderA); exactly(1).of(listener1).downloadsCompleted(); exactly(1).of(listener2).downloadsCompleted(); }}); activityCallback.addDownloadListener(listener1); activityCallback.addDownloadListener(listener2); activityCallback.addDownloadListener(listener3); activityCallback.addDownload(downloaderA); activityCallback.removeDownloadListener(listener2); activityCallback.removeDownloadListener(listener3); activityCallback.removeDownload(downloaderA); activityCallback.addDownloadListener(listener2); activityCallback.downloadsComplete(); context.assertIsSatisfied(); } /** * Test the restoreApplication method with and without the callback set. */ public void testRestoreApplication() { Mockery context = new Mockery(); final GuiCallback callback = context.mock(GuiCallback.class); GlueActivityCallback activityCallback = new GlueActivityCallback(null); context.checking(new Expectations() {{ exactly(1).of(callback).restoreApplication(); }}); activityCallback.restoreApplication(); activityCallback.setGuiCallback(callback); activityCallback.restoreApplication(); context.assertIsSatisfied(); } /** * Test the warnUser method with and without the callback set. */ public void testWarnUser() { Mockery context = new Mockery(); final GuiCallback callback = context.mock(GuiCallback.class); GlueActivityCallback activityCallback = new GlueActivityCallback(null); context.checking(new Expectations() {{ exactly(1).of(callback).warnUser("file", "oh noes"); }}); activityCallback.warnUser("file", "oh noes"); activityCallback.setGuiCallback(callback); activityCallback.warnUser("file", "oh noes"); context.assertIsSatisfied(); } /** * Signal a save location exception before and after a gui callback is installed. * For before the error should be passed along to {@link ErrorService}, when after it * should go to the set {@link GuiCallback}. */ public void testHandleDownloadException() { Mockery context = new Mockery(); final GuiCallback callback = context.mock(GuiCallback.class); final ErrorCallback mockErrorCallback = context.mock(ErrorCallback.class); final DownloadException e = new DownloadException(new IOException(), new File("a")); final DownloadAction downloadAction = context.mock(DownloadAction.class); GlueActivityCallback activityCallback = new GlueActivityCallback(null); context.checking(new Expectations() {{ exactly(1).of(mockErrorCallback).error(with(same(e)), with(any(String.class))); exactly(1).of(callback).handleDownloadException(downloadAction, e, false); }}); ErrorCallback originalErrorCallback = ErrorService.getErrorCallback(); ErrorService.setErrorCallback(mockErrorCallback); activityCallback.handleDownloadException(downloadAction, e, true); ErrorService.setErrorCallback(originalErrorCallback); activityCallback.setGuiCallback(callback); activityCallback.handleDownloadException(downloadAction, e, false); context.assertIsSatisfied(); } public void testHandleTorrent() throws DownloadException { Mockery context = new Mockery() {{ setImposteriser(ClassImposteriser.INSTANCE); }}; final DownloadManager downloadManager = context.mock(DownloadManager.class); final GuiCallback guiCallback = context.mock(GuiCallback.class); final File mockFile = context.mock(File.class); final DownloadException e = new DownloadException(new IOException(), mockFile); final File goodFile = new File("asdsadsad"); GlueActivityCallback activityCallback = new GlueActivityCallback(downloadManager); final MatchAndCopy<DownloadAction> actionCollector = new MatchAndCopy<DownloadAction>(DownloadAction.class); context.checking(new Expectations() {{ allowing(mockFile).length(); will(returnValue(10L)); allowing(mockFile).exists(); will(returnValue(true)); allowing(mockFile); one(downloadManager).downloadTorrent(with(same(mockFile)), with(any(File.class)), with(any(boolean.class))); one(downloadManager).downloadTorrent(with(same(mockFile)), with(any(File.class)), with(any(boolean.class))); will(throwException(e)); exactly(1).of(guiCallback).handleDownloadException(with(actionCollector), with(same(e)), with(any(boolean.class))); exactly(1).of(downloadManager).downloadTorrent(mockFile, goodFile, true); }}); // Torrent that does not exist activityCallback.handleTorrent(new File("f")); // Good file, torrent download good activityCallback.handleTorrent(mockFile); // Set callback to handle pending exception activityCallback.setGuiCallback(guiCallback); // Good file, torrent download returns exception activityCallback.handleTorrent(mockFile); // Try to redownload through the DownloadAction provided by // DownloadException handling actionCollector.getLastMatch().download(goodFile, true); context.assertIsSatisfied(); } /** * Tests the magnet handler function and make sure it appropriately delegates the * magnets to the guiCallback. */ public void testHandleMagnets() { Mockery context = new Mockery() {{ setImposteriser(ClassImposteriser.INSTANCE); }}; final GuiCallback guiCallback = context.mock(GuiCallback.class); final MagnetOptions[] magnets = new MagnetOptions[] { context.mock(MagnetOptions.class), context.mock(MagnetOptions.class), context.mock(MagnetOptions.class) }; GlueActivityCallback activityCallback = new GlueActivityCallback(null); context.checking(new Expectations() {{ exactly(3).of(guiCallback).handleMagnet(with(any(MagnetLink.class))); }}); activityCallback.handleMagnets(magnets); activityCallback.setGuiCallback(guiCallback); activityCallback.handleMagnets(magnets); context.assertIsSatisfied(); } /** * Tests the code relating to {@link QueryReplyListener}. Tests adding and removing the listeners * and instances that fire them. */ public void testQueryReplyListeners() { Mockery context = new Mockery(); final QueryReplyListener listener1 = context.mock(QueryReplyListener.class); final byte[] guid1 = new byte[] {'x','x','x',1,2,3,4,5,6,9,'n',10,'x','x','x','x'}; final QueryReplyListener listener2a = context.mock(QueryReplyListener.class); final QueryReplyListener listener2b = context.mock(QueryReplyListener.class); final byte[] guid2 = new byte[] {'x','x','x','x',1,2,3,4,5,6,9,'n',10,'x','x','x'}; final RemoteFileDesc rfd = context.mock(RemoteFileDesc.class); final QueryReply queryReply1 = context.mock(QueryReply.class); final QueryReply queryReply2 = context.mock(QueryReply.class); final Set<? extends IpPort> locs = new HashSet<IpPort>(); GlueActivityCallback activityCallback = new GlueActivityCallback(null); context.checking(new Expectations() {{ allowing(queryReply1).getGUID(); will(returnValue(guid1)); allowing(queryReply2).getGUID(); will(returnValue(guid2)); exactly(1).of(listener1).handleQueryReply(rfd, queryReply1, locs); exactly(1).of(listener2a).handleQueryReply(rfd, queryReply2, locs); exactly(2).of(listener2b).handleQueryReply(rfd, queryReply2, locs); }}); activityCallback.addQueryReplyListener(guid1, listener1); assertFalse(activityCallback.isQueryAlive(new GUID(guid2))); assertTrue(activityCallback.isQueryAlive(new GUID(guid1))); activityCallback.addQueryReplyListener(guid2, listener2a); activityCallback.addQueryReplyListener(guid2, listener2b); activityCallback.handleQueryResult(rfd, queryReply1, locs); activityCallback.handleQueryResult(rfd, queryReply2, locs); activityCallback.removeQueryReplyListener(guid1, listener1); activityCallback.removeQueryReplyListener(guid1, listener1); activityCallback.removeQueryReplyListener(guid2, listener2a); activityCallback.handleQueryResult(rfd, queryReply1, locs); activityCallback.handleQueryResult(rfd, queryReply2, locs); context.assertIsSatisfied(); } /** * Tests adding and removing {@link IncomingSearchListener} instances along with firing them. */ public void testIncomingSearchListeners() { Mockery context = new Mockery(); final IncomingSearchListener listener1 = context.mock(IncomingSearchListener.class); final IncomingSearchListener listener2 = context.mock(IncomingSearchListener.class); final QueryRequest queryRequest1 = context.mock(QueryRequest.class); final QueryRequest queryRequest2 = context.mock(QueryRequest.class); GlueActivityCallback activityCallback = new GlueActivityCallback(null); context.checking(new Expectations() {{ allowing(queryRequest1).getQuery(); will(returnValue("querty")); allowing(queryRequest2).getQuery(); will(returnValue("dvoraq")); exactly(2).of(listener1).handleQueryString("querty"); exactly(2).of(listener1).handleQueryString("dvoraq"); exactly(1).of(listener2).handleQueryString("querty"); exactly(1).of(listener2).handleQueryString("dvoraq"); }}); // Add two listeners activityCallback.addIncomingSearchListener(listener1); activityCallback.addIncomingSearchListener(listener2); // Pass two quieres in to be handled - picked up by the two listeners activityCallback.handleQuery(queryRequest1, "address-that-is-ignored", 555); activityCallback.handleQuery(queryRequest2, "address-that-is-ignored", 555); // Remove a listener and pass in the quieres again - make sure they are only caught // by the remaining listener activityCallback.removeIncomingSearchListener(listener2); activityCallback.handleQuery(queryRequest1, "address-that-is-ignored-again", 555); activityCallback.handleQuery(queryRequest2, "address-that-is-ignored-again", 555); // Remove the last listener - make sure further queries activityCallback.removeIncomingSearchListener(listener1); activityCallback.handleQuery(queryRequest1, "address-that-is-ignored-again", 555); activityCallback.handleQuery(queryRequest2, "address-that-is-ignored-again", 555); context.assertIsSatisfied(); } /** * Test the promptAboutCurruptDownload() method. The method does not actually * do any prompting, it just stops the downloader. Test will confirm that the * correct call is made, if the method is changed in the future then this * test will need updating. */ public void testPromptAboutCorruptDownload() { Mockery context = new Mockery(); final Downloader downloader = context.mock(Downloader.class); GlueActivityCallback activityCallback = new GlueActivityCallback(null); context.checking(new Expectations() {{ exactly(1).of(downloader).discardCorruptDownload(with(any(boolean.class))); }}); activityCallback.promptAboutCorruptDownload(downloader); context.assertIsSatisfied(); } /** * Tests the translate method. Ensures that it returns the untranslated string if there * is no {@link GuiCallback} instance otherwise delegate to it for the translation. */ public void testTranslate() { Mockery context = new Mockery(); final GuiCallback callback = context.mock(GuiCallback.class); GlueActivityCallback activityCallback = new GlueActivityCallback(null); context.checking(new Expectations() {{ exactly(1).of(callback).translate("hello"); will(returnValue("goodbye")); }}); assertEquals("hello", activityCallback.translate("hello")); activityCallback.setGuiCallback(callback); assertEquals("goodbye", activityCallback.translate("hello")); context.assertIsSatisfied(); } /** * This method currently does nothing. Call it anyways, when it starts doing something * this test will fail and should be updated accordingly. */ public void testHandleSharedFileUpdate() { GlueActivityCallback activityCallback = new GlueActivityCallback(null); activityCallback.handleSharedFileUpdate(null); } /** * Tests the {@link GlueActivityCallback#installationCurrupted()} and ensures it links * properly with {@link MessageService}. */ public void testIntallationCorrupted() { Mockery context = new Mockery(); final MessageCallback messageCallback = context.mock(MessageCallback.class); MessageCallback originalMessageCallback = MessageService.getCallback(); MessageService.setCallback(messageCallback); GlueActivityCallback activityCallback = new GlueActivityCallback(null); context.checking(new Expectations() {{ exactly(1).of(messageCallback).showError(with(any(String.class))); }}); activityCallback.installationCorrupted(); MessageService.setCallback(originalMessageCallback); context.assertIsSatisfied(); } public void testPromptTorrentUploadCancel() { Mockery context = new Mockery() {{ setImposteriser(ClassImposteriser.INSTANCE); }}; final GuiCallback guiCallbackYes = context.mock(GuiCallback.class); final GuiCallback guiCallbackNo = context.mock(GuiCallback.class); final Torrent torrentNotComplete = context.mock(Torrent.class); final Torrent torrentInactive = context.mock(Torrent.class); final Torrent torrentLowRatio = context.mock(Torrent.class); final Torrent torrentHighRatio = context.mock(Torrent.class); final GlueActivityCallback activityCallback = new GlueActivityCallback(null); context.checking(new Expectations() {{ allowing(guiCallbackYes).promptUserQuestion(with(any(String.class))); will(returnValue(true)); allowing(guiCallbackNo).promptUserQuestion(with(any(String.class))); will(returnValue(false)); allowing(torrentInactive).isStarted(); will(returnValue(false)); allowing(torrentNotComplete).isFinished(); will(returnValue(false)); allowing(torrentNotComplete).isStarted(); will(returnValue(true)); allowing(torrentLowRatio).getSeedRatio(); will(returnValue(.5f)); allowing(torrentLowRatio).isFinished(); will(returnValue(true)); allowing(torrentLowRatio).isStarted(); will(returnValue(true)); allowing(torrentHighRatio).getSeedRatio(); will(returnValue(1.5f)); allowing(torrentHighRatio).isFinished(); will(returnValue(true)); allowing(torrentHighRatio).isStarted(); will(returnValue(true)); }}); // Selecting yes with a torrent in progress should stop. activityCallback.setGuiCallback(guiCallbackYes); assertTrue(activityCallback.promptTorrentUploadCancel(torrentNotComplete)); // Selecting no with torrent not complete should not result in stop. activityCallback.setGuiCallback(guiCallbackNo); assertFalse(activityCallback.promptTorrentUploadCancel(torrentNotComplete)); // Ensure an inactive torrent will not be stopped. activityCallback.setGuiCallback(guiCallbackYes); assertFalse(activityCallback.promptTorrentUploadCancel(torrentInactive)); // Selecting yes with a torrent with a low ratio should stop. activityCallback.setGuiCallback(guiCallbackYes); assertTrue(activityCallback.promptTorrentUploadCancel(torrentLowRatio)); // Selecting no with torrent and a low ratio should not result in stop. activityCallback.setGuiCallback(guiCallbackNo); assertFalse(activityCallback.promptTorrentUploadCancel(torrentLowRatio)); // The user should not be prompted if there is a high ratio the torrent should be stopped activityCallback.setGuiCallback(guiCallbackNo); assertTrue(activityCallback.promptTorrentUploadCancel(torrentHighRatio)); context.assertIsSatisfied(); } }