BATsh

 view release on metacpan or  search on metacpan

t/0008-nested-subst.t  view on Meta::CPAN

######################################################################
#
# 0008-nested-subst.t  Regression: nested command substitution
#                      $( ... $( ... ) ) -- including pipelines at
#                      each level -- must capture correctly on BOTH
#                      cmd.exe (Windows) AND /bin/sh (Unix/BSD).
#
# BACKGROUND
#   BATsh::SH captures command-substitution output through a temporary
#   file and runs SH pipelines through per-stage temporary files.  Three
#   distinct defects made a nested $( ... ) collapse to an empty string
#   (Unix) or hang (Unix), while behaving differently again on Windows:
#
#   (1) CAPTURE-FILE COLLISION -- _cmd_subst() named its capture file
#       with the process id alone (batsh_cap_$$.tmp).  An inner $(...)
#       reused the very same path and unlink()'d it, so the outer level
#       captured nothing.  Fixed by tagging the file with the active
#       substitution-nesting depth.
#
#   (2) PIPELINE-SPLIT DEPTH -- _split_sh_pipe() counted the '(' of a
#       "$(" twice, so after a nested $(...) the $( nesting depth was
#       stuck at 1 and a bare '|' that followed it was not recognised as
#       a pipe.  Fixed by consuming both characters of "$(" and bumping
#       the depth exactly once.
#
#   (3) PIPE-STAGE COLLISION + UNLOCALIZED HANDLES -- _exec_sh_pipe()
#       named its stage files with the process id alone (batsh_shp_$$)
#       and left its dup STDOUT/STDIN globs un-local()ised.  A nested
#       pipeline (a segment whose $(...) body is itself a pipeline) then
#       clobbered the outer pipeline's stage file and saved handles; the
#       outer's final segment found no input file and blocked on the real
#       STDIN (a hang on Unix).  Fixed by tagging the stage files with the
#       active pipeline-nesting depth and local()ising the handle globs.
#
# THIS TEST
#   NS01/NS02  pure-builtin nesting (no external command): proves the
#              capture-file depth fix independently of any "perl" on PATH.
#   NS03       single-level $(...) that contains a pipeline (baseline).
#   NS04/NS05  nested $(...) with a pipeline at each level: the core
#              regression for defects (2) and (3).
#   NS06       two sibling $(...) pipelines on one line: no cross-collision
#              between non-overlapping substitutions/pipelines.
#   NS07       assignment from a nested pipeline substitution, then reuse.
#
# COMPATIBILITY: Perl 5.005_03 and later
#
######################################################################
use strict;
BEGIN { if ($] < 5.006 && !defined(&warnings::import)) {
        $INC{'warnings.pm'} = 'stub'; eval 'package warnings; sub import {}' } }
use warnings; local $^W = 1;
BEGIN { pop @INC if $INC[-1] eq '.' }
use FindBin ();
use File::Spec ();
use lib "$FindBin::Bin/../lib";

eval { require BATsh } or die "Cannot load BATsh: $@";

# Portability: NS03..NS07 shell out to a bareword "perl".  On a CPAN
# smoker the perl under test is frequently NOT on PATH as "perl"
# (perlbrew/plenv, or perl invoked by absolute path), which would turn
# these checks into false failures.  Prepend the directory of the
# running interpreter ($^X) to PATH so "perl" resolves to it.  Done by
# environment (not by embedding $^X in the command) so a Win32 path with
# backslashes never reaches SH-mode quote/escape processing.  Must
# precede the first init(): init() snapshots %ENV into STORE and
# sync_to_env() later copies STORE back to %ENV.
{
    my ($pvol, $pdirs) = File::Spec->splitpath($^X);
    my $perldir = File::Spec->catpath($pvol, $pdirs, '');
    if (length $perldir) {
        my $sep = ($^O =~ /^(?:MSWin32|dos|os2)$/) ? ';' : ':';
        $ENV{'PATH'} = (defined($ENV{'PATH'}) && length($ENV{'PATH'}))
                     ? "$perldir$sep$ENV{'PATH'}" : $perldir;
    }
}
BATsh::Env::init();

my @tests = (

    ##################################################################
    # 1. Pure-builtin nesting (no external command required)
    ##################################################################

    # NS01: one level of nesting using only the echo builtin.
    sub {
        my $out = _capture(sub {
            BATsh->run_string('echo $(echo a-$(echo b))');
        });
        $out =~ s/[\r\n]+\z//;
        _ok($out eq 'a-b', "NS01: builtin nested subst (got [$out])");



( run in 0.458 second using v1.01-cache-2.11-cpan-bbe5e583499 )