1# -*- coding: utf-8 -*- 2from __future__ import absolute_import 3from __future__ import division 4from __future__ import print_function 5 6import os 7import sys 8 9import six 10 11import _pytest._code 12import pytest 13from _pytest.debugging import _validate_usepdb_cls 14 15try: 16 breakpoint 17except NameError: 18 SUPPORTS_BREAKPOINT_BUILTIN = False 19else: 20 SUPPORTS_BREAKPOINT_BUILTIN = True 21 22 23_ENVIRON_PYTHONBREAKPOINT = os.environ.get("PYTHONBREAKPOINT", "") 24 25 26def runpdb_and_get_report(testdir, source): 27 p = testdir.makepyfile(source) 28 result = testdir.runpytest_inprocess("--pdb", p) 29 reports = result.reprec.getreports("pytest_runtest_logreport") 30 assert len(reports) == 3, reports # setup/call/teardown 31 return reports[1] 32 33 34@pytest.fixture 35def custom_pdb_calls(): 36 called = [] 37 38 # install dummy debugger class and track which methods were called on it 39 class _CustomPdb(object): 40 quitting = False 41 42 def __init__(self, *args, **kwargs): 43 called.append("init") 44 45 def reset(self): 46 called.append("reset") 47 48 def interaction(self, *args): 49 called.append("interaction") 50 51 _pytest._CustomPdb = _CustomPdb 52 return called 53 54 55@pytest.fixture 56def custom_debugger_hook(): 57 called = [] 58 59 # install dummy debugger class and track which methods were called on it 60 class _CustomDebugger(object): 61 def __init__(self, *args, **kwargs): 62 called.append("init") 63 64 def reset(self): 65 called.append("reset") 66 67 def interaction(self, *args): 68 called.append("interaction") 69 70 def set_trace(self, frame): 71 print("**CustomDebugger**") 72 called.append("set_trace") 73 74 _pytest._CustomDebugger = _CustomDebugger 75 yield called 76 del _pytest._CustomDebugger 77 78 79class TestPDB(object): 80 @pytest.fixture 81 def pdblist(self, request): 82 monkeypatch = request.getfixturevalue("monkeypatch") 83 pdblist = [] 84 85 def mypdb(*args): 86 pdblist.append(args) 87 88 plugin = request.config.pluginmanager.getplugin("debugging") 89 monkeypatch.setattr(plugin, "post_mortem", mypdb) 90 return pdblist 91 92 def test_pdb_on_fail(self, testdir, pdblist): 93 rep = runpdb_and_get_report( 94 testdir, 95 """ 96 def test_func(): 97 assert 0 98 """, 99 ) 100 assert rep.failed 101 assert len(pdblist) == 1 102 tb = _pytest._code.Traceback(pdblist[0][0]) 103 assert tb[-1].name == "test_func" 104 105 def test_pdb_on_xfail(self, testdir, pdblist): 106 rep = runpdb_and_get_report( 107 testdir, 108 """ 109 import pytest 110 @pytest.mark.xfail 111 def test_func(): 112 assert 0 113 """, 114 ) 115 assert "xfail" in rep.keywords 116 assert not pdblist 117 118 def test_pdb_on_skip(self, testdir, pdblist): 119 rep = runpdb_and_get_report( 120 testdir, 121 """ 122 import pytest 123 def test_func(): 124 pytest.skip("hello") 125 """, 126 ) 127 assert rep.skipped 128 assert len(pdblist) == 0 129 130 def test_pdb_on_BdbQuit(self, testdir, pdblist): 131 rep = runpdb_and_get_report( 132 testdir, 133 """ 134 import bdb 135 def test_func(): 136 raise bdb.BdbQuit 137 """, 138 ) 139 assert rep.failed 140 assert len(pdblist) == 0 141 142 def test_pdb_on_KeyboardInterrupt(self, testdir, pdblist): 143 rep = runpdb_and_get_report( 144 testdir, 145 """ 146 def test_func(): 147 raise KeyboardInterrupt 148 """, 149 ) 150 assert rep.failed 151 assert len(pdblist) == 1 152 153 @staticmethod 154 def flush(child): 155 if child.isalive(): 156 # Read if the test has not (e.g. test_pdb_unittest_skip). 157 child.read() 158 child.wait() 159 assert not child.isalive() 160 161 def test_pdb_unittest_postmortem(self, testdir): 162 p1 = testdir.makepyfile( 163 """ 164 import unittest 165 class Blub(unittest.TestCase): 166 def tearDown(self): 167 self.filename = None 168 def test_false(self): 169 self.filename = 'debug' + '.me' 170 assert 0 171 """ 172 ) 173 child = testdir.spawn_pytest("--pdb %s" % p1) 174 child.expect("Pdb") 175 child.sendline("p self.filename") 176 child.sendeof() 177 rest = child.read().decode("utf8") 178 assert "debug.me" in rest 179 self.flush(child) 180 181 def test_pdb_unittest_skip(self, testdir): 182 """Test for issue #2137""" 183 p1 = testdir.makepyfile( 184 """ 185 import unittest 186 @unittest.skipIf(True, 'Skipping also with pdb active') 187 class MyTestCase(unittest.TestCase): 188 def test_one(self): 189 assert 0 190 """ 191 ) 192 child = testdir.spawn_pytest("-rs --pdb %s" % p1) 193 child.expect("Skipping also with pdb active") 194 child.expect("1 skipped in") 195 child.sendeof() 196 self.flush(child) 197 198 def test_pdb_print_captured_stdout_and_stderr(self, testdir): 199 p1 = testdir.makepyfile( 200 """ 201 def test_1(): 202 import sys 203 sys.stderr.write("get\\x20rekt") 204 print("get\\x20rekt") 205 assert False 206 207 def test_not_called_due_to_quit(): 208 pass 209 """ 210 ) 211 child = testdir.spawn_pytest("--pdb %s" % p1) 212 child.expect("captured stdout") 213 child.expect("get rekt") 214 child.expect("captured stderr") 215 child.expect("get rekt") 216 child.expect("traceback") 217 child.expect("def test_1") 218 child.expect("Pdb") 219 child.sendeof() 220 rest = child.read().decode("utf8") 221 assert "Exit: Quitting debugger" in rest 222 assert "= 1 failed in" in rest 223 assert "def test_1" not in rest 224 assert "get rekt" not in rest 225 self.flush(child) 226 227 def test_pdb_dont_print_empty_captured_stdout_and_stderr(self, testdir): 228 p1 = testdir.makepyfile( 229 """ 230 def test_1(): 231 assert False 232 """ 233 ) 234 child = testdir.spawn_pytest("--pdb %s" % p1) 235 child.expect("Pdb") 236 output = child.before.decode("utf8") 237 child.sendeof() 238 assert "captured stdout" not in output 239 assert "captured stderr" not in output 240 self.flush(child) 241 242 @pytest.mark.parametrize("showcapture", ["all", "no", "log"]) 243 def test_pdb_print_captured_logs(self, testdir, showcapture): 244 p1 = testdir.makepyfile( 245 """ 246 def test_1(): 247 import logging 248 logging.warn("get " + "rekt") 249 assert False 250 """ 251 ) 252 child = testdir.spawn_pytest( 253 "--show-capture={} --pdb {}".format(showcapture, p1) 254 ) 255 if showcapture in ("all", "log"): 256 child.expect("captured log") 257 child.expect("get rekt") 258 child.expect("Pdb") 259 child.sendeof() 260 rest = child.read().decode("utf8") 261 assert "1 failed" in rest 262 self.flush(child) 263 264 def test_pdb_print_captured_logs_nologging(self, testdir): 265 p1 = testdir.makepyfile( 266 """ 267 def test_1(): 268 import logging 269 logging.warn("get " + "rekt") 270 assert False 271 """ 272 ) 273 child = testdir.spawn_pytest("--show-capture=all --pdb -p no:logging %s" % p1) 274 child.expect("get rekt") 275 output = child.before.decode("utf8") 276 assert "captured log" not in output 277 child.expect("Pdb") 278 child.sendeof() 279 rest = child.read().decode("utf8") 280 assert "1 failed" in rest 281 self.flush(child) 282 283 def test_pdb_interaction_exception(self, testdir): 284 p1 = testdir.makepyfile( 285 """ 286 import pytest 287 def globalfunc(): 288 pass 289 def test_1(): 290 pytest.raises(ValueError, globalfunc) 291 """ 292 ) 293 child = testdir.spawn_pytest("--pdb %s" % p1) 294 child.expect(".*def test_1") 295 child.expect(".*pytest.raises.*globalfunc") 296 child.expect("Pdb") 297 child.sendline("globalfunc") 298 child.expect(".*function") 299 child.sendeof() 300 child.expect("1 failed") 301 self.flush(child) 302 303 def test_pdb_interaction_on_collection_issue181(self, testdir): 304 p1 = testdir.makepyfile( 305 """ 306 import pytest 307 xxx 308 """ 309 ) 310 child = testdir.spawn_pytest("--pdb %s" % p1) 311 # child.expect(".*import pytest.*") 312 child.expect("Pdb") 313 child.sendline("c") 314 child.expect("1 error") 315 self.flush(child) 316 317 def test_pdb_interaction_on_internal_error(self, testdir): 318 testdir.makeconftest( 319 """ 320 def pytest_runtest_protocol(): 321 0/0 322 """ 323 ) 324 p1 = testdir.makepyfile("def test_func(): pass") 325 child = testdir.spawn_pytest("--pdb %s" % p1) 326 child.expect("Pdb") 327 328 # INTERNALERROR is only displayed once via terminal reporter. 329 assert ( 330 len( 331 [ 332 x 333 for x in child.before.decode().splitlines() 334 if x.startswith("INTERNALERROR> Traceback") 335 ] 336 ) 337 == 1 338 ) 339 340 child.sendeof() 341 self.flush(child) 342 343 def test_pdb_interaction_capturing_simple(self, testdir): 344 p1 = testdir.makepyfile( 345 """ 346 import pytest 347 def test_1(): 348 i = 0 349 print("hello17") 350 pytest.set_trace() 351 i == 1 352 assert 0 353 """ 354 ) 355 child = testdir.spawn_pytest(str(p1)) 356 child.expect(r"test_1\(\)") 357 child.expect("i == 1") 358 child.expect("Pdb") 359 child.sendline("c") 360 rest = child.read().decode("utf-8") 361 assert "AssertionError" in rest 362 assert "1 failed" in rest 363 assert "def test_1" in rest 364 assert "hello17" in rest # out is captured 365 self.flush(child) 366 367 def test_pdb_set_trace_kwargs(self, testdir): 368 p1 = testdir.makepyfile( 369 """ 370 import pytest 371 def test_1(): 372 i = 0 373 print("hello17") 374 pytest.set_trace(header="== my_header ==") 375 x = 3 376 assert 0 377 """ 378 ) 379 child = testdir.spawn_pytest(str(p1)) 380 child.expect("== my_header ==") 381 assert "PDB set_trace" not in child.before.decode() 382 child.expect("Pdb") 383 child.sendline("c") 384 rest = child.read().decode("utf-8") 385 assert "1 failed" in rest 386 assert "def test_1" in rest 387 assert "hello17" in rest # out is captured 388 self.flush(child) 389 390 def test_pdb_set_trace_interception(self, testdir): 391 p1 = testdir.makepyfile( 392 """ 393 import pdb 394 def test_1(): 395 pdb.set_trace() 396 """ 397 ) 398 child = testdir.spawn_pytest(str(p1)) 399 child.expect("test_1") 400 child.expect("Pdb") 401 child.sendline("q") 402 rest = child.read().decode("utf8") 403 assert "no tests ran" in rest 404 assert "reading from stdin while output" not in rest 405 assert "BdbQuit" not in rest 406 self.flush(child) 407 408 def test_pdb_and_capsys(self, testdir): 409 p1 = testdir.makepyfile( 410 """ 411 import pytest 412 def test_1(capsys): 413 print("hello1") 414 pytest.set_trace() 415 """ 416 ) 417 child = testdir.spawn_pytest(str(p1)) 418 child.expect("test_1") 419 child.send("capsys.readouterr()\n") 420 child.expect("hello1") 421 child.sendeof() 422 child.read() 423 self.flush(child) 424 425 def test_pdb_with_caplog_on_pdb_invocation(self, testdir): 426 p1 = testdir.makepyfile( 427 """ 428 def test_1(capsys, caplog): 429 import logging 430 logging.getLogger(__name__).warning("some_warning") 431 assert 0 432 """ 433 ) 434 child = testdir.spawn_pytest("--pdb %s" % str(p1)) 435 child.send("caplog.record_tuples\n") 436 child.expect_exact( 437 "[('test_pdb_with_caplog_on_pdb_invocation', 30, 'some_warning')]" 438 ) 439 child.sendeof() 440 child.read() 441 self.flush(child) 442 443 def test_set_trace_capturing_afterwards(self, testdir): 444 p1 = testdir.makepyfile( 445 """ 446 import pdb 447 def test_1(): 448 pdb.set_trace() 449 def test_2(): 450 print("hello") 451 assert 0 452 """ 453 ) 454 child = testdir.spawn_pytest(str(p1)) 455 child.expect("test_1") 456 child.send("c\n") 457 child.expect("test_2") 458 child.expect("Captured") 459 child.expect("hello") 460 child.sendeof() 461 child.read() 462 self.flush(child) 463 464 def test_pdb_interaction_doctest(self, testdir, monkeypatch): 465 p1 = testdir.makepyfile( 466 """ 467 import pytest 468 def function_1(): 469 ''' 470 >>> i = 0 471 >>> assert i == 1 472 ''' 473 """ 474 ) 475 child = testdir.spawn_pytest("--doctest-modules --pdb %s" % p1) 476 child.expect("Pdb") 477 478 assert "UNEXPECTED EXCEPTION: AssertionError()" in child.before.decode("utf8") 479 480 child.sendline("'i=%i.' % i") 481 child.expect("Pdb") 482 assert "\r\n'i=0.'\r\n" in child.before.decode("utf8") 483 484 child.sendeof() 485 rest = child.read().decode("utf8") 486 assert "1 failed" in rest 487 self.flush(child) 488 489 def test_pdb_interaction_capturing_twice(self, testdir): 490 p1 = testdir.makepyfile( 491 """ 492 import pytest 493 def test_1(): 494 i = 0 495 print("hello17") 496 pytest.set_trace() 497 x = 3 498 print("hello18") 499 pytest.set_trace() 500 x = 4 501 assert 0 502 """ 503 ) 504 child = testdir.spawn_pytest(str(p1)) 505 child.expect(r"PDB set_trace \(IO-capturing turned off\)") 506 child.expect("test_1") 507 child.expect("x = 3") 508 child.expect("Pdb") 509 child.sendline("c") 510 child.expect(r"PDB continue \(IO-capturing resumed\)") 511 child.expect(r"PDB set_trace \(IO-capturing turned off\)") 512 child.expect("x = 4") 513 child.expect("Pdb") 514 child.sendline("c") 515 child.expect("_ test_1 _") 516 child.expect("def test_1") 517 rest = child.read().decode("utf8") 518 assert "Captured stdout call" in rest 519 assert "hello17" in rest # out is captured 520 assert "hello18" in rest # out is captured 521 assert "1 failed" in rest 522 self.flush(child) 523 524 def test_pdb_with_injected_do_debug(self, testdir): 525 """Simulates pdbpp, which injects Pdb into do_debug, and uses 526 self.__class__ in do_continue. 527 """ 528 p1 = testdir.makepyfile( 529 mytest=""" 530 import pdb 531 import pytest 532 533 count_continue = 0 534 535 class CustomPdb(pdb.Pdb, object): 536 def do_debug(self, arg): 537 import sys 538 import types 539 540 if sys.version_info < (3, ): 541 do_debug_func = pdb.Pdb.do_debug.im_func 542 else: 543 do_debug_func = pdb.Pdb.do_debug 544 545 newglobals = do_debug_func.__globals__.copy() 546 newglobals['Pdb'] = self.__class__ 547 orig_do_debug = types.FunctionType( 548 do_debug_func.__code__, newglobals, 549 do_debug_func.__name__, do_debug_func.__defaults__, 550 ) 551 return orig_do_debug(self, arg) 552 do_debug.__doc__ = pdb.Pdb.do_debug.__doc__ 553 554 def do_continue(self, *args, **kwargs): 555 global count_continue 556 count_continue += 1 557 return super(CustomPdb, self).do_continue(*args, **kwargs) 558 559 def foo(): 560 print("print_from_foo") 561 562 def test_1(): 563 i = 0 564 print("hello17") 565 pytest.set_trace() 566 x = 3 567 print("hello18") 568 569 assert count_continue == 2, "unexpected_failure: %d != 2" % count_continue 570 pytest.fail("expected_failure") 571 """ 572 ) 573 child = testdir.spawn_pytest("--pdbcls=mytest:CustomPdb %s" % str(p1)) 574 child.expect(r"PDB set_trace \(IO-capturing turned off\)") 575 child.expect(r"\n\(Pdb") 576 child.sendline("debug foo()") 577 child.expect("ENTERING RECURSIVE DEBUGGER") 578 child.expect(r"\n\(\(Pdb") 579 child.sendline("c") 580 child.expect("LEAVING RECURSIVE DEBUGGER") 581 assert b"PDB continue" not in child.before 582 # No extra newline. 583 assert child.before.endswith(b"c\r\nprint_from_foo\r\n") 584 585 # set_debug should not raise outcomes.Exit, if used recrursively. 586 child.sendline("debug 42") 587 child.sendline("q") 588 child.expect("LEAVING RECURSIVE DEBUGGER") 589 assert b"ENTERING RECURSIVE DEBUGGER" in child.before 590 assert b"Quitting debugger" not in child.before 591 592 child.sendline("c") 593 child.expect(r"PDB continue \(IO-capturing resumed\)") 594 rest = child.read().decode("utf8") 595 assert "hello17" in rest # out is captured 596 assert "hello18" in rest # out is captured 597 assert "1 failed" in rest 598 assert "Failed: expected_failure" in rest 599 assert "AssertionError: unexpected_failure" not in rest 600 self.flush(child) 601 602 def test_pdb_without_capture(self, testdir): 603 p1 = testdir.makepyfile( 604 """ 605 import pytest 606 def test_1(): 607 pytest.set_trace() 608 """ 609 ) 610 child = testdir.spawn_pytest("-s %s" % p1) 611 child.expect(r">>> PDB set_trace >>>") 612 child.expect("Pdb") 613 child.sendline("c") 614 child.expect(r">>> PDB continue >>>") 615 child.expect("1 passed") 616 self.flush(child) 617 618 @pytest.mark.parametrize("capture_arg", ("", "-s", "-p no:capture")) 619 def test_pdb_continue_with_recursive_debug(self, capture_arg, testdir): 620 """Full coverage for do_debug without capturing. 621 622 This is very similar to test_pdb_interaction_continue_recursive in general, 623 but mocks out ``pdb.set_trace`` for providing more coverage. 624 """ 625 p1 = testdir.makepyfile( 626 """ 627 try: 628 input = raw_input 629 except NameError: 630 pass 631 632 def set_trace(): 633 __import__('pdb').set_trace() 634 635 def test_1(monkeypatch): 636 import _pytest.debugging 637 638 class pytestPDBTest(_pytest.debugging.pytestPDB): 639 @classmethod 640 def set_trace(cls, *args, **kwargs): 641 # Init PytestPdbWrapper to handle capturing. 642 _pdb = cls._init_pdb("set_trace", *args, **kwargs) 643 644 # Mock out pdb.Pdb.do_continue. 645 import pdb 646 pdb.Pdb.do_continue = lambda self, arg: None 647 648 print("===" + " SET_TRACE ===") 649 assert input() == "debug set_trace()" 650 651 # Simulate PytestPdbWrapper.do_debug 652 cls._recursive_debug += 1 653 print("ENTERING RECURSIVE DEBUGGER") 654 print("===" + " SET_TRACE_2 ===") 655 656 assert input() == "c" 657 _pdb.do_continue("") 658 print("===" + " SET_TRACE_3 ===") 659 660 # Simulate PytestPdbWrapper.do_debug 661 print("LEAVING RECURSIVE DEBUGGER") 662 cls._recursive_debug -= 1 663 664 print("===" + " SET_TRACE_4 ===") 665 assert input() == "c" 666 _pdb.do_continue("") 667 668 def do_continue(self, arg): 669 print("=== do_continue") 670 671 monkeypatch.setattr(_pytest.debugging, "pytestPDB", pytestPDBTest) 672 673 import pdb 674 monkeypatch.setattr(pdb, "set_trace", pytestPDBTest.set_trace) 675 676 set_trace() 677 """ 678 ) 679 child = testdir.spawn_pytest("--tb=short %s %s" % (p1, capture_arg)) 680 child.expect("=== SET_TRACE ===") 681 before = child.before.decode("utf8") 682 if not capture_arg: 683 assert ">>> PDB set_trace (IO-capturing turned off) >>>" in before 684 else: 685 assert ">>> PDB set_trace >>>" in before 686 child.sendline("debug set_trace()") 687 child.expect("=== SET_TRACE_2 ===") 688 before = child.before.decode("utf8") 689 assert "\r\nENTERING RECURSIVE DEBUGGER\r\n" in before 690 child.sendline("c") 691 child.expect("=== SET_TRACE_3 ===") 692 693 # No continue message with recursive debugging. 694 before = child.before.decode("utf8") 695 assert ">>> PDB continue " not in before 696 697 child.sendline("c") 698 child.expect("=== SET_TRACE_4 ===") 699 before = child.before.decode("utf8") 700 assert "\r\nLEAVING RECURSIVE DEBUGGER\r\n" in before 701 child.sendline("c") 702 rest = child.read().decode("utf8") 703 if not capture_arg: 704 assert "> PDB continue (IO-capturing resumed) >" in rest 705 else: 706 assert "> PDB continue >" in rest 707 assert "1 passed in" in rest 708 709 def test_pdb_used_outside_test(self, testdir): 710 p1 = testdir.makepyfile( 711 """ 712 import pytest 713 pytest.set_trace() 714 x = 5 715 """ 716 ) 717 child = testdir.spawn("{} {}".format(sys.executable, p1)) 718 child.expect("x = 5") 719 child.expect("Pdb") 720 child.sendeof() 721 self.flush(child) 722 723 def test_pdb_used_in_generate_tests(self, testdir): 724 p1 = testdir.makepyfile( 725 """ 726 import pytest 727 def pytest_generate_tests(metafunc): 728 pytest.set_trace() 729 x = 5 730 def test_foo(a): 731 pass 732 """ 733 ) 734 child = testdir.spawn_pytest(str(p1)) 735 child.expect("x = 5") 736 child.expect("Pdb") 737 child.sendeof() 738 self.flush(child) 739 740 def test_pdb_collection_failure_is_shown(self, testdir): 741 p1 = testdir.makepyfile("xxx") 742 result = testdir.runpytest_subprocess("--pdb", p1) 743 result.stdout.fnmatch_lines( 744 ["E NameError: *xxx*", "*! *Exit: Quitting debugger !*"] # due to EOF 745 ) 746 747 @pytest.mark.parametrize("post_mortem", (False, True)) 748 def test_enter_leave_pdb_hooks_are_called(self, post_mortem, testdir): 749 testdir.makeconftest( 750 """ 751 mypdb = None 752 753 def pytest_configure(config): 754 config.testing_verification = 'configured' 755 756 def pytest_enter_pdb(config, pdb): 757 assert config.testing_verification == 'configured' 758 print('enter_pdb_hook') 759 760 global mypdb 761 mypdb = pdb 762 mypdb.set_attribute = "bar" 763 764 def pytest_leave_pdb(config, pdb): 765 assert config.testing_verification == 'configured' 766 print('leave_pdb_hook') 767 768 global mypdb 769 assert mypdb is pdb 770 assert mypdb.set_attribute == "bar" 771 """ 772 ) 773 p1 = testdir.makepyfile( 774 """ 775 import pytest 776 777 def test_set_trace(): 778 pytest.set_trace() 779 assert 0 780 781 def test_post_mortem(): 782 assert 0 783 """ 784 ) 785 if post_mortem: 786 child = testdir.spawn_pytest(str(p1) + " --pdb -s -k test_post_mortem") 787 else: 788 child = testdir.spawn_pytest(str(p1) + " -k test_set_trace") 789 child.expect("enter_pdb_hook") 790 child.sendline("c") 791 if post_mortem: 792 child.expect(r"PDB continue") 793 else: 794 child.expect(r"PDB continue \(IO-capturing resumed\)") 795 child.expect("Captured stdout call") 796 rest = child.read().decode("utf8") 797 assert "leave_pdb_hook" in rest 798 assert "1 failed" in rest 799 self.flush(child) 800 801 def test_pdb_custom_cls(self, testdir, custom_pdb_calls): 802 p1 = testdir.makepyfile("""xxx """) 803 result = testdir.runpytest_inprocess("--pdb", "--pdbcls=_pytest:_CustomPdb", p1) 804 result.stdout.fnmatch_lines(["*NameError*xxx*", "*1 error*"]) 805 assert custom_pdb_calls == ["init", "reset", "interaction"] 806 807 def test_pdb_custom_cls_invalid(self, testdir): 808 result = testdir.runpytest_inprocess("--pdbcls=invalid") 809 result.stderr.fnmatch_lines( 810 [ 811 "*: error: argument --pdbcls: 'invalid' is not in the format 'modname:classname'" 812 ] 813 ) 814 815 def test_pdb_validate_usepdb_cls(self, testdir): 816 assert _validate_usepdb_cls("os.path:dirname.__name__") == ( 817 "os.path", 818 "dirname.__name__", 819 ) 820 821 assert _validate_usepdb_cls("pdb:DoesNotExist") == ("pdb", "DoesNotExist") 822 823 def test_pdb_custom_cls_without_pdb(self, testdir, custom_pdb_calls): 824 p1 = testdir.makepyfile("""xxx """) 825 result = testdir.runpytest_inprocess("--pdbcls=_pytest:_CustomPdb", p1) 826 result.stdout.fnmatch_lines(["*NameError*xxx*", "*1 error*"]) 827 assert custom_pdb_calls == [] 828 829 def test_pdb_custom_cls_with_set_trace(self, testdir, monkeypatch): 830 testdir.makepyfile( 831 custom_pdb=""" 832 class CustomPdb(object): 833 def __init__(self, *args, **kwargs): 834 skip = kwargs.pop("skip") 835 assert skip == ["foo.*"] 836 print("__init__") 837 super(CustomPdb, self).__init__(*args, **kwargs) 838 839 def set_trace(*args, **kwargs): 840 print('custom set_trace>') 841 """ 842 ) 843 p1 = testdir.makepyfile( 844 """ 845 import pytest 846 847 def test_foo(): 848 pytest.set_trace(skip=['foo.*']) 849 """ 850 ) 851 monkeypatch.setenv("PYTHONPATH", str(testdir.tmpdir)) 852 child = testdir.spawn_pytest("--pdbcls=custom_pdb:CustomPdb %s" % str(p1)) 853 854 child.expect("__init__") 855 child.expect("custom set_trace>") 856 self.flush(child) 857 858 859class TestDebuggingBreakpoints(object): 860 def test_supports_breakpoint_module_global(self): 861 """ 862 Test that supports breakpoint global marks on Python 3.7+ and not on 863 CPython 3.5, 2.7 864 """ 865 if sys.version_info.major == 3 and sys.version_info.minor >= 7: 866 assert SUPPORTS_BREAKPOINT_BUILTIN is True 867 if sys.version_info.major == 3 and sys.version_info.minor == 5: 868 assert SUPPORTS_BREAKPOINT_BUILTIN is False 869 if sys.version_info.major == 2 and sys.version_info.minor == 7: 870 assert SUPPORTS_BREAKPOINT_BUILTIN is False 871 872 @pytest.mark.skipif( 873 not SUPPORTS_BREAKPOINT_BUILTIN, reason="Requires breakpoint() builtin" 874 ) 875 @pytest.mark.parametrize("arg", ["--pdb", ""]) 876 def test_sys_breakpointhook_configure_and_unconfigure(self, testdir, arg): 877 """ 878 Test that sys.breakpointhook is set to the custom Pdb class once configured, test that 879 hook is reset to system value once pytest has been unconfigured 880 """ 881 testdir.makeconftest( 882 """ 883 import sys 884 from pytest import hookimpl 885 from _pytest.debugging import pytestPDB 886 887 def pytest_configure(config): 888 config._cleanup.append(check_restored) 889 890 def check_restored(): 891 assert sys.breakpointhook == sys.__breakpointhook__ 892 893 def test_check(): 894 assert sys.breakpointhook == pytestPDB.set_trace 895 """ 896 ) 897 testdir.makepyfile( 898 """ 899 def test_nothing(): pass 900 """ 901 ) 902 args = (arg,) if arg else () 903 result = testdir.runpytest_subprocess(*args) 904 result.stdout.fnmatch_lines(["*1 passed in *"]) 905 906 @pytest.mark.skipif( 907 not SUPPORTS_BREAKPOINT_BUILTIN, reason="Requires breakpoint() builtin" 908 ) 909 def test_pdb_custom_cls(self, testdir, custom_debugger_hook): 910 p1 = testdir.makepyfile( 911 """ 912 def test_nothing(): 913 breakpoint() 914 """ 915 ) 916 result = testdir.runpytest_inprocess( 917 "--pdb", "--pdbcls=_pytest:_CustomDebugger", p1 918 ) 919 result.stdout.fnmatch_lines(["*CustomDebugger*", "*1 passed*"]) 920 assert custom_debugger_hook == ["init", "set_trace"] 921 922 @pytest.mark.parametrize("arg", ["--pdb", ""]) 923 @pytest.mark.skipif( 924 not SUPPORTS_BREAKPOINT_BUILTIN, reason="Requires breakpoint() builtin" 925 ) 926 def test_environ_custom_class(self, testdir, custom_debugger_hook, arg): 927 testdir.makeconftest( 928 """ 929 import os 930 import sys 931 932 os.environ['PYTHONBREAKPOINT'] = '_pytest._CustomDebugger.set_trace' 933 934 def pytest_configure(config): 935 config._cleanup.append(check_restored) 936 937 def check_restored(): 938 assert sys.breakpointhook == sys.__breakpointhook__ 939 940 def test_check(): 941 import _pytest 942 assert sys.breakpointhook is _pytest._CustomDebugger.set_trace 943 """ 944 ) 945 testdir.makepyfile( 946 """ 947 def test_nothing(): pass 948 """ 949 ) 950 args = (arg,) if arg else () 951 result = testdir.runpytest_subprocess(*args) 952 result.stdout.fnmatch_lines(["*1 passed in *"]) 953 954 @pytest.mark.skipif( 955 not SUPPORTS_BREAKPOINT_BUILTIN, reason="Requires breakpoint() builtin" 956 ) 957 @pytest.mark.skipif( 958 not _ENVIRON_PYTHONBREAKPOINT == "", 959 reason="Requires breakpoint() default value", 960 ) 961 def test_sys_breakpoint_interception(self, testdir): 962 p1 = testdir.makepyfile( 963 """ 964 def test_1(): 965 breakpoint() 966 """ 967 ) 968 child = testdir.spawn_pytest(str(p1)) 969 child.expect("test_1") 970 child.expect("Pdb") 971 child.sendline("quit") 972 rest = child.read().decode("utf8") 973 assert "Quitting debugger" in rest 974 assert "reading from stdin while output" not in rest 975 TestPDB.flush(child) 976 977 @pytest.mark.skipif( 978 not SUPPORTS_BREAKPOINT_BUILTIN, reason="Requires breakpoint() builtin" 979 ) 980 def test_pdb_not_altered(self, testdir): 981 p1 = testdir.makepyfile( 982 """ 983 import pdb 984 def test_1(): 985 pdb.set_trace() 986 assert 0 987 """ 988 ) 989 child = testdir.spawn_pytest(str(p1)) 990 child.expect("test_1") 991 child.expect("Pdb") 992 child.sendline("c") 993 rest = child.read().decode("utf8") 994 assert "1 failed" in rest 995 assert "reading from stdin while output" not in rest 996 TestPDB.flush(child) 997 998 999class TestTraceOption: 1000 def test_trace_sets_breakpoint(self, testdir): 1001 p1 = testdir.makepyfile( 1002 """ 1003 def test_1(): 1004 assert True 1005 1006 def test_2(): 1007 pass 1008 1009 def test_3(): 1010 pass 1011 """ 1012 ) 1013 child = testdir.spawn_pytest("--trace " + str(p1)) 1014 child.expect("test_1") 1015 child.expect("Pdb") 1016 child.sendline("c") 1017 child.expect("test_2") 1018 child.expect("Pdb") 1019 child.sendline("c") 1020 child.expect("test_3") 1021 child.expect("Pdb") 1022 child.sendline("q") 1023 child.expect_exact("Exit: Quitting debugger") 1024 rest = child.read().decode("utf8") 1025 assert "2 passed in" in rest 1026 assert "reading from stdin while output" not in rest 1027 # Only printed once - not on stderr. 1028 assert "Exit: Quitting debugger" not in child.before.decode("utf8") 1029 TestPDB.flush(child) 1030 1031 1032def test_trace_after_runpytest(testdir): 1033 """Test that debugging's pytest_configure is re-entrant.""" 1034 p1 = testdir.makepyfile( 1035 """ 1036 from _pytest.debugging import pytestPDB 1037 1038 def test_outer(testdir): 1039 assert len(pytestPDB._saved) == 1 1040 1041 testdir.makepyfile( 1042 \""" 1043 from _pytest.debugging import pytestPDB 1044 1045 def test_inner(): 1046 assert len(pytestPDB._saved) == 2 1047 print() 1048 print("test_inner_" + "end") 1049 \""" 1050 ) 1051 1052 result = testdir.runpytest("-s", "-k", "test_inner") 1053 assert result.ret == 0 1054 1055 assert len(pytestPDB._saved) == 1 1056 """ 1057 ) 1058 result = testdir.runpytest_subprocess("-s", "-p", "pytester", str(p1)) 1059 result.stdout.fnmatch_lines(["test_inner_end"]) 1060 assert result.ret == 0 1061 1062 1063def test_quit_with_swallowed_SystemExit(testdir): 1064 """Test that debugging's pytest_configure is re-entrant.""" 1065 p1 = testdir.makepyfile( 1066 """ 1067 def call_pdb_set_trace(): 1068 __import__('pdb').set_trace() 1069 1070 1071 def test_1(): 1072 try: 1073 call_pdb_set_trace() 1074 except SystemExit: 1075 pass 1076 1077 1078 def test_2(): 1079 pass 1080 """ 1081 ) 1082 child = testdir.spawn_pytest(str(p1)) 1083 child.expect("Pdb") 1084 child.sendline("q") 1085 child.expect_exact("Exit: Quitting debugger") 1086 rest = child.read().decode("utf8") 1087 assert "no tests ran" in rest 1088 TestPDB.flush(child) 1089 1090 1091@pytest.mark.parametrize("fixture", ("capfd", "capsys")) 1092def test_pdb_suspends_fixture_capturing(testdir, fixture): 1093 """Using "-s" with pytest should suspend/resume fixture capturing.""" 1094 p1 = testdir.makepyfile( 1095 """ 1096 def test_inner({fixture}): 1097 import sys 1098 1099 print("out_inner_before") 1100 sys.stderr.write("err_inner_before\\n") 1101 1102 __import__("pdb").set_trace() 1103 1104 print("out_inner_after") 1105 sys.stderr.write("err_inner_after\\n") 1106 1107 out, err = {fixture}.readouterr() 1108 assert out =="out_inner_before\\nout_inner_after\\n" 1109 assert err =="err_inner_before\\nerr_inner_after\\n" 1110 """.format( 1111 fixture=fixture 1112 ) 1113 ) 1114 1115 child = testdir.spawn_pytest(str(p1) + " -s") 1116 1117 child.expect("Pdb") 1118 before = child.before.decode("utf8") 1119 assert ( 1120 "> PDB set_trace (IO-capturing turned off for fixture %s) >" % (fixture) 1121 in before 1122 ) 1123 1124 # Test that capturing is really suspended. 1125 child.sendline("p 40 + 2") 1126 child.expect("Pdb") 1127 assert "\r\n42\r\n" in child.before.decode("utf8") 1128 1129 child.sendline("c") 1130 rest = child.read().decode("utf8") 1131 assert "out_inner" not in rest 1132 assert "err_inner" not in rest 1133 1134 TestPDB.flush(child) 1135 assert child.exitstatus == 0 1136 assert "= 1 passed in " in rest 1137 assert "> PDB continue (IO-capturing resumed for fixture %s) >" % (fixture) in rest 1138 1139 1140def test_pdbcls_via_local_module(testdir): 1141 """It should be imported in pytest_configure or later only.""" 1142 p1 = testdir.makepyfile( 1143 """ 1144 def test(): 1145 print("before_set_trace") 1146 __import__("pdb").set_trace() 1147 """, 1148 mypdb=""" 1149 class Wrapped: 1150 class MyPdb: 1151 def set_trace(self, *args): 1152 print("set_trace_called", args) 1153 1154 def runcall(self, *args, **kwds): 1155 print("runcall_called", args, kwds) 1156 assert "func" in kwds 1157 """, 1158 ) 1159 result = testdir.runpytest( 1160 str(p1), "--pdbcls=really.invalid:Value", syspathinsert=True 1161 ) 1162 result.stdout.fnmatch_lines( 1163 [ 1164 "*= FAILURES =*", 1165 "E * --pdbcls: could not import 'really.invalid:Value': No module named *really*", 1166 ] 1167 ) 1168 assert result.ret == 1 1169 1170 result = testdir.runpytest( 1171 str(p1), "--pdbcls=mypdb:Wrapped.MyPdb", syspathinsert=True 1172 ) 1173 assert result.ret == 0 1174 result.stdout.fnmatch_lines(["*set_trace_called*", "* 1 passed in *"]) 1175 1176 # Ensure that it also works with --trace. 1177 result = testdir.runpytest( 1178 str(p1), "--pdbcls=mypdb:Wrapped.MyPdb", "--trace", syspathinsert=True 1179 ) 1180 assert result.ret == 0 1181 result.stdout.fnmatch_lines(["*runcall_called*", "* 1 passed in *"]) 1182 1183 1184def test_raises_bdbquit_with_eoferror(testdir): 1185 """It is not guaranteed that DontReadFromInput's read is called.""" 1186 if six.PY2: 1187 builtin_module = "__builtin__" 1188 input_func = "raw_input" 1189 else: 1190 builtin_module = "builtins" 1191 input_func = "input" 1192 p1 = testdir.makepyfile( 1193 """ 1194 def input_without_read(*args, **kwargs): 1195 raise EOFError() 1196 1197 def test(monkeypatch): 1198 import {builtin_module} 1199 monkeypatch.setattr({builtin_module}, {input_func!r}, input_without_read) 1200 __import__('pdb').set_trace() 1201 """.format( 1202 builtin_module=builtin_module, input_func=input_func 1203 ) 1204 ) 1205 result = testdir.runpytest(str(p1)) 1206 result.stdout.fnmatch_lines(["E *BdbQuit", "*= 1 failed in*"]) 1207 assert result.ret == 1 1208 1209 1210def test_pdb_wrapper_class_is_reused(testdir): 1211 p1 = testdir.makepyfile( 1212 """ 1213 def test(): 1214 __import__("pdb").set_trace() 1215 __import__("pdb").set_trace() 1216 1217 import mypdb 1218 instances = mypdb.instances 1219 assert len(instances) == 2 1220 assert instances[0].__class__ is instances[1].__class__ 1221 """, 1222 mypdb=""" 1223 instances = [] 1224 1225 class MyPdb: 1226 def __init__(self, *args, **kwargs): 1227 instances.append(self) 1228 1229 def set_trace(self, *args): 1230 print("set_trace_called", args) 1231 """, 1232 ) 1233 result = testdir.runpytest(str(p1), "--pdbcls=mypdb:MyPdb", syspathinsert=True) 1234 assert result.ret == 0 1235 result.stdout.fnmatch_lines( 1236 ["*set_trace_called*", "*set_trace_called*", "* 1 passed in *"] 1237 ) 1238