RSpec::Support.require_rspec_core "bisect/utilities"

module RSpec
  module Core
    module Bisect
      # @private
      # Contains the core bisect logic. Searches for examples we can ignore by
      # repeatedly running different subsets of the suite.
      class ExampleMinimizer
        attr_reader :shell_command, :runner, :all_example_ids, :failed_example_ids
        attr_accessor :remaining_ids

        def initialize(shell_command, runner, notifier)
          @shell_command = shell_command
          @runner        = runner
          @notifier      = notifier
        end

        def find_minimal_repro
          prep

          _, duration = track_duration do
            bisect(non_failing_example_ids)
          end

          notify(:bisect_complete, :duration => duration,
                                   :original_non_failing_count => non_failing_example_ids.size,
                                   :remaining_count => remaining_ids.size)

          remaining_ids + failed_example_ids
        end

        def bisect(candidate_ids)
          notify(:bisect_dependency_check_started)
          if get_expected_failures_for?([])
            notify(:bisect_dependency_check_failed)
            self.remaining_ids = []
            return
          end
          notify(:bisect_dependency_check_passed)

          bisect_over(candidate_ids)
        end

        def bisect_over(candidate_ids)
          return if candidate_ids.one?

          notify(
            :bisect_round_started,
            :candidate_range => example_range(candidate_ids),
            :candidates_count => candidate_ids.size
          )

          slice_size = (candidate_ids.length / 2.0).ceil
          lhs, rhs = candidate_ids.each_slice(slice_size).to_a

          ids_to_ignore, duration = track_duration do
            [lhs, rhs].find do |ids|
              get_expected_failures_for?(remaining_ids - ids)
            end
          end

          if ids_to_ignore
            self.remaining_ids -= ids_to_ignore
            notify(
              :bisect_round_ignoring_ids,
              :ids_to_ignore => ids_to_ignore,
              :ignore_range => example_range(ids_to_ignore),
              :remaining_ids => remaining_ids,
              :duration => duration
            )
            bisect_over(candidate_ids - ids_to_ignore)
          else
            notify(
              :bisect_round_detected_multiple_culprits,
              :duration => duration
            )
            bisect_over(lhs)
            bisect_over(rhs)
          end
        end

        def currently_needed_ids
          remaining_ids + failed_example_ids
        end

        def repro_command_for_currently_needed_ids
          return shell_command.repro_command_from(currently_needed_ids) if remaining_ids
          "(Not yet enough information to provide any repro command)"
        end

        # @private
        # Convenience class for describing a subset of the candidate examples
        ExampleRange = Struct.new(:start, :finish) do
          def description
            if start == finish
              "example #{start}"
            else
              "examples #{start}-#{finish}"
            end
          end
        end

      private

        def example_range(ids)
          ExampleRange.new(
            non_failing_example_ids.find_index(ids.first) + 1,
            non_failing_example_ids.find_index(ids.last) + 1
          )
        end

        def prep
          notify(:bisect_starting, :original_cli_args => shell_command.original_cli_args,
                                   :bisect_runner => runner.class.name)

          _, duration = track_duration do
            original_results    = runner.original_results
            @all_example_ids    = original_results.all_example_ids
            @failed_example_ids = original_results.failed_example_ids
            @remaining_ids      = non_failing_example_ids
          end

          if @failed_example_ids.empty?
            raise BisectFailedError, "\n\nNo failures found. Bisect only works " \
                  "in the presence of one or more failing examples."
          else
            notify(:bisect_original_run_complete, :failed_example_ids => failed_example_ids,
                                                  :non_failing_example_ids => non_failing_example_ids,
                                                  :duration => duration)
          end
        end

        def non_failing_example_ids
          @non_failing_example_ids ||= all_example_ids - failed_example_ids
        end

        def get_expected_failures_for?(ids)
          ids_to_run = all_example_ids & (ids + failed_example_ids)
          notify(
            :bisect_individual_run_start,
            :command => shell_command.repro_command_from(ids_to_run),
            :ids_to_run => ids_to_run
          )

          results, duration = track_duration { runner.run(ids_to_run) }
          notify(:bisect_individual_run_complete, :duration => duration, :results => results)

          abort_if_ordering_inconsistent(results)
          (failed_example_ids & results.failed_example_ids) == failed_example_ids
        end

        def track_duration
          start = ::RSpec::Core::Time.now
          [yield, ::RSpec::Core::Time.now - start]
        end

        def abort_if_ordering_inconsistent(results)
          expected_order = all_example_ids & results.all_example_ids
          return if expected_order == results.all_example_ids

          raise BisectFailedError, "\n\nThe example ordering is inconsistent. " \
                "`--bisect` relies upon consistent ordering (e.g. by passing " \
                "`--seed` if you're using random ordering) to work properly."
        end

        def notify(*args)
          @notifier.publish(*args)
        end
      end
    end
  end
end


def bisect(candidate_ids)
          notify(:bisect_dependency_check_started)
          if get_expected_failures_for?([])
            notify(:bisect_dependency_check_failed)
            self.remaining_ids = []
            return
          end
          notify(:bisect_dependency_check_passed)

          bisect_over(candidate_ids)
        end


def bisect_over(candidate_ids)
          return if candidate_ids.one?

          notify(
            :bisect_round_started,
            :candidate_range => example_range(candidate_ids),
            :candidates_count => candidate_ids.size
          )

          slice_size = (candidate_ids.length / 2.0).ceil
          lhs, rhs = candidate_ids.each_slice(slice_size).to_a

          ids_to_ignore, duration = track_duration do
            [lhs, rhs].find do |ids|
              get_expected_failures_for?(remaining_ids - ids)
            end
          end

          if ids_to_ignore
            self.remaining_ids -= ids_to_ignore
            notify(
              :bisect_round_ignoring_ids,
              :ids_to_ignore => ids_to_ignore,
              :ignore_range => example_range(ids_to_ignore),
              :remaining_ids => remaining_ids,
              :duration => duration
            )
            bisect_over(candidate_ids - ids_to_ignore)
          else
            notify(
              :bisect_round_detected_multiple_culprits,
              :duration => duration
            )
            bisect_over(lhs)
            bisect_over(rhs)
          end
        end


def prep
          notify(:bisect_starting, :original_cli_args => shell_command.original_cli_args,
                                   :bisect_runner => runner.class.name)

          _, duration = track_duration do
            original_results    = runner.original_results
            @all_example_ids    = original_results.all_example_ids
            @failed_example_ids = original_results.failed_example_ids
            @remaining_ids      = non_failing_example_ids
          end

          if @failed_example_ids.empty?
            raise BisectFailedError, "\n\nNo failures found. Bisect only works " \
                  "in the presence of one or more failing examples."
          else
            notify(:bisect_original_run_complete, :failed_example_ids => failed_example_ids,
                                                  :non_failing_example_ids => non_failing_example_ids,
                                                  :duration => duration)
          end
        end


def get_expected_failures_for?(ids)
          ids_to_run = all_example_ids & (ids + failed_example_ids)
          notify(
            :bisect_individual_run_start,
            :command => shell_command.repro_command_from(ids_to_run),
            :ids_to_run => ids_to_run
          )

          results, duration = track_duration { runner.run(ids_to_run) }
          notify(:bisect_individual_run_complete, :duration => duration, :results => results)

          abort_if_ordering_inconsistent(results)
          (failed_example_ids & results.failed_example_ids) == failed_example_ids
        end


require 'stringio'
RSpec::Support.require_rspec_core "formatters/base_bisect_formatter"
RSpec::Support.require_rspec_core "bisect/utilities"

module RSpec
  module Core
    module Bisect
      # A Bisect runner that runs requested subsets of the suite by forking
      # sub-processes. The main process bootstraps RSpec and the application
      # environment (including preloading files specified via `--require`) so
      # that the individual spec runs do not have to re-pay that cost.  Each
      # spec run happens in a forked process, ensuring that the spec files are
      # not loaded in the main process.
      #
      # For most projects, bisections that use `ForkRunner` instead of
      # `ShellRunner` will finish significantly faster, because the `ShellRunner`
      # pays the cost of booting RSpec and the app environment on _every_ run of
      # a subset. In contrast, `ForkRunner` pays that cost only once.
      #
      # However, not all projects can use `ForkRunner`. Obviously, on platforms
      # that do not support forking (e.g. Windows), it cannot be used. In addition,
      # it can cause problems for some projects that put side-effectful spec
      # bootstrapping logic that should run on every spec run directly at the top
      # level in a file loaded by `--require`, rather than in a `before(:suite)`
      # hook. For example, consider a project that relies on some top-level logic
      # in `spec_helper` to boot a Redis server for the test suite, intending the
      # Redis bootstrapping to happen on every spec run. With `ShellRunner`, the
      # bootstrapping logic will happen for each run of any subset of the suite,
      # but for `ForkRunner`, such logic will only get run once, when the
      # `RunDispatcher` boots the application environment. This might cause
      # problems. The solution is for users to move the bootstrapping logic into
      # a `before(:suite)` hook, or use the slower `ShellRunner`.
      #
      # @private
      class ForkRunner
        def self.start(shell_command, spec_runner)
          instance = new(shell_command, spec_runner)
          yield instance
        ensure
          instance.shutdown
        end

        def self.name
          :fork
        end

        def initialize(shell_command, spec_runner)
          @shell_command = shell_command
          @channel = Channel.new
          @run_dispatcher = RunDispatcher.new(spec_runner, @channel)
        end

        def run(locations)
          run_descriptor = ExampleSetDescriptor.new(locations, original_results.failed_example_ids)
          dispatch_run(run_descriptor)
        end

        def original_results
          @original_results ||= dispatch_run(ExampleSetDescriptor.new(
            @shell_command.original_locations, []))
        end

        def shutdown
          @channel.close
        end

      private

        def dispatch_run(run_descriptor)
          @run_dispatcher.dispatch_specs(run_descriptor)
          @channel.receive.tap do |result|
            if result.is_a?(String)
              raise BisectFailedError.for_failed_spec_run(result)
            end
          end
        end

        # @private
        class RunDispatcher
          def initialize(runner, channel)
            @runner = runner
            @channel = channel

            @spec_output = StringIO.new

            runner.configuration.tap do |c|
              c.reset_reporter
              c.output_stream = @spec_output
              c.error_stream = @spec_output
            end
          end

          def dispatch_specs(run_descriptor)
            pid = fork { run_specs(run_descriptor) }
            # We don't use Process.waitpid here as it was causing bisects to
            # block due to the file descriptor limit on OSX / Linux. We need
            # to detach the process to avoid having zombie processes
            # consuming slots in the kernel process table during bisect runs.
            Process.detach(pid)
          end

        private

          def run_specs(run_descriptor)
            # :nocov: - Executed in a forked process, by integration/bisect_spec
            $stdout = $stderr = @spec_output
            formatter = CaptureFormatter.new(run_descriptor.failed_example_ids)

            @runner.configuration.tap do |c|
              c.files_or_directories_to_run = run_descriptor.all_example_ids
              c.formatter = formatter
              c.load_spec_files
            end

            # `announce_filters` has the side effect of implementing the logic
            # that honors `config.run_all_when_everything_filtered` so we need
            # to call it here. When we remove `run_all_when_everything_filtered`
            # (slated for RSpec 4), we can remove this call to `announce_filters`.
            @runner.world.announce_filters

            @runner.run_specs(@runner.world.ordered_example_groups)
            latest_run_results = formatter.results

            if latest_run_results.nil? || latest_run_results.all_example_ids.empty?
              @channel.send(@spec_output.string)
            else
              @channel.send(latest_run_results)
            end
            # :nocov:
          end
        end

        class CaptureFormatter < Formatters::BaseBisectFormatter
          attr_accessor :results
          alias_method :notify_results, :results=
        end
      end
    end
  end
