rspec-core 3.7.1 → 3.8.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 (38) 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 +29 -0
  5. data/README.md +1 -1
  6. data/lib/rspec/core/bisect/coordinator.rb +26 -30
  7. data/lib/rspec/core/bisect/example_minimizer.rb +12 -8
  8. data/lib/rspec/core/bisect/fork_runner.rb +134 -0
  9. data/lib/rspec/core/bisect/server.rb +4 -13
  10. data/lib/rspec/core/bisect/{runner.rb → shell_command.rb} +27 -70
  11. data/lib/rspec/core/bisect/shell_runner.rb +73 -0
  12. data/lib/rspec/core/bisect/utilities.rb +58 -0
  13. data/lib/rspec/core/configuration.rb +133 -53
  14. data/lib/rspec/core/configuration_options.rb +41 -4
  15. data/lib/rspec/core/example.rb +4 -4
  16. data/lib/rspec/core/example_group.rb +1 -0
  17. data/lib/rspec/core/formatters.rb +10 -6
  18. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  19. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  20. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +29 -16
  21. data/lib/rspec/core/formatters/deprecation_formatter.rb +3 -1
  22. data/lib/rspec/core/formatters/exception_presenter.rb +1 -0
  23. data/lib/rspec/core/formatters/html_printer.rb +0 -2
  24. data/lib/rspec/core/formatters/protocol.rb +17 -17
  25. data/lib/rspec/core/formatters/syntax_highlighter.rb +19 -19
  26. data/lib/rspec/core/hooks.rb +1 -3
  27. data/lib/rspec/core/invocations.rb +8 -6
  28. data/lib/rspec/core/memoized_helpers.rb +2 -2
  29. data/lib/rspec/core/profiler.rb +3 -1
  30. data/lib/rspec/core/reporter.rb +3 -6
  31. data/lib/rspec/core/runner.rb +20 -14
  32. data/lib/rspec/core/shared_example_group.rb +1 -3
  33. data/lib/rspec/core/shell_escape.rb +2 -2
  34. data/lib/rspec/core/version.rb +1 -1
  35. data/lib/rspec/core/world.rb +11 -0
  36. metadata +12 -8
  37. metadata.gz.sig +0 -0
  38. data/lib/rspec/core/formatters/bisect_formatter.rb +0 -69
@@ -4,8 +4,9 @@ require 'shellwords'
4
4
  module RSpec
5
5
  module Core
6
6
  # Responsible for utilizing externally provided configuration options,
7
- # whether via the command line, `.rspec`, `~/.rspec`, `.rspec-local`
8
- # or a custom options file.
7
+ # whether via the command line, `.rspec`, `~/.rspec`,
8
+ # `$XDG_CONFIG_HOME/rspec/options`, `.rspec-local` or a custom options
9
+ # file.
9
10
  class ConfigurationOptions
10
11
  # @param args [Array<String>] command line arguments
11
12
  def initialize(args)
@@ -118,7 +119,11 @@ module RSpec
118
119
  end
119
120
 
120
121
  def file_options
121
- custom_options_file ? [custom_options] : [global_options, project_options, local_options]
122
+ if custom_options_file
123
+ [custom_options]
124
+ else
125
+ [global_options, project_options, local_options]
126
+ end
122
127
  end
123
128
 
124
129
  def env_options
@@ -168,7 +173,11 @@ module RSpec
168
173
  end
169
174
 
170
175
  def options_file_as_erb_string(path)
171
- ERB.new(File.read(path), nil, '-').result(binding)
176
+ if RUBY_VERSION >= '2.6'
177
+ ERB.new(File.read(path), :trim_mode => '-').result(binding)
178
+ else
179
+ ERB.new(File.read(path), nil, '-').result(binding)
180
+ end
172
181
  end
173
182
 
174
183
  def custom_options_file
@@ -184,6 +193,17 @@ module RSpec
184
193
  end
185
194
 
186
195
  def global_options_file
196
+ xdg_options_file_if_exists || home_options_file_path
197
+ end
198
+
199
+ def xdg_options_file_if_exists
200
+ path = xdg_options_file_path
201
+ if path && File.exist?(path)
202
+ path
203
+ end
204
+ end
205
+
206
+ def home_options_file_path
187
207
  File.join(File.expand_path("~"), ".rspec")
