Skip to content

Commit

Permalink
Merge pull request #93 from codeclimate/pb-nil-status
Browse files Browse the repository at this point in the history
Handle Open3 returning a nil status
  • Loading branch information
pbrisbin committed Feb 1, 2016
2 parents dfdc557 + 8c9b4e9 commit 97b6ba1
Showing 2 changed files with 74 additions and 0 deletions.
22 changes: 22 additions & 0 deletions lib/cc/engine/analyzers/command_line_runner.rb
Original file line number Diff line number Diff line change
@@ -15,6 +15,9 @@ def initialize(command, timeout = DEFAULT_TIMEOUT)
def run(input)
Timeout.timeout(timeout) do
out, err, status = Open3.capture3(command, stdin_data: input)

status ||= handle_open3_race_condition(out)

if status.success?
yield out
else
@@ -26,6 +29,25 @@ def run(input)
private

attr_reader :command, :timeout

# Work around a race condition in JRuby's Open3.capture3 that can lead
# to a nil status returned. We'll consider the process successful if it
# produced output that can be parsed as JSON.
#
# https://github.com/jruby/jruby/blob/master/lib/ruby/stdlib/open3.rb#L200-L201
#
def handle_open3_race_condition(out)
JSON.parse(out)
NullStatus.new(true, 0)
rescue JSON::ParserError
NullStatus.new(false, 1)
end

NullStatus = Struct.new(:success, :exitstatus) do
def success?
success
end
end
end
end
end
52 changes: 52 additions & 0 deletions spec/cc/engine/analyzers/command_line_runner_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
require "spec_helper"
require "cc/engine/duplication"

module CC::Engine::Analyzers
RSpec.describe CommandLineRunner do
describe "#run" do
it "runs the command on the input and yields the output" do
runner = CommandLineRunner.new("cat; echo hi")

output = runner.run("oh ") { |o| o }

expect(output).to eq "oh hi\n"
end


it "raises on errors" do
runner = CommandLineRunner.new("echo error output >&2; false")

expect { runner.run("") }.to raise_error(
ParserError, /code 1:\nerror output/
)
end

it "times out commands" do
runner = CommandLineRunner.new("sleep 3", 0.01)

expect { runner.run("") }.to raise_error(Timeout::Error)
end

context "when Open3 returns a nil status" do
it "accepts it if the output parses as JSON" do
runner = CommandLineRunner.new("")

allow(Open3).to receive(:capture3).and_return(["{\"type\":\"issue\"}", "", nil])

output = runner.run("") { |o| o }
expect(output).to eq "{\"type\":\"issue\"}"
end

it "raises if the output was not valid JSON" do
runner = CommandLineRunner.new("")

allow(Open3).to receive(:capture3).and_return(["", "error output", nil])

expect { runner.run("") }.to raise_error(
ParserError, /code 1:\nerror output/
)
end
end
end
end
end

0 comments on commit 97b6ba1

Please sign in to comment.