rspec-core 3.0.4 → 3.12.2

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 (85) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +2 -1
  5. data/Changelog.md +888 -2
  6. data/{License.txt → LICENSE.md} +6 -5
  7. data/README.md +165 -24
  8. data/lib/rspec/autorun.rb +1 -0
  9. data/lib/rspec/core/backtrace_formatter.rb +19 -20
  10. data/lib/rspec/core/bisect/coordinator.rb +62 -0
  11. data/lib/rspec/core/bisect/example_minimizer.rb +173 -0
  12. data/lib/rspec/core/bisect/fork_runner.rb +138 -0
  13. data/lib/rspec/core/bisect/server.rb +61 -0
  14. data/lib/rspec/core/bisect/shell_command.rb +126 -0
  15. data/lib/rspec/core/bisect/shell_runner.rb +73 -0
  16. data/lib/rspec/core/bisect/utilities.rb +69 -0
  17. data/lib/rspec/core/configuration.rb +1287 -246
  18. data/lib/rspec/core/configuration_options.rb +95 -35
  19. data/lib/rspec/core/did_you_mean.rb +46 -0
  20. data/lib/rspec/core/drb.rb +21 -12
  21. data/lib/rspec/core/dsl.rb +10 -6
  22. data/lib/rspec/core/example.rb +305 -113
  23. data/lib/rspec/core/example_group.rb +431 -223
  24. data/lib/rspec/core/example_status_persister.rb +235 -0
  25. data/lib/rspec/core/filter_manager.rb +86 -115
  26. data/lib/rspec/core/flat_map.rb +6 -4
  27. data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
  28. data/lib/rspec/core/formatters/base_formatter.rb +14 -116
  29. data/lib/rspec/core/formatters/base_text_formatter.rb +18 -21
  30. data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
  31. data/lib/rspec/core/formatters/bisect_progress_formatter.rb +157 -0
  32. data/lib/rspec/core/formatters/console_codes.rb +29 -18
  33. data/lib/rspec/core/formatters/deprecation_formatter.rb +16 -16
  34. data/lib/rspec/core/formatters/documentation_formatter.rb +49 -16
  35. data/lib/rspec/core/formatters/exception_presenter.rb +525 -0
  36. data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
  37. data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
  38. data/lib/rspec/core/formatters/helpers.rb +45 -15
  39. data/lib/rspec/core/formatters/html_formatter.rb +33 -28
  40. data/lib/rspec/core/formatters/html_printer.rb +30 -20
  41. data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
  42. data/lib/rspec/core/formatters/json_formatter.rb +18 -9
  43. data/lib/rspec/core/formatters/profile_formatter.rb +10 -9
  44. data/lib/rspec/core/formatters/progress_formatter.rb +5 -4
  45. data/lib/rspec/core/formatters/protocol.rb +182 -0
  46. data/lib/rspec/core/formatters/snippet_extractor.rb +113 -82
  47. data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
  48. data/lib/rspec/core/formatters.rb +81 -41
  49. data/lib/rspec/core/hooks.rb +314 -244
  50. data/lib/rspec/core/invocations.rb +87 -0
  51. data/lib/rspec/core/memoized_helpers.rb +161 -51
  52. data/lib/rspec/core/metadata.rb +132 -61
  53. data/lib/rspec/core/metadata_filter.rb +224 -64
  54. data/lib/rspec/core/minitest_assertions_adapter.rb +6 -3
  55. data/lib/rspec/core/mocking_adapters/flexmock.rb +4 -2
  56. data/lib/rspec/core/mocking_adapters/mocha.rb +11 -9
  57. data/lib/rspec/core/mocking_adapters/null.rb +2 -0
  58. data/lib/rspec/core/mocking_adapters/rr.rb +3 -1
  59. data/lib/rspec/core/mocking_adapters/rspec.rb +3 -1
  60. data/lib/rspec/core/notifications.rb +192 -206
  61. data/lib/rspec/core/option_parser.rb +174 -69
  62. data/lib/rspec/core/ordering.rb +48 -35
  63. data/lib/rspec/core/output_wrapper.rb +29 -0
  64. data/lib/rspec/core/pending.rb +25 -33
  65. data/lib/rspec/core/profiler.rb +34 -0
  66. data/lib/rspec/core/project_initializer/.rspec +0 -2
  67. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +59 -39
  68. data/lib/rspec/core/project_initializer.rb +5 -3
  69. data/lib/rspec/core/rake_task.rb +99 -55
  70. data/lib/rspec/core/reporter.rb +128 -15
  71. data/lib/rspec/core/ruby_project.rb +14 -6
  72. data/lib/rspec/core/runner.rb +96 -45
  73. data/lib/rspec/core/sandbox.rb +37 -0
  74. data/lib/rspec/core/set.rb +54 -0
  75. data/lib/rspec/core/shared_example_group.rb +133 -43
  76. data/lib/rspec/core/shell_escape.rb +49 -0
  77. data/lib/rspec/core/test_unit_assertions_adapter.rb +4 -4
  78. data/lib/rspec/core/version.rb +1 -1
  79. data/lib/rspec/core/warnings.rb +6 -6
  80. data/lib/rspec/core/world.rb +172 -68
  81. data/lib/rspec/core.rb +66 -21
  82. data.tar.gz.sig +0 -0
  83. metadata +93 -69
  84. metadata.gz.sig +0 -0
  85. data/lib/rspec/core/backport_random.rb +0 -336
