1
# Copyright (C) 2009, 2010 Canonical Ltd
1
# Copyright (C) 2009 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
39
38
self.call_log.append('no_op_cleanup')
41
40
def assertLogContains(self, regex):
42
self.assertContainsRe(self.get_log(), regex, re.DOTALL)
41
log = self._get_log(keep_log_file=True)
42
self.assertContainsRe(log, regex, re.DOTALL)
44
44
def failing_cleanup(self):
45
45
self.call_log.append('failing_cleanup')
116
116
self.assertLogContains('Cleanup failed:.*failing_cleanup goes boom')
119
#class TestRunCleanupReportingErrors(CleanupsTestCase):
121
# def test_cleanup_error_reported(self):
119
125
class TestDoWithCleanups(CleanupsTestCase):
121
127
def trivial_func(self):
123
129
return 'trivial result'
125
131
def test_runs_func(self):
126
"""_do_with_cleanups runs the function it is given, and returns the
132
"""do_with_cleanups runs the function it is given, and returns the
129
result = _do_with_cleanups([], self.trivial_func)
135
result = do_with_cleanups(self.trivial_func, [])
130
136
self.assertEqual('trivial result', result)
132
138
def test_runs_cleanups(self):
133
139
"""Cleanup functions are run (in the given order)."""
134
cleanup_func_1 = (self.call_log.append, ('cleanup 1',), {})
135
cleanup_func_2 = (self.call_log.append, ('cleanup 2',), {})
136
_do_with_cleanups([cleanup_func_1, cleanup_func_2], self.trivial_func)
140
cleanup_func_1 = lambda: self.call_log.append('cleanup 1')
141
cleanup_func_2 = lambda: self.call_log.append('cleanup 2')
142
do_with_cleanups(self.trivial_func, [cleanup_func_1, cleanup_func_2])
137
143
self.assertEqual(
138
144
['trivial_func', 'cleanup 1', 'cleanup 2'], self.call_log)
148
154
self.assertRaises(
149
ZeroDivisionError, _do_with_cleanups,
150
[(self.no_op_cleanup, (), {})], self.failing_func)
155
ZeroDivisionError, do_with_cleanups, self.failing_func,
156
[self.no_op_cleanup])
151
157
self.assertEqual(['failing_func', 'no_op_cleanup'], self.call_log)
153
159
def test_func_error_trumps_cleanup_error(self):
157
163
The cleanup error is be logged.
159
165
self.assertRaises(
160
ZeroDivisionError, _do_with_cleanups,
161
[(self.failing_cleanup, (), {})], self.failing_func)
166
ZeroDivisionError, do_with_cleanups, self.failing_func,
167
[self.failing_cleanup])
162
168
self.assertLogContains('Cleanup failed:.*failing_cleanup goes boom')
164
170
def test_func_passes_and_error_from_cleanup(self):
166
172
raise an error. Later cleanups are still executed.
168
174
exc = self.assertRaises(
169
Exception, _do_with_cleanups,
170
[(self.failing_cleanup, (), {}), (self.no_op_cleanup, (), {})],
175
Exception, do_with_cleanups, self.trivial_func,
176
[self.failing_cleanup, self.no_op_cleanup])
172
177
self.assertEqual('failing_cleanup goes boom!', exc.args[0])
173
178
self.assertEqual(
174
179
['trivial_func', 'failing_cleanup', 'no_op_cleanup'],
182
187
cleanups = self.make_two_failing_cleanup_funcs()
183
self.assertRaises(ErrorA, _do_with_cleanups, cleanups,
188
self.assertRaises(ErrorA, do_with_cleanups, self.trivial_func,
185
190
self.assertLogContains('Cleanup failed:.*ErrorB')
186
self.assertFalse('ErrorA' in self.get_log())
191
log = self._get_log(keep_log_file=True)
192
self.assertFalse('ErrorA' in log)
188
194
def make_two_failing_cleanup_funcs(self):
190
196
raise ErrorA('Error A')
192
198
raise ErrorB('Error B')
193
return [(raise_a, (), {}), (raise_b, (), {})]
199
return [raise_a, raise_b]
195
201
def test_multiple_cleanup_failures_debug_flag(self):
197
203
trace.push_log_file(log)
198
204
debug.debug_flags.add('cleanup')
199
205
cleanups = self.make_two_failing_cleanup_funcs()
200
self.assertRaises(ErrorA, _do_with_cleanups, cleanups,
206
self.assertRaises(ErrorA, do_with_cleanups, self.trivial_func, cleanups)
202
207
self.assertContainsRe(
203
208
log.getvalue(), "bzr: warning: Cleanup failed:.*Error B\n")
204
209
self.assertEqual(1, log.getvalue().count('bzr: warning:'),
209
214
trace.push_log_file(log)
210
215
debug.debug_flags.add('cleanup')
211
216
cleanups = self.make_two_failing_cleanup_funcs()
212
self.assertRaises(ZeroDivisionError, _do_with_cleanups, cleanups,
217
self.assertRaises(ZeroDivisionError, do_with_cleanups,
218
self.failing_func, cleanups)
214
219
self.assertContainsRe(
215
220
log.getvalue(), "bzr: warning: Cleanup failed:.*Error A\n")
216
221
self.assertContainsRe(
221
226
"""The main func may mutate the cleanups before it returns.
223
228
This allows a function to gradually add cleanups as it acquires
224
resources, rather than planning all the cleanups up-front. The
225
OperationWithCleanups helper relies on this working.
229
resources, rather than planning all the cleanups up-front.
231
# XXX: this is cute, but an object with an 'add_cleanup' method may
227
233
cleanups_list = []
228
234
def func_that_adds_cleanups():
229
235
self.call_log.append('func_that_adds_cleanups')
230
cleanups_list.append((self.no_op_cleanup, (), {}))
236
cleanups_list.append(self.no_op_cleanup)
232
result = _do_with_cleanups(cleanups_list, func_that_adds_cleanups)
238
result = do_with_cleanups(func_that_adds_cleanups, cleanups_list)
233
239
self.assertEqual('result', result)
234
240
self.assertEqual(
235
241
['func_that_adds_cleanups', 'no_op_cleanup'], self.call_log)
242
248
trace.push_log_file(log)
243
249
debug.debug_flags.add('cleanup')
244
self.assertRaises(ZeroDivisionError, _do_with_cleanups,
245
[(self.failing_cleanup, (), {})], self.failing_func)
250
self.assertRaises(ZeroDivisionError, do_with_cleanups,
251
self.failing_func, [self.failing_cleanup])
246
252
self.assertContainsRe(
248
254
"bzr: warning: Cleanup failed:.*failing_cleanup goes boom")
252
258
class ErrorA(Exception): pass
253
259
class ErrorB(Exception): pass
256
class TestOperationWithCleanups(CleanupsTestCase):
258
def test_cleanup_ordering(self):
259
"""Cleanups are added in LIFO order.
261
So cleanups added before run is called are run last, and the last
262
cleanup added during the func is run first.
266
call_log.append(('func called', foo))
267
op.add_cleanup(call_log.append, 'cleanup 2')
268
op.add_cleanup(call_log.append, 'cleanup 1')
270
owc = OperationWithCleanups(func)
271
owc.add_cleanup(call_log.append, 'cleanup 4')
272
owc.add_cleanup(call_log.append, 'cleanup 3')
273
result = owc.run('foo')
274
self.assertEqual('result', result)
276
[('func called', 'foo'), 'cleanup 1', 'cleanup 2', 'cleanup 3',
277
'cleanup 4'], call_log)