end


require 'drb/drb'
require 'drb/acl'
RSpec::Support.require_rspec_core "bisect/utilities"

module RSpec
  module Core
    # @private
    module Bisect
      # @private
      # A DRb server that receives run results from a separate RSpec process
      # started by the bisect process.
      class Server
        def self.run
          server = new
          server.start
          yield server
        ensure
          server.stop
        end

        def capture_run_results(files_or_directories_to_run=[], expected_failures=[])
          self.expected_failures  = expected_failures
          self.files_or_directories_to_run = files_or_directories_to_run
          self.latest_run_results = nil
          run_output = yield

          if latest_run_results.nil? || latest_run_results.all_example_ids.empty?
            raise BisectFailedError.for_failed_spec_run(run_output)
          end

          latest_run_results
        end

        def start
          # Only allow remote DRb requests from this machine.
          DRb.install_acl ACL.new(%w[ deny all allow localhost allow 127.0.0.1 allow ::1 ])

          # We pass `nil` as the first arg to allow it to pick a DRb port.
          @drb = DRb.start_service(nil, self)
        end

        def stop
          @drb.stop_service
        end

        def drb_port
          @drb_port ||= Integer(@drb.uri[/\d+$/])
        end

        # Fetched via DRb by the BisectDRbFormatter to determine when to abort.
        attr_accessor :expected_failures

        # Set via DRb by the BisectDRbFormatter with the results of the run.
        attr_accessor :latest_run_results

        # Fetched via DRb to tell clients which files to run
        attr_accessor :files_or_directories_to_run
      end
    end
  end
end


RSpec::Support.require_rspec_core "shell_escape"
require 'shellwords'