@@ -4,30 +4,47 @@ require 'optparse'
4
4
  module RSpec::Core
5
5
  # @private
6
6
  class Parser
7
- def self.parse(args)
8
- new.parse(args)
7
+ def self.parse(args, source=nil)
8
+ new(args).parse(source)
9
9
  end
10
10
 
11
- def parse(args)
12
- return {} if args.empty?
11
+ attr_reader :original_args
13
12
 
14
- options = args.delete('--tty') ? {:tty => true} : {}
13
+ def initialize(original_args)
14
+ @original_args = original_args
15
+ end
16
+
17
+ def parse(source=nil)
18
+ return { :files_or_directories_to_run => [] } if original_args.empty?
19
+ args = original_args.dup
20
+
21
+ options = args.delete('--tty') ? { :tty => true } : {}
15
22
  begin
16
23
  parser(options).parse!(args)
17
24
  rescue OptionParser::InvalidOption => e
18
- abort "#{e.message}\n\nPlease use --help for a listing of valid options"
25
+ abort "#{e.message}#{" (defined in #{source})" if source}\n\n" \
26
+ "Please use --help for a listing of valid options"
19
27
  end
20
28
 
29
+ options[:files_or_directories_to_run] = args
21
30
  options
22
31
  end
23
32
 
33
+ private
34
+
35
+ # rubocop:disable Metrics/AbcSize
36
+ # rubocop:disable Metrics/MethodLength
37
+ # rubocop:disable Metrics/CyclomaticComplexity
38
+ # rubocop:disable Metrics/PerceivedComplexity
24
39
  def parser(options)
25
40
  OptionParser.new do |parser|
41
+ parser.summary_width = 34
42
+
26
43
  parser.banner = "Usage: rspec [options] [files or directories]\n\n"
27
44
 
28
- parser.on('-I PATH', 'Specify PATH to add to $LOAD_PATH (may be used more than once).') do |dir|
45
+ parser.on('-I PATH', 'Specify PATH to add to $LOAD_PATH (may be used more than once).') do |dirs|
29
46
  options[:libs] ||= []
30
- options[:libs] << dir
47
+ options[:libs].concat(dirs.split(File::PATH_SEPARATOR))
31
48
  end
32
49
 
33
50
  parser.on('-r', '--require PATH', 'Require a file.') do |path|
@@ -40,10 +57,11 @@ module RSpec::Core
40
57
  end
41
58
 
42
59
  parser.on('--order TYPE[:SEED]', 'Run examples by the specified order type.',
43
- ' [defined] examples and groups are run in the order they are defined',
44
- ' [rand] randomize the order of groups and examples',
45
- ' [random] alias for rand',
46
- ' [random:SEED] e.g. --order random:123') do |o|
60
+ ' [defined] examples and groups are run in the order they are defined',
61
+ ' [rand] randomize the order of groups and examples',
62
+ ' [random] alias for rand',
63
+ ' [random:SEED] e.g. --order random:123',
64
+ ' [recently-modified] run the most recently modified files first') do |o|
47
65
  options[:order] = o
