rspec-core 3.5.4 → 3.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Changelog.md +165 -2
  5. data/README.md +16 -16
  6. data/lib/rspec/core.rb +2 -1
  7. data/lib/rspec/core/bisect/coordinator.rb +26 -30
  8. data/lib/rspec/core/bisect/example_minimizer.rb +12 -8
  9. data/lib/rspec/core/bisect/fork_runner.rb +134 -0
  10. data/lib/rspec/core/bisect/server.rb +10 -14
  11. data/lib/rspec/core/bisect/{runner.rb → shell_command.rb} +27 -70
  12. data/lib/rspec/core/bisect/shell_runner.rb +73 -0
  13. data/lib/rspec/core/bisect/utilities.rb +58 -0
  14. data/lib/rspec/core/configuration.rb +315 -79
  15. data/lib/rspec/core/configuration_options.rb +43 -4
  16. data/lib/rspec/core/did_you_mean.rb +46 -0
  17. data/lib/rspec/core/drb.rb +3 -1
  18. data/lib/rspec/core/example.rb +19 -12
  19. data/lib/rspec/core/example_group.rb +17 -7
  20. data/lib/rspec/core/formatters.rb +28 -11
  21. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  22. data/lib/rspec/core/formatters/base_formatter.rb +1 -1
  23. data/lib/rspec/core/formatters/base_text_formatter.rb +3 -5
  24. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  25. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +29 -16
  26. data/lib/rspec/core/formatters/console_codes.rb +7 -4
  27. data/lib/rspec/core/formatters/deprecation_formatter.rb +9 -9
  28. data/lib/rspec/core/formatters/documentation_formatter.rb +37 -4
  29. data/lib/rspec/core/formatters/exception_presenter.rb +21 -4
  30. data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
  31. data/lib/rspec/core/formatters/html_formatter.rb +4 -2
  32. data/lib/rspec/core/formatters/html_printer.rb +3 -3
  33. data/lib/rspec/core/formatters/html_snippet_extractor.rb +4 -0
  34. data/lib/rspec/core/formatters/json_formatter.rb +9 -3
  35. data/lib/rspec/core/formatters/progress_formatter.rb +1 -0
  36. data/lib/rspec/core/formatters/protocol.rb +43 -42
  37. data/lib/rspec/core/formatters/snippet_extractor.rb +1 -3
  38. data/lib/rspec/core/{source → formatters}/syntax_highlighter.rb +21 -1
  39. data/lib/rspec/core/hooks.rb +18 -10
  40. data/lib/rspec/core/invocations.rb +30 -10
  41. data/lib/rspec/core/memoized_helpers.rb +36 -14
  42. data/lib/rspec/core/metadata.rb +2 -3
  43. data/lib/rspec/core/metadata_filter.rb +29 -17
  44. data/lib/rspec/core/notifications.rb +34 -11
  45. data/lib/rspec/core/option_parser.rb +32 -4
  46. data/lib/rspec/core/output_wrapper.rb +29 -0
  47. data/lib/rspec/core/profiler.rb +3 -1
  48. data/lib/rspec/core/project_initializer/.rspec +0 -1
  49. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +1 -4
  50. data/lib/rspec/core/rake_task.rb +21 -1
  51. data/lib/rspec/core/reporter.rb +33 -16
  52. data/lib/rspec/core/runner.rb +31 -15
  53. data/lib/rspec/core/set.rb +5 -0
  54. data/lib/rspec/core/shared_example_group.rb +41 -19
  55. data/lib/rspec/core/shell_escape.rb +2 -2
  56. data/lib/rspec/core/version.rb +1 -1
  57. data/lib/rspec/core/world.rb +24 -5
  58. metadata +26 -20
  59. metadata.gz.sig +0 -0
  60. data/lib/rspec/core/formatters/bisect_formatter.rb +0 -69
  61. data/lib/rspec/core/source.rb +0 -86
  62. data/lib/rspec/core/source/location.rb +0 -13
  63. data/lib/rspec/core/source/node.rb +0 -93
  64. data/lib/rspec/core/source/token.rb +0 -87
@@ -5,7 +5,7 @@ module RSpec
5
5
  module Core
6
6
  module Formatters
7
7
  # RSpec's built-in formatters are all subclasses of
8
- # RSpec::Core::Formatters::BaseTextFormatter.
8
+ # RSpec::Core::Formatters::BaseFormatter.
9
9
  #
10
10
  # @see RSpec::Core::Formatters::BaseTextFormatter
11
11
  # @see RSpec::Core::Reporter
