package com.limegroup.gnutella.downloader; import java.util.NoSuchElementException; import java.util.Random; import com.limegroup.gnutella.util.BaseTestCase; import com.limegroup.gnutella.util.IntervalSet; import com.limegroup.gnutella.util.PrivilegedAccessor; public class RandomDownloadStrategyTest extends BaseTestCase { public RandomDownloadStrategyTest(String s) { super(s); } private long fileSize; private long blockSize; private TestPredeterminedRandom prng; private SelectionStrategy strategy; private IntervalSet availableBytes; public void setUp() throws Exception { fileSize = 12345; blockSize = 1234; prng = new TestPredeterminedRandom(); strategy = createRandomStrategy(fileSize, prng); availableBytes = IntervalSet.createSingletonSet(0, fileSize - 1); } public void testSequentialLocations() throws Exception { // Set up prng so our ideal location is always the 5th block boundary prng.setLongs(new long[] {5, 5, 5, 5}); // Set ints so that the strategy picks two chunks // after idealLocation (two odd ints) // followed by two chunks before idealLocation (two even ints) prng.setInts(new int[] {-1,487137, -2, 65538}); // Set the expected assignments Interval[] expectations = new Interval[4]; expectations[0] = new Interval(5 * blockSize, 6 * blockSize - 1); expectations[1] = new Interval(6 * blockSize, 7 * blockSize - 1); expectations[2] = new Interval(4 * blockSize, 5 * blockSize - 1); expectations[3] = new Interval(3 * blockSize, 4 * blockSize - 1); // Test testAssignments(strategy, availableBytes, fileSize, blockSize, expectations); } /** Tests that an interval spanning idealLocation gets split in two */ public void testSplitInterval() { // Break the neededBytes into 3 intervals availableBytes.delete(new Interval(fileSize/3)); availableBytes.delete(new Interval((2*fileSize)/3)); // Set things up to ask for a chunk bothe before and after // the middle of the file long[] idealLocations = new long[2]; idealLocations[0] = (fileSize/2)/blockSize; idealLocations[1] = idealLocations[0]; prng.setLongs(idealLocations); // Return something before the idealLocation, then // something after the idealLocation prng.setInts(new int[] {0,1}); Interval assignment = strategy.pickAssignment(availableBytes, availableBytes, blockSize); assertEquals("Failed to break Interval before idealLocation", new Interval((idealLocations[0]-1)*blockSize, idealLocations[0]*blockSize-1), assignment); assignment = strategy.pickAssignment(availableBytes, availableBytes, blockSize); assertEquals("Failed to break Interval after idealLocation", new Interval(idealLocations[1]*blockSize, (idealLocations[1]+1)*blockSize-1), assignment); } public void testSingleByteChunk() throws Exception { // Remove two single bytes availableBytes.delete(new Interval(5 * blockSize + 1)); availableBytes.delete(new Interval(6 * blockSize - 2)); // Set up the random number generator so that // it tries both above and below our single byte prng.setLongs(new long[] {5,6}); prng.setInts(new int[] {3,0}); //go forward, then go backward // We expect a single byte assignment Interval[] expectation = new Interval[2]; expectation[0] = new Interval(5 * blockSize); expectation[1] = new Interval(6 * blockSize-1); testAssignments(strategy, availableBytes, fileSize, blockSize, expectation); } /** Test the case where the availableBytes * contains the Interval we return and makes * sure a new Interval is not created if not necessary. */ public void testReturnOptimization() { availableBytes = IntervalSet.createSingletonSet(blockSize,2*blockSize-1); prng.setInt(1); prng.setLong(0); Interval assignment = strategy.pickAssignment(availableBytes, availableBytes, blockSize); assertTrue("Return optimization not working", availableBytes.getFirst() == assignment); } /** Test the case where the download is almost finished. * There's only a sliver left. */ public void testSliverDownload() { // Set ideal location before the sliver prng.setLong(1); availableBytes = IntervalSet.createSingletonSet(2475, 2483); Interval assignment = strategy.pickAssignment(availableBytes, availableBytes, blockSize); assertEquals("Only a sliver of the file left, and something other than the sliver"+ "was returned", availableBytes.getFirst(), assignment); // Same thing, except the ideal location is after the sliver prng.setLong(8); availableBytes = IntervalSet.createSingletonSet(2475,2483); assignment = strategy.pickAssignment(availableBytes, availableBytes, blockSize); assertEquals("Only a sliver of the file left, and something other than the sliver"+ "was returned", availableBytes.getFirst(), assignment); } /** * Test that various invalid inputs throw IllegalArgumentException. */ public void testInvalidInputs() { // Try an invalid block size try { strategy.pickAssignment(availableBytes, availableBytes, 0); fail("Failed to complain about invalid block size"); } catch (IllegalArgumentException e) { // Wohoo! Exception thrown... test passed... do nothing } IntervalSet badNeededBytes = null; try { // createSingletonSet might throw its own IllegalArgumentException // so create it outside of the try-catch badNeededBytes = IntervalSet.createSingletonSet(-5,10); // Try telling the strategy that we need some bytes // before the beginning of the file try { strategy.pickAssignment(availableBytes, badNeededBytes, blockSize); fail("Failed to complain about negative Intervals in neededBytes"); } catch (IllegalArgumentException e) { // Wohoo! Exception thrown... test passed... do nothing } } catch (IllegalArgumentException e) { // Wohoo! Exception thrown... test passed... do nothing } badNeededBytes = IntervalSet.createSingletonSet(fileSize,fileSize); // Try telling the strategy that we need a byte after the end // of the file try { strategy.pickAssignment(availableBytes, badNeededBytes, blockSize); fail("Failed to complain about neededBytes extending past the end of the file"); } catch (IllegalArgumentException e) { // Wohoo! Exception thrown... test passed... do nothing } } /** Make sure we throw NoSuchElement exception if there is nothing to * download. */ public void testNoAvailableBytes() { availableBytes = new IntervalSet(); prng.setInt(15); prng.setLong(2); try { strategy.pickAssignment(availableBytes, IntervalSet.createSingletonSet(0,fileSize-1), blockSize); } catch (NoSuchElementException e) { // Wohoo! Exception thrown... test passed return; } assertTrue("Failed to complain about no available bytes", false); } /////////////// helper methods //////////////// /** * A helper method that simulates chosing blocks to download and removes * them from availableBytes. * * @param strategy * @param availableBytes * @param fileSize * @param blockSize * @param blockCount * @param expectedAssignments */ private void testAssignments(SelectionStrategy strategy, IntervalSet availableBytes, long fileSize, long blockSize, Interval[] expectedAssignments) { for (int i = 0; i < expectedAssignments.length; i++) { Interval assignment = strategy.pickAssignment(availableBytes, IntervalSet.createSingletonSet(0, fileSize - 1), blockSize); availableBytes.delete(assignment); assertEquals("Wrong assignment for chunk #" + i, expectedAssignments[i], assignment); } } private SelectionStrategy createRandomStrategy(long fileSize, Random rng) throws IllegalAccessException, NoSuchFieldException { RandomDownloadStrategy rds = new RandomDownloadStrategy(fileSize); PrivilegedAccessor.setValue(rds, "pseudoRandom", rng); return rds; } }