[ruby/irb] Extract integration testing helpers out of debug command
tests (https://github.com/ruby/irb/pull/660) The ability to run a test case in a subprocess is useful for testing many other features, like nested IRB sessions. So I think it's worth extracting them into a new test case class. https://github.com/ruby/irb/commit/73b7a895f8
This commit is contained in:
parent
dc54574ade
commit
8ecd300e1e
@ -7,6 +7,11 @@ begin
|
|||||||
rescue LoadError # ruby/ruby defines helpers differently
|
rescue LoadError # ruby/ruby defines helpers differently
|
||||||
end
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
require "pty"
|
||||||
|
rescue LoadError # some platforms don't support PTY
|
||||||
|
end
|
||||||
|
|
||||||
module IRB
|
module IRB
|
||||||
class InputMethod; end
|
class InputMethod; end
|
||||||
end
|
end
|
||||||
@ -73,4 +78,109 @@ module TestIRB
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class IntegrationTestCase
|
||||||
|
LIB = File.expand_path("../../lib", __dir__)
|
||||||
|
TIMEOUT_SEC = 3
|
||||||
|
|
||||||
|
def setup
|
||||||
|
unless defined?(PTY)
|
||||||
|
omit "Integration tests require PTY."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_ruby_file(&block)
|
||||||
|
cmd = [EnvUtil.rubybin, "-I", LIB, @ruby_file.to_path]
|
||||||
|
tmp_dir = Dir.mktmpdir
|
||||||
|
|
||||||
|
@commands = []
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
PTY.spawn(integration_envs.merge("TERM" => "dumb"), *cmd) do |read, write, pid|
|
||||||
|
Timeout.timeout(TIMEOUT_SEC) do
|
||||||
|
while line = safe_gets(read)
|
||||||
|
lines << line
|
||||||
|
|
||||||
|
# means the breakpoint is triggered
|
||||||
|
if line.match?(/binding\.irb/)
|
||||||
|
while command = @commands.shift
|
||||||
|
write.puts(command)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
read.close
|
||||||
|
write.close
|
||||||
|
kill_safely(pid)
|
||||||
|
end
|
||||||
|
|
||||||
|
lines.join
|
||||||
|
rescue Timeout::Error
|
||||||
|
message = <<~MSG
|
||||||
|
Test timedout.
|
||||||
|
|
||||||
|
#{'=' * 30} OUTPUT #{'=' * 30}
|
||||||
|
#{lines.map { |l| " #{l}" }.join}
|
||||||
|
#{'=' * 27} END OF OUTPUT #{'=' * 27}
|
||||||
|
MSG
|
||||||
|
assert_block(message) { false }
|
||||||
|
ensure
|
||||||
|
File.unlink(@ruby_file) if @ruby_file
|
||||||
|
FileUtils.remove_entry tmp_dir
|
||||||
|
end
|
||||||
|
|
||||||
|
# read.gets could raise exceptions on some platforms
|
||||||
|
# https://github.com/ruby/ruby/blob/master/ext/pty/pty.c#L721-L728
|
||||||
|
def safe_gets(read)
|
||||||
|
read.gets
|
||||||
|
rescue Errno::EIO
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def kill_safely pid
|
||||||
|
return if wait_pid pid, TIMEOUT_SEC
|
||||||
|
|
||||||
|
Process.kill :TERM, pid
|
||||||
|
return if wait_pid pid, 0.2
|
||||||
|
|
||||||
|
Process.kill :KILL, pid
|
||||||
|
Process.waitpid(pid)
|
||||||
|
rescue Errno::EPERM, Errno::ESRCH
|
||||||
|
end
|
||||||
|
|
||||||
|
def wait_pid pid, sec
|
||||||
|
total_sec = 0.0
|
||||||
|
wait_sec = 0.001 # 1ms
|
||||||
|
|
||||||
|
while total_sec < sec
|
||||||
|
if Process.waitpid(pid, Process::WNOHANG) == pid
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
sleep wait_sec
|
||||||
|
total_sec += wait_sec
|
||||||
|
wait_sec *= 2
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
rescue Errno::ECHILD
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def type(command)
|
||||||
|
@commands << command
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_ruby(program)
|
||||||
|
@ruby_file = Tempfile.create(%w{irb- .rb})
|
||||||
|
@ruby_file.write(program)
|
||||||
|
@ruby_file.close
|
||||||
|
end
|
||||||
|
|
||||||
|
def integration_envs
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,24 +1,12 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
begin
|
|
||||||
require "pty"
|
|
||||||
rescue LoadError
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
require "tempfile"
|
require "tempfile"
|
||||||
require "tmpdir"
|
require "tmpdir"
|
||||||
|
|
||||||
require_relative "helper"
|
require_relative "helper"
|
||||||
|
|
||||||
module TestIRB
|
module TestIRB
|
||||||
LIB = File.expand_path("../../lib", __dir__)
|
class DebugCommandTest < IntegrationTestCase
|
||||||
|
|
||||||
class DebugCommandTestCase < TestCase
|
|
||||||
IRB_AND_DEBUGGER_OPTIONS = {
|
|
||||||
"NO_COLOR" => "true", "RUBY_DEBUG_HISTORY_FILE" => ''
|
|
||||||
}
|
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
if ruby_core?
|
if ruby_core?
|
||||||
omit "This test works only under ruby/irb"
|
omit "This test works only under ruby/irb"
|
||||||
@ -204,96 +192,8 @@ module TestIRB
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
TIMEOUT_SEC = 3
|
def integration_envs
|
||||||
|
{ "NO_COLOR" => "true", "RUBY_DEBUG_HISTORY_FILE" => '' }
|
||||||
def run_ruby_file(&block)
|
|
||||||
cmd = [EnvUtil.rubybin, "-I", LIB, @ruby_file.to_path]
|
|
||||||
tmp_dir = Dir.mktmpdir
|
|
||||||
|
|
||||||
@commands = []
|
|
||||||
lines = []
|
|
||||||
|
|
||||||
yield
|
|
||||||
|
|
||||||
PTY.spawn(IRB_AND_DEBUGGER_OPTIONS.merge("TERM" => "dumb"), *cmd) do |read, write, pid|
|
|
||||||
Timeout.timeout(TIMEOUT_SEC) do
|
|
||||||
while line = safe_gets(read)
|
|
||||||
lines << line
|
|
||||||
|
|
||||||
# means the breakpoint is triggered
|
|
||||||
if line.match?(/binding\.irb/)
|
|
||||||
while command = @commands.shift
|
|
||||||
write.puts(command)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
ensure
|
|
||||||
read.close
|
|
||||||
write.close
|
|
||||||
kill_safely(pid)
|
|
||||||
end
|
|
||||||
|
|
||||||
lines.join
|
|
||||||
rescue Timeout::Error
|
|
||||||
message = <<~MSG
|
|
||||||
Test timedout.
|
|
||||||
|
|
||||||
#{'=' * 30} OUTPUT #{'=' * 30}
|
|
||||||
#{lines.map { |l| " #{l}" }.join}
|
|
||||||
#{'=' * 27} END OF OUTPUT #{'=' * 27}
|
|
||||||
MSG
|
|
||||||
assert_block(message) { false }
|
|
||||||
ensure
|
|
||||||
File.unlink(@ruby_file) if @ruby_file
|
|
||||||
FileUtils.remove_entry tmp_dir
|
|
||||||
end
|
|
||||||
|
|
||||||
# read.gets could raise exceptions on some platforms
|
|
||||||
# https://github.com/ruby/ruby/blob/master/ext/pty/pty.c#L729-L736
|
|
||||||
def safe_gets(read)
|
|
||||||
read.gets
|
|
||||||
rescue Errno::EIO
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def kill_safely pid
|
|
||||||
return if wait_pid pid, TIMEOUT_SEC
|
|
||||||
|
|
||||||
Process.kill :TERM, pid
|
|
||||||
return if wait_pid pid, 0.2
|
|
||||||
|
|
||||||
Process.kill :KILL, pid
|
|
||||||
Process.waitpid(pid)
|
|
||||||
rescue Errno::EPERM, Errno::ESRCH
|
|
||||||
end
|
|
||||||
|
|
||||||
def wait_pid pid, sec
|
|
||||||
total_sec = 0.0
|
|
||||||
wait_sec = 0.001 # 1ms
|
|
||||||
|
|
||||||
while total_sec < sec
|
|
||||||
if Process.waitpid(pid, Process::WNOHANG) == pid
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
sleep wait_sec
|
|
||||||
total_sec += wait_sec
|
|
||||||
wait_sec *= 2
|
|
||||||
end
|
|
||||||
|
|
||||||
false
|
|
||||||
rescue Errno::ECHILD
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
def type(command)
|
|
||||||
@commands << command
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_ruby(program)
|
|
||||||
@ruby_file = Tempfile.create(%w{irb- .rb})
|
|
||||||
@ruby_file.write(program)
|
|
||||||
@ruby_file.close
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user