~bzr-pqm/bzr/bzr.dev

5177.1.1 by Vincent Ladeuil
Manually assign docstrings to command objects, so that they work with python -OO
1
# Copyright (C) 2006-2010 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1185.70.5 by Martin Pool
Pass through wrapped function name and docstrign
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1185.70.5 by Martin Pool
Pass through wrapped function name and docstrign
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1185.70.5 by Martin Pool
Pass through wrapped function name and docstrign
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1185.70.5 by Martin Pool
Pass through wrapped function name and docstrign
16
17
18
"""Tests for decorator functions"""
19
2230.2.4 by John Arbash Meinel
Add tests that decorators generate useful wrappers.
20
import inspect
21
22
from bzrlib import decorators
1185.70.5 by Martin Pool
Pass through wrapped function name and docstrign
23
from bzrlib.tests import TestCase
24
25
4634.62.2 by Andrew Bennetts
Update test_decorators, add docstring.
26
class SampleUnlockError(Exception):
27
    pass
28
29
5662.1.1 by Andrew Bennetts
Preserve identity of default values in the pretty decorators.
30
def create_decorator_sample(style, unlock_error=None, meth=None):
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
31
    """Create a DecoratorSample object, using specific lock operators.
1185.70.5 by Martin Pool
Pass through wrapped function name and docstrign
32
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
33
    :param style: The type of lock decorators to use (fast/pretty/None)
4634.62.2 by Andrew Bennetts
Update test_decorators, add docstring.
34
    :param unlock_error: If specified, an error to raise from unlock.
5662.1.1 by Andrew Bennetts
Preserve identity of default values in the pretty decorators.
35
    :param meth: a function to be decorated and added as a 'meth_read' and
36
        'meth_write' to the object.
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
37
    :return: An instantiated DecoratorSample object.
1185.70.5 by Martin Pool
Pass through wrapped function name and docstrign
38
    """
39
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
40
    if style is None:
41
        # Default
42
        needs_read_lock = decorators.needs_read_lock
43
        needs_write_lock = decorators.needs_write_lock
44
    elif style == 'pretty':
45
        needs_read_lock = decorators._pretty_needs_read_lock
46
        needs_write_lock = decorators._pretty_needs_write_lock
47
    else:
48
        needs_read_lock = decorators._fast_needs_read_lock
49
        needs_write_lock = decorators._fast_needs_write_lock
50
51
    class DecoratorSample(object):
52
        """Sample class that uses decorators.
53
54
        Log when requests go through lock_read()/unlock() or
55
        lock_write()/unlock.
56
        """
57
58
        def __init__(self):
59
            self.actions = []
60
61
        def lock_read(self):
62
            self.actions.append('lock_read')
63
64
        def lock_write(self):
65
            self.actions.append('lock_write')
66
4634.62.2 by Andrew Bennetts
Update test_decorators, add docstring.
67
        @decorators.only_raises(SampleUnlockError)
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
68
        def unlock(self):
4634.62.2 by Andrew Bennetts
Update test_decorators, add docstring.
69
            if unlock_error:
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
70
                self.actions.append('unlock_fail')
4634.62.2 by Andrew Bennetts
Update test_decorators, add docstring.
71
                raise unlock_error
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
72
            else:
73
                self.actions.append('unlock')
74
75
        @needs_read_lock
76
        def frob(self):
77
            """Frob the sample object"""
78
            self.actions.append('frob')
79
            return 'newbie'
80
81
        @needs_write_lock
82
        def bank(self, bar, biz=None):
83
            """Bank the sample, but using bar and biz."""
84
            self.actions.append(('bank', bar, biz))
85
            return (bar, biz)
86
87
        @needs_read_lock
88
        def fail_during_read(self):
89
            self.actions.append('fail_during_read')
90
            raise TypeError('during read')
91
92
        @needs_write_lock
93
        def fail_during_write(self):
94
            self.actions.append('fail_during_write')
95
            raise TypeError('during write')
96
5662.1.1 by Andrew Bennetts
Preserve identity of default values in the pretty decorators.
97
        if meth is not None:
98
            meth_read = needs_read_lock(meth)
99
            meth_write = needs_write_lock(meth)
100
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
101
    return DecoratorSample()
102
103
104
class TestDecoratorActions(TestCase):
105
106
    _decorator_style = None # default
107
108
    def test_read_lock_locks_and_unlocks(self):
109
        sam = create_decorator_sample(self._decorator_style)
110
        self.assertEqual('newbie', sam.frob())
111
        self.assertEqual(['lock_read', 'frob', 'unlock'], sam.actions)
112
113
    def test_write_lock_locks_and_unlocks(self):
114
        sam = create_decorator_sample(self._decorator_style)
115
        self.assertEqual(('bar', 'bing'), sam.bank('bar', biz='bing'))
116
        self.assertEqual(['lock_write', ('bank', 'bar', 'bing'), 'unlock'],
117
                         sam.actions)
118
119
    def test_read_lock_unlocks_during_failure(self):
120
        sam = create_decorator_sample(self._decorator_style)
