Completed
Push — master ( 4e3b27...2dc96c )
by Ionel Cristian
56s
created

tests.test_predicate_str_repr()   D

Complexity

Conditions 17

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 21
rs 4.2367
cc 17

How to fix   Complexity   

Complexity

Complex classes like tests.test_predicate_str_repr() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
from __future__ import print_function
2
3
import inspect
4
import os
5
import subprocess
6
import sys
7
8
try:
9
    from StringIO import StringIO
10
except ImportError:
11
    from io import StringIO
12
try:
13
    from itertools import izip_longest
14
except ImportError:
15
    from itertools import zip_longest as izip_longest
16
17
import pytest
18
19
import hunter
20
from hunter import And
21
from hunter import Not
22
from hunter import Or
23
from hunter import Q
24
from hunter import Query
25
from hunter import stop
26
from hunter import trace
27
from hunter import When
28
29
from hunter import CodePrinter
30
from hunter import Debugger
31
from hunter import VarsPrinter
32
33
pytest_plugins = 'pytester',
34
35
36
@pytest.yield_fixture(autouse=True, scope="function")
37
def auto_stop():
38
    try:
39
        yield
40
    finally:
41
        stop()
42
43
44
def _get_func_spec(func):
45
    spec = inspect.getargspec(func)
46
    return inspect.formatargspec(spec.args, spec.varargs)
47
48
49
def test_pth_activation():
50
    module_name = os.path.__name__
51
    expected_module = "{0}.py".format(module_name)
52
    hunter_env = "module={!r},function=\"join\"".format(module_name)
53
    func_spec = _get_func_spec(os.path.join)
54
    expected_call = "call      def join{0}:".format(func_spec)
55
56
    output = subprocess.check_output(
57
        [sys.executable, os.path.join(os.path.dirname(__file__), 'sample.py')],
58
        env=dict(os.environ, PYTHONHUNTER=hunter_env),
59
        stderr=subprocess.STDOUT,
60
    )
61
    assert expected_module.encode() in output
62
    assert expected_call.encode() in output
63
64
65
def test_pth_sample4():
66
    env = dict(os.environ, PYTHONHUNTER="CodePrinter")
67
    env.pop('COVERAGE_PROCESS_START', None)
68
    env.pop('COV_CORE_SOURCE', None)
69
    output = subprocess.check_output(
70
        [sys.executable, os.path.join(os.path.dirname(__file__), 'sample4.py')],
71
        env=env,
72
        stderr=subprocess.STDOUT,
73
    )
74
    assert output
75
76
77
def test_pth_sample2(LineMatcher):
78
    env = dict(os.environ, PYTHONHUNTER="module='__main__'")
79
    env.pop('COVERAGE_PROCESS_START', None)
80
    env.pop('COV_CORE_SOURCE', None)
81
    output = subprocess.check_output(
82
        [sys.executable, os.path.join(os.path.dirname(__file__), 'sample2.py')],
83
        env=env,
84
        stderr=subprocess.STDOUT,
85
    )
86
    lm = LineMatcher(output.decode('utf-8').splitlines())
