/* * Copyright 2017-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 com.facebook.buck.testutil.TestConsole; import com.facebook.buck.testutil.integration.ProjectWorkspace; import com.facebook.buck.testutil.integration.TemporaryPaths; import com.facebook.buck.testutil.integration.TestDataHelper; import com.facebook.buck.timing.DefaultClock; import com.facebook.buck.util.ListeningProcessExecutor; import com.facebook.buck.util.ProcessExecutorParams; import com.facebook.buck.util.SimpleProcessListener; import com.facebook.buck.util.environment.Platform; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.io.IOException; import java.nio.file.Files; 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.Random; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; public class WatchmanClientIntegrationTest { private static final long timeoutMillis = 5000L; private static final long timeoutNanos = TimeUnit.MILLISECONDS.toNanos(timeoutMillis); private ProjectWorkspace workspace; @Rule public TemporaryPaths tmp = new TemporaryPaths(); @Rule public TemporaryPaths watchmanBaseDir = new TemporaryPaths(); private Path watchmanSockFile; private ListeningProcessExecutor executor; private ListeningProcessExecutor.LaunchedProcess watchmanProcess; private void startWatchman() throws IOException, InterruptedException { Optional<Path> watchmanExe = new ExecutableFinder() .getOptionalExecutable(Watchman.WATCHMAN, ImmutableMap.copyOf(System.getenv())); if (!isSupportedPlatform() || !watchmanExe.isPresent()) { return; } Path watchmanCfgFile = watchmanBaseDir.getRoot().resolve("config.json"); // default config Files.write(watchmanCfgFile, "{}".getBytes()); Path watchmanLogFile = watchmanBaseDir.getRoot().resolve("log"); Path watchmanPidFile = watchmanBaseDir.getRoot().resolve("pid"); if (Platform.detect() == Platform.WINDOWS) { Random random = new Random(0); UUID uuid = new UUID(random.nextLong(), random.nextLong()); watchmanSockFile = Paths.get("\\\\.\\pipe\\watchman-test-" + uuid.toString()); } else { watchmanSockFile = watchmanBaseDir.getRoot().resolve("sock"); } Path watchmanStateFile = watchmanBaseDir.getRoot().resolve("state"); ProcessExecutorParams params = ProcessExecutorParams.builder() .addCommand( watchmanExe.get().toString(), "--foreground", "--log-level=2", "--sockname=" + watchmanSockFile, "--logfile=" + watchmanLogFile, "--statefile=" + watchmanStateFile, "--pidfile=" + watchmanPidFile) .setEnvironment( ImmutableMap.of( "WATCHMAN_CONFIG_FILE", watchmanCfgFile.toString(), "TMP", watchmanBaseDir.toString())) .build(); executor = new ListeningProcessExecutor(); watchmanProcess = executor.launchProcess(params, new SimpleProcessListener()); waitForWatchman(); } private void waitForWatchman() throws InterruptedException { long deadline = System.currentTimeMillis() + timeoutMillis; while (System.currentTimeMillis() < deadline) { try { Optional<WatchmanClient> optClient = Watchman.localWatchmanConnector(new TestConsole(), new DefaultClock()) .apply(watchmanSockFile); try { if (optClient.isPresent()) { optClient.get().queryWithTimeout(timeoutMillis, "get-pid"); break; } } finally { if (optClient.isPresent()) { optClient.get().close(); } } } catch (IOException e) { Thread.sleep(100L); } } } @Before public void setUp() throws IOException, InterruptedException { startWatchman(); Assume.assumeTrue(watchmanProcess != null); workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "watchman", tmp); workspace.setUp(); } @After public void tearDown() { if (watchmanProcess != null && executor != null) { executor.destroyProcess(watchmanProcess, true); } } @Test public void testWatchmanGlob() throws InterruptedException, IOException { Optional<WatchmanClient> clientOpt = Watchman.localWatchmanConnector(new TestConsole(), new DefaultClock()) .apply(watchmanSockFile); Assert.assertTrue(clientOpt.isPresent()); WatchmanClient client = clientOpt.get(); Optional<? extends Map<String, ?>> versionResponse = client.queryWithTimeout( timeoutNanos, "version", ImmutableMap.of( "required", Watchman.REQUIRED_CAPABILITIES, "optional", Watchman.ALL_CAPABILITIES.keySet())); Assert.assertTrue(versionResponse.isPresent()); Path rootPath = workspace.getDestPath(); Optional<? extends Map<String, ?>> watch = client.queryWithTimeout(timeoutNanos, "watch-project", rootPath.toString()); Assert.assertNotNull(watch.isPresent()); Map<String, ?> map = watch.get(); String watchRoot = (String) map.get("watch"); Optional<? extends Map<String, ?>> queryResponse = client.queryWithTimeout( timeoutNanos, "query", watchRoot, ImmutableMap.<String, Object>of( "glob", ImmutableList.of("**/X"), "fields", ImmutableList.of("name"))); Assert.assertTrue(queryResponse.isPresent()); Set<?> actualFileSet = ImmutableSet.copyOf((List<?>) queryResponse.get().get("files")); Set<?> expectedFileSet = ImmutableSet.of("X", "f1/X", "f2/X"); Assert.assertEquals(expectedFileSet, actualFileSet); client.close(); } private static boolean isSupportedPlatform() { switch (Platform.detect()) { case LINUX: case MACOS: case WINDOWS: return true; //$CASES-OMITTED$ default: return false; } } }