/*
* Copyright 2015-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.io;
import static org.junit.Assert.assertEquals;
import com.facebook.buck.bser.BserSerializer;
import com.facebook.buck.testutil.TestConsole;
import com.facebook.buck.timing.SettableFakeClock;
import com.facebook.buck.util.FakeListeningProcessExecutor;
import com.facebook.buck.util.FakeListeningProcessState;
import com.facebook.buck.util.ProcessExecutorParams;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
public class WatchmanTest {
private String root = Paths.get("/some/root").toAbsolutePath().toString();
private ImmutableSet<Path> rootPaths = ImmutableSet.of(Paths.get(root));
private String exe = Paths.get("/opt/bin/watchman").toAbsolutePath().toString();
private FakeExecutableFinder finder = new FakeExecutableFinder(Paths.get(exe));
private ImmutableMap<String, String> env = ImmutableMap.of();
private static final ImmutableList<Object> VERSION_QUERY =
ImmutableList.of(
"version",
ImmutableMap.of(
"required", ImmutableSet.of("cmd-watch-project"),
"optional",
ImmutableSet.of(
"term-dirname",
"cmd-watch-project",
"wildmatch",
"wildmatch_multislash",
"glob_generator",
"clock-sync-timeout")));
private static final Function<Path, Optional<WatchmanClient>> NULL_WATCHMAN_CONNECTOR =
path -> Optional.empty();
private static Function<Path, Optional<WatchmanClient>> fakeWatchmanConnector(
final Path socketName,
final long queryElapsedTimeNanos,
final Map<? extends List<? extends Object>, ? extends Map<String, ? extends Object>>
queryResults) {
return path -> {
if (!path.equals(socketName)) {
System.err.format("bad path (%s != %s", path, socketName);
return Optional.empty();
}
return Optional.of(new FakeWatchmanClient(queryElapsedTimeNanos, queryResults));
};
}
private static ByteBuffer bserSerialized(Object obj) throws IOException {
ByteBuffer buf = ByteBuffer.allocate(256).order(ByteOrder.nativeOrder());
ByteBuffer result = new BserSerializer().serializeToBuffer(obj, buf);
// Prepare the buffer for reading.
result.flip();
return result;
}
@Test
public void shouldReturnEmptyWatchmanIfVersionCheckFails()
throws InterruptedException, IOException {
SettableFakeClock clock = new SettableFakeClock(0, 0);
FakeListeningProcessExecutor executor =
new FakeListeningProcessExecutor(
ImmutableMultimap.<ProcessExecutorParams, FakeListeningProcessState>builder()
.putAll(
ProcessExecutorParams.ofCommand(exe, "--output-encoding=bser", "get-sockname"),
FakeListeningProcessState.ofExit(1))
.build(),
clock);
Watchman watchman =
Watchman.build(
executor,
NULL_WATCHMAN_CONNECTOR,
rootPaths,
env,
finder,
new TestConsole(),
clock,
Optional.empty());
assertEquals(Watchman.NULL_WATCHMAN, watchman);
}
@Test
public void shouldReturnNullWatchmanIfExtendedVersionCheckMissing()
throws InterruptedException, IOException {
SettableFakeClock clock = new SettableFakeClock(0, 0);
FakeListeningProcessExecutor executor =
new FakeListeningProcessExecutor(
ImmutableMultimap.<ProcessExecutorParams, FakeListeningProcessState>builder()
.putAll(
ProcessExecutorParams.ofCommand(exe, "--output-encoding=bser", "get-sockname"),
FakeListeningProcessState.ofStdoutBytes(
bserSerialized(
ImmutableMap.of(
"version", "3.7.9",
"sockname", "/path/to/sock"))),
FakeListeningProcessState.ofExit(0))
.build(),
clock);
Watchman watchman =
Watchman.build(
executor,
fakeWatchmanConnector(
Paths.get("/path/to/sock"),
0,
ImmutableMap.of(
VERSION_QUERY,
ImmutableMap.of("version", "3.7.9"),
ImmutableList.of("watch", root),
ImmutableMap.of("version", "3.7.9", "watch", root))),
rootPaths,
env,
finder,
new TestConsole(),
clock,
Optional.empty());
assertEquals(Watchman.NULL_WATCHMAN, watchman);
}
@Test
public void shouldFailIfWatchProjectNotAvailable() throws InterruptedException, IOException {
SettableFakeClock clock = new SettableFakeClock(0, 0);
FakeListeningProcessExecutor executor =
new FakeListeningProcessExecutor(
ImmutableMultimap.<ProcessExecutorParams, FakeListeningProcessState>builder()
.putAll(
ProcessExecutorParams.ofCommand(exe, "--output-encoding=bser", "get-sockname"),
FakeListeningProcessState.ofStdoutBytes(
bserSerialized(
ImmutableMap.of(
"version", "3.8.0",
"sockname", "/path/to/sock"))),
FakeListeningProcessState.ofExit(0))
.build(),
clock);
Watchman watchman =
Watchman.build(
executor,
fakeWatchmanConnector(
Paths.get("/path/to/sock"),
0,
ImmutableMap.of(
VERSION_QUERY,
ImmutableMap.of(
"version",
"3.8.0",
"capabilities",
ImmutableMap.of(
"term-dirname", true,
"cmd-watch-project", false,
"wildmatch", false,
"wildmatch_multislash", false,
"glob_generator", false),
"error",
"client required capabilty `cmd-watch-project` is not supported by this "
+ "server"))),
rootPaths,
env,
finder,
new TestConsole(),
clock,
Optional.empty());
assertEquals(Watchman.NULL_WATCHMAN, watchman);
}
@Test
public void watchmanVersionTakingThirtySecondsReturnsEmpty()
throws InterruptedException, IOException {
SettableFakeClock clock = new SettableFakeClock(0, 0);
FakeListeningProcessExecutor executor =
new FakeListeningProcessExecutor(
ImmutableMultimap.<ProcessExecutorParams, FakeListeningProcessState>builder()
.putAll(
ProcessExecutorParams.ofCommand(exe, "--output-encoding=bser", "get-sockname"),
FakeListeningProcessState.ofWaitNanos(TimeUnit.SECONDS.toNanos(30)),
FakeListeningProcessState.ofStdoutBytes(
bserSerialized(
ImmutableMap.of(
"version", "3.8.0",
"sockname", "/path/to/sock"))),
FakeListeningProcessState.ofExit(0))
.build(),
clock);
Watchman watchman =
Watchman.build(
executor,
fakeWatchmanConnector(
Paths.get("/path/to/sock"),
0,
ImmutableMap.of(
VERSION_QUERY,
ImmutableMap.of(
"version",
"3.8.0",
"capabilities",
ImmutableMap.of(
"term-dirname", true,
"cmd-watch-project", false,
"wildmatch", false,
"wildmatch_multislash", false,
"glob_generator", false)),
ImmutableList.of("watch-project", root),
ImmutableMap.of("version", "3.8.0", "watch", root))),
rootPaths,
env,
finder,
new TestConsole(),
clock,
Optional.of(TimeUnit.SECONDS.toMillis(5)));
assertEquals(Watchman.NULL_WATCHMAN, watchman);
}
@Test
public void watchmanWatchProjectTakingThirtySecondsReturnsEmpty()
throws InterruptedException, IOException {
SettableFakeClock clock = new SettableFakeClock(0, 0);
FakeListeningProcessExecutor executor =
new FakeListeningProcessExecutor(
ImmutableMultimap.<ProcessExecutorParams, FakeListeningProcessState>builder()
.putAll(
ProcessExecutorParams.ofCommand(exe, "--output-encoding=bser", "get-sockname"),
FakeListeningProcessState.ofStdoutBytes(
bserSerialized(
ImmutableMap.of(
"version", "3.8.0",
"sockname", "/path/to/sock"))))
.build(),
clock);
Watchman watchman =
Watchman.build(
executor,
fakeWatchmanConnector(
Paths.get("/path/to/sock"),
TimeUnit.SECONDS.toNanos(30),
ImmutableMap.of(
VERSION_QUERY,
ImmutableMap.of(
"version",
"3.8.0",
"capabilities",
ImmutableMap.<String, Boolean>builder()
.put("term-dirname", true)
.put("cmd-watch-project", false)
.put("wildmatch", false)
.put("wildmatch_multislash", false)
.put("glob_generator", false)
.put("clock-sync-timeout", false)
.build()),
ImmutableList.of("watch-project", root),
ImmutableMap.of("version", "3.8.0", "watch", root))),
rootPaths,
env,
finder,
new TestConsole(),
clock,
Optional.of(TimeUnit.SECONDS.toMillis(5)));
assertEquals(Watchman.NULL_WATCHMAN, watchman);
}
@Test
public void capabilitiesDetectedForVersion38AndLater() throws InterruptedException, IOException {
SettableFakeClock clock = new SettableFakeClock(0, 0);
FakeListeningProcessExecutor executor =
new FakeListeningProcessExecutor(
ImmutableMultimap.<ProcessExecutorParams, FakeListeningProcessState>builder()
.putAll(
ProcessExecutorParams.ofCommand(exe, "--output-encoding=bser", "get-sockname"),
FakeListeningProcessState.ofStdoutBytes(
bserSerialized(
ImmutableMap.of(
"version", "3.8.0",
"sockname", "/path/to/sock"))),
FakeListeningProcessState.ofExit(0))
.build(),
clock);
Watchman watchman =
Watchman.build(
executor,
fakeWatchmanConnector(
Paths.get("/path/to/sock"),
0,
ImmutableMap.of(
VERSION_QUERY,
ImmutableMap.of(
"version",
"3.8.0",
"capabilities",
ImmutableMap.<String, Boolean>builder()
.put("term-dirname", true)
.put("cmd-watch-project", true)
.put("wildmatch", true)
.put("wildmatch_multislash", true)
.put("glob_generator", false)
.put("clock-sync-timeout", false)
.build()),
ImmutableList.of("watch-project", root),
ImmutableMap.of("version", "3.8.0", "watch", root),
ImmutableList.of("clock", root, ImmutableMap.of()),
ImmutableMap.of("version", "3.8.0", "clock", "c:0:0:1"))),
rootPaths,
env,
finder,
new TestConsole(),
clock,
Optional.empty());
assertEquals(
ImmutableSet.of(
Watchman.Capability.DIRNAME,
Watchman.Capability.SUPPORTS_PROJECT_WATCH,
Watchman.Capability.WILDMATCH_GLOB,
Watchman.Capability.WILDMATCH_MULTISLASH),
watchman.getCapabilities());
assertEquals(ImmutableMap.of(root, "c:0:0:1"), watchman.getClockIds());
}
@Test
public void capabilitiesDetectedForVersion47AndLater() throws InterruptedException, IOException {
SettableFakeClock clock = new SettableFakeClock(0, 0);
FakeListeningProcessExecutor executor =
new FakeListeningProcessExecutor(
ImmutableMultimap.<ProcessExecutorParams, FakeListeningProcessState>builder()
.putAll(
ProcessExecutorParams.ofCommand(exe, "--output-encoding=bser", "get-sockname"),
FakeListeningProcessState.ofStdoutBytes(
bserSerialized(
ImmutableMap.of(
"version", "4.7.0",
"sockname", "/path/to/sock"))),
FakeListeningProcessState.ofExit(0))
.build(),
clock);
Watchman watchman =
Watchman.build(
executor,
fakeWatchmanConnector(
Paths.get("/path/to/sock"),
0,
ImmutableMap.of(
VERSION_QUERY,
ImmutableMap.of(
"version",
"4.7.0",
"capabilities",
ImmutableMap.<String, Boolean>builder()
.put("term-dirname", true)
.put("cmd-watch-project", true)
.put("wildmatch", true)
.put("wildmatch_multislash", true)
.put("glob_generator", true)
.put("clock-sync-timeout", true)
.build()),
ImmutableList.of("watch-project", root),
ImmutableMap.of("version", "4.7.0", "watch", root),
ImmutableList.of("clock", root, ImmutableMap.of("sync_timeout", 100)),
ImmutableMap.of("version", "4.7.0", "clock", "c:0:0:1"))),
rootPaths,
env,
finder,
new TestConsole(),
clock,
Optional.empty());
assertEquals(
ImmutableSet.of(
Watchman.Capability.DIRNAME,
Watchman.Capability.SUPPORTS_PROJECT_WATCH,
Watchman.Capability.WILDMATCH_GLOB,
Watchman.Capability.WILDMATCH_MULTISLASH,
Watchman.Capability.GLOB_GENERATOR,
Watchman.Capability.CLOCK_SYNC_TIMEOUT),
watchman.getCapabilities());
assertEquals(ImmutableMap.of(root, "c:0:0:1"), watchman.getClockIds());
}
@Test
public void emptyClockQueryShouldReturnNullClock() throws InterruptedException, IOException {
SettableFakeClock clock = new SettableFakeClock(0, 0);
FakeListeningProcessExecutor executor =
new FakeListeningProcessExecutor(
ImmutableMultimap.<ProcessExecutorParams, FakeListeningProcessState>builder()
.putAll(
ProcessExecutorParams.ofCommand(exe, "--output-encoding=bser", "get-sockname"),
FakeListeningProcessState.ofStdoutBytes(
bserSerialized(
ImmutableMap.of(
"version", "4.7.0",
"sockname", "/path/to/sock"))),
FakeListeningProcessState.ofExit(0))
.build(),
clock);
Watchman watchman =
Watchman.build(
executor,
fakeWatchmanConnector(
Paths.get("/path/to/sock"),
0,
ImmutableMap.of(
VERSION_QUERY,
ImmutableMap.of(
"version",
"4.7.0",
"capabilities",
ImmutableMap.<String, Boolean>builder()
.put("term-dirname", true)
.put("cmd-watch-project", true)
.put("wildmatch", true)
.put("wildmatch_multislash", true)
.put("glob_generator", true)
.put("clock-sync-timeout", true)
.build()),
ImmutableList.of("watch-project", root),
ImmutableMap.of("version", "4.7.0", "watch", root),
ImmutableList.of("clock", root, ImmutableMap.of("sync_timeout", 100)),
ImmutableMap.<String, Object>of())),
rootPaths,
env,
finder,
new TestConsole(),
clock,
Optional.empty());
assertEquals(ImmutableMap.of(), watchman.getClockIds());
}
}