From 14ca994f43f42a71e0337af86105e40a13ff206c Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 9 Aug 2026 23:38:14 +0900 Subject: [PATCH 1/4] Test more platforms --- .github/workflows/test.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 18324a4..2e8777e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: if: ${{ startsWith(github.repository, 'ruby/') || github.event_name != 'schedule' }} uses: ruby/actions/.github/workflows/ruby_versions.yml@master with: - engine: cruby + engine: all min_version: 2.6 # from `required_ruby_version` in io-console.gemspec test: @@ -25,9 +25,15 @@ jobs: include: - ruby: mswin os: windows-latest + - ruby: jruby-head + os: macos-15-intel + exclude: - ruby: jruby + os: windows-latest - ruby: jruby-head + os: windows-latest - ruby: truffleruby + os: windows-latest runs-on: ${{ matrix.os || 'ubuntu-latest' }} steps: - name: git config From 1b4a781de34fc1d2fbb54bce38ff08bf492b9163 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 10 Aug 2026 12:36:35 +0900 Subject: [PATCH 2/4] Test bad keywords on JRuby JRuby now rejects unknown keyword arguments as expected. --- Rakefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Rakefile b/Rakefile index ce1f6a3..9fed253 100644 --- a/Rakefile +++ b/Rakefile @@ -36,7 +36,6 @@ Rake::TestTask.new(:test) do |t| t.libs.unshift "lib/ffi" if RUBY_ENGINE == "jruby" t.libs << "test/lib" t.ruby_opts << "-rhelper" - t.options = "--ignore-name=TestIO_Console#test_bad_keyword" if RUBY_ENGINE == "jruby" t.test_files = FileList["test/**/test_*.rb"] end From fcb28a3ff7ba5bb3c8229f4b8becc50a7a495820 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 10 Aug 2026 13:45:05 +0900 Subject: [PATCH 3/4] Improve stty console support Use stty with redirected terminals and preserve console modes accurately. --- lib/ffi/io/console/stty_console.rb | 56 +++++++++++++++++++++++++----- test/io/console/test_io_console.rb | 4 +++ 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/lib/ffi/io/console/stty_console.rb b/lib/ffi/io/console/stty_console.rb index 9916a45..593ad13 100644 --- a/lib/ffi/io/console/stty_console.rb +++ b/lib/ffi/io/console/stty_console.rb @@ -3,13 +3,44 @@ module IO::Console STTY = %w[/usr/bin/stty /bin/stty].find {|path| File.executable?(path)} end -unless IO::Console::STTY && - system(IO::Console::STTY, out: File::NULL, err: %i[child out]) - raise "stty command returned nonzero exit status" -end +raise LoadError, "stty command not found" unless IO::Console::STTY warn "io/console on JRuby shells out to stty for most operations" if $VERBOSE +class IO::Console::Mode + def initialize(saved) + @saved = saved + @args = [] + end + + def initialize_copy(mode) + super + @saved = mode.__send__(:saved).dup + @args = mode.__send__(:args).dup + end + + def echo=(echo) + @args << (echo ? 'echo' : '-echo') + echo + end + + def raw(min: 1, time: nil, intr: nil) + dup.raw!(min:, time:, intr:) + end + + def raw!(min: 1, time: nil, intr: nil) + @args << 'raw' + @args.push('min', min.to_s) if min >= 0 + @args.push('time', ((time || 0) * 10).to_i.to_s) + @args.concat(%w[brkint isig opost]) if intr + self + end + + private + + attr_reader :saved, :args +end + # Non-Windows assumes stty command is available class IO private def _io_console_stty(*args) @@ -34,11 +65,11 @@ class IO end def raw(*, min: 1, time: nil, intr: nil) - saved = _io_console_stty('-g') - _io_console_stty('raw') + saved = console_mode + self.console_mode = saved.raw(min:, time:, intr:) yield self ensure - _io_console_stty(saved) if saved + self.console_mode = saved if saved end def raw!(*) @@ -73,6 +104,15 @@ def noecho _io_console_stty(saved) if saved end + def console_mode + Console::Mode.new(_io_console_stty('-g').chomp) + end + + def console_mode=(mode) + _io_console_stty(mode.__send__(:saved), *mode.__send__(:args)) + mode + end + # Not all systems return same format of stty -a output IEEE_STD_1003_2 = '(?\d+) rows; (?\d+) columns' UBUNTU = 'rows (?\d+); columns (?\d+)' @@ -96,7 +136,7 @@ def winsize=(size) row, col, _, _ = size - _io_console_stty("rows #{row} cols #{col}") + _io_console_stty('rows', row.to_s, 'cols', col.to_s) end def iflush diff --git a/test/io/console/test_io_console.rb b/test/io/console/test_io_console.rb index e88cc03..173e6a2 100644 --- a/test/io/console/test_io_console.rb +++ b/test/io/console/test_io_console.rb @@ -369,6 +369,8 @@ def test_getpass end def test_iflush + pend "stty cannot flush terminal queues" if IO.private_method_defined?(:_io_console_stty) + helper {|m, s| m.print "a" s.iflush @@ -390,6 +392,8 @@ def test_oflush end def test_ioflush + pend "stty cannot flush terminal queues" if IO.private_method_defined?(:_io_console_stty) + helper {|m, s| m.print "a" s.ioflush From d6e1af6eb394cfd88c6b33b0a1a642f214d5a867 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 10 Aug 2026 13:52:49 +0900 Subject: [PATCH 4/4] Clarify JRuby test load path setup Keep the JRuby load path in the non-extension test setup branch. --- Rakefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index 9fed253..863c1cf 100644 --- a/Rakefile +++ b/Rakefile @@ -32,8 +32,9 @@ task :test => ffi_version_file if RUBY_ENGINE == "jruby" Rake::TestTask.new(:test) do |t| if extask t.libs = [extask.lib_dir.chomp("/"+File.dirname(name))] + elsif RUBY_ENGINE == "jruby" + t.libs.unshift "lib/ffi" end - t.libs.unshift "lib/ffi" if RUBY_ENGINE == "jruby" t.libs << "test/lib" t.ruby_opts << "-rhelper" t.test_files = FileList["test/**/test_*.rb"]