File Coverage

blib/lib/App/Prove/Elasticsearch/Parser.pm
Criterion Covered Total %
statement 107 126 84.9
branch 13 30 43.3
condition 9 24 37.5
subroutine 15 16 93.7
pod 8 8 100.0
total 152 204 74.5


line stmt bran cond sub pod time code
1             # PODNAME: App::Prove::Elasticsearch::Parser
2             # ABSTRACT: Capture the output of prove, and upload the results of the test to elasticsearch
3              
4             package App::Prove::Elasticsearch::Parser;
5             $App::Prove::Elasticsearch::Parser::VERSION = '0.001';
6 1     1   105676 use strict;
  1         9  
  1         21  
7 1     1   4 use warnings;
  1         2  
  1         26  
8              
9 1     1   3 use parent qw/TAP::Parser/;
  1         2  
  1         4  
10              
11 1     1   30591 use Clone qw{clone};
  1         1874  
  1         50  
12 1     1   6 use File::Basename qw{basename dirname};
  1         1  
  1         38  
13 1     1   5 use POSIX qw{strftime};
  1         1  
  1         9  
14 1     1   1570 use App::Prove::Elasticsearch::Utils();
  1         3  
  1         1083  
15              
16             sub new {
17 2     2 1 8752 my ($class, $opts) = @_;
18 2         52 $opts = clone $opts; #Convenience, if we are passing over and over again...
19              
20             #Load our callbacks
21 2         19 $opts->{'callbacks'} = {
22             'test' => \&testCallback,
23             'comment' => \&commentCallback,
24             'unknown' => \&unknownCallback,
25             'bailout' => \&bailoutCallback,
26             'EOF' => \&EOFCallback,
27             'plan' => \&planCallback,
28             };
29              
30             my $esopts = {
31             'server.host' => delete $opts->{'server.host'},
32             'server.port' => delete $opts->{'server.port'},
33             'client.indexer' => delete $opts->{'client.indexer'},
34             'client.versioner' => delete $opts->{'client.versioner'} // 'Default',
35             'client.blamer' => delete $opts->{'client.blamer'} // 'Default',
36             'client.platformer' => delete $opts->{'client.platformer'} // 'Default',
37 2   50     20 'client.autodiscover' => delete $opts->{'client.autodiscover'},
      50        
      50        
38             };
39              
40 2         20 my $self = $class->SUPER::new($opts);
41 2 50       12930 if (ref($self->{'_iterator'}->{'command'}) eq 'ARRAY') {
42 2         8 $self->{'file'} = $self->{'_iterator'}->{'command'}->[-1];
43 2         364 print "# PROCESSING RESULTS FROM TEST FILE: $self->{'file'}\n";
44             }
45              
46 2         17 my $indexer = $esopts->{'client.indexer'};
47 2         21 _require_indexer($indexer);
48 2         19 my $versioner =
49             App::Prove::Elasticsearch::Utils::require_versioner($esopts);
50 2         31 my $platformer =
51             App::Prove::Elasticsearch::Utils::require_platformer($esopts);
52 2         16 my $blamer = App::Prove::Elasticsearch::Utils::require_blamer($esopts);
53              
54             $self->{executor} =
55 2         21 &{\&{$blamer . "::get_responsible_party"}}($self->{file});
  2         8  
  2         44  
56 2         21 $self->{sut_version} = &{\&{$versioner . "::get_version"}}($self->{file});
  2         6  
  2         19  
57 2         18 $self->{platform} = &{\&{$platformer . "::get_platforms"}}();
  2         4  
  2         15  
58 2         11 $self->{indexer} = $indexer;
59              
60             $self->{test_version} =
61 2         4 &{\&{$versioner . "::get_file_version"}}($self->{file});
  2         6  
  2         17  
62 2         17 $self->{steps} = [];
63 2         16 $self->{starttime} = [ Time::HiRes::gettimeofday() ];
64 2         6 $self->{es_opts} = $esopts;
65 2         225 return $self;
66             }
67              
68             sub _require_indexer {
69 1     1   207 my $indexer = shift;
70 1 50       48 eval "require $indexer" or die "cannot find needed indexer $indexer";
71             }
72              
73             # Look for file boundaries, etc.
74             sub unknownCallback {
75 1     1 1 66 my ($test) = @_;
76 1         11 my $self = $test->{'parser'};
77 1         27 my $line = $test->as_string;
78 1         15 $self->{'raw_output'} .= "$line\n";
79              
80             #Unofficial "Extensions" to TAP
81 1         12 my ($status_override) = $line =~ m/^% mark_status=([A-Z|_]*)/;
82 1 50       14 $self->{global_status} = $status_override if $status_override;
83              
84 1         11 return;
85             }
86              
87             # Register the current suite or test desc for use by test callback, if the line begins with the special magic words
88             sub commentCallback {
89 2     2 1 75 my ($test) = @_;
90 2         6 my $self = $test->{'parser'};
91 2         10 my $line = $test->as_string;
92 2         18 $self->{'raw_output'} .= "$line\n";
93              
94 2         13 return;
95             }
96              
97             sub testCallback {
98 2     2 1 302 my ($test) = @_;
99 2         6 my $self = $test->{'parser'};
100              
101 2         8 my $line = $test->as_string;
102 2         54 $self->{'raw_output'} .= "$line\n";
103              
104 2         40 $line =~ s/^(ok|not ok)\s[0-9]*\s-\s//g;
105 2         14 my $test_name = $line;
106              
107             #Setup args to pass to function
108 2         11 my $status_name = 'NOT OK';
109 2 50       11 if ($test->is_actual_ok()) {
110 2         18 $status_name = 'OK';
111 2 50       16 if ($test->has_skip()) {
112 0         0 $status_name = 'SKIP';
113 0         0 $test_name =~ s/^(ok|not ok)\s[0-9]*\s//g;
114 0         0 $test_name =~ s/^# skip //gi;
115             }
116 2 50       17 if ($test->has_todo()) {
117 0         0 $status_name = 'TODO PASS';
118 0         0 $test_name =~ s/^(ok|not ok)\s[0-9]*\s//g;
119 0         0 $test_name =~ s/^# todo & skip //gi; #handle todo_skip
120 0         0 $test_name =~ s/# todo\s(.*)$//gi;
121             }
122             } else {
123 0 0       0 if ($test->has_todo()) {
124 0         0 $status_name = 'TODO FAIL';
125 0         0 $test_name =~ s/^(ok|not ok)\s[0-9]*\s//g;
126 0         0 $test_name =~ s/^# todo & skip //gi; #handle todo_skip
127 0         0 $test_name =~ s/# todo\s(.*)$//gi;
128             }
129             }
130              
131             #XXX much of the above code would be unneeded if $test->description wasn't garbage
132 2         26 $test_name =~ s/\s+$//g;
133              
134             #Test done. Record elapsed time.
135 2         12 my $tm = [ Time::HiRes::gettimeofday() ];
136 2   33     35 $self->{lasttime} //= $self->{starttime};
137             push(
138 2         38 @{$self->{steps}},
139             {
140 2         4 elapsed => Time::HiRes::tv_interval($self->{'lasttime'}, $tm),
141             step => $test->number, #XXX TODO maybe this isn't right
142             name => $test_name,
143             status => $status_name,
144             }
145             );
146 2         86 $self->{lasttime} = $tm;
147              
148 2         8 return 1;
149             }
150              
151             sub bailoutCallback {
152 0     0 1 0 my ($test) = @_;
153 0         0 my $self = $test->{'parser'};
154 0         0 my $line = $test->as_string;
155 0         0 $self->{'raw_output'} .= "$line\n";
156 0         0 $self->{'is_bailout'} = 1;
157 0         0 return;
158             }
159              
160             sub EOFCallback {
161 2     2 1 2125 my ($self) = @_;
162              
163             #Test done. Record elapsed time.
164 2         9 $self->{'elapsed'} = Time::HiRes::tv_interval($self->{'starttime'});
165              
166 2         35 my $todo_failed = $self->todo() - $self->todo_passed();
167              
168 2         20 my $status = 'OK';
169              
170 2 50       15 $status = 'NOT OK' if $self->has_problems();
171              
172 2 0 33     65 $status = 'TODO PASSED'
      33        
173             if $self->todo_passed()
174             && !$self->failed()
175             && $self->is_good_plan(); #If no fails, but a TODO pass, mark as TODOP
176              
177 2 0 33     18 $status = 'TODO FAILED'
      33        
178             if $todo_failed
179             && !$self->failed()
180             && $self->is_good_plan()
181             ; #If no fails, but a TODO fail, prefer TODOF to TODOP
182              
183 2 50       9 $status = "SKIPPED" if $self->skip_all(); #Skip all, whee
184              
185 2 50       17 $status = "BAIL OUT" if $self->{is_bailout};
186              
187             #Global status override
188 2 100       9 $status = $self->{'global_status'} if $self->{'global_status'};
189 2 100       11 return if $status eq 'DISCARD';
190              
191             #Notify user about bad plan a bit better, supposing we haven't bailed
192 1 0 33     3 if (!$self->is_good_plan() && !$self->{'is_bailout'}) {
193 0         0 $self->{'raw_output'} .=
194             "\n# ERROR: Bad plan. You ran "
195             . $self->tests_run
196             . " tests, but planned "
197             . $self->tests_planned . ".";
198             }
199              
200             $self->{upload} = {
201             body => $self->{raw_output},
202             elapsed => $self->{elapsed},
203             occurred =>
204             strftime("%Y-%m-%d %H:%M:%S", localtime($self->{starttime}->[0])),
205             status => $status,
206             platform => $self->{platform},
207             executor => $self->{executor},
208             version => $self->{sut_version},
209             test_version => $self->{test_version},
210             name => basename($self->{file}),
211             path => dirname($self->{file}),
212             steps => $self->{steps},
213 1         243 steps_planned => $self->tests_planned
214             };
215              
216 1         28 &{\&{$self->{indexer} . "::index_results"}}($self->{upload});
  1         2  
  1         25  
217 1         6 return $status;
218             }
219              
220             sub planCallback {
221 2     2 1 264 my ($plan) = @_;
222 2         6 my $self = $plan->{'parser'};
223 2         18 $self->{raw_output} .= $plan->as_string . "\n";
224             }
225              
226             sub make_result {
227 7     7 1 154247 my ($self, @args) = @_;
228 7         50 my $res = $self->SUPER::make_result(@args);
229 7         564 $res->{'parser'} = $self;
230 7         37 return $res;
231             }
232              
233             1;
234              
235             __END__