This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
toke.c: move leftbracket label
[perl5.git] / lib / perl5db.t
index fe39292..b6936b2 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl
+#!./perl
 
 BEGIN {
     chdir 't' if -d 't';
@@ -8,67 +8,900 @@ BEGIN {
 
 use strict;
 use warnings;
+use Config;
 
 BEGIN {
-    if (!-c "/dev/null") {
-       print "1..0 # Skip: no /dev/null\n";
-       exit 0;
+    if (! -c "/dev/null") {
+        print "1..0 # Skip: no /dev/null\n";
+        exit 0;
     }
-    if (!-c "/dev/tty") {
-       print "1..0 # Skip: no /dev/tty\n";
-       exit 0;
+
+    my $dev_tty = '/dev/tty';
+    $dev_tty = 'TT:' if ($^O eq 'VMS');
+    if (! -c $dev_tty) {
+        print "1..0 # Skip: no $dev_tty\n";
+        exit 0;
+    }
+    if ($ENV{PERL5DB}) {
+        print "1..0 # Skip: \$ENV{PERL5DB} is already set to '$ENV{PERL5DB}'\n";
+        exit 0;
     }
 }
 
-plan(1);
+plan(34);
+
+my $rc_filename = '.perldb';
 
 sub rc {
-    open RC, ">", ".perldb" or die $!;
-    print RC @_;
-    close(RC);
+    open my $rc_fh, '>', $rc_filename
+        or die $!;
+    print {$rc_fh} @_;
+    close ($rc_fh);
+
     # overly permissive perms gives "Must not source insecure rcfile"
     # and hangs at the DB(1> prompt
-    chmod 0644, ".perldb";
+    chmod 0644, $rc_filename;
 }
 
-my $target = '../lib/perl5db/t/eval-line-bug';
+sub _slurp
+{
+    my $filename = shift;
+
+    open my $in, '<', $filename
+        or die "Cannot open '$filename' for slurping - $!";
+
+    local $/;
+    my $contents = <$in>;
+
+    close($in);
+
+    return $contents;
+}
+
+my $out_fn = 'db.out';
+
+sub _out_contents
+{
+    return _slurp($out_fn);
+}
 
-rc(
-    qq|
+{
+    my $target = '../lib/perl5db/t/eval-line-bug';
+
+    rc(
+        <<"EOF",
     &parse_options("NonStop=0 TTY=db.out LineInfo=db.out");
-    \n|,
 
-    qq|
     sub afterinit {
-       push(\@DB::typeahead,
-           'b 23',
-           'n',
-           'n',
-           'n',
-           'c', # line 23
-           'n',
-           "p \\\@{'main::_<$target'}",
-           'q',
-       );
-    }\n|,
-);
+        push(\@DB::typeahead,
+            'b 23',
+            'n',
+            'n',
+            'n',
+            'c', # line 23
+            'n',
+            "p \\\@{'main::_<$target'}",
+            'q',
+        );
+    }
+EOF
+    );
+
+    {
+        local $ENV{PERLDB_OPTS} = "ReadLine=0";
+        runperl(switches => [ '-d' ], progfile => $target);
+    }
+}
 
-runperl(switches => [ '-d' ], progfile => $target);
+like(_out_contents(), qr/sub factorial/,
+    'The ${main::_<filename} variable in the debugger was not destroyed'
+);
 
-my $contents;
 {
-    local $/;
-    open I, "<", 'db.out' or die $!;
-    $contents = <I>;
-    close(I);
+    my $target = '../lib/perl5db/t/eval-line-bug';
+
+    rc(
+        <<"EOF",
+    &parse_options("NonStop=0 TTY=db.out LineInfo=db.out");
+
+    sub afterinit {
+        push(\@DB::typeahead,
+            'b 23',
+            'c',
+            '\$new_var = "Foo"',
+            'x "new_var = <\$new_var>\\n";',
+            'q',
+        );
+    }
+EOF
+    );
+
+    {
+        local $ENV{PERLDB_OPTS} = "ReadLine=0";
+        runperl(switches => [ '-d' ], progfile => $target);
+    }
 }
 
-like($contents, qr/sub factorial/,
-    'The ${main::_<filename} variable in the debugger was not destroyed'
+like(_out_contents(), qr/new_var = <Foo>/,
+    "no strict 'vars' in evaluated lines.",
 );
 
+{
+    local $ENV{PERLDB_OPTS} = "ReadLine=0";
+    my $output = runperl(switches => [ '-d' ], progfile => '../lib/perl5db/t/lvalue-bug');
+    like($output, qr/foo is defined/, 'lvalue subs work in the debugger');
+}
+
+{
+    local $ENV{PERLDB_OPTS} = "ReadLine=0 NonStop=1";
+    my $output = runperl(switches => [ '-d' ], progfile => '../lib/perl5db/t/symbol-table-bug');
+    like($output, qr/Undefined symbols 0/, 'there are no undefined values in the symbol table');
+}
+
+SKIP: {
+    if ( $Config{usethreads} ) {
+        skip('This perl has threads, skipping non-threaded debugger tests');
+    } else {
+        my $error = 'This Perl not built to support threads';
+        my $output = runperl( switches => [ '-dt' ], stderr => 1 );
+        like($output, qr/$error/, 'Perl debugger correctly complains that it was not built with threads');
+    }
+
+}
+SKIP: {
+    if ( $Config{usethreads} ) {
+        local $ENV{PERLDB_OPTS} = "ReadLine=0 NonStop=1";
+        my $output = runperl(switches => [ '-dt' ], progfile => '../lib/perl5db/t/symbol-table-bug');
+        like($output, qr/Undefined symbols 0/, 'there are no undefined values in the symbol table when running with thread support');
+    } else {
+        skip("This perl is not threaded, skipping threaded debugger tests");
+    }
+}
+
+
+# Test [perl #61222]
+{
+    local $ENV{PERLDB_OPTS};
+    rc(
+        <<'EOF',
+        &parse_options("NonStop=0 TTY=db.out LineInfo=db.out");
+
+        sub afterinit {
+            push(@DB::typeahead,
+                'm Pie',
+                'q',
+            );
+        }
+EOF
+    );
+
+    my $output = runperl(switches => [ '-d' ], stderr => 1, progfile => '../lib/perl5db/t/rt-61222');
+    unlike(_out_contents(), qr/INCORRECT/, "[perl #61222]");
+}
+
+
+
+# Test for Proxy constants
+{
+    rc(
+        <<'EOF',
+
+&parse_options("NonStop=0 ReadLine=0 TTY=db.out LineInfo=db.out");
+
+sub afterinit {
+    push(@DB::typeahead,
+        'm main->s1',
+        'q',
+    );
+}
+
+EOF
+    );
+
+    my $output = runperl(switches => [ '-d' ], stderr => 1, progfile => '../lib/perl5db/t/proxy-constants');
+    is($output, "", "proxy constant subroutines");
+}
+
+# [perl #66110] Call a subroutine inside a regex
+{
+    local $ENV{PERLDB_OPTS} = "ReadLine=0 NonStop=1";
+    my $output = runperl(switches => [ '-d' ], stderr => 1, progfile => '../lib/perl5db/t/rt-66110');
+    like($output, "All tests successful.", "[perl #66110]");
+}
+
+# [perl 104168] level option for tracing
+{
+    rc(<<'EOF');
+&parse_options("NonStop=0 TTY=db.out LineInfo=db.out");
+
+sub afterinit {
+    push (@DB::typeahead,
+    't 2',
+    'c',
+    'q',
+    );
+
+}
+EOF
+
+    my $output = runperl(switches => [ '-d' ], stderr => 1, progfile => '../lib/perl5db/t/rt-104168');
+    my $contents = _out_contents();
+    like($contents, qr/level 2/, "[perl #104168]");
+    unlike($contents, qr/baz/, "[perl #104168]");
+}
+
+# taint tests
+
+{
+    local $ENV{PERLDB_OPTS} = "ReadLine=0 NonStop=1";
+    my $output = runperl(switches => [ '-d', '-T' ], stderr => 1,
+        progfile => '../lib/perl5db/t/taint');
+    chomp $output if $^O eq 'VMS'; # newline guaranteed at EOF
+    is($output, '[$^X][done]', "taint");
+}
+
+package DebugWrap;
+
+sub new {
+    my $class = shift;
+
+    my $self = bless {}, $class;
+
+    $self->_init(@_);
+
+    return $self;
+}
+
+sub _cmds {
+    my $self = shift;
+
+    if (@_) {
+        $self->{_cmds} = shift;
+    }
+
+    return $self->{_cmds};
+}
+
+sub _prog {
+    my $self = shift;
+
+    if (@_) {
+        $self->{_prog} = shift;
+    }
+
+    return $self->{_prog};
+}
+
+sub _output {
+    my $self = shift;
+
+    if (@_) {
+        $self->{_output} = shift;
+    }
+
+    return $self->{_output};
+}
+
+sub _include_t
+{
+    my $self = shift;
+
+    if (@_)
+    {
+        $self->{_include_t} = shift;
+    }
+
+    return $self->{_include_t};
+}
+
+sub _contents
+{
+    my $self = shift;
+
+    if (@_)
+    {
+        $self->{_contents} = shift;
+    }
+
+    return $self->{_contents};
+}
+
+sub _init
+{
+    my ($self, $args) = @_;
+
+    my $cmds = $args->{cmds};
+
+    if (ref($cmds) ne 'ARRAY') {
+        die "cmds must be an array of commands.";
+    }
+
+    $self->_cmds($cmds);
+
+    my $prog = $args->{prog};
+
+    if (ref($prog) ne '' or !defined($prog)) {
+        die "prog should be a path to a program file.";
+    }
+
+    $self->_prog($prog);
+
+    $self->_include_t($args->{include_t} ? 1 : 0);
+
+    $self->_run();
+
+    return;
+}
+
+sub _quote
+{
+    my ($self, $str) = @_;
+
+    $str =~ s/(["\@\$\\])/\\$1/g;
+    $str =~ s/\n/\\n/g;
+    $str =~ s/\r/\\r/g;
+
+    return qq{"$str"};
+}
+
+sub _run {
+    my $self = shift;
+
+    my $rc = qq{&parse_options("NonStop=0 TTY=db.out LineInfo=db.out");\n};
+
+    $rc .= join('',
+        map { "$_\n"}
+        (q#sub afterinit {#,
+         q#push (@DB::typeahead,#,
+         (map { $self->_quote($_) . "," } @{$self->_cmds()}),
+         q#);#,
+         q#}#,
+        )
+    );
+
+    # I guess two objects like that cannot be used at the same time.
+    # Oh well.
+    ::rc($rc);
+
+    my $output =
+        ::runperl(
+            switches =>
+            [
+                '-d', 
+                ($self->_include_t ? ('-I', '../lib/perl5db/t') : ())
+            ],
+            stderr => 1,
+            progfile => $self->_prog()
+        );
+
+    $self->_output($output);
+
+    $self->_contents(::_out_contents());
+
+    return;
+}
+
+sub output_like {
+    my ($self, $re, $msg) = @_;
+
+    local $::Level = $::Level + 1;
+    ::like($self->_output(), $re, $msg);
+}
+
+sub contents_like {
+    my ($self, $re, $msg) = @_;
+
+    local $::Level = $::Level + 1;
+    ::like($self->_contents(), $re, $msg);
+}
+
+package main;
+
+# Testing that we can set a line in the middle of the file.
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                'b ../lib/perl5db/t/MyModule.pm:12',
+                'c',
+                q/do { use IO::Handle; STDOUT->autoflush(1); print "Var=$var\n"; }/,
+                'c',
+                'q',
+            ],
+            include_t => 1,
+            prog => '../lib/perl5db/t/filename-line-breakpoint'
+        }
+    );
+
+    $wrapper->output_like(qr/
+        ^Var=Bar$
+            .*
+        ^In\ MyModule\.$
+            .*
+        ^In\ Main\ File\.$
+            .*
+        /msx,
+        "Can set breakpoint in a line in the middle of the file.");
+}
+
+# Testing that we can set a breakpoint
+{
+    my $wrapper = DebugWrap->new(
+        {
+            prog => '../lib/perl5db/t/breakpoint-bug',
+            cmds =>
+            [
+                'b 6',
+                'c',
+                q/do { use IO::Handle; STDOUT->autoflush(1); print "X={$x}\n"; }/,
+                'c',
+                'q',
+            ],
+        },
+    );
+
+    $wrapper->output_like(
+        qr/X=\{Two\}/msx,
+        "Can set breakpoint in a line."
+    );
+}
+
+# Testing that we can disable a breakpoint at a numeric line.
+{
+    my $wrapper = DebugWrap->new(
+        {
+            prog =>  '../lib/perl5db/t/disable-breakpoints-1',
+            cmds =>
+            [
+                'b 7',
+                'b 11',
+                'disable 7',
+                'c',
+                q/print "X={$x}\n";/,
+                'c',
+                'q',
+            ],
+        }
+    );
+
+    $wrapper->output_like(qr/X=\{SecondVal\}/ms,
+        "Can set breakpoint in a line.");
+}
+
+# Testing that we can re-enable a breakpoint at a numeric line.
+{
+    my $wrapper = DebugWrap->new(
+        {
+            prog =>  '../lib/perl5db/t/disable-breakpoints-2',
+            cmds =>
+            [
+                'b 8',
+                'b 24',
+                'disable 24',
+                'c',
+                'enable 24',
+                'c',
+                q/print "X={$x}\n";/,
+                'c',
+                'q',
+            ],
+        },
+    );
+
+    $wrapper->output_like(
+        qr/
+        X=\{SecondValOneHundred\}
+        /msx,
+        "Can set breakpoint in a line."
+    );
+}
 # clean up.
 
+# Disable and enable for breakpoints on outer files.
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                'b 10',
+                'b ../lib/perl5db/t/EnableModule.pm:14',
+                'disable ../lib/perl5db/t/EnableModule.pm:14',
+                'c',
+                'enable ../lib/perl5db/t/EnableModule.pm:14',
+                'c',
+                q/print "X={$x}\n";/,
+                'c',
+                'q',
+            ],
+            prog =>  '../lib/perl5db/t/disable-breakpoints-3',
+            include_t => 1,
+        }
+    );
+
+    $wrapper->output_like(qr/
+        X=\{SecondValTwoHundred\}
+        /msx,
+        "Can set breakpoint in a line.");
+}
+
+# Testing that the prompt with the information appears.
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds => ['q'],
+            prog => '../lib/perl5db/t/disable-breakpoints-1',
+        }
+    );
+
+    $wrapper->contents_like(qr/
+        ^main::\([^\)]*\bdisable-breakpoints-1:2\):\n
+        2:\s+my\ \$x\ =\ "One";\n
+        /msx,
+        "Prompt should display the first line of code.");
+}
+
+# Testing that R (restart) and "B *" work.
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                'b 13',
+                'c',
+                'B *',
+                'b 9',
+                'R',
+                'c',
+                q/print "X={$x};dummy={$dummy}\n";/,
+                'q',
+            ],
+            prog =>  '../lib/perl5db/t/disable-breakpoints-1',
+        }
+    );
+
+    $wrapper->output_like(qr/
+        X=\{FirstVal\};dummy=\{1\}
+        /msx,
+        "Restart and delete all breakpoints work properly.");
+}
+
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                'c 15',
+                q/print "X={$x}\n";/,
+                'c',
+                'q',
+            ],
+            prog =>  '../lib/perl5db/t/disable-breakpoints-1',
+        }
+    );
+
+    $wrapper->output_like(qr/
+        X=\{ThirdVal\}
+        /msx,
+        "'c line_num' is working properly.");
+}
+
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                'n',
+                'n',
+                'b . $exp > 200',
+                'c',
+                q/print "Exp={$exp}\n";/,
+                'q',
+            ],
+            prog => '../lib/perl5db/t/break-on-dot',
+        }
+    );
+
+    $wrapper->output_like(qr/
+        Exp=\{256\}
+        /msx,
+        "'b .' is working correctly.");
+}
+
+# Testing that the prompt with the information appears inside a subroutine call.
+# See https://rt.perl.org/rt3/Ticket/Display.html?id=104820
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                'c back',
+                'q',
+            ],
+            prog => '../lib/perl5db/t/with-subroutine',
+        }
+    );
+
+    $wrapper->contents_like(
+        qr/
+        ^main::back\([^\)\n]*\bwith-subroutine:15\):[\ \t]*\n
+        ^15:\s*print\ "hello\ back\\n";
+        /msx,
+        "Prompt should display the line of code inside a subroutine.");
+}
+
+# Checking that the p command works.
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                'p "<<<" . (4*6) . ">>>"',
+                'q',
+            ],
+            prog => '../lib/perl5db/t/with-subroutine',
+        }
+    );
+
+    $wrapper->contents_like(
+        qr/<<<24>>>/,
+        "p command works.");
+}
+
+# Tests for x.
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                q/x {500 => 600}/,
+                'q',
+            ],
+            prog => '../lib/perl5db/t/with-subroutine',
+        }
+    );
+
+    $wrapper->contents_like(
+        # qr/^0\s+HASH\([^\)]+\)\n\s+500 => 600\n/,
+        qr/^0\s+HASH\([^\)]+\)\n\s+500 => 600\n/ms,
+        "x command test."
+    );
+}
+
+# Tests for "T" (stack trace).
+{
+    my $prog_fn = '../lib/perl5db/t/rt-104168';
+    my $wrapper = DebugWrap->new(
+        {
+            prog => $prog_fn,
+            cmds =>
+            [
+                'c baz',
+                'T',
+                'q',
+            ],
+        }
+    );
+    my $re_text = join('',
+        map {
+        sprintf(
+            "%s = %s\\(\\) called from file " .
+            "'" . quotemeta($prog_fn) . "' line %s\\n",
+            (map { quotemeta($_) } @$_)
+            )
+        } 
+        (
+            ['.', 'main::baz', 14,],
+            ['.', 'main::bar', 9,],
+            ['.', 'main::foo', 6]
+        )
+    );
+    $wrapper->contents_like(
+        # qr/^0\s+HASH\([^\)]+\)\n\s+500 => 600\n/,
+        qr/^$re_text/ms,
+        "T command test."
+    );
+}
+
+# Test for s.
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                'b 9',
+                'c',
+                's',
+                q/print "X={$x};dummy={$dummy}\n";/,
+                'q',
+            ],
+            prog => '../lib/perl5db/t/disable-breakpoints-1'
+        }
+    );
+
+    $wrapper->output_like(qr/
+        X=\{SecondVal\};dummy=\{1\}
+        /msx,
+        'test for s - single step',
+    );
+}
+
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                'n',
+                'n',
+                'b . $exp > 200',
+                'c',
+                q/print "Exp={$exp}\n";/,
+                'q',
+            ],
+            prog => '../lib/perl5db/t/break-on-dot'
+        }
+    );
+
+    $wrapper->output_like(qr/
+        Exp=\{256\}
+        /msx,
+        "'b .' is working correctly.");
+}
+
+{
+    my $prog_fn = '../lib/perl5db/t/rt-104168';
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                's',
+                'q',
+            ],
+            prog => $prog_fn,
+        }
+    );
+
+    $wrapper->contents_like(
+        qr/
+        ^main::foo\([^\)\n]*\brt-104168:9\):[\ \t]*\n
+        ^9:\s*bar\(\);
+        /msx,
+        'Test for the s command.',
+    );
+}
+
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                's uncalled_subroutine()',
+                'c',
+                'q',
+            ],
+
+            prog => '../lib/perl5db/t/uncalled-subroutine'}
+    );
+
+    $wrapper->output_like(
+        qr/<1,2,3,4,5>\n/,
+        'uncalled_subroutine was called after s EXPR()',
+        );
+}
+
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                'n uncalled_subroutine()',
+                'c',
+                'q',
+            ],
+            prog => '../lib/perl5db/t/uncalled-subroutine',
+        }
+    );
+
+    $wrapper->output_like(
+        qr/<1,2,3,4,5>\n/,
+        'uncalled_subroutine was called after n EXPR()',
+        );
+}
+
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                'b fact',
+                'c',
+                'c',
+                'c',
+                'n',
+                'print "<$n>"',
+                'q',
+            ],
+            prog => '../lib/perl5db/t/fact',
+        }
+    );
+
+    $wrapper->output_like(
+        qr/<3>/,
+        'b subroutine works fine',
+    );
+}
+
+# Test for 'M' (module list).
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                'M',
+                'q',
+            ],
+            prog => '../lib/perl5db/t/load-modules'
+        }
+    );
+
+    $wrapper->contents_like(
+        qr[Scalar/Util\.pm],
+        'M (module list) works fine',
+    );
+}
+
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                'b 14',
+                'c',
+                '$flag = 1;',
+                'r',
+                'print "Var=$var\n";',
+                'q',
+            ],
+            prog => '../lib/perl5db/t/test-r-statement',
+        }
+    );
+
+    $wrapper->output_like(
+        qr/
+            ^Foo$
+                .*?
+            ^Bar$
+                .*?
+            ^Var=Test$
+        /msx,
+        'r statement is working properly.',
+    );
+}
+
+{
+    my $wrapper = DebugWrap->new(
+        {
+            cmds =>
+            [
+                'l',
+                'q',
+            ],
+            prog => '../lib/perl5db/t/test-l-statement-1',
+        }
+    );
+
+    $wrapper->contents_like(
+        qr/
+            ^1==>\s+\$x\ =\ 1;\n
+            2:\s+print\ "1\\n";\n
+            3\s*\n
+            4:\s+\$x\ =\ 2;\n
+            5:\s+print\ "2\\n";\n
+        /msx,
+        'l statement is working properly (test No. 1).',
+    );
+}
+
 END {
-    unlink qw(.perldb db.out);
+    1 while unlink ($rc_filename, $out_fn);
 }