Beispiel #1
0
 def setUp(self):
     super(TestWatchman, self).setUp()
     with mock.patch.object(Watchman, '_is_valid_executable',
                            **self.PATCH_OPTS) as mock_is_valid:
         mock_is_valid.return_value = True
         self.watchman = Watchman('/fake/path/to/watchman',
                                  self.subprocess_dir)
Beispiel #2
0
 def setUp(self):
     super().setUp()
     with unittest.mock.patch.object(Watchman, "_is_valid_executable",
                                     **self.PATCH_OPTS) as mock_is_valid:
         mock_is_valid.return_value = True
         self.watchman = Watchman("/fake/path/to/watchman",
                                  self.subprocess_dir)
 def watchman(self):
     watchman_binary = self._binary_util.select_binary(
         self._watchman_supportdir, self._watchman_version, 'watchman')
     return Watchman(watchman_binary, self._workdir,
                     self._convert_log_level(self._log_level),
                     self._startup_timeout, self._socket_timeout,
                     self._socket_path_override)
Beispiel #4
0
 def setUp(self):
   super(TestWatchman, self).setUp()
   with mock.patch.object(Watchman, '_is_valid_executable', **self.PATCH_OPTS) as mock_is_valid:
     mock_is_valid.return_value = True
     self.watchman = Watchman('/fake/path/to/watchman',
                              self.WORK_DIR,
                              metadata_base_dir=self.subprocess_dir)
Beispiel #5
0
    def test_maybe_launch_error(self):
        mock_watchman = self.create_mock_watchman(False)
        mock_watchman.launch.side_effect = Watchman.ExecutionError('oops!')

        self.watchman_launcher._watchman = mock_watchman
        self.assertFalse(self.watchman_launcher.maybe_launch())

        mock_watchman.is_alive.assert_called_once_with()
        mock_watchman.launch.assert_called_once_with()
Beispiel #6
0
  def test_maybe_launch_error(self):
    mock_watchman = self.create_mock_watchman(False)
    mock_watchman.launch.side_effect = Watchman.ExecutionError('oops!')

    wl = self.watchman_launcher()
    wl.watchman = mock_watchman
    with self.assertRaises(wl.watchman.ExecutionError):
      wl.maybe_launch()

    mock_watchman.is_alive.assert_called_once_with()
    mock_watchman.launch.assert_called_once_with()
Beispiel #7
0
 def watchman(self):
     watchman_binary = self._binary_util.select_binary(
         self._watchman_supportdir, self._watchman_version, "watchman")
     return Watchman(
         watchman_binary,
         self._metadata_base_dir,
         self._convert_log_level(self._log_level),
         self._startup_timeout,
         self._socket_timeout,
         self._socket_path_override,
     )
Beispiel #8
0
  def register_handler(self, name, metadata, callback):
    """Register subscriptions and their event handlers.

    :param str name:      the subscription name as used by watchman
    :param dict metadata: a dictionary of metadata to be serialized and passed to the watchman
                          subscribe command. this should include the match expression as well
                          as any required callback fields.
    :param func callback: the callback to execute on each matching filesystem event
    """
    assert name not in self._handlers, 'duplicate handler name: {}'.format(name)
    assert (
      isinstance(metadata, dict) and 'fields' in metadata and 'expression' in metadata
    ), 'invalid handler metadata!'
    self._handlers[name] = Watchman.EventHandler(name=name, metadata=metadata, callback=callback)
Beispiel #9
0
    def __init__(
        self, watchman: Watchman, scheduler: Scheduler, build_root: str,
    ):
        """
        :param watchman: The Watchman instance as provided by the WatchmanLauncher subsystem.
        :param session: A SchedulerSession to invalidate for.
        :param build_root: The current build root.
        """
        super().__init__()
        self._logger = logging.getLogger(__name__)
        self._watchman = watchman
        self._build_root = os.path.realpath(build_root)
        self._watchman_is_running = threading.Event()
        self._scheduler_session = scheduler.new_session(
            zipkin_trace_v2=False, build_id="fs_event_service_session"
        )

        self._handler = Watchman.EventHandler(
            name=self.PANTS_ALL_FILES_SUBSCRIPTION_NAME,
            metadata=dict(
                fields=["name"],
                # Request events for all file types.
                # NB: Touching a file invalidates its parent directory due to:
                #   https://github.com/facebook/watchman/issues/305
                # ...but if we were to skip watching directories, we'd still have to invalidate
                # the parents of any changed files, and we wouldn't see creation/deletion of
                # empty directories.
                expression=[
                    "allof",  # All of the below rules must be true to match.
                    ["not", ["dirname", "dist", self.ZERO_DEPTH]],  # Exclude the ./dist dir.
                    # N.B. 'wholename' ensures we match against the absolute ('x/y/z') vs base path ('z').
                    [
                        "not",
                        ["pcre", r"^\..*", "wholename"],
                    ],  # Exclude files in hidden dirs (.pants.d etc).
                    ["not", ["match", "*.pyc"]]  # Exclude .pyc files.
                    # TODO(kwlzn): Make exclusions here optionable.
                    # Related: https://github.com/pantsbuild/pants/issues/2956
                ],
            ),
            # NB: We stream events from Watchman in `self.run`, so we don't need a callback.
            callback=lambda: None,
        )
