Skip to content

Commit

Permalink
Add rubocop section (#145)
Browse files Browse the repository at this point in the history
  • Loading branch information
chubchenko authored Jan 11, 2020
1 parent 5e0fe57 commit 3a32a9d
Show file tree
Hide file tree
Showing 14 changed files with 311 additions and 30 deletions.
7 changes: 7 additions & 0 deletions lib/inquisition/outputter/doc/tpl/quality.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,17 @@ def rubycritic
Template.new('quality/rubycritic').render(Rubycritic.call(@issues))
end
end

def rubocop
@rubocop ||= begin
Template.new('quality/rubocop').render(Rubocop.call(@issues))
end
end
end
end
end
end
end

require_relative 'quality/rubycritic'
require_relative 'quality/rubocop'
59 changes: 59 additions & 0 deletions lib/inquisition/outputter/doc/tpl/quality/rubocop.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
module Inquisition
module Outputter
class Doc
module TPL
class Quality
class Rubocop
def self.call(issues)
new(
Security::Collector.new(issues, ::Inquisition::Rubocop::Runner).call
)
end

EXTENSIONS = %w[rubocop-rspec rubocop-rails rubocop-performance rubocop-rake rubocop-md].freeze
attr_reader :collection

def initialize(collection)
@collection = collection
end

def extensions
EXTENSIONS.each do |gem|
next if locked_gems.key?(gem)

extension = TPL::Stack::Collector.new([gem]).call.first
yield(extension&.name || gem, extension&.homepage)
end
end

def produce
binding
end

def autocorrect_issues
collection.group_by(&:context).fetch(:corrected, [])
end

def inspect_files
collection&.first&.runner&.modified_runner&.target_files || []
end

def contains_rubocop
locked_gems.key?('rubocop') ? 'present' : 'absent'
end

def link
@link ||= Stack::Collector.new(['rubocop']).call.first.homepage
end

private

def locked_gems
@locked_gems ||= ::Bundler.locked_gems.dependencies
end
end
end
end
end
end
end
3 changes: 2 additions & 1 deletion lib/inquisition/rubocop/issue.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ def to_h
category: Categorizer.find_category(offense.cop_name),
path: file,
message: offense.message,
line: offense.line
line: offense.line,
context: offense.status
}
end

Expand Down
10 changes: 10 additions & 0 deletions lib/inquisition/rubocop/offense.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module Inquisition
module Rubocop
class Offense < ::RuboCop::Cop::Team
def offenses(processed_source)
autocorrect_cops, other_cops = cops.partition(&:autocorrect?)
investigate(autocorrect_cops, processed_source).offenses + investigate(other_cops, processed_source).offenses
end
end
end
end
16 changes: 13 additions & 3 deletions lib/inquisition/rubocop/rubocop_modified_runner.rb
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
require_relative 'offense'

module Inquisition
module Rubocop
class RubocopModifiedRunner < ::RuboCop::Runner
attr_reader :issues
attr_reader :issues, :target_files

def initialize(options, config_store)
super
@options = { auto_correct: true }
@issues = []
end

def run(paths)
target_files = find_target_files(paths)
@target_files = find_target_files(paths)
inspect_files(target_files)
end

def process_file(file)
file_started(file)
offenses = file_offenses(file)
offenses = file_offense_with_autocorrect(file)
@issues << { ::RuboCop::PathUtil.relative_path(file, Rails.root.to_s) => offenses } if offenses.any?
offenses
end

def file_offense_with_autocorrect(file)
processed_source = get_processed_source(file)
config = @config_store.for(processed_source.path)
offenses = Offense.new(mobilized_cop_classes(config), config, @options).inspect_file(processed_source)
add_redundant_disables(file, offenses.compact.sort, processed_source)
end

def inspect_files(files)
inspected_files = []
each_inspected_file(files) { |file| inspected_files << file }
Expand Down
5 changes: 4 additions & 1 deletion lib/inquisition/rubocop/runner.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
module Inquisition
module Rubocop
class Runner < ::Inquisition::Runner
attr_reader :modified_runner