module RSpec
  module Core
    module Bisect
      # Provides an API to generate shell commands to run the suite for a
      # set of locations, using the given bisect server to capture the results.
      # @private
      class ShellCommand
        attr_reader :original_cli_args

        def initialize(original_cli_args)
          @original_cli_args = original_cli_args.reject { |arg| arg.start_with?("--bisect") }
        end

        def command_for(locations, server)
          parts = []

          parts << RUBY << load_path
          parts << open3_safe_escape(RSpec::Core.path_to_executable)

          parts << "--format"   << "bisect-drb"
          parts << "--drb-port" << server.drb_port

          parts.concat(reusable_cli_options)
          parts.concat(locations.map { |l| open3_safe_escape(l) })

          parts.join(" ")
        end

        def repro_command_from(locations)
          parts = []

          parts.concat environment_repro_parts
          parts << "rspec"
          parts.concat Formatters::Helpers.organize_ids(locations)
          parts.concat original_cli_args_without_locations

          parts.join(" ")
        end

        def original_locations
          parsed_original_cli_options.fetch(:files_or_directories_to_run)
        end

        def bisect_environment_hash
          if ENV.key?('SPEC_OPTS')
            { 'SPEC_OPTS' => spec_opts_without_bisect }
          else
            {}
          end
        end

        def spec_opts_without_bisect
          Shellwords.join(
            Shellwords.split(ENV.fetch('SPEC_OPTS', '')).reject do |arg|
              arg =~ /^--bisect/
            end
          )
        end

      private

        include RSpec::Core::ShellEscape
        # On JRuby, Open3.popen3 does not handle shellescaped args properly:
        # https://github.com/jruby/jruby/issues/2767
        if RSpec::Support::Ruby.jruby?
          # :nocov:
          alias open3_safe_escape quote
          # :nocov:
        else
          alias open3_safe_escape escape
        end

        def environment_repro_parts
          bisect_environment_hash.map do |k, v|
            %Q(#{k}="#{v}")
          end
        end

        def reusable_cli_options
          @reusable_cli_options ||= begin
            opts = original_cli_args_without_locations

            if (port = parsed_original_cli_options[:drb_port])
              opts -= %W[ --drb-port #{port} ]
            end

            parsed_original_cli_options.fetch(:formatters) { [] }.each do |(name, out)|
              opts -= %W[ --format #{name} -f -f#{name} ]
              opts -= %W[ --out #{out} -o -o#{out} ]
            end

            opts
          end
        end

        def original_cli_args_without_locations
          @original_cli_args_without_locations ||= begin
            files_or_dirs = parsed_original_cli_options.fetch(:files_or_directories_to_run)
            @original_cli_args - files_or_dirs
          end
        end

        def parsed_original_cli_options
          @parsed_original_cli_options ||= Parser.parse(@original_cli_args)
        end

        def load_path
          @load_path ||= "-I#{$LOAD_PATH.map { |p| open3_safe_escape(p) }.join(':')}"
        end

        # Path to the currently running Ruby executable, borrowed from Rake:
        # https://github.com/ruby/rake/blob/v10.4.2/lib/rake/file_utils.rb#L8-L12
        # Note that we skip `ENV['RUBY']` because we don't have to deal with running
        # RSpec from within a MRI source repository:
        # https://github.com/ruby/rake/commit/968682759b3b65e42748cd2befb2ff3e982272d9
        RUBY = File.join(
          RbConfig::CONFIG['bindir'],
          RbConfig::CONFIG['ruby_install_name'] + RbConfig::CONFIG['EXEEXT']).
          sub(/.*\s.*/m, '"\&"')
      end
    end
  end
end


def reusable_cli_options
          @reusable_cli_options ||= begin
            opts = original_cli_args_without_locations

            if (port = parsed_original_cli_options[:drb_port])
              opts -= %W[ --drb-port #{port} ]
            end

            parsed_original_cli_options.fetch(:formatters) { [] }.each do |(name, out)|
              opts -= %W[ --format #{name} -f -f#{name} ]
              opts -= %W[ --out #{out} -o -o#{out} ]
            end

            opts
          end
        end


require 'open3'
RSpec::Support.require_rspec_core "bisect/server"

module RSpec
  module Core
    module Bisect
      # Provides an API to run the suite for a set of locations, using
      # the given bisect server to capture the results.
      #
      # Sets of specs are run by shelling out.
      # @private
      class ShellRunner
        def self.start(shell_command, _spec_runner)
          Server.run do |server|
            yield new(server, shell_command)
          end
        end

        def self.name
          :shell
        end

        def initialize(server, shell_command)
          @server        = server
          @shell_command = shell_command
        end

        def run(locations)
          run_locations(locations, original_results.failed_example_ids)
        end

        def original_results
          @original_results ||= run_locations(@shell_command.original_locations)
        end

      private

        def run_locations(*capture_args)
          @server.capture_run_results(*capture_args) do
            run_command @shell_command.command_for([], @server)
          end
        end

        # `Open3.capture2e` does not work on JRuby:
        # https://github.com/jruby/jruby/issues/2766
        if Open3.respond_to?(:capture2e) && !RSpec::Support::Ruby.jruby?
          def run_command(cmd)
            Open3.capture2e(@shell_command.bisect_environment_hash, cmd).first
          end
        else # for 1.8.7
          # :nocov:
          def run_command(cmd)
            out = err = nil

            original_spec_opts = ENV['SPEC_OPTS']
            ENV['SPEC_OPTS'] = @shell_command.spec_opts_without_bisect

            Open3.popen3(cmd) do |_, stdout, stderr|
              # Reading the streams blocks until the process is complete
              out = stdout.read
              err = stderr.read
            end

            "Stdout:\n#{out}\n\nStderr:\n#{err}"
          ensure
            ENV['SPEC_OPTS'] = original_spec_opts
          end
          # :nocov:
        end
      end
    end
  end
end


RSpec::Support.require_rspec_core "formatters/base_text_formatter"

module RSpec
  module Core
    module Formatters
      # @private
      # Produces progress output while bisecting.
      class BisectProgressFormatter < BaseTextFormatter
        def initialize(output, bisect_runner)
          super(output)
          @bisect_runner = bisect_runner
        end

        def bisect_starting(notification)
          @round_count = 0
          output.puts bisect_started_message(notification)
          output.print "Running suite to find failures..."
        end

        def bisect_original_run_complete(notification)
          failures     = Helpers.pluralize(notification.failed_example_ids.size, "failing example")
          non_failures = Helpers.pluralize(notification.non_failing_example_ids.size, "non-failing example")

          output.puts " (#{Helpers.format_duration(notification.duration)})"
          output.puts "Starting bisect with #{failures} and #{non_failures}."
        end

        def bisect_dependency_check_started(_notification)
          output.print "Checking that failure(s) are order-dependent.."
        end

        def bisect_dependency_check_passed(_notification)
          output.puts " failure appears to be order-dependent"
        end

        def bisect_dependency_check_failed(_notification)
          output.puts " failure(s) do not require any non-failures to run first"

          if @bisect_runner == :fork
            output.puts
            output.puts "=" * 80
            output.puts "NOTE: this bisect run used `config.bisect_runner = :fork`, which generally"
            output.puts "provides significantly faster bisection runs than the old shell-based runner,"
            output.puts "but may inaccurately report that no non-failures are required. If this result"
            output.puts "is unexpected, consider setting `config.bisect_runner = :shell` and trying again."
            output.puts "=" * 80
          end
        end

        def bisect_round_started(notification, include_trailing_space=true)
          @round_count += 1
          range_desc = notification.candidate_range.description

          output.print "\nRound #{@round_count}: bisecting over non-failing #{range_desc}"
          output.print " " if include_trailing_space
        end

        def bisect_round_ignoring_ids(notification)
          range_desc = notification.ignore_range.description

          output.print " ignoring #{range_desc}"
          output.print " (#{Helpers.format_duration(notification.duration)})"
        end

        def bisect_round_detected_multiple_culprits(notification)
          output.print " multiple culprits detected - splitting candidates"
          output.print " (#{Helpers.format_duration(notification.duration)})"
        end

        def bisect_individual_run_complete(_)
          output.print '.'
        end

        def bisect_complete(notification)
          output.puts "\nBisect complete! Reduced necessary non-failing examples " \
                      "from #{notification.original_non_failing_count} to " \
                      "#{notification.remaining_count} in " \
                      "#{Helpers.format_duration(notification.duration)}."
        end

        def bisect_repro_command(notification)
          output.puts "\nThe minimal reproduction command is:\n  #{notification.repro}"
        end

        def bisect_failed(notification)
          output.puts "\nBisect failed! #{notification.failure_explanation}"
        end

        def bisect_aborted(notification)
          output.puts "\n\nBisect aborted!"
          output.puts "\nThe most minimal reproduction command discovered so far is:\n  #{notification.repro}"
        end

      private

        def bisect_started_message(notification)
          options = notification.original_cli_args.join(' ')
          "Bisect started using options: #{options.inspect}"
        end
      end

      # @private
      # Produces detailed debug output while bisecting. Used when bisect is
      # performed with `--bisect=verbose`. Designed to provide details for
      # us when we need to troubleshoot bisect bugs.
      class BisectDebugFormatter < BisectProgressFormatter
        def bisect_original_run_complete(notification)
          output.puts " (#{Helpers.format_duration(notification.duration)})"

          output.puts " - #{describe_ids 'Failing examples', notification.failed_example_ids}"
          output.puts " - #{describe_ids 'Non-failing examples', notification.non_failing_example_ids}"
        end

        def bisect_individual_run_start(notification)
          output.print "\n - Running: #{notification.command}"
        end

        def bisect_individual_run_complete(notification)
          output.print " (#{Helpers.format_duration(notification.duration)})"
        end

        def bisect_dependency_check_passed(_notification)
          output.print "\n - Failure appears to be order-dependent"
        end

        def bisect_dependency_check_failed(_notification)
          output.print "\n - Failure is not order-dependent"
        end

        def bisect_round_started(notification)
          super(notification, false)
        end

        def bisect_round_ignoring_ids(notification)
          output.print "\n - #{describe_ids 'Examples we can safely ignore', notification.ids_to_ignore}"
          output.print "\n - #{describe_ids 'Remaining non-failing examples', notification.remaining_ids}"
        end

        def bisect_round_detected_multiple_culprits(_notification)
          output.print "\n - Multiple culprits detected - splitting candidates"
        end

      private

        def describe_ids(description, ids)
          organized_ids = Formatters::Helpers.organize_ids(ids)
          formatted_ids = organized_ids.map { |id| "    - #{id}" }.join("\n")
          "#{description} (#{ids.size}):\n#{formatted_ids}"
        end

        def bisect_started_message(notification)
          "#{super} and bisect runner: #{notification.bisect_runner.inspect}"
        end
      end
    end
  end
end

class BisectProgressFormatter < < BaseTextFormatter


module RSpec
  module Core
    module Formatters
      # ConsoleCodes provides helpers for formatting console output
      # with ANSI codes, e.g. color's and bold.
      module ConsoleCodes
        # @private
        VT100_CODES =
          {
            :black         => 30,
            :red           => 31,
            :green         => 32,
            :yellow        => 33,
            :blue          => 34,
            :magenta       => 35,
            :cyan          => 36,
            :white         => 37,
            :bold_black   => '1;30',
            :bold_red     => '1;31',
            :bold_green   => '1;32',
            :bold_yellow  => '1;33',
            :bold_blue    => '1;34',
            :bold_magenta => '1;35',
            :bold_cyan    => '1;36',
            :bold_white   => '1;37',
            :bold          => 1,
          }
        # @private
        VT100_CODE_VALUES = VT100_CODES.invert

        module_function

        # @private
        def config_colors_to_methods
          @config_colors_to_methods ||=
            Configuration.instance_methods.grep(/_color\z/).inject({}) do |hash, method|
              hash[method.to_s.sub(/_color\z/, '').to_sym] = method
              hash
            end
        end

        # Fetches the correct code for the supplied symbol, or checks
        # that a code is valid. Defaults to white (37).
        #
        # @param code_or_symbol [Symbol, Fixnum] Symbol or code to check
        # @return [Fixnum] a console code
        def console_code_for(code_or_symbol)
          if (config_method = config_colors_to_methods[code_or_symbol])
            console_code_for RSpec.configuration.__send__(config_method)
          elsif VT100_CODE_VALUES.key?(code_or_symbol)
            code_or_symbol
          else
            VT100_CODES.fetch(code_or_symbol) do
              console_code_for(:white)
            end
          end
        end

        # Wraps a piece of text in ANSI codes with the supplied code. Will
        # only apply the control code if `RSpec.configuration.color_enabled?`
        # returns true.
        #
        # @param text [String] the text to wrap
        # @param code_or_symbol [Symbol, Fixnum] the desired control code
        # @return [String] the wrapped text
        def wrap(text, code_or_symbol)
          if RSpec.configuration.color_enabled?
            "\e[#{console_code_for(code_or_symbol)}m#{text}\e[0m"
          else
            text
          end
        end
      end
    end
  end
end


RSpec::Support.require_rspec_core "formatters/helpers"

module RSpec
  module Core
    module Formatters
      # @private
      class DeprecationFormatter
        Formatters.register self, :deprecation, :deprecation_summary

        attr_reader :count, :deprecation_stream, :summary_stream

        def initialize(deprecation_stream, summary_stream)
          @deprecation_stream = deprecation_stream
          @summary_stream = summary_stream
          @seen_deprecations = Set.new
          @count = 0
        end
        alias :output :deprecation_stream

        def printer
          @printer ||= case deprecation_stream
                       when File
                         ImmediatePrinter.new(FileStream.new(deprecation_stream),
                                              summary_stream, self)
                       when RaiseErrorStream
                         ImmediatePrinter.new(deprecation_stream, summary_stream, self)
                       else
                         DelayedPrinter.new(deprecation_stream, summary_stream, self)
                       end
        end

        def deprecation(notification)
          return if @seen_deprecations.include? notification

          @count += 1
          printer.print_deprecation_message notification
          @seen_deprecations << notification
        end

        def deprecation_summary(_notification)
          printer.deprecation_summary
        end

        def deprecation_message_for(data)
          if data.message
            SpecifiedDeprecationMessage.new(data)
          else
            GeneratedDeprecationMessage.new(data)
          end
        end

        RAISE_ERROR_CONFIG_NOTICE = <<-EOS.gsub(/^\s+\|/, '')
          |
          |If you need more of the backtrace for any of these deprecations to
          |identify where to make the necessary changes, you can configure
          |`config.raise_errors_for_deprecations!`, and it will turn the
          |deprecation warnings into errors, giving you the full backtrace.
        EOS

        DEPRECATION_STREAM_NOTICE = "Pass `--deprecation-out` or set " \
          "`config.deprecation_stream` to a file for full output."
        TOO_MANY_WARNINGS_NOTICE  = "Too many similar deprecation messages " \
          "reported, disregarding further reports. #{DEPRECATION_STREAM_NOTICE}"

        # @private
        SpecifiedDeprecationMessage = Struct.new(:type) do
          def initialize(data)
            @message = data.message
            super deprecation_type_for(data)
          end

          def to_s
            output_formatted @message
          end

          def too_many_warnings_message
            TOO_MANY_WARNINGS_NOTICE
          end

          private

          def output_formatted(str)
            return str unless str.lines.count > 1
            separator = '-' * 80
            "#{separator}\n#{str.chomp}\n#{separator}"
          end

          def deprecation_type_for(data)
            data.message.gsub(/(\w+\/)+\w+\.rb:\d+/, '')
          end
        end

        # @private
        GeneratedDeprecationMessage = Struct.new(:type) do
          def initialize(data)
            @data = data
            super data.deprecated
          end

          def to_s
            msg = String.new("#{@data.deprecated} is deprecated.")
            msg << " Use #{@data.replacement} instead." if @data.replacement
            msg << " Called from #{@data.call_site}."   if @data.call_site
            msg
          end

          def too_many_warnings_message
            "Too many uses of deprecated '#{type}'. #{DEPRECATION_STREAM_NOTICE}"
          end
        end

        # @private
        class ImmediatePrinter
          attr_reader :deprecation_stream, :summary_stream, :deprecation_formatter

          def initialize(deprecation_stream, summary_stream, deprecation_formatter)
            @deprecation_stream = deprecation_stream

            @summary_stream = summary_stream
            @deprecation_formatter = deprecation_formatter
          end

          def print_deprecation_message(data)
            deprecation_message = deprecation_formatter.deprecation_message_for(data)
            deprecation_stream.puts deprecation_message.to_s
          end

          def deprecation_summary
            return if deprecation_formatter.count.zero?
            deprecation_stream.summarize(summary_stream, deprecation_formatter.count)
          end
        end

        # @private
        class DelayedPrinter
          TOO_MANY_USES_LIMIT = 4

          attr_reader :deprecation_stream, :summary_stream, :deprecation_formatter

          def initialize(deprecation_stream, summary_stream, deprecation_formatter)
            @deprecation_stream = deprecation_stream
            @summary_stream = summary_stream
            @deprecation_formatter = deprecation_formatter
            @seen_deprecations = Hash.new { 0 }
            @deprecation_messages = Hash.new { |h, k| h[k] = [] }
          end

          def print_deprecation_message(data)
            deprecation_message = deprecation_formatter.deprecation_message_for(data)
            @seen_deprecations[deprecation_message] += 1

            stash_deprecation_message(deprecation_message)
          end

          def stash_deprecation_message(deprecation_message)
            if @seen_deprecations[deprecation_message] < TOO_MANY_USES_LIMIT
              @deprecation_messages[deprecation_message] << deprecation_message.to_s
            elsif @seen_deprecations[deprecation_message] == TOO_MANY_USES_LIMIT
              @deprecation_messages[deprecation_message] << deprecation_message.too_many_warnings_message
            end
          end

          def deprecation_summary
            return unless @deprecation_messages.any?

            print_deferred_deprecation_warnings
            deprecation_stream.puts RAISE_ERROR_CONFIG_NOTICE

            summary_stream.puts "\n#{Helpers.pluralize(deprecation_formatter.count, 'deprecation warning')} total"
          end

          def print_deferred_deprecation_warnings
            deprecation_stream.puts "\nDeprecation Warnings:\n\n"
            @deprecation_messages.keys.sort_by(&:type).each do |deprecation|
              messages = @deprecation_messages[deprecation]
              messages.each { |msg| deprecation_stream.puts msg }
              deprecation_stream.puts
            end
          end
        end

        # @private
        # Not really a stream, but is usable in place of one.
        class RaiseErrorStream
          def puts(message)
            raise DeprecationError, message
          end

          def summarize(summary_stream, deprecation_count)
            summary_stream.puts "\n#{Helpers.pluralize(deprecation_count, 'deprecation')} found."
          end
        end

        # @private
        # Wraps a File object and provides file-specific operations.
        class FileStream
          def initialize(file)
            @file = file

            # In one of my test suites, I got lots of duplicate output in the
            # deprecation file (e.g. 200 of the same deprecation, even though
            # the `puts` below was only called 6 times). Setting `sync = true`
            # fixes this (but we really have no idea why!).
            @file.sync = true
          end

          def puts(*args)
            @file.puts(*args)
          end

          def summarize(summary_stream, deprecation_count)
            path = @file.respond_to?(:path) ? @file.path : @file.inspect
            summary_stream.puts "\n#{Helpers.pluralize(deprecation_count, 'deprecation')} logged to #{path}"
            puts RAISE_ERROR_CONFIG_NOTICE
          end
        end
      end
    end

    # Deprecation Error.
    DeprecationError = Class.new(StandardError)
  end
end


# encoding: utf-8
RSpec::Support.require_rspec_core "formatters/console_codes"
RSpec::Support.require_rspec_core "formatters/snippet_extractor"
RSpec::Support.require_rspec_core 'formatters/syntax_highlighter'
RSpec::Support.require_rspec_support "encoded_string"

module RSpec
  module Core
    module Formatters
      # @private
      class ExceptionPresenter
        attr_reader :exception, :example, :description, :message_color,
                    :detail_formatter, :extra_detail_formatter, :backtrace_formatter

        # We want private attr_reader but that issues a warning on older rubies, refactor later
        #
        # rubocop:disable Style/AccessModifierDeclarations
        private :message_color, :detail_formatter, :extra_detail_formatter, :backtrace_formatter
        # rubocop:enable Style/AccessModifierDeclarations

        def initialize(exception, example, options={})
          @exception               = exception
          @example                 = example
          @message_color           = options.fetch(:message_color)          { RSpec.configuration.failure_color }
          @description             = options.fetch(:description)            { example.full_description }
          @detail_formatter        = options.fetch(:detail_formatter)       { Proc.new {} }
          @extra_detail_formatter  = options.fetch(:extra_detail_formatter) { Proc.new {} }
          @backtrace_formatter     = options.fetch(:backtrace_formatter)    { RSpec.configuration.backtrace_formatter }
          @indentation             = options.fetch(:indentation, 2)
          @skip_shared_group_trace = options.fetch(:skip_shared_group_trace, false)
          @failure_lines           = options[:failure_lines]
        end

        def message_lines
          add_shared_group_lines(failure_lines, Notifications::NullColorizer)
        end

        def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
          add_shared_group_lines(failure_lines, colorizer).map do |line|
            colorizer.wrap line, message_color
          end
        end

        def formatted_backtrace(exception=@exception)
          backtrace_formatter.format_backtrace(exception.backtrace, example.metadata) +
            formatted_cause(exception)
        end

        if RSpec::Support::RubyFeatures.supports_exception_cause?
          def formatted_cause(exception)
            last_cause = final_exception(exception, [exception])
            cause = []

            if exception.cause
              cause << '------------------'
              cause << '--- Caused by: ---'
              cause << "#{exception_class_name(last_cause)}:" unless exception_class_name(last_cause) =~ /RSpec/

              encoded_string(exception_message_string(last_cause)).split("\n").each do |line|
                cause << "  #{line}"
              end

              unless last_cause.backtrace.nil? || last_cause.backtrace.empty?
                lines = backtrace_formatter.format_backtrace(last_cause.backtrace, example.metadata)
                lines = [lines[0]] unless RSpec.configuration.full_cause_backtrace # rubocop:disable Metrics/BlockNesting

                lines.each do |line|
                  cause << ("  #{line}")
                end
              end
            end

            cause
          end
        else
          # :nocov:
          def formatted_cause(_)
            []
          end
          # :nocov:
        end

        def colorized_formatted_backtrace(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
          formatted_backtrace.map do |backtrace_info|
            colorizer.wrap "# #{backtrace_info}", RSpec.configuration.detail_color
          end
        end

        def fully_formatted(failure_number, colorizer=::RSpec::Core::Formatters::ConsoleCodes)
          lines = fully_formatted_lines(failure_number, colorizer)
          lines.join("\n") << "\n"
        end

        def fully_formatted_lines(failure_number, colorizer)
          lines = [
            encoded_description(description),
            detail_formatter.call(example, colorizer),
            formatted_message_and_backtrace(colorizer),
            extra_detail_formatter.call(failure_number, colorizer),
          ].compact.flatten

          lines = indent_lines(lines, failure_number)
          lines.unshift("")
          lines
        end

      private

        def final_exception(exception, previous=[])
          cause = exception.cause

          if cause && Exception === cause && !previous.include?(cause)
            previous << cause
            final_exception(cause, previous)
          else
            exception
          end
        end

        if String.method_defined?(:encoding)
          def encoding_of(string)
            string.encoding
          end

          def encoded_string(string)
            RSpec::Support::EncodedString.new(string, Encoding.default_external)
          end
        else # for 1.8.7
          # :nocov:
          def encoding_of(_string)
          end

          def encoded_string(string)
            RSpec::Support::EncodedString.new(string)
          end
          # :nocov:
        end

        def indent_lines(lines, failure_number)
          alignment_basis = ' ' * @indentation
          alignment_basis <<  "#{failure_number}) " if failure_number
          indentation = ' ' * alignment_basis.length

          lines.each_with_index.map do |line, index|
            if index == 0
              "#{alignment_basis}#{line}"
            elsif line.empty?
              line
            else
              "#{indentation}#{line}"
            end
          end
        end

        def exception_class_name(exception=@exception)
          name = exception.class.name.to_s
          name = "(anonymous error class)" if name == ''
          name
        end

        def failure_lines
          @failure_lines ||= [].tap do |lines|
            lines.concat(failure_slash_error_lines)

            sections = [failure_slash_error_lines, exception_lines]
            if sections.any? { |section| section.size > 1 } && !exception_lines.first.empty?
              lines << ''
            end

            lines.concat(exception_lines)
            lines.concat(extra_failure_lines)
          end
        end

        def failure_slash_error_lines
          lines = read_failed_lines
          if lines.count == 1
            lines[0] = "Failure/Error: #{lines[0].strip}"
          else
            least_indentation = SnippetExtractor.least_indentation_from(lines)
            lines = lines.map { |line| line.sub(/^#{least_indentation}/, '  ') }
            lines.unshift('Failure/Error:')
          end
          lines
        end

        # rubocop:disable Lint/RescueException
        # :nocov:
        if SyntaxError.instance_methods.include?(:detailed_message)
          def exception_message_string(exception)
            case exception
            when SyntaxError then exception.detailed_message.to_s
            else
              exception.message.to_s
            end
          rescue Exception => other
            "A #{exception.class} for which `exception.message.to_s` raises #{other.class}."
          end
        else
          def exception_message_string(exception)
            exception.message.to_s
          rescue Exception => other
            "A #{exception.class} for which `exception.message.to_s` raises #{other.class}."
          end
        end
        # :nocov:
        # rubocop:enable Lint/RescueException

        def exception_lines
          @exception_lines ||= begin
            lines = []
            lines << "#{exception_class_name}:" unless exception_class_name =~ /RSpec/
            encoded_string(exception_message_string(exception)).split("\n").each do |line|
              lines << (line.empty? ? line : "  #{line}")
            end
            lines
          end
        end

        def extra_failure_lines
          @extra_failure_lines ||= begin
            lines = Array(example.metadata[:extra_failure_lines])
            unless lines.empty?
              lines.unshift('') unless lines.first == ''
              lines.push('') unless lines.last == ''
            end
            lines
          end
        end

        def add_shared_group_lines(lines, colorizer)
          return lines if @skip_shared_group_trace

          example.metadata[:shared_group_inclusion_backtrace].each do |frame|
            lines << colorizer.wrap(frame.description, RSpec.configuration.default_color)
          end

          lines
        end

        def read_failed_lines
          matching_line = find_failed_line
          unless matching_line
            return ["Unable to find matching line from backtrace"]
          end

          file_and_line_number = matching_line.match(/(.+?):(\d+)(|:\d+)/)

          unless file_and_line_number
            return ["Unable to infer file and line number from backtrace"]
          end

          file_path, line_number = file_and_line_number[1..2]
          max_line_count = RSpec.configuration.max_displayed_failure_line_count
          lines = SnippetExtractor.extract_expression_lines_at(file_path, line_number.to_i, max_line_count)
          RSpec.world.syntax_highlighter.highlight(lines)
        rescue SnippetExtractor::NoSuchFileError
          ["Unable to find #{file_path} to read failed line"]
        rescue SnippetExtractor::NoSuchLineError
          ["Unable to find matching line in #{file_path}"]
        rescue SecurityError
          # :nocov: - SecurityError is no longer produced starting in ruby 2.7
          ["Unable to read failed line"]
          # :nocov:
        end

        def find_failed_line
          line_regex = RSpec.configuration.in_project_source_dir_regex
          loaded_spec_files = RSpec.configuration.loaded_spec_files

          exception_backtrace.reject! do |line|
            line.start_with?("<internal:")
          end

          exception_backtrace.find do |line|
            next unless (line_path = line[/(.+?):(\d+)(|:\d+)/, 1])
            path = File.expand_path(line_path)
            loaded_spec_files.include?(path) || path =~ line_regex
          end || exception_backtrace.first
        end

        def formatted_message_and_backtrace(colorizer)
          lines = colorized_message_lines(colorizer) + colorized_formatted_backtrace(colorizer)
          encoding = encoding_of("")
          lines.map do |line|
            RSpec::Support::EncodedString.new(line, encoding)
          end
        end

        if  String.method_defined?(:encoding)
          def encoded_description(description)
            return if description.nil?
            encoded_string(description)
          end
        else # for 1.8.7
          # :nocov:
          def encoded_description(description)
            description
          end
          # :nocov:
        end

        def exception_backtrace
          exception.backtrace || []
        end

        # @private
        # Configuring the `ExceptionPresenter` with the right set of options to handle
        # pending vs failed vs skipped and aggregated (or not) failures is not simple.
        # This class takes care of building an appropriate `ExceptionPresenter` for the
        # provided example.
        class Factory
          def build
            ExceptionPresenter.new(@exception, @example, options)
          end

        private

          def initialize(example)
            @example          = example
            @execution_result = example.execution_result
            @exception        = if @execution_result.status == :pending
                                  @execution_result.pending_exception
                                else
                                  @execution_result.exception
                                end
          end

          def options
            with_multiple_error_options_as_needed(@exception, pending_options || {})
          end

          def pending_options
            if @execution_result.pending_fixed?
              {
                :description   => "#{@example.full_description} FIXED",
                :message_color => RSpec.configuration.fixed_color,
                :failure_lines => [
                  "Expected pending '#{@execution_result.pending_message}' to fail. No error was raised."
                ]
              }
            elsif @execution_result.status == :pending
              options = {
                :message_color    => RSpec.configuration.pending_color,
                :detail_formatter => PENDING_DETAIL_FORMATTER
              }
              if RSpec.configuration.pending_failure_output == :no_backtrace
                options[:backtrace_formatter] = EmptyBacktraceFormatter
              end
              options
            end
          end

          def with_multiple_error_options_as_needed(exception, options)
            return options unless multiple_exceptions_error?(exception)

            options = options.merge(
              :failure_lines          => [],
              :extra_detail_formatter => sub_failure_list_formatter(exception, options[:message_color]),
              :detail_formatter       => multiple_exception_summarizer(exception,
                                                                       options[:detail_formatter],
                                                                       options[:message_color])
            )

            return options unless exception.aggregation_metadata[:hide_backtrace]
            options[:backtrace_formatter] = EmptyBacktraceFormatter
            options
          end

          def multiple_exceptions_error?(exception)
            MultipleExceptionError::InterfaceTag === exception
          end

          def multiple_exception_summarizer(exception, prior_detail_formatter, color)
            lambda do |example, colorizer|
              summary = if exception.aggregation_metadata[:hide_backtrace]
                          # Since the backtrace is hidden, the subfailures will come
                          # immediately after this, and using `:` will read well.
                          "Got #{exception.exception_count_description}:"
                        else
                          # The backtrace comes after this, so using a `:` doesn't make sense
                          # since the failures may be many lines below.
                          "#{exception.summary}."
                        end

              summary = colorizer.wrap(summary, color || RSpec.configuration.failure_color)
              return summary unless prior_detail_formatter
              [
                prior_detail_formatter.call(example, colorizer),
                summary
              ]
            end
          end

          def sub_failure_list_formatter(exception, message_color)
            common_backtrace_truncater = CommonBacktraceTruncater.new(exception)

            lambda do |failure_number, colorizer|
              FlatMap.flat_map(exception.all_exceptions.each_with_index) do |failure, index|
                options = with_multiple_error_options_as_needed(
                  failure,
                  :description             => nil,
                  :indentation             => 0,
                  :message_color           => message_color || RSpec.configuration.failure_color,
                  :skip_shared_group_trace => true
                )

                failure   = common_backtrace_truncater.with_truncated_backtrace(failure)
                presenter = ExceptionPresenter.new(failure, @example, options)
                presenter.fully_formatted_lines(
                  "#{failure_number ? "#{failure_number}." : ''}#{index + 1}",
                  colorizer
                )
              end
            end
          end

          # @private
          # Used to prevent a confusing backtrace from showing up from the `aggregate_failures`
          # block declared for `:aggregate_failures` metadata.
          module EmptyBacktraceFormatter
            def self.format_backtrace(*)
              []
            end
          end

          # @private
          class CommonBacktraceTruncater
            def initialize(parent)
              @parent = parent
            end

            def with_truncated_backtrace(child)
              child_bt  = child.backtrace
              parent_bt = @parent.backtrace
              return child if child_bt.nil? || child_bt.empty? || parent_bt.nil?

              index_before_first_common_frame = -1.downto(-child_bt.size).find do |index|
                parent_bt[index] != child_bt[index]
              end

              return child if index_before_first_common_frame.nil?
              return child if index_before_first_common_frame == -1

              child = child.dup
              child.set_backtrace(child_bt[0..index_before_first_common_frame])
              child
            end
          end
        end

        # @private
        PENDING_DETAIL_FORMATTER = Proc.new do |example, colorizer|
          colorizer.wrap("# #{example.execution_result.pending_message}", :detail)
        end
      end
    end

    # Provides a single exception instance that provides access to
    # multiple sub-exceptions. This is used in situations where a single
    # individual spec has multiple exceptions, such as one in the `it` block
    # and one in an `after` block.
    class MultipleExceptionError < StandardError
      # @private
      # Used so there is a common module in the ancestor chain of this class
      # and `RSpec::Expectations::MultipleExpectationsNotMetError`, which allows
      # code to detect exceptions that are instances of either, without first
      # checking to see if rspec-expectations is loaded.
      module InterfaceTag
        # Appends the provided exception to the list.
        # @param exception [Exception] Exception to append to the list.
        # @private
        def add(exception)
          # `PendingExampleFixedError` can be assigned to an example that initially has no
          # failures, but when the `aggregate_failures` around hook completes, it notifies of
          # a failure. If we do not ignore `PendingExampleFixedError` it would be surfaced to
          # the user as part of a multiple exception error, which is undesirable. While it's
          # pretty weird we handle this here, it's the best solution I've been able to come
          # up with, and `PendingExampleFixedError` always represents the _lack_ of any exception
          # so clearly when we are transitioning to a `MultipleExceptionError`, it makes sense to
          # ignore it.
          return if Pending::PendingExampleFixedError === exception

          return if exception == self

          all_exceptions << exception

          if exception.class.name =~ /RSpec/
            failures << exception
          else
            other_errors << exception
          end
        end

        # Provides a way to force `ex` to be something that satisfies the multiple
        # exception error interface. If it already satisfies it, it will be returned;
        # otherwise it will wrap it in a `MultipleExceptionError`.
        # @private
        def self.for(ex)
          return ex if self === ex
          MultipleExceptionError.new(ex)
        end
      end

      include InterfaceTag

      # @return [Array<Exception>] The list of failures.
      attr_reader :failures

      # @return [Array<Exception>] The list of other errors.
      attr_reader :other_errors

      # @return [Array<Exception>] The list of failures and other exceptions, combined.
      attr_reader :all_exceptions

      # @return [Hash] Metadata used by RSpec for formatting purposes.
      attr_reader :aggregation_metadata

      # @return [nil] Provided only for interface compatibility with
      #   `RSpec::Expectations::MultipleExpectationsNotMetError`.
      attr_reader :aggregation_block_label

      # @param exceptions [Array<Exception>] The initial list of exceptions.
      def initialize(*exceptions)
        super()

        @failures                = []
        @other_errors            = []
        @all_exceptions          = []
        @aggregation_metadata    = { :hide_backtrace => true }
        @aggregation_block_label = nil

        exceptions.each { |e| add e }
      end

      # @return [String] Combines all the exception messages into a single string.
      # @note RSpec does not actually use this -- instead it formats each exception
      #   individually.
      def message
        all_exceptions.map(&:message).join("\n\n")
      end

      # @return [String] A summary of the failure, including the block label and a count of failures.
      def summary
        "Got #{exception_count_description}"
      end

      # return [String] A description of the failure/error counts.
      def exception_count_description
        failure_count = Formatters::Helpers.pluralize(failures.size, "failure")
        return failure_count if other_errors.empty?
        error_count = Formatters::Helpers.pluralize(other_errors.size, "other error")
        "#{failure_count} and #{error_count}"
      end
    end
  end
end


def formatted_cause(exception)
            last_cause = final_exception(exception, [exception])
            cause = []

            if exception.cause
              cause << '------------------'
              cause << '--- Caused by: ---'
              cause << "#{exception_class_name(last_cause)}:" unless exception_class_name(last_cause) =~ /RSpec/

              encoded_string(exception_message_string(last_cause)).split("\n").each do |line|
                cause << "  #{line}"
              end

              unless last_cause.backtrace.nil? || last_cause.backtrace.empty?
                lines = backtrace_formatter.format_backtrace(last_cause.backtrace, example.metadata)
                lines = [lines[0]] unless RSpec.configuration.full_cause_backtrace # rubocop:disable Metrics/BlockNesting

                lines.each do |line|
                  cause << ("  #{line}")
                end
              end
            end

            cause
          end


def fully_formatted_lines(failure_number, colorizer)
          lines = [
            encoded_description(description),
            detail_formatter.call(example, colorizer),
            formatted_message_and_backtrace(colorizer),
            extra_detail_formatter.call(failure_number, colorizer),
          ].compact.flatten

          lines = indent_lines(lines, failure_number)
          lines.unshift("")
          lines
        end


def indent_lines(lines, failure_number)
          alignment_basis = ' ' * @indentation
          alignment_basis <<  "#{failure_number}) " if failure_number
          indentation = ' ' * alignment_basis.length

          lines.each_with_index.map do |line, index|
            if index == 0
              "#{alignment_basis}#{line}"
            elsif line.empty?
              line
            else
              "#{indentation}#{line}"
            end
          end
        end


def failure_lines
          @failure_lines ||= [].tap do |lines|
            lines.concat(failure_slash_error_lines)

            sections = [failure_slash_error_lines, exception_lines]
            if sections.any? { |section| section.size > 1 } && !exception_lines.first.empty?
              lines << ''
            end

            lines.concat(exception_lines)
            lines.concat(extra_failure_lines)
          end
        end


def failure_slash_error_lines
          lines = read_failed_lines
          if lines.count == 1
            lines[0] = "Failure/Error: #{lines[0].strip}"
          else
            least_indentation = SnippetExtractor.least_indentation_from(lines)
            lines = lines.map { |line| line.sub(/^#{least_indentation}/, '  ') }
            lines.unshift('Failure/Error:')
          end
          lines
        end


def exception_lines
          @exception_lines ||= begin
            lines = []
            lines << "#{exception_class_name}:" unless exception_class_name =~ /RSpec/
            encoded_string(exception_message_string(exception)).split("\n").each do |line|
              lines << (line.empty? ? line : "  #{line}")
            end
            lines
          end
        end


def extra_failure_lines
          @extra_failure_lines ||= begin
            lines = Array(example.metadata[:extra_failure_lines])
            unless lines.empty?
              lines.unshift('') unless lines.first == ''
              lines.push('') unless lines.last == ''
            end
            lines
          end
        end


def read_failed_lines
          matching_line = find_failed_line
          unless matching_line
            return ["Unable to find matching line from backtrace"]
          end

          file_and_line_number = matching_line.match(/(.+?):(\d+)(|:\d+)/)

          unless file_and_line_number
            return ["Unable to infer file and line number from backtrace"]
          end

          file_path, line_number = file_and_line_number[1..2]
          max_line_count = RSpec.configuration.max_displayed_failure_line_count
          lines = SnippetExtractor.extract_expression_lines_at(file_path, line_number.to_i, max_line_count)
          RSpec.world.syntax_highlighter.highlight(lines)
        rescue SnippetExtractor::NoSuchFileError
          ["Unable to find #{file_path} to read failed line"]
        rescue SnippetExtractor::NoSuchLineError
          ["Unable to find matching line in #{file_path}"]
        rescue SecurityError
          # :nocov: - SecurityError is no longer produced starting in ruby 2.7
          ["Unable to read failed line"]
          # :nocov:
        end


def find_failed_line
          line_regex = RSpec.configuration.in_project_source_dir_regex
          loaded_spec_files = RSpec.configuration.loaded_spec_files

          exception_backtrace.reject! do |line|
            line.start_with?("<internal:")
          end

          exception_backtrace.find do |line|
            next unless (line_path = line[/(.+?):(\d+)(|:\d+)/, 1])
            path = File.expand_path(line_path)
            loaded_spec_files.include?(path) || path =~ line_regex
          end || exception_backtrace.first
        end


def pending_options
            if @execution_result.pending_fixed?
              {
                :description   => "#{@example.full_description} FIXED",
                :message_color => RSpec.configuration.fixed_color,
                :failure_lines => [
                  "Expected pending '#{@execution_result.pending_message}' to fail. No error was raised."
                ]
              }
            elsif @execution_result.status == :pending
              options = {
                :message_color    => RSpec.configuration.pending_color,
                :detail_formatter => PENDING_DETAIL_FORMATTER
              }
              if RSpec.configuration.pending_failure_output == :no_backtrace
                options[:backtrace_formatter] = EmptyBacktraceFormatter
              end
              options
            end
          end


def with_multiple_error_options_as_needed(exception, options)
            return options unless multiple_exceptions_error?(exception)

            options = options.merge(
              :failure_lines          => [],
              :extra_detail_formatter => sub_failure_list_formatter(exception, options[:message_color]),
              :detail_formatter       => multiple_exception_summarizer(exception,
                                                                       options[:detail_formatter],
                                                                       options[:message_color])
            )

            return options unless exception.aggregation_metadata[:hide_backtrace]
            options[:backtrace_formatter] = EmptyBacktraceFormatter
            options
          end


def multiple_exception_summarizer(exception, prior_detail_formatter, color)
            lambda do |example, colorizer|
              summary = if exception.aggregation_metadata[:hide_backtrace]
                          # Since the backtrace is hidden, the subfailures will come
                          # immediately after this, and using `:` will read well.
                          "Got #{exception.exception_count_description}:"
                        else
                          # The backtrace comes after this, so using a `:` doesn't make sense
                          # since the failures may be many lines below.
                          "#{exception.summary}."
                        end

              summary = colorizer.wrap(summary, color || RSpec.configuration.failure_color)
              return summary unless prior_detail_formatter
              [
                prior_detail_formatter.call(example, colorizer),
                summary
              ]
            end
          end


def sub_failure_list_formatter(exception, message_color)
            common_backtrace_truncater = CommonBacktraceTruncater.new(exception)

            lambda do |failure_number, colorizer|
              FlatMap.flat_map(exception.all_exceptions.each_with_index) do |failure, index|
                options = with_multiple_error_options_as_needed(
                  failure,
                  :description             => nil,
                  :indentation             => 0,
                  :message_color           => message_color || RSpec.configuration.failure_color,
                  :skip_shared_group_trace => true
                )

                failure   = common_backtrace_truncater.with_truncated_backtrace(failure)
                presenter = ExceptionPresenter.new(failure, @example, options)
                presenter.fully_formatted_lines(
                  "#{failure_number ? "#{failure_number}." : ''}#{index + 1}",
                  colorizer
                )
              end
            end
          end


def with_truncated_backtrace(child)
              child_bt  = child.backtrace
              parent_bt = @parent.backtrace
              return child if child_bt.nil? || child_bt.empty? || parent_bt.nil?

              index_before_first_common_frame = -1.downto(-child_bt.size).find do |index|
                parent_bt[index] != child_bt[index]
              end

              return child if index_before_first_common_frame.nil?
              return child if index_before_first_common_frame == -1

              child = child.dup
              child.set_backtrace(child_bt[0..index_before_first_common_frame])
              child
            end


def add(exception)
          # `PendingExampleFixedError` can be assigned to an example that initially has no
          # failures, but when the `aggregate_failures` around hook completes, it notifies of
          # a failure. If we do not ignore `PendingExampleFixedError` it would be surfaced to
          # the user as part of a multiple exception error, which is undesirable. While it's
          # pretty weird we handle this here, it's the best solution I've been able to come
          # up with, and `PendingExampleFixedError` always represents the _lack_ of any exception
          # so clearly when we are transitioning to a `MultipleExceptionError`, it makes sense to
          # ignore it.
          return if Pending::PendingExampleFixedError === exception

          return if exception == self

          all_exceptions << exception

          if exception.class.name =~ /RSpec/
            failures << exception
          else
            other_errors << exception
          end
        end

class MultipleExceptionError < < StandardError


RSpec::Support.require_rspec_core "shell_escape"

module RSpec
  module Core
    module Formatters
      # Formatters helpers.
      module Helpers
        # @private
        SUB_SECOND_PRECISION = 5

        # @private
        DEFAULT_PRECISION = 2

        # @api private
        #
        # Formats seconds into a human-readable string.
        #
        # @param duration [Float, Fixnum] in seconds
        # @return [String] human-readable time
        #
        # @example
        #    format_duration(1) #=>  "1 minute 1 second"
        #    format_duration(135.14) #=> "2 minutes 15.14 seconds"
        def self.format_duration(duration)
          precision = case
                      when duration < 1 then    SUB_SECOND_PRECISION
                      when duration < 120 then  DEFAULT_PRECISION
                      when duration < 300 then  1
                      else                  0
                      end

          if duration > 60
            minutes = (duration.round / 60).to_i
            seconds = (duration - minutes * 60)

            "#{pluralize(minutes, 'minute')} #{pluralize(format_seconds(seconds, precision), 'second')}"
          else
            pluralize(format_seconds(duration, precision), 'second')
          end
        end

        # @api private
        #
        # Formats seconds to have 5 digits of precision with trailing zeros
        # removed if the number is less than 1 or with 2 digits of precision if
        # the number is greater than zero.
        #
        # @param float [Float]
        # @return [String] formatted float
        #
        # @example
        #    format_seconds(0.000006) #=> "0.00001"
        #    format_seconds(0.020000) #=> "0.02"
        #    format_seconds(1.00000000001) #=> "1"
        #
        # The precision used is set in {Helpers::SUB_SECOND_PRECISION} and
        # {Helpers::DEFAULT_PRECISION}.
        #
        # @see #strip_trailing_zeroes
        def self.format_seconds(float, precision=nil)
          return '0' if float < 0
          precision ||= (float < 1) ? SUB_SECOND_PRECISION : DEFAULT_PRECISION
          formatted = "%.#{precision}f" % float
          strip_trailing_zeroes(formatted)
        end

        # @api private
        #
        # Remove trailing zeros from a string.
        #
        # Only remove trailing zeros after a decimal place.
        # see: http://rubular.com/r/ojtTydOgpn
        #
        # @param string [String] string with trailing zeros
        # @return [String] string with trailing zeros removed
        def self.strip_trailing_zeroes(string)
          string.sub(/(?:(\..*[^0])0+|\.0+)$/, '\1')
        end
        private_class_method :strip_trailing_zeroes

        # @api private
        #
        # Pluralize a word based on a count.
        #
        # @param count [Fixnum] number of objects
        # @param string [String] word to be pluralized
        # @return [String] pluralized word
        def self.pluralize(count, string)
          pluralized_string = if count.to_f == 1
                                string
                              elsif string.end_with?('s') # e.g. "process"
                                "#{string}es" # e.g. "processes"
                              else
                                "#{string}s"
                              end

          "#{count} #{pluralized_string}"
        end

        # @api private
        # Given a list of example ids, organizes them into a compact, ordered list.
        def self.organize_ids(ids)
          grouped = ids.inject(Hash.new { |h, k| h[k] = [] }) do |hash, id|
            file, id = Example.parse_id(id)
            hash[file] << id
            hash
          end

          grouped.sort_by(&:first).map do |file, grouped_ids|
            grouped_ids = grouped_ids.sort_by { |id| id.split(':').map(&:to_i) }
            id = Metadata.id_from(:rerun_file_path => file, :scoped_id => grouped_ids.join(','))
            ShellEscape.conditionally_quote(id)
          end
        end
      end
    end
  end
end


def self.pluralize(count, string)
          pluralized_string = if count.to_f == 1
                                string
                              elsif string.end_with?('s') # e.g. "process"
                                "#{string}es" # e.g. "processes"
                              else
                                "#{string}s"
                              end

          "#{count} #{pluralized_string}"
        end


def self.organize_ids(ids)
          grouped = ids.inject(Hash.new { |h, k| h[k] = [] }) do |hash, id|
            file, id = Example.parse_id(id)
            hash[file] << id
            hash
          end

          grouped.sort_by(&:first).map do |file, grouped_ids|
            grouped_ids = grouped_ids.sort_by { |id| id.split(':').map(&:to_i) }
            id = Metadata.id_from(:rerun_file_path => file, :scoped_id => grouped_ids.join(','))
            ShellEscape.conditionally_quote(id)
          end
        end


require 'erb'

module RSpec
  module Core
    module Formatters
      # @private
      class HtmlPrinter
        include ERB::Util # For the #h method.
        def initialize(output)
          @output = output
        end

        def print_html_start
          @output.puts HTML_HEADER
          @output.puts REPORT_HEADER
        end

        def print_example_group_end
          @output.puts "
"
          @output.puts "
"
        end

        def print_example_group_start(group_id, description, number_of_parents)
          @output.puts "
"
          @output.puts "
"
          @output.puts "
#{h(description)}
"
        end

        def print_example_passed(description, run_time)
          formatted_run_time = "%.5f" % run_time
          @output.puts "
" \
            "#{h(description)}" \
            "#{formatted_run_time}s
"
        end

        def print_example_failed(pending_fixed, description, run_time, failure_id,
                                 exception, extra_content)
          formatted_run_time = "%.5f" % run_time

          @output.puts "
"
          @output.puts "      #{h(description)}"
          @output.puts "      #{formatted_run_time}s"
          @output.puts "
"
          if exception
            @output.puts "

#{h(exception[:message])}

"
            @output.puts "

#{h exception[:backtrace]}

"
          end
          @output.puts extra_content if extra_content
          @output.puts "
"
          @output.puts "
"
        end

        def print_example_pending(description, pending_message)
          @output.puts "
" \
            "#{h(description)} " \
            "(PENDING: #{h(pending_message)})
"
        end

        def print_summary(duration, example_count, failure_count, pending_count)
          totals = String.new(
            "#{example_count} example#{'s' unless example_count == 1}, "
          )
          totals << "#{failure_count} failure#{'s' unless failure_count == 1}"
          totals << ", #{pending_count} pending" if pending_count > 0

          formatted_duration = "%.5f" % duration

          @output.puts ""
          @output.puts ""
          @output.puts "
"
          @output.puts "
"
          @output.puts ""
          @output.puts ""
        end

        def flush
          @output.flush
        end

        def move_progress(percent_done)
          @output.puts "    "
          @output.flush
        end

        def make_header_red
          @output.puts "    "
        end

        def make_header_yellow
          @output.puts "    "
        end

        def make_example_group_header_red(group_id)
          @output.puts "    "
          @output.puts "    "
        end

        def make_example_group_header_yellow(group_id)
          @output.puts "    "
          @output.puts "    "
        end

      private

        def indentation_style(number_of_parents)
          "style=\"margin-left: #{(number_of_parents - 1) * 15}px;\""
        end

        REPORT_HEADER = <<-EOF

RSpec Code Examples

    <input id="passed_checkbox"  name="passed_checkbox"  type="checkbox" checked="checked" onchange="apply_filters()" value="1" /> Passed
    <input id="failed_checkbox"  name="failed_checkbox"  type="checkbox" checked="checked" onchange="apply_filters()" value="2" /> Failed
    <input id="pending_checkbox" name="pending_checkbox" type="checkbox" checked="checked" onchange="apply_filters()" value="3" /> Pending

EOF

        GLOBAL_SCRIPTS = <<-EOF

function addClass(element_id, classname) {
  document.getElementById(element_id).className += (" " + classname);
}

function removeClass(element_id, classname) {
  var elem = document.getElementById(element_id);
  var classlist = elem.className.replace(classname,'');
  elem.className = classlist;
}

function moveProgressBar(percentDone) {
  document.getElementById("rspec-header").style.width = percentDone +"%";
}

function makeRed(element_id) {
  removeClass(element_id, 'passed');
  removeClass(element_id, 'not_implemented');
  addClass(element_id,'failed');
}

function makeYellow(element_id) {
  var elem = document.getElementById(element_id);
  if (elem.className.indexOf("failed") == -1) {  // class doesn't includes failed
    if (elem.className.indexOf("not_implemented") == -1) { // class doesn't include not_implemented
      removeClass(element_id, 'passed');
      addClass(element_id,'not_implemented');
    }
  }
}

function apply_filters() {
  var passed_filter = document.getElementById('passed_checkbox').checked;
  var failed_filter = document.getElementById('failed_checkbox').checked;
  var pending_filter = document.getElementById('pending_checkbox').checked;

  assign_display_style("example passed", passed_filter);
  assign_display_style("example failed", failed_filter);
  assign_display_style("example not_implemented", pending_filter);

  assign_display_style_for_group("example_group passed", passed_filter);
  assign_display_style_for_group("example_group not_implemented", pending_filter, pending_filter || passed_filter);
  assign_display_style_for_group("example_group failed", failed_filter, failed_filter || pending_filter || passed_filter);
}

function get_display_style(display_flag) {
  var style_mode = 'none';
  if (display_flag == true) {
    style_mode = 'block';
  }
  return style_mode;
}

function assign_display_style(classname, display_flag) {
  var style_mode = get_display_style(display_flag);
  var elems = document.getElementsByClassName(classname)
  for (var i=0; i<elems.length;i++) {
    elems[i].style.display = style_mode;
  }
}

function assign_display_style_for_group(classname, display_flag, subgroup_flag) {
  var display_style_mode = get_display_style(display_flag);
  var subgroup_style_mode = get_display_style(subgroup_flag);
  var elems = document.getElementsByClassName(classname)
  for (var i=0; i<elems.length;i++) {
    var style_mode = display_style_mode;
    if ((display_flag != subgroup_flag) && (elems[i].getElementsByTagName('dt')[0].innerHTML.indexOf(", ") != -1)) {
      elems[i].style.display = subgroup_style_mode;
    } else {
      elems[i].style.display = display_style_mode;
    }
  }
}
EOF
        # rubocop:enable Layout/LineLength

        GLOBAL_STYLES = <<-EOF
#rspec-header {
  background: #65C400; color: #fff; height: 4em;
}

.rspec-report h1 {
  margin: 0px 10px 0px 10px;
  padding: 10px;
  font-family: "Lucida Grande", Helvetica, sans-serif;
  font-size: 1.8em;
  position: absolute;
}

#label {
  float:left;
}

#display-filters {
  float:left;
  padding: 28px 0 0 40%;
  font-family: "Lucida Grande", Helvetica, sans-serif;
}

#summary {
  float:right;
  padding: 5px 10px;
  font-family: "Lucida Grande", Helvetica, sans-serif;
  text-align: right;
}

#summary p {
  margin: 0 0 0 2px;
}

#summary #totals {
  font-size: 1.2em;
}

.example_group {
  margin: 0 10px 5px;
  background: #fff;
}

dl {
  margin: 0; padding: 0 0 5px;
  font: normal 11px "Lucida Grande", Helvetica, sans-serif;
}

dt {
  padding: 3px;
  background: #65C400;
  color: #fff;
  font-weight: bold;
}

dd {
  margin: 5px 0 5px 5px;
  padding: 3px 3px 3px 18px;
}

dd .duration {
  padding-left: 5px;
  text-align: right;
  right: 0px;
  float:right;
}

dd.example.passed {
  border-left: 5px solid #65C400;
  border-bottom: 1px solid #65C400;
  background: #DBFFB4; color: #3D7700;
}

dd.example.not_implemented {
  border-left: 5px solid #FAF834;
  border-bottom: 1px solid #FAF834;
  background: #FCFB98; color: #131313;
}

dd.example.pending_fixed {
  border-left: 5px solid #0000C2;
  border-bottom: 1px solid #0000C2;
  color: #0000C2; background: #D3FBFF;
}

dd.example.failed {
  border-left: 5px solid #C20000;
  border-bottom: 1px solid #C20000;
  color: #C20000; background: #FFFBD3;
}

dt.not_implemented {
  color: #000000; background: #FAF834;
}

dt.pending_fixed {
  color: #FFFFFF; background: #C40D0D;
}

dt.failed {
  color: #FFFFFF; background: #C40D0D;
}

#rspec-header.not_implemented {
  color: #000000; background: #FAF834;
}

#rspec-header.pending_fixed {
  color: #FFFFFF; background: #C40D0D;
}

#rspec-header.failed {
  color: #FFFFFF; background: #C40D0D;
}

.backtrace {
  color: #000;
  font-size: 12px;
}

a {
  color: #BE5C00;
}

/* Ruby code, style similar to vibrant ink */
.ruby {
  font-size: 12px;
  font-family: monospace;
  color: white;
  background-color: black;
  padding: 0.1em 0 0.2em 0;
}

.ruby .keyword { color: #FF6600; }
.ruby .constant { color: #339999; }
.ruby .attribute { color: white; }
.ruby .global { color: white; }
.ruby .module { color: white; }
.ruby .class { color: white; }
.ruby .string { color: #66FF00; }
.ruby .ident { color: white; }
.ruby .method { color: #FFCC00; }
.ruby .number { color: white; }
.ruby .char { color: white; }
.ruby .comment { color: #9933CC; }
.ruby .symbol { color: white; }
.ruby .regex { color: #44B4CC; }
.ruby .punct { color: white; }
.ruby .escape { color: white; }
.ruby .interp { color: white; }
.ruby .expr { color: white; }

.ruby .offending { background-color: gray; }
.ruby .linenum {
  width: 75px;
  padding: 0.1em 1em 0.2em 0;
  color: #000000;
  background-color: #FFFBD3;
}
EOF

        HTML_HEADER = <<-EOF
<!DOCTYPE html>

  <title>RSpec results</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta http-equiv="Expires" content="-1" />
  <meta http-equiv="Pragma" content="no-cache" />

EOF
      end
    end
  end
end


module RSpec
  module Core
    module Formatters
      # @api private
      #
      # Extracts code snippets by looking at the backtrace of the passed error
      # and applies synax highlighting and line numbers using html.
      class HtmlSnippetExtractor
        # @private
        module NullConverter
          def self.convert(code)
            %Q(#{code}\n# Install the coderay gem to get syntax highlighting)
          end
        end

        # @private
        module CoderayConverter
          def self.convert(code)
            CodeRay.scan(code, :ruby).html(:line_numbers => false)
          end
        end

        # rubocop:disable Style/ClassVars
        # @private
        @@converter = NullConverter

        begin
          require 'coderay'
          RSpec::Support.require_rspec_core 'formatters/syntax_highlighter'
          RSpec::Core::Formatters::SyntaxHighlighter.attempt_to_add_rspec_terms_to_coderay_keywords
          @@converter = CoderayConverter
          # rubocop:disable Lint/HandleExceptions
        rescue LoadError
          # it'll fall back to the NullConverter assigned above
          # rubocop:enable Lint/HandleExceptions
        end

        # rubocop:enable Style/ClassVars

        # @api private
        #
        # Extract lines of code corresponding to  a backtrace.
        #
        # @param backtrace [String] the backtrace from a test failure
        # @return [String] highlighted code snippet indicating where the test
        #   failure occurred
        #
        # @see #post_process
        def snippet(backtrace)
          raw_code, line = snippet_for(backtrace[0])
          highlighted = @@converter.convert(raw_code)
          post_process(highlighted, line)
        end
        # rubocop:enable Style/ClassVars

        # @api private
        #
        # Create a snippet from a line of code.
        #
        # @param error_line [String] file name with line number (i.e.
        #   'foo_spec.rb:12')
        # @return [String] lines around the target line within the file
        #
        # @see #lines_around
        def snippet_for(error_line)
          if error_line =~ /(.*):(\d+)/
            file = Regexp.last_match[1]
            line = Regexp.last_match[2].to_i
            [lines_around(file, line), line]
          else
            ["# Couldn't get snippet for #{error_line}", 1]
          end
        end

        # @api private
        #
        # Extract lines of code centered around a particular line within a
        # source file.
        #
        # @param file [String] filename
        # @param line [Fixnum] line number
        # @return [String] lines around the target line within the file (2 above
        #   and 1 below).
        def lines_around(file, line)
          if File.file?(file)
            lines = File.read(file).split("\n")
            min = [0, line - 3].max
            max = [line + 1, lines.length - 1].min
            selected_lines = []
            selected_lines.join("\n")
            lines[min..max].join("\n")
          else
            "# Couldn't get snippet for #{file}"
          end
        rescue SecurityError
          # :nocov: - SecurityError is no longer produced starting in ruby 2.7
          "# Couldn't get snippet for #{file}"
          # :nocov:
        end

        # @api private
        #
        # Adds line numbers to all lines and highlights the line where the
        # failure occurred using html `span` tags.
        #
        # @param highlighted [String] syntax-highlighted snippet surrounding the
        #   offending line of code
        # @param offending_line [Fixnum] line where failure occurred
        # @return [String] completed snippet
        def post_process(highlighted, offending_line)
          new_lines = []
          highlighted.split("\n").each_with_index do |line, i|
            new_line = "#{offending_line + i - 2}#{line}"
            new_line = "#{new_line}" if i == 2
            new_lines << new_line
          end
          new_lines.join("\n")
        end
      end
    end
  end
end


RSpec::Support.require_rspec_core "formatters/base_formatter"
require 'json'

module RSpec
  module Core
    module Formatters
      # @private
      class JsonFormatter < BaseFormatter
        Formatters.register self, :message, :dump_summary, :dump_profile, :stop, :seed, :close

        attr_reader :output_hash

        def initialize(output)
          super
          @output_hash = {
            :version => RSpec::Core::Version::STRING
          }
        end

        def message(notification)
          (@output_hash[:messages] ||= []) << notification.message
        end

        def dump_summary(summary)
          @output_hash[:summary] = {
            :duration => summary.duration,
            :example_count => summary.example_count,
            :failure_count => summary.failure_count,
            :pending_count => summary.pending_count,
            :errors_outside_of_examples_count => summary.errors_outside_of_examples_count
          }
          @output_hash[:summary_line] = summary.totals_line
        end

        def stop(group_notification)
          @output_hash[:examples] = group_notification.notifications.map do |notification|
            format_example(notification.example).tap do |hash|
              e = notification.example.exception

              if e
                hash[:exception] = {
                  :class => e.class.name,
                  :message => e.message,
                  :backtrace => notification.formatted_backtrace,
                }
              end
            end
          end
        end

        def seed(notification)
          return unless notification.seed_used?
          @output_hash[:seed] = notification.seed
        end

        def close(_notification)
          output.write @output_hash.to_json
        end

        def dump_profile(profile)
          @output_hash[:profile] = {}
          dump_profile_slowest_examples(profile)
          dump_profile_slowest_example_groups(profile)
        end

        # @api private
        def dump_profile_slowest_examples(profile)
          @output_hash[:profile] = {}
          @output_hash[:profile][:examples] = profile.slowest_examples.map do |example|
            format_example(example).tap do |hash|
              hash[:run_time] = example.execution_result.run_time
            end
          end
          @output_hash[:profile][:slowest] = profile.slow_duration
          @output_hash[:profile][:total] = profile.duration
        end

        # @api private
        def dump_profile_slowest_example_groups(profile)
          @output_hash[:profile] ||= {}
          @output_hash[:profile][:groups] = profile.slowest_groups.map do |loc, hash|
            hash.update(:location => loc)
          end
        end

      private

        def format_example(example)
          {
            :id => example.id,
            :description => example.description,
            :full_description => example.full_description,
            :status => example.execution_result.status.to_s,
            :file_path => example.metadata[:file_path],
            :line_number  => example.metadata[:line_number],
            :run_time => example.execution_result.run_time,
            :pending_message => example.execution_result.pending_message,
          }
        end
      end
    end
  end
end


def stop(group_notification)
          @output_hash[:examples] = group_notification.notifications.map do |notification|
            format_example(notification.example).tap do |hash|
              e = notification.example.exception

              if e
                hash[:exception] = {
                  :class => e.class.name,
                  :message => e.message,
                  :backtrace => notification.formatted_backtrace,
                }
              end
            end
          end
        end


module RSpec
  module Core
    module Formatters
      # @private
      class SnippetExtractor
        NoSuchFileError = Class.new(StandardError)
        NoSuchLineError = Class.new(StandardError)

        def self.extract_line_at(file_path, line_number)
          source = source_from_file(file_path)
          line = source.lines[line_number - 1]
          raise NoSuchLineError unless line
          line
        end

        def self.source_from_file(path)
          raise NoSuchFileError unless File.exist?(path)
          RSpec.world.source_from_file(path)
        end

        if RSpec::Support::RubyFeatures.ripper_supported?
          NoExpressionAtLineError = Class.new(StandardError)

          attr_reader :source, :beginning_line_number, :max_line_count

          def self.extract_expression_lines_at(file_path, beginning_line_number, max_line_count=nil)
            if max_line_count == 1
              [extract_line_at(file_path, beginning_line_number)]
            else
              source = source_from_file(file_path)
              new(source, beginning_line_number, max_line_count).expression_lines
            end
          end

          def initialize(source, beginning_line_number, max_line_count=nil)
            @source = source
            @beginning_line_number = beginning_line_number
            @max_line_count = max_line_count
          end

          def expression_lines
            line_range = line_range_of_expression

            if max_line_count && line_range.count > max_line_count
              line_range = (line_range.begin)..(line_range.begin + max_line_count - 1)
            end

            source.lines[(line_range.begin - 1)..(line_range.end - 1)]
          rescue SyntaxError, NoExpressionAtLineError
            [self.class.extract_line_at(source.path, beginning_line_number)]
          end

          private

          def line_range_of_expression
            @line_range_of_expression ||= begin
              line_range = line_range_of_location_nodes_in_expression
              initial_unclosed_tokens = unclosed_tokens_in_line_range(line_range)
              unclosed_tokens = initial_unclosed_tokens

              until (initial_unclosed_tokens & unclosed_tokens).empty?
                line_range = (line_range.begin)..(line_range.end + 1)
                unclosed_tokens = unclosed_tokens_in_line_range(line_range)
              end

              line_range
            end
          end

          def unclosed_tokens_in_line_range(line_range)
            tokens = FlatMap.flat_map(line_range) do |line_number|
              source.tokens_by_line_number[line_number]
            end

            tokens.each_with_object([]) do |token, unclosed_tokens|
              if token.opening?
                unclosed_tokens << token
              else
                index = unclosed_tokens.rindex do |unclosed_token|
                  unclosed_token.closed_by?(token)
                end
                unclosed_tokens.delete_at(index) if index
              end
            end
          end

          def line_range_of_location_nodes_in_expression
            line_numbers = expression_node.each_with_object(Set.new) do |node, set|
              set << node.location.line if node.location
            end

            line_numbers.min..line_numbers.max
          end

          def expression_node
            raise NoExpressionAtLineError if location_nodes_at_beginning_line.empty?

            @expression_node ||= begin
              common_ancestor_nodes = location_nodes_at_beginning_line.map do |node|
                node.each_ancestor.to_a
              end.reduce(:&)

              common_ancestor_nodes.find { |node| expression_outmost_node?(node) }
            end
          end

          def expression_outmost_node?(node)
            return true unless node.parent
            return false if node.type.to_s.start_with?('@')
            ![node, node.parent].all? do |n|
              # See `Ripper::PARSER_EVENTS` for the complete list of sexp types.
              type = n.type.to_s
              type.end_with?('call') || type.start_with?('method_add_')
            end
          end

          def location_nodes_at_beginning_line
            source.nodes_by_line_number[beginning_line_number]
          end
        else
          # :nocov:
          def self.extract_expression_lines_at(file_path, beginning_line_number, *)
            [extract_line_at(file_path, beginning_line_number)]
          end
          # :nocov:
        end

        def self.least_indentation_from(lines)
          lines.map { |line| line[/^[ \t]*/] }.min
        end
      end
    end
  end
end


def unclosed_tokens_in_line_range(line_range)
            tokens = FlatMap.flat_map(line_range) do |line_number|
              source.tokens_by_line_number[line_number]
            end

            tokens.each_with_object([]) do |token, unclosed_tokens|
              if token.opening?
                unclosed_tokens << token
              else
                index = unclosed_tokens.rindex do |unclosed_token|
                  unclosed_token.closed_by?(token)
                end
                unclosed_tokens.delete_at(index) if index
              end
            end
          end