def test_calling_kill_subprocesses_will_break_out_of_command_execution_wait_loop(self):

        def fake_communicate(timeout=None):
            # The fake implementation is that communicate() times out forever until os.killpg is called.
            if mock_killpg.call_count == 0 and timeout is not None:
                raise TimeoutExpired(None, timeout)
            elif mock_killpg.call_count > 0:
                return b'fake output', b'fake error'
            self.fail('Popen.communicate() should not be called without a timeout before os.killpg has been called.')

        mock_killpg = self.patch('os.killpg')
        self.mock_popen.communicate.side_effect = fake_communicate
        self.mock_popen.returncode = 1
        self.mock_popen.pid = 55555
        project_type = ProjectType()
        command_thread = SafeThread(target=project_type.execute_command_in_project, args=('echo The power is yours!',))

        # This calls execute_command_in_project() on one thread, and calls kill_subprocesses() on another. The
        # kill_subprocesses() call should cause the first thread to exit.
        command_thread.start()
        project_type.kill_subprocesses()

        # This *should* join immediately, but we specify a timeout just in case something goes wrong so that the test
        # doesn't hang. A successful join implies success. We also use the UnhandledExceptionHandler so that exceptions
        # propagate from the child thread to the test thread and fail the test.
        with UnhandledExceptionHandler.singleton():
            command_thread.join(timeout=10)
            if command_thread.is_alive():
                mock_killpg()  # Calling killpg() causes the command thread to end.
                self.fail('project_type.kill_subprocesses should cause the command execution wait loop to exit.')

        mock_killpg.assert_called_once_with(pgid=55555, sig=ANY)
Esempio n. 2
0
    def test_executing_build_teardown_multiple_times_will_raise_exception(
            self):
        self.mock_network.post().status_code = http.client.OK
        slave = self._create_cluster_slave()
        project_type_mock = self.patch(
            'app.slave.cluster_slave.util.create_project_type').return_value
        # This test uses setup_complete_event to detect when the async fetch_project() has executed.
        setup_complete_event = Event()
        project_type_mock.fetch_project.side_effect = self.no_args_side_effect(
            setup_complete_event.set)
        # This test uses teardown_event to cause a thread to block on the teardown_build() call.
        teardown_event = Event()
        project_type_mock.teardown_build.side_effect = self.no_args_side_effect(
            teardown_event.wait)

        slave.connect_to_master(self._FAKE_MASTER_URL)
        slave.setup_build(build_id=123, project_type_params={'type': 'Fake'})
        self.assertTrue(setup_complete_event.wait(timeout=5),
                        'Build setup should complete very quickly.')

        # Start the first thread that does build teardown. This thread will block on teardown_build().
        first_thread = SafeThread(target=slave._do_build_teardown_and_reset)
        first_thread.start()
        # Call build teardown() again and it should raise an exception.
        with self.assertRaises(BuildTeardownError):
            slave._do_build_teardown_and_reset()

        # Cleanup: Unblock the first thread and let it finish. We use the unhandled exception handler just in case any
        # exceptions occurred on the thread (so that they'd be passed back to the main thread and fail the test).
        teardown_event.set()
        with UnhandledExceptionHandler.singleton():
            first_thread.join()
Esempio n. 3
0
    def test_calling_kill_subprocesses_will_break_out_of_command_execution_wait_loop(
            self):
        self._mock_stdout_and_stderr(b'fake_output', b'fake_error')
        self.mock_popen.pid = 55555
        self._simulate_hanging_popen_process()

        project_type = ProjectType()
        command_thread = SafeThread(
            target=project_type.execute_command_in_project,
            args=('echo The power is yours!', ))

        # This calls execute_command_in_project() on one thread, and calls kill_subprocesses() on another. The
        # kill_subprocesses() call should cause the first thread to exit.
        command_thread.start()
        project_type.kill_subprocesses()

        # This *should* join immediately, but we specify a timeout just in case something goes wrong so that the test
        # doesn't hang. A successful join implies success. We also use the UnhandledExceptionHandler so that exceptions
        # propagate from the child thread to the test thread and fail the test.
        with UnhandledExceptionHandler.singleton():
            command_thread.join(timeout=10)
            if command_thread.is_alive():
                self.mock_killpg(
                )  # Calling killpg() causes the command thread to end.
                self.fail(
                    'project_type.kill_subprocesses should cause the command execution wait loop to exit.'
                )

        self.mock_killpg.assert_called_once_with(
            55555, ANY)  # Note: os.killpg does not accept keyword args.
    def test_executing_build_teardown_multiple_times_will_raise_exception(self):
        self.mock_network.post().status_code = http.client.OK
        slave = self._create_cluster_slave()
        project_type_mock = self.patch('app.slave.cluster_slave.util.create_project_type').return_value
        # This test uses setup_complete_event to detect when the async fetch_project() has executed.
        setup_complete_event = Event()
        project_type_mock.fetch_project.side_effect = self.no_args_side_effect(setup_complete_event.set)
        # This test uses teardown_event to cause a thread to block on the teardown_build() call.
        teardown_event = Event()
        project_type_mock.teardown_build.side_effect = self.no_args_side_effect(teardown_event.wait)

        slave.connect_to_master(self._FAKE_MASTER_URL)
        slave.setup_build(build_id=123, project_type_params={'type': 'Fake'}, build_executor_start_index=0)
        self.assertTrue(setup_complete_event.wait(timeout=5), 'Build setup should complete very quickly.')

        # Start the first thread that does build teardown. This thread will block on teardown_build().
        first_thread = SafeThread(target=slave._do_build_teardown_and_reset)
        first_thread.start()
        # Call build teardown() again and it should raise an exception.
        with self.assertRaises(BuildTeardownError):
            slave._do_build_teardown_and_reset()

        # Cleanup: Unblock the first thread and let it finish. We use the unhandled exception handler just in case any
        # exceptions occurred on the thread (so that they'd be passed back to the main thread and fail the test).
        teardown_event.set()
        with UnhandledExceptionHandler.singleton():
            first_thread.join()
