Codebase list libcapture-tiny-perl / 061c0d1
Imported Upstream version 0.13 gregor herrmann 12 years ago
34 changed file(s) with 813 addition(s) and 305 deletion(s). Raw diff Collapse all Expand all
00 Revision history for Capture-Tiny
1
2 0.13 2011-12-02 13:39:00 EST5EDT
3
4 Fixed:
5
6 - Fixed t/18-custom-capture.t failures on Windows due to tempfile
7 removal problems in the testfile
8
9 0.12 2011-12-01 16:58:05 EST5EDT
10
11 Added:
12
13 - New functions capture_stdout, capture_stderr, tee_stdout, tee_stderr
14 [rt.cpan.org #60515]
15
16 - Capture functions also returns the return values from the executed
17 coderef [rt.cpan.org #61794, adapted from patch by Christian Walde]
18
19 - Capture functions take optional custom filehandles for capturing
20 via named files instead of anonymous ones [inspired by Christian Walde]
21
22 Fixed:
23
24 - Tied filehandles based on Tie::StdHandle can now use the ":utf8"
25 layer; removed remaining TODO tests; adds Scalar::Util as a dependency
26
27 Changed:
28
29 - When Time::HiRes::usleep is available, tee operations will
30 sleep during the busy-loop waiting for tee processes to be ready
31 [rt.cpan.org #67858]
132
233 0.11 2011-05-19 23:34:23 America/New_York
334
44 META.yml
55 Makefile.PL
66 README
7 README.PATCHING
78 Todo
89 dist.ini
910 examples/rt-58208.pl
2526 t/14-stderr-tied.t
2627 t/15-stdin-tied.t
2728 t/16-catch-errors.t
29 t/17-pass-results.t
30 t/18-custom-capture.t
2831 t/lib/Cases.pm
2932 t/lib/TieLC.pm
3033 t/lib/Utils.pm
3235 xt/release/pod-coverage.t
3336 xt/release/pod-syntax.t
3437 xt/release/portability.t
38 xt/release/test-version.t
33 "David Golden <dagolden@cpan.org>"
44 ],
55 "dynamic_config" : 1,
6 "generated_by" : "Dist::Zilla version 4.200005, CPAN::Meta::Converter version 2.110580",
6 "generated_by" : "Dist::Zilla version 4.300002, CPAN::Meta::Converter version 2.112580",
77 "license" : [
88 "apache_2_0"
99 ],
2626 "prereqs" : {
2727 "configure" : {
2828 "requires" : {
29 "ExtUtils::MakeMaker" : "6.31"
29 "ExtUtils::MakeMaker" : "6.30"
3030 }
3131 },
3232 "runtime" : {
3636 "File::Spec" : 0,
3737 "File::Temp" : 0,
3838 "IO::Handle" : 0,
39 "perl" : "5.006"
39 "Scalar::Util" : 0,
40 "perl" : "5.006",
41 "strict" : 0,
42 "warnings" : 0
4043 }
4144 },
4245 "test" : {
4346 "requires" : {
4447 "Config" : 0,
4548 "File::Find" : 0,
49 "IO::File" : 0,
4650 "Test::More" : "0.62"
4751 }
4852 }
5054 "provides" : {
5155 "Capture::Tiny" : {
5256 "file" : "lib/Capture/Tiny.pm",
53 "version" : "0.11"
57 "version" : "0.13"
5458 }
5559 },
5660 "release_status" : "stable",
5963 "mailto" : "bug-capture-tiny at rt.cpan.org",
6064 "web" : "http://rt.cpan.org/Public/Dist/Display.html?Name=Capture-Tiny"
6165 },
62 "homepage" : "http://github.com/dagolden/capture-tiny/tree",
66 "homepage" : "https://github.com/dagolden/capture-tiny",
6367 "repository" : {
6468 "type" : "git",
65 "url" : "git://github.com/dagolden/capture-tiny.git",
66 "web" : "http://github.com/dagolden/capture-tiny/tree"
69 "url" : "https://github.com/dagolden/capture-tiny.git",
70 "web" : "https://github.com/dagolden/capture-tiny"
6771 }
6872 },
69 "version" : "0.11"
73 "version" : "0.13"
7074 }
7175
44 build_requires:
55 Config: 0
66 File::Find: 0
7 IO::File: 0
78 Test::More: 0.62
89 configure_requires:
9 ExtUtils::MakeMaker: 6.31
10 ExtUtils::MakeMaker: 6.30
1011 dynamic_config: 1
11 generated_by: 'Dist::Zilla version 4.200005, CPAN::Meta::Converter version 2.110580'
12 generated_by: 'Dist::Zilla version 4.300002, CPAN::Meta::Converter version 2.112580'
1213 license: apache
1314 meta-spec:
1415 url: http://module-build.sourceforge.net/META-spec-v1.4.html
2526 provides:
2627 Capture::Tiny:
2728 file: lib/Capture/Tiny.pm
28 version: 0.11
29 version: 0.13
2930 requires:
3031 Carp: 0
3132 Exporter: 0
3233 File::Spec: 0
3334 File::Temp: 0
3435 IO::Handle: 0
36 Scalar::Util: 0
3537 perl: 5.006
38 strict: 0
39 warnings: 0
3640 resources:
3741 bugtracker: http://rt.cpan.org/Public/Dist/Display.html?Name=Capture-Tiny
38 homepage: http://github.com/dagolden/capture-tiny/tree
39 repository: git://github.com/dagolden/capture-tiny.git
40 version: 0.11
42 homepage: https://github.com/dagolden/capture-tiny
43 repository: https://github.com/dagolden/capture-tiny.git
44 version: 0.13
11 use strict;
22 use warnings;
33
4 BEGIN { require 5.006; }
4 use 5.006;
55
6 use ExtUtils::MakeMaker 6.31;
6 use ExtUtils::MakeMaker 6.30;
77
88
99
1010 my %WriteMakefileArgs = (
11 'ABSTRACT' => 'Capture STDOUT and STDERR from Perl, XS or external programs',
12 'AUTHOR' => 'David Golden <dagolden@cpan.org>',
13 'BUILD_REQUIRES' => {
14 'Config' => '0',
15 'File::Find' => '0',
16 'Test::More' => '0.62'
11 "ABSTRACT" => "Capture STDOUT and STDERR from Perl, XS or external programs",
12 "AUTHOR" => "David Golden <dagolden\@cpan.org>",
13 "BUILD_REQUIRES" => {
14 "Config" => 0,
15 "File::Find" => 0,
16 "IO::File" => 0,
17 "Test::More" => "0.62"
1718 },
18 'CONFIGURE_REQUIRES' => {
19 'ExtUtils::MakeMaker' => '6.31'
19 "CONFIGURE_REQUIRES" => {
20 "ExtUtils::MakeMaker" => "6.30"
2021 },
21 'DISTNAME' => 'Capture-Tiny',
22 'EXE_FILES' => [],
23 'LICENSE' => 'apache',
24 'NAME' => 'Capture::Tiny',
25 'PREREQ_PM' => {
26 'Carp' => '0',
27 'Exporter' => '0',
28 'File::Spec' => '0',
29 'File::Temp' => '0',
30 'IO::Handle' => '0'
22 "DISTNAME" => "Capture-Tiny",
23 "EXE_FILES" => [],
24 "LICENSE" => "apache",
25 "NAME" => "Capture::Tiny",
26 "PREREQ_PM" => {
27 "Carp" => 0,
28 "Exporter" => 0,
29 "File::Spec" => 0,
30 "File::Temp" => 0,
31 "IO::Handle" => 0,
32 "Scalar::Util" => 0,
33 "strict" => 0,
34 "warnings" => 0
3135 },
32 'VERSION' => '0.11',
33 'test' => {
34 'TESTS' => 't/*.t'
36 "VERSION" => "0.13",
37 "test" => {
38 "TESTS" => "t/*.t"
3539 }
3640 );
3741
+128
-46
README less more
22 programs
33
44 VERSION
5 version 0.11
5 version 0.13
66
77 SYNOPSIS
8 use Capture::Tiny qw/capture tee capture_merged tee_merged/;
8 use Capture::Tiny ':all';
99
10 ($stdout, $stderr) = capture {
10 ($stdout, $stderr, @result) = capture {
1111 # your code here
1212 };
13
14 $stdout = capture_stdout { ... };
15 $stderr = capture_stderr { ... };
16 $merged = capture_merged { ... };
1317
1418 ($stdout, $stderr) = tee {
1519 # your code here
1620 };
1721
18 $merged = capture_merged {
19 # your code here
20 };
21
22 $merged = tee_merged {
23 # your code here
24 };
22 $stdout = tee_stdout { ... };
23 $stderr = tee_stderr { ... };
24 $merged = tee_merged { ... };
2525
2626 DESCRIPTION
27 Capture::Tiny provides a simple, portable way to capture anything sent
28 to STDOUT or STDERR, regardless of whether it comes from Perl, from XS
29 code or from an external program. Optionally, output can be teed so that
30 it is captured while being passed through to the original handles. Yes,
31 it even works on Windows. Stop guessing which of a dozen capturing
32 modules to use in any particular situation and just use this one.
33
34 This module was heavily inspired by IO::CaptureOutput, which provides
35 similar functionality without the ability to tee output and with more
36 complicated code and API.
27 Capture::Tiny provides a simple, portable way to capture almost anything
28 sent to STDOUT or STDERR, regardless of whether it comes from Perl, from
29 XS code or from an external program. Optionally, output can be teed so
30 that it is captured while being passed through to the original handles.
31 Yes, it even works on Windows (usually). Stop guessing which of a dozen
32 capturing modules to use in any particular situation and just use this
33 one.
3734
3835 USAGE
3936 The following functions are available. None are exported by default.
4037
4138 capture
42 ($stdout, $stderr) = capture \&code;
39 ($stdout, $stderr, @result) = capture \&code;
4340 $stdout = capture \&code;
4441
4542 The "capture" function takes a code reference and returns what is sent
46 to STDOUT and STDERR. In scalar context, it returns only STDOUT. If no
47 output was received, returns an empty string. Regardless of context, all
48 output is captured -- nothing is passed to the existing handles.
43 to STDOUT and STDERR as well as any return values from the code
44 reference. In scalar context, it returns only STDOUT. If no output was
45 received for a handle, it returns an empty string for that handle.
46 Regardless of calling context, all output is captured -- nothing is
47 passed to the existing handles.
4948
5049 It is prototyped to take a subroutine reference as an argument. Thus, it
5150 can be called in block form:
5453 # your code here ...
5554 };
5655
56 Note that the coderef is evaluated in list context. If you wish to force
57 scalar context on the return value, you must use the "scalar" keyword.
58
59 ($stdout, $stderr, $count) = capture {
60 my @list = qw/one two three/;
61 return scalar @list; # $count will be 3
62 };
63
64 Captures are normally done internally to an anonymous filehandle. To
65 capture via a named file (e.g. to externally monitor a long-running
66 capture), provide custom filehandles as a trailing list of option pairs:
67
68 my $out_fh = IO::File->new("out.txt", "w+");
69 my $err_fh = IO::File->new("out.txt", "w+");
70 capture { ... } stdout => $out_fh, stderr => $err_fh;
71
72 The filehandles must be read/write and seekable and should be empty.
73 Modifying the files externally during a capture operation will give
74 unpredictable results. Existing IO layers on them may be changed by the
75 capture.
76
77 capture_stdout
78 ($stdout, @result) = capture_stdout \&code;
79 $stdout = capture_stdout \&code;
80
81 The "capture_stdout" function works just like "capture" except only
82 STDOUT is captured. STDERR is not captured.
83
84 capture_stderr
85 ($stderr, @result) = capture_stderr \&code;
86 $stderr = capture_stderr \&code;
87
88 The "capture_stderr" function works just like "capture" except only
89 STDERR is captured. STDOUT is not captured.
90
5791 capture_merged
92 ($merged, @result) = capture_merged \&code;
5893 $merged = capture_merged \&code;
5994
6095 The "capture_merged" function works just like "capture" except STDOUT
6196 and STDERR are merged. (Technically, STDERR is redirected to STDOUT
62 before executing the function.) If no output was received, returns an
63 empty string. As with "capture" it may be called in block form.
97 before executing the function.)
6498
6599 Caution: STDOUT and STDERR output in the merged result are not
66100 guaranteed to be properly ordered due to buffering.
67101
68102 tee
69 ($stdout, $stderr) = tee \&code;
103 ($stdout, $stderr, @result) = tee \&code;
70104 $stdout = tee \&code;
71105
72106 The "tee" function works just like "capture", except that output is
73 captured as well as passed on to the original STDOUT and STDERR. As with
74 "capture" it may be called in block form.
107 captured as well as passed on to the original STDOUT and STDERR.
108
109 tee_stdout
110 ($stdout, @result) = tee_stdout \&code;
111 $stdout = tee_stdout \&code;
112
113 The "tee_stdout" function works just like "tee" except only STDOUT is
114 teed. STDERR is not teed (output goes to STDERR as usual).
115
116 tee_stderr
117 ($stderr, @result) = tee_stderr \&code;
118 $stderr = tee_stderr \&code;
119
120 The "tee_stderr" function works just like "tee" except only STDERR is
121 teed. STDOUT is not teed (output goes to STDOUT as usual).
75122
76123 tee_merged
124 ($merged, @result) = tee_merged \&code;
77125 $merged = tee_merged \&code;
78126
79127 The "tee_merged" function works just like "capture_merged" except that
80 output is captured as well as passed on to STDOUT. As with "capture" it
81 may be called in block form.
128 output is captured as well as passed on to STDOUT.
82129
83130 Caution: STDOUT and STDERR output in the merged result are not
84131 guaranteed to be properly ordered due to buffering.
92139 PerlIO layers
93140 Capture::Tiny does it's best to preserve PerlIO layers such as ':utf8'
94141 or ':crlf' when capturing. Layers should be applied to STDOUT or STDERR
95 *before* the call to "capture" or "tee".
96
97 Closed STDIN, STDOUT or STDERR
142 *before* the call to "capture" or "tee". This may not work for tied
143 handles (see below).
144
145 Modifying filehandles before capturing
146 Generally speaking, you should do little or no manipulation of the
147 standard IO handles prior to using Capture::Tiny. In particular,
148 closing, reopening, localizing or tying standard handles prior to
149 capture may cause a variety of unexpected, undesireable and/or
150 unreliable behaviors, as described below. Capture::Tiny does its best to
151 compensate for these situations, but the results may not be what you
152 desire.
153
154 Closed filehandles
155
98156 Capture::Tiny will work even if STDIN, STDOUT or STDERR have been
99 previously closed. However, since they may be reopened to capture or tee
100 output, any code within the captured block that depends on finding them
101 closed will, of course, not find them to be closed. If they started
157 previously closed. However, since they will be reopened to capture or
158 tee output, any code within the captured block that depends on finding
159 them closed will, of course, not find them to be closed. If they started
102160 closed, Capture::Tiny will reclose them again when the capture block
103161 finishes.
104162
105 Scalar filehandles and STDIN, STDOUT or STDERR
163 Note that this reopening will happen even for STDIN or a handle not
164 being captured to ensure that the filehandle used for capture is not
165 opened to file descriptor 0, as this causes problems on various
166 platforms.
167
168 Localized filehandles
169
170 If code localizes any of Perl's standard handles before capturing, the
171 capture will affect the localized handles and not the original ones.
172 External system calls are not affected by localizing a handle in Perl
173 and will continue to send output to the original handles (which will
174 thus not be captured).
175
176 Scalar filehandles
177
106178 If STDOUT or STDERR are reopened to scalar filehandles prior to the call
107179 to "capture" or "tee", then Capture::Tiny will override the output
108180 handle for the duration of the "capture" or "tee" call and then send
112184 Capture::Tiny attempts to preserve the semantics of STDIN opened to a
113185 scalar reference.
114186
115 Tied STDIN, STDOUT or STDERR
187 Tied handles
188
116189 If STDOUT or STDERR are tied prior to the call to "capture" or "tee",
117190 then Capture::Tiny will attempt to override the tie for the duration of
118191 the "capture" or "tee" call and then send captured output to the tied
119192 handle after the capture is complete. (Requires Perl 5.8)
120193
121 Capture::Tiny does not (yet) support resending utf8 encoded data to a
122 tied STDOUT or STDERR handle. Characters will appear as bytes.
194 Capture::Tiny may not succeed resending utf8 encoded data to a tied
195 STDOUT or STDERR handle. Characters may appear as bytes. If the tied
196 handle is based on Tie::StdHandle, then Capture::Tiny will attempt to
197 determine appropriate layers like ":utf8" from the underlying handle and
198 do the right thing.
123199
124200 Capture::Tiny attempts to preserve the semantics of tied STDIN, but
125201 capturing or teeing when STDIN is tied is currently broken on Windows.
126202
127 Modifiying STDIN, STDOUT or STDERR during a capture
203 Modifiying handles during a capture
128204 Attempting to modify STDIN, STDOUT or STDERR *during* "capture" or "tee"
129205 is almost certainly going to cause problems. Don't do that.
130206
149225 to an existing test-file that illustrates the bug or desired feature.
150226
151227 SEE ALSO
152 This is a selection of CPAN modules that provide some sort of output
228 This module was, inspired by IO::CaptureOutput, which provides similar
229 functionality without the ability to tee output and with more
230 complicated code and API. IO::CaptureOutput does not handle layers or
231 most of the unusual cases described in the "Limitations" section and I
232 no longer recommend it.
233
234 There are many other CPAN modules that provide some sort of output
153235 capture, albeit with various limitations that make them appropriate only
154236 in particular circumstances. I'm probably missing some. The long list is
155237 provided to show why I felt Capture::Tiny was necessary.
208290 This is open source software. The code repository is available for
209291 public review and contribution under the terms of the license.
210292
211 <http://github.com/dagolden/capture-tiny/tree>
212
213 git clone git://github.com/dagolden/capture-tiny.git
293 <https://github.com/dagolden/capture-tiny>
294
295 git clone https://github.com/dagolden/capture-tiny.git
214296
215297 AUTHOR
216298 David Golden <dagolden@cpan.org>
0 README.PATCHING
1
2 Thank you for considering contributing to this distribution. This file
3 contains instructions that will help you work with the source code.
4
5 The distribution is managed with Dist::Zilla. This means than many of the
6 usual files you might expect are not in the repository, but are generated
7 at release time (e.g. Makefile.PL).
8
9 However, you can run tests directly using the 'prove' tool:
10
11 $ prove -l
12 $ prove -lv t/some_test_file.t
13
14 For most distributions, 'prove' is entirely sufficent for you to test any
15 patches you have.
16
17 Likewise, much of the documentation Pod is generated at release time.
18 Depending on the distribution, some documentation may be written in a Pod
19 dialect called WikiDoc. (See Pod::WikiDoc on CPAN.) If you would like to
20 submit a documentation edit, please limit yourself to the documentation you
21 see.
22
23 If you see typos or documentation issues in the generated docs, please
24 email or open a bug ticket instead of patching.
25
26 Dist::Zilla is a very powerful authoring tool, but requires a number of
27 author-specific plugins. If you would like to use it for contributing,
28 install it from CPAN, then run one of the following commands, depending on
29 your CPAN client:
30
31 $ cpan `dzil authordeps`
32 $ dzil authordeps | cpanm
33
34 Once installed, here are some dzil commands you might try:
35
36 $ dzil build
37 $ dzil test
38 $ dzil xtest
39
40 You can learn more about Dist::Zilla at http://dzil.org/
41
44 copyright_year = 2009
55
66 [@DAGOLDEN]
7 git_remote = github
87
98 [OSPrereqs / MSWin32]
109 Win32API::File = 0
0 #
1 # This file is part of Capture-Tiny
2 #
3 # This software is Copyright (c) 2009 by David Golden.
4 #
5 # This is free software, licensed under:
6 #
7 # The Apache License, Version 2.0, January 2004
8 #
90 use Capture::Tiny qw[ capture ];
101
112 my ( $out, $err ) =
0 #
1 # This file is part of Capture-Tiny
2 #
3 # This software is Copyright (c) 2009 by David Golden.
4 #
5 # This is free software, licensed under:
6 #
7 # The Apache License, Version 2.0, January 2004
8 #
90 use strict;
101 use warnings;
112
0 #
1 # This file is part of Capture-Tiny
2 #
3 # This software is Copyright (c) 2009 by David Golden.
4 #
5 # This is free software, licensed under:
6 #
7 # The Apache License, Version 2.0, January 2004
8 #
90 use 5.006;
101 use strict;
112 use warnings;
123 package Capture::Tiny;
13 BEGIN {
14 $Capture::Tiny::VERSION = '0.11';
15 }
164 # ABSTRACT: Capture STDOUT and STDERR from Perl, XS or external programs
5 our $VERSION = '0.13'; # VERSION
176 use Carp ();
187 use Exporter ();
198 use IO::Handle ();
209 use File::Spec ();
2110 use File::Temp qw/tempfile tmpnam/;
11 use Scalar::Util qw/reftype blessed/;
2212 # Get PerlIO or fake it
2313 BEGIN {
2414 local $@;
2616 or *PerlIO::get_layers = sub { return () };
2717 }
2818
19 #--------------------------------------------------------------------------#
20 # create API subroutines and export them
21 # [do STDOUT flag, do STDERR flag, do merge flag, do tee flag]
22 #--------------------------------------------------------------------------#
23
24 my %api = (
25 capture => [1,1,0,0],
26 capture_stdout => [1,0,0,0],
27 capture_stderr => [0,1,0,0],
28 capture_merged => [1,0,1,0], # don't do STDERR since merging
29 tee => [1,1,0,1],
30 tee_stdout => [1,0,0,1],
31 tee_stderr => [0,1,0,1],
32 tee_merged => [1,0,1,1], # don't do STDERR since merging
33 );
34
35 for my $sub ( keys %api ) {
36 my $args = join q{, }, @{$api{$sub}};
37 eval "sub $sub(&;@) {unshift \@_, $args; goto \\&_capture_tee;}"; ## no critic
38 }
39
2940 our @ISA = qw/Exporter/;
30 our @EXPORT_OK = qw/capture capture_merged tee tee_merged/;
41 our @EXPORT_OK = keys %api;
3142 our %EXPORT_TAGS = ( 'all' => \@EXPORT_OK );
3243
44 #--------------------------------------------------------------------------#
45 # constants and fixtures
46 #--------------------------------------------------------------------------#
47
3348 my $IS_WIN32 = $^O eq 'MSWin32';
3449
3550 our $DEBUG = $ENV{PERL_CAPTURE_TINY_DEBUG};
51
3652 my $DEBUGFH;
37 open $DEBUGFH, ">&STDERR" if $DEBUG;
53 open $DEBUGFH, "> DEBUG" if $DEBUG;
3854
3955 *_debug = $DEBUG ? sub(@) { print {$DEBUGFH} @_ } : sub(){0};
4056
5874
5975 sub _relayer {
6076 my ($fh, $layers) = @_;
61 _debug("# requested layers (@{$layers}) to $fh\n");
77 _debug("# requested layers (@{$layers}) for @{[fileno $fh]}\n");
6278 my %seen = ( unix => 1, perlio => 1 ); # filter these out
6379 my @unique = grep { !$seen{$_}++ } @$layers;
64 _debug("# applying unique layers (@unique) to $fh\n");
80 _debug("# applying unique layers (@unique) to @{[fileno $fh]}\n");
6581 binmode($fh, join(":", ":raw", @unique));
6682 }
6783
222238 $stash->{pid}{$which} = $pid
223239 }
224240
225 sub _files_exist { -f $_ || return 0 for @_; return 1 }
241 my $have_usleep = eval "use Time::HiRes 'usleep'; 1";
242 sub _files_exist {
243 return 1 if @_ == grep { -f } @_;
244 Time::HiRes::usleep(1000) if $have_usleep;
245 return 0;
246 }
226247
227248 sub _wait_for_tees {
228249 my ($stash) = @_;
251272 }
252273
253274 sub _slurp {
254 seek $_[0],0,0; local $/; return scalar readline $_[0];
275 my ($name, $fh) = @_;
276 _debug( "# slurping captured $name with layers: @{[PerlIO::get_layers($fh)]}\n");
277 seek $fh,0,0; local $/; return scalar readline $fh
255278 }
256279
257280 #--------------------------------------------------------------------------#
260283
261284 sub _capture_tee {
262285 _debug( "# starting _capture_tee with (@_)...\n" );
263 my ($tee_stdout, $tee_stderr, $merge, $code) = @_;
286 my ($do_stdout, $do_stderr, $do_merge, $do_tee, $code, @opts) = @_;
287 Carp::confess("Custom capture options must be given as key/value pairs\n")
288 unless @opts % 2 == 0;
289 my $stash = { capture => { @opts } };
290 for my $n ( keys %{$stash->{capture}} ) {
291 my $fh = $stash->{capture}{$n};
292 Carp::confess "Custom handle for $n must be seekable\n"
293 unless ref($fh) eq 'GLOB' || (blessed($fh) && $fh->isa("IO::Seekable"));
294 }
264295 # save existing filehandles and setup captures
265296 local *CT_ORIG_STDIN = *STDIN ;
266297 local *CT_ORIG_STDOUT = *STDOUT;
272303 stderr => [PerlIO::get_layers(\*STDERR)],
273304 );
274305 _debug( "# existing layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/;
306 # get layers from underlying glob of tied filehandles if we can
307 # (this only works for things that work like Tie::StdHandle)
308 $layers{stdout} = [PerlIO::get_layers(tied *STDOUT)]
309 if tied(*STDOUT) && (reftype tied *STDOUT eq 'GLOB');
310 $layers{stderr} = [PerlIO::get_layers(tied *STDERR)]
311 if tied(*STDERR) && (reftype tied *STDERR eq 'GLOB');
312 _debug( "# tied object corrected layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/;
275313 # bypass scalar filehandles and tied handles
276314 my %localize;
277 $localize{stdin}++, local(*STDIN) if grep { $_ eq 'scalar' } @{$layers{stdin}};
278 $localize{stdout}++, local(*STDOUT) if grep { $_ eq 'scalar' } @{$layers{stdout}};
279 $localize{stderr}++, local(*STDERR) if grep { $_ eq 'scalar' } @{$layers{stderr}};
280 $localize{stdout}++, local(*STDOUT), _open( \*STDOUT, ">&=1") if tied *STDOUT && $] >= 5.008;
281 $localize{stderr}++, local(*STDERR), _open( \*STDERR, ">&=2") if tied *STDERR && $] >= 5.008;
315 $localize{stdin}++, local(*STDIN)
316 if grep { $_ eq 'scalar' } @{$layers{stdin}};
317 $localize{stdout}++, local(*STDOUT)
318 if $do_stdout && grep { $_ eq 'scalar' } @{$layers{stdout}};
319 $localize{stderr}++, local(*STDERR)
320 if ($do_stderr || $do_merge) && grep { $_ eq 'scalar' } @{$layers{stderr}};
321 $localize{stdout}++, local(*STDOUT), _open( \*STDOUT, ">&=1")
322 if $do_stdout && tied *STDOUT && $] >= 5.008;
323 $localize{stderr}++, local(*STDERR), _open( \*STDERR, ">&=2")
324 if ($do_stderr || $do_merge) && tied *STDERR && $] >= 5.008;
282325 _debug( "# localized $_\n" ) for keys %localize;
326 # proxy any closed/localized handles so we don't use fds 0, 1 or 2
283327 my %proxy_std = _proxy_std();
284 _debug( "# proxy std is @{ [%proxy_std] }\n" );
285 my $stash = { old => _copy_std() };
328 _debug( "# proxy std: @{ [%proxy_std] }\n" );
286329 # update layers after any proxying
287 %layers = (
288 stdin => [PerlIO::get_layers(\*STDIN) ],
289 stdout => [PerlIO::get_layers(\*STDOUT)],
290 stderr => [PerlIO::get_layers(\*STDERR)],
291 );
330 $layers{stdin} = [PerlIO::get_layers(\*STDIN)] if $proxy_std{stdin};
331 $layers{stdout} = [PerlIO::get_layers(\*STDOUT)] if $proxy_std{stdout};
332 $layers{stderr} = [PerlIO::get_layers(\*STDERR)] if $proxy_std{stderr};
292333 _debug( "# post-proxy layers for $_\: @{$layers{$_}}\n" ) for qw/stdin stdout stderr/;
334 # store old handles and setup handles for capture
335 $stash->{old} = _copy_std();
336 $stash->{new} = { %{$stash->{old}} }; # default to originals
337 $stash->{new}{stdout} = ($stash->{capture}{stdout} ||= File::Temp->new) if $do_stdout;
338 $stash->{new}{stderr} = ($stash->{capture}{stderr} ||= File::Temp->new) if $do_stderr;
339 _debug("# will capture stdout on " . fileno($stash->{capture}{stdout})."\n" ) if $do_stdout;
340 _debug("# will capture stderr on " . fileno($stash->{capture}{stderr})."\n" ) if $do_stderr;
293341 # get handles for capture and apply existing IO layers
294 $stash->{new}{$_} = $stash->{capture}{$_} = File::Temp->new for qw/stdout stderr/;
295 _debug("# will capture $_ on " .fileno($stash->{capture}{$_})."\n" ) for qw/stdout stderr/;
296342 # tees may change $stash->{new}
297 _start_tee( stdout => $stash ) if $tee_stdout;
298 _start_tee( stderr => $stash ) if $tee_stderr;
299 _wait_for_tees( $stash ) if $tee_stdout || $tee_stderr;
343 _start_tee( stdout => $stash ) if $do_stdout && $do_tee;
344 _start_tee( stderr => $stash ) if $do_stderr && $do_tee;
345 _wait_for_tees( $stash ) if $do_tee;
300346 # finalize redirection
301 $stash->{new}{stderr} = $stash->{new}{stdout} if $merge;
302 $stash->{new}{stdin} = $stash->{old}{stdin};
347 $stash->{new}{stderr} = $stash->{new}{stdout} if $do_merge;
303348 _debug( "# redirecting in parent ...\n" );
304349 _open_std( $stash->{new} );
305350 # execute user provided code
306 my ($exit_code, $inner_error, $outer_error);
351 my ($exit_code, $inner_error, $outer_error, @result);
307352 {
308353 local *STDIN = *CT_ORIG_STDIN if $localize{stdin}; # get original, not proxy STDIN
309 local *STDERR = *STDOUT if $merge; # minimize buffer mixups during $code
354 local *STDERR = *STDOUT if $do_merge; # minimize buffer mixups during $code
310355 _debug( "# finalizing layers ...\n" );
311 _relayer(\*STDOUT, $layers{stdout});
312 _relayer(\*STDERR, $layers{stderr}) unless $merge;
356 _relayer(\*STDOUT, $layers{stdout}) if $do_stdout;
357 _relayer(\*STDERR, $layers{stderr}) if $do_stderr;
313358 _debug( "# running code $code ...\n" );
314359 local $@;
315 eval { $code->(); $inner_error = $@ };
360 eval { @result = $code->(); $inner_error = $@ };
316361 $exit_code = $?; # save this for later
317362 $outer_error = $@; # save this for later
318363 }
319364 # restore prior filehandles and shut down tees
320 _debug( "# restoring ...\n" );
365 _debug( "# restoring filehandles ...\n" );
321366 _open_std( $stash->{old} );
322367 _close( $_ ) for values %{$stash->{old}}; # don't leak fds
323368 _unproxy( %proxy_std );
324 _kill_tees( $stash ) if $tee_stdout || $tee_stderr;
369 _debug( "# killing tee subprocesses ...\n" ) if $do_tee;
370 _kill_tees( $stash ) if $do_tee;
325371 # return captured output
326 _relayer($stash->{capture}{stdout}, $layers{stdout});
327 _relayer($stash->{capture}{stderr}, $layers{stderr}) unless $merge;
328 _debug( "# slurping captured $_ with layers: @{[PerlIO::get_layers($stash->{capture}{$_})]}\n") for qw/stdout stderr/;
329 my $got_out = _slurp($stash->{capture}{stdout});
330 my $got_err = $merge ? q() : _slurp($stash->{capture}{stderr});
331 print CT_ORIG_STDOUT $got_out if $localize{stdout} && $tee_stdout;
332 print CT_ORIG_STDERR $got_err if !$merge && $localize{stderr} && $tee_stdout;
372 _relayer($stash->{capture}{stdout}, $layers{stdout}) if $do_stdout;
373 _relayer($stash->{capture}{stderr}, $layers{stderr}) if $do_stderr;
374 my $got_out = $do_stdout ? _slurp('stdout' => $stash->{capture}{stdout}) : q();
375 my $got_err = $do_stderr ? _slurp('stderr' => $stash->{capture}{stderr}) : q();
376 _debug("# slurped " . length($got_out) . " bytes from stdout\n");
377 _debug("# slurped " . length($got_err) . " bytes from stderr\n");
378 print CT_ORIG_STDOUT $got_out
379 if $do_stdout && $do_tee && $localize{stdout};
380 print CT_ORIG_STDERR $got_err
381 if $do_stderr && $do_tee && $localize{stderr};
333382 $? = $exit_code;
334383 $@ = $inner_error if $inner_error;
335384 die $outer_error if $outer_error;
336385 _debug( "# ending _capture_tee with (@_)...\n" );
337 return $got_out if $merge;
338 return wantarray ? ($got_out, $got_err) : $got_out;
339 }
340
341 #--------------------------------------------------------------------------#
342 # create API subroutines from [tee STDOUT flag, tee STDERR, merge flag]
343 #--------------------------------------------------------------------------#
344
345 my %api = (
346 capture => [0,0,0],
347 capture_merged => [0,0,1],
348 tee => [1,1,0],
349 tee_merged => [1,0,1], # don't tee STDOUT since merging
350 );
351
352 for my $sub ( keys %api ) {
353 my $args = join q{, }, @{$api{$sub}};
354 eval "sub $sub(&) {unshift \@_, $args; goto \\&_capture_tee;}"; ## no critic
386 my @return;
387 push @return, $got_out if $do_stdout;
388 push @return, $got_err if $do_stderr;
389 push @return, @result;
390 return wantarray ? @return : $return[0];
355391 }
356392
357393 1;
366402
367403 =head1 VERSION
368404
369 version 0.11
405 version 0.13
370406
371407 =head1 SYNOPSIS
372408
373 use Capture::Tiny qw/capture tee capture_merged tee_merged/;
409 use Capture::Tiny ':all';
374410
375 ($stdout, $stderr) = capture {
411 ($stdout, $stderr, @result) = capture {
376412 # your code here
377413 };
414
415 $stdout = capture_stdout { ... };
416 $stderr = capture_stderr { ... };
417 $merged = capture_merged { ... };
378418
379419 ($stdout, $stderr) = tee {
380420 # your code here
381421 };
382422
383 $merged = capture_merged {
384 # your code here
385 };
386
387 $merged = tee_merged {
388 # your code here
389 };
423 $stdout = tee_stdout { ... };
424 $stderr = tee_stderr { ... };
425 $merged = tee_merged { ... };
390426
391427 =head1 DESCRIPTION
392428
393 Capture::Tiny provides a simple, portable way to capture anything sent to
394 STDOUT or STDERR, regardless of whether it comes from Perl, from XS code or
429 Capture::Tiny provides a simple, portable way to capture almost anything sent
430 to STDOUT or STDERR, regardless of whether it comes from Perl, from XS code or
395431 from an external program. Optionally, output can be teed so that it is
396432 captured while being passed through to the original handles. Yes, it even
397 works on Windows. Stop guessing which of a dozen capturing modules to use in
398 any particular situation and just use this one.
399
400 This module was heavily inspired by L<IO::CaptureOutput>, which provides
401 similar functionality without the ability to tee output and with more
402 complicated code and API.
433 works on Windows (usually). Stop guessing which of a dozen capturing modules
434 to use in any particular situation and just use this one.
403435
404436 =head1 USAGE
405437
407439
408440 =head2 capture
409441
410 ($stdout, $stderr) = capture \&code;
442 ($stdout, $stderr, @result) = capture \&code;
411443 $stdout = capture \&code;
412444
413445 The C<<< capture >>> function takes a code reference and returns what is sent to
414 STDOUT and STDERR. In scalar context, it returns only STDOUT. If no output
415 was received, returns an empty string. Regardless of context, all output is
416 captured -- nothing is passed to the existing handles.
446 STDOUT and STDERR as well as any return values from the code reference. In
447 scalar context, it returns only STDOUT. If no output was received for a
448 handle, it returns an empty string for that handle. Regardless of calling
449 context, all output is captured -- nothing is passed to the existing handles.
417450
418451 It is prototyped to take a subroutine reference as an argument. Thus, it
419452 can be called in block form:
422455 # your code here ...
423456 };
424457
458 Note that the coderef is evaluated in list context. If you wish to force
459 scalar context on the return value, you must use the C<<< scalar >>> keyword.
460
461 ($stdout, $stderr, $count) = capture {
462 my @list = qw/one two three/;
463 return scalar @list; # $count will be 3
464 };
465
466 Captures are normally done internally to an anonymous filehandle. To
467 capture via a named file (e.g. to externally monitor a long-running capture),
468 provide custom filehandles as a trailing list of option pairs:
469
470 my $out_fh = IO::File->new("out.txt", "w+");
471 my $err_fh = IO::File->new("out.txt", "w+");
472 capture { ... } stdout => $out_fh, stderr => $err_fh;
473
474 The filehandles must be readE<sol>write and seekable and should be empty. Modifying
475 the files externally during a capture operation will give unpredictable
476 results. Existing IO layers on them may be changed by the capture.
477
478 =head2 capture_stdout
479
480 ($stdout, @result) = capture_stdout \&code;
481 $stdout = capture_stdout \&code;
482
483 The C<<< capture_stdout >>> function works just like C<<< capture >>> except only
484 STDOUT is captured. STDERR is not captured.
485
486 =head2 capture_stderr
487
488 ($stderr, @result) = capture_stderr \&code;
489 $stderr = capture_stderr \&code;
490
491 The C<<< capture_stderr >>> function works just like C<<< capture >>> except only
492 STDERR is captured. STDOUT is not captured.
493
425494 =head2 capture_merged
426495
496 ($merged, @result) = capture_merged \&code;
427497 $merged = capture_merged \&code;
428498
429499 The C<<< capture_merged >>> function works just like C<<< capture >>> except STDOUT and
430500 STDERR are merged. (Technically, STDERR is redirected to STDOUT before
431 executing the function.) If no output was received, returns an empty string.
432 As with C<<< capture >>> it may be called in block form.
501 executing the function.)
433502
434503 Caution: STDOUT and STDERR output in the merged result are not guaranteed to be
435504 properly ordered due to buffering.
436505
437506 =head2 tee
438507
439 ($stdout, $stderr) = tee \&code;
508 ($stdout, $stderr, @result) = tee \&code;
440509 $stdout = tee \&code;
441510
442511 The C<<< tee >>> function works just like C<<< capture >>>, except that output is captured
443 as well as passed on to the original STDOUT and STDERR. As with C<<< capture >>> it
444 may be called in block form.
512 as well as passed on to the original STDOUT and STDERR.
513
514 =head2 tee_stdout
515
516 ($stdout, @result) = tee_stdout \&code;
517 $stdout = tee_stdout \&code;
518
519 The C<<< tee_stdout >>> function works just like C<<< tee >>> except only
520 STDOUT is teed. STDERR is not teed (output goes to STDERR as usual).
521
522 =head2 tee_stderr
523
524 ($stderr, @result) = tee_stderr \&code;
525 $stderr = tee_stderr \&code;
526
527 The C<<< tee_stderr >>> function works just like C<<< tee >>> except only
528 STDERR is teed. STDOUT is not teed (output goes to STDOUT as usual).
445529
446530 =head2 tee_merged
447531
532 ($merged, @result) = tee_merged \&code;
448533 $merged = tee_merged \&code;
449534
450535 The C<<< tee_merged >>> function works just like C<<< capture_merged >>> except that output
451 is captured as well as passed on to STDOUT. As with C<<< capture >>> it may be called
452 in block form.
536 is captured as well as passed on to STDOUT.
453537
454538 Caution: STDOUT and STDERR output in the merged result are not guaranteed to be
455539 properly ordered due to buffering.
466550
467551 Capture::Tiny does it's best to preserve PerlIO layers such as ':utf8' or
468552 ':crlf' when capturing. Layers should be applied to STDOUT or STDERR I<before>
469 the call to C<<< capture >>> or C<<< tee >>>.
470
471 =head2 Closed STDIN, STDOUT or STDERR
553 the call to C<<< capture >>> or C<<< tee >>>. This may not work for tied handles (see below).
554
555 =head2 Modifying filehandles before capturing
556
557 Generally speaking, you should do little or no manipulation of the standard IO
558 handles prior to using Capture::Tiny. In particular, closing, reopening,
559 localizing or tying standard handles prior to capture may cause a variety of
560 unexpected, undesireable andE<sol>or unreliable behaviors, as described below.
561 Capture::Tiny does its best to compensate for these situations, but the
562 results may not be what you desire.
563
564 B<Closed filehandles>
472565
473566 Capture::Tiny will work even if STDIN, STDOUT or STDERR have been previously
474 closed. However, since they may be reopened to capture or tee output, any code
475 within the captured block that depends on finding them closed will, of course,
476 not find them to be closed. If they started closed, Capture::Tiny will reclose
477 them again when the capture block finishes.
478
479 =head2 Scalar filehandles and STDIN, STDOUT or STDERR
567 closed. However, since they will be reopened to capture or tee output, any
568 code within the captured block that depends on finding them closed will, of
569 course, not find them to be closed. If they started closed, Capture::Tiny will
570 reclose them again when the capture block finishes.
571
572 Note that this reopening will happen even for STDIN or a handle not being
573 captured to ensure that the filehandle used for capture is not opened to file
574 descriptor 0, as this causes problems on various platforms.
575
576 B<Localized filehandles>
577
578 If code localizes any of Perl's standard handles before capturing, the capture
579 will affect the localized handles and not the original ones. External system
580 calls are not affected by localizing a handle in Perl and will continue
581 to send output to the original handles (which will thus not be captured).
582
583 B<Scalar filehandles>
480584
481585 If STDOUT or STDERR are reopened to scalar filehandles prior to the call to
482586 C<<< capture >>> or C<<< tee >>>, then Capture::Tiny will override the output handle for the
486590 Capture::Tiny attempts to preserve the semantics of STDIN opened to a scalar
487591 reference.
488592
489 =head2 Tied STDIN, STDOUT or STDERR
593 B<Tied handles>
490594
491595 If STDOUT or STDERR are tied prior to the call to C<<< capture >>> or C<<< tee >>>, then
492596 Capture::Tiny will attempt to override the tie for the duration of the
493597 C<<< capture >>> or C<<< tee >>> call and then send captured output to the tied handle after
494598 the capture is complete. (Requires Perl 5.8)
495599
496 Capture::Tiny does not (yet) support resending utf8 encoded data to a tied
497 STDOUT or STDERR handle. Characters will appear as bytes.
600 Capture::Tiny may not succeed resending utf8 encoded data to a tied
601 STDOUT or STDERR handle. Characters may appear as bytes. If the tied handle
602 is based on L<Tie::StdHandle>, then Capture::Tiny will attempt to determine
603 appropriate layers like C<<< :utf8 >>> from the underlying handle and do the right
604 thing.
498605
499606 Capture::Tiny attempts to preserve the semantics of tied STDIN, but capturing
500607 or teeing when STDIN is tied is currently broken on Windows.
501608
502 =head2 Modifiying STDIN, STDOUT or STDERR during a capture
609 =head2 Modifiying handles during a capture
503610
504611 Attempting to modify STDIN, STDOUT or STDERR I<during> C<<< capture >>> or C<<< tee >>> is
505612 almost certainly going to cause problems. Don't do that.
529636
530637 =head1 SEE ALSO
531638
532 This is a selection of CPAN modules that provide some sort of output capture,
639 This module was, inspired by L<IO::CaptureOutput>, which provides
640 similar functionality without the ability to tee output and with more
641 complicated code and API. L<IO::CaptureOutput> does not handle layers
642 or most of the unusual cases described in the L</Limitations> section and
643 I no longer recommend it.
644
645 There are many other CPAN modules that provide some sort of output capture,
533646 albeit with various limitations that make them appropriate only in particular
534647 circumstances. I'm probably missing some. The long list is provided to show
535648 why I felt Capture::Tiny was necessary.
637750 This is open source software. The code repository is available for
638751 public review and contribution under the terms of the license.
639752
640 L<http://github.com/dagolden/capture-tiny/tree>
641
642 git clone git://github.com/dagolden/capture-tiny.git
753 L<https://github.com/dagolden/capture-tiny>
754
755 git clone https://github.com/dagolden/capture-tiny.git
643756
644757 =head1 AUTHOR
645758
00 #!perl
1 #
2 # This file is part of Capture-Tiny
3 #
4 # This software is Copyright (c) 2009 by David Golden.
5 #
6 # This is free software, licensed under:
7 #
8 # The Apache License, Version 2.0, January 2004
9 #
101
112 use strict;
123 use warnings;
3223 'lib',
3324 );
3425
35 my @scripts = glob "bin/*";
26 my @scripts;
27 if ( -d 'bin' ) {
28 find(
29 sub {
30 return unless -f;
31 my $found = $File::Find::name;
32 # nothing to skip
33 push @scripts, $found;
34 },
35 'bin',
36 );
37 }
3638
3739 my $plan = scalar(@modules) + scalar(@scripts);
3840 $plan ? (plan tests => $plan) : (plan skip_all => "no tests to run");
1010
1111 my @api = qw(
1212 capture
13 capture_stdout
14 capture_stderr
1315 capture_merged
1416 tee
17 tee_stdout
18 tee_stderr
1519 tee_merged
1620 );
1721
1919
2020 run_test('capture');
2121 run_test('capture_scalar');
22 run_test('capture_stdout');
23 run_test('capture_stderr');
2224 run_test('capture_merged');
2325
2426 is( next_fd, $fd, "no file descriptors leaked" );
2626
2727 run_test('tee');
2828 run_test('tee_scalar');
29 run_test('tee_stdout');
30 run_test('tee_stderr');
2931 run_test('tee_merged');
3032
3133 is( next_fd, $fd, "no file descriptors leaked" );
2626 run_test($_) for qw(
2727 capture
2828 capture_scalar
29 capture_stdout
30 capture_stderr
2931 capture_merged
3032 );
3133
3335 run_test($_) for qw(
3436 tee
3537 tee_scalar
38 tee_stdout
39 tee_stderr
3640 tee_merged
3741 );
3842 }
2626 run_test($_) for qw(
2727 capture
2828 capture_scalar
29 capture_stdout
30 capture_stderr
2931 capture_merged
3032 );
3133
3335 run_test($_) for qw(
3436 tee
3537 tee_scalar
38 tee_stdout
39 tee_stderr
3640 tee_merged
3741 );
3842 }
2626 run_test($_) for qw(
2727 capture
2828 capture_scalar
29 capture_stdout
30 capture_stderr
2931 capture_merged
3032 );
3133
3335 run_test($_) for qw(
3436 tee
3537 tee_scalar
38 tee_stdout
39 tee_stderr
3640 tee_merged
3741 );
3842 }
3030 run_test($_) for qw(
3131 capture
3232 capture_scalar
33 capture_stdout
34 capture_stderr
3335 capture_merged
3436 );
3537
3739 run_test($_) for qw(
3840 tee
3941 tee_scalar
42 tee_stdout
43 tee_stderr
4044 tee_merged
4145 );
4246 }
3030 run_test($_) for qw(
3131 capture
3232 capture_scalar
33 capture_stdout
34 capture_stderr
3335 capture_merged
3436 );
3537
3739 run_test($_) for qw(
3840 tee
3941 tee_scalar
42 tee_stdout
43 tee_stderr
4044 tee_merged
4145 );
4246 }
3636 run_test($_) for qw(
3737 capture
3838 capture_scalar
39 capture_stdout
40 capture_stderr
3941 capture_merged
4042 );
4143
4345 run_test($_) for qw(
4446 tee
4547 tee_scalar
48 tee_stdout
49 tee_stderr
4650 tee_merged
4751 );
4852 }
00 # Copyright (c) 2009 by David Golden. All rights reserved.
11 # Licensed under Apache License, Version 2.0 (the "License").
22 # You may not use this file except in compliance with the License.
3 # A copy of the License was distributed with this file or you may obtain a
3 # A copy of the License was distributed with this file or you may obtain a
44 # copy of the License from http://www.apache.org/licenses/LICENSE-2.0
55
66 use strict;
2626 save_std(qw/stdout/);
2727 tie *STDOUT, 'TieLC', ">&=STDOUT";
2828 my $orig_tie = tied *STDOUT;
29 ok( $orig_tie, "STDOUT is tied" );
29 ok( $orig_tie, "STDOUT is tied" );
3030
3131 my $fd = next_fd;
3232
33 run_test($_, 'unicode') for qw(
33 run_test($_) for qw(
3434 capture
3535 capture_scalar
36 capture_stdout
37 capture_stderr
3638 capture_merged
3739 );
3840
3941 if ( ! $no_fork ) {
40 run_test($_, 'unicode') for qw(
42 run_test($_) for qw(
4143 tee
4244 tee_scalar
45 tee_stdout
46 tee_stderr
4347 tee_merged
4448 );
4549 }
3030
3131 my $fd = next_fd;
3232
33 run_test($_, 'unicode') for qw(
33 run_test($_) for qw(
3434 capture
3535 capture_scalar
36 capture_stdout
37 capture_stderr
3638 capture_merged
3739 );
3840
3941 if ( ! $no_fork ) {
40 run_test($_, 'unicode') for qw(
42 run_test($_) for qw(
4143 tee
4244 tee_scalar
45 tee_stdout
46 tee_stderr
4347 tee_merged
4448 );
4549 }
3535 run_test($_) for qw(
3636 capture
3737 capture_scalar
38 capture_stdout
39 capture_stderr
3840 capture_merged
3941 );
4042
4244 run_test($_) for qw(
4345 tee
4446 tee_scalar
47 tee_stdout
48 tee_stderr
4549 tee_merged
4650 );
4751 }
0 # Copyright (c) 2009 by David Golden. All rights reserved.
1 # Licensed under Apache License, Version 2.0 (the "License").
2 # You may not use this file except in compliance with the License.
3 # A copy of the License was distributed with this file or you may obtain a
4 # copy of the License from http://www.apache.org/licenses/LICENSE-2.0
5
6 use strict;
7 use warnings;
8 use Test::More;
9 use lib 't/lib';
10 use IO::Handle;
11 use Utils qw/next_fd sig_num/;
12 use Capture::Tiny ':all';
13 use Config;
14
15 plan tests => 12;
16
17 local $ENV{PERL_CAPTURE_TINY_TIMEOUT} = 0; # no timeouts
18
19 my $builder = Test::More->builder;
20 binmode($builder->failure_output, ':utf8') if $] >= 5.008;
21
22 my $fd = next_fd;
23 my ($out, $err, $res, @res);
24
25 #--------------------------------------------------------------------------#
26 # capture to array
27 #--------------------------------------------------------------------------#
28
29 ($out, $err, @res) = capture {
30 print STDOUT "foo\n";
31 print STDERR "bar\n";
32 return qw/one two three/;
33 };
34
35 is( $out, "foo\n", "capture -> STDOUT captured" );
36 is( $err, "bar\n", "capture -> STDERR captured" );
37 is_deeply( \@res, [qw/one two three/], "return values -> array" );
38
39 #--------------------------------------------------------------------------#
40 # capture to scalar
41 #--------------------------------------------------------------------------#
42
43 ($out, $err, $res) = capture {
44 print STDOUT "baz\n";
45 print STDERR "bam\n";
46 return qw/one two three/;
47 };
48
49 is( $out, "baz\n", "capture -> STDOUT captured" );
50 is( $err, "bam\n", "capture -> STDERR captured" );
51 is( $res, "one", "return value -> scalar" );
52
53 #--------------------------------------------------------------------------#
54 # capture_stdout to array
55 #--------------------------------------------------------------------------#
56
57 ($out, @res) = capture_stdout {
58 print STDOUT "foo\n";
59 return qw/one two three/;
60 };
61
62 is( $out, "foo\n", "capture_stdout -> STDOUT captured" );
63 is_deeply( \@res, [qw/one two three/], "return values -> array" );
64
65 #--------------------------------------------------------------------------#
66 # capture_merged to array
67 #--------------------------------------------------------------------------#
68
69 ($out, $res) = capture_merged {
70 print STDOUT "baz\n";
71 print STDERR "bam\n";
72 return qw/one two three/;
73 };
74
75 like( $out, qr/baz/, "capture_merged -> STDOUT captured" );
76 like( $out, qr/bam/, "capture_merged -> STDERR captured" );
77 is( $res, "one", "return value -> scalar" );
78
79 #--------------------------------------------------------------------------#
80 # finish
81 #--------------------------------------------------------------------------#
82
83 is( next_fd, $fd, "no file descriptors leaked" );
84
85 exit 0;
86
0 # Copyright (c) 2009 by David Golden. All rights reserved.
1 # Licensed under Apache License, Version 2.0 (the "License").
2 # You may not use this file except in compliance with the License.
3 # A copy of the License was distributed with this file or you may obtain a
4 # copy of the License from http://www.apache.org/licenses/LICENSE-2.0
5
6 use strict;
7 use warnings;
8 use Test::More;
9 use lib 't/lib';
10 use IO::Handle;
11 use IO::File;
12 use File::Temp qw/tmpnam/;
13 use Utils qw/next_fd sig_num/;
14 use Capture::Tiny ':all';
15 use Config;
16
17 plan tests => 9;
18
19 local $ENV{PERL_CAPTURE_TINY_TIMEOUT} = 0; # no timeouts
20
21 my $builder = Test::More->builder;
22 binmode($builder->failure_output, ':utf8') if $] >= 5.008;
23
24 my $fd = next_fd;
25 my ($out, $err, $res, @res);
26
27 #--------------------------------------------------------------------------#
28 # capture to array
29 #--------------------------------------------------------------------------#
30
31 my $temp_out = tmpnam();
32 my $temp_err = tmpnam();
33
34 ok( !-e $temp_out, "Temp out '$temp_out' doesn't exist" );
35 ok( !-e $temp_err, "Temp out '$temp_err' doesn't exist" );
36
37 my $out_fh = IO::File->new($temp_out, "w+");
38 my $err_fh = IO::File->new($temp_err, "w+");
39
40 capture {
41 print STDOUT "foo\n";
42 print STDERR "bar\n";
43 } stdout => $out_fh, stderr => $err_fh;
44
45 $out_fh->close;
46 $err_fh->close;
47
48 is( scalar do {local (@ARGV,$/) = $temp_out; <>} , "foo\n",
49 "captured STDOUT to custom handle (IO::File)"
50 );
51 is( scalar do {local (@ARGV,$/) = $temp_err; <>} , "bar\n",
52 "captured STDERR to custom handle (IO::File)"
53 );
54
55 unlink $_ for $temp_out, $temp_err;
56
57 #--------------------------------------------------------------------------#
58
59 $temp_out = tmpnam();
60 $temp_err = tmpnam();
61
62 ok( !-e $temp_out, "Temp out '$temp_out' doesn't exist" );
63 ok( !-e $temp_err, "Temp out '$temp_err' doesn't exist" );
64
65 open $out_fh, "+>", $temp_out;
66 open $err_fh, "+>", $temp_err;
67
68 capture {
69 print STDOUT "foo\n";
70 print STDERR "bar\n";
71 } stdout => $out_fh, stderr => $err_fh;
72
73 $out_fh->close;
74 $err_fh->close;
75
76 is( scalar do {local (@ARGV,$/) = $temp_out; <>} , "foo\n",
77 "captured STDOUT to custom handle (GLOB)"
78 );
79 is( scalar do {local (@ARGV,$/) = $temp_err; <>} , "bar\n",
80 "captured STDERR to custom handle (GLOB)"
81 );
82
83 unlink $_ for $temp_out, $temp_err;
84
85 #--------------------------------------------------------------------------#
86 # finish
87 #--------------------------------------------------------------------------#
88
89 close ARGV; # opened by reading from <>
90 is( next_fd, $fd, "no file descriptors leaked" );
91
92 exit 0;
93
0 #
1 # This file is part of Capture-Tiny
2 #
3 # This software is Copyright (c) 2009 by David Golden.
4 #
5 # This is free software, licensed under:
6 #
7 # The Apache License, Version 2.0, January 2004
8 #
90 package Cases;
101 use strict;
112 use warnings;
189 run_test
1910 );
2011
21 my $have_diff = eval {
22 require Test::Differences;
12 my $have_diff = eval {
13 require Test::Differences;
2314 Test::Differences->import;
2415 $Test::Differences::VERSION < 0.60; # 0.60+ is causing strange failures
2516 };
3930 my $t = shift;
4031 return unless $t eq 'unicode';
4132 my %seen;
42 my @orig_layers = grep {$_ ne 'unix' and $_ ne 'perlio' and $seen{$_}++} PerlIO::get_layers(\*STDOUT);
43 binmode(STDOUT, ":utf8") if fileno(STDOUT);
44 binmode(STDERR, ":utf8") if fileno(STDERR);
33 my @orig_layers = (
34 [ grep {$_ ne 'unix' and $_ ne 'perlio' and $seen{stdout}{$_}++} PerlIO::get_layers(\*STDOUT) ],
35 [ grep {$_ ne 'unix' and $_ ne 'perlio' and $seen{stderr}{$_}++} PerlIO::get_layers(\*STDERR) ],
36 );
37 binmode(STDOUT, ":utf8") if fileno(STDOUT);
38 binmode(STDERR, ":utf8") if fileno(STDERR);
4539 return @orig_layers;
4640 }
4741
4842 sub _restore_layers {
4943 my ($t, @orig_layers) = @_;
5044 return unless $t eq 'unicode';
51 binmode(STDOUT, join( ":", "", "raw", @orig_layers)) if fileno(STDOUT);
52 binmode(STDERR, join( ":", "", "raw", @orig_layers)) if fileno(STDERR);
45 binmode(STDOUT, join( ":", "", "raw", @{$orig_layers[0]})) if fileno(STDOUT);
46 binmode(STDERR, join( ":", "", "raw", @{$orig_layers[1]})) if fileno(STDERR);
5347 }
5448
5549 #--------------------------------------------------------------------------#
112106 _is_or_diff( $got_out, $expected[0], "$l|$m|$c|$t - got STDOUT" );
113107 },
114108 },
109 capture_stdout => {
110 cnt => 3,
111 test => sub {
112 my ($m, $c, $t, $l) = @_;
113 my ($inner_out, $inner_err);
114 my ($outer_out, $outer_err) = capture {
115 $inner_out = capture_stdout {
116 $methods{$m}->( $channels{$c}{output}->($t) );
117 };
118 };
119 my @expected = $channels{$c}{expect}->($t);
120 _is_or_diff( $inner_out, $expected[0], "$l|$m|$c|$t - inner STDOUT" );
121 _is_or_diff( $outer_out, "", "$l|$m|$c|$t - outer STDOUT" );
122 _is_or_diff( $outer_err, $expected[1], "$l|$m|$c|$t - outer STDERR" );
123 },
124 },
125 capture_stderr => {
126 cnt => 3,
127 test => sub {
128 my ($m, $c, $t, $l) = @_;
129 my ($inner_out, $inner_err);
130 my ($outer_out, $outer_err) = capture {
131 $inner_err = capture_stderr {
132 $methods{$m}->( $channels{$c}{output}->($t) );
133 };
134 };
135 my @expected = $channels{$c}{expect}->($t);
136 _is_or_diff( $inner_err, $expected[1], "$l|$m|$c|$t - inner STDERR" );
137 _is_or_diff( $outer_out, $expected[0], "$l|$m|$c|$t - outer STDOUT" );
138 _is_or_diff( $outer_err, "", "$l|$m|$c|$t - outer STDERR" );
139 },
140 },
115141 capture_merged => {
116142 cnt => 2,
117143 test => sub {
155181 _is_or_diff( $got_out, $expected[0], "$l|$m|$c|$t - got STDOUT" );
156182 _is_or_diff( $tee_out, $expected[0], "$l|$m|$c|$t - tee STDOUT" );
157183 _is_or_diff( $tee_err, $expected[1], "$l|$m|$c|$t - tee STDERR" );
184 }
185 },
186 tee_stdout => {
187 cnt => 3,
188 test => sub {
189 my ($m, $c, $t, $l) = @_;
190 my ($inner_out, $inner_err);
191 my ($tee_out, $tee_err) = capture {
192 $inner_out = tee_stdout {
193 $methods{$m}->( $channels{$c}{output}->($t) );
194 };
195 };
196 my @expected = $channels{$c}{expect}->($t);
197 _is_or_diff( $inner_out, $expected[0], "$l|$m|$c|$t - inner STDOUT" );
198 _is_or_diff( $tee_out, $expected[0], "$l|$m|$c|$t - teed STDOUT" );
199 _is_or_diff( $tee_err, $expected[1], "$l|$m|$c|$t - unmodified STDERR" );
200 }
201 },
202 tee_stderr => {
203 cnt => 3,
204 test => sub {
205 my ($m, $c, $t, $l) = @_;
206 my ($inner_out, $inner_err);
207 my ($tee_out, $tee_err) = capture {
208 $inner_err = tee_stderr {
209 $methods{$m}->( $channels{$c}{output}->($t) );
210 };
211 };
212 my @expected = $channels{$c}{expect}->($t);
213 _is_or_diff( $inner_err, $expected[1], "$l|$m|$c|$t - inner STDOUT" );
214 _is_or_diff( $tee_out, $expected[0], "$l|$m|$c|$t - unmodified STDOUT" );
215 _is_or_diff( $tee_err, $expected[1], "$l|$m|$c|$t - teed STDERR" );
158216 }
159217 },
160218 tee_merged => {
194252 for my $c ( keys %channels ) {
195253 for my $t ( keys %texts ) {
196254 my @orig_layers = _set_utf8($t);
197 local $TODO = "not yet supported"
255 local $TODO = "not supported on all platforms"
198256 if $t eq $todo;
199257 $tests{$test_type}{test}->($m, $c, $t, $test_type);
200258 _restore_layers($t, @orig_layers);
0 #
1 # This file is part of Capture-Tiny
2 #
3 # This software is Copyright (c) 2009 by David Golden.
4 #
5 # This is free software, licensed under:
6 #
7 # The Apache License, Version 2.0, January 2004
8 #
90 package TieLC;
101
112 sub TIEHANDLE
0 #
1 # This file is part of Capture-Tiny
2 #
3 # This software is Copyright (c) 2009 by David Golden.
4 #
5 # This is free software, licensed under:
6 #
7 # The Apache License, Version 2.0, January 2004
8 #
90 package Utils;
101 use strict;
112 use warnings;
00 #!perl
1 #
2 # This file is part of Capture-Tiny
3 #
4 # This software is Copyright (c) 2009 by David Golden.
5 #
6 # This is free software, licensed under:
7 #
8 # The Apache License, Version 2.0, January 2004
9 #
101
112 use Test::More;
123
00 #!perl
1 #
2 # This file is part of Capture-Tiny
3 #
4 # This software is Copyright (c) 2009 by David Golden.
5 #
6 # This is free software, licensed under:
7 #
8 # The Apache License, Version 2.0, January 2004
9 #
101
112 use Test::More;
123
00 #!perl
1 #
2 # This file is part of Capture-Tiny
3 #
4 # This software is Copyright (c) 2009 by David Golden.
5 #
6 # This is free software, licensed under:
7 #
8 # The Apache License, Version 2.0, January 2004
9 #
101 use Test::More;
112
123 eval "use Test::Pod 1.41";
00 #!perl
1 #
2 # This file is part of Capture-Tiny
3 #
4 # This software is Copyright (c) 2009 by David Golden.
5 #
6 # This is free software, licensed under:
7 #
8 # The Apache License, Version 2.0, January 2004
9 #
101
112 use Test::More;
123
0 #!/usr/bin/perl
1 use 5.006;
2 use strict;
3 use warnings;
4 use Test::More;
5
6 eval "use Test::Version 0.04";
7 plan skip_all => "Test::Version 0.04 required for testing versions"
8 if $@;
9
10 version_all_ok();
11 done_testing;