188
208
  rescue ArgumentError
189
209
  # :nocov:
@@ -191,6 +211,23 @@ module RSpec
191
211
  nil
192
212
  # :nocov:
193
213
  end
214
+
215
+ def xdg_options_file_path
216
+ xdg_config_home = resolve_xdg_config_home
217
+ if xdg_config_home
218
+ File.join(xdg_config_home, "rspec", "options")
219
+ end
220
+ end
221
+
222
+ def resolve_xdg_config_home
223
+ File.expand_path(ENV.fetch("XDG_CONFIG_HOME", "~/.config"))
224
+ rescue ArgumentError
225
+ # :nocov:
226
+ # On Ruby 2.4, `File.expand("~")` works even if `ENV['HOME']` is not set.
227
+ # But on earlier versions, it fails.
228
+ nil
229
+ # :nocov:
230
+ end
194
231
  end
195
232
  end
196
233
  end
@@ -393,7 +393,7 @@ module RSpec
393
393
  end
394
394
  end
395
395
 
396
- # rubocop:disable Style/AccessorMethodName
396
+ # rubocop:disable Naming/AccessorMethodName
397
397
 
398
398
  # @private
399
399
  #
@@ -420,7 +420,7 @@ module RSpec
420
420
  self.display_exception = exception
421
421
  end
422
422
 
423
- # rubocop:enable Style/AccessorMethodName
423
+ # rubocop:enable Naming/AccessorMethodName
424
424
 
425
425
  # @private
426
426
  #
@@ -642,12 +642,12 @@ module RSpec
642
642
  @reporter = reporter
643
643
  end
644
644
 
645
- # rubocop:disable Style/AccessorMethodName
645
+ # rubocop:disable Naming/AccessorMethodName
646
646
  def set_exception(exception)
647
647
  reporter.notify_non_example_exception(exception, "An error occurred in #{description}.")
648
648
  RSpec.world.wants_to_quit = true
649
649
  end
650
- # rubocop:enable Style/AccessorMethodName
650
+ # rubocop:enable Naming/AccessorMethodName
651
651
  end
652
652
  end
653
653
  end
@@ -107,6 +107,7 @@ module RSpec
107
107
  # @private
108
108
  # @macro [attach] define_example_method
109
109
  # @!scope class
110
+ # @method $1
110
111
  # @overload $1
111
112
  # @overload $1(&example_implementation)
112
113
  # @param example_implementation [Block] The implementation of the example.
@@ -72,7 +72,7 @@ module RSpec::Core::Formatters
72
72
  autoload :ProgressFormatter, 'rspec/core/formatters/progress_formatter'
73
73
  autoload :ProfileFormatter, 'rspec/core/formatters/profile_formatter'
74
74
  autoload :JsonFormatter, 'rspec/core/formatters/json_formatter'
75
- autoload :BisectFormatter, 'rspec/core/formatters/bisect_formatter'
75
+ autoload :BisectDRbFormatter, 'rspec/core/formatters/bisect_drb_formatter'
76
76
  autoload :ExceptionPresenter, 'rspec/core/formatters/exception_presenter'
77
77
 
78
78
  # Register the formatter class
@@ -133,9 +133,6 @@ module RSpec::Core::Formatters
133
133
  end
134
134
 
135
135
  return unless RSpec.configuration.profile_examples?
136
-
137
- @reporter.setup_profiler
138
-
139
136
  return if existing_formatter_implements?(:dump_profile)
140
137
 
141
138
  add RSpec::Core::Formatters::ProfileFormatter, output_stream
@@ -143,6 +140,13 @@ module RSpec::Core::Formatters
143
140
 
144
141
  # @private
145
142
  def add(formatter_to_use, *paths)
143
+ # If a formatter instance was passed, we can register it directly,
144
+ # with no need for any of the further processing that happens below.
145
+ if Loader.formatters.key?(formatter_to_use.class)
146
+ register formatter_to_use, notifications_for(formatter_to_use.class)
147
+ return
148
+ end
149
+
146
150
  formatter_class = find_formatter(formatter_to_use)
147
151
 
148
152
  args = paths.map { |p| p.respond_to?(:puts) ? p : open_stream(p) }
@@ -206,8 +210,8 @@ module RSpec::Core::Formatters
206
210
  ProgressFormatter