48
66
  end
49
67
 
@@ -51,45 +69,55 @@ module RSpec::Core
51
69
  options[:order] = "rand:#{seed}"
52
70
  end
53
71
 
54
- parser.on('--fail-fast', 'Abort the run on first failure.') do |o|
55
- options[:fail_fast] = true
72
+ parser.on('--bisect[=verbose]', 'Repeatedly runs the suite in order to isolate the failures to the ',
73
+ ' smallest reproducible case.') do |argument|
74
+ options[:bisect] = argument || true
75
+ options[:runner] = RSpec::Core::Invocations::Bisect.new
56
76
  end
57
77
 
58
- parser.on('--no-fail-fast', 'Do not abort the run on first failure.') do |o|
59
- options[:fail_fast] = false
78
+ parser.on('--[no-]fail-fast[=COUNT]', 'Abort the run after a certain number of failures (1 by default).') do |argument|
79
+ if argument == true
80
+ value = 1
81
+ elsif argument == false || argument == 0
82
+ value = false
83
+ else
84
+ begin
85
+ value = Integer(argument)
86
+ rescue ArgumentError
87
+ RSpec.warning "Expected an integer value for `--fail-fast`, got: #{argument.inspect}", :call_site => nil
88
+ end
89
+ end
90
+ set_fail_fast(options, value)
60
91
  end
61
92
 
62
- parser.on('--failure-exit-code CODE', Integer, 'Override the exit code used when there are failing specs.') do |code|
93
+ parser.on('--failure-exit-code CODE', Integer,
94
+ 'Override the exit code used when there are failing specs.') do |code|
63
95
  options[:failure_exit_code] = code
64
96
  end
65
97
 
66
- parser.on('--dry-run', 'Print the formatter output of your suite without',
67
- ' running any examples or hooks') do |o|
68
- options[:dry_run] = true
98
+ parser.on('--error-exit-code CODE', Integer,
99
+ 'Override the exit code used when there are errors loading or running specs outside of examples.') do |code|
100
+ options[:error_exit_code] = code
69
101
  end
70
102
 
71
- parser.on('-X', '--[no-]drb', 'Run examples via DRb.') do |o|
72
- options[:drb] = o
103
+ parser.on('-X', '--[no-]drb', 'Run examples via DRb.') do |use_drb|
104
+ options[:drb] = use_drb
105
+ options[:runner] = RSpec::Core::Invocations::DRbWithFallback.new if use_drb
73
106
  end
74
107
 
75
108
  parser.on('--drb-port PORT', 'Port to connect to the DRb server.') do |o|
76
109
  options[:drb_port] = o.to_i
77
110
  end
78
111
 
79
- parser.on('--init', 'Initialize your project with RSpec.') do |cmd|
80
- RSpec::Support.require_rspec_core "project_initializer"
81
- ProjectInitializer.new.run
82
- exit
83
- end
84
-
85
112
  parser.separator("\n **** Output ****\n\n")
86
113
 
87
114
  parser.on('-f', '--format FORMATTER', 'Choose a formatter.',
88
- ' [p]rogress (default - dots)',
89
- ' [d]ocumentation (group and example names)',
90
- ' [h]tml',
91
- ' [j]son',
92
- ' custom formatter class name') do |o|
115
+ ' [p]rogress (default - dots)',
116
+ ' [d]ocumentation (group and example names)',
117
+ ' [h]tml',
118
+ ' [j]son',
119
+ ' [f]ailures ("file:line:reason", suitable for editors integration)',
120
+ ' custom formatter class name') do |o|
93
121
  options[:formatters] ||= []
94
122
  options[:formatters] << [o]
95
123
  end
@@ -107,15 +135,32 @@ module RSpec::Core
107
135
  options[:deprecation_stream] = file
