spec/cc/engine/rubocop_spec.rb (285 lines of code) (raw):

require "spec_helper" require "cc/engine/rubocop" require "tmpdir" module CC::Engine describe Rubocop do include FilesystemHelpers before { @code = Dir.mktmpdir } describe "#run" do it "analyzes ruby files using rubocop" do create_source_file("foo.rb", <<-EORUBY) def method unused = "x" return false end EORUBY output = run_engine expect(includes_check?(output, "Lint/UselessAssignment")).to be true end it "reads the configured ruby_style file" do create_source_file("foo.rb", <<-EORUBY) def method unused = "x" and "y" return false end EORUBY create_source_file( "rubocop.yml", "Lint/UselessAssignment:\n Enabled: false\n" ) config = { "config" => "rubocop.yml" } output = run_engine(config) expect(includes_check?(output, "Style/AndOr")).to be true expect(includes_check?(output, "Lint/UselessAssignment")).to be false end it "generates a fingerprint for method/class offenses" do create_source_file("foo.rb", <<-EORUBY) def method(a, b, c, d) x = Math.sqrt((a * a) + (b * b)) + Math.sqrt((a * a) + (b * b)) y = Math.sqrt((b * b) + (c * c)) + Math.sqrt((b * b) + (c * c)) z = Math.sqrt((c * c) + (d * d)) + Math.sqrt((c * c) + (d * d)) x + y + z end EORUBY output = run_engine expect(includes_check?(output, "Metrics/AbcSize")).to be true expect(includes_fingerprint?(output, "303630e0015ba1c6de300b983babac59")).to be true end it "respects the default .rubocop.yml file" do create_source_file("foo.rb", <<-EORUBY) def method unused = "x" and "y" return false end EORUBY create_source_file( ".rubocop.yml", "Lint/UselessAssignment:\n Enabled: false\n" ) output = run_engine expect(includes_check?(output, "Style/AndOr")).to be true expect(includes_check?(output, "Lint/UselessAssignment")).to be false end it "respects excludes in an inherit_from directive" do create_source_file("foo.rb", <<-EORUBY) def method unused = "x" return false end EORUBY create_source_file("bar.rb", <<-EORUBY) def method unused = 42 return true end EORUBY create_source_file( ".rubocop.yml", "inherit_from: .rubocop_todo.yml\nAllCops:\n DisabledByDefault: true\nLint/UselessAssignment:\n Enabled: true\n" ) create_source_file( ".rubocop_todo.yml", "Lint/UselessAssignment:\n Exclude:\n - bar.rb\n" ) output = run_engine("include_paths" => ["foo.rb", "bar.rb"]) issues = output.split("\0").map { |istr| JSON.parse(istr) } lint_issues = issues.select { |issue| issue["check_name"] == "Rubocop/Lint/UselessAssignment" } expect(lint_issues.detect { |i| i["location"]["path"] == "foo.rb" }).to be_present expect(lint_issues.detect { |i| i["location"]["path"] == "bar.rb" }).to be_nil end it "reads a file with a #!.*ruby declaration at the top" do create_source_file("my_script", <<-EORUBY) #!/usr/bin/env ruby def method unused = "x" return false end EORUBY output = run_engine expect(includes_check?(output, "Lint/UselessAssignment")).to be true end it "uses excludes from the specified YAML config" do create_source_file("my_script", <<-EORUBY) #!/usr/bin/env ruby def method unused = "x" return false end EORUBY create_source_file( "rubocop.yml", "AllCops:\n Exclude:\n - \"my_script\"\n" ) config = { "config" => "rubocop.yml" } output = run_engine(config) expect(includes_check?(output, "Lint/UselessAssignment")).to be false end it "handles different locations properly" do allow_any_instance_of(RuboCop::Cop::Team).to receive(:inspect_file).and_return( [ OpenStruct.new( location: RuboCop::Cop::Lint::Syntax::PseudoSourceRange.new( 1, 0, "" ), cop_name: "fake", message: "message" ) ] ) create_source_file("my_script.rb", <<-EORUBY) #!/usr/bin/env ruby def method unused = "x" return false end EORUBY output = run_engine json = output.split("\u0000") result = JSON.parse(json.first) location = { "path" => "my_script.rb", "positions" => { "begin" => { "column" => 1, "line" => 1 }, "end" => { "column" => 1, "line" => 1 } } } expect(result["location"]).to eq(location) end it "includes complete method body for cyclomatic complexity issue" do create_source_file("my_script", <<-EORUBY) #!/usr/bin/env ruby def method(a,b,c,d,e,f,g) r = 1 if a if !b if c if !d if e if !f (1..g).each do |n| r = (r * n) - n end end end end end end end r end EORUBY output = run_engine expect(includes_check?(output, "Metrics/CyclomaticComplexity")).to be true json = JSON.parse('[' + output.split("\u0000").join(',') + ']') result = json.find do |i| i && i["check_name"] =~ %r{Metrics\/CyclomaticComplexity} end location = { "path" => "my_script", "positions" => { "begin" => { "column" => 11, "line" => 3 }, "end" => { "column" => 14, "line" => 21 } } } expect(result["location"]).to eq(location) end it "uses only include_paths when they're passed in via the config hash" do okay_contents = <<-EORUBY #!/usr/bin/env ruby puts "Hello world" EORUBY create_source_file("included_root_file.rb", okay_contents) create_source_file("subdir/subdir_file.rb", okay_contents) create_source_file("ignored_root_file.rb", <<-EORUBY) def method unused = "x" and "y" return false end EORUBY create_source_file("ignored_subdir/subdir_file.rb", <<-EORUBY) def method unused = "x" return false end EORUBY output = run_engine( "include_paths" => %w[included_root_file.rb subdir/] ) expect(includes_check?(output, "Lint/UselessAssignment")).to be false expect(includes_check?(output, "Style/AndOr")).to be false end it "ignores non-Ruby files even when passed in as include_paths" do config_yml = "foo:\n bar: \"baz\"" create_source_file("config.yml", config_yml) output = run_engine( "include_paths" => %w[config.yml] ) issue = issues(output).detect do |i| i["description"] == "unexpected token tCOLON" end expect(issue).to be nil end it "includes Ruby files even if they don't end with .rb" do create_source_file("Rakefile", <<-EORUBY) def method unused = "x" return false end EORUBY output = run_engine("include_paths" => %w[Rakefile]) expect(includes_check?(output, "Lint/UselessAssignment")).to be true end it "skips local disables" do create_source_file("test.rb", <<-EORUBY) def method # rubocop:disable UselessAssignment unused = "x" return false end EORUBY output = run_engine expect(includes_check?(output, "Lint/UselessAssignment")).to be false end it "shows full source of long methods" do create_source_file("test.rb", <<-EORUBY) def method #{"puts 'hi'\n" * 10} return false end EORUBY output = run_engine issues = output.split("\0").map { |issue| JSON.parse(issue) } issue = issues.find do |i| i["check_name"] == "Rubocop/Metrics/MethodLength" end expect(issue["location"]["positions"]["begin"]["line"]).to eq(1) expect(issue["location"]["positions"]["end"]["line"]).to eq(14) end it "shows full source of long classes" do create_source_file("test.rb", <<-EORUBY) class Awesome #{"foo = 1\n" * 102} end EORUBY output = run_engine issues = output.split("\0").map { |issue| JSON.parse(issue) } issue = issues.find do |i| i["check_name"] == "Rubocop/Metrics/ClassLength" end expect(issue["location"]["positions"]["begin"]["line"]).to eq(1) expect(issue["location"]["positions"]["end"]["line"]).to eq(105) end def includes_check?(output, cop_name) issues(output).any? { |i| i["check_name"] =~ /#{cop_name}$/ } end def includes_fingerprint?(output, fingerprint) issues(output).any? { |i| i["fingerprint"] == fingerprint } end def includes_content_for?(output, cop_name) issue = issues(output).detect { |i| i["check_name"] =~ /#{cop_name}$/ } issue["content"] && issue["content"]["body"].present? end def issues(output) output.split("\0").map { |x| JSON.parse(x) } end def run_engine(config = nil) io = StringIO.new rubocop = Rubocop.new(@code, config, io) rubocop.run io.string end end end end