rspec-core 3.0.0.beta2 → 3.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (201) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.yardopts +1 -0
  5. data/Changelog.md +297 -57
  6. data/README.md +16 -13
  7. data/lib/rspec/core.rb +55 -84
  8. data/lib/rspec/core/backport_random.rb +35 -3
  9. data/lib/rspec/core/backtrace_formatter.rb +4 -13
  10. data/lib/rspec/core/configuration.rb +330 -114
  11. data/lib/rspec/core/configuration_options.rb +38 -22
  12. data/lib/rspec/core/drb.rb +111 -0
  13. data/lib/rspec/core/dsl.rb +8 -2
  14. data/lib/rspec/core/example.rb +203 -94
  15. data/lib/rspec/core/example_group.rb +344 -316
  16. data/lib/rspec/core/filter_manager.rb +135 -90
  17. data/lib/rspec/core/flat_map.rb +1 -0
  18. data/lib/rspec/core/formatters.rb +50 -14
  19. data/lib/rspec/core/formatters/base_formatter.rb +32 -138
  20. data/lib/rspec/core/formatters/base_text_formatter.rb +32 -253
  21. data/lib/rspec/core/formatters/console_codes.rb +65 -0
  22. data/lib/rspec/core/formatters/deprecation_formatter.rb +24 -15
  23. data/lib/rspec/core/formatters/documentation_formatter.rb +7 -10
  24. data/lib/rspec/core/formatters/helpers.rb +15 -9
  25. data/lib/rspec/core/formatters/html_formatter.rb +17 -16
  26. data/lib/rspec/core/formatters/html_printer.rb +1 -0
  27. data/lib/rspec/core/formatters/json_formatter.rb +18 -20
  28. data/lib/rspec/core/formatters/profile_formatter.rb +67 -0
  29. data/lib/rspec/core/formatters/progress_formatter.rb +6 -7
  30. data/lib/rspec/core/formatters/snippet_extractor.rb +8 -6
  31. data/lib/rspec/core/hooks.rb +131 -125
  32. data/lib/rspec/core/memoized_helpers.rb +31 -26
  33. data/lib/rspec/core/metadata.rb +277 -184
  34. data/lib/rspec/core/metadata_filter.rb +86 -0
  35. data/lib/rspec/core/minitest_assertions_adapter.rb +28 -0
  36. data/lib/rspec/core/mocking_adapters/flexmock.rb +1 -1
  37. data/lib/rspec/core/mocking_adapters/mocha.rb +1 -1
  38. data/lib/rspec/core/mocking_adapters/null.rb +1 -1
  39. data/lib/rspec/core/mocking_adapters/rr.rb +2 -1
  40. data/lib/rspec/core/mocking_adapters/rspec.rb +1 -1
  41. data/lib/rspec/core/notifications.rb +435 -24
  42. data/lib/rspec/core/option_parser.rb +16 -25
  43. data/lib/rspec/core/ordering.rb +3 -1
  44. data/lib/rspec/core/pending.rb +57 -33
  45. data/lib/rspec/core/project_initializer.rb +2 -0
  46. data/lib/rspec/core/project_initializer/spec_helper.rb +5 -4
  47. data/lib/rspec/core/rake_task.rb +45 -20
  48. data/lib/rspec/core/reporter.rb +50 -22
  49. data/lib/rspec/core/ruby_project.rb +1 -0
  50. data/lib/rspec/core/runner.rb +93 -39
  51. data/lib/rspec/core/shared_context.rb +7 -5
  52. data/lib/rspec/core/shared_example_group.rb +85 -77
  53. data/lib/rspec/core/test_unit_assertions_adapter.rb +30 -0
  54. data/lib/rspec/core/version.rb +3 -1
  55. data/lib/rspec/core/warnings.rb +35 -17
  56. data/lib/rspec/core/world.rb +57 -5
  57. metadata +56 -369
  58. metadata.gz.sig +3 -3
  59. data/features/README.md +0 -13
  60. data/features/Upgrade.md +0 -352
  61. data/features/command_line/README.md +0 -25
  62. data/features/command_line/dry_run.feature +0 -29
  63. data/features/command_line/example_name_option.feature +0 -97
  64. data/features/command_line/exit_status.feature +0 -82
  65. data/features/command_line/fail_fast.feature +0 -26
  66. data/features/command_line/format_option.feature +0 -75
  67. data/features/command_line/init.feature +0 -57
  68. data/features/command_line/line_number_appended_to_path.feature +0 -140
  69. data/features/command_line/line_number_option.feature +0 -58
  70. data/features/command_line/order.feature +0 -25
  71. data/features/command_line/pattern_option.feature +0 -49
  72. data/features/command_line/rake_task.feature +0 -122
  73. data/features/command_line/randomization.feature +0 -63
  74. data/features/command_line/require_option.feature +0 -43
  75. data/features/command_line/ruby.feature +0 -23
  76. data/features/command_line/tag.feature +0 -98
  77. data/features/command_line/warnings_option.feature +0 -29
  78. data/features/configuration/alias_example_to.feature +0 -39
  79. data/features/configuration/backtrace_exclusion_patterns.feature +0 -105
  80. data/features/configuration/custom_settings.feature +0 -84
  81. data/features/configuration/default_path.feature +0 -38
  82. data/features/configuration/deprecation_stream.feature +0 -58
  83. data/features/configuration/enable_global_dsl.feature +0 -54
  84. data/features/configuration/fail_fast.feature +0 -77
  85. data/features/configuration/failure_exit_code.feature +0 -36
  86. data/features/configuration/order_and_seed.feature +0 -3
  87. data/features/configuration/output_stream.feature +0 -24
  88. data/features/configuration/overriding_global_ordering.feature +0 -93
  89. data/features/configuration/pattern.feature +0 -38
  90. data/features/configuration/profile.feature +0 -220
  91. data/features/configuration/read_options_from_file.feature +0 -90
  92. data/features/configuration/run_all_when_everything_filtered.feature +0 -76
  93. data/features/example_groups/aliasing.feature +0 -48
  94. data/features/example_groups/basic_structure.feature +0 -55
  95. data/features/example_groups/shared_context.feature +0 -74
  96. data/features/example_groups/shared_examples.feature +0 -286
  97. data/features/expectation_framework_integration/configure_expectation_framework.feature +0 -102
  98. data/features/filtering/exclusion_filters.feature +0 -135
  99. data/features/filtering/if_and_unless.feature +0 -138
  100. data/features/filtering/inclusion_filters.feature +0 -101
  101. data/features/formatters/configurable_colors.feature +0 -31
  102. data/features/formatters/custom_formatter.feature +0 -68
  103. data/features/formatters/json_formatter.feature +0 -30
  104. data/features/formatters/regression_tests.feature +0 -95
  105. data/features/formatters/text_formatter.feature +0 -46
  106. data/features/helper_methods/arbitrary_methods.feature +0 -40
  107. data/features/helper_methods/let.feature +0 -50
  108. data/features/helper_methods/modules.feature +0 -146
  109. data/features/hooks/around_hooks.feature +0 -344
  110. data/features/hooks/before_and_after_hooks.feature +0 -427
  111. data/features/hooks/filtering.feature +0 -232
  112. data/features/metadata/current_example.feature +0 -56
  113. data/features/metadata/described_class.feature +0 -17
  114. data/features/metadata/user_defined.feature +0 -100
  115. data/features/mock_framework_integration/use_any_framework.feature +0 -106
  116. data/features/mock_framework_integration/use_flexmock.feature +0 -94
  117. data/features/mock_framework_integration/use_mocha.feature +0 -95
  118. data/features/mock_framework_integration/use_rr.feature +0 -96
  119. data/features/mock_framework_integration/use_rspec.feature +0 -95
  120. data/features/pending_and_skipped_examples/README.md +0 -3
  121. data/features/pending_and_skipped_examples/pending_examples.feature +0 -118
  122. data/features/pending_and_skipped_examples/skipped_examples.feature +0 -106
  123. data/features/spec_files/arbitrary_file_suffix.feature +0 -13
  124. data/features/step_definitions/additional_cli_steps.rb +0 -83
  125. data/features/subject/explicit_subject.feature +0 -101
  126. data/features/subject/implicit_subject.feature +0 -63
  127. data/features/subject/one_liner_syntax.feature +0 -71
  128. data/features/support/env.rb +0 -21
  129. data/features/support/require_expect_syntax_in_aruba_specs.rb +0 -16
  130. data/features/support/rubinius.rb +0 -6
  131. data/lib/rspec/core/command_line.rb +0 -35
  132. data/lib/rspec/core/drb_command_line.rb +0 -26
  133. data/lib/rspec/core/drb_options.rb +0 -87
  134. data/lib/rspec/core/formatters/legacy_formatter.rb +0 -227
  135. data/lib/rspec/core/shared_example_group/collection.rb +0 -27
  136. data/spec/command_line/order_spec.rb +0 -211
  137. data/spec/rspec/core/backtrace_formatter_spec.rb +0 -230
  138. data/spec/rspec/core/command_line_spec.rb +0 -112
  139. data/spec/rspec/core/command_line_spec_output.txt +0 -0
  140. data/spec/rspec/core/configuration_options_spec.rb +0 -409
  141. data/spec/rspec/core/configuration_spec.rb +0 -1479
  142. data/spec/rspec/core/drb_command_line_spec.rb +0 -102
  143. data/spec/rspec/core/drb_options_spec.rb +0 -193
  144. data/spec/rspec/core/dsl_spec.rb +0 -88
  145. data/spec/rspec/core/example_group_spec.rb +0 -1533
  146. data/spec/rspec/core/example_spec.rb +0 -642
  147. data/spec/rspec/core/filter_manager_spec.rb +0 -229
  148. data/spec/rspec/core/formatters/base_formatter_spec.rb +0 -64
  149. data/spec/rspec/core/formatters/base_text_formatter_spec.rb +0 -303
  150. data/spec/rspec/core/formatters/deprecation_formatter_spec.rb +0 -208
  151. data/spec/rspec/core/formatters/documentation_formatter_spec.rb +0 -75
  152. data/spec/rspec/core/formatters/helpers_spec.rb +0 -104
  153. data/spec/rspec/core/formatters/html_formatted-2.1.0.html +0 -392
  154. data/spec/rspec/core/formatters/html_formatted.html +0 -397
  155. data/spec/rspec/core/formatters/html_formatter_spec.rb +0 -122
  156. data/spec/rspec/core/formatters/json_formatter_spec.rb +0 -206
  157. data/spec/rspec/core/formatters/legacy_formatter_spec.rb +0 -137
  158. data/spec/rspec/core/formatters/progress_formatter_spec.rb +0 -43
  159. data/spec/rspec/core/formatters/snippet_extractor_spec.rb +0 -26
  160. data/spec/rspec/core/formatters_spec.rb +0 -120
  161. data/spec/rspec/core/hooks_filtering_spec.rb +0 -227
  162. data/spec/rspec/core/hooks_spec.rb +0 -294
  163. data/spec/rspec/core/memoized_helpers_spec.rb +0 -495
  164. data/spec/rspec/core/metadata_spec.rb +0 -491
  165. data/spec/rspec/core/option_parser_spec.rb +0 -262
  166. data/spec/rspec/core/ordering_spec.rb +0 -102
  167. data/spec/rspec/core/pending_example_spec.rb +0 -117
  168. data/spec/rspec/core/pending_spec.rb +0 -8
  169. data/spec/rspec/core/project_initializer_spec.rb +0 -73
  170. data/spec/rspec/core/rake_task_spec.rb +0 -146
  171. data/spec/rspec/core/random_spec.rb +0 -47
  172. data/spec/rspec/core/reporter_spec.rb +0 -155
  173. data/spec/rspec/core/resources/a_bar.rb +0 -0
  174. data/spec/rspec/core/resources/a_foo.rb +0 -0
  175. data/spec/rspec/core/resources/a_spec.rb +0 -1
  176. data/spec/rspec/core/resources/custom_example_group_runner.rb +0 -14
  177. data/spec/rspec/core/resources/formatter_specs.rb +0 -58
  178. data/spec/rspec/core/resources/utf8_encoded.rb +0 -8
  179. data/spec/rspec/core/rspec_matchers_spec.rb +0 -45
  180. data/spec/rspec/core/ruby_project_spec.rb +0 -26
  181. data/spec/rspec/core/runner_spec.rb +0 -151
  182. data/spec/rspec/core/shared_context_spec.rb +0 -102
  183. data/spec/rspec/core/shared_example_group/collection_spec.rb +0 -57
  184. data/spec/rspec/core/shared_example_group_spec.rb +0 -114
  185. data/spec/rspec/core/warnings_spec.rb +0 -29
  186. data/spec/rspec/core/world_spec.rb +0 -142
  187. data/spec/rspec/core_spec.rb +0 -91
  188. data/spec/spec_helper.rb +0 -160
  189. data/spec/support/config_options_helper.rb +0 -13
  190. data/spec/support/formatter_support.rb +0 -83
  191. data/spec/support/helper_methods.rb +0 -26
  192. data/spec/support/isolate_load_path_mutation.rb +0 -5
  193. data/spec/support/isolated_directory.rb +0 -10
  194. data/spec/support/isolated_home_directory.rb +0 -16
  195. data/spec/support/legacy_formatter_using_sub_classing_example.rb +0 -87
  196. data/spec/support/matchers.rb +0 -85
  197. data/spec/support/mathn_integration_support.rb +0 -12
  198. data/spec/support/old_style_formatter_example.rb +0 -69
  199. data/spec/support/shared_example_groups.rb +0 -13
  200. data/spec/support/spec_files.rb +0 -44
  201. data/spec/support/stderr_splitter.rb +0 -36
