This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
b6936b26b0d0d5e9ba81f89ebd9c116d92393fc3
[perl5.git] / lib / perl5db.t
1 #!./perl
2
3 BEGIN {
4     chdir 't' if -d 't';
5     @INC = '../lib';
6     require './test.pl';
7 }
8
9 use strict;
10 use warnings;
11 use Config;
12
13 BEGIN {
14     if (! -c "/dev/null") {
15         print "1..0 # Skip: no /dev/null\n";
16         exit 0;
17     }
18
19     my $dev_tty = '/dev/tty';
20     $dev_tty = 'TT:' if ($^O eq 'VMS');
21     if (! -c $dev_tty) {
22         print "1..0 # Skip: no $dev_tty\n";
23         exit 0;
24     }
25     if ($ENV{PERL5DB}) {
26         print "1..0 # Skip: \$ENV{PERL5DB} is already set to '$ENV{PERL5DB}'\n";
27         exit 0;
28     }
29 }
30
31 plan(34);
32
33 my $rc_filename = '.perldb';
34
35 sub rc {
36     open my $rc_fh, '>', $rc_filename
37         or die $!;
38     print {$rc_fh} @_;
39     close ($rc_fh);
40
41     # overly permissive perms gives "Must not source insecure rcfile"
42     # and hangs at the DB(1> prompt
43     chmod 0644, $rc_filename;
44 }
45
46 sub _slurp
47 {
48     my $filename = shift;
49
50     open my $in, '<', $filename
51         or die "Cannot open '$filename' for slurping - $!";
52
53     local $/;
54     my $contents = <$in>;
55
56     close($in);
57
58     return $contents;
59 }
60
61 my $out_fn = 'db.out';
62
63 sub _out_contents
64 {
65     return _slurp($out_fn);
66 }
67
68 {
69     my $target = '../lib/perl5db/t/eval-line-bug';
70
71     rc(
72         <<"EOF",
73     &parse_options("NonStop=0 TTY=db.out LineInfo=db.out");
74
75     sub afterinit {
76         push(\@DB::typeahead,
77             'b 23',
78             'n',
79             'n',
80             'n',
81             'c', # line 23
82             'n',
83             "p \\\@{'main::_<$target'}",
84             'q',
85         );
86     }
87 EOF
88     );
89
90     {
91         local $ENV{PERLDB_OPTS} = "ReadLine=0";
92         runperl(switches => [ '-d' ], progfile => $target);
93     }
94 }
95
96 like(_out_contents(), qr/sub factorial/,
97     'The ${main::_<filename} variable in the debugger was not destroyed'
98 );
99
100 {
101     my $target = '../lib/perl5db/t/eval-line-bug';
102
103     rc(
104         <<"EOF",
105     &parse_options("NonStop=0 TTY=db.out LineInfo=db.out");
106
107     sub afterinit {
108         push(\@DB::typeahead,
109             'b 23',
110             'c',
111             '\$new_var = "Foo"',
112             'x "new_var = <\$new_var>\\n";',
113             'q',
114         );
115     }
116 EOF
117     );
118
119     {
120         local $ENV{PERLDB_OPTS} = "ReadLine=0";
121         runperl(switches => [ '-d' ], progfile => $target);
122     }
123 }
124
125 like(_out_contents(), qr/new_var = <Foo>/,
126     "no strict 'vars' in evaluated lines.",
127 );
128
129 {
130     local $ENV{PERLDB_OPTS} = "ReadLine=0";
131     my $output = runperl(switches => [ '-d' ], progfile => '../lib/perl5db/t/lvalue-bug');
132     like($output, qr/foo is defined/, 'lvalue subs work in the debugger');
133 }
134
135 {
136     local $ENV{PERLDB_OPTS} = "ReadLine=0 NonStop=1";
137     my $output = runperl(switches => [ '-d' ], progfile => '../lib/perl5db/t/symbol-table-bug');
138     like($output, qr/Undefined symbols 0/, 'there are no undefined values in the symbol table');
139 }
140
141 SKIP: {
142     if ( $Config{usethreads} ) {
143         skip('This perl has threads, skipping non-threaded debugger tests');
144     } else {
145         my $error = 'This Perl not built to support threads';
146         my $output = runperl( switches => [ '-dt' ], stderr => 1 );
147         like($output, qr/$error/, 'Perl debugger correctly complains that it was not built with threads');
148     }
149
150 }
151 SKIP: {
152     if ( $Config{usethreads} ) {
153         local $ENV{PERLDB_OPTS} = "ReadLine=0 NonStop=1";
154         my $output = runperl(switches => [ '-dt' ], progfile => '../lib/perl5db/t/symbol-table-bug');
155         like($output, qr/Undefined symbols 0/, 'there are no undefined values in the symbol table when running with thread support');
156     } else {
157         skip("This perl is not threaded, skipping threaded debugger tests");
158     }
159 }
160
161
162 # Test [perl #61222]
163 {
164     local $ENV{PERLDB_OPTS};
165     rc(
166         <<'EOF',
167         &parse_options("NonStop=0 TTY=db.out LineInfo=db.out");
168
169         sub afterinit {
170             push(@DB::typeahead,
171                 'm Pie',
172                 'q',
173             );
174         }
175 EOF
176     );
177
178     my $output = runperl(switches => [ '-d' ], stderr => 1, progfile => '../lib/perl5db/t/rt-61222');
179     unlike(_out_contents(), qr/INCORRECT/, "[perl #61222]");
180 }
181
182
183
184 # Test for Proxy constants
185 {
186     rc(
187         <<'EOF',
188
189 &parse_options("NonStop=0 ReadLine=0 TTY=db.out LineInfo=db.out");
190
191 sub afterinit {
192     push(@DB::typeahead,
193         'm main->s1',
194         'q',
195     );
196 }
197
198 EOF
199     );
200
201     my $output = runperl(switches => [ '-d' ], stderr => 1, progfile => '../lib/perl5db/t/proxy-constants');
202     is($output, "", "proxy constant subroutines");
203 }
204
205 # [perl #66110] Call a subroutine inside a regex
206 {
207     local $ENV{PERLDB_OPTS} = "ReadLine=0 NonStop=1";
208     my $output = runperl(switches => [ '-d' ], stderr => 1, progfile => '../lib/perl5db/t/rt-66110');
209     like($output, "All tests successful.", "[perl #66110]");
210 }
211
212 # [perl 104168] level option for tracing
213 {
214     rc(<<'EOF');
215 &parse_options("NonStop=0 TTY=db.out LineInfo=db.out");
216
217 sub afterinit {
218     push (@DB::typeahead,
219     't 2',
220     'c',
221     'q',
222     );
223
224 }
225 EOF
226
227     my $output = runperl(switches => [ '-d' ], stderr => 1, progfile => '../lib/perl5db/t/rt-104168');
228     my $contents = _out_contents();
229     like($contents, qr/level 2/, "[perl #104168]");
230     unlike($contents, qr/baz/, "[perl #104168]");
231 }
232
233 # taint tests
234
235 {
236     local $ENV{PERLDB_OPTS} = "ReadLine=0 NonStop=1";
237     my $output = runperl(switches => [ '-d', '-T' ], stderr => 1,
238         progfile => '../lib/perl5db/t/taint');
239     chomp $output if $^O eq 'VMS'; # newline guaranteed at EOF
240     is($output, '[$^X][done]', "taint");
241 }
242
243 package DebugWrap;
244
245 sub new {
246     my $class = shift;
247
248     my $self = bless {}, $class;
249
250     $self->_init(@_);
251
252     return $self;
253 }
254
255 sub _cmds {
256     my $self = shift;
257
258     if (@_) {
259         $self->{_cmds} = shift;
260     }
261
262     return $self->{_cmds};
263 }
264
265 sub _prog {
266     my $self = shift;
267
268     if (@_) {
269         $self->{_prog} = shift;
270     }
271
272     return $self->{_prog};
273 }
274
275 sub _output {
276     my $self = shift;
277
278     if (@_) {
279         $self->{_output} = shift;
280     }
281
282     return $self->{_output};
283 }
284
285 sub _include_t
286 {
287     my $self = shift;
288
289     if (@_)
290     {
291         $self->{_include_t} = shift;
292     }
293
294     return $self->{_include_t};
295 }
296
297 sub _contents
298 {
299     my $self = shift;
300
301     if (@_)
302     {
303         $self->{_contents} = shift;
304     }
305
306     return $self->{_contents};
307 }
308
309 sub _init
310 {
311     my ($self, $args) = @_;
312
313     my $cmds = $args->{cmds};
314
315     if (ref($cmds) ne 'ARRAY') {
316         die "cmds must be an array of commands.";
317     }
318
319     $self->_cmds($cmds);
320
321     my $prog = $args->{prog};
322
323     if (ref($prog) ne '' or !defined($prog)) {
324         die "prog should be a path to a program file.";
325     }
326
327     $self->_prog($prog);
328
329     $self->_include_t($args->{include_t} ? 1 : 0);
330
331     $self->_run();
332
333     return;
334 }
335
336 sub _quote
337 {
338     my ($self, $str) = @_;
339
340     $str =~ s/(["\@\$\\])/\\$1/g;
341     $str =~ s/\n/\\n/g;
342     $str =~ s/\r/\\r/g;
343
344     return qq{"$str"};
345 }
346
347 sub _run {
348     my $self = shift;
349
350     my $rc = qq{&parse_options("NonStop=0 TTY=db.out LineInfo=db.out");\n};
351
352     $rc .= join('',
353         map { "$_\n"}
354         (q#sub afterinit {#,
355          q#push (@DB::typeahead,#,
356          (map { $self->_quote($_) . "," } @{$self->_cmds()}),
357          q#);#,
358          q#}#,
359         )
360     );
361
362     # I guess two objects like that cannot be used at the same time.
363     # Oh well.
364     ::rc($rc);
365
366     my $output =
367         ::runperl(
368             switches =>
369             [
370                 '-d', 
371                 ($self->_include_t ? ('-I', '../lib/perl5db/t') : ())
372             ],
373             stderr => 1,
374             progfile => $self->_prog()
375         );
376
377     $self->_output($output);
378
379     $self->_contents(::_out_contents());
380
381     return;
382 }
383
384 sub output_like {
385     my ($self, $re, $msg) = @_;
386
387     local $::Level = $::Level + 1;
388     ::like($self->_output(), $re, $msg);
389 }
390
391 sub contents_like {
392     my ($self, $re, $msg) = @_;
393
394     local $::Level = $::Level + 1;
395     ::like($self->_contents(), $re, $msg);
396 }
397
398 package main;
399
400 # Testing that we can set a line in the middle of the file.
401 {
402     my $wrapper = DebugWrap->new(
403         {
404             cmds =>
405             [
406                 'b ../lib/perl5db/t/MyModule.pm:12',
407                 'c',
408                 q/do { use IO::Handle; STDOUT->autoflush(1); print "Var=$var\n"; }/,
409                 'c',
410                 'q',
411             ],
412             include_t => 1,
413             prog => '../lib/perl5db/t/filename-line-breakpoint'
414         }
415     );
416
417     $wrapper->output_like(qr/
418         ^Var=Bar$
419             .*
420         ^In\ MyModule\.$
421             .*
422         ^In\ Main\ File\.$
423             .*
424         /msx,
425         "Can set breakpoint in a line in the middle of the file.");
426 }
427
428 # Testing that we can set a breakpoint
429 {
430     my $wrapper = DebugWrap->new(
431         {
432             prog => '../lib/perl5db/t/breakpoint-bug',
433             cmds =>
434             [
435                 'b 6',
436                 'c',
437                 q/do { use IO::Handle; STDOUT->autoflush(1); print "X={$x}\n"; }/,
438                 'c',
439                 'q',
440             ],
441         },
442     );
443
444     $wrapper->output_like(
445         qr/X=\{Two\}/msx,
446         "Can set breakpoint in a line."
447     );
448 }
449
450 # Testing that we can disable a breakpoint at a numeric line.
451 {
452     my $wrapper = DebugWrap->new(
453         {
454             prog =>  '../lib/perl5db/t/disable-breakpoints-1',
455             cmds =>
456             [
457                 'b 7',
458                 'b 11',
459                 'disable 7',
460                 'c',
461                 q/print "X={$x}\n";/,
462                 'c',
463                 'q',
464             ],
465         }
466     );
467
468     $wrapper->output_like(qr/X=\{SecondVal\}/ms,
469         "Can set breakpoint in a line.");
470 }
471
472 # Testing that we can re-enable a breakpoint at a numeric line.
473 {
474     my $wrapper = DebugWrap->new(
475         {
476             prog =>  '../lib/perl5db/t/disable-breakpoints-2',
477             cmds =>
478             [
479                 'b 8',
480                 'b 24',
481                 'disable 24',
482                 'c',
483                 'enable 24',
484                 'c',
485                 q/print "X={$x}\n";/,
486                 'c',
487                 'q',
488             ],
489         },
490     );
491
492     $wrapper->output_like(
493         qr/
494         X=\{SecondValOneHundred\}
495         /msx,
496         "Can set breakpoint in a line."
497     );
498 }
499 # clean up.
500
501 # Disable and enable for breakpoints on outer files.
502 {
503     my $wrapper = DebugWrap->new(
504         {
505             cmds =>
506             [
507                 'b 10',
508                 'b ../lib/perl5db/t/EnableModule.pm:14',
509                 'disable ../lib/perl5db/t/EnableModule.pm:14',
510                 'c',
511                 'enable ../lib/perl5db/t/EnableModule.pm:14',
512                 'c',
513                 q/print "X={$x}\n";/,
514                 'c',
515                 'q',
516             ],
517             prog =>  '../lib/perl5db/t/disable-breakpoints-3',
518             include_t => 1,
519         }
520     );
521
522     $wrapper->output_like(qr/
523         X=\{SecondValTwoHundred\}
524         /msx,
525         "Can set breakpoint in a line.");
526 }
527
528 # Testing that the prompt with the information appears.
529 {
530     my $wrapper = DebugWrap->new(
531         {
532             cmds => ['q'],
533             prog => '../lib/perl5db/t/disable-breakpoints-1',
534         }
535     );
536
537     $wrapper->contents_like(qr/
538         ^main::\([^\)]*\bdisable-breakpoints-1:2\):\n
539         2:\s+my\ \$x\ =\ "One";\n
540         /msx,
541         "Prompt should display the first line of code.");
542 }
543
544 # Testing that R (restart) and "B *" work.
545 {
546     my $wrapper = DebugWrap->new(
547         {
548             cmds =>
549             [
550                 'b 13',
551                 'c',
552                 'B *',
553                 'b 9',
554                 'R',
555                 'c',
556                 q/print "X={$x};dummy={$dummy}\n";/,
557                 'q',
558             ],
559             prog =>  '../lib/perl5db/t/disable-breakpoints-1',
560         }
561     );
562
563     $wrapper->output_like(qr/
564         X=\{FirstVal\};dummy=\{1\}
565         /msx,
566         "Restart and delete all breakpoints work properly.");
567 }
568
569 {
570     my $wrapper = DebugWrap->new(
571         {
572             cmds =>
573             [
574                 'c 15',
575                 q/print "X={$x}\n";/,
576                 'c',
577                 'q',
578             ],
579             prog =>  '../lib/perl5db/t/disable-breakpoints-1',
580         }
581     );
582
583     $wrapper->output_like(qr/
584         X=\{ThirdVal\}
585         /msx,
586         "'c line_num' is working properly.");
587 }
588
589 {
590     my $wrapper = DebugWrap->new(
591         {
592             cmds =>
593             [
594                 'n',
595                 'n',
596                 'b . $exp > 200',
597                 'c',
598                 q/print "Exp={$exp}\n";/,
599                 'q',
600             ],
601             prog => '../lib/perl5db/t/break-on-dot',
602         }
603     );
604
605     $wrapper->output_like(qr/
606         Exp=\{256\}
607         /msx,
608         "'b .' is working correctly.");
609 }
610
611 # Testing that the prompt with the information appears inside a subroutine call.
612 # See https://rt.perl.org/rt3/Ticket/Display.html?id=104820
613 {
614     my $wrapper = DebugWrap->new(
615         {
616             cmds =>
617             [
618                 'c back',
619                 'q',
620             ],
621             prog => '../lib/perl5db/t/with-subroutine',
622         }
623     );
624
625     $wrapper->contents_like(
626         qr/
627         ^main::back\([^\)\n]*\bwith-subroutine:15\):[\ \t]*\n
628         ^15:\s*print\ "hello\ back\\n";
629         /msx,
630         "Prompt should display the line of code inside a subroutine.");
631 }
632
633 # Checking that the p command works.
634 {
635     my $wrapper = DebugWrap->new(
636         {
637             cmds =>
638             [
639                 'p "<<<" . (4*6) . ">>>"',
640                 'q',
641             ],
642             prog => '../lib/perl5db/t/with-subroutine',
643         }
644     );
645
646     $wrapper->contents_like(
647         qr/<<<24>>>/,
648         "p command works.");
649 }
650
651 # Tests for x.
652 {
653     my $wrapper = DebugWrap->new(
654         {
655             cmds =>
656             [
657                 q/x {500 => 600}/,
658                 'q',
659             ],
660             prog => '../lib/perl5db/t/with-subroutine',
661         }
662     );
663
664     $wrapper->contents_like(
665         # qr/^0\s+HASH\([^\)]+\)\n\s+500 => 600\n/,
666         qr/^0\s+HASH\([^\)]+\)\n\s+500 => 600\n/ms,
667         "x command test."
668     );
669 }
670
671 # Tests for "T" (stack trace).
672 {
673     my $prog_fn = '../lib/perl5db/t/rt-104168';
674     my $wrapper = DebugWrap->new(
675         {
676             prog => $prog_fn,
677             cmds =>
678             [
679                 'c baz',
680                 'T',
681                 'q',
682             ],
683         }
684     );
685     my $re_text = join('',
686         map {
687         sprintf(
688             "%s = %s\\(\\) called from file " .
689             "'" . quotemeta($prog_fn) . "' line %s\\n",
690             (map { quotemeta($_) } @$_)
691             )
692         } 
693         (
694             ['.', 'main::baz', 14,],
695             ['.', 'main::bar', 9,],
696             ['.', 'main::foo', 6]
697         )
698     );
699     $wrapper->contents_like(
700         # qr/^0\s+HASH\([^\)]+\)\n\s+500 => 600\n/,
701         qr/^$re_text/ms,
702         "T command test."
703     );
704 }
705
706 # Test for s.
707 {
708     my $wrapper = DebugWrap->new(
709         {
710             cmds =>
711             [
712                 'b 9',
713                 'c',
714                 's',
715                 q/print "X={$x};dummy={$dummy}\n";/,
716                 'q',
717             ],
718             prog => '../lib/perl5db/t/disable-breakpoints-1'
719         }
720     );
721
722     $wrapper->output_like(qr/
723         X=\{SecondVal\};dummy=\{1\}
724         /msx,
725         'test for s - single step',
726     );
727 }
728
729 {
730     my $wrapper = DebugWrap->new(
731         {
732             cmds =>
733             [
734                 'n',
735                 'n',
736                 'b . $exp > 200',
737                 'c',
738                 q/print "Exp={$exp}\n";/,
739                 'q',
740             ],
741             prog => '../lib/perl5db/t/break-on-dot'
742         }
743     );
744
745     $wrapper->output_like(qr/
746         Exp=\{256\}
747         /msx,
748         "'b .' is working correctly.");
749 }
750
751 {
752     my $prog_fn = '../lib/perl5db/t/rt-104168';
753     my $wrapper = DebugWrap->new(
754         {
755             cmds =>
756             [
757                 's',
758                 'q',
759             ],
760             prog => $prog_fn,
761         }
762     );
763
764     $wrapper->contents_like(
765         qr/
766         ^main::foo\([^\)\n]*\brt-104168:9\):[\ \t]*\n
767         ^9:\s*bar\(\);
768         /msx,
769         'Test for the s command.',
770     );
771 }
772
773 {
774     my $wrapper = DebugWrap->new(
775         {
776             cmds =>
777             [
778                 's uncalled_subroutine()',
779                 'c',
780                 'q',
781             ],
782
783             prog => '../lib/perl5db/t/uncalled-subroutine'}
784     );
785
786     $wrapper->output_like(
787         qr/<1,2,3,4,5>\n/,
788         'uncalled_subroutine was called after s EXPR()',
789         );
790 }
791
792 {
793     my $wrapper = DebugWrap->new(
794         {
795             cmds =>
796             [
797                 'n uncalled_subroutine()',
798                 'c',
799                 'q',
800             ],
801             prog => '../lib/perl5db/t/uncalled-subroutine',
802         }
803     );
804
805     $wrapper->output_like(
806         qr/<1,2,3,4,5>\n/,
807         'uncalled_subroutine was called after n EXPR()',
808         );
809 }
810
811 {
812     my $wrapper = DebugWrap->new(
813         {
814             cmds =>
815             [
816                 'b fact',
817                 'c',
818                 'c',
819                 'c',
820                 'n',
821                 'print "<$n>"',
822                 'q',
823             ],
824             prog => '../lib/perl5db/t/fact',
825         }
826     );
827
828     $wrapper->output_like(
829         qr/<3>/,
830         'b subroutine works fine',
831     );
832 }
833
834 # Test for 'M' (module list).
835 {
836     my $wrapper = DebugWrap->new(
837         {
838             cmds =>
839             [
840                 'M',
841                 'q',
842             ],
843             prog => '../lib/perl5db/t/load-modules'
844         }
845     );
846
847     $wrapper->contents_like(
848         qr[Scalar/Util\.pm],
849         'M (module list) works fine',
850     );
851 }
852
853 {
854     my $wrapper = DebugWrap->new(
855         {
856             cmds =>
857             [
858                 'b 14',
859                 'c',
860                 '$flag = 1;',
861                 'r',
862                 'print "Var=$var\n";',
863                 'q',
864             ],
865             prog => '../lib/perl5db/t/test-r-statement',
866         }
867     );
868
869     $wrapper->output_like(
870         qr/
871             ^Foo$
872                 .*?
873             ^Bar$
874                 .*?
875             ^Var=Test$
876         /msx,
877         'r statement is working properly.',
878     );
879 }
880
881 {
882     my $wrapper = DebugWrap->new(
883         {
884             cmds =>
885             [
886                 'l',
887                 'q',
888             ],
889             prog => '../lib/perl5db/t/test-l-statement-1',
890         }
891     );
892
893     $wrapper->contents_like(
894         qr/
895             ^1==>\s+\$x\ =\ 1;\n
896             2:\s+print\ "1\\n";\n
897             3\s*\n
898             4:\s+\$x\ =\ 2;\n
899             5:\s+print\ "2\\n";\n
900         /msx,
901         'l statement is working properly (test No. 1).',
902     );
903 }
904
905 END {
906     1 while unlink ($rc_filename, $out_fn);
907 }