Beispiel #10
0
 def setUp(self):
     BaseTest.setUp(self)
     with mock.patch.object(Watchman, '_is_valid_executable',
                            **self.PATCH_OPTS) as mock_is_valid:
         mock_is_valid.return_value = True
         self.watchman = Watchman('/fake/path/to/watchman', self.WORK_DIR)
Beispiel #11
0
class TestWatchman(BaseTest):
  PATCH_OPTS = dict(autospec=True, spec_set=True)
  WORK_DIR = '/path/to/a/fake/work_dir'
  BUILD_ROOT = '/path/to/a/fake/build_root'
  WATCHMAN_PATH = '/path/to/a/fake/watchman'
  TEST_DIR = '/path/to/a/fake/test'
  WATCHMAN_DIR = os.path.join(WORK_DIR, 'watchman')
  STATE_FILE = os.path.join(WATCHMAN_DIR, 'watchman.state')
  HANDLERS = [Watchman.EventHandler('test', {}, mock.Mock())]

  def setUp(self):
    BaseTest.setUp(self)
    with mock.patch.object(Watchman, '_resolve_watchman_path', **self.PATCH_OPTS) as mock_find:
      mock_find.return_value = self.WATCHMAN_PATH
      self.watchman = Watchman(self.WORK_DIR)

  def test_client_property(self):
    self.assertIsInstance(self.watchman.client, pywatchman.client)

  def test_client_property_cached(self):
    self.watchman._watchman_client = 1
    self.assertEquals(self.watchman.client, 1)

  def test_make_client(self):
    self.assertIsInstance(self.watchman._make_client(), pywatchman.client)

  def test_is_valid_executable(self):
    self.assertTrue(self.watchman._is_valid_executable(sys.executable))

  @contextmanager
  def setup_find_watchman_in_path(self):
    with mock.patch.object(Watchman, '_is_valid_executable', **self.PATCH_OPTS) as mock_valid, \
         environment_as(PATH='a:b:c'):
      yield mock_valid

  def test_find_watchman_in_path(self):
    with self.setup_find_watchman_in_path() as mock_valid:
      mock_valid.side_effect = [False, False, True]
      self.assertEquals(self.watchman._find_watchman_in_path(), 'c/watchman')
      mock_valid.assert_has_calls([
        mock.call(self.watchman, 'a/{}'.format(self.watchman.process_name)),
        mock.call(self.watchman, 'b/{}'.format(self.watchman.process_name)),
        mock.call(self.watchman, 'c/{}'.format(self.watchman.process_name))
      ])

  def test_find_watchman_in_path_neg(self):
    with self.setup_find_watchman_in_path() as mock_valid:
      mock_valid.side_effect = [False, False, False]
      self.assertIsNone(self.watchman._find_watchman_in_path())

  def test_resolve_watchman_path_provided_exception(self):
    with mock.patch.object(Watchman, '_is_valid_executable', **self.PATCH_OPTS) as mock_valid:
      mock_valid.return_value = False
      with self.assertRaises(Watchman.ExecutionError):
        self.watchman._resolve_watchman_path(self.WATCHMAN_PATH)

  def test_resolve_watchman_path_provided(self):
    with mock.patch.object(Watchman, '_is_valid_executable', **self.PATCH_OPTS) as mock_valid:
      mock_valid.return_value = True
      self.assertEquals(self.watchman._resolve_watchman_path(self.WATCHMAN_PATH),
                        self.WATCHMAN_PATH)

  def test_resolve_watchman_path_default_exception(self):
    with mock.patch.object(Watchman, '_find_watchman_in_path', **self.PATCH_OPTS) as mock_find:
      mock_find.return_value = None
      with self.assertRaises(Watchman.ExecutionError):
        self.watchman._resolve_watchman_path(None)

  def test_resolve_watchman_path_default(self):
    with mock.patch.object(Watchman, '_find_watchman_in_path', **self.PATCH_OPTS) as mock_find:
      mock_find.return_value = self.WATCHMAN_PATH
      self.assertEquals(self.watchman._resolve_watchman_path(None), self.WATCHMAN_PATH)

  def test_maybe_init_metadata(self):
    with mock.patch('pants.pantsd.watchman.safe_mkdir', **self.PATCH_OPTS) as mock_mkdir, \
         mock.patch('pants.pantsd.watchman.safe_file_dump', **self.PATCH_OPTS) as mock_file_dump:
      self.watchman._maybe_init_metadata()

      mock_mkdir.assert_called_once_with(self.WATCHMAN_DIR)
      mock_file_dump.assert_called_once_with(self.STATE_FILE, '{}')

  def test_construct_cmd(self):
    output = self.watchman._construct_cmd(['cmd', 'parts', 'etc'],
                                          'state_file',
                                          'sock_file',
                                          'log_file',
                                          'log_level')

    self.assertEquals(output, ['cmd',
                               'parts',
                               'etc',
                               '--no-save-state',
                               '--statefile=state_file',
                               '--sockname=sock_file',
                               '--logfile=log_file',
                               '--log-level',
                               'log_level'])

  def test_parse_pid_from_output(self):
    output = json.dumps(dict(pid=3))
    self.assertEquals(self.watchman._parse_pid_from_output(output), 3)

  def test_parse_pid_from_output_bad_output(self):
    output = '{bad JSON.,/#!'
    with self.assertRaises(self.watchman.InvalidCommandOutput):
      self.watchman._parse_pid_from_output(output)

  def test_parse_pid_from_output_no_pid(self):
    output = json.dumps(dict(nothing=True))
    with self.assertRaises(self.watchman.InvalidCommandOutput):
      self.watchman._parse_pid_from_output(output)

  def test_launch(self):
    with mock.patch.object(Watchman, '_maybe_init_metadata') as mock_initmeta, \
         mock.patch.object(Watchman, 'get_subprocess_output') as mock_getsubout, \
         mock.patch.object(Watchman, 'write_pid') as mock_writepid, \
         mock.patch.object(Watchman, 'write_socket') as mock_writesock:
      mock_getsubout.return_value = json.dumps(dict(pid='3'))
      self.watchman.launch()
      assert mock_getsubout.called
      mock_initmeta.assert_called_once_with()
      mock_writepid.assert_called_once_with('3')
      mock_writesock.assert_called_once_with(self.watchman._sock_file)

  def test_watch_project(self):
    self.watchman._watchman_client = mock.create_autospec(StreamableWatchmanClient, spec_set=True)
    self.watchman.watch_project(self.TEST_DIR)
    self.watchman._watchman_client.query.assert_called_once_with('watch-project', self.TEST_DIR)

  @contextmanager
  def setup_subscribed(self, iterable):
    mock_client = mock.create_autospec(StreamableWatchmanClient, spec_set=True)
    mock_client.stream_query.return_value = iter(iterable)
    self.watchman._watchman_client = mock_client
    yield mock_client
    assert mock_client.stream_query.called

  def test_subscribed_empty(self):
    """Test yielding when watchman reads timeout."""
    with self.setup_subscribed([None]):
      out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
      self.assertEquals(list(out), [(None, None)])

  def test_subscribed_response(self):
    """Test yielding on the watchman response to the initial subscribe command."""
    with self.setup_subscribed([dict(subscribe='test')]):
      out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
      self.assertEquals(list(out), [(None, None)])

  def test_subscribed_event(self):
    """Test yielding on a watchman event for a given subscription."""
    test_event = dict(subscription='test3', msg='blah')

    with self.setup_subscribed([test_event]):
      out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
      self.assertEquals(list(out), [('test3', test_event)])

  def test_subscribed_unknown_event(self):
    """Test yielding on an unknown watchman event."""
    with self.setup_subscribed([dict(unknown=True)]):
      out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
      self.assertEquals(list(out), [])
