summaryrefslogtreecommitdiff
path: root/test/ruby
diff options
context:
space:
mode:
Diffstat (limited to 'test/ruby')
-rw-r--r--test/ruby/namespace/instance_variables.rb21
-rw-r--r--test/ruby/test_ast.rb18
-rw-r--r--test/ruby/test_backtrace.rb6
-rw-r--r--test/ruby/test_compile_prism.rb3
-rw-r--r--test/ruby/test_data.rb6
-rw-r--r--test/ruby/test_encoding.rb2
-rw-r--r--test/ruby/test_gc.rb8
-rw-r--r--test/ruby/test_io_buffer.rb23
-rw-r--r--test/ruby/test_namespace.rb20
-rw-r--r--test/ruby/test_object_id.rb46
-rw-r--r--test/ruby/test_ractor.rb35
-rw-r--r--test/ruby/test_rubyoptions.rb29
-rw-r--r--test/ruby/test_struct.rb6
-rw-r--r--test/ruby/test_variable.rb14
-rw-r--r--test/ruby/test_vm_dump.rb2
-rw-r--r--test/ruby/test_zjit.rb143
16 files changed, 354 insertions, 28 deletions
diff --git a/test/ruby/namespace/instance_variables.rb b/test/ruby/namespace/instance_variables.rb
new file mode 100644
index 0000000000..1562ad5d45
--- /dev/null
+++ b/test/ruby/namespace/instance_variables.rb
@@ -0,0 +1,21 @@
+class String
+ class << self
+ attr_reader :str_ivar1
+
+ def str_ivar2
+ @str_ivar2
+ end
+ end
+
+ @str_ivar1 = 111
+ @str_ivar2 = 222
+end
+
+class StringDelegator < BasicObject
+private
+ def method_missing(...)
+ ::String.public_send(...)
+ end
+end
+
+StringDelegatorObj = StringDelegator.new
diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb
index 72a0d821a0..d22823470b 100644
--- a/test/ruby/test_ast.rb
+++ b/test/ruby/test_ast.rb
@@ -1397,6 +1397,24 @@ dummy
assert_locations(node.children[-1].locations, [[1, 0, 1, 16], [1, 0, 1, 5], [1, 8, 1, 9], [1, 13, 1, 16]])
end
+ def test_colon2_locations
+ node = ast_parse("A::B")
+ assert_locations(node.children[-1].locations, [[1, 0, 1, 4], [1, 1, 1, 3], [1, 3, 1, 4]])
+
+ node = ast_parse("A::B::C")
+ assert_locations(node.children[-1].locations, [[1, 0, 1, 7], [1, 4, 1, 6], [1, 6, 1, 7]])
+ assert_locations(node.children[-1].children[0].locations, [[1, 0, 1, 4], [1, 1, 1, 3], [1, 3, 1, 4]])
+ end
+
+ def test_colon3_locations
+ node = ast_parse("::A")
+ assert_locations(node.children[-1].locations, [[1, 0, 1, 3], [1, 0, 1, 2], [1, 2, 1, 3]])
+
+ node = ast_parse("::A::B")
+ assert_locations(node.children[-1].locations, [[1, 0, 1, 6], [1, 3, 1, 5], [1, 5, 1, 6]])
+ assert_locations(node.children[-1].children[0].locations, [[1, 0, 1, 3], [1, 0, 1, 2], [1, 2, 1, 3]])
+ end
+
def test_dot2_locations
node = ast_parse("1..2")
assert_locations(node.children[-1].locations, [[1, 0, 1, 4], [1, 1, 1, 3]])
diff --git a/test/ruby/test_backtrace.rb b/test/ruby/test_backtrace.rb
index fca7b62030..01a757f827 100644
--- a/test/ruby/test_backtrace.rb
+++ b/test/ruby/test_backtrace.rb
@@ -454,4 +454,10 @@ class TestBacktrace < Test::Unit::TestCase
foo::Bar.baz
end;
end
+
+ def test_backtrace_internal_frame
+ backtrace = tap { break caller_locations(0) }
+ assert_equal(__FILE__, backtrace[1].path) # not "<internal:kernel>"
+ assert_equal("Kernel#tap", backtrace[1].label)
+ end
end
diff --git a/test/ruby/test_compile_prism.rb b/test/ruby/test_compile_prism.rb
index 819d0d35aa..86f7f0b14f 100644
--- a/test/ruby/test_compile_prism.rb
+++ b/test/ruby/test_compile_prism.rb
@@ -1053,6 +1053,9 @@ module Prism
assert_prism_eval("for foo, in [1,2,3] do end")
assert_prism_eval("for i, j in {a: 'b'} do; i; j; end")
+
+ # Test splat node as index in for loop
+ assert_prism_eval("for *x in [[1,2], [3,4]] do; x; end")
end
############################################################################
diff --git a/test/ruby/test_data.rb b/test/ruby/test_data.rb
index bb38f8ec91..dd698fdcc4 100644
--- a/test/ruby/test_data.rb
+++ b/test/ruby/test_data.rb
@@ -280,4 +280,10 @@ class TestData < Test::Unit::TestCase
assert_not_same(test, loaded)
assert_predicate(loaded, :frozen?)
end
+
+ def test_frozen_subclass
+ test = Class.new(Data.define(:a)).freeze.new(a: 0)
+ assert_kind_of(Data, test)
+ assert_equal([:a], test.members)
+ end
end
diff --git a/test/ruby/test_encoding.rb b/test/ruby/test_encoding.rb
index ee37199be0..0ab357f53a 100644
--- a/test/ruby/test_encoding.rb
+++ b/test/ruby/test_encoding.rb
@@ -33,7 +33,7 @@ class TestEncoding < Test::Unit::TestCase
encodings.each do |e|
assert_raise(TypeError) { e.dup }
assert_raise(TypeError) { e.clone }
- assert_equal(e.object_id, Marshal.load(Marshal.dump(e)).object_id)
+ assert_same(e, Marshal.load(Marshal.dump(e)))
end
end
diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb
index daa645b349..3516cefedf 100644
--- a/test/ruby/test_gc.rb
+++ b/test/ruby/test_gc.rb
@@ -393,12 +393,10 @@ class TestGc < Test::Unit::TestCase
# Create some objects and place it in a WeakMap
wmap = ObjectSpace::WeakMap.new
- ary = Array.new(count)
- enum = count.times
- enum.each.with_index do |i|
+ ary = Array.new(count) do |i|
obj = Object.new
- ary[i] = obj
wmap[obj] = nil
+ obj
end
# Run full GC to collect stats about weak references
@@ -421,7 +419,7 @@ class TestGc < Test::Unit::TestCase
GC.start
# Sometimes the WeakMap has a few elements, which might be held on by registers.
- assert_operator(wmap.size, :<=, 2)
+ assert_operator(wmap.size, :<=, count / 1000)
assert_operator(GC.latest_gc_info(:weak_references_count), :<=, before_weak_references_count - count + error_tolerance)
assert_operator(GC.latest_gc_info(:retained_weak_references_count), :<=, before_retained_weak_references_count - count + error_tolerance)
diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb
index 55296c1f23..62c4667888 100644
--- a/test/ruby/test_io_buffer.rb
+++ b/test/ruby/test_io_buffer.rb
@@ -121,6 +121,16 @@ class TestIOBuffer < Test::Unit::TestCase
end
end
+ def test_string_mapped_buffer_frozen
+ string = "Hello World".freeze
+ IO::Buffer.for(string) do |buffer|
+ assert_raise IO::Buffer::AccessError, "Buffer is not writable!" do
+ buffer.set_string("abc")
+ end
+ assert_equal "H".ord, buffer.get_value(:U8, 0)
+ end
+ end
+
def test_non_string
not_string = Object.new
@@ -683,4 +693,17 @@ class TestIOBuffer < Test::Unit::TestCase
buf.set_string('a', 0, 0)
assert_predicate buf, :empty?
end
+
+ # https://bugs.ruby-lang.org/issues/21210
+ def test_bug_21210
+ omit "compaction is not supported on this platform" unless GC.respond_to?(:compact)
+
+ str = +"hello"
+ buf = IO::Buffer.for(str)
+ assert_predicate buf, :valid?
+
+ GC.verify_compaction_references(expand_heap: true, toward: :empty)
+
+ assert_predicate buf, :valid?
+ end
end
diff --git a/test/ruby/test_namespace.rb b/test/ruby/test_namespace.rb
index 395f244c8e..f13063be48 100644
--- a/test/ruby/test_namespace.rb
+++ b/test/ruby/test_namespace.rb
@@ -222,6 +222,26 @@ class TestNamespace < Test::Unit::TestCase
end;
end
+ def test_instance_variable
+ pend unless Namespace.enabled?
+
+ @n.require_relative('namespace/instance_variables')
+
+ assert_equal [], String.instance_variables
+ assert_equal [:@str_ivar1, :@str_ivar2], @n::StringDelegatorObj.instance_variables
+ assert_equal 111, @n::StringDelegatorObj.str_ivar1
+ assert_equal 222, @n::StringDelegatorObj.str_ivar2
+ assert_equal 222, @n::StringDelegatorObj.instance_variable_get(:@str_ivar2)
+
+ @n::StringDelegatorObj.instance_variable_set(:@str_ivar3, 333)
+ assert_equal 333, @n::StringDelegatorObj.instance_variable_get(:@str_ivar3)
+ @n::StringDelegatorObj.remove_instance_variable(:@str_ivar1)
+ assert_nil @n::StringDelegatorObj.str_ivar1
+ assert_equal [:@str_ivar2, :@str_ivar3], @n::StringDelegatorObj.instance_variables
+
+ assert_equal [], String.instance_variables
+ end
+
def test_methods_added_in_namespace_are_invisible_globally
pend unless Namespace.enabled?
diff --git a/test/ruby/test_object_id.rb b/test/ruby/test_object_id.rb
index 44421ea256..9c0099517b 100644
--- a/test/ruby/test_object_id.rb
+++ b/test/ruby/test_object_id.rb
@@ -198,3 +198,49 @@ class TestObjectIdTooComplexGeneric < TestObjectId
end
end
end
+
+class TestObjectIdRactor < Test::Unit::TestCase
+ def test_object_id_race_free
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ Warning[:experimental] = false
+ class MyClass
+ attr_reader :a, :b, :c
+ def initialize
+ @a = @b = @c = nil
+ end
+ end
+ N = 10_000
+ objs = Ractor.make_shareable(N.times.map { MyClass.new })
+ results = 4.times.map{
+ Ractor.new(objs) { |objs|
+ vars = []
+ ids = []
+ objs.each do |obj|
+ vars << obj.a << obj.b << obj.c
+ ids << obj.object_id
+ end
+ [vars, ids]
+ }
+ }.map(&:value)
+ assert_equal 1, results.uniq.size
+ end;
+ end
+
+ def test_external_object_id_ractor_move
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ Warning[:experimental] = false
+ class MyClass
+ attr_reader :a, :b, :c
+ def initialize
+ @a = @b = @c = nil
+ end
+ end
+ obj = Ractor.make_shareable(MyClass.new)
+ object_id = obj.object_id
+ obj = Ractor.new { Ractor.receive }.send(obj, move: true).value
+ assert_equal object_id, obj.object_id
+ end;
+ end
+end
diff --git a/test/ruby/test_ractor.rb b/test/ruby/test_ractor.rb
index b423993df1..3fc891da23 100644
--- a/test/ruby/test_ractor.rb
+++ b/test/ruby/test_ractor.rb
@@ -79,6 +79,26 @@ class TestRactor < Test::Unit::TestCase
end;
end
+ def test_class_instance_variables
+ assert_ractor(<<~'RUBY')
+ # Once we're in multi-ractor mode, the codepaths
+ # for class instance variables are a bit different.
+ Ractor.new {}.value
+
+ class TestClass
+ @a = 1
+ @b = 2
+ @c = 3
+ @d = 4
+ end
+
+ assert_equal 4, TestClass.remove_instance_variable(:@d)
+ assert_nil TestClass.instance_variable_get(:@d)
+ assert_equal 4, TestClass.instance_variable_set(:@d, 4)
+ assert_equal 4, TestClass.instance_variable_get(:@d)
+ RUBY
+ end
+
def test_require_raises_and_no_ractor_belonging_issue
assert_ractor(<<~'RUBY')
require "tempfile"
@@ -98,6 +118,21 @@ class TestRactor < Test::Unit::TestCase
RUBY
end
+ def test_require_non_string
+ assert_ractor(<<~'RUBY')
+ require "tempfile"
+ require "pathname"
+ f = Tempfile.new(["file_to_require_from_ractor", ".rb"])
+ f.write("")
+ f.flush
+ result = Ractor.new(f.path) do |path|
+ require Pathname.new(path)
+ "success"
+ end.value
+ assert_equal "success", result
+ RUBY
+ end
+
def assert_make_shareable(obj)
refute Ractor.shareable?(obj), "object was already shareable"
Ractor.make_shareable(obj)
diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb
index 833b6a3b7d..54ad953ee9 100644
--- a/test/ruby/test_rubyoptions.rb
+++ b/test/ruby/test_rubyoptions.rb
@@ -787,6 +787,12 @@ class TestRubyOptions < Test::Unit::TestCase
unless /mswin|mingw/ =~ RUBY_PLATFORM
opts[:rlimit_core] = 0
end
+ opts[:failed] = proc do |status, message = "", out = ""|
+ if (sig = status.termsig) && Signal.list["SEGV"] == sig
+ out = ""
+ end
+ Test::Unit::CoreAssertions::FailDesc[status, message]
+ end
ExecOptions = opts.freeze
# The regexp list that should match the entire stderr output.
@@ -836,8 +842,6 @@ class TestRubyOptions < Test::Unit::TestCase
end
def assert_segv(args, message=nil, list: SEGVTest::ExpectedStderrList, **opt, &block)
- pend "macOS 15 is not working with this assertion" if macos?(15)
-
# We want YJIT to be enabled in the subprocess if it's enabled for us
# so that the Ruby description matches.
env = Hash === args.first ? args.shift : {}
@@ -849,7 +853,11 @@ class TestRubyOptions < Test::Unit::TestCase
args.unshift(env)
test_stdin = ""
- tests = [//, list] unless block
+ if !block
+ tests = [//, list, message]
+ elsif message
+ tests = [[], [], message]
+ end
assert_in_out_err(args, test_stdin, *tests, encoding: "ASCII-8BIT",
**SEGVTest::ExecOptions, **opt, &block)
@@ -862,13 +870,12 @@ class TestRubyOptions < Test::Unit::TestCase
def test_segv_loaded_features
bug7402 = '[ruby-core:49573]'
- status = assert_segv(['-e', "END {#{SEGVTest::KILL_SELF}}",
- '-e', 'class Bogus; def to_str; exit true; end; end',
- '-e', '$".clear',
- '-e', '$".unshift Bogus.new',
- '-e', '(p $"; abort) unless $".size == 1',
- ])
- assert_not_predicate(status, :success?, "segv but success #{bug7402}")
+ assert_segv(['-e', "END {#{SEGVTest::KILL_SELF}}",
+ '-e', 'class Bogus; def to_str; exit true; end; end',
+ '-e', '$".clear',
+ '-e', '$".unshift Bogus.new',
+ '-e', '(p $"; abort) unless $".size == 1',
+ ], success: false)
end
def test_segv_setproctitle
@@ -881,8 +888,6 @@ class TestRubyOptions < Test::Unit::TestCase
end
def assert_crash_report(path, cmd = nil, &block)
- pend "macOS 15 is not working with this assertion" if macos?(15)
-
Dir.mktmpdir("ruby_crash_report") do |dir|
list = SEGVTest::ExpectedStderrList
if cmd
diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb
index ecd8ed196c..db591c306e 100644
--- a/test/ruby/test_struct.rb
+++ b/test/ruby/test_struct.rb
@@ -550,6 +550,12 @@ module TestStruct
CODE
end
+ def test_frozen_subclass
+ test = Class.new(@Struct.new(:a)).freeze.new(a: 0)
+ assert_kind_of(@Struct, test)
+ assert_equal([:a], test.members)
+ end
+
class TopStruct < Test::Unit::TestCase
include TestStruct
diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb
index 49fec2d40e..984045e05d 100644
--- a/test/ruby/test_variable.rb
+++ b/test/ruby/test_variable.rb
@@ -407,6 +407,20 @@ class TestVariable < Test::Unit::TestCase
}
end
+ def test_exivar_resize_with_compaction_stress
+ omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077
+ objs = 10_000.times.map do
+ ExIvar.new
+ end
+ EnvUtil.under_gc_compact_stress do
+ 10.times do
+ x = ExIvar.new
+ x.instance_variable_set(:@resize, 1)
+ x
+ end
+ end
+ end
+
def test_local_variables_with_kwarg
bug11674 = '[ruby-core:71437] [Bug #11674]'
v = with_kwargs_11(v1:1,v2:2,v3:3,v4:4,v5:5,v6:6,v7:7,v8:8,v9:9,v10:10,v11:11)
diff --git a/test/ruby/test_vm_dump.rb b/test/ruby/test_vm_dump.rb
index 709fd5eadf..a3e7b69913 100644
--- a/test/ruby/test_vm_dump.rb
+++ b/test/ruby/test_vm_dump.rb
@@ -5,8 +5,6 @@ return unless /darwin/ =~ RUBY_PLATFORM
class TestVMDump < Test::Unit::TestCase
def assert_darwin_vm_dump_works(args, timeout=nil)
- pend "macOS 15 is not working with this assertion" if macos?(15)
-
assert_in_out_err(args, "", [], /^\[IMPORTANT\]/, timeout: timeout || 300)
end
diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb
index 47a9f6f7dc..2b171b02b1 100644
--- a/test/ruby/test_zjit.rb
+++ b/test/ruby/test_zjit.rb
@@ -31,6 +31,20 @@ class TestZJIT < Test::Unit::TestCase
}
end
+ def test_putstring
+ assert_compiles '""', %q{
+ def test = "#{""}"
+ test
+ }, insns: [:putstring]
+ end
+
+ def test_putchilldedstring
+ assert_compiles '""', %q{
+ def test = ""
+ test
+ }, insns: [:putchilledstring]
+ end
+
def test_leave_param
assert_compiles '5', %q{
def test(n) = n
@@ -102,12 +116,39 @@ class TestZJIT < Test::Unit::TestCase
}, call_threshold: 2
end
+ def test_opt_plus_type_guard_exit_with_locals
+ assert_compiles '[6, 6.0]', %q{
+ def test(a)
+ local = 3
+ 1 + a + local
+ end
+ test(1) # profile opt_plus
+ [test(2), test(2.0)]
+ }, call_threshold: 2
+ end
+
def test_opt_plus_type_guard_nested_exit
- omit 'rewind_caller_frames is not implemented yet'
- assert_compiles '[3, 3.0]', %q{
+ assert_compiles '[4, 4.0]', %q{
def side_exit(n) = 1 + n
def jit_frame(n) = 1 + side_exit(n)
def entry(n) = jit_frame(n)
+ entry(2) # profile send
+ [entry(2), entry(2.0)]
+ }, call_threshold: 2
+ end
+
+ def test_opt_plus_type_guard_nested_exit_with_locals
+ assert_compiles '[9, 9.0]', %q{
+ def side_exit(n)
+ local = 2
+ 1 + n + local
+ end
+ def jit_frame(n)
+ local = 3
+ 1 + side_exit(n) + local
+ end
+ def entry(n) = jit_frame(n)
+ entry(2) # profile send
[entry(2), entry(2.0)]
}, call_threshold: 2
end
@@ -130,7 +171,6 @@ class TestZJIT < Test::Unit::TestCase
end
def test_opt_mult_overflow
- omit 'side exits are not implemented yet'
assert_compiles '[6, -6, 9671406556917033397649408, -9671406556917033397649408, 21267647932558653966460912964485513216]', %q{
def test(a, b)
a * b
@@ -255,6 +295,34 @@ class TestZJIT < Test::Unit::TestCase
}, insns: [:opt_ge], call_threshold: 2
end
+ def test_opt_hash_freeze
+ assert_compiles '{}', <<~RUBY, insns: [:opt_hash_freeze]
+ def test = {}.freeze
+ test
+ RUBY
+ end
+
+ def test_opt_ary_freeze
+ assert_compiles '[]', <<~RUBY, insns: [:opt_ary_freeze]
+ def test = [].freeze
+ test
+ RUBY
+ end
+
+ def test_opt_str_freeze
+ assert_compiles '""', <<~RUBY, insns: [:opt_str_freeze]
+ def test = "".freeze
+ test
+ RUBY
+ end
+
+ def test_opt_str_uminus
+ assert_compiles '""', <<~RUBY, insns: [:opt_str_uminus]
+ def test = -""
+ test
+ RUBY
+ end
+
def test_new_array_empty
assert_compiles '[]', %q{
def test = []
@@ -610,6 +678,69 @@ class TestZJIT < Test::Unit::TestCase
}
end
+ def test_uncached_getconstant_path
+ assert_compiles RUBY_COPYRIGHT.dump, %q{
+ def test = RUBY_COPYRIGHT
+ test
+ }, call_threshold: 1, insns: [:opt_getconstant_path]
+ end
+
+ def test_getconstant_path_autoload
+ # A constant-referencing expression can run arbitrary code through Kernel#autoload.
+ Dir.mktmpdir('autoload') do |tmpdir|
+ autoload_path = File.join(tmpdir, 'test_getconstant_path_autoload.rb')
+ File.write(autoload_path, 'X = RUBY_COPYRIGHT')
+
+ assert_compiles RUBY_COPYRIGHT.dump, %Q{
+ Object.autoload(:X, #{File.realpath(autoload_path).inspect})
+ def test = X
+ test
+ }, call_threshold: 1, insns: [:opt_getconstant_path]
+ end
+ end
+
+ def test_send_backtrace
+ backtrace = [
+ "-e:2:in 'Object#jit_frame1'",
+ "-e:3:in 'Object#entry'",
+ "-e:5:in 'block in <main>'",
+ "-e:6:in '<main>'",
+ ]
+ assert_compiles backtrace.inspect, %q{
+ def jit_frame2 = caller # 1
+ def jit_frame1 = jit_frame2 # 2
+ def entry = jit_frame1 # 3
+ entry # profile send # 4
+ entry # 5
+ }, call_threshold: 2
+ end
+
+ def test_putspecialobject_vm_core_and_cbase
+ assert_compiles '10', %q{
+ def test
+ alias bar test
+ 10
+ end
+
+ test
+ bar
+ }, insns: [:putspecialobject]
+ end
+
+ def test_putspecialobject_const_base
+ assert_compiles '1', %q{
+ Foo = 1
+
+ def test = Foo
+
+ # First call: populates the constant cache
+ test
+ # Second call: triggers ZJIT compilation with warm cache
+ # RubyVM::ZJIT.assert_compiles will panic if this fails to compile
+ test
+ }, call_threshold: 2
+ end
+
# tool/ruby_vm/views/*.erb relies on the zjit instructions a) being contiguous and
# b) being reliably ordered after all the other instructions.
def test_instruction_order
@@ -631,11 +762,7 @@ class TestZJIT < Test::Unit::TestCase
pipe_fd = 3
script = <<~RUBY
- _test_proc = -> {
- RubyVM::ZJIT.assert_compiles
- #{test_script}
- }
- ret_val = _test_proc.call
+ ret_val = (_test_proc = -> { RubyVM::ZJIT.assert_compiles; #{test_script.lstrip} }).call
result = {
ret_val:,
#{ unless insns.empty?