| 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__ |