Skip to content

Commit a9af216

Browse files
author
Ashley Baldwin-Hunter
committed
Merge pull request #83 from codeclimate/abh-pb-multi-issues
Report issue for each occurrence of duplication
2 parents 4e3426e + 2c7162b commit a9af216

File tree

8 files changed

+201
-29
lines changed

8 files changed

+201
-29
lines changed

lib/cc/engine/analyzers/reporter.rb

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
require 'cc/engine/analyzers/violation'
1+
require 'cc/engine/analyzers/violations'
22
require 'cc/engine/analyzers/file_thread_pool'
33
require 'thread'
44

@@ -36,11 +36,13 @@ def process_files
3636

3737
def report
3838
flay.report(StringIO.new).each do |issue|
39-
violation = new_violation(issue)
39+
violations = new_violations(issue)
4040

41-
unless reports.include?(violation.report_name)
42-
reports.add(violation.report_name)
43-
io.puts "#{violation.format.to_json}\0"
41+
violations.each do |violation|
42+
unless reports.include?(violation.report_name)
43+
reports.add(violation.report_name)
44+
io.puts "#{violation.format.to_json}\0"
45+
end
4446
end
4547
end
4648
end
@@ -60,9 +62,9 @@ def flay
6062

6163
attr_reader :engine_config, :language_strategy, :io
6264

63-
def new_violation(issue)
65+
def new_violations(issue)
6466
hashes = flay.hashes[issue.structural_hash]
65-
Violation.new(language_strategy, issue, hashes)
67+
Violations.new(language_strategy, issue, hashes)
6668
end
6769

6870
def flay_options

lib/cc/engine/analyzers/violation.rb

+4-15
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ module Analyzers
88
class Violation
99
attr_reader :issue
1010

11-
def initialize(language_strategy, issue, hashes)
11+
def initialize(language_strategy:, issue:, current_sexp:, other_sexps:)
1212
@language_strategy = language_strategy
1313
@issue = issue
14-
@hashes = hashes
14+
@current_sexp = current_sexp
15+
@other_sexps = other_sexps
1516
end
1617

1718
def format
@@ -34,19 +35,7 @@ def report_name
3435

3536
private
3637

37-
attr_reader :language_strategy, :hashes
38-
39-
def current_sexp
40-
@location ||= sorted_hashes.first
41-
end
42-
43-
def sorted_hashes
44-
@_sorted_hashes ||= hashes.sort_by(&:file)
45-
end
46-
47-
def other_sexps
48-
@other_locations ||= sorted_hashes.drop(1)
49-
end
38+
attr_reader :language_strategy, :other_sexps, :current_sexp
5039

5140
def check_name
5241
if issue.identical?

lib/cc/engine/analyzers/violations.rb

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
require "cc/engine/analyzers/violation"
2+
3+
module CC
4+
module Engine
5+
module Analyzers
6+
class Violations
7+
def initialize(language_strategy, issue, hashes)
8+
@language_strategy = language_strategy
9+
@issue = issue
10+
@hashes = hashes
11+
end
12+
13+
def each
14+
hashes.each_with_index do |sexp, i|
15+
yield Violation.new(
16+
current_sexp: sexp,
17+
other_sexps: other_sexps(hashes.dup, i),
18+
issue: issue,
19+
language_strategy: language_strategy,
20+
)
21+
end
22+
end
23+
24+
private
25+
26+
attr_reader :language_strategy, :issue, :hashes
27+
28+
def other_sexps(members, i)
29+
members.delete_at(i)
30+
members.sort_by(&:file)
31+
end
32+
end
33+
end
34+
end
35+
end

spec/cc/engine/analyzers/javascript/main_spec.rb

+4-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
console.log("hello JS!");
1717
EOJS
1818

19-
result = run_engine(engine_conf).strip
19+
issues = run_engine(engine_conf).strip.split("\0")
20+
result = issues.first.strip
2021
json = JSON.parse(result)
2122

2223
expect(json["type"]).to eq("issue")
@@ -43,7 +44,8 @@
4344
console.log("helllllllllllllllllo JS!");
4445
EOJS
4546

46-
result = run_engine(engine_conf).strip
47+
issues = run_engine(engine_conf).strip.split("\0")
48+
result = issues.first.strip
4749
json = JSON.parse(result)
4850

4951
expect(json["type"]).to eq("issue")

spec/cc/engine/analyzers/php/main_spec.rb

+4-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
}
3030
EOPHP
3131

32-
result = run_engine(engine_conf).strip
32+
issues = run_engine(engine_conf).strip.split("\0")
33+
result = issues.first.strip
3334
json = JSON.parse(result)
3435

3536
expect(json["type"]).to eq("issue")
@@ -50,7 +51,8 @@
5051

5152
it "runs against complex files" do
5253
FileUtils.cp(fixture_path("symfony_configuration.php"), File.join(@code, "configuration.php"))
53-
result = run_engine(engine_conf).strip
54+
issues = run_engine(engine_conf).strip.split("\0")
55+
result = issues.first.strip
5456

5557
expect(result).to match "\"type\":\"issue\""
5658
end

spec/cc/engine/analyzers/python/main_spec.rb

+4-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
print("Hello", "python")
1717
EOJS
1818

19-
result = run_engine(engine_conf).strip
19+
issues = run_engine(engine_conf).strip.split("\0")
20+
result = issues.first.strip
2021
json = JSON.parse(result)
2122

2223
expect(json["type"]).to eq("issue")
@@ -43,7 +44,8 @@
4344
print("Hello from the other side", "python")
4445
EOJS
4546

46-
result = run_engine(engine_conf).strip
47+
issues = run_engine(engine_conf).strip.split("\0")
48+
result = issues.first.strip
4749
json = JSON.parse(result)
4850

