diff options
author | Stan Lo <[email protected]> | 2023-08-02 19:33:38 +0100 |
---|---|---|
committer | git <[email protected]> | 2023-08-02 18:33:43 +0000 |
commit | 8ecd300e1e51f9e56bf22a8c4fb64ef475612914 (patch) | |
tree | 0796319cf792cd3290be9dbee93b38df1211fa2a /test/irb/helper.rb | |
parent | dc54574adefe798702cc93457655da40f4939669 (diff) |
[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
Diffstat (limited to 'test/irb/helper.rb')
-rw-r--r-- | test/irb/helper.rb | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/test/irb/helper.rb b/test/irb/helper.rb index 55f9e083eb..44dd41bd1b 100644 --- a/test/irb/helper.rb +++ b/test/irb/helper.rb @@ -7,6 +7,11 @@ begin rescue LoadError # ruby/ruby defines helpers differently end +begin + require "pty" +rescue LoadError # some platforms don't support PTY +end + module IRB class InputMethod; end end @@ -73,4 +78,109 @@ module TestIRB } 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 |