diff --git a/moler/cmd/commandtextualgeneric.py b/moler/cmd/commandtextualgeneric.py index 5055146f1..a83bae7fa 100644 --- a/moler/cmd/commandtextualgeneric.py +++ b/moler/cmd/commandtextualgeneric.py @@ -15,7 +15,7 @@ from moler.cmd import RegexHelper from moler.command import Command -from moler.exceptions import CommandTimeout +from threading import Lock @six.add_metaclass(abc.ABCMeta) @@ -39,6 +39,9 @@ def __init__(self, connection, prompt=None, newline_chars=None, runner=None): self.__command_string = None # String representing command on device self._cmd_escaped = None # Escaped regular expression string with command super(CommandTextualGeneric, self).__init__(connection=connection, runner=runner) + self.terminating_timeout = 3.0 # value for terminating command if it timeouts. Set positive value for command + # if they can do anything if timeout. Set 0 for command if it cannot do + # anything if timeout. self.current_ret = dict() # Placeholder for result as-it-grows, before final write into self._result self._cmd_output_started = False # If false parsing is not passed to command self._regex_helper = RegexHelper() # Object to regular expression matching @@ -57,6 +60,7 @@ def __init__(self, connection, prompt=None, newline_chars=None, runner=None): self._concatenate_before_command_starts = True # Set True to concatenate all strings from connection before # command starts, False to split lines on every new line char self._stored_exception = None # Exception stored before it is passed to base class when command is done. + self._lock_is_done = Lock() if not self._newline_chars: self._newline_chars = CommandTextualGeneric._default_newline_chars @@ -103,11 +107,12 @@ def _is_done(self): @_is_done.setter def _is_done(self, value): - if self._stored_exception: - exception = self._stored_exception - self._stored_exception = None - super(CommandTextualGeneric, self).set_exception(exception=exception) - super(CommandTextualGeneric, self.__class__)._is_done.fset(self, value) + with self._lock_is_done: + if self._stored_exception: + exception = self._stored_exception + self._stored_exception = None + super(CommandTextualGeneric, self)._set_exception_without_done(exception=exception) + super(CommandTextualGeneric, self.__class__)._is_done.fset(self, value) @staticmethod def _calculate_prompt(prompt): @@ -240,7 +245,7 @@ def set_exception(self, exception): :param exception: An exception object to set. :return: None. """ - if self.done() or not self.wait_for_prompt_on_exception or isinstance(exception, CommandTimeout): + if self.done() or not self.wait_for_prompt_on_exception: super(CommandTextualGeneric, self).set_exception(exception=exception) else: if self._stored_exception is None: diff --git a/moler/command_scheduler.py b/moler/command_scheduler.py index d1f84e3b0..a2979a097 100644 --- a/moler/command_scheduler.py +++ b/moler/command_scheduler.py @@ -45,6 +45,19 @@ def dequeue_running_on_connection(connection_observer): scheduler = CommandScheduler._get_scheduler() scheduler._remove_command(cmd=connection_observer) + @staticmethod + def is_waiting_for_execution(connection_observer): + """ + Checks if connection_observer waits in queue before passed to runner. + + :param connection_observer: ConnectionObserver object. + :return: True if connection_observer waits in queue and False if it does not wait. + """ + if connection_observer.is_command: + scheduler = CommandScheduler._get_scheduler() + return scheduler._does_it_wait_in_queue(cmd=connection_observer) + return False + # internal methods and variables _conn_lock = threading.Lock() @@ -85,6 +98,7 @@ def _add_command_to_connection(self, cmd, wait_for_slot=True): timeout=cmd.timeout, kind="scheduler.await_done", passed_time=time.time() - start_time)) + cmd.set_end_of_life() self._remove_command(cmd=cmd) return False @@ -179,6 +193,15 @@ def _add_command_to_queue(self, cmd): with lock: conn_atr['queue'].append(cmd) + def _does_it_wait_in_queue(self, cmd): + connection = cmd.connection + lock = self._lock_for_connection(connection) + conn_atr = self._locks[connection] + with lock: + if cmd in conn_atr['queue']: + return True + return False + def _submit(self, connection_observer): """ Submits a connection_observer object (command or observer) in the runner. diff --git a/moler/connection_observer.py b/moler/connection_observer.py index b01055cf0..1597d81ba 100644 --- a/moler/connection_observer.py +++ b/moler/connection_observer.py @@ -47,10 +47,19 @@ def __init__(self, connection=None, runner=None): self.runner = runner if runner else get_runner() self._future = None self.start_time = 0.0 # means epoch: 1970-01-01 00:00:00 - self.__timeout = 7 # default + self.__timeout = 7.0 # default + self.terminating_timeout = 0.0 # value for terminating connection_observer when it timeouts. Set positive value + # for command if they can do anything if timeout. Set 0 for observer or command + # if it cannot do anything if timeout. self.device_logger = logging.getLogger('moler.{}'.format(self.get_logger_name())) self.logger = logging.getLogger('moler.connection.{}'.format(self.get_logger_name())) + self.in_terminating = False # Set True if ConnectionObserver object is just after __timeout but it can do + # something during terminating_timeout. False if the ConnectionObserver object runs + # during normal timeout. For Runners only! + self.was_on_timeout_called = False # Set True if method on_timeout was called. False otherwise. For Runners + # only! + def __str__(self): return '{}(id:{})'.format(self.__class__.__name__, instance_id(self)) @@ -76,9 +85,11 @@ def __call__(self, timeout=None, *args, **kwargs): or you may delegate blocking call execution to separate thread, see: https://pymotw.com/3/asyncio/executors.html """ - started_observer = self.start(timeout, *args, **kwargs) - if started_observer: - return started_observer.await_done(*args, **kwargs) + self.start(timeout, *args, **kwargs) + # started_observer = self.start(timeout, *args, **kwargs) + # if started_observer: + # return started_observer.await_done(*args, **kwargs) + return self.await_done() # TODO: raise ConnectionObserverFailedToStart @property @@ -200,7 +211,7 @@ def await_done(self, timeout=None): with exception_stored_if_not_main_thread(self): if not self._is_running: raise ConnectionObserverNotStarted(self) - + # check if already is running self.runner.wait_for(connection_observer=self, connection_observer_future=self._future, timeout=timeout) return self.result() @@ -209,10 +220,18 @@ def cancel(self): # TODO: call cancel on runner to stop background run of connection-observer if self.cancelled() or self.done(): return False - self._is_done = True self._is_cancelled = True + self._is_done = True return True + def set_end_of_life(self): + """ + Set end of life of object. Dedicated for runners only! + + :return: None + """ + self._is_done = True + def cancelled(self): """Return True if the connection-observer has been cancelled.""" return self._is_cancelled @@ -231,8 +250,8 @@ def set_result(self, result): """Should be used to set final result""" if self.done(): raise ResultAlreadySet(self) - self._is_done = True self._result = result + self._is_done = True @abstractmethod def data_received(self, data): @@ -243,13 +262,28 @@ def data_received(self, data): pass def set_exception(self, exception): - """Should be used to indicate some failure during observation""" + """ + Should be used to indicate some failure during observation. + + :param exception: Exception to set + :return: None + """ + self._set_exception_without_done(exception) + self._is_done = True + + def _set_exception_without_done(self, exception): + """ + Should be used to indicate some failure during observation. This method does not finish connection observer + object! + + :param exception: exception to set + :return: None + """ if self._is_done: self._log(logging.WARNING, "Trial to set exception {!r} on already done {}".format(exception, self), levels_to_go_up=2) return - self._is_done = True ConnectionObserver._change_unraised_exception(new_exception=exception, observer=self) self._log(logging.INFO, "{}.{} has set exception {!r}".format(self.__class__.__module__, self, exception), diff --git a/moler/runner.py b/moler/runner.py index 2f14fbde9..49a13e4b0 100644 --- a/moler/runner.py +++ b/moler/runner.py @@ -95,28 +95,30 @@ def __exit__(self, exc_type, exc_val, exc_tb): def time_out_observer(connection_observer, timeout, passed_time, runner_logger, kind="background_run"): """Set connection_observer status to timed-out""" - if not connection_observer.done(): - if hasattr(connection_observer, "command_string"): - exception = CommandTimeout(connection_observer=connection_observer, - timeout=timeout, kind=kind, passed_time=passed_time) - else: - exception = ConnectionObserverTimeout(connection_observer=connection_observer, - timeout=timeout, kind=kind, passed_time=passed_time) - # TODO: secure_data_received() may change status of connection_observer - # TODO: and if secure_data_received() runs inside threaded connection - we have race - connection_observer.set_exception(exception) + if not connection_observer.was_on_timeout_called: + connection_observer.was_on_timeout_called = True + if not connection_observer.done(): + if hasattr(connection_observer, "command_string"): + exception = CommandTimeout(connection_observer=connection_observer, + timeout=timeout, kind=kind, passed_time=passed_time) + else: + exception = ConnectionObserverTimeout(connection_observer=connection_observer, + timeout=timeout, kind=kind, passed_time=passed_time) + # TODO: secure_data_received() may change status of connection_observer + # TODO: and if secure_data_received() runs inside threaded connection - we have race + connection_observer.set_exception(exception) - connection_observer.on_timeout() + connection_observer.on_timeout() - observer_info = "{}.{}".format(connection_observer.__class__.__module__, connection_observer) - timeout_msg = "has timed out after {:.2f} seconds.".format(passed_time) - msg = "{} {}".format(observer_info, timeout_msg) + observer_info = "{}.{}".format(connection_observer.__class__.__module__, connection_observer) + timeout_msg = "has timed out after {:.2f} seconds.".format(passed_time) + msg = "{} {}".format(observer_info, timeout_msg) - # levels_to_go_up: extract caller info to log where .time_out_observer has been called from - connection_observer._log(logging.INFO, msg, levels_to_go_up=2) - log_into_logger(runner_logger, level=logging.DEBUG, - msg="{} {}".format(connection_observer, timeout_msg), - levels_to_go_up=1) + # levels_to_go_up: extract caller info to log where .time_out_observer has been called from + connection_observer._log(logging.INFO, msg, levels_to_go_up=2) + log_into_logger(runner_logger, level=logging.DEBUG, + msg="{} {}".format(connection_observer, timeout_msg), + levels_to_go_up=1) def result_for_runners(connection_observer): @@ -194,8 +196,10 @@ def _stop(self, no_wait=False): class ThreadPoolExecutorRunner(ConnectionObserverRunner): def __init__(self, executor=None): """Create instance of ThreadPoolExecutorRunner class""" + self._tick = 0.005 # Tick for sleep or partial timeout self._in_shutdown = False self._i_own_executor = False + self._was_timeout_called = False self.executor = executor self.logger = logging.getLogger('moler.runner.thread-pool') self.logger.debug("created") @@ -318,7 +322,6 @@ def wait_for(self, connection_observer, connection_observer_future, timeout=None remain_time, msg = his_remaining_time("await max.", timeout=max_timeout, from_start_time=start_time) else: remain_time, msg = his_remaining_time("remaining", timeout=observer_timeout, from_start_time=start_time) - self.logger.debug("go foreground: {} - {}".format(connection_observer, msg)) if connection_observer_future is None: @@ -326,31 +329,67 @@ def wait_for(self, connection_observer, connection_observer_future, timeout=None self.logger) if end_of_life: return None + if not self._execute_till_eol(connection_observer=connection_observer, + connection_observer_future=connection_observer_future, + max_timeout=max_timeout, + await_timeout=await_timeout, + remain_time=remain_time): + # code below is to close ConnectionObserver and future objects + self._end_of_life_of_future_and_connection_observer(connection_observer, connection_observer_future) + return None + def _execute_till_eol(self, connection_observer, connection_observer_future, max_timeout, await_timeout, remain_time): + eol_remain_time = remain_time # either we wait forced-max-timeout or we check done-status each 0.1sec tick - if remain_time > 0.0: + if eol_remain_time > 0.0: future = connection_observer_future or connection_observer._future assert future is not None if max_timeout: done, not_done = wait([future], timeout=remain_time) if (future in done) or connection_observer.done(): self._cancel_submitted_future(connection_observer, future) - return None + return True + self._wait_for_time_out(connection_observer, connection_observer_future, + timeout=await_timeout) + if connection_observer.terminating_timeout > 0.0: + connection_observer.in_terminating = True + done, not_done = wait([future], timeout=connection_observer.terminating_timeout) + if (future in done) or connection_observer.done(): + self._cancel_submitted_future(connection_observer, future) + return True else: - wait_tick = 0.1 - while remain_time > 0.0: - done, not_done = wait([future], timeout=wait_tick) + while eol_remain_time > 0.0: + done, not_done = wait([future], timeout=self._tick) if (future in done) or connection_observer.done(): self._cancel_submitted_future(connection_observer, future) - return None + return True + already_passed = time.time() - connection_observer.start_time + eol_timeout = connection_observer.timeout + connection_observer.terminating_timeout + eol_remain_time = eol_timeout - already_passed timeout = connection_observer.timeout - already_passed = time.time() - start_time remain_time = timeout - already_passed - - # code below is for timed out observer - self._wait_for_time_out(connection_observer, connection_observer_future, - timeout=await_timeout) - return None + if remain_time <= 0.0: + self._wait_for_time_out(connection_observer, connection_observer_future, + timeout=await_timeout) + if not connection_observer.in_terminating: + connection_observer.in_terminating = True + else: + self._wait_for_not_started_connection_observer_is_done(connection_observer=connection_observer) + return False + + def _wait_for_not_started_connection_observer_is_done(self, connection_observer): + # Have to wait till connection_observer is done with terminaing timeout. + eol_remain_time = connection_observer.terminating_timeout + start_time = time.time() + while not connection_observer.done() and eol_remain_time > 0.0: + time.sleep(self._tick) + eol_remain_time = start_time + connection_observer.terminating_timeout - time.time() + + def _end_of_life_of_future_and_connection_observer(self, connection_observer, connection_observer_future): + future = connection_observer_future or connection_observer._future + if future: + future.cancel(no_wait=True) + connection_observer.set_end_of_life() @staticmethod def _cancel_submitted_future(connection_observer, connection_observer_future): @@ -362,7 +401,6 @@ def _wait_for_time_out(self, connection_observer, connection_observer_future, ti passed = time.time() - connection_observer.start_time future = connection_observer_future or connection_observer._future if future: - future.cancel(no_wait=True) with future.observer_lock: time_out_observer(connection_observer=connection_observer, timeout=timeout, passed_time=passed, @@ -455,7 +493,7 @@ def feed(self, connection_observer, subscribed_data_receiver, stop_feeding, feed if not subscribed_data_receiver: subscribed_data_receiver = self._start_feeding(connection_observer, observer_lock) - time.sleep(0.005) # give control back before we start processing + time.sleep(self._tick) # give control back before we start processing self._feed_loop(connection_observer, stop_feeding, observer_lock) @@ -478,17 +516,31 @@ def _feed_loop(self, connection_observer, stop_feeding, observer_lock): run_duration = time.time() - start_time # we need to check connection_observer.timeout at each round since timeout may change # during lifetime of connection_observer - if (connection_observer.timeout is not None) and (run_duration >= connection_observer.timeout): - with observer_lock: - time_out_observer(connection_observer, - timeout=connection_observer.timeout, - passed_time=run_duration, - runner_logger=self.logger) - break + timeout = connection_observer.timeout + if connection_observer.in_terminating: + timeout = connection_observer.terminating_timeout + if (timeout is not None) and (run_duration >= timeout): + if connection_observer.in_terminating: + msg = "{} underlying real command failed to finish during {} seconds. It will be forcefully" \ + " terminated".format(connection_observer, timeout) + self.logger.info(msg) + connection_observer.set_end_of_life() + else: + with observer_lock: + time_out_observer(connection_observer, + timeout=connection_observer.timeout, + passed_time=run_duration, + runner_logger=self.logger) + if connection_observer.terminating_timeout >= 0.0: + start_time = time.time() + connection_observer.in_terminating = True + else: + break + if self._in_shutdown: self.logger.debug("shutdown so cancelling {}".format(connection_observer)) connection_observer.cancel() - time.sleep(0.005) # give moler_conn a chance to feed observer + time.sleep(self._tick) # give moler_conn a chance to feed observer def timeout_change(self, timedelta): pass @@ -530,7 +582,7 @@ def await_future_or_eol(connection_observer, remain_time, start_time, timeout, l already_passed = now - start_time remain_time = timeout - already_passed observer_lifetime_passed = now - connection_observer.start_time - remain_observer_lifetime = connection_observer.timeout - observer_lifetime_passed + remain_observer_lifetime = connection_observer.timeout + connection_observer.terminating_timeout - observer_lifetime_passed # we timeout on earlier timeout (timeout or connection_observer.timeout) if remain_observer_lifetime <= 0.0: remain_time = 0.0 diff --git a/test/integration/py3test_runners.py b/test/integration/py3test_runners.py index 15e190f25..6341f5fd1 100644 --- a/test/integration/py3test_runners.py +++ b/test/integration/py3test_runners.py @@ -279,9 +279,9 @@ async def test_future_accommodates_to_extending_timeout_of_observer(connection_o logger.debug("after second await asyncio.sleep(0.1)") with pytest.raises(ResultNotAvailableYet): # not timed out yet connection_observer.result() - logger.debug("final await asyncio.sleep(0.3)") - await asyncio.sleep(0.3) - logger.debug("after final await asyncio.sleep(0.3)") + logger.debug("final await asyncio.sleep(0.4)") + await asyncio.sleep(0.4) + logger.debug("after final await asyncio.sleep(0.4)") with pytest.raises(MolerTimeout): # should time out connection_observer.result() @@ -339,7 +339,8 @@ def test_wait_for__times_out_on_specified_timeout(connection_observer): from moler.exceptions import MolerTimeout observer_runner = connection_observer.runner - connection_observer.timeout = 0.5 + connection_observer.timeout = 1.5 + connection_observer.terminating_timeout = 0.0 start_time = connection_observer.start_time = time.time() future = observer_runner.submit(connection_observer) time.sleep(0.1) @@ -427,6 +428,7 @@ def test_wait_for__direct_timeout_takes_precedence_over_extended_observer_timeou observer_runner = connection_observer.runner connection_observer.timeout = 0.2 + connection_observer.terminating_timeout = 0.0 start_time = connection_observer.start_time = time.time() future = observer_runner.submit(connection_observer) diff --git a/test/integration/test_io_raw_terminal.py b/test/integration/test_io_raw_terminal.py index 9fad83a1d..82c906f6b 100644 --- a/test/integration/test_io_raw_terminal.py +++ b/test/integration/test_io_raw_terminal.py @@ -75,7 +75,7 @@ def test_terminal_whoami_ls(terminal_connection): def test_terminal_lsof(terminal_connection): terminal = terminal_connection cmd = Lsof(connection=terminal, options="| grep python") - ret = cmd() + ret = cmd(timeout=30) assert ret["NUMBER"] > 1 diff --git a/test/integration/test_many_commands_connection.py b/test/integration/test_many_commands_connection.py index c09e1bd47..727b38283 100644 --- a/test/integration/test_many_commands_connection.py +++ b/test/integration/test_many_commands_connection.py @@ -10,6 +10,7 @@ import time from moler.event_awaiter import EventAwaiter from moler.exceptions import CommandTimeout +from moler.command_scheduler import CommandScheduler def test_two_commands_uptime_whoami(buffer_connection, command_output_and_expected_result_uptime_whoami): @@ -19,8 +20,10 @@ def test_two_commands_uptime_whoami(buffer_connection, command_output_and_expect uptime_cmd = Uptime(connection=buffer_connection.moler_connection) whoami_cmd = Whoami(connection=buffer_connection.moler_connection) uptime_cmd.start(timeout=2) + time.sleep(0.005) whoami_cmd.start(timeout=2) time.sleep(0.05) + assert CommandScheduler.is_waiting_for_execution(connection_observer=whoami_cmd) is True buffer_connection.moler_connection.data_received(command_output[0].encode("utf-8")) time.sleep(0.2) buffer_connection.moler_connection.data_received(command_output[1].encode("utf-8")) @@ -29,6 +32,7 @@ def test_two_commands_uptime_whoami(buffer_connection, command_output_and_expect ret_whoami = whoami_cmd.result() assert ret_uptime == expected_result[0] assert ret_whoami == expected_result[1] + assert CommandScheduler.is_waiting_for_execution(connection_observer=whoami_cmd) is False def test_two_commands_uptime(buffer_connection, command_output_and_expected_result_uptime): diff --git a/test/test_connection_observer.py b/test/test_connection_observer.py index a202a4d0c..f4ec56450 100644 --- a/test/test_connection_observer.py +++ b/test/test_connection_observer.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- __author__ = 'Grzegorz Latuszek, Marcin Usielski' -__copyright__ = 'Copyright (C) 2018, Nokia' +__copyright__ = 'Copyright (C) 2018-2019, Nokia' __email__ = 'grzegorz.latuszek@nokia.com, marcin.usielski@nokia.com' import importlib @@ -151,7 +151,7 @@ def start(self, timeout, param1, param2): connection_observer = ParametrizedObserver() - connection_observer(1., 23, "foo") + connection_observer.start(1., 23, "foo") assert called_with_params == [23, "foo"] @@ -167,7 +167,7 @@ def start(self, timeout, param1, param2): observer = ParametrizedObserver() - observer(param2="foo", param1=23) + observer.start(timeout=1.0, param2="foo", param1=23) assert called_with_params == [23, "foo"] @@ -428,6 +428,7 @@ def test_connection_observer_one_exception(): none_exceptions = ConnectionObserver.get_unraised_exceptions(True) assert 0 == len(none_exceptions) cmd.set_exception(CommandTimeout(cmd, 0.1)) + cmd._is_done = True active_exceptions = ConnectionObserver.get_unraised_exceptions(True) assert 1 == len(active_exceptions) try: @@ -445,15 +446,22 @@ def test_connection_observer_exception_do_not_remove(): time.sleep(0.1) from moler.cmd.unix.ls import Ls from moler.exceptions import CommandTimeout + from moler.exceptions import WrongUsage cmd = Ls(None) none_exceptions = ConnectionObserver.get_unraised_exceptions(True) assert 0 == len(none_exceptions) cmd.set_exception(CommandTimeout(cmd, 0.1)) + cmd._is_done = True active_exceptions = ConnectionObserver.get_unraised_exceptions(False) assert 1 == len(active_exceptions) cmd = Ls(None) - cmd.set_exception(CommandTimeout(cmd, 0.1)) + ctoe = CommandTimeout(cmd, 0.1) + cwue = WrongUsage(cmd, "Another exception") + cmd.set_exception(ctoe) + cmd._is_done = True + cmd.set_exception(cwue) active_exceptions = ConnectionObserver.get_unraised_exceptions(False) + assert ctoe == active_exceptions[1] assert 2 == len(active_exceptions) active_exceptions = ConnectionObserver.get_unraised_exceptions(True) assert 2 == len(active_exceptions) diff --git a/test/test_moler_test.py b/test/test_moler_test.py index 5bdd1924f..b38b48190 100644 --- a/test/test_moler_test.py +++ b/test/test_moler_test.py @@ -14,11 +14,13 @@ def test_moler_test_warn(): ConnectionObserver.get_unraised_exceptions() MolerTest.warning("Warning test") + ConnectionObserver.get_unraised_exceptions() def test_moler_test_not_raise_exception_when_no_steps_end_for_global_method_twice(): ConnectionObserver.get_unraised_exceptions() moler_test_not_raise_exception_when_no_steps_end_for_global_method_twice() + ConnectionObserver.get_unraised_exceptions() def test_moler_test_raise_exception_when_not_callable_passed(): @@ -26,6 +28,7 @@ def test_moler_test_raise_exception_when_not_callable_passed(): var = "no callable" with pytest.raises(MolerStatusException): MolerTest._decorate(var) + ConnectionObserver.get_unraised_exceptions() def test_moler_test_wrapper(): @@ -33,6 +36,7 @@ def test_moler_test_wrapper(): decorated = moler_test_raise_exception_when_no_steps_end_for_global_method ret = MolerTest._wrapper(decorated, False) assert decorated == ret + ConnectionObserver.get_unraised_exceptions() def test_moler_test_exception_no_exception(): @@ -43,43 +47,52 @@ def test_moler_test_exception_no_exception(): cmd._is_done = True with pytest.raises(MolerStatusException): moler_test_not_raise_exception_when_no_steps_end_for_global_method() + ConnectionObserver.get_unraised_exceptions() def test_moler_test_not_raise_exception_when_steps_end(moler_test_se): ConnectionObserver.get_unraised_exceptions() moler_test_se.test_not_raise_exception_when_steps_end() + ConnectionObserver.get_unraised_exceptions() def test_moler_test_test_raise_exception_when_not_call_steps_end(moler_test_se): ConnectionObserver.get_unraised_exceptions() with pytest.raises(MolerStatusException): moler_test_se.test_raise_exception_when_not_call_steps_end() + ConnectionObserver.get_unraised_exceptions() def test_moler_test_raise_exception_when_log_error(moler_test_se): ConnectionObserver.get_unraised_exceptions() with pytest.raises(MolerStatusException): moler_test_se.test_raise_exception_when_log_error() + ConnectionObserver.get_unraised_exceptions() def test_moler_test_raise_exception_when_log_error_raise_exception_set(moler_test_se): ConnectionObserver.get_unraised_exceptions() with pytest.raises(MolerStatusException): moler_test_se.test_raise_exception_when_log_error_raise_exception_set() + ConnectionObserver.get_unraised_exceptions() def test_moler_test_not_raise_exception_when_no_steps_end(moler_test): ConnectionObserver.get_unraised_exceptions() moler_test.test_not_raise_exception_when_no_steps_end() + ConnectionObserver.get_unraised_exceptions() def test_moler_test_raise_exception_when_no_steps_end_for_global_method(): with pytest.raises(MolerStatusException): moler_test_raise_exception_when_no_steps_end_for_global_method() + ConnectionObserver.get_unraised_exceptions() def test_moler_test_not_raise_exception_when_no_steps_end_for_global_method(): + ConnectionObserver.get_unraised_exceptions() moler_test_not_raise_exception_when_no_steps_end_for_global_method() + ConnectionObserver.get_unraised_exceptions() # connection observer running in background thread may raise exception @@ -96,12 +109,12 @@ def function_using_observer(): # for real usage observer should be started to run background thread that will set_exception() # but for unit tests we just call it (simulating background thread) observer.set_exception(exc) - print(observer.result()) + observer.result() with pytest.raises(ObserverExceptionClass) as err: function_using_observer() assert err.value == exc - + ConnectionObserver.get_unraised_exceptions() def test_exception_in_observer_is_ignored_if_no_result_called_nor_decorator_on_function(do_nothing_connection_observer, ObserverExceptionClass): @@ -110,6 +123,7 @@ def function_using_observer(): observer.set_exception(ObserverExceptionClass("some error inside observer")) function_using_observer() # should not raise so test should pass + ConnectionObserver.get_unraised_exceptions() def test_log_error_in_next_test_when_previous_set_exception(do_nothing_connection_observer, @@ -127,7 +141,7 @@ def function_using_observer(): observer = do_nothing_connection_observer # for real usage observer should be started to run background thread that will set_exception() # but for unit tests we just call it (simulating background thread) - print(observer.result()) + observer.result() MolerTest.steps_end() function_using_observer_and_set_exception() @@ -135,6 +149,7 @@ def function_using_observer(): with pytest.raises(MolerStatusException) as err: function_using_observer() assert "some error inside observer" in str(err.value) + ConnectionObserver.get_unraised_exceptions() def test_exception_in_observer_is_raised_if_no_result_called_but_decorator_on_function(do_nothing_connection_observer, @@ -149,7 +164,7 @@ def function_using_observer(): with pytest.raises(MolerStatusException) as err: function_using_observer() - + ConnectionObserver.get_unraised_exceptions() def test_exception_in_observer_is_raised_if_no_result_called_but_parameterless_decorator_on_function( do_nothing_connection_observer, @@ -164,6 +179,7 @@ def function_using_observer(): with pytest.raises(MolerStatusException) as err: function_using_observer() + ConnectionObserver.get_unraised_exceptions() def test_exception_in_observer_is_raised_if_no_result_called_but_decorator_on_method(do_nothing_connection_observer, @@ -181,6 +197,7 @@ def method_using_observer(self): with pytest.raises(MolerStatusException) as err: MyTest().method_using_observer() + ConnectionObserver.get_unraised_exceptions() def test_exception_in_observer_is_raised_if_no_result_called_but_parameterless_decorator_on_method( @@ -197,6 +214,7 @@ def method_using_observer(self): with pytest.raises(MolerStatusException) as err: MyTest().method_using_observer() + ConnectionObserver.get_unraised_exceptions() def test_exception_in_observer_is_raised_if_no_result_called_but_decorator_on_classmethod( @@ -215,6 +233,7 @@ def method_using_observer(cls): observer.set_exception(exc) MyTest.method_using_observer() + ConnectionObserver.get_unraised_exceptions() def test_exception_in_observer_is_raised_if_no_result_called_but_parameterless_decorator_on_classmethod( @@ -233,6 +252,7 @@ def method_using_observer(cls): observer.set_exception(exc) MyTest.method_using_observer() + ConnectionObserver.get_unraised_exceptions() def test_exception_in_observer_is_raised_if_no_result_called_but_decorator_on_staticmethod( @@ -251,6 +271,7 @@ def method_using_observer(): observer.set_exception(exc) MyTest.method_using_observer() + ConnectionObserver.get_unraised_exceptions() def test_exception_in_observer_is_raised_if_no_result_called_but_parameterless_decorator_on_staticmethod( @@ -269,6 +290,7 @@ def method_using_observer(): observer.set_exception(exc) MyTest.method_using_observer() + ConnectionObserver.get_unraised_exceptions() def test_exception_in_observer_is_raised_if_no_result_called_but_decorator_on_class(do_nothing_connection_observer, @@ -284,6 +306,7 @@ def method_using_observer(self): with pytest.raises(MolerStatusException) as err: MyTest().method_using_observer() + ConnectionObserver.get_unraised_exceptions() def test_exception_in_observer_is_raised_if_no_result_called_but_parameterless_decorator_on_class( @@ -300,6 +323,7 @@ def method_using_observer(self): with pytest.raises(MolerStatusException) as err: MyTest().method_using_observer() + ConnectionObserver.get_unraised_exceptions() def test_exception_in_observer_is_raised_if_no_result_called_but_decorator_on_derived_class( @@ -319,6 +343,7 @@ def method_of_derived_class(self): with pytest.raises(MolerStatusException) as err: MyTest().method_using_observer() + ConnectionObserver.get_unraised_exceptions() def test_exception_in_observer_is_raised_if_no_result_called_but_parameterless_decorator_on_derived_class( @@ -338,6 +363,7 @@ def method_of_derived_class(self): with pytest.raises(MolerStatusException) as err: MyTest().method_using_observer() + ConnectionObserver.get_unraised_exceptions() def test_info_with_dump(): diff --git a/test/unix/test_cmd_ping.py b/test/unix/test_cmd_ping.py index 25897afde..101e1698b 100644 --- a/test/unix/test_cmd_ping.py +++ b/test/unix/test_cmd_ping.py @@ -19,6 +19,7 @@ def test_ping_returns_proper_command_string(buffer_connection): def test_ping_observer_timeout(buffer_connection): from moler.exceptions import CommandTimeout + cmd_ping = Ping(buffer_connection.moler_connection, destination='localhost') + cmd_ping.terminating_timeout = 0 with pytest.raises(CommandTimeout): - cmd_ping = Ping(buffer_connection.moler_connection, destination='localhost') cmd_ping(timeout=0.1) diff --git a/test/unix/test_cmd_telnet.py b/test/unix/test_cmd_telnet.py index afcf217aa..8991a2e7f 100644 --- a/test/unix/test_cmd_telnet.py +++ b/test/unix/test_cmd_telnet.py @@ -43,6 +43,7 @@ def test_calling_telnet_timeout(buffer_connection, command_output_and_expected_r buffer_connection.remote_inject_response([command_output]) telnet_cmd = Telnet(connection=buffer_connection.moler_connection, login="user", password="english", port=1500, host="host.domain.net", expected_prompt="host:.*#") + telnet_cmd.terminating_timeout = 0.1 from moler.exceptions import CommandTimeout with pytest.raises(CommandTimeout): telnet_cmd(timeout=0.5) diff --git a/test/unix/test_cmd_uptime.py b/test/unix/test_cmd_uptime.py index 19818360a..f74084356 100644 --- a/test/unix/test_cmd_uptime.py +++ b/test/unix/test_cmd_uptime.py @@ -3,11 +3,12 @@ Testing of uptime command. """ __author__ = 'Marcin Usielski' -__copyright__ = 'Copyright (C) 2018, Nokia' +__copyright__ = 'Copyright (C) 2018-2019, Nokia' __email__ = 'marcin.usielski@nokia.com' import pytest -from moler.exceptions import CommandFailure +from moler.exceptions import CommandFailure +from moler.exceptions import CommandTimeout def test_calling_uptime_returns_result_parsed_from_command_output(buffer_connection, @@ -29,6 +30,14 @@ def test_calling_uptime_fails_unsupported_format(buffer_connection, command_unsu uptime_cmd() +def test_calling_uptime_timeout(buffer_connection): + from moler.cmd.unix.uptime import Uptime + uptime_cmd = Uptime(connection=buffer_connection.moler_connection) + uptime_cmd.terminating_timeout = 0.2 + uptime_cmd.timeout = 0.2 + with pytest.raises(CommandTimeout): + uptime_cmd() + def test_uptime_returns_proper_command_string(buffer_connection): from moler.cmd.unix.uptime import Uptime uptime_cmd = Uptime(buffer_connection.moler_connection) diff --git a/test/unix/test_cmd_zip.py b/test/unix/test_cmd_zip.py index 571446de8..4f63ef071 100644 --- a/test/unix/test_cmd_zip.py +++ b/test/unix/test_cmd_zip.py @@ -38,6 +38,7 @@ def test_calling_zip_timeout(buffer_connection, command_output_and_expected_resu command_output, expected_result = command_output_and_expected_result_timeout buffer_connection.remote_inject_response([command_output]) zip_cmd = Zip(connection=buffer_connection.moler_connection, options="", zip_file="test.zip", file_name="test.txt") + zip_cmd.terminating_timeout = 0 from moler.exceptions import CommandTimeout with pytest.raises(CommandTimeout) as exception: zip_cmd(timeout=0.5)