Source code

Revision control

Other Tools

1
# This Source Code Form is subject to the terms of the Mozilla Public
2
# License, v. 2.0. If a copy of the MPL was not distributed with this
3
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5
from __future__ import absolute_import
6
7
import functools
8
import types
9
10
from unittest.case import (
11
SkipTest,
12
)
13
14
15
def parameterized(func_suffix, *args, **kwargs):
16
r"""Decorator which generates methods given a base method and some data.
17
18
**func_suffix** is used as a suffix for the new created method and must be
19
unique given a base method. if **func_suffix** countains characters that
20
are not allowed in normal python function name, these characters will be
21
replaced with "_".
22
23
This decorator can be used more than once on a single base method. The class
24
must have a metaclass of :class:`MetaParameterized`.
25
26
Example::
27
28
# This example will generate two methods:
29
#
30
# - MyTestCase.test_it_1
31
# - MyTestCase.test_it_2
32
#
33
class MyTestCase(MarionetteTestCase):
34
@parameterized("1", 5, named='name')
35
@parameterized("2", 6, named='name2')
36
def test_it(self, value, named=None):
37
print value, named
38
39
:param func_suffix: will be used as a suffix for the new method
40
:param \*args: arguments to pass to the new method
41
:param \*\*kwargs: named arguments to pass to the new method
42
"""
43
def wrapped(func):
44
if not hasattr(func, 'metaparameters'):
45
func.metaparameters = []
46
func.metaparameters.append((func_suffix, args, kwargs))
47
return func
48
return wrapped
49
50
51
def run_if_e10s(reason):
52
"""Decorator which runs a test if e10s mode is active."""
53
def decorator(test_item):
54
if not isinstance(test_item, types.FunctionType):
55
raise Exception('Decorator only supported for functions')
56
57
@functools.wraps(test_item)
58
def skip_wrapper(self, *args, **kwargs):
59
with self.marionette.using_context('chrome'):
60
multi_process_browser = not self.marionette.execute_script("""
61
try {
62
return Services.appinfo.browserTabsRemoteAutostart;
63
} catch (e) {
64
return false;
65
}
66
""")
67
if multi_process_browser:
68
raise SkipTest(reason)
69
return test_item(self, *args, **kwargs)
70
return skip_wrapper
71
return decorator
72
73
74
def run_if_manage_instance(reason):
75
"""Decorator which runs a test if Marionette manages the application instance."""
76
def decorator(test_item):
77
if not isinstance(test_item, types.FunctionType):
78
raise Exception('Decorator only supported for functions')
79
80
@functools.wraps(test_item)
81
def skip_wrapper(self, *args, **kwargs):
82
if self.marionette.instance is None:
83
raise SkipTest(reason)
84
return test_item(self, *args, **kwargs)
85
return skip_wrapper
86
return decorator
87
88
89
def skip_if_chrome(reason):
90
"""Decorator which skips a test if chrome context is active."""
91
def decorator(test_item):
92
if not isinstance(test_item, types.FunctionType):
93
raise Exception('Decorator only supported for functions')
94
95
@functools.wraps(test_item)
96
def skip_wrapper(self, *args, **kwargs):
97
if self.marionette._send_message('getContext', key='value') == 'chrome':
98
raise SkipTest(reason)
99
return test_item(self, *args, **kwargs)
100
return skip_wrapper
101
return decorator
102
103
104
def skip_if_desktop(reason):
105
"""Decorator which skips a test if run on desktop."""
106
def decorator(test_item):
107
if not isinstance(test_item, types.FunctionType):
108
raise Exception('Decorator only supported for functions')
109
110
@functools.wraps(test_item)
111
def skip_wrapper(self, *args, **kwargs):
112
if self.marionette.session_capabilities.get('browserName') == 'firefox':
113
raise SkipTest(reason)
114
return test_item(self, *args, **kwargs)
115
return skip_wrapper
116
return decorator
117
118
119
def skip_if_e10s(reason):
120
"""Decorator which skips a test if e10s mode is active."""
121
def decorator(test_item):
122
if not isinstance(test_item, types.FunctionType):
123
raise Exception('Decorator only supported for functions')
124
125
@functools.wraps(test_item)
126
def skip_wrapper(self, *args, **kwargs):
127
with self.marionette.using_context('chrome'):
128
multi_process_browser = self.marionette.execute_script("""
129
try {
130
return Services.appinfo.browserTabsRemoteAutostart;
131
} catch (e) {
132
return false;
133
}
134
""")
135
if multi_process_browser:
136
raise SkipTest(reason)
137
return test_item(self, *args, **kwargs)
138
return skip_wrapper
139
return decorator
140
141
142
def skip_unless_browser_pref(reason, pref, predicate=bool):
143
"""Decorator which skips a test based on the value of a browser preference.
144
145
:param reason: Message describing why the test need to be skipped.
146
:param pref: the preference name
147
:param predicate: a function that should return false to skip the test.
148
The function takes one parameter, the preference value.
149
Defaults to the python built-in bool function.
150
151
Note that the preference must exist, else a failure is raised.
152
153
Example: ::
154
155
class TestSomething(MarionetteTestCase):
156
@skip_unless_browser_pref("Sessionstore needs to be enabled for crashes",
157
"browser.sessionstore.resume_from_crash",
158
lambda value: value is True,
159
)
160
def test_foo(self):
161
pass # test implementation here
162
163
"""
164
def decorator(test_item):
165
if not isinstance(test_item, types.FunctionType):
166
raise Exception('Decorator only supported for functions')
167
if not callable(predicate):
168
raise ValueError('predicate must be callable')
169
170
@functools.wraps(test_item)
171
def skip_wrapper(self, *args, **kwargs):
172
value = self.marionette.get_pref(pref)
173
if value is None:
174
self.fail("No such browser preference: {0!r}".format(pref))
175
if not predicate(value):
176
raise SkipTest(reason)
177
return test_item(self, *args, **kwargs)
178
return skip_wrapper
179
return decorator
180
181
182
def skip_unless_protocol(reason, predicate):
183
"""Decorator which skips a test if the predicate does not match the current protocol level."""
184
def decorator(test_item):
185
if not isinstance(test_item, types.FunctionType):
186
raise Exception('Decorator only supported for functions')
187
if not callable(predicate):
188
raise ValueError('predicate must be callable')
189
190
@functools.wraps(test_item)
191
def skip_wrapper(self, *args, **kwargs):
192
level = self.marionette.client.protocol
193
if not predicate(level):
194
raise SkipTest(reason)
195
return test_item(self, *args, **kwargs)
196
return skip_wrapper
197
return decorator
198
199
200
def with_parameters(parameters):
201
"""Decorator which generates methods given a base method and some data.
202
203
Acts like :func:`parameterized`, but define all methods in one call.
204
205
Example::
206
207
# This example will generate two methods:
208
#
209
# - MyTestCase.test_it_1
210
# - MyTestCase.test_it_2
211
#
212
213
DATA = [("1", [5], {'named':'name'}), ("2", [6], {'named':'name2'})]
214
215
class MyTestCase(MarionetteTestCase):
216
@with_parameters(DATA)
217
def test_it(self, value, named=None):
218
print value, named
219
220
:param parameters: list of tuples (**func_suffix**, **args**, **kwargs**)
221
defining parameters like in :func:`todo`.
222
"""
223
def wrapped(func):
224
func.metaparameters = parameters
225
return func
226
return wrapped