~bzr-pqm/bzr/bzr.dev

1185.70.5 by Martin Pool
Pass through wrapped function name and docstrign
1
# Copyright (C) 2006 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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
26
def create_decorator_sample(style, except_in_unlock=False):
27
    """Create a DecoratorSample object, using specific lock operators.
1185.70.5 by Martin Pool
Pass through wrapped function name and docstrign
28
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
29
    :param style: The type of lock decorators to use (fast/pretty/None)
30
    :param except_in_unlock: If True, raise an exception during unlock
31
    :return: An instantiated DecoratorSample object.
1185.70.5 by Martin Pool
Pass through wrapped function name and docstrign
32
    """
33
3316.3.2 by John Arbash Meinel
Finish fix for bug #125784. need_read/write_lock decorators should attempt to raise an original exception.
34
    if style is None:
35
        # Default
36
        needs_read_lock = decorators.needs_read_lock
37
        needs_write_lock = decorators.needs_write_lock
38
    elif style == 'pretty':
39
        needs_read_lock = decorators._pretty_needs_read_lock
40
        needs_write_lock = decorators._pretty_needs_write_lock
41
    else:
42
        needs_read_lock = decorators._fast_needs_read_lock
43
        needs_write_lock = decorators._fast_needs_write_lock
44
45
    class DecoratorSample(object):
46
        """Sample class that uses decorators.
47
48
        Log when requests go through lock_read()/unlock() or
49
        lock_write()/unlock.
50
        """
51
52
        def __init__(self):
53
            self.actions = []
54
55
        def lock_read(self):
56
            self.actions.append('lock_read')
57
58
        def lock_write(self):
59
            self.actions.append('lock_write')
60
61
        def unlock(self):
62
            if except_in_unlock:
63
                self.actions.append('unlock_fail')
64
                raise KeyError('during unlock')
65
            else:
66
                self.actions.append('unlock')
67
68
        @needs_read_lock
69
        def frob(self):
70
            """Frob the sample object"""
71
            self.actions.append('frob')
72
            return 'newbie'
73
74
        @needs_write_lock
75
        def bank(self, bar, biz=None):
76
            """Bank the sample, but using bar and biz."""
77
            self.actions.append(('bank', bar, biz))
78
            return (bar, biz)
79
80
        @needs_read_lock
81
        def fail_during_read(self):
82
            self.actions.append('fail_during_read')
83
            raise TypeError('during read')
84
85
        @needs_write_lock
86
        def fail_during_write(self):
87
            self.actions.append('fail_during_write')
88
            raise TypeError('during write')
89
90
    return DecoratorSample()
91
92
93
class TestDecoratorActions(TestCase):
94
95
    _decorator_style = None # default
96
97
    def test_read_lock_locks_and_unlocks(self):
98
        sam = create_decorator_sample(self._decorator_style)
99
        self.assertEqual('newbie', sam.frob())
100
        self.assertEqual(['lock_read', 'frob', 'unlock'], sam.actions)
101
102
    def test_write_lock_locks_and_unlocks(self):
103
        sam = create_decorator_sample(self._decorator_style)
104
        self.assertEqual(('bar', 'bing'), sam.bank('bar', biz='bing'))
105
        self.assertEqual(['lock_write', ('bank', 'bar', 'bing'), 'unlock'],
106
                         sam.actions)
107
108
    def test_read_lock_unlocks_during_failure(self):
109
        sam = create_decorator_sample(self._decorator_style)
110
        self.assertRaises(TypeError, sam.fail_during_read)
111
        self.assertEqual(['lock_read', 'fail_during_read', 'unlock'],
112
                         sam.actions)
113
114
    def test_write_lock_unlocks_during_failure(self):
115
        sam = create_decorator_sample(self._decorator_style)
116
        self.assertRaises(TypeError, sam.fail_during_write)
117
        self.assertEqual(['lock_write', 'fail_during_write', 'unlock'],
118
                         sam.actions)
119
120
    def test_read_lock_raises_original_error(self):
121
        sam = create_decorator_sample(self._decorator_style,
122
                                      except_in_unlock=True)
123
        self.assertRaises(TypeError, sam.fail_during_read)
124
        self.assertEqual(['lock_read', 'fail_during_read', 'unlock_fail'],
125
                         sam.actions)
126
127
    def test_write_lock_raises_original_error(self):
128
        sam = create_decorator_sample(self._decorator_style,
129
                                      except_in_unlock=True)
130
        self.assertRaises(TypeError, sam.fail_during_write)
131
        self.assertEqual(['lock_write', 'fail_during_write', 'unlock_fail'],
132
                         sam.actions)
133
134
    def test_read_lock_raises_unlock_error(self):
135
        sam = create_decorator_sample(self._decorator_style,
136
                                      except_in_unlock=True)
137
        self.assertRaises(KeyError, sam.frob)
138
        self.assertEqual(['lock_read', 'frob', 'unlock_fail'], sam.actions)
139
140
    def test_write_lock_raises_unlock_error(self):
141
        sam = create_decorator_sample(self._decorator_style,
142
                                      except_in_unlock=True)
143
        self.assertRaises(KeyError, sam.bank, 'bar', biz='bing')
144
        self.assertEqual(['lock_write', ('bank', 'bar', 'bing'),
145
                          'unlock_fail'], sam.actions)
146
147
148
class TestFastDecoratorActions(TestDecoratorActions):
149
150
    _decorator_style = 'fast'
151
152
153
class TestPrettyDecoratorActions(TestDecoratorActions):
154
155
    _decorator_style = 'pretty'