87
    lm.fnmatch_lines([
88
        '*tests*sample2.py:* call      if __name__ == "__main__":  #*',
89
        '*tests*sample2.py:* line      if __name__ == "__main__":  #*',
90
        '*tests*sample2.py:* line          import functools',
91
        '*tests*sample2.py:* line          def deco(opt):',
92
        '*tests*sample2.py:* line          @deco(1)',
93
        '*tests*sample2.py:* call          def deco(opt):',
94
        '*tests*sample2.py:* line              def decorator(func):',
95
        '*tests*sample2.py:* line              return decorator',
96
        '*tests*sample2.py:* return            return decorator',
97
        '*                 * ...       return value: <function deco*',
98
        '*tests*sample2.py:* line          @deco(2)',
99
        '*tests*sample2.py:* call          def deco(opt):',
100
        '*tests*sample2.py:* line              def decorator(func):',
101
        '*tests*sample2.py:* line              return decorator',
102
        '*tests*sample2.py:* return            return decorator',
103
        '*                 * ...       return value: <function deco*',
104
        '*tests*sample2.py:* line          @deco(3)',
105
        '*tests*sample2.py:* call          def deco(opt):',
106
        '*tests*sample2.py:* line              def decorator(func):',
107
        '*tests*sample2.py:* line              return decorator',
108
        '*tests*sample2.py:* return            return decorator',
109
        '*                 * ...       return value: <function deco*',
110
        '*tests*sample2.py:* call              def decorator(func):',
111
        '*tests*sample2.py:* line                  @functools.wraps(func)',
112
        '*tests*sample2.py:* line                  return wrapper',
113
        '*tests*sample2.py:* return                return wrapper',
114
        '*                 * ...       return value: <function foo *',
115
        '*tests*sample2.py:* call              def decorator(func):',
116
        '*tests*sample2.py:* line                  @functools.wraps(func)',
117
        '*tests*sample2.py:* line                  return wrapper',
118
        '*tests*sample2.py:* return                return wrapper',
119
        '*                 * ...       return value: <function foo *',
120
        '*tests*sample2.py:* call              def decorator(func):',
121
        '*tests*sample2.py:* line                  @functools.wraps(func)',
122
        '*tests*sample2.py:* line                  return wrapper',
123
        '*tests*sample2.py:* return                return wrapper',
124
        '*                 * ...       return value: <function foo *',
125
        '*tests*sample2.py:* line          foo(',
126
        "*tests*sample2.py:* line              'a*',",
127
        "*tests*sample2.py:* line              'b'",
128
        '*tests*sample2.py:* call                  @functools.wraps(func)',
129
        '*                 *    |                  def wrapper(*args):',
130
        '*tests*sample2.py:* line                      return func(*args)',
131
        '*tests*sample2.py:* call                  @functools.wraps(func)',
132
        '*                 *    |                  def wrapper(*args):',
133
        '*tests*sample2.py:* line                      return func(*args)',
134
        '*tests*sample2.py:* call                  @functools.wraps(func)',
135
        '*                 *    |                  def wrapper(*args):',
136
        '*tests*sample2.py:* line                      return func(*args)',
137
        '*tests*sample2.py:* call          @deco(1)',
138
        '*                 *    |          @deco(2)',
139
        '*                 *    |          @deco(3)',
140
        '*                 *    |          def foo(*args):',
141
        '*tests*sample2.py:* line              return args',
142
        '*tests*sample2.py:* return            return args',
143
        "*                 * ...       return value: ('a*', 'b')",
144
        "*tests*sample2.py:* return                    return func(*args)",
145
        "*                 * ...       return value: ('a*', 'b')",
146
        "*tests*sample2.py:* return                    return func(*args)",
147
        "*                 * ...       return value: ('a*', 'b')",
148
        "*tests*sample2.py:* return                    return func(*args)",
149
        "*                 * ...       return value: ('a*', 'b')",
150
        "*tests*sample2.py:* line          try:",
151
        "*tests*sample2.py:* line              None(",
152
        "*tests*sample2.py:* line                  'a',",
153
        "*tests*sample2.py:* line                  'b'",
154
        "*tests*sample2.py:* exception             'b'",
155
        "*                 * ...       exception value: *",
156
        "*tests*sample2.py:* line          except:",
157
        "*tests*sample2.py:* line              pass",
158
        "*tests*sample2.py:* return            pass",
159
        "*                   ...       return value: None",
160
    ])
161
162
163
def test_predicate_str_repr():
164
    assert repr(Q(module='a')).endswith("predicates.Query: query={'module': 'a'}>")
165
    assert str(Q(module='a')) == "Query(module='a')"
166
167
    assert "predicates.When: condition=<hunter." in repr(Q(module='a', action='foo'))
168
    assert "predicates.Query: query={'module': 'a'}>, actions=['foo']>" in repr(Q(module='a', action='foo'))
169
    assert str(Q(module='a', action='foo')) == "When(Query(module='a'), 'foo')"
