Skip to content

add count_threshold option #142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 7, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ engines:

This will tell the duplication engine to analyze Ruby and JavaScript files.

### Threshold
### Mass Threshold

We set useful threshold defaults for the languages we support but you may want
to adjust these settings based on your project guidelines.

The threshold configuration represents the minimum "mass" a code block must have
to be analyzed for duplication. If the engine is too easily reporting
The mass threshold configuration represents the minimum "mass" a code block must
have to be analyzed for duplication. If the engine is too easily reporting
duplication, try raising the threshold. If you suspect that the engine isn't
catching enough duplication, try lowering the threshold. The best setting tends
to differ from language to language.
Expand All @@ -68,6 +68,29 @@ engines:
Note that you have the update the YAML structure under the `languages` key to
the Hash type to support extra configuration.

### Count Threshold

By default, the duplication engine will report code that has been duplicated in just two locations. You can be less strict by only raising a warning if code is duplicated in three or more locations only. To adjust this setting, add a `count_threshold` key to your config. For instance, to use the default `mass_threshold` for ruby, but to enforce the [Rule of Three][rule-of-three], you could use this configuration:

```yaml
engines:
duplication:
enabled: true
config:
languages:
ruby:
count_threshold: 3
```

You can also change the default count_threshold for all languages:

```yaml
engines:
duplication:
enabled: true
count_threshold: 3
```

### Excluding files and directories

As with any other Code Climate engine, you can exclude certain files or
Expand All @@ -86,4 +109,5 @@ engines:
[what-is-duplication]: https://docs.codeclimate.com/docs/duplication-concept
[flay]: https://github.com/seattlerb/flay
[cli]: https://github.com/codeclimate/codeclimate
[rule-of-three]: https://en.wikipedia.org/wiki/Rule_of_three_(computer_programming)
[exclude-files-engine]: https://docs.codeclimate.com/docs/excluding-files-and-folders#section-exclude-paths-for-specific-engines
4 changes: 4 additions & 0 deletions lib/cc/engine/analyzers/analyzer_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ def mass_threshold
engine_config.mass_threshold_for(self.class::LANGUAGE) || self.class::DEFAULT_MASS_THRESHOLD
end

def count_threshold
engine_config.count_threshold_for(self.class::LANGUAGE)
end

def calculate_points(mass)
overage = mass - mass_threshold
base_points + (overage * points_per_overage)
Expand Down
12 changes: 12 additions & 0 deletions lib/cc/engine/analyzers/engine_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module CC
module Engine
module Analyzers
class EngineConfig
DEFAULT_COUNT_THRESHOLD = 2

def initialize(hash)
@config = normalize(hash)
end
Expand Down Expand Up @@ -30,6 +32,16 @@ def mass_threshold_for(language)
end
end

def count_threshold_for(language)
threshold = fetch_language(language)["count_threshold"] ||
config.fetch("config", {}).fetch("count_threshold", nil) ||
DEFAULT_COUNT_THRESHOLD

if threshold
threshold.to_i
end
end

def fetch_language(language)
language = config.
fetch("languages", {}).
Expand Down
1 change: 1 addition & 0 deletions lib/cc/engine/analyzers/reporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def report
violations = new_violations(issue)

violations.each do |violation|
next if (violation.occurrences + 1) < language_strategy.count_threshold
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here's where the magic happens

debug("Violation name=#{violation.report_name} mass=#{violation.mass}")

unless reports.include?(violation.report_name)
Expand Down
8 changes: 4 additions & 4 deletions lib/cc/engine/analyzers/violation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ def mass
current_sexp.mass
end

def occurrences
other_sexps.count
end

private

attr_reader :language_strategy, :other_sexps, :current_sexp
Expand Down Expand Up @@ -96,10 +100,6 @@ def description
description += " (mass = #{mass})"
description
end

def occurrences
other_sexps.count
end
end
end
end
Expand Down
41 changes: 41 additions & 0 deletions spec/cc/engine/analyzers/engine_config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,47 @@
end
end

describe "count_threshold_for" do
it "returns configured count threshold as integer" do
engine_config = CC::Engine::Analyzers::EngineConfig.new({
"config" => {
"languages" => {
"EliXiR" => {
"count_threshold" => "3",
},
},
},
})

expect(engine_config.count_threshold_for("elixir")).to eq(3)
end

it "returns default value when language value is empty" do
engine_config = CC::Engine::Analyzers::EngineConfig.new({
"config" => {
"count_threshold" => "4",
"languages" => {
"ruby" => "",
},
},
})

expect(engine_config.count_threshold_for("ruby")).to eq(4)
end

it "returns 2 by default" do
engine_config = CC::Engine::Analyzers::EngineConfig.new({
"config" => {
"languages" => {
"ruby" => "",
},
},
})

expect(engine_config.count_threshold_for("ruby")).to eq(2)
end
end

describe "include_paths" do
it "returns given include paths" do
engine_config = CC::Engine::Analyzers::EngineConfig.new({
Expand Down