diff --git a/src/osmo_gsm_tester/event_loop.py b/src/osmo_gsm_tester/event_loop.py new file mode 100644 index 00000000..d082898e --- /dev/null +++ b/src/osmo_gsm_tester/event_loop.py @@ -0,0 +1,64 @@ +# osmo_gsm_tester: Event loop +# +# Copyright (C) 2016-2017 by sysmocom - s.f.m.c. GmbH +# +# Author: Pau Espin Pedrol +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +# These will be initialized before each test run. +# A test script can thus establish its context by doing: +# from osmo_gsm_tester.test import * +import time +poll_funcs = [] + +def register_poll_func(func): + global poll_funcs + poll_funcs.append(func) + +def unregister_poll_func(func): + global poll_funcs + poll_funcs.remove(func) + +def poll(): + global poll_funcs + for func in poll_funcs: + func() + +def wait_no_raise(log_obj, condition, condition_args, condition_kwargs, timeout, timestep): + if not timeout or timeout < 0: + log_obj.raise_exn('wait() *must* time out at some point. timeout=%r' % timeout) + if timestep < 0.1: + timestep = 0.1 + + started = time.time() + while True: + poll() + if condition(*condition_args, **condition_kwargs): + return True + waited = time.time() - started + if waited > timeout: + return False + time.sleep(timestep) + +def wait(log_obj, condition, *condition_args, timeout=300, timestep=1, **condition_kwargs): + if not wait_no_raise(log_obj, condition, condition_args, condition_kwargs, timeout, timestep): + log_obj.raise_exn('Wait timeout') + +def sleep(log_obj, seconds): + assert seconds > 0. + wait_no_raise(log_obj, lambda: False, [], {}, timeout=seconds, timestep=min(seconds, 1)) + + +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/src/osmo_gsm_tester/ofono_client.py b/src/osmo_gsm_tester/ofono_client.py index faa91928..1ff98a91 100644 --- a/src/osmo_gsm_tester/ofono_client.py +++ b/src/osmo_gsm_tester/ofono_client.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from . import log, test, util +from . import log, test, util, event_loop from pydbus import SystemBus, Variant import time @@ -57,13 +57,14 @@ def dbus_connect(dbus_iface, handler): DeferredHandling.defer_queue.''' return DeferredHandling(dbus_iface, handler).subscription_id - -def poll(): +def poll_glib(): global glib_main_ctx while glib_main_ctx.pending(): glib_main_ctx.iteration() DeferredHandling.handle_queue() +event_loop.register_poll_func(poll_glib) + def systembus_get(path): global bus return bus.get('org.ofono', path) @@ -89,7 +90,7 @@ class Modem(log.Origin): self.sms_received_list = [] # init interfaces and connect to signals: self.dbus_obj() - test.poll() + event_loop.poll() def set_msisdn(self, msisdn): self.msisdn = msisdn @@ -106,13 +107,13 @@ class Modem(log.Origin): def _dbus_set_bool(self, name, bool_val, iface=I_MODEM): # to make sure any pending signals are received before we send out more DBus requests - test.poll() + event_loop.poll() val = bool(bool_val) self.log('Setting', name, val) self.dbus_obj()[iface].SetProperty(name, Variant('b', val)) - test.wait(self.property_is, name, bool_val) + event_loop.wait(self, self.property_is, name, bool_val) def property_is(self, name, val): is_val = self.properties().get(name) diff --git a/src/osmo_gsm_tester/process.py b/src/osmo_gsm_tester/process.py index a687de6a..e9567b24 100644 --- a/src/osmo_gsm_tester/process.py +++ b/src/osmo_gsm_tester/process.py @@ -22,7 +22,7 @@ import time import subprocess import signal -from . import log, test +from . import log, test, event_loop from .util import Dir class Process(log.Origin): @@ -206,7 +206,7 @@ class Process(log.Origin): return self.result is not None def wait(self, timeout=300): - test.wait(self.terminated, timeout=timeout) + event_loop.wait(self, self.terminated, timeout=timeout) class RemoteProcess(Process): diff --git a/src/osmo_gsm_tester/suite.py b/src/osmo_gsm_tester/suite.py index 06648974..d55ee925 100644 --- a/src/osmo_gsm_tester/suite.py +++ b/src/osmo_gsm_tester/suite.py @@ -22,7 +22,7 @@ import sys import time import copy import traceback -from . import config, log, template, util, resource, schema, ofono_client, osmo_nitb +from . import config, log, template, util, resource, schema, ofono_client, osmo_nitb, event_loop from . import test class Timeout(Exception): @@ -114,7 +114,7 @@ class Test(log.Origin): with self: self.status = Test.UNKNOWN self.start_timestamp = time.time() - test.setup(suite_run, self, ofono_client, sys.modules[__name__]) + test.setup(suite_run, self, ofono_client, sys.modules[__name__], event_loop) self.log('START') with self.redirect_stdout(): util.run_python_file('%s.%s' % (self.suite.name(), self.name()), @@ -225,6 +225,7 @@ class SuiteRun(log.Origin): self.log('Suite run start') try: self.mark_start() + event_loop.register_poll_func(self.poll) if not self.reserved_resources: self.reserve_resources() for test in self.definition.tests: @@ -243,6 +244,7 @@ class SuiteRun(log.Origin): # base exception is raised. Make sure to stop processes in this # finally section. Resources are automatically freed with 'atexit'. self.stop_processes() + event_loop.unregister_poll_func(self.poll) self.duration = time.time() - self.start_timestamp if self.test_failed_ctr: self.status = SuiteRun.FAIL @@ -287,32 +289,7 @@ class SuiteRun(log.Origin): self.log('using MSISDN', msisdn) return msisdn - def _wait(self, condition, condition_args, condition_kwargs, timeout, timestep): - if not timeout or timeout < 0: - raise RuntimeError('wait() *must* time out at some point. timeout=%r' % timeout) - if timestep < 0.1: - timestep = 0.1 - - started = time.time() - while True: - self.poll() - if condition(*condition_args, **condition_kwargs): - return True - waited = time.time() - started - if waited > timeout: - return False - time.sleep(timestep) - - def wait(self, condition, *condition_args, timeout=300, timestep=1, **condition_kwargs): - if not self._wait(condition, condition_args, condition_kwargs, timeout, timestep): - raise Timeout('Timeout expired') - - def sleep(self, seconds): - assert seconds > 0. - self._wait(lambda: False, [], {}, timeout=seconds, timestep=min(seconds, 1)) - def poll(self): - ofono_client.poll() if self._processes: for process in self._processes: if process.terminated(): diff --git a/src/osmo_gsm_tester/test.py b/src/osmo_gsm_tester/test.py index f584c928..e7ee232e 100644 --- a/src/osmo_gsm_tester/test.py +++ b/src/osmo_gsm_tester/test.py @@ -28,14 +28,15 @@ log = None dbg = None err = None wait = None +wait_no_raise = None sleep = None poll = None prompt = None Timeout = None Failure = None -def setup(suite_run, _test, ofono_client, suite_module): - global trial, suite, test, resources, log, dbg, err, wait, sleep, poll, prompt, Failure, Timeout +def setup(suite_run, _test, ofono_client, suite_module, event_module): + global trial, suite, test, resources, log, dbg, err, wait, wait_no_raise, sleep, poll, prompt, Failure, Timeout trial = suite_run.trial suite = suite_run test = _test @@ -43,9 +44,10 @@ def setup(suite_run, _test, ofono_client, suite_module): log = test.log dbg = test.dbg err = test.err - wait = suite_run.wait - sleep = suite_run.sleep - poll = suite_run.poll + wait = lambda *args, **kwargs: event_module.wait(suite_run, *args, **kwargs) + wait_no_raise = lambda *args, **kwargs: event_module.wait_no_raise(suite_run, *args, **kwargs) + sleep = lambda *args, **kwargs: event_module.sleep(suite_run, *args, **kwargs) + poll = event_module.poll prompt = suite_run.prompt Failure = suite_module.Failure Timeout = suite_module.Timeout