File Coverage

blib/lib/MooX/Cmd/Tester.pm
Criterion Covered Total %
statement 69 79 87.3
branch 28 38 73.6
condition 9 17 52.9
subroutine 16 19 84.2
pod 3 3 100.0
total 125 156 80.1


line stmt bran cond sub pod time code
1             package MooX::Cmd::Tester;
2             # ABSTRACT: MooX cli app commands tester
3             our $VERSION = '1.000';
4 6     6   902350 use strict;
  6         13  
  6         241  
5 6     6   63 use warnings;
  6         26  
  6         475  
6              
7             require Exporter;
8 6     6   48 use Test::More import => ['!pass'];
  6         15  
  6         76  
9 6     6   6253 use Package::Stash;
  6         67212  
  6         345  
10 6     6   3806 use Capture::Tiny qw(:all);
  6         242221  
  6         1345  
11              
12 6     6   64 use parent qw(Test::Builder::Module Exporter);
  6         13  
  6         54  
13              
14             our @EXPORT = qw(test_cmd test_cmd_ok);
15             our @EXPORT_OK = qw(test_cmd test_cmd_ok);
16              
17             our $TEST_IN_PROGRESS;
18             my $CLASS = __PACKAGE__;
19              
20             BEGIN
21             {
22             *CORE::GLOBAL::exit = sub {
23 0 0   0   0 return CORE::exit(@_) unless $TEST_IN_PROGRESS;
24 0         0 MooX::Cmd::Tester::Exited->throw($_[0]);
25 6     6   10195 };
26             }
27              
28              
29 28     28 1 909 sub result_class { 'MooX::Cmd::Tester::Result' }
30              
31              
32             sub test_cmd
33             {
34 28     28 1 1244048 my ($app, $argv) = @_;
35              
36 28         168 my $result = _run_with_capture($app, $argv);
37 28 100 50     226 my $exit_code = defined $result->{error} ? ((0 + $!) || -1) : 0;
38              
39             $result->{error}
40 3         108 and eval { $result->{error}->isa('MooX::Cmd::Tester::Exited') }
41 28 50 66     223 and $exit_code = ${$result->{error}};
  0         0  
42              
43 28         152 result_class->new(
44             {
45             exit_code => $exit_code,
46             %$result,
47             }
48             );
49             }
50              
51              
52             sub test_cmd_ok
53             {
54 16     16 1 470384 my $rv = test_cmd(@_);
55              
56 16         99 my $test_ident = $rv->app . " => [ " . join(" ", @{$_[1]}) . " ]";
  16         103  
57 16 50       182 ok(!$rv->error, "Everythink ok running cmd $test_ident") or diag($rv->error);
58             # no error and cmd means, we're reasonable successful so far
59             $rv
60             and not $rv->error
61             and $rv->cmd
62             and $rv->cmd->command_name
63 16 100 33     12345 and ok($rv->cmd->command_commands->{$rv->cmd->command_name}, "found command at $test_ident");
      33        
      66        
64              
65 16         4011 $rv;
66             }
67              
68             ## no critic qw(ProhibitSubroutinePrototypes)
69             sub _capture_merged(&)
70             {
71 28     28   75 my $code = shift;
72 28         67 my ($stdout, $stderr, $merged, $ok);
73 28 50       179 if ($^O eq 'MSWin32')
74             {
75 0     0   0 ($stdout, $stderr, $ok) = tee { $code->(); };
  0         0  
76 0         0 $merged = $stdout . $stderr;
77             }
78             else
79             {
80             ($merged) = tee_merged
81             {
82 28     28   21563495 ($stdout, $stderr, $ok) = tee { $code->() };
  28         22545709  
83 28         1398 };
84             }
85 28         7753682 ($stdout, $stderr, $merged, $ok);
86             }
87              
88             sub _run_with_capture
89             {
90 28     28   106 my ($app, $argv) = @_;
91              
92 28         72 my ($execute_rv, $cmd, $cmd_name, $error);
93              
94             my ($stdout, $stderr, $merged, $ok) = _capture_merged
95             {
96 28 100   28   135 eval {
97 28         280 local $TEST_IN_PROGRESS = 1;
98 28         286 local @ARGV = @$argv;
99              
100 28         1468 my $tb = $CLASS->builder();
101              
102 28 100       2403 $cmd = ref $app ? $app : $app->new_with_cmd;
103 25 100       200 ref $app and $app = ref $app;
104 25         183 my $test_ident = "$app => [ " . join(" ", @$argv) . " ]";
105 25         735 ok($cmd->isa($app), "got a '$app' from new_with_cmd");
106             @$argv
107             and defined($cmd_name = $cmd->command_name)
108 25 100 66     26775 and ok((grep { index($cmd_name, $_) != -1 } @$argv), "proper cmd name from $test_ident");
  24         160  
109 25         7823 ok(scalar @{$cmd->command_chain} <= 1 + scalar @$argv, "\$#argv vs. command chain length testing $test_ident");
  25         414  
110 25 100       19331 @$argv and ok($cmd->command_chain_end == $cmd->command_chain->[-1], "command_chain_end ok");
111              
112 25 100       9764 unless ($execute_rv = $cmd->execute_return)
113             {
114 9         36 my ($command_execute_from_new, $command_execute_method_name);
115 9         80 my $cce = $cmd->can("command_chain_end");
116 9 50       835 $cce and $cce = $cce->($cmd);
117 9 50       122 $cce and $command_execute_from_new = $cce->can("command_execute_from_new");
118 9 100       209 $command_execute_from_new and $command_execute_from_new = $command_execute_from_new->($cce);
119 9 50       117 $command_execute_from_new or $command_execute_method_name = $cce->can('command_execute_method_name');
120 9 100       185 $command_execute_method_name
121             and $execute_rv = [$cce->can($command_execute_method_name->($cce))->($cce)];
122             }
123 25         720 1;
124             } or $error = 1;
125 28 100       296 $@ and $error = $@;
126 28         349 };
127              
128             return {
129 28         1713 app => $app,
130             cmd => $cmd,
131             stdout => $stdout,
132             stderr => $stderr,
133             output => $merged,
134             error => $error,
135             execute_rv => $execute_rv,
136             };
137             }
138              
139             {
140             ## no critic qw(ProhibitMultiplePackages)
141             package # no-index
142             MooX::Cmd::Tester::Result;
143              
144             sub new
145             {
146 28     28   101 my ($class, $arg) = @_;
147 28         439 bless $arg => $class;
148             }
149             }
150              
151             my $res = Package::Stash->new("MooX::Cmd::Tester::Result");
152             for my $attr (qw(app cmd stdout stderr output error execute_rv exit_code))
153             {
154 158     158   7873 $res->add_symbol('&' . $attr, sub { $_[0]->{$attr} });
155             }
156              
157             {
158             ## no critic qw(ProhibitMultiplePackages)
159             package # no-index
160             MooX::Cmd::Tester::Exited;
161              
162             sub throw
163             {
164 0     0     my ($class, $code) = @_;
165 0 0         defined $code or $code = 0;
166 0           my $self = (bless \$code => $class);
167             ## no critic qw(RequireCarping)
168 0           die $self;
169             }
170             }
171              
172              
173             1;
174              
175             __END__