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.
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,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -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