diff --git a/src/main/java/com/turn/ttorrent/common/Torrent.java b/src/main/java/com/turn/ttorrent/common/Torrent.java index 1f698e074..e468dc560 100644 --- a/src/main/java/com/turn/ttorrent/common/Torrent.java +++ b/src/main/java/com/turn/ttorrent/common/Torrent.java @@ -133,6 +133,7 @@ public TorrentFile(File file, long size) { * BitTorrent specification) and create a Torrent object from it. * * @param torrent The meta-info byte data. + * @param parent The parent directory or location of the torrent files. * @param seeder Whether we'll be seeding for this torrent or not. * @throws IOException When the info dictionary can't be read or * encoded and hashed back to create the torrent's SHA-1 hash. @@ -401,22 +402,14 @@ public boolean isSeeder() { return this.seeder; } - /** + /** * Save this torrent meta-info structure into a .torrent file. * - * @param file The file to write to. + * @param output The stream to write to. * @throws IOException If an I/O error occurs while writing the file. */ - public void save(File file) throws IOException { - FileOutputStream fOut = null; - try { - fOut = new FileOutputStream(file); - fOut.write(this.getEncoded()); - } finally { - if (fOut != null){ - fOut.close(); - } - } + public void save(OutputStream output) throws IOException { + output.write(this.getEncoded()); } public static byte[] hash(byte[] data) throws NoSuchAlgorithmException { @@ -599,7 +592,7 @@ public static Torrent create(File source, List> announceList, * considering we'll be a full initial seeder for it. *

* - * @param source The parent directory or location of the torrent files, + * @param parent The parent directory or location of the torrent files, * also used as the torrent's name. * @param files The files to add into this torrent. * @param announceList The announce URIs organized as tiers that will @@ -961,8 +954,8 @@ public static void main(String[] args) { torrent = Torrent.create(source, announceURI, creator); } - fos.write(torrent.getEncoded()); - } else { + torrent.save(fos); + } else { Torrent.load(new File(filenameValue), true); } } catch (Exception e) { diff --git a/src/main/java/com/turn/ttorrent/tracker/Tracker.java b/src/main/java/com/turn/ttorrent/tracker/Tracker.java index 94174697d..6ba05d8b1 100644 --- a/src/main/java/com/turn/ttorrent/tracker/Tracker.java +++ b/src/main/java/com/turn/ttorrent/tracker/Tracker.java @@ -25,9 +25,6 @@ import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.URL; -import java.security.NoSuchAlgorithmException; -import java.util.Collection; -import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; @@ -51,7 +48,7 @@ *

