summaryrefslogtreecommitdiff
path: root/test/irb/helper.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/irb/helper.rb')
-rw-r--r--test/irb/helper.rb221
1 files changed, 221 insertions, 0 deletions
diff --git a/test/irb/helper.rb b/test/irb/helper.rb
new file mode 100644
index 0000000000..acaf6277f3
--- /dev/null
+++ b/test/irb/helper.rb
@@ -0,0 +1,221 @@
+require "test/unit"
+require "pathname"
+require "rubygems"
+
+begin
+ require_relative "../lib/helper"
+ require_relative "../lib/envutil"
+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
+
+module TestIRB
+ RUBY_3_4 = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0.dev")
+ class TestCase < Test::Unit::TestCase
+ class TestInputMethod < ::IRB::InputMethod
+ attr_reader :list, :line_no
+
+ def initialize(list = [])
+ @line_no = 0
+ @list = list
+ end
+
+ def gets
+ @list[@line_no]&.tap {@line_no += 1}
+ end
+
+ def eof?
+ @line_no >= @list.size
+ end
+
+ def encoding
+ Encoding.default_external
+ end
+
+ def reset
+ @line_no = 0
+ end
+ end
+
+ def ruby_core?
+ !Pathname(__dir__).join("../../", "irb.gemspec").exist?
+ end
+
+ def save_encodings
+ @default_encoding = [Encoding.default_external, Encoding.default_internal]
+ @stdio_encodings = [STDIN, STDOUT, STDERR].map {|io| [io.external_encoding, io.internal_encoding] }
+ end
+
+ def restore_encodings
+ EnvUtil.suppress_warning do
+ Encoding.default_external, Encoding.default_internal = *@default_encoding
+ [STDIN, STDOUT, STDERR].zip(@stdio_encodings) do |io, encs|
+ io.set_encoding(*encs)
+ end
+ end
+ end
+
+ def without_rdoc(&block)
+ ::Kernel.send(:alias_method, :irb_original_require, :require)
+
+ ::Kernel.define_method(:require) do |name|
+ raise LoadError, "cannot load such file -- rdoc (test)" if name.match?("rdoc") || name.match?(/^rdoc\/.*/)
+ ::Kernel.send(:irb_original_require, name)
+ end
+
+ yield
+ ensure
+ EnvUtil.suppress_warning {
+ ::Kernel.send(:alias_method, :require, :irb_original_require)
+ ::Kernel.undef_method :irb_original_require
+ }
+ end
+ end
+
+ class IntegrationTestCase < TestCase
+ LIB = File.expand_path("../../lib", __dir__)
+ TIMEOUT_SEC = 3
+
+ def setup
+ @envs = {}
+ @tmpfiles = []
+
+ unless defined?(PTY)
+ omit "Integration tests require PTY."
+ end
+
+ if ruby_core?
+ omit "This test works only under ruby/irb"
+ end
+
+ write_rc <<~RUBY
+ IRB.conf[:USE_PAGER] = false
+ RUBY
+ end
+
+ def teardown
+ @tmpfiles.each do |tmpfile|
+ File.unlink(tmpfile)
+ end
+ end
+
+ def run_ruby_file(&block)
+ cmd = [EnvUtil.rubybin, "-I", LIB, @ruby_file.to_path]
+ tmp_dir = Dir.mktmpdir
+
+ @commands = []
+ lines = []
+
+ yield
+
+ # Test should not depend on user's irbrc file
+ @envs["HOME"] ||= tmp_dir
+ @envs["XDG_CONFIG_HOME"] ||= tmp_dir
+ @envs["IRBRC"] = nil unless @envs.key?("IRBRC")
+
+ envs_for_spawn = @envs.merge('TERM' => 'dumb', 'TEST_IRB_FORCE_INTERACTIVE' => 'true')
+
+ PTY.spawn(envs_for_spawn, *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
+ 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{irbtest- .rb})
+ @tmpfiles << @ruby_file
+ @ruby_file.write(program)
+ @ruby_file.close
+ end
+
+ def write_rc(content)
+ # Append irbrc content if a tempfile for it already exists
+ if @irbrc
+ @irbrc = File.open(@irbrc, "a")
+ else
+ @irbrc = Tempfile.new('irbrc')
+ @tmpfiles << @irbrc
+ end
+
+ @irbrc.write(content)
+ @irbrc.close
+ @envs['IRBRC'] = @irbrc.path
+ end
+ end
+end