@@ -1,5 +1,6 @@
1
1
  module RSpec
2
2
  module Core
3
+ # @private
3
4
  # Manages the filtering of examples and groups by matching tags declared on
4
5
  # the command line or options files, or filters declared via
5
6
  # `RSpec.configure`, with hash key/values submitted within example group
@@ -66,149 +67,193 @@ module RSpec
66
67
  # @see Configuration#filter_run_including
67
68
  # @see Configuration#filter_run_excluding
68
69
  class FilterManager
69
- STANDALONE_FILTERS = [:locations, :line_numbers, :full_description]
70
+ attr_reader :exclusions, :inclusions
70
71
 
71
- PROC_HEX_NUMBER = /0x[0-9a-f]+@/
72
- PROJECT_DIR = File.expand_path('.')
72
+ def initialize
73
+ @exclusions, @inclusions = FilterRules.build
74
+ end
73
75
 
74
- def self.inspect_filter_hash(hash)
75
- hash.inspect.gsub(PROC_HEX_NUMBER, '').gsub(PROJECT_DIR, '.').gsub(' (lambda)','')
76
+ # @api private
77
+ #
78
+ # @param file_path [String]
79
+ # @param line_numbers [Array]
80
+ def add_location(file_path, line_numbers)
81
+ # locations is a hash of expanded paths to arrays of line
82
+ # numbers to match against. e.g.
83
+ # { "path/to/file.rb" => [37, 42] }
84
+ locations = inclusions.delete(:locations) || Hash.new { |h,k| h[k] = [] }
85
+ locations[File.expand_path(file_path)].push(*line_numbers)
86
+ inclusions.add_location(locations)
87
+ end
88
+
89
+ def empty?
90
+ inclusions.empty? && exclusions.empty?
76
91
  end
