Revision control

++ #!/usr/bin/env perl
++ use strict;
++ use warnings;
++
++ package TestReliableExe;
++
++ use Capture::Tiny qw( capture );
++ use English qw( -no_match_vars );
++ use Env::Path;
++ use File::Spec::Functions;
++ use File::Temp qw( tempdir );
++ use FindBin;
++ use Readonly;
++ use Test::Exception;
++ use Test::More;
++ use File::Basename qw( basename );
++
++ use lib catfile( $FindBin::Bin, qw(..) x 2 );
++ use QtQA::Proc::Reliable::TESTDATA qw( %TESTDATA );
++ use QtQA::Test::More qw( :all );
++
++ use base 'QtQA::TestScript';
++
++ =head1 NAME
++
++ 30-testscript-reliable-exe.t - system test of exe's `reliable' feature
++
++ =head1 DESCRIPTION
++
++ This test runs various commands with simulated error cases and verifies that
++ exe automatically recovers from certain errors.
++
++ =cut
++
++ sub new
++ {
++ my ($class) = @_;
++
++ my $self = $class->SUPER::new( );
++ bless( $self, $class );
++ return $self;
++ }
++
++ sub run_one_test
++ {
++ my ($self, $testname, $testdata) = @_;
++
++ my $tempdir = tempdir( basename($0).'.XXXXXX', TMPDIR => 1, CLEANUP => 1 );
++
++ my $command = $testdata->{ command };
++ my %mock_command = %{$testdata->{ mock_command }};
++ my $reliable = $testdata->{ reliable } // 1;
++ my $expected_stderr = $testdata->{ expected_exe_stderr }
++ // $testdata->{ expected_raw_stderr }
++ // q{};
++ my $expected_stdout = $testdata->{ expected_exe_stdout }
++ // $testdata->{ expected_raw_stdout }
++ // q{};
++ my $expected_status = $testdata->{ expected_status } // 0;
++
++ $mock_command{ directory } = $tempdir;
++
++ if (! exists $mock_command{ name }) {
++ $mock_command{ name } = $command->[0];
++ }
++
++ create_mock_command( %mock_command );
++
++ # Put our mock command first in PATH
++ local $ENV{ PATH } = $ENV{ PATH };
++ Env::Path->PATH->Prepend( $tempdir );
++
++ # Usually, we just pass our command to exe ...
++ my @exe_args = @{$command};
++
++ # ... but, for a non-auto `reliable', we pass options too.
++ if (ref($reliable) || $reliable != 1) {
++ @exe_args = ( { reliable => $reliable }, @exe_args );
++ }
++
++ my ($lives_or_dies_text, $lives_or_dies_ok)
++ = ($expected_status == 0) ? ('exits successfully', \&lives_ok)
++ : ('fails as expected', \&dies_ok )
++ ;
++
++ my ($stdout, $stderr) = capture {
++ $lives_or_dies_ok->(
++ sub { $self->exe( @exe_args ) },
++ "$testname $lives_or_dies_text",
++ );
++ };
++
++ # exe() should have set $? to the expected value
++ is( $?, $expected_status, "$testname \$? set as expected" );
++
++ # Immediately prior to stdout should always be the command;
++ # Prior to that may be either CWD or PATH lines.
++ my $prefix = quotemeta( '+ '.join(' ', @{$command}) );
++ $prefix = qr{
++ \A
++ (?:\+\ CWD:\ [^\n]+\n)?
++ (?:\+\ PATH:\ [^\n]+\n)?
++ $prefix \n
++ }xms;
++ like( $stdout, $prefix, "$testname stdout first line looks correct" );
++
++ # Remove prefix for subsequent comparison
++ $stdout =~ s{$prefix}{}xms;
++
++ is_or_like( $stdout, $expected_stdout, "$testname stdout looks correct" );
++ is_or_like( $stderr, $expected_stderr, "$testname stderr looks correct" );
++
++ return;
++ }
++
++ sub run
++ {
++ my ($self) = @_;
++
++ while (my ($testname, $testdata) = each %TESTDATA) {
++ $self->run_one_test($testname, $testdata);
++ }
++
++ done_testing( );
++
++ return;
++ }
++
++ TestReliableExe->new( )->run( ) if (!caller);
++ 1;
++