@@ -1,5 +1,4 @@
1
1
  RSpec::Support.require_rspec_core "formatters/base_formatter"
2
- RSpec::Support.require_rspec_core "formatters/console_codes"
3
2
 
4
3
  module RSpec
5
4
  module Core
@@ -58,18 +57,17 @@ module RSpec
58
57
 
59
58
  # @api public
60
59
  #
61
- # Invoked at the very end, `close` allows the formatter to clean
62
- # up resources, e.g. open streams, etc.
60
+ # Invoked at the end of a suite run. Allows the formatter to do any
61
+ # tidying up, but be aware that formatter output streams may be used
62
+ # elsewhere so don't actually close them.
63
63
  #
64
64
  # @param _notification [NullNotification] (Ignored)
65
65
  def close(_notification)
66
- return unless IO === output
67
66
  return if output.closed?
68
67
 
69
68
  output.puts
70
69
 
71
70
  output.flush
72
- output.close unless output == $stdout
73
71
  end
74
72
  end
75
73
  end
@@ -0,0 +1,29 @@
1
+ require 'drb/drb'
2
+ RSpec::Support.require_rspec_core "formatters/base_bisect_formatter"
3
+
4
+ module RSpec
5
+ module Core
6
+ module Formatters
7
+ # Used by `--bisect`. When it shells out and runs a portion of the suite, it uses
8
+ # this formatter as a means to have the status reported back to it, via DRb.
9
+ #
10
+ # Note that since DRb calls carry considerable overhead compared to normal
11
+ # method calls, we try to minimize the number of DRb calls for perf reasons,
12
+ # opting to communicate only at the start and the end of the run, rather than
13
+ # after each example.
14
+ # @private
15
+ class BisectDRbFormatter < BaseBisectFormatter
16
+ def initialize(_output)
17
+ drb_uri = "druby://localhost:#{RSpec.configuration.drb_port}"
18
+ @bisect_server = DRbObject.new_with_uri(drb_uri)
19
+ RSpec.configuration.files_or_directories_to_run = @bisect_server.files_or_directories_to_run
20
+ super(Set.new(@bisect_server.expected_failures))
21
+ end
22
+
23
+ def notify_results(results)
24
+ @bisect_server.latest_run_results = results
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -6,19 +6,14 @@ module RSpec
6
6
  # @private
7
7
  # Produces progress output while bisecting.
8
8
  class BisectProgressFormatter < BaseTextFormatter
9
- # We've named all events with a `bisect_` prefix to prevent naming collisions.
10
- Formatters.register self, :bisect_starting, :bisect_original_run_complete,
11
- :bisect_round_started, :bisect_individual_run_complete,
12
- :bisect_complete, :bisect_repro_command,
13
- :bisect_failed, :bisect_aborted,
14
- :bisect_round_ignoring_ids, :bisect_round_detected_multiple_culprits,
15
- :bisect_dependency_check_started, :bisect_dependency_check_passed,
16
- :bisect_dependency_check_failed
9
+ def initialize(output, bisect_runner)
10
+ super(output)
11
+ @bisect_runner = bisect_runner
12
+ end
17
13
 
18
14
  def bisect_starting(notification)
19
15
  @round_count = 0
20
- options = notification.original_cli_args.join(' ')
21
- output.puts "Bisect started using options: #{options.inspect}"
16
+ output.puts bisect_started_message(notification)
22
17
  output.print "Running suite to find failures..."
23
18
  end
24
19
 
@@ -40,6 +35,16 @@ module RSpec
40
35
 
41
36
  def bisect_dependency_check_failed(_notification)
42
37
  output.puts " failure(s) do not require any non-failures to run first"
38
+
39
+ if @bisect_runner == :fork
40
+ output.puts
41
+ output.puts "=" * 80
42
+ output.puts "NOTE: this bisect run used `config.bisect_runner = :fork`, which generally"
43
+ output.puts "provides significantly faster bisection runs than the old shell-based runner,"
44
+ output.puts "but may inaccurately report that no non-failures are required. If this result"
45
+ output.puts "is unexpected, consider setting `config.bisect_runner = :shell` and trying again."
46
+ output.puts "=" * 80
47
+ end
43
48
  end
44
49
 
45
50
  def bisect_round_started(notification, include_trailing_space=true)
@@ -85,16 +90,20 @@ module RSpec
85
90
  output.puts "\n\nBisect aborted!"
86
91
  output.puts "\nThe most minimal reproduction command discovered so far is:\n #{notification.repro}"
87
92
  end
