require 'erb'
require 'shellwords'

module RSpec
  module Core
    # Responsible for utilizing externally provided configuration options,
    # whether via the command line, `.rspec`, `~/.rspec`,
    # `$XDG_CONFIG_HOME/rspec/options`, `.rspec-local` or a custom options
    # file.
    class ConfigurationOptions
      # @param args [Array<String>] command line arguments
      def initialize(args)
        @args = args.dup
        organize_options
      end

      # Updates the provided {Configuration} instance based on the provided
      # external configuration options.
      #
      # @param config [Configuration] the configuration instance to update
      def configure(config)
        process_options_into config
        configure_filter_manager config.filter_manager
        load_formatters_into config
      end

      # @api private
      # Updates the provided {FilterManager} based on the filter options.
      # @param filter_manager [FilterManager] instance to update
      def configure_filter_manager(filter_manager)
        @filter_manager_options.each do |command, value|
          filter_manager.__send__ command, value
        end
      end

      # @return [Hash] the final merged options, drawn from all external sources
      attr_reader :options

      # @return [Array<String>] the original command-line arguments
      attr_reader :args

    private

      def organize_options
        @filter_manager_options = []

        @options = (file_options << command_line_options << env_options).each do |opts|
          @filter_manager_options << [:include, opts.delete(:inclusion_filter)] if opts.key?(:inclusion_filter)
          @filter_manager_options << [:exclude, opts.delete(:exclusion_filter)] if opts.key?(:exclusion_filter)
        end

        @options = @options.inject(:libs => [], :requires => []) do |hash, opts|
          hash.merge(opts) do |key, oldval, newval|
            [:libs, :requires].include?(key) ? oldval + newval : newval
          end
        end
      end

      UNFORCED_OPTIONS = Set.new([
        :requires, :profile, :drb, :libs, :files_or_directories_to_run,
        :full_description, :full_backtrace, :tty
      ])

      UNPROCESSABLE_OPTIONS = Set.new([:formatters])

      def force?(key)
        !UNFORCED_OPTIONS.include?(key)
      end

      def order(keys)
        OPTIONS_ORDER.reverse_each do |key|
          keys.unshift(key) if keys.delete(key)
        end
        keys
      end

      OPTIONS_ORDER = [
        # It's important to set this before anything that might issue a
        # deprecation (or otherwise access the reporter).
        :deprecation_stream,

        # In order for `RSpec.configuration.dry_run?` to return `true` during
        # processing the `requires` option, it must be parsed before it.
        :dry_run,

        # load paths depend on nothing, but must be set before `requires`
        # to support load-path-relative requires.
        :libs,

        # `files_or_directories_to_run` uses `default_path` so it must be
        # set before it.
        :default_path, :only_failures,

        # These must be set before `requires` to support checking
        # `config.files_to_run` from within `spec_helper.rb` when a
        # `-rspec_helper` option is used.
        :files_or_directories_to_run, :pattern, :exclude_pattern,

        # Necessary so that the `--seed` option is applied before requires,
        # in case required files do something with the provided seed.
        # (such as seed global randomization with it).
        :order,

        # In general, we want to require the specified files as early as
        # possible. The `--require` option is specifically intended to allow
        # early requires. For later requires, they can just put the require in
        # their spec files, but `--require` provides a unique opportunity for
        # users to instruct RSpec to load an extension file early for maximum
        # flexibility.
        :requires
      ]

      def process_options_into(config)
        opts = options.reject { |k, _| UNPROCESSABLE_OPTIONS.include? k }

        order(opts.keys).each do |key|
          force?(key) ? config.force(key => opts[key]) : config.__send__("#{key}=", opts[key])
        end
      end

      def load_formatters_into(config)
        options[:formatters].each { |pair| config.add_formatter(*pair) } if options[:formatters]
      end

      def file_options
        if custom_options_file
          [custom_options]
        else
          [global_options, project_options, local_options]
        end
      end

      def env_options
        return {} unless ENV['SPEC_OPTS']

        parse_args_ignoring_files_or_dirs_to_run(
          Shellwords.split(ENV["SPEC_OPTS"]),
          "ENV['SPEC_OPTS']"
        )
      end

      def command_line_options
        @command_line_options ||= Parser.parse(@args)
      end

      def custom_options
        options_from(custom_options_file)
      end

      def local_options
        @local_options ||= options_from(local_options_file)
      end

      def project_options
        @project_options ||= options_from(project_options_file)
      end

      def global_options
        @global_options ||= options_from(global_options_file)
      end

      def options_from(path)
        args = args_from_options_file(path)
        parse_args_ignoring_files_or_dirs_to_run(args, path)
      end

      def parse_args_ignoring_files_or_dirs_to_run(args, source)
        options = Parser.parse(args, source)
        options.delete(:files_or_directories_to_run)
        options
      end

      def args_from_options_file(path)
        return [] unless path && File.exist?(path)
        config_string = options_file_as_erb_string(path)
        config_lines = config_string.split(/\n+/).reject { |s| s =~ /\A\s*#/ }
        FlatMap.flat_map(config_lines, &:shellsplit)
      end

      # :nocov:
      def options_file_as_erb_string(path)
        if RUBY_VERSION >= '2.6'
          ERB.new(File.read(path), :trim_mode => '-').result(binding)
        else
          ERB.new(File.read(path), nil, '-').result(binding)
        end
      end
      # :nocov:

      def custom_options_file
        command_line_options[:custom_options_file]
      end

      def project_options_file
        "./.rspec"
      end

      def local_options_file
        "./.rspec-local"
      end

      def global_options_file
        xdg_options_file_if_exists || home_options_file_path
      end

      def xdg_options_file_if_exists
        path = xdg_options_file_path
        if path && File.exist?(path)
          path
        end
      end

      def home_options_file_path
        File.join(File.expand_path("~"), ".rspec")
      rescue ArgumentError
        # :nocov:
        RSpec.warning "Unable to find ~/.rspec because the HOME environment variable is not set"
        nil
        # :nocov:
      end

      def xdg_options_file_path
        xdg_config_home = resolve_xdg_config_home
        if xdg_config_home
          File.join(xdg_config_home, "rspec", "options")
        end
      end

      def resolve_xdg_config_home
        File.expand_path(ENV.fetch("XDG_CONFIG_HOME", "~/.config"))
      rescue ArgumentError
        # :nocov:
        # On Ruby 2.4, `File.expand("~")` works even if `ENV['HOME']` is not set.
        # But on earlier versions, it fails.
        nil
        # :nocov:
      end
    end
  end
end


require 'drb/drb'

module RSpec
  module Core
    # @private
    class DRbRunner
      def initialize(options, configuration=RSpec.configuration)
        @options       = options
        @configuration = configuration
      end

      def drb_port
        @options.options[:drb_port] || ENV['RSPEC_DRB'] || 8989
      end

      def run(err, out)
        begin
          DRb.start_service("druby://localhost:0")
        rescue SocketError, Errno::EADDRNOTAVAIL
          DRb.start_service("druby://:0")
        end
        spec_server = DRbObject.new_with_uri("druby://127.0.0.1:#{drb_port}")
        spec_server.run(drb_argv, err, out)
      end

      def drb_argv
        @drb_argv ||= begin
          @options.configure_filter_manager(@configuration.filter_manager)
          DRbOptions.new(@options.options, @configuration.filter_manager).options
        end
      end
    end

    # @private
    class DRbOptions
      def initialize(submitted_options, filter_manager)
        @submitted_options = submitted_options
        @filter_manager = filter_manager
      end

      def options
        argv = []
        argv << "--color"        if @submitted_options[:color]
        argv << "--force-color"  if @submitted_options[:color_mode] == :on
        argv << "--no-color"     if @submitted_options[:color_mode] == :off
        argv << "--profile"      if @submitted_options[:profile_examples]
        argv << "--backtrace"    if @submitted_options[:full_backtrace]
        argv << "--tty"          if @submitted_options[:tty]
        argv << "--fail-fast"    if @submitted_options[:fail_fast]
        argv << "--options"      << @submitted_options[:custom_options_file] if @submitted_options[:custom_options_file]
        argv << "--order"        << @submitted_options[:order]               if @submitted_options[:order]

        add_failure_exit_code(argv)
        add_error_exit_code(argv)
        add_full_description(argv)
        add_filter(argv, :inclusion, @filter_manager.inclusions)
        add_filter(argv, :exclusion, @filter_manager.exclusions)
        add_formatters(argv)
        add_libs(argv)
        add_requires(argv)

        argv + @submitted_options[:files_or_directories_to_run]
      end

      def add_failure_exit_code(argv)
        return unless @submitted_options[:failure_exit_code]

        argv << "--failure-exit-code" << @submitted_options[:failure_exit_code].to_s
      end

      def add_error_exit_code(argv)
        return unless @submitted_options[:error_exit_code]

        argv << "--error-exit-code" << @submitted_options[:error_exit_code].to_s
      end

      def add_full_description(argv)
        return unless @submitted_options[:full_description]

        # The argument to --example is regexp-escaped before being stuffed
        # into a regexp when received for the first time (see OptionParser).
        # Hence, merely grabbing the source of this regexp will retain the
        # backslashes, so we must remove them.
        @submitted_options[:full_description].each do |description|
          argv << "--example" << description.source.delete('\\')
        end
      end

      CONDITIONAL_FILTERS = [:if, :unless]

      def add_filter(argv, name, hash)
        hash.each_pair do |k, v|
          next if CONDITIONAL_FILTERS.include?(k)
          tag = name == :inclusion ? k.to_s.dup : "~#{k}".dup
          tag << ":#{v}" if v.is_a?(String)
          argv << "--tag" << tag
        end unless hash.empty?
      end

      def add_formatters(argv)
        @submitted_options[:formatters].each do |pair|
          argv << "--format" << pair[0]
          argv << "--out" << pair[1] if pair[1]
        end if @submitted_options[:formatters]
      end

      def add_libs(argv)
        @submitted_options[:libs].each do |path|
          argv << "-I" << path
        end if @submitted_options[:libs]
      end

      def add_requires(argv)
        @submitted_options[:requires].each do |path|
          argv << "--require" << path
        end if @submitted_options[:requires]
      end
    end
  end
end


RSpec::Support.require_rspec_support 'recursive_const_methods'

module RSpec
  module Core
    # rubocop:disable Metrics/ClassLength

    # ExampleGroup and {Example} are the main structural elements of
    # rspec-core. Consider this example:
    #
    #     RSpec.describe Thing do
    #       it "does something" do
    #       end
    #     end
    #
    # The object returned by `describe Thing` is a subclass of ExampleGroup.
    # The object returned by `it "does something"` is an instance of Example,
    # which serves as a wrapper for an instance of the ExampleGroup in which it
    # is declared.
    #
    # Example group bodies (e.g. `describe` or `context` blocks) are evaluated
    # in the context of a new subclass of ExampleGroup. Individual examples are
    # evaluated in the context of an instance of the specific ExampleGroup
    # subclass to which they belong.
    #
    # Besides the class methods defined here, there are other interesting macros
    # defined in {Hooks}, {MemoizedHelpers::ClassMethods} and
    # {SharedExampleGroup}. There are additional instance methods available to
    # your examples defined in {MemoizedHelpers} and {Pending}.
    class ExampleGroup
      extend Hooks

      include MemoizedHelpers
      extend MemoizedHelpers::ClassMethods
      include Pending
      extend SharedExampleGroup

      # Define a singleton method for the singleton class (remove the method if
      # it's already been defined).
      # @private
      def self.idempotently_define_singleton_method(name, &definition)
        (class << self; self; end).module_exec do
          remove_method(name) if method_defined?(name) && instance_method(name).owner == self
          define_method(name, &definition)
        end
      end

      # @!group Metadata

      # The [Metadata](Metadata) object associated with this group.
      # @see Metadata
      def self.metadata
        @metadata ||= nil
      end

      # Temporarily replace the provided metadata.
      # Intended primarily to allow an example group's singleton class
      # to return the metadata of the example that it exists for. This
      # is necessary for shared example group inclusion to work properly
      # with singleton example groups.
      # @private
      def self.with_replaced_metadata(meta)
        orig_metadata = metadata
        @metadata = meta
        yield
      ensure
        @metadata = orig_metadata
      end

      # @private
      # @return [Metadata] belonging to the parent of a nested {ExampleGroup}
      def self.superclass_metadata
        @superclass_metadata ||= superclass.respond_to?(:metadata) ? superclass.metadata : nil
      end

      # @private
      def self.delegate_to_metadata(*names)
        names.each do |name|
          idempotently_define_singleton_method(name) { metadata.fetch(name) }
        end
      end

      delegate_to_metadata :described_class, :file_path, :location

      # @return [String] the current example group description
      def self.description
        description = metadata[:description]
        RSpec.configuration.format_docstrings_block.call(description)
      end

      # Returns the class or module passed to the `describe` method (or alias).
      # Returns nil if the subject is not a class or module.
      # @example
      #     RSpec.describe Thing do
      #       it "does something" do
      #         described_class == Thing
      #       end
      #     end
      #
      def described_class
        self.class.described_class
      end

      # @!endgroup

      # @!group Defining Examples

      # @private
      # @macro [attach] define_example_method
      #   @!scope class
      #   @method $1
      #   @overload $1
      #   @overload $1(&example_implementation)
      #     @param example_implementation [Block] The implementation of the example.
      #   @overload $1(doc_string, *metadata)
      #     @param doc_string [String] The example's doc string.
      #     @param metadata [Array<Symbol>, Hash] Metadata for the example.
      #       Symbols will be transformed into hash entries with `true` values.
      #   @overload $1(doc_string, *metadata, &example_implementation)
      #     @param doc_string [String] The example's doc string.
      #     @param metadata [Array<Symbol>, Hash] Metadata for the example.
      #       Symbols will be transformed into hash entries with `true` values.
      #     @param example_implementation [Block] The implementation of the example.
      #   @yield [Example] the example object
      #   @example
      #     $1 do
      #     end
      #
      #     $1 "does something" do
      #     end
      #
      #     $1 "does something", :slow, :uses_js do
      #     end
      #
      #     $1 "does something", :with => 'additional metadata' do
      #     end
      #
      #     $1 "does something" do |ex|
      #       # ex is the Example object that contains metadata about the example
      #     end
      #
      #  @example
      #     $1 "does something", :slow, :load_factor => 100 do
      #     end
      #
      def self.define_example_method(name, extra_options={})
        idempotently_define_singleton_method(name) do |*all_args, &block|
          desc, *args = *all_args

          options = Metadata.build_hash_from(args)
          options.update(:skip => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block
          options.update(extra_options)

          RSpec::Core::Example.new(self, desc, options, block)
        end
      end

      # Defines an example within a group.
      define_example_method :example
      # Defines an example within a group.
      # This is the primary API to define a code example.
      define_example_method :it
      # Defines an example within a group.
      # Useful for when your docstring does not read well off of `it`.
      # @example
      #  RSpec.describe MyClass do
      #    specify "#do_something is deprecated" do
      #      # ...
      #    end
      #  end
      define_example_method :specify

      # Shortcut to define an example with `:focus => true`.
      # @see example
      define_example_method :focus,    :focus => true
      # Shortcut to define an example with `:focus => true`.
      # @see example
      define_example_method :fexample, :focus => true
      # Shortcut to define an example with `:focus => true`.
      # @see example
      define_example_method :fit,      :focus => true
      # Shortcut to define an example with `:focus => true`.
      # @see example
      define_example_method :fspecify, :focus => true
      # Shortcut to define an example with `:skip => 'Temporarily skipped with xexample'`.
      # @see example
      define_example_method :xexample, :skip => 'Temporarily skipped with xexample'
      # Shortcut to define an example with `:skip => 'Temporarily skipped with xit'`.
      # @see example
      define_example_method :xit,      :skip => 'Temporarily skipped with xit'
      # Shortcut to define an example with `:skip => 'Temporarily skipped with xspecify'`.
      # @see example
      define_example_method :xspecify, :skip => 'Temporarily skipped with xspecify'
      # Shortcut to define an example with `:skip => true`
      # @see example
      define_example_method :skip,     :skip => true
      # Shortcut to define an example with `:pending => true`
      # @see example
      define_example_method :pending,  :pending => true

      # @!endgroup

      # @!group Defining Example Groups

      # @private
      # @macro [attach] define_example_group_method
      #   @!scope class
      #   @overload $1
      #   @overload $1(&example_group_definition)
      #     @param example_group_definition [Block] The definition of the example group.
      #   @overload $1(doc_string, *metadata, &example_implementation)
      #     @param doc_string [String] The group's doc string.
      #     @param metadata [Array<Symbol>, Hash] Metadata for the group.
      #       Symbols will be transformed into hash entries with `true` values.
      #     @param example_group_definition [Block] The definition of the example group.
      #
      #   Generates a subclass of this example group which inherits
      #   everything except the examples themselves.
      #
      #   @example
      #
      #     RSpec.describe "something" do # << This describe method is defined in
      #                                   # << RSpec::Core::DSL, included in the
      #                                   # << global namespace (optional)
      #       before do
      #         do_something_before
      #       end
      #
      #       before(:example, :clean_env) do
      #         env.clear!
      #       end
      #
      #       let(:thing) { Thing.new }
      #
      #       $1 "attribute (of something)" do
      #         # examples in the group get the before hook
      #         # declared above, and can access `thing`
      #       end
      #
      #       $1 "needs additional setup", :clean_env, :implementation => JSON do
      #         # specifies that hooks with matching metadata
      #         # should be be run additionally
      #       end
      #     end
      #
      # @see DSL#describe
      def self.define_example_group_method(name, metadata={})
        idempotently_define_singleton_method(name) do |*args, &example_group_block|
          thread_data = RSpec::Support.thread_local_data
          top_level   = self == ExampleGroup

          registration_collection =
            if top_level
              if thread_data[:in_example_group]
                raise "Creating an isolated context from within a context is " \
                      "not allowed. Change `RSpec.#{name}` to `#{name}` or " \
                      "move this to a top-level scope."
              end

              thread_data[:in_example_group] = true
              RSpec.world.example_groups
            else
              children
            end

          begin
            description = args.shift
            combined_metadata = metadata.dup
            combined_metadata.merge!(args.pop) if args.last.is_a? Hash
            args << combined_metadata

            subclass(self, description, args, registration_collection, &example_group_block)
          ensure
            thread_data.delete(:in_example_group) if top_level
          end
        end

        RSpec::Core::DSL.expose_example_group_alias(name)
      end

      define_example_group_method :example_group

      # An alias of `example_group`. Generally used when grouping examples by a
      # thing you are describing (e.g. an object, class or method).
      # @see example_group
      define_example_group_method :describe

      # An alias of `example_group`. Generally used when grouping examples
      # contextually (e.g. "with xyz", "when xyz" or "if xyz").
      # @see example_group
      define_example_group_method :context

      # Shortcut to temporarily make an example group skipped.
      # @see example_group
      define_example_group_method :xdescribe, :skip => "Temporarily skipped with xdescribe"

      # Shortcut to temporarily make an example group skipped.
      # @see example_group
      define_example_group_method :xcontext,  :skip => "Temporarily skipped with xcontext"

      # Shortcut to define an example group with `:focus => true`.
      # @see example_group
      define_example_group_method :fdescribe, :focus => true

      # Shortcut to define an example group with `:focus => true`.
      # @see example_group
      define_example_group_method :fcontext,  :focus => true

      # @!endgroup

      # @!group Including Shared Example Groups

      # @private
      # @macro [attach] define_nested_shared_group_method
      #   @!scope class
      #
      #   @see SharedExampleGroup
      def self.define_nested_shared_group_method(new_name, report_label="it should behave like")
        idempotently_define_singleton_method(new_name) do |name, *args, &customization_block|
          # Pass :caller so the :location metadata is set properly.
          # Otherwise, it'll be set to the next line because that's
          # the block's source_location.
          group = example_group("#{report_label} #{name}", :caller => (the_caller = caller)) do
            find_and_eval_shared("examples", name, the_caller.first, *args, &customization_block)
          end
          group.metadata[:shared_group_name] = name
          group
        end
      end

      # Generates a nested example group and includes the shared content
      # mapped to `name` in the nested group.
      define_nested_shared_group_method :it_behaves_like, "behaves like"
      # Generates a nested example group and includes the shared content
      # mapped to `name` in the nested group.
      define_nested_shared_group_method :it_should_behave_like

      # Includes shared content mapped to `name` directly in the group in which
      # it is declared, as opposed to `it_behaves_like`, which creates a nested
      # group. If given a block, that block is also eval'd in the current
      # context.
      #
      # @see SharedExampleGroup
      def self.include_context(name, *args, &block)
        find_and_eval_shared("context", name, caller.first, *args, &block)
      end

      # Includes shared content mapped to `name` directly in the group in which
      # it is declared, as opposed to `it_behaves_like`, which creates a nested
      # group. If given a block, that block is also eval'd in the current
      # context.
      #
      # @see SharedExampleGroup
      def self.include_examples(name, *args, &block)
        find_and_eval_shared("examples", name, caller.first, *args, &block)
      end

      # Clear memoized values when adding/removing examples
      # @private
      def self.reset_memoized
        @descendant_filtered_examples = nil
        @_descendants = nil
        @parent_groups = nil
        @declaration_locations = nil
      end

      # Adds an example to the example group
      def self.add_example(example)
        reset_memoized
        examples << example
      end

      # Removes an example from the example group
      def self.remove_example(example)
        reset_memoized
        examples.delete example
      end

      # @private
      def self.find_and_eval_shared(label, name, inclusion_location, *args, &customization_block)
        shared_module = RSpec.world.shared_example_group_registry.find(parent_groups, name)

        unless shared_module
          raise ArgumentError, "Could not find shared #{label} #{name.inspect}"
        end

        shared_module.include_in(
          self, Metadata.relative_path(inclusion_location),
          args, customization_block
        )
      end

      # @!endgroup

      # @private
      def self.subclass(parent, description, args, registration_collection, &example_group_block)
        subclass = Class.new(parent)
        subclass.set_it_up(description, args, registration_collection, &example_group_block)
        subclass.module_exec(&example_group_block) if example_group_block

        # The LetDefinitions module must be included _after_ other modules
        # to ensure that it takes precedence when there are name collisions.
        # Thus, we delay including it until after the example group block
        # has been eval'd.
        MemoizedHelpers.define_helpers_on(subclass)

        subclass
      end

      # @private
      def self.set_it_up(description, args, registration_collection, &example_group_block)
        # Ruby 1.9 has a bug that can lead to infinite recursion and a
        # SystemStackError if you include a module in a superclass after
        # including it in a subclass: https://gist.github.com/845896
        # To prevent this, we must include any modules in
        # RSpec::Core::ExampleGroup before users create example groups and have
        # a chance to include the same module in a subclass of
        # RSpec::Core::ExampleGroup. So we need to configure example groups
        # here.
        ensure_example_groups_are_configured

        # Register the example with the group before creating the metadata hash.
        # This is necessary since creating the metadata hash triggers
        # `when_first_matching_example_defined` callbacks, in which users can
        # load RSpec support code which defines hooks. For that to work, the
        # examples and example groups must be registered at the time the
        # support code is called or be defined afterwards.
        # Begin defined beforehand but registered afterwards causes hooks to
        # not be applied where they should.
        registration_collection << self

        @user_metadata = Metadata.build_hash_from(args)

        @metadata = Metadata::ExampleGroupHash.create(
          superclass_metadata, @user_metadata,
          superclass.method(:next_runnable_index_for),
          description, *args, &example_group_block
        )

        config = RSpec.configuration
        config.apply_derived_metadata_to(@metadata)

        ExampleGroups.assign_const(self)

        @currently_executing_a_context_hook = false

        config.configure_group(self)
      end

      # @private
      def self.examples
        @examples ||= []
      end

      # @private
      def self.filtered_examples
        RSpec.world.filtered_examples[self]
      end

      # @private
      def self.descendant_filtered_examples
        @descendant_filtered_examples ||= filtered_examples +
          FlatMap.flat_map(children, &:descendant_filtered_examples)
      end

      # @private
      def self.children
        @children ||= []
      end

      # @private
      # Traverses the tree of groups, starting with `self`, then the children, recursively.
      # Halts the traversal of a branch of the tree as soon as the passed block returns true.
      # Note that siblings groups and their sub-trees will continue to be explored.
      # This is intended to make it easy to find the top-most group that satisfies some
      # condition.
      def self.traverse_tree_until(&block)
        return if yield self

        children.each do |child|
          child.traverse_tree_until(&block)
        end
      end

      # @private
      def self.next_runnable_index_for(file)
        if self == ExampleGroup
          # We add 1 so the ids start at 1 instead of 0. This is
          # necessary for this branch (but not for the other one)
          # because we register examples and groups with the
          # `children` and `examples` collection BEFORE this
          # method is called as part of metadata hash creation,
          # but the example group is recorded with
          # `RSpec.world.example_group_counts_by_spec_file` AFTER
          # the metadata hash is created and the group is returned
          # to the caller.
          RSpec.world.num_example_groups_defined_in(file) + 1
        else
          children.count + examples.count
        end
      end

      # @private
      def self.descendants
        @_descendants ||= [self] + FlatMap.flat_map(children, &:descendants)
      end

      ## @private
      def self.parent_groups
        @parent_groups ||= ancestors.select { |a| a < RSpec::Core::ExampleGroup }
      end

      # @private
      def self.top_level?
        superclass == ExampleGroup
      end

      # @private
      def self.ensure_example_groups_are_configured
        unless defined?(@@example_groups_configured)
          RSpec.configuration.configure_mock_framework
          RSpec.configuration.configure_expectation_framework
          # rubocop:disable Style/ClassVars
          @@example_groups_configured = true
          # rubocop:enable Style/ClassVars
        end
      end

      # @private
      def self.before_context_ivars
        @before_context_ivars ||= {}
      end

      # @private
      def self.store_before_context_ivars(example_group_instance)
        each_instance_variable_for_example(example_group_instance) do |ivar|
          before_context_ivars[ivar] = example_group_instance.instance_variable_get(ivar)
        end
      end

      # Returns true if a `before(:context)` or `after(:context)`
      # hook is currently executing.
      def self.currently_executing_a_context_hook?
        @currently_executing_a_context_hook
      end

      # @private
      def self.run_before_context_hooks(example_group_instance)
        set_ivars(example_group_instance, superclass_before_context_ivars)

        @currently_executing_a_context_hook = true

        ContextHookMemoized::Before.isolate_for_context_hook(example_group_instance) do
          hooks.run(:before, :context, example_group_instance)
        end
      ensure
        store_before_context_ivars(example_group_instance)
        @currently_executing_a_context_hook = false
      end

      if RUBY_VERSION.to_f >= 1.9
        # @private
        def self.superclass_before_context_ivars
          superclass.before_context_ivars
        end
      else # 1.8.7
        # :nocov:
        # @private
        def self.superclass_before_context_ivars
          if superclass.respond_to?(:before_context_ivars)
            superclass.before_context_ivars
          else
            # `self` must be the singleton class of an ExampleGroup instance.
            # On 1.8.7, the superclass of a singleton class of an instance of A
            # is A's singleton class. On 1.9+, it's A. On 1.8.7, the first ancestor
            # is A, so we can mirror 1.8.7's behavior here. Note that we have to
            # search for the first that responds to `before_context_ivars`
            # in case a module has been included in the singleton class.
            ancestors.find { |a| a.respond_to?(:before_context_ivars) }.before_context_ivars
          end
        end
        # :nocov:
      end

      # @private
      def self.run_after_context_hooks(example_group_instance)
        set_ivars(example_group_instance, before_context_ivars)

        @currently_executing_a_context_hook = true

        ContextHookMemoized::After.isolate_for_context_hook(example_group_instance) do
          hooks.run(:after, :context, example_group_instance)
        end
      ensure
        before_context_ivars.clear
        @currently_executing_a_context_hook = false
      end

      # Runs all the examples in this group.
      def self.run(reporter=RSpec::Core::NullReporter)
        return if RSpec.world.wants_to_quit
        reporter.example_group_started(self)

        should_run_context_hooks = descendant_filtered_examples.any?
        begin
          RSpec.current_scope = :before_context_hook
          run_before_context_hooks(new('before(:context) hook')) if should_run_context_hooks
          result_for_this_group = run_examples(reporter)
          results_for_descendants = ordering_strategy.order(children).map { |child| child.run(reporter) }.all?
          result_for_this_group && results_for_descendants
        rescue Pending::SkipDeclaredInExample => ex
          for_filtered_examples(reporter) { |example| example.skip_with_exception(reporter, ex) }
          true
        rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex
          for_filtered_examples(reporter) { |example| example.fail_with_exception(reporter, ex) }
          RSpec.world.wants_to_quit = true if reporter.fail_fast_limit_met?
          false
        ensure
          RSpec.current_scope = :after_context_hook
          run_after_context_hooks(new('after(:context) hook')) if should_run_context_hooks
          reporter.example_group_finished(self)
        end
      end

      # @private
      def self.ordering_strategy
        order = metadata.fetch(:order, :global)
        registry = RSpec.configuration.ordering_registry

        registry.fetch(order) do
          warn <<-WARNING.gsub(/^ +\|/, '')
            |WARNING: Ignoring unknown ordering specified using `:order => #{order.inspect}` metadata.
            |         Falling back to configured global ordering.
            |         Unrecognized ordering specified at: #{location}
          WARNING

          registry.fetch(:global)
        end
      end

      # @private
      def self.run_examples(reporter)
        ordering_strategy.order(filtered_examples).map do |example|
          next if RSpec.world.wants_to_quit
          instance = new(example.inspect_output)
          set_ivars(instance, before_context_ivars)
          succeeded = example.run(instance, reporter)
          if !succeeded && reporter.fail_fast_limit_met?
            RSpec.world.wants_to_quit = true
          end
          succeeded
        end.all?
      end

      # @private
      def self.for_filtered_examples(reporter, &block)
        filtered_examples.each(&block)

        children.each do |child|
          reporter.example_group_started(child)
          child.for_filtered_examples(reporter, &block)
          reporter.example_group_finished(child)
        end
        false
      end

      # @private
      def self.declaration_locations
        @declaration_locations ||= [Metadata.location_tuple_from(metadata)] +
          examples.map { |e| Metadata.location_tuple_from(e.metadata) } +
          FlatMap.flat_map(children, &:declaration_locations)
      end

      # @return [String] the unique id of this example group. Pass
      #   this at the command line to re-run this exact example group.
      def self.id
        Metadata.id_from(metadata)
      end

      # @private
      def self.top_level_description
        parent_groups.last.description
      end

      # @private
      def self.set_ivars(instance, ivars)
        ivars.each { |name, value| instance.instance_variable_set(name, value) }
      end

      if RUBY_VERSION.to_f < 1.9
        # :nocov:
        # @private
        INSTANCE_VARIABLE_TO_IGNORE = '@__inspect_output'.freeze
        # :nocov:
      else
        # @private
        INSTANCE_VARIABLE_TO_IGNORE = :@__inspect_output
      end

      # @private
      def self.each_instance_variable_for_example(group)
        group.instance_variables.each do |ivar|
          yield ivar unless ivar == INSTANCE_VARIABLE_TO_IGNORE
        end
      end

      # @private
      def initialize(inspect_output=nil)
        @__inspect_output = inspect_output || '(no description provided)'
        super() # no args get passed
      end

      # @private
      def inspect
        "#<#{self.class} #{@__inspect_output}>"
      end

      unless method_defined?(:singleton_class) # for 1.8.7
        # :nocov:
        # @private
        def singleton_class
          class << self; self; end
        end
        # :nocov:
      end

      # @private
      def self.update_inherited_metadata(updates)
        metadata.update(updates) do |key, existing_group_value, new_inherited_value|
          @user_metadata.key?(key) ? existing_group_value : new_inherited_value
        end

        RSpec.configuration.configure_group(self)
        examples.each { |ex| ex.update_inherited_metadata(updates) }
        children.each { |group| group.update_inherited_metadata(updates) }
      end

      # Raised when an RSpec API is called in the wrong scope, such as `before`
      # being called from within an example rather than from within an example
      # group block.
      WrongScopeError = Class.new(NoMethodError)

      def self.method_missing(name, *args)
        if method_defined?(name)
          raise WrongScopeError,
                "`#{name}` is not available on an example group (e.g. a " \
                "`describe` or `context` block). It is only available from " \
                "within individual examples (e.g. `it` blocks) or from " \
                "constructs that run in the scope of an example (e.g. " \
                "`before`, `let`, etc)."
        end

        super
      end
      private_class_method :method_missing

    private

      def method_missing(name, *args)
        if self.class.respond_to?(name)
          raise WrongScopeError,
                "`#{name}` is not available from within an example (e.g. an " \
                "`it` block) or from constructs that run in the scope of an " \
                "example (e.g. `before`, `let`, etc). It is only available " \
                "on an example group (e.g. a `describe` or `context` block)."
        end

        super(name, *args)
      end
      ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
    end
    # rubocop:enable Metrics/ClassLength

    # @private
    # Unnamed example group used by `SuiteHookContext`.
    class AnonymousExampleGroup < ExampleGroup
      def self.metadata
        {}
      end
    end

    # Contains information about the inclusion site of a shared example group.
    class SharedExampleGroupInclusionStackFrame
      # @return [String] the name of the shared example group
      attr_reader :shared_group_name
      # @return [String] the location where the shared example was included
      attr_reader :inclusion_location

      # @private
      def initialize(shared_group_name, inclusion_location)
        @shared_group_name  = shared_group_name
        @inclusion_location = inclusion_location
      end

      # @return [String] The {#inclusion_location}, formatted for display by a formatter.
      def formatted_inclusion_location
        @formatted_inclusion_location ||= begin
          RSpec.configuration.backtrace_formatter.backtrace_line(
            inclusion_location.sub(/(:\d+):in .+$/, '\1')
          )
        end
      end

      # @return [String] Description of this stack frame, in the form used by
      #   RSpec's built-in formatters.
      def description
        @description ||= "Shared Example Group: #{shared_group_name.inspect} " \
          "called from #{formatted_inclusion_location}"
      end

      # @private
      def self.current_backtrace
        shared_example_group_inclusions.reverse
      end

      # @private
      def self.with_frame(name, location)
        current_stack = shared_example_group_inclusions
        if current_stack.any? { |frame| frame.shared_group_name == name }
          raise ArgumentError, "can't include shared examples recursively"
        else
          current_stack << new(name, location)
          yield
        end
      ensure
        current_stack.pop
      end

      # @private
      def self.shared_example_group_inclusions
        RSpec::Support.thread_local_data[:shared_example_group_inclusions] ||= []
      end
    end
  end

  # @private
  #
  # Namespace for the example group subclasses generated by top-level
  # `describe`.
  module ExampleGroups
    extend Support::RecursiveConstMethods

    def self.assign_const(group)
      base_name   = base_name_for(group)
      const_scope = constant_scope_for(group)
      name        = disambiguate(base_name, const_scope)

      const_scope.const_set(name, group)
    end

    def self.constant_scope_for(group)
      const_scope = group.superclass
      const_scope = self if const_scope == ::RSpec::Core::ExampleGroup
      const_scope
    end

    def self.remove_all_constants
      constants.each do |constant|
        __send__(:remove_const, constant)
      end
    end

    def self.base_name_for(group)
      return "Anonymous".dup if group.description.empty?

      # Convert to CamelCase.
      name = ' ' + group.description
      name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do
        match = ::Regexp.last_match[1]
        match.upcase!
        match
      end

      name.lstrip!                # Remove leading whitespace
      name.gsub!(/\W/, ''.freeze) # JRuby, RBX and others don't like non-ascii in const names

      # Ruby requires first const letter to be A-Z. Use `Nested`
      # as necessary to enforce that.
      name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1'.freeze)

      name
    end

    if RUBY_VERSION == '1.9.2'
      # :nocov:
      class << self
        alias _base_name_for base_name_for
        def base_name_for(group)
          _base_name_for(group) + '_'
        end
      end
      private_class_method :_base_name_for
      # :nocov:
    end

    def self.disambiguate(name, const_scope)
      return name unless const_defined_on?(const_scope, name)

      # Add a trailing number if needed to disambiguate from an existing
      # constant.
      name << "_2"
      name.next! while const_defined_on?(const_scope, name)
      name
    end
  end
end


def self.set_it_up(description, args, registration_collection, &example_group_block)
        # Ruby 1.9 has a bug that can lead to infinite recursion and a
        # SystemStackError if you include a module in a superclass after
        # including it in a subclass: https://gist.github.com/845896
        # To prevent this, we must include any modules in
        # RSpec::Core::ExampleGroup before users create example groups and have
        # a chance to include the same module in a subclass of
        # RSpec::Core::ExampleGroup. So we need to configure example groups
        # here.
        ensure_example_groups_are_configured

        # Register the example with the group before creating the metadata hash.
        # This is necessary since creating the metadata hash triggers
        # `when_first_matching_example_defined` callbacks, in which users can
        # load RSpec support code which defines hooks. For that to work, the
        # examples and example groups must be registered at the time the
        # support code is called or be defined afterwards.
        # Begin defined beforehand but registered afterwards causes hooks to
        # not be applied where they should.
        registration_collection << self

        @user_metadata = Metadata.build_hash_from(args)

        @metadata = Metadata::ExampleGroupHash.create(
          superclass_metadata, @user_metadata,
          superclass.method(:next_runnable_index_for),
          description, *args, &example_group_block
        )

        config = RSpec.configuration
        config.apply_derived_metadata_to(@metadata)

        ExampleGroups.assign_const(self)

        @currently_executing_a_context_hook = false

        config.configure_group(self)
      end


def self.run_before_context_hooks(example_group_instance)
        set_ivars(example_group_instance, superclass_before_context_ivars)

        @currently_executing_a_context_hook = true

        ContextHookMemoized::Before.isolate_for_context_hook(example_group_instance) do
          hooks.run(:before, :context, example_group_instance)
        end
      ensure
        store_before_context_ivars(example_group_instance)
        @currently_executing_a_context_hook = false
      end


def self.run_after_context_hooks(example_group_instance)
        set_ivars(example_group_instance, before_context_ivars)

        @currently_executing_a_context_hook = true

        ContextHookMemoized::After.isolate_for_context_hook(example_group_instance) do
          hooks.run(:after, :context, example_group_instance)
        end
      ensure
        before_context_ivars.clear
        @currently_executing_a_context_hook = false
      end


def self.ordering_strategy
        order = metadata.fetch(:order, :global)
        registry = RSpec.configuration.ordering_registry

        registry.fetch(order) do
          warn <<-WARNING.gsub(/^ +\|/, '')
            |WARNING: Ignoring unknown ordering specified using `:order => #{order.inspect}` metadata.
            |         Falling back to configured global ordering.
            |         Unrecognized ordering specified at: #{location}
          WARNING

          registry.fetch(:global)
        end
      end


def self.method_missing(name, *args)
        if method_defined?(name)
          raise WrongScopeError,
                "`#{name}` is not available on an example group (e.g. a " \
                "`describe` or `context` block). It is only available from " \
                "within individual examples (e.g. `it` blocks) or from " \
                "constructs that run in the scope of an example (e.g. " \
                "`before`, `let`, etc)."
        end

        super
      end


def method_missing(name, *args)
        if self.class.respond_to?(name)
          raise WrongScopeError,
                "`#{name}` is not available from within an example (e.g. an " \
                "`it` block) or from constructs that run in the scope of an " \
                "example (e.g. `before`, `let`, etc). It is only available " \
                "on an example group (e.g. a `describe` or `context` block)."
        end

        super(name, *args)
      end


def self.with_frame(name, location)
        current_stack = shared_example_group_inclusions
        if current_stack.any? { |frame| frame.shared_group_name == name }
          raise ArgumentError, "can't include shared examples recursively"
        else
          current_stack << new(name, location)
          yield
        end
      ensure
        current_stack.pop
      end


def self.base_name_for(group)
      return "Anonymous".dup if group.description.empty?

      # Convert to CamelCase.
      name = ' ' + group.description
      name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do
        match = ::Regexp.last_match[1]
        match.upcase!
        match
      end

      name.lstrip!                # Remove leading whitespace
      name.gsub!(/\W/, ''.freeze) # JRuby, RBX and others don't like non-ascii in const names

      # Ruby requires first const letter to be A-Z. Use `Nested`
      # as necessary to enforce that.
      name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1'.freeze)

      name
    end


RSpec::Support.require_rspec_support "directory_maker"

module RSpec
  module Core
    # Persists example ids and their statuses so that we can filter
    # to just the ones that failed the last time they ran.
    # @private
    class ExampleStatusPersister
      def self.load_from(file_name)
        return [] unless File.exist?(file_name)
        ExampleStatusParser.parse(File.read(file_name))
      end

      def self.persist(examples, file_name)
        new(examples, file_name).persist
      end

      def initialize(examples, file_name)
        @examples  = examples
        @file_name = file_name
      end

      def persist
        RSpec::Support::DirectoryMaker.mkdir_p(File.dirname(@file_name))
        File.open(@file_name, File::RDWR | File::CREAT) do |f|
          # lock the file while reading / persisting to avoid a race
          # condition where parallel or unrelated spec runs race to
          # update the same file
          f.flock(File::LOCK_EX)
          unparsed_previous_runs = f.read
          f.rewind
          f.write(dump_statuses(unparsed_previous_runs))
          f.flush
          f.truncate(f.pos)
        end
      end

    private

      def dump_statuses(unparsed_previous_runs)
        statuses_from_previous_runs = ExampleStatusParser.parse(unparsed_previous_runs)
        merged_statuses = ExampleStatusMerger.merge(statuses_from_this_run, statuses_from_previous_runs)
        ExampleStatusDumper.dump(merged_statuses)
      end

      def statuses_from_this_run
        @examples.map do |ex|
          result = ex.execution_result

          {
            :example_id => ex.id,
            :status     => result.status ? result.status.to_s : Configuration::UNKNOWN_STATUS,
            :run_time   => result.run_time ? Formatters::Helpers.format_duration(result.run_time) : ""
          }
        end
      end
    end

    # Merges together a list of example statuses from this run
    # and a list from previous runs (presumably loaded from disk).
    # Each example status object is expected to be a hash with
    # at least an `:example_id` and a `:status` key. Examples that
    # were loaded but not executed (due to filtering, `--fail-fast`
    # or whatever) should have a `:status` of `UNKNOWN_STATUS`.
    #
    # This will produce a new list that:
    #   - Will be missing examples from previous runs that we know for sure
    #     no longer exist.
    #   - Will have the latest known status for any examples that either
    #     definitively do exist or may still exist.
    #   - Is sorted by file name and example definition order, so that
    #     the saved file is easily scannable if users want to inspect it.
    # @private
    class ExampleStatusMerger
      def self.merge(this_run, from_previous_runs)
        new(this_run, from_previous_runs).merge
      end

      def initialize(this_run, from_previous_runs)
        @this_run           = hash_from(this_run)
        @from_previous_runs = hash_from(from_previous_runs)
        @file_exists_cache  = Hash.new { |hash, file| hash[file] = File.exist?(file) }
      end

      def merge
        delete_previous_examples_that_no_longer_exist

        @this_run.merge(@from_previous_runs) do |_ex_id, new, old|
          new.fetch(:status) == Configuration::UNKNOWN_STATUS ? old : new
        end.values.sort_by(&method(:sort_value_from))
      end

    private

      def hash_from(example_list)
        example_list.inject({}) do |hash, example|
          hash[example.fetch(:example_id)] = example
          hash
        end
      end

      def delete_previous_examples_that_no_longer_exist
        @from_previous_runs.delete_if do |ex_id, _|
          example_must_no_longer_exist?(ex_id)
        end
      end

      def example_must_no_longer_exist?(ex_id)
        # Obviously, it exists if it was loaded for this spec run...
        return false if @this_run.key?(ex_id)

        spec_file = spec_file_from(ex_id)

        # `this_run` includes examples that were loaded but not executed.
        # Given that, if the spec file for this example was loaded,
        # but the id does not still exist, it's safe to assume that
        # the example must no longer exist.
        return true if loaded_spec_files.include?(spec_file)

        # The example may still exist as long as the file exists...
        !@file_exists_cache[spec_file]
      end

      def loaded_spec_files
        @loaded_spec_files ||= Set.new(@this_run.keys.map(&method(:spec_file_from)))
      end

      def spec_file_from(ex_id)
        ex_id.split("[").first
      end

      def sort_value_from(example)
        file, scoped_id = Example.parse_id(example.fetch(:example_id))
        [file, *scoped_id.split(":").map(&method(:Integer))]
      end
    end

    # Dumps a list of hashes in a pretty, human readable format
    # for later parsing. The hashes are expected to have symbol
    # keys and string values, and each hash should have the same
    # set of keys.
    # @private
    class ExampleStatusDumper
      def self.dump(examples)
        new(examples).dump
      end

      def initialize(examples)
        @examples = examples
      end

      def dump
        return nil if @examples.empty?
        (formatted_header_rows + formatted_value_rows).join("\n") << "\n"
      end

    private

      def formatted_header_rows
        @formatted_header_rows ||= begin
          dividers = column_widths.map { |w| "-" * w }
          [formatted_row_from(headers.map(&:to_s)), formatted_row_from(dividers)]
        end
      end

      def formatted_value_rows
        @formatted_value_rows ||= rows.map do |row|
          formatted_row_from(row)
        end
      end

      def rows
        @rows ||= @examples.map { |ex| ex.values_at(*headers) }
      end

      def formatted_row_from(row_values)
        padded_values = row_values.each_with_index.map do |value, index|
          value.ljust(column_widths[index])
        end

        padded_values.join(" | ") << " |"
      end

      def headers
        @headers ||= @examples.first.keys
      end

      def column_widths
        @column_widths ||= begin
          value_sets = rows.transpose

          headers.each_with_index.map do |header, index|
            values = value_sets[index] << header.to_s
            values.map(&:length).max
          end
        end
      end
    end

    # Parses a string that has been previously dumped by ExampleStatusDumper.
    # Note that this parser is a bit naive in that it does a simple split on
    # "\n" and " | ", with no concern for handling escaping. For now, that's
    # OK because the values we plan to persist (example id, status, and perhaps
    # example duration) are highly unlikely to contain "\n" or " | " -- after
    # all, who puts those in file names?
    # @private
    class ExampleStatusParser
      def self.parse(string)
        new(string).parse
      end

      def initialize(string)
        @header_line, _, *@row_lines = string.lines.to_a
      end

      def parse
        @row_lines.map { |line| parse_row(line) }
      end

    private

      def parse_row(line)
        Hash[headers.zip(split_line(line))]
      end

      def headers
        @headers ||= split_line(@header_line).grep(/\S/).map(&:to_sym)
      end

      def split_line(line)
        line.split(/\s+\|\s+?/, -1)
      end
    end
  end
end


def statuses_from_this_run
        @examples.map do |ex|
          result = ex.execution_result

          {
            :example_id => ex.id,
            :status     => result.status ? result.status.to_s : Configuration::UNKNOWN_STATUS,
            :run_time   => result.run_time ? Formatters::Helpers.format_duration(result.run_time) : ""
          }
        end
      end


module RSpec
  module Core
    # @private
    class FilterManager
      attr_reader :exclusions, :inclusions

      def initialize
        @exclusions, @inclusions = FilterRules.build
      end

      # @api private
      #
      # @param file_path [String]
      # @param line_numbers [Array]
      def add_location(file_path, line_numbers)
        # locations is a hash of expanded paths to arrays of line
        # numbers to match against. e.g.
        #   { "path/to/file.rb" => [37, 42] }
        add_path_to_arrays_filter(:locations, File.expand_path(file_path), line_numbers)
      end

      def add_ids(rerun_path, scoped_ids)
        # ids is a hash of relative paths to arrays of ids
        # to match against. e.g.
        #   { "./path/to/file.rb" => ["1:1", "2:4"] }
        rerun_path = Metadata.relative_path(File.expand_path rerun_path)
        add_path_to_arrays_filter(:ids, rerun_path, scoped_ids)
      end

      def empty?
        inclusions.empty? && exclusions.empty?
      end

      def prune(examples)
        # Semantically, this is unnecessary (the filtering below will return the empty
        # array unmodified), but for perf reasons it's worth exiting early here. Users
        # commonly have top-level examples groups that do not have any direct examples
        # and instead have nested groups with examples. In that kind of situation,
        # `examples` will be empty.
        return examples if examples.empty?

        examples = prune_conditionally_filtered_examples(examples)

        if inclusions.standalone?
          examples.select { |e| inclusions.include_example?(e) }
        else
          locations, ids, non_scoped_inclusions = inclusions.split_file_scoped_rules

          examples.select do |ex|
            file_scoped_include?(ex.metadata, ids, locations) do
              !exclusions.include_example?(ex) && non_scoped_inclusions.include_example?(ex)
            end
          end
        end
      end

      def exclude(*args)
        exclusions.add(args.last)
      end

      def exclude_only(*args)
        exclusions.use_only(args.last)
      end

      def exclude_with_low_priority(*args)
        exclusions.add_with_low_priority(args.last)
      end

      def include(*args)
        inclusions.add(args.last)
      end

      def include_only(*args)
        inclusions.use_only(args.last)
      end

      def include_with_low_priority(*args)
        inclusions.add_with_low_priority(args.last)
      end

    private

      def add_path_to_arrays_filter(filter_key, path, values)
        filter = inclusions.delete(filter_key) || Hash.new { |h, k| h[k] = [] }
        filter[path].concat(values)
        inclusions.add(filter_key => filter)
      end

      def prune_conditionally_filtered_examples(examples)
        examples.reject do |ex|
          meta = ex.metadata
          !meta.fetch(:if, true) || meta[:unless]
        end
      end

      # When a user specifies a particular spec location, that takes priority
      # over any exclusion filters (such as if the spec is tagged with `:slow`
      # and there is a `:slow => true` exclusion filter), but only for specs
      # defined in the same file as the location filters. Excluded specs in
      # other files should still be excluded.
      def file_scoped_include?(ex_metadata, ids, locations)
        no_id_filters = ids[ex_metadata[:rerun_file_path]].empty?
        no_location_filters = locations[
          File.expand_path(ex_metadata[:rerun_file_path])
        ].empty?

        return yield if no_location_filters && no_id_filters

        MetadataFilter.filter_applies?(:ids, ids, ex_metadata) ||
        MetadataFilter.filter_applies?(:locations, locations, ex_metadata)
      end
    end

    # @private
    class FilterRules
      PROC_HEX_NUMBER = /0x[0-9a-f]+@?/
      PROJECT_DIR = File.expand_path('.')

      attr_accessor :opposite
      attr_reader :rules

      def self.build
        exclusions = ExclusionRules.new
        inclusions = InclusionRules.new
        exclusions.opposite = inclusions
        inclusions.opposite = exclusions
        [exclusions, inclusions]
      end

      def initialize(rules={})
        @rules = rules
      end

      def add(updated)
        @rules.merge!(updated).each_key { |k| opposite.delete(k) }
      end

      def add_with_low_priority(updated)
        updated = updated.merge(@rules)
        opposite.each_pair { |k, v| updated.delete(k) if updated[k] == v }
        @rules.replace(updated)
      end

      def use_only(updated)
        updated.each_key { |k| opposite.delete(k) }
        @rules.replace(updated)
      end

      def clear
        @rules.clear
      end

      def delete(key)
        @rules.delete(key)
      end

      def fetch(*args, &block)
        @rules.fetch(*args, &block)
      end

      def [](key)
        @rules[key]
      end

      def empty?
        rules.empty?
      end

      def each_pair(&block)
        @rules.each_pair(&block)
      end

      def description
        rules.inspect.gsub(PROC_HEX_NUMBER, '').gsub(PROJECT_DIR, '.').gsub(' (lambda)', '')
      end

      def include_example?(example)
        MetadataFilter.apply?(:any?, @rules, example.metadata)
      end
    end

    # @private
    ExclusionRules = FilterRules

    # @private
    class InclusionRules < FilterRules
      def add(*args)
        apply_standalone_filter(*args) || super
      end

      def add_with_low_priority(*args)
        apply_standalone_filter(*args) || super
      end

      def include_example?(example)
        @rules.empty? || super
      end

      def standalone?
        is_standalone_filter?(@rules)
      end

      def split_file_scoped_rules
        rules_dup = @rules.dup
        locations = rules_dup.delete(:locations) { Hash.new([]) }
        ids       = rules_dup.delete(:ids)       { Hash.new([]) }

        return locations, ids, self.class.new(rules_dup)
      end

    private

      def apply_standalone_filter(updated)
        return true if standalone?
        return nil unless is_standalone_filter?(updated)

        replace_filters(updated)
        true
      end

      def replace_filters(new_rules)
        @rules.replace(new_rules)
        opposite.clear
      end

      def is_standalone_filter?(rules)
        rules.key?(:full_description)
      end
    end
  end
end


RSpec::Support.require_rspec_support "directory_maker"

# ## Built-in Formatters
#
# * progress (default) - Prints dots for passing examples, `F` for failures, `*`
#                        for pending.
# * documentation - Prints the docstrings passed to `describe` and `it` methods
#                   (and their aliases).
# * html
# * json - Useful for archiving data for subsequent analysis.
#
# The progress formatter is the default, but you can choose any one or more of
# the other formatters by passing with the `--format` (or `-f` for short)
# command-line option, e.g.
#
#     rspec --format documentation
#
# You can also send the output of multiple formatters to different streams, e.g.
#
#     rspec --format documentation --format html --out results.html
#
# This example sends the output of the documentation formatter to `$stdout`, and
# the output of the html formatter to results.html.
#
# ## Custom Formatters
#
# You can tell RSpec to use a custom formatter by passing its path and name to
# the `rspec` command. For example, if you define MyCustomFormatter in
# path/to/my_custom_formatter.rb, you would type this command:
#
#     rspec --require path/to/my_custom_formatter.rb --format MyCustomFormatter
#
# The reporter calls every formatter with this protocol:
#
# * To start
#   * `start(StartNotification)`
# * Once per example group
#   * `example_group_started(GroupNotification)`
# * Once per example
#   * `example_started(ExampleNotification)`
# * One of these per example, depending on outcome
#   * `example_passed(ExampleNotification)`
#   * `example_failed(FailedExampleNotification)`
#   * `example_pending(ExampleNotification)`
# * Optionally at any time
#   * `message(MessageNotification)`
# * At the end of the suite
#   * `stop(ExamplesNotification)`
#   * `start_dump(NullNotification)`
#   * `dump_pending(ExamplesNotification)`
#   * `dump_failures(ExamplesNotification)`
#   * `dump_summary(SummaryNotification)`
#   * `seed(SeedNotification)`
#   * `close(NullNotification)`
#
# Only the notifications to which you subscribe your formatter will be called
# on your formatter. To subscribe your formatter use:
# `RSpec::Core::Formatters#register` e.g.
#
# `RSpec::Core::Formatters.register FormatterClassName, :example_passed, :example_failed`
#
# We recommend you implement the methods yourself; for simplicity we provide the
# default formatter output via our notification objects but if you prefer you
# can subclass `RSpec::Core::Formatters::BaseTextFormatter` and override the
# methods you wish to enhance.
#
# @see RSpec::Core::Formatters::BaseTextFormatter
# @see RSpec::Core::Reporter
module RSpec::Core::Formatters
  autoload :DocumentationFormatter,   'rspec/core/formatters/documentation_formatter'
  autoload :HtmlFormatter,            'rspec/core/formatters/html_formatter'
  autoload :FallbackMessageFormatter, 'rspec/core/formatters/fallback_message_formatter'
  autoload :ProgressFormatter,        'rspec/core/formatters/progress_formatter'
  autoload :ProfileFormatter,         'rspec/core/formatters/profile_formatter'
  autoload :JsonFormatter,            'rspec/core/formatters/json_formatter'
  autoload :BisectDRbFormatter,       'rspec/core/formatters/bisect_drb_formatter'
  autoload :ExceptionPresenter,       'rspec/core/formatters/exception_presenter'
  autoload :FailureListFormatter,     'rspec/core/formatters/failure_list_formatter'

  # Register the formatter class
  # @param formatter_class [Class] formatter class to register
  # @param notifications [Array<Symbol>] one or more notifications to be
  #   registered to the specified formatter
  #
  # @see RSpec::Core::Formatters::BaseFormatter
  def self.register(formatter_class, *notifications)
    Loader.formatters[formatter_class] = notifications
  end

  # @api private
  #
  # `RSpec::Core::Formatters::Loader` is an internal class for
  # managing formatters used by a particular configuration. It is
  # not expected to be used directly, but only through the configuration
  # interface.
  class Loader
    # @api private
    #
    # Internal formatters are stored here when loaded.
    def self.formatters
      @formatters ||= {}
    end

    # @api private
    def initialize(reporter)
      @formatters = []
      @reporter = reporter
      self.default_formatter = 'progress'
    end

    # @return [Array] the loaded formatters
    attr_reader :formatters

    # @return [Reporter] the reporter
    attr_reader :reporter

    # @return [String] the default formatter to setup, defaults to `progress`
    attr_accessor :default_formatter

    # @private
    def prepare_default(output_stream, deprecation_stream)
      reporter.prepare_default(self, output_stream, deprecation_stream)
    end

    # @private
    def setup_default(output_stream, deprecation_stream)
      add default_formatter, output_stream if @formatters.empty?

      unless @formatters.any? { |formatter| DeprecationFormatter === formatter }
        add DeprecationFormatter, deprecation_stream, output_stream
      end

      unless existing_formatter_implements?(:message)
        add FallbackMessageFormatter, output_stream
      end

      return unless RSpec.configuration.profile_examples?
      return if existing_formatter_implements?(:dump_profile)

      add RSpec::Core::Formatters::ProfileFormatter, output_stream
    end

    # @private
    def add(formatter_to_use, *paths)
      # If a formatter instance was passed, we can register it directly,
      # with no need for any of the further processing that happens below.
      if Loader.formatters.key?(formatter_to_use.class)
        register formatter_to_use, notifications_for(formatter_to_use.class)
        return
      end

      formatter_class = find_formatter(formatter_to_use)

      args = paths.map { |p| p.respond_to?(:puts) ? p : open_stream(p) }

      if !Loader.formatters[formatter_class].nil?
        formatter = formatter_class.new(*args)
        register formatter, notifications_for(formatter_class)
      elsif defined?(RSpec::LegacyFormatters)
        formatter = RSpec::LegacyFormatters.load_formatter formatter_class, *args
        register formatter, formatter.notifications
      else
        call_site = "Formatter added at: #{::RSpec::CallerFilter.first_non_rspec_line}"

        RSpec.warn_deprecation <<-WARNING.gsub(/\s*\|/, ' ')
          |The #{formatter_class} formatter uses the deprecated formatter
          |interface not supported directly by RSpec 3.
          |
          |To continue to use this formatter you must install the
          |`rspec-legacy_formatters` gem, which provides support
          |for legacy formatters or upgrade the formatter to a
          |compatible version.
          |
          |#{call_site}
        WARNING
      end
    end

  private

    def find_formatter(formatter_to_use)
      built_in_formatter(formatter_to_use) ||
      custom_formatter(formatter_to_use)   ||
      (raise ArgumentError, "Formatter '#{formatter_to_use}' unknown - " \
                            "maybe you meant 'documentation' or 'progress'?.")
    end

    def register(formatter, notifications)
      return if duplicate_formatter_exists?(formatter)
      @reporter.register_listener formatter, *notifications
      @formatters << formatter
      formatter
    end

    def duplicate_formatter_exists?(new_formatter)
      @formatters.any? do |formatter|
        formatter.class == new_formatter.class &&
          has_matching_output?(formatter, new_formatter)
      end
    end

    def has_matching_output?(formatter, new_formatter)
      return true unless formatter.respond_to?(:output) && new_formatter.respond_to?(:output)
      formatter.output == new_formatter.output
    end

    def existing_formatter_implements?(notification)
      @reporter.registered_listeners(notification).any?
    end

    def built_in_formatter(key)
      case key.to_s
      when 'd', 'doc', 'documentation'
        DocumentationFormatter
      when 'h', 'html'
        HtmlFormatter
      when 'p', 'progress'
        ProgressFormatter
      when 'j', 'json'
        JsonFormatter
      when 'bisect-drb'
        BisectDRbFormatter
      when 'f', 'failures'
        FailureListFormatter
      end
    end

    def notifications_for(formatter_class)
      formatter_class.ancestors.inject(::RSpec::Core::Set.new) do |notifications, klass|
        notifications.merge Loader.formatters.fetch(klass) { ::RSpec::Core::Set.new }
      end
    end

    def custom_formatter(formatter_ref)
      if Class === formatter_ref
        formatter_ref
      elsif string_const?(formatter_ref)
        begin
          formatter_ref.gsub(/^::/, '').split('::').inject(Object) { |a, e| a.const_get e }
        rescue NameError
          require(path_for(formatter_ref)) ? retry : raise
        end
      end
    end

    def string_const?(str)
      str.is_a?(String) && /\A[A-Z][a-zA-Z0-9_:]*\z/ =~ str
    end

    def path_for(const_ref)
      underscore_with_fix_for_non_standard_rspec_naming(const_ref)
    end

    def underscore_with_fix_for_non_standard_rspec_naming(string)
      underscore(string).sub(%r{(^|/)r_spec($|/)}, '\\1rspec\\2')
    end

    # activesupport/lib/active_support/inflector/methods.rb, line 48
    def underscore(camel_cased_word)
      word = camel_cased_word.to_s.dup
      word.gsub!(/::/, '/')
      word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
      word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
      word.tr!("-", "_")
      word.downcase!
      word
    end

    def open_stream(path_or_wrapper)
      if RSpec::Core::OutputWrapper === path_or_wrapper
        path_or_wrapper.output = open_stream(path_or_wrapper.output)
        path_or_wrapper
      else
        RSpec::Support::DirectoryMaker.mkdir_p(File.dirname(path_or_wrapper))
        File.new(path_or_wrapper, 'w')
      end
    end
  end
end


def add(formatter_to_use, *paths)
      # If a formatter instance was passed, we can register it directly,
      # with no need for any of the further processing that happens below.
      if Loader.formatters.key?(formatter_to_use.class)
        register formatter_to_use, notifications_for(formatter_to_use.class)
        return
      end

      formatter_class = find_formatter(formatter_to_use)

      args = paths.map { |p| p.respond_to?(:puts) ? p : open_stream(p) }

      if !Loader.formatters[formatter_class].nil?
        formatter = formatter_class.new(*args)
        register formatter, notifications_for(formatter_class)
      elsif defined?(RSpec::LegacyFormatters)
        formatter = RSpec::LegacyFormatters.load_formatter formatter_class, *args
        register formatter, formatter.notifications
      else
        call_site = "Formatter added at: #{::RSpec::CallerFilter.first_non_rspec_line}"

        RSpec.warn_deprecation <<-WARNING.gsub(/\s*\|/, ' ')
          |The #{formatter_class} formatter uses the deprecated formatter
          |interface not supported directly by RSpec 3.
          |
          |To continue to use this formatter you must install the
          |`rspec-legacy_formatters` gem, which provides support
          |for legacy formatters or upgrade the formatter to a
          |compatible version.
          |
          |#{call_site}
        WARNING
      end
    end


def built_in_formatter(key)
      case key.to_s
      when 'd', 'doc', 'documentation'
        DocumentationFormatter
      when 'h', 'html'
        HtmlFormatter
      when 'p', 'progress'
        ProgressFormatter
      when 'j', 'json'
        JsonFormatter
      when 'bisect-drb'
        BisectDRbFormatter
      when 'f', 'failures'
        FailureListFormatter
      end
    end


def custom_formatter(formatter_ref)
      if Class === formatter_ref
        formatter_ref
      elsif string_const?(formatter_ref)
        begin
          formatter_ref.gsub(/^::/, '').split('::').inject(Object) { |a, e| a.const_get e }
        rescue NameError
          require(path_for(formatter_ref)) ? retry : raise
        end
      end
    end