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)
for 1.8.7
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 @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 exception_message_string(exception)
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
def exception_message_string(exception)
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
def extra_failure_lines
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 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 && Exception === 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.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_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, [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 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 # :nocov: - SecurityError is no longer produced starting in ruby 2.7 ["Unable to read failed line"] # :nocov: end