77
92
 
78
- class InclusionFilterHash < Hash
79
- def description
80
- FilterManager.inspect_filter_hash self
93
+ def prune(examples)
94
+ if inclusions.standalone?
95
+ base_exclusions = ExclusionRules.new
96
+ examples.select {|e| !base_exclusions.include_example?(e) && include?(e) }
97
+ else
98
+ examples.select {|e| !exclude?(e) && include?(e)}
81
99
  end
82
100
  end
83
101
 
84
- class ExclusionFilterHash < Hash
85
- CONDITIONAL_FILTERS = {
86
- :if => lambda { |value| !value },
87
- :unless => lambda { |value| value }
88
- }
102
+ def exclude(*args)
103
+ exclusions.add(args.last)
104
+ end
89
105
 
90
- def initialize(*)
91
- super
92
- CONDITIONAL_FILTERS.each {|k,v| store(k, v)}
93
- end
106
+ def exclude_only(*args)
107
+ exclusions.use_only(args.last)
108
+ end
94
109
 
95
- def description
96
- FilterManager.inspect_filter_hash without_conditional_filters
97
- end
110
+ def exclude_with_low_priority(*args)
111
+ exclusions.add_with_low_priority(args.last)
112
+ end
98
113
 
99
- def empty_without_conditional_filters?
100
- without_conditional_filters.empty?
101
- end
114
+ def exclude?(example)
115
+ exclusions.include_example?(example)
116
+ end
102
117
 
