Blame t/lib/Cases.pm

Packit d18d0a
package Cases;
Packit d18d0a
use strict;
Packit d18d0a
use warnings;
Packit d18d0a
use Test::More;
Packit d18d0a
use Capture::Tiny ':all';
Packit d18d0a
Packit d18d0a
require Exporter;
Packit d18d0a
our @ISA = 'Exporter';
Packit d18d0a
our @EXPORT_OK = qw(
Packit d18d0a
  run_test
Packit d18d0a
);
Packit d18d0a
Packit d18d0a
my $locale_ok = eval {
Packit d18d0a
    my $err = capture_stderr { system($^X, '-we', 1) };
Packit d18d0a
    $err !~ /setting locale failed/i;
Packit d18d0a
};
Packit d18d0a
Packit d18d0a
my $have_diff = eval {
Packit d18d0a
  require Test::Differences;
Packit d18d0a
  Test::Differences->import;
Packit d18d0a
  $Test::Differences::VERSION < 0.60; # 0.60+ is causing strange failures
Packit d18d0a
};
Packit d18d0a
Packit d18d0a
sub _is_or_diff {
Packit d18d0a
  my ($g,$e,$l) = @_;
Packit d18d0a
  if ( $have_diff ) { eq_or_diff( $g, $e, $l ); }
Packit d18d0a
  else { is( $g, $e, $l ); }
Packit d18d0a
}
Packit d18d0a
Packit d18d0a
sub _binmode {
Packit d18d0a
  my $text = shift;
Packit d18d0a
  return $text eq 'unicode' ? 'binmode(STDOUT,q{:utf8}); binmode(STDERR,q{:utf8});' : '';
Packit d18d0a
}
Packit d18d0a
Packit d18d0a
sub _set_utf8 {
Packit d18d0a
  my $t = shift;
Packit d18d0a
  return unless $t eq 'unicode';
Packit d18d0a
  my %seen;
Packit d18d0a
  my @orig_layers = (
Packit d18d0a
    [ grep {$_ ne 'unix' and $_ ne 'perlio' and $seen{stdout}{$_}++} PerlIO::get_layers(\*STDOUT) ],
Packit d18d0a
    [ grep {$_ ne 'unix' and $_ ne 'perlio' and $seen{stderr}{$_}++} PerlIO::get_layers(\*STDERR) ],
Packit d18d0a
  );
Packit d18d0a
  binmode(STDOUT, ":utf8") if fileno(STDOUT);
Packit d18d0a
  binmode(STDERR, ":utf8") if fileno(STDERR);
Packit d18d0a
  return @orig_layers;
Packit d18d0a
}
Packit d18d0a
Packit d18d0a
sub _restore_layers {
Packit d18d0a
  my ($t, @orig_layers) = @_;
Packit d18d0a
  return unless $t eq 'unicode';
Packit d18d0a
  binmode(STDOUT, join( ":", "", "raw", @{$orig_layers[0]})) if fileno(STDOUT);
Packit d18d0a
  binmode(STDERR, join( ":", "", "raw", @{$orig_layers[1]})) if fileno(STDERR);
Packit d18d0a
}
Packit d18d0a
Packit d18d0a
#--------------------------------------------------------------------------#
Packit d18d0a
Packit d18d0a
my %texts = (
Packit d18d0a
  short => 'Hello World',
Packit d18d0a
  multiline => 'First line\nSecond line\n',
Packit d18d0a
  ( $] lt "5.008" ? () : ( unicode => 'Hi! \x{263a}\n') ),
