// Path: src/test/java/mirror/TestUtils.java
// public static void move(final String from, final String to) {
//   new Execute(new String[] { "mv", from, to }).toSystemOut();
// }
// 
// Path: src/test/java/mirror/TestUtils.java
// public static void writeFile(final File file, final String data) throws IOException {
//   FileUtils.writeStringToFile(file, data, UTF_8);
// }
// 
// Path: src/main/java/mirror/tasks/TaskFactory.java
// public interface TaskFactory {
// 
//   default TaskHandle runTask(TaskLogic logic) {
//     return runTask(logic, null);
//   }
// 
//   TaskHandle runTask(TaskLogic logic, Runnable onFailure);
// 
//   void stopTask(TaskLogic logic);
// 
//   default TaskPool newTaskPool() {
//     return new TaskPool(this);
//   }
// 
// }
// 
// Path: src/main/java/mirror/tasks/ThreadBasedTaskFactory.java
// public class ThreadBasedTaskFactory implements TaskFactory {
// 
//   private final ConcurrentHashMap<TaskLogic, ThreadBasedTask> tasks = new ConcurrentHashMap<>();
// 
//   @Override
//   public TaskHandle runTask(TaskLogic logic, Runnable onFailure) {
//     ThreadBasedTask task = new ThreadBasedTask(logic, onFailure, () -> {
//       // we don't need to call stopTask(logic) or task.stop because the task is already stopping,
//       // we just to ensure we remove this entry from our map to avoid memory leaks
//       tasks.remove(logic);
//     });
//     tasks.put(logic, task);
//     task.start();
//     return () -> stopTask(logic);
//   }
// 
//   // Note that this method isn't synchronized, as while shutting down one task,
//   // while we block on task.stop completing, the task might ask us to shut down
//   // one of it's child tasks, but from it's own thread, which would block.
//   @Override
//   public void stopTask(TaskLogic logic) {
//     ThreadBasedTask task = tasks.get(logic);
//     if (task != null) {
//       tasks.remove(logic);
//       task.stop();
//     }
//   }
// 
// }


import static mirror.TestUtils.move;
import static mirror.TestUtils.writeFile;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import java.io.File;
import java.nio.file.FileSystems;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.apache.commons.io.FileUtils;
import org.jooq.lambda.Seq;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import mirror.tasks.TaskFactory;
import mirror.tasks.ThreadBasedTaskFactory;
package mirror;





/**
 * Tests {@link WatchServiceFileWatcher}.
 *
 * Currently ignored because when files are created, e.g. foo.txt,
 * {@link WatchServiceFileWatcher} will sometimes send one update
 * (create) and sometimes send two.
 * 
 * This is just what the underlying inotify events are, so it is not
 * wrong, but it makes test assertions annoying.
 */
@Ignore
public class WatchServiceFileWatcherTest {

  static {
    LoggingConfig.init();
  }
  private static final File dir = new File("./build/FileWatcherTest");
  private final TaskFactory taskFactory = new ThreadBasedTaskFactory();
  private final BlockingQueue<Update> queue = new ArrayBlockingQueue<>(100);
  private FileWatcher watcher;

  @Before
  public void clearFiles() throws Exception {
    if (dir.exists()) {
      FileUtils.forceDelete(dir);
    }
    dir.mkdirs();
    watcher = new WatchServiceFileWatcher(taskFactory, FileSystems.getDefault().newWatchService(), dir.toPath(), queue);
    watcher.performInitialScan();
    taskFactory.runTask(watcher);
  }

  @After
  public void stopWatcher() throws Exception {
    taskFactory.stopTask(watcher);
  }

