Merge lp:~veebers/autopilot/fixing_backend_being_none into lp:autopilot/1.3

Proposed by Christopher Lee
Status: Merged
Approved by: Ɓukasz Zemczak
Approved revision: 343
Merged at revision: 343
Proposed branch: lp:~veebers/autopilot/fixing_backend_being_none
Merge into: lp:autopilot/1.3
Diff against target: 271 lines (+47/-59)
5 files modified
autopilot/introspection/__init__.py (+10/-10)
autopilot/introspection/dbus.py (+16/-37)
autopilot/introspection/qt.py (+1/-1)
autopilot/tests/unit/test_introspection_features.py (+19/-10)
autopilot/tests/unit/test_matchers.py (+1/-1)
To merge this branch: bzr merge lp:~veebers/autopilot/fixing_backend_being_none
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Thomi Richards (community) Approve
Review via email: mp+188762@code.launchpad.net

Commit message

Fixes issue where a classes _Backend can be None causes uncaught exceptions.

Description of the change

Fixes issue where a classes _Backend can be None causes uncaught exceptions.
The backend is now stored as an instance variable.

To post a comment you must log in.
Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Looks good to me, except that changing class methods into regular (instance) methods is likely to break compatibility - probably not with Touch apps, but more liekly with unity7 or the desktop apps. Let's make sure this is not the case before merging this.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Andy Doan (doanac) wrote :

I've run the smoke image testing suite on my mako at home with this branch. I'm not sure if this is a problem or not. When run under our CI logic, I get a unity8 failure:

18:29:58.118 INFO globals:50 - Starting test unity8.indicators_client.tests.test_battery.TestDisplayMenus.test_brightness_slider (with touch)
18:29:58.119 WARNING testcase:118 - No tracing available - install the python-autopilot-trace package!
18:29:58.155 WARNING testcase:155 - Process manager backend unavailable, application snapshot support disabled.
18:29:58.227 INFO __init__:156 - Launching process: ['/usr/bin/indicators-client', '-testability', '-geometry', None]
18:29:58.272 ERROR testresult:35 - ERROR: unity8.indicators_client.tests.test_battery.TestDisplayMenus.test_brightness_slider(with touch)
18:29:58.274 ERROR testresult:35 - traceback: {{{
Traceback (most recent call last):
File "/home/phablet/autopilot/unity8/indicators_client/tests/test_battery.py", line 22, in setUp
super(TestDisplayMenus, self).setUp(self.geometry, self.grid_unit)
File "/home/phablet/autopilot/unity8/indicators_client/tests/__init__.py", line 57, in setUp
self.launch_test_local(geometry)
File "/home/phablet/autopilot/unity8/indicators_client/tests/__init__.py", line 62, in launch_test_local
"-geometry", geometry, app_type='qt')
File "/home/phablet/autopilot-override/autopilot/testcase.py", line 279, in launch_test_application
process = launch_application(launcher, app_path, *arguments, **kwargs)
File "/home/phablet/autopilot-override/autopilot/introspection/__init__.py", line 128, in launch_application
cwd=cwd
File "/home/phablet/autopilot-override/autopilot/introspection/__init__.py", line 167, in launch_process
**kwargs
File "/usr/lib/python2.7/subprocess.py", line 709, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1326, in _execute_child
raise child_exception
TypeError: execv() arg 2 must contain only strings
}}}

However, phablet-test-run is working. So i think its just something environmental at my place.

Revision history for this message
Andy Doan (doanac) wrote :