108
136
  end
109
137
 
110
- parser.on('-b', '--backtrace', 'Enable full backtrace.') do |o|
138
+ parser.on('-b', '--backtrace', 'Enable full backtrace.') do |_o|
111
139
  options[:full_backtrace] = true
112
140
  end
113
141
 
114
- parser.on('-c', '--[no-]color', '--[no-]colour', 'Enable color in the output.') do |o|
115
- options[:color] = o
142
+ parser.on('-c', '--color', '--colour', '') do |_o|
143
+ # flag will be excluded from `--help` output because it is deprecated
144
+ options[:color] = true
145
+ options[:color_mode] = :automatic
146
+ end
147
+
148
+ parser.on('--force-color', '--force-colour', 'Force the output to be in color, even if the output is not a TTY') do |_o|
149
+ if options[:color_mode] == :off
150
+ abort "Please only use one of `--force-color` and `--no-color`"
151
+ end
152
+ options[:color_mode] = :on
153
+ end
154
+
155
+ parser.on('--no-color', '--no-colour', 'Force the output to not be in color, even if the output is a TTY') do |_o|
156
+ if options[:color_mode] == :on
157
+ abort "Please only use one of --force-color and --no-color"
158
+ end
159
+ options[:color_mode] = :off
116
160
  end
117
161
 
118
- parser.on('-p', '--[no-]profile [COUNT]', 'Enable profiling of examples and list the slowest examples (default: 10).') do |argument|
162
+ parser.on('-p', '--[no-]profile [COUNT]',
163
+ 'Enable profiling of examples and list the slowest examples (default: 10).') do |argument|
119
164
  options[:profile_examples] = if argument.nil?
120
165
  true
121
166
  elsif argument == false
@@ -124,16 +169,24 @@ module RSpec::Core
124
169
  begin
125
170
  Integer(argument)
126
171
  rescue ArgumentError
127
- RSpec.warning "Non integer specified as profile count, seperate " +
128
- "your path from options with -- e.g. " +
172
+ RSpec.warning "Non integer specified as profile count, separate " \
173
+ "your path from options with -- e.g. " \
129
174
  "`rspec --profile -- #{argument}`",
130
- :call_site => nil
175
+ :call_site => nil
131
176
  true
132
177
  end
133
178
  end
134
179
  end
135
180
 
181
+ parser.on('--dry-run', 'Print the formatter output of your suite without',
182
+ ' running any examples or hooks') do |_o|
183
+ options[:dry_run] = true
184
+ end
185
+
136
186
  parser.on('-w', '--warnings', 'Enable ruby warnings') do
187
+ if Object.const_defined?(:Warning) && Warning.respond_to?(:[]=)
188
+ Warning[:deprecated] = true
189
+ end
137
190
  $VERBOSE = true
138
191
  end
139
192
 
@@ -141,23 +194,52 @@ module RSpec::Core
141
194
 
142
195
  **** Filtering/tags ****
143
196
 
144
- In addition to the following options for selecting specific files, groups,
145
- or examples, you can select a single example by appending the line number to
197
+ In addition to the following options for selecting specific files, groups, or
198
+ examples, you can select individual examples by appending the line number(s) to
146
199
  the filename:
147
200
 
148
- rspec path/to/a_spec.rb:37
201
+ rspec path/to/a_spec.rb:37:87
202
+
203
+ You can also pass example ids enclosed in square brackets:
204
+
205
+ rspec path/to/a_spec.rb[1:5,1:6] # run the 5th and 6th examples/groups defined in the 1st group
149
206
 
150
207
  FILTERING
151
208
 
209
+ parser.on('--only-failures', "Filter to just the examples that failed the last time they ran.") do
210
+ configure_only_failures(options)
211
+ end
212
+
213
+ parser.on("-n", "--next-failure", "Apply `--only-failures` and abort after one failure.",
214
+ " (Equivalent to `--only-failures --fail-fast --order defined`)") do
215
+ configure_only_failures(options)
216
+ set_fail_fast(options, 1)
217
+ options[:order] ||= 'defined'
218
+ end
219
+
152
220
  parser.on('-P', '--pattern PATTERN', 'Load files matching pattern (default: "spec/**/*_spec.rb").') do |o|