  @Test
  public void testDirectoryRename() throws Exception {
    // given a directory is created
    File dir1 = new File(dir, "dir1");
    dir1.mkdir();
    // and then renamed
    File dir2 = new File(dir, "dir2");
// Path: src/test/java/mirror/TestUtils.java
// public static void move(final String from, final String to) {
//   new Execute(new String[] { "mv", from, to }).toSystemOut();
// }
// 
// Path: src/test/java/mirror/TestUtils.java
// public static void writeFile(final File file, final String data) throws IOException {
//   FileUtils.writeStringToFile(file, data, UTF_8);
// }
// 
// Path: src/main/java/mirror/tasks/TaskFactory.java
// public interface TaskFactory {
// 
//   default TaskHandle runTask(TaskLogic logic) {
//     return runTask(logic, null);
//   }
// 
//   TaskHandle runTask(TaskLogic logic, Runnable onFailure);
// 
//   void stopTask(TaskLogic logic);
// 
//   default TaskPool newTaskPool() {
//     return new TaskPool(this);
//   }
// 
// }
// 
// Path: src/main/java/mirror/tasks/ThreadBasedTaskFactory.java
// public class ThreadBasedTaskFactory implements TaskFactory {
// 
//   private final ConcurrentHashMap<TaskLogic, ThreadBasedTask> tasks = new ConcurrentHashMap<>();
// 
//   @Override
//   public TaskHandle runTask(TaskLogic logic, Runnable onFailure) {
//     ThreadBasedTask task = new ThreadBasedTask(logic, onFailure, () -> {
//       // we don't need to call stopTask(logic) or task.stop because the task is already stopping,
//       // we just to ensure we remove this entry from our map to avoid memory leaks
//       tasks.remove(logic);
//     });
//     tasks.put(logic, task);
//     task.start();
//     return () -> stopTask(logic);
//   }
// 
//   // Note that this method isn't synchronized, as while shutting down one task,
//   // while we block on task.stop completing, the task might ask us to shut down
//   // one of it's child tasks, but from it's own thread, which would block.
//   @Override
//   public void stopTask(TaskLogic logic) {
//     ThreadBasedTask task = tasks.get(logic);
//     if (task != null) {
//       tasks.remove(logic);
//       task.stop();
//     }
//   }
// 
// }


// Path: src/test/java/mirror/WatchServiceFileWatcherTest.java
import static mirror.TestUtils.move;
import static mirror.TestUtils.writeFile;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import java.io.File;
import java.nio.file.FileSystems;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.apache.commons.io.FileUtils;
import org.jooq.lambda.Seq;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import mirror.tasks.TaskFactory;
import mirror.tasks.ThreadBasedTaskFactory;

package mirror;





/**
 * Tests {@link WatchServiceFileWatcher}.
 *
 * Currently ignored because when files are created, e.g. foo.txt,
 * {@link WatchServiceFileWatcher} will sometimes send one update
 * (create) and sometimes send two.
 * 
 * This is just what the underlying inotify events are, so it is not
 * wrong, but it makes test assertions annoying.
 */
@Ignore
public class WatchServiceFileWatcherTest {

  static {
    LoggingConfig.init();
  }
  private static final File dir = new File("./build/FileWatcherTest");
  private final TaskFactory taskFactory = new ThreadBasedTaskFactory();
  private final BlockingQueue<Update> queue = new ArrayBlockingQueue<>(100);
  private FileWatcher watcher;

  @Before
  public void clearFiles() throws Exception {
    if (dir.exists()) {
      FileUtils.forceDelete(dir);
    }
    dir.mkdirs();
    watcher = new WatchServiceFileWatcher(taskFactory, FileSystems.getDefault().newWatchService(), dir.toPath(), queue);
    watcher.performInitialScan();
    taskFactory.runTask(watcher);
  }

  @After
  public void stopWatcher() throws Exception {
    taskFactory.stopTask(watcher);
  }

  @Test
  public void testDirectoryRename() throws Exception {
    // given a directory is created
    File dir1 = new File(dir, "dir1");
    dir1.mkdir();
    // and then renamed
    File dir2 = new File(dir, "dir2");
    move(dir1.toString(), dir2.toString());