Skip to content

Commit

Permalink
Merge pull request #843 from samphippen/backtrace-cleaner
Browse files Browse the repository at this point in the history
Backtrace cleaner
  • Loading branch information
myronmarston committed Mar 26, 2013
2 parents 973a03b + 56d7c0a commit 8ac0f7e
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 39 deletions.
42 changes: 42 additions & 0 deletions lib/rspec/core/backtrace_cleaner.rb
@@ -0,0 +1,42 @@
module RSpec
module Core
class BacktraceCleaner

DEFAULT_EXCLUSION_PATTERNS = [
/\/lib\d*\/ruby\//,
/org\/jruby\//,
/bin\//,
%r|/gems/|,
/spec\/spec_helper\.rb/,
/lib\/rspec\/(core|expectations|matchers|mocks)/
]

attr_accessor :inclusion_patterns
attr_accessor :exclusion_patterns

def initialize(inclusion_patterns=nil, exclusion_patterns=DEFAULT_EXCLUSION_PATTERNS.dup)
@exclusion_patterns = exclusion_patterns

if inclusion_patterns.nil?
@inclusion_patterns = (matches_an_exclusion_pattern? Dir.getwd) ? [Regexp.new(Dir.getwd)] : []
else
@inclusion_patterns = inclusion_patterns
end
end

def exclude?(line)
@inclusion_patterns.none? {|p| line =~ p} and matches_an_exclusion_pattern?(line)
end

def full_backtrace=(true_or_false)
@exclusion_patterns = true_or_false ? [] : DEFAULT_EXCLUSION_PATTERNS.dup
end

private

def matches_an_exclusion_pattern?(line)
@exclusion_patterns.any? {|p| line =~ p}
end
end
end
end
90 changes: 62 additions & 28 deletions lib/rspec/core/configuration.rb
@@ -1,4 +1,5 @@
require 'fileutils'
require 'rspec/core/backtrace_cleaner'
require 'rspec/core/ruby_project'

module RSpec
Expand Down Expand Up @@ -81,15 +82,6 @@ def self.add_setting(name, opts={})

# @macro [attach] add_setting
# @attribute $1
# Patterns to match against lines in backtraces presented in failure
# messages in order to filter them out (default:
# DEFAULT_BACKTRACE_PATTERNS). You can either replace this list using
# the setter or modify it using the getter.
#
# To override this behavior and display a full backtrace, use
# `--backtrace` on the command line, in a `.rspec` file, or in the
# `rspec_options` attribute of RSpec's rake task.
add_setting :backtrace_clean_patterns

# Path to use if no path is provided to the `rspec` command (default:
# `"spec"`). Allows you to just type `rspec` instead of `rspec spec` to
Expand Down Expand Up @@ -183,14 +175,7 @@ def self.add_setting(name, opts={})
# @private
attr_accessor :filter_manager

DEFAULT_BACKTRACE_PATTERNS = [
/\/lib\d*\/ruby\//,
/org\/jruby\//,
/bin\//,
%r|/gems/|,
/spec\/spec_helper\.rb/,
/lib\/rspec\/(core|expectations|matchers|mocks)/
]
attr_reader :backtrace_cleaner

def initialize
@expectation_frameworks = []
Expand All @@ -201,7 +186,9 @@ def initialize
@color = false
@pattern = '**/*_spec.rb'
@failure_exit_code = 1
@backtrace_clean_patterns = DEFAULT_BACKTRACE_PATTERNS.dup

@backtrace_cleaner = BacktraceCleaner.new

@default_path = 'spec'
@filter_manager = FilterManager.new
@preferred_options = {}
Expand Down Expand Up @@ -276,15 +263,6 @@ def add_setting(name, opts={})
send("#{name}=", default) if default
end

# Used by formatters to ask whether a backtrace line should be displayed
# or not, based on the line matching any `backtrace_clean_patterns`.
def cleaned_from_backtrace?(line)
# TODO (David 2011-12-25) why are we asking the configuration to do
# stuff? Either use the patterns directly or enapsulate the filtering
# in a BacktraceCleaner object.
backtrace_clean_patterns.any? { |regex| line =~ regex }
end

# Returns the configured mock framework adapter module
def mock_framework
mock_with :rspec unless @mock_framework
Expand All @@ -296,6 +274,62 @@ def mock_framework=(framework)
mock_with framework
end

# The patterns to discard from backtraces. Deprecated, use
# Configuration#backtrace_exclusion_patterns instead
#
# Defaults to RSpec::Core::BacktraceCleaner::DEFAULT_EXCLUSION_PATTERNS
#
# One can replace the list by using the setter or modify it through the
# getter
#
# To override this behaviour and display a full backtrace, use
# `--backtrace`on the command line, in a `.rspec` file, or in the
# `rspec_options` attribute of RSpec's rake task.
def backtrace_clean_patterns
RSpec.deprecate("RSpec::Core::Configuration#backtrace_clean_patterns",
"RSpec::Core::Configuration#backtrace_exclusion_patterns")
@backtrace_cleaner.exclusion_patterns
end

