diff --git a/README.md b/README.md index b672e9ec..d2020974 100644 --- a/README.md +++ b/README.md @@ -262,7 +262,7 @@ report. ### Defining custom filters -You can currently define a filter using either a String (that will then be Regexp-matched against each source file's path), +You can currently define a filter using either a String or Regexp (that will then be Regexp-matched against each source file's path), a block or by passing in your own Filter class. #### String filter @@ -275,6 +275,16 @@ end This simple string filter will remove all files that match "/test/" in their path. +#### Regex filter + +```ruby +SimpleCov.start do + add_filter %r{^/test/} +end +``` + +This simple regex filter will remove all files that start with /test/ in their path. + #### Block filter ```ruby @@ -305,6 +315,17 @@ Defining your own filters is pretty easy: Just inherit from SimpleCov::Filter an the filter, a true return value from this method will result in the removal of the given source_file. The filter_argument method is being set in the SimpleCov::Filter initialize method and thus is set to 5 in this example. +#### Array filter + +```ruby +SimpleCov.start do + proc = Proc.new { |source_file| fales } + add_filter ["string", /regex/, proc, LineFilter.new(5)] +end +``` + +You can pass in an array containing any of the other filter types. + #### Ignoring/skipping code You can exclude code from the coverage report by wrapping it in `# :nocov:`. diff --git a/doc/editor-integration.md b/doc/editor-integration.md index 7d0dd095..2a115f26 100644 --- a/doc/editor-integration.md +++ b/doc/editor-integration.md @@ -7,7 +7,12 @@ Some editors have a graphical integration for the simplecov gem. Adds an overview of your current test coverage to Atom. +#### [Sublime Editor: Simple​Cov](https://packagecontrol.io/packages/SimpleCov) +*by sentience* + +Adds in editor live coverage highlighting, status bar coverage information, and summary coverage information. + #### [cadre](https://github.com/nyarly/cadre) *by Judson Lester* -Includes a formatter for Simplecov that emits a Vim script to mark up code files with coverage information. \ No newline at end of file +Includes a formatter for Simplecov that emits a Vim script to mark up code files with coverage information. diff --git a/lib/simplecov/configuration.rb b/lib/simplecov/configuration.rb index b4308cbf..ce36929c 100644 --- a/lib/simplecov/configuration.rb +++ b/lib/simplecov/configuration.rb @@ -291,16 +291,12 @@ def add_group(group_name, filter_argument = nil, &filter_proc) # The actual filter processor. Not meant for direct use # def parse_filter(filter_argument = nil, &filter_proc) - if filter_argument.is_a?(SimpleCov::Filter) - filter_argument - elsif filter_argument.is_a?(String) - SimpleCov::StringFilter.new(filter_argument) - elsif filter_proc - SimpleCov::BlockFilter.new(filter_proc) - elsif filter_argument.is_a?(Array) - SimpleCov::ArrayFilter.new(filter_argument) + filter = filter_argument || filter_proc + + if filter + SimpleCov::Filter.build_filter(filter) else - raise ArgumentError, "Please specify either a string or a block to filter with" + raise ArgumentError, "Please specify either a filter or a block to filter with" end end end diff --git a/lib/simplecov/defaults.rb b/lib/simplecov/defaults.rb index b8f1c888..ad4264e1 100644 --- a/lib/simplecov/defaults.rb +++ b/lib/simplecov/defaults.rb @@ -25,8 +25,8 @@ SimpleCov.profiles.define "rails" do load_profile "test_frameworks" - add_filter "/config/" - add_filter "/db/" + add_filter %r{^/config/} + add_filter %r{/^/db/} add_group "Controllers", "app/controllers" add_group "Channels", "app/channels" if defined?(ActionCable) diff --git a/lib/simplecov/filter.rb b/lib/simplecov/filter.rb index b6c11a29..3acc4f29 100644 --- a/lib/simplecov/filter.rb +++ b/lib/simplecov/filter.rb @@ -24,13 +24,40 @@ def passes?(source_file) warn "#{Kernel.caller.first}: [DEPRECATION] #passes? is deprecated. Use #matches? instead." matches?(source_file) end + + def self.build_filter(filter_argument) + return filter_argument if filter_argument.is_a?(SimpleCov::Filter) + class_for_argument(filter_argument).new(filter_argument) + end + + def self.class_for_argument(filter_argument) + if filter_argument.is_a?(String) + SimpleCov::StringFilter + elsif filter_argument.is_a?(Regexp) + SimpleCov::RegexFilter + elsif filter_argument.is_a?(Array) + SimpleCov::ArrayFilter + elsif filter_argument.is_a?(Proc) + SimpleCov::BlockFilter + else + raise ArgumentError, "You have provided an unrecognized filter type" + end + end end class StringFilter < SimpleCov::Filter # Returns true when the given source file's filename matches the # string configured when initializing this Filter with StringFilter.new('somestring) def matches?(source_file) - (source_file.filename =~ /#{filter_argument}/) + (source_file.project_filename =~ /#{filter_argument}/) + end + end + + class RegexFilter < SimpleCov::Filter + # Returns true when the given source file's filename matches the + # regex configured when initializing this Filter with RegexFilter.new(/someregex/) + def matches?(source_file) + (source_file.project_filename =~ filter_argument) end end @@ -43,11 +70,19 @@ def matches?(source_file) end class ArrayFilter < SimpleCov::Filter - # Returns true if any of the file paths passed in the given array matches the string - # configured when initializing this Filter with StringFilter.new(['some/path', 'other/path']) + def initialize(filter_argument) + filter_objects = filter_argument.map do |arg| + Filter.build_filter(arg) + end + + super(filter_objects) + end + + # Returns true if any of the filters in the array match the given source file. + # Configure this Filter like StringFilter.new(['some/path', /^some_regex/, Proc.new {|src_file| ... }]) def matches?(source_files_list) filter_argument.any? do |arg| - source_files_list.filename =~ /#{arg}/ + arg.matches?(source_files_list) end end end diff --git a/lib/simplecov/source_file.rb b/lib/simplecov/source_file.rb index 37b6c947..33eb9fb1 100644 --- a/lib/simplecov/source_file.rb +++ b/lib/simplecov/source_file.rb @@ -80,6 +80,11 @@ def initialize(filename, coverage) @coverage = coverage end + # The path to this source file relative to the projects directory + def project_filename + @filename.sub(/^#{SimpleCov.root}/, "") + end + # The source code for this file. Aliased as :source def src # We intentionally read source code lazily to diff --git a/spec/filters_spec.rb b/spec/filters_spec.rb index 87bba2e0..6cdaa84b 100644 --- a/spec/filters_spec.rb +++ b/spec/filters_spec.rb @@ -26,6 +26,27 @@ expect(SimpleCov::StringFilter.new("sample.rb")).to be_matches subject end + it "doesn't match a parent directory with a new SimpleCov::StringFilter" do + parent_dir_name = File.basename(File.expand_path("..", File.dirname(__FILE__))) + expect(SimpleCov::StringFilter.new(parent_dir_name)).not_to be_matches subject + end + + it "matches a new SimpleCov::StringFilter '/fixtures/'" do + expect(SimpleCov::StringFilter.new("sample.rb")).to be_matches subject + end + + it "matches a new SimpleCov::RegexFilter /\/fixtures\//" do + expect(SimpleCov::RegexFilter.new(/\/fixtures\//)).to be_matches subject + end + + it "doesn't match a new SimpleCov::RegexFilter /^\/fixtures\//" do + expect(SimpleCov::RegexFilter.new(/^\/fixtures\//)).not_to be_matches subject + end + + it "matches a new SimpleCov::RegexFilter /^\/spec\//" do + expect(SimpleCov::RegexFilter.new(/^\/spec\//)).to be_matches subject + end + it "doesn't match a new SimpleCov::BlockFilter that is not applicable" do expect(SimpleCov::BlockFilter.new(proc { |s| File.basename(s.filename) == "foo.rb" })).not_to be_matches subject end @@ -46,6 +67,45 @@ expect(SimpleCov::ArrayFilter.new(["sample.rb", "other_file.rb"])).to be_matches subject end + it "doesn't match a parent directory with a new SimpleCov::ArrayFilter" do + parent_dir_name = File.basename(File.expand_path("..", File.dirname(__FILE__))) + expect(SimpleCov::ArrayFilter.new([parent_dir_name])).not_to be_matches subject + end + + it "matches a new SimpleCov::ArrayFilter when /sample.rb/ is passed as array" do + expect(SimpleCov::ArrayFilter.new([/sample.rb/])).to be_matches subject + end + + it "doesn't match a new SimpleCov::ArrayFilter when a file path different than /sample.rb/ is passed as array" do + expect(SimpleCov::ArrayFilter.new([/other_file.rb/])).not_to be_matches subject + end + + it "matches a new SimpleCov::ArrayFilter when a block is passed as array and returns true" do + expect(SimpleCov::ArrayFilter.new([proc { true }])).to be_matches subject + end + + it "doesn't match a new SimpleCov::ArrayFilter when a block that returns false is passed as array" do + expect(SimpleCov::ArrayFilter.new([proc { false }])).not_to be_matches subject + end + + it "matches a new SimpleCov::ArrayFilter when a custom class that returns true is passed as array" do + filter = Class.new(SimpleCov::Filter) do + def matches?(_) + true + end + end.new(nil) + expect(SimpleCov::ArrayFilter.new([filter])).to be_matches subject + end + + it "doesn't match a new SimpleCov::ArrayFilter when a custom class that returns false is passed as array" do + filter = Class.new(SimpleCov::Filter) do + def matches?(_) + false + end + end.new(nil) + expect(SimpleCov::ArrayFilter.new([filter])).not_to be_matches subject + end + context "with no filters set up and a basic source file in an array" do before do @prev_filters = SimpleCov.filters @@ -94,5 +154,19 @@ expect(SimpleCov.filtered(subject)).to be_a SimpleCov::FileList end end + + describe ".class_for_argument" do + it "returns SimpleCov::StringFilter for a string" do + expect(SimpleCov::Filter.class_for_argument("filestring")).to eq(SimpleCov::StringFilter) + end + + it "returns SimpleCov::RegexFilter for a string" do + expect(SimpleCov::Filter.class_for_argument(/regex/)).to eq(SimpleCov::RegexFilter) + end + + it "returns SimpleCov::RegexFilter for a string" do + expect(SimpleCov::Filter.class_for_argument(%w[file1 file2])).to eq(SimpleCov::ArrayFilter) + end + end end end diff --git a/spec/source_file_spec.rb b/spec/source_file_spec.rb index 2bebff63..c8809786 100644 --- a/spec/source_file_spec.rb +++ b/spec/source_file_spec.rb @@ -16,6 +16,10 @@ expect(subject.src).to eq(subject.source) end + it "has a project filename which removes the project directory" do + expect(subject.project_filename).to eq("/spec/fixtures/sample.rb") + end + it "has source_lines equal to lines" do expect(subject.lines).to eq(subject.source_lines) end