rspec-teamcity 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +674 -0
- data/README.md +30 -0
- data/Rakefile +2 -0
- data/lib/rspec/teamcity.rb +693 -0
- data/lib/rspec/teamcity/formatter_initializer.rb +50 -0
- data/lib/rspec/teamcity/rake_exceptions.rb +25 -0
- data/lib/rspec/teamcity/rakerunner_consts.rb +131 -0
- data/lib/rspec/teamcity/runner_common.rb +62 -0
- data/lib/rspec/teamcity/utils/logger_util.rb +90 -0
- data/lib/rspec/teamcity/utils/runner_utils.rb +182 -0
- data/lib/rspec/teamcity/utils/service_message_factory.rb +279 -0
- data/lib/rspec/teamcity/utils/std_capture_helper.rb +103 -0
- data/lib/rspec/teamcity/utils/string_ext.rb +28 -0
- data/lib/rspec/teamcity/utils/url_formatter.rb +35 -0
- data/lib/rspec/teamcity/version.rb +5 -0
- data/lib/rspec/teamcity_rspec3.rb +448 -0
- data/rspec-teamcity.gemspec +28 -0
- metadata +119 -0
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
rspec-teamcity
|
2
|
+
=======================
|
3
|
+
|
4
|
+
TeamCity rspec reporter
|
5
|
+
|
6
|
+
TeamCity has a built in rspec reporter, but it only works in certain scenarios.
|
7
|
+
I ran into a scenario where I needed to run spec on an EC2 machine and TeamCity
|
8
|
+
needed to know the results, so I created this for situations like that.
|
9
|
+
|
10
|
+
Installation
|
11
|
+
------------
|
12
|
+
|
13
|
+
Add to your Gemfile
|
14
|
+
```
|
15
|
+
gem 'rspec-teamcity', git: 'https://github.com/apechimp/rspec-teamcity'
|
16
|
+
```
|
17
|
+
|
18
|
+
Usage
|
19
|
+
-----
|
20
|
+
|
21
|
+
```bash
|
22
|
+
rspec spec --require <absolute-path-to-source-code> --format Spec::Runner::Formatter::TeamcityFormatter
|
23
|
+
```
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require 'rspec/teamcity'
|
27
|
+
RSpec.configure do |config|
|
28
|
+
config.add_formatter Spec::Runner::Formatter::TeamcityFormatter
|
29
|
+
end
|
30
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,693 @@
|
|
1
|
+
# Copyright 2000-2014 JetBrains s.r.o.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
# @author Roman.Chernyatchik
|
16
|
+
# @date 18:02:54
|
17
|
+
|
18
|
+
require_relative 'teamcity/formatter_initializer'
|
19
|
+
|
20
|
+
require_relative 'teamcity/utils/logger_util'
|
21
|
+
require_relative 'teamcity/rake_exceptions'
|
22
|
+
require_relative 'teamcity/rakerunner_consts'
|
23
|
+
|
24
|
+
SPEC_FORMATTER_LOG = ::Rake::TeamCity::Utils::RSpecFileLogger.new
|
25
|
+
SPEC_FORMATTER_LOG.log_msg("spec formatter.rb loaded.")
|
26
|
+
|
27
|
+
require_relative 'teamcity/runner_common'
|
28
|
+
require_relative 'teamcity/utils/service_message_factory'
|
29
|
+
require_relative 'teamcity/utils/std_capture_helper'
|
30
|
+
require_relative 'teamcity/utils/runner_utils'
|
31
|
+
require_relative 'teamcity/utils/url_formatter'
|
32
|
+
|
33
|
+
if Spec::Runner::Formatter::RSPEC_VERSION_3
|
34
|
+
require_relative 'teamcity_rspec3'
|
35
|
+
else
|
36
|
+
module Spec
|
37
|
+
module Runner
|
38
|
+
module Formatter
|
39
|
+
class TeamcityFormatter < (RSPEC_VERSION_2 ? RSpec::Core::Formatters::BaseFormatter : Spec::Runner::Formatter::BaseFormatter)
|
40
|
+
include ::Rake::TeamCity::StdCaptureHelper
|
41
|
+
include ::Rake::TeamCity::RunnerUtils
|
42
|
+
include ::Rake::TeamCity::RunnerCommon
|
43
|
+
include ::Rake::TeamCity::Utils::UrlFormatter
|
44
|
+
|
45
|
+
RUNNER_ISNT_COMPATIBLE_MESSAGE = "TeamCity Rake Runner Plugin isn't compatible with this RSpec version.\n\n"
|
46
|
+
RUNNER_RSPEC_FAILED = "Failed to run RSpec.."
|
47
|
+
|
48
|
+
TEAMCITY_FORMATTER_INTERNAL_ERRORS =[]
|
49
|
+
@@reporter_closed = false
|
50
|
+
|
51
|
+
########## Teamcity #############################
|
52
|
+
def log(msg)
|
53
|
+
send_msg(msg)
|
54
|
+
|
55
|
+
# returns:
|
56
|
+
msg
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.closed?()
|
60
|
+
@@reporter_closed
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.close()
|
64
|
+
@@reporter_closed = true
|
65
|
+
end
|
66
|
+
|
67
|
+
######## Spec formatter ########################
|
68
|
+
def initialize(*args)
|
69
|
+
# Rspec 1.0.8 - 1.1.12, 1.2.0 rspec support
|
70
|
+
# 1. initialize(where)
|
71
|
+
# 2. initialize(options, where)
|
72
|
+
# 3. initialize(options, output)
|
73
|
+
#
|
74
|
+
# RSpec 2.x support
|
75
|
+
# 4. initialize(output)
|
76
|
+
output_stream = nil
|
77
|
+
method_arity = args.length
|
78
|
+
if method_arity == 1
|
79
|
+
# old API
|
80
|
+
# initialize(where)
|
81
|
+
output_stream = args[0]
|
82
|
+
super(output_stream)
|
83
|
+
@options = nil
|
84
|
+
elsif method_arity == 2
|
85
|
+
# initialize(options, where)
|
86
|
+
# 1.1.3 and higher
|
87
|
+
output_stream = args[1]
|
88
|
+
super(args[0], output_stream)
|
89
|
+
else
|
90
|
+
log_and_raise_internal_error RUNNER_ISNT_COMPATIBLE_MESSAGE + "BaseFormatter.initialize arity = #{method_arity}.", true
|
91
|
+
end
|
92
|
+
|
93
|
+
# Initializes
|
94
|
+
@groups_stack = []
|
95
|
+
@ex_group_finished_event_supported = nil
|
96
|
+
|
97
|
+
# check out output stream is a Drb stream, in such case all commands should be send there
|
98
|
+
redirect_output_via_drb = !output_stream.nil? && (defined? DRb::DRbObject) && output_stream.kind_of?(DRb::DRbObject)
|
99
|
+
if redirect_output_via_drb
|
100
|
+
@@original_stdout = output_stream
|
101
|
+
end
|
102
|
+
|
103
|
+
###############################################
|
104
|
+
|
105
|
+
# Setups Test runner's MessageFactory
|
106
|
+
set_message_factory(::Rake::TeamCity::MessageFactory)
|
107
|
+
log_test_reporter_attached()
|
108
|
+
end
|
109
|
+
|
110
|
+
def start(example_count)
|
111
|
+
super
|
112
|
+
|
113
|
+
@example_count = example_count
|
114
|
+
|
115
|
+
# Log count of examples
|
116
|
+
if ::Rake::TeamCity.is_in_idea_mode
|
117
|
+
log(@message_factory.create_tests_count(example_count))
|
118
|
+
elsif ::Rake::TeamCity.is_in_buildserver_mode
|
119
|
+
log(@message_factory.create_progress_message("Starting.. (#{@example_count} examples)"))
|
120
|
+
end
|
121
|
+
debug_log("Examples: (#{@example_count} examples)")
|
122
|
+
|
123
|
+
# Saves STDOUT, STDERR because bugs in RSpec/formatter can break it
|
124
|
+
@sout, @serr = copy_stdout_stderr
|
125
|
+
|
126
|
+
debug_log("Starting..")
|
127
|
+
end
|
128
|
+
|
129
|
+
# For RSpec < 1.1
|
130
|
+
def add_behaviour(name)
|
131
|
+
super
|
132
|
+
my_add_example_group(name)
|
133
|
+
end
|
134
|
+
|
135
|
+
#For RSpec >= 1.1, <= 1.2.3
|
136
|
+
#For RSpec >= 2.0.0.beta1 and < 2.0.0.beta.19
|
137
|
+
def add_example_group(example_group)
|
138
|
+
super
|
139
|
+
my_add_example_group(example_group.description, example_group)
|
140
|
+
end
|
141
|
+
|
142
|
+
#For RSpec >= 1.2.4
|
143
|
+
#For RSpec >= 2.0.0.beta.19
|
144
|
+
def example_group_started(example_group)
|
145
|
+
super
|
146
|
+
|
147
|
+
desc = if rspec_2? && !ex_group_finished_event_supported?
|
148
|
+
# temporary work around for rspec 2.0 < 2.0.0.beta22
|
149
|
+
example_group.ancestors.reverse.inject("") { |name, group| name + " " + group.description.strip }
|
150
|
+
else
|
151
|
+
# rspec 1.x && >= 2.0.0.beta22
|
152
|
+
example_group.description
|
153
|
+
end
|
154
|
+
my_add_example_group(desc, example_group)
|
155
|
+
end
|
156
|
+
|
157
|
+
#For RSpec >= 2.0.0.beta.22
|
158
|
+
def example_group_finished(example_group)
|
159
|
+
close_example_group
|
160
|
+
end
|
161
|
+
|
162
|
+
#########################################################
|
163
|
+
#########################################################
|
164
|
+
# start / fail /pass /pending method
|
165
|
+
#########################################################
|
166
|
+
#########################################################
|
167
|
+
@@RUNNING_EXAMPLES_STORAGE = {}
|
168
|
+
|
169
|
+
#Sometimes example_started is executed in another group
|
170
|
+
#Such behavior leds to inconsistent order of exaple_started, example_passed/failed/pending events
|
171
|
+
#This bug usually reproduced in specs on RSpec project.
|
172
|
+
#
|
173
|
+
#This hack helps in this problem:
|
174
|
+
# *Output start capture at example started
|
175
|
+
# *Example start/passed/failed/pending methods shares example's full and output files in map
|
176
|
+
#
|
177
|
+
# In fact this is a HACK
|
178
|
+
# TODO: events branch
|
179
|
+
def example_started(example)
|
180
|
+
# Due to rspec 2.1.0 regression
|
181
|
+
if rspec_2? || rspec_1_2_0?
|
182
|
+
return
|
183
|
+
end
|
184
|
+
# Rspec < 2.1.0
|
185
|
+
report_example_started(example)
|
186
|
+
end
|
187
|
+
|
188
|
+
def example_passed(example)
|
189
|
+
if rspec_2? || rspec_1_2_0?
|
190
|
+
# Due to regression in rspec 1_2_0 we had to report event here
|
191
|
+
report_example_started(example)
|
192
|
+
end
|
193
|
+
|
194
|
+
debug_log("example_passed[#{example_description(example)}] #{example}")
|
195
|
+
|
196
|
+
stop_capture_output_and_log_it(example)
|
197
|
+
|
198
|
+
close_test_block(example)
|
199
|
+
end
|
200
|
+
|
201
|
+
# failure is Spec::Runner::Reporter::Failure
|
202
|
+
def example_failed(*args)
|
203
|
+
method_arity = args.length
|
204
|
+
if method_arity == 1
|
205
|
+
# rspec 2.0
|
206
|
+
# example_failed(example)
|
207
|
+
example = args[0]
|
208
|
+
execution_result = args[0].execution_result
|
209
|
+
# :exception hash key rspec >= 2.2.0, :exception_encountered in older versions
|
210
|
+
failure = execution_result[:exception] || execution_result[:exception_encountered]
|
211
|
+
elsif method_arity == 3
|
212
|
+
# rspec 1.x
|
213
|
+
# RSpec < #3305 (i.e. <= 1.1.3)
|
214
|
+
# example_failed(example, counter, failure)
|
215
|
+
example, failure = args[0], args[2]
|
216
|
+
else
|
217
|
+
log_and_raise_internal_error RUNNER_ISNT_COMPATIBLE_MESSAGE + "BaseFormatter.example_pending arity = #{method_arity}.", true
|
218
|
+
end
|
219
|
+
example_failed_3args(example, failure)
|
220
|
+
end
|
221
|
+
|
222
|
+
# failure is Spec::Runner::Reporter::Failure
|
223
|
+
def example_failed_3args(example, failure)
|
224
|
+
if rspec_2? || rspec_1_2_0?
|
225
|
+
# Due to regression in rspec 1_2_0 we had to report event here
|
226
|
+
report_example_started(example)
|
227
|
+
end
|
228
|
+
|
229
|
+
if get_data_from_storage(example).nil?
|
230
|
+
#TODO: #638 - See http://rspec.lighthouseapp.com/projects/5645-rspec/tickets/638
|
231
|
+
desc = example_description(example)
|
232
|
+
if desc == "after(:all)" || desc == "before(:all)"
|
233
|
+
@setup_failed = true
|
234
|
+
example_started(example)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
debug_log("example failed[#{example_description(example)}] #{example}")
|
239
|
+
|
240
|
+
stop_capture_output_and_log_it(example)
|
241
|
+
|
242
|
+
# example service data
|
243
|
+
example_data = get_data_from_storage(example)
|
244
|
+
additional_flowid_suffix = example_data.additional_flowid_suffix
|
245
|
+
running_example_full_name = example_data.full_name
|
246
|
+
|
247
|
+
# Failure message:
|
248
|
+
if rspec_2?
|
249
|
+
def failure.expectation_not_met?
|
250
|
+
self.exception.kind_of?(RSpec::Expectations::ExpectationNotMetError)
|
251
|
+
end
|
252
|
+
|
253
|
+
def failure.pending_fixed?
|
254
|
+
if defined? RSpec::Core::Pending::PendingExampleFixedError
|
255
|
+
# rspec >= 2.8.0
|
256
|
+
self.exception.kind_of?(RSpec::Core::Pending::PendingExampleFixedError)
|
257
|
+
elsif defined? RSpec::Core::PendingExampleFixedError
|
258
|
+
# rspec >= 2.0.0.beta.19
|
259
|
+
self.exception.kind_of?(RSpec::Core::PendingExampleFixedError)
|
260
|
+
else
|
261
|
+
# rspec < 2.0.0.beta.19
|
262
|
+
self.exception.kind_of?(RSpec::Expectations::PendingExampleFixedError)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
message = if failure.exception.nil?
|
268
|
+
# for unknown failure
|
269
|
+
"[Without Exception]";
|
270
|
+
elsif (failure.expectation_not_met? || failure.pending_fixed?)
|
271
|
+
# for expectation error (Spec::Expectations::ExpectationNotMetError)
|
272
|
+
# and
|
273
|
+
# for pending fixed (Spec::Example::PendingExampleFixedError)
|
274
|
+
failure.exception.message
|
275
|
+
else
|
276
|
+
# for other exception
|
277
|
+
"#{failure.exception.class.name}: #{failure.exception.message}"
|
278
|
+
end
|
279
|
+
|
280
|
+
# Backtrace
|
281
|
+
backtrace = calc_backtrace(failure.exception, example)
|
282
|
+
|
283
|
+
#if ::Rake::TeamCity.is_in_buildserver_mode
|
284
|
+
# # failure description
|
285
|
+
# #full_failure_description = message
|
286
|
+
# #(full_failure_description += "\n\n " + backtrace) if backtrace
|
287
|
+
#end
|
288
|
+
|
289
|
+
debug_log("Example failing... full name = [#{running_example_full_name}], Message:\n#{message} \n\nBackrace:\n#{backtrace}\n\n, additional flowid suffix=[#{additional_flowid_suffix}]")
|
290
|
+
|
291
|
+
# Expectation failures will be shown as failures and other exceptions as Errors
|
292
|
+
if failure.expectation_not_met?
|
293
|
+
log(@message_factory.create_test_failed(running_example_full_name, message, backtrace))
|
294
|
+
else
|
295
|
+
log(@message_factory.create_test_error(running_example_full_name, message, backtrace))
|
296
|
+
end
|
297
|
+
close_test_block(example)
|
298
|
+
end
|
299
|
+
|
300
|
+
def calc_backtrace(exception, example)
|
301
|
+
return "" if exception.nil?
|
302
|
+
if rspec_2? && respond_to?(:format_backtrace) && self.class.instance_method(:format_backtrace).arity == 2
|
303
|
+
format_backtrace(exception.backtrace, example).join("\n")
|
304
|
+
else
|
305
|
+
::Rake::TeamCity::RunnerCommon.format_backtrace(exception.backtrace)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def example_pending(*args)
|
310
|
+
method_arity = args.length
|
311
|
+
if method_arity == 1
|
312
|
+
# rspec 2.0
|
313
|
+
# example_pending(example)
|
314
|
+
example = args[0]
|
315
|
+
execution_result = args[0].execution_result
|
316
|
+
example_group_desc, example, message = nil, example, execution_result[:pending_message]
|
317
|
+
elsif method_arity == 2
|
318
|
+
# rev. #3305 (http://rspec.rubyforge.org/svn/trunk) changes
|
319
|
+
# RSpec 1.1.4, RSPec >= 1.2.4(3rd args is optional)
|
320
|
+
# example_pending(example, message)
|
321
|
+
example_group_desc, example, message = nil, *args
|
322
|
+
elsif method_arity == 3
|
323
|
+
if args[1].is_a?(String)
|
324
|
+
# RSpec 1.1.8 and higher, RSpec 1.2.0 ... RSPec 1.2.4
|
325
|
+
# example_pending(example, message, pending_caller)
|
326
|
+
example_group_desc, example, message = nil, *args
|
327
|
+
else
|
328
|
+
# RSpec < #3305 (i.e. <= 1.1.3)
|
329
|
+
# example_pending(example_group_description, example, message)
|
330
|
+
example_group_desc, example, message = args
|
331
|
+
end
|
332
|
+
else
|
333
|
+
log_and_raise_internal_error RUNNER_ISNT_COMPATIBLE_MESSAGE + "BaseFormatter.example_pending arity = #{method_arity}.", true
|
334
|
+
end
|
335
|
+
example_pending_3args(example_group_desc, example, message)
|
336
|
+
end
|
337
|
+
|
338
|
+
# example_group_desc - can be nil
|
339
|
+
def example_pending_3args(example_group_desc, example, message)
|
340
|
+
if rspec_2? || rspec_1_2_0?
|
341
|
+
# Due to regression in rspec 1_2_0 we had to report event here
|
342
|
+
report_example_started(example)
|
343
|
+
end
|
344
|
+
|
345
|
+
debug_log("pending: #{example_group_desc}, #{example_description(example)}, #{message}, #{example}")
|
346
|
+
|
347
|
+
# stop capturing
|
348
|
+
stop_capture_output_and_log_it(example)
|
349
|
+
|
350
|
+
if example_group_desc
|
351
|
+
#Old API, <= 1.1.3
|
352
|
+
assert_example_group_valid(example_group_desc)
|
353
|
+
end
|
354
|
+
|
355
|
+
# example service data
|
356
|
+
example_data = get_data_from_storage(example)
|
357
|
+
additional_flowid_suffix = example_data.additional_flowid_suffix
|
358
|
+
running_example_full_name = example_data.full_name
|
359
|
+
|
360
|
+
debug_log("Example pending... [#{@groups_stack.last}].[#{running_example_full_name}] - #{message}, additional flowid suffix=[#{additional_flowid_suffix}]")
|
361
|
+
log(@message_factory.create_test_ignored(running_example_full_name, "Pending: #{message}"))
|
362
|
+
|
363
|
+
close_test_block(example)
|
364
|
+
end
|
365
|
+
|
366
|
+
|
367
|
+
# see snippet_extractor.rb
|
368
|
+
# Here we can add file link or show code lined
|
369
|
+
# def extra_failure_content(failure)
|
370
|
+
# require 'spec/runner/formatter/snippet_extractor'
|
371
|
+
# @snippet_extractor ||= SnippetExtractor.new
|
372
|
+
# " <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(failure.exception)}</code></pre>"
|
373
|
+
# end
|
374
|
+
|
375
|
+
# For Rspec:
|
376
|
+
# 4 args - rspec < 2.0
|
377
|
+
# 0 args - rspec >= 2.0
|
378
|
+
def dump_summary(duration = @duration,
|
379
|
+
example_count = @example_count,
|
380
|
+
failure_count = failed_examples().length,
|
381
|
+
pending_count = pending_examples().length)
|
382
|
+
|
383
|
+
# Repairs stdout and stderr just in case
|
384
|
+
repair_process_output
|
385
|
+
|
386
|
+
if dry_run?
|
387
|
+
totals = "This was a dry-run"
|
388
|
+
else
|
389
|
+
totals = "#{example_count} example#{'s' unless example_count == 1}, #{failure_count} failure#{'s' unless failure_count == 1}, #{example_count - failure_count - pending_count} passed"
|
390
|
+
totals << ", #{pending_count} pending" if pending_count > 0
|
391
|
+
end
|
392
|
+
|
393
|
+
close_example_group
|
394
|
+
|
395
|
+
# Total statistic
|
396
|
+
debug_log(totals)
|
397
|
+
log(totals)
|
398
|
+
|
399
|
+
# Time statistic from Spec Runner
|
400
|
+
status_message = "Finished in #{duration} seconds"
|
401
|
+
debug_log(status_message)
|
402
|
+
log(status_message)
|
403
|
+
|
404
|
+
#Really must be '@example_count == example_count', it is hack for spec trunk tests
|
405
|
+
if !@setup_failed && @example_count > example_count
|
406
|
+
msg = "#{RUNNER_ISNT_COMPATIBLE_MESSAGE}Error: Not all examples have been run! (#{example_count} of #{@example_count})\n#{gather_unfinished_examples_name}"
|
407
|
+
|
408
|
+
log_and_raise_internal_error msg
|
409
|
+
debug_log(msg)
|
410
|
+
end unless @groups_stack.empty?
|
411
|
+
|
412
|
+
unless @@RUNNING_EXAMPLES_STORAGE.empty?
|
413
|
+
# unfinished examples statistics
|
414
|
+
msg = RUNNER_ISNT_COMPATIBLE_MESSAGE + gather_unfinished_examples_name
|
415
|
+
log_and_raise_internal_error msg
|
416
|
+
end
|
417
|
+
|
418
|
+
# finishing
|
419
|
+
@@RUNNING_EXAMPLES_STORAGE.clear()
|
420
|
+
|
421
|
+
debug_log("Summary finished.")
|
422
|
+
end
|
423
|
+
|
424
|
+
# RSPec >= 2.0
|
425
|
+
def close
|
426
|
+
tc_rspec_do_close
|
427
|
+
end
|
428
|
+
|
429
|
+
###########################################################################
|
430
|
+
###########################################################################
|
431
|
+
###########################################################################
|
432
|
+
private
|
433
|
+
|
434
|
+
# For rspec >= 2.0.0.beta22 API
|
435
|
+
# new "example_group_finished" event was added in beta.22
|
436
|
+
def ex_group_finished_event_supported?
|
437
|
+
if @ex_group_finished_event_supported.nil?
|
438
|
+
methods = self.class.superclass.instance_methods
|
439
|
+
# Holy shit!!! ----> in ruby 1.8.x "instance_methods" returns collection of string and in 1.9.x collection of symbols!
|
440
|
+
@ex_group_finished_event_supported = methods.include?("example_group_finished") || methods.include?(:example_group_finished)
|
441
|
+
end
|
442
|
+
@ex_group_finished_event_supported
|
443
|
+
end
|
444
|
+
|
445
|
+
def gather_unfinished_examples_name
|
446
|
+
if @@RUNNING_EXAMPLES_STORAGE.empty?
|
447
|
+
return ""
|
448
|
+
end
|
449
|
+
|
450
|
+
msg = "Following examples weren't finished:"
|
451
|
+
count = 1
|
452
|
+
@@RUNNING_EXAMPLES_STORAGE.each { |key, value|
|
453
|
+
msg << "\n #{count}. Example : '#{value.full_name}'"
|
454
|
+
sout_str, serr_str = get_redirected_stdout_stderr_from_files(value.stdout_file_new, value.stderr_file_new)
|
455
|
+
unless sout_str.empty?
|
456
|
+
msg << "\n[Example Output]:\n#{sout_str}"
|
457
|
+
end
|
458
|
+
unless serr_str.empty?
|
459
|
+
msg << "\n[Example Error Output]:\n#{serr_str}"
|
460
|
+
end
|
461
|
+
|
462
|
+
count += 1
|
463
|
+
}
|
464
|
+
msg
|
465
|
+
end
|
466
|
+
|
467
|
+
def example_description(example)
|
468
|
+
example.description || "<noname>"
|
469
|
+
end
|
470
|
+
|
471
|
+
# Due to rspec 2.1.0 regression we had to report fake started event in pass/finish/fail/pendings events
|
472
|
+
# and ignore it in example_started event
|
473
|
+
def report_example_started(example)
|
474
|
+
my_running_example_desc = example_description(example)
|
475
|
+
debug_log("example started [#{my_running_example_desc}] #{example}")
|
476
|
+
|
477
|
+
current_group_description = @groups_stack.last
|
478
|
+
my_running_example_full_name = "#{current_group_description} #{my_running_example_desc}"
|
479
|
+
|
480
|
+
# Send open event
|
481
|
+
debug_log("Example starting.. - full name = [#{my_running_example_full_name}], desc = [#{my_running_example_desc}]")
|
482
|
+
log(@message_factory.create_test_started(my_running_example_full_name, location_from_link(*extract_source_location_from_example(example))))
|
483
|
+
|
484
|
+
# Start capturing...
|
485
|
+
std_files = capture_output_start_external
|
486
|
+
started_at_ms = rspec_2? ?
|
487
|
+
get_time_in_ms(example.execution_result[:started_at]) :
|
488
|
+
get_current_time_in_ms
|
489
|
+
|
490
|
+
debug_log("Output capturing started.")
|
491
|
+
|
492
|
+
put_data_to_storage(example, RunningExampleData.new(my_running_example_full_name, "", started_at_ms, *std_files))
|
493
|
+
end
|
494
|
+
|
495
|
+
# Repairs SDOUT, STDERR from saved data
|
496
|
+
def repair_process_output
|
497
|
+
if !@sout.nil? && !@serr.nil?
|
498
|
+
@sout.flush
|
499
|
+
@serr.flush
|
500
|
+
reopen_stdout_stderr(@sout, @serr)
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
def dry_run?
|
505
|
+
(@options && (@options.dry_run)) ? true : false
|
506
|
+
end
|
507
|
+
|
508
|
+
# Refactored initialize method. Is used for support rspec API < 1.1 and >= 1.1.
|
509
|
+
# spec_location_info : "$PATH:$LINE_NUM"
|
510
|
+
def my_add_example_group(group_desc, example_group = nil)
|
511
|
+
# If "group finished" API isn't available, let's close the previous block
|
512
|
+
if !rspec_2? || !ex_group_finished_event_supported?
|
513
|
+
close_example_group
|
514
|
+
end
|
515
|
+
|
516
|
+
# New block starts.
|
517
|
+
@groups_stack << "#{group_desc}"
|
518
|
+
|
519
|
+
description = @groups_stack.last
|
520
|
+
debug_log("Adding example group(behaviour)...: [#{description}]...")
|
521
|
+
log(@message_factory.create_suite_started(description,
|
522
|
+
location_from_link(*extract_source_location_from_group(example_group))))
|
523
|
+
end
|
524
|
+
|
525
|
+
def close_test_block(example)
|
526
|
+
example_data = remove_data_from_storage(example)
|
527
|
+
finished_at_ms = rspec_2? ?
|
528
|
+
get_time_in_ms(example.execution_result[:finished_at]) :
|
529
|
+
get_current_time_in_ms
|
530
|
+
duration = finished_at_ms - example_data.start_time_in_ms
|
531
|
+
|
532
|
+
additional_flowid_suffix = example_data.additional_flowid_suffix
|
533
|
+
running_example_full_name = example_data.full_name
|
534
|
+
|
535
|
+
debug_log("Example finishing... full example name = [#{running_example_full_name}], duration = #{duration} ms, additional flowid suffix=[#{additional_flowid_suffix}]")
|
536
|
+
diagnostic_info = (rspec_2? ? "rspec2 [#{::RSpec::Core::Version::STRING}]" : "rspec1") + ", f/s=(#{finished_at_ms}, #{example_data.start_time_in_ms}), duration=#{duration}, time.now=#{Time.now.to_s}" + (rspec_2? ? ", raw[:started_at]=#{example.execution_result[:started_at].to_s}, raw[:finished_at]=#{example.execution_result[:finished_at].to_s}, raw[:run_time]=#{example.execution_result[:run_time].to_s}" : "")
|
537
|
+
|
538
|
+
log(@message_factory.create_test_finished(running_example_full_name, duration, ::Rake::TeamCity.is_in_buildserver_mode ? nil : diagnostic_info))
|
539
|
+
end
|
540
|
+
|
541
|
+
def close_example_group
|
542
|
+
# do nothing if it no groups were added before (e.g. 1.x api)
|
543
|
+
return if @groups_stack.empty?
|
544
|
+
|
545
|
+
# get and remove
|
546
|
+
current_group_description = @groups_stack.pop
|
547
|
+
|
548
|
+
debug_log("Closing example group(behaviour): [#{current_group_description}].")
|
549
|
+
log(@message_factory.create_suite_finished(current_group_description))
|
550
|
+
end
|
551
|
+
|
552
|
+
def debug_log(string)
|
553
|
+
# Logs output.
|
554
|
+
SPEC_FORMATTER_LOG.log_msg(string)
|
555
|
+
end
|
556
|
+
|
557
|
+
def stop_capture_output_and_log_it(example)
|
558
|
+
example_data = get_data_from_storage(example)
|
559
|
+
additional_flowid_suffix = example_data.additional_flowid_suffix
|
560
|
+
running_example_full_name = example_data.full_name
|
561
|
+
|
562
|
+
stdout_string, stderr_string = capture_output_end_external(*example_data.get_std_files)
|
563
|
+
debug_log("Example capturing was stopped.")
|
564
|
+
|
565
|
+
debug_log("My stdOut: [#{stdout_string}] additional flow id=[#{additional_flowid_suffix}]")
|
566
|
+
if stdout_string && !stdout_string.empty?
|
567
|
+
log(@message_factory.create_test_output_message(running_example_full_name, true, stdout_string))
|
568
|
+
end
|
569
|
+
debug_log("My stdErr: [#{stderr_string}] additional flow id=[#{additional_flowid_suffix}]")
|
570
|
+
if stderr_string && !stderr_string.empty?
|
571
|
+
log(@message_factory.create_test_output_message(running_example_full_name, false, stderr_string))
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
######################################################
|
576
|
+
############# Assertions #############################
|
577
|
+
######################################################
|
578
|
+
# def assert_example_valid(example_desc)
|
579
|
+
# if (example_desc != @my_running_example_desc)
|
580
|
+
# msg = "Example [#{example_desc}] doesn't correspond to current running example [#{@my_running_example_desc}]!"
|
581
|
+
# debug_log(msg)
|
582
|
+
# ... [send error to teamcity] ...
|
583
|
+
# close_test_block
|
584
|
+
#
|
585
|
+
# raise ::Rake::TeamCity::InnerException, msg, caller
|
586
|
+
# end
|
587
|
+
# end
|
588
|
+
|
589
|
+
# We doesn't support concurrent example groups executing
|
590
|
+
def assert_example_group_valid(group_description)
|
591
|
+
current_group_description = @groups_stack.last
|
592
|
+
if group_description != current_group_description
|
593
|
+
msg = "Example group(behaviour) [#{group_description}] doesn't correspond to current running example group [#{ current_group_description}]!"
|
594
|
+
debug_log(msg)
|
595
|
+
|
596
|
+
raise ::Rake::TeamCity::InnerException, msg, caller
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
######################################################
|
601
|
+
def log_and_raise_internal_error(msg, raise_now = false)
|
602
|
+
debug_log(msg)
|
603
|
+
|
604
|
+
log(msg)
|
605
|
+
log(@message_factory.create_build_error_report(RUNNER_RSPEC_FAILED))
|
606
|
+
|
607
|
+
excep_data = [msg, caller]
|
608
|
+
if raise_now
|
609
|
+
@@RUNNING_EXAMPLES_STORAGE.clear()
|
610
|
+
raise ::Rake::TeamCity::InnerException, *excep_data
|
611
|
+
end
|
612
|
+
TEAMCITY_FORMATTER_INTERNAL_ERRORS << excep_data
|
613
|
+
end
|
614
|
+
|
615
|
+
def get_data_from_storage(example)
|
616
|
+
@@RUNNING_EXAMPLES_STORAGE[example.object_id]
|
617
|
+
end
|
618
|
+
|
619
|
+
def remove_data_from_storage(example)
|
620
|
+
@@RUNNING_EXAMPLES_STORAGE.delete(example.object_id)
|
621
|
+
end
|
622
|
+
|
623
|
+
def put_data_to_storage(example, data)
|
624
|
+
@@RUNNING_EXAMPLES_STORAGE[example.object_id] = data
|
625
|
+
end
|
626
|
+
|
627
|
+
def rspec_2?
|
628
|
+
::Spec::Runner::Formatter::RSPEC_VERSION_2
|
629
|
+
end
|
630
|
+
|
631
|
+
def rspec_1_2_0?
|
632
|
+
::Spec::VERSION::MAJOR == 1 &&
|
633
|
+
::Spec::VERSION::MINOR == 2 &&
|
634
|
+
::Spec::VERSION::TINY == 0
|
635
|
+
end
|
636
|
+
|
637
|
+
######################################################
|
638
|
+
######################################################
|
639
|
+
#TODO remove flowid
|
640
|
+
class RunningExampleData
|
641
|
+
attr_reader :full_name # full task name, example name in build log
|
642
|
+
# TODO: Remove!
|
643
|
+
attr_reader :additional_flowid_suffix # to support concurrently running examples
|
644
|
+
attr_reader :start_time_in_ms # start time of example
|
645
|
+
attr_reader :stdout_file_old # before capture
|
646
|
+
attr_reader :stderr_file_old # before capture
|
647
|
+
attr_reader :stdout_file_new #current capturing storage
|
648
|
+
attr_reader :stderr_file_new # current capturing storage
|
649
|
+
|
650
|
+
def initialize(full_name, additional_flowid_suffix, start_time_in_ms, stdout_file_old, stderr_file_old, stdout_file_new, stderr_file_new)
|
651
|
+
@full_name = full_name
|
652
|
+
# TODO: Remove!
|
653
|
+
@additional_flowid_suffix = additional_flowid_suffix
|
654
|
+
@start_time_in_ms = start_time_in_ms
|
655
|
+
@stdout_file_old = stdout_file_old
|
656
|
+
@stderr_file_old = stderr_file_old
|
657
|
+
@stdout_file_new = stdout_file_new
|
658
|
+
@stderr_file_new = stderr_file_new
|
659
|
+
end
|
660
|
+
|
661
|
+
def get_std_files
|
662
|
+
return @stdout_file_old, @stderr_file_old, @stdout_file_new, @stderr_file_new
|
663
|
+
end
|
664
|
+
end
|
665
|
+
end
|
666
|
+
end
|
667
|
+
end
|
668
|
+
end
|
669
|
+
end
|
670
|
+
def tc_rspec_do_close
|
671
|
+
if ::Spec::Runner::Formatter::TeamcityFormatter.closed?
|
672
|
+
return
|
673
|
+
end
|
674
|
+
|
675
|
+
::Spec::Runner::Formatter::TeamcityFormatter.close
|
676
|
+
|
677
|
+
SPEC_FORMATTER_LOG.log_msg("spec formatter.rb: Finished")
|
678
|
+
SPEC_FORMATTER_LOG.close
|
679
|
+
|
680
|
+
unless Spec::Runner::Formatter::TeamcityFormatter::TEAMCITY_FORMATTER_INTERNAL_ERRORS.empty?
|
681
|
+
several_exc = Spec::Runner::Formatter::TeamcityFormatter::TEAMCITY_FORMATTER_INTERNAL_ERRORS.length > 1
|
682
|
+
excep_data = Spec::Runner::Formatter::TeamcityFormatter::TEAMCITY_FORMATTER_INTERNAL_ERRORS[0]
|
683
|
+
|
684
|
+
common_msg = (several_exc ? "Several exceptions have occured. First exception:\n" : "") + excep_data[0] + "\n"
|
685
|
+
common_backtrace = excep_data[1]
|
686
|
+
|
687
|
+
raise ::Rake::TeamCity::InnerException, common_msg, common_backtrace
|
688
|
+
end
|
689
|
+
end
|
690
|
+
|
691
|
+
at_exit do
|
692
|
+
tc_rspec_do_close()
|
693
|
+
end
|