Skip to content
This repository was archived by the owner on Mar 17, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ before_install:

script:
- docker exec taurus-test /bin/bash -c "cd taurus ; python setup.py install"
- docker exec taurus-test /bin/bash -c "taurustestsuite"
- docker exec taurus-test /bin/bash -c "TAURUS_STARTER_WAIT=5 taurustestsuite -e 'taurus\.core\.util\.test\.test_timer'"
8 changes: 6 additions & 2 deletions lib/taurus/core/tango/starter.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,12 @@ def startDs(self, synch=True, wait_seconds=10):
if self.isRunning():
_log.info('Server %s has been started' % self.ds_name)
##############################################################
# TODO: this workaround doesn't seem necessary (see isRunning)
# time.sleep(3)
# Workaround to avoid race conditions
# TODO: Find root cause of race condition and fix
_wait = float(os.environ.get('TAURUS_STARTER_WAIT', 0))
if _wait:
_log.info('Waiting %g s after start' % _wait)
time.sleep(_wait)
##############################################################
return
else:
Expand Down
119 changes: 74 additions & 45 deletions lib/taurus/core/tango/tangoattribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,6 @@ def __init__(self, name, parent, **kwargs):

self.call__init__(TaurusAttribute, name, parent, **kwargs)

self._events_working = False

attr_info = None
if parent:
attr_name = self.getSimpleName()
Expand Down Expand Up @@ -611,14 +609,14 @@ def _subscribeEvents(self):
self.__subscription_state = SubscriptionState.Subscribing
self.__chg_evt_id = self.__dev_hw_obj.subscribe_event(
attr_name, PyTango.EventType.CHANGE_EVENT,
self, [])
self, []) # connects to self.push_event callback

except:
self.__subscription_state = SubscriptionState.PendingSubscribe
self._activatePolling()
self.__chg_evt_id = self.__dev_hw_obj.subscribe_event(
attr_name, PyTango.EventType.CHANGE_EVENT,
self, [], True)
self, [], True) # connects to self.push_event callback

def _unsubscribeEvents(self):
# Careful in this method: This is intended to be executed in the cleanUp
Expand Down Expand Up @@ -659,7 +657,7 @@ def _subscribeConfEvents(self):
self.__cfg_evt_id = self.__dev_hw_obj.subscribe_event(
attr_name,
PyTango.EventType.ATTR_CONF_EVENT,
self, [], True)
self, [], True) # connects to self.push_event callback
except PyTango.DevFailed, e:
self.debug("Error trying to subscribe to CONFIGURATION events.")
self.traceback()
Expand Down Expand Up @@ -690,57 +688,88 @@ def _unsubscribeConfEvents(self):
self.trace(str(e))

def push_event(self, event):
"""Method invoked by the PyTango layer when a change event occurs.
Default implementation propagates the event to all listeners."""
"""Method invoked by the PyTango layer when an event occurs.
It propagates the event to listeners and delegates other tasks to
specific handlers for different event types.
"""
# if it is a configuration event
if isinstance(event, PyTango.AttrConfEventData):
etype, evalue = self._pushConfEvent(event)
# if it is an attribute event
else:
etype, evalue = self._pushAttrEvent(event)

curr_time = time.time()
# notify the listeners if required (i.e, if etype is not None)
if etype is None:
return
manager = Manager()
sm = self.getSerializationMode()
listeners = tuple(self._listeners)
if sm == TaurusSerializationMode.Concurrent:
manager.addJob(self.fireEvent, None, etype, evalue,
listeners=listeners)
else:
self.fireEvent(etype, evalue, listeners=listeners)

def _pushAttrEvent(self, event):
"""Handler of (non-configuration) events from the PyTango layer.
It handles the subscription and the (de)activation of polling

:param event: (A PyTango event)

:return: (evt_type, evt_value) Tuple containing the event type and the
event value. evt_type is a `TaurusEventType` (or None to
indicate that there should not be notification to listeners).
evt_value is a TaurusValue, an Exception, or None.
"""
if not event.err:
# if it is a configuration event
if isinstance(event, PyTango.AttrConfEventData):
event_type = TaurusEventType.Config
self._decodeAttrInfoEx(event.attr_conf)
# make sure that there is a self.__attr_value
if self.__attr_value is None:
# TODO: maybe we can avoid this read?
self.__attr_value = self.getValueObj(cache=False)
# if it is an attribute event
else:
event_type = TaurusEventType.Change
self.__attr_value, self.__attr_err = self.decode(
event.attr_value), None
self.__subscription_state = SubscriptionState.Subscribed
self.__subscription_event.set()
if not self.isPollingForced():
self._deactivatePolling()
# notify the listeners
listeners = tuple(self._listeners)
if sm == TaurusSerializationMode.Concurrent:
manager.addJob(self.fireEvent, None, event_type,
self.__attr_value, listeners=listeners)
else:
self.fireEvent(event_type, self.__attr_value,
listeners=listeners)
self.__attr_value, self.__attr_err = self.decode(
event.attr_value), None
self.__subscription_state = SubscriptionState.Subscribed
self.__subscription_event.set()
if not self.isPollingForced():
self._deactivatePolling()
return TaurusEventType.Change, self.__attr_value

