line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# ABSTRACT: Upload your TAP results to TestRail |
2
|
|
|
|
|
|
|
# PODNAME: Test::Rail::Parser |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package Test::Rail::Parser; |
5
|
|
|
|
|
|
|
$Test::Rail::Parser::VERSION = '0.052'; |
6
|
3
|
|
|
3
|
|
55105
|
use strict; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
95
|
|
7
|
3
|
|
|
3
|
|
14
|
use warnings; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
93
|
|
8
|
3
|
|
|
3
|
|
13
|
use utf8; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
21
|
|
9
|
|
|
|
|
|
|
|
10
|
3
|
|
|
3
|
|
86
|
use parent qw/TAP::Parser/; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
19
|
|
11
|
3
|
|
|
3
|
|
108836
|
use Carp qw{cluck confess}; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
186
|
|
12
|
3
|
|
|
3
|
|
28
|
use POSIX qw{floor strftime}; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
24
|
|
13
|
3
|
|
|
3
|
|
2791
|
use Clone qw{clone}; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
171
|
|
14
|
|
|
|
|
|
|
|
15
|
3
|
|
|
3
|
|
18
|
use TestRail::API; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
105
|
|
16
|
3
|
|
|
3
|
|
404
|
use TestRail::Utils; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
96
|
|
17
|
3
|
|
|
3
|
|
16
|
use Scalar::Util qw{reftype}; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
147
|
|
18
|
|
|
|
|
|
|
|
19
|
3
|
|
|
3
|
|
18
|
use File::Basename qw{basename}; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
12856
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
sub new { |
22
|
57
|
|
|
57
|
1
|
90557
|
my ( $class, $opts ) = @_; |
23
|
57
|
|
|
|
|
251958
|
$opts = clone $opts; #Convenience, if we are passing over and over again... |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
#Load our callbacks |
26
|
57
|
|
|
|
|
8370
|
$opts->{'callbacks'} = { |
27
|
|
|
|
|
|
|
'test' => \&testCallback, |
28
|
|
|
|
|
|
|
'comment' => \&commentCallback, |
29
|
|
|
|
|
|
|
'unknown' => \&unknownCallback, |
30
|
|
|
|
|
|
|
'bailout' => \&bailoutCallback, |
31
|
|
|
|
|
|
|
'EOF' => \&EOFCallback, |
32
|
|
|
|
|
|
|
'plan' => \&planCallback, |
33
|
|
|
|
|
|
|
}; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
my $tropts = { |
36
|
|
|
|
|
|
|
'apiurl' => delete $opts->{'apiurl'}, |
37
|
|
|
|
|
|
|
'user' => delete $opts->{'user'}, |
38
|
|
|
|
|
|
|
'pass' => delete $opts->{'pass'}, |
39
|
|
|
|
|
|
|
'debug' => delete $opts->{'debug'}, |
40
|
|
|
|
|
|
|
'browser' => delete $opts->{'browser'}, |
41
|
|
|
|
|
|
|
'run' => delete $opts->{'run'}, |
42
|
|
|
|
|
|
|
'project' => delete $opts->{'project'}, |
43
|
|
|
|
|
|
|
'project_id' => delete $opts->{'project_id'}, |
44
|
|
|
|
|
|
|
'step_results' => delete $opts->{'step_results'}, |
45
|
|
|
|
|
|
|
'plan' => delete $opts->{'plan'}, |
46
|
|
|
|
|
|
|
'plan_id' => delete $opts->{'plan_id'}, |
47
|
|
|
|
|
|
|
'configs' => delete $opts->{'configs'} // [], |
48
|
|
|
|
|
|
|
'testsuite_id' => delete $opts->{'testsuite_id'}, |
49
|
|
|
|
|
|
|
'testsuite' => delete $opts->{'testsuite'}, |
50
|
|
|
|
|
|
|
'encoding' => delete $opts->{'encoding'}, |
51
|
|
|
|
|
|
|
'sections' => delete $opts->{'sections'}, |
52
|
|
|
|
|
|
|
'autoclose' => delete $opts->{'autoclose'}, |
53
|
|
|
|
|
|
|
'config_group' => delete $opts->{'config_group'}, |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
#Stubs for extension by subclassers |
56
|
|
|
|
|
|
|
'result_options' => delete $opts->{'result_options'}, |
57
|
|
|
|
|
|
|
'result_custom_options' => delete $opts->{'result_custom_options'}, |
58
|
|
|
|
|
|
|
'test_bad_status' => delete $opts->{'test_bad_status'}, |
59
|
57
|
|
100
|
|
|
2185
|
'max_tries' => delete $opts->{'max_tries'} || 1, |
|
|
|
100
|
|
|
|
|
60
|
|
|
|
|
|
|
}; |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
confess("plan passed, but no run passed!") |
63
|
57
|
50
|
66
|
|
|
930
|
if !$tropts->{'run'} && $tropts->{'plan'}; |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
#Allow natural confessing from constructor |
66
|
|
|
|
|
|
|
#Force-on POST redirects for maximum compatibility |
67
|
|
|
|
|
|
|
#Also ensure all opts that need string type have it when undef |
68
|
|
|
|
|
|
|
my $tr = TestRail::API->new( |
69
|
|
|
|
|
|
|
$tropts->{'apiurl'} // '', |
70
|
|
|
|
|
|
|
$tropts->{'user'} // '', |
71
|
|
|
|
|
|
|
$tropts->{'pass'} // '', |
72
|
|
|
|
|
|
|
$tropts->{'encoding'} // '', |
73
|
|
|
|
|
|
|
$tropts->{'debug'}, |
74
|
|
|
|
|
|
|
1, |
75
|
|
|
|
|
|
|
$tropts->{max_tries}, |
76
|
56
|
|
50
|
|
|
1737
|
{ 'skip_usercache' => 1 }, |
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
77
|
|
|
|
|
|
|
); |
78
|
56
|
|
|
|
|
230
|
$tropts->{'testrail'} = $tr; |
79
|
|
|
|
|
|
|
$tr->{'browser'} = $tropts->{'browser'} |
80
|
56
|
50
|
|
|
|
1752
|
if defined( $tropts->{'browser'} ); #allow mocks |
81
|
56
|
|
|
|
|
169
|
$tr->{'debug'} = 0; #Always suppress in production |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
#Get project ID from name, if not provided |
84
|
56
|
100
|
|
|
|
264
|
if ( !defined( $tropts->{'project_id'} ) ) { |
85
|
52
|
|
|
|
|
142
|
my $pname = $tropts->{'project'}; |
86
|
52
|
|
|
|
|
469
|
$tropts->{'project'} = $tr->getProjectByName($pname); |
87
|
|
|
|
|
|
|
confess("Could not list projects! Shutting down.") |
88
|
52
|
50
|
|
|
|
240
|
if ( $tropts->{'project'} == -500 ); |
89
|
52
|
50
|
|
|
|
199
|
if ( !$tropts->{'project'} ) { |
90
|
0
|
|
|
|
|
0
|
confess( |
91
|
|
|
|
|
|
|
"No project (or project_id) provided, or that which was provided was invalid!" |
92
|
|
|
|
|
|
|
); |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
else { |
96
|
4
|
|
|
|
|
33
|
$tropts->{'project'} = $tr->getProjectByID( $tropts->{'project_id'} ); |
97
|
|
|
|
|
|
|
confess("No such project with ID $tropts->{project_id}!") |
98
|
4
|
50
|
|
|
|
29
|
if !$tropts->{'project'}; |
99
|
|
|
|
|
|
|
} |
100
|
56
|
|
|
|
|
178
|
$tropts->{'project_id'} = $tropts->{'project'}->{'id'}; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# Ok, let's cache the users since we have the project ID now |
103
|
56
|
|
|
|
|
436
|
$tr->getUsers( $tropts->{'project_id'} ); |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
#Discover possible test statuses |
106
|
56
|
|
|
|
|
547
|
$tropts->{'statuses'} = $tr->getPossibleTestStatuses(); |
107
|
56
|
|
|
|
|
187
|
my @ok = grep { $_->{'name'} eq 'passed' } @{ $tropts->{'statuses'} }; |
|
504
|
|
|
|
|
938
|
|
|
56
|
|
|
|
|
195
|
|
108
|
56
|
|
|
|
|
128
|
my @not_ok = grep { $_->{'name'} eq 'failed' } @{ $tropts->{'statuses'} }; |
|
504
|
|
|
|
|
793
|
|
|
56
|
|
|
|
|
145
|
|
109
|
56
|
|
|
|
|
115
|
my @skip = grep { $_->{'name'} eq 'skip' } @{ $tropts->{'statuses'} }; |
|
504
|
|
|
|
|
849
|
|
|
56
|
|
|
|
|
152
|
|
110
|
56
|
|
|
|
|
158
|
my @todof = grep { $_->{'name'} eq 'todo_fail' } @{ $tropts->{'statuses'} }; |
|
504
|
|
|
|
|
784
|
|
|
56
|
|
|
|
|
137
|
|
111
|
56
|
|
|
|
|
115
|
my @todop = grep { $_->{'name'} eq 'todo_pass' } @{ $tropts->{'statuses'} }; |
|
504
|
|
|
|
|
755
|
|
|
56
|
|
|
|
|
123
|
|
112
|
56
|
|
|
|
|
106
|
my @retest = grep { $_->{'name'} eq 'retest' } @{ $tropts->{'statuses'} }; |
|
504
|
|
|
|
|
753
|
|
|
56
|
|
|
|
|
113
|
|
113
|
56
|
|
|
|
|
116
|
my @tbad; |
114
|
|
|
|
|
|
|
@tbad = |
115
|
162
|
|
|
|
|
263
|
grep { $_->{'name'} eq $tropts->{test_bad_status} } |
116
|
18
|
|
|
|
|
65
|
@{ $tropts->{'statuses'} } |
117
|
56
|
100
|
|
|
|
214
|
if $tropts->{test_bad_status}; |
118
|
56
|
50
|
|
|
|
185
|
confess("No status with internal name 'passed' in TestRail!") |
119
|
|
|
|
|
|
|
unless scalar(@ok); |
120
|
56
|
50
|
|
|
|
180
|
confess("No status with internal name 'failed' in TestRail!") |
121
|
|
|
|
|
|
|
unless scalar(@not_ok); |
122
|
56
|
50
|
|
|
|
206
|
confess("No status with internal name 'skip' in TestRail!") |
123
|
|
|
|
|
|
|
unless scalar(@skip); |
124
|
56
|
50
|
|
|
|
157
|
confess("No status with internal name 'todo_fail' in TestRail!") |
125
|
|
|
|
|
|
|
unless scalar(@todof); |
126
|
56
|
50
|
|
|
|
175
|
confess("No status with internal name 'todo_pass' in TestRail!") |
127
|
|
|
|
|
|
|
unless scalar(@todop); |
128
|
56
|
50
|
|
|
|
174
|
confess("No status with internal name 'retest' in TestRail!") |
129
|
|
|
|
|
|
|
unless scalar(@retest); |
130
|
|
|
|
|
|
|
confess( |
131
|
|
|
|
|
|
|
"No status with internal name '$tropts->{test_bad_status}' in TestRail!" |
132
|
56
|
100
|
100
|
|
|
958
|
) unless scalar(@tbad) || !$tropts->{test_bad_status}; |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
#Map in all the statuses |
135
|
55
|
|
|
|
|
148
|
foreach my $status ( @{ $tropts->{'statuses'} } ) { |
|
55
|
|
|
|
|
204
|
|
136
|
495
|
|
|
|
|
1202
|
$tropts->{ $status->{'name'} } = $status; |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
#Special aliases |
140
|
55
|
|
|
|
|
163
|
$tropts->{'ok'} = $ok[0]; |
141
|
55
|
|
|
|
|
142
|
$tropts->{'not_ok'} = $not_ok[0]; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
confess "testsuite and testsuite_id are mutually exclusive" |
144
|
55
|
50
|
66
|
|
|
478
|
if ( $tropts->{'testsuite_id'} && $tropts->{'testsuite'} ); |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
#Grab testsuite by name if needed |
147
|
55
|
100
|
|
|
|
193
|
if ( $tropts->{'testsuite'} ) { |
148
|
|
|
|
|
|
|
my $ts = $tr->getTestSuiteByName( $tropts->{'project_id'}, |
149
|
3
|
|
|
|
|
40
|
$tropts->{'testsuite'} ); |
150
|
3
|
50
|
|
|
|
15
|
confess( "No such testsuite '" . $tropts->{'testsuite'} . "' found!" ) |
151
|
|
|
|
|
|
|
unless $ts; |
152
|
3
|
|
|
|
|
16
|
$tropts->{'testsuite_id'} = $ts->{'id'}; |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
#Grab run |
156
|
55
|
|
|
|
|
139
|
my ( $run, $plan, $config_ids ); |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# See if we have to create a configuration |
159
|
55
|
|
|
|
|
520
|
my $configz2create = $tr->getConfigurations( $tropts->{'project_id'} ); |
160
|
|
|
|
|
|
|
@$configz2create = grep { |
161
|
55
|
|
|
|
|
162
|
my $c = $_; |
|
231
|
|
|
|
|
291
|
|
162
|
231
|
|
|
|
|
268
|
( grep { $_ eq $c->{'name'} } @{ $tropts->{'configs'} } ) |
|
73
|
|
|
|
|
219
|
|
|
231
|
|
|
|
|
691
|
|
163
|
|
|
|
|
|
|
} @$configz2create; |
164
|
55
|
100
|
100
|
|
|
307
|
if ( scalar(@$configz2create) && $tropts->{'config_group'} ) { |
165
|
|
|
|
|
|
|
my $cgroup = $tr->getConfigurationGroupByName( $tropts->{project_id}, |
166
|
3
|
|
|
|
|
34
|
$tropts->{'config_group'} ); |
167
|
3
|
50
|
|
|
|
13
|
unless ( ref($cgroup) eq 'HASH' ) { |
168
|
3
|
|
|
|
|
144
|
print "# Adding Configuration Group $tropts->{config_group}...\n"; |
169
|
|
|
|
|
|
|
$cgroup = $tr->addConfigurationGroup( $tropts->{project_id}, |
170
|
3
|
|
|
|
|
63
|
$tropts->{'config_group'} ); |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
confess( |
173
|
3
|
50
|
|
|
|
106
|
"Could neither find nor create the provided configuration group '$tropts->{config_group}'" |
174
|
|
|
|
|
|
|
) unless ref($cgroup) eq 'HASH'; |
175
|
3
|
|
|
|
|
12
|
foreach my $cc (@$configz2create) { |
176
|
3
|
|
|
|
|
112
|
print "# Adding Configuration $cc->{name}...\n"; |
177
|
3
|
|
|
|
|
27
|
$tr->addConfiguration( $cgroup->{'id'}, $cc->{'name'} ); |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
#check if configs passed are defined for project. If we can't get all the IDs, something's hinky |
182
|
|
|
|
|
|
|
@$config_ids = $tr->translateConfigNamesToIds( $tropts->{'project_id'}, |
183
|
55
|
|
|
|
|
286
|
@{ $tropts->{'configs'} } ); |
|
55
|
|
|
|
|
287
|
|
184
|
55
|
50
|
50
|
|
|
465
|
confess("Could not retrieve list of valid configurations for your project.") |
185
|
|
|
|
|
|
|
unless ( reftype($config_ids) || 'undef' ) eq 'ARRAY'; |
186
|
55
|
|
|
|
|
158
|
my @bogus_configs = grep { !defined($_) } @$config_ids; |
|
16
|
|
|
|
|
64
|
|
187
|
55
|
|
|
|
|
105
|
my $num_bogus = scalar(@bogus_configs); |
188
|
55
|
50
|
|
|
|
332
|
confess( |
189
|
|
|
|
|
|
|
"Detected $num_bogus bad config names passed. Check available configurations for your project." |
190
|
|
|
|
|
|
|
) if $num_bogus; |
191
|
|
|
|
|
|
|
|
192
|
55
|
100
|
|
|
|
302
|
if ( $tropts->{'plan'} ) { |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
#Attempt to find run, filtered by configurations |
195
|
21
|
50
|
|
|
|
79
|
if ( $tropts->{'plan_id'} ) { |
196
|
0
|
|
|
|
|
0
|
$plan = $tr->getPlanByID( $tropts->{'plan_id'} ); |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
else { |
199
|
|
|
|
|
|
|
$plan = |
200
|
21
|
|
|
|
|
182
|
$tr->getPlanByName( $tropts->{'project_id'}, $tropts->{'plan'} ); |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
confess( |
203
|
|
|
|
|
|
|
"Test plan provided is completed, and spawning was not indicated") |
204
|
|
|
|
|
|
|
if ( ref $plan eq 'HASH' ) |
205
|
|
|
|
|
|
|
&& $plan->{'is_completed'} |
206
|
21
|
100
|
100
|
|
|
1487
|
&& ( !$tropts->{'testsuite_id'} ); |
|
|
|
100
|
|
|
|
|
207
|
20
|
100
|
100
|
|
|
338
|
if ( $plan && !$plan->{'is_completed'} ) { |
208
|
12
|
|
|
|
|
104
|
$tropts->{'plan'} = $plan; |
209
|
|
|
|
|
|
|
$run = $tr->getChildRunByName( $plan, $tropts->{'run'}, |
210
|
12
|
|
|
|
|
134
|
$tropts->{'configs'} ); #Find plan filtered by configs |
211
|
|
|
|
|
|
|
|
212
|
12
|
100
|
100
|
|
|
132
|
if ( defined($run) && ( reftype($run) || 'undef' ) eq 'HASH' ) { |
|
|
|
66
|
|
|
|
|
213
|
10
|
|
|
|
|
31
|
$tropts->{'run'} = $run; |
214
|
10
|
|
|
|
|
28
|
$tropts->{'run_id'} = $run->{'id'}; |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
else { |
218
|
|
|
|
|
|
|
#Try to make it if spawn is passed |
219
|
|
|
|
|
|
|
$tropts->{'plan'} = $tr->createPlan( $tropts->{'project_id'}, |
220
|
|
|
|
|
|
|
$tropts->{'plan'}, "Test plan created by TestRail::API" ) |
221
|
8
|
50
|
|
|
|
95
|
if $tropts->{'testsuite_id'}; |
222
|
|
|
|
|
|
|
confess("Could not find plan " |
223
|
|
|
|
|
|
|
. $tropts->{'plan'} |
224
|
|
|
|
|
|
|
. " in provided project, and spawning failed (or was not indicated)!" |
225
|
8
|
50
|
|
|
|
576
|
) if !$tropts->{'plan'}; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
else { |
229
|
34
|
|
|
|
|
331
|
$run = $tr->getRunByName( $tropts->{'project_id'}, $tropts->{'run'} ); |
230
|
|
|
|
|
|
|
confess( |
231
|
|
|
|
|
|
|
"Test run provided is completed, and spawning was not indicated") |
232
|
|
|
|
|
|
|
if ( ref $run eq 'HASH' ) |
233
|
|
|
|
|
|
|
&& $run->{'is_completed'} |
234
|
34
|
100
|
100
|
|
|
375
|
&& ( !$tropts->{'testsuite_id'} ); |
|
|
|
100
|
|
|
|
|
235
|
33
|
100
|
100
|
|
|
595
|
if ( defined($run) |
|
|
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
236
|
|
|
|
|
|
|
&& ( reftype($run) || 'undef' ) eq 'HASH' |
237
|
|
|
|
|
|
|
&& !$run->{'is_completed'} ) |
238
|
|
|
|
|
|
|
{ |
239
|
12
|
|
|
|
|
76
|
$tropts->{'run'} = $run; |
240
|
12
|
|
|
|
|
35
|
$tropts->{'run_id'} = $run->{'id'}; |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
#If spawn was passed and we don't have a Run ID yet, go ahead and make it |
245
|
53
|
100
|
100
|
|
|
497
|
if ( $tropts->{'testsuite_id'} && !$tropts->{'run_id'} ) { |
246
|
30
|
|
|
|
|
1833
|
print "# Spawning run\n"; |
247
|
30
|
|
|
|
|
164
|
my $cases = []; |
248
|
30
|
100
|
|
|
|
160
|
if ( $tropts->{'sections'} ) { |
249
|
9
|
|
|
|
|
95
|
print "# with specified sections\n"; |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
#Then translate the sections into an array of case IDs. |
252
|
|
|
|
|
|
|
confess("Sections passed to spawn must be ARRAYREF") |
253
|
9
|
50
|
50
|
|
|
154
|
unless ( reftype( $tropts->{'sections'} ) || 'undef' ) eq 'ARRAY'; |
254
|
8
|
|
|
|
|
34
|
@{ $tropts->{'sections'} } = $tr->sectionNamesToIds( |
255
|
|
|
|
|
|
|
$tropts->{'project_id'}, |
256
|
|
|
|
|
|
|
$tropts->{'testsuite_id'}, |
257
|
9
|
|
|
|
|
44
|
@{ $tropts->{'sections'} } |
|
9
|
|
|
|
|
111
|
|
258
|
|
|
|
|
|
|
); |
259
|
8
|
|
|
|
|
20
|
foreach my $section ( @{ $tropts->{'sections'} } ) { |
|
8
|
|
|
|
|
25
|
|
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
#Get the child sections, and append them to our section list so we get their cases too. |
262
|
|
|
|
|
|
|
my $append_sections = $tr->getChildSections( |
263
|
|
|
|
|
|
|
$tropts->{'project_id'}, |
264
|
|
|
|
|
|
|
{ |
265
|
|
|
|
|
|
|
'id' => $section, |
266
|
9
|
|
|
|
|
75
|
'suite_id' => $tropts->{'testsuite_id'} |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
); |
269
|
|
|
|
|
|
|
@$append_sections = grep { |
270
|
9
|
|
|
|
|
32
|
my $sc = $_; |
|
6
|
|
|
|
|
8
|
|
271
|
18
|
|
|
|
|
32
|
!scalar( grep { $_ == $sc->{'id'} } |
272
|
6
|
|
|
|
|
7
|
@{ $tropts->{'sections'} } ) |
|
6
|
|
|
|
|
9
|
|
273
|
|
|
|
|
|
|
} @$append_sections |
274
|
|
|
|
|
|
|
; #de-dup in case the user added children to the list |
275
|
9
|
|
|
|
|
22
|
@$append_sections = map { $_->{'id'} } @$append_sections; |
|
2
|
|
|
|
|
5
|
|
276
|
9
|
|
|
|
|
15
|
push( @{ $tropts->{'sections'} }, @$append_sections ); |
|
9
|
|
|
|
|
26
|
|
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
my $section_cases = $tr->getCases( |
279
|
|
|
|
|
|
|
$tropts->{'project_id'}, |
280
|
9
|
|
|
|
|
68
|
$tropts->{'testsuite_id'}, |
281
|
|
|
|
|
|
|
{ 'section_id' => $section } |
282
|
|
|
|
|
|
|
); |
283
|
9
|
50
|
50
|
|
|
75
|
push( @$cases, @$section_cases ) |
284
|
|
|
|
|
|
|
if ( reftype($section_cases) || 'undef' ) eq 'ARRAY'; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
29
|
100
|
|
|
|
133
|
if ( scalar(@$cases) ) { |
289
|
5
|
|
|
|
|
16
|
@$cases = map { $_->{'id'} } @$cases; |
|
11
|
|
|
|
|
50
|
|
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
else { |
292
|
24
|
|
|
|
|
67
|
$cases = undef; |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
|
295
|
29
|
100
|
|
|
|
112
|
if ( $tropts->{'plan'} ) { |
296
|
9
|
|
|
|
|
222
|
print "# inside of plan\n"; |
297
|
|
|
|
|
|
|
$plan = $tr->createRunInPlan( |
298
|
|
|
|
|
|
|
$tropts->{'plan'}->{'id'}, |
299
|
|
|
|
|
|
|
$tropts->{'testsuite_id'}, |
300
|
9
|
|
|
|
|
128
|
$tropts->{'run'}, undef, $config_ids, $cases |
301
|
|
|
|
|
|
|
); |
302
|
|
|
|
|
|
|
$run = $plan->{'runs'}->[0] |
303
|
|
|
|
|
|
|
if exists( $plan->{'runs'} ) |
304
|
|
|
|
|
|
|
&& ( reftype( $plan->{'runs'} ) || 'undef' ) eq 'ARRAY' |
305
|
9
|
50
|
50
|
|
|
705
|
&& scalar( @{ $plan->{'runs'} } ); |
|
9
|
|
33
|
|
|
65
|
|
|
|
|
50
|
|
|
|
|
306
|
9
|
50
|
50
|
|
|
90
|
if ( defined($run) && ( reftype($run) || 'undef' ) eq 'HASH' ) { |
|
|
|
33
|
|
|
|
|
307
|
9
|
|
|
|
|
32
|
$tropts->{'run'} = $run; |
308
|
9
|
|
|
|
|
26
|
$tropts->{'run_id'} = $run->{'id'}; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
else { |
312
|
|
|
|
|
|
|
$run = $tr->createRun( |
313
|
|
|
|
|
|
|
$tropts->{'project_id'}, |
314
|
|
|
|
|
|
|
$tropts->{'testsuite_id'}, |
315
|
20
|
|
|
|
|
280
|
$tropts->{'run'}, |
316
|
|
|
|
|
|
|
"Automatically created Run from TestRail::API", |
317
|
|
|
|
|
|
|
undef, |
318
|
|
|
|
|
|
|
undef, |
319
|
|
|
|
|
|
|
$cases |
320
|
|
|
|
|
|
|
); |
321
|
20
|
50
|
50
|
|
|
1708
|
if ( defined($run) && ( reftype($run) || 'undef' ) eq 'HASH' ) { |
|
|
|
33
|
|
|
|
|
322
|
20
|
|
|
|
|
89
|
$tropts->{'run'} = $run; |
323
|
20
|
|
|
|
|
74
|
$tropts->{'run_id'} = $run->{'id'}; |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
confess("Could not spawn run with requested parameters!") |
327
|
29
|
50
|
|
|
|
124
|
if !$tropts->{'run_id'}; |
328
|
29
|
|
|
|
|
1443
|
print "# Success!\n"; |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
confess( |
331
|
|
|
|
|
|
|
"No run ID provided, and no run with specified name exists in provided project/plan!" |
332
|
52
|
100
|
|
|
|
679
|
) if !$tropts->{'run_id'}; |
333
|
|
|
|
|
|
|
|
334
|
51
|
|
|
|
|
531
|
my $self = $class->SUPER::new($opts); |
335
|
51
|
100
|
66
|
|
|
358288
|
if ( defined( $self->{'_iterator'}->{'command'} ) |
336
|
|
|
|
|
|
|
&& reftype( $self->{'_iterator'}->{'command'} ) eq 'ARRAY' ) |
337
|
|
|
|
|
|
|
{ |
338
|
33
|
|
|
|
|
217
|
$self->{'file'} = $self->{'_iterator'}->{'command'}->[-1]; |
339
|
33
|
|
|
|
|
1860
|
print "# PROCESSING RESULTS FROM TEST FILE: $self->{'file'}\n"; |
340
|
33
|
|
|
|
|
330
|
$self->{'track_time'} = 1; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
else { |
343
|
|
|
|
|
|
|
#Not running inside of prove in real-time, don't bother with tracking elapsed times. |
344
|
18
|
|
|
|
|
51
|
$self->{'track_time'} = 0; |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
#Make sure the step results field passed exists on the system |
348
|
51
|
|
|
|
|
211
|
my $sr_name = $tropts->{'step_results'}; |
349
|
|
|
|
|
|
|
$tropts->{'step_results'} = |
350
|
|
|
|
|
|
|
$tr->getTestResultFieldByName( $tropts->{'step_results'}, |
351
|
|
|
|
|
|
|
$tropts->{'project_id'} ) |
352
|
51
|
100
|
|
|
|
383
|
if defined $tropts->{'step_results'}; |
353
|
|
|
|
|
|
|
confess( |
354
|
|
|
|
|
|
|
"Invalid step results value '$sr_name' passed. Check the spelling and confirm that your project can use the '$sr_name' custom result field." |
355
|
51
|
100
|
100
|
|
|
1826
|
) if ref $tropts->{'step_results'} ne 'HASH' && $sr_name; |
356
|
|
|
|
|
|
|
|
357
|
50
|
|
|
|
|
299
|
$self->{'tr_opts'} = $tropts; |
358
|
50
|
|
|
|
|
276
|
$self->{'errors'} = 0; |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
#Start the shot clock |
361
|
50
|
|
|
|
|
229
|
$self->{'starttime'} = time(); |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
#Make sure we get the time it took to get to each step from the last correctly |
364
|
50
|
|
|
|
|
137
|
$self->{'lasttime'} = $self->{'starttime'}; |
365
|
50
|
|
|
|
|
250
|
$self->{'raw_output'} = ""; |
366
|
|
|
|
|
|
|
|
367
|
50
|
|
|
|
|
15641
|
return $self; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
# Look for file boundaries, etc. |
371
|
|
|
|
|
|
|
sub unknownCallback { |
372
|
87
|
|
|
87
|
1
|
3165
|
my ($test) = @_; |
373
|
87
|
|
|
|
|
171
|
my $self = $test->{'parser'}; |
374
|
87
|
|
|
|
|
249
|
my $line = $test->as_string; |
375
|
87
|
|
|
|
|
409
|
$self->{'raw_output'} .= "$line\n"; |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
#Unofficial "Extensions" to TAP |
378
|
87
|
|
|
|
|
194
|
my ($status_override) = $line =~ m/^% mark_status=([a-z|_]*)/; |
379
|
87
|
100
|
|
|
|
211
|
if ($status_override) { |
380
|
|
|
|
|
|
|
cluck "Unknown status override" |
381
|
1
|
50
|
|
|
|
6
|
unless defined $self->{'tr_opts'}->{$status_override}->{'id'}; |
382
|
|
|
|
|
|
|
$self->{'global_status'} = |
383
|
|
|
|
|
|
|
$self->{'tr_opts'}->{$status_override}->{'id'} |
384
|
1
|
50
|
|
|
|
6
|
if $self->{'tr_opts'}->{$status_override}; |
385
|
|
|
|
|
|
|
print "# Overriding status to $status_override (" |
386
|
|
|
|
|
|
|
. $self->{'global_status'} |
387
|
|
|
|
|
|
|
. ")...\n" |
388
|
1
|
50
|
|
|
|
36
|
if $self->{'global_status'}; |
389
|
|
|
|
|
|
|
} |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
#XXX I'd love to just rely on the 'name' attr in App::Prove::State::Result::Test, but... |
392
|
|
|
|
|
|
|
#try to pick out the filename if we are running this on TAP in files, where App::Prove is uninvolved |
393
|
87
|
|
|
|
|
466
|
my $file = TestRail::Utils::getFilenameFromTapLine($line); |
394
|
87
|
100
|
100
|
|
|
381
|
$self->{'file'} = $file if !$self->{'file'} && $file; |
395
|
87
|
|
|
|
|
183
|
return; |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
# Register the current suite or test desc for use by test callback, if the line begins with the special magic words |
399
|
|
|
|
|
|
|
sub commentCallback { |
400
|
77
|
|
|
77
|
1
|
1959
|
my ($test) = @_; |
401
|
77
|
|
|
|
|
141
|
my $self = $test->{'parser'}; |
402
|
77
|
|
|
|
|
302
|
my $line = $test->as_string; |
403
|
77
|
|
|
|
|
484
|
$self->{'raw_output'} .= "$line\n"; |
404
|
|
|
|
|
|
|
|
405
|
77
|
50
|
|
|
|
287
|
if ( $line =~ m/^#TESTDESC:\s*/ ) { |
406
|
0
|
|
|
|
|
0
|
$self->{'tr_opts'}->{'test_desc'} = $line; |
407
|
0
|
|
|
|
|
0
|
$self->{'tr_opts'}->{'test_desc'} =~ s/^#TESTDESC:\s*//g; |
408
|
|
|
|
|
|
|
} |
409
|
77
|
|
|
|
|
179
|
return; |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
sub testCallback { |
413
|
69
|
|
|
69
|
1
|
9264
|
my ($test) = @_; |
414
|
69
|
|
|
|
|
230
|
my $self = $test->{'parser'}; |
415
|
|
|
|
|
|
|
|
416
|
69
|
100
|
|
|
|
425
|
if ( $self->{'track_time'} ) { |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
#Test done. Record elapsed time. |
419
|
45
|
|
|
|
|
128
|
my $tm = time(); |
420
|
|
|
|
|
|
|
$self->{'tr_opts'}->{'result_options'}->{'elapsed'} = |
421
|
45
|
|
|
|
|
207
|
_compute_elapsed( $self->{'lasttime'}, $tm ); |
422
|
|
|
|
|
|
|
$self->{'elapse_display'} = |
423
|
|
|
|
|
|
|
defined( $self->{'tr_opts'}->{'result_options'}->{'elapsed'} ) |
424
|
45
|
100
|
|
|
|
390
|
? $self->{'tr_opts'}->{'result_options'}->{'elapsed'} |
425
|
|
|
|
|
|
|
: "0s"; |
426
|
45
|
|
|
|
|
156
|
$self->{'lasttime'} = $tm; |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
69
|
|
|
|
|
249
|
my $line = $test->as_string; |
430
|
69
|
|
|
|
|
1724
|
my $tline = $line; |
431
|
|
|
|
|
|
|
$tline = "[" |
432
|
|
|
|
|
|
|
. strftime( "%H:%M:%S %b %e %Y", localtime( $self->{'lasttime'} ) ) |
433
|
|
|
|
|
|
|
. " ($self->{elapse_display})] $line" |
434
|
69
|
100
|
|
|
|
3589
|
if $self->{'track_time'}; |
435
|
69
|
|
|
|
|
445
|
$self->{'raw_output'} .= "$tline\n"; |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
#Don't do anything if we don't want to map TR case => ok or use step-by-step results |
438
|
69
|
100
|
|
|
|
336
|
if ( !$self->{'tr_opts'}->{'step_results'} ) { |
439
|
|
|
|
|
|
|
print |
440
|
|
|
|
|
|
|
"# step_results not set. No action to be taken, except on a whole test basis.\n" |
441
|
57
|
50
|
|
|
|
1913
|
if $self->{'tr_opts'}->{'debug'}; |
442
|
57
|
|
|
|
|
325
|
return 1; |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
|
445
|
12
|
|
|
|
|
145
|
$line =~ s/^(ok|not ok)\s[0-9]*\s-\s//g; |
446
|
12
|
|
|
|
|
44
|
my $test_name = $line; |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
print "# Assuming test name is '$test_name'...\n" |
449
|
12
|
50
|
33
|
|
|
135
|
if $self->{'tr_opts'}->{'debug'} && !$self->{'tr_opts'}->{'step_results'}; |
450
|
|
|
|
|
|
|
|
451
|
12
|
|
|
|
|
26
|
my $todo_reason; |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
#Setup args to pass to function |
454
|
12
|
|
|
|
|
45
|
my $status = $self->{'tr_opts'}->{'not_ok'}->{'id'}; |
455
|
12
|
|
|
|
|
53
|
my $status_name = 'NOT OK'; |
456
|
12
|
100
|
|
|
|
60
|
if ( $test->is_actual_ok() ) { |
457
|
9
|
|
|
|
|
90
|
$status = $self->{'tr_opts'}->{'ok'}->{'id'}; |
458
|
9
|
|
|
|
|
31
|
$status_name = 'OK'; |
459
|
9
|
100
|
|
|
|
35
|
if ( $test->has_skip() ) { |
460
|
1
|
|
|
|
|
7
|
$status = $self->{'tr_opts'}->{'skip'}->{'id'}; |
461
|
1
|
|
|
|
|
6
|
$status_name = 'SKIP'; |
462
|
1
|
|
|
|
|
9
|
$test_name =~ s/^(ok|not ok)\s[0-9]*\s//g; |
463
|
1
|
|
|
|
|
4
|
$test_name =~ s/^# skip //gi; |
464
|
1
|
|
|
|
|
32
|
print "# '$test_name'\n"; |
465
|
|
|
|
|
|
|
} |
466
|
9
|
100
|
|
|
|
66
|
if ( $test->has_todo() ) { |
467
|
4
|
|
|
|
|
40
|
$status = $self->{'tr_opts'}->{'todo_pass'}->{'id'}; |
468
|
4
|
|
|
|
|
25
|
$status_name = 'TODO PASS'; |
469
|
4
|
|
|
|
|
30
|
$test_name =~ s/^(ok|not ok)\s[0-9]*\s//g; |
470
|
4
|
|
|
|
|
15
|
$test_name =~ s/^# todo & skip //gi; #handle todo_skip |
471
|
4
|
|
|
|
|
35
|
$test_name =~ s/# todo\s(.*)$//gi; |
472
|
4
|
|
|
|
|
21
|
$todo_reason = $test->explanation(); |
473
|
|
|
|
|
|
|
} |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
else { |
476
|
3
|
100
|
|
|
|
33
|
if ( $test->has_todo() ) { |
477
|
1
|
|
|
|
|
12
|
$status = $self->{'tr_opts'}->{'todo_fail'}->{'id'}; |
478
|
1
|
|
|
|
|
8
|
$status_name = 'TODO FAIL'; |
479
|
1
|
|
|
|
|
12
|
$test_name =~ s/^(ok|not ok)\s[0-9]*\s//g; |
480
|
1
|
|
|
|
|
10
|
$test_name =~ s/^# todo & skip //gi; #handle todo_skip |
481
|
1
|
|
|
|
|
14
|
$test_name =~ s/# todo\s(.*)$//gi; |
482
|
1
|
|
|
|
|
10
|
$todo_reason = $test->explanation(); |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
} |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
#XXX much of the above code would be unneeded if $test->description wasn't garbage |
487
|
12
|
|
|
|
|
150
|
$test_name =~ s/\s+$//g; |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
#If this is a TODO, set the reason in the notes |
490
|
12
|
100
|
|
|
|
68
|
$self->{'tr_opts'}->{'test_notes'} .= "\nTODO reason: $todo_reason\n" |
491
|
|
|
|
|
|
|
if $todo_reason; |
492
|
|
|
|
|
|
|
|
493
|
12
|
|
|
|
|
49
|
my $sr_sys_name = $self->{'tr_opts'}->{'step_results'}->{'name'}; |
494
|
|
|
|
|
|
|
$self->{'tr_opts'}->{'result_custom_options'} = {} |
495
|
12
|
100
|
|
|
|
57
|
if !defined $self->{'tr_opts'}->{'result_custom_options'}; |
496
|
|
|
|
|
|
|
$self->{'tr_opts'}->{'result_custom_options'}->{$sr_sys_name} = [] |
497
|
12
|
100
|
|
|
|
71
|
if !defined $self->{'tr_opts'}->{'result_custom_options'}->{$sr_sys_name}; |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
#TimeStamp every particular step |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
$line = "[" |
502
|
|
|
|
|
|
|
. strftime( "%H:%M:%S %b %e %Y", localtime( $self->{'lasttime'} ) ) |
503
|
|
|
|
|
|
|
. " ($self->{elapse_display})] $line" |
504
|
12
|
100
|
|
|
|
509
|
if $self->{'track_time'}; |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
#XXX Obviously getting the 'expected' and 'actual' from the tap DIAGs would be ideal |
507
|
|
|
|
|
|
|
push( |
508
|
12
|
|
|
|
|
42
|
@{ $self->{'tr_opts'}->{'result_custom_options'}->{$sr_sys_name} }, |
|
12
|
|
|
|
|
113
|
|
509
|
|
|
|
|
|
|
TestRail::API::buildStepResults( $line, "OK", $status_name, $status ) |
510
|
|
|
|
|
|
|
); |
511
|
12
|
50
|
|
|
|
3692
|
print "# Appended step results.\n" if $self->{'tr_opts'}->{'debug'}; |
512
|
12
|
|
|
|
|
72
|
return 1; |
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
sub bailoutCallback { |
516
|
1
|
|
|
1
|
1
|
37
|
my ($test) = @_; |
517
|
1
|
|
|
|
|
5
|
my $self = $test->{'parser'}; |
518
|
1
|
|
|
|
|
13
|
my $line = $test->as_string; |
519
|
1
|
|
|
|
|
11
|
$self->{'raw_output'} .= "$line\n"; |
520
|
|
|
|
|
|
|
|
521
|
1
|
50
|
|
|
|
9
|
if ( $self->{'tr_opts'}->{'step_results'} ) { |
522
|
1
|
|
|
|
|
4
|
my $sr_sys_name = $self->{'tr_opts'}->{'step_results'}->{'name'}; |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
#Handle the case where we die right off |
525
|
1
|
|
50
|
|
|
4
|
$self->{'tr_opts'}->{'result_custom_options'}->{$sr_sys_name} //= []; |
526
|
|
|
|
|
|
|
push( |
527
|
1
|
|
|
|
|
7
|
@{ $self->{'tr_opts'}->{'result_custom_options'}->{$sr_sys_name} }, |
528
|
|
|
|
|
|
|
TestRail::API::buildStepResults( |
529
|
|
|
|
|
|
|
"Bail Out!.", "Continued testing", |
530
|
1
|
|
|
|
|
2
|
$test->explanation, $self->{'tr_opts'}->{'not_ok'}->{'id'} |
531
|
|
|
|
|
|
|
) |
532
|
|
|
|
|
|
|
); |
533
|
|
|
|
|
|
|
} |
534
|
1
|
|
|
|
|
7
|
$self->{'is_bailout'} = 1; |
535
|
1
|
|
|
|
|
4
|
return; |
536
|
|
|
|
|
|
|
} |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
sub EOFCallback { |
539
|
45
|
|
|
45
|
1
|
1084102
|
my ($self) = @_; |
540
|
|
|
|
|
|
|
|
541
|
45
|
100
|
|
|
|
305
|
if ( $self->{'track_time'} ) { |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
#Test done. Record elapsed time. |
544
|
|
|
|
|
|
|
$self->{'tr_opts'}->{'result_options'}->{'elapsed'} = |
545
|
29
|
|
|
|
|
213
|
_compute_elapsed( $self->{'starttime'}, time() ); |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
#Fail if the file is not set |
549
|
45
|
50
|
|
|
|
241
|
if ( !defined( $self->{'file'} ) ) { |
550
|
0
|
|
|
|
|
0
|
cluck( |
551
|
|
|
|
|
|
|
"ERROR: Cannot detect filename, will not be able to find a Test Case with that name" |
552
|
|
|
|
|
|
|
); |
553
|
0
|
|
|
|
|
0
|
$self->{'errors'}++; |
554
|
0
|
|
|
|
|
0
|
return 0; |
555
|
|
|
|
|
|
|
} |
556
|
|
|
|
|
|
|
|
557
|
45
|
|
|
|
|
144
|
my $run_id = $self->{'tr_opts'}->{'run_id'}; |
558
|
45
|
|
|
|
|
4413
|
my $test_name = basename( $self->{'file'} ); |
559
|
|
|
|
|
|
|
|
560
|
45
|
|
|
|
|
236
|
my $status = $self->{'tr_opts'}->{'ok'}->{'id'}; |
561
|
45
|
|
|
|
|
270
|
my $todo_failed = $self->todo() - $self->todo_passed(); |
562
|
45
|
100
|
|
|
|
713
|
$status = $self->{'tr_opts'}->{'not_ok'}->{'id'} if $self->has_problems(); |
563
|
45
|
100
|
100
|
|
|
974
|
if ( !$self->tests_run() |
|
|
|
100
|
|
|
|
|
564
|
|
|
|
|
|
|
&& !$self->is_good_plan() |
565
|
|
|
|
|
|
|
&& $self->{'tr_opts'}->{test_bad_status} ) |
566
|
|
|
|
|
|
|
{ #No tests were run, no plan, code is probably bad so allow custom marking |
567
|
|
|
|
|
|
|
$status = |
568
|
1
|
|
|
|
|
23
|
$self->{'tr_opts'}->{ $self->{'tr_opts'}->{test_bad_status} }->{'id'}; |
569
|
|
|
|
|
|
|
} |
570
|
45
|
100
|
100
|
|
|
722
|
$status = $self->{'tr_opts'}->{'todo_pass'}->{'id'} |
|
|
|
100
|
|
|
|
|
571
|
|
|
|
|
|
|
if $self->todo_passed() |
572
|
|
|
|
|
|
|
&& !$self->failed() |
573
|
|
|
|
|
|
|
&& $self->is_good_plan(); #If no fails, but a TODO pass, mark as TODOP |
574
|
45
|
50
|
66
|
|
|
679
|
$status = $self->{'tr_opts'}->{'todo_fail'}->{'id'} |
|
|
|
66
|
|
|
|
|
575
|
|
|
|
|
|
|
if $todo_failed |
576
|
|
|
|
|
|
|
&& !$self->failed() |
577
|
|
|
|
|
|
|
&& $self->is_good_plan() |
578
|
|
|
|
|
|
|
; #If no fails, but a TODO fail, prefer TODOF to TODOP |
579
|
45
|
100
|
|
|
|
273
|
$status = $self->{'tr_opts'}->{'skip'}->{'id'} |
580
|
|
|
|
|
|
|
if $self->skip_all(); #Skip all, whee |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
#Global status override |
583
|
45
|
100
|
|
|
|
424
|
$status = $self->{'global_status'} if $self->{'global_status'}; |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
#Notify user about bad plan a bit better, supposing we haven't bailed |
586
|
45
|
100
|
100
|
|
|
143
|
if ( !$self->is_good_plan() |
|
|
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
587
|
|
|
|
|
|
|
&& !$self->{'is_bailout'} |
588
|
|
|
|
|
|
|
&& defined $self->tests_run |
589
|
|
|
|
|
|
|
&& defined $self->tests_planned ) |
590
|
|
|
|
|
|
|
{ |
591
|
6
|
|
|
|
|
232
|
$self->{'raw_output'} .= |
592
|
|
|
|
|
|
|
"\n# ERROR: Bad plan. You ran " |
593
|
|
|
|
|
|
|
. $self->tests_run |
594
|
|
|
|
|
|
|
. " tests, but planned " |
595
|
|
|
|
|
|
|
. $self->tests_planned . "."; |
596
|
6
|
100
|
|
|
|
98
|
if ( $self->{'tr_opts'}->{'step_results'} ) { |
597
|
3
|
|
|
|
|
13
|
my $sr_sys_name = $self->{'tr_opts'}->{'step_results'}->{'name'}; |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
#Handle the case where we die right off |
600
|
3
|
|
100
|
|
|
29
|
$self->{'tr_opts'}->{'result_custom_options'}->{$sr_sys_name} //= |
601
|
|
|
|
|
|
|
[]; |
602
|
|
|
|
|
|
|
push( |
603
|
|
|
|
|
|
|
@{ |
604
|
3
|
|
|
|
|
8
|
$self->{'tr_opts'}->{'result_custom_options'} |
605
|
3
|
|
|
|
|
14
|
->{$sr_sys_name} |
606
|
|
|
|
|
|
|
}, |
607
|
|
|
|
|
|
|
TestRail::API::buildStepResults( |
608
|
|
|
|
|
|
|
"Bad Plan.", |
609
|
|
|
|
|
|
|
$self->tests_planned . " Tests", |
610
|
|
|
|
|
|
|
$self->tests_run . " Tests", |
611
|
|
|
|
|
|
|
$status |
612
|
|
|
|
|
|
|
) |
613
|
|
|
|
|
|
|
); |
614
|
|
|
|
|
|
|
} |
615
|
|
|
|
|
|
|
} |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
#Optional args |
618
|
45
|
|
|
|
|
472
|
my $notes = $self->{'raw_output'}; |
619
|
45
|
|
|
|
|
142
|
my $options = $self->{'tr_opts'}->{'result_options'}; |
620
|
45
|
|
|
|
|
131
|
my $custom_options = $self->{'tr_opts'}->{'result_custom_options'}; |
621
|
|
|
|
|
|
|
|
622
|
45
|
|
|
|
|
1537
|
print "# Setting results...\n"; |
623
|
45
|
|
|
|
|
398
|
my $cres = |
624
|
|
|
|
|
|
|
$self->_set_result( $run_id, $test_name, $status, $notes, $options, |
625
|
|
|
|
|
|
|
$custom_options ); |
626
|
45
|
|
|
|
|
312
|
$self->_test_closure(); |
627
|
45
|
|
|
|
|
681
|
$self->{'global_status'} = $status; |
628
|
|
|
|
|
|
|
|
629
|
45
|
50
|
|
|
|
223
|
undef $self->{'tr_opts'} unless $self->{'tr_opts'}->{'debug'}; |
630
|
|
|
|
|
|
|
|
631
|
45
|
|
|
|
|
428
|
return $cres; |
632
|
|
|
|
|
|
|
} |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
sub planCallback { |
635
|
43
|
|
|
43
|
1
|
5322
|
my ($plan) = @_; |
636
|
43
|
|
|
|
|
146
|
my $self = $plan->{'parser'}; |
637
|
43
|
50
|
|
|
|
222
|
$self->{raw_output} .= $plan->as_string if $plan->as_string; |
638
|
|
|
|
|
|
|
} |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
sub _set_result { |
641
|
45
|
|
|
45
|
|
270
|
my ( $self, $run_id, $test_name, $status, $notes, $options, |
642
|
|
|
|
|
|
|
$custom_options ) |
643
|
|
|
|
|
|
|
= @_; |
644
|
45
|
|
|
|
|
91
|
my $tc; |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
print "# Test elapsed: " . $options->{'elapsed'} . "\n" |
647
|
45
|
100
|
|
|
|
326
|
if $options->{'elapsed'}; |
648
|
|
|
|
|
|
|
|
649
|
45
|
|
|
|
|
578
|
print "# Attempting to find case by title '" |
650
|
|
|
|
|
|
|
. $test_name |
651
|
|
|
|
|
|
|
. "' in run $run_id...\n"; |
652
|
|
|
|
|
|
|
$tc = |
653
|
45
|
|
|
|
|
738
|
$self->{'tr_opts'}->{'testrail'}->getTestByName( $run_id, $test_name ); |
654
|
45
|
100
|
100
|
|
|
598
|
if ( !defined($tc) || ( reftype($tc) || 'undef' ) ne 'HASH' ) { |
|
|
|
66
|
|
|
|
|
655
|
1
|
|
|
|
|
371
|
cluck("ERROR: Could not find test case: $tc"); |
656
|
1
|
|
|
|
|
667
|
$self->{'errors'}++; |
657
|
1
|
|
|
|
|
8
|
return 0; |
658
|
|
|
|
|
|
|
} |
659
|
|
|
|
|
|
|
|
660
|
44
|
50
|
|
|
|
202
|
my $xid = $tc ? $tc->{'id'} : '???'; |
661
|
|
|
|
|
|
|
|
662
|
44
|
|
|
|
|
82
|
my $cres; |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
#Set test result |
665
|
44
|
50
|
|
|
|
167
|
if ($tc) { |
666
|
44
|
|
|
|
|
2036
|
print |
667
|
|
|
|
|
|
|
"# Reporting result of case $xid in run $self->{'tr_opts'}->{'run_id'} as status '$status'..."; |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
# createTestResults(test_id,status_id,comment,options,custom_options) |
670
|
|
|
|
|
|
|
$cres = |
671
|
|
|
|
|
|
|
$self->{'tr_opts'}->{'testrail'} |
672
|
44
|
|
|
|
|
533
|
->createTestResults( $tc->{'id'}, $status, $notes, $options, |
673
|
|
|
|
|
|
|
$custom_options ); |
674
|
44
|
100
|
100
|
|
|
8547
|
print "# OK! (set to $status)\n" |
675
|
|
|
|
|
|
|
if ( reftype($cres) || 'undef' ) eq 'HASH'; |
676
|
|
|
|
|
|
|
} |
677
|
44
|
100
|
100
|
|
|
1255
|
if ( !$tc || ( ( reftype($cres) || 'undef' ) ne 'HASH' ) ) { |
|
|
|
66
|
|
|
|
|
678
|
1
|
|
|
|
|
16
|
print "# Failed!\n"; |
679
|
1
|
|
|
|
|
13
|
print "# No Such test case in TestRail ($xid).\n"; |
680
|
1
|
|
|
|
|
12
|
$self->{'errors'}++; |
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
} |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
#Compute the expected testrail date interval from 2 unix timestamps. |
686
|
|
|
|
|
|
|
sub _compute_elapsed { |
687
|
78
|
|
|
78
|
|
934
|
my ( $begin, $end ) = @_; |
688
|
78
|
|
|
|
|
176
|
my $secs_elapsed = $end - $begin; |
689
|
78
|
|
|
|
|
526
|
my $mins_elapsed = floor( $secs_elapsed / 60 ); |
690
|
78
|
|
|
|
|
255
|
my $secs_remain = $secs_elapsed % 60; |
691
|
78
|
|
|
|
|
241
|
my $hours_elapsed = floor( $mins_elapsed / 60 ); |
692
|
78
|
|
|
|
|
192
|
my $mins_remain = $mins_elapsed % 60; |
693
|
|
|
|
|
|
|
|
694
|
78
|
|
|
|
|
299
|
my $datestr = ""; |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
#You have bigger problems if your test takes days |
697
|
78
|
100
|
|
|
|
281
|
if ($hours_elapsed) { |
698
|
2
|
|
|
|
|
20
|
$datestr .= "$hours_elapsed" . "h $mins_remain" . "m"; |
699
|
|
|
|
|
|
|
} |
700
|
|
|
|
|
|
|
else { |
701
|
76
|
|
|
|
|
377
|
$datestr .= "$mins_elapsed" . "m"; |
702
|
|
|
|
|
|
|
} |
703
|
78
|
100
|
|
|
|
210
|
if ($mins_elapsed) { |
704
|
3
|
|
|
|
|
6
|
$datestr .= " $secs_remain" . "s"; |
705
|
|
|
|
|
|
|
} |
706
|
|
|
|
|
|
|
else { |
707
|
75
|
|
|
|
|
280
|
$datestr .= " $secs_elapsed" . "s"; |
708
|
|
|
|
|
|
|
} |
709
|
78
|
100
|
|
|
|
316
|
undef $datestr if $datestr eq "0m 0s"; |
710
|
78
|
|
|
|
|
521
|
return $datestr; |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
sub _test_closure { |
714
|
45
|
|
|
45
|
|
214
|
my ($self) = @_; |
715
|
45
|
100
|
|
|
|
297
|
return unless $self->{'tr_opts'}->{'autoclose'}; |
716
|
8
|
100
|
|
|
|
75
|
my $is_plan = $self->{'tr_opts'}->{'plan'} ? 1 : 0; |
717
|
|
|
|
|
|
|
my $id = |
718
|
|
|
|
|
|
|
$self->{'tr_opts'}->{'plan'} |
719
|
|
|
|
|
|
|
? $self->{'tr_opts'}->{'plan'}->{'id'} |
720
|
8
|
100
|
|
|
|
61
|
: $self->{'tr_opts'}->{'run'}; |
721
|
|
|
|
|
|
|
|
722
|
8
|
100
|
|
|
|
58
|
if ($is_plan) { |
723
|
|
|
|
|
|
|
my $plan_summary = |
724
|
4
|
|
|
|
|
54
|
$self->{'tr_opts'}->{'testrail'}->getPlanSummary($id); |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
return |
727
|
|
|
|
|
|
|
if ( $plan_summary->{'totals'}->{'Untested'} + |
728
|
4
|
100
|
|
|
|
28
|
$plan_summary->{'totals'}->{'Retest'} ); |
729
|
3
|
|
|
|
|
114
|
print "# No more outstanding cases detected. Closing Plan.\n"; |
730
|
3
|
|
|
|
|
28
|
$self->{'plan_closed'} = 1; |
731
|
3
|
|
|
|
|
31
|
return $self->{'tr_opts'}->{'testrail'}->closePlan($id); |
732
|
|
|
|
|
|
|
} |
733
|
|
|
|
|
|
|
|
734
|
4
|
|
|
|
|
62
|
my ($run_summary) = $self->{'tr_opts'}->{'testrail'}->getRunSummary($id); |
735
|
|
|
|
|
|
|
return |
736
|
|
|
|
|
|
|
if ( $run_summary->{'run_status'}->{'Untested'} + |
737
|
4
|
100
|
|
|
|
39
|
$run_summary->{'run_status'}->{'Retest'} ); |
738
|
1
|
|
|
|
|
53
|
print "# No more outstanding cases detected. Closing Run.\n"; |
739
|
1
|
|
|
|
|
10
|
$self->{'run_closed'} = 1; |
740
|
|
|
|
|
|
|
return $self->{'tr_opts'}->{'testrail'} |
741
|
1
|
|
|
|
|
17
|
->closeRun( $self->{'tr_opts'}->{'run_id'} ); |
742
|
|
|
|
|
|
|
} |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
sub make_result { |
745
|
277
|
|
|
277
|
1
|
26326182
|
my ( $self, @args ) = @_; |
746
|
277
|
|
|
|
|
1133
|
my $res = $self->SUPER::make_result(@args); |
747
|
277
|
|
|
|
|
13482
|
$res->{'parser'} = $self; |
748
|
277
|
|
|
|
|
1081
|
return $res; |
749
|
|
|
|
|
|
|
} |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
1; |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
__END__ |