event_loop: Create a global event loop to poll and wait for events
Tweaked-by: nhofmeyr Change-Id: Iaa78bae0f053496377609b24a11ebaef3fd77598
This commit is contained in:
parent
095d1290a6
commit
927344b4dc
|
@ -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
|
|
@ -17,7 +17,7 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# 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
|
from pydbus import SystemBus, Variant
|
||||||
import time
|
import time
|
||||||
|
@ -57,13 +57,14 @@ def dbus_connect(dbus_iface, handler):
|
||||||
DeferredHandling.defer_queue.'''
|
DeferredHandling.defer_queue.'''
|
||||||
return DeferredHandling(dbus_iface, handler).subscription_id
|
return DeferredHandling(dbus_iface, handler).subscription_id
|
||||||
|
|
||||||
|
def poll_glib():
|
||||||
def poll():
|
|
||||||
global glib_main_ctx
|
global glib_main_ctx
|
||||||
while glib_main_ctx.pending():
|
while glib_main_ctx.pending():
|
||||||
glib_main_ctx.iteration()
|
glib_main_ctx.iteration()
|
||||||
DeferredHandling.handle_queue()
|
DeferredHandling.handle_queue()
|
||||||
|
|
||||||
|
event_loop.register_poll_func(poll_glib)
|
||||||
|
|
||||||
def systembus_get(path):
|
def systembus_get(path):
|
||||||
global bus
|
global bus
|
||||||
return bus.get('org.ofono', path)
|
return bus.get('org.ofono', path)
|
||||||
|
@ -89,7 +90,7 @@ class Modem(log.Origin):
|
||||||
self.sms_received_list = []
|
self.sms_received_list = []
|
||||||
# init interfaces and connect to signals:
|
# init interfaces and connect to signals:
|
||||||
self.dbus_obj()
|
self.dbus_obj()
|
||||||
test.poll()
|
event_loop.poll()
|
||||||
|
|
||||||
def set_msisdn(self, msisdn):
|
def set_msisdn(self, msisdn):
|
||||||
self.msisdn = msisdn
|
self.msisdn = msisdn
|
||||||
|
@ -106,13 +107,13 @@ class Modem(log.Origin):
|
||||||
|
|
||||||
def _dbus_set_bool(self, name, bool_val, iface=I_MODEM):
|
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
|
# to make sure any pending signals are received before we send out more DBus requests
|
||||||
test.poll()
|
event_loop.poll()
|
||||||
|
|
||||||
val = bool(bool_val)
|
val = bool(bool_val)
|
||||||
self.log('Setting', name, val)
|
self.log('Setting', name, val)
|
||||||
self.dbus_obj()[iface].SetProperty(name, Variant('b', 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):
|
def property_is(self, name, val):
|
||||||
is_val = self.properties().get(name)
|
is_val = self.properties().get(name)
|
||||||
|
|
|
@ -22,7 +22,7 @@ import time
|
||||||
import subprocess
|
import subprocess
|
||||||
import signal
|
import signal
|
||||||
|
|
||||||
from . import log, test
|
from . import log, test, event_loop
|
||||||
from .util import Dir
|
from .util import Dir
|
||||||
|
|
||||||
class Process(log.Origin):
|
class Process(log.Origin):
|
||||||
|
@ -206,7 +206,7 @@ class Process(log.Origin):
|
||||||
return self.result is not None
|
return self.result is not None
|
||||||
|
|
||||||
def wait(self, timeout=300):
|
def wait(self, timeout=300):
|
||||||
test.wait(self.terminated, timeout=timeout)
|
event_loop.wait(self, self.terminated, timeout=timeout)
|
||||||
|
|
||||||
|
|
||||||
class RemoteProcess(Process):
|
class RemoteProcess(Process):
|
||||||
|
|
|
@ -22,7 +22,7 @@ import sys
|
||||||
import time
|
import time
|
||||||
import copy
|
import copy
|
||||||
import traceback
|
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
|
from . import test
|
||||||
|
|
||||||
class Timeout(Exception):
|
class Timeout(Exception):
|
||||||
|
@ -114,7 +114,7 @@ class Test(log.Origin):
|
||||||
with self:
|
with self:
|
||||||
self.status = Test.UNKNOWN
|
self.status = Test.UNKNOWN
|
||||||
self.start_timestamp = time.time()
|
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')
|
self.log('START')
|
||||||
with self.redirect_stdout():
|
with self.redirect_stdout():
|
||||||
util.run_python_file('%s.%s' % (self.suite.name(), self.name()),
|
util.run_python_file('%s.%s' % (self.suite.name(), self.name()),
|
||||||
|
@ -225,6 +225,7 @@ class SuiteRun(log.Origin):
|
||||||
self.log('Suite run start')
|
self.log('Suite run start')
|
||||||
try:
|
try:
|
||||||
self.mark_start()
|
self.mark_start()
|
||||||
|
event_loop.register_poll_func(self.poll)
|
||||||
if not self.reserved_resources:
|
if not self.reserved_resources:
|
||||||
self.reserve_resources()
|
self.reserve_resources()
|
||||||
for test in self.definition.tests:
|
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
|
# base exception is raised. Make sure to stop processes in this
|
||||||
# finally section. Resources are automatically freed with 'atexit'.
|
# finally section. Resources are automatically freed with 'atexit'.
|
||||||
self.stop_processes()
|
self.stop_processes()
|
||||||
|
event_loop.unregister_poll_func(self.poll)
|
||||||
self.duration = time.time() - self.start_timestamp
|
self.duration = time.time() - self.start_timestamp
|
||||||
if self.test_failed_ctr:
|
if self.test_failed_ctr:
|
||||||
self.status = SuiteRun.FAIL
|
self.status = SuiteRun.FAIL
|
||||||
|
@ -287,32 +289,7 @@ class SuiteRun(log.Origin):
|
||||||
self.log('using MSISDN', msisdn)
|
self.log('using MSISDN', msisdn)
|
||||||
return 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):
|
def poll(self):
|
||||||
ofono_client.poll()
|
|
||||||
if self._processes:
|
if self._processes:
|
||||||
for process in self._processes:
|
for process in self._processes:
|
||||||
if process.terminated():
|
if process.terminated():
|
||||||
|
|
|
@ -28,14 +28,15 @@ log = None
|
||||||
dbg = None
|
dbg = None
|
||||||
err = None
|
err = None
|
||||||
wait = None
|
wait = None
|
||||||
|
wait_no_raise = None
|
||||||
sleep = None
|
sleep = None
|
||||||
poll = None
|
poll = None
|
||||||
prompt = None
|
prompt = None
|
||||||
Timeout = None
|
Timeout = None
|
||||||
Failure = None
|
Failure = None
|
||||||
|
|
||||||
def setup(suite_run, _test, ofono_client, suite_module):
|
def setup(suite_run, _test, ofono_client, suite_module, event_module):
|
||||||
global trial, suite, test, resources, log, dbg, err, wait, sleep, poll, prompt, Failure, Timeout
|
global trial, suite, test, resources, log, dbg, err, wait, wait_no_raise, sleep, poll, prompt, Failure, Timeout
|
||||||
trial = suite_run.trial
|
trial = suite_run.trial
|
||||||
suite = suite_run
|
suite = suite_run
|
||||||
test = _test
|
test = _test
|
||||||
|
@ -43,9 +44,10 @@ def setup(suite_run, _test, ofono_client, suite_module):
|
||||||
log = test.log
|
log = test.log
|
||||||
dbg = test.dbg
|
dbg = test.dbg
|
||||||
err = test.err
|
err = test.err
|
||||||
wait = suite_run.wait
|
wait = lambda *args, **kwargs: event_module.wait(suite_run, *args, **kwargs)
|
||||||
sleep = suite_run.sleep
|
wait_no_raise = lambda *args, **kwargs: event_module.wait_no_raise(suite_run, *args, **kwargs)
|
||||||
poll = suite_run.poll
|
sleep = lambda *args, **kwargs: event_module.sleep(suite_run, *args, **kwargs)
|
||||||
|
poll = event_module.poll
|
||||||
prompt = suite_run.prompt
|
prompt = suite_run.prompt
|
||||||
Failure = suite_module.Failure
|
Failure = suite_module.Failure
|
||||||
Timeout = suite_module.Timeout
|
Timeout = suite_module.Timeout
|
||||||
|
|
Loading…
Reference in New Issue