207
211
  when 'j', 'json'
208
212
  JsonFormatter
209
- when 'bisect'
210
- BisectFormatter
213
+ when 'bisect-drb'
214
+ BisectDRbFormatter
211
215
  end
212
216
  end
213
217
 
@@ -0,0 +1,45 @@
1
+ RSpec::Support.require_rspec_core "bisect/utilities"
2
+
3
+ module RSpec
4
+ module Core
5
+ module Formatters
6
+ # Contains common logic for formatters used by `--bisect` to communicate results
7
+ # back to the bisect runner.
8
+ #
9
+ # Subclasses must define a `notify_results(all_example_ids, failed_example_ids)`
10
+ # method.
11
+ # @private
12
+ class BaseBisectFormatter
13
+ def self.inherited(formatter)
14
+ Formatters.register formatter, :start_dump, :example_failed, :example_finished
15
+ end
16
+
17
+ def initialize(expected_failures)
18
+ @all_example_ids = []
19
+ @failed_example_ids = []
20
+ @remaining_failures = expected_failures
21
+ end
22
+
23
+ def example_failed(notification)
24
+ @failed_example_ids << notification.example.id
25
+ end
26
+
27
+ def example_finished(notification)
28
+ @all_example_ids << notification.example.id
29
+ return unless @remaining_failures.include?(notification.example.id)
30
+ @remaining_failures.delete(notification.example.id)
31
+
32
+ status = notification.example.execution_result.status
33
+ return if status == :failed && !@remaining_failures.empty?
34
+ RSpec.world.wants_to_quit = true
35
+ end
36
+
37
+ def start_dump(_notification)
38
+ # `notify_results` is defined in the subclass
39
+ notify_results(Bisect::ExampleSetDescriptor.new(
40
+ @all_example_ids, @failed_example_ids))
41
+ end
42
+ end
43
+ end
44
+ end
45
+ 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
@@ -62,6 +62,7 @@ module RSpec
62
62
  TOO_MANY_WARNINGS_NOTICE = "Too many similar deprecation messages " \
63
63
  "reported, disregarding further reports. #{DEPRECATION_STREAM_NOTICE}"
64
64
 
65
+ # @private
65
66
  SpecifiedDeprecationMessage = Struct.new(:type) do
66
67
  def initialize(data)
67
68
  @message = data.message
@@ -80,7 +81,7 @@ module RSpec
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
@@ -379,6 +379,7 @@ module RSpec
379
379
  parent_bt[index] != child_bt[index]
380
380
  end
381
381
 
382
+ return child if index_before_first_common_frame.nil?
382
383
  return child if index_before_first_common_frame == -1
383
384
 
384
385
  child = child.dup
@@ -115,7 +115,6 @@ module RSpec
115
115
  "style=\"margin-left: #{(number_of_parents - 1) * 15}px;\""
116
116
  end
117
117
 
118
- # rubocop:disable LineLength
119
118
  REPORT_HEADER = <<-EOF
120
119
  <div class="rspec-report">
121
120
 
@@ -139,7 +138,6 @@ module RSpec
139
138
 
140
139
  <div class="results">
141
140
  EOF
142
- # rubocop:enable LineLength
143
141
 
144
142
  GLOBAL_SCRIPTS = <<-EOF
145
143
 
@@ -17,12 +17,12 @@ module RSpec
17
17
  # @see RSpec::Core::Formatters::BaseTextFormatter
18
18
  # @see RSpec::Core::Reporter
19
19
  class Protocol
20
- # @method initialize
20
+ # @method initialize(output)
21
21
  # @api public
22
22
  #
23
23
  # @param output [IO] the formatter output
24
24
 
25
- # @method start
25
+ # @method start(notification)
26
26
  # @api public
27
27
  # @group Suite Notifications
28
28
  #
@@ -35,7 +35,7 @@ module RSpec
35
35
  #
36
36
  # @param notification [Notifications::StartNotification]
37
37
 
38
- # @method example_group_started
38
+ # @method example_group_started(notification)
39
39
  # @api public
40
40
  # @group Group Notifications
41
41
  #
@@ -48,7 +48,7 @@ module RSpec
48
48
  # @param notification [Notifications::GroupNotification] containing example_group
49
49
  # subclass of {ExampleGroup}
50
50
 