153
- options[:pattern] = o
221
+ if options[:pattern]
222
+ options[:pattern] += ',' + o
223
+ else
224
+ options[:pattern] = o
225
+ end
226
+ end
227
+
228
+ parser.on('--exclude-pattern PATTERN',
229
+ 'Load files except those matching pattern. Opposite effect of --pattern.') do |o|
230
+ options[:exclude_pattern] = o
154
231
  end
155
232
 
156
233
  parser.on('-e', '--example STRING', "Run examples whose full nested names include STRING (may be",
157
- " used more than once)") do |o|
234
+ " used more than once)") do |o|
158
235
  (options[:full_description] ||= []) << Regexp.compile(Regexp.escape(o))
159
236
  end
160
237
 
238
+ parser.on('-E', '--example-matches REGEX', "Run examples whose full nested names match REGEX (may be",
239
+ " used more than once)") do |o|
240
+ (options[:full_description] ||= []) << Regexp.compile(o)
241
+ end
242
+
161
243
  parser.on('-t', '--tag TAG[:VALUE]',
162
244
  'Run examples with the specified tag, or exclude examples',
163
245
  'by adding ~ before the tag.',
@@ -165,54 +247,77 @@ FILTERING
165
247
  ' - TAG is always converted to a symbol') do |tag|
166
248
  filter_type = tag =~ /^~/ ? :exclusion_filter : :inclusion_filter
167
249
 
168
- name,value = tag.gsub(/^(~@|~|@)/, '').split(':',2)
250
+ name, value = tag.gsub(/^(~@|~|@)/, '').split(':', 2)
169
251
  name = name.to_sym
170
252
 
171
- options[filter_type] ||= {}
172
- options[filter_type][name] = case value
173
- when nil then true # The default value for tags is true
174
- when 'true' then true
175
- when 'false' then false
176
- when 'nil' then nil
177
- when /^:/ then value[1..-1].to_sym
178
- when /^\d+$/ then Integer(value)
179
- when /^\d+.\d+$/ then Float(value)
180
- else
181
- value
182
- end
253
+ parsed_value = case value
254
+ when nil then true # The default value for tags is true
255
+ when 'true' then true
256
+ when 'false' then false
257
+ when 'nil' then nil
258
+ when /^:/ then value[1..-1].to_sym
259
+ when /^\d+$/ then Integer(value)
260
+ when /^\d+.\d+$/ then Float(value)
261
+ else
262
+ value
263
+ end
264
+
265
+ add_tag_filter(options, filter_type, name, parsed_value)
183
266
  end
184
267
 
185
268
  parser.on('--default-path PATH', 'Set the default path where RSpec looks for examples (can',
186
- ' be a path to a file or a directory).') do |path|
269
+ ' be a path to a file or a directory).') do |path|
187
270
  options[:default_path] = path
188
271
  end
189
272
 
190
273
  parser.separator("\n **** Utility ****\n\n")
191
274
 
275
+ parser.on('--init', 'Initialize your project with RSpec.') do |_cmd|
276
+ options[:runner] = RSpec::Core::Invocations::InitializeProject.new
277
+ end
278
+
192
279
  parser.on('-v', '--version', 'Display the version.') do
193
- puts RSpec::Core::Version::STRING
194
- exit
280
+ options[:runner] = RSpec::Core::Invocations::PrintVersion.new
195
281
  end
196
282
 
197
- # these options would otherwise be confusing to users, so we forcibly prevent them from executing
198
- # --I is too similar to -I
199
- # -d was a shorthand for --debugger, which is removed, but now would trigger --default-path
283
+ # These options would otherwise be confusing to users, so we forcibly
284
+ # prevent them from executing.
285
+ #
286
+ # * --I is too similar to -I.
287
+ # * -d was a shorthand for --debugger, which is removed, but now would
288
+ # trigger --default-path.
200
289
  invalid_options = %w[-d --I]
201
290
 
