rspec-core 3.0.0.beta2 → 3.0.0.rc1

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