51
- # @method example_group_finished
51
+ # @method example_group_finished(notification)
52
52
  # @api public
53
53
  # @group Group Notifications
54
54
  #
@@ -57,7 +57,7 @@ module RSpec
57
57
  # @param notification [Notifications::GroupNotification] containing example_group
58
58
  # subclass of {ExampleGroup}
59
59
 
60
- # @method example_started
60
+ # @method example_started(notification)
61
61
  # @api public
62
62
  # @group Example Notifications
63
63
  #
@@ -66,7 +66,7 @@ module RSpec
66
66
  # @param notification [Notifications::ExampleNotification] containing example subclass
67
67
  # of {Example}
68
68
 
69
- # @method example_finished
69
+ # @method example_finished(notification)
70
70
  # @api public
71
71
  # @group Example Notifications
72
72
  #
@@ -75,7 +75,7 @@ module RSpec
75
75
  # @param notification [Notifications::ExampleNotification] containing example subclass
76
76
  # of {Example}
77
77
 
78
- # @method example_passed
78
+ # @method example_passed(notification)
79
79
  # @api public
80
80
  # @group Example Notifications
81
81
  #
@@ -84,7 +84,7 @@ module RSpec
84
84
  # @param notification [Notifications::ExampleNotification] containing example subclass
85
85
  # of {Example}
86
86
 
87
- # @method example_pending
87
+ # @method example_pending(notification)
88
88
  # @api public
89
89
  # @group Example Notifications
90
90
  #
@@ -93,7 +93,7 @@ module RSpec
93
93
  # @param notification [Notifications::ExampleNotification] containing example subclass
94
94
  # of {Example}
95
95
 
96
- # @method example_failed
96
+ # @method example_failed(notification)
97
97
  # @api public
98
98
  # @group Example Notifications
99
99
  #
@@ -102,7 +102,7 @@ module RSpec
102
102
  # @param notification [Notifications::ExampleNotification] containing example subclass
103
103
  # of {Example}
104
104
 
105
- # @method message
105
+ # @method message(notification)
106
106
  # @api public
107
107
  # @group Suite Notifications
108
108
  #
@@ -110,7 +110,7 @@ module RSpec
110
110
  #
111
111
  # @param notification [Notifications::MessageNotification] containing message
112
112
 
113
- # @method stop
113
+ # @method stop(notification)
114
114
  # @api public
115
115
  # @group Suite Notifications
116
116
  #
@@ -119,7 +119,7 @@ module RSpec
119
119
  #
120
120
  # @param notification [Notifications::NullNotification]
121
121
 
122
- # @method start_dump
122
+ # @method start_dump(notification)
123
123
  # @api public
124
124
  # @group Suite Notifications
125
125
  #
@@ -130,7 +130,7 @@ module RSpec
130
130
  #
131
131
  # @param notification [Notifications::NullNotification]
132
132
 
133
- # @method dump_failures
133
+ # @method dump_failures(notification)
134
134
  # @api public
135
135
  # @group Suite Notifications
136
136
  #
@@ -138,7 +138,7 @@ module RSpec
138
138
  #
139
139
  # @param notification [Notifications::NullNotification]
140
140
 
141
- # @method dump_summary
141
+ # @method dump_summary(summary)
142
142
  # @api public
143
143
  # @group Suite Notifications
144
144
  #
@@ -148,7 +148,7 @@ module RSpec
148
148
  # @param summary [Notifications::SummaryNotification] containing duration,
149
149
  # example_count, failure_count and pending_count
150
150
 
151
- # @method dump_profile
151
+ # @method dump_profile(profile)
152
152
  # @api public
153
153
  # @group Suite Notifications
154
154
  #
@@ -158,7 +158,7 @@ module RSpec
158
158
  # @param profile [Notifications::ProfileNotification] containing duration,
159
159
  # slowest_examples and slowest_example_groups
160
160
 
161
- # @method dump_pending
161
+ # @method dump_pending(notification)
162
162
  # @api public
163
163
  # @group Suite Notifications
164
164
  #
@@ -167,7 +167,7 @@ module RSpec
167
167
  #
168
168
  # @param notification [Notifications::NullNotification]
169
169
 
170
- # @method close
170
+ # @method close(notification)
171
171
  # @api public
172
172
  # @group Suite Notifications
173
173
  #