elif event.errors[0].reason in EVENT_TO_POLLING_EXCEPTIONS:
if self.isPollingActive():
return
self.info("Activating polling. Reason: %s", event.errors[0].reason)
self.__subscription_state = SubscriptionState.PendingSubscribe
self._activatePolling()
if not self.isPollingActive():
self.info("Activating polling. Reason: %s",
event.errors[0].reason)
self.__subscription_state = SubscriptionState.PendingSubscribe
self._activatePolling()
return None, None

else:
self.__attr_value, self.__attr_err = None, PyTango.DevFailed(
*event.errors)
self.__subscription_state = SubscriptionState.Subscribed
self.__subscription_event.set()
self._deactivatePolling()
listeners = tuple(self._listeners)
if sm == TaurusSerializationMode.Concurrent:
manager.addJob(self.fireEvent, None, TaurusEventType.Error,
self.__attr_err, listeners=listeners)
else:
self.fireEvent(TaurusEventType.Error, self.__attr_err,
listeners=listeners)
return TaurusEventType.Error, self.__attr_err

def _pushConfEvent(self, event):
"""Handler of AttrConfEventData events from the PyTango layer.

:param event: (PyTango.AttrConfEventData)

:return: (evt_type, evt_value) Tuple containing the event type and the
event value. evt_type is a `TaurusEventType` (or None to
indicate that there should not be notification to listeners).
evt_value is a TaurusValue, an Exception, or None.
"""
if not event.err:
# update conf-related attributes
self._decodeAttrInfoEx(event.attr_conf)
# make sure that there is a self.__attr_value
if self.__attr_value is None:
# TODO: maybe we can avoid this read?
self.__attr_value = self.getValueObj(cache=False)
return TaurusEventType.Config, self.__attr_value

else:
self.__attr_value, self.__attr_err = None, PyTango.DevFailed(
*event.errors)
return TaurusEventType.Error, self.__attr_err

def isWrite(self, cache=True):
return self.getTangoWritable(cache) == PyTango.AttrWriteType.WRITE
Expand Down
104 changes: 51 additions & 53 deletions lib/taurus/core/tango/test/test_tangoattribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,58 +55,55 @@

# ==============================================================================
# Test writing fragment values
##
# DISABLED: these tests break test isolation (looks like Reset is not working
# for configurations). Until we solve it, we disable all these tests
#
# @insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu',
# cfg='range', value=[float('-inf'), float('inf')],
# expected=[Quantity(float('-inf')), Quantity(float('inf'))]
# )
# @insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu',
# cfg='range', value=[Quantity(float('-inf')),
# Quantity(float('inf'))],
# expected=[Quantity(float('-inf')), Quantity(float('inf'))])
# @insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu',
# cfg='range', value=[100, 300],
# expected=[Quantity(100), Quantity(300)])
# @insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu',
# cfg='range', value=[Quantity(100), Quantity(300)],
# expected=[Quantity(100), Quantity(300)])
# @insertTest(helper_name='write_read_conf', attr_name='float_scalar',
# cfg='range', value=[Quantity(-5, 'mm'), Quantity(5, 'mm')],
# expected=[Quantity(-0.005, 'm'), Quantity(5, 'mm')])
# @insertTest(helper_name='write_read_conf', attr_name='short_spectrum',
# cfg='label', value='Just a Test',
# expected='Just a Test')
# @insertTest(helper_name='write_read_conf', attr_name='boolean_spectrum',
# cfg='label', value='Just_a_Test',
# expected='Just_a_Test')
# @insertTest(helper_name='write_read_conf', attr_name='short_scalar',
# cfg='warnings', value=[Quantity(-2, 'mm'), Quantity(2, 'mm')],
# expected=[Quantity(-2, 'mm'), Quantity(0.002, 'm')])
# @insertTest(helper_name='write_read_conf', attr_name='short_image',
# cfg='warnings', value=[Quantity(-2, 'mm'), Quantity(2, 'mm')],
# expected=[Quantity(-0.002, 'm'), Quantity(2, 'mm')])
# @insertTest(helper_name='write_read_conf', attr_name='float_image',
# cfg='warnings', value=[Quantity(-0.75, 'mm'),
# Quantity(0.75, 'mm')],
# expected=[Quantity(-0.00075, 'm'), Quantity(0.75, 'mm')])
# @insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu',
# cfg='warnings', value=[100, 300],
# expected=[Quantity(100), Quantity(300)])
# @insertTest(helper_name='write_read_conf', attr_name='short_scalar',
# cfg='alarms', value=[Quantity(-50, 'mm'), Quantity(50, 'mm')],
# expected=[Quantity(-50, 'mm'), Quantity(50, 'mm')])
# @insertTest(helper_name='write_read_conf', attr_name='short_image',
# cfg='alarms', value=[Quantity(-2, 'mm'), Quantity(2, 'mm')],
# expected=[Quantity(-0.002, 'm'), Quantity(2, 'mm')])
# @insertTest(helper_name='write_read_conf', attr_name='float_image',
# cfg='alarms', value=[Quantity(-0.75, 'mm'), Quantity(0.75, 'mm')],
# expected=[Quantity(-0.00075, 'm'), Quantity(0.75, 'mm')])
# @insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu',
# cfg='alarms', value=[100, 300],
# expected=[Quantity(100), Quantity(300)])

@insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu',
cfg='range', value=[float('-inf'), float('inf')],
expected=[Quantity(float('-inf')), Quantity(float('inf'))]
)
@insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu',
cfg='range', value=[Quantity(float('-inf')),
Quantity(float('inf'))],
expected=[Quantity(float('-inf')), Quantity(float('inf'))])
@insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu',
cfg='range', value=[100, 300],
expected=[Quantity(100), Quantity(300)])
@insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu',
cfg='range', value=[Quantity(100), Quantity(300)],
expected=[Quantity(100), Quantity(300)])
@insertTest(helper_name='write_read_conf', attr_name='float_scalar',
cfg='range', value=[Quantity(-5, 'mm'), Quantity(5, 'mm')],
expected=[Quantity(-0.005, 'm'), Quantity(5, 'mm')])
@insertTest(helper_name='write_read_conf', attr_name='short_spectrum',
cfg='label', value='Just a Test',
expected='Just a Test')
@insertTest(helper_name='write_read_conf', attr_name='boolean_spectrum',
cfg='label', value='Just_a_Test',
expected='Just_a_Test')
@insertTest(helper_name='write_read_conf', attr_name='short_scalar',
cfg='warnings', value=[Quantity(-2, 'mm'), Quantity(2, 'mm')],
expected=[Quantity(-2, 'mm'), Quantity(0.002, 'm')])
@insertTest(helper_name='write_read_conf', attr_name='short_image',
cfg='warnings', value=[Quantity(-2, 'mm'), Quantity(2, 'mm')],
expected=[Quantity(-0.002, 'm'), Quantity(2, 'mm')])
@insertTest(helper_name='write_read_conf', attr_name='float_image',
cfg='warnings', value=[Quantity(-0.75, 'mm'),
Quantity(0.75, 'mm')],
expected=[Quantity(-0.00075, 'm'), Quantity(0.75, 'mm')])
@insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu',
cfg='warnings', value=[100, 300],
expected=[Quantity(100), Quantity(300)])
@insertTest(helper_name='write_read_conf', attr_name='short_scalar',
cfg='alarms', value=[Quantity(-50, 'mm'), Quantity(50, 'mm')],
expected=[Quantity(-50, 'mm'), Quantity(50, 'mm')])
@insertTest(helper_name='write_read_conf', attr_name='short_image',
cfg='alarms', value=[Quantity(-2, 'mm'), Quantity(2, 'mm')],
expected=[Quantity(-0.002, 'm'), Quantity(2, 'mm')])
@insertTest(helper_name='write_read_conf', attr_name='float_image',
cfg='alarms', value=[Quantity(-0.75, 'mm'), Quantity(0.75, 'mm')],
expected=[Quantity(-0.00075, 'm'), Quantity(0.75, 'mm')])
@insertTest(helper_name='write_read_conf', attr_name='short_scalar_nu',
cfg='alarms', value=[100, 300],
expected=[Quantity(100), Quantity(300)])

