1from testfixtures import (
2    Replacer,
3    Replace,
4    ShouldRaise,
5    TempDirectory,
6    replace,
7    compare,
8    not_there,
9    )
10from unittest import TestCase
11
12import os
13
14from testfixtures.tests import sample1
15from testfixtures.tests import sample2
16from ..compat import PY3
17
18from warnings import catch_warnings
19
20
21class TestReplace(TestCase):
22
23    def test_function(self):
24
25        def test_z():
26            return 'replacement z'
27
28        compare(sample1.z(), 'original z')
29
30        @replace('testfixtures.tests.sample1.z', test_z)
31        def test_something():
32            compare(sample1.z(), 'replacement z')
33
34        compare(sample1.z(), 'original z')
35
36        test_something()
37
38        compare(sample1.z(), 'original z')
39
40    def test_class(self):
41
42        OriginalX = sample1.X
43
44        class ReplacementX(sample1.X):
45            pass
46
47        self.failIf(OriginalX is ReplacementX)
48        self.failUnless(isinstance(sample1.X(), OriginalX))
49
50        @replace('testfixtures.tests.sample1.X', ReplacementX)
51        def test_something():
52            self.failIf(OriginalX is ReplacementX)
53            self.failUnless(isinstance(sample1.X(), ReplacementX))
54
55        self.failIf(OriginalX is ReplacementX)
56        self.failUnless(isinstance(sample1.X(), OriginalX))
57
58        test_something()
59
60        self.failIf(OriginalX is ReplacementX)
61        self.failUnless(isinstance(sample1.X(), OriginalX))
62
63    def test_method(self):
64
65        def test_y(self):
66            return self
67
68        compare(sample1.X().y(), 'original y')
69
70        @replace('testfixtures.tests.sample1.X.y', test_y)
71        def test_something():
72            self.failUnless(isinstance(sample1.X().y(), sample1.X))
73
74        compare(sample1.X().y(), 'original y')
75
76        test_something()
77
78        compare(sample1.X().y(), 'original y')
79
80    def test_class_method(self):
81
82        def rMethod(cls):
83            return (cls, 1)
84
85        compare(sample1.X().aMethod(), sample1.X)
86
87        @replace('testfixtures.tests.sample1.X.aMethod', rMethod)
88        def test_something(r):
89            compare(r, rMethod)
90            compare(sample1.X().aMethod(), (sample1.X, 1))
91
92        compare(sample1.X().aMethod(), sample1.X)
93
94        test_something()
95
96        compare(sample1.X().aMethod(), sample1.X)
97
98    def test_multiple_replace(self):
99
100        def test_y(self):
101            return 'test y'
102
103        def test_z():
104            return 'test z'
105
106        compare(sample1.z(), 'original z')
107        compare(sample1.X().y(), 'original y')
108
109        @replace('testfixtures.tests.sample1.z', test_z)
110        @replace('testfixtures.tests.sample1.X.y', test_y)
111        def test_something(passed_test_y, passed_test_z):
112            compare(test_z, passed_test_z)
113            compare(test_y, passed_test_y)
114            compare(sample1.z(), 'test z')
115            compare(sample1.X().y(), 'test y')
116
117        compare(sample1.z(), 'original z')
118        compare(sample1.X().y(), 'original y')
119
120        test_something()
121
122        compare(sample1.z(), 'original z')
123        compare(sample1.X().y(), 'original y')
124
125    def test_gotcha(self):
126        # Just because you replace an object in one context,
127        # doesn't meant that it's replaced in all contexts!
128
129        def test_z():
130            return 'test z'
131
132        compare(sample1.z(), 'original z')
133        compare(sample2.z(), 'original z')
134
135        @replace('testfixtures.tests.sample1.z', test_z)
136        def test_something():
137            compare(sample1.z(), 'test z')
138            compare(sample2.z(), 'original z')
139
140        compare(sample1.z(), 'original z')
141        compare(sample2.z(), 'original z')
142
143        test_something()
144
145        compare(sample1.z(), 'original z')
146        compare(sample2.z(), 'original z')
147
148    def test_raises(self):
149
150        def test_z():
151            return 'replacement z'
152
153        compare(sample1.z(), 'original z')
154
155        @replace('testfixtures.tests.sample1.z', test_z)
156        def test_something():
157            compare(sample1.z(), 'replacement z')
158            raise Exception()
159
160        compare(sample1.z(), 'original z')
161
162        with ShouldRaise():
163            test_something()
164
165        compare(sample1.z(), 'original z')
166
167    def test_want_replacement(self):
168
169        o = object()
170
171        @replace('testfixtures.tests.sample1.z', o)
172        def test_something(r):
173            self.failUnless(r is o)
174            self.failUnless(sample1.z is o)
175
176        test_something()
177
178    def test_not_there(self):
179
180        o = object()
181
182        @replace('testfixtures.tests.sample1.bad', o)
183        def test_something(r):
184            pass  # pragma: no cover
185
186        with ShouldRaise(AttributeError("Original 'bad' not found")):
187            test_something()
188
189    def test_not_there_ok(self):
190
191        o = object()
192
193        @replace('testfixtures.tests.sample1.bad', o, strict=False)
194        def test_something(r):
195            self.failUnless(r is o)
196            self.failUnless(sample1.bad is o)
197
198        test_something()
199
200    def test_replace_dict(self):
201
202        from testfixtures.tests.sample1 import someDict
203
204        original = someDict['key']
205        replacement = object()
206
207        @replace('testfixtures.tests.sample1.someDict.key', replacement)
208        def test_something(obj):
209            self.failUnless(obj is replacement)
210            self.failUnless(someDict['key'] is replacement)
211
212        test_something()
213
214        self.failUnless(someDict['key'] is original)
215
216    def test_replace_delattr(self):
217
218        from testfixtures.tests import sample1
219
220        @replace('testfixtures.tests.sample1.someDict', not_there)
221        def test_something(obj):
222            self.failIf(hasattr(sample1, 'someDict'))
223
224        test_something()
225
226        self.assertEqual(sample1.someDict,
227                         {'complex_key': [1, 2, 3], 'key': 'value'})
228
229    def test_replace_delattr_not_there(self):
230
231        @replace('testfixtures.tests.sample1.foo', not_there)
232        def test_something(obj):
233            pass  # pragma: no cover
234
235        with ShouldRaise(AttributeError("Original 'foo' not found")):
236            test_something()
237
238    def test_replace_delattr_not_there_not_strict(self):
239
240        from testfixtures.tests import sample1
241
242        @replace('testfixtures.tests.sample1.foo',
243                 not_there, strict=False)
244        def test_something(obj):
245            self.failIf(hasattr(sample1, 'foo'))
246
247        test_something()
248
249    def test_replace_delattr_not_there_restored(self):
250
251        from testfixtures.tests import sample1
252
253        @replace('testfixtures.tests.sample1.foo',
254                 not_there, strict=False)
255        def test_something(obj):
256            sample1.foo = 'bar'
257
258        test_something()
259        self.failIf(hasattr(sample1, 'foo'))
260
261    def test_replace_delattr_cant_remove(self):
262        with Replacer() as r:
263            with ShouldRaise(TypeError(
264                "can't set attributes of "
265                "built-in/extension type 'datetime.datetime'"
266                    )):
267                r.replace('datetime.datetime.today', not_there)
268
269    def test_replace_delattr_cant_remove_not_strict(self):
270        with Replacer() as r:
271            with ShouldRaise(TypeError(
272                "can't set attributes of "
273                "built-in/extension type 'datetime.datetime'"
274                    )):
275                r.replace('datetime.datetime.today', not_there, strict=False)
276
277    def test_replace_dict_remove_key(self):
278
279        from testfixtures.tests.sample1 import someDict
280
281        @replace('testfixtures.tests.sample1.someDict.key', not_there)
282        def test_something(obj):
283            self.failIf('key' in someDict)
284
285        test_something()
286
287        self.assertEqual(sorted(someDict.keys()), ['complex_key', 'key'])
288
289    def test_replace_dict_remove_key_not_there(self):
290
291        from testfixtures.tests.sample1 import someDict
292
293        @replace('testfixtures.tests.sample1.someDict.badkey', not_there)
294        def test_something(obj):
295            self.failIf('badkey' in someDict)  # pragma: no cover
296
297        with ShouldRaise(AttributeError("Original 'badkey' not found")):
298            test_something()
299
300        self.assertEqual(sorted(someDict.keys()), ['complex_key', 'key'])
301
302    def test_replace_dict_remove_key_not_there_not_strict(self):
303
304        from testfixtures.tests.sample1 import someDict
305
306        @replace('testfixtures.tests.sample1.someDict.badkey',
307                 not_there, strict=False)
308        def test_something(obj):
309            self.failIf('badkey' in someDict)
310
311        test_something()
312
313        self.assertEqual(sorted(someDict.keys()), ['complex_key', 'key'])
314
315    def test_replace_dict_ensure_key_not_there_restored(self):
316
317        from testfixtures.tests.sample1 import someDict
318
319        @replace('testfixtures.tests.sample1.someDict.badkey',
320                 not_there, strict=False)
321        def test_something(obj):
322            someDict['badkey'] = 'some test value'
323
324        test_something()
325
326        self.assertEqual(sorted(someDict.keys()), ['complex_key', 'key'])
327
328    def test_replace_dict_not_there(self):
329
330        from testfixtures.tests.sample1 import someDict
331
332        replacement = object()
333
334        @replace('testfixtures.tests.sample1.someDict.key2',
335                 replacement,
336                 strict=False)
337        def test_something(obj):
338            self.failUnless(obj is replacement)
339            self.failUnless(someDict['key2'] is replacement)
340
341        test_something()
342
343        self.assertEqual(sorted(someDict.keys()), ['complex_key', 'key'])
344
345    def test_replace_dict_not_there_empty_string(self):
346
347        from testfixtures.tests.sample1 import someDict
348
349        @replace('testfixtures.tests.sample1.someDict.key2', '', strict=False)
350        def test_something():
351            self.assertEqual(someDict['key2'], '')
352
353        test_something()
354
355        self.assertEqual(sorted(someDict.keys()), ['complex_key', 'key'])
356
357    def test_replace_complex(self):
358
359        from testfixtures.tests.sample1 import someDict
360
361        original = someDict['complex_key'][1]
362        replacement = object()
363
364        @replace('testfixtures.tests.sample1.someDict.complex_key.1',
365                 replacement)
366        def test_something(obj):
367            self.failUnless(obj is replacement)
368            self.assertEqual(someDict['complex_key'], [1, obj, 3])
369
370        test_something()
371
372        self.assertEqual(someDict['complex_key'], [1, 2, 3])
373
374        self.failUnless(original is someDict['complex_key'][1])
375
376    def test_replacer_del(self):
377        r = Replacer()
378        r.replace('testfixtures.tests.sample1.left_behind',
379                  object(), strict=False)
380        with catch_warnings(record=True) as w:
381            del r
382            self.assertTrue(len(w), 1)
383            compare(str(w[0].message),
384                    "Replacer deleted without being restored, originals left:"
385                    " {'testfixtures.tests.sample1.left_behind': <not_there>}")
386
387    def test_multiple_replaces(self):
388        orig = os.path.sep
389        with Replacer() as r:
390            r.replace('os.path.sep', '$')
391            compare(os.path.sep, '$')
392            r.replace('os.path.sep', '=')
393            compare(os.path.sep, '=')
394        compare(orig, os.path.sep)
395
396    def test_sub_module_import(self):
397        with TempDirectory() as dir:
398            dir.write('module/__init__.py', b'')
399            dir.write('module/submodule.py', b'def foo(): return "foo"')
400
401            with Replacer() as r:
402                r.replace('sys.path', [dir.path])
403
404                def bar():
405                    return "bar"
406                # now test
407
408                r.replace('module.submodule.foo', bar)
409
410                from module.submodule import foo
411                compare(foo(), "bar")
412
413    def test_staticmethod(self):
414        compare(sample1.X.bMethod(), 2)
415        with Replacer() as r:
416            r.replace('testfixtures.tests.sample1.X.bMethod', lambda: 1)
417            compare(sample1.X.bMethod(), 1)
418        compare(sample1.X.bMethod(), 2)
419
420    def test_use_as_cleanup(self):
421        def test_z():
422            return 'replacement z'
423
424        compare(sample1.z(), 'original z')
425        replace = Replacer()
426        compare(sample1.z(), 'original z')
427        replace('testfixtures.tests.sample1.z', test_z)
428        cleanup = replace.restore
429        try:
430            compare(sample1.z(), 'replacement z')
431        finally:
432            cleanup()
433        compare(sample1.z(), 'original z')
434
435    def test_replace_context_manager(self):
436        def test_z():
437            return 'replacement z'
438
439        compare(sample1.z(), 'original z')
440
441        with Replace('testfixtures.tests.sample1.z', test_z) as z:
442            compare(z(), 'replacement z')
443            compare(sample1.z(), 'replacement z')
444
445        compare(sample1.z(), 'original z')
446
447    def test_multiple_context_managers(self):
448
449        def test_y(self):
450            return 'test y'
451
452        def test_z():
453            return 'test z'
454
455        compare(sample1.z(), 'original z')
456        compare(sample1.X().y(), 'original y')
457
458        with Replacer() as replace:
459            z = replace('testfixtures.tests.sample1.z', test_z)
460            y = replace('testfixtures.tests.sample1.X.y', test_y)
461            compare(z(), 'test z')
462            if PY3:
463                compare(y, sample1.X.y)
464            compare(sample1.X().y(), 'test y')
465            compare(sample1.z(), 'test z')
466            compare(sample1.X().y(), 'test y')
467
468        compare(sample1.z(), 'original z')
469        compare(sample1.X().y(), 'original y')
470
471    def test_context_manager_not_strict(self):
472        def test_z():
473            return 'replacement z'
474
475        with Replace('testfixtures.tests.sample1.foo', test_z, strict=False):
476            compare(sample1.foo(), 'replacement z')
477