93
+
94
+ private
95
+
96
+ def bisect_started_message(notification)
97
+ options = notification.original_cli_args.join(' ')
98
+ "Bisect started using options: #{options.inspect}"
99
+ end
88
100
  end
89
101
 
90
102
  # @private
91
- # Produces detailed debug output while bisecting. Used when
92
- # bisect is performed while the `DEBUG_RSPEC_BISECT` ENV var is used.
93
- # Designed to provide details for us when we need to troubleshoot bisect bugs.
103
+ # Produces detailed debug output while bisecting. Used when bisect is
104
+ # performed with `--bisect=verbose`. Designed to provide details for
105
+ # us when we need to troubleshoot bisect bugs.
94
106
  class BisectDebugFormatter < BisectProgressFormatter
95
- Formatters.register self, :bisect_original_run_complete, :bisect_individual_run_start,
96
- :bisect_individual_run_complete, :bisect_round_ignoring_ids
97
-
98
107
  def bisect_original_run_complete(notification)
99
108
  output.puts " (#{Helpers.format_duration(notification.duration)})"
100
109
 
@@ -138,6 +147,10 @@ module RSpec
138
147
  formatted_ids = organized_ids.map { |id| " - #{id}" }.join("\n")
139
148
  "#{description} (#{ids.size}):\n#{formatted_ids}"
140
149
  end
150
+
151
+ def bisect_started_message(notification)
152
+ "#{super} and bisect runner: #{notification.bisect_runner.inspect}"
153
+ end
141
154
  end
142
155
  end
143
156
  end
@@ -23,9 +23,12 @@ module RSpec
23
23
  module_function
24
24
 
25
25
  # @private
26
- CONFIG_COLORS_TO_METHODS = Configuration.instance_methods.grep(/_color\z/).inject({}) do |hash, method|
27
- hash[method.to_s.sub(/_color\z/, '').to_sym] = method
28
- hash
26
+ def config_colors_to_methods
27
+ @config_colors_to_methods ||=
28
+ Configuration.instance_methods.grep(/_color\z/).inject({}) do |hash, method|
29
+ hash[method.to_s.sub(/_color\z/, '').to_sym] = method
30
+ hash
31
+ end
29
32
  end
30
33
 
31
34
  # Fetches the correct code for the supplied symbol, or checks
@@ -34,7 +37,7 @@ module RSpec
34
37
  # @param code_or_symbol [Symbol, Fixnum] Symbol or code to check
35
38
  # @return [Fixnum] a console code
36
39
  def console_code_for(code_or_symbol)
37
- if (config_method = CONFIG_COLORS_TO_METHODS[code_or_symbol])
40
+ if (config_method = config_colors_to_methods[code_or_symbol])
38
41
  console_code_for RSpec.configuration.__send__(config_method)
39
42
  elsif VT100_CODE_VALUES.key?(code_or_symbol)
40
43
  code_or_symbol
@@ -59,7 +59,10 @@ module RSpec
59
59
 
60
60
  DEPRECATION_STREAM_NOTICE = "Pass `--deprecation-out` or set " \
61
61
  "`config.deprecation_stream` to a file for full output."
62
+ TOO_MANY_WARNINGS_NOTICE = "Too many similar deprecation messages " \
63
+ "reported, disregarding further reports. #{DEPRECATION_STREAM_NOTICE}"
62
64
 
65
+ # @private
63
66
  SpecifiedDeprecationMessage = Struct.new(:type) do
64
67
  def initialize(data)
65
68
  @message = data.message
@@ -71,16 +74,14 @@ module RSpec
71
74
  end
72
75
 
73
76
  def too_many_warnings_message
74
- msg = "Too many similar deprecation messages reported, disregarding further reports. "
75
- msg << DEPRECATION_STREAM_NOTICE
76
- msg
77
+ TOO_MANY_WARNINGS_NOTICE
77
78
  end
78
79
 
79
80
  private
80
81
 
81
82
  def output_formatted(str)
82
83
  return str unless str.lines.count > 1
83
- separator = "#{'-' * 80}"
84
+ separator = '-' * 80
84
85
  "#{separator}\n#{str.chomp}\n#{separator}"
85
86
  end
86
87
 
@@ -89,6 +90,7 @@ module RSpec
89
90
  end
90
91
  end
91
92
 
93
+ # @private
92
94
  GeneratedDeprecationMessage = Struct.new(:type) do
93
95
  def initialize(data)
94
96
  @data = data
@@ -96,16 +98,14 @@ module RSpec
96
98
  end
97
99
 
98
100
  def to_s