Esempio n. 5
0
 def run(self, *args, **kwargs):
     app_thread = SafeThread(
         name=self._THREAD_NAME,
         target=self.async_run,
         args=args,
         kwargs=kwargs,
     )
     app_thread.start()
     app_thread.join()
Esempio n. 6
0
    def test_exception_on_safe_thread_calls_teardown_callbacks(self):
        my_awesome_teardown_callback = MagicMock()
        unhandled_exception_handler = UnhandledExceptionHandler.singleton()
        unhandled_exception_handler.add_teardown_callback(my_awesome_teardown_callback, 'fake arg', fake_kwarg='boop')

        def my_terrible_method():
            raise Exception('Sic semper tyrannis!')

        thread = SafeThread(target=my_terrible_method)
        thread.start()
        thread.join()

        my_awesome_teardown_callback.assert_called_once_with('fake arg', fake_kwarg='boop')
Esempio n. 7
0
    def test_normal_execution_on_safe_thread_does_not_call_teardown_callbacks(self):
        my_lonely_teardown_callback = MagicMock()
        unhandled_exception_handler = UnhandledExceptionHandler.singleton()
        unhandled_exception_handler.add_teardown_callback(my_lonely_teardown_callback)

        def my_fantastic_method():
            print('Veritas vos liberabit!')

        thread = SafeThread(target=my_fantastic_method)
        thread.start()
        thread.join()

        self.assertFalse(my_lonely_teardown_callback.called,
                         'The teardown callback should not be called unless an exception is raised.')
    def test_exception_on_safe_thread_calls_teardown_callbacks(self):
        my_awesome_teardown_callback = MagicMock()
        unhandled_exception_handler = UnhandledExceptionHandler.singleton()
        unhandled_exception_handler.add_teardown_callback(
            my_awesome_teardown_callback, 'fake arg', fake_kwarg='boop')

        def my_terrible_method():
            raise Exception('Sic semper tyrannis!')

        thread = SafeThread(target=my_terrible_method)
        thread.start()
        thread.join()

        my_awesome_teardown_callback.assert_called_once_with('fake arg',
                                                             fake_kwarg='boop')
    def test_normal_execution_on_safe_thread_does_not_call_teardown_callbacks(
            self):
        my_lonely_teardown_callback = MagicMock()
        unhandled_exception_handler = UnhandledExceptionHandler.singleton()
        unhandled_exception_handler.add_teardown_callback(
            my_lonely_teardown_callback)

        def my_fantastic_method():
            print('Veritas vos liberabit!')

        thread = SafeThread(target=my_fantastic_method)
        thread.start()
        thread.join()

        self.assertFalse(
            my_lonely_teardown_callback.called,
            'The teardown callback should not be called unless an exception is raised.'
        )
Esempio n. 10
0
    def test_calling_kill_subprocesses_will_break_out_of_command_execution_wait_loop(self):
        self._mock_out_popen_communicate()

        project_type = ProjectType()
        command_thread = SafeThread(target=project_type.execute_command_in_project, args=('echo The power is yours!',))

        # This calls execute_command_in_project() on one thread, and calls kill_subprocesses() on another. The
        # kill_subprocesses() call should cause the first thread to exit.
        command_thread.start()
        project_type.kill_subprocesses()

        # This *should* join immediately, but we specify a timeout just in case something goes wrong so that the test
        # doesn't hang. A successful join implies success. We also use the UnhandledExceptionHandler so that exceptions
        # propagate from the child thread to the test thread and fail the test.
        with UnhandledExceptionHandler.singleton():
            command_thread.join(timeout=10)
            if command_thread.is_alive():
                self.mock_killpg()  # Calling killpg() causes the command thread to end.
                self.fail('project_type.kill_subprocesses should cause the command execution wait loop to exit.')

        self.mock_killpg.assert_called_once_with(55555, ANY)  # Note: os.killpg does not accept keyword args.