package com.limegroup.gnutella.downloader;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import junit.framework.Test;
import org.limewire.collection.Range;
import org.limewire.core.settings.SharingSettings;
import org.limewire.io.ConnectableImpl;
import org.limewire.util.PrivilegedAccessor;
import com.google.inject.Injector;
import com.limegroup.gnutella.LimeTestUtils;
import com.limegroup.gnutella.RemoteFileDesc;
import com.limegroup.gnutella.URN;
import com.limegroup.gnutella.library.FileDesc;
import com.limegroup.gnutella.library.FileManager;
import com.limegroup.gnutella.util.LimeTestCase;
public class IncompleteFileManagerTest extends LimeTestCase {
private IncompleteFileManager incompleteFileManager;
private RemoteFileDesc rfd1, rfd2;
private FileManager fm;
private VerifyingFileFactory verifyingFileFactory;
private Injector injector;
public IncompleteFileManagerTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(IncompleteFileManagerTest.class);
}
@Override
public void setUp() {
injector = LimeTestUtils.createInjector();
fm = injector.getInstance(FileManager.class);
verifyingFileFactory = injector.getInstance(VerifyingFileFactory.class);
incompleteFileManager = injector.getInstance(IncompleteFileManager.class);
}
/** @param urn a SHA1 urn, or null */
public RemoteFileDesc newRFD(String name, int size, String urn) {
try {
Set<URN> urns=new HashSet<URN>(1);
if (urn!=null)
urns.add(URN.createSHA1Urn(urn));
return injector.getInstance(RemoteFileDescFactory.class).createRemoteFileDesc(new ConnectableImpl("18.239.0.144", 6346, false), 13l, name, size, new byte[16],
56, 4, true, null, urns, false, "", -1);
} catch (IOException e) {
fail("Invalid URN", e);
return null;
}
}
/////////////////////////////////////////////////////////////
public void testLegacy() throws Throwable {
File file=new File(getSaveDirectory(), "T-748-test.txt");
Iterator iter=null;
VerifyingFile vf = verifyingFileFactory.createVerifyingFile(748);
//0 blocks
assertNull(incompleteFileManager.getEntry(file));
assertEquals(0, incompleteFileManager.getBlockSize(file));
//1 block
vf.addInterval(Range.createRange(0,10));
incompleteFileManager.addEntry(file,vf, true);
assertEquals(11, incompleteFileManager.getBlockSize(file));//full inclusive now
iter=incompleteFileManager.getEntry(file).getBlocks().iterator();
assertEquals(Range.createRange(0, 10), iter.next());
assertTrue(! iter.hasNext());
SharingSettings.INCOMPLETE_PURGE_TIME.setValue(26);
File young=new FakeTimedFile(25);
File old=new FakeTimedFile(27);
assertTrue(!isOld(young));
assertTrue(isOld(old));
}
public void testTempName() throws Throwable {
assertEquals("T-748-abc def",
tempName("abc def", 748, 0));
assertEquals("T-748-abc def (2)",
tempName("abc def", 748, 2));
assertEquals("T-748-abc.txt",
tempName("abc.txt", 748, 1));
assertEquals("T-748-abc (2).txt",
tempName("abc.txt", 748, 2));
assertEquals("T-748-.txt",
tempName(".txt", 748, 1));
assertEquals("T-748- (2).txt",
tempName(".txt", 748, 2));
}
/** Different name or size ==> different temp file */
public void testGetFile_DifferentSize() throws Throwable {
rfd1=newRFD("some file name", 1839, null);
rfd2=newRFD("some file name", 1223, null);
assertTrue(! IncompleteFileManager.same(rfd1, rfd2));
File tmp1=incompleteFileManager.getFile(rfd1);
File tmp2=incompleteFileManager.getFile(rfd2);
assertNotEquals(tmp2, tmp1);
}
/**
* You should be able to resume to a file with same hash but different name.
*/
public void testGetFile_DifferentNameSameHash() throws Throwable {
rfd1=newRFD("some file name", 1839,
"urn:sha1:GLSTHIPQGSSZTS5FJUPAKPZWUGYQYPFB");
rfd2=newRFD("another file name", 1839,
"urn:sha1:GLSTHIPQGSSZTS5FJUPAKPZWUGYQYPFB");
assertTrue(IncompleteFileManager.same(rfd1, rfd2));
File tmp1=incompleteFileManager.getFile(rfd1);
File tmp2=incompleteFileManager.getFile(rfd2);
assertEquals(tmp1, tmp2);
}
/**
* You should NOT be able to resume to file w/ same name but different hash.
*/
public void testGetFile_SameNameDifferentHash() throws Throwable {
rfd1=newRFD("some file name", 1839,
"urn:sha1:GLSTHIPQGSSZTS5FJUPAKPZWUGYQYPFB");
rfd2=newRFD("some file name", 1839,
"urn:sha1:LSTHIPQGSGSZTS5FJPAKPZWUGYQYPFBU");
assertTrue(! IncompleteFileManager.same(rfd1, rfd2));
File tmp1=incompleteFileManager.getFile(rfd1);
File tmp2=incompleteFileManager.getFile(rfd2);
assertNotEquals(tmp2, tmp1);
}
/** Risky resumes are allowed: no hashes */
public void testGetFile_NoHash() throws Throwable {
rfd1=newRFD("some file name", 1839, null);
rfd2=newRFD("some file name", 1839,
"urn:sha1:GLSTHIPQGSSZTS5FJUPAKPZWUGYQYPFB");
assertTrue(IncompleteFileManager.same(rfd1, rfd2));
File tmp1=incompleteFileManager.getFile(rfd1);
File tmp2=incompleteFileManager.getFile(rfd2);
assertEquals(tmp1, tmp2);
}
/** Checks that removeEntry clears blocks AND hashes. */
public void testRemoveEntry() throws Throwable {
//Populate IFM with a hash.
rfd1=newRFD("some file name", 1839,
"urn:sha1:GLSTHIPQGSSZTS5FJUPAKPZWUGYQYPFB");
File tmp1=incompleteFileManager.getFile(rfd1);
VerifyingFile vf=verifyingFileFactory.createVerifyingFile(1839);
incompleteFileManager.addEntry(tmp1, vf, true);
//After deleting entry there should be no more blocks...
incompleteFileManager.removeEntry(tmp1);
assertNull(incompleteFileManager.getEntry(tmp1));
//...and same temp file should be used for different hash. [sic]
rfd2=newRFD("some file name", 1839,
"urn:sha1:LSTHIPQGSGSZTS5FJPAKPZWUGYQYPFBU");
File tmp2=incompleteFileManager.getFile(rfd2);
assertEquals(tmp1, tmp2);
assertEquals(tmp2, incompleteFileManager.getFile(newRFD("some file name", 1839, null)));
}
/**
* Checks that addEntry & removeEntry notify the FileManager of the
* added / removed incomplete file.
*/
public void testFileManagerIsNotified() throws Exception {
assertEquals(0, fm.getIncompleteFileList().size()); // begin with 0 shared.
//Populate IFM with a hash.
rfd1=newRFD("some file name", 1839,
"urn:sha1:GLSTHIPQGSSZTS5FJUPAKPZWUGYQYPFB");
File tmp1=incompleteFileManager.getFile(rfd1);
VerifyingFile vf=verifyingFileFactory.createVerifyingFile(1839);
incompleteFileManager.addEntry(tmp1, vf, true);
assertEquals(1, fm.getIncompleteFileList().size()); // 1 added.
// make sure it's associated with a URN.
URN urn = URN.createSHA1Urn(
"urn:sha1:GLSTHIPQGSSZTS5FJUPAKPZWUGYQYPFB");
FileDesc fd = fm.getIncompleteFileList().getFileDesc(urn);
assertNotNull(urn);
assertInstanceof(com.limegroup.gnutella.library.IncompleteFileDesc.class, fd);
incompleteFileManager.removeEntry(tmp1);
assertEquals(0, fm.getIncompleteFileList().size()); // back to 0 shared.
}
public void testCompletedHash_NotFound() throws Throwable{
File tmp2=new File("T-1839-some file name");
assertNull(incompleteFileManager.getCompletedHash(tmp2));
}
public void testCompletedHash_Found() throws Throwable {
rfd1=newRFD("some file name", 1839,
"urn:sha1:GLSTHIPQGSSZTS5FJUPAKPZWUGYQYPFB");
File tmp1=incompleteFileManager.getFile(rfd1);
try {
URN urn=URN.createSHA1Urn(
"urn:sha1:GLSTHIPQGSSZTS5FJUPAKPZWUGYQYPFB");
assertEquals(urn, incompleteFileManager.getCompletedHash(tmp1));
} catch (IOException e) {
fail("Couldn't make URN", e);
}
}
public void testCompletedName() throws Throwable {
File tmp1=new File("T-1839-some file.txt");
assertEquals("some file.txt", IncompleteFileManager.getCompletedName(tmp1));
}
public void testCompletedName_IllegalArgument() throws Throwable {
try {
IncompleteFileManager.getCompletedName(new File("no dash.txt"));
fail("Accepted bad file");
} catch (IllegalArgumentException pass) { }
try {
IncompleteFileManager.getCompletedName(new File("T-one dash.txt"));
fail("Accepted bad file");
} catch (IllegalArgumentException pass) { }
try {
IncompleteFileManager.getCompletedName(new File("T-123-"));
fail("Accepted bad file");
} catch (IllegalArgumentException pass) { }
}
public void testCompletedSize() throws Throwable {
File tmp1=new File("T-1839-some file.txt");
assertEquals(1839, IncompleteFileManager.getCompletedSize(tmp1));
}
public void testCompletedSize_IllegalArgument() throws Throwable {
try {
IncompleteFileManager.getCompletedSize(new File("no dash.txt"));
fail("Accepted bad file");
} catch (IllegalArgumentException pass) { }
try {
IncompleteFileManager.getCompletedSize(new File("T-one dash.txt"));
fail("Accepted bad file");
} catch (IllegalArgumentException pass) { }
try {
IncompleteFileManager.getCompletedSize(new File("T--no number.txt"));
fail("Accepted bad file");
} catch (IllegalArgumentException pass) { }
try {
IncompleteFileManager.getCompletedSize(new File("T-x-bad number.txt"));
fail("Accepted bad file");
} catch (IllegalArgumentException pass) { }
}
/** Tests that hash information is purged when calling purge(true). */
public void testPurgeHash_Yes() throws Throwable {
//These files have the same hash, but no blocks have been written.
RemoteFileDesc rfd1=newRFD("file name.txt", 1839,
"urn:sha1:GLSTHIPQGSSZTS5FJUPAKPZWUGYQYPFB");
RemoteFileDesc rfd1b=newRFD("other file.txt", 1839,
"urn:sha1:GLSTHIPQGSSZTS5FJUPAKPZWUGYQYPFB");
File file1=incompleteFileManager.getFile(rfd1);
file1.delete(); // getFile will create it, we don't want it created.
File file1b=incompleteFileManager.getFile(rfd1b);
file1b.delete();
assertEquals(file1, file1b);
//These files have the same hash, but blocks have been written to disk.
RemoteFileDesc rfd2=newRFD("another name.txt", 1839,
"urn:sha1:LSTHIPQGSSZGTS5FJUPAKPZWUGYQYPFB");
RemoteFileDesc rfd2b=newRFD("yet another file.txt", 1839,
"urn:sha1:LSTHIPQGSSZGTS5FJUPAKPZWUGYQYPFB");
File file2=incompleteFileManager.getFile(rfd2);
file2.delete();
File file2b=incompleteFileManager.getFile(rfd2b);
file2b.delete();
assertEquals(file2, file2b);
try {
file2.createNewFile();
} catch (IOException e) {
fail("Couldn't create "+file2, e);
}
//After purging, only hashes associated with files that exists remain.
List<File> emptyFiles = Collections.emptyList();
incompleteFileManager.initialPurge(emptyFiles);
File file1c=incompleteFileManager.getFile(rfd1b);
assertNotEquals(file1b, file1c);
File file2c=incompleteFileManager.getFile(rfd2b);
assertEquals(file2b ,file2c);
file2.delete();
}
/** Tests that hash information is not purged when calling purge(false). */
public void testPurgeHash_No() throws Throwable {
RemoteFileDesc rfd1=newRFD("file name.txt", 1839,
"urn:sha1:GLSTHIPQGSSZTS5FJUPAKPZWUGYQYPFB");
RemoteFileDesc rfd2=newRFD("other file.txt", 1839,
"urn:sha1:GLSTHIPQGSSZTS5FJUPAKPZWUGYQYPFB");
File file1=incompleteFileManager.getFile(rfd1);
File file2=incompleteFileManager.getFile(rfd2);
assertEquals(file1, file2);
incompleteFileManager.purge(); //Does nothing
File file2b=incompleteFileManager.getFile(rfd2);
assertEquals(file2, file2b);
}
private static String tempName(String s, int one, int two) throws Throwable {
try {
return (String)PrivilegedAccessor.invokeMethod(
IncompleteFileManager.class, "tempName",
new Object[] {s, new Long(one), new Integer(two)},
new Class[] {String.class, Long.TYPE, Integer.TYPE});
} catch(Exception e) {
if ( e.getCause() != null )
throw e.getCause();
else throw e;
}
}
private static boolean isOld(File f) throws Throwable {
try {
return ((Boolean)PrivilegedAccessor.invokeMethod(
IncompleteFileManager.class, "isOld",
new Object[] {f},
new Class[] {File.class} )).booleanValue();
} catch(Exception e) {
if ( e.getCause() != null )
throw e.getCause();
else throw e;
}
}
static class FakeTimedFile extends File {
private long days;
FakeTimedFile(int days) {
super("whatever.txt");
this.days=days;
}
@Override
public long lastModified() {
//30 days ago
return System.currentTimeMillis()-days*24l*60l*60l*1000l;
}
}
}