103
- private
118
+ def include(*args)
119
+ inclusions.add(args.last)
120
+ end
104
121
 
105
- if RUBY_VERSION.to_f < 2.1
106
- def without_conditional_filters
107
- # On 1.8.7, Hash#reject returns a hash but Hash#select returns an array.
108
- reject {|k,v| CONDITIONAL_FILTERS[k] == v}
109
- end
110
- else
111
- def without_conditional_filters
112
- # On ruby 2.1 #reject on a subclass of Hash emits warnings, but #select does not.
113
- select {|k,v| CONDITIONAL_FILTERS[k] != v}
114
- end
115
- end
122
+ def include_only(*args)
123
+ inclusions.use_only(args.last)
116
124
  end
117
125
 
118
- attr_reader :exclusions, :inclusions
126
+ def include_with_low_priority(*args)
127
+ inclusions.add_with_low_priority(args.last)
128
+ end
119
129
 
120
- def initialize
121
- @exclusions = ExclusionFilterHash.new
122
- @inclusions = InclusionFilterHash.new
130
+ def include?(example)
131
+ inclusions.include_example?(example)
123
132
  end
133
+ end
124
134
 
125
- def add_location(file_path, line_numbers)
126
- # locations is a hash of expanded paths to arrays of line
127
- # numbers to match against. e.g.
128
- # { "path/to/file.rb" => [37, 42] }
129
- locations = @inclusions.delete(:locations) || Hash.new {|h,k| h[k] = []}
130
- locations[File.expand_path(file_path)].push(*line_numbers)
135
+ # @private
136
+ class FilterRules
137
+ PROC_HEX_NUMBER = /0x[0-9a-f]+@/
138
+ PROJECT_DIR = File.expand_path('.')
139
+
140
+ attr_accessor :opposite
141
+ attr_reader :rules
131
142
 
132
- replace_filters :locations => locations
143
+ def self.build
144
+ exclusions = ExclusionRules.new
145
+ inclusions = InclusionRules.new
146
+ exclusions.opposite = inclusions
147
+ inclusions.opposite = exclusions
148
+ [exclusions, inclusions]
133
149
  end
134
150
 
135
- def empty?
136
- inclusions.empty? && exclusions.empty_without_conditional_filters?
151
+ def initialize(*args, &block)
152
+ @rules = Hash.new(*args, &block)
137
153
  end
138
154
 
139
- def prune(examples)
140
- examples.select {|e| !exclude?(e) && include?(e)}
155
+ def add(updated)
156
+ @rules.merge!(updated).each_key { |k| opposite.delete(k) }
141
157
  end
142
158
 
143
- def exclude(*args)
144
- merge(@exclusions, @inclusions, *args)
159
+ def add_with_low_priority(_updated)
160
+ updated = _updated.merge(@rules)
161
+ opposite.each_pair { |k,v| updated.delete(k) if updated[k] == v }
162
+ @rules.replace(updated)
145
163
  end
146
164
 
