File-Replace-Inplace
view release on metacpan or search on metacpan
t/25_tie_handle_argv.t view on Meta::CPAN
#!/usr/bin/env perl
use warnings;
use strict;
=head1 Synopsis
Tests for the Perl module Tie::Handle::Argv.
=head1 Author, Copyright, and License
Copyright (c) 2018-2023 Hauke Daempfling (haukex@zero-g.net)
at the Leibniz Institute of Freshwater Ecology and Inland Fisheries (IGB),
Berlin, Germany, L<http://www.igb-berlin.de/>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see L<http://www.gnu.org/licenses/>.
=cut
use FindBin ();
use lib $FindBin::Bin;
use File_Replace_Testlib;
use Test::More tests=>21;
use Cwd qw/getcwd/;
use File::Temp qw/tempdir/;
use warnings FATAL => qw/ io inplace /;
our $DEBUG = 0;
our $FE = $] ge '5.012' && $] lt '5.029007' ? !!0 : !!1; # FE="first eof", see https://github.com/Perl/perl5/issues/16786
#TODO Later: Why is $BE needed here, but not in the ::Inplace tests?
our $BE; # BE="buggy eof", Perl 5.14.x had several regressions regarding eof (and a few others) (gets set below)
our $CE; # CE="can't eof()", Perl <5.12 doesn't support eof() on tied filehandles (gets set below)
our $FL = undef; # FL="First Line"
# Apparently there are some versions of Perl on Win32 where the following two appear to work slightly differently.
# I've seen differing results on different systems and I'm not sure why, so I set it dynamically... not pretty, but this test isn't critical.
if ( $^O eq 'MSWin32' && $] ge '5.014' && $] lt '5.018' )
{ $FL = $.; $FE = defined($.) }
diag "WARNING: Perl 5.16 or better is strongly recommended for Tie::Handle::Argv (see documentation)" if $] lt '5.016';
BEGIN { use_ok('Tie::Handle::Argv') }
## no critic (RequireCarping)
sub testboth { ## no critic (RequireArgUnpacking)
# test that both regular ARGV and our tied base class act the same
die "bad nr of args" unless @_==2 || @_==3;
my ($name, $sub, $args) = @_;
my $stdin = delete $$args{stdin};
{
local (*ARGV, $.); ## no critic (RequireInitializationForLocalVars)
my $osi = defined($stdin) ? OverrideStdin->new($stdin) : undef;
subtest "$name - untied" => $sub;
$osi and $osi->restore;
}
{
local (*ARGV, $.); ## no critic (RequireInitializationForLocalVars)
local $CE = $] lt '5.012';
local $BE = $] ge '5.014' && $] lt '5.016';
tie *ARGV, 'Tie::Handle::Argv', debug=>$DEBUG;
my $osi = defined($stdin) ? OverrideStdin->new($stdin) : undef;
subtest "$name - tied" => $sub;
$osi and $osi->restore;
untie *ARGV;
}
return;
}
testboth 'basic test' => sub { plan tests=>1;
my @tf = (newtempfn("Foo\nBar\n"), newtempfn("Quz\nBaz"));
my @states;
@ARGV = @tf; ## no critic (RequireLocalizedPunctuationVars)
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof, $_] while <>;
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
is_deeply \@states, [
[[@tf], undef, !!0, $FL, $FE ],
[[$tf[1]], $tf[0], !!1, 1, !!0, "Foo\n"],
[[$tf[1]], $tf[0], !!1, 2, !!1, "Bar\n"],
[[], $tf[1], !!1, 3, !!0, "Quz\n"],
[[], $tf[1], !!1, 4, !!1, "Baz" ],
[[], $tf[1], !!0, 4, $BE?!!0:!!1 ],
], 'states' or diag explain \@states;
};
testboth 'basic test with eof()' => sub {
plan $CE ? ( skip_all=>"eof() not supported on tied handles on Perl<5.12" ) : (tests=>1);
my @tf = (newtempfn("Foo\nBar"), newtempfn("Quz\nBaz\n"));
my @states;
local @ARGV = @tf; # this also tests "local"ization after constructing the object
# WARNING: eof() modifies $ARGV (and potentially others), so don't do e.g. [$ARGV, $., eof, eof()]!!
# See e.g. https://www.perlmonks.org/?node_id=289044 and https://www.perlmonks.org/?node_id=1076954
# and https://www.perlmonks.org/?node_id=1164369 and probably more
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof], eof();
# eof() will open the first file, so record the current state again:
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof], eof();
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof, $_], eof() while <>;
# another call to eof() now would open and try to read STDIN (we test that in the STDIN tests)
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
is_deeply \@states, [
[[@tf], undef, !!0, $FL, $FE ], !!0,
[[$tf[1]], $tf[0], !!1, $BE?$FL:0, !!0 ], !!0,
[[$tf[1]], $tf[0], !!1, 1, !!0, "Foo\n"], !!0,
[[$tf[1]], $tf[0], !!1, 2, !!1, "Bar" ], !!0,
[[], $tf[1], !!1, 3, !!0, "Quz\n"], !!0,
[[], $tf[1], !!1, 4, !!1, "Baz\n"], !!1,
[[], $tf[1], !!0, 4, $BE?!!0:!!1 ],
], 'states' or diag explain \@states;
};
subtest 'custom files & filename' => sub { plan tests=>3;
local (*ARGV, $.); ## no critic (RequireInitializationForLocalVars)
local $BE = $] ge '5.014' && $] lt '5.016';
my @testfiles1;
my $testfilename1;
my $obj = tie *ARGV, 'Tie::Handle::Argv', debug=>$DEBUG, files=>\@testfiles1, filename=>\$testfilename1;
my @tf = (newtempfn("Foo\nBar\n"), newtempfn("Quz\nBaz"));
my @states;
@ARGV = ("foo"); ## no critic (RequireLocalizedPunctuationVars)
@testfiles1 = @tf;
push @states, [[@ARGV], $ARGV, [@testfiles1], $testfilename1, defined(fileno ARGV), $., eof];
push @states, [[@ARGV], $ARGV, [@testfiles1], $testfilename1, defined(fileno ARGV), $., eof, $_] while <>;
push @states, [[@ARGV], $ARGV, [@testfiles1], $testfilename1, defined(fileno ARGV), $., eof];
is_deeply \@states, [
[["foo"], undef, [@tf], undef, !!0, $FL, $FE ],
[["foo"], undef, [$tf[1]], $tf[0], !!1, 1, !!0, "Foo\n"],
[["foo"], undef, [$tf[1]], $tf[0], !!1, 2, !!1, "Bar\n"],
[["foo"], undef, [], $tf[1], !!1, 3, !!0, "Quz\n"],
[["foo"], undef, [], $tf[1], !!1, 4, !!1, "Baz" ],
[["foo"], undef, [], $tf[1], !!0, 4, $BE?!!0:!!1 ],
], 'states' or diag explain \@states;
{ # make code coverage happy
is 0+@testfiles1, 0, 'testfiles empty';
$obj->init_empty_argv;
is_deeply \@testfiles1, ['-'], 'testfiles was populated';
}
untie *ARGV;
};
testboth 'readline contexts' => sub { plan tests=>2;
# we test scalar everywhere, need to test the others too
my @tf = (newtempfn("Alpha"), newtempfn("Bravo\nCharlie\nDelta"), newtempfn("Echo\n!!!"));
my @states;
@ARGV = @tf; ## no critic (RequireLocalizedPunctuationVars)
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
for (1..2) {
<>; # void ctx
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
}
my @got = <>; # list ctx
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
is_deeply \@got, ["Charlie\n","Delta","Echo\n","!!!"], 'list ctx'
or diag explain \@got;
is_deeply \@states, [
[[@tf], undef, !!0, $FL, $FE ],
[[@tf[1,2]], $tf[0], !!1, 1, !!1 ],
[[$tf[2]], $tf[1], !!1, 2, !!0 ],
[[], $tf[2], !!0, 6, $BE?!!0:!!1],
], 'states' or diag explain \@states;
};
testboth 'restart argv' => sub { plan tests=>1;
my $tfn = newtempfn("111\n222\n333\n");
my @states;
@ARGV = ($tfn); ## no critic (RequireLocalizedPunctuationVars)
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof, $_] while <>;
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
@ARGV = ($tfn); ## no critic (RequireLocalizedPunctuationVars)
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof, $_] while <>;
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
is_deeply \@states, [
t/25_tie_handle_argv.t view on Meta::CPAN
is_deeply [<>], ["Hullo","World!\n","Foo!"], '<> in list ctx';
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
ok !-e $tf[$_], "file ".($_+1)." doesn't exist" for 2,4;
is_deeply \@states, [
[[@tf], undef, !!0, $FL, $FE ],
[[@tf[2..6]], $tf[1], !!1, 1, !!1, "Hullo" ],
[[$tf[6]], $tf[5], !!1, 2, !!0, "World!\n"],
[[$tf[6]], $tf[5], !!1, 3, !!1, "Foo!" ],
[[], $tf[6], !!0, 3, $BE?!!0:!!1 ],
[[@tf], $tf[6], !!0, 3, $BE?!!0:!!1 ],
[[], $tf[6], !!0, 3, $BE?!!0:!!1 ],
], 'states' or diag explain \@states;
is $warncount, 4, 'warning count';
};
testboth 'premature close' => sub { plan tests=>1;
my @tf = (newtempfn("Foo\nBar\n"), newtempfn("Quz\nBaz"));
my @states;
@ARGV = @tf; ## no critic (RequireLocalizedPunctuationVars)
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
my $l=<>;
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof, $l];
close ARGV;
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof, $_] while <>;
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
is_deeply \@states, [
[[@tf], undef, !!0, $FL, $FE ],
[[$tf[1]], $tf[0], !!1, 1, !!0, "Foo\n"],
[[$tf[1]], $tf[0], !!0, 0, !!1 ],
[[], $tf[1], !!1, 1, !!0, "Quz\n"],
[[], $tf[1], !!1, 2, !!1, "Baz" ],
[[], $tf[1], !!0, 2, $BE?!!0:!!1 ],
], 'states' or diag explain \@states;
};
subtest 'special filenames and double-diamond' => sub {
# Windows filenames don't allow any of <, >, or |, so we can't create files with these names to test with.
# Since we're mostly testing the logic of the module vs. Perl, if these tests pass on *NIX, it should be ok.
plan $^O eq 'MSWin32' ? (skip_all => 'special filenames won\'t work on Windows') : (tests=>2*2);
my $prevdir = getcwd;
my $tmpdir = tempdir(DIR=>$TEMPDIR,CLEANUP=>1);
chdir($tmpdir) or die "chdir $tmpdir: $!";
# this should also apply to piped opens etc.
spew("foo","I am foo\nbar");
spew("<foo","I am <foo!\nquz");
testboth 'diamond' => sub { plan tests=>1;
my @states;
@ARGV = ("<foo"); ## no critic (RequireLocalizedPunctuationVars)
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof, $_] while <>;
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
is_deeply \@states, [
[["<foo"], undef, !!0, undef, $FE ],
[[], "<foo", !!1, 1, !!0, "I am foo\n"],
[[], "<foo", !!1, 2, !!1, "bar" ],
[[], "<foo", !!0, 2, $BE?!!0:!!1 ],
], 'states' or diag explain \@states;
};
testboth 'double-diamond' => sub {
plan $] lt '5.022' ? (skip_all => 'need Perl >=5.22 for double-diamond') : (tests=>1);
my @states;
@ARGV = ("<foo"); ## no critic (RequireLocalizedPunctuationVars)
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
my $code = <<' ENDCODE'; # need to eval this because otherwise <<>> is a syntax error on older Perls
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof, $_] while <<>>;
; 1
ENDCODE
eval $code or die $@||"unknown error"; ## no critic (ProhibitStringyEval, ProhibitMixedBooleanOperators)
push @states, [[@ARGV], $ARGV, defined(fileno ARGV), $., eof];
TODO: { local $TODO; tied(*ARGV) and $TODO = "double-diamond not yet supported with tied filehandles (?)"; ## no critic (RequireInitializationForLocalVars)
is_deeply \@states, [
[["<foo"], undef, !!0, undef, $FE ],
[[], "<foo", !!1, 1, !!0, "I am <foo!\n"],
[[], "<foo", !!1, 2, !!1, "quz" ],
[[], "<foo", !!0, 2, !!1 ],
], 'states for double-diamond';
}
};
chdir($prevdir) or warn "chdir $prevdir: $!";
};
subtest 'debugging (and coverage)' => sub { plan tests=>4;
local (*ARGV, $.); ## no critic (RequireInitializationForLocalVars)
note "Expect some debug output here:";
@ARGV = (newtempfn("One\nTwo")); ## no critic (RequireLocalizedPunctuationVars)
my $db = Test::More->builder->output;
tie *ARGV, 'Tie::Handle::Argv', debug=>$db;
is scalar <>, "One\n", 'debug w/ handle';
untie *ARGV;
@ARGV = (newtempfn("Three")); ## no critic (RequireLocalizedPunctuationVars)
local *STDERR = $db;
tie *ARGV, 'Tie::Handle::Argv', debug=>1;
# for code coverage of the (unlikely) condition that eof was
# false but readline still returns undef:
open my $sfh, '<', \(my $somestr = "") or die $!; ## no critic (RequireBriefOpen)
tied(*ARGV)->set_inner_handle(Tie::Handle::NeverEof->new($sfh));
ok !tied(*ARGV)->EOF(), 'eof is false';
is scalar <>, undef, 'debug w/o handle';
like exception { tied(*ARGV)->_debug() },
qr/\bnot enough arguments\b/, '_debug not enough args';
untie *ARGV;
};
subtest 'misc errors' => sub { plan tests=>7;
local (*ARGV, $.); ## no critic (RequireInitializationForLocalVars)
like exception {
tie *ARGV, 'Tie::Handle::Argv', "foo";
}, qr/\bbad number of arguments\b/, 'tiehandle bad nr of args';
like exception {
tie *ARGV, 'Tie::Handle::Argv', foo => "bar";
}, qr/\bunknown argument 'foo'/, 'tiehandle bad arg';
like exception {
tie *ARGV, 'Tie::Handle::Argv', files => "files";
}, qr/\bmust be an arrayref\b/, 'tiehandle bad files arg';
like exception {
tie *ARGV, 'Tie::Handle::Argv', filename => "filename";
}, qr/\bmust be a scalar ref\b/, 'tiehandle bad filename arg';
tie *ARGV, 'Tie::Handle::Argv', debug=>$DEBUG;
like exception { tied(*ARGV)->_close() },
qr/\bbad number of arguments\b/, '_close bad nr of args';
( run in 1.969 second using v1.01-cache-2.11-cpan-8dfa8b56332 )