4951
expect(json["type"]).to eq("issue")

spec/cc/engine/analyzers/ruby/main_spec.rb

+34-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ module CC::Engine::Analyzers
2828
end
2929
EORUBY
3030

31-
result = run_engine(engine_conf).strip
31+
issues = run_engine(engine_conf).strip.split("\0")
32+
result = issues.first.strip
3233
json = JSON.parse(result)
3334

3435
expect(json["type"]).to eq("issue")
@@ -47,6 +48,38 @@ module CC::Engine::Analyzers
4748
expect(json["fingerprint"]).to eq("f21b75bbd135ec3ae6638364d5c73762")
4849
end
4950

51+
it "creates an issue for each occurrence of the duplicated code" do
52+
create_source_file("foo.rb", <<-EORUBY)
53+
describe '#ruby?' do
54+
before { subject.type = 'ruby' }
55+
56+
it 'returns true' do
57+
expect(subject.ruby?).to be true
58+
end
59+
end
60+
61+
describe '#js?' do
62+
before { subject.type = 'js' }
63+
64+
it 'returns true' do
65+
expect(subject.js?).to be true
66+
end
67+
end
68+
69+
describe '#whaddup?' do
70+
before { subject.type = 'js' }
71+
72+
it 'returns true' do
73+
expect(subject.js?).to be true
74+
end
75+
end
76+
EORUBY
77+
78+
issues = run_engine(engine_conf).strip.split("\0")
79+
80+
expect(issues.length).to eq(3)
81+
end
82+
5083
it "skips unparsable files" do
5184
create_source_file("foo.rb", <<-EORUBY)
5285
---
+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
require "spec_helper"
2+
require "cc/engine/duplication"
3+
4+
module CC::Engine::Analyzers
5+
RSpec.describe Violations do
6+
describe "#each" do
7+
it "yields correct number of violations" do
8+
issue = double(:issue, mass: 10, identical?: true)
9+
hashes = sexps
10+
language_strategy = double(:language_strategy, calculate_points: 30)
11+
12+
violations = []
13+
14+
Violations.new(language_strategy, issue, hashes).each do |v|
15+
violations << v
16+
end
17+
18+
expect(violations.length).to eq(3)
19+
end
20+
21+
it "yields violation objects with correct information" do
22+
issue = double(:issue, mass: 10, identical?: true)
23+
hashes = sexps
24+
language_strategy = double(:language_strategy, calculate_points: 30)
25+
26+
violations = []
27+
28+
Violations.new(language_strategy, issue, hashes).each do |v|
29+
violations << v
30+
end
31+
32+
first_formatted = violations[0].format
33+
second_formatted = violations[1].format
34+
third_formatted = violations[2].format
35+
36+
expect(first_formatted[:type]).to eq("issue")
37+
expect(first_formatted[:check_name]).to eq("Identical code")
38+
expect(first_formatted[:description]).to eq("Identical code found in 2 other locations")
39+
expect(first_formatted[:categories]).to eq(["Duplication"])
40+
expect(first_formatted[:remediation_points]).to eq(30)
41+
expect(first_formatted[:location]).to eq({:path=>"file.rb", :lines=>{:begin=>1, :end=>5}})
42+
expect(first_formatted[:other_locations]).to eq([
43+
{ :path => "file.rb", :lines => { :begin => 9, :end => 13} },
44+
{ :path => "file.rb", :lines => { :begin => 17, :end => 21} },
45+
])
46+
expect(first_formatted[:fingerprint]).to eq("c2712b56bff2becf4ae2a8469e1171c7")
47+
48+
expect(second_formatted[:location]).to eq({:path=>"file.rb", :lines=>{:begin=>9, :end=>13}})
49+
expect(second_formatted[:other_locations]).to eq([
50+
{ :path => "file.rb", :lines => { :begin => 1, :end => 5} },
51+
{ :path => "file.rb", :lines => { :begin => 17, :end => 21} },
52+
])
53+
54+
expect(third_formatted[:location]).to eq({:path=>"file.rb", :lines=>{:begin=>17, :end=>21}})
55+
expect(third_formatted[:other_locations]).to eq([
56+
{ :path => "file.rb", :lines => { :begin => 1, :end => 5} },
57+
{ :path => "file.rb", :lines => { :begin => 9, :end => 13} },
58+
])
59+
end
60+
61+
def sexps
62+
source = <<-SOURCE
63+
describe '#ruby?' do
64+
before { subject.type = 'ruby' }
65+
66+
it 'returns true' do
67+
expect(subject.ruby?).to be true
68+
end
69+
end
70+
71+
describe '#js?' do
72+
before { subject.type = 'js' }
73+
74+
it 'returns true' do
75+
expect(subject.js?).to be true
76+
end
77+
end
78+
79+
describe '#whaddup?' do
80+
before { subject.type = 'js' }
81+
82+
it 'returns true' do
83+
expect(subject.js?).to be true
84+
end
85+
end
86+
SOURCE
87+
88+
flay = Flay.new({
89+
diff: false,
90+
mass: CC::Engine::Analyzers::Ruby::Main::DEFAULT_MASS_THRESHOLD,
91+
summary: false,
92+
verbose: false,
93+
number: true,
94+
timeout: 10,
95+
liberal: false,
96+
fuzzy: false,
97+
only: nil,
98+
})
99+
100+
sexp = RubyParser.new.process(source, "file.rb")
101+
flay.process_sexp(sexp)
102+
report = flay.analyze[0]
103+
sexps = flay.hashes[report.structural_hash]
104+
end
105+
end
106+
end
107+
end

0 commit comments

Comments
 (0)