# ==============================================================================
# Test encode-decode of empty arrays
Expand Down Expand Up @@ -764,7 +761,8 @@ def write_read_attr(self, attrname=None, setvalue=None, expected=None,
if setvalue is None:
read_value = a.read()
else:
read_value = a.write(setvalue, with_read=True)
a.write(setvalue)
read_value = a.read(cache=False)

msg = ('read() for "%s" did not return a TangoAttrValue (got a %s)' %
(attrname, read_value.__class__.__name__))
Expand Down
5 changes: 0 additions & 5 deletions lib/taurus/qt/qtgui/base/test/test_taurusbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,6 @@ def setUp(self):
def getDisplayValue(self, model=None, expected=None):
'''Check if setModel works when using parent model'''
self._widget.setModel(model)
# ----------------------------
# workaround for https://sourceforge.net/p/tauruslib/tickets/334/
import time
time.sleep(BaseWidgetTestCase._BUG_334_WORKAROUND_TIME)
# ----------------------------
got = self._widget.getDisplayValue()
msg = ('getDisplayValue for "%s" should be %r (got %r)' %
(model, expected, got))
Expand Down
6 changes: 3 additions & 3 deletions lib/taurus/qt/qtgui/display/test/test_tauruslabel.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def text(self, model=None, expected=None, fgRole=None, maxdepr=0):
self._widget.setModel(model)
if fgRole is not None:
self._widget.setFgRole(fgRole)
self._app.processEvents()
self.processEvents(repetitions=10, sleep=.1)
got = str(self._widget.text())
msg = ('wrong text for "%s":\n expected: %s\n got: %s' %
(model, expected, got))
Expand All @@ -156,11 +156,11 @@ def text(self, model=None, expected=None, fgRole=None, maxdepr=0):
# expected: (0, 255, 0)
# got: (239, 240, 241) # <-- these values change (unitialized garbage?)
# ~~~~~~~~~~~~~~~~~~~~~~~
@unittest.skip('bgRole tests fail randomly. Skip until fixed.')
#@unittest.skip('bgRole tests fail randomly. Skip until fixed.')
def bgRole(self, model=None, expected=None, bgRole=None, maxdepr=0):
'''Check that the label text'''
self._widget.setModel(model)
self._app.processEvents()
self.processEvents(repetitions=10, sleep=.1)
if bgRole is not None:
self._widget.setBgRole(bgRole)
p = self._widget.palette()
Expand Down
4 changes: 2 additions & 2 deletions lib/taurus/qt/qtgui/panel/test/test_taurusvalue.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def test_bug126(self):
self._widget.setModel('tango:' + DEV_NAME + '/double_scalar')
label = 'MIXEDcase'
w.setLabelConfig(label)
self._app.processEvents() # required
self.processEvents(repetitions=10, sleep=.1)
shownLabel = str(w.labelWidget().text())
msg = 'Shown label ("%s") differs from set label ("%s")' % (shownLabel,
label)
Expand All @@ -66,7 +66,7 @@ def texts(self, model=None, expected=None, fgRole=None, maxdepr=0):
self._widget.setModel(model)
if fgRole is not None:
self._widget.setFgRole(fgRole)
self._app.processEvents()
self.processEvents(repetitions=10, sleep=.1)
got = (str(self._widget.labelWidget().text()),
str(self._widget.readWidget().text()),
str(self._widget.writeWidget().displayText()),
Expand Down
13 changes: 6 additions & 7 deletions lib/taurus/qt/qtgui/test/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

"""Utilities for creating generic tests for Taurus widgets"""

import time
import taurus.core
from taurus.external import unittest
from taurus.qt.qtgui.application import TaurusApplication
Expand All @@ -48,8 +49,6 @@ class BaseWidgetTestCase(object):
initargs = []
initkwargs = {}

_BUG_334_WORKAROUND_TIME = 1 # TODO: remove this when proper fix is done

def setUp(self):
"""
Preconditions:
Expand All @@ -71,11 +70,6 @@ def setUp(self):

if self._klass is not None:
self._widget = self._klass(*self.initargs, **self.initkwargs)
# ----------------------------
# workaround for https://sourceforge.net/p/tauruslib/tickets/334/
import time
time.sleep(self._BUG_334_WORKAROUND_TIME)
# ----------------------------

def assertMaxDeprecations(self, maximum, msg=None):
"""Assertion method that checks that the number of deprecations issued
Expand All @@ -89,6 +83,11 @@ def assertMaxDeprecations(self, maximum, msg=None):
(deps, maximum, self._depCounter.pretty()))
self.assertTrue(deps <= maximum, msg)

def processEvents(self, repetitions=1, sleep=0):
for i in xrange(repetitions):
time.sleep(sleep)
self._app.processEvents()


class GenericWidgetTestCase(BaseWidgetTestCase):

Expand Down