/*
* Copyright 2013-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.facebook.buck.util;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.facebook.buck.event.BuckEvent;
import com.facebook.buck.event.BuckEventBus;
import com.facebook.buck.event.BuckEventBusFactory;
import com.facebook.buck.event.FakeBuckEventListener;
import com.facebook.buck.event.WatchmanStatusEvent;
import com.facebook.buck.io.FakeWatchmanClient;
import com.facebook.buck.io.MorePaths;
import com.facebook.buck.io.PathOrGlobMatcher;
import com.facebook.buck.io.ProjectWatch;
import com.facebook.buck.io.Watchman;
import com.facebook.buck.io.WatchmanCursor;
import com.facebook.buck.io.WatchmanDiagnostic;
import com.facebook.buck.io.WatchmanDiagnosticEvent;
import com.facebook.buck.io.WatchmanDiagnosticEventListener;
import com.facebook.buck.io.WatchmanQuery;
import com.facebook.buck.timing.FakeClock;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class WatchmanWatcherTest {
private static final Path FAKE_ROOT = Paths.get("/fake/root").toAbsolutePath();
private static final WatchmanQuery FAKE_QUERY = WatchmanQuery.of("/fake/root", ImmutableMap.of());
private static final List<Object> FAKE_UUID_QUERY = FAKE_QUERY.toList("n:buckduuid");
private static final List<Object> FAKE_CLOCK_QUERY = FAKE_QUERY.toList("c:0:0");
private static final Path FAKE_SECONDARY_ROOT = Paths.get("/fake/secondary").toAbsolutePath();
private static final WatchmanQuery FAKE_SECONDARY_QUERY =
WatchmanQuery.of("/fake/SECONDARY", ImmutableMap.of());
private EventBus eventBus;
private EventBuffer eventBuffer;
@Before
public void setUp() {
eventBuffer = new EventBuffer();
eventBus = new EventBus();
eventBus.register(eventBuffer);
}
@After
public void cleanUp() {
// Clear interrupted state so it doesn't affect any other test.
Thread.interrupted();
}
@Test
public void whenFilesListIsEmptyThenNoEventsAreGenerated()
throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.of(
"version",
"2.9.2",
"clock",
"c:1386170113:26390:5:50273",
"is_fresh_instance",
false,
"files",
ImmutableList.of());
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.NONE);
assertTrue(eventBuffer.events.isEmpty());
}
@Test
public void whenNameThenModifyEventIsGenerated() throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.of(
"files", ImmutableList.of(ImmutableMap.<String, Object>of("name", "foo/bar/baz")));
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.NONE);
WatchmanPathEvent pathEvent = (WatchmanPathEvent) eventBuffer.getOnlyEvent();
assertEquals(WatchmanPathEvent.Kind.MODIFY, pathEvent.getKind());
assertEquals(
"Path should match watchman output.",
MorePaths.pathWithPlatformSeparators("foo/bar/baz"),
pathEvent.getPath().toString());
}
@Test
public void whenNewIsTrueThenCreateEventIsGenerated() throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.of(
"files",
ImmutableList.of(ImmutableMap.<String, Object>of("name", "foo/bar/baz", "new", true)));
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.NONE);
assertEquals(
"Should be create event.",
WatchmanPathEvent.Kind.CREATE,
((WatchmanPathEvent) eventBuffer.getOnlyEvent()).getKind());
}
@Test
public void whenExistsIsFalseThenDeleteEventIsGenerated()
throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.of(
"files",
ImmutableList.of(
ImmutableMap.<String, Object>of("name", "foo/bar/baz", "exists", false)));
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.NONE);
assertEquals(
"Should be delete event.",
WatchmanPathEvent.Kind.DELETE,
((WatchmanPathEvent) eventBuffer.getOnlyEvent()).getKind());
}
@Test
public void whenNewAndNotExistsThenDeleteEventIsGenerated()
throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.of(
"files",
ImmutableList.of(
ImmutableMap.<String, Object>of(
"name", "foo/bar/baz",
"new", true,
"exists", false)));
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.NONE);
assertEquals(
"Should be delete event.",
WatchmanPathEvent.Kind.DELETE,
((WatchmanPathEvent) eventBuffer.getOnlyEvent()).getKind());
}
@Test
public void whenMultipleFilesThenMultipleEventsGenerated()
throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.of(
"files",
ImmutableList.of(
ImmutableMap.<String, Object>of("name", "foo/bar/baz"),
ImmutableMap.<String, Object>of("name", "foo/bar/boz")));
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.NONE);
assertEquals(
"Path should match watchman output.",
MorePaths.pathWithPlatformSeparators("foo/bar/baz"),
((WatchmanPathEvent) eventBuffer.events.get(0)).getPath().toString());
assertEquals(
"Path should match watchman output.",
MorePaths.pathWithPlatformSeparators("foo/bar/boz"),
((WatchmanPathEvent) eventBuffer.events.get(1)).getPath().toString());
}
@Test
public void whenTooManyChangesThenOverflowEventGenerated()
throws IOException, InterruptedException {
ImmutableList.Builder<ImmutableMap<String, Object>> changedFiles =
new ImmutableList.Builder<>();
// The threshold is 10000; go a little above that.
for (int i = 0; i < 10010; i++) {
changedFiles.add(
ImmutableMap.<String, Object>of("name", "foo/bar/baz" + Integer.toString(i)));
}
ImmutableMap<String, Object> watchmanOutput = ImmutableMap.of("files", changedFiles.build());
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.NONE);
assertThat(eventBuffer.getOnlyEvent(), instanceOf(WatchmanOverflowEvent.class));
}
@Test
public void whenWatchmanFailsThenOverflowEventGenerated()
throws IOException, InterruptedException {
WatchmanWatcher watcher =
createWatcher(
eventBus,
new FakeWatchmanClient(
0 /* queryElapsedTimeNanos */,
ImmutableMap.of(FAKE_UUID_QUERY, ImmutableMap.of()),
new IOException("oops")),
10000 /* timeout */);
try {
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.NONE);
fail("Should have thrown IOException.");
} catch (IOException e) {
assertTrue("Should be expected error", e.getMessage().startsWith("oops"));
}
assertThat(eventBuffer.getOnlyEvent(), instanceOf(WatchmanOverflowEvent.class));
}
@Test
public void whenWatchmanInterruptedThenOverflowEventGenerated()
throws IOException, InterruptedException {
String message = "Boo!";
WatchmanWatcher watcher =
createWatcher(
eventBus,
new FakeWatchmanClient(
0 /* queryElapsedTimeNanos */,
ImmutableMap.of(FAKE_UUID_QUERY, ImmutableMap.of()),
new InterruptedException(message)),
10000 /* timeout */);
try {
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.NONE);
} catch (InterruptedException e) {
assertEquals("Should be test interruption.", e.getMessage(), message);
}
assertTrue(Thread.currentThread().isInterrupted());
assertThat(eventBuffer.getOnlyEvent(), instanceOf(WatchmanOverflowEvent.class));
}
@Test
public void whenQueryResultContainsErrorThenHumanReadableExceptionThrown()
throws IOException, InterruptedException {
String watchmanError = "Watch does not exist.";
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.of("version", "2.9.2", "error", watchmanError);
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
try {
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.NONE);
fail("Should have thrown RuntimeException");
} catch (RuntimeException e) {
assertThat(
"Should contain watchman error.", e.getMessage(), Matchers.containsString(watchmanError));
}
}
@Test(expected = WatchmanWatcherException.class)
public void whenQueryResultContainsErrorThenOverflowEventGenerated()
throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.of(
"version", "2.9.2",
"error", "Watch does not exist.");
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
try {
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.NONE);
} finally {
assertThat(eventBuffer.getOnlyEvent(), instanceOf(WatchmanOverflowEvent.class));
}
}
@Test
public void whenWatchmanInstanceIsFreshAndActionIsPostThenOverflowEventIsPosted()
throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.of(
"version",
"2.9.2",
"clock",
"c:1386170113:26390:5:50273",
"is_fresh_instance",
true,
"files",
ImmutableList.of());
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.POST_OVERFLOW_EVENT);
assertThat(
"should have overflow event",
eventBuffer.getOnlyEvent(),
instanceOf(WatchmanOverflowEvent.class));
}
@Test
public void whenWatchmanInstanceIsFreshAndActionIsNoneThenNoEventIsPosted()
throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.of(
"version",
"2.9.2",
"clock",
"c:1386170113:26390:5:50273",
"is_fresh_instance",
true,
"files",
ImmutableList.of());
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.NONE);
assertTrue("no events were posted", eventBuffer.events.isEmpty());
}
@Test
public void whenParseTimesOutThenOverflowGenerated() throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.of(
"version",
"2.9.2",
"clock",
"c:1386170113:26390:5:50273",
"is_fresh_instance",
true,
"files",
ImmutableList.of());
WatchmanWatcher watcher =
createWatcher(
eventBus,
new FakeWatchmanClient(
10000000000L /* queryElapsedTimeNanos */,
ImmutableMap.of(FAKE_UUID_QUERY, watchmanOutput)),
-1 /* timeout */);
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.NONE);
assertThat(
"should have overflow event",
eventBuffer.getOnlyEvent(),
instanceOf(WatchmanOverflowEvent.class));
}
@Test
public void watchmanQueryWithRepoRelativePrefix() {
WatchmanQuery query =
WatchmanWatcher.createQuery(
ProjectWatch.of("path/to/repo", Optional.of("project")),
ImmutableSet.of(),
ImmutableSet.of(Watchman.Capability.DIRNAME));
assertThat(query.toList(""), hasItem(hasEntry("relative_root", "project")));
}
@Test
public void watchmanQueryWithExcludePathsAddsExpressionToQuery() {
WatchmanQuery query =
WatchmanWatcher.createQuery(
ProjectWatch.of("/path/to/repo", Optional.empty()),
ImmutableSet.of(
new PathOrGlobMatcher(Paths.get("foo")),
new PathOrGlobMatcher(Paths.get("bar/baz"))),
ImmutableSet.of(Watchman.Capability.DIRNAME));
assertEquals(
WatchmanQuery.of(
"/path/to/repo",
ImmutableMap.of(
"expression",
ImmutableList.of(
"not",
ImmutableList.of(
"anyof",
ImmutableList.of("type", "d"),
ImmutableList.of("dirname", "foo"),
ImmutableList.of(
"dirname", MorePaths.pathWithPlatformSeparators("bar/baz")))),
"empty_on_fresh_instance", true,
"fields", ImmutableList.of("name", "exists", "new"))),
query);
}
@Test
public void watchmanQueryWithExcludePathsAddsMatchExpressionToQueryIfDirnameNotAvailable() {
WatchmanQuery query =
WatchmanWatcher.createQuery(
ProjectWatch.of("/path/to/repo", Optional.empty()),
ImmutableSet.of(
new PathOrGlobMatcher(Paths.get("foo")),
new PathOrGlobMatcher(Paths.get("bar/baz"))),
ImmutableSet.of());
assertEquals(
WatchmanQuery.of(
"/path/to/repo",
ImmutableMap.of(
"expression",
ImmutableList.of(
"not",
ImmutableList.of(
"anyof",
ImmutableList.of("type", "d"),
ImmutableList.of("match", "foo" + File.separator + "*", "wholename"),
ImmutableList.of(
"match",
"bar" + File.separator + "baz" + File.separator + "*",
"wholename"))),
"empty_on_fresh_instance", true,
"fields", ImmutableList.of("name", "exists", "new"))),
query);
}
@Test
public void watchmanQueryRelativizesExcludePaths() {
String watchRoot = Paths.get("/path/to/repo").toAbsolutePath().toString();
WatchmanQuery query =
WatchmanWatcher.createQuery(
ProjectWatch.of(watchRoot, Optional.empty()),
ImmutableSet.of(
new PathOrGlobMatcher(Paths.get("/path/to/repo/foo").toAbsolutePath()),
new PathOrGlobMatcher(Paths.get("/path/to/repo/bar/baz").toAbsolutePath())),
ImmutableSet.of(Watchman.Capability.DIRNAME));
assertEquals(
WatchmanQuery.of(
watchRoot,
ImmutableMap.of(
"expression",
ImmutableList.of(
"not",
ImmutableList.of(
"anyof",
ImmutableList.of("type", "d"),
ImmutableList.of("dirname", "foo"),
ImmutableList.of(
"dirname", MorePaths.pathWithPlatformSeparators("bar/baz")))),
"empty_on_fresh_instance", true,
"fields", ImmutableList.of("name", "exists", "new"))),
query);
}
@Test
public void watchmanQueryWithExcludeGlobsAddsExpressionToQuery() {
WatchmanQuery query =
WatchmanWatcher.createQuery(
ProjectWatch.of("/path/to/repo", Optional.empty()),
ImmutableSet.of(new PathOrGlobMatcher("*.pbxproj")),
ImmutableSet.of(Watchman.Capability.DIRNAME));
assertEquals(
WatchmanQuery.of(
"/path/to/repo",
ImmutableMap.of(
"expression",
ImmutableList.of(
"not",
ImmutableList.of(
"anyof",
ImmutableList.of("type", "d"),
ImmutableList.of(
"match",
"*.pbxproj",
"wholename",
ImmutableMap.<String, Object>of("includedotfiles", true)))),
"empty_on_fresh_instance", true,
"fields", ImmutableList.of("name", "exists", "new"))),
query);
}
@Test
public void whenWatchmanProducesAWarningThenOverflowEventNotGenerated()
throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.of("files", ImmutableList.of(), "warning", "message");
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.NONE);
assertTrue(eventBuffer.events.isEmpty());
}
@Test
public void whenWatchmanProducesAWarningThenDiagnosticEventGenerated()
throws IOException, InterruptedException {
String message = "Find me!";
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.of("files", ImmutableList.of(), "warning", message);
BuckEventBus buckEventBus = BuckEventBusFactory.newInstance();
FakeBuckEventListener listener = new FakeBuckEventListener();
buckEventBus.register(listener);
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
watcher.postEvents(buckEventBus, WatchmanWatcher.FreshInstanceAction.NONE);
ImmutableList<WatchmanDiagnosticEvent> diagnostics =
RichStream.from(listener.getEvents())
.filter(WatchmanDiagnosticEvent.class)
.toImmutableList();
assertThat(diagnostics, hasSize(1));
assertThat(diagnostics.get(0).getDiagnostic().getMessage(), Matchers.containsString(message));
}
@Test
public void whenWatchmanProducesAWarningThenWarningAddedToCache()
throws IOException, InterruptedException {
String message = "I'm a warning!";
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.of("files", ImmutableList.of(), "warning", message);
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
Set<WatchmanDiagnostic> diagnostics = new HashSet<>();
BuckEventBus buckEventBus = BuckEventBusFactory.newInstance(new FakeClock(0));
buckEventBus.register(new WatchmanDiagnosticEventListener(buckEventBus, diagnostics));
watcher.postEvents(buckEventBus, WatchmanWatcher.FreshInstanceAction.NONE);
assertThat(
diagnostics, hasItem(WatchmanDiagnostic.of(WatchmanDiagnostic.Level.WARNING, message)));
}
@Test
public void watcherInsertsAndUpdatesClockId() throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.<String, Object>of("clock", "c:0:1", "files", ImmutableList.of());
WatchmanWatcher watcher =
createWatcher(
eventBus,
new FakeWatchmanClient(
0 /* queryElapsedTimeNanos */, ImmutableMap.of(FAKE_CLOCK_QUERY, watchmanOutput)),
10000 /* timeout */,
"c:0:0" /* sinceParam */);
assertThat(watcher.getWatchmanQuery(FAKE_ROOT), hasItem(hasEntry("since", "c:0:0")));
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.POST_OVERFLOW_EVENT);
assertThat(watcher.getWatchmanQuery(FAKE_ROOT), hasItem(hasEntry("since", "c:0:1")));
}
@Test
public void watcherOverflowUpdatesClockId() throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanOutput =
ImmutableMap.<String, Object>of("clock", "c:1:0", "is_fresh_instance", true);
WatchmanWatcher watcher =
createWatcher(
eventBus,
new FakeWatchmanClient(
0 /* queryElapsedTimeNanos */, ImmutableMap.of(FAKE_CLOCK_QUERY, watchmanOutput)),
10000 /* timeout */,
"c:0:0" /* sinceParam */);
assertThat(watcher.getWatchmanQuery(FAKE_ROOT), hasItem(hasEntry("since", "c:0:0")));
watcher.postEvents(
BuckEventBusFactory.newInstance(new FakeClock(0)),
WatchmanWatcher.FreshInstanceAction.POST_OVERFLOW_EVENT);
assertThat(watcher.getWatchmanQuery(FAKE_ROOT), hasItem(hasEntry("since", "c:1:0")));
assertThat(
"should have overflow event",
eventBuffer.getOnlyEvent(),
instanceOf(WatchmanOverflowEvent.class));
}
@Test
public void whenWatchmanReportsZeroFilesChangedThenPostEvent()
throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanOutput = ImmutableMap.of("files", ImmutableList.of());
WatchmanWatcher watcher = createWatcher(eventBus, watchmanOutput);
final Set<BuckEvent> events = Sets.newHashSet();
BuckEventBus bus = BuckEventBusFactory.newInstance(new FakeClock(0));
bus.register(
new Object() {
@Subscribe
public void listen(WatchmanStatusEvent event) {
events.add(event);
}
});
watcher.postEvents(bus, WatchmanWatcher.FreshInstanceAction.POST_OVERFLOW_EVENT);
boolean zeroFilesChangedSeen = false;
System.err.println(String.format("Events: %d", events.size()));
for (BuckEvent event : events) {
zeroFilesChangedSeen |= event.getEventName().equals("WatchmanZeroFileChanges");
}
assertTrue(zeroFilesChangedSeen);
}
@Test
public void whenWatchmanCellReportsFilesChangedThenPostEvent()
throws IOException, InterruptedException {
ImmutableMap<String, Object> watchmanRootOutput = ImmutableMap.of("files", ImmutableList.of());
ImmutableMap<String, Object> watchmanSecondaryOutput =
ImmutableMap.of(
"files", ImmutableList.of(ImmutableMap.<String, Object>of("name", "foo/bar/baz")));
WatchmanWatcher watcher =
new WatchmanWatcher(
eventBus,
new FakeWatchmanClient(
0,
ImmutableMap.of(
FAKE_CLOCK_QUERY,
watchmanRootOutput,
FAKE_SECONDARY_QUERY.toList("c:0:0"),
watchmanSecondaryOutput)),
10000,
ImmutableMap.of(
FAKE_ROOT, FAKE_QUERY,
FAKE_SECONDARY_ROOT, FAKE_SECONDARY_QUERY),
ImmutableMap.of(
FAKE_ROOT, new WatchmanCursor("c:0:0"),
FAKE_SECONDARY_ROOT, new WatchmanCursor("c:0:0")));
final Set<BuckEvent> events = Sets.newHashSet();
BuckEventBus bus = BuckEventBusFactory.newInstance(new FakeClock(0));
bus.register(
new Object() {
@Subscribe
public void listen(WatchmanStatusEvent event) {
events.add(event);
}
});
watcher.postEvents(bus, WatchmanWatcher.FreshInstanceAction.POST_OVERFLOW_EVENT);
boolean zeroFilesChangedSeen = false;
System.err.println(String.format("Events: %d", events.size()));
for (BuckEvent event : events) {
System.err.println(String.format("Event: %s", event));
zeroFilesChangedSeen |= event.getEventName().equals("WatchmanZeroFileChanges");
}
assertFalse(zeroFilesChangedSeen);
}
private WatchmanWatcher createWatcher(
EventBus eventBus, ImmutableMap<String, ? extends Object> response) {
return createWatcher(
eventBus,
new FakeWatchmanClient(
0 /* queryElapsedTimeNanos */, ImmutableMap.of(FAKE_UUID_QUERY, response)),
10000 /* timeout */);
}
private WatchmanWatcher createWatcher(
EventBus eventBus, FakeWatchmanClient watchmanClient, long timeoutMillis) {
return createWatcher(eventBus, watchmanClient, timeoutMillis, "n:buckduuid" /* sinceCursor */);
}
private WatchmanWatcher createWatcher(
EventBus eventBus,
FakeWatchmanClient watchmanClient,
long timeoutMillis,
String sinceCursor) {
return new WatchmanWatcher(
eventBus,
watchmanClient,
timeoutMillis,
ImmutableMap.of(FAKE_ROOT, FAKE_QUERY),
ImmutableMap.of(FAKE_ROOT, new WatchmanCursor(sinceCursor)));
}
private static class EventBuffer {
public final List<WatchmanEvent> events = new ArrayList<>();
@Subscribe
public void on(WatchmanEvent event) {
events.add(event);
}
/** Helper to retrieve the only event that should be in the list. */
public WatchmanEvent getOnlyEvent() {
assertEquals("Should contain only one event", 1, events.size());
return events.get(0);
}
}
}