170
171
    assert "predicates.Not: predicate=<hunter." in repr(~Q(module='a'))
172
    assert "predicates.Query: query={'module': 'a'}>>" in repr(~Q(module='a'))
173
    assert str(~Q(module='a')) == "Not(Query(module='a'))"
174
175
    assert "predicates.Or: predicates=(<hunter." in repr(Q(module='a') | Q(module='b'))
176
    assert "predicates.Query: query={'module': 'a'}>, " in repr(Q(module='a') | Q(module='b'))
177
    assert repr(Q(module='a') | Q(module='b')).endswith("predicates.Query: query={'module': 'b'}>)>")
178
    assert str(Q(module='a') | Q(module='b')) == "Or(Query(module='a'), Query(module='b'))"
179
180
    assert "predicates.And: predicates=(<hunter." in repr(Q(module='a') & Q(module='b'))
181
    assert "predicates.Query: query={'module': 'a'}>," in repr(Q(module='a') & Q(module='b'))
182
    assert repr(Q(module='a') & Q(module='b')).endswith("predicates.Query: query={'module': 'b'}>)>")
183
    assert str(Q(module='a') & Q(module='b')) == "And(Query(module='a'), Query(module='b'))"
184
185
186
def test_predicate_q_nest_1():
187
    assert repr(Q(Q(module='a'))).endswith("predicates.Query: query={'module': 'a'}>")
188
189
190
def test_predicate_q_expansion():
191
    assert Q(1, 2, module=3) == Or(1, 2, Q(module=3))
192
    assert Q(1, 2, module=3, action=4) == When(Or(1, 2, Q(module=3)), 4)
193
    assert Q(1, 2, module=3, actions=[4, 5]) == When(Or(1, 2, Q(module=3)), 4, 5)
194
195
196
def test_predicate_and():
197
    assert And(1, 2) == And(1, 2)
198
    assert Q(module=1) & Q(module=2) == And(Q(module=1), Q(module=2))
199
    assert Q(module=1) & Q(module=2) & Q(module=3) == And(Q(module=1), Q(module=2), Q(module=3))
200
201
    assert (Q(module=1) & Q(module=2))({'module': 3}) == False
202
    assert (Q(module=1) & Q(function=2))({'module': 1, 'function': 2}) == True
203
204
    assert And(1, 2) | 3 == Or(And(1, 2), 3)
205
206
207
def test_predicate_or():
208
    assert Q(module=1) | Q(module=2) == Or(Q(module=1), Q(module=2))
209
    assert Q(module=1) | Q(module=2) | Q(module=3) == Or(Q(module=1), Q(module=2), Q(module=3))
210
211
    assert (Q(module=1) | Q(module=2))({'module': 3}) == False
212
    assert (Q(module=1) | Q(module=2))({'module': 2}) == True
213
214
    assert Or(1, 2) & 3 == And(Or(1, 2), 3)
215
216
217
def test_tracing_bare(LineMatcher):
218
    lines = StringIO()
219
    with trace(CodePrinter(stream=lines)):
220
        def a():
221
            return 1
222
223
        b = a()
224
        b = 2
225
        try:
226
            raise Exception("BOOM!")
227
        except Exception:
228
            pass
229
    print(lines.getvalue())
230
    lm = LineMatcher(lines.getvalue().splitlines())
231
    lm.fnmatch_lines([
232
        "* ...       return value: <hunter.*tracer.Tracer *",
233
        "*test_hunter.py* call              def a():",
234
        "*test_hunter.py* line                  return 1",
235
        "*test_hunter.py* return                return 1",
236
        "* ...       return value: 1",
237
    ])
238
239
240
def test_tracing_printing_failures(LineMatcher):
241
    lines = StringIO()
242
    with trace(CodePrinter(stream=lines), VarsPrinter("x", stream=lines)):
243
        class Bad(Exception):
244
            def __repr__(self):
245
                raise RuntimeError("I'm a bad class!")
246
247
        def a():
248
            x = Bad()
249
            return x
250
251
        def b():
252
            x = Bad()
253
            raise x
254
255
        a()
256
        try:
