/usr/lib/python2.7/dist-packages/autopilot/matchers/__init__.py is in python-autopilot 1.4.1+15.10.20150911-0ubuntu2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
#
# Autopilot Functional Test Tool
# Copyright (C) 2012-2013 Canonical
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""Autopilot-specific testtools matchers."""
from __future__ import absolute_import
from functools import partial
from testtools.matchers import Matcher, Mismatch
from autopilot.utilities import sleep
class Eventually(Matcher):
"""Asserts that a value will eventually equal a given Matcher object.
This matcher wraps another testtools matcher object. It makes that other
matcher work with a timeout. This is necessary for several reasons:
1. Since most actions in a GUI applicaton take some time to complete, the
test may need to wait for the application to enter the expected state.
2. Since the test is running in a separate process to the application under
test, test authors cannot make any assumptions about when the
application under test will recieve CPU time to update to the expected
state.
There are two main ways of using the Eventually matcher:
**Attributes from the application**::
self.assertThat(window.maximized, Eventually(Equals(True)))
Here, ``window`` is an object generated by autopilot from the applications
state. This pattern of usage will cover 90% (or more) of the assertions in
an autopilot test. Note that any matcher can be used - either from
testtools or any custom matcher that implements the matcher API::
self.assertThat(window.height, Eventually(GreaterThan(200)))
**Callable Objects**::
self.assertThat(
autopilot.platform.model, Eventually(Equals("Galaxy Nexus")))
In this example we're using the :func:`autopilot.platform.model` function
as a callable. In this form, Eventually matches against the return value
of the callable.
This can also be used to use a regular python property inside an Eventually
matcher::
self.assertThat(lambda: self.mouse.x, Eventually(LessThan(10)))
.. note:: Using this form generally makes your tests less readable, and
should be used with great care. It also relies the test author to have
knowledge about the implementation of the object being matched against.
In this example, if ``self.mouse.x`` were ever to change to be a
regular python attribute, this test would likely break.
**Timeout**
By default timeout period is ten seconds. This can be altered by passing
the timeout keyword::
self.assertThat(foo.bar, Eventually(Equals(123), timeout=30))
.. warning:: The Eventually matcher does not work with any other matcher
that expects a callable argument (such as testtools' 'Raises' matcher)
"""
def __init__(self, matcher, **kwargs):
super(Eventually, self).__init__()
self.timeout = kwargs.pop('timeout', 10)
if kwargs:
raise ValueError(
"Unknown keyword arguments: %s" % ', '.join(kwargs.keys()))
match_fun = getattr(matcher, 'match', None)
if match_fun is None or not callable(match_fun):
raise TypeError(
"Eventually must be called with a testtools matcher argument.")
self.matcher = matcher
def match(self, value):
if callable(value):
wait_fun = partial(_callable_wait_for, value)
else:
wait_fun = getattr(value, 'wait_for', None)
if wait_fun is None or not callable(wait_fun):
raise TypeError(
"Eventually is only usable with attributes that have a "
"wait_for function or callable objects.")
try:
wait_fun(self.matcher, self.timeout)
except AssertionError as e:
return Mismatch(str(e))
return None
def __str__(self):
return "Eventually " + str(self.matcher)
def _callable_wait_for(refresh_fn, matcher, timeout):
"""Like the patched :meth:`wait_for method`, but for callable objects
instead of patched variables.
"""
time_left = timeout
while True:
new_value = refresh_fn()
mismatch = matcher.match(new_value)
if mismatch:
failure_msg = mismatch.describe()
else:
return
if time_left >= 1:
sleep(1)
time_left -= 1
else:
sleep(time_left)
break
# can't give a very descriptive message here, especially as refresh_fn
# is likely to be a lambda.
raise AssertionError(
"After %.1f seconds test failed: %s" % (timeout, failure_msg))
|