I can reproduce the error now. It happens when running the test by itself. ie:

 phablet-test-run -n -v unity8.indicators_client.tests.test_battery.TestDisplayMenus.test_auto_bright_switch

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) :
review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'autopilot/introspection/__init__.py'
--- autopilot/introspection/__init__.py 2013-09-26 05:31:16 +0000
+++ autopilot/introspection/__init__.py 2013-10-02 04:15:28 +0000
@@ -42,7 +42,6 @@
42 AP_INTROSPECTION_IFACE,42 AP_INTROSPECTION_IFACE,
43)43)
44from autopilot.introspection.dbus import (44from autopilot.introspection.dbus import (
45 _clear_backends_for_proxy_object,
46 CustomEmulatorBase,45 CustomEmulatorBase,
47 DBusIntrospectionObject,46 DBusIntrospectionObject,
48 get_classname_from_path,47 get_classname_from_path,
@@ -474,16 +473,17 @@
474 proxy_bases = proxy_bases + (emulator_base, )473 proxy_bases = proxy_bases + (emulator_base, )
475 cls_name, cls_state = _get_proxy_object_class_name_and_state(data_source)474 cls_name, cls_state = _get_proxy_object_class_name_and_state(data_source)
476475
477 _clear_backends_for_proxy_object(emulator_base)476 # Merge the object hierarchy.
478 clsobj = type(477 clsobj = type(str("%sBase" % cls_name), proxy_bases, {})
479 # Merge the object hierarchy.
480 str("%sBase" % cls_name), proxy_bases, dict(_Backend=data_source)
481 )
482478
483 proxy_class = type(str(cls_name), (clsobj,), {})479 proxy_class = type(str(cls_name), (clsobj,), {})
484480
485 proxy = proxy_class.get_root_instance()481 try:
486 return proxy482 dbus_tuple = data_source.introspection_iface.GetState("/")[0]
483 path, state = dbus_tuple
484 return proxy_class(state, path, data_source)
485 except IndexError:
486 raise RuntimeError("Unable to find root object of %r" % proxy_class)
487487
488488
489def _get_proxy_object_base_classes(backend):489def _get_proxy_object_base_classes(backend):
@@ -518,8 +518,8 @@
518class ApplicationProxyObject(DBusIntrospectionObject):518class ApplicationProxyObject(DBusIntrospectionObject):
519 """A class that better supports query data from an application."""519 """A class that better supports query data from an application."""
520520
521 def __init__(self, state, path):521 def __init__(self, state, path, backend):
522 super(ApplicationProxyObject, self).__init__(state, path)522 super(ApplicationProxyObject, self).__init__(state, path, backend)
523 self._process = None523 self._process = None
524524
525 def set_process(self, process):525 def set_process(self, process):
526526
=== modified file 'autopilot/introspection/dbus.py'
--- autopilot/introspection/dbus.py 2013-09-27 05:48:12 +0000
+++ autopilot/introspection/dbus.py 2013-10-02 04:15:28 +0000
@@ -111,20 +111,6 @@
111 return class_object111 return class_object
112112
113113
114def _clear_backends_for_proxy_object(proxy_object):
115 """Iterate over the object registry and clear the dbus backend address set
116 on any class that has the same id as the specified proxy_object.
117
118 This is required so consecutive tests do not end up re-using the same dbus
119 backend address as a previous run, which will probably be incorrect.
120
121 """
122 global _object_registry
123 for cls in _object_registry[proxy_object._id].itervalues():
124 if type(proxy_object) is not cls:
125 cls._Backend = None
126
127
128def translate_state_keys(state_dict):114def translate_state_keys(state_dict):
129 """Translates the *state_dict* passed in so the keys are usable as python115 """Translates the *state_dict* passed in so the keys are usable as python
130 attributes."""116 attributes."""
@@ -161,13 +147,12 @@
161147
162 __metaclass__ = IntrospectableObjectMetaclass148 __metaclass__ = IntrospectableObjectMetaclass
163149
164 _Backend = None150 def __init__(self, state_dict, path, backend):
165
166 def __init__(self, state_dict, path):
167 self.__state = {}151 self.__state = {}
168 self.__refresh_on_attribute = True152 self.__refresh_on_attribute = True
169 self._set_properties(state_dict)153 self._set_properties(state_dict)
170 self._path = path154 self._path = path
155 self._backend = backend
171156
172 def _set_properties(self, state_dict):157 def _set_properties(self, state_dict):
173 """Creates and set attributes of *self* based on contents of158 """Creates and set attributes of *self* based on contents of
@@ -462,8 +447,7 @@
462 _, new_state = self.get_new_state()447 _, new_state = self.get_new_state()
463 self._set_properties(new_state)448 self._set_properties(new_state)
464449
465 @classmethod450 def get_all_instances(self):
466 def get_all_instances(cls):
467 """Get all instances of this class that exist within the Application451 """Get all instances of this class that exist within the Application
468 state tree.452 state tree.
469453
@@ -481,23 +465,22 @@
481 :return: List (possibly empty) of class instances.465 :return: List (possibly empty) of class instances.
482466
483 """467 """
484 cls_name = cls.__name__468 cls_name = type(self).__name__
485 instances = cls.get_state_by_path("//%s" % (cls_name))469 instances = self.get_state_by_path("//%s" % (cls_name))
486 return [cls.make_introspection_object(i) for i in instances]470 return [self.make_introspection_object(i) for i in instances]
487471
488 @classmethod472 def get_root_instance(self):
489 def get_root_instance(cls):
490 """Get the object at the root of this tree.473 """Get the object at the root of this tree.
491474
492 This will return an object that represents the root of the475 This will return an object that represents the root of the
493 introspection tree.476 introspection tree.
494477
495 """478 """
496 instances = cls.get_state_by_path("/")479 instances = self.get_state_by_path("/")
497 if len(instances) != 1:480 if len(instances) != 1:
498 logger.error("Could not retrieve root object.")481 logger.error("Could not retrieve root object.")
499 return None482 return None
500 return cls.make_introspection_object(instances[0])483 return self.make_introspection_object(instances[0])
501484
502 def __getattr__(self, name):485 def __getattr__(self, name):
503 # avoid recursion if for some reason we have no state set (should never486 # avoid recursion if for some reason we have no state set (should never
@@ -514,8 +497,7 @@
514 "Class '%s' has no attribute '%s'." %497 "Class '%s' has no attribute '%s'." %
515 (self.__class__.__name__, name))498 (self.__class__.__name__, name))
516499
517 @classmethod500 def get_state_by_path(self, piece):
518 def get_state_by_path(cls, piece):
519 """Get state for a particular piece of the state tree.501 """Get state for a particular piece of the state tree.
520502
521 You should probably never need to call this directly.503 You should probably never need to call this directly.
@@ -530,7 +512,7 @@
530 "XPath query must be a string, not %r", type(piece))512 "XPath query must be a string, not %r", type(piece))
531513
532 with Timer("GetState %s" % piece):514 with Timer("GetState %s" % piece):
533 data = cls._Backend.introspection_iface.GetState(piece)515 data = self._backend.introspection_iface.GetState(piece)
534 if len(data) > 15:516 if len(data) > 15:
535 logger.warning(517 logger.warning(
536 "Your query '%s' returned a lot of data (%d items). This "518 "Your query '%s' returned a lot of data (%d items). This "
@@ -562,8 +544,7 @@
562 else:544 else:
563 return self._path + "[id=%d]" % self.id545 return self._path + "[id=%d]" % self.id
564546
565 @classmethod547 def make_introspection_object(self, dbus_tuple):
566 def make_introspection_object(cls, dbus_tuple):
567 """Make an introspection object given a DBus tuple of548 """Make an introspection object given a DBus tuple of
568 (path, state_dict).549 (path, state_dict).
569550
@@ -572,23 +553,21 @@
572 path, state = dbus_tuple553 path, state = dbus_tuple
573 name = get_classname_from_path(path)554 name = get_classname_from_path(path)
574 try:555 try:
575 class_type = _object_registry[cls._id][name]556 class_type = _object_registry[self._id][name]
576 if class_type._Backend is None:
577 class_type._Backend = cls._Backend
578 except KeyError:557 except KeyError:
579 get_debug_logger().warning(558 get_debug_logger().warning(
580 "Generating introspection instance for type '%s' based on "559 "Generating introspection instance for type '%s' based on "
581 "generic class.", name)560 "generic class.", name)
582 # we want the object to inherit from the custom emulator base, not561 # we want the object to inherit from the custom emulator base, not
583 # the object class that is doing the selecting562 # the object class that is doing the selecting
584 for base in cls.__bases__:563 for base in type(self).__bases__:
585 if issubclass(base, CustomEmulatorBase):564 if issubclass(base, CustomEmulatorBase):
586 base_class = base565 base_class = base
587 break566 break
588 else:567 else:
589 base_class = cls568 base_class = type(self)
590 class_type = type(str(name), (base_class,), {})569 class_type = type(str(name), (base_class,), {})
591 return class_type(state, path)570 return class_type(state, path, self._backend)
592571
593 @contextmanager572 @contextmanager
594 def no_automatic_refreshing(self):573 def no_automatic_refreshing(self):
595574
=== modified file 'autopilot/introspection/qt.py'
--- autopilot/introspection/qt.py 2013-09-13 14:53:52 +0000
+++ autopilot/introspection/qt.py 2013-10-02 04:15:28 +0000
@@ -101,7 +101,7 @@
101101
102 """102 """
103103
104 return self._Backend.qt_introspection_iface104 return self._backend.qt_introspection_iface
105105
106 @property106 @property
107 def slots(self):107 def slots(self):
108108
=== modified file 'autopilot/tests/unit/test_introspection_features.py'
--- autopilot/tests/unit/test_introspection_features.py 2013-09-27 05:48:12 +0000
+++ autopilot/tests/unit/test_introspection_features.py 2013-10-02 04:15:28 +0000
@@ -18,7 +18,7 @@
18#18#
1919
2020
21from mock import patch21from mock import patch, Mock
22from testtools import TestCase22from testtools import TestCase
23from testtools.matchers import Equals, NotEquals23from testtools.matchers import Equals, NotEquals
24from testscenarios import TestWithScenarios24from testscenarios import TestWithScenarios
@@ -89,7 +89,8 @@
89 def test_can_access_path_attribute(self):89 def test_can_access_path_attribute(self):
90 fake_object = DBusIntrospectionObject(90 fake_object = DBusIntrospectionObject(
91 dict(id=123, path='/some/path'),91 dict(id=123, path='/some/path'),
92 '/'92 '/',
93 None
93 )94 )
94 with fake_object.no_automatic_refreshing():95 with fake_object.no_automatic_refreshing():
95 self.assertThat(fake_object.path, Equals('/some/path'))96 self.assertThat(fake_object.path, Equals('/some/path'))
@@ -101,10 +102,14 @@
101 'large' is defined as more than 15.102 'large' is defined as more than 15.
102103
103 """104 """
104 with patch.object(DBusIntrospectionObject, '_Backend') as p:105 fake_object = DBusIntrospectionObject(
105 p.introspection_iface.GetState.return_value = \106 dict(id=123, path='/some/path'),
106 [('/path', {}) for i in range(16)]107 '/',
107 DBusIntrospectionObject.get_state_by_path('some_query')108 Mock()
109 )
110 fake_object._backend.introspection_iface.GetState.return_value = \
111 [('/path', {}) for i in range(16)]
112 fake_object.get_state_by_path('some_query')
108113
109 mock_logger.warning.assert_called_once_with(114 mock_logger.warning.assert_called_once_with(
110 "Your query '%s' returned a lot of data (%d items). This "115 "Your query '%s' returned a lot of data (%d items). This "
@@ -120,9 +125,13 @@
120 'small' is defined as 15 or fewer.125 'small' is defined as 15 or fewer.
121126
122 """127 """
123 with patch.object(DBusIntrospectionObject, '_Backend') as p:128 fake_object = DBusIntrospectionObject(
124 p.introspection_iface.GetState.return_value = \129 dict(id=123, path='/some/path'),
125 [('/path', {}) for i in range(15)]130 '/',
126 DBusIntrospectionObject.get_state_by_path('some_query')131 Mock()
132 )
133 fake_object._backend.introspection_iface.GetState.return_value = \
134 [('/path', {}) for i in range(15)]
135 fake_object.get_state_by_path('some_query')
127136
128 self.assertThat(mock_logger.warning.called, Equals(False))137 self.assertThat(mock_logger.warning.called, Equals(False))
129138
=== modified file 'autopilot/tests/unit/test_matchers.py'
--- autopilot/tests/unit/test_matchers.py 2013-08-07 03:18:32 +0000
+++ autopilot/tests/unit/test_matchers.py 2013-10-02 04:15:28 +0000
@@ -47,7 +47,7 @@
47 """47 """
48 class FakeObject(DBusIntrospectionObject):48 class FakeObject(DBusIntrospectionObject):
49 def __init__(self, props):49 def __init__(self, props):
50 super(FakeObject, self).__init__(props, "/FakeObject")50 super(FakeObject, self).__init__(props, "/FakeObject", None)
51 FakeObject._fake_props = props51 FakeObject._fake_props = props
5252
53 @classmethod53 @classmethod

Subscribers

People subscribed via source and target branches