rspec-teamcity 0.0.1

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