The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!perl -T

use strict;
use warnings;

use Test::More tests => 3 + 7 * 2 + 8;

use Scope::Upper qw<uplevel HERE UP>;

sub depth {
 my $depth = 0;
 while (1) {
  my @c = caller($depth);
  last unless @c;
  ++$depth;
 }
 return $depth - 1;
}

is depth(),                           0, 'check top depth';
is sub { depth() }->(),               1, 'check subroutine call depth';
is do { local $@; eval { depth() } }, 1, 'check eval block depth';

{
 my $desc = 'uplevel in uplevel : lower frame';
 local $@;
 my @ret = eval {
  1, sub {
   is depth(), 2, "$desc: correct depth 1";
   2, uplevel(sub {
    is depth(), 2, "$desc: correct depth 2";
    3, sub {
     is depth(), 3, "$desc: correct depth 3";
     4, uplevel(sub {
      is depth(), 3, "$desc: correct depth 4";
      return 5, @_;
     }, 6, @_, HERE);
    }->(7, @_);
   }, 8, @_, HERE);
  }->(9);
 };
 is $@,      '',              "$desc: no error";
 is depth(), 0,               "$desc: correct depth outside";
 is_deeply \@ret, [ 1 .. 9 ], "$desc: correct return value"
}

{
 my $desc = 'uplevel in uplevel : same frame';
 local $@;
 my @ret = eval {
  11, sub {
   is depth(), 2, "$desc: correct depth 1";
   12, uplevel(sub {
    is depth(), 2, "$desc: correct depth 2";
    13, sub {
     is depth(), 3, "$desc: correct depth 3";
     14, uplevel(sub {
      is depth(), 2, "$desc: correct depth 4";
      return 15, @_;
     }, 16, @_, UP);
    }->(17, @_);
   }, 18, @_, HERE);
  }->(19);
 };
 is $@,      '',                "$desc: no error";
 is depth(), 0,                 "$desc: correct depth outside";
 is_deeply \@ret, [ 11 .. 19 ], "$desc: correct return value"
}

{
 my $desc = 'uplevel in uplevel : higher frame';
 local $@;
 my @ret = eval {
  20, sub {
   is depth(), 2, "$desc: correct depth 1";
   21, sub {
    is depth(), 3, "$desc: correct depth 2";
    22, uplevel(sub {
     is depth(), 3, "$desc: correct depth 3";
     23, sub {
      is depth(), 4, "$desc: correct depth 4";
      24, uplevel(sub {
       is depth(), 2, "$desc: correct depth 5";
       return 25, @_;
      }, 26, @_, UP UP);
     }->(27, @_);
    }, 28, @_, HERE);
   }->(29, @_);
  }->('2A');
 };
 is $@,      '',                      "$desc: no error";
 is depth(), 0,                       "$desc: correct depth outside";
 is_deeply \@ret, [ 20 .. 29, '2A' ], "$desc: correct return value"
}