require 'support/aruba_support'
require 'support/formatter_support'

RSpec.describe 'Suite hook errors' do
  include_context "aruba support"
  include FormatterSupport

  let(:failure_exit_code) { rand(97) + 2 } # 2..99

  if RSpec::Support::Ruby.jruby? && JRUBY_VERSION >= '9.0.0.0'
    let(:spec_line_suffix) { ":in `block in (root)'" }
  elsif RSpec::Support::Ruby.jruby?
    let(:spec_line_suffix) { ":in `(root)'" }
  elsif RUBY_VERSION == "1.8.7"
    let(:spec_line_suffix) { "" }
  else
    let(:spec_line_suffix) { ":in `block (2 levels) in <top (required)>'" }
  end

  before do
    # get out of `aruba` sub-dir so that `filter_gems_from_backtrace 'aruba'`
    # below does not filter out our spec file.
    expect(dirs.pop).to eq "aruba"

    clean_current_dir

    RSpec.configure do |c|
      c.filter_gems_from_backtrace "aruba"
      c.backtrace_exclusion_patterns << %r{/rspec-core/spec/} << %r{rspec_with_simplecov}
      c.failure_exit_code = failure_exit_code
    end
  end

  def run_spec_expecting_non_zero(before_or_after)
    write_file "the_spec.rb", "
      RSpec.configure do |c|
        c.#{before_or_after}(:suite) do
          raise 'boom'
        end
      end

      RSpec.describe do
        it { }
      end
    "

    run_command "the_spec.rb"
    expect(last_cmd_exit_status).to eq(failure_exit_code)
    normalize_durations(last_cmd_stdout)
  end

  it 'nicely formats errors in `before(:suite)` hooks and exits with non-zero' do
    output = run_spec_expecting_non_zero(:before)
    expect(output).to eq unindent(<<-EOS)

      An error occurred in a `before(:suite)` hook.
      Failure/Error: raise 'boom'

      RuntimeError:
        boom
      # ./the_spec.rb:4#{spec_line_suffix}


      Finished in n.nnnn seconds (files took n.nnnn seconds to load)
      0 examples, 0 failures
    EOS
  end

  it 'nicely formats errors in `after(:suite)` hooks and exits with non-zero' do
    output = run_spec_expecting_non_zero(:after)
    expect(output).to eq unindent(<<-EOS)
      .
      An error occurred in an `after(:suite)` hook.
      Failure/Error: raise 'boom'

      RuntimeError:
        boom
      # ./the_spec.rb:4#{spec_line_suffix}


      Finished in n.nnnn seconds (files took n.nnnn seconds to load)
      1 example, 0 failures
    EOS
  end

  it 'nicely formats errors from multiple :suite hooks of both types and exits with non-zero' do
    write_file "the_spec.rb", "
      RSpec.configure do |c|
        c.before(:suite) { raise 'before 1' }
        c.before(:suite) { raise 'before 2' }
        c.after(:suite) { raise 'after 1' }
        c.after(:suite) { raise 'after 2' }
      end

      RSpec.describe do
        it { }
      end
    "

    run_command "the_spec.rb"
    expect(last_cmd_exit_status).to eq(failure_exit_code)
    output = normalize_durations(last_cmd_stdout)

    expect(output).to eq unindent(<<-EOS)

      An error occurred in a `before(:suite)` hook.
      Failure/Error: c.before(:suite) { raise 'before 1' }

      RuntimeError:
        before 1
      # ./the_spec.rb:3#{spec_line_suffix}

      An error occurred in a `before(:suite)` hook.
      Failure/Error: c.before(:suite) { raise 'before 2' }

      RuntimeError:
        before 2
      # ./the_spec.rb:4#{spec_line_suffix}

      An error occurred in an `after(:suite)` hook.
      Failure/Error: c.after(:suite) { raise 'after 2' }

      RuntimeError:
        after 2
      # ./the_spec.rb:6#{spec_line_suffix}

      An error occurred in an `after(:suite)` hook.
      Failure/Error: c.after(:suite) { raise 'after 1' }

      RuntimeError:
        after 1
      # ./the_spec.rb:5#{spec_line_suffix}


      Finished in n.nnnn seconds (files took n.nnnn seconds to load)
      0 examples, 0 failures
    EOS
  end
end
