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_manage_instance(reason):
52
"""Decorator which runs a test if Marionette manages the application instance."""
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
if self.marionette.instance is None:
60
raise SkipTest(reason)
61
return test_item(self, *args, **kwargs)
62
return skip_wrapper
63
return decorator
64
65
66
def skip_if_chrome(reason):
67
"""Decorator which skips a test if chrome context is active."""
68
def decorator(test_item):
69
if not isinstance(test_item, types.FunctionType):
70
raise Exception('Decorator only supported for functions')
71
72
@functools.wraps(test_item)
73
def skip_wrapper(self, *args, **kwargs):
74
if self.marionette._send_message('getContext', key='value') == 'chrome':
75
raise SkipTest(reason)
76
return test_item(self, *args, **kwargs)
77
return skip_wrapper
78
return decorator
79
80
81
def skip_if_desktop(reason):
82
"""Decorator which skips a test if run on desktop."""
83
def decorator(test_item):
84
if not isinstance(test_item, types.FunctionType):
85
raise Exception('Decorator only supported for functions')
86
87
@functools.wraps(test_item)
88
def skip_wrapper(self, *args, **kwargs):
89
if self.marionette.session_capabilities.get('browserName') == 'firefox':
90
raise SkipTest(reason)
91
return test_item(self, *args, **kwargs)
92
return skip_wrapper
93
return decorator
94
95
96
def skip_unless_browser_pref(reason, pref, predicate=bool):
97
"""Decorator which skips a test based on the value of a browser preference.
98
99
:param reason: Message describing why the test need to be skipped.
100
:param pref: the preference name
101
:param predicate: a function that should return false to skip the test.
102
The function takes one parameter, the preference value.
103
Defaults to the python built-in bool function.
104
105
Note that the preference must exist, else a failure is raised.
106
107
Example: ::
108
109
class TestSomething(MarionetteTestCase):
110
@skip_unless_browser_pref("Sessionstore needs to be enabled for crashes",
111
"browser.sessionstore.resume_from_crash",
112
lambda value: value is True,
113
)
114
def test_foo(self):
115
pass # test implementation here
116
117
"""
118
def decorator(test_item):
119
if not isinstance(test_item, types.FunctionType):
120
raise Exception('Decorator only supported for functions')
121
if not callable(predicate):
122
raise ValueError('predicate must be callable')
123
124
@functools.wraps(test_item)
125
def skip_wrapper(self, *args, **kwargs):
126
value = self.marionette.get_pref(pref)
127
if value is None:
128
self.fail("No such browser preference: {0!r}".format(pref))
129
if not predicate(value):
130
raise SkipTest(reason)
131
return test_item(self, *args, **kwargs)
132
return skip_wrapper
133
return decorator
134
135
136
def skip_unless_protocol(reason, predicate):
137
"""Decorator which skips a test if the predicate does not match the current protocol level."""
138
def decorator(test_item):
139
if not isinstance(test_item, types.FunctionType):
140
raise Exception('Decorator only supported for functions')
141
if not callable(predicate):
142
raise ValueError('predicate must be callable')
143
144
@functools.wraps(test_item)
145
def skip_wrapper(self, *args, **kwargs):
146
level = self.marionette.client.protocol
147
if not predicate(level):
148
raise SkipTest(reason)
149
return test_item(self, *args, **kwargs)
150
return skip_wrapper
151
return decorator
152
153
154
def with_parameters(parameters):
155
"""Decorator which generates methods given a base method and some data.
156
157
Acts like :func:`parameterized`, but define all methods in one call.
158
159
Example::
160
161
# This example will generate two methods:
162
#
163
# - MyTestCase.test_it_1
164
# - MyTestCase.test_it_2
165
#
166
167
DATA = [("1", [5], {'named':'name'}), ("2", [6], {'named':'name2'})]
168
169
class MyTestCase(MarionetteTestCase):
170
@with_parameters(DATA)
171
def test_it(self, value, named=None):
172
print value, named
173
174
:param parameters: list of tuples (**func_suffix**, **args**, **kwargs**)
175
defining parameters like in :func:`todo`.
176
"""
177
def wrapped(func):
178
func.metaparameters = parameters
179
return func
180
return wrapped