1185.70.5 by Martin Pool
Pass through wrapped function name and docstrign
156
157
158
class TestDecoratorDocs(TestCase):
159
    """Test method decorators"""
160
161
    def test_read_lock_passthrough(self):
162
        """@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.
163
        sam = create_decorator_sample(None)
2230.2.4 by John Arbash Meinel
Add tests that decorators generate useful wrappers.
164
        self.assertEqual('frob', sam.frob.__name__)
165
        self.assertEqual('Frob the sample object', sam.frob.__doc__)
166
167
    def test_write_lock_passthrough(self):
168
        """@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.
169
        sam = create_decorator_sample(None)
2230.2.4 by John Arbash Meinel
Add tests that decorators generate useful wrappers.
170
        self.assertEqual('bank', sam.bank.__name__)
171
        self.assertEqual('Bank the sample, but using bar and biz.',
172
                         sam.bank.__doc__)
173
174
    def test_argument_passthrough(self):
175
        """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.
176
        sam = create_decorator_sample(None)
2230.2.4 by John Arbash Meinel
Add tests that decorators generate useful wrappers.
177
        sam.bank('1', biz='2')
178
        self.assertEqual(['lock_write',
179
                          ('bank', '1', '2'),
180
                          'unlock',
181
                         ], sam.actions)
182
183
184
class TestPrettyDecorators(TestCase):
185
    """Test that pretty decorators generate nice looking wrappers."""
186
187
    def get_formatted_args(self, func):
188
        """Return a nicely formatted string for the arguments to a function.
189
190
        This generates something like "(foo, bar=None)".
191
        """
192
        return inspect.formatargspec(*inspect.getargspec(func))
193
194
    def test__pretty_needs_read_lock(self):
195
        """Test that _pretty_needs_read_lock generates a nice wrapper."""
196
197
        @decorators._pretty_needs_read_lock
198
        def my_function(foo, bar, baz=None, biz=1):
199
            """Just a function that supplies several arguments."""
200
201
        self.assertEqual('my_function', my_function.__name__)
202
        self.assertEqual('my_function_read_locked',
203
                         my_function.func_code.co_name)
204
        self.assertEqual('(foo, bar, baz=None, biz=1)',
205
                         self.get_formatted_args(my_function))
206
        self.assertEqual('Just a function that supplies several arguments.',
207
                         inspect.getdoc(my_function))
208
209
    def test__fast_needs_read_lock(self):
210
        """Test the output of _fast_needs_read_lock."""
211
212
        @decorators._fast_needs_read_lock
213
        def my_function(foo, bar, baz=None, biz=1):
214
            """Just a function that supplies several arguments."""
215
216
        self.assertEqual('my_function', my_function.__name__)
217
        self.assertEqual('read_locked', my_function.func_code.co_name)
218
        self.assertEqual('(self, *args, **kwargs)',
219
                         self.get_formatted_args(my_function))
220
        self.assertEqual('Just a function that supplies several arguments.',
221
                         inspect.getdoc(my_function))
222
223
    def test__pretty_needs_write_lock(self):
224
        """Test that _pretty_needs_write_lock generates a nice wrapper."""
225
226
        @decorators._pretty_needs_write_lock
227
        def my_function(foo, bar, baz=None, biz=1):
228
            """Just a function that supplies several arguments."""
229
230
        self.assertEqual('my_function', my_function.__name__)
231
        self.assertEqual('my_function_write_locked',
232
                         my_function.func_code.co_name)
233
        self.assertEqual('(foo, bar, baz=None, biz=1)',
234
                         self.get_formatted_args(my_function))
235
        self.assertEqual('Just a function that supplies several arguments.',
236
                         inspect.getdoc(my_function))
237
238
    def test__fast_needs_write_lock(self):
239
        """Test the output of _fast_needs_write_lock."""
240
241
        @decorators._fast_needs_write_lock
242
        def my_function(foo, bar, baz=None, biz=1):
243
            """Just a function that supplies several arguments."""
244
245
        self.assertEqual('my_function', my_function.__name__)
246
        self.assertEqual('write_locked', my_function.func_code.co_name)
247
        self.assertEqual('(self, *args, **kwargs)',
248
                         self.get_formatted_args(my_function))
249
        self.assertEqual('Just a function that supplies several arguments.',
250
                         inspect.getdoc(my_function))
251
252
    def test_use_decorators(self):
253
        """Test that you can switch the type of the decorators."""
254
        cur_read = decorators.needs_read_lock
255
        cur_write = decorators.needs_write_lock
256
        try:
257
            decorators.use_fast_decorators()
258
            self.assertIs(decorators._fast_needs_read_lock,
259
                          decorators.needs_read_lock)
260
            self.assertIs(decorators._fast_needs_write_lock,
261
                          decorators.needs_write_lock)
262
263
            decorators.use_pretty_decorators()
264
            self.assertIs(decorators._pretty_needs_read_lock,
265
                          decorators.needs_read_lock)
266
            self.assertIs(decorators._pretty_needs_write_lock,
267
                          decorators.needs_write_lock)
268
269
            # One more switch to make sure it wasn't just good luck that the
270
            # functions pointed to the correct version
271
            decorators.use_fast_decorators()
272
            self.assertIs(decorators._fast_needs_read_lock,
273
                          decorators.needs_read_lock)
274
            self.assertIs(decorators._fast_needs_write_lock,
275
                          decorators.needs_write_lock)
276
        finally:
277
            decorators.needs_read_lock = cur_read
278
            decorators.needs_write_lock = cur_write