Merge lp:~nuclearbob/autopilot/bug1210260 into lp:autopilot
- bug1210260
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Thomi Richards |
Approved revision: | 466 |
Merged at revision: | 455 |
Proposed branch: | lp:~nuclearbob/autopilot/bug1210260 |
Merge into: | lp:autopilot |
Diff against target: |
388 lines (+292/-20) 2 files modified
autopilot/introspection/dbus.py (+95/-20) autopilot/tests/unit/test_introspection_features.py (+197/-0) |
To merge this branch: | bzr merge lp:~nuclearbob/autopilot/bug1210260 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Thomi Richards (community) | Needs Fixing | ||
PS Jenkins bot | continuous-integration | Needs Fixing | |
Max Brustkern (community) | Needs Resubmitting | ||
Review via email: mp+208260@code.launchpad.net |
Commit message
Autopilot emulators can declare a validate_
Description of the change
Autopilot emulators can declare a validate_
Thomi Richards (thomir-deactivatedaccount) wrote : | # |
Thomi Richards (thomir-deactivatedaccount) wrote : | # |
Hi Max,
I assume you want a review on this, since you created this MP :)
Here goes:
12 + class_type = _select_
let's not call this '_select_emulator'. How about '_get_proxy_
22 + p<F7>7ath, state = dbus_tuple
ummm... O.0
31 + @classmethod
32 + def validate_
33 + name = get_classname_
34 + return cls.__name__ == name
This needs a docstring. The docstring needs to describe:
- What this method is for.
- How users can override it.
- What the default implementation does.
43 +def _select_
Please change the parameter 'objects' to have a mode descriptive name. 'proxy_
44 + class_type = None
Same comment as above. I realise that this is all my fault :)
45 + for item_name in objects:
46 + item = objects[item_name]
Two things here. First, let's change thos evariable names to be a bit more informative. Second, use the dictionaries 'items' method if you want to get both keys and values (for k,v in my_dict.items():). However, in this case you only care about the values, so this should work:
for proxy_class in objects.values():
(note: I haven't changed the 'items' name from the previous point. I'm sure you can cope :))
On a further note, this entire function can be written in two lines:
possible_classes = [ c for c in objects.values() if c.validate_
if len(possible_
raise ValueError(...)
return possible_classes[0] if possible_classes else None
On a final note for this function, your exception needs more information. If we have more than one proxy class in this situation, we need to be able to debug that. A nice error message should include the classes that are applicable... so perhaps something like:
raise ValueError(
"More than one custom proxy class matches this state. Possible classes are %s. State is %s. Path is %s" % (
','
repr(state),
path
)
)
Also note that the error message really shouldn't use the word 'emulator' in it :)
63 +
In general, please avoid adding whitespace to diffs unless you have to.
109 + def test_class_
110 + self.assertThat
111 + Equals(True))
I don't think it's a good idea to assign 'Rectangle' to the class instance. Instead, nest them in the test class. Also, 'Rectangle' and 'Circle' names don't tell us anything about the classes, so let's use better names. Something like this should work:
class MakeIntrospecti
class DefaultSelector
pass
class AlwaysSelected(
@classmethod
def validate_
class NeverSelected(
@classmethod
...
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:444
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Thomi Richards (thomir-deactivatedaccount) wrote : | # |
Please set this back to 'needs review' when you'd like another review.
Thanks
Max Brustkern (nuclearbob) wrote : | # |
Should be cleaned up now per Thomi's review.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:457
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Thomi Richards (thomir-deactivatedaccount) wrote : | # |
Hi,
This is looking much better, thanks for your work. There are still a few issues though.
10 + :rtype: instance of appropriate CustomProxyObject class
You seem to like using the :rtype: field. In general, we haven't used this before in autopilot, so unless there's a good reason to, I'd prefer to encode this information in the :returns: field. For example, this:
9 + :returns: introspection object
10 + :rtype: instance of appropriate CustomProxyObject class
...could more simply be written as:
:returns: A proxy object that derives from DBusIntrospecti
...or something similar. If there's a good reason to use :rtype: that I've missed, please let me know.
51 + :type path: string
Same here. This:
50 + :param path: The dbus path of the object to check
51 + :type path: string
should just be:
:param path: A string containing the path of the object to check.
Finally, in general we don't insist on docstrings for private methods / functions, unless their intent is hard to divine from the function / class name (however, if tht's the case then the name probably needs to be changed). I'm not saying *don't* document them, just htat you don't need to go to so much effort to document them. Often a single line docstring will suffice.
Now, on to the tests:
@patch(
@patch(
def test_get_
'Called _get_default_
retval = 'return'
class_dict = {'DefaultSelector': self.DefaultSel
default = self.DefaultSel
path = '/path/
state = {}
gpoc_return = _get_proxy_
A few things:
First, don't use the side_effects attribute on the mock to indicate a failure. First, it will produce an error, not a failure, and second, it's not particularly expressive.
Second, when you just want some placeholder text, it's better to use 'self.getUnique
Third, since this is a unit test, this should fail in exactly one way. We often ignore that rule, but in this case, obeying it helps make this test a lot clearer. The intent of this test is that the '_get_proxy_
Here's how I'd do it:
@patch(
@patch(
Max Brustkern (nuclearbob) wrote : | # |
More updates that should address the latest comments, I think.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:463
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:464
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Thomi Richards (thomir-deactivatedaccount) wrote : | # |
Hi,
This looks great - with one teensy exception - please change these:
:raises: ValueError if more than one class in the dict matches
to be:
:raises ValueError: if more than one class in the dict matches
i.e.- as we discussed in the PEP257 thread.
After that's done, I'm happy to merge it.
Cheers
Max Brustkern (nuclearbob) wrote : | # |
Should be taken care of now.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:465
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 466. By Max Brustkern
-
Merged trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:466
http://
Executed test runs:
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Thomi Richards (thomir-deactivatedaccount) wrote : | # |
LGTM
Preview Diff
1 | === modified file 'autopilot/introspection/dbus.py' |
2 | --- autopilot/introspection/dbus.py 2014-03-11 16:39:25 +0000 |
3 | +++ autopilot/introspection/dbus.py 2014-03-13 21:44:45 +0000 |
4 | @@ -413,7 +413,7 @@ |
5 | of the appropriate type (the latter case is for overridden emulator |
6 | classes). |
7 | |
8 | - :raises: **TypeError** if neither *type_name* or keyword filters are |
9 | + :raises TypeError: if neither *type_name* or keyword filters are |
10 | provided. |
11 | |
12 | .. seealso:: |
13 | @@ -466,7 +466,7 @@ |
14 | automatically retrieves new state every time this object's attributes |
15 | are read. |
16 | |
17 | - :raises: **StateNotFound** if the object in the application under test |
18 | + :raises StateNotFound: if the object in the application under test |
19 | has been destroyed. |
20 | |
21 | """ |
22 | @@ -530,7 +530,7 @@ |
23 | |
24 | :param piece: an XPath-like query that specifies which bit of the tree |
25 | you want to look at. |
26 | - :raises: **TypeError** on invalid *piece* parameter. |
27 | + :raises TypeError: on invalid *piece* parameter. |
28 | |
29 | """ |
30 | if not isinstance(piece, six.string_types): |
31 | @@ -598,25 +598,16 @@ |
32 | (path, state_dict). |
33 | |
34 | This only works for classes that derive from DBusIntrospectionObject. |
35 | + |
36 | + :returns: A proxy object that derives from DBusIntrospectionObject |
37 | + :raises ValueError: if more than one class is appropriate for this |
38 | + dbus_tuple |
39 | + |
40 | """ |
41 | path, state = dbus_tuple |
42 | - name = get_classname_from_path(path) |
43 | - try: |
44 | - class_type = _object_registry[self._id][name] |
45 | - except KeyError: |
46 | - get_debug_logger().warning( |
47 | - "Generating introspection instance for type '%s' based on " |
48 | - "generic class.", name) |
49 | - # we want the object to inherit from the custom emulator base, not |
50 | - # the object class that is doing the selecting |
51 | - for base in type(self).__bases__: |
52 | - if issubclass(base, CustomEmulatorBase): |
53 | - base_class = base |
54 | - break |
55 | - else: |
56 | - base_class = type(self) |
57 | - class_type = type(str(name), (base_class,), {}) |
58 | - return class_type(state, path, self._backend) |
59 | + class_object = _get_proxy_object_class( |
60 | + _object_registry[self._id], type(self), path, state) |
61 | + return class_object(state, path, self._backend) |
62 | |
63 | def print_tree(self, output=None, maxdepth=None, _curdepth=0): |
64 | """Print properties of the object and its children to a stream. |
65 | @@ -678,6 +669,24 @@ |
66 | finally: |
67 | self.__refresh_on_attribute = True |
68 | |
69 | + @classmethod |
70 | + def validate_dbus_object(cls, path, _state): |
71 | + """Return whether this class is the appropriate proxy object class for |
72 | + a given dbus path and state. |
73 | + |
74 | + The default version matches the name of the dbus object and the class. |
75 | + Subclasses of CustomProxyObject can override it to define a different |
76 | + validation method. |
77 | + |
78 | + :param path: The dbus path of the object to check |
79 | + :param state: The dbus state dict of the object to check |
80 | + (ignored in default implementation) |
81 | + :returns: Whether this class is appropriate for the dbus object |
82 | + |
83 | + """ |
84 | + name = get_classname_from_path(path) |
85 | + return cls.__name__ == name |
86 | + |
87 | |
88 | def _is_valid_server_side_filter_param(key, value): |
89 | """Return True if the key and value parameters are valid for server-side |
90 | @@ -741,6 +750,72 @@ |
91 | raise ValueError("Unsupported value type: {}".format(type(value))) |
92 | |
93 | |
94 | +def _get_proxy_object_class(proxy_class_dict, default_class, path, state): |
95 | + """Return a custom proxy class, either from the list or the default. |
96 | + |
97 | + Use helper functions to check the class list or return the default. |
98 | + :param proxy_class_dict: dict of proxy classes to try |
99 | + :param default_class: default class to use if nothing in dict matches |
100 | + :param path: dbus path |
101 | + :param state: dbus state |
102 | + :returns: appropriate custom proxy class |
103 | + :raises ValueError: if more than one class in the dict matches |
104 | + |
105 | + """ |
106 | + class_type = _try_custom_proxy_classes(proxy_class_dict, path, state) |
107 | + if class_type: |
108 | + return class_type |
109 | + return _get_default_proxy_class(default_class, |
110 | + get_classname_from_path(path)) |
111 | + |
112 | + |
113 | +def _try_custom_proxy_classes(proxy_class_dict, path, state): |
114 | + """Identify which custom proxy class matches the dbus path and state. |
115 | + |
116 | + If more than one class in proxy_class_dict matches, raise an exception. |
117 | + :param proxy_class_dict: dict of proxy classes to try |
118 | + :param path: dbus path |
119 | + :param state: dbus state dict |
120 | + :returns: matching custom proxy class |
121 | + :raises ValueError: if more than one class matches |
122 | + |
123 | + """ |
124 | + possible_classes = [c for c in proxy_class_dict.values() if |
125 | + c.validate_dbus_object(path, state)] |
126 | + if len(possible_classes) > 1: |
127 | + raise ValueError( |
128 | + 'More than one custom proxy class matches this object: ' |
129 | + 'Matching classes are: %s. State is %s. Path is %s.' |
130 | + ','.join([repr(c) for c in possible_classes]), |
131 | + repr(state), |
132 | + path) |
133 | + if len(possible_classes) == 1: |
134 | + return possible_classes[0] |
135 | + return None |
136 | + |
137 | + |
138 | +def _get_default_proxy_class(default_class, name): |
139 | + """Return a custom proxy object class of the default or a base class. |
140 | + |
141 | + We want the object to inherit from CustomEmulatorBase, not the object |
142 | + class that is doing the selecting. |
143 | + :param default_class: default class to use if no bases match |
144 | + :param name: name of new class |
145 | + :returns: custom proxy object class |
146 | + |
147 | + """ |
148 | + get_debug_logger().warning( |
149 | + "Generating introspection instance for type '%s' based on generic " |
150 | + "class.", name) |
151 | + for base in default_class.__bases__: |
152 | + if issubclass(base, CustomEmulatorBase): |
153 | + base_class = base |
154 | + break |
155 | + else: |
156 | + base_class = default_class |
157 | + return type(str(name), (base_class,), {}) |
158 | + |
159 | + |
160 | class _CustomEmulatorMeta(IntrospectableObjectMetaclass): |
161 | |
162 | def __new__(cls, name, bases, d): |
163 | |
164 | === modified file 'autopilot/tests/unit/test_introspection_features.py' |
165 | --- autopilot/tests/unit/test_introspection_features.py 2014-03-11 17:36:53 +0000 |
166 | +++ autopilot/tests/unit/test_introspection_features.py 2014-03-13 21:44:45 +0000 |
167 | @@ -27,6 +27,7 @@ |
168 | from testtools import TestCase |
169 | from testtools.matchers import ( |
170 | Equals, |
171 | + IsInstance, |
172 | Not, |
173 | NotEquals, |
174 | Raises, |
175 | @@ -46,13 +47,18 @@ |
176 | _get_application_name_from_dbus_address, |
177 | _get_search_criteria_string_representation, |
178 | _maybe_filter_connections_by_app_name, |
179 | + get_classname_from_path, |
180 | get_proxy_object_for_existing_process, |
181 | ProcessSearchError, |
182 | ) |
183 | from autopilot.introspection.dbus import ( |
184 | _get_filter_string_for_key_value_pair, |
185 | + _get_default_proxy_class, |
186 | _is_valid_server_side_filter_param, |
187 | + _get_proxy_object_class, |
188 | _object_passes_filters, |
189 | + _object_registry, |
190 | + _try_custom_proxy_classes, |
191 | CustomEmulatorBase, |
192 | DBusIntrospectionObject, |
193 | ) |
194 | @@ -639,3 +645,194 @@ |
195 | ) |
196 | ) |
197 | ) |
198 | + |
199 | + |
200 | +class MakeIntrospectionObjectTests(TestCase): |
201 | + |
202 | + """Test selection of custom proxy object class.""" |
203 | + |
204 | + class DefaultSelector(CustomEmulatorBase): |
205 | + pass |
206 | + |
207 | + class AlwaysSelected(CustomEmulatorBase): |
208 | + @classmethod |
209 | + def validate_dbus_object(cls, path, state): |
210 | + """Validate always. |
211 | + |
212 | + :returns: True |
213 | + |
214 | + """ |
215 | + return True |
216 | + |
217 | + class NeverSelected(CustomEmulatorBase): |
218 | + @classmethod |
219 | + def validate_dbus_object(cls, path, state): |
220 | + """Validate never. |
221 | + |
222 | + :returns: False |
223 | + |
224 | + """ |
225 | + return False |
226 | + |
227 | + def test_class_has_validation_method(self): |
228 | + """Verify that a class has a validation method by default.""" |
229 | + self.assertTrue(callable(self.DefaultSelector.validate_dbus_object)) |
230 | + |
231 | + @patch('autopilot.introspection.dbus._get_proxy_object_class') |
232 | + def test_make_introspection_object(self, gpoc): |
233 | + """Verify that make_introspection_object makes the right call.""" |
234 | + gpoc.return_value = self.DefaultSelector |
235 | + fake_object = self.DefaultSelector( |
236 | + dict(id=[0, 123], path=[0, '/some/path']), |
237 | + '/', |
238 | + Mock() |
239 | + ) |
240 | + new_fake = fake_object.make_introspection_object(('/Object', {})) |
241 | + self.assertThat(new_fake, IsInstance(self.DefaultSelector)) |
242 | + gpoc.assert_called_once_with( |
243 | + _object_registry[fake_object._id], |
244 | + self.DefaultSelector, |
245 | + '/Object', |
246 | + {} |
247 | + ) |
248 | + |
249 | + @patch('autopilot.introspection.dbus._try_custom_proxy_classes') |
250 | + @patch('autopilot.introspection.dbus._get_default_proxy_class') |
251 | + def test_get_proxy_object_class_return_from_list(self, gdpc, tcpc): |
252 | + """_get_proxy_object_class should return the value of |
253 | + _try_custom_proxy_classes if there is one.""" |
254 | + token = self.getUniqueString() |
255 | + tcpc.return_value = token |
256 | + gpoc_return = _get_proxy_object_class(None, None, None, None) |
257 | + |
258 | + self.assertThat(gpoc_return, Equals(token)) |
259 | + self.assertFalse(gdpc.called) |
260 | + |
261 | + @patch('autopilot.introspection.dbus._try_custom_proxy_classes') |
262 | + def test_get_proxy_object_class_send_right_args(self, tcpc): |
263 | + """_get_proxy_object_class should send the right arguments to |
264 | + _try_custom_proxy_classes.""" |
265 | + class_dict = {'DefaultSelector': self.DefaultSelector} |
266 | + path = '/path/to/DefaultSelector' |
267 | + state = {} |
268 | + _get_proxy_object_class(class_dict, None, path, state) |
269 | + tcpc.assert_called_once_with(class_dict, path, state) |
270 | + |
271 | + @patch('autopilot.introspection.dbus._try_custom_proxy_classes') |
272 | + def test_get_proxy_object_class_not_handle_error(self, tcpc): |
273 | + """_get_proxy_object_class should not handle an exception raised by |
274 | + _try_custom_proxy_classes.""" |
275 | + tcpc.side_effect = ValueError |
276 | + self.assertThat( |
277 | + lambda: _get_proxy_object_class( |
278 | + None, |
279 | + None, |
280 | + None, |
281 | + None |
282 | + ), |
283 | + raises(ValueError)) |
284 | + |
285 | + @patch('autopilot.introspection.dbus._try_custom_proxy_classes') |
286 | + @patch('autopilot.introspection.dbus._get_default_proxy_class') |
287 | + @patch('autopilot.introspection.dbus.get_classname_from_path') |
288 | + def test_get_proxy_object_class_call_default_call(self, gcfp, gdpc, tcpc): |
289 | + """_get_proxy_object_class should call _get_default_proxy_class if |
290 | + _try_custom_proxy_classes returns None.""" |
291 | + tcpc.return_value = None |
292 | + _get_proxy_object_class(None, None, None, None) |
293 | + self.assertTrue(gdpc.called) |
294 | + |
295 | + @patch('autopilot.introspection.dbus._try_custom_proxy_classes') |
296 | + @patch('autopilot.introspection.dbus._get_default_proxy_class') |
297 | + def test_get_proxy_object_class_default_args(self, gdpc, tcpc): |
298 | + """_get_proxy_object_class should pass the correct arguments to |
299 | + _get_default_proxy_class""" |
300 | + tcpc.return_value = None |
301 | + default = self.DefaultSelector |
302 | + path = '/path/to/DefaultSelector' |
303 | + _get_proxy_object_class(None, default, path, None) |
304 | + gdpc.assert_called_once_with(default, get_classname_from_path(path)) |
305 | + |
306 | + @patch('autopilot.introspection.dbus._try_custom_proxy_classes') |
307 | + @patch('autopilot.introspection.dbus._get_default_proxy_class') |
308 | + @patch('autopilot.introspection.dbus.get_classname_from_path') |
309 | + def test_get_proxy_object_class_default(self, gcfp, gdpc, tcpc): |
310 | + """_get_proxy_object_class should return the value of |
311 | + _get_default_proxy_class if _try_custom_proxy_classes returns None.""" |
312 | + token = self.getUniqueString() |
313 | + gdpc.return_value = token |
314 | + tcpc.return_value = None |
315 | + gpoc_return = _get_proxy_object_class(None, None, None, None) |
316 | + self.assertThat(gpoc_return, Equals(token)) |
317 | + |
318 | + def test_try_custom_proxy_classes_zero_results(self): |
319 | + """_try_custom_proxy_classes must return None if no classes match.""" |
320 | + proxy_class_dict = {'NeverSelected': self.NeverSelected} |
321 | + path = '/path/to/NeverSelected' |
322 | + state = {} |
323 | + class_type = _try_custom_proxy_classes(proxy_class_dict, path, state) |
324 | + self.assertThat(class_type, Equals(None)) |
325 | + |
326 | + def test_try_custom_proxy_classes_one_result(self): |
327 | + """_try_custom_proxy_classes must return the matching class if there is |
328 | + exacly 1.""" |
329 | + proxy_class_dict = {'DefaultSelector': self.DefaultSelector} |
330 | + path = '/path/to/DefaultSelector' |
331 | + state = {} |
332 | + class_type = _try_custom_proxy_classes(proxy_class_dict, path, state) |
333 | + self.assertThat(class_type, Equals(self.DefaultSelector)) |
334 | + |
335 | + def test_try_custom_proxy_classes_two_results(self): |
336 | + """_try_custom_proxy_classes must raise ValueError if multiple classes |
337 | + match.""" |
338 | + proxy_class_dict = {'DefaultSelector': self.DefaultSelector, |
339 | + 'AlwaysSelected': self.AlwaysSelected} |
340 | + path = '/path/to/DefaultSelector' |
341 | + state = {} |
342 | + self.assertThat( |
343 | + lambda: _try_custom_proxy_classes( |
344 | + proxy_class_dict, |
345 | + path, |
346 | + state |
347 | + ), |
348 | + raises(ValueError) |
349 | + ) |
350 | + |
351 | + @patch('autopilot.introspection.dbus.get_debug_logger') |
352 | + def test_get_default_proxy_class_logging(self, gdl): |
353 | + """_get_default_proxy_class should log a message.""" |
354 | + _get_default_proxy_class(self.DefaultSelector, None) |
355 | + gdl.assert_called_once_with() |
356 | + |
357 | + def test_get_default_proxy_class_base(self): |
358 | + """Subclass must return an emulator of base class.""" |
359 | + class SubclassedProxy(self.DefaultSelector): |
360 | + pass |
361 | + |
362 | + result = _get_default_proxy_class(SubclassedProxy, 'Object') |
363 | + self.assertTrue(result, Equals(self.DefaultSelector)) |
364 | + |
365 | + def test_get_default_proxy_class_base_instead_of_self(self): |
366 | + """Subclass must not use self if base class works.""" |
367 | + class SubclassedProxy(self.DefaultSelector): |
368 | + pass |
369 | + |
370 | + result = _get_default_proxy_class(SubclassedProxy, 'Object') |
371 | + self.assertFalse(issubclass(result, SubclassedProxy)) |
372 | + |
373 | + def test_get_default_proxy_class(self): |
374 | + """Must default to own class if no usable bases present.""" |
375 | + result = _get_default_proxy_class(self.DefaultSelector, 'Object') |
376 | + self.assertTrue(result, Equals(self.DefaultSelector)) |
377 | + |
378 | + def test_get_default_proxy_name(self): |
379 | + """Must default to own class if no usable bases present.""" |
380 | + token = self.getUniqueString() |
381 | + result = _get_default_proxy_class(self.DefaultSelector, token) |
382 | + self.assertThat(result.__name__, Equals(token)) |
383 | + |
384 | + def test_validate_dbus_object_matches_on_class_name(self): |
385 | + """Validate_dbus_object must match class name.""" |
386 | + selected = self.DefaultSelector.validate_dbus_object( |
387 | + '/DefaultSelector', {}) |
388 | + self.assertTrue(selected) |
Resubmitted to get a better diff, since trunk has moved on since the MP was created.