NAME
IO::Capture::Extended - Extend functionality of IO::Capture
SYNOPSIS
The programmer interface consists of two classes:
IO::Capture::Stdout::Extended
use IO::Capture::Stdout::Extended;
$capture = IO::Capture::Stdout::Extended->new();
$capture->start();
# some code that prints to STDOUT
$capture->stop();
# scalar context: return number of print statements with 'fox'
$matches = $capture->grep_print_statements('fox');
# list context: return list of print statements with 'fox'
@matches = $capture->grep_print_statements('fox');
# return number of print statements
$matches = $capture->statements;
# scalar context: return number of pattern matches
$regex = qr/some regular expression/;
$matches = $capture->matches($regex);
# list context: return list of pattern matches
@matches = $capture->matches($regex);
# return reference to array holding list of pattern matches
$matchesref = $capture->matches_ref($regex);
# scalar context: return number of 'screen' lines printed
$screen_lines = $capture->all_screen_lines();
# list context: return list of 'screen' lines printed
@all_screen_lines = $capture->all_screen_lines();
IO::Capture::Stderr::Extended
$capture = IO::Capture::Stderr::Extended->new();
$capture->start();
# some code that prints to STDERR
$capture->stop();
... and then use all the methods defined above for IO::Capture::Stdout::Extended.
DESCRIPTION
IO::Capture::Extended is a distribution consisting of two classes, each of which is a collection of subroutines which are useful in extending the functionality of CPAN modules IO::Capture::Stdout and IO::Capture::Stderr, particularly when used in a testing context such as that provided by Test::Simple, Test::More or other modules built on Test::Builder.
USAGE
Requirements
IO::Capture distribution, available from CPAN http://search.cpan.org/~reynolds/IO-Capture-0.05/. Use version 0.05 or later to take advantage of important bug fixes. The IO::Capture distribution includes base class IO::Capture, IO::Capture::Stdout, IO::Capture::Stderr and other packages. It also includes useful documentation in IO::Capture::Overview.
General Comments
The IO::Capture::Stdout::Extended and IO::Capture::Stdout::Extended methods are designed to provide return values which work nicely as arguments to Test::More functions such as ok()
, is()
and like()
. The examples below illustrate that objective. Suggestions are welcome for additional methods which would fulfill that objective.
Individual Methods
Note: Since IO::Capture::Extended is structured so as to make exactly the same methods available for IO::Capture::Stdout::Extended and IO::Capture::Stderr::Extended, whenver there appears below a reference to, e.g., IO::Capture::Stdout::Extended::grep_print_statements()
, you should assume that the remarks apply equally to IO::Capture::Stderr::Extended::grep_print_statements()
. Wherever reference is made to STDOUT, the remarks apply to STDERR as well.
grep_print_statements()
Scalar Context
Problem: You wish to test a function that prints to STDOUT. You can predict the number of
print
statements that match a pattern and wish to test that prediction. (The example below is adapted from IO::Capture::Overview.)sub print_fox { print "The quick brown fox jumped over ..."; print "garden wall"; print "The quick red fox jumped over ..."; print "garden wall"; } $capture->start; print_fox(); $capture->stop; $matches = $capture->grep_print_statements('fox'); is($capture->grep_print_statements('fox'), 2, "correct no. of print statements grepped");
Solution: Precede the function call with a call to the IO::Capture::Stdout::Extended
start()
method and follow it with a call to thestop()
method. Callgrep_print_statements
. Use its return value as one of two arguments toTest::More::is()
. Use your prediction as the other argument. Add a useful comment tois()
.Potential Pitfall: The number of print statements captured between
IO::Capture::Stdout::Extended::start()
andstop()
is not necessarily the number of lines that would appear to be printed to standard output by a given block of code. If your subroutine or other code block prints partial lines -- i.e., lines lacking\n
newline characters -- the number of print statements will be greater than the number of ''screen lines.'' This is illustrated by the following:sub print_fox_long { print "The quick brown fox jumped over ..."; print "a less adept fox\n"; print "The quick red fox jumped over ..."; print "the garden wall\n"; } $capture->start; print_fox_long(); $capture->stop; $matches = $capture->grep_print_statements("fox"); is($capture->grep_print_statements("fox"), 3, "correct no. of print statements grepped");
The number of
print
statements matchingfox
is three -- even though the number of lines on the screen which appear on STDOUT containingfox
is only two.List Context
Problem: As above, you wish to test a function that prints to STDOUT. This time, you can predict the content of
print
statements that match a pattern and wish to test that prediction.%matches = map { $_, 1 } $capture->grep_print_statements('fox'); is(keys %matches, 2, "correct no. of print statements grepped"); ok($matches{'The quick brown fox jumped over ...'}, 'print statement correctly grepped'); ok($matches{'The quick red fox jumped over ...'}, 'print statement correctly grepped');
Solution: As above, call
grep_print_statements
, but map its output to a 'seen-hash'. You can then use the number of keys in that seen-hash as an argument toTest::More::is()
. You can useok()
to test whether the keys of that hash are as predicted.
statements()
Problem: You've written a function which prints to STDOUT. You can make a prediction as to the number of screen lines which should be printed. You want to test that prediction with Test::More::is()
.
sub print_greek {
local $_;
print "$_\n" for (qw| alpha beta gamma delta |);
}
$capture->start();
print_greek();
$capture->stop();
is($capture->statements, 4,
"number of print statements is correct");
Solution: Precede the function call with a call to the IO::Capture::Stdout start()
method and follow it with a call to the stop()
method. Call IO::Capture::Stdout::Extended::statements()
and use its return value as the first argument to is()
. Use your prediction as the second argument to is()
. Be sure to write a useful comment for your test.
Potential Pitfall: The number of print statements returned by statements
is not necessarily the number of lines that would appear to be printed to standard output by a given block of code. If your subroutine or other code block prints partial lines -- i.e., lines lacking \n
newline characters -- the number of print statements will be greater than the number of ''screen lines.'' This is illustrated by the following:
sub print_greek_long {
local $_;
for (qw| alpha beta gamma delta |) {
print $_;
print "\n";
}
}
$capture->start();
print_greek_long();
$capture->stop();
is($capture->statements, 8,
"number of print statements is correct");
This pitfall can be avoided by using all_screen_lines()
below.
all_screen_lines()
Scalar Context
Returns the number of lines which would normally be counted by eye on STDOUT. This number is not necessarily equal to the number of
print()
statements found in the captured output. This method avoids the 'pitfall' found when usingstatements()
above.$capture->start(); print_greek_long(); $capture->stop(); $screen_lines = $capture->all_screen_lines; is($screen_lines, 4, "correct no. of lines printed to screen");
List Context
Returns an array holding lines as normally viewed on STDOUT. The size of this array is not necessarily equal to the number of
print()
statements found in the captured output. This method avoids the 'pitfall' found when usingstatements()
above.$capture->start(); print_greek_long(); $capture->stop(); @all_screen_lines = $capture->all_screen_lines; is($all_screen_lines[0], "alpha", "line correctly printed to screen"); is($all_screen_lines[1], "beta", "line correctly printed to screen");
Any newline (
\n
) appearing at the end of a screen line is not included in the list of lines returned by this method, i.e., the lines are chomped.
matches()
Scalar Context
Problem: You've written a function which, much like the ''mail merge'' function in word processing programs, extracts data from some data source, merges the data with text in a standard form, and prints the result to STDOUT. You make a prediction as to the number of forms which are printed to STDOUT and wish to confirm that prediction.
my @week = ( [ qw| Monday Lundi Lunes | ], [ qw| Tuesday Mardi Martes | ], [ qw| Wednesday Mercredi Miercoles | ], [ qw| Thursday Jeudi Jueves | ], [ qw| Friday Vendredi Viernes | ], [ qw| Saturday Samedi Sabado | ], [ qw| Sunday Dimanche Domingo | ], ); sub print_week { my $weekref = shift; my @week = @{$weekref}; for (my $day=0; $day<=$#week; $day++) { print "English: $week[$day][0]\n"; print "French: $week[$day][1]\n"; print "Spanish: $week[$day][2]\n"; print "\n"; } } $capture->start(); print_week(\@week); $capture->stop(); $regex = qr/English:.*?French:.*?Spanish:/s; is($capture->matches($regex), 7, "correct number of forms printed to screen");
Solution: Precede the function call with a call to the IO::Capture::Stdout
start()
method and follow it with a call to thestop()
method. Write a Perl regular expression and assign it to a variable using theqr//
notation. (Remember to use the/s
modifier if the text you are testing crosses screen lines.) Pass the regex variable toIO::Capture::Stdout::Extended::matches()
and use the return value of that method call as one argument toTest::More::is()
. Use your prediction as the second argument tois()
. Be sure to write a useful comment for your test.List Context
Problem: As above, you've written a function which, much like the ''mail merge'' function in word processing programs, extracts data from some data source, merges the data with text in a standard form, and prints the result to STDOUT. This time, however, you wish to do a quick test on the results by examining a sample form.
$capture->start(); print_week(\@week); # as defined above $capture->stop(); @matches = $capture->matches($regex); $predicted = "English: Monday\nFrench: Lundi\nSpanish:"; is($matches[0], $predicted, "first form matches test portion");
Solution: Same as above, but capture the output of
matches()
in a list or array. Write a string which predicts typical contents of one instance of your merged form. Use the contents of one form (one element in the list output) and the prediction string as arguments tois()
. Be sure to write a useful comment for your test.Problem: As above, but now you wish to make sure that a form was generated for each required field in the data source.
$regex = qr/French:\s+(.*?)\n/s; @predicted = qw| Lundi Mardi Mercredi Jeudi Vendredi Samedi Dimanche |; ok(eq_array( [ $capture->matches($regex) ], \@predicted ), "all predicted matches found");
Solution: Similar to above, but this time you predict which data points are going to be present in the output. Store that prediction in a list or array. Take references to (a) the array holding that prediction; and (b) the result of
$capture-
matches($regex)>. Pass those two references to Test::More's utility functioneq_array
, which will return a true value if the underlying arrays are identical element-by-element. Pass that value in turn to <ok()>. As always, be sure to write a useful comment for your test.
matches_ref()
Problem: Same as the first ''List Context'' example above, but now you would prefer to work with a method that returns an array reference rather than all the elements in a list.
$matchesref = $capture->matches_ref($regex);
is(${$matchesref}[0], $predicted, "first form matches test portion");
Solution: Call IO::Capture::Stdout::Extended::matches_ref()
instead of matches
. You will have to rephrase your test in terms of an element of a dereferenced array.
BUGS
As Paul Johnson says, ''Did I mention that this is alpha code?''.
SUPPORT
Contact the author or post to the perl-qa mailing list.
ACKNOWLEDGEMENTS
Thanks go first and foremost to the two authors of the IO::Capture distribution, Mark Reynolds and Jon Morgan. Mark Reynolds was responsive to this module's author's suggestions for bug fixes and additional tests. The documentation found in IO::Capture::Overview was particularly helpful in showing me how to extend IO::Capture's functionality. This distribution is maintained independently but will be updated as needed if and when IO::Capture is revised.
The methods in IO::Capture::Extended are offered to the Perl community in gratitude for being turned on to IO::Capture by David Cantrell on the perl.qa mailing list in February 2005 (http://www.nntp.perl.org/group/perl.qa/3567).
Other contributors to that discussion thread whose suggestions are reflected in this module were David H. Adler, David Golden, Michael G. Schwern and Tels. Fergal Daly also made suggestions in separate communications.
The structure for this module was created with my own hacked-up version of R. Geoffrey Avery's modulemaker utility, based on his CPAN module ExtUtils::ModuleMaker.
AUTHOR
James E Keenan. CPAN ID: JKEENAN. jkeenan [at] cpan [dot] org.
COPYRIGHT
Copyright 2005-15 James E Keenan.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the LICENSE file included with this module.
SEE ALSO
perl(1). IO::Capture; IO::Capture::Stdout; IO::Capture::Overview. Test::Simple; Test::More.