-
Notifications
You must be signed in to change notification settings - Fork 24
Tune remediation points to match Classic #61
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,10 @@ class Base | |
::RubyParser::SyntaxError, | ||
] | ||
|
||
DEFAULT_MASS_THRESHOLD = 28 | ||
BASE_POINTS = 1_500_000 | ||
POINTS_PER = 50_000 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gordondiggs I'm slightly worried that using inferred constants makes the code harder to follow for future devs. Now that you see it, any doubts? If you're not worried, I'm not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it's any more confusing than using the inherited methods, which I don't find confusing at all. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay cool. Thanks! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think of I didn't find the current name intuitive. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds okay to me. Only arguments to consider against:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That works for me 👍 |
||
|
||
def initialize(engine_config:) | ||
@engine_config = engine_config | ||
end | ||
|
@@ -37,6 +41,10 @@ def base_points | |
self.class::BASE_POINTS | ||
end | ||
|
||
def points_per | ||
self.class::POINTS_PER | ||
end | ||
|
||
private | ||
|
||
attr_reader :engine_config | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,8 +8,12 @@ module Analyzers | |
class Violation | ||
attr_reader :issue | ||
|
||
def initialize(base_points, issue, hashes) | ||
@base_points = base_points | ||
DEFAULT_POINTS = 1_500_000 | ||
|
||
def initialize(language, issue, hashes) | ||
@base_points = language.base_points | ||
@points_per = language.points_per | ||
@threshold = language.mass_threshold | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's fine to assign the whole strategy here and call methods on the strategy elsewhere in the class. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call |
||
@issue = issue | ||
@hashes = hashes | ||
end | ||
|
@@ -32,9 +36,17 @@ def report_name | |
"#{current_sexp.file}-#{current_sexp.line}" | ||
end | ||
|
||
def calculate_points | ||
if issue.mass >= threshold | ||
base_points + (overage * points_per) | ||
else | ||
DEFAULT_POINTS | ||
end | ||
end | ||
|
||
private | ||
|
||
attr_reader :base_points, :hashes | ||
attr_reader :base_points, :points_per, :threshold, :hashes | ||
|
||
def current_sexp | ||
@location ||= sorted_hashes.first | ||
|
@@ -56,8 +68,8 @@ def name | |
end | ||
end | ||
|
||
def calculate_points | ||
base_points * issue.mass | ||
def overage | ||
issue.mass - threshold | ||
end | ||
|
||
def format_location | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
RSpec.describe CC::Engine::Analyzers::Violation, in_tmpdir: true do | ||
include AnalyzerSpecHelpers | ||
|
||
describe "#calculate_points" do | ||
context "when issue mass exceeds threshold" do | ||
it "calculates mass overage points" do | ||
language = stub_language(base_points: 100, points_per: 5, mass_threshold: 10) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think of changing the name to strategy = double(base_points: 100, points_per: 5, mass_threshold: 10) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that much better. Thanks! |
||
issue = double(:issue, mass: 15) | ||
hashes = [] | ||
|
||
expected_points = 100 + 5 * (issue.mass - 10) | ||
|
||
violation = CC::Engine::Analyzers::Violation.new(language, issue, hashes) | ||
points = violation.calculate_points | ||
|
||
expect(points).to eq(expected_points) | ||
end | ||
end | ||
|
||
context "when issue mass is less than threshold" do | ||
it "uses default" do | ||
language = stub_language(base_points: 100, points_per: 5, mass_threshold: 18) | ||
issue = double(:issue, mass: 15) | ||
hashes = [] | ||
|
||
expected_points = CC::Engine::Analyzers::Violation::DEFAULT_POINTS | ||
|
||
violation = CC::Engine::Analyzers::Violation.new(language, issue, hashes) | ||
points = violation.calculate_points | ||
|
||
expect(points).to eq(CC::Engine::Analyzers::Violation::DEFAULT_POINTS) | ||
end | ||
end | ||
|
||
context "when issue mass equals threshold" do | ||
it "calculates remediation points" do | ||
language = stub_language(base_points: 100, points_per: 5, mass_threshold: 18) | ||
issue = double(:issue, mass: language.mass_threshold) | ||
hashes = [] | ||
|
||
expected_points = 100 + 5 * (issue.mass - language.mass_threshold) | ||
|
||
violation = CC::Engine::Analyzers::Violation.new(language, issue, hashes) | ||
points = violation.calculate_points | ||
|
||
expect(points).to eq(expected_points) | ||
end | ||
end | ||
|
||
def stub_language(args) | ||
double( | ||
:language, | ||
base_points: args[:base_points], | ||
points_per: args[:points_per], | ||
mass_threshold: args[:mass_threshold] | ||
) | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to clarify that this is intentional and vetted: previously these were different between PHP & Python/Javascript, and this change makes them the same, but keeps Ruby different.
The previous values were, I believe, picked after a lot of manual testing to get something that felt "right". What the "right" default threshold choice is for a language is dependent on how the parser the engines uses for that language ends up expressing ASTs. I.e. small parser differences can change how verbose a mass threshold ends up seeming.
Based on the classic code, it looks like this value was taken directly from there (and it didn't apply to Python in classic). I'd personally be pretty surprised if the AST results of all the languages was the same between classic & this engine: at the very least I'm pretty sure the JS parser behavior is different.
For that reason, I'm not sure the classic thresholds will make sense here, because they are likely to produce different results here than they did in classic. Do you have a sample corpus you've been using to verify same results for each of these languages between classic/these changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wfleming Thanks for the tip about the thought behind the thresholds and parsing. I can investigate that.
I reference the classic rubric you mentioned in my commit, along with the Python source.
I've been keeping track of test repos
here. (removed link)In general, it seemed that on classic, duplication and complexity issues had much greater grade impact than they did on Platform.
I tested the current duplication setup on
app
- but could do some further testing with this exact setup on additional repos.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it's a question of effort vs accuracy, but if the intent is to get results similar to classic, we should probably have a small corpus of files for each language, get the overall mass of each file as calculated both by classic & by this engine, and compare those to see if the thresholds (and to a lesser extent
per_point
values) need to be scaled to get matching results.