Beispiel #12
0
 def setUp(self):
   BaseTest.setUp(self)
   with mock.patch.object(Watchman, '_resolve_watchman_path', **self.PATCH_OPTS) as mock_find:
     mock_find.return_value = self.WATCHMAN_PATH
     self.watchman = Watchman(self.WORK_DIR)
Beispiel #13
0
class TestWatchman(TestBase):
    PATCH_OPTS = dict(autospec=True, spec_set=True)
    BUILD_ROOT = "/path/to/a/fake/build_root"
    WATCHMAN_PATH = "/path/to/a/fake/watchman"
    TEST_DIR = "/path/to/a/fake/test"
    HANDLERS = [Watchman.EventHandler("test", {}, unittest.mock.Mock())]

    def setUp(self):
        super().setUp()
        with unittest.mock.patch.object(Watchman, "_is_valid_executable",
                                        **self.PATCH_OPTS) as mock_is_valid:
            mock_is_valid.return_value = True
            self.watchman = Watchman("/fake/path/to/watchman",
                                     self.subprocess_dir)

    @property
    def _watchman_dir(self):
        return os.path.join(self.subprocess_dir, "watchman")

    @property
    def _state_file(self):
        return os.path.join(self._watchman_dir, "watchman.state")

    def test_client_property(self):
        self.assertIsInstance(self.watchman.client, pywatchman.client)

    def test_client_property_cached(self):
        self.watchman._watchman_client = 1
        self.assertEqual(self.watchman.client, 1)

    def test_make_client(self):
        self.assertIsInstance(self.watchman._make_client(), pywatchman.client)

    def test_is_valid_executable(self):
        self.assertTrue(self.watchman._is_valid_executable(sys.executable))

    def test_resolve_watchman_path_provided_exception(self):
        with self.assertRaises(Watchman.ExecutionError):
            self.watchman = Watchman("/fake/path/to/watchman",
                                     metadata_base_dir=self.subprocess_dir)

    def test_maybe_init_metadata(self):
        # TODO(#7106): is this the right path to patch?
        with unittest.mock.patch(
                "pants.pantsd.watchman.safe_mkdir",
                **self.PATCH_OPTS) as mock_mkdir, unittest.mock.patch(
                    "pants.pantsd.watchman.safe_file_dump",
                    **self.PATCH_OPTS) as mock_file_dump:
            self.watchman._maybe_init_metadata()

            mock_mkdir.assert_called_once_with(self._watchman_dir)
            mock_file_dump.assert_called_once_with(self._state_file,
                                                   b"{}",
                                                   mode="wb")

    def test_construct_cmd(self):
        output = self.watchman._construct_cmd(["cmd", "parts", "etc"],
                                              "state_file", "sock_file",
                                              "pid_file", "log_file",
                                              "log_level")

        self.assertEqual(
            output,
            [
                "cmd",
                "parts",
                "etc",
                "--no-save-state",
                "--no-site-spawner",
                "--statefile=state_file",
                "--sockname=sock_file",
                "--pidfile=pid_file",
                "--logfile=log_file",
                "--log-level",
                "log_level",
            ],
        )

    def test_parse_pid_from_output(self):
        output = json.dumps(dict(pid=3))
        self.assertEqual(self.watchman._parse_pid_from_output(output), 3)

    def test_parse_pid_from_output_bad_output(self):
        output = "{bad JSON.,/#!"
        with self.assertRaises(Watchman.InvalidCommandOutput):
            self.watchman._parse_pid_from_output(output)

    def test_parse_pid_from_output_no_pid(self):
        output = json.dumps(dict(nothing=True))
        with self.assertRaises(Watchman.InvalidCommandOutput):
            self.watchman._parse_pid_from_output(output)

    def test_launch(self):
        with unittest.mock.patch.object(
                Watchman, "_maybe_init_metadata"
        ) as mock_initmeta, unittest.mock.patch.object(
                Watchman, "get_subprocess_output"
        ) as mock_getsubout, unittest.mock.patch.object(
                Watchman,
                "write_pid") as mock_writepid, unittest.mock.patch.object(
                    Watchman, "write_socket") as mock_writesock:
            mock_getsubout.return_value = json.dumps(dict(pid="3"))
            self.watchman.launch()
            assert mock_getsubout.called
            mock_initmeta.assert_called_once_with()
            mock_writepid.assert_called_once_with("3")
            mock_writesock.assert_called_once_with(self.watchman._sock_file)

    def test_watch_project(self):
        self.watchman._watchman_client = unittest.mock.create_autospec(
            StreamableWatchmanClient, spec_set=True)
        self.watchman.watch_project(self.TEST_DIR)
        self.watchman._watchman_client.query.assert_called_once_with(
            "watch-project", self.TEST_DIR)

    @contextmanager
    def setup_subscribed(self, iterable):
        mock_client = unittest.mock.create_autospec(StreamableWatchmanClient,
                                                    spec_set=True)
        mock_client.stream_query.return_value = iter(iterable)
        self.watchman._watchman_client = mock_client
        yield mock_client
        assert mock_client.stream_query.called

    def test_subscribed_empty(self):
        """Test yielding when watchman reads timeout."""
        with self.setup_subscribed([None]):
            out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
            self.assertEqual(list(out), [(None, None)])

    def test_subscribed_response(self):
        """Test yielding on the watchman response to the initial subscribe command."""
        with self.setup_subscribed([dict(subscribe="test")]):
            out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
            self.assertEqual(list(out), [(None, None)])

    def test_subscribed_event(self):
        """Test yielding on a watchman event for a given subscription."""
        test_event = dict(subscription="test3", msg="blah")

        with self.setup_subscribed([test_event]):
            out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
            self.assertEqual(list(out), [("test3", test_event)])

    def test_subscribed_unknown_event(self):
        """Test yielding on an unknown watchman event."""
        with self.setup_subscribed([dict(unknown=True)]):
            out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
            self.assertEqual(list(out), [])