Packit d18d0a
);
Packit d18d0a
Packit d18d0a
#--------------------------------------------------------------------------#
Packit d18d0a
#  fcn($perl_code_string) => execute the perl in current process or subprocess
Packit d18d0a
#--------------------------------------------------------------------------#
Packit d18d0a
Packit d18d0a
my %methods = (
Packit d18d0a
  perl    => sub { eval $_[0] },
Packit d18d0a
  sys  => sub { system($^X, '-e', $_[0]) },
Packit d18d0a
);
Packit d18d0a
Packit d18d0a
#--------------------------------------------------------------------------#
Packit d18d0a
Packit d18d0a
my %channels = (
Packit d18d0a
  stdout  => {
Packit d18d0a
    output => sub { _binmode($_[0]) . "print STDOUT qq{STDOUT:$texts{$_[0]}}" },
Packit d18d0a
    expect => sub { eval "qq{STDOUT:$texts{$_[0]}}", "" },
Packit d18d0a
  },
Packit d18d0a
  stderr  => {
Packit d18d0a
    output => sub { _binmode($_[0]) . "print STDERR qq{STDERR:$texts{$_[0]}}" },
Packit d18d0a
    expect => sub { "", eval "qq{STDERR:$texts{$_[0]}}" },
Packit d18d0a
  },
Packit d18d0a
  both    => {
Packit d18d0a
    output => sub { _binmode($_[0]) . "print STDOUT qq{STDOUT:$texts{$_[0]}}; print STDERR qq{STDERR:$texts{$_[0]}}" },
Packit d18d0a
    expect => sub { eval "qq{STDOUT:$texts{$_[0]}}", eval "qq{STDERR:$texts{$_[0]}}" },
Packit d18d0a
  },
Packit d18d0a
  empty   => {
Packit d18d0a
    output => sub { _binmode($_[0]) . "print STDOUT qq{}; print STDERR qq{}" },
Packit d18d0a
    expect => sub { "", "" },
Packit d18d0a
  },
Packit d18d0a
  nooutput=> {
Packit d18d0a
    output => sub { _binmode($_[0]) },
Packit d18d0a
    expect => sub { "", "" },
Packit d18d0a
  },
Packit d18d0a
);
Packit d18d0a
Packit d18d0a
#--------------------------------------------------------------------------#
Packit d18d0a
Packit d18d0a
my %tests = (
Packit d18d0a
  capture => {
Packit d18d0a
    cnt   => 2,
Packit d18d0a
    test  => sub {
Packit d18d0a
      my ($m, $c, $t, $l) = @_;
Packit d18d0a
      my ($got_out, $got_err) = capture {
Packit d18d0a
        $methods{$m}->( $channels{$c}{output}->($t) );
Packit d18d0a
      };
Packit d18d0a
      my @expected = $channels{$c}{expect}->($t);
Packit d18d0a
      _is_or_diff( $got_out, $expected[0], "$l|$m|$c|$t - got STDOUT" );
Packit d18d0a
      _is_or_diff( $got_err, $expected[1], "$l|$m|$c|$t - got STDERR" );
Packit d18d0a
    },
Packit d18d0a
  },
Packit d18d0a
  capture_scalar => {
Packit d18d0a
    cnt   => 1,
Packit d18d0a
    test  => sub {
Packit d18d0a
      my ($m, $c, $t, $l) = @_;
Packit d18d0a
      my $got_out = capture {
Packit d18d0a
        $methods{$m}->( $channels{$c}{output}->($t) );
Packit d18d0a
      };
Packit d18d0a
      my @expected = $channels{$c}{expect}->($t);
Packit d18d0a
      _is_or_diff( $got_out, $expected[0], "$l|$m|$c|$t - got STDOUT" );
Packit d18d0a
    },
Packit d18d0a
  },
Packit d18d0a
  capture_stdout => {
Packit d18d0a
    cnt   => 3,
Packit d18d0a
    test  => sub {
Packit d18d0a
      my ($m, $c, $t, $l) = @_;
Packit d18d0a
      my ($inner_out, $inner_err);
Packit d18d0a
      my ($outer_out, $outer_err) = capture {
Packit d18d0a
        $inner_out = capture_stdout {
Packit d18d0a
          $methods{$m}->( $channels{$c}{output}->($t) );
Packit d18d0a
        };
Packit d18d0a
      };
Packit d18d0a
      my @expected = $channels{$c}{expect}->($t);
Packit d18d0a
      _is_or_diff( $inner_out, $expected[0], "$l|$m|$c|$t - inner STDOUT" );
Packit d18d0a
      _is_or_diff( $outer_out, "",           "$l|$m|$c|$t - outer STDOUT" );
Packit d18d0a
      _is_or_diff( $outer_err, $expected[1], "$l|$m|$c|$t - outer STDERR" );
Packit d18d0a
    },
Packit d18d0a
  },
Packit d18d0a
  capture_stderr => {
Packit d18d0a
    cnt   => 3,
Packit d18d0a
    test  => sub {
Packit d18d0a
      my ($m, $c, $t, $l) = @_;
Packit d18d0a
      my ($inner_out, $inner_err);
Packit d18d0a
      my ($outer_out, $outer_err) = capture {
Packit d18d0a
        $inner_err = capture_stderr {
Packit d18d0a
          $methods{$m}->( $channels{$c}{output}->($t) );
Packit d18d0a
        };
Packit d18d0a
      };
Packit d18d0a
      my @expected = $channels{$c}{expect}->($t);
Packit d18d0a
      _is_or_diff( $inner_err, $expected[1], "$l|$m|$c|$t - inner STDERR" );
Packit d18d0a
      _is_or_diff( $outer_out, $expected[0], "$l|$m|$c|$t - outer STDOUT" );
Packit d18d0a
      _is_or_diff( $outer_err, "",           "$l|$m|$c|$t - outer STDERR" );
Packit d18d0a
    },
Packit d18d0a
  },
Packit d18d0a
  capture_merged => {
Packit d18d0a
    cnt   => 2,
Packit d18d0a
    test  => sub {
Packit d18d0a
      my ($m, $c, $t, $l) = @_;
Packit d18d0a
      my $got_out = capture_merged {
Packit d18d0a
        $methods{$m}->( $channels{$c}{output}->($t) );
Packit d18d0a
      };
Packit d18d0a
      my @expected = $channels{$c}{expect}->($t);
Packit d18d0a
      like( $got_out, qr/\Q$expected[0]\E/, "$l|$m|$c|$t - got STDOUT" );
Packit d18d0a
      like( $got_out, qr/\Q$expected[1]\E/, "$l|$m|$c|$t - got STDERR" );
Packit d18d0a
    },
Packit d18d0a
  },
Packit d18d0a
  tee => {
Packit d18d0a
    cnt => 4,
Packit d18d0a
    test => sub {
Packit d18d0a
      my ($m, $c, $t, $l) = @_;
Packit d18d0a
      my ($got_out, $got_err);
Packit d18d0a
      my ($tee_out, $tee_err) = capture {
Packit d18d0a
        ($got_out, $got_err) = tee {
Packit d18d0a
          $methods{$m}->( $channels{$c}{output}->($t) );
Packit d18d0a
        };
Packit d18d0a
      };
Packit d18d0a
      my @expected = $channels{$c}{expect}->($t);
Packit d18d0a
      _is_or_diff( $got_out, $expected[0], "$l|$m|$c|$t - got STDOUT" );
Packit d18d0a
      _is_or_diff( $tee_out, $expected[0], "$l|$m|$c|$t - tee STDOUT" );
Packit d18d0a
      _is_or_diff( $got_err, $expected[1], "$l|$m|$c|$t - got STDERR" );
Packit d18d0a
      _is_or_diff( $tee_err, $expected[1], "$l|$m|$c|$t - tee STDERR" );
Packit d18d0a
    }
Packit d18d0a
  },
Packit d18d0a
  tee_scalar => {
Packit d18d0a
    cnt => 3,
Packit d18d0a
    test => sub {
Packit d18d0a
      my ($m, $c, $t, $l) = @_;
Packit d18d0a
      my ($got_out, $got_err);
Packit d18d0a
      my ($tee_out, $tee_err) = capture {
Packit d18d0a
        $got_out = tee {
Packit d18d0a
          $methods{$m}->( $channels{$c}{output}->($t) );
Packit d18d0a
        };
Packit d18d0a
      };
Packit d18d0a
      my @expected = $channels{$c}{expect}->($t);
Packit d18d0a
      _is_or_diff( $got_out, $expected[0], "$l|$m|$c|$t - got STDOUT" );
Packit d18d0a
      _is_or_diff( $tee_out, $expected[0], "$l|$m|$c|$t - tee STDOUT" );
Packit d18d0a
      _is_or_diff( $tee_err, $expected[1], "$l|$m|$c|$t - tee STDERR" );
Packit d18d0a
    }
Packit d18d0a
  },
Packit d18d0a
  tee_stdout => {
Packit d18d0a
    cnt => 3,
Packit d18d0a
    test => sub {
Packit d18d0a
      my ($m, $c, $t, $l) = @_;
Packit d18d0a
      my ($inner_out, $inner_err);
Packit d18d0a
      my ($tee_out, $tee_err) = capture {
Packit d18d0a
        $inner_out = tee_stdout {
Packit d18d0a
          $methods{$m}->( $channels{$c}{output}->($t) );
Packit d18d0a
        };
Packit d18d0a
      };
Packit d18d0a
      my @expected = $channels{$c}{expect}->($t);
Packit d18d0a
      _is_or_diff( $inner_out, $expected[0], "$l|$m|$c|$t - inner STDOUT" );
Packit d18d0a
      _is_or_diff( $tee_out, $expected[0], "$l|$m|$c|$t - teed STDOUT" );
Packit d18d0a
      _is_or_diff( $tee_err, $expected[1], "$l|$m|$c|$t - unmodified STDERR" );
Packit d18d0a
    }
Packit d18d0a
  },
Packit d18d0a
  tee_stderr => {
Packit d18d0a
    cnt => 3,
Packit d18d0a
    test => sub {
Packit d18d0a
      my ($m, $c, $t, $l) = @_;
Packit d18d0a
      my ($inner_out, $inner_err);
Packit d18d0a
      my ($tee_out, $tee_err) = capture {
Packit d18d0a
        $inner_err = tee_stderr {
Packit d18d0a
          $methods{$m}->( $channels{$c}{output}->($t) );
Packit d18d0a
        };
Packit d18d0a
      };
Packit d18d0a
      my @expected = $channels{$c}{expect}->($t);
Packit d18d0a
      _is_or_diff( $inner_err, $expected[1], "$l|$m|$c|$t - inner STDOUT" );
Packit d18d0a
      _is_or_diff( $tee_out, $expected[0], "$l|$m|$c|$t - unmodified STDOUT" );
Packit d18d0a
      _is_or_diff( $tee_err, $expected[1], "$l|$m|$c|$t - teed STDERR" );
Packit d18d0a
    }
Packit d18d0a
  },
Packit d18d0a
  tee_merged => {
Packit d18d0a
    cnt => 5,
Packit d18d0a
    test => sub {
Packit d18d0a
      my ($m, $c, $t, $l) = @_;
Packit d18d0a
      my ($got_out, $got_err);
Packit d18d0a
      my ($tee_out, $tee_err) = capture {
Packit d18d0a
        $got_out = tee_merged {
Packit d18d0a
          $methods{$m}->( $channels{$c}{output}->($t) );
Packit d18d0a
        };
Packit d18d0a
      };
Packit d18d0a
      my @expected = $channels{$c}{expect}->($t);
Packit d18d0a
      like( $got_out, qr/\Q$expected[0]\E/, "$l|$m|$c|$t - got STDOUT" );
Packit d18d0a
      like( $got_out, qr/\Q$expected[1]\E/, "$l|$m|$c|$t - got STDERR" );
Packit d18d0a
      like( $tee_out, qr/\Q$expected[0]\E/, "$l|$m|$c|$t - tee STDOUT (STDOUT)" );
Packit d18d0a
      like( $tee_out, qr/\Q$expected[1]\E/, "$l|$m|$c|$t - tee STDOUT (STDERR)" );
Packit d18d0a
      _is_or_diff( $tee_err, '', "$l|$m|$c|$t - tee STDERR" );
Packit d18d0a
    }
Packit d18d0a
  },
Packit d18d0a
);
Packit d18d0a
Packit d18d0a
#--------------------------------------------------------------------------#
Packit d18d0a
# What I want to be able to do:
Packit d18d0a
#
Packit d18d0a
# test_it(
Packit d18d0a
#   input => 'short',
Packit d18d0a
#   channels => 'both',
Packit d18d0a
#   method => 'perl'
Packit d18d0a
# )
Packit d18d0a
Packit d18d0a
sub run_test {
Packit d18d0a
  my $test_type = shift or return;
Packit d18d0a
  my $todo = shift || '';
Packit d18d0a
  my $skip_utf8 = shift || '';
Packit d18d0a
  local $ENV{PERL_CAPTURE_TINY_TIMEOUT} = 0; # don't timeout during testing
Packit d18d0a
  for my $m ( keys %methods ) {
Packit d18d0a
    if ( ($m eq 'sys' || substr($test_type,0,3) eq 'tee' ) && ! $locale_ok ) {
Packit d18d0a
        SKIP: {
Packit d18d0a
            skip "Perl could not initialize locale", 1
Packit d18d0a
        };
Packit d18d0a
        next;
Packit d18d0a
    }
Packit d18d0a
    for my $c ( keys %channels ) {
Packit d18d0a
      for my $t ( keys %texts     ) {
Packit d18d0a
        next if $t eq 'unicode' && $skip_utf8;
Packit d18d0a
        my @orig_layers = _set_utf8($t);
Packit d18d0a
        local $TODO = "not supported on all platforms"
Packit d18d0a
          if $t eq $todo;
Packit d18d0a
        $tests{$test_type}{test}->($m, $c, $t, $test_type);
Packit d18d0a
        _restore_layers($t, @orig_layers);
Packit d18d0a
      }
Packit d18d0a
    }
Packit d18d0a
  }
Packit d18d0a
}
Packit d18d0a
Packit d18d0a
1;