Added config to tell how much torrent have to be seed simultaneously. Start seeding another torrent when one stop.

This commit is contained in:
anthonyraymond 2017-05-25 22:59:36 +02:00
parent 4588658156
commit 05a85073e9
13 changed files with 277 additions and 122 deletions

View file

@ -1,7 +1,6 @@
{
"minUploadRate": 180,
"maxUploadRate": 190,
"seedFor": 1200,
"waitBetweenSeed": 240,
"simultaneousSeed": 2,
"client": "azureus-5.7.5.0.client"
}

View file

@ -12,22 +12,19 @@ public class AppConfiguration {
private final int minUploadRate;
private final int maxUploadRate;
private final int seedFor;
private final int waitBetweenSeed;
private final int simultaneousSeed;
private final String client;
@JsonCreator
AppConfiguration(
@JsonProperty(value = "minUploadRate", required = true) final int minUploadRate,
@JsonProperty(value = "maxUploadRate", required = true) final int maxUploadRate,
@JsonProperty(value = "seedFor", required = true) final int seedFor,
@JsonProperty(value = "waitBetweenSeed", required = true) final int waitBetweenSeed,
@JsonProperty(value = "simultaneousSeed", required = true) final int simultaneousSeed,
@JsonProperty(value = "client", required = true) final String client
) {
this.minUploadRate = minUploadRate;
this.maxUploadRate = maxUploadRate;
this.seedFor = seedFor;
this.waitBetweenSeed = waitBetweenSeed;
this.simultaneousSeed = simultaneousSeed;
this.client = client;
validate();
@ -41,12 +38,8 @@ public class AppConfiguration {
return minUploadRate;
}
public int getSeedFor() {
return seedFor;
}
public int getWaitBetweenSeed() {
return waitBetweenSeed;
public int getSimultaneousSeed() {
return simultaneousSeed;
}
@JsonProperty("client")
@ -64,11 +57,8 @@ public class AppConfiguration {
if (maxUploadRate <= minUploadRate) {
throw new AppConfigurationIntegrityException("maxUploadRate must be strictly greater than minUploadRate.");
}
if (seedFor < 1) {
throw new AppConfigurationIntegrityException("seedFor must be greater than 1.");
}
if (waitBetweenSeed < 1) {
throw new AppConfigurationIntegrityException("waitBetweenSeed must be greater than 1.");
if (simultaneousSeed < 1) {
throw new AppConfigurationIntegrityException("simultaneousSeed must be greater than 0.");
}
if (StringUtils.isBlank(client)) {
throw new AppConfigurationIntegrityException("client is required, no file name given.");
@ -82,13 +72,12 @@ public class AppConfiguration {
final AppConfiguration that = (AppConfiguration) o;
return minUploadRate == that.minUploadRate &&
maxUploadRate == that.maxUploadRate &&
seedFor == that.seedFor &&
waitBetweenSeed == that.waitBetweenSeed &&
simultaneousSeed == that.simultaneousSeed &&
Objects.equal(client, that.client);
}
@Override
public int hashCode() {
return Objects.hashCode(minUploadRate, maxUploadRate, seedFor, waitBetweenSeed, client);
return Objects.hashCode(minUploadRate, maxUploadRate, simultaneousSeed, client);
}
}

View file

@ -0,0 +1,14 @@
package org.araymond.joal.core.torrent.watcher;
import org.araymond.joal.core.ttorent.client.MockedTorrent;
/**
* Created by raymo on 23/05/2017.
*/
public interface TorrentFileChangeAware {
void onTorrentAdded(final MockedTorrent torrent);
void onTorrentRemoved(final MockedTorrent torrent);
}

View file

@ -1,6 +1,6 @@
package org.araymond.joal.core.torrent.watcher;
import com.google.common.collect.Iterators;
import com.google.common.base.Preconditions;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.lang3.StringUtils;
import org.araymond.joal.core.events.TorrentFileAddedForSeed;
@ -22,10 +22,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.*;
/**
* Created by raymo on 28/01/2017.
@ -37,8 +34,8 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
private final TorrentFileWatcher watcher;
private final Map<File, MockedTorrent> torrentFiles;
private final Path archiveFolder;
private final Random rand;
private final ApplicationEventPublisher publisher;
private final Set<TorrentFileChangeAware> torrentFileChangeListener;
private boolean isInitOver = false;
@ -77,15 +74,24 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
this.torrentFiles = Collections.synchronizedMap(new HashMap<File, MockedTorrent>());
this.rand = new Random();
this.watcher = new TorrentFileWatcher(this, torrentFolder);
torrentFileChangeListener = new HashSet<>();
}
@Override
public void onFileDelete(final File file) {
if (!this.torrentFiles.containsKey(file)) {
return;
}
final MockedTorrent torrent = this.torrentFiles.get(file);
if (torrent == null) {
return;
}
logger.info("Torrent file deleting detected, hot deleted file: {}", file.getAbsolutePath());
this.torrentFiles.remove(file);
this.torrentFileChangeListener.forEach(listener -> listener.onTorrentRemoved(torrent));
}
@Override
@ -96,6 +102,7 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
this.torrentFiles.put(file, torrent);
if (this.isInitOver) {
this.publisher.publishEvent(new TorrentFileAddedForSeed(torrent));
this.torrentFileChangeListener.forEach(listener -> listener.onTorrentAdded(torrent));
}
} catch (final IOException | NoSuchAlgorithmException e) {
logger.warn("File '{}' not added to torrent list, because failed to read file '", file.getAbsolutePath(), e);
@ -109,19 +116,27 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
this.onFileCreate(file);
}
public MockedTorrent getRandomTorrentFile() throws NoMoreTorrentsFileAvailableException {
if (this.torrentFiles.isEmpty()) {
logger.error("There is no more .torrent file available.");
throw new NoMoreTorrentsFileAvailableException("No more torrent file available.");
}
final int indexToPick = this.rand.nextInt(this.torrentFiles.size());
return Iterators.get(this.torrentFiles.values().iterator(), indexToPick);
public void registerListener(final TorrentFileChangeAware listener) {
this.torrentFileChangeListener.add(listener);
}
public void moveToArchiveFolder(final File torrentFile) {
public void unRegisterListener(final TorrentFileChangeAware listener) {
this.torrentFileChangeListener.remove(listener);
}
public MockedTorrent getTorrentNotIn(final List<MockedTorrent> unwantedTorrents) throws NoMoreTorrentsFileAvailableException {
Preconditions.checkNotNull(unwantedTorrents, "List of unwantedTorrents cannot be null.");
return this.torrentFiles.values().stream()
.filter(torrent -> !unwantedTorrents.contains(torrent))
.findAny()
.orElseThrow(() -> new NoMoreTorrentsFileAvailableException("No more torrent file available."));
}
void moveToArchiveFolder(final File torrentFile) {
if (!torrentFile.exists()) {
return;
}
this.onFileDelete(torrentFile);
try {
Files.deleteIfExists(archiveFolder.resolve(torrentFile.getName()));
@ -132,17 +147,8 @@ public class TorrentFileProvider extends FileAlterationListenerAdaptor {
}
}
public void moveToArchiveFolder(final MockedTorrent torrentFile) {
final File fileToArchive = this.torrentFiles.entrySet().stream()
.filter(entry -> entry.getValue().getHexInfoHash().equalsIgnoreCase(torrentFile.getHexInfoHash()))
.findFirst()
.orElseThrow(() -> {
final IllegalStateException e = new IllegalStateException("Failed to find which torrent file to archive.");
logger.warn("Failed to find which torrent file to archive.", e);
return e;
})
.getKey();
this.moveToArchiveFolder(fileToArchive);
public void moveToArchiveFolder(final MockedTorrent torrent) {
this.moveToArchiveFolder(torrent.getPath().toFile());
}
public int getTorrentCount() {

View file

@ -9,6 +9,7 @@ import org.araymond.joal.core.events.NoMoreLeechers;
import org.araymond.joal.core.events.NoMoreTorrentsFileAvailable;
import org.araymond.joal.core.events.announce.AnnounceRequestingEvent;
import org.araymond.joal.core.exception.NoMoreTorrentsFileAvailableException;
import org.araymond.joal.core.torrent.watcher.TorrentFileChangeAware;
import org.araymond.joal.core.torrent.watcher.TorrentFileProvider;
import org.araymond.joal.core.ttorent.client.announce.Announcer;
import org.araymond.joal.core.ttorent.client.announce.AnnouncerEventListener;
@ -20,11 +21,12 @@ import org.springframework.context.ApplicationEventPublisher;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* Created by raymo on 14/05/2017.
*/
public class Client implements AnnouncerEventListener {
public class Client implements AnnouncerEventListener, TorrentFileChangeAware {
private static final Logger logger = LoggerFactory.getLogger(Client.class);
private final JoalConfigProvider configProvider;
@ -52,10 +54,11 @@ public class Client implements AnnouncerEventListener {
}
public void share() {
this.torrentFileProvider.registerListener(this);
this.setState(ClientState.STARTING);
this.bandwidthDispatcher.start();
// TODO : number of torrent to seed should be in config
final int numberOfTorrentToSeed = 1;
final int numberOfTorrentToSeed = configProvider.get().getSimultaneousSeed();
for (int i = 0; i < numberOfTorrentToSeed; ++i) {
try {
addSeedingTorrent();
@ -67,23 +70,32 @@ public class Client implements AnnouncerEventListener {
this.setState(ClientState.STARTED);
}
private Announcer addSeedingTorrent() throws NoMoreTorrentsFileAvailableException {
//TODO : replace getRandom by something else to ensure we are not seeding the same torrent twice
final MockedTorrent torrent = torrentFileProvider.getRandomTorrentFile();
private void addSeedingTorrent() throws NoMoreTorrentsFileAvailableException {
final List<MockedTorrent> unwantedTorrent = this.announcers.stream()
.map(Announcer::getSeedingTorrent)
.collect(Collectors.toList());
final MockedTorrent torrent = torrentFileProvider.getTorrentNotIn(unwantedTorrent);
this.addSeedingTorrent(torrent);
}
private void addSeedingTorrent(final MockedTorrent torrent) {
final Announcer announcer = new Announcer(torrent, this.self, this.bitTorrentClient, publisher);
announcer.registerEventListener(this);
logger.debug("Added announcer for Torrent {}", torrent.getName());
this.announcers.add(announcer);
logger.debug("Added announcer for Torrent {}", torrent.getName());
announcer.start();
return announcer;
}
public void stop() {
this.setState(ClientState.STOPPING);
this.torrentFileProvider.unRegisterListener(this);
this.bandwidthDispatcher.stop();
// We need to work with a copy of the list, because when stop, an event is launched that remove the announcer from the list.
// And it result in a concurrent modification exception.
this.bandwidthDispatcher.stop();
Lists.newArrayList(this.announcers).forEach(Announcer::stop);
this.setState(ClientState.STOPPED);
}
@ -94,7 +106,25 @@ public class Client implements AnnouncerEventListener {
}
}
//TODO : add event handler to catch TorrentFileAdded / TorrentFileRemoved, to be able to stop when removed, and add if new torrent is available and this.announcers < numberOfTorrentToSeed
@Override
public void onTorrentAdded(final MockedTorrent torrent) {
if (this.currentState != ClientState.STARTED) {
return;
}
if (this.announcers.size() >= configProvider.get().getSimultaneousSeed()) {
return;
}
addSeedingTorrent(torrent);
}
@Override
public void onTorrentRemoved(final MockedTorrent torrent) {
for (final Announcer announcer : this.announcers) {
if (announcer.isForTorrent(torrent)) {
announcer.stop();
}
}
}
@Override
public void onAnnounceRequesting(final RequestEvent event, final TorrentWithStats torrent) {

View file

@ -54,6 +54,10 @@ public class MockedTorrent extends Torrent {
return new MockedTorrent(data, true, torrent.toPath());
}
public Path getPath() {
return path;
}
@Override
public boolean equals(final Object o) {
// TODO : consider a better way to handle equals and hashcode

View file

@ -122,6 +122,11 @@ public class Announcer implements Runnable, AnnounceResponseListener {
@Override
public void handleDiscoveredPeers(final TorrentWithStats torrent, final List<Peer> peers) {
logger.info(
"Peers discovery for torrent {}: {} leechers",
torrent.getTorrent().getName(),
peers == null ? 0 : peers.size()
);
if (peers == null || peers.isEmpty()) {
this.eventListeners.forEach(listener -> listener.onNoMoreLeecherForTorrent(this, torrent));
}
@ -222,10 +227,11 @@ public class Announcer implements Runnable, AnnounceResponseListener {
while (!this.stop) {
try {
this.getCurrentTrackerClient().announce(event, false);
for (final AnnouncerEventListener listener : this.eventListeners) {
listener.onAnnounceRequesting(event, this.torrent);
}
this.getCurrentTrackerClient().announce(event, false);
this.promoteCurrentTrackerClient();
event = AnnounceRequestMessage.RequestEvent.NONE;
} catch (final AnnounceException ae) {
@ -257,10 +263,10 @@ public class Announcer implements Runnable, AnnounceResponseListener {
event = AnnounceRequestMessage.RequestEvent.STOPPED;
try {
this.getCurrentTrackerClient().announce(event, true);
for (final AnnouncerEventListener listener : this.eventListeners) {
listener.onAnnounceRequesting(event, this.torrent);
}
this.getCurrentTrackerClient().announce(event, true);
} catch (final AnnounceException ae) {
logger.warn(ae.getMessage());
}
@ -308,6 +314,14 @@ public class Announcer implements Runnable, AnnounceResponseListener {
.get(this.currentClient);
}
public MockedTorrent getSeedingTorrent() {
return this.torrent.getTorrent();
}
public boolean isForTorrent(final MockedTorrent torrent) {
return this.torrent.getTorrent().equals(torrent);
}
/**
* Promote the current tracker client to the top of its tier.
* <p>

View file

@ -19,62 +19,54 @@ public class AppConfigurationSerializationTest {
@Test
public void shouldFailToDeserializeIfMinUploadRateIsNotDefined() throws IOException {
assertThatThrownBy(() -> mapper.readValue("{\"maxUploadRate\":190,\"seedFor\":1200,\"waitBetweenSeed\":1200,\"client\":\"azureus.client\"}", AppConfiguration.class))
assertThatThrownBy(() -> mapper.readValue("{\"maxUploadRate\":190,\"simultaneousSeed\":2,\"client\":\"azureus.client\"}", AppConfiguration.class))
.isInstanceOf(JsonMappingException.class)
.hasMessageContaining("Missing required creator property 'minUploadRate'");
}
@Test
public void shouldFailToDeserializeIfMaxUploadRateIsNotDefined() throws IOException {
assertThatThrownBy(() -> mapper.readValue("{\"minUploadRate\":180,\"seedFor\":1200,\"waitBetweenSeed\":1200,\"client\":\"azureus.client\"}", AppConfiguration.class))
assertThatThrownBy(() -> mapper.readValue("{\"minUploadRate\":180,\"simultaneousSeed\":2,\"client\":\"azureus.client\"}", AppConfiguration.class))
.isInstanceOf(JsonMappingException.class)
.hasMessageContaining("Missing required creator property 'maxUploadRate'");
}
@Test
public void shouldFailToDeserializeIfSeedForIsNotDefined() throws IOException {
assertThatThrownBy(() -> mapper.readValue("{\"minUploadRate\":180,\"maxUploadRate\":190,\"waitBetweenSeed\":1200,\"client\":\"azureus.client\"}", AppConfiguration.class))
public void shouldFailToDeserializeIfSimultaneousSeedIsNotDefined() throws IOException {
assertThatThrownBy(() -> mapper.readValue("{\"minUploadRate\":180,\"maxUploadRate\":190,\"client\":\"azureus.client\"}", AppConfiguration.class))
.isInstanceOf(JsonMappingException.class)
.hasMessageContaining("Missing required creator property 'seedFor'");
}
@Test
public void shouldFailToDeserializeIfWaitBetweenSeedIsNotDefined() throws IOException {
assertThatThrownBy(() -> mapper.readValue("{\"minUploadRate\":180,\"maxUploadRate\":190,\"seedFor\":1200,\"client\":\"azureus.client\"}", AppConfiguration.class))
.isInstanceOf(JsonMappingException.class)
.hasMessageContaining("Missing required creator property 'waitBetweenSeed'");
.hasMessageContaining("Missing required creator property 'simultaneousSeed'");
}
@Test
public void shouldFailToDeserializeIfClientIsNotDefined() throws IOException {
assertThatThrownBy(() -> mapper.readValue("{\"minUploadRate\":180,\"maxUploadRate\":190,\"seedFor\":1200,\"waitBetweenSeed\":1200}", AppConfiguration.class))
assertThatThrownBy(() -> mapper.readValue("{\"minUploadRate\":180,\"maxUploadRate\":190,\"simultaneousSeed\":2}", AppConfiguration.class))
.isInstanceOf(JsonMappingException.class)
.hasMessageContaining("Missing required creator property 'client'");
}
@Test
public void shouldSerialize() throws JsonProcessingException {
final AppConfiguration config = new AppConfiguration(180, 190, 1200, 1200, "azureus.client");
assertThat(mapper.writeValueAsString(config)).isEqualTo("{\"minUploadRate\":180,\"maxUploadRate\":190,\"seedFor\":1200,\"waitBetweenSeed\":1200,\"client\":\"azureus.client\"}");
final AppConfiguration config = new AppConfiguration(180, 190, 2, "azureus.client");
assertThat(mapper.writeValueAsString(config)).isEqualTo("{\"minUploadRate\":180,\"maxUploadRate\":190,\"simultaneousSeed\":2,\"client\":\"azureus.client\"}");
}
@Test
public void shouldDeserialize() throws IOException {
final AppConfiguration config = mapper.readValue(
"{\"minUploadRate\":180,\"maxUploadRate\":190,\"seedFor\":1200,\"waitBetweenSeed\":1200,\"client\":\"azureus.client\"}",
"{\"minUploadRate\":180,\"maxUploadRate\":190,\"simultaneousSeed\":2,\"client\":\"azureus.client\"}",
AppConfiguration.class
);
assertThat(config.getMinUploadRate()).isEqualTo(180);
assertThat(config.getMaxUploadRate()).isEqualTo(190);
assertThat(config.getSeedFor()).isEqualTo(1200);
assertThat(config.getWaitBetweenSeed()).isEqualTo(1200);
assertThat(config.getSimultaneousSeed()).isEqualTo(2);
assertThat(config.getClientFileName()).isEqualTo("azureus.client");
}
@Test
public void shouldSerializeAndDeserialize() throws IOException {
final AppConfiguration config = mapper.readValue(
"{\"minUploadRate\":180,\"maxUploadRate\":190,\"seedFor\":1200,\"waitBetweenSeed\":1200,\"client\":\"azureus.client\"}",
"{\"minUploadRate\":180,\"maxUploadRate\":190,\"simultaneousSeed\":2,\"client\":\"azureus.client\"}",
AppConfiguration.class
);

View file

@ -12,110 +12,95 @@ public class AppConfigurationTest {
@Test
public void ShouldNotBuildIfMinUploadRateIsLessThanZero() {
assertThatThrownBy(() -> new AppConfiguration(-1, 190, 1200, 1200, "azureus.client"))
assertThatThrownBy(() -> new AppConfiguration(-1, 190, 2, "azureus.client"))
.isInstanceOf(AppConfigurationIntegrityException.class)
.hasMessageContaining("minUploadRate must be at least 0.");
}
@Test
public void shouldCreateIfMinUploadRateEqualsZero() {
final AppConfiguration config = new AppConfiguration(0, 190, 1200, 1200, "azureus.client");
final AppConfiguration config = new AppConfiguration(0, 190, 2, "azureus.client");
assertThat(config.getMinUploadRate()).isEqualTo(0);
}
@Test
public void ShouldNotBuildIfMaxUploadRateIsLessThanOne() {
assertThatThrownBy(() -> new AppConfiguration(180, -1, 1200, 1200, "azureus.client"))
assertThatThrownBy(() -> new AppConfiguration(180, -1, 2, "azureus.client"))
.isInstanceOf(AppConfigurationIntegrityException.class)
.hasMessageContaining("maxUploadRate must greater than 0.");
}
@Test
public void shouldCreateIfMinUploadRateEqualsOne() {
final AppConfiguration config = new AppConfiguration(0, 1, 1200, 1200, "azureus.client");
final AppConfiguration config = new AppConfiguration(0, 1, 2, "azureus.client");
assertThat(config.getMaxUploadRate()).isEqualTo(1);
}
@Test
public void ShouldNotBuildIfMaxRateIsLesserThanMinRate() {
assertThatThrownBy(() -> new AppConfiguration(180, 150, 1200, 1200, "azureus.client"))
assertThatThrownBy(() -> new AppConfiguration(180, 150, 2, "azureus.client"))
.isInstanceOf(AppConfigurationIntegrityException.class)
.hasMessageContaining("maxUploadRate must be strictly greater than minUploadRate.");
}
@Test
public void ShouldNotBuildIfMaxRateEqualsThanMinRate() {
assertThatThrownBy(() -> new AppConfiguration(180, 180, 1200, 1200, "azureus.client"))
assertThatThrownBy(() -> new AppConfiguration(180, 180, 2, "azureus.client"))
.isInstanceOf(AppConfigurationIntegrityException.class)
.hasMessageContaining("maxUploadRate must be strictly greater than minUploadRate.");
}
@Test
public void ShouldNotBuildIfSeedForIsLessThanOne() {
assertThatThrownBy(() -> new AppConfiguration(180, 190, 0, 1200, "azureus.client"))
public void ShouldNotBuildIfSimultaneousSeedIsLessThanOne() {
assertThatThrownBy(() -> new AppConfiguration(180, 190, 0, "azureus.client"))
.isInstanceOf(AppConfigurationIntegrityException.class)
.hasMessageContaining("seedFor must be greater than 1.");
.hasMessageContaining("simultaneousSeed must be greater than 0.");
}
@Test
public void shouldCreateIfSeedForIsOne() {
final AppConfiguration config = new AppConfiguration(180, 190, 1, 1200, "azureus.client");
public void shouldCreateIfSimultaneousSeedIsOne() {
final AppConfiguration config = new AppConfiguration(180, 190, 1, "azureus.client");
assertThat(config.getSeedFor()).isEqualTo(1);
}
@Test
public void ShouldNotBuildIfWaitBetweenSeedIsLessThanOne() {
assertThatThrownBy(() -> new AppConfiguration(180, 190, 1200, 0, "azureus.client"))
.isInstanceOf(AppConfigurationIntegrityException.class)
.hasMessageContaining("waitBetweenSeed must be greater than 1.");
}
@Test
public void shouldCreateIfWaitBetweenSeedIsOne() {
final AppConfiguration config = new AppConfiguration(180, 190, 1200, 1, "azureus.client");
assertThat(config.getWaitBetweenSeed()).isEqualTo(1);
assertThat(config.getSimultaneousSeed()).isEqualTo(1);
}
@Test
public void ShouldNotBuildIfClientIsNull() {
assertThatThrownBy(() -> new AppConfiguration(180, 190, 1200, 1200, null))
assertThatThrownBy(() -> new AppConfiguration(180, 190, 2, null))
.isInstanceOf(AppConfigurationIntegrityException.class)
.hasMessageContaining("client is required, no file name given.");
}
@Test
public void ShouldNotBuildIfClientIsEmpty() {
assertThatThrownBy(() -> new AppConfiguration(180, 190, 1200, 1200, " "))
assertThatThrownBy(() -> new AppConfiguration(180, 190, 2, " "))
.isInstanceOf(AppConfigurationIntegrityException.class)
.hasMessageContaining("client is required, no file name given.");
}
@Test
public void shouldBuild() {
final AppConfiguration config = new AppConfiguration(180, 190, 1250, 1200, "azureus.client");
final AppConfiguration config = new AppConfiguration(180, 190, 2, "azureus.client");
assertThat(config.getMinUploadRate()).isEqualTo(180);
assertThat(config.getMaxUploadRate()).isEqualTo(190);
assertThat(config.getSeedFor()).isEqualTo(1250);
assertThat(config.getWaitBetweenSeed()).isEqualTo(1200);
assertThat(config.getSimultaneousSeed()).isEqualTo(2);
assertThat(config.getClientFileName()).isEqualTo("azureus.client");
}
@Test
public void shouldBeEqualsByProperties() {
final AppConfiguration config = new AppConfiguration(180, 190, 1200, 1200, "azureus.client");
final AppConfiguration config2 = new AppConfiguration(180, 190, 1200, 1200, "azureus.client");
final AppConfiguration config = new AppConfiguration(180, 190, 2, "azureus.client");
final AppConfiguration config2 = new AppConfiguration(180, 190, 2, "azureus.client");
assertThat(config).isEqualTo(config2);
}
@Test
public void shouldHaveSameHashCodeWithSameProperties() {
final AppConfiguration config = new AppConfiguration(180, 190, 1200, 1200, "azureus.client");
final AppConfiguration config2 = new AppConfiguration(180, 190, 1200, 1200, "azureus.client");
final AppConfiguration config = new AppConfiguration(180, 190, 2, "azureus.client");
final AppConfiguration config2 = new AppConfiguration(180, 190, 2, "azureus.client");
assertThat(config.hashCode()).isEqualTo(config2.hashCode());
}

View file

@ -20,8 +20,7 @@ public class JoalConfigProviderTest {
public static final AppConfiguration defaultConfig = new AppConfiguration(
180,
190,
840,
600,
2,
"azureus-5.7.5.0.client"
);

View file

@ -17,6 +17,7 @@ import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Comparator;
import static org.assertj.core.api.Assertions.assertThat;
@ -65,7 +66,7 @@ public class TorrentFileProviderDITest {
@Test
public void shouldInjectConfigProvider() throws NoMoreTorrentsFileAvailableException {
assertThat(torrentFileProvider.getTorrentCount()).isEqualTo(1);
assertThat(torrentFileProvider.getRandomTorrentFile().getName()).isEqualTo("ubuntu-17.04-desktop-amd64.iso");
assertThat(torrentFileProvider.getTorrentNotIn(new ArrayList<>()).getName()).isEqualTo("ubuntu-17.04-desktop-amd64.iso");
}
}

View file

@ -1,11 +1,14 @@
package org.araymond.joal.core.torrent.watcher;
import org.araymond.joal.core.exception.NoMoreTorrentsFileAvailableException;
import org.araymond.joal.core.ttorent.client.MockedTorrent;
import org.araymond.joal.core.utils.TorrentFileCreator;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.context.ApplicationEventPublisher;
import java.io.File;
@ -15,11 +18,14 @@ import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.concurrent.CountDownLatch;
import static java.nio.file.Files.exists;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.fail;
/**
* Created by raymo on 13/03/2017.
@ -66,7 +72,7 @@ public class TorrentFileProviderTest {
public void shouldFailIfFolderDoesNotContainsTorrentFiles() throws IOException {
final TorrentFileProvider provider = new TorrentFileProvider(resourcePath.toString(), Mockito.mock(ApplicationEventPublisher.class));
assertThatThrownBy(provider::getRandomTorrentFile)
assertThatThrownBy(() -> provider.getTorrentNotIn(new ArrayList<>()))
.isInstanceOf(NoMoreTorrentsFileAvailableException.class)
.hasMessageContaining("No more torrent file available.");
}
@ -126,7 +132,7 @@ public class TorrentFileProviderTest {
assertThat(provider.getTorrentCount()).isEqualTo(1);
assertThat(archivedTorrentPath.resolve("ubuntu.torrent")).doesNotExist();
provider.moveToArchiveFolder(provider.getRandomTorrentFile());
provider.moveToArchiveFolder(provider.getTorrentNotIn(new ArrayList<>()));
assertThat(torrentsPath.resolve("ubuntu.torrent")).doesNotExist();
assertThat(archivedTorrentPath.resolve("ubuntu.torrent")).exists();
@ -147,5 +153,122 @@ public class TorrentFileProviderTest {
assertThat(archivedTorrentPath.resolve("ubuntu.torrent")).exists();
}
@Test
public void shouldNotFailIfFileIsNotPresentWhenArchiving() throws IOException {
final Path torrentFile = TorrentFileCreator.create(torrentsPath.resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
final TorrentFileProvider provider = new TorrentFileProvider(resourcePath.toString(), Mockito.mock(ApplicationEventPublisher.class));
try {
provider.moveToArchiveFolder(torrentFile.resolve("dd.torrent").toFile());
} catch (final Throwable throwable) {
fail("should not fail if file were not present.");
}
}
@Test
public void shouldCallOnFileDeleteBeforeDeletingFileWhenArchiving() throws IOException {
final Path torrentFile = TorrentFileCreator.create(torrentsPath.resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
final TorrentFileProvider provider = Mockito.spy(new TorrentFileProvider(resourcePath.toString(), Mockito.mock(ApplicationEventPublisher.class)));
Mockito.doAnswer(invocation -> {
assertThat(torrentFile.toFile()).exists();
return null;
}).when(provider).onFileDelete(torrentFile.toFile());
provider.onFileCreate(torrentFile.toFile());
provider.moveToArchiveFolder(torrentFile.toFile());
Mockito.verify(provider, Mockito.times(1)).moveToArchiveFolder(torrentFile.toFile());
assertThat(torrentFile.toFile()).doesNotExist();
}
@Test
public void shouldNotifyListenerOnFileAdded() throws IOException {
final Path torrentFile = TorrentFileCreator.create(torrentsPath.resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
final TorrentFileProvider provider = new TorrentFileProvider(resourcePath.toString(), Mockito.mock(ApplicationEventPublisher.class));
final CountDownLatch createLock = new CountDownLatch(1);
final CountDownLatch deleteLock = new CountDownLatch(1);
provider.registerListener(new CountDownLatchListener(createLock, deleteLock));
provider.postConstruct();
provider.onFileCreate(torrentFile.toFile());
assertThat(createLock.getCount()).isEqualTo(0);
assertThat(deleteLock.getCount()).isEqualTo(1);
provider.preDestroy();
}
@Test
public void shouldNotNotifyFileAddedBeforeInitIsOver() throws IOException {
final Path torrentFile = TorrentFileCreator.create(torrentsPath.resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
final TorrentFileProvider provider = new TorrentFileProvider(resourcePath.toString(), Mockito.mock(ApplicationEventPublisher.class));
final CountDownLatch createLock = new CountDownLatch(1);
final CountDownLatch deleteLock = new CountDownLatch(1);
provider.registerListener(new CountDownLatchListener(createLock, deleteLock));
provider.onFileCreate(torrentFile.toFile());
assertThat(createLock.getCount()).isEqualTo(1);
assertThat(deleteLock.getCount()).isEqualTo(1);
}
@Test
public void shouldNotifyListenerOnFileRemoved() throws IOException {
final Path torrentFile = TorrentFileCreator.create(torrentsPath.resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
final TorrentFileProvider provider = new TorrentFileProvider(resourcePath.toString(), Mockito.mock(ApplicationEventPublisher.class));
provider.onFileCreate(torrentFile.toFile());
final CountDownLatch createLock = new CountDownLatch(1);
final CountDownLatch deleteLock = new CountDownLatch(1);
provider.registerListener(new CountDownLatchListener(createLock, deleteLock));
provider.onFileDelete(torrentFile.toFile());
assertThat(createLock.getCount()).isEqualTo(1);
assertThat(deleteLock.getCount()).isEqualTo(0);
}
@Test
public void shouldNotifyListenerOnFileChanged() throws IOException {
final Path torrentFile = TorrentFileCreator.create(torrentsPath.resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
final TorrentFileProvider provider = new TorrentFileProvider(resourcePath.toString(), Mockito.mock(ApplicationEventPublisher.class));
final CountDownLatch createLock = new CountDownLatch(1);
final CountDownLatch deleteLock = new CountDownLatch(1);
provider.registerListener(new CountDownLatchListener(createLock, deleteLock));
provider.postConstruct();
provider.onFileChange(torrentFile.toFile());
assertThat(createLock.getCount()).isEqualTo(0);
assertThat(deleteLock.getCount()).isEqualTo(0);
provider.preDestroy();
}
private static final class CountDownLatchListener implements TorrentFileChangeAware {
private final CountDownLatch createLock;
private final CountDownLatch deleteLock;
private CountDownLatchListener(final CountDownLatch createLock, final CountDownLatch deleteLock) {
this.createLock = createLock;
this.deleteLock = deleteLock;
}
@Override
public void onTorrentAdded(final MockedTorrent torrent) {
createLock.countDown();
}
@Override
public void onTorrentRemoved(final MockedTorrent torrent) {
deleteLock.countDown();
}
}
}

View file

@ -1,7 +1,6 @@
{
"minUploadRate": 180,
"maxUploadRate": 190,
"seedFor": 840,
"waitBetweenSeed": 600,
"simultaneousSeed": 2,
"client": "azureus-5.7.5.0.client"
}