rspec-core 3.5.4 → 3.9.0

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.
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