257
            b()
258
        except Exception as exc:
259
            pass
260
    lm = LineMatcher(lines.getvalue().splitlines())
261
    lm.fnmatch_lines([
262
        """* ...       return value: <hunter.*tracer.Tracer *""",
263
        """*tests*test_hunter.py:* call              class Bad(Exception):""",
264
        """*tests*test_hunter.py:* line              class Bad(Exception):""",
265
        """*tests*test_hunter.py:* line                  def __repr__(self):""",
266
        """*tests*test_hunter.py:* return                def __repr__(self):""",
267
        """* ...       return value: *""",
268
        """*tests*test_hunter.py:* call              def a():""",
269
        """*tests*test_hunter.py:* line                  x = Bad()""",
270
        """*tests*test_hunter.py:* line                  return x""",
271
        """* vars      x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""",
272
        """*tests*test_hunter.py:* return                return x""",
273
        """* ...       return value: !!! FAILED REPR: RuntimeError("I'm a bad class!",)""",
274
        """* vars      x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""",
275
        """*tests*test_hunter.py:* call              def b():""",
276
        """*tests*test_hunter.py:* line                  x = Bad()""",
277
        """*tests*test_hunter.py:* line                  raise x""",
278
        """* vars      x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""",
279
        """*tests*test_hunter.py:* exception             raise x""",
280
        """* ...       exception value: !!! FAILED REPR: RuntimeError("I'm a bad class!",)""",
281
        """* vars      x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""",
282
        """*tests*test_hunter.py:* return                raise x""",
283
        """* ...       return value: None""",
284
        """* vars      x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""",
285
    ])
286
287
288
def test_tracing_vars(LineMatcher):
289
    lines = StringIO()
290
    with trace(actions=[VarsPrinter('b', stream=lines), CodePrinter(stream=lines)]):
291
        def a():
292
            b = 1
293
            b = 2
294
            return 1
295
296
        b = a()
297
        b = 2
298
        try:
299
            raise Exception("BOOM!")
300
        except Exception:
301
            pass
302
    print(lines.getvalue())
303
    lm = LineMatcher(lines.getvalue().splitlines())
304
    lm.fnmatch_lines([
305
        "* ...       return value: <hunter.*tracer.Tracer *",
306
        "*test_hunter.py* call              def a():",
307
        "*test_hunter.py* line                  b = 1",
308
        "* vars      b => 1",
309
        "*test_hunter.py* line                  b = 2",
310
        "* vars      b => 2",
311
        "*test_hunter.py* line                  return 1",
312
        "* vars      b => 2",
313
        "*test_hunter.py* return                return 1",
314
        "* ...       return value: 1",
315
    ])
316
317
318
def test_trace_merge():
319
    trace(function="a")
320
    trace(function="b")
321
    assert trace(function="c")._handler == When(Q(function="c"), CodePrinter)
322
323
324
def test_trace_api_expansion():
325
    # simple use
326
    with trace(function="foobar") as t:
327
        assert t._handler == When(Q(function="foobar"), CodePrinter)
328
329
    # "or" by expression
330
    with trace(module="foo", function="foobar") as t:
331
        assert t._handler == When(Q(module="foo", function="foobar"), CodePrinter)
332
333
    # pdb.set_trace
334
    with trace(function="foobar", action=Debugger) as t:
335
        assert t._handler == When(Q(function="foobar"), Debugger)
336
337
    # pdb.set_trace on any hits
338
    with trace(module="foo", function="foobar", action=Debugger) as t:
339
        assert t._handler == When(Q(module="foo", function="foobar"), Debugger)
340
341
    # pdb.set_trace when function is foobar, otherwise just print when module is foo
342
    with trace(Q(function="foobar", action=Debugger), module="foo") as t:
343
        assert t._handler == When(Or(
344
            When(Q(function="foobar"), Debugger),
345
            Q(module="foo")
346
        ), CodePrinter)
347
348
    # dumping variables from stack
349
    with trace(Q(function="foobar", action=VarsPrinter("foobar")), module="foo") as t:
