diff --git a/tests/python/pants_test/pantsd/BUILD b/tests/python/pants_test/pantsd/BUILD index ed3b038ce77..4184846ddaa 100644 --- a/tests/python/pants_test/pantsd/BUILD +++ b/tests/python/pants_test/pantsd/BUILD @@ -71,7 +71,7 @@ python_tests( 'tests/python/pants_test/testutils:process_test_util' ], tags = {'integration'}, - timeout = 300 + timeout = 600 ) python_tests( diff --git a/tests/python/pants_test/pantsd/test_pantsd_integration.py b/tests/python/pants_test/pantsd/test_pantsd_integration.py index 17d4cc8912a..084fbde082f 100644 --- a/tests/python/pants_test/pantsd/test_pantsd_integration.py +++ b/tests/python/pants_test/pantsd/test_pantsd_integration.py @@ -5,6 +5,8 @@ from __future__ import (absolute_import, division, generators, nested_scopes, print_function, unicode_literals, with_statement) +import functools +import itertools import os import signal import time @@ -19,15 +21,30 @@ class PantsDaemonMonitor(ProcessManager): def __init__(self, metadata_base_dir=None): super(PantsDaemonMonitor, self).__init__(name='pantsd', metadata_base_dir=metadata_base_dir) - def await_pantsd(self, timeout=10): + def _log(self): + print('PantsDaemonMonitor: pid is {} is_alive={}'.format(self._pid, self.is_alive())) + + def await_pantsd(self, timeout=3): + self._process = None self._pid = self.await_pid(timeout) self.assert_running() + return self._pid def assert_running(self): + self._log() assert self._pid is not None and self.is_alive(), 'pantsd should be running!' + return self._pid def assert_stopped(self): + self._log() assert self._pid is not None and self.is_dead(), 'pantsd should be stopped!' + return self._pid + + +def banner(s): + print('=' * 63) + print('- {} {}'.format(s, '-' * (60 - len(s)))) + print('=' * 63) def read_pantsd_log(workdir): @@ -43,6 +60,7 @@ def pantsd_test_context(self, log_level='info'): with self.temporary_workdir() as workdir_base: pid_dir = os.path.join(workdir_base, '.pids') workdir = os.path.join(workdir_base, '.workdir.pants.d') + print('\npantsd log is {}/pantsd/pantsd.log'.format(workdir)) pantsd_config = { 'GLOBAL': { 'enable_pantsd': True, @@ -54,79 +72,56 @@ def pantsd_test_context(self, log_level='info'): } } checker = PantsDaemonMonitor(pid_dir) - yield workdir, pantsd_config, checker - - def test_pantsd_compile(self): - with self.pantsd_test_context('debug') as (workdir, pantsd_config, checker): - # Explicitly kill any running pantsd instances for the current buildroot. - self.assert_success(self.run_pants_with_workdir(['kill-pantsd'], workdir, pantsd_config)) + self.assert_success_runner(workdir, pantsd_config, ['kill-pantsd']) try: - # Start pantsd implicitly via a throwaway invocation. - self.assert_success(self.run_pants_with_workdir(['help'], workdir, pantsd_config)) - checker.await_pantsd() - - # This tests a deeper pantsd-based run by actually invoking a full compile. - self.assert_success( - self.run_pants_with_workdir( - ['compile', 'examples/src/scala/org/pantsbuild/example/hello/welcome'], - workdir, - pantsd_config) - ) - checker.assert_running() + yield workdir, pantsd_config, checker finally: - try: - for line in read_pantsd_log(workdir): - print(line) - finally: - # Explicitly kill pantsd (from a pantsd-launched runner). - self.assert_success(self.run_pants_with_workdir(['kill-pantsd'], workdir, pantsd_config)) - checker.assert_stopped() + banner('BEGIN pantsd.log') + for line in read_pantsd_log(workdir): + print(line) + banner('END pantsd.log') + self.assert_success_runner(workdir, pantsd_config, ['kill-pantsd']) + checker.assert_stopped() + + @contextmanager + def pantsd_successful_run_context(self, log_level='info'): + with self.pantsd_test_context(log_level) as (workdir, pantsd_config, checker): + yield ( + functools.partial( + self.assert_success_runner, + workdir, + pantsd_config + ), + checker, + workdir + ) + + def assert_success_runner(self, workdir, config, cmd): + print('running: ./pants {}'.format(' '.join(cmd))) + return self.assert_success( + self.run_pants_with_workdir(cmd, workdir, config) + ) + + def test_pantsd_compile(self): + with self.pantsd_successful_run_context('debug') as (pantsd_run, checker, workdir): + # This tests a deeper pantsd-based run by actually invoking a full compile. + pantsd_run(['compile', 'examples/src/scala/org/pantsbuild/example/hello/welcome']) + checker.await_pantsd() def test_pantsd_run(self): - with self.pantsd_test_context('debug') as (workdir, pantsd_config, checker): - print('log: {}/pantsd/pantsd.log'.format(workdir)) - # Explicitly kill any running pantsd instances for the current buildroot. - print('\nkill-pantsd') - self.assert_success(self.run_pants_with_workdir(['kill-pantsd'], workdir, pantsd_config)) - try: - # Start pantsd implicitly via a throwaway invocation. - print('help') - self.assert_success(self.run_pants_with_workdir(['help'], workdir, pantsd_config)) - checker.await_pantsd() - - print('list 3rdparty:') - self.assert_success(self.run_pants_with_workdir(['list', '3rdparty:'], - workdir, - pantsd_config)) - checker.assert_running() + with self.pantsd_successful_run_context('debug') as (pantsd_run, checker, workdir): + pantsd_run(['list', '3rdparty:']) + checker.await_pantsd() - print('list :') - self.assert_success(self.run_pants_with_workdir(['list', ':'], - workdir, - pantsd_config)) - checker.assert_running() + pantsd_run(['list', ':']) + checker.assert_running() - print('list ::') - self.assert_success(self.run_pants_with_workdir(['list', '::'], - workdir, - pantsd_config)) - checker.assert_running() + pantsd_run(['list', '::']) + checker.assert_running() - # And again using the cached BuildGraph. - print('list ::') - self.assert_success(self.run_pants_with_workdir(['list', '::'], - workdir, - pantsd_config)) - checker.assert_running() - finally: - try: - for line in read_pantsd_log(workdir): - print(line) - finally: - # Explicitly kill pantsd (from a pantsd-launched runner). - print('kill-pantsd') - self.assert_success(self.run_pants_with_workdir(['kill-pantsd'], workdir, pantsd_config)) - checker.assert_stopped() + # And again using the cached BuildGraph. + pantsd_run(['list', '::']) + checker.assert_running() # Assert there were no warnings or errors thrown in the pantsd log. for line in read_pantsd_log(workdir): @@ -138,66 +133,56 @@ def test_pantsd_run(self): def test_pantsd_broken_pipe(self): with self.pantsd_test_context() as (workdir, pantsd_config, checker): - # Explicitly kill any running pantsd instances for the current buildroot. - self.assert_success(self.run_pants_with_workdir(['kill-pantsd'], workdir, pantsd_config)) - try: - # Start pantsd implicitly via a throwaway invocation. - self.assert_success(self.run_pants_with_workdir(['help'], workdir, pantsd_config)) - checker.await_pantsd() - - run = self.run_pants_with_workdir('help | head -1', workdir, pantsd_config, shell=True) - self.assertNotIn('broken pipe', run.stderr_data.lower()) - checker.assert_running() - finally: - # Explicitly kill pantsd (from a pantsd-launched runner). - self.assert_success(self.run_pants_with_workdir(['kill-pantsd'], workdir, pantsd_config)) - checker.assert_stopped() + run = self.run_pants_with_workdir('help | head -1', workdir, pantsd_config, shell=True) + self.assertNotIn('broken pipe', run.stderr_data.lower()) + checker.await_pantsd() def test_pantsd_stacktrace_dump(self): - with self.pantsd_test_context() as (workdir, pantsd_config, checker): - print('log: {}/pantsd/pantsd.log'.format(workdir)) - # Explicitly kill any running pantsd instances for the current buildroot. - self.assert_success(self.run_pants_with_workdir(['kill-pantsd'], workdir, pantsd_config)) - try: - # Start pantsd implicitly via a throwaway invocation. - self.assert_success(self.run_pants_with_workdir(['help'], workdir, pantsd_config)) - checker.await_pantsd() + with self.pantsd_successful_run_context() as (pantsd_run, checker, workdir): + pantsd_run(['help']) + checker.await_pantsd() - os.kill(checker.pid, signal.SIGUSR2) + os.kill(checker.pid, signal.SIGUSR2) - # Wait for log flush. - time.sleep(2) + # Wait for log flush. + time.sleep(2) - self.assertIn('Current thread 0x', '\n'.join(read_pantsd_log(workdir))) - finally: - # Explicitly kill pantsd (from a pantsd-launched runner). - self.assert_success(self.run_pants_with_workdir(['kill-pantsd'], workdir, pantsd_config)) - checker.assert_stopped() + self.assertIn('Current thread 0x', '\n'.join(read_pantsd_log(workdir))) - def test_pantsd_runner_doesnt_die_after_failed_run(self): + def test_pantsd_pantsd_runner_doesnt_die_after_failed_run(self): with self.pantsd_test_context() as (workdir, pantsd_config, checker): - self.assert_success(self.run_pants_with_workdir(['kill-pantsd'], workdir, pantsd_config)) - try: - # Start pantsd implicitly via a throwaway invocation. - self.assert_success(self.run_pants_with_workdir(['help'], workdir, pantsd_config)) - checker.await_pantsd() - - # Run target that throws an exception in pants. - self.assert_failure( - self.run_pants_with_workdir( - ['bundle', 'testprojects/src/java/org/pantsbuild/testproject/bundle:missing-files'], - workdir, - pantsd_config) - ) - checker.assert_running() - - # Check for no stray pantsd-runner prcesses. - self.assertFalse(check_process_exists_by_command('pantsd-runner')) - - # Assert pantsd is in a good functional state. - self.assert_success(self.run_pants_with_workdir(['help'], workdir, pantsd_config)) + # Run target that throws an exception in pants. + self.assert_failure( + self.run_pants_with_workdir( + ['bundle', 'testprojects/src/java/org/pantsbuild/testproject/bundle:missing-files'], + workdir, + pantsd_config) + ) + checker.await_pantsd() + + # Check for no stray pantsd-runner prcesses. + self.assertFalse(check_process_exists_by_command('pantsd-runner')) + + # Assert pantsd is in a good functional state. + self.assert_success(self.run_pants_with_workdir(['help'], workdir, pantsd_config)) + checker.assert_running() + + def test_pantsd_lifecycle_invalidation(self): + """Runs pants commands with pantsd enabled, in a loop, alternating between options that + should invalidate pantsd and incur a restart and then asserts for pid consistency. + """ + with self.pantsd_successful_run_context() as (pantsd_run, checker, workdir): + variants = ( + ['-ldebug', 'help'], + ['-linfo', 'help'] + ) + last_pid = None + for cmd in itertools.chain(*itertools.repeat(variants, 3)): + pantsd_run(cmd) + next_pid = checker.await_pantsd() + if last_pid: + self.assertNotEqual(last_pid, next_pid) + last_pid = next_pid + + pantsd_run(cmd) checker.assert_running() - finally: - # Explicitly kill pantsd (from a pantsd-launched runner). - self.assert_success(self.run_pants_with_workdir(['kill-pantsd'], workdir, pantsd_config)) - checker.assert_stopped()