933 lines
22 KiB
Perl
933 lines
22 KiB
Perl
package Test::Output;
|
|
|
|
use warnings;
|
|
use strict;
|
|
|
|
use Test::Builder;
|
|
use Capture::Tiny qw/capture capture_stdout capture_stderr capture_merged/;
|
|
|
|
use Exporter qw(import);
|
|
|
|
our %EXPORT_TAGS = (
|
|
stdout => [
|
|
qw(
|
|
stdout_is stdout_isnt stdout_like stdout_unlike
|
|
)
|
|
],
|
|
stderr => [
|
|
qw(
|
|
stderr_is stderr_isnt stderr_like stderr_unlike
|
|
)
|
|
],
|
|
output => [
|
|
qw(
|
|
output_is output_isnt output_like output_unlike
|
|
)
|
|
],
|
|
combined => [
|
|
qw(
|
|
combined_is combined_isnt combined_like combined_unlike
|
|
)
|
|
],
|
|
functions => [
|
|
qw(
|
|
output_from stderr_from stdout_from combined_from
|
|
)
|
|
],
|
|
tests => [
|
|
qw(
|
|
output_is output_isnt output_like output_unlike
|
|
stderr_is stderr_isnt stderr_like stderr_unlike
|
|
stdout_is stdout_isnt stdout_like stdout_unlike
|
|
combined_is combined_isnt combined_like combined_unlike
|
|
)
|
|
],
|
|
all => [
|
|
qw(
|
|
output_is output_isnt output_like output_unlike
|
|
stderr_is stderr_isnt stderr_like stderr_unlike
|
|
stdout_is stdout_isnt stdout_like stdout_unlike
|
|
combined_is combined_isnt combined_like combined_unlike
|
|
output_from stderr_from stdout_from combined_from
|
|
)
|
|
],
|
|
);
|
|
|
|
our @EXPORT = keys %{
|
|
{
|
|
map { $_ => 1 }
|
|
map {
|
|
@{ $EXPORT_TAGS{$_} }
|
|
}
|
|
keys %EXPORT_TAGS
|
|
}
|
|
};
|
|
|
|
my $Test = Test::Builder->new;
|
|
|
|
=encoding utf8
|
|
|
|
=head1 NAME
|
|
|
|
Test::Output - Utilities to test STDOUT and STDERR messages.
|
|
|
|
=cut
|
|
|
|
our $VERSION = '1.031';
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use Test::More tests => 4;
|
|
use Test::Output;
|
|
|
|
sub writer {
|
|
print "Write out.\n";
|
|
print STDERR "Error out.\n";
|
|
}
|
|
|
|
stdout_is(\&writer,"Write out.\n",'Test STDOUT');
|
|
|
|
stderr_isnt(\&writer,"No error out.\n",'Test STDERR');
|
|
|
|
combined_is(
|
|
\&writer,
|
|
"Write out.\nError out.\n",
|
|
'Test STDOUT & STDERR combined'
|
|
);
|
|
|
|
output_is(
|
|
\&writer,
|
|
"Write out.\n",
|
|
"Error out.\n",
|
|
'Test STDOUT & STDERR'
|
|
);
|
|
|
|
# Use bare blocks.
|
|
|
|
stdout_is { print "test" } "test", "Test STDOUT";
|
|
stderr_isnt { print "bad test" } "test", "Test STDERR";
|
|
output_is { print 'STDOUT'; print STDERR 'STDERR' }
|
|
"STDOUT", "STDERR", "Test output";
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Test::Output provides a simple interface for testing output sent to C<STDOUT>
|
|
or C<STDERR>. A number of different utilities are included to try and be as
|
|
flexible as possible to the tester.
|
|
|
|
Likewise, L<Capture::Tiny> provides a much more robust capture mechanism without
|
|
than the original L<Test::Output::Tie>.
|
|
|
|
=cut
|
|
|
|
=head1 TESTS
|
|
|
|
=cut
|
|
|
|
=head2 STDOUT
|
|
|
|
=over 4
|
|
|
|
=item B<stdout_is>
|
|
|
|
=item B<stdout_isnt>
|
|
|
|
stdout_is ( $coderef, $expected, 'description' );
|
|
stdout_is { ... } $expected, 'description';
|
|
stdout_isnt( $coderef, $expected, 'description' );
|
|
stdout_isnt { ... } $expected, 'description';
|
|
|
|
C<stdout_is()> captures output sent to C<STDOUT> from C<$coderef> and compares
|
|
it against C<$expected>. The test passes if equal.
|
|
|
|
C<stdout_isnt()> passes if C<STDOUT> is not equal to C<$expected>.
|
|
|
|
=cut
|
|
|
|
sub stdout_is (&$;$$) {
|
|
my $test = shift;
|
|
my $expected = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
my $stdout = stdout_from($test);
|
|
|
|
my $ok = ( $stdout eq $expected );
|
|
|
|
$Test->ok( $ok, $description )
|
|
|| $Test->diag("STDOUT is:\n$stdout\nnot:\n$expected\nas expected");
|
|
|
|
return $ok;
|
|
}
|
|
|
|
sub stdout_isnt (&$;$$) {
|
|
my $test = shift;
|
|
my $expected = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
my $stdout = stdout_from($test);
|
|
|
|
my $ok = ( $stdout ne $expected );
|
|
|
|
$Test->ok( $ok, $description )
|
|
|| $Test->diag("STDOUT:\n$stdout\nmatching:\n$expected\nnot expected");
|
|
|
|
return $ok;
|
|
}
|
|
|
|
=item B<stdout_like>
|
|
|
|
=item B<stdout_unlike>
|
|
|
|
stdout_like ( $coderef, qr/$expected/, 'description' );
|
|
stdout_like { ... } qr/$expected/, 'description';
|
|
stdout_unlike( $coderef, qr/$expected/, 'description' );
|
|
stdout_unlike { ... } qr/$expected/, 'description';
|
|
|
|
C<stdout_like()> captures the output sent to C<STDOUT> from C<$coderef> and compares
|
|
it to the regex in C<$expected>. The test passes if the regex matches.
|
|
|
|
C<stdout_unlike()> passes if STDOUT does not match the regex.
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
sub stdout_like (&$;$$) {
|
|
my $test = shift;
|
|
my $expected = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
unless ( my $regextest = _chkregex( 'stdout_like' => $expected ) ) {
|
|
return $regextest;
|
|
}
|
|
|
|
my $stdout = stdout_from($test);
|
|
|
|
my $ok = ( $stdout =~ $expected );
|
|
|
|
$Test->ok( $ok, $description )
|
|
|| $Test->diag("STDOUT:\n$stdout\ndoesn't match:\n$expected\nas expected");
|
|
|
|
return $ok;
|
|
}
|
|
|
|
sub stdout_unlike (&$;$$) {
|
|
my $test = shift;
|
|
my $expected = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
unless ( my $regextest = _chkregex( 'stdout_unlike' => $expected ) ) {
|
|
return $regextest;
|
|
}
|
|
|
|
my $stdout = stdout_from($test);
|
|
|
|
my $ok = ( $stdout !~ $expected );
|
|
|
|
$Test->ok( $ok, $description )
|
|
|| $Test->diag("STDOUT:\n$stdout\nmatches:\n$expected\nnot expected");
|
|
|
|
return $ok;
|
|
}
|
|
|
|
=head2 STDERR
|
|
|
|
=over 4
|
|
|
|
=item B<stderr_is>
|
|
|
|
=item B<stderr_isnt>
|
|
|
|
stderr_is ( $coderef, $expected, 'description' );
|
|
stderr_is {... } $expected, 'description';
|
|
|
|
stderr_isnt( $coderef, $expected, 'description' );
|
|
stderr_isnt {... } $expected, 'description';
|
|
|
|
C<stderr_is()> is similar to C<stdout_is>, except that it captures C<STDERR>. The
|
|
test passes if C<STDERR> from C<$coderef> equals C<$expected>.
|
|
|
|
C<stderr_isnt()> passes if C<STDERR> is not equal to C<$expected>.
|
|
|
|
=cut
|
|
|
|
sub stderr_is (&$;$$) {
|
|
my $test = shift;
|
|
my $expected = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
my $stderr = stderr_from($test);
|
|
|
|
my $ok = ( $stderr eq $expected );
|
|
|
|
$Test->ok( $ok, $description )
|
|
|| $Test->diag("STDERR is:\n$stderr\nnot:\n$expected\nas expected");
|
|
|
|
return $ok;
|
|
}
|
|
|
|
sub stderr_isnt (&$;$$) {
|
|
my $test = shift;
|
|
my $expected = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
my $stderr = stderr_from($test);
|
|
|
|
my $ok = ( $stderr ne $expected );
|
|
|
|
$Test->ok( $ok, $description )
|
|
|| $Test->diag("STDERR:\n$stderr\nmatches:\n$expected\nnot expected");
|
|
|
|
return $ok;
|
|
}
|
|
|
|
=item B<stderr_like>
|
|
|
|
=item B<stderr_unlike>
|
|
|
|
stderr_like ( $coderef, qr/$expected/, 'description' );
|
|
stderr_like { ...} qr/$expected/, 'description';
|
|
stderr_unlike( $coderef, qr/$expected/, 'description' );
|
|
stderr_unlike { ...} qr/$expected/, 'description';
|
|
|
|
C<stderr_like()> is similar to C<stdout_like()> except that it compares the regex
|
|
C<$expected> to C<STDERR> captured from C<$codref>. The test passes if the regex
|
|
matches.
|
|
|
|
C<stderr_unlike()> passes if C<STDERR> does not match the regex.
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
sub stderr_like (&$;$$) {
|
|
my $test = shift;
|
|
my $expected = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
unless ( my $regextest = _chkregex( 'stderr_like' => $expected ) ) {
|
|
return $regextest;
|
|
}
|
|
|
|
my $stderr = stderr_from($test);
|
|
|
|
my $ok = ( $stderr =~ $expected );
|
|
|
|
$Test->ok( $ok, $description )
|
|
|| $Test->diag("STDERR:\n$stderr\ndoesn't match:\n$expected\nas expected");
|
|
|
|
return $ok;
|
|
}
|
|
|
|
sub stderr_unlike (&$;$$) {
|
|
my $test = shift;
|
|
my $expected = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
unless ( my $regextest = _chkregex( 'stderr_unlike' => $expected ) ) {
|
|
return $regextest;
|
|
}
|
|
|
|
my $stderr = stderr_from($test);
|
|
|
|
my $ok = ( $stderr !~ $expected );
|
|
|
|
$Test->ok( $ok, $description )
|
|
|| $Test->diag("STDERR:\n$stderr\nmatches:\n$expected\nnot expected");
|
|
|
|
return $ok;
|
|
}
|
|
|
|
=head2 COMBINED OUTPUT
|
|
|
|
=over 4
|
|
|
|
=item B<combined_is>
|
|
|
|
=item B<combined_isnt>
|
|
|
|
combined_is ( $coderef, $expected, 'description' );
|
|
combined_is {... } $expected, 'description';
|
|
combined_isnt ( $coderef, $expected, 'description' );
|
|
combined_isnt {... } $expected, 'description';
|
|
|
|
C<combined_is()> directs C<STDERR> to C<STDOUT> then captures C<STDOUT>. This is
|
|
equivalent to UNIXs C<< 2>&1 >>. The test passes if the combined C<STDOUT>
|
|
and C<STDERR> from $coderef equals $expected.
|
|
|
|
C<combined_isnt()> passes if combined C<STDOUT> and C<STDERR> are not equal
|
|
to C<$expected>.
|
|
|
|
=cut
|
|
|
|
sub combined_is (&$;$$) {
|
|
my $test = shift;
|
|
my $expected = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
my $combined = combined_from($test);
|
|
|
|
my $ok = ( $combined eq $expected );
|
|
|
|
$Test->ok( $ok, $description )
|
|
|| $Test->diag(
|
|
"STDOUT & STDERR are:\n$combined\nnot:\n$expected\nas expected");
|
|
|
|
return $ok;
|
|
}
|
|
|
|
sub combined_isnt (&$;$$) {
|
|
my $test = shift;
|
|
my $expected = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
my $combined = combined_from($test);
|
|
|
|
my $ok = ( $combined ne $expected );
|
|
|
|
$Test->ok( $ok, $description )
|
|
|| $Test->diag(
|
|
"STDOUT & STDERR:\n$combined\nmatching:\n$expected\nnot expected");
|
|
|
|
return $ok;
|
|
}
|
|
|
|
=item B<combined_like>
|
|
|
|
=item B<combined_unlike>
|
|
|
|
combined_like ( $coderef, qr/$expected/, 'description' );
|
|
combined_like { ...} qr/$expected/, 'description';
|
|
combined_unlike ( $coderef, qr/$expected/, 'description' );
|
|
combined_unlike { ...} qr/$expected/, 'description';
|
|
|
|
C<combined_like()> is similar to C<combined_is()> except that it compares a regex
|
|
(C<$expected)> to C<STDOUT> and C<STDERR> captured from C<$codref>. The test passes if
|
|
the regex matches.
|
|
|
|
C<combined_unlike()> passes if the combined C<STDOUT> and C<STDERR> does not match
|
|
the regex.
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
sub combined_like (&$;$$) {
|
|
my $test = shift;
|
|
my $expected = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
unless ( my $regextest = _chkregex( 'combined_like' => $expected ) ) {
|
|
return $regextest;
|
|
}
|
|
|
|
my $combined = combined_from($test);
|
|
|
|
my $ok = ( $combined =~ $expected );
|
|
|
|
$Test->ok( $ok, $description )
|
|
|| $Test->diag(
|
|
"STDOUT & STDERR:\n$combined\ndon't match:\n$expected\nas expected");
|
|
|
|
return $ok;
|
|
}
|
|
|
|
sub combined_unlike (&$;$$) {
|
|
my $test = shift;
|
|
my $expected = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
unless ( my $regextest = _chkregex( 'combined_unlike' => $expected ) ) {
|
|
return $regextest;
|
|
}
|
|
|
|
my $combined = combined_from($test);
|
|
|
|
my $ok = ( $combined !~ $expected );
|
|
|
|
$Test->ok( $ok, $description )
|
|
|| $Test->diag(
|
|
"STDOUT & STDERR:\n$combined\nmatching:\n$expected\nnot expected");
|
|
|
|
return $ok;
|
|
}
|
|
|
|
=head2 OUTPUT
|
|
|
|
=over 4
|
|
|
|
=item B<output_is>
|
|
|
|
=item B<output_isnt>
|
|
|
|
output_is ( $coderef, $expected_stdout, $expected_stderr, 'description' );
|
|
output_is {... } $expected_stdout, $expected_stderr, 'description';
|
|
output_isnt( $coderef, $expected_stdout, $expected_stderr, 'description' );
|
|
output_isnt {... } $expected_stdout, $expected_stderr, 'description';
|
|
|
|
The C<output_is()> function is a combination of the C<stdout_is()> and C<stderr_is()>
|
|
functions. For example:
|
|
|
|
output_is(sub {print "foo"; print STDERR "bar";},'foo','bar');
|
|
|
|
is functionally equivalent to
|
|
|
|
stdout_is(sub {print "foo";},'foo')
|
|
&& stderr_is(sub {print STDERR "bar";'bar');
|
|
|
|
except that C<$coderef> is only executed once.
|
|
|
|
Unlike C<stdout_is()> and C<stderr_is()> which ignore STDERR and STDOUT
|
|
respectively, C<output_is()> requires both C<STDOUT> and C<STDERR> to match in order
|
|
to pass. Setting either C<$expected_stdout> or C<$expected_stderr> to C<undef>
|
|
ignores C<STDOUT> or C<STDERR> respectively.
|
|
|
|
output_is(sub {print "foo"; print STDERR "bar";},'foo',undef);
|
|
|
|
is the same as
|
|
|
|
stdout_is(sub {print "foo";},'foo')
|
|
|
|
C<output_isnt()> provides the opposite function of C<output_is()>. It is a
|
|
combination of C<stdout_isnt()> and C<stderr_isnt()>.
|
|
|
|
output_isnt(sub {print "foo"; print STDERR "bar";},'bar','foo');
|
|
|
|
is functionally equivalent to
|
|
|
|
stdout_is(sub {print "foo";},'bar')
|
|
&& stderr_is(sub {print STDERR "bar";'foo');
|
|
|
|
As with C<output_is()>, setting either C<$expected_stdout> or C<$expected_stderr> to
|
|
C<undef> ignores the output to that facility.
|
|
|
|
output_isnt(sub {print "foo"; print STDERR "bar";},undef,'foo');
|
|
|
|
is the same as
|
|
|
|
stderr_is(sub {print STDERR "bar";},'foo')
|
|
|
|
=cut
|
|
|
|
sub output_is (&$$;$$) {
|
|
my $test = shift;
|
|
my $expout = shift;
|
|
my $experr = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
my ( $stdout, $stderr ) = output_from($test);
|
|
|
|
my $ok = 1;
|
|
my $diag;
|
|
|
|
if ( defined($experr) && defined($expout) ) {
|
|
unless ( $stdout eq $expout ) {
|
|
$ok = 0;
|
|
$diag .= "STDOUT is:\n$stdout\nnot:\n$expout\nas expected";
|
|
}
|
|
unless ( $stderr eq $experr ) {
|
|
$diag .= "\n" unless ($ok);
|
|
$ok = 0;
|
|
$diag .= "STDERR is:\n$stderr\nnot:\n$experr\nas expected";
|
|
}
|
|
}
|
|
elsif ( defined($expout) ) {
|
|
$ok = ( $stdout eq $expout );
|
|
$diag .= "STDOUT is:\n$stdout\nnot:\n$expout\nas expected";
|
|
}
|
|
elsif ( defined($experr) ) {
|
|
$ok = ( $stderr eq $experr );
|
|
$diag .= "STDERR is:\n$stderr\nnot:\n$experr\nas expected";
|
|
}
|
|
else {
|
|
unless ( $stdout eq '' ) {
|
|
$ok = 0;
|
|
$diag .= "STDOUT is:\n$stdout\nnot:\n\nas expected";
|
|
}
|
|
unless ( $stderr eq '' ) {
|
|
$diag .= "\n" unless ($ok);
|
|
$ok = 0;
|
|
$diag .= "STDERR is:\n$stderr\nnot:\n\nas expected";
|
|
}
|
|
}
|
|
|
|
$Test->ok( $ok, $description ) || $Test->diag($diag);
|
|
|
|
return $ok;
|
|
}
|
|
|
|
sub output_isnt (&$$;$$) {
|
|
my $test = shift;
|
|
my $expout = shift;
|
|
my $experr = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
my ( $stdout, $stderr ) = output_from($test);
|
|
|
|
my $ok = 1;
|
|
my $diag;
|
|
|
|
if ( defined($experr) && defined($expout) ) {
|
|
if ( $stdout eq $expout ) {
|
|
$ok = 0;
|
|
$diag .= "STDOUT:\n$stdout\nmatching:\n$expout\nnot expected";
|
|
}
|
|
if ( $stderr eq $experr ) {
|
|
$diag .= "\n" unless ($ok);
|
|
$ok = 0;
|
|
$diag .= "STDERR:\n$stderr\nmatching:\n$experr\nnot expected";
|
|
}
|
|
}
|
|
elsif ( defined($expout) ) {
|
|
$ok = ( $stdout ne $expout );
|
|
$diag = "STDOUT:\n$stdout\nmatching:\n$expout\nnot expected";
|
|
}
|
|
elsif ( defined($experr) ) {
|
|
$ok = ( $stderr ne $experr );
|
|
$diag = "STDERR:\n$stderr\nmatching:\n$experr\nnot expected";
|
|
}
|
|
else {
|
|
if ( $stdout eq '' ) {
|
|
$ok = 0;
|
|
$diag = "STDOUT:\n$stdout\nmatching:\n\nnot expected";
|
|
}
|
|
if ( $stderr eq '' ) {
|
|
$diag .= "\n" unless ($ok);
|
|
$ok = 0;
|
|
$diag .= "STDERR:\n$stderr\nmatching:\n\nnot expected";
|
|
}
|
|
}
|
|
|
|
$Test->ok( $ok, $description ) || $Test->diag($diag);
|
|
|
|
return $ok;
|
|
}
|
|
|
|
=item B<output_like>
|
|
|
|
=item B<output_unlike>
|
|
|
|
output_like ( $coderef, $regex_stdout, $regex_stderr, 'description' );
|
|
output_like { ... } $regex_stdout, $regex_stderr, 'description';
|
|
output_unlike( $coderef, $regex_stdout, $regex_stderr, 'description' );
|
|
output_unlike { ... } $regex_stdout, $regex_stderr, 'description';
|
|
|
|
C<output_like()> and C<output_unlike()> follow the same principles as C<output_is()>
|
|
and C<output_isnt()> except they use a regular expression for matching.
|
|
|
|
C<output_like()> attempts to match C<$regex_stdout> and C<$regex_stderr> against
|
|
C<STDOUT> and C<STDERR> produced by $coderef. The test passes if both match.
|
|
|
|
output_like(sub {print "foo"; print STDERR "bar";},qr/foo/,qr/bar/);
|
|
|
|
The above test is successful.
|
|
|
|
Like C<output_is()>, setting either C<$regex_stdout> or C<$regex_stderr> to
|
|
C<undef> ignores the output to that facility.
|
|
|
|
output_like(sub {print "foo"; print STDERR "bar";},qr/foo/,undef);
|
|
|
|
is the same as
|
|
|
|
stdout_like(sub {print "foo"; print STDERR "bar";},qr/foo/);
|
|
|
|
C<output_unlike()> test pass if output from C<$coderef> doesn't match
|
|
C<$regex_stdout> and C<$regex_stderr>.
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
sub output_like (&$$;$$) {
|
|
my $test = shift;
|
|
my $expout = shift;
|
|
my $experr = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
my ( $stdout, $stderr ) = output_from($test);
|
|
|
|
my $ok = 1;
|
|
|
|
unless (
|
|
my $regextest = _chkregex(
|
|
'output_like_STDERR' => $experr,
|
|
'output_like_STDOUT' => $expout
|
|
)
|
|
)
|
|
{
|
|
return $regextest;
|
|
}
|
|
|
|
my $diag;
|
|
if ( defined($experr) && defined($expout) ) {
|
|
unless ( $stdout =~ $expout ) {
|
|
$ok = 0;
|
|
$diag .= "STDOUT:\n$stdout\ndoesn't match:\n$expout\nas expected";
|
|
}
|
|
unless ( $stderr =~ $experr ) {
|
|
$diag .= "\n" unless ($ok);
|
|
$ok = 0;
|
|
$diag .= "STDERR:\n$stderr\ndoesn't match:\n$experr\nas expected";
|
|
}
|
|
}
|
|
elsif ( defined($expout) ) {
|
|
$ok = ( $stdout =~ $expout );
|
|
$diag .= "STDOUT:\n$stdout\ndoesn't match:\n$expout\nas expected";
|
|
}
|
|
elsif ( defined($experr) ) {
|
|
$ok = ( $stderr =~ $experr );
|
|
$diag .= "STDERR:\n$stderr\ndoesn't match:\n$experr\nas expected";
|
|
}
|
|
else {
|
|
unless ( $stdout eq '' ) {
|
|
$ok = 0;
|
|
$diag .= "STDOUT is:\n$stdout\nnot:\n\nas expected";
|
|
}
|
|
unless ( $stderr eq '' ) {
|
|
$diag .= "\n" unless ($ok);
|
|
$ok = 0;
|
|
$diag .= "STDERR is:\n$stderr\nnot:\n\nas expected";
|
|
}
|
|
}
|
|
|
|
$Test->ok( $ok, $description ) || $Test->diag($diag);
|
|
|
|
return $ok;
|
|
}
|
|
|
|
sub output_unlike (&$$;$$) {
|
|
my $test = shift;
|
|
my $expout = shift;
|
|
my $experr = shift;
|
|
my $options = shift if ( ref( $_[0] ) );
|
|
my $description = shift;
|
|
|
|
my ( $stdout, $stderr ) = output_from($test);
|
|
|
|
my $ok = 1;
|
|
|
|
unless (
|
|
my $regextest = _chkregex(
|
|
'output_unlike_STDERR' => $experr,
|
|
'output_unlike_STDOUT' => $expout
|
|
)
|
|
)
|
|
{
|
|
return $regextest;
|
|
}
|
|
|
|
my $diag;
|
|
if ( defined($experr) && defined($expout) ) {
|
|
if ( $stdout =~ $expout ) {
|
|
$ok = 0;
|
|
$diag .= "STDOUT:\n$stdout\nmatches:\n$expout\nnot expected";
|
|
}
|
|
if ( $stderr =~ $experr ) {
|
|
$diag .= "\n" unless ($ok);
|
|
$ok = 0;
|
|
$diag .= "STDERR:\n$stderr\nmatches:\n$experr\nnot expected";
|
|
}
|
|
}
|
|
elsif ( defined($expout) ) {
|
|
$ok = ( $stdout !~ $expout );
|
|
$diag .= "STDOUT:\n$stdout\nmatches:\n$expout\nnot expected";
|
|
}
|
|
elsif ( defined($experr) ) {
|
|
$ok = ( $stderr !~ $experr );
|
|
$diag .= "STDERR:\n$stderr\nmatches:\n$experr\nnot expected";
|
|
}
|
|
|
|
$Test->ok( $ok, $description ) || $Test->diag($diag);
|
|
|
|
return $ok;
|
|
}
|
|
|
|
=head1 EXPORTS
|
|
|
|
By default, all subroutines are exported by default.
|
|
|
|
=over 4
|
|
|
|
=item * :stdout - the subs with C<stdout> in the name.
|
|
|
|
=item * :stderr - the subs with C<stderr> in the name.
|
|
|
|
=item * :functions - the subs with C<_from> at the end.
|
|
|
|
=item * :output - the subs with C<output> in the name.
|
|
|
|
=item * :combined - the subs with C<combined> in the name.
|
|
|
|
=item * :tests - everything that outputs TAP
|
|
|
|
=item * :all - everything (which is the same as the default)
|
|
|
|
=back
|
|
|
|
=head1 FUNCTIONS
|
|
|
|
=cut
|
|
|
|
=head2 stdout_from
|
|
|
|
my $stdout = stdout_from($coderef)
|
|
my $stdout = stdout_from { ... };
|
|
|
|
stdout_from() executes $coderef and captures STDOUT.
|
|
|
|
=cut
|
|
|
|
sub stdout_from (&) {
|
|
my $test = shift;
|
|
|
|
my $stdout = capture_stdout {
|
|
select( ( select(STDOUT), $| = 1 )[0] );
|
|
$test->()
|
|
};
|
|
|
|
return $stdout;
|
|
}
|
|
|
|
=head2 stderr_from
|
|
|
|
my $stderr = stderr_from($coderef)
|
|
my $stderr = stderr_from { ... };
|
|
|
|
C<stderr_from()> executes C<$coderef> and captures C<STDERR>.
|
|
|
|
=cut
|
|
|
|
sub stderr_from (&) {
|
|
my $test = shift;
|
|
|
|
# XXX why is this here and not in output_from or combined_from -- xdg, 2012-05-13
|
|
local $SIG{__WARN__} = sub { print STDERR @_ }
|
|
if $] < 5.008;
|
|
|
|
my $stderr = capture_stderr {
|
|
select( ( select(STDERR), $| = 1 )[0] );
|
|
$test->()
|
|
};
|
|
|
|
return $stderr;
|
|
}
|
|
|
|
=head2 output_from
|
|
|
|
my ($stdout, $stderr) = output_from($coderef)
|
|
my ($stdout, $stderr) = output_from {...};
|
|
|
|
C<output_from()> executes C<$coderef> one time capturing both C<STDOUT> and C<STDERR>.
|
|
|
|
=cut
|
|
|
|
sub output_from (&) {
|
|
my $test = shift;
|
|
|
|
my ($stdout, $stderr) = capture {
|
|
select( ( select(STDOUT), $| = 1 )[0] );
|
|
select( ( select(STDERR), $| = 1 )[0] );
|
|
$test->();
|
|
};
|
|
|
|
return ( $stdout, $stderr );
|
|
}
|
|
|
|
=head2 combined_from
|
|
|
|
my $combined = combined_from($coderef);
|
|
my $combined = combined_from {...};
|
|
|
|
C<combined_from()> executes C<$coderef> one time combines C<STDOUT> and C<STDERR>, and
|
|
captures them. C<combined_from()> is equivalent to using C<< 2>&1 >> in UNIX.
|
|
|
|
=cut
|
|
|
|
sub combined_from (&) {
|
|
my $test = shift;
|
|
|
|
my $combined = capture_merged {
|
|
select( ( select(STDOUT), $| = 1 )[0] );
|
|
select( ( select(STDERR), $| = 1 )[0] );
|
|
$test->();
|
|
};
|
|
|
|
return $combined;
|
|
}
|
|
|
|
sub _chkregex {
|
|
my %regexs = @_;
|
|
|
|
foreach my $test ( keys(%regexs) ) {
|
|
next unless ( defined( $regexs{$test} ) );
|
|
|
|
my $usable_regex = $Test->maybe_regex( $regexs{$test} );
|
|
unless ( defined($usable_regex) ) {
|
|
my $ok = $Test->ok( 0, $test );
|
|
|
|
$Test->diag("'$regexs{$test}' doesn't look much like a regex to me.");
|
|
# unless $ok;
|
|
|
|
return $ok;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
=head1 AUTHOR
|
|
|
|
Currently maintained by brian d foy, C<bdfoy@cpan.org>.
|
|
|
|
Shawn Sorichetti, C<< <ssoriche@cpan.org> >>
|
|
|
|
=head1 SOURCE AVAILABILITY
|
|
|
|
This module is in Github:
|
|
|
|
http://github.com/briandfoy/test-output/tree/master
|
|
|
|
=head1 BUGS
|
|
|
|
Please report any bugs or feature requests to
|
|
C<bug-test-output@rt.cpan.org>, or through the web interface at
|
|
L<http://rt.cpan.org>. I will be notified, and then you'll automatically
|
|
be notified of progress on your bug as I make changes.
|
|
|
|
=head1 ACKNOWLEDGEMENTS
|
|
|
|
Thanks to chromatic whose TieOut.pm was the basis for capturing output.
|
|
|
|
Also thanks to rjbs for his help cleaning the documentation, and pushing me to
|
|
L<Sub::Exporter>. (This feature has been removed since it uses none of
|
|
L<Sub::Exporter>'s strengths).
|
|
|
|
Thanks to David Wheeler for providing code block support and tests.
|
|
|
|
Thanks to Michael G Schwern for the solution to combining C<STDOUT> and C<STDERR>.
|
|
|
|
=head1 COPYRIGHT & LICENSE
|
|
|
|
Copyright 2005-2013 Shawn Sorichetti, All Rights Reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
|
under the same terms as Perl itself.
|
|
|
|
=cut
|
|
|
|
1; # End of Test::Output
|