121
        self.assertRaises(TypeError, sam.fail_during_read)
122
        self.assertEqual(['lock_read', 'fail_during_read', 'unlock'],
123
                         sam.actions)
124
125
    def test_write_lock_unlocks_during_failure(self):
126
        sam = create_decorator_sample(self._decorator_style)
127
        self.assertRaises(TypeError, sam.fail_during_write)
128
        self.assertEqual(['lock_write', 'fail_during_write', 'unlock'],
129
                         sam.actions)
130
131
    def test_read_lock_raises_original_error(self):
132
        sam = create_decorator_sample(self._decorator_style,
4634.62.2 by Andrew Bennetts
Update test_decorators, add docstring.
133
                                      unlock_error=SampleUnlockError())
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
134
        self.assertRaises(TypeError, sam.fail_during_read)
135
        self.assertEqual(['lock_read', 'fail_during_read', 'unlock_fail'],
136
                         sam.actions)
137
138
    def test_write_lock_raises_original_error(self):
139
        sam = create_decorator_sample(self._decorator_style,
4634.62.2 by Andrew Bennetts
Update test_decorators, add docstring.
140
                                      unlock_error=SampleUnlockError())
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
141
        self.assertRaises(TypeError, sam.fail_during_write)
142
        self.assertEqual(['lock_write', 'fail_during_write', 'unlock_fail'],
143
                         sam.actions)
144
145
    def test_read_lock_raises_unlock_error(self):
146
        sam = create_decorator_sample(self._decorator_style,
4634.62.2 by Andrew Bennetts
Update test_decorators, add docstring.
147
                                      unlock_error=SampleUnlockError())
148
        self.assertRaises(SampleUnlockError, sam.frob)
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
149
        self.assertEqual(['lock_read', 'frob', 'unlock_fail'], sam.actions)
150
151
    def test_write_lock_raises_unlock_error(self):
152
        sam = create_decorator_sample(self._decorator_style,
4634.62.2 by Andrew Bennetts
Update test_decorators, add docstring.
153
                                      unlock_error=SampleUnlockError())
154
        self.assertRaises(SampleUnlockError, sam.bank, 'bar', biz='bing')
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
155
        self.assertEqual(['lock_write', ('bank', 'bar', 'bing'),
156
                          'unlock_fail'], sam.actions)
157
5662.1.1 by Andrew Bennetts
Preserve identity of default values in the pretty decorators.
158
    def test_read_lock_preserves_default_str_kwarg_identity(self):
159
        a_constant = 'A str used as a constant'
160
        def meth(self, param=a_constant):
161
            return param
162
        sam = create_decorator_sample(self._decorator_style, meth=meth)
163
        self.assertIs(a_constant, sam.meth_read())
164
165
    def test_write_lock_preserves_default_str_kwarg_identity(self):
166
        a_constant = 'A str used as a constant'
167
        def meth(self, param=a_constant):
168
            return param
169
        sam = create_decorator_sample(self._decorator_style, meth=meth)
170
        self.assertIs(a_constant, sam.meth_write())
171
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
172
173
class TestFastDecoratorActions(TestDecoratorActions):
174
175
    _decorator_style = 'fast'
176
177
178
class TestPrettyDecoratorActions(TestDecoratorActions):
179
180
    _decorator_style = 'pretty'
1185.70.5 by Martin Pool
Pass through wrapped function name and docstrign
181
182
183
class TestDecoratorDocs(TestCase):
184
    """Test method decorators"""
185
186
    def test_read_lock_passthrough(self):
187
        """@needs_read_lock exposes underlying name and doc."""
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
188
        sam = create_decorator_sample(None)
2230.2.4 by John Arbash Meinel
Add tests that decorators generate useful wrappers.
189
        self.assertEqual('frob', sam.frob.__name__)
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
190
        self.assertDocstring('Frob the sample object', sam.frob)
2230.2.4 by John Arbash Meinel
Add tests that decorators generate useful wrappers.
191
192
    def test_write_lock_passthrough(self):
193
        """@needs_write_lock exposes underlying name and doc."""
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
194
        sam = create_decorator_sample(None)
2230.2.4 by John Arbash Meinel
Add tests that decorators generate useful wrappers.
195
        self.assertEqual('bank', sam.bank.__name__)
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
196
        self.assertDocstring('Bank the sample, but using bar and biz.',
197
                             sam.bank)
2230.2.4 by John Arbash Meinel
Add tests that decorators generate useful wrappers.
198
199
    def test_argument_passthrough(self):
200
        """Test that arguments get passed around properly."""
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
201
        sam = create_decorator_sample(None)
2230.2.4 by John Arbash Meinel
Add tests that decorators generate useful wrappers.
202
        sam.bank('1', biz='2')
203
        self.assertEqual(['lock_write',
204
                          ('bank', '1', '2'),
205
                          'unlock',
206
                         ], sam.actions)
207
208
209
class TestPrettyDecorators(TestCase):
210
    """Test that pretty decorators generate nice looking wrappers."""
211
212
    def get_formatted_args(self, func):
213
        """Return a nicely formatted string for the arguments to a function.
214
215
        This generates something like "(foo, bar=None)".
216
        """
