]> git.vpit.fr Git - perl/modules/Scope-Upper.git/blobdiff - t/85-stress-unwind.t
Do less function calls in Scope::Upper::TestGenerator::gen()
[perl/modules/Scope-Upper.git] / t / 85-stress-unwind.t
index 75bad3e18dbefe98b3a409c4cb496d7d161cbfaf..5242747a024f7d96a5cf60a4d1dcfd3ae56eafe7 100644 (file)
@@ -3,18 +3,36 @@
 use strict;
 use warnings;
 
-use Test::More 'no_plan';
+use lib 't/lib';
+use Test::Leaner 'no_plan';
 
-use Scope::Upper qw/unwind/;
+use Scope::Upper qw<unwind UP HERE>;
 
-our ($call, @args, $args);
+# perl 5.8.0 is not happy when @args is a lexical, so we have to use a global.
+# It's slightly faster too.
 
-$call = sub {
+our @args;
+
+my $args_code;
+if ("$]" < 5.008) {
+ # perl 5.6.x is really bad at closures, hence make it compile a function call
+ # instead.
+ *_get_args = sub { @args };
+ $args_code = '_get_args()';
+} else {
+ $args_code = '@args';
+}
+
+my $call = sub {
  my ($height, $level, $i) = @_;
- return [ [ "unwind(\@args => $level)\n", '' ] ];
+ $level = $level ? 'UP ' x $level : 'HERE';
+ return [ [ "unwind($args_code => $level)\n", [ \@args ] ] ];
 };
 
-sub list { @_ }
+# @_[0 .. $#_] also ought to work, but it sometimes evaluates to nonsense in
+# scalar context on perl 5.8.5 and below.
+
+sub list { wantarray ? @_ : $_[$#_] }
 
 my @blocks = (
  [ 'sub {',     '}->()' ],
@@ -22,36 +40,52 @@ my @blocks = (
 );
 
 my @contexts = (
- [ '',        '; ()' ],
- [ 'scalar(', ')' ],
- [ 'list(',   ')' ],
+ [ '',        '; ()', 'v' ],
+ [ 'scalar(', ')',    's' ],
+ [ 'list(',   ')',    'l' ],
 );
 
-@blocks   = map [ map "$_\n", @$_ ], @blocks;
-@contexts = map [ map "$_\n", @$_ ], @contexts;
+for my $block (@blocks) {
+ $_ .= "\n" for @$block[0, 1];
+}
+for my $cxt (@contexts) {
+ $_ .= "\n" for @$cxt[0, 1];
+}
+
+sub contextify {
+ my ($cxt, $active, $exp, @items) = @_;
+ return $exp unless $active;
+ if ($cxt eq 'v') {
+  return [ ];
+ } elsif ($cxt eq 's') {
+  return [ $cxt, @$exp ];
+ } else {
+  return [ @items, @$exp ];
+ }
+}
+
+my $integer = 0;
+my $items   = 0;
 
 sub gen {
  my ($height, $level, $i) = @_;
  push @_, $i = 0 if @_ == 2;
  my @res;
  my $up = $i == $height + 1 ? $call->(@_) : gen($height, $level, $i + 1);
- if ($i + $level == $height + 1) {
-  for (@$up) {
-   $_->[1] = "return($args)\n";
-  }
- }
+ my $active = $i <= ($height - $level);
  for my $base (@$up) {
   my ($code, $exp) = @$base;
   for my $blk (@blocks) {
    for my $cx (@contexts) {
     push @res, [
      $blk->[0] . $cx->[0] . $code . $cx->[1] . $blk->[1],
-     $blk->[0] . $cx->[0] . $exp .  $cx->[1] . $blk->[1],
+     contextify($cx->[2], $active, $exp),
     ];
-    my $list = join ', ', map { int rand 10 } 0 .. rand 3;
+    my @items = map $integer++, 0 .. ($items++ % 3);
+    my $list  = join ', ', @items;
     push @res, [
-     $blk->[0] . $cx->[0] . "($list, " . $code . ')' . $cx->[1] . $blk->[1],
-     $blk->[0] . $cx->[0] . "($list, " . $exp .  ')' . $cx->[1] . $blk->[1],
+     $blk->[0] . $cx->[0] . "($list, $code)" . $cx->[1] . $blk->[1],
+     contextify($cx->[2], $active, $exp, @items),
     ];
    }
   }
@@ -59,29 +93,56 @@ sub gen {
  return \@res;
 }
 
-sub runtests {
- my $tests = gen @_;
- for (@$tests) {
 no warnings 'void';
 my @res = eval $_->[0];
-  my @exp = eval $_->[1] unless $@;
-  if ($@ || !is_deeply \@res, \@exp) {
-   diag "=== vvv Test vvv ===";
-   diag $_->[0];
-   diag "------- Got --------";
-   diag join(', ', map { defined($_) ? $_ : '(undef)' } @res);
-   diag "----- Expected -----";
-   diag join(', ', map { defined($_) ? $_ : '(undef)' } @exp);
-   diag "=== ^^^^^^^^^^^^ ===";
+sub linearize { join ', ', map { defined($_) ? $_ : '(undef)' } @_ }
+
+sub expect {
my @spec = @{$_[0]};
my @acc;
+ for my $s (reverse @spec) {
+  if (ref $s) {
+   unshift @acc, @$s;
+  } elsif ($s =~ /^[0-9]+$/) {
+   unshift @acc, $s;
+  } elsif ($s eq 's') {
+   @acc = (@acc ? $acc[-1] : undef);
+  } else {
+   return 'XXX';
   }
  }
+ return linearize @acc;
 }
 
-for ([ ], [ 'A' ], [ qw/B C/ ]) {
- @args = @$_;
- $args = '(' . join(', ', map "'$_'", @args) . ')';
- runtests 0, 0;
- runtests 0, 1;
- runtests 1, 0;
- runtests 1, 1;
+my @arg_lists = ([ ], [ 'A' ], [ qw<B C> ]);
+
+for my $height (0 .. 1) {
+ for my $level (0 .. 1) {
+  my $i;
+  my $tests = gen $height, $level;
+  for (@$tests) {
+   my ($code, $exp_spec) = @$_;
+   ++$i;
+   my $desc = "stress unwind $height $level $i";
+   my $cb = do {
+    no warnings 'void';
+    eval "sub { $code }";
+   };
+   if ($@) {
+    fail "$desc : test did not compile" for 1 .. @arg_lists;
+   } else {
+    for (@arg_lists) {
+     @args = @$_;
+     my $res = linearize $cb->();
+     my $exp = expect $exp_spec;
+     if ($res ne $exp) {
+      diag <<DIAG;
+=== This testcase failed ===
+$code;
+==== vvvvv Errors vvvvvv ===
+DIAG
+     }
+     is $res, $exp, "$desc [@args]";
+    }
+   }
+  }
+ }
 }