147
- def exclude!(*args)
148
- replace(@exclusions, @inclusions, *args)
165
+ def use_only(updated)
166
+ updated.each_key { |k| opposite.delete(k) }
167
+ @rules.replace(updated)
149
168
  end
150
169
 
151
- def exclude_with_low_priority(*args)
152
- reverse_merge(@exclusions, @inclusions, *args)
170
+ def clear
171
+ @rules.clear
153
172
  end
154
173
 
155
- def exclude?(example)
156
- @exclusions.empty? ? false : example.any_apply?(@exclusions)
174
+ def delete(key)
175
+ @rules.delete(key)
157
176
  end
158
177
 
159
- def include(*filters)
160
- set_standalone_filter(*filters) || merge(@inclusions, @exclusions, *filters)
178
+ def fetch(*args, &block)
179
+ @rules.fetch(*args, &block)
161
180
  end
162
181
 
163
- def include!(*filters)
164
- set_standalone_filter(*filters) || replace(@inclusions, @exclusions, *filters)
182
+ def [](key)
183
+ @rules[key]
165
184
  end
166
185
 
167
- def include_with_low_priority(*filters)
168
- set_standalone_filter(*filters) || reverse_merge(@inclusions, @exclusions, *filters)
186
+ def empty?
187
+ rules.empty?
169
188
  end
170
189
 
171
- def include?(example)
172
- @inclusions.empty? ? true : example.any_apply?(@inclusions)
190
+ def each_pair(&block)
191
+ @rules.each_pair(&block)
173
192
  end
174
193
 
175
- private
194
+ def description
195
+ rules.inspect.gsub(PROC_HEX_NUMBER, '').gsub(PROJECT_DIR, '.').gsub(' (lambda)','')
196
+ end
197
+ end
176
198
 
177
- def set_standalone_filter(*args)
178
- if already_set_standalone_filter?
179
- true
180
- elsif is_standalone_filter?(args.last)
181
- replace_filters(args.last)
182
- true
183
- end
199
+ # @private
200
+ class InclusionRules < FilterRules
201
+ STANDALONE_FILTERS = [:locations, :full_description]
202
+
203
+ def add_location(locations)
204
+ replace_filters({ :locations => locations })
184
205
  end
185
206
 
186
- def replace_filters(rule)
187
- @inclusions.replace(rule)
188
- @exclusions.clear
207
+ def add(*args)
208
+ set_standalone_filter(*args) || super
189
209
  end
190
210
 
191
- def merge(orig, opposite, *updates)
192
- orig.merge!(updates.last).each_key {|k| opposite.delete(k)}
211
+ def add_with_low_priority(*args)
212
+ set_standalone_filter(*args) || super
193
213
  end
194
214
 
195
- def replace(orig, opposite, *updates)
196
- updates.last.each_key {|k| opposite.delete(k)}
197
- orig.replace(updates.last)
215
+ def use(*args)
216
+ set_standalone_filter(*args) || super
198
217
  end
199
218
 
200
- def reverse_merge(orig, opposite, *updates)
201
- updated = updates.last.merge(orig)
202
- opposite.each_pair {|k,v| updated.delete(k) if updated[k] == v}
203
- orig.replace(updated)
219
+ def include_example?(example)
220
+ @rules.empty? ? true : example.any_apply?(@rules)
204
221
  end
205
222
 
206
- def already_set_standalone_filter?
207
- is_standalone_filter?(inclusions)
223
+ def standalone?
224
+ is_standalone_filter?(@rules)
208
225
  end
209
226
 
210
- def is_standalone_filter?(filter)
211
- STANDALONE_FILTERS.any? {|key| filter.has_key?(key)}
227
+ private
228
+
229
+ def set_standalone_filter(updated)
230
+ return true if standalone?
231
+
232
+ if is_standalone_filter?(updated)
233
+ replace_filters(updated)
234
+ true
235
+ end
236
+ end
237
+
238
+ def replace_filters(new_rules)
239
+ @rules.replace(new_rules)
240
+ opposite.clear
241
+ end
242
+
243
+ def is_standalone_filter?(rules)
244
+ STANDALONE_FILTERS.any? { |key| rules.has_key?(key) }
245
+ end
246
+ end
247
+
248
+ # @private
249
+ class ExclusionRules < FilterRules
250
+ CONDITIONAL_FILTERS = {
251
+ :if => lambda { |value| !value },
252
+ :unless => lambda { |value| value }
253
+ }.freeze
254
+
255
+ def include_example?(example)
256
+ example.any_apply?(@rules) || example.any_apply?(CONDITIONAL_FILTERS)
212
257
  end
213
258
  end
214
259
  end
@@ -1,5 +1,6 @@
1
1
  module RSpec
2
2
  module Core
3
+ # @private
3
4
  module FlatMap
4
5
  if [].respond_to?(:flat_map)
5
6
  def flat_map(array)
@@ -1,5 +1,3 @@
1
- require 'rspec/core/formatters/legacy_formatter'
2
-
3
1
  # ## Built-in Formatters
4
2
  #
5
3
  # * progress (default) - prints dots for passing examples, `F` for failures, `*` for pending
