content: package org.araymond.joal.core.torrent.watcher;
  
  import org.araymond.joal.core.SeedManager;
  import org.araymond.joal.core.exception.NoMoreTorrentsFileAvailableException;
  import org.araymond.joal.core.torrent.torrent.MockedTorrent;
  import org.araymond.joal.core.utils.TorrentFileCreator;
  import org.junit.jupiter.api.AfterEach;
  import org.junit.jupiter.api.BeforeEach;
  import org.junit.jupiter.api.Test;
  import org.mockito.Mockito;
  
  import java.io.File;
  import java.io.FileNotFoundException;
  import java.io.IOException;
  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.*;
  
  /**
   * Created by raymo on 13/03/2017.
   */
  public class TorrentFileProviderTest {
  
      private static final SeedManager.JoalFoldersPath joalFoldersPath = new SeedManager.JoalFoldersPath(Paths.get("src/test/resources/configtest"));
  
      private void resetDirectories() throws IOException {
          if (Files.exists(joalFoldersPath.getTorrentsDirPath())) {
              Files.walk(joalFoldersPath.getTorrentsDirPath(), FileVisitOption.FOLLOW_LINKS)
                      .sorted(Comparator.reverseOrder())
                      .map(Path::toFile)
                      .forEach(File::delete);
          }
          Files.createDirectory(joalFoldersPath.getTorrentsDirPath());
      }
  
      @AfterEach
      @BeforeEach
      public void setUpAndTearDown() throws IOException {
          resetDirectories();
      }
  
      @Test
      public void shouldNotBuildIfFolderDoesNotExists() {
          assertThatThrownBy(() -> new TorrentFileProvider(new SeedManager.JoalFoldersPath(Paths.get("nop"))))
                  .isInstanceOf(FileNotFoundException.class)
                  .hasMessageMatching("^Torrent folder \\[.*] not found$");
      }
  
      @Test
      public void shouldCreateArchiveFolderIfNotCreatedAlready() throws IOException {
          Files.deleteIfExists(joalFoldersPath.getTorrentArchiveDirPath());
          assertThat(exists(joalFoldersPath.getTorrentArchiveDirPath())).isFalse();
          new TorrentFileProvider(joalFoldersPath).init();
          assertThat(exists(joalFoldersPath.getTorrentArchiveDirPath())).isTrue();
      }
  
      @Test
      public void shouldFailIfFolderDoesNotContainsTorrentFiles() throws IOException {
          final TorrentFileProvider provider = new TorrentFileProvider(joalFoldersPath);
  
          assertThatThrownBy(() -> provider.getTorrentNotIn(new ArrayList<>()))
                  .isInstanceOf(NoMoreTorrentsFileAvailableException.class)
                  .hasMessageContaining("No more torrent files available");
      }
  
      @Test
      public void shouldAddFileToListOnCreation() throws IOException {
          TorrentFileCreator.create(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
          final TorrentFileProvider provider = new TorrentFileProvider(joalFoldersPath);
          assertThat(provider.getTorrentCount()).isEqualTo(0);
  
          provider.onFileCreate(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent").toFile());
          assertThat(provider.getTorrentCount()).isEqualTo(1);
      }
  
      @Test
      public void shouldNotAddDuplicatedFiles() throws IOException {
          TorrentFileCreator.create(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
          final TorrentFileProvider provider = new TorrentFileProvider(joalFoldersPath);
          assertThat(provider.getTorrentCount()).isEqualTo(0);
  
          provider.onFileCreate(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent").toFile());
          provider.onFileCreate(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent").toFile());
          assertThat(provider.getTorrentCount()).isEqualTo(1);
      }
  
      @Test
      public void shouldRemoveFileFromListOnDeletion() throws IOException {
          final TorrentFileProvider provider = new TorrentFileProvider(joalFoldersPath);
          TorrentFileCreator.create(joalFoldersPath.getTorrentsDirPath().resolve("ninja.torrent"), TorrentFileCreator.TorrentType.NINJA_HEAT);
  
          assertThat(provider.getTorrentCount()).isEqualTo(0);
          provider.onFileCreate(joalFoldersPath.getTorrentsDirPath().resolve("ninja.torrent").toFile());
          assertThat(provider.getTorrentCount()).isEqualTo(1);
  
          provider.onFileDelete(joalFoldersPath.getTorrentsDirPath().resolve("ninja.torrent").toFile());
          assertThat(provider.getTorrentCount()).isEqualTo(0);
      }
  
      @Test
      public void shouldRemoveThenAddFileToListOnUpdate() throws IOException {
          TorrentFileCreator.create(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
          final TorrentFileProvider provider = new TorrentFileProvider(joalFoldersPath);
  
          provider.onFileCreate(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent").toFile());
          assertThat(provider.getTorrentCount()).isEqualTo(1);
  
          provider.onFileChange(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent").toFile());
          assertThat(provider.getTorrentCount()).isEqualTo(1);
      }
  
      @Test
      public void shouldMoveTorrentFileToArchivedFolderFromInfoHash() throws IOException, NoMoreTorrentsFileAvailableException {
          TorrentFileCreator.create(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
  
          final TorrentFileProvider provider = new TorrentFileProvider(joalFoldersPath);
          provider.init();
          provider.onFileCreate(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent").toFile());
          assertThat(provider.getTorrentCount()).isEqualTo(1);
  
          assertThat(joalFoldersPath.getTorrentArchiveDirPath().resolve("ubuntu.torrent")).doesNotExist();
          provider.moveToArchiveFolder(provider.getTorrentFiles().get(0).getTorrentInfoHash());
          assertThat(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent")).doesNotExist();
  
          assertThat(joalFoldersPath.getTorrentArchiveDirPath().resolve("ubuntu.torrent")).exists();
      }
  
      @Test
      public void shouldMoveTorrentFileToArchivedFolder() throws IOException {
          final Path torrentFile = TorrentFileCreator.create(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
  
          final TorrentFileProvider provider = new TorrentFileProvider(joalFoldersPath);
          provider.init();
          provider.onFileCreate(torrentFile.toFile());
          assertThat(provider.getTorrentCount()).isEqualTo(1);
  
          assertThat(joalFoldersPath.getTorrentArchiveDirPath().resolve("ubuntu.torrent")).doesNotExist();
          provider.moveToArchiveFolder(torrentFile.toFile());
          assertThat(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent")).doesNotExist();
  
          assertThat(joalFoldersPath.getTorrentArchiveDirPath().resolve("ubuntu.torrent")).exists();
      }
  
      @Test
      public void shouldNotFailIfFileIsNotPresentWhenArchiving() throws IOException {
          final Path torrentFile = TorrentFileCreator.create(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
  
          final TorrentFileProvider provider = new TorrentFileProvider(joalFoldersPath);
  
          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(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
  
          final TorrentFileProvider provider = Mockito.spy(new TorrentFileProvider(joalFoldersPath));
          provider.init();
          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(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
  
          final TorrentFileProvider provider = new TorrentFileProvider(joalFoldersPath);
          provider.start();
  
  
          final CountDownLatch createLock = new CountDownLatch(1);
          final CountDownLatch deleteLock = new CountDownLatch(1);
          final TorrentFileChangeAware listener = new CountDownLatchListener(createLock, deleteLock);
          provider.registerListener(listener);
  
          provider.onFileCreate(torrentFile.toFile());
  
          assertThat(createLock.getCount()).isEqualTo(0);
          assertThat(deleteLock.getCount()).isEqualTo(1);
          provider.stop();
          provider.unRegisterListener(listener);
      }
  
      @Test
      public void shouldNotifyListenerOnFileRemoved() throws IOException {
          final Path torrentFile = TorrentFileCreator.create(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
  
          final TorrentFileProvider provider = new TorrentFileProvider(joalFoldersPath);
          provider.onFileCreate(torrentFile.toFile());
  
          final CountDownLatch createLock = new CountDownLatch(1);
          final CountDownLatch deleteLock = new CountDownLatch(1);
          final TorrentFileChangeAware listener = new CountDownLatchListener(createLock, deleteLock);
          provider.registerListener(listener);
  
          provider.onFileDelete(torrentFile.toFile());
          provider.unRegisterListener(listener);
  
          assertThat(createLock.getCount()).isEqualTo(1);
          assertThat(deleteLock.getCount()).isEqualTo(0);
      }
  
      @Test
      public void shouldNotifyListenerOnFileChanged() throws IOException {
          final Path torrentFile = TorrentFileCreator.create(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
  
          final TorrentFileProvider provider = new TorrentFileProvider(joalFoldersPath);
          provider.start();
  
          final CountDownLatch createLock = new CountDownLatch(1);
          final CountDownLatch deleteLock = new CountDownLatch(1);
          final TorrentFileChangeAware listener = new CountDownLatchListener(createLock, deleteLock);
          provider.registerListener(listener);
  
          provider.onFileChange(torrentFile.toFile());
  
          assertThat(createLock.getCount()).isEqualTo(0);
          assertThat(deleteLock.getCount()).isEqualTo(0);
          provider.stop();
          provider.unRegisterListener(listener);
      }
  
      @Test
      public void shouldUnRegisterListener() throws IOException {
          final Path torrentFile = TorrentFileCreator.create(joalFoldersPath.getTorrentsDirPath().resolve("ubuntu.torrent"), TorrentFileCreator.TorrentType.UBUNTU);
          final Path torrentFile2 = TorrentFileCreator.create(joalFoldersPath.getTorrentsDirPath().resolve("audio.torrent"), TorrentFileCreator.TorrentType.AUDIO);
  
          final TorrentFileProvider provider = new TorrentFileProvider(joalFoldersPath);
          provider.start();
  
          final CountDownLatch createLock = new CountDownLatch(2);
          final CountDownLatch deleteLock = new CountDownLatch(2);
          final TorrentFileChangeAware listener = new CountDownLatchListener(createLock, deleteLock);
          provider.registerListener(listener);
  
          provider.onFileCreate(torrentFile.toFile());
          provider.unRegisterListener(listener);
          assertThat(createLock.getCount()).isEqualTo(1);
          provider.onFileCreate(torrentFile2.toFile());
  
          assertThat(createLock.getCount()).isEqualTo(1);
          assertThat(deleteLock.getCount()).isEqualTo(2);
          provider.stop();
          provider.unRegisterListener(listener);
      }
  
  
      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 onTorrentFileAdded(final MockedTorrent torrent) {
              createLock.countDown();
          }
  
          @Override
          public void onTorrentFileRemoved(final MockedTorrent torrent) {
              deleteLock.countDown();
          }
  
      }
  
  }
  