event_loop: Create a global event loop to poll and wait for events
Tweaked-by: nhofmeyr
Change-Id: Iaa78bae0f053496377609b24a11ebaef3fd77598
diff --git a/src/osmo_gsm_tester/event_loop.py b/src/osmo_gsm_tester/event_loop.py
new file mode 100644
index 0000000..d082898
--- /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 <pespin@sysmocom.de>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+# 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 faa9192..1ff98a9 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 <http://www.gnu.org/licenses/>.
-from . import log, test, util
+from . import log, test, util, event_loop
from pydbus import SystemBus, Variant
import time
@@ -57,13 +57,14 @@
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 @@
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 @@
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 a687de6..e9567b2 100644
--- a/src/osmo_gsm_tester/process.py
+++ b/src/osmo_gsm_tester/process.py
@@ -22,7 +22,7 @@
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 @@
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 0664897..d55ee92 100644
--- a/src/osmo_gsm_tester/suite.py
+++ b/src/osmo_gsm_tester/suite.py
@@ -22,7 +22,7 @@
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 @@
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 @@
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 @@
# 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 @@
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 f584c92..e7ee232 100644
--- a/src/osmo_gsm_tester/test.py
+++ b/src/osmo_gsm_tester/test.py
@@ -28,14 +28,15 @@
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 @@
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