Beispiel #14
0
class TestWatchman(TestBase):
  PATCH_OPTS = dict(autospec=True, spec_set=True)
  BUILD_ROOT = '/path/to/a/fake/build_root'
  WATCHMAN_PATH = '/path/to/a/fake/watchman'
  TEST_DIR = '/path/to/a/fake/test'
  HANDLERS = [Watchman.EventHandler('test', {}, mock.Mock())]

  def setUp(self):
    super(TestWatchman, self).setUp()
    with mock.patch.object(Watchman, '_is_valid_executable', **self.PATCH_OPTS) as mock_is_valid:
      mock_is_valid.return_value = True
      self.watchman = Watchman('/fake/path/to/watchman', self.subprocess_dir)

  @property
  def _watchman_dir(self):
    return os.path.join(self.subprocess_dir, 'watchman')

  @property
  def _state_file(self):
    return os.path.join(self._watchman_dir, 'watchman.state')

  def test_client_property(self):
    self.assertIsInstance(self.watchman.client, pywatchman.client)

  def test_client_property_cached(self):
    self.watchman._watchman_client = 1
    self.assertEqual(self.watchman.client, 1)

  def test_make_client(self):
    self.assertIsInstance(self.watchman._make_client(), pywatchman.client)

  def test_is_valid_executable(self):
    self.assertTrue(self.watchman._is_valid_executable(sys.executable))

  def test_resolve_watchman_path_provided_exception(self):
    with self.assertRaises(Watchman.ExecutionError):
      self.watchman = Watchman('/fake/path/to/watchman',
                               metadata_base_dir=self.subprocess_dir)

  def test_maybe_init_metadata(self):
    # TODO(#7106): is this the right path to patch?
    with mock.patch('pants.pantsd.watchman.safe_mkdir', **self.PATCH_OPTS) as mock_mkdir, \
         mock.patch('pants.pantsd.watchman.safe_file_dump', **self.PATCH_OPTS) as mock_file_dump:
      self.watchman._maybe_init_metadata()

      mock_mkdir.assert_called_once_with(self._watchman_dir)
      mock_file_dump.assert_called_once_with(self._state_file, b'{}', mode='wb')

  def test_construct_cmd(self):
    output = self.watchman._construct_cmd(['cmd', 'parts', 'etc'],
                                          'state_file',
                                          'sock_file',
                                          'pid_file',
                                          'log_file',
                                          'log_level')

    self.assertEqual(output, ['cmd',
                               'parts',
                               'etc',
                               '--no-save-state',
                               '--no-site-spawner',
                               '--statefile=state_file',
                               '--sockname=sock_file',
                               '--pidfile=pid_file',
                               '--logfile=log_file',
                               '--log-level',
                               'log_level'])

  def test_parse_pid_from_output(self):
    output = json.dumps(dict(pid=3))
    self.assertEqual(self.watchman._parse_pid_from_output(output), 3)

  def test_parse_pid_from_output_bad_output(self):
    output = '{bad JSON.,/#!'
    with self.assertRaises(Watchman.InvalidCommandOutput):
      self.watchman._parse_pid_from_output(output)

  def test_parse_pid_from_output_no_pid(self):
    output = json.dumps(dict(nothing=True))
    with self.assertRaises(Watchman.InvalidCommandOutput):
      self.watchman._parse_pid_from_output(output)

  def test_launch(self):
    with mock.patch.object(Watchman, '_maybe_init_metadata') as mock_initmeta, \
         mock.patch.object(Watchman, 'get_subprocess_output') as mock_getsubout, \
         mock.patch.object(Watchman, 'write_pid') as mock_writepid, \
         mock.patch.object(Watchman, 'write_socket') as mock_writesock:
      mock_getsubout.return_value = json.dumps(dict(pid='3'))
      self.watchman.launch()
      assert mock_getsubout.called
      mock_initmeta.assert_called_once_with()
      mock_writepid.assert_called_once_with('3')
      mock_writesock.assert_called_once_with(self.watchman._sock_file)

  def test_watch_project(self):
    self.watchman._watchman_client = mock.create_autospec(StreamableWatchmanClient, spec_set=True)
    self.watchman.watch_project(self.TEST_DIR)
    self.watchman._watchman_client.query.assert_called_once_with('watch-project', self.TEST_DIR)

  @contextmanager
  def setup_subscribed(self, iterable):
    mock_client = mock.create_autospec(StreamableWatchmanClient, spec_set=True)
    mock_client.stream_query.return_value = iter(iterable)
    self.watchman._watchman_client = mock_client
    yield mock_client
    assert mock_client.stream_query.called

  def test_subscribed_empty(self):
    """Test yielding when watchman reads timeout."""
    with self.setup_subscribed([None]):
      out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
      self.assertEqual(list(out), [(None, None)])

  def test_subscribed_response(self):
    """Test yielding on the watchman response to the initial subscribe command."""
    with self.setup_subscribed([dict(subscribe='test')]):
      out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
      self.assertEqual(list(out), [(None, None)])

  def test_subscribed_event(self):
    """Test yielding on a watchman event for a given subscription."""
    test_event = dict(subscription='test3', msg='blah')

    with self.setup_subscribed([test_event]):
      out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
      self.assertEqual(list(out), [('test3', test_event)])

  def test_subscribed_unknown_event(self):
    """Test yielding on an unknown watchman event."""
    with self.setup_subscribed([dict(unknown=True)]):
      out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
      self.assertEqual(list(out), [])