* The tracker usually listens on port 6969 (the standard BitTorrent tracker * port). Torrents must be registered directly to this tracker with the - * {@link #announce(Torrent torrent)} method. + * {@link #announce(TrackedTorrent torrent)} method. *

* * @author mpetazzoni @@ -183,15 +180,7 @@ public void stop() { } } - public ConcurrentMap getTorrentsMap() { - return torrents; - } - - public Collection getTrackedTorrents(){ - return torrents.values(); - } - - /** + /** * Announce a new torrent on this tracker. * *

@@ -206,7 +195,7 @@ public Collection getTrackedTorrents(){ * different from the supplied Torrent object if the tracker already * contained a torrent with the same hash. */ - public synchronized TrackedTorrent announce(Torrent torrent) throws IOException, NoSuchAlgorithmException { + public synchronized TrackedTorrent announce(TrackedTorrent torrent) { TrackedTorrent existing = this.torrents.get(torrent.getHexInfoHash()); if (existing != null) { @@ -215,16 +204,10 @@ public synchronized TrackedTorrent announce(Torrent torrent) throws IOException, return existing; } - final TrackedTorrent result; - if (torrent instanceof TrackedTorrent) { - result = (TrackedTorrent) torrent; - } else { - result = new TrackedTorrent(torrent); - } - this.torrents.put(torrent.getHexInfoHash(), result); + this.torrents.put(torrent.getHexInfoHash(), torrent); logger.info("Registered new torrent for '{}' with hash {}.", torrent.getName(), torrent.getHexInfoHash()); - return result; + return torrent; } /** diff --git a/src/test/java/com/turn/ttorrent/FileUtil.java b/src/test/java/com/turn/ttorrent/FileUtil.java deleted file mode 100644 index 3abff5e1d..000000000 --- a/src/test/java/com/turn/ttorrent/FileUtil.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.turn.ttorrent; - -import java.io.Closeable; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; - -public class FileUtil { - - public static File getTempDirectory() { - return new File(System.getProperty("java.io.tmpdir")); - } - - public static void delete(File file) { - if (file.isDirectory()) { - File[] files = file.listFiles(); - for (File f: files) { - delete(f); - } - } - deleteFile(file); - } - - private static boolean deleteFile(File file) { - if (!file.exists()) return false; - for (int i=0; i<10; i++) { - if (file.delete()) return true; - try { - Thread.sleep(1); - } catch (InterruptedException e) { - // - } - } - return false; - } - - public static void writeFile(File file, String content) throws IOException { - FileWriter fw = null; - try { - fw = new FileWriter(file); - fw.write(content); - } finally { - close(fw); - } - } - - public static void close(Closeable closeable) { - if (closeable != null) { - try { - closeable.close(); - } catch (IOException e) { - // - } - } - } -} diff --git a/src/test/java/com/turn/ttorrent/TempFiles.java b/src/test/java/com/turn/ttorrent/TempFiles.java deleted file mode 100644 index bf0953d1a..000000000 --- a/src/test/java/com/turn/ttorrent/TempFiles.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.turn.ttorrent; - -import java.io.*; -import java.util.*; - -public class TempFiles { - private static final File ourCurrentTempDir = FileUtil.getTempDirectory(); - private final File myCurrentTempDir; - - private static Random ourRandom; - - static { - ourRandom = new Random(); - ourRandom.setSeed(System.currentTimeMillis()); - } - - private final List myFilesToDelete = new ArrayList(); - private final Thread myShutdownHook; - private volatile boolean myInsideShutdownHook; - - public TempFiles() { - myCurrentTempDir = ourCurrentTempDir; - if (!myCurrentTempDir.isDirectory()) { - - throw new IllegalStateException("Temp directory is not a directory, was deleted by some process: " + myCurrentTempDir.getAbsolutePath() + - "\njava.io.tmpdir: " + FileUtil.getTempDirectory()); - } - - myShutdownHook = new Thread(new Runnable() { - public void run() { - myInsideShutdownHook = true; - cleanup(); - } - }); - Runtime.getRuntime().addShutdownHook(myShutdownHook); - } - - private File doCreateTempDir(String prefix, String suffix) throws IOException { - prefix = prefix == null ? "" : prefix; - suffix = suffix == null ? ".tmp" : suffix; - - do { - int count = ourRandom.nextInt(); - final File f = new File(myCurrentTempDir, prefix + count + suffix); - if (!f.exists() && f.mkdirs()) { - return f.getCanonicalFile(); - } - } while (true); - - } - private File doCreateTempFile(String prefix, String suffix) throws IOException { - final File file = doCreateTempDir(prefix, suffix); - file.delete(); - file.createNewFile(); - return file; - } - - public final File createTempFile(String content) throws IOException { - File tempFile = createTempFile(); - FileUtil.writeFile(tempFile, content); - return tempFile; - } - - public final File createTempFile() throws IOException { - File tempFile = doCreateTempFile("test", null); - registerAsTempFile(tempFile); - return tempFile; - } - - public void registerAsTempFile(final File tempFile) { - myFilesToDelete.add(tempFile); - } - - public final File createTempFile(int size) throws IOException { - File tempFile = createTempFile(); - int bufLen = Math.min(8 * 1024, size); - if (bufLen == 0) return tempFile; - final OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile)); - try { - byte[] buf = new byte[bufLen]; - for (int i=0; i < buf.length; i++) { - buf[i] = (byte)Math.round(Math.random()*128); - } - - int numWritten = 0; - for (int i=0; i numWritten) { - fos.write(buf, 0, size - numWritten); - } - } finally { - fos.close(); - } - - return tempFile; - } - - /** - * Returns a File object for created temp directory. - * Also stores the value into this object accessed with {@link #getCurrentTempDir()} - * - * @return a File object for created temp directory - * @throws IOException if directory creation fails. - */ - public final File createTempDir() throws IOException { - File f = doCreateTempDir("test", ""); - registerAsTempFile(f); - return f; - } - - /** - * Returns the current directory used by the test or null if no test is running or no directory is created yet. - * - * @return see above - */ - public File getCurrentTempDir() { - return myCurrentTempDir; - } - - public void cleanup() { - try { - for (File file : myFilesToDelete) { - FileUtil.delete(file); - } - - myFilesToDelete.clear(); - } finally { - if (!myInsideShutdownHook) { - Runtime.getRuntime().removeShutdownHook(myShutdownHook); - } - } - } -} \ No newline at end of file diff --git a/src/test/java/com/turn/ttorrent/WaitFor.java b/src/test/java/com/turn/ttorrent/WaitFor.java deleted file mode 100644 index 41ab425ed..000000000 --- a/src/test/java/com/turn/ttorrent/WaitFor.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.turn.ttorrent; - -public abstract class WaitFor { - private long myPollInterval = 100; - - protected WaitFor() { - this(40 * 1000); - } - - protected WaitFor(long timeout) { - long started = System.currentTimeMillis(); - try { - while(true) { - if (condition()) return; - if (System.currentTimeMillis() - started < timeout) { - Thread.sleep(myPollInterval); - } else { - break; - } - } - - } catch (InterruptedException e) { - //NOP - } - } - - protected WaitFor(long timeout, long pollInterval) { - this(timeout); - myPollInterval = pollInterval; - } - - protected abstract boolean condition(); -} diff --git a/src/test/java/com/turn/ttorrent/common/TorrentTest.java b/src/test/java/com/turn/ttorrent/common/TorrentTest.java index 666fde629..10df101f0 100644 --- a/src/test/java/com/turn/ttorrent/common/TorrentTest.java +++ b/src/test/java/com/turn/ttorrent/common/TorrentTest.java @@ -1,30 +1,47 @@ package com.turn.ttorrent.common; -import org.testng.annotations.Test; - import java.io.File; -import java.io.IOException; +import java.lang.reflect.Method; import java.net.URI; -import java.net.URISyntaxException; -import java.security.NoSuchAlgorithmException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; @Test -public class TorrentTest { - - public void test_create_torrent() throws URISyntaxException, IOException, NoSuchAlgorithmException, InterruptedException { - URI announceURI = new URI("http://localhost:6969/announce"); - String createdBy = "Test"; - Torrent t = Torrent.create(new File("src/test/resources/parentFiles/file1.jar"), announceURI, createdBy); - assertEquals(createdBy, t.getCreatedBy()); - assertEquals(announceURI, t.getAnnounceList().get(0).get(0)); - } - - public void load_torrent_created_by_utorrent() throws IOException, NoSuchAlgorithmException, URISyntaxException { - Torrent t = Torrent.load(new File("src/test/resources/torrents/file1.jar.torrent")); - assertEquals(new URI("http://localhost:6969/announce"), t.getAnnounceList().get(0).get(0)); - assertEquals("B92D38046C76D73948E14C42DF992CAF25489D08", t.getHexInfoHash()); - assertEquals("uTorrent/3130", t.getCreatedBy()); - } +public class TorrentTest { + + private static final Logger logger = + LoggerFactory.getLogger(TorrentTest.class); + + @BeforeMethod(alwaysRun = true) + protected void setUp(Method testMethod) throws Exception { + String testName = testMethod.getDeclaringClass().getSimpleName() + "." + testMethod.getName(); + logger.trace("Test starting: " + testName); + } + + @AfterMethod(alwaysRun = true) + protected void tearDown(Method testMethod) throws Exception { + String testName = testMethod.getDeclaringClass().getSimpleName() + "." + testMethod.getName(); + logger.trace("Test finished: " + testName); + } + + public void test_create_torrent() throws Exception { + URI announceURI = new URI("http://localhost:6969/announce"); + String createdBy = "Test"; + Torrent t = Torrent.create(new File("src/test/resources/parentFiles/file1.jar"), announceURI, createdBy); + assertEquals(createdBy, t.getCreatedBy()); + assertEquals(announceURI, t.getAnnounceList().get(0).get(0)); + } + + public void load_torrent_created_by_utorrent() throws Exception { + Torrent t = Torrent.load(new File("src/test/resources/torrents/file1.jar.torrent")); + assertEquals(new URI("http://localhost:6969/announce"), t.getAnnounceList().get(0).get(0)); + assertEquals("B92D38046C76D73948E14C42DF992CAF25489D08", t.getHexInfoHash()); + assertEquals("uTorrent/3130", t.getCreatedBy()); + } } diff --git a/src/test/java/com/turn/ttorrent/testutil/TempFiles.java b/src/test/java/com/turn/ttorrent/testutil/TempFiles.java new file mode 100644 index 000000000..9f8a06250 --- /dev/null +++ b/src/test/java/com/turn/ttorrent/testutil/TempFiles.java @@ -0,0 +1,137 @@ +package com.turn.ttorrent.testutil; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import org.apache.commons.io.FileUtils; + +public class TempFiles { + + private static final File ourCurrentTempDir = new File(FileUtils.getTempDirectory(), "ttorrent-test"); + + private static final Random ourRandom = new Random(); + + private final File myCurrentTempDir; + private final List myFilesToDelete = new ArrayList(); + private final Thread myShutdownHook; + private volatile boolean myInsideShutdownHook; + + public TempFiles() { + myCurrentTempDir = ourCurrentTempDir; + if (!myCurrentTempDir.isDirectory() && !myCurrentTempDir.mkdirs()) { + + throw new IllegalStateException( + "Temp directory is not a directory, was deleted by some process: " + + myCurrentTempDir.getAbsolutePath()); + } + + myShutdownHook = new Thread(new Runnable() { + public void run() { + myInsideShutdownHook = true; + cleanup(); + } + }); + Runtime.getRuntime().addShutdownHook(myShutdownHook); + } + + private File doCreateTempDir(String prefix, String suffix) + throws IOException { + prefix = prefix == null ? "" : prefix; + suffix = suffix == null ? ".tmp" : suffix; + + do { + int count = ourRandom.nextInt(); + final File f = new File(myCurrentTempDir, prefix + count + suffix); + if (!f.exists() && f.mkdirs()) { + return f.getCanonicalFile(); + } + } while (true); + + } + + private File doCreateTempFile(String prefix, String suffix) + throws IOException { + final File file = doCreateTempDir(prefix, suffix); + file.delete(); + file.createNewFile(); + return file; + } + + public final File createTempFile() throws IOException { + File tempFile = doCreateTempFile("test", null); + registerAsTempFile(tempFile); + return tempFile; + } + + public void registerAsTempFile(final File tempFile) { + myFilesToDelete.add(tempFile); + } + + public final File createTempFile(int size) throws IOException { + File tempFile = createTempFile(); + int bufLen = Math.min(8 * 1024, size); + if (bufLen == 0) return tempFile; + final OutputStream fos = new BufferedOutputStream(new FileOutputStream(tempFile)); + try { + byte[] buf = new byte[bufLen]; + ourRandom.nextBytes(buf); + + int numWritten = 0; + for (int i = 0; i < size / buf.length; i++) { + fos.write(buf); + numWritten += buf.length; + } + + if (size > numWritten) { + fos.write(buf, 0, size - numWritten); + } + } finally { + fos.close(); + } + + return tempFile; + } + + /** + * Returns a File object for created temp directory. + * Also stores the value into this object accessed with {@link #getCurrentTempDir()} + * + * @return a File object for created temp directory + * @throws IOException if directory creation fails. + */ + public final File createTempDir() throws IOException { + File f = doCreateTempDir("test", ""); + registerAsTempFile(f); + return f; + } + + /** + * Returns the current directory used by the test or null if no test is running or no directory is created yet. + * + * @return see above + */ + public File getCurrentTempDir() { + return myCurrentTempDir; + } + + public void cleanup() { + try { + for (File file : myFilesToDelete) { + FileUtils.deleteQuietly(file); + } + + myFilesToDelete.clear(); + FileUtils.deleteQuietly(ourCurrentTempDir); + } finally { + if (!myInsideShutdownHook) { + Runtime.getRuntime().removeShutdownHook(myShutdownHook); + } + } + } +} diff --git a/src/test/java/com/turn/ttorrent/testutil/WaitFor.java b/src/test/java/com/turn/ttorrent/testutil/WaitFor.java new file mode 100644 index 000000000..b8cd42c9d --- /dev/null +++ b/src/test/java/com/turn/ttorrent/testutil/WaitFor.java @@ -0,0 +1,28 @@ +package com.turn.ttorrent.testutil; + +public abstract class WaitFor { + + private static final long DEFAULT_WAIT_TIMEOUT = 40 * 1000; + + private static final long DEFAULT_POLL_INTERVAL = 100; + + protected WaitFor() { + this(DEFAULT_WAIT_TIMEOUT); + } + + protected WaitFor(long timeout) { + this(timeout, DEFAULT_POLL_INTERVAL); + } + + protected WaitFor(long timeout, long interval) { + long started = System.currentTimeMillis(); + try { + while (!condition() && System.currentTimeMillis() - started < timeout) { + Thread.sleep(interval); + } + } catch (InterruptedException ignored) { + } + } + + protected abstract boolean condition(); +} diff --git a/src/test/java/com/turn/ttorrent/tracker/TrackerTest.java b/src/test/java/com/turn/ttorrent/tracker/TrackerTest.java index 7742ea48a..3802b9938 100644 --- a/src/test/java/com/turn/ttorrent/tracker/TrackerTest.java +++ b/src/test/java/com/turn/ttorrent/tracker/TrackerTest.java @@ -1,260 +1,268 @@ package com.turn.ttorrent.tracker; -import com.turn.ttorrent.TempFiles; -import com.turn.ttorrent.WaitFor; +import com.turn.ttorrent.testutil.TempFiles; +import com.turn.ttorrent.testutil.WaitFor; import com.turn.ttorrent.client.Client; import com.turn.ttorrent.client.SharedTorrent; import com.turn.ttorrent.common.Torrent; -import org.apache.commons.io.FileUtils; -import org.apache.log4j.BasicConfigurator; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; import java.io.File; import java.io.IOException; +import java.lang.reflect.Method; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.URISyntaxException; import java.security.NoSuchAlgorithmException; -import java.util.*; +import java.util.Collection; +import java.util.Map; import java.util.zip.CRC32; import java.util.zip.Checksum; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + import static org.testng.Assert.*; @Test -public class TrackerTest { - private static final String TEST_RESOURCES = "src/test/resources"; - private Tracker tracker; - private TempFiles tempFiles; - - @BeforeMethod - protected void setUp() throws Exception { - BasicConfigurator.configure(); - tempFiles = new TempFiles(); - startTracker(); - } - - public void test_share_and_download() throws IOException, NoSuchAlgorithmException, InterruptedException { - final TrackedTorrent tt = this.tracker.announce(loadTorrent("file1.jar.torrent")); - assertEquals(0, tt.getPeers().size()); - - Client seeder = createClient(completeTorrent("file1.jar.torrent")); - - assertEquals(tt.getHexInfoHash(), seeder.getTorrent().getHexInfoHash()); - - final File downloadDir = tempFiles.createTempDir(); - Client leech = createClient(incompleteTorrent("file1.jar.torrent", downloadDir)); - - try { - seeder.share(); - - leech.download(); - - waitForFileInDir(downloadDir, "file1.jar"); - assertFilesEqual(new File(TEST_RESOURCES + "/parentFiles/file1.jar"), new File(downloadDir, "file1.jar")); - } finally { - leech.stop(true); - seeder.stop(true); - } - } - - public void tracker_accepts_torrent_from_seeder() throws IOException, NoSuchAlgorithmException, InterruptedException { - final SharedTorrent torrent = completeTorrent("file1.jar.torrent"); - tracker.announce(torrent); - Client seeder = createClient(torrent); - - try { - seeder.share(); - - waitForSeeder(seeder.getTorrent().getInfoHash()); - - Collection trackedTorrents = this.tracker.getTrackedTorrents(); - assertEquals(1, trackedTorrents.size()); - - TrackedTorrent trackedTorrent = trackedTorrents.iterator().next(); - Map peers = trackedTorrent.getPeers(); - assertEquals(1, peers.size()); - assertTrue(peers.values().iterator().next().isCompleted()); // seed - assertEquals(1, trackedTorrent.seeders()); - assertEquals(0, trackedTorrent.leechers()); - } finally { - seeder.stop(true); - } - } - - public void tracker_accepts_torrent_from_leech() throws IOException, NoSuchAlgorithmException, InterruptedException { - - final File downloadDir = tempFiles.createTempDir(); - final SharedTorrent torrent = incompleteTorrent("file1.jar.torrent", downloadDir); - tracker.announce(torrent); - Client leech = createClient(torrent); - - try { - leech.download(); - - waitForPeers(1); - - Collection trackedTorrents = this.tracker.getTrackedTorrents(); - assertEquals(1, trackedTorrents.size()); - - TrackedTorrent trackedTorrent = trackedTorrents.iterator().next(); - Map peers = trackedTorrent.getPeers(); - assertEquals(1, peers.size()); - assertFalse(peers.values().iterator().next().isCompleted()); // leech - assertEquals(0, trackedTorrent.seeders()); - assertEquals(1, trackedTorrent.leechers()); - } finally { - leech.stop(true); - } - } - - public void tracker_accepts_torrent_from_seeder_plus_leech() throws IOException, NoSuchAlgorithmException, InterruptedException { - assertEquals(0, this.tracker.getTrackedTorrents().size()); - - final SharedTorrent completeTorrent = completeTorrent("file1.jar.torrent"); - tracker.announce(completeTorrent); - Client seeder = createClient(completeTorrent); - - final File downloadDir = tempFiles.createTempDir(); - final SharedTorrent incompleteTorrent = incompleteTorrent("file1.jar.torrent", downloadDir); - Client leech = createClient(incompleteTorrent); - - try { - seeder.share(); - leech.download(); - - waitForFileInDir(downloadDir, "file1.jar"); - } finally { - seeder.stop(true); - leech.stop(true); - } - } - - private Set listFileNames(File downloadDir) { - if (downloadDir == null) return Collections.emptySet(); - Set names = new HashSet(); - File[] files = downloadDir.listFiles(); - if (files == null) return Collections.emptySet(); - for (File f: files) { - names.add(f.getName()); - } - return names; - } - - - public void large_file_download() throws IOException, URISyntaxException, NoSuchAlgorithmException, InterruptedException { - - - File tempFile = tempFiles.createTempFile(201 * 1024 * 1024); - - Torrent torrent = Torrent.create(tempFile, this.tracker.getAnnounceUrl().toURI(), "Test"); - File torrentFile = new File(tempFile.getParentFile(), tempFile.getName() + ".torrent"); - torrent.save(torrentFile); - tracker.announce(torrent); - - Client seeder = createClient(SharedTorrent.fromFile(torrentFile, tempFile.getParentFile())); - - final File downloadDir = tempFiles.createTempDir(); - Client leech = createClient(SharedTorrent.fromFile(torrentFile, downloadDir)); - - try { - seeder.share(); - leech.download(); - - waitForFileInDir(downloadDir, tempFile.getName()); - assertFilesEqual(tempFile, new File(downloadDir, tempFile.getName())); - } finally { - seeder.stop(true); - leech.stop(true); - } - } - - public void test_announce() throws IOException, NoSuchAlgorithmException { - assertEquals(0, this.tracker.getTrackedTorrents().size()); - - this.tracker.announce(loadTorrent("file1.jar.torrent")); - - assertEquals(1, this.tracker.getTrackedTorrents().size()); - } - - private void waitForSeeder(final byte[] torrentHash) { - new WaitFor() { - @Override - protected boolean condition() { - for (TrackedTorrent tt: TrackerTest.this.tracker.getTrackedTorrents()) { - if (tt.seeders() == 1 && tt.getHexInfoHash().equals(Torrent.byteArrayToHexString(torrentHash))) return true; - } - - return false; - } - }; - } - - private void waitForPeers(final int numPeers) { - new WaitFor() { - @Override - protected boolean condition() { - for (TrackedTorrent tt: TrackerTest.this.tracker.getTrackedTorrents()) { - if (tt.getPeers().size() == numPeers) return true; - } - - return false; - } - }; - } - - private void waitForFileInDir(final File downloadDir, final String fileName) { - new WaitFor() { - @Override - protected boolean condition() { - return new File(downloadDir, fileName).isFile(); - } - }; - - assertTrue(new File(downloadDir, fileName).isFile()); - } - - private TrackedTorrent loadTorrent(String name) throws IOException, NoSuchAlgorithmException { - return new TrackedTorrent(Torrent.load(new File(TEST_RESOURCES + "/torrents", name), true)); - } - - - @AfterMethod - protected void tearDown() throws Exception { - stopTracker(); - tempFiles.cleanup(); - } - - private void startTracker() throws IOException { - this.tracker = new Tracker(new InetSocketAddress(6969)); - this.tracker.start(); - } - - private Client createClient(SharedTorrent torrent) throws IOException, NoSuchAlgorithmException, InterruptedException { - return new Client(InetAddress.getLocalHost(), torrent); - } - - private SharedTorrent completeTorrent(String name) throws IOException, NoSuchAlgorithmException { - File torrentFile = new File(TEST_RESOURCES + "/torrents", name); - File parentFiles = new File(TEST_RESOURCES + "/parentFiles"); - return SharedTorrent.fromFile(torrentFile, parentFiles); - } - - private SharedTorrent incompleteTorrent(String name, File destDir) throws IOException, NoSuchAlgorithmException { - File torrentFile = new File(TEST_RESOURCES + "/torrents", name); - return SharedTorrent.fromFile(torrentFile, destDir); - } - - private void stopTracker() { - this.tracker.stop(); - } - - private void assertFilesEqual(File f1, File f2) throws IOException { - assertEquals(f1.length(), f2.length(), "Files size differs"); - Checksum c1 = FileUtils.checksum(f1, new CRC32()); - Checksum c2 = FileUtils.checksum(f2, new CRC32()); - assertEquals(c1.getValue(), c2.getValue()); - } +public class TrackerTest { + + private static final Logger logger = + LoggerFactory.getLogger(TrackerTest.class); + + private static final String TEST_RESOURCES = "src/test/resources"; + private static final int TRACKER_PORT = + Integer.getInteger("ttorrent.test.tracker_port", 6969); + + private Tracker tracker; + private TempFiles tempFiles; + private Torrent testTorrent; + private File testFile; + + @BeforeMethod(alwaysRun = true) + protected void setUp(Method testMethod) throws Exception { + String testName = testMethod.getDeclaringClass().getSimpleName() + "." + testMethod.getName(); + logger.trace("Test starting: " + testName); + tempFiles = new TempFiles(); + startTracker(); + testFile = new File(TEST_RESOURCES + "/parentFiles/file1.jar"); + String creator = String.format("%s (ttorrent)", testName); + testTorrent = Torrent.create(testFile, + this.tracker.getAnnounceUrl().toURI(), creator); + } + + @AfterMethod(alwaysRun = true) + protected void tearDown(Method testMethod) throws Exception { + String testName = testMethod.getDeclaringClass().getSimpleName() + "." + testMethod.getName(); + stopTracker(); + tempFiles.cleanup(); + logger.trace("Test finished: " + testName); + } + + public void test_share_and_download() throws Exception { + final TrackedTorrent tt = this.tracker.announce(loadTorrent()); + assertEquals(0, tt.getPeers().size()); + + Client seeder = createClient(completeTorrent()); + + assertEquals(tt.getHexInfoHash(), seeder.getTorrent().getHexInfoHash()); + + final File downloadDir = tempFiles.createTempDir(); + Client leech = createClient(incompleteTorrent(downloadDir)); + + try { + seeder.share(); + + leech.download(); + + waitForFileInDir(downloadDir, testFile.getName()); + assertFilesEqual(testFile, new File(downloadDir, testFile.getName())); + } finally { + leech.stop(true); + seeder.stop(true); + } + } + + public void tracker_accepts_torrent_from_seeder() throws Exception { + final SharedTorrent torrent = completeTorrent(); + tracker.announce(new TrackedTorrent(torrent)); + Client seeder = createClient(torrent); + + try { + seeder.share(); + + waitForSeeder(seeder.getTorrent().getInfoHash()); + + Collection trackedTorrents = this.tracker.getTrackedTorrents(); + assertEquals(1, trackedTorrents.size()); + + TrackedTorrent trackedTorrent = trackedTorrents.iterator().next(); + Map peers = trackedTorrent.getPeers(); + assertEquals(1, peers.size()); + assertTrue(peers.values().iterator().next().isCompleted()); // seed + assertEquals(1, trackedTorrent.seeders()); + assertEquals(0, trackedTorrent.leechers()); + } finally { + seeder.stop(true); + } + } + + public void tracker_accepts_torrent_from_leech() throws Exception { + + final File downloadDir = tempFiles.createTempDir(); + final SharedTorrent torrent = incompleteTorrent(downloadDir); + tracker.announce(new TrackedTorrent(torrent)); + Client leech = createClient(torrent); + + try { + leech.download(); + + waitForPeers(1); + + Collection trackedTorrents = this.tracker.getTrackedTorrents(); + assertEquals(1, trackedTorrents.size()); + + TrackedTorrent trackedTorrent = trackedTorrents.iterator().next(); + Map peers = trackedTorrent.getPeers(); + assertEquals(1, peers.size()); + assertFalse(peers.values().iterator().next().isCompleted()); // leech + assertEquals(0, trackedTorrent.seeders()); + assertEquals(1, trackedTorrent.leechers()); + } finally { + leech.stop(true); + } + } + + public void tracker_accepts_torrent_from_seeder_plus_leech() throws Exception { + assertEquals(0, this.tracker.getTrackedTorrents().size()); + + final SharedTorrent completeTorrent = completeTorrent(); + tracker.announce(new TrackedTorrent(completeTorrent)); + Client seeder = createClient(completeTorrent); + + final File downloadDir = tempFiles.createTempDir(); + final SharedTorrent incompleteTorrent = incompleteTorrent(downloadDir); + Client leech = createClient(incompleteTorrent); + + try { + seeder.share(); + leech.download(); + + waitForFileInDir(downloadDir, testFile.getName()); + } finally { + seeder.stop(true); + leech.stop(true); + } + } + + public void large_file_download() throws Exception { + + File tempFile = tempFiles.createTempFile(201 * 1024 * 1024); + + Torrent torrent = Torrent.create(tempFile, this.tracker.getAnnounceUrl().toURI(), "Test"); + File torrentFile = new File(tempFile.getParentFile(), tempFile.getName() + ".torrent"); + FileUtils.writeByteArrayToFile(torrentFile, torrent.getEncoded()); + tracker.announce(new TrackedTorrent(torrent)); + + Client seeder = createClient(SharedTorrent.fromFile(torrentFile, tempFile.getParentFile())); + + final File downloadDir = tempFiles.createTempDir(); + Client leech = createClient(SharedTorrent.fromFile(torrentFile, downloadDir)); + + try { + seeder.share(); + leech.download(); + + waitForFileInDir(downloadDir, tempFile.getName()); + assertFilesEqual(tempFile, new File(downloadDir, tempFile.getName())); + } finally { + seeder.stop(true); + leech.stop(true); + } + } + + public void test_announce() throws Exception { + assertEquals(0, this.tracker.getTrackedTorrents().size()); + + this.tracker.announce(loadTorrent()); + + assertEquals(1, this.tracker.getTrackedTorrents().size()); + } + + private void waitForSeeder(final byte[] torrentHash) { + new WaitFor() { + @Override + protected boolean condition() { + for (TrackedTorrent tt : TrackerTest.this.tracker.getTrackedTorrents()) { + if (tt.seeders() == 1 && tt.getHexInfoHash().equals(Torrent.byteArrayToHexString(torrentHash))) + return true; + } + + return false; + } + }; + } + + private void waitForPeers(final int numPeers) { + new WaitFor() { + @Override + protected boolean condition() { + for (TrackedTorrent tt : TrackerTest.this.tracker.getTrackedTorrents()) { + if (tt.getPeers().size() == numPeers) return true; + } + + return false; + } + }; + } + + private void waitForFileInDir(final File downloadDir, final String fileName) { + new WaitFor(120 * 1000) { + @Override + protected boolean condition() { + return new File(downloadDir, fileName).isFile(); + } + }; + + assertTrue(new File(downloadDir, fileName).isFile()); + } + + private TrackedTorrent loadTorrent() + throws IOException, NoSuchAlgorithmException { + return new TrackedTorrent(testTorrent); + } + + private void startTracker() throws IOException { + this.tracker = new Tracker(new InetSocketAddress(TRACKER_PORT)); + this.tracker.start(); + } + + private Client createClient(SharedTorrent torrent) + throws IOException, NoSuchAlgorithmException, InterruptedException { + return new Client(InetAddress.getLocalHost(), torrent); + } + + private SharedTorrent completeTorrent() + throws IOException, NoSuchAlgorithmException { + File parentFiles = new File(TEST_RESOURCES + "/parentFiles"); + return new SharedTorrent(testTorrent, parentFiles); + } + + private SharedTorrent incompleteTorrent(File destDir) + throws IOException, NoSuchAlgorithmException { + return new SharedTorrent(testTorrent, destDir); + } + + private void stopTracker() { + this.tracker.stop(); + } + + private void assertFilesEqual(File f1, File f2) throws IOException { + assertEquals(f1.length(), f2.length(), "Files size differs"); + Checksum c1 = FileUtils.checksum(f1, new CRC32()); + Checksum c2 = FileUtils.checksum(f2, new CRC32()); + assertEquals(c1.getValue(), c2.getValue()); + } } diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties new file mode 100644 index 000000000..5fe42d854 --- /dev/null +++ b/src/test/resources/log4j.properties @@ -0,0 +1,9 @@ +# Set root logger level to DEBUG and its only appender to A1. +log4j.rootLogger=DEBUG, A1 + +# A1 is set to be a ConsoleAppender. +log4j.appender.A1=org.apache.log4j.ConsoleAppender + +# A1 uses PatternLayout. +log4j.appender.A1.layout=org.apache.log4j.PatternLayout +log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n