Completed
Push — master ( 0fdc83...4e3b27 )
by Ionel Cristian
55s
created

tests.test_predicate_compression()   C

Complexity

Conditions 8

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 9
rs 6.6667
cc 8
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')) == "Query(module='a')"
165
    assert str(Q(module='a')) == "Query(module='a')"
166
    assert repr(Q(module='a', action='foo')) == "When(condition=Query(module='a'), actions=['foo'])"
167
    assert str(Q(module='a', action='foo')) == "When(condition=Query(module='a'), actions=['foo'])"
168
    assert repr(~Q(module='a')) == "Not(predicate=Query(module='a'))"
169
    assert str(~Q(module='a')) == "Not(Query(module='a'))"
170
171
    assert repr(Q(module='a') | Q(module='b')) == "Or(predicates=(Query(module='a'), Query(module='b')))"
172
    assert str(Q(module='a') | Q(module='b')) == "Or(Query(module='a'), Query(module='b'))"
173
    assert repr(Q(module='a') & Q(module='b')) == "And(predicates=(Query(module='a'), Query(module='b')))"
174
    assert str(Q(module='a') & Q(module='b')) == "And(Query(module='a'), Query(module='b'))"
175
176
177
def test_predicate_q_nest_1():
178
    assert repr(Q(Q(module='a'))) == "Query(module='a')"
179
180
181
def test_predicate_q_expansion():
182
    assert Q(1, 2, module=3) == Or(1, 2, Q(module=3))
183
    assert Q(1, 2, module=3, action=4) == When(Or(1, 2, Q(module=3)), 4)
184
    assert Q(1, 2, module=3, actions=[4, 5]) == When(Or(1, 2, Q(module=3)), 4, 5)
185
186
187
def test_predicate_and():
188
    assert And(1, 2) == And(1, 2)
189
    assert Q(module=1) & Q(module=2) == And(Q(module=1), Q(module=2))
190
    assert Q(module=1) & Q(module=2) & Q(module=3) == And(Q(module=1), Q(module=2), Q(module=3))
191
192
    assert (Q(module=1) & Q(module=2))({'module': 3}) == False
193
    assert (Q(module=1) & Q(function=2))({'module': 1, 'function': 2}) == True
194
195
    assert And(1, 2) | 3 == Or(And(1, 2), 3)
196
197
198
def test_predicate_or():
199
    assert Q(module=1) | Q(module=2) == Or(Q(module=1), Q(module=2))
200
    assert Q(module=1) | Q(module=2) | Q(module=3) == Or(Q(module=1), Q(module=2), Q(module=3))
201
202
    assert (Q(module=1) | Q(module=2))({'module': 3}) == False
203
    assert (Q(module=1) | Q(module=2))({'module': 2}) == True
204
205
    assert Or(1, 2) & 3 == And(Or(1, 2), 3)
206
207
208
def test_tracing_bare(LineMatcher):
209
    lines = StringIO()
210
    with trace(CodePrinter(stream=lines)):
211
        def a():
212
            return 1
213
214
        b = a()
215
        b = 2
216
        try:
217
            raise Exception("BOOM!")
218
        except Exception:
219
            pass
220
    print(lines.getvalue())
221
    lm = LineMatcher(lines.getvalue().splitlines())
222
    lm.fnmatch_lines([
223
        "* ...       return value: <hunter.*tracer.Tracer *",
224
        "*test_hunter.py* call              def a():",
225
        "*test_hunter.py* line                  return 1",
226
        "*test_hunter.py* return                return 1",
227
        "* ...       return value: 1",
228
    ])
229
230
231
def test_tracing_printing_failures(LineMatcher):
232
    lines = StringIO()
233
    with trace(CodePrinter(stream=lines), VarsPrinter("x", stream=lines)):
234
        class Bad(Exception):
235
            def __repr__(self):
236
                raise RuntimeError("I'm a bad class!")
237
238
        def a():
239
            x = Bad()
240
            return x
241
242
        def b():
243
            x = Bad()
244
            raise x
245
246
        a()
247
        try:
248
            b()