Beispiel #15
0
class TestWatchman(BaseTest):
  PATCH_OPTS = dict(autospec=True, spec_set=True)
  BUILD_ROOT = '/path/to/a/fake/build_root'
  WATCHMAN_PATH = '/path/to/a/fake/watchman'
  TEST_DIR = '/path/to/a/fake/test'
  HANDLERS = [Watchman.EventHandler('test', {}, mock.Mock())]

  def setUp(self):
    super(TestWatchman, self).setUp()
    with mock.patch.object(Watchman, '_is_valid_executable', **self.PATCH_OPTS) as mock_is_valid:
      mock_is_valid.return_value = True
      self.watchman = Watchman('/fake/path/to/watchman', self.subprocess_dir)

  @property
  def _watchman_dir(self):
    return os.path.join(self.subprocess_dir, 'watchman')

  @property
  def _state_file(self):
    return os.path.join(self._watchman_dir, 'watchman.state')

  def test_client_property(self):
    self.assertIsInstance(self.watchman.client, pywatchman.client)

  def test_client_property_cached(self):
    self.watchman._watchman_client = 1
    self.assertEquals(self.watchman.client, 1)

  def test_make_client(self):
    self.assertIsInstance(self.watchman._make_client(), pywatchman.client)

  def test_is_valid_executable(self):
    self.assertTrue(self.watchman._is_valid_executable(sys.executable))

  def test_resolve_watchman_path_provided_exception(self):
    with self.assertRaises(Watchman.ExecutionError):
      self.watchman = Watchman('/fake/path/to/watchman',
                               metadata_base_dir=self.subprocess_dir)

  def test_maybe_init_metadata(self):
    with mock.patch('pants.pantsd.watchman.safe_mkdir', **self.PATCH_OPTS) as mock_mkdir, \
         mock.patch('pants.pantsd.watchman.safe_file_dump', **self.PATCH_OPTS) as mock_file_dump:
      self.watchman._maybe_init_metadata()

      mock_mkdir.assert_called_once_with(self._watchman_dir)
      mock_file_dump.assert_called_once_with(self._state_file, '{}')

  def test_construct_cmd(self):
    output = self.watchman._construct_cmd(['cmd', 'parts', 'etc'],
                                          'state_file',
                                          'sock_file',
                                          'log_file',
                                          'log_level')

    self.assertEquals(output, ['cmd',
                               'parts',
                               'etc',
                               '--no-save-state',
                               '--statefile=state_file',
                               '--sockname=sock_file',
                               '--logfile=log_file',
                               '--log-level',
                               'log_level'])

  def test_parse_pid_from_output(self):
    output = json.dumps(dict(pid=3))
    self.assertEquals(self.watchman._parse_pid_from_output(output), 3)

  def test_parse_pid_from_output_bad_output(self):
    output = '{bad JSON.,/#!'
    with self.assertRaises(self.watchman.InvalidCommandOutput):
      self.watchman._parse_pid_from_output(output)

  def test_parse_pid_from_output_no_pid(self):
    output = json.dumps(dict(nothing=True))
    with self.assertRaises(self.watchman.InvalidCommandOutput):
      self.watchman._parse_pid_from_output(output)

  def test_launch(self):
    with mock.patch.object(Watchman, '_maybe_init_metadata') as mock_initmeta, \
         mock.patch.object(Watchman, 'get_subprocess_output') as mock_getsubout, \
         mock.patch.object(Watchman, 'write_pid') as mock_writepid, \
         mock.patch.object(Watchman, 'write_socket') as mock_writesock:
      mock_getsubout.return_value = json.dumps(dict(pid='3'))
      self.watchman.launch()
      assert mock_getsubout.called
      mock_initmeta.assert_called_once_with()
      mock_writepid.assert_called_once_with('3')
      mock_writesock.assert_called_once_with(self.watchman._sock_file)

  def test_watch_project(self):
    self.watchman._watchman_client = mock.create_autospec(StreamableWatchmanClient, spec_set=True)
    self.watchman.watch_project(self.TEST_DIR)
    self.watchman._watchman_client.query.assert_called_once_with('watch-project', self.TEST_DIR)

  @contextmanager
  def setup_subscribed(self, iterable):
    mock_client = mock.create_autospec(StreamableWatchmanClient, spec_set=True)
    mock_client.stream_query.return_value = iter(iterable)
    self.watchman._watchman_client = mock_client
    yield mock_client
    assert mock_client.stream_query.called

  def test_subscribed_empty(self):
    """Test yielding when watchman reads timeout."""
    with self.setup_subscribed([None]):
      out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
      self.assertEquals(list(out), [(None, None)])

  def test_subscribed_response(self):
    """Test yielding on the watchman response to the initial subscribe command."""
    with self.setup_subscribed([dict(subscribe='test')]):
      out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
      self.assertEquals(list(out), [(None, None)])

  def test_subscribed_event(self):
    """Test yielding on a watchman event for a given subscription."""
    test_event = dict(subscription='test3', msg='blah')

    with self.setup_subscribed([test_event]):
      out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
      self.assertEquals(list(out), [('test3', test_event)])

  def test_subscribed_unknown_event(self):
    """Test yielding on an unknown watchman event."""
    with self.setup_subscribed([dict(unknown=True)]):
      out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
      self.assertEquals(list(out), [])
