From a0b14a17e78609929eb4e6a9888de3ef9775495d Mon Sep 17 00:00:00 2001 From: Jonathan Steel Date: Fri, 16 Jun 2017 14:34:05 -0400 Subject: [PATCH 1/7] Allow for regular expression filters Fixes #514 --- lib/simplecov/configuration.rb | 6 ++---- lib/simplecov/defaults.rb | 4 ++-- lib/simplecov/filter.rb | 20 ++++++++++++++++++++ lib/simplecov/source_file.rb | 6 ++++++ spec/filters_spec.rb | 30 ++++++++++++++++++++++++++++++ spec/source_file_spec.rb | 4 ++++ 6 files changed, 64 insertions(+), 6 deletions(-) diff --git a/lib/simplecov/configuration.rb b/lib/simplecov/configuration.rb index b4308cbf..4f709c49 100644 --- a/lib/simplecov/configuration.rb +++ b/lib/simplecov/configuration.rb @@ -293,12 +293,10 @@ def add_group(group_name, filter_argument = nil, &filter_proc) 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) + elsif filter_argument + SimpleCov::Filter.class_for_argument(filter_argument).new(filter_argument) else raise ArgumentError, "Please specify either a string or a block to filter with" 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..9b2bfaac 100644 --- a/lib/simplecov/filter.rb +++ b/lib/simplecov/filter.rb @@ -24,6 +24,18 @@ def passes?(source_file) warn "#{Kernel.caller.first}: [DEPRECATION] #passes? is deprecated. Use #matches? instead." matches?(source_file) 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 + else + raise ArgumentError, "You have provided an unrecognized filter type" + end + end end class StringFilter < SimpleCov::Filter @@ -34,6 +46,14 @@ def matches?(source_file) 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 + class BlockFilter < SimpleCov::Filter # Returns true if the block given when initializing this filter with BlockFilter.new {|src_file| ... } # returns true for the given source file. diff --git a/lib/simplecov/source_file.rb b/lib/simplecov/source_file.rb index 37b6c947..abe4875a 100644 --- a/lib/simplecov/source_file.rb +++ b/lib/simplecov/source_file.rb @@ -80,6 +80,12 @@ def initialize(filename, coverage) @coverage = coverage end + # The path to this source file relative to the projects directory + def project_filename + project_dir = File.dirname(File.expand_path(File.basename(__FILE__))) + @filename.sub(/^#{project_dir}/, "") + 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..ed81b60a 100644 --- a/spec/filters_spec.rb +++ b/spec/filters_spec.rb @@ -26,6 +26,22 @@ expect(SimpleCov::StringFilter.new("sample.rb")).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 @@ -94,5 +110,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 From ddc1008064d135f6a910e75249c4e6a422de4990 Mon Sep 17 00:00:00 2001 From: Jonathan Steel Date: Fri, 16 Jun 2017 15:21:13 -0400 Subject: [PATCH 2/7] The parent directories should not match filters --- lib/simplecov/filter.rb | 4 ++-- spec/filters_spec.rb | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/simplecov/filter.rb b/lib/simplecov/filter.rb index 9b2bfaac..cb0f17ed 100644 --- a/lib/simplecov/filter.rb +++ b/lib/simplecov/filter.rb @@ -42,7 +42,7 @@ 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 @@ -67,7 +67,7 @@ class ArrayFilter < SimpleCov::Filter # configured when initializing this Filter with StringFilter.new(['some/path', 'other/path']) def matches?(source_files_list) filter_argument.any? do |arg| - source_files_list.filename =~ /#{arg}/ + source_files_list.project_filename =~ /#{arg}/ end end end diff --git a/spec/filters_spec.rb b/spec/filters_spec.rb index ed81b60a..c3787531 100644 --- a/spec/filters_spec.rb +++ b/spec/filters_spec.rb @@ -26,6 +26,11 @@ 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 @@ -62,6 +67,11 @@ 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 + context "with no filters set up and a basic source file in an array" do before do @prev_filters = SimpleCov.filters From 5f88bb55c996709ce5878db632935d1906b10225 Mon Sep 17 00:00:00 2001 From: Jonathan Steel Date: Mon, 19 Jun 2017 15:38:24 -0400 Subject: [PATCH 3/7] Allow array filters to accept any type --- README.md | 23 ++++++++++++++++++++++- lib/simplecov/configuration.rb | 10 +++++----- lib/simplecov/filter.rb | 20 +++++++++++++++++--- 3 files changed, 44 insertions(+), 9 deletions(-) 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/lib/simplecov/configuration.rb b/lib/simplecov/configuration.rb index 4f709c49..6e34df8b 100644 --- a/lib/simplecov/configuration.rb +++ b/lib/simplecov/configuration.rb @@ -291,14 +291,14 @@ 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) + filter = filter_argument || filter_proc + if filter_argument.is_a?(SimpleCov::Filter) filter_argument - elsif filter_proc - SimpleCov::BlockFilter.new(filter_proc) - elsif filter_argument - SimpleCov::Filter.class_for_argument(filter_argument).new(filter_argument) + elsif filter + SimpleCov::Filter.class_for_argument(filter).new(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/filter.rb b/lib/simplecov/filter.rb index cb0f17ed..f0f53b7c 100644 --- a/lib/simplecov/filter.rb +++ b/lib/simplecov/filter.rb @@ -32,6 +32,8 @@ def self.class_for_argument(filter_argument) 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 @@ -63,11 +65,23 @@ 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_argument.map! do |arg| + if arg.is_a?(SimpleCov::Filter) + arg + else + Filter.class_for_argument(arg).new(arg) + end + end + + super(filter_argument) + 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.project_filename =~ /#{arg}/ + arg.matches?(source_files_list) end end end From e31bce9ed67836253186303fbc090ed50595e9dd Mon Sep 17 00:00:00 2001 From: Jonathan Steel Date: Wed, 21 Jun 2017 09:06:50 -0400 Subject: [PATCH 4/7] Note about sublime integration --- doc/editor-integration.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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. From 7250a700e931462f6f9abb054512c4f93100b858 Mon Sep 17 00:00:00 2001 From: Jonathan Steel Date: Wed, 21 Jun 2017 09:46:33 -0400 Subject: [PATCH 5/7] Avoid clobbering passed in arguments and minor cleanup --- lib/simplecov/configuration.rb | 6 ++---- lib/simplecov/filter.rb | 11 +++++++++-- spec/filters_spec.rb | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/lib/simplecov/configuration.rb b/lib/simplecov/configuration.rb index 6e34df8b..ce36929c 100644 --- a/lib/simplecov/configuration.rb +++ b/lib/simplecov/configuration.rb @@ -293,10 +293,8 @@ def add_group(group_name, filter_argument = nil, &filter_proc) def parse_filter(filter_argument = nil, &filter_proc) filter = filter_argument || filter_proc - if filter_argument.is_a?(SimpleCov::Filter) - filter_argument - elsif filter - SimpleCov::Filter.class_for_argument(filter).new(filter) + if filter + SimpleCov::Filter.build_filter(filter) else raise ArgumentError, "Please specify either a filter or a block to filter with" end diff --git a/lib/simplecov/filter.rb b/lib/simplecov/filter.rb index f0f53b7c..5fbfd6bc 100644 --- a/lib/simplecov/filter.rb +++ b/lib/simplecov/filter.rb @@ -25,7 +25,14 @@ def passes?(source_file) 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) + return filter_argument if filter_argument.is_a?(SimpleCov::Filter) + if filter_argument.is_a?(String) SimpleCov::StringFilter elsif filter_argument.is_a?(Regexp) @@ -66,7 +73,7 @@ def matches?(source_file) class ArrayFilter < SimpleCov::Filter def initialize(filter_argument) - filter_argument.map! do |arg| + filter_objects = filter_argument.map do |arg| if arg.is_a?(SimpleCov::Filter) arg else @@ -74,7 +81,7 @@ def initialize(filter_argument) end end - super(filter_argument) + super(filter_objects) end # Returns true if any of the filters in the array match the given source file. diff --git a/spec/filters_spec.rb b/spec/filters_spec.rb index c3787531..6cdaa84b 100644 --- a/spec/filters_spec.rb +++ b/spec/filters_spec.rb @@ -72,6 +72,40 @@ 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 From b4ca146d9a2570526d4ca0bc68dd5fab2068513b Mon Sep 17 00:00:00 2001 From: Jonathan Steel Date: Wed, 21 Jun 2017 11:28:23 -0400 Subject: [PATCH 6/7] Use the refactored code in all cases --- lib/simplecov/filter.rb | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/simplecov/filter.rb b/lib/simplecov/filter.rb index 5fbfd6bc..3acc4f29 100644 --- a/lib/simplecov/filter.rb +++ b/lib/simplecov/filter.rb @@ -31,8 +31,6 @@ def self.build_filter(filter_argument) end def self.class_for_argument(filter_argument) - return filter_argument if filter_argument.is_a?(SimpleCov::Filter) - if filter_argument.is_a?(String) SimpleCov::StringFilter elsif filter_argument.is_a?(Regexp) @@ -74,11 +72,7 @@ def matches?(source_file) class ArrayFilter < SimpleCov::Filter def initialize(filter_argument) filter_objects = filter_argument.map do |arg| - if arg.is_a?(SimpleCov::Filter) - arg - else - Filter.class_for_argument(arg).new(arg) - end + Filter.build_filter(arg) end super(filter_objects) From cc8b81d712cfb987cde3736ec82e90b169be845f Mon Sep 17 00:00:00 2001 From: Jonathan Steel Date: Thu, 22 Jun 2017 10:28:28 -0400 Subject: [PATCH 7/7] Fix project path to use the SimpleCov.root --- lib/simplecov/source_file.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/simplecov/source_file.rb b/lib/simplecov/source_file.rb index abe4875a..33eb9fb1 100644 --- a/lib/simplecov/source_file.rb +++ b/lib/simplecov/source_file.rb @@ -82,8 +82,7 @@ def initialize(filename, coverage) # The path to this source file relative to the projects directory def project_filename - project_dir = File.dirname(File.expand_path(File.basename(__FILE__))) - @filename.sub(/^#{project_dir}/, "") + @filename.sub(/^#{SimpleCov.root}/, "") end # The source code for this file. Aliased as :source