/vendor/bundler/ruby/1.9.1/gems/rspec-core-2.13.1/lib/rspec/core/filter_manager.rb

https://bitbucket.org/toihrk/fusuma · Ruby · 203 lines · 103 code · 28 blank · 72 comment · 9 complexity · 131f3524e9648c830f5fe7c8301c0304 MD5 · raw file

  1. module RSpec
  2. module Core
  3. # Manages the filtering of examples and groups by matching tags declared on
  4. # the command line or options files, or filters declared via
  5. # `RSpec.configure`, with hash key/values submitted within example group
  6. # and/or example declarations. For example, given this declaration:
  7. #
  8. # describe Thing, :awesome => true do
  9. # it "does something" do
  10. # # ...
  11. # end
  12. # end
  13. #
  14. # That group (or any other with `:awesome => true`) would be filtered in
  15. # with any of the following commands:
  16. #
  17. # rspec --tag awesome:true
  18. # rspec --tag awesome
  19. # rspec -t awesome:true
  20. # rspec -t awesome
  21. #
  22. # Prefixing the tag names with `~` negates the tags, thus excluding this group with
  23. # any of:
  24. #
  25. # rspec --tag ~awesome:true
  26. # rspec --tag ~awesome
  27. # rspec -t ~awesome:true
  28. # rspec -t ~awesome
  29. #
  30. # ## Options files and command line overrides
  31. #
  32. # Tag declarations can be stored in `.rspec`, `~/.rspec`, or a custom
  33. # options file. This is useful for storing defaults. For example, let's
  34. # say you've got some slow specs that you want to suppress most of the
  35. # time. You can tag them like this:
  36. #
  37. # describe Something, :slow => true do
  38. #
  39. # And then store this in `.rspec`:
  40. #
  41. # --tag ~slow:true
  42. #
  43. # Now when you run `rspec`, that group will be excluded.
  44. #
  45. # ## Overriding
  46. #
  47. # Of course, you probably want to run them sometimes, so you can override
  48. # this tag on the command line like this:
  49. #
  50. # rspec --tag slow:true
  51. #
  52. # ## RSpec.configure
  53. #
  54. # You can also store default tags with `RSpec.configure`. We use `tag` on
  55. # the command line (and in options files like `.rspec`), but for historical
  56. # reasons we use the term `filter` in `RSpec.configure:
  57. #
  58. # RSpec.configure do |c|
  59. # c.filter_run_including :foo => :bar
  60. # c.filter_run_excluding :foo => :bar
  61. # end
  62. #
  63. # These declarations can also be overridden from the command line.
  64. #
  65. # @see RSpec.configure
  66. # @see Configuration#filter_run_including
  67. # @see Configuration#filter_run_excluding
  68. class FilterManager
  69. DEFAULT_EXCLUSIONS = {
  70. :if => lambda { |value| !value },
  71. :unless => lambda { |value| value }
  72. }
  73. STANDALONE_FILTERS = [:locations, :line_numbers, :full_description]
  74. module Describable
  75. PROC_HEX_NUMBER = /0x[0-9a-f]+@/
  76. PROJECT_DIR = File.expand_path('.')
  77. def description
  78. reject { |k, v| RSpec::Core::FilterManager::DEFAULT_EXCLUSIONS[k] == v }.inspect.gsub(PROC_HEX_NUMBER, '').gsub(PROJECT_DIR, '.').gsub(' (lambda)','')
  79. end
  80. def empty_without_conditional_filters?
  81. reject { |k, v| RSpec::Core::FilterManager::DEFAULT_EXCLUSIONS[k] == v }.empty?
  82. end
  83. end
  84. module BackwardCompatibility
  85. def merge(orig, opposite, *updates)
  86. _warn_deprecated_keys(updates.last)
  87. super
  88. end
  89. def reverse_merge(orig, opposite, *updates)
  90. _warn_deprecated_keys(updates.last)
  91. super
  92. end
  93. # Supports a use case that probably doesn't exist: overriding the
  94. # if/unless procs.
  95. def _warn_deprecated_keys(updates)
  96. _warn_deprecated_key(:unless, updates) if updates.has_key?(:unless)
  97. _warn_deprecated_key(:if, updates) if updates.has_key?(:if)
  98. end
  99. # Emits a deprecation warning for keys that will not be supported in
  100. # the future.
  101. def _warn_deprecated_key(key, updates)
  102. RSpec.warn_deprecation("\nDEPRECATION NOTICE: FilterManager#exclude(#{key.inspect} => #{updates[key].inspect}) is deprecated with no replacement, and will be removed from rspec-3.0.")
  103. @exclusions[key] = updates.delete(key)
  104. end
  105. end
  106. attr_reader :exclusions, :inclusions
  107. def initialize
  108. @exclusions = DEFAULT_EXCLUSIONS.dup.extend(Describable)
  109. @inclusions = {}.extend(Describable)
  110. extend(BackwardCompatibility)
  111. end
  112. def add_location(file_path, line_numbers)
  113. # locations is a hash of expanded paths to arrays of line
  114. # numbers to match against. e.g.
  115. # { "path/to/file.rb" => [37, 42] }
  116. locations = @inclusions.delete(:locations) || Hash.new {|h,k| h[k] = []}
  117. locations[File.expand_path(file_path)].push(*line_numbers)
  118. @inclusions.replace(:locations => locations)
  119. @exclusions.clear
  120. end
  121. def empty?
  122. inclusions.empty? && exclusions.empty_without_conditional_filters?
  123. end
  124. def prune(examples)
  125. examples.select {|e| !exclude?(e) && include?(e)}
  126. end
  127. def exclude(*args)
  128. merge(@exclusions, @inclusions, *args)
  129. end
  130. def exclude!(*args)
  131. replace(@exclusions, @inclusions, *args)
  132. end
  133. def exclude_with_low_priority(*args)
  134. reverse_merge(@exclusions, @inclusions, *args)
  135. end
  136. def exclude?(example)
  137. @exclusions.empty? ? false : example.any_apply?(@exclusions)
  138. end
  139. def include(*args)
  140. unless_standalone(*args) { merge(@inclusions, @exclusions, *args) }
  141. end
  142. def include!(*args)
  143. unless_standalone(*args) { replace(@inclusions, @exclusions, *args) }
  144. end
  145. def include_with_low_priority(*args)
  146. unless_standalone(*args) { reverse_merge(@inclusions, @exclusions, *args) }
  147. end
  148. def include?(example)
  149. @inclusions.empty? ? true : example.any_apply?(@inclusions)
  150. end
  151. private
  152. def unless_standalone(*args)
  153. is_standalone_filter?(args.last) ? @inclusions.replace(args.last) : yield unless already_set_standalone_filter?
  154. end
  155. def merge(orig, opposite, *updates)
  156. orig.merge!(updates.last).each_key {|k| opposite.delete(k)}
  157. end
  158. def replace(orig, opposite, *updates)
  159. updates.last.each_key {|k| opposite.delete(k)}
  160. orig.replace(updates.last)
  161. end
  162. def reverse_merge(orig, opposite, *updates)
  163. updated = updates.last.merge(orig)
  164. opposite.each_pair {|k,v| updated.delete(k) if updated[k] == v}
  165. orig.replace(updated)
  166. end
  167. def already_set_standalone_filter?
  168. is_standalone_filter?(inclusions)
  169. end
  170. def is_standalone_filter?(filter)
  171. STANDALONE_FILTERS.any? {|key| filter.has_key?(key)}
  172. end
  173. end
  174. end
  175. end