Beispiel #16
0
 def test_resolve_watchman_path_provided_exception(self):
   with self.assertRaises(Watchman.ExecutionError):
     self.watchman = Watchman('/fake/path/to/watchman',
                              metadata_base_dir=self.subprocess_dir)
Beispiel #17
0
 def watchman(self):
     if not self._watchman:
         self._watchman = Watchman(self._workdir,
                                   log_level=self._watchman_log_level,
                                   watchman_path=self._watchman_path)
     return self._watchman
Beispiel #18
0
class TestWatchman(BaseTest):
    PATCH_OPTS = dict(autospec=True, spec_set=True)
    WORK_DIR = "/path/to/a/fake/work_dir"
    BUILD_ROOT = "/path/to/a/fake/build_root"
    WATCHMAN_PATH = "/path/to/a/fake/watchman"
    TEST_DIR = "/path/to/a/fake/test"
    WATCHMAN_DIR = os.path.join(WORK_DIR, "watchman")
    STATE_FILE = os.path.join(WATCHMAN_DIR, "watchman.state")
    HANDLERS = [Watchman.EventHandler("test", {}, mock.Mock())]

    def setUp(self):
        BaseTest.setUp(self)
        with mock.patch.object(Watchman, "_resolve_watchman_path", **self.PATCH_OPTS) as mock_find:
            mock_find.return_value = self.WATCHMAN_PATH
            self.watchman = Watchman(self.WORK_DIR)

    def test_client_property(self):
        self.assertIsInstance(self.watchman.client, pywatchman.client)

    def test_client_property_cached(self):
        self.watchman._watchman_client = 1
        self.assertEquals(self.watchman.client, 1)

    def test_make_client(self):
        self.assertIsInstance(self.watchman._make_client(), pywatchman.client)

    def test_is_valid_executable(self):
        self.assertTrue(self.watchman._is_valid_executable(sys.executable))

    @contextmanager
    def setup_find_watchman_in_path(self):
        with mock.patch.object(Watchman, "_is_valid_executable", **self.PATCH_OPTS) as mock_valid, environment_as(
            PATH="a:b:c"
        ):
            yield mock_valid

    def test_find_watchman_in_path(self):
        with self.setup_find_watchman_in_path() as mock_valid:
            mock_valid.side_effect = [False, False, True]
            self.assertEquals(self.watchman._find_watchman_in_path(), "c/watchman")
            mock_valid.assert_has_calls(
                [
                    mock.call(self.watchman, "a/{}".format(self.watchman.process_name)),
                    mock.call(self.watchman, "b/{}".format(self.watchman.process_name)),
                    mock.call(self.watchman, "c/{}".format(self.watchman.process_name)),
                ]
            )

    def test_find_watchman_in_path_neg(self):
        with self.setup_find_watchman_in_path() as mock_valid:
            mock_valid.side_effect = [False, False, False]
            self.assertIsNone(self.watchman._find_watchman_in_path())

    def test_resolve_watchman_path_provided_exception(self):
        with mock.patch.object(Watchman, "_is_valid_executable", **self.PATCH_OPTS) as mock_valid:
            mock_valid.return_value = False
            with self.assertRaises(Watchman.ExecutionError):
                self.watchman._resolve_watchman_path(self.WATCHMAN_PATH)

    def test_resolve_watchman_path_provided(self):
        with mock.patch.object(Watchman, "_is_valid_executable", **self.PATCH_OPTS) as mock_valid:
            mock_valid.return_value = True
            self.assertEquals(self.watchman._resolve_watchman_path(self.WATCHMAN_PATH), self.WATCHMAN_PATH)

    def test_resolve_watchman_path_default_exception(self):
        with mock.patch.object(Watchman, "_find_watchman_in_path", **self.PATCH_OPTS) as mock_find:
            mock_find.return_value = None
            with self.assertRaises(Watchman.ExecutionError):
                self.watchman._resolve_watchman_path(None)

    def test_resolve_watchman_path_default(self):
        with mock.patch.object(Watchman, "_find_watchman_in_path", **self.PATCH_OPTS) as mock_find:
            mock_find.return_value = self.WATCHMAN_PATH
            self.assertEquals(self.watchman._resolve_watchman_path(None), self.WATCHMAN_PATH)

    def test_maybe_init_metadata(self):
        with mock.patch("pants.pantsd.watchman.safe_mkdir", **self.PATCH_OPTS) as mock_mkdir, mock.patch.object(
            Watchman, "_write_file", **self.PATCH_OPTS
        ) as mock_write_file:
            self.watchman._maybe_init_metadata()

            mock_mkdir.assert_called_once_with(self.WATCHMAN_DIR)
            mock_write_file.assert_called_once_with(self.watchman, self.STATE_FILE, "{}")

    def test_construct_cmd(self):
        output = self.watchman._construct_cmd(
            ["cmd", "parts", "etc"], "state_file", "sock_file", "log_file", "log_level"
        )

        self.assertEquals(
            output,
            [
                "cmd",
                "parts",
                "etc",
                "--no-save-state",
                "--statefile=state_file",
                "--sockname=sock_file",
                "--logfile=log_file",
                "--log-level",
                "log_level",
            ],
        )

    def test_parse_pid_from_output(self):
        output = json.dumps(dict(pid=3))
        self.assertEquals(self.watchman._parse_pid_from_output(output), 3)

    def test_parse_pid_from_output_bad_output(self):
        output = "{bad JSON.,/#!"
        with self.assertRaises(self.watchman.InvalidCommandOutput):
            self.watchman._parse_pid_from_output(output)

    def test_parse_pid_from_output_no_pid(self):
        output = json.dumps(dict(nothing=True))
        with self.assertRaises(self.watchman.InvalidCommandOutput):
            self.watchman._parse_pid_from_output(output)

    def test_launch(self):
        with mock.patch.object(Watchman, "_maybe_init_metadata") as mock_initmeta, mock.patch.object(
            Watchman, "get_subprocess_output"
        ) as mock_getsubout, mock.patch.object(Watchman, "write_pid") as mock_writepid, mock.patch.object(
            Watchman, "write_socket"
        ) as mock_writesock:
            mock_getsubout.return_value = json.dumps(dict(pid="3"))
            self.watchman.launch()
            assert mock_getsubout.called
            mock_initmeta.assert_called_once_with()
            mock_writepid.assert_called_once_with("3")
            mock_writesock.assert_called_once_with(self.watchman._sock_file)

    def test_watch_project(self):
        self.watchman._watchman_client = mock.create_autospec(StreamableWatchmanClient, spec_set=True)
        self.watchman.watch_project(self.TEST_DIR)
        self.watchman._watchman_client.query.assert_called_once_with("watch-project", self.TEST_DIR)

    @contextmanager
    def setup_subscribed(self, iterable):
        mock_client = mock.create_autospec(StreamableWatchmanClient, spec_set=True)
        mock_client.stream_query.return_value = iter(iterable)
        self.watchman._watchman_client = mock_client
        yield mock_client
        assert mock_client.stream_query.called

    def test_subscribed_empty(self):
        """Test yielding when watchman reads timeout."""
        with self.setup_subscribed([None]):
            out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
            self.assertEquals(list(out), [(None, None)])

    def test_subscribed_response(self):
        """Test yielding on the watchman response to the initial subscribe command."""
        with self.setup_subscribed([dict(subscribe="test")]):
            out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
            self.assertEquals(list(out), [(None, None)])

    def test_subscribed_event(self):
        """Test yielding on a watchman event for a given subscription."""
        test_event = dict(subscription="test3", msg="blah")

        with self.setup_subscribed([test_event]):
            out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
            self.assertEquals(list(out), [("test3", test_event)])

    def test_subscribed_unknown_event(self):
        """Test yielding on an unknown watchman event."""
        with self.setup_subscribed([dict(unknown=True)]):
            out = self.watchman.subscribed(self.BUILD_ROOT, self.HANDLERS)
            self.assertEquals(list(out), [])