def call
offenses = RubocopModifiedRunner.new({}, ::Inquisition::Rubocop.configuration).run([Rails.root.to_s])
@modified_runner = RubocopModifiedRunner.new({}, ::Inquisition::Rubocop.configuration)
offenses = modified_runner.run([Rails.root.to_s])
offenses.each { |offense| create_issues(offense) }
@issues
end
Expand Down
149 changes: 149 additions & 0 deletions spec/inquisition/outputter/doc/tpl/quality/rubocop_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
RSpec.describe Inquisition::Outputter::Doc::TPL::Quality::Rubocop do
include_examples 'produceable' do
subject(:tpl) { described_class.new([]) }
end

let(:issue) do
Inquisition::Issue.new(
category: Inquisition::Category::SECURITY,
path: 'app/controllers/users_controller.rb',
line: 42,
severity: Inquisition::Severity::LOW,
message: 'Metrics/LineLength: Line is too long. [132/125]',
context: :unsupported,
runner: Inquisition::Rubocop::Runner.new
)
end

describe '.call' do
let(:issue) { instance_double(Inquisition::Issue) }
let(:collector) { instance_double(Inquisition::Outputter::Doc::TPL::Security::Collector) }

before do
allow(Inquisition::Outputter::Doc::TPL::Security::Collector).to receive(:new).and_return(collector)
allow(collector).to receive(:call).and_return([issue])
allow(described_class).to receive(:new)

described_class.call([issue])
end

it do
expect(Inquisition::Outputter::Doc::TPL::Security::Collector).to have_received(:new)
.with([issue], ::Inquisition::Rubocop::Runner)
end

it { expect(described_class).to have_received(:new) }
end

describe 'link' do
subject(:link) { described_class.new([]).link }

let(:stack) { instance_double(Inquisition::Outputter::Doc::TPL::Stack::Collector) }
let(:struct) { double(OpenStruct, homepage: 'https://github.com/rubocop-hq/rubocop') }

before do
allow(Inquisition::Outputter::Doc::TPL::Stack::Collector).to receive(:new).with(['rubocop']).and_return(stack)
allow(stack).to receive(:call).and_return([struct])
end

it do
expect(link).to eq(struct.homepage)
end
end

describe '#extensions' do
subject(:rubocop) { described_class.new([]) }

let(:bundler_gems) { instance_double(Bundler::LockfileParser, dependencies: dependency) }
let(:dependency) do
{ 'rubocop-rspec' => '', 'rubocop-performance' => '', 'rubocop-rake' => '', 'rubocop-md' => '' }
end
let(:absent_gem) { 'rubocop-rails' }
let(:stack_collector) { instance_double(Inquisition::Outputter::Doc::TPL::Stack::Collector) }
let(:struct) { double(OpenStruct, name: absent_gem, homepage: 'https://github.com/rubocop-hq/rubocop-rails') }

before do
allow(Bundler).to receive(:locked_gems).and_return(bundler_gems)
allow(Inquisition::Outputter::Doc::TPL::Stack::Collector).to receive(:new)
.with([absent_gem]).and_return(stack_collector)
allow(stack_collector).to receive(:call).and_return([struct])
end

it do
expect do |block|
rubocop.extensions(&block)
end.to yield_with_args(absent_gem, struct.homepage)
end
end

describe '#inspect_files' do
subject(:inspect_files) { described_class.new(issues).inspect_files }

let(:instance_modified_runner) do
instance_double(Inquisition::Rubocop::RubocopModifiedRunner,
target_files: ['app/controllers/application_controller.rb'])
end

context 'when collection exists' do
let(:issues) { [issue] }

before { allow(issue.runner).to receive(:modified_runner).and_return(instance_modified_runner) }

it { expect(inspect_files).to eq(instance_modified_runner.target_files) }
end

context 'when collection not exists' do
let(:issues) { [] }

it { expect(inspect_files).to eq([]) }
end
end