217
        return inspect.formatargspec(*inspect.getargspec(func))
218
219
    def test__pretty_needs_read_lock(self):
220
        """Test that _pretty_needs_read_lock generates a nice wrapper."""
221
222
        @decorators._pretty_needs_read_lock
223
        def my_function(foo, bar, baz=None, biz=1):
224
            """Just a function that supplies several arguments."""
225
226
        self.assertEqual('my_function', my_function.__name__)
227
        self.assertEqual('my_function_read_locked',
228
                         my_function.func_code.co_name)
229
        self.assertEqual('(foo, bar, baz=None, biz=1)',
230
                         self.get_formatted_args(my_function))
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
231
        self.assertDocstring(
232
            'Just a function that supplies several arguments.', my_function)
2230.2.4 by John Arbash Meinel
Add tests that decorators generate useful wrappers.
233
234
    def test__fast_needs_read_lock(self):
235
        """Test the output of _fast_needs_read_lock."""
236
237
        @decorators._fast_needs_read_lock
238
        def my_function(foo, bar, baz=None, biz=1):
239
            """Just a function that supplies several arguments."""
240
241
        self.assertEqual('my_function', my_function.__name__)
242
        self.assertEqual('read_locked', my_function.func_code.co_name)
243
        self.assertEqual('(self, *args, **kwargs)',
244
                         self.get_formatted_args(my_function))
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
245
        self.assertDocstring(
246
            'Just a function that supplies several arguments.', my_function)
2230.2.4 by John Arbash Meinel
Add tests that decorators generate useful wrappers.
247
248
    def test__pretty_needs_write_lock(self):
249
        """Test that _pretty_needs_write_lock generates a nice wrapper."""
250
251
        @decorators._pretty_needs_write_lock
252
        def my_function(foo, bar, baz=None, biz=1):
253
            """Just a function that supplies several arguments."""
254
255
        self.assertEqual('my_function', my_function.__name__)
256
        self.assertEqual('my_function_write_locked',
257
                         my_function.func_code.co_name)
258
        self.assertEqual('(foo, bar, baz=None, biz=1)',
259
                         self.get_formatted_args(my_function))
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
260
        self.assertDocstring(
261
            'Just a function that supplies several arguments.', my_function)
2230.2.4 by John Arbash Meinel
Add tests that decorators generate useful wrappers.
262
263
    def test__fast_needs_write_lock(self):
264
        """Test the output of _fast_needs_write_lock."""
265
266
        @decorators._fast_needs_write_lock
267
        def my_function(foo, bar, baz=None, biz=1):
268
            """Just a function that supplies several arguments."""
269
270
        self.assertEqual('my_function', my_function.__name__)
271
        self.assertEqual('write_locked', my_function.func_code.co_name)
272
        self.assertEqual('(self, *args, **kwargs)',
273
                         self.get_formatted_args(my_function))
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
274
        self.assertDocstring(
275
            'Just a function that supplies several arguments.', my_function)
2230.2.4 by John Arbash Meinel
Add tests that decorators generate useful wrappers.
276
277
    def test_use_decorators(self):
278
        """Test that you can switch the type of the decorators."""
279
        cur_read = decorators.needs_read_lock
280
        cur_write = decorators.needs_write_lock
281
        try:
282
            decorators.use_fast_decorators()
283
            self.assertIs(decorators._fast_needs_read_lock,
284
                          decorators.needs_read_lock)
285
            self.assertIs(decorators._fast_needs_write_lock,
286
                          decorators.needs_write_lock)
287
288
            decorators.use_pretty_decorators()
289
            self.assertIs(decorators._pretty_needs_read_lock,
290
                          decorators.needs_read_lock)
291
            self.assertIs(decorators._pretty_needs_write_lock,
292
                          decorators.needs_write_lock)
293
294
            # One more switch to make sure it wasn't just good luck that the
295
            # functions pointed to the correct version
296
            decorators.use_fast_decorators()
297
            self.assertIs(decorators._fast_needs_read_lock,
298
                          decorators.needs_read_lock)
299
            self.assertIs(decorators._fast_needs_write_lock,
300
                          decorators.needs_write_lock)
301
        finally:
302
            decorators.needs_read_lock = cur_read
303
            decorators.needs_write_lock = cur_write
4634.62.2 by Andrew Bennetts
Update test_decorators, add docstring.
304
305
306
class TestOnlyRaisesDecorator(TestCase):
307
308
    def raise_ZeroDivisionError(self):
309
        1/0
310
        
311
    def test_raises_approved_error(self):
312
        decorator = decorators.only_raises(ZeroDivisionError)
313
        decorated_meth = decorator(self.raise_ZeroDivisionError)
314
        self.assertRaises(ZeroDivisionError, decorated_meth)
315
316
    def test_quietly_logs_unapproved_errors(self):
317
        decorator = decorators.only_raises(IOError)
318
        decorated_meth = decorator(self.raise_ZeroDivisionError)
319
        self.assertLogsError(ZeroDivisionError, decorated_meth)
320
        
321