### Question:
FileChangeMonitor implements FileMonitor { @Override public void start(Listener listener) { LOGGER.debug("start, initialDelay={}, pollPeriod={}", new Object[] {initialDelay, pollPeriod}); synchronized (this) { if (monitoredTask != null) { String message = format("File monitor for '%s' is already started", monitoredFile); throw new IllegalStateException(message); } monitoredTask = executor.scheduleAtFixedRate( detectFileChangesTask(listener), initialDelay.toMillis(), pollPeriod.toMillis(), MILLISECONDS); } } FileChangeMonitor(String monitoredFile, Duration initialDelay, Duration pollPeriod);  FileChangeMonitor(String monitoredFile); @Override void start(Listener listener); void stop();  }### Answer:
@Test public void canBeStartedOnlyOnce() { FileChangeMonitor.Listener listener = mock(FileChangeMonitor.Listener.class); FileChangeMonitor monitor = new FileChangeMonitor(monitoredFile.toString()); monitor.start(listener); Exception e = assertThrows(IllegalStateException.class, () -> monitor.start(listener)); assertThat(e.getMessage(), matchesPattern("File monitor for '.*' is already started")); }
@Test public void initialPollNotifiesListeners() { monitor.start(listener); verify(listener, timeout(3000).times(1)).fileChanged(); }
@Test public void notifiesListenersOnFileChange() throws Exception { monitor.start(listener); verify(listener, timeout(3000).times(1)).fileChanged(); Thread.sleep(250); for (int i = 2; i < 10; i++) { write(monitoredFile, format("content-v%d", i)); verify(listener, timeout(3000).times(i)).fileChanged(); LOGGER.info(format("verified v%d", i)); } }
@Test public void recoversFromFileDeletions() throws Exception { monitor.start(listener); verify(listener, timeout(3000).times(1)).fileChanged(); delete(monitoredFile); Thread.sleep(2000); write(monitoredFile, "some new content"); verify(listener, timeout(3000).times(2)).fileChanged(); }
@Test public void detectsFileSizeChanges() throws Exception { monitor.start(listener); verify(listener, timeout(3000).times(1)).fileChanged(); }
@Test public void recoversFromTruncatedFiles() throws Exception { monitor.start(listener); verify(listener, timeout(3000).times(1)).fileChanged(); write(monitoredFile, ""); verify(listener, timeout(3000).times(2)).fileChanged(); write(monitoredFile, "some new content"); verify(listener, timeout(3000).times(3)).fileChanged(); }