Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(946)

Side by Side Diff: Lib/test/test_pdb.py

Issue 14913: tokenize the source to manage Pdb breakpoints
Patch Set: Created 11 months, 1 week ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « Lib/pdb.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # A test suite for pdb; not very comprehensive at the moment. 1 # A test suite for pdb; not very comprehensive at the moment.
2 2
3 import imp 3 import imp
4 import bdb
4 import pdb 5 import pdb
5 import sys 6 import sys
6 import unittest 7 import unittest
7 import subprocess 8 import subprocess
8 import textwrap 9 import textwrap
10 import time
11 try:
12 import threading
13 except ImportError:
14 threading = None
9 15
10 from test import support 16 from test import support
11 # This little helper class is essential for testing pdb under doctest. 17 # This little helper class is essential for testing pdb under doctest.
12 from test.test_doctest import _FakeInput 18 from test.test_doctest import _FakeInput
13 19
14 20
15 class PdbTestInput(object): 21 class PdbTestInput(object):
16 """Context manager that makes testing Pdb in doctests easier.""" 22 """Context manager that makes testing Pdb in doctests easier."""
17 23
18 def __init__(self, input): 24 def __init__(self, input):
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 Breakpoint 4 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:5 271 Breakpoint 4 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:5
266 (Pdb) continue 272 (Pdb) continue
267 2 273 2
268 Deleted breakpoint 4 at <doctest test.test_pdb.test_pdb_breakpoint_commands[ 0]>:5 274 Deleted breakpoint 4 at <doctest test.test_pdb.test_pdb_breakpoint_commands[ 0]>:5
269 > <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>(5)test_function() 275 > <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>(5)test_function()
270 -> print(3) 276 -> print(3)
271 (Pdb) break 277 (Pdb) break
272 (Pdb) continue 278 (Pdb) continue
273 3 279 3
274 4 280 4
281 """
282
283
284 def test_issue_14792():
285 """
286 >>> def foo():
287 ... x = 1
288 ... x = 2
289
290 >>> def test_function():
291 ... import pdb; pdb.Pdb(nosigint=True).set_trace()
292 ... foo()
293
294 >>> with PdbTestInput([
295 ... 'step',
296 ... 'step',
297 ... 'break foo',
298 ... 'continue',
299 ... ]):
300 ... test_function()
301 > <doctest test.test_pdb.test_issue_14792[1]>(3)test_function()
302 -> foo()
303 (Pdb) step
304 --Call--
305 > <doctest test.test_pdb.test_issue_14792[0]>(1)foo()
306 -> def foo():
307 (Pdb) step
308 > <doctest test.test_pdb.test_issue_14792[0]>(2)foo()
309 -> x = 1
310 (Pdb) break foo
311 Breakpoint 1 at <doctest test.test_pdb.test_issue_14792[0]>:2
312 (Pdb) continue
275 """ 313 """
276 314
277 315
278 def do_nothing(): 316 def do_nothing():
279 pass 317 pass
280 318
281 def do_something(): 319 def do_something():
282 print(42) 320 print(42)
283 321
284 def test_list_commands(): 322 def test_list_commands():
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after
588 (Pdb) continue 626 (Pdb) continue
589 627
590 >>> with PdbTestInput(['x', 'continue']): 628 >>> with PdbTestInput(['x', 'continue']):
591 ... x=0 629 ... x=0
592 ... pdb_invoke('runeval', compile('x+1', '<string>', 'eval')) 630 ... pdb_invoke('runeval', compile('x+1', '<string>', 'eval'))
593 > <string>(1)<module>()->None 631 > <string>(1)<module>()->None
594 (Pdb) x 632 (Pdb) x
595 1 633 1
596 (Pdb) continue 634 (Pdb) continue
597 """ 635 """
636
637
638 def normalize(result, filename='', strip_bp_lnum=False):
639 """Normalize a test result."""
640 lines = []
641 for line in result.splitlines():
642 while line.startswith('(Pdb) ') or line.startswith('(com) '):
643 line = line[6:]
644 words = line.split()
645 line = []
646 # Replace tabs with spaces
647 for word in words:
648 if filename:
649 idx = word.find(filename)
650 # Remove the filename prefix
651 if idx > 0:
652 word = word[idx:]
653 if idx >=0 and strip_bp_lnum:
654 idx = word.find(':')
655 # Remove the ':' separator and breakpoint line number
656 if idx > 0:
657 word = word[:idx]
658 line.append(word)
659 line = ' '.join(line)
660 lines.append(line.strip())
661 return '\n'.join(lines)
598 662
599 663
600 class PdbTestCase(unittest.TestCase): 664 class PdbTestCase(unittest.TestCase):
601 665
602 def run_pdb(self, script, commands): 666 def run_pdb(self, script, commands, filename):
603 """Run 'script' lines with pdb and the pdb 'commands'.""" 667 """Run 'script' lines with pdb and the pdb 'commands'."""
604 filename = 'main.py'
605 with open(filename, 'w') as f: 668 with open(filename, 'w') as f:
606 f.write(textwrap.dedent(script)) 669 f.write(textwrap.dedent(script))
607 self.addCleanup(support.unlink, filename) 670 self.addCleanup(support.unlink, filename)
608 cmd = [sys.executable, '-m', 'pdb', filename] 671 cmd = [sys.executable, '-m', 'pdb', filename]
609 stdout = stderr = None 672 stdout = stderr = None
610 with subprocess.Popen(cmd, stdout=subprocess.PIPE, 673 with subprocess.Popen(cmd, stdout=subprocess.PIPE,
611 stdin=subprocess.PIPE, 674 stdin=subprocess.PIPE,
612 stderr=subprocess.STDOUT, 675 stderr=subprocess.PIPE,
613 ) as proc: 676 ) as proc:
614 stdout, stderr = proc.communicate(str.encode(commands)) 677 stdout, stderr = proc.communicate(str.encode(commands))
615 stdout = stdout and bytes.decode(stdout) 678 stdout = stdout and bytes.decode(stdout)
616 stderr = stderr and bytes.decode(stderr) 679 stderr = stderr and bytes.decode(stderr)
617 return stdout, stderr 680 return stdout, stderr
618 681
619 def test_issue7964(self): 682 def test_issue7964(self):
620 # open the file as binary so we can force \r\n newline 683 # open the file as binary so we can force \r\n newline
621 with open(support.TESTFN, 'wb') as f: 684 with open(support.TESTFN, 'wb') as f:
622 f.write(b'print("testing my pdb")\r\n') 685 f.write(b'print("testing my pdb")\r\n')
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
655 step 718 step
656 quit 719 quit
657 """ 720 """
658 bar = """ 721 bar = """
659 def bar(): 722 def bar():
660 pass 723 pass
661 """ 724 """
662 with open('bar.py', 'w') as f: 725 with open('bar.py', 'w') as f:
663 f.write(textwrap.dedent(bar)) 726 f.write(textwrap.dedent(bar))
664 self.addCleanup(support.unlink, 'bar.py') 727 self.addCleanup(support.unlink, 'bar.py')
665 stdout, stderr = self.run_pdb(script, commands) 728 stdout, stderr = self.run_pdb(script, commands, 'main.py')
666 self.assertTrue( 729 self.assertTrue(
667 any('main.py(5)foo()->None' in l for l in stdout.splitlines()), 730 any('main.py(5)foo()->None' in l for l in stdout.splitlines()),
668 'Fail to step into the caller after a return') 731 'Fail to step into the caller after a return')
669 732
733 def test_bdb_module_parser(self):
734 script = """
735 '''Module documentation.'''
736 X = None
737
738 def bar(a, b):
739 # comment
740 pass # comment
741 pass
742 # comment
743
744 def main(x=None,
745 y=None):
746 '''Function documentation.'''
747 def nested_def():
748 pass
749
750 bar(x,
751 y)
752 # comment
753 pass
754 bar(
755 x, y # comment
756 )
757 class NestedClass:
758 def n_foo(self):
759 pass
760 pass
761
762 return bar(
763 x, y
764 )
765
766 class C:
767 '''Class documentation.'''
768 x = None
769 y = None
770
771 class D:
772 def d_foo(self):
773 pass
774 class E:
775 def e_foo(self):
776 pass
777
778 def c_bar(self):
779 pass
780
781 c_bar2 = c_bar
782 c_bar3 = c_bar
783
784 def foo():
785 pass
786
787 if __name__ == '__main__':
788 result = main(
789 1, 2
790 )
791 print(result)
792
793 """
794 expected = """
795 Functions:
796 [('bar', 5),
797 ('main', 11),
798 ('C.D.d_foo', 39),
799 ('C.D.E.e_foo', 42),
800 ('C.c_bar', 45),
801 ('foo', 51)]
802 Functions and classes firstlineno:
803 [(5, 5),
804 (11, 11),
805 (14, 11),
806 (24, 11),
807 (25, 11),
808 (33, 0),
809 (38, 0),
810 (39, 39),
811 (41, 0),
812 (42, 42),
813 (45, 45),
814 (51, 51)]
815 Last line: 59
816 Module lines:
817 [(3, 3, 1, 0),
818 (33, 36, 1, 0),
819 (38, 38, 1, 0),
820 (41, 41, 1, 0),
821 (48, 49, 1, 0),
822 (54, 54, 1, 0),
823 (55, 57, 2, 0),
824 (58, 58, 1, 0)]
825 Functions lines:
826 [(5, 5, 0, 5), (7, 8, 1, 5)]
827 [(11, 12, 0, 11),
828 (14, 15, 1, 11),
829 (17, 18, 2, 11),
830 (20, 20, 1, 11),
831 (21, 23, 2, 11),
832 (24, 27, 1, 11),
833 (29, 31, 2, 11)]
834 [(39, 39, 0, 39), (40, 40, 1, 39)]
835 [(42, 42, 0, 42), (43, 43, 1, 42)]
836 [(45, 45, 0, 45), (46, 46, 1, 45)]
837 [(51, 51, 0, 51), (52, 52, 1, 51)]
838 """
839 filename = 'main.py'
840 with open(filename, 'w') as f:
841 f.write(textwrap.dedent(script))
842 self.addCleanup(support.unlink, filename)
843 result = str(bdb.ModuleSource(filename))
844 expected = textwrap.dedent(expected)
845 self.assertTrue(result in expected,
846 '\n\nExpected:\n{}\nGot:\n{}\n'
847 'Fail to parse correctly a module.'.format(expected, result))
848
849 def test_breakpoints_set_on_non_statement(self):
850 script = """
851 def foo():
852
853 # comment
854 x = 1
855
856 x = len(
857 'foo'
858 )
859
860 foo()
861 """
862 commands = """
863 break 3
864 break 4
865 break 8
866 continue
867 break
868 continue
869 quit
870 """
871 expected = """
872 > main.py(2)<module>()
873 -> def foo():
874 Breakpoint 1 at main.py:3
875 Breakpoint 2 at main.py:4
876 Breakpoint 3 at main.py:8
877 > main.py(5)foo()
878 -> x = 1
879 Num Type Disp Enb Where
880 1 breakpoint keep yes at main.py:3
881 breakpoint already hit 1 time
882 2 breakpoint keep yes at main.py:4
883 breakpoint already hit 1 time
884 3 breakpoint keep yes at main.py:8
885 > main.py(7)foo()
886 -> x = len(
887 """
888 filename = 'main.py'
889 stdout, stderr = self.run_pdb(script, commands, filename)
890 stdout = normalize(stdout, filename)
891 expected = normalize(expected)
892 self.assertTrue(stdout in expected,
893 '\n\nExpected:\n{}\nGot:\n{}\n'
894 'Fail to stop at breakpoint set at empty line, comment line or'
895 ' multi-line statement.'.format(expected, stdout))
896
897 def test_issue14789(self):
898 script = """
899 def bar(a):
900 x = 1
901
902 bar(10)
903 bar(20)
904 """
905 commands = """
906 break bar
907 commands 1
908 print a
909 end
910 ignore 1 1
911 break bar
912 commands 2
913 print a + 1
914 end
915 ignore 2 1
916 continue
917 break
918 quit
919 """
920 expected = """
921 > main.py(2)<module>()
922 -> def bar(a):
923 Breakpoint 1 at main.py:3
924 Will ignore next 1 crossing of breakpoint 1.
925 Breakpoint 2 at main.py:3
926 Will ignore next 1 crossing of breakpoint 2.
927 20
928 21
929 > main.py(3)bar()
930 -> x = 1
931 Num Type Disp Enb Where
932 1 breakpoint keep yes at main.py:3
933 breakpoint already hit 2 times
934 2 breakpoint keep yes at main.py:3
935 breakpoint already hit 2 times
936 """
937 filename = 'main.py'
938 stdout, stderr = self.run_pdb(script, commands, filename)
939 stdout = normalize(stdout, filename)
940 expected = normalize(expected)
941 self.assertTrue(stdout in expected,
942 '\n\nExpected:\n{}\nGot:\n{}\n'
943 'Fail to handle two breakpoints set on the same line.'.format(
944 expected, stdout))
945
946 def test_issue14795(self):
947 script = """
948 class C:
949 def foo(self):
950 pass
951 """
952 commands = """
953 break C.foo
954 break bar.bar
955 quit
956 """
957 bar = """
958 def bar():
959 pass
960 """
961 expected = """
962 > main.py(2)<module>()
963 -> class C:
964 Breakpoint 1 at main.py:4
965 Breakpoint 2 at bar.py:3
966 """
967 with open('bar.py', 'w') as f:
968 f.write(textwrap.dedent(bar))
969 self.addCleanup(support.unlink, 'bar.py')
970 filename = 'main.py'
971 stdout, stderr = self.run_pdb(script, commands, filename)
972 stdout = normalize(normalize(stdout, filename), 'bar.py')
973 expected = normalize(expected)
974 self.assertTrue(stdout in expected,
975 '\n\nExpected:\n{}\nGot:\n{}\n'
976 'Fail to set a breakpoint in a method, or in a function whose'
977 ' module is not yet imported.'.format(expected, stdout))
978
979 def test_issue_14808(self):
980 script = """
981 def foo():
982 pass
983
984 def bar():
985 pass
986
987 foo()
988 bar()
989 """
990 commands = """
991 break 2
992 break bar
993 continue
994 continue
995 break
996 quit
997 """
998 expected = """
999 > main.py(2)<module>()
1000 -> def foo():
1001 Breakpoint 1 at main.py:2
1002 Breakpoint 2 at main.py:6
1003 > main.py(3)foo()
1004 -> pass
1005 > main.py(6)bar()
1006 -> pass
1007 Num Type Disp Enb Where
1008 1 breakpoint keep yes at main.py:2
1009 breakpoint already hit 1 time
1010 2 breakpoint keep yes at main.py:6
1011 breakpoint already hit 1 time
1012 """
1013 filename = 'main.py'
1014 stdout, stderr = self.run_pdb(script, commands, filename)
1015 stdout = normalize(stdout, filename)
1016 expected = normalize(expected)
1017 self.assertTrue(stdout in expected,
1018 '\n\nExpected:\n{}\nGot:\n{}\n'
1019 'Fail to stop at breakpoint set at function definition line'
1020 ' or at function name.'.format(expected, stdout))
1021
1022 @unittest.skipIf(not threading, 'the source file is changed in a thread')
1023 def test_issue_14912(self):
1024 script = """
1025 import bar
1026
1027 def foo():
1028 bar.bar()
1029
1030 foo()
1031 """
1032 commands = """
1033 break bar.bar
1034 continue
1035 list
1036 import time; time.sleep(2)
1037 restart
1038 continue
1039 list
1040 quit
1041 """
1042 bar = """
1043 def bar():
1044 x = 1
1045 x = 2
1046 """
1047 bar_2 = """
1048 def bar():
1049 # comment
1050 # comment
1051 x = 1
1052 x = 2
1053 """
1054 expected = """
1055 > main.py(2)<module>()
1056 -> import bar
1057 Breakpoint 1 at bar.py:3
1058 > bar.py(3)bar()
1059 -> x = 1
1060 1
1061 2 def bar():
1062 3 B-> x = 1
1063 4 x = 2
1064 [EOF]
1065 Restarting main.py with arguments:
1066 main.py
1067 > main.py(2)<module>()
1068 -> import bar
1069 > bar.py(5)bar()
1070 -> x = 1
1071 1
1072 2 def bar():
1073 3 B # comment
1074 4 # comment
1075 5 -> x = 1
1076 6 x = 2
1077 [EOF]
1078 """
1079 with open('bar.py', 'w') as f:
1080 f.write(textwrap.dedent(bar))
1081 self.addCleanup(support.unlink, 'bar.py')
1082 filename = 'main.py'
1083 with open(filename, 'w') as f:
1084 f.write(textwrap.dedent(script))
1085 self.addCleanup(support.unlink, filename)
1086 cmd = [sys.executable, '-m', 'pdb', filename]
1087 stdout = stderr = None
1088 with subprocess.Popen(cmd, stdout=subprocess.PIPE,
1089 stdin=subprocess.PIPE,
1090 stderr=subprocess.PIPE,
1091 ) as proc:
1092 def update_source():
1093 # Wait for the Pdb subprocess to be fully started.
1094 time.sleep(1)
1095 with open('bar.py', 'w') as f:
1096 f.write(textwrap.dedent(bar_2))
1097 threading.Thread(target=update_source).start()
1098 stdout, stderr = proc.communicate(str.encode(commands))
1099 stdout = stdout and bytes.decode(stdout)
1100 stderr = stderr and bytes.decode(stderr)
1101 stdout = normalize(normalize(stdout, filename), 'bar.py')
1102 expected = normalize(expected, 'bar.py')
1103 self.assertTrue(stdout in expected,
1104 '\n\nExpected:\n{}\nGot:\n{}\n'
1105 'Fail to restart a new session after the source of an imported'
1106 ' module has been modified.'.format(expected, stdout))
1107
1108 def test_set_breakpoint_by_function_name(self):
1109 script = """
1110 class C:
1111 c_foo = 1
1112
1113 class D:
1114 def d_foo(self):
1115 pass
1116
1117 def foo():
1118 pass
1119
1120 not_a_function = 1
1121 foo()
1122 """
1123 commands = """
1124 break C
1125 break C.c_foo
1126 break D.d_foo
1127 break foo
1128 break not_a_function
1129 break len
1130 break logging.handlers.SocketHandler.close
1131 continue
1132 break C
1133 break C.c_foo
1134 break D.d_foo
1135 break foo
1136 break not_a_function
1137 quit
1138 """
1139 expected = """
1140 > main.py(2)<module>()
1141 -> class C:
1142 *** Bad name: "C".
1143 *** Bad name: "C.c_foo".
1144 Breakpoint 1 at main.py:7
1145 Breakpoint 2 at main.py:10
1146 *** Bad name: "not_a_function".
1147 *** Cannot set a breakpoint at "len"
1148 Breakpoint 3 at handlers.py:<LINE_NUMBER>
1149 > main.py(10)foo()
1150 -> pass
1151 *** Bad function name: "C".
1152 *** Cannot set a breakpoint at "C.c_foo"
1153 Breakpoint 4 at main.py:7
1154 Breakpoint 5 at main.py:10
1155 *** Cannot set a breakpoint at "not_a_function"
1156 """
1157 filename = 'main.py'
1158 stdout, stderr = self.run_pdb(script, commands, filename)
1159 stdout = normalize(normalize(stdout, 'handlers.py', strip_bp_lnum=True), filename)
1160 expected = normalize(expected, 'handlers.py', strip_bp_lnum=True)
1161 self.assertTrue(stdout in expected,
1162 '\n\nExpected:\n{}\nGot:\n{}\n'
1163 'Fail to handle a breakpoint set by function name.'.format(expected, stdout))
1164
1165 def test_breakpoint_set_by_line_and_funcname(self):
1166 script = """
1167 def foo():
1168 pass
1169
1170 foo()
1171 """
1172 commands = """
1173 break foo
1174 break 2
1175 continue
1176 break
1177 quit
1178 """
1179 expected = """
1180 > main.py(2)<module>()
1181 -> def foo():
1182 Breakpoint 1 at main.py:3
1183 Breakpoint 2 at main.py:2
1184 > main.py(3)foo()
1185 -> pass
1186 Num Type Disp Enb Where
1187 1 breakpoint keep yes at main.py:3
1188 breakpoint already hit 1 time
1189 2 breakpoint keep yes at main.py:2
1190 breakpoint already hit 1 time
1191 """
1192 filename = 'main.py'
1193 stdout, stderr = self.run_pdb(script, commands, filename)
1194 stdout = normalize(stdout, filename)
1195 expected = normalize(expected)
1196 self.assertTrue(stdout in expected,
1197 '\n\nExpected:\n{}\nGot:\n{}\n'
1198 'Failed hits for breakpoints set by line number and by function'
1199 ' name.'.format(expected, stdout))
1200
670 def tearDown(self): 1201 def tearDown(self):
671 support.unlink(support.TESTFN) 1202 support.unlink(support.TESTFN)
672 1203
673 1204
674 def test_main(): 1205 def test_main():
675 from test import test_pdb 1206 from test import test_pdb
676 support.run_doctest(test_pdb, verbosity=True) 1207 support.run_doctest(test_pdb, verbosity=True)
677 support.run_unittest(PdbTestCase) 1208 support.run_unittest(PdbTestCase)
678 1209
679 1210
680 if __name__ == '__main__': 1211 if __name__ == '__main__':
681 test_main() 1212 test_main()
OLDNEW
« no previous file with comments | « Lib/pdb.py ('k') | no next file » | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld cbc36f91f3f7