package com.limegroup.gnutella.lws.server; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import junit.framework.Test; import junit.textui.TestRunner; import com.limegroup.gnutella.DownloadManager; import com.limegroup.gnutella.DownloadServices; import com.limegroup.gnutella.downloader.CoreDownloader; import com.limegroup.gnutella.downloader.LWSIntegrationServices; import com.limegroup.gnutella.downloader.StoreDownloader; /** * Tests the <code>Download</code> command. */ public class LWSDownloadTest extends AbstractCommunicationSupportWithNoLocalServer { private final LWSDownloadTestConstants constants = new LWSDownloadTestConstants(); /** * Set up the little web server. */ @Override protected void afterSetup() { super.afterSetup(); // Make sure we are downloading from the right spot LWSIntegrationServices services = getInstance(LWSIntegrationServices.class); services.setDownloadPrefix(constants.HOST + ":" + constants.PORT); // Start up the server server = new SimpleWebServer(constants); server.start(); // Reset and authenticate doAuthenticate(); } /** * Tear down the little web server. */ @Override protected void afterTearDown() { super.afterTearDown(); server.stop(); sleep(2000); } public LWSDownloadTest(String s) { super(s); } public static Test suite() { return buildTestSuite(LWSDownloadTest.class); } public static void main(String[] args) { TestRunner.run(suite()); } // ------------------------------------------------------------------ // Tests // ------------------------------------------------------------------ public void testSimple() { doDownloadTest(null,null,true,""); } public void testTwoDownloadsAtOnce() { final boolean[] done = {false,false}; final MutableString id1 = new MutableString(); final MutableString id2 = new MutableString(); Thread download1 = new Thread(new Runnable() { public void run() { doDownloadTest(id1,null,true,""); done[0] = true; } }); download1.start(); Thread download2 = new Thread(new Runnable() { public void run() { doDownloadTest(id2,null,true,""); done[1] = true; } }); download2.start(); // IDs should be the same assertEquals("IDs should be the same", id1.get(), id2.get()); DownloadServices downloadServices = getInstance(DownloadServices.class); boolean haveSeenOneDownloader = false; while (!done[0]) { if (!haveSeenOneDownloader) { haveSeenOneDownloader = downloadServices.getNumDownloads() == 1; } int num = downloadServices.getNumDownloads(); assertTrue("getNumDownloads should be 0 or 1, not " + num, num == 0 || num == 1); } assertTrue("Should have seen one downloader", haveSeenOneDownloader); try { download1.join(); } catch (InterruptedException e) { LOG.error(e); } try { download2.join(); } catch (InterruptedException e) { LOG.error(e); } } /* * These two tests were made non-tests because we don't ever actually pause * downloads from the webpage, and these tests, while pass most of the time, * occasionally fail as false negatives. In the event we start pausing and * resuming downloads from the webpage we'll reinstate them. */ public void omitTestPauseResumeStopAll() { final boolean[] done = {false,false,false}; final MutableString[] mids = {new MutableString(),new MutableString(),new MutableString()}; Thread download1 = new Thread(new Runnable() { public void run() { doDownloadTest(mids[0],null,false,""); done[0] = true; } }); Thread download2 = new Thread(new Runnable() { public void run() { doDownloadTest(mids[1],null,false,"1"); done[1] = true; } }); Thread download3 = new Thread(new Runnable() { public void run() { doDownloadTest(mids[2],null,false,"2"); done[2] = true; } }); download1.start(); download2.start(); download3.start(); sleep(1000); boolean isActive; boolean isPaused; isActive = false; for (StoreDownloader storeDownloader : getStoreDownloaders()) { if (storeDownloader.isActive()) { isActive = true; break; } } assertTrue("One should be active", isActive); final Map<String, String> args = new HashMap<String, String>(); // Now pause the download sendCommandToClientAndWait("PauseAllDownloads", args); isPaused = false; for (StoreDownloader storeDownloader : getStoreDownloaders()) { if (storeDownloader.isPaused()) { isPaused = true; break; } } assertTrue("One should be paused", isPaused); // Now resume the download sendCommandToClientAndWait("ResumeAllDownloads", args); isActive = false; for (StoreDownloader storeDownloader : getStoreDownloaders()) { if (storeDownloader.isActive()) { isActive = true; break; } } assertTrue("One should be active", isActive); // Now pause the download, again sendCommandToClientAndWait("PauseAllDownloads", args); isPaused = false; for (StoreDownloader storeDownloader : getStoreDownloaders()) { if (storeDownloader.isPaused()) { isPaused = true; break; } } // Now resume the download, again sendCommandToClientAndWait("ResumeAllDownloads", args); isActive = false; for (StoreDownloader storeDownloader : getStoreDownloaders()) { if (storeDownloader.isActive()) { isActive = true; break; } } assertTrue("One should be active", isActive); // Now stop the download sendCommandToClientAndWait("StopAllDownloads", args); assertTrue("Should be empty", getStoreDownloaders(false).isEmpty()); try { download1.join(); } catch (InterruptedException e) { LOG.error(e); } try { download2.join(); } catch (InterruptedException e) { LOG.error(e); } try { download3.join(); } catch (InterruptedException e) { LOG.error(e); } } public void omitTestPauseResumeStop() { final boolean[] done = {false}; final MutableString mid = new MutableString(); Thread download1 = new Thread(new Runnable() { public void run() { doDownloadTest(mid,null,false,""); done[0] = true; } }); download1.start(); sleep(1000); StoreDownloader storeDownloader = getStoreDownloader(); assertTrue("Should be active", storeDownloader.isActive()); String id = mid.get().split(" ")[0]; final Map<String, String> args = new HashMap<String, String>(); args.put("id", id); // Now pause the download sendCommandToClientAndWait("PauseDownload", args); assertTrue("Should be paused", storeDownloader.isPaused()); // Now resume the download sendCommandToClientAndWait("ResumeDownload", args); assertTrue("Should be resumed", storeDownloader.isActive() || storeDownloader.isCompleted()); assertFalse("Should not be paused", storeDownloader.isPaused() || storeDownloader.isCompleted()); // Now pause the download, again sendCommandToClientAndWait("PauseDownload", args); assertTrue("Should be paused", storeDownloader.isPaused() || storeDownloader.isCompleted()); // Now resume the download, again sendCommandToClientAndWait("ResumeDownload", args); assertTrue("Should be resumed", storeDownloader.isActive() || storeDownloader.isCompleted()); assertFalse("Should not be paused", storeDownloader.isPaused() || storeDownloader.isCompleted()); // Now stop the download sendCommandToClientAndWait("StopDownload", args); assertTrue("Should be empty",getStoreDownloaders(false).isEmpty()); try { download1.join(); } catch (InterruptedException e) { LOG.error(e); } } // ------------------------------------------------------------------ // Support // ------------------------------------------------------------------ private SimpleWebServer server; private StoreDownloader getStoreDownloader() { StoreDownloader storeDownloader = getStoreDownloaders().get(0); assertNotNull(storeDownloader); return storeDownloader; } private List<StoreDownloader> getStoreDownloaders() { return getStoreDownloaders(true); } private List<StoreDownloader> getStoreDownloaders(boolean checkForEmpty) { DownloadManager man = getInstance(DownloadManager.class); List<StoreDownloader> res = new ArrayList<StoreDownloader>(); for (CoreDownloader coreDownloader : man.getAllDownloaders()) { if (coreDownloader instanceof StoreDownloader) { res.add((StoreDownloader)coreDownloader); } } if (checkForEmpty) { assertFalse("Should not be empty", res.isEmpty()); } return res; } private final static class MutableString { String s; public void set(String s) {this.s = s;} public String get() {return s;} @Override public String toString() {return get();} } /** * @param inId will hold the id of the downloader -- can be null * @param r run this is the loop -- can be null * @param checkComplete whether we check that the download finished at the end * @param fudge fudge factor to add to the url, file, and ID, so we can have multiple downloaders; */ private void doDownloadTest(MutableString inId, Runnable r, boolean checkComplete, String fudge) { long length = constants.LENGTH; Map<String, String> args = new HashMap<String, String>(); String id = constants.ID + fudge; args.put("url", constants.URL + fudge); args.put("file", (!fudge.equals("") ? "/" + fudge : "") + constants.FILE); args.put("id", id); args.put("length", String.valueOf(length)); // Send the client a command to start the download String downloaderIDAndProgressBarID = sendCommandToClient("Download", args); if (inId != null) { String[] parts = downloaderIDAndProgressBarID.split(" "); assertEquals("Should have a downloader ID and progress bar ID", 2, parts.length); boolean found = false; String downloaderID = parts[0]; for (StoreDownloader storeDownloader : getStoreDownloaders()) { if (downloaderID.equals(String.valueOf(System.identityHashCode(storeDownloader)))) { found = true; break; } } assertTrue("Should have found a store downloader for id " + downloaderID, found); assertEquals("Should be the same as given", id, parts[1]); inId.set(downloaderIDAndProgressBarID); } File savedFile = new File(_storeDir, args.get("file")); // Do a busy wait until we've run out of time or the file we wanted // was downloaded and is the size we wanted for (long toStop = System.currentTimeMillis() + constants.DOWNLOAD_WAIT_TIME; System.currentTimeMillis() < toStop;) { if (server.getBytesWritten() == length && savedFile.length() == length) { break; } if (r != null) r.run(); sleep(1000); } // // We may have stopped it // if (checkComplete) { assertEquals(savedFile.getAbsolutePath(), length, savedFile.length()); assertEquals(String.valueOf(savedFile.length()), length, server.getBytesWritten()); } } private void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException ignore) { // ignore } } /** * This is used, because there is a slight race condition in sending * commands. But 1 second is plenty enough time, and if it takes longer, * that is a problem as well. */ private void sleepForASecond() { sleep(1000); } /** * Sends a command to the client and then waits; returns the result. * * @return the result of sending the command to the client. */ private String sendCommandToClientAndWait(String cmd, Map<String,String> args) { sleepForASecond(); String res = sendCommandToClient(cmd, args); sleepForASecond(); return res; } }