@@ -56,8 +54,14 @@ module RSpec::Core::Formatters
56
54
  autoload :DocumentationFormatter, 'rspec/core/formatters/documentation_formatter'
57
55
  autoload :HtmlFormatter, 'rspec/core/formatters/html_formatter'
58
56
  autoload :ProgressFormatter, 'rspec/core/formatters/progress_formatter'
57
+ autoload :ProfileFormatter, 'rspec/core/formatters/profile_formatter'
59
58
  autoload :JsonFormatter, 'rspec/core/formatters/json_formatter'
60
59
 
60
+ # Register the formatter class
61
+ # @param formatter_class [Class] formatter class to register
62
+ # @param notifications [Symbol, ...] one or more notifications to be registered to the specified formatter
63
+ #
64
+ # @see RSpec::Core::Formatters::BaseFormatter
61
65
  def self.register(formatter_class, *notifications)
62
66
  Loader.formatters[formatter_class] = notifications
63
67
  end
@@ -80,38 +84,66 @@ module RSpec::Core::Formatters
80
84
  def initialize(reporter)
81
85
  @formatters = []
82
86
  @reporter = reporter
87
+ self.default_formatter = 'progress'
83
88
  end
84
- attr_reader :formatters, :reporter
85
89
 
86
- # @api private
90
+ # @return [Array] the loaded formatters
91
+ attr_reader :formatters
92
+
93
+ # @return [Reporter] the reporter
94
+ attr_reader :reporter
95
+
96
+ # @return [String] the default formatter to setup, defaults to `progress`
97
+ attr_accessor :default_formatter
98
+
99
+ # @private
87
100
  def setup_default(output_stream, deprecation_stream)
88
101
  if @formatters.empty?
89
- add 'progress', output_stream
102
+ add default_formatter, output_stream
90
103
  end
91
104
  unless @formatters.any? { |formatter| DeprecationFormatter === formatter }
92
105
  add DeprecationFormatter, deprecation_stream, output_stream
93
106
  end
107
+ if RSpec.configuration.profile_examples? && !existing_formatter_implements?(:dump_profile)
108
+ add RSpec::Core::Formatters::ProfileFormatter, output_stream
109
+ end
94
110
  end
95
111
 
96
- # @api private
112
+ # @private
97
113
  def add(formatter_to_use, *paths)
98
114
  formatter_class = find_formatter(formatter_to_use)
99
115
 
100
- args = paths.map { |p| String === p ? file_at(p) : p }
116
+ args = paths.map { |p| p.respond_to?(:puts) ? p : file_at(p) }
101
117
 
102
118
  if !Loader.formatters[formatter_class].nil?
103
119
  formatter = formatter_class.new(*args)
104
120
  @reporter.register_listener formatter, *notifications_for(formatter_class)
105
- else
106
- formatter = LegacyFormatter.new(formatter_class, *args)
121
+ elsif defined?(RSpec::LegacyFormatters)
122
+ formatter = RSpec::LegacyFormatters.load_formatter formatter_class, *args
107
123
  @reporter.register_listener formatter, *formatter.notifications
108
- end
124
+ else
125
+ line = ::RSpec::CallerFilter.first_non_rspec_line
126
+ if line
127
+ call_site = "Formatter added at: #{line}"
128
+ else
129
+ call_site = "The formatter was added via command line flag or your "+
130
+ "`.rspec` file."
131
+ end
109
132
 
110
- @formatters << formatter unless duplicate_formatter_exists?(formatter)
111
- if formatter.is_a?(LegacyFormatter)
112
- RSpec.warn_deprecation "The #{formatter_class} formatter uses the deprecated formatter interface.\n Formatter added at: #{::RSpec::CallerFilter.first_non_rspec_line}"
133
+ RSpec.warn_deprecation <<-WARNING.gsub(/\s*\|/,' ')
134
+ |The #{formatter_class} formatter uses the deprecated formatter
135
+ |interface not supported directly by RSpec 3.
136
+ |
137
+ |To continue to use this formatter you must install the
138
+ |`rspec-legacy_formatters` gem, which provides support
139
+ |for legacy formatters or upgrade the formatter to a
140
+ |compatible version.
141
+ |
142
+ |#{call_site}
143
+ WARNING
144
+ return
113
145
  end
114
-
146
+ @formatters << formatter unless duplicate_formatter_exists?(formatter)
115
147
  formatter
116
148
  end
117
149
 
@@ -129,6 +161,10 @@ module RSpec::Core::Formatters
129
161
  end
130
162
  end
131
163
 
164
+ def existing_formatter_implements?(notification)
165
+ @reporter.registered_listeners(notification).any?
166
+ end
167
+
132
168
  def built_in_formatter(key)
133
169
  case key.to_s
134
170
  when 'd', 'doc', 'documentation'
@@ -1,4 +1,4 @@
1
- require 'rspec/core/formatters/helpers'
1
+ RSpec::Support.require_rspec_core "formatters/helpers"
2
2
  require 'stringio'
3
3
 
4
4
  module RSpec
@@ -14,22 +14,15 @@ module RSpec
14
14
  class BaseFormatter
