event_loop: Create a global event loop to poll and wait for events

Tweaked-by: nhofmeyr
Change-Id: Iaa78bae0f053496377609b24a11ebaef3fd77598
This commit is contained in:
Pau Espin 2017-05-22 16:38:49 +02:00
parent 095d1290a6
commit 927344b4dc
5 changed files with 84 additions and 40 deletions

View File

@ -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

View File

@ -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 @@ 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)

View File

@ -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):

View File

@ -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():

View File

@ -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