291
+ hidden_options = invalid_options + %w[-c]
292
+
202
293
  parser.on_tail('-h', '--help', "You're looking at it.") do
203
- # removing the blank invalid options from the output
204
- puts parser.to_s.gsub(/^\s+(#{invalid_options.join('|')})\s*$\n/,'')
205
- exit
294
+ options[:runner] = RSpec::Core::Invocations::PrintHelp.new(parser, hidden_options)
206
295
  end
207
296
 
208
- # this prevents usage of the invalid_options
297
+ # This prevents usage of the invalid_options.
209
298
  invalid_options.each do |option|
210
299
  parser.on(option) do
211
300
  raise OptionParser::InvalidOption.new
212
301
  end
213
302
  end
214
-
215
303
  end
216
304
  end
305
+ # rubocop:enable Metrics/AbcSize
306
+ # rubocop:enable Metrics/MethodLength
307
+ # rubocop:enable Metrics/CyclomaticComplexity
308
+ # rubocop:enable Metrics/PerceivedComplexity
309
+
310
+ def add_tag_filter(options, filter_type, tag_name, value=true)
311
+ (options[filter_type] ||= {})[tag_name] = value
312
+ end
313
+
314
+ def set_fail_fast(options, value)
315
+ options[:fail_fast] = value
316
+ end
317
+
318
+ def configure_only_failures(options)
319
+ options[:only_failures] = true
320
+ add_tag_filter(options, :inclusion_filter, :last_run_status, 'failed')
321
+ end
217
322
  end
218
323
  end
@@ -1,14 +1,5 @@
1
1
  module RSpec
2
2
  module Core
3
- if defined?(::Random)
4
- # @private
5
- RandomNumberGenerator = ::Random
6
- else
7
- RSpec::Support.require_rspec_core "backport_random"
8
- # @private
9
- RandomNumberGenerator = RSpec::Core::Backports::Random
10
- end
11
-
12
3
  # @private
13
4
  module Ordering
14
5
  # @private
@@ -33,25 +24,45 @@ module RSpec
33
24
 
34
25
  def order(items)
35
26
  @used = true
36
- rng = RandomNumberGenerator.new(@configuration.seed)
37
- shuffle items, rng
27
+
28
+ seed = @configuration.seed.to_s
29
+ items.sort_by { |item| jenkins_hash_digest(seed + item.id) }
38
30
  end
39
31
 
40
- if RUBY_VERSION > '1.9.3'
41
- def shuffle(list, rng)
42
- list.shuffle(:random => rng)
43
- end
44
- else
45
- def shuffle(list, rng)
46
- shuffled = list.dup
47
- shuffled.size.times do |i|
48
- j = i + rng.rand(shuffled.size - i)
49
- next if i == j
50
- shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
51
- end
52
-
53
- shuffled
32
+ private
33
+
34
+ # http://en.wikipedia.org/wiki/Jenkins_hash_function
35
+ # Jenkins provides a good distribution and is simpler than MD5.
36
+ # It's a bit slower than MD5 (primarily because `Digest::MD5` is
37
+ # implemented in C) but has the advantage of not requiring us
38
+ # to load another part of stdlib, which we try to minimize.
39
+ def jenkins_hash_digest(string)
40
+ hash = 0
41
+
42
+ string.each_byte do |byte|
43
+ hash += byte
44
+ hash &= MAX_32_BIT
45
+ hash += ((hash << 10) & MAX_32_BIT)
46
+ hash &= MAX_32_BIT
47
+ hash ^= hash >> 6
54
48
  end
49
+
50
+ hash += ((hash << 3) & MAX_32_BIT)
51
+ hash &= MAX_32_BIT
52
+ hash ^= hash >> 11
53
+ hash += ((hash << 15) & MAX_32_BIT)
54
+ hash &= MAX_32_BIT
55
+ hash
56
+ end
57
+
58
+ MAX_32_BIT = 4_294_967_295
59
+ end
60
+
61
+ # @private
62
+ # Orders items by modification time (most recent modified first).
63
+ class RecentlyModified
64
+ def order(list)
65
+ list.sort_by { |item| -File.mtime(item.metadata[:absolute_file_path]).to_i }
55
66
  end
56
67
  end
57
68
 
@@ -74,7 +85,8 @@ module RSpec
74
85
  @configuration = configuration
75
86
  @strategies = {}
76
87
 
77
- register(:random, Random.new(configuration))
88
+ register(:random, Random.new(configuration))
89
+ register(:recently_modified, RecentlyModified.new)
78
90
 
79
91
  identity = Identity.new
80
92
  register(:defined, identity)
@@ -123,29 +135,31 @@ module RSpec
123
135
 
124
136
  def order=(type)
125
137
  order, seed = type.to_s.split(':')
126
- @seed = seed = seed.to_i if seed
138
+ @seed = seed.to_i if seed
127
139
 
128
140
  ordering_name = if order.include?('rand')
129
- :random
130
- elsif order == 'defined'
131
- :defined
132
- end
141
+ :random
142
+ elsif order == 'defined'
143
+ :defined
144
+ elsif order == 'recently-modified'
145
+ :recently_modified
146
+ end
133
147
 
134
148
  register_ordering(:global, ordering_registry.fetch(ordering_name)) if ordering_name
135
149
  end
136
150
 
137
151
  def force(hash)
138
- if hash.has_key?(:seed)
152
+ if hash.key?(:seed)
139
153
  self.seed = hash[:seed]
140
154
  @seed_forced = true
141
155
  @order_forced = true
142
- elsif hash.has_key?(:order)
156
+ elsif hash.key?(:order)
143
157
  self.order = hash[:order]
144
158
  @order_forced = true
145
159
  end
146
160
  end
147
161
 
148
- def register_ordering(name, strategy = Custom.new(Proc.new { |l| yield l }))
162
+ def register_ordering(name, strategy=Custom.new(Proc.new { |l| yield l }))
149
163
  return if @order_forced && name == :global
150
164
  ordering_registry.register(name, strategy)
151
165
  end
@@ -153,4 +167,3 @@ module RSpec
153
167
  end
154
168
  end
155
169
  end
156
-
@@ -0,0 +1,29 @@
1
+ module RSpec
2
+ module Core
3
+ # @private
4
+ class OutputWrapper
5
+ # @private
6
+ attr_accessor :output
7
+
8
+ # @private
9
+ def initialize(output)
10
+ @output = output
11
+ end
12
+
13
+ def respond_to?(name, priv=false)
14
+ output.respond_to?(name, priv)
15
+ end
16
+
17
+ def method_missing(name, *args, &block)
18
+ output.send(name, *args, &block)
19
+ end
20
+
21
+ # Redirect calls for IO interface methods
22
+ IO.instance_methods(false).each do |method|
23
+ define_method(method) do |*args, &block|
24
+ output.send(method, *args, &block)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,9 +1,10 @@
1
1
  module RSpec
2
2
  module Core
3
- # Provides methods to mark examples as pending. These methods are available to be
4
- # called from within any example or hook.
3
+ # Provides methods to mark examples as pending. These methods are available
4
+ # to be called from within any example or hook.
5
5
  module Pending
6
- # Raised in the middle of an example to indicate that it should be marked as skipped.
6
+ # Raised in the middle of an example to indicate that it should be marked
7
+ # as skipped.
7
8
  class SkipDeclaredInExample < StandardError
8
9
  attr_reader :argument
9
10
 
@@ -12,8 +13,9 @@ module RSpec
12
13
  end
13
14
  end
14
15
 
15
- # If Test::Unit is loaded, we'll use its error as baseclass, so that Test::Unit
16
- # will report unmet RSpec expectations as failures rather than errors.
16
+ # If Test::Unit is loaded, we'll use its error as baseclass, so that
17
+ # Test::Unit will report unmet RSpec expectations as failures rather than
18
+ # errors.
17
19
  begin
18
20
  class PendingExampleFixedError < Test::Unit::AssertionFailedError; end
19
21
  rescue
@@ -36,7 +38,7 @@ module RSpec
36
38
  # @param message [String] optional message to add to the summary report.
37
39
  #
38
40
  # @example
39
- # describe "an example" do
41
+ # describe "some behaviour" do
40
42
  # # reported as "Pending: no reason given"
41
43
  # it "is pending with no message" do
42
44
  # pending
@@ -50,28 +52,20 @@ module RSpec
50
52
  # end
51
53
  # end
52
54
  #
53
- # @note `before(:example)` hooks are eval'd when you use the `pending`
54
- # method within an example. If you want to declare an example `pending`
55
- # and bypass the `before` hooks as well, you can pass `:pending => true`
56
- # to the `it` method:
57
- #
58
- # it "does something", :pending => true do
59
- # # ...
60
- # end
61
- #
62
- # or pass `:pending => "something else getting finished"` to add a
63
- # message to the summary report:
64
- #
65
- # it "does something", :pending => "something else getting finished" do
66
- # # ...
67
- # end
55
+ # @note When using `pending` inside an example body using this method
56
+ # hooks, such as `before(:example)`, have already be run. This means that
57
+ # a failure from the code in the `before` hook will prevent the example
58
+ # from being considered pending, as the example body would not be
59
+ # executed. If you need to consider hooks as pending as well you can use
60
+ # the pending metadata as an alternative, e.g.
61
+ # `it "does something", pending: "message"`.
68
62
  def pending(message=nil)
69
63
  current_example = RSpec.current_example
70
64
 
71
65
  if block_given?
72
66
  raise ArgumentError, <<-EOS.gsub(/^\s+\|/, '')
73
67
  |The semantics of `RSpec::Core::Pending#pending` have changed in
74
- |RSpec 3. In RSpec 2.x, it caused the example to be skipped. In
68
+ |RSpec 3. In RSpec 2.x, it caused the example to be skipped. In
75
69
  |RSpec 3, the rest of the example is still run but is expected to
76
70
  |fail, and will be marked as a failure (rather than as pending) if
77
71
  |the example passes.
@@ -89,7 +83,7 @@ module RSpec
89
83
  elsif current_example
90
84
  Pending.mark_pending! current_example, message
91
85
  else
92
- raise "`pending` may not be used outside of examples, such as in " +
86
+ raise "`pending` may not be used outside of examples, such as in " \
93
87
  "before(:context). Maybe you want `skip`?"
94
88
  end
95
89
  end
@@ -116,16 +110,14 @@ module RSpec
116
110
  def skip(message=nil)
117
111
  current_example = RSpec.current_example
118
112
 
119
- if current_example
120
- Pending.mark_skipped! current_example, message
121
- end
113
+ Pending.mark_skipped!(current_example, message) if current_example
122
114
 
123
115
  raise SkipDeclaredInExample.new(message)
124
116
  end
125
117
 
126
118
  # @private
127
119
  #
128
- # Mark example as skipped
120
+ # Mark example as skipped.
129
121
  #
130
122
  # @param example [RSpec::Core::Example] the example to mark as skipped
131
123
  # @param message_or_bool [Boolean, String] the message to use, or true
@@ -136,16 +128,16 @@ module RSpec
136
128
 
137
129
  # @private
138
130
  #
139
- # Mark example as pending
131
+ # Mark example as pending.
140
132
  #
141
133
  # @param example [RSpec::Core::Example] the example to mark as pending
142
134
  # @param message_or_bool [Boolean, String] the message to use, or true
143
135
  def self.mark_pending!(example, message_or_bool)
144
136
  message = if !message_or_bool || !(String === message_or_bool)
145
- NO_REASON_GIVEN
146
- else
147
- message_or_bool
148
- end
137
+ NO_REASON_GIVEN
138
+ else
139
+ message_or_bool
140
+ end
149
141
 
150
142
  example.metadata[:pending] = true
151
143
  example.execution_result.pending_message = message
@@ -154,7 +146,7 @@ module RSpec
154
146
 
155
147
  # @private
156
148
  #
157
- # Mark example as fixed
149
+ # Mark example as fixed.
158
150
  #
159
151
  # @param example [RSpec::Core::Example] the example to mark as fixed
160
152
  def self.mark_fixed!(example)