350
        assert t._handler == When(Or(
351
            When(Q(function="foobar"), VarsPrinter("foobar")),
352
            Q(module="foo"),
353
        ), CodePrinter)
354
355
    with trace(Q(function="foobar", action=VarsPrinter("foobar", "mumbojumbo")), module="foo") as t:
356
        assert t._handler == When(Or(
357
            When(Q(function="foobar"), VarsPrinter("foobar", "mumbojumbo")),
358
            Q(module="foo"),
359
        ), CodePrinter)
360
361
    # multiple actions
362
    with trace(Q(function="foobar", actions=[VarsPrinter("foobar"), Debugger]), module="foo") as t:
363
        assert t._handler == When(Or(
364
            When(Q(function="foobar"), VarsPrinter("foobar"), Debugger),
365
            Q(module="foo"),
366
        ), CodePrinter)
367
368
    # customization
369
    assert trace(lambda event: event.locals.get("node") == "Foobar",
370
                 module="foo", function="foobar")
371
    assert trace(Q(lambda event: event.locals.get("node") == "Foobar",
372
                   function="foobar", actions=[VarsPrinter("foobar"), Debugger]), module="foo", )
373
    assert trace(Q(function="foobar", actions=[VarsPrinter("foobar"),
374
                                               lambda event: print("some custom output")]), module="foo", )
375
376
377
def test_trace_with_class_actions():
378
    with trace(CodePrinter):
379
        def a():
380
            pass
381
382
        a()
383
384
385
def test_predicate_no_inf_recursion():
386
    assert Or(And(1)) == 1
387
    assert Or(Or(1)) == 1
388
    assert And(Or(1)) == 1
389
    assert And(And(1)) == 1
390
    predicate = Q(Q(lambda ev: 1, module='wat'))
391
    print('predicate:', predicate)
392
    predicate('foo')
393
394
395
def test_predicate_compression():
396
    assert Or(Or(1, 2), And(3)) == Or(1, 2, 3)
397
    assert Or(Or(1, 2), 3) == Or(1, 2, 3)
398
    assert Or(1, Or(2, 3), 4) == Or(1, 2, 3, 4)
399
    assert And(1, 2, Or(3, 4)).predicates == (1, 2, Or(3, 4))
400
401
    assert repr(Or(Or(1, 2), And(3))) == repr(Or(1, 2, 3))
402
    assert repr(Or(Or(1, 2), 3)) == repr(Or(1, 2, 3))
403
    assert repr(Or(1, Or(2, 3), 4)) == repr(Or(1, 2, 3, 4))
404
405
406
def test_predicate_not():
407
    assert Not(1).predicate == 1
408
    assert ~Or(1, 2) == Not(Or(1, 2))
409
    assert ~And(1, 2) == Not(And(1, 2))
410
411
    assert ~Not(1) == 1
412
413
    assert ~Query(module=1) | ~Query(module=2) == Not(And(Query(module=1), Query(module=2)))
414
    assert ~Query(module=1) & ~Query(module=2) == Not(Or(Query(module=1), Query(module=2)))
415
416
    assert ~Query(module=1) | Query(module=2) == Or(Not(Query(module=1)), Query(module=2))
417
    assert ~Query(module=1) & Query(module=2) == And(Not(Query(module=1)), Query(module=2))
418
419
    assert ~(Query(module=1) & Query(module=2)) == Not(And(Query(module=1), Query(module=2)))
420
    assert ~(Query(module=1) | Query(module=2)) == Not(Or(Query(module=1), Query(module=2)))
421
422
    assert repr(~Or(1, 2)) == repr(Not(Or(1, 2)))
423
    assert repr(~And(1, 2)) == repr(Not(And(1, 2)))
424
425
    assert repr(~Query(module=1) | ~Query(module=2)) == repr(Not(And(Query(module=1), Query(module=2))))
426
    assert repr(~Query(module=1) & ~Query(module=2)) == repr(Not(Or(Query(module=1), Query(module=2))))
427
428
    assert repr(~(Query(module=1) & Query(module=2))) == repr(Not(And(Query(module=1), Query(module=2))))