15
15
 
16
16
  # all formatters inheriting from this formatter will receive these notifications
17
- Formatters.register self, :start, :example_group_started, :example_started,
18
- :example_pending, :example_failed, :close
19
- include Helpers
17
+ Formatters.register self, :start, :example_group_started, :close
20
18
  attr_accessor :example_group
21
- attr_reader :examples, :output
22
- attr_reader :failed_examples, :pending_examples
19
+ attr_reader :output
23
20
 
24
21
  # @api public
25
22
  #
26
- # @param output
23
+ # @param output [IO] the formatter output
27
24
  def initialize(output)
28
25
  @output = output || StringIO.new
29
- @example_count = @pending_count = @failure_count = 0
30
- @examples = []
31
- @failed_examples = []
32
- @pending_examples = []
33
26
  @example_group = nil
34
27
  end
35
28
 
@@ -42,7 +35,7 @@ module RSpec
42
35
  # This will only be invoked once, and the next one to be invoked
43
36
  # is {#example_group_started}.
44
37
  #
45
- # @param example_count
38
+ # @param notification [StartNotification]
46
39
  def start(notification)
47
40
  start_sync_output
48
41
  @example_count = notification.count
@@ -52,12 +45,10 @@ module RSpec
52
45
  #
53
46
  # This method is invoked at the beginning of the execution of each example group.
54
47
  #
55
- # @param example_group subclass of `RSpec::Core::ExampleGroup`
56
- #
57
48
  # The next method to be invoked after this is {#example_passed},
58
49
  # {#example_pending}, or {#example_group_finished}.
59
50
  #
60
- # @param example_group
51
+ # @param notification [GroupNotification] containing example_group subclass of `RSpec::Core::ExampleGroup`
61
52
  def example_group_started(notification)
62
53
  @example_group = notification.group
63
54
  end
@@ -67,56 +58,47 @@ module RSpec
67
58
  #
68
59
  # Invoked at the end of the execution of each example group.
69
60
  #
70
- # @param example_group subclass of `RSpec::Core::ExampleGroup`
61
+ # @param notification [GroupNotification] containing example_group subclass of `RSpec::Core::ExampleGroup`
71
62
 
63
+ # @method example_started
72
64
  # @api public
73
65
  #
74
66
  # Invoked at the beginning of the execution of each example.
75
67
  #
76
- # @param example instance of subclass of `RSpec::Core::ExampleGroup`
77
- # @return [Array]
78
- def example_started(notification)
79
- examples << notification.example
80
- end
68
+ # @param notification [ExampleNotification] containing example subclass of `RSpec::Core::Example`
81
69
 
82
70
  # @method example_passed
83
71
  # @api public
84
72
  #
85
73
  # Invoked when an example passes.
86
74
  #
87
- # @param example instance of subclass of `RSpec::Core::ExampleGroup`
75
+ # @param notification [ExampleNotification] containing example subclass of `RSpec::Core::Example`
88
76
 
77
+ # @method example_pending
89
78
  # Invoked when an example is pending.
90
79
  #
91
- # @param example instance of subclass of `RSpec::Core::ExampleGroup`
92
- # @return [Array]
93
- def example_pending(notification)
94
- @pending_examples << notification.example
95
- end
80
+ # @param notification [ExampleNotification] containing example subclass of `RSpec::Core::Example`
96
81
 
82
+ # @method example_failed
97
83
  # @api public
98
84
  #
99
85
  # Invoked when an example fails.
100
86
  #
101
- # @param example instance of subclass of `RSpec::Core::ExampleGroup`
102
- # @return [Array]
103
- def example_failed(notification)
104
- @failed_examples << notification.example
105
- end
87
+ # @param notification [ExampleNotification] containing example subclass of `RSpec::Core::Example`
106
88
 
107
89
  # @method message
108
90
  # @api public
109
91
  #
110
92
  # Used by the reporter to send messages to the output stream.
111
93
  #
112
- # @param [String] message
94
+ # @param notification [MessageNotification] containing message
113
95
 
114
96
  # @method stop
115
97
  # @api public
116
98
  #
117
99
  # Invoked after all examples have executed, before dumping post-run reports.
118
100
  #
119
- # @return [nil]
101
+ # @param notification [NullNotification]
120
102
 
121
103
  # @method start_dump
122
104
  # @api public
@@ -125,14 +107,14 @@ module RSpec
125
107
  # to be invoked after this one is {#dump_failures}
126
108
  # (BaseTextFormatter then calls {#dump_failure} once for each failed example.)
127
109
  #
128
- # @return [nil]
110
+ # @param notification [NullNotification]
129
111
 
130
112
  # @method dump_failures
131
113
  # @api public
132
114
  #
133
115
  # Dumps detailed information about each example failure.
134
116
  #
135
- # @return [nil]
117
+ # @param notification [NullNotification]
136
118
 
137
119
  # @method dump_summary
138
120
  # @api public
@@ -140,73 +122,37 @@ module RSpec
140
122
  # This method is invoked after the dumping of examples and failures. Each parameter
