summaryrefslogtreecommitdiff
path: root/test/irb/helper.rb
diff options
context:
space:
mode:
authorStan Lo <[email protected]>2023-08-02 19:33:38 +0100
committergit <[email protected]>2023-08-02 18:33:43 +0000
commit8ecd300e1e51f9e56bf22a8c4fb64ef475612914 (patch)
tree0796319cf792cd3290be9dbee93b38df1211fa2a /test/irb/helper.rb
parentdc54574adefe798702cc93457655da40f4939669 (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.rb110
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