line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Tapper::CLI::Testplan; |
2
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:TAPPER'; |
3
|
|
|
|
|
|
|
# ABSTRACT: Handle testplans |
4
|
|
|
|
|
|
|
$Tapper::CLI::Testplan::VERSION = '5.0.6'; |
5
|
1
|
|
|
1
|
|
904
|
use 5.010; |
|
1
|
|
|
|
|
4
|
|
6
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
27
|
|
7
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
23
|
|
8
|
1
|
|
|
1
|
|
449
|
use Perl6::Junction qw/all/; |
|
1
|
|
|
|
|
7128
|
|
|
1
|
|
|
|
|
67
|
|
9
|
1
|
|
|
1
|
|
8
|
use English '-no_match_vars'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
5
|
|
10
|
1
|
|
|
1
|
|
343
|
no if $] >= 5.018, warnings => "experimental"; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
8
|
|
11
|
|
|
|
|
|
|
|
12
|
1
|
|
|
1
|
|
755
|
use JSON::XS; |
|
1
|
|
|
|
|
2668
|
|
|
1
|
|
|
|
|
59
|
|
13
|
1
|
|
|
1
|
|
426
|
use YAML::XS; |
|
1
|
|
|
|
|
2710
|
|
|
1
|
|
|
|
|
676
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub testplanlist |
18
|
|
|
|
|
|
|
{ |
19
|
|
|
|
|
|
|
|
20
|
0
|
|
|
0
|
1
|
|
my ($c) = @_; |
21
|
0
|
|
|
|
|
|
$c->getopt( 'name|n=s@', 'path|p=s@', 'testrun|t=s@', 'id|i=i@','active|a','verbose|v', 'format=s', 'help|?' ); |
22
|
|
|
|
|
|
|
|
23
|
0
|
0
|
|
|
|
|
if ( $c->options->{help} ) { |
24
|
0
|
|
|
|
|
|
say STDERR "Usage: $0 testplan-list [--path=path|-p=path]* [--name|-n=name]* [--testrun=id|-t=id]* [--id=number|-i=number] [--active|-a] [ --format=JSON|YAML ] [--verbose|-v]"; |
25
|
0
|
|
|
|
|
|
say STDERR ""; |
26
|
0
|
|
|
|
|
|
say STDERR " --path|-p Path name of testplans to list."; |
27
|
0
|
|
|
|
|
|
say STDERR " Only slashes(/) are allowed as separators."; |
28
|
0
|
|
|
|
|
|
say STDERR " Can be an SQL like condition (i.e. '\%name\%'). Make sure your shell does not break it."; |
29
|
0
|
|
|
|
|
|
say STDERR " Can be given multiple times"; |
30
|
0
|
|
|
|
|
|
say STDERR " Will reduce number of testplans when given with --testrun or --name, can't go with --id"; |
31
|
0
|
|
|
|
|
|
say STDERR " --name|-n name of testplans to list."; |
32
|
0
|
|
|
|
|
|
say STDERR " Can be an SQL like condition (i.e. '\%name\%'). Make sure your shell does not break it."; |
33
|
0
|
|
|
|
|
|
say STDERR " Can be given multiple times"; |
34
|
0
|
|
|
|
|
|
say STDERR " Will reduce number of testplans when given with --testrun or --path, can't go with --id"; |
35
|
0
|
|
|
|
|
|
say STDERR " --testrun|-t Show testplan containing this testrun id"; |
36
|
0
|
|
|
|
|
|
say STDERR " Can be given multiple times"; |
37
|
0
|
|
|
|
|
|
say STDERR " Will reduce number of testplans when given with --name or --path, can't go with --id"; |
38
|
0
|
|
|
|
|
|
say STDERR " --id|-i Show testplan of given id"; |
39
|
0
|
|
|
|
|
|
say STDERR " Can be given multiple times. Implies -v"; |
40
|
0
|
|
|
|
|
|
say STDERR " Will override --testrun, --path and --name"; |
41
|
0
|
|
|
|
|
|
say STDERR " --active|-a Only show testplan with testruns that are not finished yet."; |
42
|
0
|
|
|
|
|
|
say STDERR " Will reduce number of testplans when given with any other filter."; |
43
|
0
|
|
|
|
|
|
say STDERR " --format Give output in this format. Valid values are YAML, JSON. Case insensitive. Always verbose."; |
44
|
0
|
|
|
|
|
|
say STDERR " --verbose|-v Show testplan with id, name and associated testruns. Without only testplan id is shown."; |
45
|
0
|
|
|
|
|
|
say STDERR " --help Print this help message and exit."; |
46
|
0
|
|
|
|
|
|
exit -1; |
47
|
|
|
|
|
|
|
} |
48
|
0
|
|
|
|
|
|
my @ids; |
49
|
|
|
|
|
|
|
my $filtered; |
50
|
0
|
|
|
|
|
|
my $format = $c->options->{format}; |
51
|
|
|
|
|
|
|
|
52
|
0
|
|
|
|
|
|
require Tapper::Model; |
53
|
0
|
0
|
|
|
|
|
if (@{$c->options->{testrun} || []}) { |
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
54
|
0
|
|
|
|
|
|
my $testruns = Tapper::Model::model('TestrunDB')->resultset('Testrun')->search({id => $c->options->{testrun}}); |
55
|
0
|
|
|
|
|
|
while (my $testrun = $testruns->next) { |
56
|
0
|
0
|
|
|
|
|
push @ids, $testrun->testplan_id if $testrun->testplan_id; |
57
|
|
|
|
|
|
|
} |
58
|
0
|
0
|
|
|
|
|
} elsif ( @{$c->options->{name} || []}) { |
59
|
0
|
|
|
|
|
|
my $regex = join("|", map { "($_)" } @{$c->options->{name}}); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
60
|
0
|
|
|
|
|
|
my $instances = Tapper::Model::model('TestrunDB')->resultset('TestplanInstance'); |
61
|
0
|
|
|
|
|
|
while (my $instance = $instances->next) { |
62
|
0
|
0
|
0
|
|
|
|
push @ids, $instance->id if $instance->path and $instance->path =~ /$regex/; |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
} else { |
65
|
0
|
|
|
|
|
|
my $instances = Tapper::Model::model('TestrunDB')->resultset('TestplanInstance'); |
66
|
0
|
|
|
|
|
|
while (my $instance = $instances->next) { |
67
|
0
|
|
|
|
|
|
push @ids, $instance->id; |
68
|
|
|
|
|
|
|
} |
69
|
0
|
|
|
|
|
|
$c->options->{verbose} = 1; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# a join would be faster and maybe cleaner |
73
|
0
|
0
|
|
|
|
|
if ($c->options->{active}) { |
74
|
0
|
|
|
|
|
|
my @local_ids = @ids; |
75
|
0
|
|
|
|
|
|
my $instances = Tapper::Model::model('TestrunDB')->resultset('TestplanInstance')->search({id => \@local_ids}); |
76
|
0
|
|
|
|
|
|
@ids = (); |
77
|
0
|
|
|
|
|
|
while (my $instance = $instances->next) { |
78
|
0
|
0
|
0
|
|
|
|
if ($instance->testruns and grep {$_->testrun_scheduling->status ne 'finished'} $instance->testruns->all) { |
|
0
|
|
|
|
|
|
|
79
|
0
|
|
|
|
|
|
push @ids, $instance->id; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
} |
82
|
0
|
|
|
|
|
|
$instances = Tapper::Model::model('TestrunDB')->resultset('TestplanInstance')->search({id => [ @ids ]}); |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
0
|
0
|
|
|
|
|
if ($c->options->{quiet}) { |
86
|
0
|
|
|
|
|
|
return join ("\n",@ids); |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
0
|
|
|
|
|
|
my %inst_data; |
90
|
0
|
|
|
|
|
|
my $instances = Tapper::Model::model('TestrunDB')->resultset('TestplanInstance')->search({id => \@ids}); |
91
|
0
|
|
|
|
|
|
while (my $instance = $instances->next) { |
92
|
|
|
|
|
|
|
$inst_data{$instance->id} = |
93
|
|
|
|
|
|
|
{ |
94
|
|
|
|
|
|
|
path => $instance->path ? $instance->path : '', |
95
|
|
|
|
|
|
|
name => $instance->path ? $instance->path : '', |
96
|
0
|
0
|
|
|
|
|
testruns => [ map { {id => $_->id, status => ''.$_->testrun_scheduling->status} } $instance->testruns ], # stringify enum object |
|
0
|
0
|
|
|
|
|
|
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
} |
99
|
0
|
0
|
|
|
|
|
if ($c->options->{format}) { |
100
|
1
|
|
|
1
|
|
8
|
use Data::Dumper; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
559
|
|
101
|
0
|
|
|
|
|
|
given(lc($c->options->{format})) { |
102
|
0
|
|
|
|
|
|
when ('yaml') { return YAML::XS::Dump(\%inst_data)} |
|
0
|
|
|
|
|
|
|
103
|
0
|
|
|
|
|
|
when ('json') { return encode_json(\%inst_data)} |
|
0
|
|
|
|
|
|
|
104
|
0
|
|
|
|
|
|
default { die "unknown format: ",$c->options->{format}} |
|
0
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
} else { |
107
|
0
|
0
|
|
|
|
|
if ($c->options->{verbose}) { |
108
|
0
|
|
|
|
|
|
my @testplan_info; |
109
|
0
|
|
|
|
|
|
foreach my $id (keys %inst_data) { |
110
|
|
|
|
|
|
|
my $line = join(" - ", |
111
|
|
|
|
|
|
|
$id, |
112
|
|
|
|
|
|
|
$inst_data{$id}->{path}, |
113
|
0
|
|
|
|
|
|
"testruns: ".join(", ", map{$_->{id}} @{$inst_data{$id}->{testruns}}) |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
); |
115
|
0
|
|
|
|
|
|
push @testplan_info, $line; |
116
|
|
|
|
|
|
|
} |
117
|
0
|
|
|
|
|
|
return join "\n", @testplan_info; |
118
|
|
|
|
|
|
|
} else { |
119
|
0
|
|
|
|
|
|
return join "\n", map { $_->id} $instances->all; |
|
0
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
sub testplannew |
127
|
|
|
|
|
|
|
{ |
128
|
0
|
|
|
0
|
1
|
|
my ($c) = @_; |
129
|
0
|
|
|
|
|
|
$c->getopt( 'include|I=s@', 'name=s', 'path=s', 'file=s', 'D=s%', 'dryrun|n', 'guide|g', 'quiet|q', 'subst_json=s','verbose|v', 'help|?' ); |
130
|
|
|
|
|
|
|
|
131
|
0
|
|
|
|
|
|
my $opt = $c->options; |
132
|
|
|
|
|
|
|
|
133
|
0
|
0
|
0
|
|
|
|
if ( $opt->{help} or not $opt->{file}) { |
134
|
0
|
|
|
|
|
|
say STDERR "Usage: $0 testplan-new --file=s [ -dry-run|n ] [ -v ] [ -Dkey=value ] [ --path=s ] [ --name=s ] [ --include=s ]*"; |
135
|
0
|
|
|
|
|
|
say STDERR ""; |
136
|
0
|
|
|
|
|
|
say STDERR " -D Define a key=value pair used for macro expansion"; |
137
|
0
|
|
|
|
|
|
say STDERR " --dryrun Just print evaluated testplan without submit to DB"; |
138
|
0
|
|
|
|
|
|
say STDERR " --file Use (macro) testplan file"; |
139
|
0
|
|
|
|
|
|
say STDERR " --guide Just print self-documentation"; |
140
|
0
|
|
|
|
|
|
say STDERR " --include Add include directory (multiple allowed)"; |
141
|
0
|
|
|
|
|
|
say STDERR " --name Provide a name for this testplan instance"; |
142
|
0
|
|
|
|
|
|
say STDERR " --path Put this path into db instead of file path"; |
143
|
0
|
|
|
|
|
|
say STDERR " --subst_json File name that contains macro expansion values in JSON formaxt"; |
144
|
0
|
|
|
|
|
|
say STDERR " --verbose Show more progress output."; |
145
|
0
|
|
|
|
|
|
say STDERR " --quiet Only show testplan ids, suppress path, name and testrun ids."; |
146
|
0
|
|
|
|
|
|
say STDERR " --help Print this help message and exit."; |
147
|
0
|
|
|
|
|
|
exit -1; |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
0
|
0
|
|
|
|
|
die "Testplan file needed\n" if not $opt->{file}; |
151
|
0
|
0
|
|
|
|
|
die "Testplan file @{[ $opt->{file} ]} does not exist" if not -e $opt->{file}; |
|
0
|
|
|
|
|
|
|
152
|
0
|
0
|
|
|
|
|
die "Testplan file @{[ $opt->{file} ]} is not readable" if not -r $opt->{file}; |
|
0
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
|
154
|
0
|
|
|
|
|
|
require Tapper::Cmd::Testplan; |
155
|
0
|
0
|
|
|
|
|
if ($opt->{subst_json}) { |
156
|
1
|
|
|
1
|
|
8
|
use File::Slurp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
393
|
|
157
|
0
|
|
|
|
|
|
my $data = File::Slurp::read_file($opt->{subst_json}); |
158
|
0
|
|
|
|
|
|
$opt->{substitutes} = JSON::XS::decode_json($data); |
159
|
|
|
|
|
|
|
} else { |
160
|
0
|
|
|
|
|
|
$opt->{substitutes} = $opt->{D}; |
161
|
|
|
|
|
|
|
} |
162
|
0
|
|
|
|
|
|
my $cmd = Tapper::Cmd::Testplan->new; |
163
|
0
|
0
|
|
|
|
|
if ($opt->{guide}) { |
164
|
0
|
|
|
|
|
|
return $cmd->guide($opt->{file}, $opt->{substitutes}, $opt->{include}); |
165
|
|
|
|
|
|
|
} |
166
|
0
|
0
|
|
|
|
|
if ($opt->{dryrun}) { |
167
|
0
|
|
|
|
|
|
return $cmd->apply_macro($opt->{file}, $opt->{substitutes}, $opt->{include}); |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
0
|
|
|
|
|
|
my $answer = $cmd->testplannew($opt); |
171
|
|
|
|
|
|
|
# Format: |
172
|
|
|
|
|
|
|
# TESTPLANID: TESTRUNID TESTRUNID TESTRUNID |
173
|
|
|
|
|
|
|
my $output = |
174
|
|
|
|
|
|
|
$answer->{testplan_id} |
175
|
|
|
|
|
|
|
. ': ' |
176
|
0
|
0
|
|
|
|
|
. join(' ', @{$answer->{testrun_ids} || []}); |
|
0
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
|
178
|
0
|
|
|
|
|
|
return $output; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub setup |
184
|
|
|
|
|
|
|
{ |
185
|
0
|
|
|
0
|
1
|
|
my ($c) = @_; |
186
|
0
|
|
|
|
|
|
$c->register('testplan-send', \&testplansend, 'Send choosen testplan reports'); |
187
|
0
|
|
|
|
|
|
$c->register('testplan-list', \&testplanlist, 'List testplans matching a given pattern'); |
188
|
0
|
|
|
|
|
|
$c->register('testplan-tj-send', \&testplan_tj_send, 'Send all testplan reports that are due according to taskjuggler plan'); |
189
|
0
|
|
|
|
|
|
$c->register('testplan-tj-generate', \&testplan_tj_generate, 'Apply all testplans that are due according to taskjuggler plan'); |
190
|
0
|
|
|
|
|
|
$c->register('testplan-new', \&testplannew, 'Create new testplan instance from file'); |
191
|
0
|
0
|
|
|
|
|
if ($c->can('group_commands')) { |
192
|
0
|
|
|
|
|
|
$c->group_commands('Testplan commands', 'testplan-send', 'testplan-list', 'testplan-tj-send', 'testplan-tj-generate', 'testplan-new'); |
193
|
|
|
|
|
|
|
} |
194
|
0
|
|
|
|
|
|
return; |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
1; # End of Tapper::CLI |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
__END__ |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=pod |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=encoding UTF-8 |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=head1 NAME |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
Tapper::CLI::Testplan - Handle testplans |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=head1 SYNOPSIS |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
This module is part of the Tapper::CLI framework. It is supposed to be |
212
|
|
|
|
|
|
|
used together with App::Rad. All following functions expect their |
213
|
|
|
|
|
|
|
arguments as $c->options->{$arg}. |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
use App::Rad; |
216
|
|
|
|
|
|
|
use Tapper::CLI::Testplan; |
217
|
|
|
|
|
|
|
Tapper::CLI::Testplan::setup($c); |
218
|
|
|
|
|
|
|
App::Rad->run(); |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=head1 NAME |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
Tapper::CLI::Testplan - Tapper - testplan related commands for the tapper CLI |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=head1 FUNCTIONS |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=head2 testplanlist |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
List testplans matching a given pattern. |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=head2 testplannew |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
Create new testplan instance from file. |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head2 setup |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
Initialize the testplan functions for tapper CLI |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=head1 AUTHOR |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
AMD OSRC Tapper Team <tapper@amd64.org> |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
This software is Copyright (c) 2020 by Advanced Micro Devices, Inc.. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
This is free software, licensed under: |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
The (two-clause) FreeBSD License |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=cut |