def test_native_logging(self): expected_msg = "\[DEBUG\] engine::scheduler: Launching \d+ root" pants_run = self.run_pants(["-linfo", "list", "src/scala::"]) assertNotRegex(self, pants_run.stderr_data, expected_msg) pants_run = self.run_pants(["-ldebug", "list", "src/scala::"]) assertRegex(self, pants_run.stderr_data, expected_msg)
def test_pantsd_parent_runner_killed(self): with self.pantsd_test_context() as (workdir, config, checker): # Launch a run that will wait for a file to be created (but do not create that file). file_to_make = os.path.join(workdir, 'some_magic_file') waiter_handle = self.run_pants_with_workdir_without_waiting( [ 'run', 'testprojects/src/python/coordinated_runs:waiter', '--', file_to_make ], workdir, config, ) # Wait for the python run to be running. time.sleep(5) checker.assert_started() # Locate the single "parent" pantsd-runner process, and kill it. pantsd_runner_processes = [ p for p in checker.runner_process_context.current_processes() if p.ppid() == 1 ] self.assertEquals(1, len(pantsd_runner_processes)) parent_runner_process = pantsd_runner_processes[0] # Send SIGTERM parent_runner_process.terminate() waiter_run = waiter_handle.join() # Ensure that we saw the pantsd-runner process's failure in the client's stderr. self.assert_failure(waiter_run) assertRegex( self, waiter_run.stderr_data, """\ Signal {signum} was raised\\. Exiting with failure\\. \\(backtrace omitted\\) """.format(signum=signal.SIGTERM))
def test_pantsd_invalidation_file_tracking(self): test_file = 'testprojects/src/python/print_env/main.py' config = { 'GLOBAL': { 'pantsd_invalidation_globs': '["testprojects/src/python/print_env/*"]' } } with self.pantsd_successful_run_context( extra_config=config) as (pantsd_run, checker, workdir, _): pantsd_run(['help']) checker.assert_started() # Let any fs events quiesce. time.sleep(5) def full_pantsd_log(): return '\n'.join(read_pantsd_log(workdir)) # Check the logs. assertRegex(self, full_pantsd_log(), r'watching invalidating files:.*{}'.format(test_file)) checker.assert_running() touch(test_file) # Permit ample time for the async file event propagate in CI. time.sleep(10) checker.assert_stopped() self.assertIn('saw file events covered by invalidation globs', full_pantsd_log())
def test_no_fast(self): args = ['test.go', '--no-fast', 'contrib/go/examples/src/go/libA'] pants_run = self.run_pants(args) self.assert_success(pants_run) # libA depends on libB, so both tests should be run. assertRegex(self, pants_run.stdout_data, r'ok\s+libA') assertRegex(self, pants_run.stdout_data, r'ok\s+libB')
def test_pantsd_invalidation_stale_sources(self): test_path = 'tests/python/pants_test/daemon_correctness_test_0001' test_build_file = os.path.join(test_path, 'BUILD') test_src_file = os.path.join(test_path, 'some_file.py') has_source_root_regex = r'"source_root": ".*/{}"'.format(test_path) export_cmd = ['export', test_path] try: with self.pantsd_successful_run_context() as (pantsd_run, checker, workdir, _): safe_mkdir(test_path, clean=True) pantsd_run(['help']) checker.assert_started() safe_file_dump( test_build_file, "python_library(sources=globs('some_non_existent_file.py'))" ) result = pantsd_run(export_cmd) checker.assert_running() assertNotRegex(self, result.stdout_data, has_source_root_regex) safe_file_dump(test_build_file, "python_library(sources=globs('*.py'))") result = pantsd_run(export_cmd) checker.assert_running() assertNotRegex(self, result.stdout_data, has_source_root_regex) safe_file_dump(test_src_file, 'import this\n') result = pantsd_run(export_cmd) checker.assert_running() assertRegex(self, result.stdout_data, has_source_root_regex) finally: rm_rf(test_path)
def test_rst_normal(self): pants_run = self.run_pants([ 'markdown', 'testprojects/src/java/org/pantsbuild/testproject/page:senserst' ]) self.assert_success(pants_run) out_path = os.path.join( get_buildroot(), 'dist', 'markdown/html', 'testprojects/src/java/org/pantsbuild/testproject/page', 'sense.html') with safe_open(out_path, 'r') as outfile: page_html = outfile.read() # should get Sense and Sensibility in title (or TITLE, sheesh): assertRegex( self, page_html, r'(?i).*<title[^>]*>\s*Sense\s+and\s+Sensibility\s*</title') # should get formatted with h1: assertRegex( self, page_html, r'(?i).*<h1[^>]*>\s*They\s+Heard\s+Her\s+With\s+Surprise\s*</h1>' ) # should get formatted with _something_ assertRegex(self, page_html, r'.*>\s*inhabiting\s*</') assertRegex(self, page_html, r'.*>\s*civilly\s*</') # there should be a link that has href="http://www.calderdale.gov.uk/" assertRegex(self, page_html, r'.*<a [^>]*href\s*=\s*[\'"]http://www.calderdale')
def test_backup_logging_on_fatal_error(self): sink = self._gen_sink_subclass() with self.captured_logging(level=logging.ERROR) as captured: with temporary_dir() as tmpdir: sink.reset_log_location(tmpdir) with mock.patch.object(sink, '_try_write_with_flush', autospec=sink) as mock_write: mock_write.side_effect = ExceptionSink.ExceptionSinkError( 'fake write failure') sink.log_exception('XXX') errors = list(captured.errors()) self.assertEqual(2, len(errors)) def format_log_rx(log_file_type): return '.*'.join( re.escape(s) for s in [ "pants.base.exception_sink: Error logging the message 'XXX' to the {log_file_type} file " "handle for".format(log_file_type=log_file_type), "at pid {pid}".format(pid=os.getpid()), "\nfake write failure", ]) assertRegex(self, str(errors[0]), format_log_rx('pid-specific')) assertRegex(self, str(errors[1]), format_log_rx('shared'))
def test_pantsd_invalidation_file_tracking(self): test_dir = 'testprojects/src/python/print_env' config = {'GLOBAL': {'pantsd_invalidation_globs': '["%s/*"]' %(test_dir)}} with self.pantsd_successful_run_context(extra_config=config) as ( pantsd_run, checker, workdir, _ ): pantsd_run(['help']) checker.assert_started() # Let any fs events quiesce. time.sleep(5) def full_pantsd_log(): return '\n'.join(read_pantsd_log(workdir)) # Check the logs. assertRegex( self, full_pantsd_log(), r'watching invalidating files:.*{}'.format(test_dir) ) checker.assert_running() # Create a new file in test_dir with temporary_file(suffix='.py', binary_mode=False, root_dir=test_dir) as temp_f: temp_f.write("import that\n") temp_f.close() time.sleep(10) checker.assert_stopped() self.assertIn('saw file events covered by invalidation globs', full_pantsd_log())
def test_logs_unhandled_exception(self): with temporary_dir() as tmpdir: pants_run = self.run_pants_with_workdir( [ '--no-enable-pantsd', 'list', '//:this-target-does-not-exist' ], workdir=tmpdir, # The backtrace should be omitted when --print-exception-stacktrace=False. print_exception_stacktrace=False) self.assert_failure(pants_run) assertRegex( self, pants_run.stderr_data, """\\A\ timestamp: ([^\n]+) Exception caught: \\(pants\\.build_graph\\.address_lookup_error\\.AddressLookupError\\) \\(backtrace omitted\\) Exception message: Build graph construction failed: ExecutionError 1 Exception encountered: ResolveError: "this-target-does-not-exist" was not found in namespace ""\\. Did you mean one of: """) pid_specific_log_file, shared_log_file = self._get_log_file_paths( tmpdir, pants_run) self._assert_unhandled_exception_log_matches( pants_run.pid, read_file(pid_specific_log_file, binary_mode=False)) self._assert_unhandled_exception_log_matches( pants_run.pid, read_file(shared_log_file, binary_mode=False))
def test_pantsd_invalidation_stale_sources(self): test_path = 'tests/python/pants_test/daemon_correctness_test_0001' test_build_file = os.path.join(test_path, 'BUILD') test_src_file = os.path.join(test_path, 'some_file.py') has_source_root_regex = r'"source_root": ".*/{}"'.format(test_path) export_cmd = ['export', test_path] try: with self.pantsd_successful_run_context() as (pantsd_run, checker, workdir, _): safe_mkdir(test_path, clean=True) pantsd_run(['help']) checker.assert_started() safe_file_dump(test_build_file, "python_library(sources=globs('some_non_existent_file.py'))") result = pantsd_run(export_cmd) checker.assert_running() assertNotRegex(self, result.stdout_data, has_source_root_regex) safe_file_dump(test_build_file, "python_library(sources=globs('*.py'))") result = pantsd_run(export_cmd) checker.assert_running() assertNotRegex(self, result.stdout_data, has_source_root_regex) safe_file_dump(test_src_file, 'import this\n') result = pantsd_run(export_cmd) checker.assert_running() assertRegex(self, result.stdout_data, has_source_root_regex) finally: rm_rf(test_path)
def test_list_parse_java_targets(self): pants_run = self.do_command('list', 'testprojects/tests/java/org/pantsbuild/build_parsing::', success=True) assertRegex(self, pants_run.stdout_data, r'testprojects/tests/java/org/pantsbuild/build_parsing:trailing_glob_doublestar' )
def test_engine_list(self): pants_run = self.run_pants(['-ldebug', 'list', '3rdparty::']) self.assert_success(pants_run) assertRegex(self, pants_run.stderr_data, 'build_graph is: .*LegacyBuildGraph') assertRegex(self, pants_run.stderr_data, 'computed \d+ nodes in') assertNotRegex(self, pants_run.stderr_data, 'pantsd is running at pid \d+')
def _assert_graceful_signal_log_matches(self, pid, signum, contents): assertRegex( self, contents, """\ timestamp: ([^\n]+) process title: ([^\n]+) sys\\.argv: ([^\n]+) pid: {pid} Signal {signum} was raised\\. Exiting with failure\\. \\(backtrace omitted\\) """.format(pid=pid, signum=signum))
def test_run_repl_with_3(self): # Run a Python 3 repl on a Python 2/3 library target. Avoid some known-to-choke-on interpreters. command = ['repl', '--python-setup-interpreter-constraints=["CPython>=3.3"]', 'testprojects/src/python/interpreter_selection:echo_interpreter_version_lib', '--quiet'] program = 'from interpreter_selection.echo_interpreter_version import say_hello; say_hello()' pants_run = self.run_pants(command=command, stdin_data=program) assertRegex(self, pants_run.stdout_data, r'3\.\d\.\d')
def assertWarnInfoOutput(self, lines): """Check to see that only warn and info output appears in the stream. The first line may start with WARN] or WARNING] depending on whether 'WARN' has been registered as a global log level. See options_bootstrapper.py. """ self.assertEqual(2, len(lines)) assertRegex(self, lines[0], '^WARN\w*] warn') self.assertEqual('INFO] info', lines[1])
def test_no_fast(self): args = ['test.go', '--no-fast', 'contrib/go/examples/src/go/libA'] pants_run = self.run_pants(args) self.assert_success(pants_run) # libA depends on libB, so both tests should be run. assertRegex(self, pants_run.stdout_data, r'ok\s+libA') assertRegex(self, pants_run.stdout_data, r'ok\s+libB')
def test_list_parse_java_targets(self): pants_run = self.do_command( 'list', 'testprojects/tests/java/org/pantsbuild/build_parsing::', success=True) assertRegex( self, pants_run.stdout_data, r'testprojects/tests/java/org/pantsbuild/build_parsing:trailing_glob_doublestar' )
def test_run_repl_with_3(self): # Run a Python 3 repl on a Python 2/3 library target. Avoid some known-to-choke-on interpreters. command = [ 'repl', '--python-setup-interpreter-constraints=["CPython>=3.3"]', 'testprojects/src/python/interpreter_selection:echo_interpreter_version_lib', '--quiet' ] program = 'from interpreter_selection.echo_interpreter_version import say_hello; say_hello()' pants_run = self.run_pants(command=command, stdin_data=program) assertRegex(self, pants_run.stdout_data, r'3\.\d\.\d')
def test_exclude_invalid_string(self): build_path = os.path.join(self.PATH_PREFIX, 'BUILD.invalid') build_content = '''python_library(name='exclude_strings_disallowed', sources=rglobs('*.py', exclude='aa.py'))''' with self.temporary_file_content(build_path, build_content, binary_mode=False): pants_run = self.do_command('filemap', self._mk_target('exclude_strings_disallowed'), success=False) assertRegex(self, pants_run.stderr_data, r'Excludes of type `.*` are not supported')
def test_go_command_gopath(self): go_distribution = self.distribution() go_cmd = go_distribution.create_go_cmd(cmd='env', gopath='/tmp/fred', args=['GOROOT']) self.assertEqual({'GOROOT': go_distribution.goroot, 'GOPATH': '/tmp/fred'}, go_cmd.env) self.assertEqual('go', os.path.basename(go_cmd.cmdline[0])) self.assertEqual(['env', 'GOROOT'], go_cmd.cmdline[1:]) regex = GoDistributionTest._generate_go_command_regex(gopath='/tmp/fred', final_value='GOROOT') assertRegex(self, str(go_cmd), regex)
def test_prints_traceback_on_sigusr2(self): with self._make_waiter_handle() as (workdir, pid, join): # Send SIGUSR2, then sleep so the signal handler from faulthandler.register() can run. os.kill(pid, signal.SIGUSR2) time.sleep(1) waiter_run = join() self.assert_success(waiter_run) assertRegex(self, waiter_run.stderr_data, """\ Current thread [^\n]+ \\(most recent call first\\): """)
def test_prints_traceback_on_sigusr2(self): with self._make_waiter_handle() as (workdir, pid, join): # Send SIGUSR2, then sleep so the signal handler from faulthandler.register() can run. os.kill(pid, signal.SIGUSR2) time.sleep(1) waiter_run = join() self.assert_success(waiter_run) assertRegex(self, waiter_run.stderr_data, """\ Current thread [^\n]+ \\(most recent call first\\): """)
def test_native_logging(self): expected_msg = "\[DEBUG\] engine::scheduler: Launching \d+ root" pants_run = self.run_pants([ "-linfo", "list", "src/scala::" ]) assertNotRegex(self, pants_run.stderr_data, expected_msg) pants_run = self.run_pants([ "-ldebug", "list", "src/scala::" ]) assertRegex(self, pants_run.stderr_data, expected_msg)
def _assert_graceful_signal_log_matches(self, pid, signum, signame, contents): assertRegex(self, contents, """\ timestamp: ([^\n]+) process title: ([^\n]+) sys\\.argv: ([^\n]+) pid: {pid} Signal {signum} \\({signame}\\) was raised\\. Exiting with failure\\. """.format(pid=pid, signum=signum, signame=signame)) # Ensure we write all output such as stderr and reporting files before closing any streams. self.assertNotIn( 'Exception message: I/O operation on closed file.', contents)
def _assert_graceful_signal_log_matches(self, pid, signum, signame, contents): assertRegex(self, contents, """\ timestamp: ([^\n]+) process title: ([^\n]+) sys\\.argv: ([^\n]+) pid: {pid} Signal {signum} \\({signame}\\) was raised\\. Exiting with failure\\. """.format(pid=pid, signum=signum, signame=signame)) # Ensure we write all output such as stderr and reporting files before closing any streams. self.assertNotIn( 'Exception message: I/O operation on closed file.', contents)
def _assert_pantsd_keyboardinterrupt_signal(self, signum, regexps=[], quit_timeout=None): """Send a signal to the thin pailgun client and observe the error messaging. :param int signum: The signal to send. :param regexps: Assert that all of these regexps match somewhere in stderr. :type regexps: list of str :param float quit_timeout: The duration of time to wait for the pailgun client to flush all of its output and die after being killed. """ # TODO: This tests that pantsd-runner processes actually die after the thin client receives the # specified signal. with self.pantsd_test_context() as (workdir, config, checker): # Launch a run that will wait for a file to be created (but do not create that file). file_to_make = os.path.join(workdir, 'some_magic_file') if quit_timeout is not None: timeout_args = [ '--pantsd-pailgun-quit-timeout={}'.format(quit_timeout) ] else: timeout_args = [] argv = timeout_args + [ 'run', 'testprojects/src/python/coordinated_runs:waiter', '--', file_to_make ] waiter_handle = self.run_pants_with_workdir_without_waiting( argv, workdir, config) client_pid = waiter_handle.process.pid checker.assert_started() checker.assert_pantsd_runner_started(client_pid) # Get all the pantsd-runner processes while they're still around. pantsd_runner_processes = checker.runner_process_context.current_processes( ) # This should kill the pantsd-runner processes through the RemotePantsRunner signal handler. os.kill(client_pid, signum) waiter_run = waiter_handle.join() self.assert_failure(waiter_run) for regexp in regexps: assertRegex(self, waiter_run.stderr_data, regexp) time.sleep(1) for proc in pantsd_runner_processes: # TODO: we could be checking the return codes of the subprocesses, but psutil is currently # limited on non-Windows hosts -- see https://psutil.readthedocs.io/en/latest/#processes. # The pantsd-runner processes should be dead, and they should have exited with 1. self.assertFalse(proc.is_running())
def test_dumps_stack_trace(self): graph = ExecutionGraph([self.job('A', raising_wrapper, [])], True) capturing_logger = CapturingLogger() with self.assertRaises(ExecutionFailure): graph.execute(ImmediatelyExecutingPool(), capturing_logger) error_logs = capturing_logger.log_entries['error'] self.assertEqual(2, len(error_logs), msg='Wanted one error log, got: {}'.format(error_logs)) self.assertEqual("A failed: I'm an error", error_logs[0]) regex = re.compile( "Traceback:.*in raising_wrapper.*raise Exception\\(\"I'm an error\"\\)", re.DOTALL, ) assertRegex(self, error_logs[1], regex)
def test_dumps_traceback_on_sigabrt(self): # SIGABRT sends a traceback to the log file for the current process thanks to # faulthandler.enable(). with self._send_signal_to_waiter_handle(signal.SIGABRT) as (workdir, waiter_run): # Check that the logs show an abort signal and the beginning of a traceback. pid_specific_log_file, shared_log_file = self._get_log_file_paths(workdir, waiter_run) assertRegex(self, read_file(pid_specific_log_file), """\ Fatal Python error: Aborted Thread [^\n]+ \\(most recent call first\\): """) # faulthandler.enable() only allows use of a single logging file at once for fatal tracebacks. self.assertEqual('', read_file(shared_log_file))
def test_dumps_traceback_on_sigabrt(self): # SIGABRT sends a traceback to the log file for the current process thanks to # faulthandler.enable(). with self._send_signal_to_waiter_handle(signal.SIGABRT) as (workdir, waiter_run): # Check that the logs show an abort signal and the beginning of a traceback. pid_specific_log_file, shared_log_file = self._get_log_file_paths(workdir, waiter_run) assertRegex(self, read_file(pid_specific_log_file, binary_mode=False), """\ Fatal Python error: Aborted Thread [^\n]+ \\(most recent call first\\): """) # faulthandler.enable() only allows use of a single logging file at once for fatal tracebacks. self.assertEqual('', read_file(shared_log_file, binary_mode=False))
def test_nailgun_connect_timeout(self): pants_run = self.run_pants( ['compile', self.target], # Override the PANTS_CONFIG_FILES="pants.travis-ci.ini" used within TravisCI to enable # nailgun usage for the purpose of exercising that stack in the integration test. config={'DEFAULT': {'execution_strategy': 'nailgun'}, 'compile.zinc': {'nailgun_timeout_seconds': '0.00002'}} ) self.assert_failure(pants_run) assertRegex(self, pants_run.stdout_data, """\ compile\\(examples/src/java/org/pantsbuild/example/hello/greet:greet\\) failed: \ Problem launching via <no nailgun connection> command org\\.pantsbuild\\.zinc\\.compiler\\.Main .*: \ Failed to read nailgun output after 2e\-05 seconds!""")
def _assert_unhandled_exception_log_matches(self, pid, file_contents): assertRegex( self, file_contents, """\ timestamp: ([^\n]+) process title: ([^\n]+) sys\\.argv: ([^\n]+) pid: {pid} Exception caught: \\(pants\\.build_graph\\.address_lookup_error\\.AddressLookupError\\) (.|\n)* Exception message: Build graph construction failed: ExecutionError 1 Exception encountered: ResolveError: "this-target-does-not-exist" was not found in namespace ""\\. Did you mean one of: """.format(pid=pid))
def test_go_test_with_options(self): args = [ 'test.go', '--shlexed-build-and-test-flags=["-v"]', 'contrib/go/examples/src/go/libA', ] pants_run = self.run_pants(args) self.assert_success(pants_run) assertRegex(self, pants_run.stdout_data, r'ok\s+libA') assertRegex(self, pants_run.stdout_data, r'ok\s+libB') # Ensure that more verbose output is presented. self.assertIn('=== RUN TestAdd', pants_run.stdout_data) self.assertIn('PASS', pants_run.stdout_data)
def test_changed_with_deleted_target_transitive(self): with create_isolated_git_repo() as worktree: safe_delete( os.path.join( worktree, 'src/resources/org/pantsbuild/resourceonly/BUILD')) pants_run = self.run_pants([ 'list', '--changed-parent=HEAD', '--changed-include-dependees=transitive' ]) self.assert_failure(pants_run) assertRegex( self, pants_run.stderr_data, 'src/resources/org/pantsbuild/resourceonly:.*did not exist')
def test_prints_traceback_on_sigusr2(self): with self._make_waiter_handle() as (workdir, waiter_handle): time.sleep(5) # Send SIGUSR2, then sleep so the signal handler from faulthandler.register() can run. os.kill(waiter_handle.process.pid, signal.SIGUSR2) time.sleep(1) # This target will wait forever, so kill the process and ensure its output is correct. os.kill(waiter_handle.process.pid, signal.SIGKILL) waiter_run = waiter_handle.join() self.assert_failure(waiter_run) assertRegex( self, waiter_run.stderr_data, """\ Current thread [^\n]+ \\(most recent call first\\): """)
def test_go_test_with_options(self): args = [ 'test.go', '--shlexed-build-and-test-flags=["-v"]', 'contrib/go/examples/src/go/libA', ] pants_run = self.run_pants(args) self.assert_success(pants_run) assertRegex(self, pants_run.stdout_data, r'ok\s+libA') assertRegex(self, pants_run.stdout_data, r'ok\s+libB') # Ensure that more verbose output is presented. self.assertIn('=== RUN TestAdd', pants_run.stdout_data) self.assertIn('PASS', pants_run.stdout_data)
def _assert_unhandled_exception_log_matches(self, pid, file_contents): assertRegex(self, file_contents, """\ timestamp: ([^\n]+) process title: ([^\n]+) sys\\.argv: ([^\n]+) pid: {pid} Exception caught: \\([^)]*\\) (.|\n)* Exception message:.* 1 Exception encountered: ResolveError: "this-target-does-not-exist" was not found in namespace ""\\. Did you mean one of: """.format(pid=pid)) # Ensure we write all output such as stderr and reporting files before closing any streams. self.assertNotIn( 'Exception message: I/O operation on closed file.', file_contents)
def test_pants_binary(self): with temporary_dir() as tmp_dir: pex = os.path.join(tmp_dir, 'main.pex') command=[ '--pants-distdir={}'.format(tmp_dir), 'binary', '{}:main'.format(self.fasthello_project)] pants_run = self.run_pants(command=command) self.assert_success(pants_run) # Check that the pex was built. self.assertTrue(os.path.isfile(pex)) # Check that the pex runs. output = subprocess.check_output(pex).decode('utf-8') self._assert_native_greeting(output) # Check that we have exactly one wheel output. single_wheel_output = assert_single_element(glob.glob(os.path.join(tmp_dir, '*.whl'))) assertRegex(self, os.path.basename(single_wheel_output), r'\A{}'.format(re.escape('fasthello-1.0.0+')))
def _assert_unhandled_exception_log_matches(self, pid, file_contents): assertRegex(self, file_contents, """\ timestamp: ([^\n]+) process title: ([^\n]+) sys\\.argv: ([^\n]+) pid: {pid} Exception caught: \\(pants\\.build_graph\\.address_lookup_error\\.AddressLookupError\\) (.|\n)* Exception message: Build graph construction failed: ExecutionError 1 Exception encountered: ResolveError: "this-target-does-not-exist" was not found in namespace ""\\. Did you mean one of: """.format(pid=pid)) # Ensure we write all output such as stderr and reporting files before closing any streams. self.assertNotIn( 'Exception message: I/O operation on closed file.', file_contents)
def test_dumps_logs_on_terminate(self): # Send a SIGTERM to the local pants process. with self._send_signal_to_waiter_handle( signal.SIGTERM) as (workdir, waiter_run): assertRegex( self, waiter_run.stderr_data, """\ timestamp: ([^\n]+) Signal {signum} was raised. Exiting with failure. \\(backtrace omitted\\) """.format(signum=signal.SIGTERM)) # Check that the logs show a graceful exit by SIGTERM. pid_specific_log_file, shared_log_file = self._get_log_file_paths( workdir, waiter_run) self._assert_graceful_signal_log_matches( waiter_run.pid, signal.SIGTERM, read_file(pid_specific_log_file, binary_mode=False)) self._assert_graceful_signal_log_matches( waiter_run.pid, signal.SIGTERM, read_file(shared_log_file, binary_mode=False))
def test_dumps_logs_on_signal(self): """Send signals which are handled, but don't get converted into a KeyboardInterrupt.""" signal_names = { signal.SIGQUIT: 'SIGQUIT', signal.SIGTERM: 'SIGTERM', } for (signum, signame) in signal_names.items(): with self._send_signal_to_waiter_handle(signum) as (workdir, waiter_run): assertRegex(self, waiter_run.stderr_data, """\ timestamp: ([^\n]+) Signal {signum} \\({signame}\\) was raised\\. Exiting with failure\\. """.format(signum=signum, signame=signame)) # Check that the logs show a graceful exit by SIGTERM. pid_specific_log_file, shared_log_file = self._get_log_file_paths(workdir, waiter_run) self._assert_graceful_signal_log_matches( waiter_run.pid, signum, signame, read_file(pid_specific_log_file)) self._assert_graceful_signal_log_matches( waiter_run.pid, signum, signame, read_file(shared_log_file))
def test_dumps_logs_on_signal(self): """Send signals which are handled, but don't get converted into a KeyboardInterrupt.""" signal_names = { signal.SIGQUIT: 'SIGQUIT', signal.SIGTERM: 'SIGTERM', } for (signum, signame) in signal_names.items(): with self._send_signal_to_waiter_handle(signum) as (workdir, waiter_run): assertRegex(self, waiter_run.stderr_data, """\ timestamp: ([^\n]+) Signal {signum} \\({signame}\\) was raised\\. Exiting with failure\\. """.format(signum=signum, signame=signame)) # Check that the logs show a graceful exit by SIGTERM. pid_specific_log_file, shared_log_file = self._get_log_file_paths(workdir, waiter_run) self._assert_graceful_signal_log_matches( waiter_run.pid, signum, signame, read_file(pid_specific_log_file, binary_mode=False)) self._assert_graceful_signal_log_matches( waiter_run.pid, signum, signame, read_file(shared_log_file, binary_mode=False))
def _assert_pantsd_keyboardinterrupt_signal(self, signum, regexps=[], quit_timeout=None): """Send a signal to the thin pailgun client and observe the error messaging. :param int signum: The signal to send. :param regexps: Assert that all of these regexps match somewhere in stderr. :type regexps: list of str :param float quit_timeout: The duration of time to wait for the pailgun client to flush all of its output and die after being killed. """ # TODO: This tests that pantsd-runner processes actually die after the thin client receives the # specified signal. with self.pantsd_test_context() as (workdir, config, checker): # Launch a run that will wait for a file to be created (but do not create that file). file_to_make = os.path.join(workdir, 'some_magic_file') if quit_timeout is not None: timeout_args = ['--pantsd-pailgun-quit-timeout={}'.format(quit_timeout)] else: timeout_args = [] argv = timeout_args + [ 'run', 'testprojects/src/python/coordinated_runs:waiter', '--', file_to_make ] waiter_handle = self.run_pants_with_workdir_without_waiting(argv, workdir, config) client_pid = waiter_handle.process.pid checker.assert_started() checker.assert_pantsd_runner_started(client_pid) # Get all the pantsd-runner processes while they're still around. pantsd_runner_processes = checker.runner_process_context.current_processes() # This should kill the pantsd-runner processes through the RemotePantsRunner signal handler. os.kill(client_pid, signum) waiter_run = waiter_handle.join() self.assert_failure(waiter_run) for regexp in regexps: assertRegex(self, waiter_run.stderr_data, regexp) time.sleep(1) for proc in pantsd_runner_processes: # TODO: we could be checking the return codes of the subprocesses, but psutil is currently # limited on non-Windows hosts -- see https://psutil.readthedocs.io/en/latest/#processes. # The pantsd-runner processes should be dead, and they should have exited with 1. self.assertFalse(proc.is_running())
def test_pydist_binary(self): with temporary_dir() as tmp_dir: pex = os.path.join(tmp_dir, 'main_with_no_conflict.pex') command = [ '--pants-distdir={}'.format(tmp_dir), 'binary', '{}:main_with_no_conflict'.format(self.hello_install_requires_dir), ] pants_run = self.run_pants(command=command) self.assert_success(pants_run) # Check that the pex was built. self.assertTrue(os.path.isfile(pex)) # Check that the pex runs. output = subprocess.check_output(pex).decode('utf-8') self._assert_nation_and_greeting(output) # Check that we have exactly one wheel output. single_wheel_output = assert_single_element(glob.glob(os.path.join(tmp_dir, '*.whl'))) assertRegex(self, os.path.basename(single_wheel_output), r'\A{}'.format(re.escape('hello_with_install_requires-1.0.0+')))
def test_logs_unhandled_exception(self): with temporary_dir() as tmpdir: pants_run = self.run_pants_with_workdir( ['--no-enable-pantsd', 'list', '//:this-target-does-not-exist'], workdir=tmpdir, # The backtrace should be omitted when --print-exception-stacktrace=False. print_exception_stacktrace=False) self.assert_failure(pants_run) assertRegex(self, pants_run.stderr_data, """\ timestamp: ([^\n]+) Exception caught: \\(pants\\.engine\\.scheduler\\.ExecutionError\\) \\(backtrace omitted\\) Exception message: 1 Exception encountered: ResolveError: "this-target-does-not-exist" was not found in namespace ""\\. Did you mean one of: """) pid_specific_log_file, shared_log_file = self._get_log_file_paths(tmpdir, pants_run) self._assert_unhandled_exception_log_matches( pants_run.pid, read_file(pid_specific_log_file)) self._assert_unhandled_exception_log_matches( pants_run.pid, read_file(shared_log_file))
def test_rst_normal(self): pants_run = self.run_pants(['markdown', 'testprojects/src/java/org/pantsbuild/testproject/page:senserst']) self.assert_success(pants_run) out_path = os.path.join(get_buildroot(), 'dist', 'markdown/html', 'testprojects/src/java/org/pantsbuild/testproject/page', 'sense.html') with safe_open(out_path, 'r') as outfile: page_html = outfile.read() # should get Sense and Sensibility in title (or TITLE, sheesh): assertRegex(self, page_html, r'(?i).*<title[^>]*>\s*Sense\s+and\s+Sensibility\s*</title') # should get formatted with h1: assertRegex(self, page_html, r'(?i).*<h1[^>]*>\s*They\s+Heard\s+Her\s+With\s+Surprise\s*</h1>') # should get formatted with _something_ assertRegex(self, page_html, r'.*>\s*inhabiting\s*</') assertRegex(self, page_html, r'.*>\s*civilly\s*</') # there should be a link that has href="http://www.calderdale.gov.uk/" assertRegex(self, page_html, r'.*<a [^>]*href\s*=\s*[\'"]http://www.calderdale')
def test_go_test_simple(self): args = ['test', 'contrib/go/examples/src/go/libA'] pants_run = self.run_pants(args) self.assert_success(pants_run) # libA depends on libB, so both tests should be run. assertRegex(self, pants_run.stdout_data, r'ok\s+libA') assertRegex(self, pants_run.stdout_data, r'ok\s+libB') # Run a second time and see that they are cached. # TODO: this is better done with a unit test, and as noted in #7188, testing interaction with a # remote cache should probably be added somewhere. pants_run = self.run_pants(args) self.assert_success(pants_run) assertRegex(self, pants_run.stdout_data, r'contrib/go/examples/src/go/libA\s+\.+\s+SUCCESS') assertRegex(self, pants_run.stdout_data, r'contrib/go/examples/src/go/libB\s+\.+\s+SUCCESS') # Assert that we do *not* contain verbose output. self.assertNotIn('=== RUN TestAdd', pants_run.stdout_data) self.assertNotIn('PASS', pants_run.stdout_data)
def test_go_run_cgo(self): args = ['-q', 'run', 'contrib/go/examples/src/go/cgo'] pants_run = self.run_pants(args) self.assert_success(pants_run) assertRegex(self, pants_run.stdout_data.strip(), r'^Random from C: \d+$')
def test_changed_with_deleted_target_transitive(self): with create_isolated_git_repo() as worktree: safe_delete(os.path.join(worktree, 'src/resources/org/pantsbuild/resourceonly/BUILD')) pants_run = self.run_pants(['list', '--changed-parent=HEAD', '--changed-include-dependees=transitive']) self.assert_failure(pants_run) assertRegex(self, pants_run.stderr_data, 'src/resources/org/pantsbuild/resourceonly:.*did not exist')