rspec-teamcity 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|