describe '#contains_rubocop' do
subject(:contains_rubocop) { described_class.new([]).contains_rubocop }

let(:bundler_gems) { instance_double(Bundler::LockfileParser, dependencies: dependency) }

before { allow(Bundler).to receive(:locked_gems).and_return(bundler_gems) }

context 'when gem present' do
let(:dependency) { { 'rubocop' => '' } }

it { expect(contains_rubocop).to eq('present') }
end

context 'when gem absent' do
let(:dependency) { {} }

it { expect(contains_rubocop).to eq('absent') }
end
end

describe '#autocorrect_issues' do
subject(:autocorrect_issues) { described_class.new(issues).autocorrect_issues }

context 'when collection exists' do
let(:issues) { [issue, issue_autocorrect] }

let(:issue_autocorrect) do
Inquisition::Issue.new(
category: Inquisition::Category::SECURITY,
path: 'app/controllers/users_controller.rb',
line: 42,
severity: Inquisition::Severity::LOW,
message: 'Metrics/LineLength: Line is too long. [132/125]',
context: :corrected,
runner: Inquisition::Rubocop::Runner.new
)
end

it { expect(autocorrect_issues).to eq([issue_autocorrect]) }
end

context 'when collection not exists' do
let(:issues) { [] }

it { expect(autocorrect_issues).to eq([]) }
end
end
end
6 changes: 4 additions & 2 deletions spec/inquisition/rubocop/issue_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
message: 'Style/WordArray: Use `%w` or `%W` for an array of words.',
path: 'db/schema.rb',
line: 21,
category: Inquisition::Category::STYLE
category: Inquisition::Category::STYLE,
context: :unsupported
}
end

Expand All @@ -18,7 +19,8 @@
severity: rubocop_severity,
message: options[:message],
line: options[:line],
cop_name: 'Style/WordArray'
cop_name: 'Style/WordArray',
status: :unsupported
)
end

Expand Down
19 changes: 19 additions & 0 deletions spec/inquisition/rubocop/offense_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
RSpec.describe Inquisition::Rubocop::Offense do
let(:offense) { described_class.new(registry, config, {}).offenses(processed_source) }

let(:config) { instance_double(RuboCop::Config) }
let(:registry) { instance_double(RuboCop::Cop::Registry) }
let(:processed_source) { instance_double(RuboCop::ProcessedSource) }

it { expect(described_class).to be < RuboCop::Cop::Team }

describe '#offenses' do
before { allow(registry).to receive(:enabled).and_return([]) }

context 'when valid_syntax? for processed_source returns true' do
it 'returns offenses' do
expect(offense).to be_empty
end
end
end
end
5 changes: 3 additions & 2 deletions spec/inquisition/rubocop/rubocop_modified_runner_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,19 @@
let(:issues) { ['issue file', ['issue body']] }
let(:offense) { instance_double(RuboCop::Cop::Offense) }
let(:offenses) { [{ file => [offense] }] }
let(:processed_source) { instance_double(RuboCop::ProcessedSource) }

before do
allow(runner).to receive(:find_target_files).and_return(target_files)
allow(runner).to receive(:file_offenses).with("#{Rails.root}/#{file}").and_return([offense])
allow(runner).to receive(:file_offense_with_autocorrect).with("#{Rails.root}/#{file}").and_return([offense])
allow(offense).to receive(:disabled?).and_return([offense])
allow(runner).to receive(:file_started).with("#{Rails.root}/#{file}")
end

it 'returns offenses' do
expect(runner.run([Rails.root])).to eq(offenses)
expect(runner).to have_received(:find_target_files)
expect(runner).to have_received(:file_offenses).with("#{Rails.root}/#{file}")
expect(runner).to have_received(:file_offense_with_autocorrect).with("#{Rails.root}/#{file}")
expect(offense).to have_received(:disabled?)
expect(runner).to have_received(:file_started).with("#{Rails.root}/#{file}")
end
Expand Down
Loading

0 comments on commit 3a32a9d

Please sign in to comment.