def backtrace_clean_patterns=(patterns)
RSpec.deprecate("RSpec::Core::Configuration#backtrace_clean_patterns",
"RSpec::Core::Configuration#backtrace_exclusion_patterns")
@backtrace_cleaner.exclusion_patterns = patterns
end

# The patterns to always include to backtraces.
#
# Defaults to [Regexp.new Dir.getwd] if the current working directory
# matches any of the exclusion patterns. Otherwise it defaults to empty.
#
# One can replace the list by using the setter or modify it through the
# getter
def backtrace_inclusion_patterns
@backtrace_cleaner.inclusion_patterns
end

def backtrace_inclusion_patterns=(patterns)
@backtrace_cleaner.inclusion_patterns = patterns
end

# The patterns to discard from backtraces.
#
# Defaults to RSpec::Core::BacktraceCleaner::DEFAULT_EXCLUSION_PATTERNS
#
# One can replace the list by using the setter or modify it through the
# getter
#
# To override this behaviour and display a full backtrace, use
# `--backtrace`on the command line, in a `.rspec` file, or in the
# `rspec_options` attribute of RSpec's rake task.
def backtrace_exclusion_patterns
@backtrace_cleaner.exclusion_patterns
end

def backtrace_exclusion_patterns=(patterns)
@backtrace_cleaner.exclusion_patterns = patterns
end

# Sets the mock framework adapter module.
#
# `framework` can be a Symbol or a Module.
Expand Down Expand Up @@ -425,7 +459,7 @@ def expect_with(*frameworks)
end

def full_backtrace=(true_or_false)
@backtrace_clean_patterns = true_or_false ? [] : DEFAULT_BACKTRACE_PATTERNS
@backtrace_cleaner.full_backtrace = true_or_false
end

def color(output=output_stream)
Expand Down
2 changes: 1 addition & 1 deletion lib/rspec/core/formatters/helpers.rb
Expand Up @@ -18,7 +18,7 @@ def format_backtrace(backtrace, options = {})
protected

def backtrace_line(line)
return nil if RSpec.configuration.cleaned_from_backtrace?(line)
return nil if RSpec.configuration.backtrace_cleaner.exclude?(line)
RSpec::Core::Metadata::relative_path(line)
rescue SecurityError
nil
Expand Down
56 changes: 56 additions & 0 deletions spec/rspec/core/backtrace_cleaner_spec.rb
@@ -0,0 +1,56 @@
require "spec_helper"

module RSpec::Core
describe BacktraceCleaner do
context "with no patterns" do
it "keeps all lines" do
lines = ["/tmp/a_file", "some_random_text", "hello\330\271!"]
cleaner = BacktraceCleaner.new([], [])
expect(lines.all? {|line| cleaner.exclude? line}).to be_false
end
end

context "with an exclusion pattern but no inclusion patterns" do
it "excludes lines that match the exclusion pattern" do
cleaner = BacktraceCleaner.new([], [/remove/])
expect(cleaner.exclude? "remove me").to be_true
end

it "keeps lines that do not match the exclusion pattern" do
cleaner = BacktraceCleaner.new([], [/remove/])
expect(cleaner.exclude? "apple").to be_false
end
end

context "with an exclusion pattern and an inclusion pattern" do
it "excludes lines that match the exclusion pattern but not the inclusion pattern" do
cleaner = BacktraceCleaner.new([/keep/], [/discard/])
expect(cleaner.exclude? "discard").to be_true
end

it "keeps lines that match the inclusion pattern and the exclusion pattern" do
cleaner = BacktraceCleaner.new([/hi/], [/.*/])
expect(cleaner.exclude? "hi").to be_false
end

it "keeps lines that match neither pattern" do
cleaner = BacktraceCleaner.new([/hi/], [/delete/])
expect(cleaner.exclude? "fish").to be_false
end
end

context "with an exclusion pattern that matches the current working directory" do
it "defaults to having one inclusion pattern, the current working directory" do
cleaner = BacktraceCleaner.new(nil, [/.*/])
expect(Dir.getwd =~ cleaner.inclusion_patterns.first).to be_true
end
end

context "with an exclusion pattern that does not match the current working directory" do
it "defaults to having no exclusion patterns" do
cleaner = BacktraceCleaner.new(nil, [/i_wont_match_a_directory/])
expect(cleaner.inclusion_patterns.length).to be_zero
end
end
end
end
49 changes: 39 additions & 10 deletions spec/rspec/core/configuration_spec.rb
Expand Up @@ -940,16 +940,16 @@ def metadata_hash(*args)

