class RSpec::Core::Formatters::ExceptionPresenter
@private
def add_shared_group_lines(lines, colorizer)
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 colorized_formatted_backtrace(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
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 colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
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 encoded_description(description)
def encoded_description(description) return if description.nil? encoded_string(description) end
def encoded_description(description)
def encoded_description(description) description end
def encoded_string(string)
def encoded_string(string) RSpec::Support::EncodedString.new(string, Encoding.default_external) end
def encoded_string(string)
def encoded_string(string) RSpec::Support::EncodedString.new(string) end
def encoding_of(string)
def encoding_of(string) string.encoding end
def encoding_of(_string)
for 1.8.7
def encoding_of(_string) end
def exception_backtrace
def exception_backtrace exception.backtrace || [] end
def exception_class_name(exception=@exception)
def exception_class_name(exception=@exception) name = exception.class.name.to_s name = "(anonymous error class)" if name == '' name end
def exception_lines
def exception_lines lines = [] lines << "#{exception_class_name}:" unless exception_class_name =~ /RSpec/ encoded_string(exception.message.to_s).split("\n").each do |line| lines << (line.empty? ? line : " #{line}") end lines end
def extra_failure_lines
def extra_failure_lines @extra_failure_lines ||= begin lines = Array(example.metadata[:extra_failure_lines]) unless lines.empty? lines.unshift('') lines.push('') end lines end end
def failure_lines
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
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 final_exception(exception, previous=[])
def final_exception(exception, previous=[]) cause = exception.cause if cause && !previous.include?(cause) previous << cause final_exception(cause, previous) else exception end end
def find_failed_line
def find_failed_line line_regex = RSpec.configuration.in_project_source_dir_regex loaded_spec_files = RSpec.configuration.loaded_spec_files 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_backtrace(exception=@exception)
def formatted_backtrace(exception=@exception) backtrace_formatter.format_backtrace(exception.backtrace, example.metadata) + formatted_cause(exception) end
def formatted_cause(exception)
def formatted_cause(exception) last_cause = final_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(last_cause.message.to_s).split("\n").each do |line| cause << " #{line}" end cause << (" #{backtrace_formatter.format_backtrace(last_cause.backtrace, example.metadata).first}") end cause end
def formatted_cause(_)
def formatted_cause(_) [] end
def formatted_message_and_backtrace(colorizer)
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
def fully_formatted(failure_number, colorizer=::RSpec::Core::Formatters::ConsoleCodes)
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)
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)
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 initialize(exception, example, options={})
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
def message_lines add_shared_group_lines(failure_lines, Notifications::NullColorizer) end
def read_failed_lines
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 ["Unable to read failed line"] end