249
        except Exception as exc:
250
            pass
251
    lm = LineMatcher(lines.getvalue().splitlines())
252
    lm.fnmatch_lines([
253
        """* ...       return value: <hunter.*tracer.Tracer *""",
254
        """*tests*test_hunter.py:* call              class Bad(Exception):""",
255
        """*tests*test_hunter.py:* line              class Bad(Exception):""",
256
        """*tests*test_hunter.py:* line                  def __repr__(self):""",
257
        """*tests*test_hunter.py:* return                def __repr__(self):""",
258
        """* ...       return value: *""",
259
        """*tests*test_hunter.py:* call              def a():""",
260
        """*tests*test_hunter.py:* line                  x = Bad()""",
261
        """*tests*test_hunter.py:* line                  return x""",
262
        """* vars      x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""",
263
        """*tests*test_hunter.py:* return                return x""",
264
        """* ...       return value: !!! FAILED REPR: RuntimeError("I'm a bad class!",)""",
265
        """* vars      x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""",
266
        """*tests*test_hunter.py:* call              def b():""",
267
        """*tests*test_hunter.py:* line                  x = Bad()""",
268
        """*tests*test_hunter.py:* line                  raise x""",
269
        """* vars      x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""",
270
        """*tests*test_hunter.py:* exception             raise x""",
271
        """* ...       exception value: !!! FAILED REPR: RuntimeError("I'm a bad class!",)""",
272
        """* vars      x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""",
273
        """*tests*test_hunter.py:* return                raise x""",
274
        """* ...       return value: None""",
275
        """* vars      x => !!! FAILED REPR: RuntimeError("I'm a bad class!",)""",
276
    ])
277
278
279
def test_tracing_vars(LineMatcher):
280
    lines = StringIO()
281
    with trace(actions=[VarsPrinter('b', stream=lines), CodePrinter(stream=lines)]):
282
        def a():
283
            b = 1
284
            b = 2
285
            return 1
286
287
        b = a()
288
        b = 2
289
        try:
290
            raise Exception("BOOM!")
291
        except Exception:
292
            pass
293
    print(lines.getvalue())
294
    lm = LineMatcher(lines.getvalue().splitlines())
295
    lm.fnmatch_lines([
296
        "* ...       return value: <hunter.*tracer.Tracer *",
297
        "*test_hunter.py* call              def a():",
298
        "*test_hunter.py* line                  b = 1",
299
        "* vars      b => 1",
300
        "*test_hunter.py* line                  b = 2",
301
        "* vars      b => 2",
302
        "*test_hunter.py* line                  return 1",
303
        "* vars      b => 2",
304
        "*test_hunter.py* return                return 1",
305
        "* ...       return value: 1",
306
    ])
307
308
309
def test_trace_merge():
310
    trace(function="a")
311
    trace(function="b")
312
    assert trace(function="c")._handler == When(Q(function="c"), CodePrinter)
313
314
315
def test_trace_api_expansion():
316
    # simple use
317
    with trace(function="foobar") as t:
318
        assert t._handler == When(Q(function="foobar"), CodePrinter)
319
320
    # "or" by expression
321
    with trace(module="foo", function="foobar") as t:
322
        assert t._handler == When(Q(module="foo", function="foobar"), CodePrinter)
323
324
    # pdb.set_trace
325
    with trace(function="foobar", action=Debugger) as t:
326
        assert t._handler == When(Q(function="foobar"), Debugger)
327
328
    # pdb.set_trace on any hits
329
    with trace(module="foo", function="foobar", action=Debugger) as t:
330
        assert t._handler == When(Q(module="foo", function="foobar"), Debugger)
331
332
    # pdb.set_trace when function is foobar, otherwise just print when module is foo
333
    with trace(Q(function="foobar", action=Debugger), module="foo") as t:
334
        assert t._handler == When(Or(
335
            When(Q(function="foobar"), Debugger),
336
            Q(module="foo")
337
        ), CodePrinter)
338
339
    # dumping variables from stack
340
    with trace(Q(function="foobar", action=VarsPrinter("foobar")), module="foo") as t:
341
        assert t._handler == When(Or(
342
            When(Q(function="foobar"), VarsPrinter("foobar")),
343
            Q(module="foo"),
344
        ), CodePrinter)
345
346
    with trace(Q(function="foobar", action=VarsPrinter("foobar", "mumbojumbo")), module="foo") as t:
347
        assert t._handler == When(Or(
348
            When(Q(function="foobar"), VarsPrinter("foobar", "mumbojumbo")),
349
            Q(module="foo"),
350
        ), CodePrinter)
351
352
    # multiple actions
353
    with trace(Q(function="foobar", actions=[VarsPrinter("foobar"), Debugger]), module="foo") as t:
354
        assert t._handler == When(Or(
355
            When(Q(function="foobar"), VarsPrinter("foobar"), Debugger),
356
            Q(module="foo"),
357
        ), CodePrinter)
358
359
    # customization
360
    assert trace(lambda event: event.locals.get("node") == "Foobar",
361
                 module="foo", function="foobar")
362
    assert trace(Q(lambda event: event.locals.get("node") == "Foobar",
363
                   function="foobar", actions=[VarsPrinter("foobar"), Debugger]), module="foo", )
364
    assert trace(Q(function="foobar", actions=[VarsPrinter("foobar"),
365
                                               lambda event: print("some custom output")]), module="foo", )
366
367
368
def test_trace_with_class_actions():
369
    with trace(CodePrinter):
370
        def a():
371
            pass
372
373
        a()
374
375
376
def test_predicate_no_inf_recursion():
377
    assert Or(And(1)) == 1
378
    assert Or(Or(1)) == 1
379
    assert And(Or(1)) == 1
380
    assert And(And(1)) == 1
381
    predicate = Q(Q(lambda ev: 1, module='wat'))
382
    print('predicate:', predicate)
383
    predicate('foo')
384
385
386
def test_predicate_compression():
387
    assert Or(Or(1, 2), And(3)) == Or(1, 2, 3)
388
    assert Or(Or(1, 2), 3) == Or(1, 2, 3)
389
    assert Or(1, Or(2, 3), 4) == Or(1, 2, 3, 4)
390
    assert And(1, 2, Or(3, 4)).predicates == (1, 2, Or(3, 4))
391
392
    assert repr(Or(Or(1, 2), And(3))) == repr(Or(1, 2, 3))
393
    assert repr(Or(Or(1, 2), 3)) == repr(Or(1, 2, 3))
394
    assert repr(Or(1, Or(2, 3), 4)) == repr(Or(1, 2, 3, 4))
395
396
397
def test_predicate_not():
398
    assert Not(1).predicate == 1
399
    assert ~Or(1, 2) == Not(Or(1, 2))
400
    assert ~And(1, 2) == Not(And(1, 2))
401
402
    assert ~Not(1) == 1
403
404
    assert ~Query(module=1) | ~Query(module=2) == Not(And(Query(module=1), Query(module=2)))
405
    assert ~Query(module=1) & ~Query(module=2) == Not(Or(Query(module=1), Query(module=2)))
406
407
    assert ~Query(module=1) | Query(module=2) == Or(Not(Query(module=1)), Query(module=2))
408
    assert ~Query(module=1) & Query(module=2) == And(Not(Query(module=1)), Query(module=2))
409
410
    assert ~(Query(module=1) & Query(module=2)) == Not(And(Query(module=1), Query(module=2)))
411
    assert ~(Query(module=1) | Query(module=2)) == Not(Or(Query(module=1), Query(module=2)))
412
413
    assert repr(~Or(1, 2)) == repr(Not(Or(1, 2)))
414
    assert repr(~And(1, 2)) == repr(Not(And(1, 2)))
415
416
    assert repr(~Query(module=1) | ~Query(module=2)) == repr(Not(And(Query(module=1), Query(module=2))))
417
    assert repr(~Query(module=1) & ~Query(module=2)) == repr(Not(Or(Query(module=1), Query(module=2))))
418
419
    assert repr(~(Query(module=1) & Query(module=2))) == repr(Not(And(Query(module=1), Query(module=2))))
