File Coverage

blib/lib/Tapper/Cmd/Testplan.pm
Criterion Covered Total %
statement 85 127 66.9
branch 8 12 66.6
condition 3 8 37.5
subroutine 21 27 77.7
pod 10 11 90.9
total 127 185 68.6


line stmt bran cond sub pod time code
1             package Tapper::Cmd::Testplan;
2             our $AUTHORITY = 'cpan:TAPPER';
3             $Tapper::Cmd::Testplan::VERSION = '5.0.11';
4 2     2   5611668 use 5.010;
  2         14  
5 2     2   536 use Moose;
  2         521809  
  2         13  
6              
7 2     2   13971 use Cwd;
  2         6  
  2         147  
8 2     2   13 use Try::Tiny;
  2         6  
  2         110  
9 2     2   14 use YAML::Syck;
  2         4  
  2         112  
10 2     2   736 use Tapper::Model 'model';
  2         4331  
  2         98  
11 2     2   1048 use Tapper::Reports::DPath::TT;
  2         349484  
  2         129  
12 2     2   19 use File::Slurp 'slurp';
  2         6  
  2         124  
13 2     2   452 use Perl6::Junction 'any';
  2         7023  
  2         3023  
14              
15             extends 'Tapper::Cmd';
16              
17              
18              
19             sub get_module_for_type
20             {
21 5     5 1 31 my ($self, $type) = @_;
22            
23 5 100       44 if ( lc($type) eq 'multitest' ) {
    50          
24 4         19 return "Tapper::Cmd::Testrun";
25             }
26             elsif ( lc($type) eq 'scenario') {
27 1         7 return "Tapper::Cmd::Scenario"
28             }
29             else {
30 0         0 $type = ucfirst($type); return "Tapper::Cmd::$type";
  0         0  
31             }
32             }
33              
34              
35             sub add {
36 4     4 1 28526 my ($self, $plan_content, $path, $name) = @_;
37              
38 4         35 my @plans = YAML::Syck::Load($plan_content);
39             # use Data::Dumper;
40             # print STDERR "plans: ".Dumper($plan_content);
41             # print STDERR "plans: ".Dumper(\@plans);
42              
43 4         879 my $instance = model('TestrunDB')->resultset('TestplanInstance')->new({
44             evaluated_testplan => $plan_content,
45             path => $path,
46             name => $name,
47             });
48 4         2773 $instance->insert;
49              
50 4         68081 my @testrun_ids;
51 4         203 foreach my $plan (@plans) {
52 5 50       221 die "Missing plan type for the following testplan: \n".Dump($plan) unless $plan->{type};
53 5         48 my $module = $self->get_module_for_type($plan->{type});
54              
55             try {
56 5     5   879 eval "use $module";
  1     1   721  
  1     1   5  
  1     1   28  
  1     1   12  
  1     1   2  
  1         18  
  1         1260  
  1         8  
  1         32  
  1         15  
  1         4  
  1         22  
  1         16  
  1         4  
  1         29  
57             } catch {
58 0     0   0 die "Can not load '$module' to handle testplan of type $plan->{type}: $!";
59 5         67 };
60              
61 5         143 my $handler = "$module"->new();
62 5   66     3587 my $description = $plan->{testplan_description} || $plan->{description};
63 5         148 my @new_ids = $handler->create($description, $instance->id);
64 5         68 push @testrun_ids, @new_ids;
65              
66             }
67 4         993 return { testplan_id => $instance->id, testrun_ids => \@testrun_ids };
68             }
69              
70              
71             sub del {
72 1     1 1 23203 my ($self, $id) = @_;
73 1         6 my $testplan = model('TestrunDB')->resultset('TestplanInstance')->find($id);
74 1         3143 while(my $testrun = $testplan->testruns->next) {
75 4 50       69292 if ($testrun->testrun_scheduling->status eq 'running') {
76 0         0 my $message = model('TestrunDB')->resultset('Message')->new({testrun_id => $testrun->id,
77             type => 'state',
78             message => {
79             state => 'quit',
80             error => 'Testplan cancelled'
81             }});
82 0         0 $message->insert();
83             }
84 4         26106 $testrun->testrun_scheduling->testrun->testplan_id(undef);
85 4         20527 $testrun->testrun_scheduling->testrun->update;
86 4         70225 $testrun->testrun_scheduling->status('finished');
87 4         1489 $testrun->testrun_scheduling->update;
88             }
89 1         23084 $testplan->delete();
90 1         17428 return 0;
91             }
92              
93              
94             sub cancel {
95 0     0 1 0 my ($self, $id, $comment) = @_;
96              
97 0   0     0 $comment ||= 'Testplan cancelled';
98 0         0 my $testplan = model('TestrunDB')->resultset('TestplanInstance')->find($id);
99 0         0 my $cmd = Tapper::Cmd::Testrun->new;
100 0         0 my $testruns = $testplan->testruns;
101 0         0 while(my $testrun = $testruns->next) {
102 0         0 $cmd->cancel($testrun->id, $comment);
103             }
104 0         0 return 0;
105             }
106              
107              
108             sub rerun
109             {
110 1     1 1 15627 my ($self, $id) = @_;
111              
112 1         7 my $testplan = model('TestrunDB')->resultset('TestplanInstance')->find($id);
113 1 50       3251 die "No testplan with ID $id\n" unless $testplan;
114              
115 1         47 return $self->add($testplan->evaluated_testplan, $testplan->path, $testplan->name);
116             }
117              
118              
119             sub parse_path
120             {
121 0     0 1 0 my ($self, $filename) = @_;
122 0         0 $filename = Cwd::abs_path($filename);
123 0         0 my $basedir = Tapper::Config->subconfig->{paths}{testplan_path};
124             # splitting filename at basedir returns an array with the empty
125             # string before and the path after the basedir
126 0         0 my $path = (split $basedir, $filename)[1];
127 0         0 return $path;
128             }
129              
130             sub get_shortname {
131              
132 1     1 0 4 my ( $or_self, $s_plan ) = @_;
133              
134 1         12 foreach my $s_line ( split /\n/, $s_plan ) {
135 22 100       69 if ( $s_line =~ /^[#\s]*-?\s*(?:short)?name\s*:\s*(.+?)\s*$/i ) {
136 1         3 my $shortname = $1;
137 1         4 $shortname =~ s/['"]+//g;
138 1         4 return $shortname;
139             }
140             }
141              
142 0         0 return;
143              
144             }
145              
146              
147             sub guide
148             {
149 0     0 1 0 my ($self, $file, $substitutes, $include) = @_;
150 0         0 my $text;
151 0         0 my $guide = $self->apply_macro($file,
152             $substitutes,
153             $include);
154              
155 0         0 my @guide = grep { m/^###/ } split (qr/\n/, $guide);
  0         0  
156 0         0 $text = "Self-documentation:\n";
157 0         0 $text = join "\n", map { my $l = $_; $l =~ s/^###/ /; "$l" } @guide;
  0         0  
  0         0  
  0         0  
158 0         0 return $text;
159             }
160              
161              
162             sub testplannew {
163 1     1 1 42677 my ($self, $opt) = @_;
164              
165 1         12 my $plan = $self->apply_macro($opt->{file}, $opt->{substitutes}, $opt->{include});
166 1   33     58023 my $path = $opt->{path} || $self->parse_path($opt->{file});
167 1         6 my $name = $self->get_shortname($plan);
168 1         8 return $self->add($plan, $path, $name);
169             }
170              
171              
172             sub status
173             {
174 0     0 1 0 my ($self, $id) = @_;
175 0         0 my $results = model('TestrunDB')->fetch_raw_sql({
176             query_name => 'testplans::testplan_status',
177             fetch_type => '$%',
178             query_vals => {testplan_id => $id},
179             });
180 0         0 my $testruns_rs = model->resultset("Testrun")->search({testplan_id => $id});
181 0         0 my %testrun_ids = map { $_->id => $_->topic_name} $testruns_rs->all;
  0         0  
182 0         0 $results->{testruns} = \%testrun_ids;
183 0         0 return $results;
184              
185             # TODO check how we can get old infos back in
186             # * status - one of 'schedule', 'running', 'pass', 'fail'
187             # * complete_percentage - percentage of finished testruns
188             # * started_percentage - percentage of running and finished testruns
189             # * success_percentage - average of success rates of finished testruns
190             }
191              
192              
193             sub testplan_files
194             {
195 0     0 1 0 my ($self, $testplan_id, $filter) = @_;
196 0         0 my $results_rawsql = model('TestrunDB')->fetch_raw_sql({
197             query_name => 'testplans::reportfile',
198             fetch_type => '@@',
199             query_vals => {testplan_id => $testplan_id, filter => $filter},
200             });
201 0         0 my @results = map { $_->[0] } @$results_rawsql;
  0         0  
202 0         0 return \@results;
203             }
204              
205              
206             1; # End of Tapper::Cmd::Testplan
207              
208             __END__
209              
210             =pod
211              
212             =encoding UTF-8
213              
214             =head1 NAME
215              
216             Tapper::Cmd::Testplan
217              
218             =head1 SYNOPSIS
219              
220             This project offers functions to add, delete or update testplan
221             instances in the database.
222              
223             use Tapper::Cmd::Testplan;
224              
225             my $cmd = Tapper::Cmd::Testplan->new();
226             my $res = $cmd->add($plan);
227             $cmd->update($res->{testplan_id}, $new_plan);
228             $cmd->del($res->{testplan_id});
229              
230             ...
231              
232             =head1 NAME
233              
234             Tapper::Cmd::Testplan - Backend functions for manipluation of testplan instances in the database
235              
236             =head1 FUNCTIONS
237              
238             =head2 get_module_for_type
239              
240             Get the name of the Tapper::Cmd module that is reponsible for a given
241             type. The name of the module is optimized for the Tapper developer but
242             the type given in the testplan should be telling for the testplan user.
243              
244             @param string - type
245              
246             @return string - name of the responsible module
247              
248             =head2 add
249              
250             Add a new testplan instance to database and create the associated
251             testruns. The function expects a string containing the evaluated test
252             plan content and a path.
253              
254             @param string - plan content
255             @param string - path
256             @optparam string - name
257              
258             @return int - testplan instance id
259              
260             @throws die()
261              
262             =head2 del
263              
264             Delete testrun with given id from database. Please not that this does
265             not remove the associated testruns.
266              
267             @param int - testplan instance id
268              
269             @return success - 0
270             @return error - exception
271              
272             @throws die()
273              
274             =head2 cancel
275              
276             Cancel testplan by canceling all of its testruns.
277              
278             @param int - testplan instance id
279              
280             @return success - 0
281             @return error - exception
282              
283             @throws die()
284              
285             =head2 rerun
286              
287             Reapply the evaluated testplan of the given testplan instance.
288              
289             @param int - testplan instance id
290              
291             @return success - new testplan id
292             @return error - exception
293              
294             @throws die()
295              
296             =head2 parse_path
297              
298             Get the test plan path from the filename. This is a little more tricky
299             since we do not simply want the dirname but kind of an "un-prefix".
300              
301             @param string - file name
302              
303             @return string - test plan path
304              
305             =head2 guide
306              
307             Get self documentation of a testplan file.
308              
309             @param string - file name of testplan file
310              
311             @return success - documentation text
312              
313             @throws - die()
314              
315             =head2 testplannew
316              
317             Create a testplan instance from a file.
318              
319             @param hash ref - options containing
320              
321             required:
322             * file: string, path of the testplan file
323             * substitutes: hash ref, substitute variables for Template Toolkit
324              
325             optional:
326             * include: array ref of strings containing include paths
327             * path: string, alternative path instead of real path
328             * name: string, overwrite shortname in plan
329              
330             @return success - testplan id
331              
332             @throws die
333              
334             =head2 status
335              
336             Get information of one testplan.
337              
338             @param int - testplan id
339              
340             @return - hash ref -
341             * count_fail 0,
342             * count_pass 2,
343             * count_pending 0,
344             * name "HWXYZ",
345             * path undef,
346             * testplan_date "2014-03-24",
347             * testplan_id 1040,
348             * testplan_time "14:17"
349              
350             @throws - die
351              
352             =head2 testplan_files
353              
354             Get all files that belong to a testplan.
355              
356             @param int - testplan id
357             @param string - filter
358              
359             @return array ref - list of report file ids
360              
361             @throws - die
362              
363             =head1 AUTHORS
364              
365             =over 4
366              
367             =item *
368              
369             AMD OSRC Tapper Team <tapper@amd64.org>
370              
371             =item *
372              
373             Tapper Team <tapper-ops@amazon.com>
374              
375             =back
376              
377             =head1 COPYRIGHT AND LICENSE
378              
379             This software is Copyright (c) 2020 by Advanced Micro Devices, Inc..
380              
381             This is free software, licensed under:
382              
383             The (two-clause) FreeBSD License
384              
385             =cut