describe "#full_backtrace=" do
context "given true" do
it "clears the backtrace clean patterns" do
it "clears the backtrace exclusion patterns" do
config.full_backtrace = true
expect(config.backtrace_clean_patterns).to eq([])
expect(config.backtrace_exclusion_patterns).to eq([])
end
end

context "given false" do
it "restores backtrace clean patterns" do
config.full_backtrace = false
expect(config.backtrace_clean_patterns).to eq(RSpec::Core::Configuration::DEFAULT_BACKTRACE_PATTERNS)
expect(config.backtrace_exclusion_patterns).to eq(RSpec::Core::BacktraceCleaner::DEFAULT_EXCLUSION_PATTERNS)
end
end

Expand All @@ -958,29 +958,58 @@ def metadata_hash(*args)
config_2 = Configuration.new

config_1.full_backtrace = true
expect(config_2.backtrace_clean_patterns).not_to be_empty
expect(config_2.backtrace_exclusion_patterns).not_to be_empty
end
end

describe "#cleaned_from_backtrace? defaults" do
describe "#backtrace_clean_patterns=" do
it "actually receives the new filter values" do
RSpec.stub(:warn_deprecation)
config = Configuration.new
config.backtrace_clean_patterns = [/.*/]
expect(config.backtrace_cleaner.exclude? "this").to be_true
end
end

describe "#backtrace_clean_patterns" do
it "is deprecated" do
RSpec.stub(:warn_deprecation)
RSpec.should_receive(:warn_deprecation)
config = Configuration.new
config.backtrace_clean_patterns
end

it "can be appended to" do
RSpec.stub(:warn_deprecation)
config = Configuration.new
config.backtrace_clean_patterns << /.*/
expect(config.backtrace_cleaner.exclude? "this").to be_true
end
end

describe ".backtrace_cleaner#exclude? defaults" do
it "returns true for rspec files" do
expect(config.cleaned_from_backtrace?("lib/rspec/core.rb")).to be_true
expect(config.backtrace_cleaner.exclude?("lib/rspec/core.rb")).to be_true
end

it "returns true for spec_helper" do
expect(config.cleaned_from_backtrace?("spec/spec_helper.rb")).to be_true
expect(config.backtrace_cleaner.exclude?("spec/spec_helper.rb")).to be_true
end

it "returns true for java files (for JRuby)" do
expect(config.cleaned_from_backtrace?("org/jruby/RubyArray.java:2336")).to be_true
expect(config.backtrace_cleaner.exclude?("org/jruby/RubyArray.java:2336")).to be_true
end

it "returns true for files within installed gems" do
expect(config.cleaned_from_backtrace?('ruby-1.8.7-p334/gems/mygem-2.3.0/lib/mygem.rb')).to be_true
expect(config.backtrace_cleaner.exclude?('ruby-1.8.7-p334/gems/mygem-2.3.0/lib/mygem.rb')).to be_true
end

it "returns false for files in projects containing 'gems' in the name" do
expect(config.cleaned_from_backtrace?('code/my-gems-plugin/lib/plugin.rb')).to be_false
expect(config.backtrace_cleaner.exclude?('code/my-gems-plugin/lib/plugin.rb')).to be_false
end

it "returns false for something in the current working directory" do
expect(config.backtrace_cleaner.exclude?("#{Dir.getwd}/arbitrary")).to be_false
end
end

Expand Down
1 change: 1 addition & 0 deletions spec/rspec/core/formatters/html_formatter_spec.rb
Expand Up @@ -33,6 +33,7 @@ module Formatters
out.set_encoding("utf-8") if out.respond_to?(:set_encoding)

command_line = RSpec::Core::CommandLine.new(options)
command_line.instance_variable_get("@configuration").backtrace_cleaner.inclusion_patterns = []
command_line.run(err, out)
out.string.gsub(/\d+\.\d+(s| seconds)/, "n.nnnn\\1")
end
Expand Down
1 change: 1 addition & 0 deletions spec/rspec/core/formatters/text_mate_formatter_spec.rb
Expand Up @@ -33,6 +33,7 @@ module Formatters
out.set_encoding("utf-8") if out.respond_to?(:set_encoding)

command_line = RSpec::Core::CommandLine.new(options)
command_line.instance_variable_get("@configuration").backtrace_cleaner.inclusion_patterns = []
command_line.run(err, out)
out.string.gsub(/\d+\.\d+(s| seconds)/, "n.nnnn\\1")
end
Expand Down

0 comments on commit 8ac0f7e

Please sign in to comment.