141
123
  # is assigned to a corresponding attribute.
142
124
  #
143
- # @param duration
144
- # @param example_count
145
- # @param failure_count
146
- # @param pending_count
125
+ # @param summary [SummaryNotification] containing duration, example_count,
126
+ # failure_count and pending_count
147
127
 
148
- # @method dump_pending
128
+ # @method dump_profile
149
129
  # @api public
150
130
  #
151
- # Outputs a report of pending examples. This gets invoked
152
- # after the summary if option is set to do so.
131
+ # This method is invoked after the dumping the summary if profiling is
132
+ # enabled.
153
133
  #
154
- # @return [nil]
134
+ # @param profile [ProfileNotification] containing duration, slowest_examples
135
+ # and slowest_example_groups
155
136
 
156
- # @method dump_profile
137
+ # @method dump_pending
157
138
  # @api public
158
139
  #
159
- # This methods is invoked form formatters to show slowest examples and example groups
160
- # when using `--profile COUNT` (default 10).
140
+ # Outputs a report of pending examples. This gets invoked
141
+ # after the summary if option is set to do so.
161
142
  #
162
- # @return [nil]
143
+ # @param notification [NullNotification]
163
144
 
164
145
  # @api public
165
146
  #
166
147
  # Invoked at the very end, `close` allows the formatter to clean
167
148
  # up resources, e.g. open streams, etc.
149
+ #
150
+ # @param notification [NullNotification]
168
151
  def close(notification)
169
152
  restore_sync_output
170
153
  end
171
154
 
172
- # @api public
173
- #
174
- # Formats the given backtrace based on configuration and
175
- # the metadata of the given example.
176
- def format_backtrace(backtrace, example)
177
- configuration.backtrace_formatter.format_backtrace(backtrace, example.metadata)
178
- end
179
-
180
- protected
181
-
182
- def configuration
183
- RSpec.configuration
184
- end
185
-
186
- def read_failed_line(exception, example)
187
- unless matching_line = find_failed_line(exception.backtrace, example.file_path)
188
- return "Unable to find matching line from backtrace"
189
- end
190
-
191
- file_path, line_number = matching_line.match(/(.+?):(\d+)(|:\d+)/)[1..2]
192
-
193
- if File.exist?(file_path)
194
- File.readlines(file_path)[line_number.to_i - 1] ||
195
- "Unable to find matching line in #{file_path}"
196
- else
197
- "Unable to find #{file_path} to read failed line"
198
- end
199
- rescue SecurityError
200
- "Unable to read failed line"
201
- end
202
-
203
- def find_failed_line(backtrace, path)
204
- path = File.expand_path(path)
205
- backtrace.detect { |line|
206
- match = line.match(/(.+?):(\d+)(|:\d+)/)
207
- match && match[1].downcase == path.downcase
208
- }
209
- end
155
+ private
210
156
 
211
157
  def start_sync_output
212
158
  @old_sync, output.sync = output.sync, true if output_supports_sync
@@ -220,58 +166,6 @@ module RSpec
220
166
  output.respond_to?(:sync=)
221
167
  end
222
168
 
223
- def profile_examples?
224
- configuration.profile_examples
225
- end
226
-
227
- def fail_fast?
228
- configuration.fail_fast
229
- end
230
-
231
- def color_enabled?
232
- configuration.color_enabled?(output)
233
- end
234
-
235
- def mute_profile_output?(failure_count)
236
- # Don't print out profiled info if there are failures and `--fail-fast` is used, it just clutters the output
237
- !profile_examples? || (fail_fast? && failure_count != 0)
238
- end
239
-
240
- # @api private
241
- def slowest_examples
242
- number_of_examples = RSpec.configuration.profile_examples
243
- sorted_examples = examples.sort_by {|example|
244
- example.execution_result[:run_time] }.reverse.first(number_of_examples)
245
-
246
- total, slows = [examples, sorted_examples].map do |exs|
247
- exs.inject(0.0) {|i, e| i + e.execution_result[:run_time] }
248
- end
249
- {:examples => sorted_examples, :total => total, :slows => slows}
250
- end
251
-
252
- # @api private
253
- def slowest_groups
254
- number_of_examples = RSpec.configuration.profile_examples
255
- example_groups = {}
256
-
257
- examples.each do |example|
258
- location = example.example_group.parent_groups.last.metadata[:example_group][:location]
259
-
260
- example_groups[location] ||= Hash.new(0)
261
- example_groups[location][:total_time] += example.execution_result[:run_time]
262
- example_groups[location][:count] += 1
263
- example_groups[location][:description] = example.example_group.top_level_description unless example_groups[location].has_key?(:description)
264
- end
265
-
266
- # stop if we've only one example group
267
- return {} if example_groups.keys.length <= 1
268
-
269
- example_groups.each_value do |hash|
270
- hash[:average] = hash[:total_time].to_f / hash[:count]
271
- end
272
-
273
- example_groups.sort_by {|_, hash| -hash[:average]}.first(number_of_examples)
274
- end
275
169
  end
276
170
  end
277
171
  end