99
- msg = "#{@data.deprecated} is deprecated."
101
+ msg = String.new("#{@data.deprecated} is deprecated.")
100
102
  msg << " Use #{@data.replacement} instead." if @data.replacement
101
- msg << " Called from #{@data.call_site}." if @data.call_site
103
+ msg << " Called from #{@data.call_site}." if @data.call_site
102
104
  msg
103
105
  end
104
106
 
105
107
  def too_many_warnings_message
106
- msg = "Too many uses of deprecated '#{type}'. "
107
- msg << DEPRECATION_STREAM_NOTICE
108
- msg
108
+ "Too many uses of deprecated '#{type}'. #{DEPRECATION_STREAM_NOTICE}"
109
109
  end
110
110
  end
111
111
 
@@ -1,16 +1,24 @@
1
1
  RSpec::Support.require_rspec_core "formatters/base_text_formatter"
2
+ RSpec::Support.require_rspec_core "formatters/console_codes"
2
3
 
3
4
  module RSpec
4
5
  module Core
5
6
  module Formatters
6
7
  # @private
7
8
  class DocumentationFormatter < BaseTextFormatter
8
- Formatters.register self, :example_group_started, :example_group_finished,
9
+ Formatters.register self, :example_started, :example_group_started, :example_group_finished,
9
10
  :example_passed, :example_pending, :example_failed
10
11
 
11
12
  def initialize(output)
12
13
  super
13
14
  @group_level = 0
15
+
16
+ @example_running = false
17
+ @messages = []
18
+ end
19
+
20
+ def example_started(_notification)
21
+ @example_running = true
14
22
  end
15
23
 
16
24
  def example_group_started(notification)
@@ -21,24 +29,49 @@ module RSpec
21
29
  end
22
30
 
23
31
  def example_group_finished(_notification)
24
- @group_level -= 1
32
+ @group_level -= 1 if @group_level > 0
25
33
  end
26
34
 
27
35
  def example_passed(passed)
28
36
  output.puts passed_output(passed.example)
37
+
38
+ flush_messages
39
+ @example_running = false
29
40
  end
30
41
 
31
42
  def example_pending(pending)
32
43
  output.puts pending_output(pending.example,
33
44
  pending.example.execution_result.pending_message)
45
+
46
+ flush_messages
47
+ @example_running = false
34
48
  end
35
49
 
36
50
  def example_failed(failure)
37
51
  output.puts failure_output(failure.example)
52
+
53
+ flush_messages
54
+ @example_running = false
55
+ end
56
+
57
+ def message(notification)
58
+ if @example_running
59
+ @messages << notification.message
60
+ else
61
+ output.puts "#{current_indentation}#{notification.message}"
62
+ end
38
63
  end
39
64
 
40
65
  private
41
66
 
67
+ def flush_messages
68
+ @messages.each do |message|
69
+ output.puts "#{current_indentation(1)}#{message}"
70
+ end
71
+
72
+ @messages.clear
73
+ end
74
+
42
75
  def passed_output(example)
43
76
  ConsoleCodes.wrap("#{current_indentation}#{example.description.strip}", :success)
44
77
  end
@@ -60,8 +93,8 @@ module RSpec
60
93
  @next_failure_index += 1
61
94
  end
62
95
 
63
- def current_indentation
64
- ' ' * @group_level
96
+ def current_indentation(offset=0)
97
+ ' ' * (@group_level + offset)
65
98
  end
66
99
  end
67
100
  end
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
+ RSpec::Support.require_rspec_core "formatters/console_codes"
2
3
  RSpec::Support.require_rspec_core "formatters/snippet_extractor"
4
+ RSpec::Support.require_rspec_core 'formatters/syntax_highlighter'
3
5
  RSpec::Support.require_rspec_support "encoded_string"
4
6
 
5
7
  module RSpec
@@ -79,7 +81,7 @@ module RSpec
79
81
 
80
82
  def fully_formatted_lines(failure_number, colorizer)