420
    assert repr(~(Query(module=1) | Query(module=2))) == repr(Not(Or(Query(module=1), Query(module=2))))
421
422
    assert Not(Q(module=1))({'module': 1}) == False
423
424
425
def test_predicate_query_allowed():
426
    pytest.raises(TypeError, Query, 1)
427
    pytest.raises(TypeError, Query, a=1)
428
429
430
def test_predicate_when_allowed():
431
    pytest.raises(TypeError, When, 1)
432
433
434
@pytest.mark.parametrize('expr,inp,expected', [
435
    ({'module': "abc"}, {'module': "abc"}, True),
436
    ({'module': "abcd"}, {'module': "abc"}, False),
437
    ({'module': "abcd"}, {'module': "abce"}, False),
438
    ({'module': "abc"}, {'module': "abcd"}, False),
439
    ({'module': ("abc", "xyz")}, {'module': "abc"}, True),
440
    ({'module': ("abc", "xyz")}, {'module': "abcd"}, True),
441
    ({'module': ("abc", "xyz")}, {'module': "xyzw"}, True),
442
    ({'module': ("abc", "xyz")}, {'module': "fooabc"}, False),
443
    ({'module': ("abc", "xyz")}, {'module': 1}, False),
444
445
    ({'module': ["abc", "xyz"]}, {'module': "abc"}, True),
446
    ({'module': ["abc", "xyz"]}, {'module': "abcd"}, True),
447
    ({'module': ["abc", "xyz"]}, {'module': "xyzw"}, True),
448
    ({'module': ["abc", "xyz"]}, {'module': "fooabc"}, False),
449
    ({'module': ["abc", "xyz"]}, {'module': 1}, False),
450
451
    ({'module': {"abc", "xyz"}}, {'module': "abc"}, True),
452
    ({'module': {"abc", "xyz"}}, {'module': "abcd"}, True),
453
    ({'module': {"abc", "xyz"}}, {'module': "xyzw"}, True),
454
    ({'module': {"abc", "xyz"}}, {'module': "fooabc"}, False),
455
    ({'module': {"abc", "xyz"}}, {'module': 1}, False),
456
457
    ({'module': "abc"}, {'module': 1}, False),
458
])
459
def test_predicate_matching(expr, inp, expected):
460
    assert Query(**expr)(inp) == expected
461
462
463
def test_predicate_when():
464
    called = []
465
    assert When(Q(module=1), lambda ev: called.append(ev))({'module': 2}) == False
466
    assert called == []
467
468
    assert When(Q(module=1), lambda ev: called.append(ev))({'module': 1}) == True
469
    assert called == [{'module': 1}]
470
471
    called = []
472
    assert Q(module=1, action=lambda ev: called.append(ev))({'module': 1}) == True
473
    assert called == [{'module': 1}]
474
475
    called = [[], []]
476
    predicate = (
477
        Q(module=1, action=lambda ev: called[0].append(ev)) |
478
        Q(module=2, action=lambda ev: called[1].append(ev))
479
    )
480
    assert predicate({'module': 1}) == True
481
    assert called == [[{'module': 1}], []]
482
483
    assert predicate({'module': 2}) == True
484
    assert called == [[{'module': 1}], [{'module': 2}]]
485
486
    called = [[], []]
487
    predicate = (
488
        Q(module=1, action=lambda ev: called[0].append(ev)) &
489
        Q(function=2, action=lambda ev: called[1].append(ev))
490
    )
491
    assert predicate({'module': 2}) == False
492
    assert called == [[], []]
493
494
    assert predicate({'module': 1, 'function': 2}) == True
495
    assert called == [[{'module': 1, 'function': 2}], [{'module': 1, 'function': 2}]]
496
497
498
def test_proper_backend():
499
    if os.environ.get('PUREPYTHONTRACER'):
500
        assert repr(hunter._TRACER).startswith('<hunter.tracer.Tracer')
501
    else:
502
        assert repr(hunter._TRACER).startswith('<hunter._tracer.Tracer')
503