429
    assert repr(~(Query(module=1) | Query(module=2))) == repr(Not(Or(Query(module=1), Query(module=2))))
430
431
    assert Not(Q(module=1))({'module': 1}) == False
432
433
434
def test_predicate_query_allowed():
435
    pytest.raises(TypeError, Query, 1)
436
    pytest.raises(TypeError, Query, a=1)
437
438
439
def test_predicate_when_allowed():
440
    pytest.raises(TypeError, When, 1)
441
442
443
@pytest.mark.parametrize('expr,inp,expected', [
444
    ({'module': "abc"}, {'module': "abc"}, True),
445
    ({'module': "abcd"}, {'module': "abc"}, False),
446
    ({'module': "abcd"}, {'module': "abce"}, False),
447
    ({'module': "abc"}, {'module': "abcd"}, False),
448
    ({'module': ("abc", "xyz")}, {'module': "abc"}, True),
449
    ({'module': ("abc", "xyz")}, {'module': "abcd"}, True),
450
    ({'module': ("abc", "xyz")}, {'module': "xyzw"}, True),
451
    ({'module': ("abc", "xyz")}, {'module': "fooabc"}, False),
452
    ({'module': ("abc", "xyz")}, {'module': 1}, False),
453
454
    ({'module': ["abc", "xyz"]}, {'module': "abc"}, True),
455
    ({'module': ["abc", "xyz"]}, {'module': "abcd"}, True),
456
    ({'module': ["abc", "xyz"]}, {'module': "xyzw"}, True),
457
    ({'module': ["abc", "xyz"]}, {'module': "fooabc"}, False),
458
    ({'module': ["abc", "xyz"]}, {'module': 1}, False),
459
460
    ({'module': {"abc", "xyz"}}, {'module': "abc"}, True),
461
    ({'module': {"abc", "xyz"}}, {'module': "abcd"}, True),
462
    ({'module': {"abc", "xyz"}}, {'module': "xyzw"}, True),
463
    ({'module': {"abc", "xyz"}}, {'module': "fooabc"}, False),
464
    ({'module': {"abc", "xyz"}}, {'module': 1}, False),
465
466
    ({'module': "abc"}, {'module': 1}, False),
467
])
468
def test_predicate_matching(expr, inp, expected):
469
    assert Query(**expr)(inp) == expected
470
471
472
def test_predicate_when():
473
    called = []
474
    assert When(Q(module=1), lambda ev: called.append(ev))({'module': 2}) == False
475
    assert called == []
476
477
    assert When(Q(module=1), lambda ev: called.append(ev))({'module': 1}) == True
478
    assert called == [{'module': 1}]
479
480
    called = []
481
    assert Q(module=1, action=lambda ev: called.append(ev))({'module': 1}) == True
482
    assert called == [{'module': 1}]
483
484
    called = [[], []]
485
    predicate = (
486
        Q(module=1, action=lambda ev: called[0].append(ev)) |
487
        Q(module=2, action=lambda ev: called[1].append(ev))
488
    )
489
    assert predicate({'module': 1}) == True
490
    assert called == [[{'module': 1}], []]
491
492
    assert predicate({'module': 2}) == True
493
    assert called == [[{'module': 1}], [{'module': 2}]]
494
495
    called = [[], []]
496
    predicate = (
497
        Q(module=1, action=lambda ev: called[0].append(ev)) &
498
        Q(function=2, action=lambda ev: called[1].append(ev))
499
    )
500
    assert predicate({'module': 2}) == False
501
    assert called == [[], []]
502
503
    assert predicate({'module': 1, 'function': 2}) == True
504
    assert called == [[{'module': 1, 'function': 2}], [{'module': 1, 'function': 2}]]
505
506
507
def test_proper_backend():
508
    if os.environ.get('PUREPYTHONTRACER'):
509
        assert repr(hunter._TRACER).startswith('<hunter.tracer.Tracer')
510
    else:
511
        assert repr(hunter._TRACER).startswith('<hunter._tracer.Tracer')
512