81
83
  lines = [
82
- description,
84
+ encoded_description(description),
83
85
  detail_formatter.call(example, colorizer),
84
86
  formatted_message_and_backtrace(colorizer),
85
87
  extra_detail_formatter.call(failure_number, colorizer),
@@ -214,7 +216,7 @@ module RSpec
214
216
  file_path, line_number = file_and_line_number[1..2]
215
217
  max_line_count = RSpec.configuration.max_displayed_failure_line_count
216
218
  lines = SnippetExtractor.extract_expression_lines_at(file_path, line_number.to_i, max_line_count)
217
- RSpec.world.source_cache.syntax_highlighter.highlight(lines)
219
+ RSpec.world.syntax_highlighter.highlight(lines)
218
220
  rescue SnippetExtractor::NoSuchFileError
219
221
  ["Unable to find #{file_path} to read failed line"]
220
222
  rescue SnippetExtractor::NoSuchLineError
@@ -242,6 +244,17 @@ module RSpec
242
244
  end
243
245
  end
244
246
 
247
+ if String.method_defined?(:encoding)
248
+ def encoded_description(description)
249
+ return if description.nil?
250
+ encoded_string(description)
251
+ end
252
+ else # for 1.8.7
253
+ def encoded_description(description)
254
+ description
255
+ end
256
+ end
257
+
245
258
  def exception_backtrace
246
259
  exception.backtrace || []
247
260
  end
@@ -278,7 +291,7 @@ module RSpec
278
291
  :description => "#{@example.full_description} FIXED",
279
292
  :message_color => RSpec.configuration.fixed_color,
280
293
  :failure_lines => [
281
- "Expected pending '#{@execution_result.pending_message}' to fail. No Error was raised."
294
+ "Expected pending '#{@execution_result.pending_message}' to fail. No error was raised."
282
295
  ]
283
296
  }
284
297
  elsif @execution_result.status == :pending
@@ -345,7 +358,10 @@ module RSpec
345
358
 
346
359
  failure = common_backtrace_truncater.with_truncated_backtrace(failure)
347
360
  presenter = ExceptionPresenter.new(failure, @example, options)
348
- presenter.fully_formatted_lines("#{failure_number}.#{index + 1}", colorizer)
361
+ presenter.fully_formatted_lines(
362
+ "#{failure_number ? "#{failure_number}." : ''}#{index + 1}",
363
+ colorizer
364
+ )
349
365
  end
350
366
  end
351
367
  end
@@ -374,6 +390,7 @@ module RSpec
374
390
  parent_bt[index] != child_bt[index]
375
391
  end
376
392
 
393
+ return child if index_before_first_common_frame.nil?
377
394
  return child if index_before_first_common_frame == -1
378
395
 
379
396
  child = child.dup
@@ -0,0 +1,23 @@
1
+ RSpec::Support.require_rspec_core "formatters/base_formatter"
2
+
3
+ module RSpec
4
+ module Core
5
+ module Formatters
6
+ # @private
7
+ class FailureListFormatter < BaseFormatter
8
+ Formatters.register self, :example_failed, :dump_profile, :message
9
+
10
+ def example_failed(failure)
11
+ output.puts "#{failure.example.location}:#{failure.example.description}"
12
+ end
13
+
14
+ # Discard profile and messages
15
+ #
16
+ # These outputs are not really relevant in the context of this failure
17
+ # list formatter.
18
+ def dump_profile(_profile); end
19
+ def message(_message); end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -69,10 +69,12 @@ module RSpec
69
69
  example = failure.example
70
70
 
71
71
  exception = failure.exception
72
+ message_lines = failure.fully_formatted_lines(nil, RSpec::Core::Notifications::NullColorizer)
72
73
  exception_details = if exception
73
74
  {
74
- :message => failure.message_lines.join("\n"),
75
- :backtrace => failure.formatted_backtrace.join("\n")
75
+ # drop 2 removes the description (regardless of newlines) and leading blank line
76
+ :message => message_lines.drop(2).join("\n"),
77
+ :backtrace => failure.formatted_backtrace.join("\n"),
76
78
  }
77
79
  end
78
80
  extra = extra_failure_content(failure)
@@ -59,7 +59,9 @@ module RSpec
59
59
  end
60
60
 
61
61
  def print_summary(duration, example_count, failure_count, pending_count)
62
- totals = "#{example_count} example#{'s' unless example_count == 1}, "
62
+ totals = String.new(
63
+ "#{example_count} example#{'s' unless example_count == 1}, "
64
+ )
63
65
  totals << "#{failure_count} failure#{'s' unless failure_count == 1}"
64
66
  totals << ", #{pending_count} pending" if pending_count > 0
65
67
 
@@ -113,7 +115,6 @@ module RSpec
113
115
  "style=\"margin-left: #{(number_of_parents - 1) * 15}px;\""
114
116
  end
115
117
 
116
- # rubocop:disable LineLength
117
118
  REPORT_HEADER = <<-EOF
118
119
  <div class="rspec-report">
119
120
 
@@ -137,7 +138,6 @@ module RSpec
137
138
 
138
139
  <div class="results">
139
140
  EOF
140
- # rubocop:enable LineLength
141
141
 
142
142
  GLOBAL_SCRIPTS = <<-EOF
143
143