Beispiel #19
0
 def test_resolve_watchman_path_provided_exception(self):
   with self.assertRaises(Watchman.ExecutionError):
     self.watchman = Watchman('/fake/path/to/watchman',
                              metadata_base_dir=self.subprocess_dir)
Beispiel #20
0
 def setUp(self):
     BaseTest.setUp(self)
     with mock.patch.object(Watchman, '_resolve_watchman_path',
                            **self.PATCH_OPTS) as mock_find:
         mock_find.return_value = self.WATCHMAN_PATH
         self.watchman = Watchman(self.WORK_DIR)
Beispiel #21
0
 def test_resolve_watchman_path_provided_exception(self):
     with self.assertRaises(Watchman.ExecutionError):
         self.watchman = Watchman('/fake/path/to/watchman', self.WORK_DIR)
Beispiel #22
0
 def test_resolve_watchman_path_provided_exception(self):
   with self.assertRaises(Watchman.ExecutionError):
     self.watchman = Watchman('/fake/path/to/watchman', self.WORK_DIR)
Beispiel #23
0
 def setUp(self):
   BaseTest.setUp(self)
   with mock.patch.object(Watchman, '_is_valid_executable', **self.PATCH_OPTS) as mock_is_valid:
     mock_is_valid.return_value = True
     self.watchman = Watchman('/fake/path/to/watchman', self.WORK_DIR)