summaryrefslogtreecommitdiff
path: root/test/ruby
diff options
context:
space:
mode:
Diffstat (limited to 'test/ruby')
-rw-r--r--test/ruby/enc/test_cesu8.rb4
-rw-r--r--test/ruby/enc/test_emoji_breaks.rb57
-rw-r--r--test/ruby/enc/test_grapheme_breaks.rb115
-rw-r--r--test/ruby/enc/test_regex_casefold.rb2
-rw-r--r--test/ruby/marshaltestlib.rb2
-rw-r--r--test/ruby/sentence.rb2
-rw-r--r--test/ruby/test_alias.rb65
-rw-r--r--test/ruby/test_argf.rb123
-rw-r--r--test/ruby/test_arithmetic_sequence.rb18
-rw-r--r--test/ruby/test_array.rb430
-rw-r--r--test/ruby/test_assignment.rb60
-rw-r--r--test/ruby/test_ast.rb249
-rw-r--r--test/ruby/test_autoload.rb69
-rw-r--r--test/ruby/test_backtrace.rb56
-rw-r--r--test/ruby/test_basicinstructions.rb5
-rw-r--r--test/ruby/test_bignum.rb97
-rw-r--r--test/ruby/test_call.rb9
-rw-r--r--test/ruby/test_case.rb12
-rw-r--r--test/ruby/test_class.rb103
-rw-r--r--test/ruby/test_complex.rb5
-rw-r--r--test/ruby/test_const.rb28
-rw-r--r--test/ruby/test_defined.rb161
-rw-r--r--test/ruby/test_dir.rb249
-rw-r--r--test/ruby/test_dir_m17n.rb53
-rw-r--r--test/ruby/test_econv.rb19
-rw-r--r--test/ruby/test_encoding.rb33
-rw-r--r--test/ruby/test_enum.rb184
-rw-r--r--test/ruby/test_enumerator.rb38
-rw-r--r--test/ruby/test_env.rb979
-rw-r--r--test/ruby/test_eval.rb83
-rw-r--r--test/ruby/test_exception.rb530
-rw-r--r--test/ruby/test_fiber.rb177
-rw-r--r--test/ruby/test_file.rb8
-rw-r--r--test/ruby/test_file_exhaustive.rb150
-rw-r--r--test/ruby/test_fixnum.rb1
-rw-r--r--test/ruby/test_float.rb36
-rw-r--r--test/ruby/test_frozen_error.rb57
-rw-r--r--test/ruby/test_gc.rb92
-rw-r--r--test/ruby/test_gc_compact.rb133
-rw-r--r--test/ruby/test_hash.rb488
-rw-r--r--test/ruby/test_inlinecache.rb110
-rw-r--r--test/ruby/test_insns_leaf.rb46
-rw-r--r--test/ruby/test_integer.rb63
-rw-r--r--test/ruby/test_io.rb248
-rw-r--r--test/ruby/test_io_buffer.rb360
-rw-r--r--test/ruby/test_io_m17n.rb12
-rw-r--r--test/ruby/test_iseq.rb208
-rw-r--r--test/ruby/test_iterator.rb3
-rw-r--r--test/ruby/test_jit.rb307
-rw-r--r--test/ruby/test_jit_debug.rb17
-rw-r--r--test/ruby/test_keyword.rb2235
-rw-r--r--test/ruby/test_lambda.rb129
-rw-r--r--test/ruby/test_lazy_enumerator.rb8
-rw-r--r--test/ruby/test_literal.rb63
-rw-r--r--test/ruby/test_m17n.rb14
-rw-r--r--test/ruby/test_m17n_comb.rb25
-rw-r--r--test/ruby/test_marshal.rb190
-rw-r--r--test/ruby/test_math.rb54
-rw-r--r--test/ruby/test_memory_view.rb341
-rw-r--r--test/ruby/test_method.rb376
-rw-r--r--test/ruby/test_method_cache.rb76
-rw-r--r--test/ruby/test_module.rb876
-rw-r--r--test/ruby/test_name_error.rb156
-rw-r--r--test/ruby/test_nomethod_error.rb109
-rw-r--r--test/ruby/test_notimp.rb85
-rw-r--r--test/ruby/test_numeric.rb99
-rw-r--r--test/ruby/test_object.rb54
-rw-r--r--test/ruby/test_objectspace.rb73
-rw-r--r--test/ruby/test_optimization.rb96
-rw-r--r--test/ruby/test_pack.rb38
-rw-r--r--test/ruby/test_parse.rb215
-rw-r--r--test/ruby/test_pattern_matching.rb460
-rw-r--r--test/ruby/test_primitive.rb78
-rw-r--r--test/ruby/test_proc.rb333
-rw-r--r--test/ruby/test_process.rb279
-rw-r--r--test/ruby/test_rand.rb353
-rw-r--r--test/ruby/test_random_formatter.rb123
-rw-r--r--test/ruby/test_range.rb55
-rw-r--r--test/ruby/test_rational.rb34
-rw-r--r--test/ruby/test_refinement.rb298
-rw-r--r--test/ruby/test_regexp.rb209
-rw-r--r--test/ruby/test_require.rb164
-rw-r--r--test/ruby/test_require_lib.rb3
-rw-r--r--test/ruby/test_rubyoptions.rb143
-rw-r--r--test/ruby/test_rubyvm.rb57
-rw-r--r--test/ruby/test_rubyvm_jit.rb (renamed from test/ruby/test_rubyvm_mjit.rb)2
-rw-r--r--test/ruby/test_settracefunc.rb418
-rw-r--r--test/ruby/test_sprintf.rb15
-rw-r--r--test/ruby/test_stack.rb82
-rw-r--r--test/ruby/test_string.rb208
-rw-r--r--test/ruby/test_struct.rb96
-rw-r--r--test/ruby/test_super.rb114
-rw-r--r--test/ruby/test_symbol.rb39
-rw-r--r--test/ruby/test_syntax.rb412
-rw-r--r--test/ruby/test_thread.rb99
-rw-r--r--test/ruby/test_thread_cv.rb63
-rw-r--r--test/ruby/test_thread_queue.rb107
-rw-r--r--test/ruby/test_time.rb108
-rw-r--r--test/ruby/test_time_tz.rb61
-rw-r--r--test/ruby/test_transcode.rb58
-rw-r--r--test/ruby/test_undef.rb16
-rw-r--r--test/ruby/test_variable.rb96
-rw-r--r--test/ruby/test_vm_dump.rb2
-rw-r--r--test/ruby/test_weakmap.rb36
-rw-r--r--test/ruby/test_yjit.rb703
105 files changed, 13432 insertions, 3560 deletions
diff --git a/test/ruby/enc/test_cesu8.rb b/test/ruby/enc/test_cesu8.rb
index d9debe76cd..68a08389ea 100644
--- a/test/ruby/enc/test_cesu8.rb
+++ b/test/ruby/enc/test_cesu8.rb
@@ -106,4 +106,8 @@ EOT
assert_equal chr, ord.chr("cesu-8")
end
end
+
+ def test_cesu8_left_adjust_char_head
+ assert_equal("", "\u{10000}".encode("cesu-8").chop)
+ end
end
diff --git a/test/ruby/enc/test_emoji_breaks.rb b/test/ruby/enc/test_emoji_breaks.rb
index 7048d8d59f..cdde4da9bf 100644
--- a/test/ruby/enc/test_emoji_breaks.rb
+++ b/test/ruby/enc/test_emoji_breaks.rb
@@ -13,7 +13,7 @@ class TestEmojiBreaks::BreakTest
@filename = filename
@line_number = line_number
@comment = comment.gsub(/\s+/, ' ').strip
- if filename=='emoji-test'
+ if filename=='emoji-test' or filename=='emoji-variation-sequences'
codes, @type = data.split(/\s*;\s*/)
@shortname = ''
else
@@ -31,22 +31,42 @@ class TestEmojiBreaks::BreakTest
end
end
+class TestEmojiBreaks::BreakFile
+ attr_reader :basename, :fullname, :version
+ FILES = []
+
+ def initialize(basename, path, version)
+ @basename = basename
+ @fullname = "#{path}/#{basename}.txt" # File.expand_path(path + version, __dir__)
+ @version = version
+ FILES << self
+ end
+
+ def self.files
+ FILES
+ end
+end
+
class TestEmojiBreaks < Test::Unit::TestCase
- EMOJI_DATA_FILES = %w[emoji-sequences emoji-test emoji-variation-sequences emoji-zwj-sequences]
- EMOJI_VERSION = RbConfig::CONFIG['UNICODE_EMOJI_VERSION']
- EMOJI_DATA_PATH = File.expand_path("../../../enc/unicode/data/emoji/#{EMOJI_VERSION}", __dir__)
+ UNICODE_VERSION = RbConfig::CONFIG['UNICODE_VERSION']
+ UNICODE_DATA_PATH = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}/ucd/emoji", __dir__)
+ EMOJI_VERSION = RbConfig::CONFIG['UNICODE_EMOJI_VERSION']
+ EMOJI_DATA_PATH = File.expand_path("../../../enc/unicode/data/emoji/#{EMOJI_VERSION}", __dir__)
- def self.expand_filename(basename)
- File.expand_path("#{EMOJI_DATA_PATH}/#{basename}.txt", __dir__)
+ EMOJI_DATA_FILES = %w[emoji-sequences emoji-test emoji-zwj-sequences].map do |basename|
+ BreakFile.new(basename, EMOJI_DATA_PATH, EMOJI_VERSION)
end
+ UNICODE_DATA_FILE = BreakFile.new('emoji-variation-sequences', UNICODE_DATA_PATH, UNICODE_VERSION[0..-3]) # [0..-3] deals with a versioning mismatch problem in Unicode
+ EMOJI_DATA_FILES << UNICODE_DATA_FILE
def self.data_files_available?
EMOJI_DATA_FILES.all? do |f|
- File.exist?(expand_filename(f))
+ File.exist?(f.fullname)
end
end
def test_data_files_available
+ assert_equal 4, EMOJI_DATA_FILES.size # debugging test
unless TestEmojiBreaks.data_files_available?
skip "Emoji data files not available in #{EMOJI_DATA_PATH}."
end
@@ -56,17 +76,26 @@ end
TestEmojiBreaks.data_files_available? and class TestEmojiBreaks
def read_data
tests = []
- EMOJI_DATA_FILES.each do |filename|
+ EMOJI_DATA_FILES.each do |file|
version_mismatch = true
file_tests = []
- IO.foreach(TestEmojiBreaks.expand_filename(filename), encoding: Encoding::UTF_8) do |line|
+ IO.foreach(file.fullname, encoding: Encoding::UTF_8) do |line|
line.chomp!
- raise "File Name Mismatch" if $.==1 and not line=="# #{filename}.txt"
- version_mismatch = false if line=="# Version: #{EMOJI_VERSION}"
- next if /\A(#|\z)/.match? line
- file_tests << BreakTest.new(filename, $., *line.split('#')) rescue 'whatever'
+ raise "File Name Mismatch: line: #{line}, expected filename: #{file.basename}.txt" if $.==1 and not line=="# #{file.basename}.txt"
+ version_mismatch = false if line =~ /^# Version: #{file.version}/
+ next if line.match?(/\A(#|\z)/)
+ if line =~ /^(\h{4,6})\.\.(\h{4,6}) *(;.+)/ # deal with Unicode ranges in emoji-sequences.txt (Bug #18028)
+ range_start = $1.to_i(16)
+ range_end = $2.to_i(16)
+ rest = $3
+ (range_start..range_end).each do |code_point|
+ file_tests << BreakTest.new(file.basename, $., *(code_point.to_s(16)+rest).split('#', 2))
+ end
+ else
+ file_tests << BreakTest.new(file.basename, $., *line.split('#', 2))
+ end
end
- raise "File Version Mismatch" if version_mismatch
+ raise "File Version Mismatch: file: #{file.fullname}, version: #{file.version}" if version_mismatch
tests += file_tests
end
tests
diff --git a/test/ruby/enc/test_grapheme_breaks.rb b/test/ruby/enc/test_grapheme_breaks.rb
index 2d210946a9..e8f3aa04a7 100644
--- a/test/ruby/enc/test_grapheme_breaks.rb
+++ b/test/ruby/enc/test_grapheme_breaks.rb
@@ -4,31 +4,28 @@
require "test/unit"
class TestGraphemeBreaksFromFile < Test::Unit::TestCase
-end
-
-class TestGraphemeBreaksFromFile::BreakTest
- attr_reader :clusters, :string, :comment, :line_number
+ class BreakTest
+ attr_reader :clusters, :string, :comment, :line_number
- def initialize(line_number, data, comment)
- @line_number = line_number
- @comment = comment
- @clusters = data.sub(/\A\s*÷\s*/, '')
- .sub(/\s*÷\s*\z/, '')
- .split(/\s*÷\s*/)
- .map do |cl|
- cl.split(/\s*×\s*/)
- .map do |ch|
- c = ch.to_i(16)
- # eliminate cases with surrogates
- raise ArgumentError if 0xD800 <= c and c <= 0xDFFF
- c.chr('UTF-8')
- end.join
- end
- @string = @clusters.join
+ def initialize(line_number, data, comment)
+ @line_number = line_number
+ @comment = comment
+ @clusters = data.sub(/\A\s*÷\s*/, '')
+ .sub(/\s*÷\s*\z/, '')
+ .split(/\s*÷\s*/)
+ .map do |cl|
+ cl.split(/\s*×\s*/)
+ .map do |ch|
+ c = ch.to_i(16)
+ # eliminate cases with surrogates
+ raise ArgumentError if 0xD800 <= c and c <= 0xDFFF
+ c.chr('UTF-8')
+ end.join
+ end
+ @string = @clusters.join
+ end
end
-end
-class TestGraphemeBreaksFromFile < Test::Unit::TestCase
UNICODE_VERSION = RbConfig::CONFIG['UNICODE_VERSION']
path = File.expand_path("../../../enc/unicode/data/#{UNICODE_VERSION}", __dir__)
UNICODE_DATA_PATH = File.directory?("#{path}/ucd/auxiliary") ? "#{path}/ucd/auxiliary" : path
@@ -43,53 +40,53 @@ class TestGraphemeBreaksFromFile < Test::Unit::TestCase
skip "Unicode data file GraphemeBreakTest not available in #{UNICODE_DATA_PATH}."
end
end
-end
-TestGraphemeBreaksFromFile.file_available? and class TestGraphemeBreaksFromFile
- def read_data
- tests = []
- IO.foreach(GRAPHEME_BREAK_TEST_FILE, encoding: Encoding::UTF_8) do |line|
- if $. == 1 and not line.start_with?("# GraphemeBreakTest-#{UNICODE_VERSION}.txt")
- raise "File Version Mismatch"
+ if file_available?
+ def read_data
+ tests = []
+ IO.foreach(GRAPHEME_BREAK_TEST_FILE, encoding: Encoding::UTF_8) do |line|
+ if $. == 1 and not line.start_with?("# GraphemeBreakTest-#{UNICODE_VERSION}.txt")
+ raise "File Version Mismatch"
+ end
+ next if /\A#/.match? line
+ tests << BreakTest.new($., *line.chomp.split('#')) rescue 'whatever'
end
- next if /\A#/.match? line
- tests << BreakTest.new($., *line.chomp.split('#')) rescue 'whatever'
+ tests
end
- tests
- end
- def all_tests
- @@tests ||= read_data
- rescue Errno::ENOENT
- @@tests ||= []
- end
+ def all_tests
+ @@tests ||= read_data
+ rescue Errno::ENOENT
+ @@tests ||= []
+ end
- def test_each_grapheme_cluster
- all_tests.each do |test|
- expected = test.clusters
- actual = test.string.each_grapheme_cluster.to_a
- assert_equal expected, actual,
- "line #{test.line_number}, expected '#{expected}', " +
- "but got '#{actual}', comment: #{test.comment}"
+ def test_each_grapheme_cluster
+ all_tests.each do |test|
+ expected = test.clusters
+ actual = test.string.each_grapheme_cluster.to_a
+ assert_equal expected, actual,
+ "line #{test.line_number}, expected '#{expected}', " +
+ "but got '#{actual}', comment: #{test.comment}"
+ end
end
- end
- def test_backslash_X
- all_tests.each do |test|
- clusters = test.clusters.dup
- string = test.string.dup
- removals = 0
- while string.sub!(/\A\X/, '')
- removals += 1
- clusters.shift
- expected = clusters.join
+ def test_backslash_X
+ all_tests.each do |test|
+ clusters = test.clusters.dup
+ string = test.string.dup
+ removals = 0
+ while string.sub!(/\A\X/, '')
+ removals += 1
+ clusters.shift
+ expected = clusters.join
+ assert_equal expected, string,
+ "line #{test.line_number}, removals: #{removals}, expected '#{expected}', " +
+ "but got '#{string}', comment: #{test.comment}"
+ end
assert_equal expected, string,
- "line #{test.line_number}, removals: #{removals}, expected '#{expected}', " +
+ "line #{test.line_number}, after last removal, expected '#{expected}', " +
"but got '#{string}', comment: #{test.comment}"
end
- assert_equal expected, string,
- "line #{test.line_number}, after last removal, expected '#{expected}', " +
- "but got '#{string}', comment: #{test.comment}"
end
end
end
diff --git a/test/ruby/enc/test_regex_casefold.rb b/test/ruby/enc/test_regex_casefold.rb
index 2b252bd441..ec5dc7f220 100644
--- a/test/ruby/enc/test_regex_casefold.rb
+++ b/test/ruby/enc/test_regex_casefold.rb
@@ -11,7 +11,7 @@ class TestCaseFold < Test::Unit::TestCase
def check_downcase_properties(expected, start, *flags)
assert_equal expected, start.downcase(*flags)
- temp = start
+ temp = start.dup
assert_equal expected, temp.downcase!(*flags)
assert_equal expected, expected.downcase(*flags)
temp = expected
diff --git a/test/ruby/marshaltestlib.rb b/test/ruby/marshaltestlib.rb
index 5c48a8d853..7f100b7873 100644
--- a/test/ruby/marshaltestlib.rb
+++ b/test/ruby/marshaltestlib.rb
@@ -112,7 +112,7 @@ module MarshalTestLib
marshal_equal(Exception.new('foo')) {|o| o.message}
obj = Object.new
e = assert_raise(NoMethodError) {obj.no_such_method()}
- marshal_equal(e) {|o| o.message}
+ marshal_equal(e) {|o| o.message.lines.first.chomp}
end
def test_exception_subclass
diff --git a/test/ruby/sentence.rb b/test/ruby/sentence.rb
index 28fb5d1cf8..9bfd7c7599 100644
--- a/test/ruby/sentence.rb
+++ b/test/ruby/sentence.rb
@@ -353,7 +353,7 @@ class Sentence
# * No rule derives to empty sequence
# * Underivable rule simplified
# * No channel rule
- # * Symbols which has zero or one choices are not appered in rhs.
+ # * Symbols which has zero or one choices are not appeared in rhs.
#
# Note that the rules which can derive empty and non-empty
# sequences are modified to derive only non-empty sequences.
diff --git a/test/ruby/test_alias.rb b/test/ruby/test_alias.rb
index 33fb82e1d7..0d33cb993c 100644
--- a/test/ruby/test_alias.rb
+++ b/test/ruby/test_alias.rb
@@ -35,6 +35,18 @@ class TestAlias < Test::Unit::TestCase
end
end
+ class Alias4 < Alias0
+ alias foo1 foo
+ alias foo2 foo1
+ alias foo3 foo2
+ end
+
+ class Alias5 < Alias4
+ alias foo1 foo
+ alias foo3 foo2
+ alias foo2 foo1
+ end
+
def test_alias
x = Alias2.new
assert_equal "foo", x.bar
@@ -47,6 +59,20 @@ class TestAlias < Test::Unit::TestCase
assert_raise(NoMethodError) { x.quux }
end
+ def test_alias_inspect
+ o = Alias4.new
+ assert_equal("TestAlias::Alias4(TestAlias::Alias0)#foo()", o.method(:foo).inspect.split[1])
+ assert_equal("TestAlias::Alias4(TestAlias::Alias0)#foo1(foo)()", o.method(:foo1).inspect.split[1])
+ assert_equal("TestAlias::Alias4(TestAlias::Alias0)#foo2(foo)()", o.method(:foo2).inspect.split[1])
+ assert_equal("TestAlias::Alias4(TestAlias::Alias0)#foo3(foo)()", o.method(:foo3).inspect.split[1])
+
+ o = Alias5.new
+ assert_equal("TestAlias::Alias5(TestAlias::Alias0)#foo()", o.method(:foo).inspect.split[1])
+ assert_equal("TestAlias::Alias5(TestAlias::Alias0)#foo1(foo)()", o.method(:foo1).inspect.split[1])
+ assert_equal("TestAlias::Alias5(TestAlias::Alias0)#foo2(foo)()", o.method(:foo2).inspect.split[1])
+ assert_equal("TestAlias::Alias5(TestAlias::Alias0)#foo3(foo)()", o.method(:foo3).inspect.split[1])
+ end
+
def test_nonexistmethod
assert_raise(NameError){
Class.new{
@@ -227,4 +253,43 @@ class TestAlias < Test::Unit::TestCase
assert_equal(:foo, k.instance_method(:bar).original_name)
assert_equal(:foo, name)
end
+
+ def test_alias_suppressing_redefinition
+ assert_in_out_err(%w[-w], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class A
+ def foo; end
+ alias foo foo
+ def foo; end
+ end
+ end;
+ end
+
+ class C2
+ public :system
+ alias_method :bar, :system
+ alias_method :system, :bar
+ end
+
+ def test_zsuper_alias_visibility
+ assert(C2.new.respond_to?(:system))
+ end
+
+ def test_alias_memory_leak
+ assert_no_memory_leak([], "#{<<~"begin;"}", "#{<<~'end;'}", rss: true)
+ begin;
+ class A
+ 500.times do
+ 1000.times do |i|
+ define_method(:"foo_#{i}") {}
+
+ alias :"foo_#{i}" :"foo_#{i}"
+
+ remove_method :"foo_#{i}"
+ end
+ GC.start
+ end
+ end
+ end;
+ end
end
diff --git a/test/ruby/test_argf.rb b/test/ruby/test_argf.rb
index a76bdccf45..e3bd1cd075 100644
--- a/test/ruby/test_argf.rb
+++ b/test/ruby/test_argf.rb
@@ -387,6 +387,21 @@ class TestArgf < Test::Unit::TestCase
assert_equal("foo", File.read(name+suffix))
end
+ def test_inplace_bug_17117
+ assert_in_out_err(["-", @t1.path], "#{<<~"{#"}#{<<~'};'}")
+ {#
+ #!/usr/bin/ruby -pi.bak
+ BEGIN {
+ GC.start
+ arr = []
+ 1000000.times { |x| arr << "fooo#{x}" }
+ }
+ puts "hello"
+ };
+ assert_equal("hello\n1\nhello\n2\n", File.read(@t1.path))
+ assert_equal("1\n2\n", File.read("#{@t1.path}.bak"))
+ end
+
def test_encoding
ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
{#
@@ -725,6 +740,25 @@ class TestArgf < Test::Unit::TestCase
["\"a\\n\\n\"", "\"b\\n\""], [])
end
+ def test_each_line_chomp
+ assert_in_out_err(['-e', 'ARGF.each_line(chomp: false) {|para| p para}'], "a\nb\n",
+ ["\"a\\n\"", "\"b\\n\""], [])
+ assert_in_out_err(['-e', 'ARGF.each_line(chomp: true) {|para| p para}'], "a\nb\n",
+ ["\"a\"", "\"b\""], [])
+
+ t = make_tempfile
+ argf = ARGF.class.new(t.path)
+ lines = []
+ begin
+ argf.each_line(chomp: true) do |line|
+ lines << line
+ end
+ ensure
+ argf.close
+ end
+ assert_equal(%w[foo bar baz], lines)
+ end
+
def test_each_byte
ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
{#
@@ -971,53 +1005,55 @@ class TestArgf < Test::Unit::TestCase
assert_nil(argf.gets, bug4274)
end
- def test_readlines_twice
- bug5952 = '[ruby-dev:45160]'
- assert_ruby_status(["-e", "2.times {STDIN.tty?; readlines}"], "", bug5952)
+ def test_readlines_chomp
+ t = make_tempfile
+ argf = ARGF.class.new(t.path)
+ begin
+ assert_equal(%w[foo bar baz], argf.readlines(chomp: true))
+ ensure
+ argf.close
+ end
+
+ assert_in_out_err(['-e', 'p readlines(chomp: true)'], "a\nb\n",
+ ["[\"a\", \"b\"]"], [])
end
- def test_lines
- ruby('-W1', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
- {#
- $stderr = $stdout
- s = []
- ARGF.lines {|l| s << l }
- p s
- };
- assert_match(/deprecated/, f.gets)
- assert_equal("[\"1\\n\", \"2\\n\", \"3\\n\", \"4\\n\", \"5\\n\", \"6\\n\"]\n", f.read)
+ def test_readline_chomp
+ t = make_tempfile
+ argf = ARGF.class.new(t.path)
+ begin
+ assert_equal("foo", argf.readline(chomp: true))
+ ensure
+ argf.close
end
+
+ assert_in_out_err(['-e', 'p readline(chomp: true)'], "a\nb\n",
+ ["\"a\""], [])
end
- def test_bytes
- ruby('-W1', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
- {#
- $stderr = $stdout
- print Marshal.dump(ARGF.bytes.to_a)
- };
- assert_match(/deprecated/, f.gets)
- assert_equal([49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10], Marshal.load(f.read))
+ def test_gets_chomp
+ t = make_tempfile
+ argf = ARGF.class.new(t.path)
+ begin
+ assert_equal("foo", argf.gets(chomp: true))
+ ensure
+ argf.close
end
+
+ assert_in_out_err(['-e', 'p gets(chomp: true)'], "a\nb\n",
+ ["\"a\""], [])
end
- def test_chars
- ruby('-W1', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
- {#
- $stderr = $stdout
- print [Marshal.dump(ARGF.chars.to_a)].pack('m')
- };
- assert_match(/deprecated/, f.gets)
- assert_equal(["1", "\n", "2", "\n", "3", "\n", "4", "\n", "5", "\n", "6", "\n"], Marshal.load(f.read.unpack('m').first))
- end
+ def test_readlines_twice
+ bug5952 = '[ruby-dev:45160]'
+ assert_ruby_status(["-e", "2.times {STDIN.tty?; readlines}"], "", bug5952)
end
- def test_codepoints
+ def test_each_codepoint
ruby('-W1', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
{#
- $stderr = $stdout
- print Marshal.dump(ARGF.codepoints.to_a)
+ print Marshal.dump(ARGF.each_codepoint.to_a)
};
- assert_match(/deprecated/, f.gets)
assert_equal([49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10], Marshal.load(f.read))
end
end
@@ -1074,4 +1110,23 @@ class TestArgf < Test::Unit::TestCase
assert_raise(TypeError, bug11610) {gets}
};
end
+
+ def test_sized_read
+ s = "a"
+ [@t1, @t2, @t3].each { |t|
+ File.binwrite(t.path, s)
+ s = s.succ
+ }
+
+ ruby('-e', "print ARGF.read(3)", @t1.path, @t2.path, @t3.path) do |f|
+ assert_equal("abc", f.read)
+ end
+
+ argf = ARGF.class.new(@t1.path, @t2.path, @t3.path)
+ begin
+ assert_equal("abc", argf.read(3))
+ ensure
+ argf.close
+ end
+ end
end
diff --git a/test/ruby/test_arithmetic_sequence.rb b/test/ruby/test_arithmetic_sequence.rb
index 45a0ab9222..5e2a825265 100644
--- a/test/ruby/test_arithmetic_sequence.rb
+++ b/test/ruby/test_arithmetic_sequence.rb
@@ -162,11 +162,6 @@ class TestArithmeticSequence < Test::Unit::TestCase
assert_equal([], seq.first(1))
assert_equal([], seq.first(3))
- seq = 1.step(10, by: 0)
- assert_equal(1, seq.first)
- assert_equal([1], seq.first(1))
- assert_equal([1, 1, 1], seq.first(3))
-
seq = 10.0.step(-1.0, by: -2.0)
assert_equal(10.0, seq.first)
assert_equal([10.0], seq.first(1))
@@ -269,6 +264,11 @@ class TestArithmeticSequence < Test::Unit::TestCase
assert_instance_of Integer, res[1]
end
+ def test_last_bug17218
+ seq = (1.0997r .. 1.1r).step(0.0001r)
+ assert_equal(1.1r, seq.last, '[ruby-core:100312] [Bug #17218]')
+ end
+
def test_to_a
assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 1.step(10).to_a)
assert_equal([1, 3, 5, 7, 9], 1.step(10, 2).to_a)
@@ -284,6 +284,11 @@ class TestArithmeticSequence < Test::Unit::TestCase
'[ruby-core:90648] [Bug #15444]')
end
+ def test_to_a_bug17218
+ seq = (1.0997r .. 1.1r).step(0.0001r)
+ assert_equal([1.0997r, 1.0998r, 1.0999r, 1.1r], seq.to_a, '[ruby-core:100312] [Bug #17218]')
+ end
+
def test_slice
seq = 1.step(10, 2)
assert_equal([[1, 3, 5], [7, 9]], seq.each_slice(3).to_a)
@@ -340,9 +345,6 @@ class TestArithmeticSequence < Test::Unit::TestCase
[10, 8, 6, 4, 2].each do |i|
assert_equal(i, seq.next)
end
-
- seq = ((1/10r)..(1/2r)).step(0)
- assert_equal(1/10r, seq.next)
end
def test_next_bug15444
diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb
index 476cf795f0..0ed8ada95c 100644
--- a/test/ruby/test_array.rb
+++ b/test/ruby/test_array.rb
@@ -7,7 +7,6 @@ require "rbconfig/sizeof"
class TestArray < Test::Unit::TestCase
def setup
@verbose = $VERBOSE
- $VERBOSE = nil
@cls = Array
end
@@ -15,6 +14,11 @@ class TestArray < Test::Unit::TestCase
$VERBOSE = @verbose
end
+ def assert_equal_instance(x, y, *msg)
+ assert_equal(x, y, *msg)
+ assert_instance_of(x.class, y)
+ end
+
def test_percent_i
assert_equal([:foo, :bar], %i[foo bar])
assert_equal([:"\"foo"], %i["foo])
@@ -114,6 +118,9 @@ class TestArray < Test::Unit::TestCase
assert_equal('1', (x * 1).join(":"))
assert_equal('', (x * 0).join(":"))
+ assert_instance_of(Array, (@cls[] * 5))
+ assert_instance_of(Array, (@cls[1] * 5))
+
*x = *(1..7).to_a
assert_equal(7, x.size)
assert_equal([1, 2, 3, 4, 5, 6, 7], x)
@@ -645,8 +652,17 @@ class TestArray < Test::Unit::TestCase
b.concat(b, b)
assert_equal([4, 5, 4, 5, 4, 5], b)
- assert_raise(TypeError) { [0].concat(:foo) }
- assert_raise(FrozenError) { [0].freeze.concat(:foo) }
+ assert_raise(TypeError) { @cls[0].concat(:foo) }
+ assert_raise(FrozenError) { @cls[0].freeze.concat(:foo) }
+
+ a = @cls[nil]
+ def (x = Object.new).to_ary
+ ary = Array.new(2)
+ ary << [] << [] << :ok
+ end
+ EnvUtil.under_gc_stress {a.concat(x)}
+ GC.start
+ assert_equal(:ok, a.last)
end
def test_count
@@ -654,7 +670,7 @@ class TestArray < Test::Unit::TestCase
assert_equal(5, a.count)
assert_equal(2, a.count(1))
assert_equal(3, a.count {|x| x % 2 == 1 })
- assert_equal(2, a.count(1) {|x| x % 2 == 1 })
+ assert_equal(2, assert_warning(/given block not used/) {a.count(1) {|x| x % 2 == 1 }})
assert_raise(ArgumentError) { a.count(0, 1) }
bug8654 = '[ruby-core:56072]'
@@ -747,6 +763,15 @@ class TestArray < Test::Unit::TestCase
a = @cls[ 5, 6, 7, 8, 9, 10 ]
assert_equal(9, a.delete_if {|i| break i if i > 8; i < 7})
assert_equal(@cls[7, 8, 9, 10], a, bug2545)
+
+ assert_raise(FrozenError) do
+ a = @cls[1, 2, 3, 42]
+ a.delete_if do
+ a.freeze
+ true
+ end
+ end
+ assert_equal(@cls[1, 2, 3, 42], a)
end
def test_dup
@@ -842,14 +867,14 @@ class TestArray < Test::Unit::TestCase
a2 = @cls[ 5, 6 ]
a3 = @cls[ 4, a2 ]
a4 = @cls[ a1, a3 ]
- assert_equal(@cls[1, 2, 3, 4, 5, 6], a4.flatten)
- assert_equal(@cls[ a1, a3], a4)
+ assert_equal_instance([1, 2, 3, 4, 5, 6], a4.flatten)
+ assert_equal_instance(@cls[ a1, a3], a4)
a5 = @cls[ a1, @cls[], a3 ]
- assert_equal(@cls[1, 2, 3, 4, 5, 6], a5.flatten)
- assert_equal(@cls[1, 2, 3, 4, [5, 6]], a5.flatten(1))
- assert_equal(@cls[], @cls[].flatten)
- assert_equal(@cls[],
+ assert_equal_instance([1, 2, 3, 4, 5, 6], a5.flatten)
+ assert_equal_instance([1, 2, 3, 4, [5, 6]], a5.flatten(1))
+ assert_equal_instance([], @cls[].flatten)
+ assert_equal_instance([],
@cls[@cls[@cls[@cls[],@cls[]],@cls[@cls[]],@cls[]],@cls[@cls[@cls[]]]].flatten)
end
@@ -886,6 +911,17 @@ class TestArray < Test::Unit::TestCase
assert_raise(NoMethodError, bug12738) { a.flatten.m }
end
+ def test_flatten_recursive
+ a = []
+ a << a
+ assert_raise(ArgumentError) { a.flatten }
+ b = [1]; c = [2, b]; b << c
+ assert_raise(ArgumentError) { b.flatten }
+
+ assert_equal([1, 2, b], b.flatten(1))
+ assert_equal([1, 2, 1, 2, 1, c], b.flatten(4))
+ end
+
def test_flatten!
a1 = @cls[ 1, 2, 3]
a2 = @cls[ 5, 6 ]
@@ -1073,6 +1109,19 @@ class TestArray < Test::Unit::TestCase
assert_not_include(a, [1,2])
end
+ def test_intersect?
+ a = @cls[ 1, 2, 3]
+ assert_send([a, :intersect?, [3]])
+ assert_not_send([a, :intersect?, [4]])
+ assert_not_send([a, :intersect?, []])
+ end
+
+ def test_intersect_big_array
+ assert_send([@cls[ 1, 4, 5 ]*64, :intersect?, @cls[ 1, 2, 3 ]*64])
+ assert_not_send([@cls[ 1, 2, 3 ]*64, :intersect?, @cls[ 4, 5, 6 ]*64])
+ assert_not_send([@cls[], :intersect?, @cls[ 1, 2, 3 ]*64])
+ end
+
def test_index
a = @cls[ 'cat', 99, /a/, 99, @cls[ 1, 2, 3] ]
assert_equal(0, a.index('cat'))
@@ -1081,7 +1130,7 @@ class TestArray < Test::Unit::TestCase
assert_nil(a.index('ca'))
assert_nil(a.index([1,2]))
- assert_equal(1, a.index(99) {|x| x == 'cat' })
+ assert_equal(1, assert_warn(/given block not used/) {a.index(99) {|x| x == 'cat' }})
end
def test_values_at
@@ -1092,42 +1141,42 @@ class TestArray < Test::Unit::TestCase
end
def test_join
- $, = ""
+ assert_deprecated_warning {$, = ""}
a = @cls[]
- assert_equal("", a.join)
+ assert_equal("", assert_deprecated_warn(/non-nil value/) {a.join})
assert_equal("", a.join(','))
- assert_equal(Encoding::US_ASCII, a.join.encoding)
+ assert_equal(Encoding::US_ASCII, assert_deprecated_warn(/non-nil value/) {a.join}.encoding)
- $, = ""
+ assert_deprecated_warning {$, = ""}
a = @cls[1, 2]
- assert_equal("12", a.join)
- assert_equal("12", a.join(nil))
+ assert_equal("12", assert_deprecated_warn(/non-nil value/) {a.join})
+ assert_equal("12", assert_deprecated_warn(/non-nil value/) {a.join(nil)})
assert_equal("1,2", a.join(','))
- $, = ""
+ assert_deprecated_warning {$, = ""}
a = @cls[1, 2, 3]
- assert_equal("123", a.join)
- assert_equal("123", a.join(nil))
+ assert_equal("123", assert_deprecated_warn(/non-nil value/) {a.join})
+ assert_equal("123", assert_deprecated_warn(/non-nil value/) {a.join(nil)})
assert_equal("1,2,3", a.join(','))
- $, = ":"
+ assert_deprecated_warning {$, = ":"}
a = @cls[1, 2, 3]
- assert_equal("1:2:3", a.join)
- assert_equal("1:2:3", a.join(nil))
+ assert_equal("1:2:3", assert_deprecated_warn(/non-nil value/) {a.join})
+ assert_equal("1:2:3", assert_deprecated_warn(/non-nil value/) {a.join(nil)})
assert_equal("1,2,3", a.join(','))
- $, = ""
+ assert_deprecated_warning {$, = ""}
e = ''.force_encoding('EUC-JP')
u = ''.force_encoding('UTF-8')
- assert_equal(Encoding::US_ASCII, [[]].join.encoding)
- assert_equal(Encoding::US_ASCII, [1, [u]].join.encoding)
- assert_equal(Encoding::UTF_8, [u, [e]].join.encoding)
- assert_equal(Encoding::UTF_8, [u, [1]].join.encoding)
- assert_equal(Encoding::UTF_8, [Struct.new(:to_str).new(u)].join.encoding)
+ assert_equal(Encoding::US_ASCII, assert_deprecated_warn(/non-nil value/) {[[]].join}.encoding)
+ assert_equal(Encoding::US_ASCII, assert_deprecated_warn(/non-nil value/) {[1, [u]].join}.encoding)
+ assert_equal(Encoding::UTF_8, assert_deprecated_warn(/non-nil value/) {[u, [e]].join}.encoding)
+ assert_equal(Encoding::UTF_8, assert_deprecated_warn(/non-nil value/) {[u, [1]].join}.encoding)
+ assert_equal(Encoding::UTF_8, assert_deprecated_warn(/non-nil value/) {[Struct.new(:to_str).new(u)].join}.encoding)
bug5379 = '[ruby-core:39776]'
- assert_equal(Encoding::US_ASCII, [[], u, nil].join.encoding, bug5379)
- assert_equal(Encoding::UTF_8, [[], "\u3042", nil].join.encoding, bug5379)
+ assert_equal(Encoding::US_ASCII, assert_deprecated_warn(/non-nil value/) {[[], u, nil].join}.encoding, bug5379)
+ assert_equal(Encoding::UTF_8, assert_deprecated_warn(/non-nil value/) {[[], "\u3042", nil].join}.encoding, bug5379)
ensure
$, = nil
end
@@ -1245,6 +1294,12 @@ class TestArray < Test::Unit::TestCase
=end
end
+ def test_pack_with_buffer
+ n = [ 65, 66, 67 ]
+ str = "a" * 100
+ assert_equal("aaaABC", n.pack("@3ccc", buffer: str.dup), "[Bug #19116]")
+ end
+
def test_pop
a = @cls[ 'cat', 'dog' ]
assert_equal('dog', a.pop)
@@ -1304,6 +1359,15 @@ class TestArray < Test::Unit::TestCase
a = @cls[ 5, 6, 7, 8, 9, 10 ]
assert_equal(9, a.reject! {|i| break i if i > 8; i < 7})
assert_equal(@cls[7, 8, 9, 10], a, bug2545)
+
+ assert_raise(FrozenError) do
+ a = @cls[1, 2, 3, 42]
+ a.reject! do
+ a.freeze
+ true
+ end
+ end
+ assert_equal(@cls[1, 2, 3, 42], a)
end
def test_shared_array_reject!
@@ -1421,7 +1485,7 @@ class TestArray < Test::Unit::TestCase
assert_nil(a.rindex('ca'))
assert_nil(a.rindex([1,2]))
- assert_equal(3, a.rindex(99) {|x| x == [1,2,3] })
+ assert_equal(3, assert_warning(/given block not used/) {a.rindex(99) {|x| x == [1,2,3] }})
bug15951 = "[Bug #15951]"
o2 = Object.new
@@ -1461,35 +1525,74 @@ class TestArray < Test::Unit::TestCase
assert_equal(1, a.slice(-100))
assert_nil(a.slice(-101))
- assert_equal(@cls[1], a.slice(0,1))
- assert_equal(@cls[100], a.slice(99,1))
- assert_equal(@cls[], a.slice(100,1))
- assert_equal(@cls[100], a.slice(99,100))
- assert_equal(@cls[100], a.slice(-1,1))
- assert_equal(@cls[99], a.slice(-2,1))
+ assert_equal_instance([1], a.slice(0,1))
+ assert_equal_instance([100], a.slice(99,1))
+ assert_equal_instance([], a.slice(100,1))
+ assert_equal_instance([100], a.slice(99,100))
+ assert_equal_instance([100], a.slice(-1,1))
+ assert_equal_instance([99], a.slice(-2,1))
- assert_equal(@cls[10, 11, 12], a.slice(9, 3))
- assert_equal(@cls[10, 11, 12], a.slice(-91, 3))
+ assert_equal_instance([10, 11, 12], a.slice(9, 3))
+ assert_equal_instance([10, 11, 12], a.slice(-91, 3))
assert_nil(a.slice(-101, 2))
- assert_equal(@cls[1], a.slice(0..0))
- assert_equal(@cls[100], a.slice(99..99))
- assert_equal(@cls[], a.slice(100..100))
- assert_equal(@cls[100], a.slice(99..200))
- assert_equal(@cls[100], a.slice(-1..-1))
- assert_equal(@cls[99], a.slice(-2..-2))
-
- assert_equal(@cls[10, 11, 12], a.slice(9..11))
- assert_equal(@cls[98, 99, 100], a.slice(97..))
- assert_equal(@cls[10, 11, 12], a.slice(-91..-89))
- assert_equal(@cls[10, 11, 12], a.slice(-91..-89))
+ assert_equal_instance([1], a.slice(0..0))
+ assert_equal_instance([100], a.slice(99..99))
+ assert_equal_instance([], a.slice(100..100))
+ assert_equal_instance([100], a.slice(99..200))
+ assert_equal_instance([100], a.slice(-1..-1))
+ assert_equal_instance([99], a.slice(-2..-2))
+
+ assert_equal_instance([10, 11, 12], a.slice(9..11))
+ assert_equal_instance([98, 99, 100], a.slice(97..))
+ assert_equal_instance([10, 11, 12], a.slice(-91..-89))
+ assert_equal_instance([10, 11, 12], a.slice(-91..-89))
+
+ assert_equal_instance([5, 8, 11], a.slice((4..12)%3))
+ assert_equal_instance([95, 97, 99], a.slice((94..)%2))
+
+ # [0] [1] [2] [3] [4] [5] [6] [7]
+ # ary = [ 1 2 3 4 5 6 7 8 ... ]
+ # (0) (1) (2) <- (..7) % 3
+ # (2) (1) (0) <- (7..) % -3
+ assert_equal_instance([1, 4, 7], a.slice((..7)%3))
+ assert_equal_instance([8, 5, 2], a.slice((7..)% -3))
+
+ # [-98] [-97] [-96] [-95] [-94] [-93] [-92] [-91] [-90]
+ # ary = [ ... 3 4 5 6 7 8 9 10 11 ... ]
+ # (0) (1) (2) <- (-98..-90) % 3
+ # (2) (1) (0) <- (-90..-98) % -3
+ assert_equal_instance([3, 6, 9], a.slice((-98..-90)%3))
+ assert_equal_instance([11, 8, 5], a.slice((-90..-98)% -3))
+
+ # [ 48] [ 49] [ 50] [ 51] [ 52] [ 53]
+ # [-52] [-51] [-50] [-49] [-48] [-47]
+ # ary = [ ... 49 50 51 52 53 54 ... ]
+ # (0) (1) (2) <- (48..-47) % 2
+ # (2) (1) (0) <- (-47..48) % -2
+ assert_equal_instance([49, 51, 53], a.slice((48..-47)%2))
+ assert_equal_instance([54, 52, 50], a.slice((-47..48)% -2))
+
+ idx = ((3..90) % 2).to_a
+ assert_equal_instance(a.values_at(*idx), a.slice((3..90)%2))
+ idx = 90.step(3, -2).to_a
+ assert_equal_instance(a.values_at(*idx), a.slice((90 .. 3)% -2))
+ end
+
+ def test_slice_out_of_range
+ a = @cls[*(1..100).to_a]
assert_nil(a.slice(-101..-1))
assert_nil(a.slice(-101..))
+ assert_raise_with_message(RangeError, "((-101..-1).%(2)) out of range") { a.slice((-101..-1)%2) }
+ assert_raise_with_message(RangeError, "((-101..).%(2)) out of range") { a.slice((-101..)%2) }
+
assert_nil(a.slice(10, -3))
assert_equal @cls[], a.slice(10..7)
+
+ assert_equal([100], a.slice(-1, 1_000_000_000))
end
def test_slice!
@@ -1502,15 +1605,18 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[1, 2, 3, 5], a)
a = @cls[1, 2, 3, 4, 5]
- assert_equal(@cls[3,4], a.slice!(2,2))
+ s = a.slice!(2,2)
+ assert_equal_instance([3,4], s)
assert_equal(@cls[1, 2, 5], a)
a = @cls[1, 2, 3, 4, 5]
- assert_equal(@cls[4,5], a.slice!(-2,2))
+ s = a.slice!(-2,2)
+ assert_equal_instance([4,5], s)
assert_equal(@cls[1, 2, 3], a)
a = @cls[1, 2, 3, 4, 5]
- assert_equal(@cls[3,4], a.slice!(2..3))
+ s = a.slice!(2..3)
+ assert_equal_instance([3,4], s)
assert_equal(@cls[1, 2, 5], a)
a = @cls[1, 2, 3, 4, 5]
@@ -1535,6 +1641,21 @@ class TestArray < Test::Unit::TestCase
assert_raise(ArgumentError) { @cls[1].slice!(0, 0, 0) }
end
+ def test_slice_out_of_range!
+ a = @cls[*(1..100).to_a]
+
+ assert_nil(a.clone.slice!(-101..-1))
+ assert_nil(a.clone.slice!(-101..))
+
+ # assert_raise_with_message(RangeError, "((-101..-1).%(2)) out of range") { a.clone.slice!((-101..-1)%2) }
+ # assert_raise_with_message(RangeError, "((-101..).%(2)) out of range") { a.clone.slice!((-101..)%2) }
+
+ assert_nil(a.clone.slice!(10, -3))
+ assert_equal @cls[], a.clone.slice!(10..7)
+
+ assert_equal([100], a.clone.slice!(-1, 1_000_000_000))
+ end
+
def test_sort
a = @cls[ 4, 1, 2, 3 ]
assert_equal(@cls[1, 2, 3, 4], a.sort)
@@ -1574,6 +1695,37 @@ class TestArray < Test::Unit::TestCase
assert_equal([1, 2, 3, 4], a)
end
+ def test_freeze_inside_sort!
+ array = [1, 2, 3, 4, 5]
+ frozen_array = nil
+ assert_raise(FrozenError) do
+ count = 0
+ array.sort! do |a, b|
+ array.freeze if (count += 1) == 6
+ frozen_array ||= array.map.to_a if array.frozen?
+ b <=> a
+ end
+ end
+ assert_equal(frozen_array, array)
+
+ object = Object.new
+ array = [1, 2, 3, 4, 5]
+ object.define_singleton_method(:>){|_| array.freeze; true}
+ assert_raise(FrozenError) do
+ array.sort! do |a, b|
+ object
+ end
+ end
+
+ object = Object.new
+ array = [object, object]
+ object.define_singleton_method(:>){|_| array.freeze; true}
+ object.define_singleton_method(:<=>){|o| object}
+ assert_raise(FrozenError) do
+ array.sort!
+ end
+ end
+
def test_sort_with_callcc
need_continuation
n = 1000
@@ -1637,6 +1789,13 @@ class TestArray < Test::Unit::TestCase
TEST
end
+ def test_sort_uncomparable
+ assert_raise(ArgumentError) {[1, Float::NAN].sort}
+ assert_raise(ArgumentError) {[1.0, Float::NAN].sort}
+ assert_raise(ArgumentError) {[Float::NAN, 1].sort}
+ assert_raise(ArgumentError) {[Float::NAN, 1.0].sort}
+ end
+
def test_to_a
a = @cls[ 1, 2, 3 ]
a_id = a.__id__
@@ -1668,19 +1827,19 @@ class TestArray < Test::Unit::TestCase
end
def test_to_s
- $, = ""
+ assert_deprecated_warning {$, = ""}
a = @cls[]
assert_equal("[]", a.to_s)
- $, = ""
+ assert_deprecated_warning {$, = ""}
a = @cls[1, 2]
assert_equal("[1, 2]", a.to_s)
- $, = ""
+ assert_deprecated_warning {$, = ""}
a = @cls[1, 2, 3]
assert_equal("[1, 2, 3]", a.to_s)
- $, = ":"
+ assert_deprecated_warning {$, = ""}
a = @cls[1, 2, 3]
assert_equal("[1, 2, 3]", a.to_s)
ensure
@@ -1734,10 +1893,12 @@ class TestArray < Test::Unit::TestCase
end
def test_min
+ assert_equal(3, [3].min)
assert_equal(1, [1, 2, 3, 1, 2].min)
assert_equal(3, [1, 2, 3, 1, 2].min {|a,b| b <=> a })
cond = ->((a, ia), (b, ib)) { (b <=> a).nonzero? or ia <=> ib }
assert_equal([3, 2], [1, 2, 3, 1, 2].each_with_index.min(&cond))
+ assert_equal(1.0, [3.0, 1.0, 2.0].min)
ary = %w(albatross dog horse)
assert_equal("albatross", ary.min)
assert_equal("dog", ary.min {|a,b| a.length <=> b.length })
@@ -1755,11 +1916,20 @@ class TestArray < Test::Unit::TestCase
assert_same(obj, [obj, 1.0].min)
end
+ def test_min_uncomparable
+ assert_raise(ArgumentError) {[1, Float::NAN].min}
+ assert_raise(ArgumentError) {[1.0, Float::NAN].min}
+ assert_raise(ArgumentError) {[Float::NAN, 1].min}
+ assert_raise(ArgumentError) {[Float::NAN, 1.0].min}
+ end
+
def test_max
+ assert_equal(1, [1].max)
assert_equal(3, [1, 2, 3, 1, 2].max)
assert_equal(1, [1, 2, 3, 1, 2].max {|a,b| b <=> a })
cond = ->((a, ia), (b, ib)) { (b <=> a).nonzero? or ia <=> ib }
assert_equal([1, 3], [1, 2, 3, 1, 2].each_with_index.max(&cond))
+ assert_equal(3.0, [1.0, 3.0, 2.0].max)
ary = %w(albatross dog horse)
assert_equal("horse", ary.max)
assert_equal("albatross", ary.max {|a,b| a.length <=> b.length })
@@ -1776,7 +1946,15 @@ class TestArray < Test::Unit::TestCase
assert_same(obj, [obj, 1.0].max)
end
+ def test_max_uncomparable
+ assert_raise(ArgumentError) {[1, Float::NAN].max}
+ assert_raise(ArgumentError) {[1.0, Float::NAN].max}
+ assert_raise(ArgumentError) {[Float::NAN, 1].max}
+ assert_raise(ArgumentError) {[Float::NAN, 1.0].max}
+ end
+
def test_minmax
+ assert_equal([3, 3], [3].minmax)
assert_equal([1, 3], [1, 2, 3, 1, 2].minmax)
assert_equal([3, 1], [1, 2, 3, 1, 2].minmax {|a,b| b <=> a })
cond = ->((a, ia), (b, ib)) { (b <=> a).nonzero? or ia <=> ib }
@@ -1847,26 +2025,22 @@ class TestArray < Test::Unit::TestCase
sc = Class.new(@cls)
a = sc[]
b = a.dup
- assert_instance_of(sc, a.uniq)
- assert_equal(sc[], a.uniq)
+ assert_equal_instance([], a.uniq)
assert_equal(b, a)
a = sc[1]
b = a.dup
- assert_instance_of(sc, a.uniq)
- assert_equal(sc[1], a.uniq)
+ assert_equal_instance([1], a.uniq)
assert_equal(b, a)
a = sc[1, 1]
b = a.dup
- assert_instance_of(sc, a.uniq)
- assert_equal(sc[1], a.uniq)
+ assert_equal_instance([1], a.uniq)
assert_equal(b, a)
a = sc[1, 1]
b = a.dup
- assert_instance_of(sc, a.uniq{|x| x})
- assert_equal(sc[1], a.uniq{|x| x})
+ assert_equal_instance([1], a.uniq{|x| x})
assert_equal(b, a)
end
@@ -2281,23 +2455,23 @@ class TestArray < Test::Unit::TestCase
end
def test_take
- assert_equal([1,2,3], [1,2,3,4,5,0].take(3))
+ assert_equal_instance([1,2,3], @cls[1,2,3,4,5,0].take(3))
assert_raise(ArgumentError, '[ruby-dev:34123]') { [1,2].take(-1) }
- assert_equal([1,2], [1,2].take(1000000000), '[ruby-dev:34123]')
+ assert_equal_instance([1,2], @cls[1,2].take(1000000000), '[ruby-dev:34123]')
end
def test_take_while
- assert_equal([1,2], [1,2,3,4,5,0].take_while {|i| i < 3 })
+ assert_equal_instance([1,2], @cls[1,2,3,4,5,0].take_while {|i| i < 3 })
end
def test_drop
- assert_equal([4,5,0], [1,2,3,4,5,0].drop(3))
+ assert_equal_instance([4,5,0], @cls[1,2,3,4,5,0].drop(3))
assert_raise(ArgumentError, '[ruby-dev:34123]') { [1,2].drop(-1) }
- assert_equal([], [1,2].drop(1000000000), '[ruby-dev:34123]')
+ assert_equal_instance([], @cls[1,2].drop(1000000000), '[ruby-dev:34123]')
end
def test_drop_while
- assert_equal([3,4,5,0], [1,2,3,4,5,0].drop_while {|i| i < 3 })
+ assert_equal_instance([3,4,5,0], @cls[1,2,3,4,5,0].drop_while {|i| i < 3 })
end
LONGP = [127, 63, 31, 15, 7].map {|x| 2**x-1 }.find do |x|
@@ -2322,13 +2496,13 @@ class TestArray < Test::Unit::TestCase
def test_initialize
assert_nothing_raised { [].instance_eval { initialize } }
- assert_nothing_raised { Array.new { } }
+ assert_warning(/given block not used/) { Array.new { } }
assert_equal([1, 2, 3], Array.new([1, 2, 3]))
assert_raise(ArgumentError) { Array.new(-1, 1) }
assert_raise(ArgumentError) { Array.new(LONGP, 1) }
assert_equal([1, 1, 1], Array.new(3, 1))
assert_equal([1, 1, 1], Array.new(3) { 1 })
- assert_equal([1, 1, 1], Array.new(3, 1) { 1 })
+ assert_equal([1, 1, 1], assert_warning(/block supersedes default value argument/) {Array.new(3, 1) { 1 }})
end
def test_aset_error
@@ -2344,6 +2518,9 @@ class TestArray < Test::Unit::TestCase
assert_raise(ArgumentError) { [0].freeze[0, 0, 0] = 0 }
assert_raise(TypeError) { [0][:foo] = 0 }
assert_raise(FrozenError) { [0].freeze[:foo] = 0 }
+
+ # [Bug #17271]
+ assert_raise_with_message(RangeError, "-7.. out of range") { [*0..5][-7..] = 1 }
end
def test_first2
@@ -2374,11 +2551,11 @@ class TestArray < Test::Unit::TestCase
def test_aref
assert_raise(ArgumentError) { [][0, 0, 0] }
- assert_raise(TypeError) { [][(1..10).step(2)] }
+ assert_raise(ArgumentError) { @cls[][0, 0, 0] }
end
def test_fetch
- assert_equal(1, [].fetch(0, 0) { 1 })
+ assert_equal(1, assert_warning(/block supersedes default value argument/) {[].fetch(0, 0) { 1 }})
assert_equal(1, [0, 1].fetch(-1))
assert_raise(IndexError) { [0, 1].fetch(2) }
assert_raise(IndexError) { [0, 1].fetch(-3) }
@@ -2447,6 +2624,27 @@ class TestArray < Test::Unit::TestCase
assert_equal("12345", [1,[2,[3,4],5]].join)
end
+ def test_join_recheck_elements_type
+ x = Struct.new(:ary).new
+ def x.to_str
+ ary[2] = [0, 1, 2]
+ "z"
+ end
+ (x.ary = ["a", "b", "c", x])
+ assert_equal("ab012z", x.ary.join(""))
+ end
+
+ def test_join_recheck_array_length
+ x = Struct.new(:ary).new
+ def x.to_str
+ ary.clear
+ ary[0] = "b"
+ "z"
+ end
+ x.ary = Array.new(1023) {"a"*1} << x
+ assert_equal("b", x.ary.join(""))
+ end
+
def test_to_a2
klass = Class.new(Array)
a = klass.new.to_a
@@ -2495,6 +2693,15 @@ class TestArray < Test::Unit::TestCase
a = @cls[ 1, 2, 3, 4, 5 ]
a.select! {|i| a.clear if i == 5; false }
assert_equal(0, a.size, bug13053)
+
+ assert_raise(FrozenError) do
+ a = @cls[1, 2, 3, 42]
+ a.select! do
+ a.freeze
+ false
+ end
+ end
+ assert_equal(@cls[1, 2, 3, 42], a)
end
# also select!
@@ -2510,6 +2717,15 @@ class TestArray < Test::Unit::TestCase
a = @cls[ 1, 2, 3, 4, 5 ]
assert_equal(a, a.keep_if { |i| i > 3 })
assert_equal(@cls[4, 5], a)
+
+ assert_raise(FrozenError) do
+ a = @cls[1, 2, 3, 42]
+ a.keep_if do
+ a.freeze
+ false
+ end
+ end
+ assert_equal(@cls[1, 2, 3, 42], a)
end
def test_filter
@@ -2575,13 +2791,24 @@ class TestArray < Test::Unit::TestCase
def test_zip_bug
bug8153 = "ruby-core:53650"
- r = 1..1
+ r = [1]
def r.respond_to?(*)
super
end
assert_equal [[42, 1]], [42].zip(r), bug8153
end
+ def test_zip_with_enumerator
+ bug17814 = "ruby-core:103513"
+
+ step = 0.step
+ e = Enumerator.produce { step.next }
+ a = %w(a b c)
+ assert_equal([["a", 0], ["b", 1], ["c", 2]], a.zip(e), bug17814)
+ assert_equal([["a", 3], ["b", 4], ["c", 5]], a.zip(e), bug17814)
+ assert_equal([["a", 6], ["b", 7], ["c", 8]], a.zip(e), bug17814)
+ end
+
def test_transpose
assert_equal([[1, :a], [2, :b], [3, :c]],
[[1, 2, 3], [:a, :b, :c]].transpose)
@@ -2608,25 +2835,21 @@ class TestArray < Test::Unit::TestCase
assert_not_equal([0, 1, 2], [0, 1, 3])
end
- A = Array.new(3, &:to_s)
- B = A.dup
-
def test_equal_resize
+ $test_equal_resize_a = Array.new(3, &:to_s)
+ $test_equal_resize_b = $test_equal_resize_a.dup
o = Object.new
def o.==(o)
- A.clear
- B.clear
+ $test_equal_resize_a.clear
+ $test_equal_resize_b.clear
true
end
- A[1] = o
- assert_equal(A, B)
+ $test_equal_resize_a[1] = o
+ assert_equal($test_equal_resize_a, $test_equal_resize_b)
end
def test_flatten_error
a = []
- a << a
- assert_raise(ArgumentError) { a.flatten }
-
f = [].freeze
assert_raise(ArgumentError) { a.flatten!(1, 2) }
assert_raise(TypeError) { a.flatten!(:foo) }
@@ -2861,15 +3084,6 @@ class TestArray < Test::Unit::TestCase
end
end
- class Array2 < Array
- end
-
- def test_array_subclass
- assert_equal(Array2, Array2[1,2,3].uniq.class, "[ruby-dev:34581]")
- assert_equal(Array2, Array2[1,2][0,1].class) # embedded
- assert_equal(Array2, Array2[*(1..100)][1..99].class) #not embedded
- end
-
def test_initialize2
a = [1] * 1000
a.instance_eval { initialize }
@@ -3004,6 +3218,8 @@ class TestArray < Test::Unit::TestCase
assert_equal(nil, a.bsearch {|x| 1 * (2**100) })
assert_equal(nil, a.bsearch {|x| (-1) * (2**100) })
+ assert_equal(4, a.bsearch {|x| (4 - x).to_r })
+
assert_include([4, 7], a.bsearch {|x| (2**100).coerce((1 - x / 4) * (2**100)).first })
end
@@ -3039,6 +3255,8 @@ class TestArray < Test::Unit::TestCase
assert_equal(nil, a.bsearch_index {|x| 1 * (2**100) })
assert_equal(nil, a.bsearch_index {|x| (-1) * (2**100) })
+ assert_equal(1, a.bsearch_index {|x| (4 - x).to_r })
+
assert_include([1, 2], a.bsearch_index {|x| (2**100).coerce((1 - x / 4) * (2**100)).first })
end
@@ -3209,3 +3427,23 @@ class TestArray < Test::Unit::TestCase
end
end
end
+
+class TestArraySubclass < TestArray
+ def setup
+ @verbose = $VERBOSE
+ @cls = Class.new(Array)
+ end
+
+ def test_to_a
+ a = @cls[ 1, 2, 3 ]
+ a_id = a.__id__
+ assert_equal_instance([1, 2, 3], a.to_a)
+ assert_not_equal(a_id, a.to_a.__id__)
+ end
+
+ def test_array_subclass
+ assert_equal(Array, @cls[1,2,3].uniq.class, "[ruby-dev:34581]")
+ assert_equal(Array, @cls[1,2][0,1].class) # embedded
+ assert_equal(Array, @cls[*(1..100)][1..99].class) #not embedded
+ end
+end
diff --git a/test/ruby/test_assignment.rb b/test/ruby/test_assignment.rb
index bf6602ab13..41e8bffe82 100644
--- a/test/ruby/test_assignment.rb
+++ b/test/ruby/test_assignment.rb
@@ -81,6 +81,64 @@ class TestAssignment < Test::Unit::TestCase
a,b,*c = [*[1,2]]; assert_equal([1,2,[]], [a,b,c])
end
+ def test_massign_order
+ order = []
+ define_singleton_method(:x1){order << :x1; self}
+ define_singleton_method(:y1){order << :y1; self}
+ define_singleton_method(:z=){|x| order << [:z=, x]}
+ define_singleton_method(:x2){order << :x2; self}
+ define_singleton_method(:x3){order << :x3; self}
+ define_singleton_method(:x4){order << :x4; self}
+ define_singleton_method(:x5=){|x| order << [:x5=, x]; self}
+ define_singleton_method(:[]=){|*args| order << [:[]=, *args]}
+ define_singleton_method(:r1){order << :r1; :r1}
+ define_singleton_method(:r2){order << :r2; :r2}
+
+ x1.y1.z, x2[1, 2, 3], self[4] = r1, 6, r2
+ assert_equal([:x1, :y1, :x2, :r1, :r2, [:z=, :r1], [:[]=, 1, 2, 3, 6], [:[]=, 4, :r2]], order)
+ order.clear
+
+ x1.y1.z, *x2[1, 2, 3], self[4] = r1, 6, 7, r2
+ assert_equal([:x1, :y1, :x2, :r1, :r2, [:z=, :r1], [:[]=, 1, 2, 3, [6, 7]], [:[]=, 4, :r2]], order)
+ order.clear
+
+ x1.y1.z, *x2[1, 2, 3], x3[4] = r1, 6, 7, r2
+ assert_equal([:x1, :y1, :x2, :x3, :r1, :r2, [:z=, :r1], [:[]=, 1, 2, 3, [6, 7]], [:[]=, 4, :r2]], order)
+ order.clear
+
+ x1.y1.z, *x2[1, 2, 3], x3[4], x4.x5 = r1, 6, 7, r2, 8
+ assert_equal([:x1, :y1, :x2, :x3, :x4, :r1, :r2, [:z=, :r1], [:[]=, 1, 2, 3, [6, 7]], [:[]=, 4, :r2], [:x5=, 8]], order)
+ order.clear
+
+ (x1.y1.z, x2.x5), _a = [r1, r2], 7
+ assert_equal([:x1, :y1, :x2, :r1, :r2, [:z=, :r1], [:x5=, :r2]], order)
+ order.clear
+
+ (x1.y1.z, x1.x5), *x2[1, 2, 3] = [r1, 5], 6, 7, r2, 8
+ assert_equal([:x1, :y1, :x1, :x2, :r1, :r2, [:z=, :r1], [:x5=, 5], [:[]=, 1, 2, 3, [6, 7, :r2, 8]]], order)
+ order.clear
+
+ *x2[1, 2, 3], (x3[4], x4.x5) = 6, 7, [r2, 8]
+ assert_equal([:x2, :x3, :x4, :r2, [:[]=, 1, 2, 3, [6, 7]], [:[]=, 4, :r2], [:x5=, 8]], order)
+ order.clear
+
+ (x1.y1.z, x1.x5), *x2[1, 2, 3], x3[4], x4.x5 = [r1, 5], 6, 7, r2, 8
+ assert_equal([:x1, :y1, :x1, :x2, :x3, :x4, :r1, :r2, [:z=, :r1], [:x5=, 5], [:[]=, 1, 2, 3, [6, 7]], [:[]=, 4, :r2], [:x5=, 8]], order)
+ order.clear
+
+ (x1.y1.z, x1.x5), *x2[1, 2, 3], (x3[4], x4.x5) = [r1, 5], 6, 7, [r2, 8]
+ assert_equal([:x1, :y1, :x1, :x2, :x3, :x4, :r1, :r2, [:z=, :r1], [:x5=, 5], [:[]=, 1, 2, 3, [6, 7]], [:[]=, 4, :r2], [:x5=, 8]], order)
+ order.clear
+
+ ((x1.y1.z, x1.x5), _a), *x2[1, 2, 3], ((x3[4], x4.x5), _b) = [[r1, 5], 10], 6, 7, [[r2, 8], 11]
+ assert_equal([:x1, :y1, :x1, :x2, :x3, :x4, :r1, :r2, [:z=, :r1], [:x5=, 5], [:[]=, 1, 2, 3, [6, 7]], [:[]=, 4, :r2], [:x5=, 8]], order)
+ order.clear
+
+ ((x1.y1.z, *x1.x5), _a), *x2[1, 2, 3], ((*x3[4], x4.x5), _b) = [[r1, 5], 10], 6, 7, [[r2, 8], 11]
+ assert_equal([:x1, :y1, :x1, :x2, :x3, :x4, :r1, :r2, [:z=, :r1], [:x5=, [5]], [:[]=, 1, 2, 3, [6, 7]], [:[]=, 4, [:r2]], [:x5=, 8]], order)
+ order.clear
+ end
+
def test_massign_splat
a,b,*c = *[]; assert_equal([nil,nil,[]], [a,b,c])
a,b,*c = *[1]; assert_equal([1,nil,[]], [a,b,c])
@@ -456,7 +514,7 @@ class TestAssignment < Test::Unit::TestCase
assert(defined?(a))
assert_nil(a)
- # multiple asignment
+ # multiple assignment
a, b = 1, 2
assert_equal 1, a
assert_equal 2, b
diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb
index 4c156650b8..cd7299f200 100644
--- a/test/ruby/test_ast.rb
+++ b/test/ruby/test_ast.rb
@@ -42,10 +42,11 @@ class TestAst < Test::Unit::TestCase
class Helper
attr_reader :errors
- def initialize(path)
+ def initialize(path, src: nil)
@path = path
@errors = []
@debug = false
+ @ast = RubyVM::AbstractSyntaxTree.parse(src) if src
end
def validate_range
@@ -184,7 +185,7 @@ class TestAst < Test::Unit::TestCase
end
end
- def test_of
+ def test_of_proc_and_method
proc = Proc.new { 1 + 2 }
method = self.method(__method__)
@@ -193,7 +194,6 @@ class TestAst < Test::Unit::TestCase
assert_instance_of(RubyVM::AbstractSyntaxTree::Node, node_proc)
assert_instance_of(RubyVM::AbstractSyntaxTree::Node, node_method)
- assert_raise(TypeError) { RubyVM::AbstractSyntaxTree.of("1 + 2") }
Tempfile.create(%w"test_of .rb") do |tmp|
tmp.print "#{<<-"begin;"}\n#{<<-'end;'}"
@@ -210,6 +210,122 @@ class TestAst < Test::Unit::TestCase
end
end
+ def sample_backtrace_location
+ [caller_locations(0).first, __LINE__]
+ end
+
+ def test_of_backtrace_location
+ backtrace_location, lineno = sample_backtrace_location
+ node = RubyVM::AbstractSyntaxTree.of(backtrace_location)
+ assert_instance_of(RubyVM::AbstractSyntaxTree::Node, node)
+ assert_equal(lineno, node.first_lineno)
+ end
+
+ def test_of_error
+ assert_raise(TypeError) { RubyVM::AbstractSyntaxTree.of("1 + 2") }
+ end
+
+ def test_of_proc_and_method_under_eval
+ keep_script_lines_back = RubyVM.keep_script_lines
+ RubyVM.keep_script_lines = false
+
+ method = self.method(eval("def example_method_#{$$}; end"))
+ assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
+
+ method = self.method(eval("def self.example_singleton_method_#{$$}; end"))
+ assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
+
+ method = eval("proc{}")
+ assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
+
+ method = self.method(eval("singleton_class.define_method(:example_define_method_#{$$}){}"))
+ assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
+
+ method = self.method(eval("define_singleton_method(:example_dsm_#{$$}){}"))
+ assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
+
+ method = eval("Class.new{def example_method; end}.instance_method(:example_method)")
+ assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
+
+ method = eval("Class.new{def example_method; end}.instance_method(:example_method)")
+ assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(method) }
+
+ ensure
+ RubyVM.keep_script_lines = keep_script_lines_back
+ end
+
+ def test_of_proc_and_method_under_eval_with_keep_script_lines
+ keep_script_lines_back = RubyVM.keep_script_lines
+ RubyVM.keep_script_lines = true
+
+ method = self.method(eval("def example_method_#{$$}_with_keep_script_lines; end"))
+ assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
+
+ method = self.method(eval("def self.example_singleton_method_#{$$}_with_keep_script_lines; end"))
+ assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
+
+ method = eval("proc{}")
+ assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
+
+ method = self.method(eval("singleton_class.define_method(:example_define_method_#{$$}_with_keep_script_lines){}"))
+ assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
+
+ method = self.method(eval("define_singleton_method(:example_dsm_#{$$}_with_keep_script_lines){}"))
+ assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
+
+ method = eval("Class.new{def example_method_with_keep_script_lines; end}.instance_method(:example_method_with_keep_script_lines)")
+ assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
+
+ method = eval("Class.new{def example_method_with_keep_script_lines; end}.instance_method(:example_method_with_keep_script_lines)")
+ assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(method))
+
+ ensure
+ RubyVM.keep_script_lines = keep_script_lines_back
+ end
+
+ def test_of_backtrace_location_under_eval
+ keep_script_lines_back = RubyVM.keep_script_lines
+ RubyVM.keep_script_lines = false
+
+ m = Module.new do
+ eval(<<-END, nil, __FILE__, __LINE__)
+ def self.sample_backtrace_location
+ caller_locations(0).first
+ end
+ END
+ end
+ backtrace_location = m.sample_backtrace_location
+ assert_raise(ArgumentError) { RubyVM::AbstractSyntaxTree.of(backtrace_location) }
+
+ ensure
+ RubyVM.keep_script_lines = keep_script_lines_back
+ end
+
+ def test_of_backtrace_location_under_eval_with_keep_script_lines
+ keep_script_lines_back = RubyVM.keep_script_lines
+ RubyVM.keep_script_lines = true
+
+ m = Module.new do
+ eval(<<-END, nil, __FILE__, __LINE__)
+ def self.sample_backtrace_location
+ caller_locations(0).first
+ end
+ END
+ end
+ backtrace_location = m.sample_backtrace_location
+ node = RubyVM::AbstractSyntaxTree.of(backtrace_location)
+ assert_instance_of(RubyVM::AbstractSyntaxTree::Node, node)
+ assert_equal(2, node.first_lineno)
+
+ ensure
+ RubyVM.keep_script_lines = keep_script_lines_back
+ end
+
+ def test_of_c_method
+ c = Class.new { attr_reader :foo }
+ assert_nil(RubyVM::AbstractSyntaxTree.of(c.instance_method(:foo)))
+ end
+
def test_scope_local_variables
node = RubyVM::AbstractSyntaxTree.parse("_x = 0")
lv, _, body = *node.children
@@ -252,6 +368,19 @@ class TestAst < Test::Unit::TestCase
mid, defn = body.children
assert_equal(:a, mid)
assert_equal(:SCOPE, defn.type)
+ _, args, = defn.children
+ assert_equal(:ARGS, args.type)
+ end
+
+ def test_defn_endless
+ node = RubyVM::AbstractSyntaxTree.parse("def a = nil")
+ _, _, body = *node.children
+ assert_equal(:DEFN, body.type)
+ mid, defn = body.children
+ assert_equal(:a, mid)
+ assert_equal(:SCOPE, defn.type)
+ _, args, = defn.children
+ assert_equal(:ARGS, args.type)
end
def test_defs
@@ -262,6 +391,20 @@ class TestAst < Test::Unit::TestCase
assert_equal(:VCALL, recv.type)
assert_equal(:b, mid)
assert_equal(:SCOPE, defn.type)
+ _, args, = defn.children
+ assert_equal(:ARGS, args.type)
+ end
+
+ def test_defs_endless
+ node = RubyVM::AbstractSyntaxTree.parse("def a.b = nil")
+ _, _, body = *node.children
+ assert_equal(:DEFS, body.type)
+ recv, mid, defn = body.children
+ assert_equal(:VCALL, recv.type)
+ assert_equal(:b, mid)
+ assert_equal(:SCOPE, defn.type)
+ _, args, = defn.children
+ assert_equal(:ARGS, args.type)
end
def test_dstr
@@ -312,4 +455,104 @@ class TestAst < Test::Unit::TestCase
assert_equal(false, kwrest.call('**nil'))
assert_equal([:a], kwrest.call('**a'))
end
+
+ def test_ranges_numbered_parameter
+ helper = Helper.new(__FILE__, src: "1.times {_1}")
+ helper.validate_range
+ assert_equal([], helper.errors)
+ end
+
+ def test_op_asgn2
+ node = RubyVM::AbstractSyntaxTree.parse("struct.field += foo")
+ _, _, body = *node.children
+ assert_equal(:OP_ASGN2, body.type)
+ recv, _, mid, op, value = body.children
+ assert_equal(:VCALL, recv.type)
+ assert_equal(:field, mid)
+ assert_equal(:+, op)
+ assert_equal(:VCALL, value.type)
+ end
+
+ def test_args
+ rest = 6
+ node = RubyVM::AbstractSyntaxTree.parse("proc { |a| }")
+ _, args = *node.children.last.children[1].children
+ assert_equal(nil, args.children[rest])
+
+ node = RubyVM::AbstractSyntaxTree.parse("proc { |a,| }")
+ _, args = *node.children.last.children[1].children
+ assert_equal(:NODE_SPECIAL_EXCESSIVE_COMMA, args.children[rest])
+
+ node = RubyVM::AbstractSyntaxTree.parse("proc { |*a| }")
+ _, args = *node.children.last.children[1].children
+ assert_equal(:a, args.children[rest])
+ end
+
+ def test_keep_script_lines_for_parse
+ node = RubyVM::AbstractSyntaxTree.parse(<<~END, keep_script_lines: true)
+1.times do
+ 2.times do
+ end
+end
+__END__
+dummy
+ END
+
+ expected = [
+ "1.times do\n",
+ " 2.times do\n",
+ " end\n",
+ "end\n",
+ "__END__\n",
+ ]
+ assert_equal(expected, node.script_lines)
+
+ expected =
+ "1.times do\n" +
+ " 2.times do\n" +
+ " end\n" +
+ "end"
+ assert_equal(expected, node.source)
+
+ expected =
+ "do\n" +
+ " 2.times do\n" +
+ " end\n" +
+ "end"
+ assert_equal(expected, node.children.last.children.last.source)
+
+ expected =
+ "2.times do\n" +
+ " end"
+ assert_equal(expected, node.children.last.children.last.children.last.source)
+ end
+
+ def test_keep_script_lines_for_of
+ proc = Proc.new { 1 + 2 }
+ method = self.method(__method__)
+
+ node_proc = RubyVM::AbstractSyntaxTree.of(proc, keep_script_lines: true)
+ node_method = RubyVM::AbstractSyntaxTree.of(method, keep_script_lines: true)
+
+ assert_equal("{ 1 + 2 }", node_proc.source)
+ assert_equal("def test_keep_script_lines_for_of\n", node_method.source.lines.first)
+ end
+
+ def test_encoding_with_keep_script_lines
+ enc = Encoding::EUC_JP
+ code = "__ENCODING__".encode(enc)
+
+ assert_equal(enc, eval(code))
+
+ node = RubyVM::AbstractSyntaxTree.parse(code, keep_script_lines: false)
+ assert_equal(enc, node.children[2].children[0])
+
+ node = RubyVM::AbstractSyntaxTree.parse(code, keep_script_lines: true)
+ assert_equal(enc, node.children[2].children[0])
+ end
+
+ def test_e_option
+ assert_in_out_err(["-e", "def foo; end; pp RubyVM::AbstractSyntaxTree.of(method(:foo)).type"],
+ "", [":SCOPE"], [])
+ end
end
diff --git a/test/ruby/test_autoload.rb b/test/ruby/test_autoload.rb
index 2e53c9203d..7010645317 100644
--- a/test/ruby/test_autoload.rb
+++ b/test/ruby/test_autoload.rb
@@ -65,7 +65,27 @@ p Foo::Bar
}
end
+ def test_autoload_p_with_static_extensions
+ require 'rbconfig'
+ omit unless RbConfig::CONFIG['EXTSTATIC'] == 'static'
+ begin
+ require 'fcntl.so'
+ rescue LoadError
+ omit('fcntl not included in the build')
+ end
+
+ assert_separately(['--disable-all'], <<~RUBY)
+ autoload :Fcntl, 'fcntl.so'
+
+ assert_equal('fcntl.so', autoload?(:Fcntl))
+ assert(Object.const_defined?(:Fcntl))
+ assert_equal('constant', defined?(Fcntl), '[Bug #19115]')
+ RUBY
+ end
+
def test_autoload_with_unqualified_file_name # [ruby-core:69206]
+ Object.send(:remove_const, :A) if Object.const_defined?(:A)
+
lp = $LOAD_PATH.dup
lf = $LOADED_FEATURES.dup
@@ -425,8 +445,25 @@ p Foo::Bar
end
end
- def test_no_leak
- assert_no_memory_leak([], '', <<~'end;', 'many autoloads', timeout: 60)
+ def test_source_location
+ bug = "Bug16764"
+ Dir.mktmpdir('autoload') do |tmpdir|
+ path = "#{tmpdir}/test-#{bug}.rb"
+ File.write(path, "C::#{bug} = __FILE__\n")
+ assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ class C; end
+ C.autoload(:Bug16764, #{path.dump})
+ assert_equal [__FILE__, __LINE__-1], C.const_source_location(#{bug.dump})
+ assert_equal #{path.dump}, C.const_get(#{bug.dump})
+ assert_equal [#{path.dump}, 1], C.const_source_location(#{bug.dump})
+ end;
+ end
+ end
+
+ def test_no_memory_leak
+ assert_no_memory_leak([], '', "#{<<~"begin;"}\n#{<<~'end;'}", 'many autoloads', timeout: 60)
+ begin;
200000.times do |i|
m = Module.new
m.instance_eval do
@@ -437,6 +474,31 @@ p Foo::Bar
end;
end
+ def test_autoload_after_failed_and_removed_from_loaded_features
+ Dir.mktmpdir('autoload') do |tmpdir|
+ autoload_path = File.join(tmpdir, "test-bug-15790.rb")
+ File.write(autoload_path, '')
+
+ assert_separately(%W[-I #{tmpdir}], <<-RUBY)
+ path = #{File.realpath(autoload_path).inspect}
+ autoload :X, path
+ assert_equal(path, Object.autoload?(:X))
+
+ assert_raise(NameError){X}
+ assert_nil(Object.autoload?(:X))
+ assert_equal(false, Object.const_defined?(:X))
+
+ $LOADED_FEATURES.delete(path)
+ assert_equal(false, Object.const_defined?(:X))
+ assert_nil(Object.autoload?(:X))
+
+ assert_raise(NameError){X}
+ assert_equal(false, Object.const_defined?(:X))
+ assert_nil(Object.autoload?(:X))
+ RUBY
+ end
+ end
+
def add_autoload(path)
(@autoload_paths ||= []) << path
::Object.class_eval {autoload(:AutoloadTest, path)}
@@ -444,6 +506,7 @@ p Foo::Bar
def remove_autoload_constant
$".replace($" - @autoload_paths)
- ::Object.class_eval {remove_const(:AutoloadTest)}
+ ::Object.class_eval {remove_const(:AutoloadTest)} if defined? Object::AutoloadTest
+ TestAutoload.class_eval {remove_const(:AutoloadTest)} if defined? TestAutoload::AutoloadTest
end
end
diff --git a/test/ruby/test_backtrace.rb b/test/ruby/test_backtrace.rb
index 00c96b3b9f..aa79db24cb 100644
--- a/test/ruby/test_backtrace.rb
+++ b/test/ruby/test_backtrace.rb
@@ -138,10 +138,66 @@ class TestBacktrace < Test::Unit::TestCase
rec[m]
end
+ def test_caller_with_limit
+ x = nil
+ c = Class.new do
+ define_method(:bar) do
+ x = caller(1, 1)
+ end
+ end
+ [c.new].group_by(&:bar)
+ assert_equal 1, x.length
+ assert_equal caller(0), caller(0, nil)
+ end
+
def test_caller_with_nil_length
assert_equal caller(0), caller(0, nil)
end
+ def test_caller_locations_first_label
+ def self.label
+ caller_locations.first.label
+ end
+
+ def self.label_caller
+ label
+ end
+
+ assert_equal 'label_caller', label_caller
+
+ [1].group_by do
+ assert_equal 'label_caller', label_caller
+ end
+ end
+
+ def test_caller_limit_cfunc_iseq_no_pc
+ def self.a; [1].group_by { b } end
+ def self.b
+ [
+ caller_locations(2, 1).first.base_label,
+ caller_locations(3, 1).first.base_label
+ ]
+ end
+ assert_equal({["each", "group_by"]=>[1]}, a)
+ end
+
+ def test_caller_location_inspect_cfunc_iseq_no_pc
+ def self.foo
+ @res = caller_locations(2, 1).inspect
+ end
+ @line = __LINE__ + 1
+ 1.times.map { 1.times.map { foo } }
+ assert_equal("[\"#{__FILE__}:#{@line}:in `times'\"]", @res)
+ end
+
+ def test_caller_location_path_cfunc_iseq_no_pc
+ def self.foo
+ @res = caller_locations(2, 1)[0].path
+ end
+ 1.times.map { 1.times.map { foo } }
+ assert_equal(__FILE__, @res)
+ end
+
def test_caller_locations
cs = caller(0); locs = caller_locations(0).map{|loc|
loc.to_s
diff --git a/test/ruby/test_basicinstructions.rb b/test/ruby/test_basicinstructions.rb
index ab32ee54e2..f6b69cc1e5 100644
--- a/test/ruby/test_basicinstructions.rb
+++ b/test/ruby/test_basicinstructions.rb
@@ -428,7 +428,9 @@ class TestBasicInstructions < Test::Unit::TestCase
end
class CVarA
- @@cv = 'CVarA@@cv'
+ def self.setup
+ @@cv = 'CVarA@@cv'
+ end
def self.cv() @@cv end
def self.cv=(v) @@cv = v end
class << self
@@ -449,6 +451,7 @@ class TestBasicInstructions < Test::Unit::TestCase
end
def test_class_variable
+ CVarA.setup
assert_equal 'CVarA@@cv', CVarA.cv
assert_equal 'CVarA@@cv', CVarA.cv2
assert_equal 'CVarA@@cv', CVarA.new.cv
diff --git a/test/ruby/test_bignum.rb b/test/ruby/test_bignum.rb
index 434c5befd9..3ffe7114b5 100644
--- a/test/ruby/test_bignum.rb
+++ b/test/ruby/test_bignum.rb
@@ -19,23 +19,22 @@ class TestBignum < Test::Unit::TestCase
end
BIGNUM_MIN_BITS = n
- T_ZERO = 0.to_bignum
- T_ONE = 1.to_bignum
- T_MONE = (-1).to_bignum
- T31 = (2**31).to_bignum # 2147483648
- T31P = (T31 - 1).to_bignum # 2147483647
- T32 = (2**32).to_bignum # 4294967296
- T32P = (T32 - 1).to_bignum # 4294967295
- T64 = (2**64).to_bignum # 18446744073709551616
- T64P = (T64 - 1).to_bignum # 18446744073709551615
- T128 = (2**128).to_bignum
- T128P = (T128 - 1).to_bignum
- T1024 = (2**1024).to_bignum
- T1024P = (T1024 - 1).to_bignum
+ T_ZERO = Bug::Integer.to_bignum(0)
+ T_ONE = Bug::Integer.to_bignum(1)
+ T_MONE = Bug::Integer.to_bignum(-1)
+ T31 = Bug::Integer.to_bignum(2**31) # 2147483648
+ T31P = Bug::Integer.to_bignum(T31 - 1) # 2147483647
+ T32 = Bug::Integer.to_bignum(2**32) # 4294967296
+ T32P = Bug::Integer.to_bignum(T32 - 1) # 4294967295
+ T64 = Bug::Integer.to_bignum(2**64) # 18446744073709551616
+ T64P = Bug::Integer.to_bignum(T64 - 1) # 18446744073709551615
+ T128 = Bug::Integer.to_bignum(2**128)
+ T128P = Bug::Integer.to_bignum(T128 - 1)
+ T1024 = Bug::Integer.to_bignum(2**1024)
+ T1024P = Bug::Integer.to_bignum(T1024 - 1)
def setup
@verbose = $VERBOSE
- $VERBOSE = nil
@fmax = Float::MAX.to_i
@fmax2 = @fmax * 2
@big = (1 << BIGNUM_MIN_BITS) - 1
@@ -214,9 +213,11 @@ class TestBignum < Test::Unit::TestCase
def test_to_f
assert_nothing_raised { T31P.to_f.to_i }
- assert_raise(FloatDomainError) { (1024**1024).to_f.to_i }
- assert_equal(1, (2**50000).to_f.infinite?)
- assert_equal(-1, (-(2**50000)).to_f.infinite?)
+ assert_raise(FloatDomainError) {
+ assert_warning(/out of Float range/) {(1024**1024).to_f}.to_i
+ }
+ assert_equal(1, assert_warning(/out of Float range/) {(2**50000).to_f}.infinite?)
+ assert_equal(-1, assert_warning(/out of Float range/) {(-(2**50000)).to_f}.infinite?)
end
def test_cmp
@@ -415,7 +416,7 @@ class TestBignum < Test::Unit::TestCase
def test_divide
bug5490 = '[ruby-core:40429]'
assert_raise(ZeroDivisionError, bug5490) {T1024./(0)}
- assert_equal(Float::INFINITY, T1024./(0.0), bug5490)
+ assert_equal(Float::INFINITY, assert_warning(/out of Float range/) {T1024./(0.0)}, bug5490)
end
def test_div
@@ -466,8 +467,8 @@ class TestBignum < Test::Unit::TestCase
def test_pow
assert_equal(1.0, T32 ** 0.0)
assert_equal(1.0 / T32, T32 ** -1)
- assert_equal(1, (T32 ** T32).infinite?)
- assert_equal(1, (T32 ** (2**30-1)).infinite?)
+ assert_equal(1, assert_warning(/may be too big/) {T32 ** T32}.infinite?)
+ assert_equal(1, assert_warning(/may be too big/) {T32 ** (2**30-1)}.infinite?)
### rational changes the behavior of Bignum#**
#assert_raise(TypeError) { T32**"foo" }
@@ -505,39 +506,57 @@ class TestBignum < Test::Unit::TestCase
end
def test_and_with_float
- assert_raise(TypeError) { T1024 & 1.5 }
+ assert_raise(TypeError) {
+ assert_warning(/out of Float range/) {T1024 & 1.5}
+ }
end
def test_and_with_rational
- assert_raise(TypeError, "#1792") { T1024 & Rational(3, 2) }
+ assert_raise(TypeError, "#1792") {
+ assert_warn(/out of Float range/) {T1024 & Rational(3, 2)}
+ }
end
def test_and_with_nonintegral_numeric
- assert_raise(TypeError, "#1792") { T1024 & DummyNumeric.new }
+ assert_raise(TypeError, "#1792") {
+ assert_warn(/out of Float range/) {T1024 & DummyNumeric.new}
+ }
end
def test_or_with_float
- assert_raise(TypeError) { T1024 | 1.5 }
+ assert_raise(TypeError) {
+ assert_warn(/out of Float range/) {T1024 | 1.5}
+ }
end
def test_or_with_rational
- assert_raise(TypeError, "#1792") { T1024 | Rational(3, 2) }
+ assert_raise(TypeError, "#1792") {
+ assert_warn(/out of Float range/) {T1024 | Rational(3, 2)}
+ }
end
def test_or_with_nonintegral_numeric
- assert_raise(TypeError, "#1792") { T1024 | DummyNumeric.new }
+ assert_raise(TypeError, "#1792") {
+ assert_warn(/out of Float range/) {T1024 | DummyNumeric.new}
+ }
end
def test_xor_with_float
- assert_raise(TypeError) { T1024 ^ 1.5 }
+ assert_raise(TypeError) {
+ assert_warn(/out of Float range/) {T1024 ^ 1.5}
+ }
end
def test_xor_with_rational
- assert_raise(TypeError, "#1792") { T1024 ^ Rational(3, 2) }
+ assert_raise(TypeError, "#1792") {
+ assert_warn(/out of Float range/) {T1024 ^ Rational(3, 2)}
+ }
end
def test_xor_with_nonintegral_numeric
- assert_raise(TypeError, "#1792") { T1024 ^ DummyNumeric.new }
+ assert_raise(TypeError, "#1792") {
+ assert_warn(/out of Float range/) {T1024 ^ DummyNumeric.new}
+ }
end
def test_shift2
@@ -613,7 +632,7 @@ class TestBignum < Test::Unit::TestCase
time = Time.now
end_flag = false
num = (65536 ** 65536)
- q = Queue.new
+ q = Thread::Queue.new
thread = Thread.new do
assert_raise(RuntimeError) {
q << true
@@ -663,7 +682,7 @@ class TestBignum < Test::Unit::TestCase
end
def test_too_big_to_s
- if (big = 2**31-1).fixnum?
+ if Bug::Integer.fixnum?(big = 2**31-1)
return
end
assert_raise_with_message(RangeError, /too big to convert/) {(1 << big).to_s}
@@ -746,7 +765,7 @@ class TestBignum < Test::Unit::TestCase
end
def test_digits
- assert_equal([90, 78, 56, 34, 12], 1234567890.to_bignum.digits(100))
+ assert_equal([90, 78, 56, 34, 12], Bug::Integer.to_bignum(1234567890).digits(100))
assert_equal([7215, 2413, 6242], T1024P.digits(10_000).first(3))
assert_equal([11], 11.digits(T1024P))
assert_equal([T1024P - 1, 1], (T1024P + T1024P - 1).digits(T1024P))
@@ -759,13 +778,13 @@ class TestBignum < Test::Unit::TestCase
end
def test_digits_for_invalid_base_numbers
- assert_raise(ArgumentError) { T1024P.to_bignum.digits(0) }
- assert_raise(ArgumentError) { T1024P.to_bignum.digits(-1) }
- assert_raise(ArgumentError) { T1024P.to_bignum.digits(0.to_bignum) }
- assert_raise(ArgumentError) { T1024P.to_bignum.digits(1.to_bignum) }
- assert_raise(ArgumentError) { T1024P.to_bignum.digits(-T1024P) }
- assert_raise(ArgumentError) { 10.digits(0.to_bignum) }
- assert_raise(ArgumentError) { 10.digits(1.to_bignum) }
+ assert_raise(ArgumentError) { Bug::Integer.to_bignum(T1024P).digits(0) }
+ assert_raise(ArgumentError) { Bug::Integer.to_bignum(T1024P).digits(-1) }
+ assert_raise(ArgumentError) { Bug::Integer.to_bignum(T1024P).digits(Bug::Integer.to_bignum(0)) }
+ assert_raise(ArgumentError) { Bug::Integer.to_bignum(T1024P).digits(Bug::Integer.to_bignum(1)) }
+ assert_raise(ArgumentError) { Bug::Integer.to_bignum(T1024P).digits(-T1024P) }
+ assert_raise(ArgumentError) { 10.digits(Bug::Integer.to_bignum(0)) }
+ assert_raise(ArgumentError) { 10.digits(Bug::Integer.to_bignum(1)) }
end
def test_digits_for_non_integral_base_numbers
diff --git a/test/ruby/test_call.rb b/test/ruby/test_call.rb
index 2a1b671cac..67b3a936d4 100644
--- a/test/ruby/test_call.rb
+++ b/test/ruby/test_call.rb
@@ -99,4 +99,13 @@ class TestCall < Test::Unit::TestCase
ary = [1, 2]
assert_equal([0, 1, 2, 1], aaa(0, *ary, ary.shift), bug12860)
end
+
+ def test_call_block_order
+ bug16504 = '[ruby-core:96769] [Bug# 16504]'
+ b = proc{}
+ ary = [1, 2, b]
+ assert_equal([1, 2, b], aaa(*ary, &ary.pop), bug16504)
+ ary = [1, 2, b]
+ assert_equal([0, 1, 2, b], aaa(0, *ary, &ary.pop), bug16504)
+ end
end
diff --git a/test/ruby/test_case.rb b/test/ruby/test_case.rb
index 77612a8945..4a0f1bf78d 100644
--- a/test/ruby/test_case.rb
+++ b/test/ruby/test_case.rb
@@ -102,6 +102,18 @@ class TestCase < Test::Unit::TestCase
else
assert(false)
end
+ case 0
+ when 0r
+ assert(true)
+ else
+ assert(false)
+ end
+ case 0
+ when 0i
+ assert(true)
+ else
+ assert(false)
+ end
end
def test_method_missing
diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb
index ca78473026..07c34ce9d5 100644
--- a/test/ruby/test_class.rb
+++ b/test/ruby/test_class.rb
@@ -312,7 +312,7 @@ class TestClass < Test::Unit::TestCase
end
def test_invalid_yield_from_class_definition
- assert_raise(LocalJumpError) {
+ assert_raise(SyntaxError) {
EnvUtil.suppress_warning {eval("class C; yield; end")}
}
end
@@ -483,6 +483,53 @@ class TestClass < Test::Unit::TestCase
assert_equal(:foo, d.foo)
end
+ def test_clone_singleton_class_exists
+ klass = Class.new do
+ def self.bar; :bar; end
+ end
+
+ o = klass.new
+ o.singleton_class
+ clone = o.clone
+
+ assert_empty(o.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.instance_methods(false))
+ assert_empty(o.singleton_class.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.singleton_class.instance_methods(false))
+ end
+
+ def test_clone_when_singleton_class_of_singleton_class_exists
+ klass = Class.new do
+ def self.bar; :bar; end
+ end
+
+ o = klass.new
+ o.singleton_class.singleton_class
+ clone = o.clone
+
+ assert_empty(o.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.instance_methods(false))
+ assert_empty(o.singleton_class.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.singleton_class.instance_methods(false))
+ end
+
+ def test_clone_when_method_exists_on_singleton_class_of_singleton_class
+ klass = Class.new do
+ def self.bar; :bar; end
+ end
+
+ o = klass.new
+ o.singleton_class.singleton_class.define_method(:s2_method) { :s2 }
+ clone = o.clone
+
+ assert_empty(o.singleton_class.instance_methods(false))
+ assert_empty(clone.singleton_class.instance_methods(false))
+ assert_equal(:s2, o.singleton_class.s2_method)
+ assert_equal(:s2, clone.singleton_class.s2_method)
+ assert_equal([:s2_method], o.singleton_class.singleton_class.instance_methods(false))
+ assert_equal([:s2_method], clone.singleton_class.singleton_class.instance_methods(false))
+ end
+
def test_singleton_class_p
feature7609 = '[ruby-core:51087] [Feature #7609]'
assert_predicate(self.singleton_class, :singleton_class?, feature7609)
@@ -683,4 +730,58 @@ class TestClass < Test::Unit::TestCase
end;
end
+
+ def test_assign_frozen_class_to_const
+ c = Class.new.freeze
+ assert_same(c, Module.new.module_eval("self::Foo = c"))
+ c = Class.new.freeze
+ assert_same(c, Module.new.const_set(:Foo, c))
+ end
+
+ def test_subclasses
+ c = Class.new
+ sc = Class.new(c)
+ ssc = Class.new(sc)
+ [c, sc, ssc].each do |k|
+ k.include Module.new
+ k.new.define_singleton_method(:force_singleton_class){}
+ end
+ assert_equal([sc], c.subclasses)
+ assert_equal([ssc], sc.subclasses)
+ assert_equal([], ssc.subclasses)
+
+ object_subclasses = Object.subclasses
+ assert_include(object_subclasses, c)
+ assert_not_include(object_subclasses, sc)
+ assert_not_include(object_subclasses, ssc)
+ object_subclasses.each do |subclass|
+ assert_equal Object, subclass.superclass, "Expected #{subclass}.superclass to be Object"
+ end
+ end
+
+ def test_subclass_gc
+ c = Class.new
+ 10_000.times do
+ cc = Class.new(c)
+ 100.times { Class.new(cc) }
+ end
+ assert(c.subclasses.size <= 10_000)
+ end
+
+ def test_subclass_gc_stress
+ 10000.times do
+ c = Class.new
+ 100.times { Class.new(c) }
+ assert(c.subclasses.size <= 100)
+ end
+ end
+
+ def test_classext_memory_leak
+ assert_no_memory_leak([], <<-PREP, <<-CODE, rss: true)
+code = proc { Class.new }
+1_000.times(&code)
+PREP
+3_000_000.times(&code)
+CODE
+ end
end
diff --git a/test/ruby/test_complex.rb b/test/ruby/test_complex.rb
index a4fe9d4232..a3a7546575 100644
--- a/test/ruby/test_complex.rb
+++ b/test/ruby/test_complex.rb
@@ -220,6 +220,11 @@ class Complex_Test < Test::Unit::TestCase
def test_polar
assert_equal([1,2], Complex.polar(1,2).polar)
assert_equal(Complex.polar(1.0, Math::PI * 2 / 3), Complex.polar(1, Math::PI * 2 / 3))
+
+ assert_in_out_err([], <<-'end;', ['OK'], [])
+ Complex.polar(1, Complex(1, 0))
+ puts :OK
+ end;
end
def test_uplus
diff --git a/test/ruby/test_const.rb b/test/ruby/test_const.rb
index 1c73b66648..f6b9ea83d3 100644
--- a/test/ruby/test_const.rb
+++ b/test/ruby/test_const.rb
@@ -3,20 +3,30 @@
require 'test/unit'
class TestConst < Test::Unit::TestCase
- TEST1 = 1
- TEST2 = 2
- module Const
- TEST3 = 3
- TEST4 = 4
- end
+ Constants_Setup = -> do
+ remove_const :TEST1 if defined? ::TestConst::TEST1
+ remove_const :TEST2 if defined? ::TestConst::TEST2
+ remove_const :Const if defined? ::TestConst::Const
+ remove_const :Const2 if defined? ::TestConst::Const2
+
+ TEST1 = 1
+ TEST2 = 2
- module Const2
- TEST3 = 6
- TEST4 = 8
+ module Const
+ TEST3 = 3
+ TEST4 = 4
+ end
+
+ module Const2
+ TEST3 = 6
+ TEST4 = 8
+ end
end
def test_const
+ Constants_Setup.call
+
assert defined?(TEST1)
assert_equal 1, TEST1
assert defined?(TEST2)
diff --git a/test/ruby/test_defined.rb b/test/ruby/test_defined.rb
index 9976db3b6f..3324a09afe 100644
--- a/test/ruby/test_defined.rb
+++ b/test/ruby/test_defined.rb
@@ -23,40 +23,80 @@ class TestDefined < Test::Unit::TestCase
return !defined?(yield)
end
- def test_defined
+ def test_defined_global_variable
$x = nil
assert(defined?($x)) # global variable
assert_equal('global-variable', defined?($x))# returns description
+ end
+ def test_defined_local_variable
assert_nil(defined?(foo)) # undefined
foo=5
assert(defined?(foo)) # local variable
+ end
+ def test_defined_constant
assert(defined?(Array)) # constant
assert(defined?(::Array)) # toplevel constant
assert(defined?(File::Constants)) # nested constant
+ end
+
+ def test_defined_public_method
assert(defined?(Object.new)) # method
assert(defined?(Object::new)) # method
+ end
+
+ def test_defined_private_method
assert(!defined?(Object.print)) # private method
+ end
+
+ def test_defined_operator
assert(defined?(1 == 2)) # operator expression
+ end
+ def test_defined_protected_method
f = Foo.new
assert_nil(defined?(f.foo)) # protected method
f.bar(f) { |v| assert(v) }
+ f.bar(Class.new(Foo).new) { |v| assert(v, "inherited protected method") }
+ end
+
+ def test_defined_undefined_method
+ f = Foo.new
assert_nil(defined?(f.quux)) # undefined method
+ end
+
+ def test_defined_undefined_argument
+ f = Foo.new
assert_nil(defined?(f.baz(x))) # undefined argument
x = 0
assert(defined?(f.baz(x)))
assert_nil(defined?(f.quux(x)))
assert(defined?(print(x)))
assert_nil(defined?(quux(x)))
+ end
+
+ def test_defined_attrasgn
+ f = Foo.new
assert(defined?(f.attr = 1))
f.attrasgn_test { |v| assert(v) }
+ end
+
+ def test_defined_undef
+ x = Object.new
+ def x.foo; end
+ assert(defined?(x.foo))
+ x.instance_eval {undef :foo}
+ assert(!defined?(x.foo), "undefed method should not be defined?")
+ end
+ def test_defined_yield
assert(defined_test) # not iterator
assert(!defined_test{}) # called as iterator
+ end
+ def test_defined_matchdata
/a/ =~ ''
assert_equal nil, defined?($&)
assert_equal nil, defined?($`)
@@ -85,12 +125,65 @@ class TestDefined < Test::Unit::TestCase
assert_equal 'global-variable', defined?($+)
assert_equal 'global-variable', defined?($1)
assert_equal nil, defined?($2)
+ end
+ def test_defined_literal
assert_equal("nil", defined?(nil))
assert_equal("true", defined?(true))
assert_equal("false", defined?(false))
assert_equal("expression", defined?(1))
+ end
+
+ def test_defined_method
+ self_ = self
+ assert_equal("method", defined?(test_defined_method))
+ assert_equal("method", defined?(self.test_defined_method))
+ assert_equal("method", defined?(self_.test_defined_method))
+
+ assert_equal(nil, defined?(1.test_defined_method))
+ assert_equal("method", defined?(1.to_i))
+ assert_equal(nil, defined?(1.to_i.test_defined_method))
+ assert_equal(nil, defined?(1.test_defined_method.to_i))
+
+ assert_equal("method", defined?("x".reverse))
+ assert_equal("method", defined?("x".reverse(1)))
+ assert_equal("method", defined?("x".reverse.reverse))
+ assert_equal(nil, defined?("x".reverse(1).reverse))
+
+ assert_equal("method", defined?(1.to_i(10)))
+ assert_equal("method", defined?(1.to_i("x")))
+ assert_equal(nil, defined?(1.to_i("x").undefined))
+ assert_equal(nil, defined?(1.to_i(undefined).to_i))
+ assert_equal(nil, defined?(1.to_i("x").undefined.to_i))
+ assert_equal(nil, defined?(1.to_i(undefined).to_i.to_i))
+ end
+
+ def test_defined_method_single_call
+ times_called = 0
+ define_singleton_method(:t) do
+ times_called += 1
+ self
+ end
+ assert_equal("method", defined?(t))
+ assert_equal(0, times_called)
+
+ assert_equal("method", defined?(t.t))
+ assert_equal(1, times_called)
+
+ times_called = 0
+ assert_equal("method", defined?(t.t.t))
+ assert_equal(2, times_called)
+
+ times_called = 0
+ assert_equal("method", defined?(t.t.t.t))
+ assert_equal(3, times_called)
+
+ times_called = 0
+ assert_equal("method", defined?(t.t.t.t.t))
+ assert_equal(4, times_called)
+ end
+ def test_defined_empty_paren_expr
bug8224 = '[ruby-core:54024] [Bug #8224]'
(1..3).each do |level|
expr = "("*level+")"*level
@@ -214,6 +307,23 @@ class TestDefined < Test::Unit::TestCase
assert_separately([], "assert_nil(defined?(super))")
end
+ def test_respond_to
+ obj = "#{self.class.name}##{__method__}"
+ class << obj
+ def respond_to?(mid)
+ true
+ end
+ end
+ assert_warn(/deprecated method signature.*\n.*respond_to\? is defined here/) do
+ Warning[:deprecated] = true
+ defined?(obj.foo)
+ end
+ assert_warn('') do
+ Warning[:deprecated] = false
+ defined?(obj.foo)
+ end
+ end
+
class ExampleRespondToMissing
attr_reader :called
@@ -257,4 +367,53 @@ class TestDefined < Test::Unit::TestCase
def test_top_level_constant_not_defined
assert_nil(defined?(TestDefined::Object))
end
+
+ class RefinedClass
+ end
+
+ module RefiningModule
+ refine RefinedClass do
+ def pub
+ end
+
+ private
+
+ def priv
+ end
+ end
+
+ def self.call_without_using(x = RefinedClass.new)
+ defined?(x.pub)
+ end
+
+ def self.vcall_without_using(x = RefinedClass.new)
+ x.instance_eval {defined?(priv)}
+ end
+
+ using self
+
+ def self.call_with_using(x = RefinedClass.new)
+ defined?(x.pub)
+ end
+
+ def self.vcall_with_using(x = RefinedClass.new)
+ x.instance_eval {defined?(priv)}
+ end
+ end
+
+ def test_defined_refined_call_without_using
+ assert(!RefiningModule.call_without_using, "refined public method without using")
+ end
+
+ def test_defined_refined_vcall_without_using
+ assert(!RefiningModule.vcall_without_using, "refined private method without using")
+ end
+
+ def test_defined_refined_call_with_using
+ assert(RefiningModule.call_with_using, "refined public method with using")
+ end
+
+ def test_defined_refined_vcall_with_using
+ assert(RefiningModule.vcall_with_using, "refined private method with using")
+ end
end
diff --git a/test/ruby/test_dir.rb b/test/ruby/test_dir.rb
index 1bb228fd45..39a1dae889 100644
--- a/test/ruby/test_dir.rb
+++ b/test/ruby/test_dir.rb
@@ -8,7 +8,6 @@ class TestDir < Test::Unit::TestCase
def setup
@verbose = $VERBOSE
- $VERBOSE = nil
@root = File.realpath(Dir.mktmpdir('__test_dir__'))
@nodir = File.join(@root, "dummy")
@dirs = []
@@ -88,36 +87,67 @@ class TestDir < Test::Unit::TestCase
end
def test_chdir
- @pwd = Dir.pwd
- @env_home = ENV["HOME"]
- @env_logdir = ENV["LOGDIR"]
+ pwd = Dir.pwd
+ env_home = ENV["HOME"]
+ env_logdir = ENV["LOGDIR"]
ENV.delete("HOME")
ENV.delete("LOGDIR")
assert_raise(Errno::ENOENT) { Dir.chdir(@nodir) }
assert_raise(ArgumentError) { Dir.chdir }
- ENV["HOME"] = @pwd
+ ENV["HOME"] = pwd
Dir.chdir do
- assert_equal(@pwd, Dir.pwd)
- Dir.chdir(@root)
+ assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) }
+
+ assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(@root) }
+ assert_equal(@root, Dir.pwd)
+
+ assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) }
+
+ assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; Dir.chdir(@root) }.join }
+ assert_raise(RuntimeError) { Thread.new { Thread.current.report_on_exception = false; Dir.chdir(@root) { } }.join }
+
+ assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) }
+
+ assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(@root) }
assert_equal(@root, Dir.pwd)
+
+ assert_warning(/conflicting chdir during another chdir block/) { Dir.chdir(pwd) }
+ Dir.chdir(@root) do
+ assert_equal(@root, Dir.pwd)
+ end
+ assert_equal(pwd, Dir.pwd)
end
ensure
begin
- Dir.chdir(@pwd)
+ Dir.chdir(pwd)
rescue
- abort("cannot return the original directory: #{ @pwd }")
+ abort("cannot return the original directory: #{ pwd }")
end
- if @env_home
- ENV["HOME"] = @env_home
- else
- ENV.delete("HOME")
+ ENV["HOME"] = env_home
+ ENV["LOGDIR"] = env_logdir
+ end
+
+ def test_chdir_conflict
+ pwd = Dir.pwd
+ q = Thread::Queue.new
+ t = Thread.new do
+ q.pop
+ Dir.chdir(pwd) rescue $!
+ end
+ Dir.chdir(pwd) do
+ q.push nil
+ assert_instance_of(RuntimeError, t.value)
end
- if @env_logdir
- ENV["LOGDIR"] = @env_logdir
- else
- ENV.delete("LOGDIR")
+
+ t = Thread.new do
+ q.pop
+ Dir.chdir(pwd){} rescue $!
+ end
+ Dir.chdir(pwd) do
+ q.push nil
+ assert_instance_of(RuntimeError, t.value)
end
end
@@ -135,16 +165,26 @@ class TestDir < Test::Unit::TestCase
end
def test_glob
- assert_equal((%w(. ..) + ("a".."z").to_a).map{|f| File.join(@root, f) },
- Dir.glob(File.join(@root, "*"), File::FNM_DOTMATCH).sort)
- assert_equal([@root] + ("a".."z").map {|f| File.join(@root, f) }.sort,
- Dir.glob([@root, File.join(@root, "*")]).sort)
+ assert_equal((%w(.) + ("a".."z").to_a).map{|f| File.join(@root, f) },
+ Dir.glob(File.join(@root, "*"), File::FNM_DOTMATCH))
+ assert_equal([@root] + ("a".."z").map {|f| File.join(@root, f) },
+ Dir.glob([@root, File.join(@root, "*")]))
+ assert_equal([@root] + ("a".."z").map {|f| File.join(@root, f) },
+ Dir.glob([@root, File.join(@root, "*")], sort: false).sort)
+ assert_equal([@root] + ("a".."z").map {|f| File.join(@root, f) },
+ Dir.glob([@root, File.join(@root, "*")], sort: true))
assert_raise_with_message(ArgumentError, /nul-separated/) do
Dir.glob(@root + "\0\0\0" + File.join(@root, "*"))
end
+ assert_raise_with_message(ArgumentError, /expected true or false/) do
+ Dir.glob(@root, sort: 1)
+ end
+ assert_raise_with_message(ArgumentError, /expected true or false/) do
+ Dir.glob(@root, sort: nil)
+ end
- assert_equal(("a".."z").step(2).map {|f| File.join(File.join(@root, f), "") }.sort,
- Dir.glob(File.join(@root, "*/")).sort)
+ assert_equal(("a".."z").step(2).map {|f| File.join(File.join(@root, f), "") },
+ Dir.glob(File.join(@root, "*/")))
assert_equal([File.join(@root, '//a')], Dir.glob(@root + '//a'))
FileUtils.touch(File.join(@root, "{}"))
@@ -154,7 +194,7 @@ class TestDir < Test::Unit::TestCase
assert_equal([], Dir.glob(File.join(@root, '[a-\\')))
assert_equal([File.join(@root, "a")], Dir.glob(File.join(@root, 'a\\')))
- assert_equal(("a".."f").map {|f| File.join(@root, f) }.sort, Dir.glob(File.join(@root, '[abc/def]')).sort)
+ assert_equal(("a".."f").map {|f| File.join(@root, f) }, Dir.glob(File.join(@root, '[abc/def]')))
open(File.join(@root, "}}{}"), "wb") {}
open(File.join(@root, "}}a"), "wb") {}
@@ -171,6 +211,9 @@ class TestDir < Test::Unit::TestCase
Dir.chdir(@root) do
assert_include(Dir.glob("a/**/*", File::FNM_DOTMATCH), "a/.", bug8006)
+ Dir.mkdir("a/b")
+ assert_not_include(Dir.glob("a/**/*", File::FNM_DOTMATCH), "a/b/.")
+
FileUtils.mkdir_p("a/b/c/d/e/f")
assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/e/f"), bug6977)
assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/d/e/f"), bug6977)
@@ -184,7 +227,7 @@ class TestDir < Test::Unit::TestCase
dirs = ["a/.x", "a/b/.y"]
FileUtils.mkdir_p(dirs)
dirs.map {|dir| open("#{dir}/z", "w") {}}
- assert_equal([], Dir.glob("a/**/z").sort, bug8283)
+ assert_equal([], Dir.glob("a/**/z"), bug8283)
assert_equal(["a/.x/z"], Dir.glob("a/**/.x/z"), bug8283)
assert_equal(["a/.x/z"], Dir.glob("a/.x/**/z"), bug8283)
assert_equal(["a/b/.y/z"], Dir.glob("a/**/.y/z"), bug8283)
@@ -202,6 +245,9 @@ class TestDir < Test::Unit::TestCase
bug15540 = '[ruby-core:91110] [Bug #15540]'
assert_equal(["c/d/a/", "c/d/a/b/", "c/d/a/b/c/", "c/e/a/", "c/e/a/b/", "c/e/a/b/c/"],
Dir.glob('c/{d,e}/a/**/'), bug15540)
+
+ assert_equal(["c/e/a/", "c/e/a/b/", "c/e/a/b/c/", "c/d/a/", "c/d/a/b/", "c/d/a/b/c/"],
+ Dir.glob('c/{e,d}/a/**/'))
end
end
@@ -213,6 +259,31 @@ class TestDir < Test::Unit::TestCase
end
end
+ def test_glob_recursive_with_brace
+ Dir.chdir(@root) do
+ bug19042 = '[ruby-core:110220] [Bug #19042]'
+ %w"c/dir_a c/dir_b c/dir_b/dir".each do |d|
+ Dir.mkdir(d)
+ end
+ expected = %w"c/dir_a/file c/dir_b/dir/file"
+ expected.each do |f|
+ File.write(f, "")
+ end
+ assert_equal(expected, Dir.glob("**/{dir_a,dir_b/dir}/file"), bug19042)
+ end
+ end
+
+ def test_glob_order
+ Dir.chdir(@root) do
+ assert_equal(["#{@root}/a", "#{@root}/b"], Dir.glob("#{@root}/[ba]"))
+ assert_equal(["#{@root}/b", "#{@root}/a"], Dir.glob(%W"#{@root}/b #{@root}/a"))
+ assert_equal(["#{@root}/b", "#{@root}/a"], Dir.glob("#{@root}/{b,a}"))
+ end
+ assert_equal(["a", "b"], Dir.glob("[ba]", base: @root))
+ assert_equal(["b", "a"], Dir.glob(%W"b a", base: @root))
+ assert_equal(["b", "a"], Dir.glob("{b,a}", base: @root))
+ end
+
if Process.const_defined?(:RLIMIT_NOFILE)
def test_glob_too_may_open_files
assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}", chdir: @root)
@@ -237,21 +308,38 @@ class TestDir < Test::Unit::TestCase
Dir.mkdir(File.join(@root, "a/dir"))
dirs = @dirs + %w[a/dir/]
dirs.sort!
- assert_equal(files, Dir.glob("*/*.c", base: @root).sort)
- assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: ".").sort})
- assert_equal(%w[foo.c], Dir.chdir(@root) {Dir.glob("*.c", base: "a").sort})
- assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: "").sort})
- assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: nil).sort})
- assert_equal(@dirs, Dir.glob("*/", base: @root).sort)
- assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: ".").sort})
- assert_equal(%w[dir/], Dir.chdir(@root) {Dir.glob("*/", base: "a").sort})
- assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: "").sort})
- assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: nil).sort})
- assert_equal(dirs, Dir.glob("**/*/", base: @root).sort)
- assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: ".").sort})
- assert_equal(%w[dir/], Dir.chdir(@root) {Dir.glob("**/*/", base: "a").sort})
- assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: "").sort})
- assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: nil).sort})
+
+ assert_equal(files, Dir.glob("*/*.c", base: @root))
+ assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: ".")})
+ assert_equal(%w[foo.c], Dir.chdir(@root) {Dir.glob("*.c", base: "a")})
+ assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: "")})
+ assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: nil)})
+ assert_equal(@dirs, Dir.glob("*/", base: @root))
+ assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: ".")})
+ assert_equal(%w[dir/], Dir.chdir(@root) {Dir.glob("*/", base: "a")})
+ assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: "")})
+ assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: nil)})
+ assert_equal(dirs, Dir.glob("**/*/", base: @root))
+ assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: ".")})
+ assert_equal(%w[dir/], Dir.chdir(@root) {Dir.glob("**/*/", base: "a")})
+ assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: "")})
+ assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: nil)})
+
+ assert_equal(files, Dir.glob("*/*.c", base: @root, sort: false).sort)
+ assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: ".", sort: false).sort})
+ assert_equal(%w[foo.c], Dir.chdir(@root) {Dir.glob("*.c", base: "a", sort: false).sort})
+ assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: "", sort: false).sort})
+ assert_equal(files, Dir.chdir(@root) {Dir.glob("*/*.c", base: nil, sort: false).sort})
+ assert_equal(@dirs, Dir.glob("*/", base: @root))
+ assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: ".", sort: false).sort})
+ assert_equal(%w[dir/], Dir.chdir(@root) {Dir.glob("*/", base: "a", sort: false).sort})
+ assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: "", sort: false).sort})
+ assert_equal(@dirs, Dir.chdir(@root) {Dir.glob("*/", base: nil, sort: false).sort})
+ assert_equal(dirs, Dir.glob("**/*/", base: @root))
+ assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: ".", sort: false).sort})
+ assert_equal(%w[dir/], Dir.chdir(@root) {Dir.glob("**/*/", base: "a", sort: false).sort})
+ assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: "", sort: false).sort})
+ assert_equal(dirs, Dir.chdir(@root) {Dir.glob("**/*/", base: nil, sort: false).sort})
end
def test_glob_base_dir
@@ -260,12 +348,31 @@ class TestDir < Test::Unit::TestCase
Dir.mkdir(File.join(@root, "a/dir"))
dirs = @dirs + %w[a/dir/]
dirs.sort!
- assert_equal(files, Dir.open(@root) {|d| Dir.glob("*/*.c", base: d)}.sort)
+
+ assert_equal(files, Dir.open(@root) {|d| Dir.glob("*/*.c", base: d)})
assert_equal(%w[foo.c], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("*.c", base: d)}})
- assert_equal(@dirs, Dir.open(@root) {|d| Dir.glob("*/", base: d).sort})
- assert_equal(%w[dir/], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("*/", base: d).sort}})
- assert_equal(dirs, Dir.open(@root) {|d| Dir.glob("**/*/", base: d).sort})
- assert_equal(%w[dir/], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("**/*/", base: d).sort}})
+ assert_equal(@dirs, Dir.open(@root) {|d| Dir.glob("*/", base: d)})
+ assert_equal(%w[dir/], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("*/", base: d)}})
+ assert_equal(dirs, Dir.open(@root) {|d| Dir.glob("**/*/", base: d)})
+ assert_equal(%w[dir/], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("**/*/", base: d)}})
+
+ assert_equal(files, Dir.open(@root) {|d| Dir.glob("*/*.c", base: d, sort: false).sort})
+ assert_equal(%w[foo.c], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("*.c", base: d, sort: false).sort}})
+ assert_equal(@dirs, Dir.open(@root) {|d| Dir.glob("*/", base: d, sort: false).sort})
+ assert_equal(%w[dir/], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("*/", base: d, sort: false).sort}})
+ assert_equal(dirs, Dir.open(@root) {|d| Dir.glob("**/*/", base: d, sort: false).sort})
+ assert_equal(%w[dir/], Dir.chdir(@root) {Dir.open("a") {|d| Dir.glob("**/*/", base: d, sort: false).sort}})
+ end
+
+ def test_glob_ignore_casefold_invalid_encoding
+ bug14456 = "[ruby-core:85448]"
+ filename = "\u00AAa123".encode('ISO-8859-1')
+ File.write(File.join(@root, filename), "")
+ matches = Dir.chdir(@root) {|d| Dir.glob("*a123".encode('UTF-8'), File::FNM_CASEFOLD)}
+ assert_equal(1, matches.size, bug14456)
+ matches.each{|f| f.force_encoding('ISO-8859-1')}
+ # Handle MacOS/Windows, which saves under a different filename
+ assert_include([filename, "\u00C2\u00AAa123".encode('ISO-8859-1')], matches.first, bug14456)
end
def assert_entries(entries, children_only = false)
@@ -277,26 +384,52 @@ class TestDir < Test::Unit::TestCase
def test_entries
assert_entries(Dir.open(@root) {|dir| dir.entries})
- assert_entries(Dir.entries(@root).to_a)
+ assert_entries(Dir.entries(@root))
assert_raise(ArgumentError) {Dir.entries(@root+"\0")}
+ [Encoding::UTF_8, Encoding::ASCII_8BIT].each do |enc|
+ assert_equal(enc, Dir.entries(@root, encoding: enc).first.encoding)
+ end
end
def test_foreach
assert_entries(Dir.open(@root) {|dir| dir.each.to_a})
assert_entries(Dir.foreach(@root).to_a)
assert_raise(ArgumentError) {Dir.foreach(@root+"\0").to_a}
+ newdir = @root+"/new"
+ e = Dir.foreach(newdir)
+ assert_raise(Errno::ENOENT) {e.to_a}
+ Dir.mkdir(newdir)
+ File.write(newdir+"/a", "")
+ assert_equal(%w[. .. a], e.to_a.sort)
+ [Encoding::UTF_8, Encoding::ASCII_8BIT].each do |enc|
+ e = Dir.foreach(newdir, encoding: enc)
+ assert_equal(enc, e.to_a.first.encoding)
+ end
end
def test_children
assert_entries(Dir.open(@root) {|dir| dir.children}, true)
assert_entries(Dir.children(@root), true)
assert_raise(ArgumentError) {Dir.children(@root+"\0")}
+ [Encoding::UTF_8, Encoding::ASCII_8BIT].each do |enc|
+ assert_equal(enc, Dir.children(@root, encoding: enc).first.encoding)
+ end
end
def test_each_child
assert_entries(Dir.open(@root) {|dir| dir.each_child.to_a}, true)
assert_entries(Dir.each_child(@root).to_a, true)
assert_raise(ArgumentError) {Dir.each_child(@root+"\0").to_a}
+ newdir = @root+"/new"
+ e = Dir.each_child(newdir)
+ assert_raise(Errno::ENOENT) {e.to_a}
+ Dir.mkdir(newdir)
+ File.write(newdir+"/a", "")
+ assert_equal(%w[a], e.to_a)
+ [Encoding::UTF_8, Encoding::ASCII_8BIT].each do |enc|
+ e = Dir.each_child(newdir, encoding: enc)
+ assert_equal(enc, e.to_a.first.encoding)
+ end
end
def test_dir_enc
@@ -337,10 +470,10 @@ class TestDir < Test::Unit::TestCase
end
assert_equal([*"a".."z", *"symlink-a".."symlink-z"].each_slice(2).map {|f, _| File.join(@root, f + "/") }.sort,
- Dir.glob(File.join(@root, "*/")).sort)
+ Dir.glob(File.join(@root, "*/")))
- assert_equal([@root + "/", *[*"a".."z"].each_slice(2).map {|f, _| File.join(@root, f + "/") }.sort],
- Dir.glob(File.join(@root, "**/")).sort)
+ assert_equal([@root + "/", *[*"a".."z"].each_slice(2).map {|f, _| File.join(@root, f + "/") }],
+ Dir.glob(File.join(@root, "**/")))
end
def test_glob_metachar
@@ -424,8 +557,8 @@ class TestDir < Test::Unit::TestCase
Dir.mkdir('some-dir')
File.write('some-dir/foo', 'some content')
- assert_equal [ 'dir-symlink', 'some-dir' ], Dir['*'].sort
- assert_equal [ 'dir-symlink', 'some-dir', 'some-dir/foo' ], Dir['**/*'].sort
+ assert_equal [ 'dir-symlink', 'some-dir' ], Dir['*']
+ assert_equal [ 'dir-symlink', 'some-dir', 'some-dir/foo' ], Dir['**/*']
end
end
end
@@ -471,9 +604,21 @@ class TestDir < Test::Unit::TestCase
ensure
fs.clear
end
- list = Dir.glob("*").sort
+ list = Dir.glob("*")
assert_not_empty(list)
assert_equal([*"a".."z"], list)
end;
end if defined?(Process::RLIMIT_NOFILE)
+
+ def test_glob_array_with_destructive_element
+ args = Array.new(100, "")
+ pat = Struct.new(:ary).new(args)
+ args.push(pat, *Array.new(100) {"."*40})
+ def pat.to_path
+ ary.clear
+ GC.start
+ ""
+ end
+ assert_empty(Dir.glob(args))
+ end
end
diff --git a/test/ruby/test_dir_m17n.rb b/test/ruby/test_dir_m17n.rb
index c2c0c4999e..67bad8a514 100644
--- a/test/ruby/test_dir_m17n.rb
+++ b/test/ruby/test_dir_m17n.rb
@@ -17,27 +17,19 @@ class TestDir_M17N < Test::Unit::TestCase
assert_separately(["-E#{encoding}"], <<-EOS, :chdir=>dir)
filename = #{code}.chr('UTF-8').force_encoding("#{encoding}")
File.open(filename, "w") {}
- opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
- ents = Dir.entries(".", **(opts||{}))
+ ents = Dir.entries(".")
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ filename = filename.encode("UTF-8")
+ end
assert_include(ents, filename)
EOS
return if /cygwin/ =~ RUBY_PLATFORM
assert_separately(%w[-EASCII-8BIT], <<-EOS, :chdir=>dir)
filename = #{code}.chr('UTF-8').force_encoding("ASCII-8BIT")
- opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
- ents = Dir.entries(".", **(opts||{}))
- expected_filename = #{code}.chr('UTF-8').encode(Encoding.find("filesystem")) rescue expected_filename = "?"
- expected_filename = expected_filename.force_encoding("ASCII-8BIT")
+ ents = Dir.entries(".")
if /mswin|mingw/ =~ RUBY_PLATFORM
- case
- when ents.include?(filename)
- when ents.include?(expected_filename)
- filename = expected_filename
- else
- ents = Dir.entries(".", :encoding => Encoding.find("filesystem"))
- filename = expected_filename
- end
+ filename.force_encoding("UTF-8")
end
assert_include(ents, filename)
EOS
@@ -199,27 +191,23 @@ class TestDir_M17N < Test::Unit::TestCase
assert_separately(%w[-EEUC-JP], <<-'EOS', :chdir=>d)
filename = "\xA4\xA2".force_encoding("euc-jp")
File.open(filename, "w") {}
- opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
- ents = Dir.entries(".", **(opts||{}))
+ ents = Dir.entries(".")
if /darwin/ =~ RUBY_PLATFORM
filename = filename.encode("utf-8").force_encoding("euc-jp")
+ elsif /mswin|mingw/ =~ RUBY_PLATFORM
+ filename = filename.encode("utf-8")
end
assert_include(ents, filename)
EOS
assert_separately(%w[-EASCII-8BIT], <<-'EOS', :chdir=>d)
filename = "\xA4\xA2".force_encoding('ASCII-8BIT')
- win_expected_filename = filename.encode(Encoding.find("filesystem"), "euc-jp") rescue "?"
- opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
- ents = Dir.entries(".", **(opts||{}))
+ ents = Dir.entries(".")
unless ents.include?(filename)
case RUBY_PLATFORM
when /darwin/
filename = filename.encode("utf-8", "euc-jp").b
when /mswin|mingw/
- if ents.include?(win_expected_filename.b)
- ents = Dir.entries(".", :encoding => Encoding.find("filesystem"))
- filename = win_expected_filename
- end
+ filename = filename.encode("utf-8", "euc-jp")
end
end
assert_include(ents, filename)
@@ -414,13 +402,8 @@ class TestDir_M17N < Test::Unit::TestCase
orig.each {|n| open(n, "w") {}}
enc = Encoding.find("filesystem")
enc = Encoding::ASCII_8BIT if enc == Encoding::US_ASCII
- if /mswin|mingw/ =~ RUBY_PLATFORM
- opts = {:encoding => enc}
- orig.map! {|o| o.encode("filesystem") rescue o.tr("^a-z", "?")}
- else
- orig.each {|o| o.force_encoding(enc) }
- end
- ents = Dir.entries(".", **(opts||{})).reject {|n| /\A\./ =~ n}
+ orig.each {|o| o.force_encoding(enc) }
+ ents = Dir.entries(".").reject {|n| /\A\./ =~ n}
ents.sort!
PP.assert_equal(orig, ents, bug7267)
}
@@ -431,13 +414,9 @@ class TestDir_M17N < Test::Unit::TestCase
expected = []
results = []
orig.each {|o|
- if /mswin|mingw/ =~ RUBY_PLATFORM
- n = (o.encode("filesystem") rescue next)
- else
- enc = Encoding.find("filesystem")
- enc = Encoding::ASCII_8BIT if enc == Encoding::US_ASCII
- n = o.dup.force_encoding(enc)
- end
+ enc = Encoding.find("filesystem")
+ enc = Encoding::ASCII_8BIT if enc == Encoding::US_ASCII
+ n = o.dup.force_encoding(enc)
expected << n
with_tmpdir {
Dir.mkdir(o)
diff --git a/test/ruby/test_econv.rb b/test/ruby/test_econv.rb
index a469614d84..1aad0de347 100644
--- a/test/ruby/test_econv.rb
+++ b/test/ruby/test_econv.rb
@@ -803,7 +803,7 @@ class TestEncodingConverter < Test::Unit::TestCase
assert_equal('', ec.finish)
ec = Encoding::Converter.new("", "xml_attr_content_escape")
- assert_equal('&amp;&lt;&gt;&quot;', ec.convert("&<>\""))
+ assert_equal('&amp;&lt;&gt;&quot;&apos;', ec.convert("&<>\"'"))
assert_equal('', ec.finish)
end
@@ -844,7 +844,7 @@ class TestEncodingConverter < Test::Unit::TestCase
def test_xml_hasharg
assert_equal("&amp;\e$B$&\e(B&#x2665;&amp;\"'".force_encoding("iso-2022-jp"),
"&\u3046\u2665&\"'".encode("iso-2022-jp", xml: :text))
- assert_equal("\"&amp;\e$B$&\e(B&#x2661;&amp;&quot;'\"".force_encoding("iso-2022-jp"),
+ assert_equal("\"&amp;\e$B$&\e(B&#x2661;&amp;&quot;&apos;\"".force_encoding("iso-2022-jp"),
"&\u3046\u2661&\"'".encode("iso-2022-jp", xml: :attr))
assert_equal("&amp;\u3046\u2661&amp;\"'".force_encoding("utf-8"),
@@ -912,6 +912,21 @@ class TestEncodingConverter < Test::Unit::TestCase
assert_raise_with_message(ArgumentError, /\u{3042}/) {
Encoding::Converter.new("", "", newline: "\u{3042}".to_sym)
}
+ newlines = %i[universal_newline crlf_newline cr_newline]
+ (2..newlines.size).each do |i|
+ newlines.combination(i) do |opts|
+ assert_raise(Encoding::ConverterNotFoundError, "#{opts} are mutually exclusive") do
+ Encoding::Converter.new("", "", **opts.inject({}) {|o,nl|o[nl]=true;o})
+ end
+ end
+ end
+ newlines.each do |nl|
+ opts = {newline: :universal, nl => true}
+ ec2 = assert_warning(/:newline option precedes/, opts.inspect) do
+ Encoding::Converter.new("", "", **opts)
+ end
+ assert_equal(ec1, ec2)
+ end
end
def test_default_external
diff --git a/test/ruby/test_encoding.rb b/test/ruby/test_encoding.rb
index 019cb2417f..4a6dd932ed 100644
--- a/test/ruby/test_encoding.rb
+++ b/test/ruby/test_encoding.rb
@@ -56,11 +56,36 @@ class TestEncoding < Test::Unit::TestCase
end
def test_replicate
- assert_instance_of(Encoding, Encoding::UTF_8.replicate('UTF-8-ANOTHER'))
- assert_instance_of(Encoding, Encoding::ISO_2022_JP.replicate('ISO-2022-JP-ANOTHER'))
+ assert_separately([], "#{<<~'END;'}")
+ assert_instance_of(Encoding, Encoding::UTF_8.replicate("UTF-8-ANOTHER#{Time.now.to_f}"))
+ assert_instance_of(Encoding, Encoding::ISO_2022_JP.replicate("ISO-2022-JP-ANOTHER#{Time.now.to_f}"))
bug3127 = '[ruby-dev:40954]'
assert_raise(TypeError, bug3127) {Encoding::UTF_8.replicate(0)}
- assert_raise(ArgumentError, bug3127) {Encoding::UTF_8.replicate("\0")}
+ assert_raise_with_message(ArgumentError, /\bNUL\b/, bug3127) {Encoding::UTF_8.replicate("\0")}
+ END;
+ end
+
+ def test_extra_encoding
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ 200.times {|i|
+ Encoding::UTF_8.replicate("dummy#{i}")
+ }
+ e = Encoding.list.last
+ format = "%d".force_encoding(e)
+ assert_equal("0", format % 0)
+ assert_equal(e, format.dup.encoding)
+ assert_equal(e, (format*1).encoding)
+
+ assert_equal(e, (("x"*30).force_encoding(e)*1).encoding)
+ GC.start
+
+ name = "A" * 64
+ Encoding.list.each do |enc|
+ assert_raise(ArgumentError) {enc.replicate(name)}
+ name.succ!
+ end
+ end;
end
def test_dummy_p
@@ -130,7 +155,7 @@ class TestEncoding < Test::Unit::TestCase
assert_equal(Encoding::US_ASCII, __ENCODING__)
$:.unshift("/\x80")
assert_raise_with_message(LoadError, /\[Bug #16382\]/) do
- $:.resolve_feature_path "[Bug #16382]"
+ require "[Bug #16382]"
end
end;
end
diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb
index 7b647231c8..b0c43b9a7f 100644
--- a/test/ruby/test_enum.rb
+++ b/test/ruby/test_enum.rb
@@ -27,7 +27,6 @@ class TestEnumerable < Test::Unit::TestCase
end
end
@verbose = $VERBOSE
- $VERBOSE = nil
end
def teardown
@@ -63,11 +62,32 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal([[2, 1], [2, 4]], a)
end
+ def test_grep_optimization
+ bug17030 = '[ruby-core:99156]'
+ 'set last match' =~ /set last (.*)/
+ assert_equal([:a, 'b', :c], [:a, 'b', 'z', :c, 42, nil].grep(/[a-d]/), bug17030)
+ assert_equal(['z', 42, nil], [:a, 'b', 'z', :c, 42, nil].grep_v(/[a-d]/), bug17030)
+ assert_equal('match', $1, bug17030)
+
+ regexp = Regexp.new('x')
+ assert_equal([], @obj.grep(regexp), bug17030) # sanity check
+ def regexp.===(other)
+ true
+ end
+ assert_equal([1, 2, 3, 1, 2], @obj.grep(regexp), bug17030)
+
+ o = Object.new
+ def o.to_str
+ 'hello'
+ end
+ assert_same(o, [o].grep(/ll/).first, bug17030)
+ end
+
def test_count
assert_equal(5, @obj.count)
assert_equal(2, @obj.count(1))
assert_equal(3, @obj.count {|x| x % 2 == 1 })
- assert_equal(2, @obj.count(1) {|x| x % 2 == 1 })
+ assert_equal(2, assert_warning(/given block not used/) {@obj.count(1) {|x| x % 2 == 1 }})
assert_raise(ArgumentError) { @obj.count(0, 1) }
if RUBY_ENGINE == "ruby"
@@ -95,7 +115,7 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal(1, @obj.find_index {|x| x % 2 == 0 })
assert_equal(nil, @obj.find_index {|x| false })
assert_raise(ArgumentError) { @obj.find_index(0, 1) }
- assert_equal(1, @obj.find_index(2) {|x| x == 1 })
+ assert_equal(1, assert_warning(/given block not used/) {@obj.find_index(2) {|x| x == 1 }})
end
def test_find_all
@@ -114,6 +134,12 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal([1, 2, 3, 1, 2], @obj.to_a)
end
+ def test_to_a_keywords
+ @obj.singleton_class.remove_method(:each)
+ def @obj.each(foo:) yield foo end
+ assert_equal([1], @obj.to_a(foo: 1))
+ end
+
def test_to_a_size_symbol
sym = Object.new
class << sym
@@ -206,7 +232,7 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal(48, @obj.inject {|z, x| z * 2 + x })
assert_equal(12, @obj.inject(:*))
assert_equal(24, @obj.inject(2) {|z, x| z * x })
- assert_equal(24, @obj.inject(2, :*) {|z, x| z * x })
+ assert_equal(24, assert_warning(/given block not used/) {@obj.inject(2, :*) {|z, x| z * x }})
assert_equal(nil, @empty.inject() {9})
end
@@ -228,17 +254,75 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal(15, [3, 5, 7].inject(:+))
assert_float_equal(15.0, [3, 5, 7.0].inject(:+))
assert_equal(2*FIXNUM_MAX, Array.new(2, FIXNUM_MAX).inject(:+))
+ assert_equal(3*FIXNUM_MAX, Array.new(3, FIXNUM_MAX).inject(:+))
assert_equal(2*(FIXNUM_MAX+1), Array.new(2, FIXNUM_MAX+1).inject(:+))
assert_equal(10*FIXNUM_MAX, Array.new(10, FIXNUM_MAX).inject(:+))
assert_equal(0, ([FIXNUM_MAX, 1, -FIXNUM_MAX, -1]*10).inject(:+))
assert_equal(FIXNUM_MAX*10, ([FIXNUM_MAX+1, -1]*10).inject(:+))
assert_equal(2*FIXNUM_MIN, Array.new(2, FIXNUM_MIN).inject(:+))
+ assert_equal(3*FIXNUM_MIN, Array.new(3, FIXNUM_MIN).inject(:+))
assert_equal((FIXNUM_MAX+1).to_f, [FIXNUM_MAX, 1, 0.0].inject(:+))
assert_float_equal(10.0, [3.0, 5].inject(2.0, :+))
assert_float_equal((FIXNUM_MAX+1).to_f, [0.0, FIXNUM_MAX+1].inject(:+))
assert_equal(2.0+3.0i, [2.0, 3.0i].inject(:+))
end
+ def test_inject_op_redefined
+ assert_separately([], "#{<<~"end;"}\n""end")
+ k = Class.new do
+ include Enumerable
+ def each
+ yield 1
+ yield 2
+ yield 3
+ end
+ end
+ all_assertions_foreach("", *%i[+ * / - %]) do |op|
+ bug = '[ruby-dev:49510] [Bug#12178] should respect redefinition'
+ begin
+ Integer.class_eval do
+ alias_method :orig, op
+ define_method(op) do |x|
+ 0
+ end
+ end
+ assert_equal(0, k.new.inject(op), bug)
+ ensure
+ Integer.class_eval do
+ undef_method op
+ alias_method op, :orig
+ end
+ end
+ end;
+ end
+
+ def test_inject_op_private
+ assert_separately([], "#{<<~"end;"}\n""end")
+ k = Class.new do
+ include Enumerable
+ def each
+ yield 1
+ yield 2
+ yield 3
+ end
+ end
+ all_assertions_foreach("", *%i[+ * / - %]) do |op|
+ bug = '[ruby-core:81349] [Bug #13592] should respect visibility'
+ assert_raise_with_message(NoMethodError, /private method/, bug) do
+ begin
+ Integer.class_eval do
+ private op
+ end
+ k.new.inject(op)
+ ensure
+ Integer.class_eval do
+ public op
+ end
+ end
+ end
+ end;
+ end
+
def test_inject_array_op_redefined
assert_separately([], "#{<<~"end;"}\n""end")
all_assertions_foreach("", *%i[+ * / - %]) do |op|
@@ -279,6 +363,25 @@ class TestEnumerable < Test::Unit::TestCase
end;
end
+ def test_refine_Enumerable_then_include
+ assert_separately([], "#{<<~"end;"}\n")
+ module RefinementBug
+ refine Enumerable do
+ def refined_method
+ :rm
+ end
+ end
+ end
+ using RefinementBug
+
+ class A
+ include Enumerable
+ end
+
+ assert_equal(:rm, [].refined_method)
+ end;
+ end
+
def test_partition
assert_equal([[1, 3, 1], [2, 2]], @obj.partition {|x| x % 2 == 1 })
cond = ->(x, i) { x % 2 == 1 }
@@ -297,6 +400,45 @@ class TestEnumerable < Test::Unit::TestCase
def test_tally
h = {1 => 2, 2 => 2, 3 => 1}
assert_equal(h, @obj.tally)
+
+ h = {1 => 5, 2 => 2, 3 => 1, 4 => "x"}
+ assert_equal(h, @obj.tally({1 => 3, 4 => "x"}))
+
+ assert_raise(TypeError) do
+ @obj.tally({1 => ""})
+ end
+
+ h = {1 => 2, 2 => 2, 3 => 1}
+ assert_same(h, @obj.tally(h))
+
+ h = {1 => 2, 2 => 2, 3 => 1}.freeze
+ assert_raise(FrozenError) do
+ @obj.tally(h)
+ end
+ assert_equal({1 => 2, 2 => 2, 3 => 1}, h)
+
+ hash_convertible = Object.new
+ def hash_convertible.to_hash
+ {1 => 3, 4 => "x"}
+ end
+ assert_equal({1 => 5, 2 => 2, 3 => 1, 4 => "x"}, @obj.tally(hash_convertible))
+
+ hash_convertible = Object.new
+ def hash_convertible.to_hash
+ {1 => 3, 4 => "x"}.freeze
+ end
+ assert_raise(FrozenError) do
+ @obj.tally(hash_convertible)
+ end
+ assert_equal({1 => 3, 4 => "x"}, hash_convertible.to_hash)
+
+ assert_raise(TypeError) do
+ @obj.tally(BasicObject.new)
+ end
+
+ h = {1 => 2, 2 => 2, 3 => 1}
+ assert_equal(h, @obj.tally(Hash.new(100)))
+ assert_equal(h, @obj.tally(Hash.new {100}))
end
def test_first
@@ -319,6 +461,17 @@ class TestEnumerable < Test::Unit::TestCase
empty.first
empty.block.call
end;
+
+ bug18475 = '[ruby-dev:107059]'
+ assert_in_out_err([], <<-'end;', [], /unexpected break/, bug18475)
+ e = Enumerator.new do |g|
+ Thread.new do
+ g << 1
+ end.join
+ end
+
+ e.first
+ end;
end
def test_sort
@@ -341,7 +494,7 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal(false, [true, true, false].all?)
assert_equal(true, [].all?)
assert_equal(true, @empty.all?)
- assert_equal(true, @obj.all?(Fixnum))
+ assert_equal(true, @obj.all?(Integer))
assert_equal(false, @obj.all?(1..2))
end
@@ -595,6 +748,9 @@ class TestEnumerable < Test::Unit::TestCase
ary.clear
(1..10).each_slice(11) {|a| ary << a}
assert_equal([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]], ary)
+
+ assert_equal(1..10, (1..10).each_slice(3) { })
+ assert_equal([], [].each_slice(3) { })
end
def test_each_cons
@@ -614,6 +770,9 @@ class TestEnumerable < Test::Unit::TestCase
ary.clear
(1..5).each_cons(6) {|a| ary << a}
assert_empty(ary)
+
+ assert_equal(1..5, (1..5).each_cons(3) { })
+ assert_equal([], [].each_cons(3) { })
end
def test_zip
@@ -1134,6 +1293,21 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal([1, [1, 2]], Foo.new.to_enum.uniq)
end
+ def test_compact
+ class << (enum = Object.new)
+ include Enumerable
+ def each
+ yield 3
+ yield nil
+ yield 7
+ yield 9
+ yield nil
+ end
+ end
+
+ assert_equal([3, 7, 9], enum.compact)
+ end
+
def test_transient_heap_sort_by
klass = Class.new do
include Comparable
diff --git a/test/ruby/test_enumerator.rb b/test/ruby/test_enumerator.rb
index 75cf1aeec6..c823b79c6d 100644
--- a/test/ruby/test_enumerator.rb
+++ b/test/ruby/test_enumerator.rb
@@ -69,18 +69,17 @@ class TestEnumerator < Test::Unit::TestCase
def test_initialize
assert_equal([1, 2, 3], @obj.to_enum(:foo, 1, 2, 3).to_a)
- _, err = capture_io do
- assert_equal([1, 2, 3], Enumerator.new(@obj, :foo, 1, 2, 3).to_a)
- end
- assert_match 'Enumerator.new without a block is deprecated', err
+ assert_raise(ArgumentError) {
+ Enumerator.new(@obj, :foo, 1, 2, 3)
+ }
assert_equal([1, 2, 3], Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.take(3))
assert_raise(ArgumentError) { Enumerator.new }
enum = @obj.to_enum
assert_raise(NoMethodError) { enum.each {} }
enum.freeze
- assert_raise(FrozenError) {
- capture_io do
+ assert_raise(ArgumentError) {
+ capture_output do
# warning: Enumerator.new without a block is deprecated; use Object#to_enum
enum.__send__(:initialize, @obj, :foo)
end
@@ -697,6 +696,11 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal([0, 1], u.force)
end
+ def test_compact
+ u = [0, 1, nil, 2, 3, nil].to_enum.lazy.compact
+ assert_equal([0, 1, 2, 3], u.force)
+ end
+
def test_enum_chain_and_plus
r = 1..5
@@ -812,6 +816,28 @@ class TestEnumerator < Test::Unit::TestCase
)
end
+ def test_chain_with_index
+ assert_equal([[3, 0], [4, 1]], [3].chain([4]).with_index.to_a)
+ end
+
+ def test_lazy_chain
+ ea = (10..).lazy.select(&:even?).take(10)
+ ed = (20..).lazy.select(&:odd?)
+ chain = (ea + ed).select{|x| x % 3 == 0}
+ assert_equal(12, chain.next)
+ assert_equal(18, chain.next)
+ assert_equal(24, chain.next)
+ assert_equal(21, chain.next)
+ assert_equal(27, chain.next)
+ assert_equal(33, chain.next)
+ end
+
+ def test_chain_undef_methods
+ chain = [1].to_enum + [2].to_enum
+ meths = (chain.methods & [:feed, :next, :next_values, :peek, :peek_values])
+ assert_equal(0, meths.size)
+ end
+
def test_produce
assert_raise(ArgumentError) { Enumerator.produce }
diff --git a/test/ruby/test_env.rb b/test/ruby/test_env.rb
index f93cd503d8..87ccd5102b 100644
--- a/test/ruby/test_env.rb
+++ b/test/ruby/test_env.rb
@@ -22,7 +22,6 @@ class TestEnv < Test::Unit::TestCase
def setup
@verbose = $VERBOSE
- $VERBOSE = nil
@backup = ENV.to_hash
ENV.delete('test')
ENV.delete('TEST')
@@ -63,6 +62,46 @@ class TestEnv < Test::Unit::TestCase
}
end
+ def test_dup
+ assert_raise(TypeError) {
+ ENV.dup
+ }
+ end
+
+ def test_clone
+ warning = /ENV\.clone is deprecated; use ENV\.to_h instead/
+ clone = assert_deprecated_warning(warning) {
+ ENV.clone
+ }
+ assert_same(ENV, clone)
+
+ clone = assert_deprecated_warning(warning) {
+ ENV.clone(freeze: false)
+ }
+ assert_same(ENV, clone)
+
+ clone = assert_deprecated_warning(warning) {
+ ENV.clone(freeze: nil)
+ }
+ assert_same(ENV, clone)
+
+ assert_raise(TypeError) {
+ ENV.clone(freeze: true)
+ }
+ assert_raise(ArgumentError) {
+ ENV.clone(freeze: 1)
+ }
+ assert_raise(ArgumentError) {
+ ENV.clone(foo: false)
+ }
+ assert_raise(ArgumentError) {
+ ENV.clone(1)
+ }
+ assert_raise(ArgumentError) {
+ ENV.clone(1, foo: false)
+ }
+ end
+
def test_has_value
val = 'a'
val.succ! while ENV.has_value?(val) || ENV.has_value?(val.upcase)
@@ -84,7 +123,6 @@ class TestEnv < Test::Unit::TestCase
ENV['test'] = val[0...-1]
assert_nil(ENV.key(val))
- assert_nil(ENV.index(val))
assert_nil(ENV.key(val.upcase))
ENV['test'] = val
if IGNORE_CASE
@@ -106,6 +144,7 @@ class TestEnv < Test::Unit::TestCase
assert_invalid_env {|v| ENV.delete(v)}
assert_nil(ENV.delete("TEST"))
assert_nothing_raised { ENV.delete(PATH_ENV) }
+ assert_equal("NO TEST", ENV.delete("TEST") {|name| "NO "+name})
end
def test_getenv
@@ -286,6 +325,17 @@ class TestEnv < Test::Unit::TestCase
assert_equal({"foo"=>"bar", "baz"=>"qux"}, ENV.slice("foo", "baz"))
end
+ def test_except
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ ENV["bar"] = "rab"
+ assert_equal({"bar"=>"rab", "baz"=>"qux", "foo"=>"bar"}, ENV.except())
+ assert_equal({"bar"=>"rab", "baz"=>"qux", "foo"=>"bar"}, ENV.except(""))
+ assert_equal({"bar"=>"rab", "baz"=>"qux", "foo"=>"bar"}, ENV.except("unknown"))
+ assert_equal({"bar"=>"rab"}, ENV.except("foo", "baz"))
+ end
+
def test_clear
ENV.clear
assert_equal(0, ENV.size)
@@ -358,7 +408,8 @@ class TestEnv < Test::Unit::TestCase
assert_equal("foo", v)
end
assert_invalid_env {|var| ENV.assoc(var)}
- assert_equal(Encoding.find("locale"), v.encoding)
+ encoding = /mswin|mingw/ =~ RUBY_PLATFORM ? Encoding::UTF_8 : Encoding.find("locale")
+ assert_equal(encoding, v.encoding)
end
def test_has_value2
@@ -406,8 +457,8 @@ class TestEnv < Test::Unit::TestCase
def check(as, bs)
if IGNORE_CASE
- as = as.map {|xs| xs.map {|x| x.upcase } }
- bs = bs.map {|xs| xs.map {|x| x.upcase } }
+ as = as.map {|k, v| [k.upcase, v] }
+ bs = bs.map {|k, v| [k.upcase, v] }
end
assert_equal(as.sort, bs.sort)
end
@@ -433,6 +484,8 @@ class TestEnv < Test::Unit::TestCase
ENV["foo"] = "xxx"
ENV.replace({"foo"=>"bar", "baz"=>"qux"})
check(ENV.to_hash.to_a, [%w(foo bar), %w(baz qux)])
+ ENV.replace({"Foo"=>"Bar", "Baz"=>"Qux"})
+ check(ENV.to_hash.to_a, [%w(Foo Bar), %w(Baz Qux)])
end
def test_update
@@ -450,15 +503,15 @@ class TestEnv < Test::Unit::TestCase
end
def test_huge_value
- huge_value = "bar" * 40960
- ENV["foo"] = "bar"
- if /mswin/ =~ RUBY_PLATFORM
- assert_raise(Errno::EINVAL) { ENV["foo"] = huge_value }
- assert_equal("bar", ENV["foo"])
+ if /mswin|ucrt/ =~ RUBY_PLATFORM
+ # On Windows >= Vista each environment variable can be max 32768 characters
+ huge_value = "bar" * 10900
else
- assert_nothing_raised { ENV["foo"] = huge_value }
- assert_equal(huge_value, ENV["foo"])
+ huge_value = "bar" * 40960
end
+ ENV["foo"] = "overwritten"
+ assert_nothing_raised { ENV["foo"] = huge_value }
+ assert_equal(huge_value, ENV["foo"])
end
if /mswin|mingw/ =~ RUBY_PLATFORM
@@ -525,6 +578,892 @@ class TestEnv < Test::Unit::TestCase
assert_nil(e1, bug12475)
end
+ def ignore_case_str
+ IGNORE_CASE ? "true" : "false"
+ end
+
+ def str_for_yielding_exception_class(code_str, exception_var: "raised_exception")
+ <<-"end;"
+ #{exception_var} = nil
+ begin
+ #{code_str}
+ rescue Exception => e
+ #{exception_var} = e
+ end
+ Ractor.yield #{exception_var}.class
+ end;
+ end
+
+ def str_for_assert_raise_on_yielded_exception_class(expected_error_class, ractor_var)
+ <<-"end;"
+ error_class = #{ractor_var}.take
+ assert_raise(#{expected_error_class}) do
+ if error_class < Exception
+ raise error_class
+ end
+ end
+ end;
+ end
+
+ def str_to_yield_invalid_envvar_errors(var_name, code_str)
+ <<-"end;"
+ envvars_to_check = [
+ "foo\0bar",
+ "#{'\xa1\xa1'}".force_encoding(Encoding::UTF_16LE),
+ "foo".force_encoding(Encoding::ISO_2022_JP),
+ ]
+ envvars_to_check.each do |#{var_name}|
+ #{str_for_yielding_exception_class(code_str)}
+ end
+ end;
+ end
+
+ def str_to_receive_invalid_envvar_errors(ractor_var)
+ <<-"end;"
+ 3.times do
+ #{str_for_assert_raise_on_yielded_exception_class(ArgumentError, ractor_var)}
+ end
+ end;
+ end
+
+ STR_DEFINITION_FOR_CHECK = %Q{
+ def check(as, bs)
+ if #{IGNORE_CASE ? "true" : "false"}
+ as = as.map {|k, v| [k.upcase, v] }
+ bs = bs.map {|k, v| [k.upcase, v] }
+ end
+ assert_equal(as.sort, bs.sort)
+ end
+ }
+
+ def test_bracket_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ Ractor.yield ENV['test']
+ Ractor.yield ENV['TEST']
+ ENV['test'] = 'foo'
+ Ractor.yield ENV['test']
+ Ractor.yield ENV['TEST']
+ ENV['TEST'] = 'bar'
+ Ractor.yield ENV['TEST']
+ Ractor.yield ENV['test']
+ #{str_for_yielding_exception_class("ENV[1]")}
+ #{str_for_yielding_exception_class("ENV[1] = 'foo'")}
+ #{str_for_yielding_exception_class("ENV['test'] = 0")}
+ end
+ assert_nil(r.take)
+ assert_nil(r.take)
+ assert_equal('foo', r.take)
+ if #{ignore_case_str}
+ assert_equal('foo', r.take)
+ else
+ assert_nil(r.take)
+ end
+ assert_equal('bar', r.take)
+ if #{ignore_case_str}
+ assert_equal('bar', r.take)
+ else
+ assert_equal('foo', r.take)
+ end
+ 3.times do
+ #{str_for_assert_raise_on_yielded_exception_class(TypeError, "r")}
+ end
+ end;
+ end
+
+ def test_dup_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ #{str_for_yielding_exception_class("ENV.dup")}
+ end
+ #{str_for_assert_raise_on_yielded_exception_class(TypeError, "r")}
+ end;
+ end
+
+ def test_clone_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ original_warning_state = Warning[:deprecated]
+ Warning[:deprecated] = false
+
+ begin
+ Ractor.yield ENV.clone.object_id
+ Ractor.yield ENV.clone(freeze: false).object_id
+ Ractor.yield ENV.clone(freeze: nil).object_id
+
+ #{str_for_yielding_exception_class("ENV.clone(freeze: true)")}
+ #{str_for_yielding_exception_class("ENV.clone(freeze: 1)")}
+ #{str_for_yielding_exception_class("ENV.clone(foo: false)")}
+ #{str_for_yielding_exception_class("ENV.clone(1)")}
+ #{str_for_yielding_exception_class("ENV.clone(1, foo: false)")}
+
+ ensure
+ Warning[:deprecated] = original_warning_state
+ end
+ end
+ assert_equal(ENV.object_id, r.take)
+ assert_equal(ENV.object_id, r.take)
+ assert_equal(ENV.object_id, r.take)
+ #{str_for_assert_raise_on_yielded_exception_class(TypeError, "r")}
+ 4.times do
+ #{str_for_assert_raise_on_yielded_exception_class(ArgumentError, "r")}
+ end
+ end;
+ end
+
+ def test_has_value_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ val = 'a'
+ val.succ! while ENV.has_value?(val) || ENV.has_value?(val.upcase)
+ ENV['test'] = val[0...-1]
+ Ractor.yield(ENV.has_value?(val))
+ Ractor.yield(ENV.has_value?(val.upcase))
+ ENV['test'] = val
+ Ractor.yield(ENV.has_value?(val))
+ Ractor.yield(ENV.has_value?(val.upcase))
+ ENV['test'] = val.upcase
+ Ractor.yield ENV.has_value?(val)
+ Ractor.yield ENV.has_value?(val.upcase)
+ end
+ assert_equal(false, r.take)
+ assert_equal(false, r.take)
+ assert_equal(true, r.take)
+ assert_equal(false, r.take)
+ assert_equal(false, r.take)
+ assert_equal(true, r.take)
+ end;
+ end
+
+ def test_key_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ val = 'a'
+ val.succ! while ENV.has_value?(val) || ENV.has_value?(val.upcase)
+ ENV['test'] = val[0...-1]
+ Ractor.yield ENV.key(val)
+ Ractor.yield ENV.key(val.upcase)
+ ENV['test'] = val
+ Ractor.yield ENV.key(val)
+ Ractor.yield ENV.key(val.upcase)
+ ENV['test'] = val.upcase
+ Ractor.yield ENV.key(val)
+ Ractor.yield ENV.key(val.upcase)
+ end
+ assert_nil(r.take)
+ assert_nil(r.take)
+ if #{ignore_case_str}
+ assert_equal('TEST', r.take.upcase)
+ else
+ assert_equal('test', r.take)
+ end
+ assert_nil(r.take)
+ assert_nil(r.take)
+ if #{ignore_case_str}
+ assert_equal('TEST', r.take.upcase)
+ else
+ assert_equal('test', r.take)
+ end
+ end;
+
+ end
+
+ def test_delete_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ #{str_to_yield_invalid_envvar_errors("v", "ENV.delete(v)")}
+ Ractor.yield ENV.delete("TEST")
+ #{str_for_yielding_exception_class("ENV.delete('#{PATH_ENV}')")}
+ Ractor.yield(ENV.delete("TEST"){|name| "NO "+name})
+ end
+ #{str_to_receive_invalid_envvar_errors("r")}
+ assert_nil(r.take)
+ exception_class = r.take
+ assert_equal(NilClass, exception_class)
+ assert_equal("NO TEST", r.take)
+ end;
+ end
+
+ def test_getenv_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ #{str_to_yield_invalid_envvar_errors("v", "ENV[v]")}
+ ENV["#{PATH_ENV}"] = ""
+ Ractor.yield ENV["#{PATH_ENV}"]
+ Ractor.yield ENV[""]
+ end
+ #{str_to_receive_invalid_envvar_errors("r")}
+ assert_equal("", r.take)
+ assert_nil(r.take)
+ end;
+ end
+
+ def test_fetch_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV["test"] = "foo"
+ Ractor.yield ENV.fetch("test")
+ ENV.delete("test")
+ #{str_for_yielding_exception_class("ENV.fetch('test')", exception_var: "ex")}
+ Ractor.yield ex.receiver.object_id
+ Ractor.yield ex.key
+ Ractor.yield ENV.fetch("test", "foo")
+ Ractor.yield(ENV.fetch("test"){"bar"})
+ #{str_to_yield_invalid_envvar_errors("v", "ENV.fetch(v)")}
+ #{str_for_yielding_exception_class("ENV.fetch('#{PATH_ENV}', 'foo')")}
+ ENV['#{PATH_ENV}'] = ""
+ Ractor.yield ENV.fetch('#{PATH_ENV}')
+ end
+ assert_equal("foo", r.take)
+ #{str_for_assert_raise_on_yielded_exception_class(KeyError, "r")}
+ assert_equal(ENV.object_id, r.take)
+ assert_equal("test", r.take)
+ assert_equal("foo", r.take)
+ assert_equal("bar", r.take)
+ #{str_to_receive_invalid_envvar_errors("r")}
+ exception_class = r.take
+ assert_equal(NilClass, exception_class)
+ assert_equal("", r.take)
+ end;
+ end
+
+ def test_aset_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ #{str_for_yielding_exception_class("ENV['test'] = nil")}
+ ENV["test"] = nil
+ Ractor.yield ENV["test"]
+ #{str_to_yield_invalid_envvar_errors("v", "ENV[v] = 'test'")}
+ #{str_to_yield_invalid_envvar_errors("v", "ENV['test'] = v")}
+ end
+ exception_class = r.take
+ assert_equal(NilClass, exception_class)
+ assert_nil(r.take)
+ #{str_to_receive_invalid_envvar_errors("r")}
+ #{str_to_receive_invalid_envvar_errors("r")}
+ end;
+ end
+
+ def test_keys_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ a = ENV.keys
+ Ractor.yield a
+ end
+ a = r.take
+ assert_kind_of(Array, a)
+ a.each {|k| assert_kind_of(String, k) }
+ end;
+
+ end
+
+ def test_each_key_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV.each_key {|k| Ractor.yield(k)}
+ Ractor.yield "finished"
+ end
+ while((x=r.take) != "finished")
+ assert_kind_of(String, x)
+ end
+ end;
+ end
+
+ def test_values_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ a = ENV.values
+ Ractor.yield a
+ end
+ a = r.take
+ assert_kind_of(Array, a)
+ a.each {|k| assert_kind_of(String, k) }
+ end;
+ end
+
+ def test_each_value_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV.each_value {|k| Ractor.yield(k)}
+ Ractor.yield "finished"
+ end
+ while((x=r.take) != "finished")
+ assert_kind_of(String, x)
+ end
+ end;
+ end
+
+ def test_each_pair_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV.each_pair {|k, v| Ractor.yield([k,v])}
+ Ractor.yield "finished"
+ end
+ while((k,v=r.take) != "finished")
+ assert_kind_of(String, k)
+ assert_kind_of(String, v)
+ end
+ end;
+ end
+
+ def test_reject_bang_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ h1 = {}
+ ENV.each_pair {|k, v| h1[k] = v }
+ ENV["test"] = "foo"
+ ENV.reject! {|k, v| #{ignore_case_str} ? k.upcase == "TEST" : k == "test" }
+ h2 = {}
+ ENV.each_pair {|k, v| h2[k] = v }
+ Ractor.yield [h1, h2]
+ Ractor.yield(ENV.reject! {|k, v| #{ignore_case_str} ? k.upcase == "TEST" : k == "test" })
+ end
+ h1, h2 = r.take
+ assert_equal(h1, h2)
+ assert_nil(r.take)
+ end;
+ end
+
+ def test_delete_if_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ h1 = {}
+ ENV.each_pair {|k, v| h1[k] = v }
+ ENV["test"] = "foo"
+ ENV.delete_if {|k, v| #{ignore_case_str} ? k.upcase == "TEST" : k == "test" }
+ h2 = {}
+ ENV.each_pair {|k, v| h2[k] = v }
+ Ractor.yield [h1, h2]
+ Ractor.yield (ENV.delete_if {|k, v| #{ignore_case_str} ? k.upcase == "TEST" : k == "test" }).object_id
+ end
+ h1, h2 = r.take
+ assert_equal(h1, h2)
+ assert_equal(ENV.object_id, r.take)
+ end;
+ end
+
+ def test_select_bang_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ h1 = {}
+ ENV.each_pair {|k, v| h1[k] = v }
+ ENV["test"] = "foo"
+ ENV.select! {|k, v| #{ignore_case_str} ? k.upcase != "TEST" : k != "test" }
+ h2 = {}
+ ENV.each_pair {|k, v| h2[k] = v }
+ Ractor.yield [h1, h2]
+ Ractor.yield(ENV.select! {|k, v| #{ignore_case_str} ? k.upcase != "TEST" : k != "test" })
+ end
+ h1, h2 = r.take
+ assert_equal(h1, h2)
+ assert_nil(r.take)
+ end;
+ end
+
+ def test_filter_bang_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ h1 = {}
+ ENV.each_pair {|k, v| h1[k] = v }
+ ENV["test"] = "foo"
+ ENV.filter! {|k, v| #{ignore_case_str} ? k.upcase != "TEST" : k != "test" }
+ h2 = {}
+ ENV.each_pair {|k, v| h2[k] = v }
+ Ractor.yield [h1, h2]
+ Ractor.yield(ENV.filter! {|k, v| #{ignore_case_str} ? k.upcase != "TEST" : k != "test" })
+ end
+ h1, h2 = r.take
+ assert_equal(h1, h2)
+ assert_nil(r.take)
+ end;
+ end
+
+ def test_keep_if_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ h1 = {}
+ ENV.each_pair {|k, v| h1[k] = v }
+ ENV["test"] = "foo"
+ ENV.keep_if {|k, v| #{ignore_case_str} ? k.upcase != "TEST" : k != "test" }
+ h2 = {}
+ ENV.each_pair {|k, v| h2[k] = v }
+ Ractor.yield [h1, h2]
+ Ractor.yield (ENV.keep_if {|k, v| #{ignore_case_str} ? k.upcase != "TEST" : k != "test" }).object_id
+ end
+ h1, h2 = r.take
+ assert_equal(h1, h2)
+ assert_equal(ENV.object_id, r.take)
+ end;
+ end
+
+ def test_values_at_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV["test"] = "foo"
+ Ractor.yield ENV.values_at("test", "test")
+ end
+ assert_equal(["foo", "foo"], r.take)
+ end;
+ end
+
+ def test_select_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV["test"] = "foo"
+ h = ENV.select {|k| #{ignore_case_str} ? k.upcase == "TEST" : k == "test" }
+ Ractor.yield h.size
+ k = h.keys.first
+ v = h.values.first
+ Ractor.yield [k, v]
+ end
+ assert_equal(1, r.take)
+ k, v = r.take
+ if #{ignore_case_str}
+ assert_equal("TEST", k.upcase)
+ assert_equal("FOO", v.upcase)
+ else
+ assert_equal("test", k)
+ assert_equal("foo", v)
+ end
+ end;
+ end
+
+ def test_filter_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV["test"] = "foo"
+ h = ENV.filter {|k| #{ignore_case_str} ? k.upcase == "TEST" : k == "test" }
+ Ractor.yield(h.size)
+ k = h.keys.first
+ v = h.values.first
+ Ractor.yield [k, v]
+ end
+ assert_equal(1, r.take)
+ k, v = r.take
+ if #{ignore_case_str}
+ assert_equal("TEST", k.upcase)
+ assert_equal("FOO", v.upcase)
+ else
+ assert_equal("test", k)
+ assert_equal("foo", v)
+ end
+ end;
+ end
+
+ def test_slice_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ ENV["bar"] = "rab"
+ Ractor.yield(ENV.slice())
+ Ractor.yield(ENV.slice(""))
+ Ractor.yield(ENV.slice("unknown"))
+ Ractor.yield(ENV.slice("foo", "baz"))
+ end
+ assert_equal({}, r.take)
+ assert_equal({}, r.take)
+ assert_equal({}, r.take)
+ assert_equal({"foo"=>"bar", "baz"=>"qux"}, r.take)
+ end;
+ end
+
+ def test_except_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ ENV["bar"] = "rab"
+ Ractor.yield ENV.except()
+ Ractor.yield ENV.except("")
+ Ractor.yield ENV.except("unknown")
+ Ractor.yield ENV.except("foo", "baz")
+ end
+ assert_equal({"bar"=>"rab", "baz"=>"qux", "foo"=>"bar"}, r.take)
+ assert_equal({"bar"=>"rab", "baz"=>"qux", "foo"=>"bar"}, r.take)
+ assert_equal({"bar"=>"rab", "baz"=>"qux", "foo"=>"bar"}, r.take)
+ assert_equal({"bar"=>"rab"}, r.take)
+ end;
+ end
+
+ def test_clear_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV.clear
+ Ractor.yield ENV.size
+ end
+ assert_equal(0, r.take)
+ end;
+ end
+
+ def test_to_s_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV.to_s
+ end
+ assert_equal("ENV", r.take)
+ end;
+ end
+
+ def test_inspect_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ s = ENV.inspect
+ Ractor.yield s
+ end
+ s = r.take
+ if #{ignore_case_str}
+ s = s.upcase
+ assert(s == '{"FOO"=>"BAR", "BAZ"=>"QUX"}' || s == '{"BAZ"=>"QUX", "FOO"=>"BAR"}')
+ else
+ assert(s == '{"foo"=>"bar", "baz"=>"qux"}' || s == '{"baz"=>"qux", "foo"=>"bar"}')
+ end
+ end;
+ end
+
+ def test_to_a_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ a = ENV.to_a
+ Ractor.yield a
+ end
+ a = r.take
+ assert_equal(2, a.size)
+ if #{ignore_case_str}
+ a = a.map {|x| x.map {|y| y.upcase } }
+ assert(a == [%w(FOO BAR), %w(BAZ QUX)] || a == [%w(BAZ QUX), %w(FOO BAR)])
+ else
+ assert(a == [%w(foo bar), %w(baz qux)] || a == [%w(baz qux), %w(foo bar)])
+ end
+ end;
+ end
+
+ def test_rehash_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV.rehash
+ end
+ assert_nil(r.take)
+ end;
+ end
+
+ def test_size_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ s = ENV.size
+ ENV["test"] = "foo"
+ Ractor.yield [s, ENV.size]
+ end
+ s, s2 = r.take
+ assert_equal(s + 1, s2)
+ end;
+ end
+
+ def test_empty_p_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV.clear
+ Ractor.yield ENV.empty?
+ ENV["test"] = "foo"
+ Ractor.yield ENV.empty?
+ end
+ assert r.take
+ assert !r.take
+ end;
+ end
+
+ def test_has_key_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ Ractor.yield ENV.has_key?("test")
+ ENV["test"] = "foo"
+ Ractor.yield ENV.has_key?("test")
+ #{str_to_yield_invalid_envvar_errors("v", "ENV.has_key?(v)")}
+ end
+ assert !r.take
+ assert r.take
+ #{str_to_receive_invalid_envvar_errors("r")}
+ end;
+ end
+
+ def test_assoc_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ Ractor.yield ENV.assoc("test")
+ ENV["test"] = "foo"
+ Ractor.yield ENV.assoc("test")
+ #{str_to_yield_invalid_envvar_errors("v", "ENV.assoc(v)")}
+ end
+ assert_nil(r.take)
+ k, v = r.take
+ if #{ignore_case_str}
+ assert_equal("TEST", k.upcase)
+ assert_equal("FOO", v.upcase)
+ else
+ assert_equal("test", k)
+ assert_equal("foo", v)
+ end
+ #{str_to_receive_invalid_envvar_errors("r")}
+ encoding = /mswin|mingw/ =~ RUBY_PLATFORM ? Encoding::UTF_8 : Encoding.find("locale")
+ assert_equal(encoding, v.encoding)
+ end;
+ end
+
+ def test_has_value2_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV.clear
+ Ractor.yield ENV.has_value?("foo")
+ ENV["test"] = "foo"
+ Ractor.yield ENV.has_value?("foo")
+ end
+ assert !r.take
+ assert r.take
+ end;
+ end
+
+ def test_rassoc_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV.clear
+ Ractor.yield ENV.rassoc("foo")
+ ENV["foo"] = "bar"
+ ENV["test"] = "foo"
+ ENV["baz"] = "qux"
+ Ractor.yield ENV.rassoc("foo")
+ end
+ assert_nil(r.take)
+ k, v = r.take
+ if #{ignore_case_str}
+ assert_equal("TEST", k.upcase)
+ assert_equal("FOO", v.upcase)
+ else
+ assert_equal("test", k)
+ assert_equal("foo", v)
+ end
+ end;
+ end
+
+ def test_to_hash_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ h = {}
+ ENV.each {|k, v| h[k] = v }
+ Ractor.yield [h, ENV.to_hash]
+ end
+ h, h2 = r.take
+ assert_equal(h, h2)
+ end;
+ end
+
+ def test_to_h_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ Ractor.yield [ENV.to_hash, ENV.to_h]
+ Ractor.yield [ENV.map {|k, v| ["$\#{k}", v.size]}.to_h, ENV.to_h {|k, v| ["$\#{k}", v.size]}]
+ end
+ a, b = r.take
+ assert_equal(a,b)
+ c, d = r.take
+ assert_equal(c,d)
+ end;
+ end
+
+ def test_reject_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ h1 = {}
+ ENV.each_pair {|k, v| h1[k] = v }
+ ENV["test"] = "foo"
+ h2 = ENV.reject {|k, v| #{ignore_case_str} ? k.upcase == "TEST" : k == "test" }
+ Ractor.yield [h1, h2]
+ end
+ h1, h2 = r.take
+ assert_equal(h1, h2)
+ end;
+ end
+
+ def test_shift_in_ractor
+ assert_ractor(<<-"end;")
+ #{STR_DEFINITION_FOR_CHECK}
+ r = Ractor.new do
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ a = ENV.shift
+ b = ENV.shift
+ Ractor.yield [a,b]
+ Ractor.yield ENV.shift
+ end
+ a,b = r.take
+ check([a, b], [%w(foo bar), %w(baz qux)])
+ assert_nil(r.take)
+ end;
+ end
+
+ def test_invert_in_ractor
+ assert_ractor(<<-"end;")
+ #{STR_DEFINITION_FOR_CHECK}
+ r = Ractor.new do
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ Ractor.yield(ENV.invert)
+ end
+ check(r.take.to_a, [%w(bar foo), %w(qux baz)])
+ end;
+ end
+
+ def test_replace_in_ractor
+ assert_ractor(<<-"end;")
+ #{STR_DEFINITION_FOR_CHECK}
+ r = Ractor.new do
+ ENV["foo"] = "xxx"
+ ENV.replace({"foo"=>"bar", "baz"=>"qux"})
+ Ractor.yield ENV.to_hash
+ ENV.replace({"Foo"=>"Bar", "Baz"=>"Qux"})
+ Ractor.yield ENV.to_hash
+ end
+ check(r.take.to_a, [%w(foo bar), %w(baz qux)])
+ check(r.take.to_a, [%w(Foo Bar), %w(Baz Qux)])
+ end;
+ end
+
+ def test_update_in_ractor
+ assert_ractor(<<-"end;")
+ #{STR_DEFINITION_FOR_CHECK}
+ r = Ractor.new do
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ ENV.update({"baz"=>"quux","a"=>"b"})
+ Ractor.yield ENV.to_hash
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ ENV.update({"baz"=>"quux","a"=>"b"}) {|k, v1, v2| k + "_" + v1 + "_" + v2 }
+ Ractor.yield ENV.to_hash
+ end
+ check(r.take.to_a, [%w(foo bar), %w(baz quux), %w(a b)])
+ check(r.take.to_a, [%w(foo bar), %w(baz baz_qux_quux), %w(a b)])
+ end;
+ end
+
+ def test_huge_value_in_ractor
+ assert_ractor(<<-"end;")
+ huge_value = "bar" * 40960
+ r = Ractor.new huge_value do |v|
+ ENV["foo"] = "bar"
+ #{str_for_yielding_exception_class("ENV['foo'] = v ")}
+ Ractor.yield ENV["foo"]
+ end
+
+ if /mswin|ucrt/ =~ RUBY_PLATFORM
+ #{str_for_assert_raise_on_yielded_exception_class(Errno::EINVAL, "r")}
+ result = r.take
+ assert_equal("bar", result)
+ else
+ exception_class = r.take
+ assert_equal(NilClass, exception_class)
+ result = r.take
+ assert_equal(huge_value, result)
+ end
+ end;
+ end
+
+ def test_frozen_env_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ #{str_for_yielding_exception_class("ENV.freeze")}
+ end
+ #{str_for_assert_raise_on_yielded_exception_class(TypeError, "r")}
+ end;
+ end
+
+ def test_frozen_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ ENV["#{PATH_ENV}"] = "/"
+ ENV.each do |k, v|
+ Ractor.yield [k.frozen?]
+ Ractor.yield [v.frozen?]
+ end
+ ENV.each_key do |k|
+ Ractor.yield [k.frozen?]
+ end
+ ENV.each_value do |v|
+ Ractor.yield [v.frozen?]
+ end
+ ENV.each_key do |k|
+ Ractor.yield [ENV[k].frozen?, "[\#{k.dump}]"]
+ Ractor.yield [ENV.fetch(k).frozen?, "fetch(\#{k.dump})"]
+ end
+ Ractor.yield "finished"
+ end
+ while((params=r.take) != "finished")
+ assert(*params)
+ end
+ end;
+ end
+
+ def test_shared_substring_in_ractor
+ assert_ractor(<<-"end;")
+ r = Ractor.new do
+ bug12475 = '[ruby-dev:49655] [Bug #12475]'
+ n = [*"0".."9"].join("")*3
+ e0 = ENV[n0 = "E\#{n}"]
+ e1 = ENV[n1 = "E\#{n}."]
+ ENV[n0] = nil
+ ENV[n1] = nil
+ ENV[n1.chop] = "T\#{n}.".chop
+ ENV[n0], e0 = e0, ENV[n0]
+ ENV[n1], e1 = e1, ENV[n1]
+ Ractor.yield [n, e0, e1, bug12475]
+ end
+ n, e0, e1, bug12475 = r.take
+ assert_equal("T\#{n}", e0, bug12475)
+ assert_nil(e1, bug12475)
+ end;
+ end
+
+ def test_ivar_in_env_should_not_be_access_from_non_main_ractors
+ assert_ractor <<~RUBY
+ ENV.instance_eval{ @a = "hello" }
+ assert_equal "hello", ENV.instance_variable_get(:@a)
+
+ r_get = Ractor.new do
+ ENV.instance_variable_get(:@a)
+ rescue Ractor::IsolationError => e
+ e
+ end
+ assert_equal Ractor::IsolationError, r_get.take.class
+
+ r_get = Ractor.new do
+ ENV.instance_eval{ @a }
+ rescue Ractor::IsolationError => e
+ e
+ end
+
+ assert_equal Ractor::IsolationError, r_get.take.class
+
+ r_set = Ractor.new do
+ ENV.instance_eval{ @b = "hello" }
+ rescue Ractor::IsolationError => e
+ e
+ end
+
+ assert_equal Ractor::IsolationError, r_set.take.class
+ RUBY
+ end
+
if RUBY_PLATFORM =~ /bccwin|mswin|mingw/
def test_memory_leak_aset
bug9977 = '[ruby-dev:48323] [Bug #9977]'
@@ -566,15 +1505,13 @@ class TestEnv < Test::Unit::TestCase
end;
end
- if Encoding.find("locale") == Encoding::UTF_8
- def test_utf8
- text = "testing \u{e5 e1 e2 e4 e3 101 3042}"
- test = ENV["test"]
- ENV["test"] = text
- assert_equal text, ENV["test"]
- ensure
- ENV["test"] = test
- end
+ def test_utf8
+ text = "testing \u{e5 e1 e2 e4 e3 101 3042}"
+ test = ENV["test"]
+ ENV["test"] = text
+ assert_equal text, ENV["test"]
+ ensure
+ ENV["test"] = test
end
end
end
diff --git a/test/ruby/test_eval.rb b/test/ruby/test_eval.rb
index 3d6116edbc..d55977c986 100644
--- a/test/ruby/test_eval.rb
+++ b/test/ruby/test_eval.rb
@@ -219,6 +219,12 @@ class TestEval < Test::Unit::TestCase
end
end
+ def test_instance_exec_cvar
+ [Object.new, [], 7, :sym, true, false, nil].each do |obj|
+ assert_equal(13, obj.instance_exec{@@cvar})
+ end
+ end
+
def test_instance_eval_method
bug2788 = '[ruby-core:28324]'
[Object.new, [], nil, true, false].each do |o|
@@ -253,6 +259,70 @@ class TestEval < Test::Unit::TestCase
assert_equal(2, bar)
end
+ def test_instance_exec_block_basic
+ forall_TYPE do |o|
+ assert_equal nil, o.instance_exec { nil }
+ assert_equal true, o.instance_exec { true }
+ assert_equal false, o.instance_exec { false }
+ assert_equal o, o.instance_exec { self }
+ assert_equal 1, o.instance_exec { 1 }
+ assert_equal :sym, o.instance_exec { :sym }
+
+ assert_equal 11, o.instance_exec { 11 }
+ assert_equal 12, o.instance_exec { @ivar } unless o.frozen?
+ assert_equal 13, o.instance_exec { @@cvar }
+ assert_equal 14, o.instance_exec { $gvar__eval }
+ assert_equal 15, o.instance_exec { Const }
+ assert_equal 16, o.instance_exec { 7 + 9 }
+ assert_equal 17, o.instance_exec { 17.to_i }
+ assert_equal "18", o.instance_exec { "18" }
+ assert_equal "19", o.instance_exec { "1#{9}" }
+
+ 1.times {
+ assert_equal 12, o.instance_exec { @ivar } unless o.frozen?
+ assert_equal 13, o.instance_exec { @@cvar }
+ assert_equal 14, o.instance_exec { $gvar__eval }
+ assert_equal 15, o.instance_exec { Const }
+ }
+ end
+ end
+
+ def test_instance_exec_method_definition
+ klass = Class.new
+ o = klass.new
+
+ o.instance_exec do
+ def foo
+ :foo_result
+ end
+ end
+
+ assert_respond_to o, :foo
+ refute_respond_to klass, :foo
+ refute_respond_to klass.new, :foo
+
+ assert_equal :foo_result, o.foo
+ end
+
+ def test_instance_exec_eval_method_definition
+ klass = Class.new
+ o = klass.new
+
+ o.instance_exec do
+ eval %{
+ def foo
+ :foo_result
+ end
+ }
+ end
+
+ assert_respond_to o, :foo
+ refute_respond_to klass, :foo
+ refute_respond_to klass.new, :foo
+
+ assert_equal :foo_result, o.foo
+ end
+
#
# From ruby/test/ruby/test_eval.rb
#
@@ -347,6 +417,10 @@ class TestEval < Test::Unit::TestCase
assert_equal(55, eval("foo22"))
assert_equal(55, foo22)
}.call
+
+ self.class.class_eval do
+ remove_const :EvTest
+ end
end
def test_nil_instance_eval_cvar
@@ -470,9 +544,12 @@ class TestEval < Test::Unit::TestCase
end
def test_eval_location_binding
- assert_warning(/__FILE__ in eval/) do
- assert_equal(__FILE__, eval("__FILE__", binding))
- end
+ assert_equal(['(eval)', 1], eval("[__FILE__, __LINE__]", nil))
+ assert_equal(['(eval)', 1], eval("[__FILE__, __LINE__]", binding))
+ assert_equal(['foo', 1], eval("[__FILE__, __LINE__]", nil, 'foo'))
+ assert_equal(['foo', 1], eval("[__FILE__, __LINE__]", binding, 'foo'))
+ assert_equal(['foo', 2], eval("[__FILE__, __LINE__]", nil, 'foo', 2))
+ assert_equal(['foo', 2], eval("[__FILE__, __LINE__]", binding, 'foo', 2))
end
def test_fstring_instance_eval
diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb
index 56cd19d0a2..ffca877f1e 100644
--- a/test/ruby/test_exception.rb
+++ b/test/ruby/test_exception.rb
@@ -78,6 +78,77 @@ class TestException < Test::Unit::TestCase
assert(!bad)
end
+ def test_exception_in_ensure_with_next
+ string = "[ruby-core:82936] [Bug #13930]"
+ assert_raise_with_message(RuntimeError, string) do
+ lambda do
+ next
+ rescue
+ assert(false)
+ ensure
+ raise string
+ end.call
+ assert(false)
+ end
+
+ assert_raise_with_message(RuntimeError, string) do
+ flag = true
+ while flag
+ flag = false
+ begin
+ next
+ rescue
+ assert(false)
+ ensure
+ raise string
+ end
+ end
+ end
+
+ iseq = RubyVM::InstructionSequence.compile(<<-RUBY)
+ begin
+ while true
+ break
+ end
+ rescue
+ end
+ RUBY
+
+ assert_equal false, iseq.to_a[13].any?{|(e,_)| e == :throw}
+ end
+
+ def test_exception_in_ensure_with_redo
+ string = "[ruby-core:82936] [Bug #13930]"
+
+ assert_raise_with_message(RuntimeError, string) do
+ i = 0
+ lambda do
+ i += 1
+ redo if i < 2
+ rescue
+ assert(false)
+ ensure
+ raise string
+ end.call
+ assert(false)
+ end
+ end
+
+ def test_exception_in_ensure_with_return
+ @string = "[ruby-core:97104] [Bug #16618]"
+ def self.meow
+ return if true # This if modifier suppresses "warning: statement not reached"
+ assert(false)
+ rescue
+ assert(false)
+ ensure
+ raise @string
+ end
+ assert_raise_with_message(RuntimeError, @string) do
+ meow
+ end
+ end
+
def test_errinfo_in_debug
bug9568 = EnvUtil.labeled_class("[ruby-core:61091] [Bug #9568]", RuntimeError) do
def to_s
@@ -181,6 +252,27 @@ class TestException < Test::Unit::TestCase
}
end
+ def test_catch_throw_in_require_cant_be_rescued
+ bug18562 = '[ruby-core:107403]'
+ Tempfile.create(["dep", ".rb"]) {|t|
+ t.puts("throw :extdep, 42")
+ t.close
+
+ rescue_all = Class.new(Exception)
+ def rescue_all.===(_)
+ raise "should not reach here"
+ end
+
+ v = assert_throw(:extdep, bug18562) do
+ require t.path
+ rescue rescue_all => e
+ assert(false, "should not reach here")
+ end
+
+ assert_equal(42, v, bug18562)
+ }
+ end
+
def test_throw_false
bug12743 = '[ruby-core:77229] [Bug #12743]'
Thread.start {
@@ -438,16 +530,6 @@ end.join
assert_not_send([e, :success?], "abort means failure")
end
- def test_nomethoderror
- bug3237 = '[ruby-core:29948]'
- str = "\u2600"
- id = :"\u2604"
- msg = "undefined method `#{id}' for \"#{str}\":String"
- assert_raise_with_message(NoMethodError, msg, bug3237) do
- str.__send__(id)
- end
- end
-
def test_errno
assert_equal(Encoding.find("locale"), Errno::EINVAL.new.message.encoding)
end
@@ -499,6 +581,35 @@ end.join
end;
end
+ def test_ensure_after_nomemoryerror
+ skip "Forcing NoMemoryError causes problems in some environments"
+ assert_separately([], "$_ = 'a' * 1_000_000_000_000_000_000")
+ rescue NoMemoryError
+ assert_raise(NoMemoryError) do
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ bug15779 = bug15779 = '[ruby-core:92342]'
+ begin;
+ require 'open-uri'
+
+ begin
+ 'a' * 1_000_000_000_000_000_000
+ ensure
+ URI.open('http://www.ruby-lang.org/')
+ end
+ end;
+ end
+ rescue Test::Unit::AssertionFailedError
+ # Possibly compiled with -DRUBY_DEBUG, in which
+ # case rb_bug is used instead of NoMemoryError,
+ # and we cannot test ensure after NoMemoryError.
+ rescue RangeError
+ # MingW can raise RangeError instead of NoMemoryError,
+ # so we cannot test this case.
+ rescue Timeout::Error
+ # Solaris 11 CI times out instead of raising NoMemoryError,
+ # so we cannot test this case.
+ end
+
def test_equal
bug5865 = '[ruby-core:41979]'
assert_equal(RuntimeError.new("a"), RuntimeError.new("a"), bug5865)
@@ -753,7 +864,7 @@ end.join
bug12741 = '[ruby-core:77222] [Bug #12741]'
x = Thread.current
- q = Queue.new
+ q = Thread::Queue.new
y = Thread.start do
q.pop
begin
@@ -824,261 +935,28 @@ end.join
}
end
- def test_anonymous_message
- assert_in_out_err([], "raise Class.new(RuntimeError), 'foo'", [], /foo\n/)
- end
-
- PrettyObject =
- Class.new(BasicObject) do
- alias object_id __id__
- def pretty_inspect; "`obj'"; end
- alias inspect pretty_inspect
- end
-
- def test_frozen_error_receiver
- obj = Object.new.freeze
- (obj.foo = 1) rescue (e = $!)
- assert_same(obj, e.receiver)
- obj.singleton_class.const_set(:A, 2) rescue (e = $!)
- assert_same(obj.singleton_class, e.receiver)
- end
-
- def test_frozen_error_initialize
- obj = Object.new
- exc = FrozenError.new("bar", obj)
- assert_equal("bar", exc.message)
- assert_same(obj, exc.receiver)
-
- exc = FrozenError.new("bar")
- assert_equal("bar", exc.message)
- assert_raise_with_message(ArgumentError, "no receiver is available") {
- exc.receiver
- }
-
- exc = FrozenError.new
- assert_equal("FrozenError", exc.message)
- assert_raise_with_message(ArgumentError, "no receiver is available") {
- exc.receiver
- }
- end
-
- def test_frozen_error_message
- obj = Object.new.freeze
- e = assert_raise_with_message(FrozenError, /can't modify frozen #{obj.class}/) {
- obj.instance_variable_set(:@test, true)
- }
- assert_include(e.message, obj.inspect)
-
- klass = Class.new do
- def init
- @x = true
- end
- def inspect
- init
- super
- end
- end
- obj = klass.new.freeze
- e = assert_raise_with_message(FrozenError, /can't modify frozen #{obj.class}/) {
- obj.init
- }
- assert_include(e.message, klass.inspect)
- end
-
- def test_name_error_new_default
- error = NameError.new
- assert_equal("NameError", error.message)
- end
-
- def test_name_error_new_message
- error = NameError.new("Message")
- assert_equal("Message", error.message)
- end
-
- def test_name_error_new_name
- error = NameError.new("Message")
- assert_nil(error.name)
-
- error = NameError.new("Message", :foo)
- assert_equal(:foo, error.name)
- end
-
- def test_name_error_new_receiver
- receiver = Object.new
-
- error = NameError.new
- assert_raise(ArgumentError) {error.receiver}
- assert_equal("NameError", error.message)
-
- error = NameError.new(receiver: receiver)
- assert_equal(["NameError", receiver],
- [error.message, error.receiver])
-
- error = NameError.new("Message", :foo, receiver: receiver)
- assert_equal(["Message", receiver, :foo],
- [error.message, error.receiver, error.name])
- end
-
- def test_nomethod_error_new_default
- error = NoMethodError.new
- assert_equal("NoMethodError", error.message)
- end
-
- def test_nomethod_error_new_message
- error = NoMethodError.new("Message")
- assert_equal("Message", error.message)
- end
-
- def test_nomethod_error_new_name
- error = NoMethodError.new("Message")
- assert_nil(error.name)
-
- error = NoMethodError.new("Message", :foo)
- assert_equal(:foo, error.name)
- end
-
- def test_nomethod_error_new_name_args
- error = NoMethodError.new("Message", :foo)
- assert_nil(error.args)
-
- error = NoMethodError.new("Message", :foo, [1, 2])
- assert_equal([:foo, [1, 2]], [error.name, error.args])
- end
-
- def test_nomethod_error_new_name_args_priv
- error = NoMethodError.new("Message", :foo, [1, 2])
- assert_not_predicate(error, :private_call?)
-
- error = NoMethodError.new("Message", :foo, [1, 2], true)
- assert_equal([:foo, [1, 2], true],
- [error.name, error.args, error.private_call?])
- end
-
- def test_nomethod_error_new_receiver
- receiver = Object.new
-
- error = NoMethodError.new
- assert_raise(ArgumentError) {error.receiver}
-
- error = NoMethodError.new(receiver: receiver)
- assert_equal(receiver, error.receiver)
-
- error = NoMethodError.new("Message")
- assert_raise(ArgumentError) {error.receiver}
-
- error = NoMethodError.new("Message", receiver: receiver)
- assert_equal(["Message", receiver],
- [error.message, error.receiver])
-
- error = NoMethodError.new("Message", :foo)
- assert_raise(ArgumentError) {error.receiver}
-
- msg = defined?(DidYouMean.formatter) ?
- "Message\nDid you mean? for" : "Message"
-
- error = NoMethodError.new("Message", :foo, receiver: receiver)
- assert_equal([msg, :foo, receiver],
- [error.message, error.name, error.receiver])
-
- error = NoMethodError.new("Message", :foo, [1, 2])
- assert_raise(ArgumentError) {error.receiver}
-
- error = NoMethodError.new("Message", :foo, [1, 2], receiver: receiver)
- assert_equal([msg, :foo, [1, 2], receiver],
- [error.message, error.name, error.args, error.receiver])
-
- error = NoMethodError.new("Message", :foo, [1, 2], true)
- assert_raise(ArgumentError) {error.receiver}
-
- error = NoMethodError.new("Message", :foo, [1, 2], true, receiver: receiver)
- assert_equal([:foo, [1, 2], true, receiver],
- [error.name, error.args, error.private_call?, error.receiver])
- end
-
- def test_name_error_info_const
- obj = PrettyObject.new
-
- e = assert_raise(NameError) {
- obj.instance_eval("Object")
- }
- assert_equal(:Object, e.name)
-
- e = assert_raise(NameError) {
- BasicObject::X
- }
- assert_same(BasicObject, e.receiver)
- assert_equal(:X, e.name)
- end
-
- def test_name_error_info_method
- obj = PrettyObject.new
-
- e = assert_raise(NameError) {
- obj.instance_eval {foo}
- }
- assert_equal(:foo, e.name)
- assert_same(obj, e.receiver)
-
- e = assert_raise(NoMethodError) {
- obj.foo(1, 2)
- }
- assert_equal(:foo, e.name)
- assert_equal([1, 2], e.args)
- assert_same(obj, e.receiver)
- assert_not_predicate(e, :private_call?)
-
- e = assert_raise(NoMethodError) {
- obj.instance_eval {foo(1, 2)}
- }
- assert_equal(:foo, e.name)
- assert_equal([1, 2], e.args)
- assert_same(obj, e.receiver)
- assert_predicate(e, :private_call?)
- end
-
- def test_name_error_info_local_variables
- obj = PrettyObject.new
- def obj.test(a, b=nil, *c, &d)
- e = a
- 1.times {|f| g = foo; g}
- e
+ def test_cause_exception_in_cause_message
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}") do |outs, errs, status|
+ begin;
+ exc = Class.new(StandardError) do
+ def initialize(obj, cnt)
+ super(obj)
+ @errcnt = cnt
+ end
+ def to_s
+ return super if @errcnt <= 0
+ @errcnt -= 1
+ raise "xxx"
+ end
+ end.new("ok", 10)
+ raise "[Bug #17033]", cause: exc
+ end;
+ assert_equal(1, errs.count {|m| m.include?("[Bug #17033]")}, proc {errs.pretty_inspect})
end
-
- e = assert_raise(NameError) {
- obj.test(3)
- }
- assert_equal(:foo, e.name)
- assert_same(obj, e.receiver)
- assert_equal(%i[a b c d e f g], e.local_variables.sort)
end
- def test_name_error_info_method_missing
- obj = PrettyObject.new
- def obj.method_missing(*)
- super
- end
-
- e = assert_raise(NoMethodError) {
- obj.foo(1, 2)
- }
- assert_equal(:foo, e.name)
- assert_equal([1, 2], e.args)
- assert_same(obj, e.receiver)
- assert_not_predicate(e, :private_call?)
-
- e = assert_raise(NoMethodError) {
- obj.instance_eval {foo(1, 2)}
- }
- assert_equal(:foo, e.name)
- assert_equal([1, 2], e.args)
- assert_same(obj, e.receiver)
- assert_predicate(e, :private_call?)
- end
-
- def test_name_error_info_parent_iseq_mark
- assert_separately(['-', File.join(__dir__, 'bug-11928.rb')], <<-'end;')
- -> {require ARGV[0]}.call
- end;
+ def test_anonymous_message
+ assert_in_out_err([], "raise Class.new(RuntimeError), 'foo'", [], /foo\n/)
end
def test_output_string_encoding
@@ -1158,25 +1036,37 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end
end
- def capture_warning_warn
+ def capture_warning_warn(category: false)
verbose = $VERBOSE
+ deprecated = Warning[:deprecated]
+ experimental = Warning[:experimental]
warning = []
::Warning.class_eval do
alias_method :warn2, :warn
remove_method :warn
- define_method(:warn) do |str|
- warning << str
+ if category
+ define_method(:warn) do |str, category: nil|
+ warning << [str, category]
+ end
+ else
+ define_method(:warn) do |str|
+ warning << str
+ end
end
end
$VERBOSE = true
+ Warning[:deprecated] = true
+ Warning[:experimental] = true
yield
return warning
ensure
$VERBOSE = verbose
+ Warning[:deprecated] = deprecated
+ Warning[:experimental] = experimental
::Warning.class_eval do
remove_method :warn
@@ -1186,14 +1076,38 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end
def test_warning_warn
- warning = capture_warning_warn {@a}
- assert_match(/instance variable @a not initialized/, warning[0])
+ warning = capture_warning_warn {$asdfasdsda_test_warning_warn}
+ assert_match(/global variable `\$asdfasdsda_test_warning_warn' not initialized/, warning[0])
assert_equal(["a\nz\n"], capture_warning_warn {warn "a\n", "z"})
assert_equal([], capture_warning_warn {warn})
assert_equal(["\n"], capture_warning_warn {warn ""})
end
+ def test_warn_deprecated_backwards_compatibility_category
+ warning = capture_warning_warn { Dir.exists?("non-existent") }
+
+ assert_match(/deprecated/, warning[0])
+ end
+
+ def test_warn_deprecated_category
+ warning = capture_warning_warn(category: true) { Dir.exists?("non-existent") }
+
+ assert_equal :deprecated, warning[0][1]
+ end
+
+ def test_warn_deprecated_to_remove_backwards_compatibility_category
+ warning = capture_warning_warn { Object.new.tainted? }
+
+ assert_match(/deprecated/, warning[0])
+ end
+
+ def test_warn_deprecated_to_remove_category
+ warning = capture_warning_warn(category: true) { Object.new.tainted? }
+
+ assert_equal :deprecated, warning[0][1]
+ end
+
def test_kernel_warn_uplevel
warning = capture_warning_warn {warn("test warning", uplevel: 0)}
assert_equal("#{__FILE__}:#{__LINE__-1}: warning: test warning\n", warning[0])
@@ -1205,8 +1119,7 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
assert_raise(ArgumentError) {warn("test warning", uplevel: -1)}
assert_in_out_err(["-e", "warn 'ok', uplevel: 1"], '', [], /warning:/)
warning = capture_warning_warn {warn("test warning", {uplevel: 0})}
- assert_equal("#{__FILE__}:#{__LINE__-1}: warning: The last argument is used as the keyword parameter\n", warning[0])
- assert_match(/warning: for method defined here|warning: test warning/, warning[1])
+ assert_match(/test warning.*{:uplevel=>0}/m, warning[0])
warning = capture_warning_warn {warn("test warning", **{uplevel: 0})}
assert_equal("#{__FILE__}:#{__LINE__-1}: warning: test warning\n", warning[0])
warning = capture_warning_warn {warn("test warning", {uplevel: 0}, **{})}
@@ -1235,10 +1148,12 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
t.puts "require '#{basename}'"
t.close
$LOAD_PATH.push(File.dirname(t))
- warning = capture_warning_warn {require basename}
+ warning = capture_warning_warn {
+ assert require(basename)
+ }
ensure
$LOAD_PATH.pop
- $LOADED_FEATURES.delete(t)
+ $LOADED_FEATURES.delete(t.path)
end
assert_equal(1, warning.size)
assert_match(/circular require/, warning.first)
@@ -1246,7 +1161,7 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end
def test_warning_warn_super
- assert_in_out_err(%[-W0], "#{<<~"{#"}\n#{<<~'};'}", [], /instance variable @a not initialized/)
+ assert_in_out_err(%[-W0], "#{<<~"{#"}\n#{<<~'};'}", [], /global variable `\$asdfiasdofa_test_warning_warn_super' not initialized/)
{#
module Warning
def warn(message)
@@ -1255,7 +1170,7 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end
$VERBOSE = true
- @a
+ $asdfiasdofa_test_warning_warn_super
};
end
@@ -1263,6 +1178,55 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
assert_raise(TypeError) {Warning[nil]}
assert_raise(ArgumentError) {Warning[:XXXX]}
assert_include([true, false], Warning[:deprecated])
+ assert_include([true, false], Warning[:experimental])
+ end
+
+ def test_warning_category_deprecated
+ warning = EnvUtil.verbose_warning do
+ deprecated = Warning[:deprecated]
+ Warning[:deprecated] = true
+ Warning.warn "deprecated feature", category: :deprecated
+ ensure
+ Warning[:deprecated] = deprecated
+ end
+ assert_equal "deprecated feature", warning
+
+ warning = EnvUtil.verbose_warning do
+ deprecated = Warning[:deprecated]
+ Warning[:deprecated] = false
+ Warning.warn "deprecated feature", category: :deprecated
+ ensure
+ Warning[:deprecated] = deprecated
+ end
+ assert_empty warning
+ end
+
+ def test_warning_category_experimental
+ warning = EnvUtil.verbose_warning do
+ experimental = Warning[:experimental]
+ Warning[:experimental] = true
+ Warning.warn "experimental feature", category: :experimental
+ ensure
+ Warning[:experimental] = experimental
+ end
+ assert_equal "experimental feature", warning
+
+ warning = EnvUtil.verbose_warning do
+ experimental = Warning[:experimental]
+ Warning[:experimental] = false
+ Warning.warn "experimental feature", category: :experimental
+ ensure
+ Warning[:experimental] = experimental
+ end
+ assert_empty warning
+ end
+
+ def test_undef_Warning_warn
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ Warning.undef_method(:warn)
+ assert_raise(NoMethodError) { warn "" }
+ end;
end
def test_undefined_backtrace
@@ -1414,13 +1378,11 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
if Exception.to_tty?
assert_match(/\e/, message)
message = message.gsub(/\e\[[\d;]*m/, '')
- assert_operator(message, :start_with?, remark)
- assert_operator(message, :end_with?, bottom)
else
assert_not_match(/\e/, message)
- assert_operator(message, :start_with?, bottom)
- assert_operator(message, :end_with?, top)
end
+ assert_operator(message, :start_with?, bottom)
+ assert_operator(message, :end_with?, top)
end
def test_exception_in_message
@@ -1450,6 +1412,18 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
end;
end
+ def test_marshal_circular_cause
+ begin
+ raise RuntimeError, "err", [], cause: Exception.new
+ rescue => e
+ end
+ dump = Marshal.dump(e).sub(/o:\x0EException\x08;.0;.0;.0/, "@\x05")
+ assert_raise_with_message(ArgumentError, /circular cause/, ->{dump.inspect}) do
+ e = Marshal.load(dump)
+ assert_same(e, e.cause)
+ end
+ end
+
def test_super_in_method_missing
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb
index a7ed9ac7e0..e4b7322bd9 100644
--- a/test/ruby/test_fiber.rb
+++ b/test/ruby/test_fiber.rb
@@ -34,8 +34,8 @@ class TestFiber < Test::Unit::TestCase
end
def test_many_fibers
- skip 'This is unstable on GitHub Actions --jit-wait. TODO: debug it' if RubyVM::MJIT.enabled?
- max = 10_000
+ skip 'This is unstable on GitHub Actions --jit-wait. TODO: debug it' if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
+ max = 1000
assert_equal(max, max.times{
Fiber.new{}
})
@@ -50,7 +50,7 @@ class TestFiber < Test::Unit::TestCase
end
def test_many_fibers_with_threads
- assert_normal_exit <<-SRC, timeout: 60
+ assert_normal_exit <<-SRC, timeout: (/solaris/i =~ RUBY_PLATFORM ? 1000 : 60)
max = 1000
@cnt = 0
(1..100).map{|ti|
@@ -169,6 +169,16 @@ class TestFiber < Test::Unit::TestCase
assert_equal(:ok, fib.raise)
end
+ def test_raise_transferring_fiber
+ root = Fiber.current
+ fib = Fiber.new { root.transfer }
+ fib.transfer
+ assert_raise(RuntimeError){
+ fib.raise "can raise with transfer: true"
+ }
+ assert_not_predicate(fib, :alive?)
+ end
+
def test_transfer
ary = []
f2 = nil
@@ -184,6 +194,33 @@ class TestFiber < Test::Unit::TestCase
assert_equal([:baz], ary)
end
+ def test_terminate_transferred_fiber
+ log = []
+ fa1 = fa2 = fb1 = r1 = nil
+
+ fa1 = Fiber.new{
+ fa2 = Fiber.new{
+ log << :fa2_terminate
+ }
+ fa2.resume
+ log << :fa1_terminate
+ }
+ fb1 = Fiber.new{
+ fa1.transfer
+ log << :fb1_terminate
+ }
+
+ r1 = Fiber.new{
+ fb1.transfer
+ log << :r1_terminate
+ }
+
+ r1.resume
+ log << :root_terminate
+
+ assert_equal [:fa2_terminate, :fa1_terminate, :r1_terminate, :root_terminate], log
+ end
+
def test_tls
#
def tvar(var, val)
@@ -278,29 +315,71 @@ class TestFiber < Test::Unit::TestCase
assert_instance_of(Class, Fiber.new(&Class.new.method(:undef_method)).resume(:to_s), bug5083)
end
- def test_prohibit_resume_transferred_fiber
+ def test_prohibit_transfer_to_resuming_fiber
+ root_fiber = Fiber.current
+
assert_raise(FiberError){
- root_fiber = Fiber.current
- f = Fiber.new{
- root_fiber.transfer
- }
- f.transfer
- f.resume
+ fiber = Fiber.new{ root_fiber.transfer }
+ fiber.resume
+ }
+
+ fa1 = Fiber.new{
+ _fa2 = Fiber.new{ root_fiber.transfer }
+ }
+ fb1 = Fiber.new{
+ _fb2 = Fiber.new{ root_fiber.transfer }
}
+ fa1.transfer
+ fb1.transfer
+
assert_raise(FiberError){
- g=nil
- f=Fiber.new{
- g.resume
- g.resume
- }
- g=Fiber.new{
- f.resume
- f.resume
+ fa1.transfer
+ }
+ assert_raise(FiberError){
+ fb1.transfer
+ }
+ end
+
+ def test_prohibit_transfer_to_yielding_fiber
+ f1 = f2 = f3 = nil
+
+ f1 = Fiber.new{
+ f2 = Fiber.new{
+ f3 = Fiber.new{
+ p f3: Fiber.yield
+ }
+ f3.resume
}
- f.transfer
+ f2.resume
+ }
+ f1.resume
+
+ assert_raise(FiberError){ f3.transfer 10 }
+ end
+
+ def test_prohibit_resume_to_transferring_fiber
+ root_fiber = Fiber.current
+
+ assert_raise(FiberError){
+ Fiber.new{
+ root_fiber.resume
+ }.transfer
+ }
+
+ f1 = f2 = nil
+ f1 = Fiber.new do
+ f2.transfer
+ end
+ f2 = Fiber.new do
+ f1.resume # attempt to resume transferring fiber
+ end
+
+ assert_raise(FiberError){
+ f1.transfer
}
end
+
def test_fork_from_fiber
skip 'fork not supported' unless Process.respond_to?(:fork)
pid = nil
@@ -312,13 +391,12 @@ class TestFiber < Test::Unit::TestCase
Fiber.new {
xpid = fork do
# enough to trigger GC on old root fiber
- count = 10000
- count = 1000 if /openbsd/i =~ RUBY_PLATFORM
+ count = 1000
count.times do
Fiber.new {}.transfer
Fiber.new { Fiber.yield }
end
- exit!(0)
+ exit!(true)
end
}.transfer
_, status = Process.waitpid2(xpid)
@@ -327,8 +405,13 @@ class TestFiber < Test::Unit::TestCase
end.resume
end
pid, status = Process.waitpid2(pid)
- assert_equal(0, status.exitstatus, bug5700)
- assert_equal(false, status.signaled?, bug5700)
+ assert_not_predicate(status, :signaled?, bug5700)
+ assert_predicate(status, :success?, bug5700)
+
+ pid = Fiber.new {fork}.resume
+ pid, status = Process.waitpid2(pid)
+ assert_not_predicate(status, :signaled?)
+ assert_predicate(status, :success?)
end
def test_exit_in_fiber
@@ -341,56 +424,12 @@ class TestFiber < Test::Unit::TestCase
def test_fatal_in_fiber
assert_in_out_err(["-r-test-/fatal/rb_fatal", "-e", <<-EOS], "", [], /ok/)
Fiber.new{
- rb_fatal "ok"
+ Bug.rb_fatal "ok"
}.resume
puts :ng # unreachable.
EOS
end
- def invoke_rec script, vm_stack_size, machine_stack_size, use_length = true
- env = {}
- env['RUBY_FIBER_VM_STACK_SIZE'] = vm_stack_size.to_s if vm_stack_size
- env['RUBY_FIBER_MACHINE_STACK_SIZE'] = machine_stack_size.to_s if machine_stack_size
- out = Dir.mktmpdir("test_fiber") {|tmpdir|
- out, err, status = EnvUtil.invoke_ruby([env, '-e', script], '', true, true, chdir: tmpdir, timeout: 30)
- assert(!status.signaled?, FailDesc[status, nil, err])
- out
- }
- use_length ? out.length : out
- end
-
- def test_stack_size
- h_default = eval(invoke_rec('p RubyVM::DEFAULT_PARAMS', nil, nil, false))
- h_0 = eval(invoke_rec('p RubyVM::DEFAULT_PARAMS', 0, 0, false))
- h_large = eval(invoke_rec('p RubyVM::DEFAULT_PARAMS', 1024 * 1024 * 5, 1024 * 1024 * 10, false))
-
- assert_operator(h_default[:fiber_vm_stack_size], :>, h_0[:fiber_vm_stack_size])
- assert_operator(h_default[:fiber_vm_stack_size], :<, h_large[:fiber_vm_stack_size])
- assert_operator(h_default[:fiber_machine_stack_size], :>=, h_0[:fiber_machine_stack_size])
- assert_operator(h_default[:fiber_machine_stack_size], :<=, h_large[:fiber_machine_stack_size])
-
- # check VM machine stack size
- script = '$stdout.sync=true; def rec; print "."; rec; end; Fiber.new{rec}.resume'
- size_default = invoke_rec script, nil, nil
- assert_operator(size_default, :>, 0)
- size_0 = invoke_rec script, 0, nil
- assert_operator(size_default, :>, size_0)
- size_large = invoke_rec script, 1024 * 1024 * 5, nil
- assert_operator(size_default, :<, size_large)
-
- return if /mswin|mingw/ =~ RUBY_PLATFORM
-
- # check machine stack size
- # Note that machine stack size may not change size (depend on OSs)
- script = '$stdout.sync=true; def rec; print "."; 1.times{1.times{1.times{rec}}}; end; Fiber.new{rec}.resume'
- vm_stack_size = 1024 * 1024
- size_default = invoke_rec script, vm_stack_size, nil
- size_0 = invoke_rec script, vm_stack_size, 0
- assert_operator(size_default, :>=, size_0)
- size_large = invoke_rec script, vm_stack_size, 1024 * 1024 * 10
- assert_operator(size_default, :<=, size_large)
- end
-
def test_separate_lastmatch
bug7678 = '[ruby-core:51331]'
/a/ =~ "a"
diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb
index 96e2ca291f..d570b6f6fb 100644
--- a/test/ruby/test_file.rb
+++ b/test/ruby/test_file.rb
@@ -233,11 +233,9 @@ class TestFile < Test::Unit::TestCase
end
def test_chown
- assert_nothing_raised {
- File.open(__FILE__) {|f| f.chown(-1, -1) }
- }
- assert_nothing_raised("[ruby-dev:27140]") {
- File.open(__FILE__) {|f| f.chown nil, nil }
+ Tempfile.create("test-chown") {|f|
+ assert_nothing_raised {f.chown(-1, -1)}
+ assert_nothing_raised("[ruby-dev:27140]") {f.chown(nil, nil)}
}
end
diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb
index 975bcb6bc2..a960ef0d74 100644
--- a/test/ruby/test_file_exhaustive.rb
+++ b/test/ruby/test_file_exhaustive.rb
@@ -78,6 +78,19 @@ class TestFileExhaustive < Test::Unit::TestCase
@notownedfile
end
+ def grpownedfile
+ return nil unless POSIX
+ return @grpownedfile if defined? @grpownedfile
+ if group = (Process.groups - [Process.egid]).last
+ grpownedfile = make_tmp_filename("grpownedfile")
+ make_file("grpowned", grpownedfile)
+ File.chown(nil, group, grpownedfile)
+ return @grpownedfile = grpownedfile
+ end
+ rescue
+ @grpownedfile = nil
+ end
+
def suidfile
return @suidfile if defined? @suidfile
if POSIX
@@ -130,7 +143,7 @@ class TestFileExhaustive < Test::Unit::TestCase
@hardlinkfile = make_tmp_filename("hardlinkfile")
begin
File.link(regular_file, @hardlinkfile)
- rescue NotImplementedError, Errno::EINVAL # EINVAL for Windows Vista
+ rescue NotImplementedError, Errno::EINVAL, Errno::EACCES # EINVAL for Windows Vista, EACCES for Android Termux
@hardlinkfile = nil
end
@hardlinkfile
@@ -160,9 +173,7 @@ class TestFileExhaustive < Test::Unit::TestCase
end
def chardev
- return @chardev if defined? @chardev
- @chardev = File::NULL == "/dev/null" ? "/dev/null" : nil
- @chardev
+ File::NULL
end
def blockdev
@@ -319,7 +330,7 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_file.not_chardev?(regular_file)
assert_file.not_chardev?(utf8_file)
assert_file.not_chardev?(nofile)
- assert_file.chardev?(chardev) if chardev
+ assert_file.chardev?(chardev)
end
def test_exist_p
@@ -493,6 +504,9 @@ class TestFileExhaustive < Test::Unit::TestCase
def test_grpowned_p ## xxx
assert_file.grpowned?(regular_file)
assert_file.grpowned?(utf8_file)
+ if file = grpownedfile
+ assert_file.grpowned?(file)
+ end
end if POSIX
def io_open(file_name)
@@ -679,6 +693,13 @@ class TestFileExhaustive < Test::Unit::TestCase
File.utime(t + 1, t + 2, zerofile)
assert_equal(t + 1, File.atime(zerofile))
assert_equal(t + 2, File.mtime(zerofile))
+ Dir.mktmpdir do |dir|
+ Dir.chdir(dir) do
+ path = "foo\u{30b3 30d4 30fc}"
+ File.write(path, "") rescue next
+ assert_equal(1, File.utime(nil, nil, path))
+ end
+ end
end
def test_utime_symlinkfile
@@ -880,6 +901,8 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_equal("#{Dir.pwd}/#{path}", File.expand_path(path))
assert_incompatible_encoding {|d| File.expand_path(d)}
+
+ assert_equal(Encoding::UTF_8, File.expand_path("foo", "#{drive}/").encoding)
end
def test_expand_path_encoding_filesystem
@@ -1245,6 +1268,12 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_equal(@dir, File.dirname(regular_file))
assert_equal(@dir, File.dirname(utf8_file))
assert_equal(".", File.dirname(""))
+ assert_equal(regular_file, File.dirname(regular_file, 0))
+ assert_equal(@dir, File.dirname(regular_file, 1))
+ assert_equal(File.dirname(@dir), File.dirname(regular_file, 2))
+ return if /mswin/ =~ RUBY_PLATFORM && ENV.key?('GITHUB_ACTIONS') # rootdir and tmpdir are in different drives
+ assert_equal(rootdir, File.dirname(regular_file, regular_file.count('/')))
+ assert_raise(ArgumentError) {File.dirname(regular_file, -1)}
end
def test_dirname_encoding
@@ -1377,21 +1406,24 @@ class TestFileExhaustive < Test::Unit::TestCase
end
def test_flock_exclusive
+ timeout = EnvUtil.apply_timeout_scale(0.1).to_s
File.open(regular_file, "r+") do |f|
f.flock(File::LOCK_EX)
- assert_separately(["-rtimeout", "-", regular_file], "#{<<~begin}#{<<~"end;"}")
- begin
+ assert_separately(["-rtimeout", "-", regular_file, timeout], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ timeout = ARGV[1].to_f
open(ARGV[0], "r") do |f|
- Timeout.timeout(0.1) do
+ Timeout.timeout(timeout) do
assert(!f.flock(File::LOCK_SH|File::LOCK_NB))
end
end
end;
- assert_separately(["-rtimeout", "-", regular_file], "#{<<~begin}#{<<~"end;"}")
- begin
+ assert_separately(["-rtimeout", "-", regular_file, timeout], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ timeout = ARGV[1].to_f
open(ARGV[0], "r") do |f|
assert_raise(Timeout::Error) do
- Timeout.timeout(0.1) do
+ Timeout.timeout(timeout) do
f.flock(File::LOCK_SH)
end
end
@@ -1403,21 +1435,24 @@ class TestFileExhaustive < Test::Unit::TestCase
end
def test_flock_shared
+ timeout = EnvUtil.apply_timeout_scale(0.1).to_s
File.open(regular_file, "r+") do |f|
f.flock(File::LOCK_SH)
- assert_separately(["-rtimeout", "-", regular_file], "#{<<~begin}#{<<~"end;"}")
- begin
+ assert_separately(["-rtimeout", "-", regular_file, timeout], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ timeout = ARGV[1].to_f
open(ARGV[0], "r") do |f|
- Timeout.timeout(0.1) do
+ Timeout.timeout(timeout) do
assert(f.flock(File::LOCK_SH))
end
end
end;
- assert_separately(["-rtimeout", "-", regular_file], "#{<<~begin}#{<<~"end;"}")
- begin
+ assert_separately(["-rtimeout", "-", regular_file, timeout], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ timeout = ARGV[1].to_f
open(ARGV[0], "r+") do |f|
assert_raise(Timeout::Error) do
- Timeout.timeout(0.1) do
+ Timeout.timeout(timeout) do
f.flock(File::LOCK_EX)
end
end
@@ -1439,6 +1474,7 @@ class TestFileExhaustive < Test::Unit::TestCase
fn1,
zerofile,
notownedfile,
+ grpownedfile,
suidfile,
sgidfile,
stickyfile,
@@ -1449,31 +1485,56 @@ class TestFileExhaustive < Test::Unit::TestCase
fifo,
socket
].compact.each do |f|
- assert_equal(File.atime(f), test(?A, f))
- assert_equal(File.ctime(f), test(?C, f))
- assert_equal(File.mtime(f), test(?M, f))
- assert_equal(File.blockdev?(f), test(?b, f))
- assert_equal(File.chardev?(f), test(?c, f))
- assert_equal(File.directory?(f), test(?d, f))
- assert_equal(File.exist?(f), test(?e, f))
- assert_equal(File.file?(f), test(?f, f))
- assert_equal(File.setgid?(f), test(?g, f))
- assert_equal(File.grpowned?(f), test(?G, f))
- assert_equal(File.sticky?(f), test(?k, f))
- assert_equal(File.symlink?(f), test(?l, f))
- assert_equal(File.owned?(f), test(?o, f))
- assert_nothing_raised { test(?O, f) }
- assert_equal(File.pipe?(f), test(?p, f))
- assert_equal(File.readable?(f), test(?r, f))
- assert_equal(File.readable_real?(f), test(?R, f))
- assert_equal(File.size?(f), test(?s, f))
- assert_equal(File.socket?(f), test(?S, f))
- assert_equal(File.setuid?(f), test(?u, f))
- assert_equal(File.writable?(f), test(?w, f))
- assert_equal(File.writable_real?(f), test(?W, f))
- assert_equal(File.executable?(f), test(?x, f))
- assert_equal(File.executable_real?(f), test(?X, f))
- assert_equal(File.zero?(f), test(?z, f))
+ assert_equal(File.atime(f), test(?A, f), f)
+ assert_equal(File.ctime(f), test(?C, f), f)
+ assert_equal(File.mtime(f), test(?M, f), f)
+ assert_equal(File.blockdev?(f), test(?b, f), f)
+ assert_equal(File.chardev?(f), test(?c, f), f)
+ assert_equal(File.directory?(f), test(?d, f), f)
+ assert_equal(File.exist?(f), test(?e, f), f)
+ assert_equal(File.file?(f), test(?f, f), f)
+ assert_equal(File.setgid?(f), test(?g, f), f)
+ assert_equal(File.grpowned?(f), test(?G, f), f)
+ assert_equal(File.sticky?(f), test(?k, f), f)
+ assert_equal(File.symlink?(f), test(?l, f), f)
+ assert_equal(File.owned?(f), test(?o, f), f)
+ assert_nothing_raised(f) { test(?O, f) }
+ assert_equal(File.pipe?(f), test(?p, f), f)
+ assert_equal(File.readable?(f), test(?r, f), f)
+ assert_equal(File.readable_real?(f), test(?R, f), f)
+ assert_equal(File.size?(f), test(?s, f), f)
+ assert_equal(File.socket?(f), test(?S, f), f)
+ assert_equal(File.setuid?(f), test(?u, f), f)
+ assert_equal(File.writable?(f), test(?w, f), f)
+ assert_equal(File.writable_real?(f), test(?W, f), f)
+ assert_equal(File.executable?(f), test(?x, f), f)
+ assert_equal(File.executable_real?(f), test(?X, f), f)
+ assert_equal(File.zero?(f), test(?z, f), f)
+
+ stat = File.stat(f)
+ assert_equal(stat.atime, File.atime(f), f)
+ assert_equal(stat.ctime, File.ctime(f), f)
+ assert_equal(stat.mtime, File.mtime(f), f)
+ assert_equal(stat.blockdev?, File.blockdev?(f), f)
+ assert_equal(stat.chardev?, File.chardev?(f), f)
+ assert_equal(stat.directory?, File.directory?(f), f)
+ assert_equal(stat.file?, File.file?(f), f)
+ assert_equal(stat.setgid?, File.setgid?(f), f)
+ assert_equal(stat.grpowned?, File.grpowned?(f), f)
+ assert_equal(stat.sticky?, File.sticky?(f), f)
+ assert_equal(File.lstat(f).symlink?, File.symlink?(f), f)
+ assert_equal(stat.owned?, File.owned?(f), f)
+ assert_equal(stat.pipe?, File.pipe?(f), f)
+ assert_equal(stat.readable?, File.readable?(f), f)
+ assert_equal(stat.readable_real?, File.readable_real?(f), f)
+ assert_equal(stat.size?, File.size?(f), f)
+ assert_equal(stat.socket?, File.socket?(f), f)
+ assert_equal(stat.setuid?, File.setuid?(f), f)
+ assert_equal(stat.writable?, File.writable?(f), f)
+ assert_equal(stat.writable_real?, File.writable_real?(f), f)
+ assert_equal(stat.executable?, File.executable?(f), f)
+ assert_equal(stat.executable_real?, File.executable_real?(f), f)
+ assert_equal(stat.zero?, File.zero?(f), f)
end
assert_equal(false, test(?-, @dir, fn1))
assert_equal(true, test(?-, fn1, fn1))
@@ -1583,7 +1644,7 @@ class TestFileExhaustive < Test::Unit::TestCase
def test_stat_chardev_p
assert_not_predicate(File::Stat.new(@dir), :chardev?)
assert_not_predicate(File::Stat.new(regular_file), :chardev?)
- assert_predicate(File::Stat.new(chardev), :chardev?) if chardev
+ assert_predicate(File::Stat.new(chardev), :chardev?)
end
def test_stat_readable_p
@@ -1674,6 +1735,9 @@ class TestFileExhaustive < Test::Unit::TestCase
def test_stat_grpowned_p ## xxx
assert_predicate(File::Stat.new(regular_file), :grpowned?)
+ if file = grpownedfile
+ assert_predicate(File::Stat.new(file), :grpowned?)
+ end
end if POSIX
def test_stat_suid
diff --git a/test/ruby/test_fixnum.rb b/test/ruby/test_fixnum.rb
index bd18067dda..2878258920 100644
--- a/test/ruby/test_fixnum.rb
+++ b/test/ruby/test_fixnum.rb
@@ -4,7 +4,6 @@ require 'test/unit'
class TestFixnum < Test::Unit::TestCase
def setup
@verbose = $VERBOSE
- $VERBOSE = nil
end
def teardown
diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb
index 7cbf3b5a8f..57a46fce92 100644
--- a/test/ruby/test_float.rb
+++ b/test/ruby/test_float.rb
@@ -171,6 +171,24 @@ class TestFloat < Test::Unit::TestCase
assert_raise(ArgumentError, n += z + "A") {Float(n)}
assert_raise(ArgumentError, n += z + ".0") {Float(n)}
end
+
+ x = nil
+ 2000.times do
+ x = Float("0x"+"0"*30)
+ break unless x == 0.0
+ end
+ assert_equal(0.0, x, ->{"%a" % x})
+ x = nil
+ 2000.times do
+ begin
+ x = Float("0x1."+"0"*270)
+ rescue ArgumentError => e
+ raise unless /"0x1\.0{270}"/ =~ e.message
+ else
+ break
+ end
+ end
+ assert_nil(x, ->{"%a" % x})
end
def test_divmod
@@ -305,6 +323,7 @@ class TestFloat < Test::Unit::TestCase
assert_equal(1.0, 1.0 ** (2**32))
assert_equal(1.0, 1.0 ** 1.0)
assert_raise(TypeError) { 1.0 ** nil }
+ assert_equal(9.0, 3.0 ** 2)
end
def test_eql
@@ -764,6 +783,9 @@ class TestFloat < Test::Unit::TestCase
assert_raise_with_message(ArgumentError, /xxx/) {
1.0.round(half: "\0xxx")
}
+ assert_raise_with_message(Encoding::CompatibilityError, /ASCII incompatible/) {
+ 1.0.round(half: "up".force_encoding("utf-16be"))
+ }
end
def test_Float
@@ -878,6 +900,11 @@ class TestFloat < Test::Unit::TestCase
end
assert_equal([5.0, 4.0, 3.0, 2.0], 5.0.step(1.5, -1).to_a)
+
+ assert_equal(11, ((0.24901079128550474)..(340.2500808898068)).step(34.00010700985213).to_a.size)
+ assert_equal(11, ((0.24901079128550474)..(340.25008088980684)).step(34.00010700985213).to_a.size)
+ assert_equal(11, ((-0.24901079128550474)..(-340.2500808898068)).step(-34.00010700985213).to_a.size)
+ assert_equal(11, ((-0.24901079128550474)..(-340.25008088980684)).step(-34.00010700985213).to_a.size)
end
def test_step2
@@ -889,7 +916,9 @@ class TestFloat < Test::Unit::TestCase
a = rand
b = a+rand*1000
s = (b - a) / 10
- assert_equal(10, (a...b).step(s).to_a.length)
+ b = a + s*9.999999
+ seq = (a...b).step(s)
+ assert_equal(10, seq.to_a.length, seq.inspect)
end
assert_equal([1.0, 2.9, 4.8, 6.699999999999999], (1.0...6.8).step(1.9).to_a)
@@ -898,6 +927,11 @@ class TestFloat < Test::Unit::TestCase
(1.0 ... e).step(1E-16) do |n|
assert_operator(n, :<=, e)
end
+
+ assert_equal(10, ((0.24901079128550474)...(340.2500808898068)).step(34.00010700985213).to_a.size)
+ assert_equal(11, ((0.24901079128550474)...(340.25008088980684)).step(34.00010700985213).to_a.size)
+ assert_equal(10, ((-0.24901079128550474)...(-340.2500808898068)).step(-34.00010700985213).to_a.size)
+ assert_equal(11, ((-0.24901079128550474)...(-340.25008088980684)).step(-34.00010700985213).to_a.size)
end
def test_singleton_method
diff --git a/test/ruby/test_frozen_error.rb b/test/ruby/test_frozen_error.rb
new file mode 100644
index 0000000000..ace1e4c775
--- /dev/null
+++ b/test/ruby/test_frozen_error.rb
@@ -0,0 +1,57 @@
+require 'test/unit'
+
+class TestFrozenError < Test::Unit::TestCase
+ def test_new_default
+ exc = FrozenError.new
+ assert_equal("FrozenError", exc.message)
+ assert_raise_with_message(ArgumentError, "no receiver is available") {
+ exc.receiver
+ }
+ end
+
+ def test_new_message
+ exc = FrozenError.new("bar")
+ assert_equal("bar", exc.message)
+ assert_raise_with_message(ArgumentError, "no receiver is available") {
+ exc.receiver
+ }
+ end
+
+ def test_new_receiver
+ obj = Object.new
+ exc = FrozenError.new("bar", receiver: obj)
+ assert_equal("bar", exc.message)
+ assert_same(obj, exc.receiver)
+ end
+
+ def test_message
+ obj = Object.new.freeze
+ e = assert_raise_with_message(FrozenError, /can't modify frozen #{obj.class}/) {
+ obj.instance_variable_set(:@test, true)
+ }
+ assert_include(e.message, obj.inspect)
+
+ klass = Class.new do
+ def init
+ @x = true
+ end
+ def inspect
+ init
+ super
+ end
+ end
+ obj = klass.new.freeze
+ e = assert_raise_with_message(FrozenError, /can't modify frozen #{obj.class}/) {
+ obj.init
+ }
+ assert_include(e.message, klass.inspect)
+ end
+
+ def test_receiver
+ obj = Object.new.freeze
+ e = assert_raise(FrozenError) {def obj.foo; end}
+ assert_same(obj, e.receiver)
+ e = assert_raise(FrozenError) {obj.singleton_class.const_set(:A, 2)}
+ assert_same(obj.singleton_class, e.receiver)
+ end
+end
diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb
index ef99f69f50..fa81bcb1ad 100644
--- a/test/ruby/test_gc.rb
+++ b/test/ruby/test_gc.rb
@@ -56,6 +56,7 @@ class TestGc < Test::Unit::TestCase
return unless use_rgengc?
skip 'stress' if GC.stress
+ 3.times { GC.start } # full mark and next time it should be minor mark
GC.start(full_mark: false)
assert_nil GC.latest_gc_info(:major_by)
@@ -91,16 +92,23 @@ class TestGc < Test::Unit::TestCase
assert_kind_of(Integer, res[:count])
stat, count = {}, {}
- GC.start
- GC.stat(stat)
- ObjectSpace.count_objects(count)
+ 2.times{ # to ignore const cache imemo creation
+ GC.start
+ GC.stat(stat)
+ ObjectSpace.count_objects(count)
+ # repeat same methods invocation for cache object creation.
+ GC.stat(stat)
+ ObjectSpace.count_objects(count)
+ }
assert_equal(count[:TOTAL]-count[:FREE], stat[:heap_live_slots])
assert_equal(count[:FREE], stat[:heap_free_slots])
# measure again without GC.start
- 1000.times{ "a" + "b" }
- GC.stat(stat)
- ObjectSpace.count_objects(count)
+ 2.times{ # to ignore const cache imemo creation
+ 1000.times{ "a" + "b" }
+ GC.stat(stat)
+ ObjectSpace.count_objects(count)
+ }
assert_equal(count[:FREE], stat[:heap_free_slots])
end
@@ -141,10 +149,15 @@ class TestGc < Test::Unit::TestCase
assert_equal :newobj, GC.latest_gc_info[:gc_by]
eom
+ GC.latest_gc_info(h = {}) # allocate hash and rehearsal
GC.start
- assert_equal :force, GC.latest_gc_info[:major_by] if use_rgengc?
- assert_equal :method, GC.latest_gc_info[:gc_by]
- assert_equal true, GC.latest_gc_info[:immediate_sweep]
+ GC.start
+ GC.start
+ GC.latest_gc_info(h)
+
+ assert_equal :force, h[:major_by] if use_rgengc?
+ assert_equal :method, h[:gc_by]
+ assert_equal true, h[:immediate_sweep]
GC.stress = true
assert_equal :force, GC.latest_gc_info[:major_by]
@@ -162,6 +175,16 @@ class TestGc < Test::Unit::TestCase
assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {GC.latest_gc_info(:"\u{30eb 30d3 30fc}")}
end
+ def test_stress_compile_send
+ assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "")
+ GC.stress = true
+ begin
+ eval("A::B.c(1, 1, d: 234)")
+ rescue
+ end
+ EOS
+ end
+
def test_singleton_method
assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "[ruby-dev:42832]")
GC.stress = true
@@ -191,6 +214,11 @@ class TestGc < Test::Unit::TestCase
def test_gc_parameter
env = {
+ "RUBY_GC_HEAP_INIT_SLOTS" => "100"
+ }
+ assert_in_out_err([env, "-W0", "-e", "exit"], "", [], [], "[Bug #19284]")
+
+ env = {
"RUBY_GC_MALLOC_LIMIT" => "60000000",
"RUBY_GC_HEAP_INIT_SLOTS" => "100000"
}
@@ -224,12 +252,6 @@ class TestGc < Test::Unit::TestCase
# always full GC when RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR < 1.0
assert_in_out_err([env, "-e", "1000_000.times{Object.new}; p(GC.stat[:minor_gc_count] < GC.stat[:major_gc_count])"], "", ['true'], //, "") if use_rgengc?
- # check obsolete
- assert_in_out_err([{'RUBY_FREE_MIN' => '100'}, '-w', '-eexit'], '', [],
- /RUBY_FREE_MIN is obsolete. Use RUBY_GC_HEAP_FREE_SLOTS instead/)
- assert_in_out_err([{'RUBY_HEAP_MIN_SLOTS' => '100'}, '-w', '-eexit'], '', [],
- /RUBY_HEAP_MIN_SLOTS is obsolete. Use RUBY_GC_HEAP_INIT_SLOTS instead/)
-
env = {
"RUBY_GC_MALLOC_LIMIT" => "60000000",
"RUBY_GC_MALLOC_LIMIT_MAX" => "160000000",
@@ -425,49 +447,43 @@ class TestGc < Test::Unit::TestCase
end
def test_exception_in_finalizer_procs
- result = []
+ assert_in_out_err(["-W0"], "#{<<~"begin;"}\n#{<<~'end;'}", %w[c1 c2])
c1 = proc do
- result << :c1
+ puts "c1"
raise
end
c2 = proc do
- result << :c2
+ puts "c2"
raise
end
- tap {
- tap {
+ begin;
+ tap do
obj = Object.new
ObjectSpace.define_finalizer(obj, c1)
ObjectSpace.define_finalizer(obj, c2)
obj = nil
- }
- }
- GC.start
- skip "finalizers did not get run" if result.empty?
- assert_equal([:c1, :c2], result)
+ end
+ end;
end
def test_exception_in_finalizer_method
- @result = []
+ assert_in_out_err(["-W0"], "#{<<~"begin;"}\n#{<<~'end;'}", %w[c1 c2])
def self.c1(x)
- @result << :c1
+ puts "c1"
raise
end
def self.c2(x)
- @result << :c2
+ puts "c2"
raise
end
- tap {
- tap {
+ begin;
+ tap do
obj = Object.new
ObjectSpace.define_finalizer(obj, method(:c1))
ObjectSpace.define_finalizer(obj, method(:c2))
obj = nil
- }
- }
- GC.start
- skip "finalizers did not get run" if @result.empty?
- assert_equal([:c1, :c2], @result)
+ end
+ end;
end
def test_object_ids_never_repeat
@@ -477,4 +493,10 @@ class TestGc < Test::Unit::TestCase
b = 1000.times.map { Object.new.object_id }
assert_empty(a & b)
end
+
+ def test_ast_node_buffer
+ # https://github.com/ruby/ruby/pull/4416
+ Module.new.class_eval( (["# shareable_constant_value: literal"] +
+ (0..100000).map {|i| "M#{ i } = {}" }).join("\n"))
+ end
end
diff --git a/test/ruby/test_gc_compact.rb b/test/ruby/test_gc_compact.rb
index e93e775279..42ad028530 100644
--- a/test/ruby/test_gc_compact.rb
+++ b/test/ruby/test_gc_compact.rb
@@ -1,8 +1,115 @@
# frozen_string_literal: true
require 'test/unit'
require 'fiddle'
+require 'etc'
+
+if RUBY_PLATFORM =~ /s390x/
+ warn "Currently, it is known that the compaction does not work well on s390x; contribution is welcome https://github.com/ruby/ruby/pull/5077"
+ return
+end
class TestGCCompact < Test::Unit::TestCase
+ module SupportsCompact
+ def setup
+ skip "autocompact not supported on this platform" unless supports_auto_compact?
+ super
+ end
+
+ private
+
+ def supports_auto_compact?
+ return true unless defined?(Etc::SC_PAGE_SIZE)
+
+ begin
+ return GC::INTERNAL_CONSTANTS[:HEAP_PAGE_SIZE] % Etc.sysconf(Etc::SC_PAGE_SIZE) == 0
+ rescue NotImplementedError
+ rescue ArgumentError
+ end
+
+ true
+ end
+ end
+
+ include SupportsCompact
+
+ class AutoCompact < Test::Unit::TestCase
+ include SupportsCompact
+
+ def test_enable_autocompact
+ before = GC.auto_compact
+ GC.auto_compact = true
+ assert GC.auto_compact
+ ensure
+ GC.auto_compact = before
+ end
+
+ def test_disable_autocompact
+ before = GC.auto_compact
+ GC.auto_compact = false
+ refute GC.auto_compact
+ ensure
+ GC.auto_compact = before
+ end
+
+ def test_major_compacts
+ before = GC.auto_compact
+ GC.auto_compact = true
+ compact = GC.stat :compact_count
+ GC.start
+ assert_operator GC.stat(:compact_count), :>, compact
+ ensure
+ GC.auto_compact = before
+ end
+
+ def test_implicit_compaction_does_something
+ before = GC.auto_compact
+ list = []
+ list2 = []
+
+ # Try to make some fragmentation
+ 500.times {
+ list << Object.new
+ Object.new
+ Object.new
+ }
+ count = GC.stat :compact_count
+ GC.auto_compact = true
+ n = 1_000_000
+ n.times do
+ break if count < GC.stat(:compact_count)
+ list2 << Object.new
+ end and skip "implicit compaction didn't happen within #{n} objects"
+ compact_stats = GC.latest_compact_info
+ refute_predicate compact_stats[:considered], :empty?
+ refute_predicate compact_stats[:moved], :empty?
+ ensure
+ GC.auto_compact = before
+ end
+ end
+
+ def os_page_size
+ return true unless defined?(Etc::SC_PAGE_SIZE)
+ end
+
+ def setup
+ skip "autocompact not supported on this platform" unless supports_auto_compact?
+ super
+ end
+
+ def test_gc_compact_stats
+ list = []
+
+ # Try to make some fragmentation
+ 500.times {
+ list << Object.new
+ Object.new
+ Object.new
+ }
+ compact_stats = GC.compact
+ refute_predicate compact_stats[:considered], :empty?
+ refute_predicate compact_stats[:moved], :empty?
+ end
+
def memory_location(obj)
(Fiddle.dlwrap(obj) >> 1)
end
@@ -39,20 +146,24 @@ class TestGCCompact < Test::Unit::TestCase
hash = list_of_objects.hash
GC.verify_compaction_references(toward: :empty)
assert_equal hash, list_of_objects.hash
- end
-
- def walk_ast ast
- children = ast.children.grep(RubyVM::AbstractSyntaxTree::Node)
- children.each do |child|
- assert child.type
- walk_ast child
- end
+ GC.verify_compaction_references(double_heap: false)
+ assert_equal hash, list_of_objects.hash
end
def test_ast_compacts
- ast = RubyVM::AbstractSyntaxTree.parse_file __FILE__
- assert GC.compact
- walk_ast ast
+ assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
+ begin;
+ def walk_ast ast
+ children = ast.children.grep(RubyVM::AbstractSyntaxTree::Node)
+ children.each do |child|
+ assert child.type
+ walk_ast child
+ end
+ end
+ ast = RubyVM::AbstractSyntaxTree.parse_file #{__FILE__.dump}
+ assert GC.compact
+ walk_ast ast
+ end;
end
def test_compact_count
diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb
index ccc3355930..683ec3855d 100644
--- a/test/ruby/test_hash.rb
+++ b/test/ruby/test_hash.rb
@@ -86,7 +86,6 @@ class TestHash < Test::Unit::TestCase
'nil' => nil
]
@verbose = $VERBOSE
- $VERBOSE = nil
end
def teardown
@@ -114,16 +113,36 @@ class TestHash < Test::Unit::TestCase
assert_equal(2, h[1])
end
- def test_dup_will_rehash
- set1 = @cls[]
- set2 = @cls[set1 => true]
+ def test_dup_will_not_rehash
+ assert_hash_does_not_rehash(&:dup)
+ end
- set1[set1] = true
+ def assert_hash_does_not_rehash
+ obj = Object.new
+ class << obj
+ attr_accessor :hash_calls
+ def hash
+ @hash_calls += 1
+ super
+ end
+ end
+ obj.hash_calls = 0
+ hash = {obj => 42}
+ assert_equal(1, obj.hash_calls)
+ yield hash
+ assert_equal(1, obj.hash_calls)
+ end
- assert_equal set2, set2.dup
+ def test_select_reject_will_not_rehash
+ assert_hash_does_not_rehash do |hash|
+ hash.select { true }
+ end
+ assert_hash_does_not_rehash do |hash|
+ hash.reject { false }
+ end
end
- def test_s_AREF
+ def test_s_AREF_from_hash
h = @cls["a" => 100, "b" => 200]
assert_equal(100, h['a'])
assert_equal(200, h['b'])
@@ -134,11 +153,21 @@ class TestHash < Test::Unit::TestCase
assert_equal(200, h['b'])
assert_nil(h['c'])
+ h = @cls[Hash.new(42)]
+ assert_nil(h['a'])
+
+ h = @cls[Hash.new {42}]
+ assert_nil(h['a'])
+ end
+
+ def test_s_AREF_from_list
h = @cls["a", 100, "b", 200]
assert_equal(100, h['a'])
assert_equal(200, h['b'])
assert_nil(h['c'])
+ end
+ def test_s_AREF_from_pairs
h = @cls[[["a", 100], ["b", 200]]]
assert_equal(100, h['a'])
assert_equal(200, h['b'])
@@ -266,10 +295,13 @@ class TestHash < Test::Unit::TestCase
end
def test_AREF_fstring_key
+ # warmup ObjectSpace.count_objects
+ ObjectSpace.count_objects
+
h = {"abc" => 1}
- before = GC.stat(:total_allocated_objects)
+ before = ObjectSpace.count_objects[:T_STRING]
5.times{ h["abc"] }
- assert_equal before, GC.stat(:total_allocated_objects)
+ assert_equal before, ObjectSpace.count_objects[:T_STRING]
end
def test_ASET_fstring_key
@@ -417,6 +449,15 @@ class TestHash < Test::Unit::TestCase
true
}
assert_equal(base.size, n)
+
+ h = base.dup
+ assert_raise(FrozenError) do
+ h.delete_if do
+ h.freeze
+ true
+ end
+ end
+ assert_equal(base.dup, h)
end
def test_keep_if
@@ -424,6 +465,14 @@ class TestHash < Test::Unit::TestCase
assert_equal({3=>4,5=>6}, h.keep_if {|k, v| k + v >= 7 })
h = @cls[1=>2,3=>4,5=>6]
assert_equal({1=>2,3=>4,5=>6}, h.keep_if{true})
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_raise(FrozenError) do
+ h.keep_if do
+ h.freeze
+ false
+ end
+ end
+ assert_equal(@cls[1=>2,3=>4,5=>6], h)
end
def test_compact
@@ -453,6 +502,7 @@ class TestHash < Test::Unit::TestCase
h1 = @cls[h => 1]
assert_equal(h1, h1.dup)
h[1] = 2
+ h1.rehash
assert_equal(h1, h1.dup)
end
@@ -699,6 +749,33 @@ class TestHash < Test::Unit::TestCase
assert_not_send([h, :instance_variable_defined?, :@foo])
end
+ def test_reject_on_identhash
+ h = @cls[1=>2,3=>4,5=>6]
+ h.compare_by_identity
+ str1 = +'str'
+ str2 = +'str'
+ h[str1] = 1
+ h[str2] = 2
+ expected = {}.compare_by_identity
+ expected[str1] = 1
+ expected[str2] = 2
+ h2 = h.reject{|k,| k != 'str'}
+ assert_equal(expected, h2)
+ assert_equal(true, h2.compare_by_identity?)
+ h2 = h.reject{true}
+ assert_equal({}.compare_by_identity, h2)
+ assert_equal(true, h2.compare_by_identity?)
+
+ h = @cls[]
+ h.compare_by_identity
+ h2 = h.reject{true}
+ assert_equal({}.compare_by_identity, h2)
+ assert_equal(true, h2.compare_by_identity?)
+ h2 = h.reject{|k,| k != 'str'}
+ assert_equal({}.compare_by_identity, h2)
+ assert_equal(true, h2.compare_by_identity?)
+ end
+
def test_reject!
base = @cls[ 1 => 'one', 2 => false, true => 'true', 'cat' => 99 ]
h1 = @cls[ 1 => 'one', 2 => false, true => 'true' ]
@@ -720,6 +797,15 @@ class TestHash < Test::Unit::TestCase
h = base.dup
assert_equal(h3, h.reject! {|k,v| v })
assert_equal(h3, h)
+
+ h = base.dup
+ assert_raise(FrozenError) do
+ h.reject! do
+ h.freeze
+ true
+ end
+ end
+ assert_equal(base.dup, h)
end
def test_replace
@@ -875,7 +961,7 @@ class TestHash < Test::Unit::TestCase
def test_to_s
h = @cls[ 1 => 2, "cat" => "dog", 1.5 => :fred ]
assert_equal(h.inspect, h.to_s)
- $, = ":"
+ assert_deprecated_warning { $, = ":" }
assert_equal(h.inspect, h.to_s)
h = @cls[]
assert_equal(h.inspect, h.to_s)
@@ -944,7 +1030,7 @@ class TestHash < Test::Unit::TestCase
end
def test_fetch2
- assert_equal(:bar, @h.fetch(0, :foo) { :bar })
+ assert_equal(:bar, assert_warning(/block supersedes default value argument/) {@h.fetch(0, :foo) { :bar }})
end
def test_default_proc
@@ -983,6 +1069,19 @@ class TestHash < Test::Unit::TestCase
assert_equal("FOO", h.shift)
end
+ def test_shift_for_empty_hash
+ # [ruby-dev:51159]
+ h = @cls[]
+ 100.times{|n|
+ while h.size < n
+ k = Random.rand 0..1<<30
+ h[k] = 1
+ end
+ 0 while h.shift
+ assert_equal({}, h)
+ }
+ end
+
def test_reject_bang2
assert_equal({1=>2}, @cls[1=>2,3=>4].reject! {|k, v| k + v == 7 })
assert_nil(@cls[1=>2,3=>4].reject! {|k, v| k == 5 })
@@ -1017,12 +1116,47 @@ class TestHash < Test::Unit::TestCase
assert_not_send([h, :instance_variable_defined?, :@foo])
end
+ def test_select_on_identhash
+ h = @cls[1=>2,3=>4,5=>6]
+ h.compare_by_identity
+ str1 = +'str'
+ str2 = +'str'
+ h[str1] = 1
+ h[str2] = 2
+ expected = {}.compare_by_identity
+ expected[str1] = 1
+ expected[str2] = 2
+ h2 = h.select{|k,| k == 'str'}
+ assert_equal(expected, h2)
+ assert_equal(true, h2.compare_by_identity?)
+ h2 = h.select{false}
+ assert_equal({}.compare_by_identity, h2)
+ assert_equal(true, h2.compare_by_identity?)
+
+ h = @cls[]
+ h.compare_by_identity
+ h2 = h.select{false}
+ assert_equal({}.compare_by_identity, h2)
+ assert_equal(true, h2.compare_by_identity?)
+ h2 = h.select{|k,| k == 'str'}
+ assert_equal({}.compare_by_identity, h2)
+ assert_equal(true, h2.compare_by_identity?)
+ end
+
def test_select!
h = @cls[1=>2,3=>4,5=>6]
assert_equal(h, h.select! {|k, v| k + v >= 7 })
assert_equal({3=>4,5=>6}, h)
h = @cls[1=>2,3=>4,5=>6]
assert_equal(nil, h.select!{true})
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_raise(FrozenError) do
+ h.select! do
+ h.freeze
+ false
+ end
+ end
+ assert_equal(@cls[1=>2,3=>4,5=>6], h)
end
def test_slice
@@ -1033,6 +1167,65 @@ class TestHash < Test::Unit::TestCase
assert_equal({}, {}.slice)
end
+ def test_slice_on_identhash
+ h = @cls[1=>2,3=>4,5=>6]
+ h.compare_by_identity
+ str1 = +'str'
+ str2 = +'str'
+ h[str1] = 1
+ h[str2] = 2
+ sliced = h.slice(str1, str2)
+ expected = {}.compare_by_identity
+ expected[str1] = 1
+ expected[str2] = 2
+ assert_equal(expected, sliced)
+ assert_equal(true, sliced.compare_by_identity?)
+ sliced = h.slice
+ assert_equal({}.compare_by_identity, sliced)
+ assert_equal(true, sliced.compare_by_identity?)
+
+ h = @cls[]
+ h.compare_by_identity
+ sliced= h.slice
+ assert_equal({}.compare_by_identity, sliced)
+ assert_equal(true, sliced.compare_by_identity?)
+ sliced = h.slice(str1, str2)
+ assert_equal({}.compare_by_identity, sliced)
+ assert_equal(true, sliced.compare_by_identity?)
+ end
+
+ def test_except
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_equal({5=>6}, h.except(1, 3))
+ assert_equal({1=>2,3=>4,5=>6}, h.except(7))
+ assert_equal({1=>2,3=>4,5=>6}, h.except)
+ assert_equal({}, {}.except)
+ end
+
+ def test_except_on_identhash
+ h = @cls[1=>2,3=>4,5=>6]
+ h.compare_by_identity
+ str1 = +'str'
+ str2 = +'str'
+ h[str1] = 1
+ h[str2] = 2
+ excepted = h.except(str1, str2)
+ assert_equal({1=>2,3=>4,5=>6}.compare_by_identity, excepted)
+ assert_equal(true, excepted.compare_by_identity?)
+ excepted = h.except
+ assert_equal(h, excepted)
+ assert_equal(true, excepted.compare_by_identity?)
+
+ h = @cls[]
+ h.compare_by_identity
+ excepted = h.except
+ assert_equal({}.compare_by_identity, excepted)
+ assert_equal(true, excepted.compare_by_identity?)
+ excepted = h.except(str1, str2)
+ assert_equal({}.compare_by_identity, excepted)
+ assert_equal(true, excepted.compare_by_identity?)
+ end
+
def test_filter
assert_equal({3=>4,5=>6}, @cls[1=>2,3=>4,5=>6].filter {|k, v| k + v >= 7 })
@@ -1067,6 +1260,14 @@ class TestHash < Test::Unit::TestCase
assert_equal({3=>4,5=>6}, h)
h = @cls[1=>2,3=>4,5=>6]
assert_equal(nil, h.filter!{true})
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_raise(FrozenError) do
+ h.filter! do
+ h.freeze
+ false
+ end
+ end
+ assert_equal(@cls[1=>2,3=>4,5=>6], h)
end
def test_clear2
@@ -1090,6 +1291,15 @@ class TestHash < Test::Unit::TestCase
assert_raise(FrozenError) { h2.replace(42) }
end
+ def test_replace_memory_leak
+ assert_no_memory_leak([], "#{<<-"begin;"}", "#{<<-'end;'}")
+ h = ("aa".."zz").each_with_index.to_h
+ 10_000.times {h.dup}
+ begin;
+ 500_000.times {h.dup.replace(h)}
+ end;
+ end
+
def test_size2
assert_equal(0, @cls[].size)
end
@@ -1101,6 +1311,7 @@ class TestHash < Test::Unit::TestCase
def o.to_hash; @cls[]; end
def o.==(x); true; end
assert_equal({}, o)
+ o.singleton_class.remove_method(:==)
def o.==(x); false; end
assert_not_equal({}, o)
@@ -1117,6 +1328,7 @@ class TestHash < Test::Unit::TestCase
def o.to_hash; @cls[]; end
def o.eql?(x); true; end
assert_send([@cls[], :eql?, o])
+ o.singleton_class.remove_method(:eql?)
def o.eql?(x); false; end
assert_not_send([@cls[], :eql?, o])
end
@@ -1157,6 +1369,20 @@ class TestHash < Test::Unit::TestCase
assert_equal({1=>8, 2=>4, 3=>4, 5=>7}, h1)
end
+ def test_update5
+ h = @cls[a: 1, b: 2, c: 3]
+ assert_raise(FrozenError) do
+ h.update({a: 10, b: 20}){ |key, v1, v2| key == :b && h.freeze; v2 }
+ end
+ assert_equal(@cls[a: 10, b: 2, c: 3], h)
+
+ h = @cls[a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10]
+ assert_raise(FrozenError) do
+ h.update({a: 10, b: 20}){ |key, v1, v2| key == :b && h.freeze; v2 }
+ end
+ assert_equal(@cls[a: 10, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10], h)
+ end
+
def test_merge
h1 = @cls[1=>2, 3=>4]
h2 = {1=>3, 5=>7}
@@ -1168,6 +1394,48 @@ class TestHash < Test::Unit::TestCase
assert_equal({1=>8, 2=>4, 3=>4, 5=>7}, h1.merge(h2, h3) {|k, v1, v2| k + v1 + v2 })
end
+ def test_merge_on_identhash
+ h = @cls[1=>2,3=>4,5=>6]
+ h.compare_by_identity
+ str1 = +'str'
+ str2 = +'str'
+ h[str1] = 1
+ h[str2] = 2
+ expected = h.dup
+ expected[7] = 8
+ h2 = h.merge(7=>8)
+ assert_equal(expected, h2)
+ assert_equal(true, h2.compare_by_identity?)
+ h2 = h.merge({})
+ assert_equal(h, h2)
+ assert_equal(true, h2.compare_by_identity?)
+
+ h = @cls[]
+ h.compare_by_identity
+ h1 = @cls[7=>8]
+ h1.compare_by_identity
+ h2 = h.merge(7=>8)
+ assert_equal(h1, h2)
+ assert_equal(true, h2.compare_by_identity?)
+ h2 = h.merge({})
+ assert_equal(h, h2)
+ assert_equal(true, h2.compare_by_identity?)
+ end
+
+ def test_merge!
+ h = @cls[a: 1, b: 2, c: 3]
+ assert_raise(FrozenError) do
+ h.merge!({a: 10, b: 20}){ |key, v1, v2| key == :b && h.freeze; v2 }
+ end
+ assert_equal(@cls[a: 10, b: 2, c: 3], h)
+
+ h = @cls[a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10]
+ assert_raise(FrozenError) do
+ h.merge!({a: 10, b: 20}){ |key, v1, v2| key == :b && h.freeze; v2 }
+ end
+ assert_equal(@cls[a: 10, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10], h)
+ end
+
def test_assoc
assert_equal([3,4], @cls[1=>2, 3=>4, 5=>6].assoc(3))
assert_nil(@cls[1=>2, 3=>4, 5=>6].assoc(4))
@@ -1465,6 +1733,15 @@ class TestHash < Test::Unit::TestCase
assert_no_memory_leak([], prepare, code, bug9187)
end
+ def test_memory_size_after_delete
+ require 'objspace'
+ h = {}
+ 1000.times {|i| h[i] = true}
+ big = ObjectSpace.memsize_of(h)
+ 1000.times {|i| h.delete(i)}
+ assert_operator ObjectSpace.memsize_of(h), :<, big/10
+ end
+
def test_wrapper
bug9381 = '[ruby-core:59638] [Bug #9381]'
@@ -1624,6 +1901,8 @@ class TestHash < Test::Unit::TestCase
}
assert_equal([10, 20, 30], [1, 2, 3].map(&h))
+
+ assert_equal(true, h.to_proc.lambda?)
end
def test_transform_keys
@@ -1638,6 +1917,27 @@ class TestHash < Test::Unit::TestCase
y = x.transform_keys.with_index {|k, i| "#{k}.#{i}" }
assert_equal(%w(a.0 b.1 c.2), y.keys)
+
+ assert_equal({A: 1, B: 2, c: 3}, x.transform_keys({a: :A, b: :B, d: :D}))
+ assert_equal({A: 1, B: 2, "c" => 3}, x.transform_keys({a: :A, b: :B, d: :D}, &:to_s))
+ end
+
+ def test_transform_keys_on_identhash
+ h = @cls[1=>2,3=>4,5=>6]
+ h.compare_by_identity
+ str1 = +'str'
+ str2 = +'str'
+ h[str1] = 1
+ h[str2] = 2
+ h2 = h.transform_keys(&:itself)
+ assert_equal(Hash[h.to_a], h2)
+ assert_equal(false, h2.compare_by_identity?)
+
+ h = @cls[]
+ h.compare_by_identity
+ h2 = h.transform_keys(&:itself)
+ assert_equal({}, h2)
+ assert_equal(false, h2.compare_by_identity?)
end
def test_transform_keys_bang
@@ -1657,21 +1957,57 @@ class TestHash < Test::Unit::TestCase
x.transform_keys! {|k| -k }
assert_equal([-1, :a, 1, :b], x.flatten)
+ x = @cls[a: 1, b: 2, c: 3]
+ x.transform_keys! { |k| k == :b && break }
+ assert_equal({false => 1, b: 2, c: 3}, x)
+
x = @cls[true => :a, false => :b]
x.transform_keys! {|k| !k }
assert_equal([false, :a, true, :b], x.flatten)
+
+ x = @cls[a: 1, b: 2, c: 3]
+ x.transform_keys!({a: :A, b: :B, d: :D})
+ assert_equal({A: 1, B: 2, c: 3}, x)
+ x = @cls[a: 1, b: 2, c: 3]
+ x.transform_keys!({a: :A, b: :B, d: :D}, &:to_s)
+ assert_equal({A: 1, B: 2, "c" => 3}, x)
end
def test_transform_values
x = @cls[a: 1, b: 2, c: 3]
+ x.default = 42
y = x.transform_values {|v| v ** 2 }
assert_equal([1, 4, 9], y.values_at(:a, :b, :c))
assert_not_same(x, y)
+ assert_nil(y.default)
+
+ x.default_proc = proc {|h, k| k}
+ y = x.transform_values {|v| v ** 2 }
+ assert_nil(y.default_proc)
+ assert_nil(y.default)
y = x.transform_values.with_index {|v, i| "#{v}.#{i}" }
assert_equal(%w(1.0 2.1 3.2), y.values_at(:a, :b, :c))
end
+ def test_transform_values_on_identhash
+ h = @cls[1=>2,3=>4,5=>6]
+ h.compare_by_identity
+ str1 = +'str'
+ str2 = +'str'
+ h[str1] = 1
+ h[str2] = 2
+ h2 = h.transform_values(&:itself)
+ assert_equal(h, h2)
+ assert_equal(true, h2.compare_by_identity?)
+
+ h = @cls[]
+ h.compare_by_identity
+ h2 = h.transform_values(&:itself)
+ assert_equal({}.compare_by_identity, h2)
+ assert_equal(true, h2.compare_by_identity?)
+ end
+
def test_transform_values_bang
x = @cls[a: 1, b: 2, c: 3]
y = x.transform_values! {|v| v ** 2 }
@@ -1679,8 +2015,21 @@ class TestHash < Test::Unit::TestCase
assert_same(x, y)
x = @cls[a: 1, b: 2, c: 3]
+ x.transform_values! { |v| v == 2 && break }
+ assert_equal({a: false, b: 2, c: 3}, x)
+
+ x = @cls[a: 1, b: 2, c: 3]
y = x.transform_values!.with_index {|v, i| "#{v}.#{i}" }
assert_equal(%w(1.0 2.1 3.2), y.values_at(:a, :b, :c))
+
+ x = @cls[a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10]
+ assert_raise(FrozenError) do
+ x.transform_values!() do |v|
+ x.freeze if v == 2
+ v.succ
+ end
+ end
+ assert_equal(@cls[a: 2, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10], x)
end
def test_broken_hash_value
@@ -1730,9 +2079,6 @@ class TestHash < Test::Unit::TestCase
class TestSubHash < TestHash
class SubHash < Hash
- def reject(*)
- super
- end
end
def setup
@@ -1740,4 +2086,118 @@ class TestHash < Test::Unit::TestCase
super
end
end
+
+ ruby2_keywords def get_flagged_hash(*args)
+ args.last
+ end
+
+ def check_flagged_hash(k: :NG)
+ k
+ end
+
+ def test_ruby2_keywords_hash?
+ flagged_hash = get_flagged_hash(k: 1)
+ assert_equal(true, Hash.ruby2_keywords_hash?(flagged_hash))
+ assert_equal(false, Hash.ruby2_keywords_hash?({}))
+ assert_raise(TypeError) { Hash.ruby2_keywords_hash?(1) }
+ end
+
+ def test_ruby2_keywords_hash
+ hash = {k: 1}
+ assert_equal(false, Hash.ruby2_keywords_hash?(hash))
+ hash = Hash.ruby2_keywords_hash(hash)
+ assert_equal(true, Hash.ruby2_keywords_hash?(hash))
+ assert_equal(1, check_flagged_hash(*[hash]))
+ assert_raise(TypeError) { Hash.ruby2_keywords_hash(1) }
+ end
+
+ def test_ar2st
+ # insert
+ obj = Object.new
+ obj.instance_variable_set(:@h, h = {})
+ def obj.hash
+ 10.times{|i| @h[i] = i}
+ 0
+ end
+ def obj.inspect
+ 'test'
+ end
+ h[obj] = true
+ assert_equal '{0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9, test=>true}', h.inspect
+
+ # delete
+ obj = Object.new
+ obj.instance_variable_set(:@h, h = {})
+ def obj.hash
+ 10.times{|i| @h[i] = i}
+ 0
+ end
+ def obj.inspect
+ 'test'
+ end
+ def obj.eql? other
+ other.class == Object
+ end
+ obj2 = Object.new
+ def obj2.hash
+ 0
+ end
+
+ h[obj2] = true
+ h.delete obj
+ assert_equal '{0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9}', h.inspect
+
+ # lookup
+ obj = Object.new
+ obj.instance_variable_set(:@h, h = {})
+ def obj.hash
+ 10.times{|i| @h[i] = i}
+ 0
+ end
+ def obj.inspect
+ 'test'
+ end
+ def obj.eql? other
+ other.class == Object
+ end
+ obj2 = Object.new
+ def obj2.hash
+ 0
+ end
+
+ h[obj2] = true
+ assert_equal true, h[obj]
+ end
+
+ def test_bug_12706
+ assert_raise(ArgumentError) do
+ {a: 1}.each(&->(k, v) {})
+ end
+ end
+
+ def test_any_hash_fixable
+ 20.times do
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ require "delegate"
+ typename = DelegateClass(String)
+
+ hash = {
+ "Int" => true,
+ "Float" => true,
+ "String" => true,
+ "Boolean" => true,
+ "WidgetFilter" => true,
+ "WidgetAggregation" => true,
+ "WidgetEdge" => true,
+ "WidgetSortOrder" => true,
+ "WidgetGrouping" => true,
+ }
+
+ hash.each_key do |key|
+ assert_send([hash, :key?, typename.new(key)])
+ end
+ end;
+ end
+ end
end
diff --git a/test/ruby/test_inlinecache.rb b/test/ruby/test_inlinecache.rb
new file mode 100644
index 0000000000..d48d95d74e
--- /dev/null
+++ b/test/ruby/test_inlinecache.rb
@@ -0,0 +1,110 @@
+# -*- coding: us-ascii -*-
+# frozen_string_literal: true
+
+require 'test/unit'
+
+class TestMethodInlineCache < Test::Unit::TestCase
+ def test_alias
+ m0 = Module.new do
+ def foo; :M0 end
+ end
+ m1 = Module.new do
+ include m0
+ end
+ c = Class.new do
+ include m1
+ alias bar foo
+ end
+ d = Class.new(c) do
+ end
+
+ test = -> do
+ d.new.bar
+ end
+
+ assert_equal :M0, test[]
+
+ c.class_eval do
+ def bar
+ :C
+ end
+ end
+
+ assert_equal :C, test[]
+ end
+
+ def test_zsuper
+ assert_separately [], <<-EOS
+ class C
+ private def foo
+ :C
+ end
+ end
+
+ class D < C
+ public :foo
+ end
+
+ class E < D; end
+ class F < E; end
+
+ test = -> do
+ F.new().foo
+ end
+
+ assert_equal :C, test[]
+
+ class E
+ def foo; :E; end
+ end
+
+ assert_equal :E, test[]
+ EOS
+ end
+
+ def test_module_methods_redefiniton
+ m0 = Module.new do
+ def foo
+ super
+ end
+ end
+
+ c1 = Class.new do
+ def foo
+ :C1
+ end
+ end
+
+ c2 = Class.new do
+ def foo
+ :C2
+ end
+ end
+
+ d1 = Class.new(c1) do
+ include m0
+ end
+
+ d2 = Class.new(c2) do
+ include m0
+ end
+
+ assert_equal :C1, d1.new.foo
+
+ m = Module.new do
+ def foo
+ super
+ end
+ end
+
+ d1.class_eval do
+ include m
+ end
+
+ d2.class_eval do
+ include m
+ end
+
+ assert_equal :C2, d2.new.foo
+ end
+end
diff --git a/test/ruby/test_insns_leaf.rb b/test/ruby/test_insns_leaf.rb
new file mode 100644
index 0000000000..9c9a4324cb
--- /dev/null
+++ b/test/ruby/test_insns_leaf.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: false
+require 'test/unit'
+
+class TestInsnsLeaf < Test::Unit::TestCase
+ require "set"
+
+ class Id
+ attr_reader :db_id
+ def initialize(db_id)
+ @db_id = db_id
+ end
+
+ def ==(other)
+ other.class == self.class && other.db_id == db_id
+ end
+ alias_method :eql?, :==
+
+ def hash
+ 10
+ end
+
+ def <=>(other)
+ db_id <=> other.db_id if other.is_a?(self.class)
+ end
+ end
+
+ class Namespace
+ IDS = Set[
+ Id.new(1).freeze,
+ Id.new(2).freeze,
+ Id.new(3).freeze,
+ Id.new(4).freeze,
+ ].freeze
+
+ class << self
+ def test?(id)
+ IDS.include?(id)
+ end
+ end
+ end
+
+ def test_insns_leaf
+ assert Namespace.test?(Id.new(1)), "IDS should include 1"
+ assert !Namespace.test?(Id.new(5)), "IDS should not include 5"
+ end
+end
diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb
index a111698771..2f3f00ecda 100644
--- a/test/ruby/test_integer.rb
+++ b/test/ruby/test_integer.rb
@@ -2,16 +2,9 @@
require 'test/unit'
class TestInteger < Test::Unit::TestCase
- BDSIZE = 0x4000000000000000.coerce(0)[0].size
- def self.bdsize(x)
- ((x + 1) / 8 + BDSIZE) / BDSIZE * BDSIZE
- end
- def bdsize(x)
- self.class.bdsize(x)
- end
-
FIXNUM_MIN = RbConfig::LIMITS['FIXNUM_MIN']
FIXNUM_MAX = RbConfig::LIMITS['FIXNUM_MAX']
+ LONG_MAX = RbConfig::LIMITS['LONG_MAX']
def test_aref
@@ -96,11 +89,16 @@ class TestInteger < Test::Unit::TestCase
assert_equal(0, 1 << -0x40000001)
assert_equal(0, 1 << -0x80000000)
assert_equal(0, 1 << -0x80000001)
- # assert_equal(bdsize(0x80000000), (1 << 0x80000000).size)
+
+ char_bit = RbConfig::LIMITS["UCHAR_MAX"].bit_length
+ size_max = RbConfig::LIMITS["SIZE_MAX"]
+ size_bit_max = size_max * char_bit
+ assert_raise_with_message(RangeError, /shift width/) {
+ 1 << size_bit_max
+ }
end
def test_rshift
- # assert_equal(bdsize(0x40000001), (1 >> -0x40000001).size)
assert_predicate((1 >> 0x80000000), :zero?)
assert_predicate((1 >> 0xffffffff), :zero?)
assert_predicate((1 >> 0x100000000), :zero?)
@@ -260,6 +258,7 @@ class TestInteger < Test::Unit::TestCase
assert_equal("a", "a".ord.chr)
assert_raise(RangeError) { (-1).chr }
assert_raise(RangeError) { 0x100.chr }
+ assert_raise_with_message(RangeError, "3000000000 out of char range") { 3_000_000_000.chr }
end
def test_upto
@@ -298,6 +297,31 @@ class TestInteger < Test::Unit::TestCase
end
end
+ def test_times_bignum_redefine_plus_lt
+ assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ called = false
+ Integer.class_eval do
+ alias old_plus +
+ undef +
+ define_method(:+){|x| called = true; 1}
+ alias old_lt <
+ undef <
+ define_method(:<){|x| called = true}
+ end
+ big = 2**65
+ big.times{break 0}
+ Integer.class_eval do
+ undef +
+ alias + old_plus
+ undef <
+ alias < old_lt
+ end
+ bug18377 = "[ruby-core:106361]"
+ assert_equal(false, called, bug18377)
+ end;
+ end
+
def assert_int_equal(expected, result, mesg = nil)
assert_kind_of(Integer, result, mesg)
assert_equal(expected, result, mesg)
@@ -575,6 +599,8 @@ class TestInteger < Test::Unit::TestCase
assert_equal([0, 9, 8, 7, 6, 5, 4, 3, 2, 1], 1234567890.digits)
assert_equal([90, 78, 56, 34, 12], 1234567890.digits(100))
assert_equal([10, 5, 6, 8, 0, 10, 8, 6, 1], 1234567890.digits(13))
+ assert_equal((2 ** 1024).to_s(7).chars.map(&:to_i).reverse, (2 ** 1024).digits(7))
+ assert_equal([0] * 100 + [1], (2 ** (128 * 100)).digits(2 ** 128))
end
def test_digits_for_negative_numbers
@@ -659,4 +685,21 @@ class TestInteger < Test::Unit::TestCase
def o.fdiv(x); 1; end
assert_equal(1.0, 1.fdiv(o))
end
+
+ def test_try_convert
+ assert_equal(1, Integer.try_convert(1))
+ assert_equal(1, Integer.try_convert(1.0))
+ assert_nil Integer.try_convert("1")
+ o = Object.new
+ assert_nil Integer.try_convert(o)
+ def o.to_i; 1; end
+ assert_nil Integer.try_convert(o)
+ o = Object.new
+ def o.to_int; 1; end
+ assert_equal(1, Integer.try_convert(o))
+
+ o = Object.new
+ def o.to_int; Object.new; end
+ assert_raise_with_message(TypeError, /can't convert Object to Integer/) {Integer.try_convert(o)}
+ end
end
diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb
index f3b08154c8..4f54052d3b 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -394,6 +394,24 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_each_byte_closed
+ pipe(proc do |w|
+ w << "abc def"
+ w.close
+ end, proc do |r|
+ assert_raise(IOError) do
+ r.each_byte {|byte| r.close if byte == 32 }
+ end
+ end)
+ make_tempfile {|t|
+ File.open(t, 'rt') {|f|
+ assert_raise(IOError) do
+ f.each_byte {|c| f.close if c == 10}
+ end
+ }
+ }
+ end
+
def test_each_codepoint
make_tempfile {|t|
bug2959 = '[ruby-core:28650]'
@@ -405,16 +423,21 @@ class TestIO < Test::Unit::TestCase
}
end
- def test_codepoints
+ def test_each_codepoint_closed
+ pipe(proc do |w|
+ w.print("abc def")
+ w.close
+ end, proc do |r|
+ assert_raise(IOError) do
+ r.each_codepoint {|c| r.close if c == 32}
+ end
+ end)
make_tempfile {|t|
- bug2959 = '[ruby-core:28650]'
- a = ""
File.open(t, 'rt') {|f|
- assert_warn(/deprecated/) {
- f.codepoints {|c| a << c}
- }
+ assert_raise(IOError) do
+ f.each_codepoint {|c| f.close if c == 10}
+ end
}
- assert_equal("foo\nbar\nbaz\n", a, bug2959)
}
end
@@ -453,6 +476,18 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_copy_stream_append_to_nonempty
+ with_srccontent("foobar") {|src, content|
+ preface = 'preface'
+ File.write('dst', preface)
+ File.open('dst', 'ab') do |dst|
+ ret = IO.copy_stream(src, dst)
+ assert_equal(content.bytesize, ret)
+ assert_equal(preface + content, File.read("dst"))
+ end
+ }
+ end
+
def test_copy_stream_smaller
with_srccontent {|src, content|
@@ -620,7 +655,7 @@ class TestIO < Test::Unit::TestCase
if have_nonblock?
def test_copy_stream_no_busy_wait
- skip "MJIT has busy wait on GC. This sometimes fails with --jit." if RubyVM::MJIT.enabled?
+ skip "MJIT has busy wait on GC. This sometimes fails with --jit." if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
skip "multiple threads already active" if Thread.list.size > 1
msg = 'r58534 [ruby-core:80969] [Backport #13533]'
@@ -1459,6 +1494,13 @@ class TestIO < Test::Unit::TestCase
end)
end
+ def test_readpartial_zero_size
+ File.open(IO::NULL) do |r|
+ assert_empty(r.readpartial(0, s = "01234567"))
+ assert_empty(s)
+ end
+ end
+
def test_readpartial_buffer_error
with_pipe do |r, w|
s = ""
@@ -1504,6 +1546,13 @@ class TestIO < Test::Unit::TestCase
end)
end
+ def test_read_zero_size
+ File.open(IO::NULL) do |r|
+ assert_empty(r.read(0, s = "01234567"))
+ assert_empty(s)
+ end
+ end
+
def test_read_buffer_error
with_pipe do |r, w|
s = ""
@@ -1541,6 +1590,13 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_read_nonblock_zero_size
+ File.open(IO::NULL) do |r|
+ assert_empty(r.read_nonblock(0, s = "01234567"))
+ assert_empty(s)
+ end
+ end
+
def test_write_nonblock_simple_no_exceptions
pipe(proc do |w|
w.write_nonblock('1', exception: false)
@@ -1575,7 +1631,7 @@ class TestIO < Test::Unit::TestCase
end if have_nonblock?
def test_read_nonblock_no_exceptions
- skip '[ruby-core:90895] MJIT worker may leave fd open in a forked child' if RubyVM::MJIT.enabled? # TODO: consider acquiring GVL from MJIT worker.
+ skip '[ruby-core:90895] MJIT worker may leave fd open in a forked child' if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # TODO: consider acquiring GVL from MJIT worker.
with_pipe {|r, w|
assert_equal :wait_readable, r.read_nonblock(4096, exception: false)
w.puts "HI!"
@@ -1822,8 +1878,7 @@ class TestIO < Test::Unit::TestCase
end)
end
- def test_lines
- verbose, $VERBOSE = $VERBOSE, nil
+ def test_each_line
pipe(proc do |w|
w.puts "foo"
w.puts "bar"
@@ -1831,20 +1886,17 @@ class TestIO < Test::Unit::TestCase
w.close
end, proc do |r|
e = nil
- assert_warn(/deprecated/) {
- e = r.lines
+ assert_warn('') {
+ e = r.each_line
}
assert_equal("foo\n", e.next)
assert_equal("bar\n", e.next)
assert_equal("baz\n", e.next)
assert_raise(StopIteration) { e.next }
end)
- ensure
- $VERBOSE = verbose
end
- def test_bytes
- verbose, $VERBOSE = $VERBOSE, nil
+ def test_each_byte2
pipe(proc do |w|
w.binmode
w.puts "foo"
@@ -1853,20 +1905,17 @@ class TestIO < Test::Unit::TestCase
w.close
end, proc do |r|
e = nil
- assert_warn(/deprecated/) {
- e = r.bytes
+ assert_warn('') {
+ e = r.each_byte
}
(%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c|
assert_equal(c.ord, e.next)
end
assert_raise(StopIteration) { e.next }
end)
- ensure
- $VERBOSE = verbose
end
- def test_chars
- verbose, $VERBOSE = $VERBOSE, nil
+ def test_each_char2
pipe(proc do |w|
w.puts "foo"
w.puts "bar"
@@ -1874,16 +1923,14 @@ class TestIO < Test::Unit::TestCase
w.close
end, proc do |r|
e = nil
- assert_warn(/deprecated/) {
- e = r.chars
+ assert_warn('') {
+ e = r.each_char
}
(%w(f o o) + ["\n"] + %w(b a r) + ["\n"] + %w(b a z) + ["\n"]).each do |c|
assert_equal(c, e.next)
end
assert_raise(StopIteration) { e.next }
end)
- ensure
- $VERBOSE = verbose
end
def test_readbyte
@@ -2232,7 +2279,7 @@ class TestIO < Test::Unit::TestCase
def test_autoclose_true_closed_by_finalizer
# http://ci.rvm.jp/results/trunk-mjit@silicon-docker/1465760
# http://ci.rvm.jp/results/trunk-mjit@silicon-docker/1469765
- skip 'this randomly fails with MJIT' if RubyVM::MJIT.enabled?
+ skip 'this randomly fails with MJIT' if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
feature2250 = '[ruby-core:26222]'
pre = 'ft2250'
@@ -2284,26 +2331,14 @@ class TestIO < Test::Unit::TestCase
def o.to_open(**kw); kw; end
assert_equal({:a=>1}, open(o, a: 1))
- w = /The last argument is used as the keyword parameter.*for `(to_)?open'/m
- redefined = nil
- w.singleton_class.define_method(:===) do |s|
- match = super(s)
- redefined = !$1
- match
- end
-
- assert_warn(w) do
- assert_equal({:a=>1}, open(o, {a: 1}))
- end
+ assert_raise(ArgumentError) { open(o, {a: 1}) }
class << o
remove_method(:to_open)
end
def o.to_open(kw); kw; end
assert_equal({:a=>1}, open(o, a: 1))
- unless redefined
- assert_equal({:a=>1}, open(o, {a: 1}))
- end
+ assert_equal({:a=>1}, open(o, {a: 1}))
end
def test_open_pipe
@@ -2501,6 +2536,17 @@ class TestIO < Test::Unit::TestCase
end
end
+ def test_reopen_ivar
+ assert_ruby_status([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ f = File.open(IO::NULL)
+ f.instance_variable_set(:@foo, 42)
+ f.reopen(STDIN)
+ f.instance_variable_defined?(:@foo)
+ f.instance_variable_get(:@foo)
+ end;
+ end
+
def test_foreach
a = []
IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x }
@@ -2580,11 +2626,13 @@ class TestIO < Test::Unit::TestCase
end
def test_print_separators
- EnvUtil.suppress_warning {$, = ':'}
- $\ = "\n"
+ EnvUtil.suppress_warning {
+ $, = ':'
+ $\ = "\n"
+ }
pipe(proc do |w|
w.print('a')
- w.print('a','b','c')
+ EnvUtil.suppress_warning {w.print('a','b','c')}
w.close
end, proc do |r|
assert_equal("a\n", r.gets)
@@ -2642,7 +2690,7 @@ class TestIO < Test::Unit::TestCase
end
capture.clear
- assert_warning(/[.#]write is outdated/) do
+ assert_deprecated_warning(/[.#]write is outdated/) do
stdout, $stdout = $stdout, capture
puts "hey"
ensure
@@ -2825,7 +2873,7 @@ __END__
def test_flush_in_finalizer2
bug3910 = '[ruby-dev:42341]'
- Tempfile.open("bug3910") {|t|
+ Tempfile.create("bug3910") {|t|
path = t.path
t.close
begin
@@ -2844,7 +2892,6 @@ __END__
end
}
end
- t.close!
}
end
@@ -3138,11 +3185,8 @@ __END__
assert_equal("\00f", File.read(path))
assert_equal(1, File.write(path, "f", 0, encoding: "UTF-8"))
assert_equal("ff", File.read(path))
- assert_raise(TypeError) {
- assert_warn(/The last argument is split into positional and keyword parameters/) do
- File.write(path, "foo", Object.new => Object.new)
- end
- }
+ File.write(path, "foo", Object.new => Object.new)
+ assert_equal("foo", File.read(path))
end
end
@@ -3341,11 +3385,17 @@ __END__
data = "a" * 100
with_pipe do |r,w|
th = Thread.new {r.sysread(100, buf)}
+
Thread.pass until th.stop?
- buf.replace("")
- assert_empty(buf, bug6099)
+
+ assert_equal 100, buf.bytesize
+
+ msg = /can't modify string; temporarily locked/
+ assert_raise_with_message(RuntimeError, msg) do
+ buf.replace("")
+ end
+ assert_predicate(th, :alive?)
w.write(data)
- Thread.pass while th.alive?
th.join
end
assert_equal(data, buf, bug6099)
@@ -3466,10 +3516,17 @@ __END__
tempfiles = []
(0..fd_setsize+1).map {|i|
- tempfiles << Tempfile.open("test_io_select_with_many_files")
+ tempfiles << Tempfile.create("test_io_select_with_many_files")
}
- IO.select(tempfiles)
+ begin
+ IO.select(tempfiles)
+ ensure
+ tempfiles.each { |t|
+ t.close
+ File.unlink(t.path)
+ }
+ end
}, bug8080, timeout: 100
end if defined?(Process::RLIMIT_NOFILE)
@@ -3643,15 +3700,27 @@ __END__
end
def test_open_flag_binary
+ binary_enc = Encoding.find("BINARY")
make_tempfile do |t|
open(t.path, File::RDONLY, flags: File::BINARY) do |f|
assert_equal true, f.binmode?
+ assert_equal binary_enc, f.external_encoding
end
open(t.path, 'r', flags: File::BINARY) do |f|
assert_equal true, f.binmode?
+ assert_equal binary_enc, f.external_encoding
end
open(t.path, mode: 'r', flags: File::BINARY) do |f|
assert_equal true, f.binmode?
+ assert_equal binary_enc, f.external_encoding
+ end
+ open(t.path, File::RDONLY|File::BINARY) do |f|
+ assert_equal true, f.binmode?
+ assert_equal binary_enc, f.external_encoding
+ end
+ open(t.path, File::RDONLY|File::BINARY, autoclose: true) do |f|
+ assert_equal true, f.binmode?
+ assert_equal binary_enc, f.external_encoding
end
end
end if File::BINARY != 0
@@ -3700,7 +3769,7 @@ __END__
begin;
bug13158 = '[ruby-core:79262] [Bug #13158]'
closed = nil
- q = Queue.new
+ q = Thread::Queue.new
IO.pipe do |r, w|
thread = Thread.new do
begin
@@ -3811,30 +3880,6 @@ __END__
end
end;
end
-
- def test_write_no_garbage
- skip "multiple threads already active" if Thread.list.size > 1
- res = {}
- ObjectSpace.count_objects(res) # creates strings on first call
- [ 'foo'.b, '*' * 24 ].each do |buf|
- with_pipe do |r, w|
- GC.disable
- begin
- before = ObjectSpace.count_objects(res)[:T_STRING]
- n = w.write(buf)
- s = w.syswrite(buf)
- after = ObjectSpace.count_objects(res)[:T_STRING]
- ensure
- GC.enable
- end
- assert_equal before, after,
- "no strings left over after write [ruby-core:78898] [Bug #13085]: #{ before } strings before write -> #{ after } strings after write"
- assert_not_predicate buf, :frozen?, 'no inadvertent freeze'
- assert_equal buf.bytesize, n, 'IO#write wrote expected size'
- assert_equal s, n, 'IO#syswrite wrote expected size'
- end
- end
- end
end
def test_pread
@@ -3914,21 +3959,22 @@ __END__
end
end
- def test_select_leak
+ def test_select_memory_leak
# avoid malloc arena explosion from glibc and jemalloc:
env = {
'MALLOC_ARENA_MAX' => '1',
'MALLOC_ARENA_TEST' => '1',
'MALLOC_CONF' => 'narenas:1',
}
- assert_no_memory_leak([env], <<-"end;", <<-"end;", rss: true, timeout: 60)
+ assert_no_memory_leak([env], "#{<<~"begin;"}\n#{<<~'else;'}", "#{<<~'end;'}", rss: true, timeout: 60)
+ begin;
r, w = IO.pipe
rset = [r]
wset = [w]
exc = StandardError.new(-"select used to leak on exception")
exc.set_backtrace([])
Thread.new { IO.select(rset, wset, nil, 0) }.join
- end;
+ else;
th = Thread.new do
Thread.handle_interrupt(StandardError => :on_blocking) do
begin
@@ -3953,4 +3999,34 @@ __END__
assert_raise(TypeError) {Marshal.dump(w)}
}
end
+
+ def test_marshal_closed_io
+ bug18077 = '[ruby-core:104927] [Bug #18077]'
+ r, w = IO.pipe
+ r.close; w.close
+ assert_raise(TypeError, bug18077) {Marshal.dump(r)}
+
+ class << r
+ undef_method :closed?
+ end
+ assert_raise(TypeError, bug18077) {Marshal.dump(r)}
+ end
+
+ def test_stdout_to_closed_pipe
+ EnvUtil.invoke_ruby(["-e", "loop {puts :ok}"], "", true, true) do
+ |in_p, out_p, err_p, pid|
+ out = out_p.gets
+ out_p.close
+ err = err_p.read
+ ensure
+ status = Process.wait2(pid)[1]
+ assert_equal("ok\n", out)
+ assert_empty(err)
+ assert_not_predicate(status, :success?)
+ if Signal.list["PIPE"]
+ assert_predicate(status, :signaled?)
+ assert_equal("PIPE", Signal.signame(status.termsig) || status.termsig)
+ end
+ end
+ end
end
diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb
new file mode 100644
index 0000000000..c1034efe34
--- /dev/null
+++ b/test/ruby/test_io_buffer.rb
@@ -0,0 +1,360 @@
+# frozen_string_literal: false
+
+require 'tempfile'
+
+class TestIOBuffer < Test::Unit::TestCase
+ experimental = Warning[:experimental]
+ begin
+ Warning[:experimental] = false
+ IO::Buffer.new(0)
+ ensure
+ Warning[:experimental] = experimental
+ end
+
+ def assert_negative(value)
+ assert(value < 0, "Expected #{value} to be negative!")
+ end
+
+ def assert_positive(value)
+ assert(value > 0, "Expected #{value} to be positive!")
+ end
+
+ def test_flags
+ assert_equal 1, IO::Buffer::EXTERNAL
+ assert_equal 2, IO::Buffer::INTERNAL
+ assert_equal 4, IO::Buffer::MAPPED
+
+ assert_equal 32, IO::Buffer::LOCKED
+ assert_equal 64, IO::Buffer::PRIVATE
+
+ assert_equal 128, IO::Buffer::READONLY
+ end
+
+ def test_endian
+ assert_equal 4, IO::Buffer::LITTLE_ENDIAN
+ assert_equal 8, IO::Buffer::BIG_ENDIAN
+ assert_equal 8, IO::Buffer::NETWORK_ENDIAN
+
+ assert_include [IO::Buffer::LITTLE_ENDIAN, IO::Buffer::BIG_ENDIAN], IO::Buffer::HOST_ENDIAN
+ end
+
+ def test_default_size
+ assert_equal IO::Buffer::DEFAULT_SIZE, IO::Buffer.new.size
+ end
+
+ def test_new_internal
+ buffer = IO::Buffer.new(1024, IO::Buffer::INTERNAL)
+ assert_equal 1024, buffer.size
+ refute buffer.external?
+ assert buffer.internal?
+ refute buffer.mapped?
+ end
+
+ def test_new_mapped
+ buffer = IO::Buffer.new(1024, IO::Buffer::MAPPED)
+ assert_equal 1024, buffer.size
+ refute buffer.external?
+ refute buffer.internal?
+ assert buffer.mapped?
+ end
+
+ def test_new_readonly
+ buffer = IO::Buffer.new(128, IO::Buffer::INTERNAL|IO::Buffer::READONLY)
+ assert buffer.readonly?
+
+ assert_raise IO::Buffer::AccessError do
+ buffer.set_string("")
+ end
+
+ assert_raise IO::Buffer::AccessError do
+ buffer.set_string("!", 1)
+ end
+ end
+
+ def test_file_mapped
+ buffer = File.open(__FILE__) {|file| IO::Buffer.map(file, nil, 0, IO::Buffer::READONLY)}
+ contents = buffer.get_string
+
+ assert_include contents, "Hello World"
+ assert_equal Encoding::BINARY, contents.encoding
+ end
+
+ def test_file_mapped_invalid
+ assert_raise NoMethodError do
+ IO::Buffer.map("foobar")
+ end
+ end
+
+ def test_string_mapped
+ string = "Hello World"
+ buffer = IO::Buffer.for(string)
+ assert buffer.readonly?
+ end
+
+ def test_string_mapped_frozen
+ string = "Hello World".freeze
+ buffer = IO::Buffer.for(string)
+ assert buffer.readonly?
+ end
+
+ def test_string_mapped_mutable
+ string = "Hello World"
+ IO::Buffer.for(string) do |buffer|
+ refute buffer.readonly?
+
+ # Cannot modify string as it's locked by the buffer:
+ assert_raise RuntimeError do
+ string[0] = "h"
+ end
+
+ buffer.set_value(:U8, 0, "h".ord)
+
+ # Buffer releases it's ownership of the string:
+ buffer.free
+
+ assert_equal "hello World", string
+ end
+ end
+
+ def test_non_string
+ not_string = Object.new
+
+ assert_raise TypeError do
+ IO::Buffer.for(not_string)
+ end
+ end
+
+ def test_resize_mapped
+ buffer = IO::Buffer.new
+
+ buffer.resize(2048)
+ assert_equal 2048, buffer.size
+
+ buffer.resize(4096)
+ assert_equal 4096, buffer.size
+ end
+
+ def test_resize_preserve
+ message = "Hello World"
+ buffer = IO::Buffer.new(1024)
+ buffer.set_string(message)
+ buffer.resize(2048)
+ assert_equal message, buffer.get_string(0, message.bytesize)
+ end
+
+ def test_resize_zero_internal
+ buffer = IO::Buffer.new(1)
+
+ buffer.resize(0)
+ assert_equal 0, buffer.size
+
+ buffer.resize(1)
+ assert_equal 1, buffer.size
+ end
+
+ def test_resize_zero_external
+ buffer = IO::Buffer.for('1')
+
+ assert_raise IO::Buffer::AccessError do
+ buffer.resize(0)
+ end
+ end
+
+ def test_compare_same_size
+ buffer1 = IO::Buffer.new(1)
+ assert_equal buffer1, buffer1
+
+ buffer2 = IO::Buffer.new(1)
+ buffer1.set_value(:U8, 0, 0x10)
+ buffer2.set_value(:U8, 0, 0x20)
+
+ assert_negative buffer1 <=> buffer2
+ assert_positive buffer2 <=> buffer1
+ end
+
+ def test_compare_different_size
+ buffer1 = IO::Buffer.new(3)
+ buffer2 = IO::Buffer.new(5)
+
+ assert_negative buffer1 <=> buffer2
+ assert_positive buffer2 <=> buffer1
+ end
+
+ def test_slice
+ buffer = IO::Buffer.new(128)
+ slice = buffer.slice(8, 32)
+ slice.set_string("Hello World")
+ assert_equal("Hello World", buffer.get_string(8, 11))
+ end
+
+ def test_slice_bounds
+ buffer = IO::Buffer.new(128)
+
+ assert_raise ArgumentError do
+ buffer.slice(128, 10)
+ end
+
+ # assert_raise RuntimeError do
+ # pp buffer.slice(-10, 10)
+ # end
+ end
+
+ def test_locked
+ buffer = IO::Buffer.new(128, IO::Buffer::INTERNAL|IO::Buffer::LOCKED)
+
+ assert_raise IO::Buffer::LockedError do
+ buffer.resize(256)
+ end
+
+ assert_equal 128, buffer.size
+
+ assert_raise IO::Buffer::LockedError do
+ buffer.free
+ end
+
+ assert_equal 128, buffer.size
+ end
+
+ def test_get_string
+ message = "Hello World 🤓"
+
+ buffer = IO::Buffer.new(128)
+ buffer.set_string(message)
+
+ chunk = buffer.get_string(0, message.bytesize, Encoding::UTF_8)
+ assert_equal message, chunk
+ assert_equal Encoding::UTF_8, chunk.encoding
+
+ chunk = buffer.get_string(0, message.bytesize, Encoding::BINARY)
+ assert_equal Encoding::BINARY, chunk.encoding
+
+ assert_raise_with_message(ArgumentError, /exceeds buffer size/) do
+ buffer.get_string(0, 129)
+ end
+
+ assert_raise_with_message(ArgumentError, /exceeds buffer size/) do
+ buffer.get_string(129)
+ end
+ end
+
+ # We check that values are correctly round tripped.
+ RANGES = {
+ :U8 => [0, 2**8-1],
+ :S8 => [-2**7, 0, 2**7-1],
+
+ :U16 => [0, 2**16-1],
+ :S16 => [-2**15, 0, 2**15-1],
+ :u16 => [0, 2**16-1],
+ :s16 => [-2**15, 0, 2**15-1],
+
+ :U32 => [0, 2**32-1],
+ :S32 => [-2**31, 0, 2**31-1],
+ :u32 => [0, 2**32-1],
+ :s32 => [-2**31, 0, 2**31-1],
+
+ :U64 => [0, 2**64-1],
+ :S64 => [-2**63, 0, 2**63-1],
+ :u64 => [0, 2**64-1],
+ :s64 => [-2**63, 0, 2**63-1],
+
+ :F32 => [-1.0, 0.0, 0.5, 1.0, 128.0],
+ :F64 => [-1.0, 0.0, 0.5, 1.0, 128.0],
+ }
+
+ def test_get_set_primitives
+ buffer = IO::Buffer.new(128)
+
+ RANGES.each do |type, values|
+ values.each do |value|
+ buffer.set_value(type, 0, value)
+ assert_equal value, buffer.get_value(type, 0), "Converting #{value} as #{type}."
+ end
+ end
+ end
+
+ def test_clear
+ buffer = IO::Buffer.new(16)
+ buffer.set_string("Hello World!")
+ end
+
+ def test_invalidation
+ input, output = IO.pipe
+
+ # (1) rb_write_internal creates IO::Buffer object,
+ buffer = IO::Buffer.new(128)
+
+ # (2) it is passed to (malicious) scheduler
+ # (3) scheduler starts a thread which call system call with the buffer object
+ thread = Thread.new{buffer.locked{input.read}}
+
+ Thread.pass until thread.stop?
+
+ # (4) scheduler returns
+ # (5) rb_write_internal invalidate the buffer object
+ assert_raise IO::Buffer::LockedError do
+ buffer.free
+ end
+
+ # (6) the system call access the memory area after invalidation
+ output.write("Hello World")
+ output.close
+ thread.join
+
+ input.close
+ end
+
+ def test_read
+ io = Tempfile.new
+ io.write("Hello World")
+ io.seek(0)
+
+ buffer = IO::Buffer.new(128)
+ buffer.read(io, 5)
+
+ assert_equal "Hello", buffer.get_string(0, 5)
+ ensure
+ io.close!
+ end
+
+ def test_write
+ io = Tempfile.new
+
+ buffer = IO::Buffer.new(128)
+ buffer.set_string("Hello")
+ buffer.write(io, 5)
+
+ io.seek(0)
+ assert_equal "Hello", io.read(5)
+ ensure
+ io.close!
+ end
+
+ def test_pread
+ io = Tempfile.new
+ io.write("Hello World")
+ io.seek(0)
+
+ buffer = IO::Buffer.new(128)
+ buffer.pread(io, 5, 6)
+
+ assert_equal "World", buffer.get_string(0, 5)
+ assert_equal 0, io.tell
+ ensure
+ io.close!
+ end
+
+ def test_pwrite
+ io = Tempfile.new
+
+ buffer = IO::Buffer.new(128)
+ buffer.set_string("World")
+ buffer.pwrite(io, 5, 6)
+
+ assert_equal 0, io.tell
+
+ io.seek(6)
+ assert_equal "World", io.read(5)
+ ensure
+ io.close!
+ end
+end
diff --git a/test/ruby/test_io_m17n.rb b/test/ruby/test_io_m17n.rb
index e5b0ef0585..27b16a2a36 100644
--- a/test/ruby/test_io_m17n.rb
+++ b/test/ruby/test_io_m17n.rb
@@ -776,10 +776,10 @@ EOT
assert_equal(eucjp, r.read)
end)
- assert_raise_with_message(ArgumentError, /invalid name encoding/) do
+ assert_raise_with_message(ArgumentError, /invalid encoding name/) do
with_pipe("UTF-8", "UTF-8".encode("UTF-32BE")) {}
end
- assert_raise_with_message(ArgumentError, /invalid name encoding/) do
+ assert_raise_with_message(ArgumentError, /invalid encoding name/) do
with_pipe("UTF-8".encode("UTF-32BE")) {}
end
@@ -2047,19 +2047,19 @@ EOT
with_tmpdir {
open("raw.txt", "wb", xml: :attr) {|f| f.print '&<>"\''; f.puts "\u4E02\u3042" }
content = File.read("raw.txt", :mode=>"rb:ascii-8bit")
- assert_equal("\"&amp;&lt;&gt;&quot;'\u4E02\u3042\n\"".force_encoding("ascii-8bit"), content)
+ assert_equal("\"&amp;&lt;&gt;&quot;&apos;\u4E02\u3042\n\"".force_encoding("ascii-8bit"), content)
open("ascii.txt", "wb:us-ascii", xml: :attr) {|f| f.print '&<>"\''; f.puts "\u4E02\u3042" }
content = File.read("ascii.txt", :mode=>"rb:ascii-8bit")
- assert_equal("\"&amp;&lt;&gt;&quot;'&#x4E02;&#x3042;\n\"".force_encoding("ascii-8bit"), content)
+ assert_equal("\"&amp;&lt;&gt;&quot;&apos;&#x4E02;&#x3042;\n\"".force_encoding("ascii-8bit"), content)
open("iso-2022-jp.txt", "wb:iso-2022-jp", xml: :attr) {|f| f.print '&<>"\''; f.puts "\u4E02\u3042" }
content = File.read("iso-2022-jp.txt", :mode=>"rb:ascii-8bit")
- assert_equal("\"&amp;&lt;&gt;&quot;'&#x4E02;\e$B$\"\e(B\n\"".force_encoding("ascii-8bit"), content)
+ assert_equal("\"&amp;&lt;&gt;&quot;&apos;&#x4E02;\e$B$\"\e(B\n\"".force_encoding("ascii-8bit"), content)
open("utf-16be.txt", "wb:utf-16be", xml: :attr) {|f| f.print '&<>"\''; f.puts "\u4E02\u3042" }
content = File.read("utf-16be.txt", :mode=>"rb:ascii-8bit")
- assert_equal("\0\"\0&\0a\0m\0p\0;\0&\0l\0t\0;\0&\0g\0t\0;\0&\0q\0u\0o\0t\0;\0'\x4E\x02\x30\x42\0\n\0\"".force_encoding("ascii-8bit"), content)
+ assert_equal("\0\"\0&\0a\0m\0p\0;\0&\0l\0t\0;\0&\0g\0t\0;\0&\0q\0u\0o\0t\0;\0&\0a\0p\0o\0s\0;\x4E\x02\x30\x42\0\n\0\"".force_encoding("ascii-8bit"), content)
open("eucjp.txt", "w:euc-jp:utf-8", xml: :attr) {|f|
f.print "\u4E02" # U+4E02 is 0x3021 in JIS X 0212
diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb
index 7c384c19bd..f01d36cc5a 100644
--- a/test/ruby/test_iseq.rb
+++ b/test/ruby/test_iseq.rb
@@ -10,13 +10,16 @@ class TestISeq < Test::Unit::TestCase
end
def compile(src, line = nil, opt = nil)
+ unless line
+ line = caller_locations(1).first.lineno
+ end
EnvUtil.suppress_warning do
ISeq.new(src, __FILE__, __FILE__, line, opt)
end
end
- def lines src
- body = compile(src).to_a[13]
+ def lines src, lines = nil
+ body = compile(src, lines).to_a[13]
body.find_all{|e| e.kind_of? Integer}
end
@@ -25,24 +28,22 @@ class TestISeq < Test::Unit::TestCase
end
def test_to_a_lines
- src = <<-EOS
+ assert_equal [__LINE__+1, __LINE__+2, __LINE__+4], lines(<<-EOS, __LINE__+1)
p __LINE__ # 1
p __LINE__ # 2
# 3
p __LINE__ # 4
EOS
- assert_equal [1, 2, 4], lines(src)
- src = <<-EOS
+ assert_equal [__LINE__+2, __LINE__+4], lines(<<-EOS, __LINE__+1)
# 1
p __LINE__ # 2
# 3
p __LINE__ # 4
# 5
EOS
- assert_equal [2, 4], lines(src)
- src = <<-EOS
+ assert_equal [__LINE__+3, __LINE__+4, __LINE__+7, __LINE__+9], lines(<<~EOS, __LINE__+1)
1 # should be optimized out
2 # should be optimized out
p __LINE__ # 3
@@ -53,10 +54,9 @@ class TestISeq < Test::Unit::TestCase
8 # should be optimized out
9
EOS
- assert_equal [3, 4, 7, 9], lines(src)
end
- def test_unsupport_type
+ def test_unsupported_type
ary = compile("p").to_a
ary[9] = :foobar
assert_raise_with_message(TypeError, /:foobar/) {ISeq.load(ary)}
@@ -82,6 +82,91 @@ class TestISeq < Test::Unit::TestCase
end;
end if defined?(RubyVM::InstructionSequence.load)
+ def test_cdhash_after_roundtrip
+ # CDHASH was not built properly when loading from binary and
+ # was causing opt_case_dispatch to clobber its stack canary
+ # for its "leaf" instruction attribute.
+ iseq = compile(<<~EOF, __LINE__+1)
+ case Class.new(String).new("foo")
+ when "foo"
+ 42
+ end
+ EOF
+ assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
+ end
+
+ def test_super_with_block
+ iseq = compile(<<~EOF, __LINE__+1)
+ def (Object.new).touch(*) # :nodoc:
+ foo { super }
+ end
+ 42
+ EOF
+ assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
+ end
+
+ def test_super_with_block_hash_0
+ iseq = compile(<<~EOF, __LINE__+1)
+ # [Bug #18250] `req` specifically cause `Assertion failed: (key != 0), function hash_table_raw_insert`
+ def (Object.new).touch(req, *)
+ foo { super }
+ end
+ 42
+ EOF
+ assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
+ end
+
+ def test_super_with_block_and_kwrest
+ iseq = compile(<<~EOF, __LINE__+1)
+ def (Object.new).touch(**) # :nodoc:
+ foo { super }
+ end
+ 42
+ EOF
+ assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
+ end
+
+ def test_lambda_with_ractor_roundtrip
+ iseq = compile(<<~EOF, __LINE__+1)
+ x = 42
+ y = nil.instance_eval{ lambda { x } }
+ Ractor.make_shareable(y)
+ y.call
+ EOF
+ assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
+ end
+
+ def test_super_with_anonymous_block
+ iseq = compile(<<~EOF, __LINE__+1)
+ def (Object.new).touch(&) # :nodoc:
+ foo { super }
+ end
+ 42
+ EOF
+ assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
+ end
+
+ def test_ractor_unshareable_outer_variable
+ name = "\u{2603 26a1}"
+ y = nil.instance_eval do
+ eval("proc {#{name} = nil; proc {|x| #{name} = x}}").call
+ end
+ assert_raise_with_message(ArgumentError, /\(#{name}\)/) do
+ Ractor.make_shareable(y)
+ end
+ y = nil.instance_eval do
+ eval("proc {#{name} = []; proc {|x| #{name}}}").call
+ end
+ assert_raise_with_message(Ractor::IsolationError, /`#{name}'/) do
+ Ractor.make_shareable(y)
+ end
+ obj = Object.new
+ def obj.foo(*) nil.instance_eval{ ->{super} } end
+ assert_raise_with_message(Ractor::IsolationError, /hidden variable/) do
+ Ractor.make_shareable(obj.foo)
+ end
+ end
+
def test_disasm_encoding
src = "\u{3042} = 1; \u{3042}; \u{3043}"
asm = compile(src).disasm
@@ -98,6 +183,22 @@ class TestISeq < Test::Unit::TestCase
assert_include(RubyVM::InstructionSequence.of(obj.method(name)).disasm, name)
end
+ def test_compile_file_encoding
+ Tempfile.create(%w"test_iseq .rb") do |f|
+ f.puts "{ '\u00de' => 'Th', '\u00df' => 'ss', '\u00e0' => 'a' }"
+ f.close
+
+ EnvUtil.with_default_external(Encoding::US_ASCII) do
+ assert_warn('') {
+ load f.path
+ }
+ assert_nothing_raised(SyntaxError) {
+ RubyVM::InstructionSequence.compile_file(f.path)
+ }
+ end
+ end
+ end
+
LINE_BEFORE_METHOD = __LINE__
def method_test_line_trace
@@ -108,16 +209,16 @@ class TestISeq < Test::Unit::TestCase
end
def test_line_trace
- iseq = compile \
- %q{ a = 1
+ iseq = compile(<<~EOF, __LINE__+1)
+ a = 1
b = 2
c = 3
# d = 4
e = 5
# f = 6
g = 7
+ EOF
- }
assert_equal([1, 2, 3, 5, 7], iseq.line_trace_all)
iseq.line_trace_specify(1, true) # line 2
iseq.line_trace_specify(3, true) # line 5
@@ -187,8 +288,8 @@ class TestISeq < Test::Unit::TestCase
s1, s2, s3, s4 = compile(code, line, {frozen_string_literal: true}).eval
assert_predicate(s1, :frozen?)
assert_predicate(s2, :frozen?)
- assert_predicate(s3, :frozen?)
- assert_predicate(s4, :frozen?)
+ assert_not_predicate(s3, :frozen?)
+ assert_not_predicate(s4, :frozen?)
end
# Safe call chain is not optimized when Coverage is running.
@@ -289,6 +390,22 @@ class TestISeq < Test::Unit::TestCase
end
end
+ def anon_star(*); end
+
+ def test_anon_param_in_disasm
+ iseq = RubyVM::InstructionSequence.of(method(:anon_star))
+ param_names = iseq.to_a[iseq.to_a.index(:method) + 1]
+ assert_equal [2], param_names
+ end
+
+ def anon_block(&); end
+
+ def test_anon_block_param_in_disasm
+ iseq = RubyVM::InstructionSequence.of(method(:anon_block))
+ param_names = iseq.to_a[iseq.to_a.index(:method) + 1]
+ assert_equal [:&], param_names
+ end
+
def strip_lineno(source)
source.gsub(/^.*?: /, "")
end
@@ -425,6 +542,11 @@ class TestISeq < Test::Unit::TestCase
a1 = iseq.to_a
a2 = iseq2.to_a
assert_equal(a1, a2, message(mesg) {diff iseq.disassemble, iseq2.disassemble})
+ if iseq2.script_lines
+ assert_kind_of(Array, iseq2.script_lines)
+ else
+ assert_nil(iseq2.script_lines)
+ end
iseq2
end
@@ -465,6 +587,11 @@ class TestISeq < Test::Unit::TestCase
attr_reader :i
end
end;
+
+ # cleanup
+ ::Object.class_eval do
+ remove_const :P
+ end
end
def collect_from_binary_tracepoint_lines(tracepoint_type, filename)
@@ -561,9 +688,58 @@ class TestISeq < Test::Unit::TestCase
end
def test_iseq_builtin_to_a
- insns = RubyVM::InstructionSequence.of([].method(:pack)).to_a.last
- invokebuiltin = insns.find { |insn| insn.is_a?(Array) && insn[0] == :opt_invokebuiltin_delegate_leave }
+ invokebuiltin = eval(EnvUtil.invoke_ruby(['-e', <<~EOS], '', true).first)
+ insns = RubyVM::InstructionSequence.of([].method(:pack)).to_a.last
+ p insns.find { |insn| insn.is_a?(Array) && insn[0] == :opt_invokebuiltin_delegate_leave }
+ EOS
assert_not_nil(invokebuiltin)
assert_equal([:func_ptr, :argc, :index, :name], invokebuiltin[1].keys)
end
+
+ def test_iseq_builtin_load
+ Tempfile.create(["builtin", ".iseq"]) do |f|
+ f.binmode
+ f.write(RubyVM::InstructionSequence.of(1.method(:abs)).to_binary)
+ f.close
+ assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ bin = File.binread(ARGV[0])
+ assert_raise(ArgumentError) do
+ RubyVM::InstructionSequence.load_from_binary(bin)
+ end
+ end;
+ end
+ end
+
+ def test_iseq_option_debug_level
+ assert_raise(TypeError) {ISeq.compile("", debug_level: "")}
+ assert_ruby_status([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ RubyVM::InstructionSequence.compile("", debug_level: 5)
+ end;
+ end
+
+ def test_mandatory_only
+ assert_separately [], <<~RUBY
+ at0 = Time.at(0)
+ assert_equal at0, Time.public_send(:at, 0, 0)
+ RUBY
+ end
+
+ def test_mandatory_only_redef
+ assert_separately ['-W0'], <<~RUBY
+ r = Ractor.new{
+ Float(10)
+ module Kernel
+ undef Float
+ def Float(n)
+ :new
+ end
+ end
+ GC.start
+ Float(30)
+ }
+ assert_equal :new, r.take
+ RUBY
+ end
end
diff --git a/test/ruby/test_iterator.rb b/test/ruby/test_iterator.rb
index 54c095338f..820d5591c1 100644
--- a/test/ruby/test_iterator.rb
+++ b/test/ruby/test_iterator.rb
@@ -339,8 +339,7 @@ class TestIterator < Test::Unit::TestCase
marity_test(:marity_test)
marity_test(:p)
- lambda(&method(:assert)).call(true)
- lambda(&get_block{|a,n| assert(a,n)}).call(true, "marity")
+ get_block{|a,n| assert(a,n)}.call(true, "marity")
end
def foo
diff --git a/test/ruby/test_jit.rb b/test/ruby/test_jit.rb
index a8ce18b48c..07ac76210d 100644
--- a/test/ruby/test_jit.rb
+++ b/test/ruby/test_jit.rb
@@ -10,14 +10,19 @@ class TestJIT < Test::Unit::TestCase
IGNORABLE_PATTERNS = [
/\AJIT recompile: .+\n\z/,
/\AJIT inline: .+\n\z/,
+ /\AJIT cancel: .+\n\z/,
/\ASuccessful MJIT finish\n\z/,
]
+ MAX_CACHE_PATTERNS = [
+ /\AJIT compaction \([^)]+\): .+\n\z/,
+ /\AToo many JIT code, but skipped unloading units for JIT compaction\n\z/,
+ /\ANo units can be unloaded -- .+\n\z/,
+ ]
# trace_* insns are not compiled for now...
TEST_PENDING_INSNS = RubyVM::INSTRUCTION_NAMES.select { |n| n.start_with?('trace_') }.map(&:to_sym) + [
# not supported yet
:defineclass,
- :opt_call_c_function,
# to be tested
:invokebuiltin,
@@ -34,22 +39,36 @@ class TestJIT < Test::Unit::TestCase
@untested_insns ||= (RubyVM::INSTRUCTION_NAMES.map(&:to_sym) - TEST_PENDING_INSNS)
end
- def setup
- unless JITSupport.supported?
- skip 'JIT seems not supported on this platform'
+ def self.setup
+ return if defined?(@setup_hooked)
+ @setup_hooked = true
+
+ # ci.rvm.jp caches its build environment. Clean up temporary files left by SEGV.
+ if ENV['RUBY_DEBUG']&.include?('ci')
+ Dir.glob("#{ENV.fetch('TMPDIR', '/tmp')}/_ruby_mjit_p*u*.*").each do |file|
+ puts "test/ruby/test_jit.rb: removing #{file}"
+ File.unlink(file)
+ end
end
# ruby -w -Itest/lib test/ruby/test_jit.rb
- if $VERBOSE && !defined?(@@at_exit_hooked)
+ if $VERBOSE
+ pid = $$
at_exit do
- unless TestJIT.untested_insns.empty?
+ if pid == $$ && !TestJIT.untested_insns.empty?
warn "you may want to add tests for following insns, when you have a chance: #{TestJIT.untested_insns.join(' ')}"
end
end
- @@at_exit_hooked = true
end
end
+ def setup
+ unless JITSupport.supported?
+ skip 'JIT seems not supported on this platform'
+ end
+ self.class.setup
+ end
+
def test_compile_insn_nop
assert_compile_once('nil rescue true', result_inspect: 'nil', insns: %i[nop])
end
@@ -224,16 +243,8 @@ class TestJIT < Test::Unit::TestCase
end;
end
- def test_compile_insn_putstring_concatstrings_tostring
- assert_compile_once('"a#{}b" + "c"', result_inspect: '"abc"', insns: %i[putstring concatstrings tostring])
- end
-
- def test_compile_insn_freezestring
- assert_eval_with_jit("#{<<~"begin;"}\n#{<<~'end;'}", stdout: 'true', success_count: 1, insns: %i[freezestring])
- begin;
- # frozen_string_literal: true
- print proc { "#{true}".frozen? }.call
- end;
+ def test_compile_insn_putstring_concatstrings_objtostring
+ assert_compile_once('"a#{}b" + "c"', result_inspect: '"abc"', insns: %i[putstring concatstrings objtostring])
end
def test_compile_insn_toregexp
@@ -309,10 +320,6 @@ class TestJIT < Test::Unit::TestCase
assert_compile_once('{}["true"] = true', result_inspect: 'true', insns: %i[swap topn])
end
- def test_compile_insn_reverse
- assert_compile_once('q, (w, e), r = 1, [2, 3], 4; [q, w, e, r]', result_inspect: '[1, 2, 3, 4]', insns: %i[reverse])
- end
-
def test_compile_insn_reput
skip "write test"
end
@@ -353,7 +360,7 @@ class TestJIT < Test::Unit::TestCase
end
def test_compile_insn_send
- assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '1', success_count: 2, insns: %i[send])
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '1', success_count: 3, insns: %i[send])
begin;
print proc { yield_self { 1 } }.call
end;
@@ -475,8 +482,8 @@ class TestJIT < Test::Unit::TestCase
end;
end
- def test_compile_insn_checktype
- assert_compile_once("#{<<~"begin;"}\n#{<<~'end;'}", result_inspect: '"42"', insns: %i[checktype])
+ def test_compile_insn_objtostring
+ assert_compile_once("#{<<~"begin;"}\n#{<<~'end;'}", result_inspect: '"42"', insns: %i[objtostring])
begin;
a = '2'
"4#{a}"
@@ -492,7 +499,7 @@ class TestJIT < Test::Unit::TestCase
end
def test_compile_insn_checkmatch_opt_case_dispatch
- assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '"world"', insns: %i[checkmatch opt_case_dispatch])
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '"world"', insns: %i[opt_case_dispatch])
begin;
case 'hello'
when 'hello'
@@ -593,16 +600,26 @@ class TestJIT < Test::Unit::TestCase
assert_compile_once("'true' =~ /true/", result_inspect: '0', insns: %i[opt_regexpmatch2])
end
- def test_compile_insn_opt_call_c_function
- skip "support this in opt_call_c_function (low priority)"
- end
-
def test_compile_insn_opt_invokebuiltin_delegate_leave
- insns = collect_insns(RubyVM::InstructionSequence.of("\x00".method(:unpack)).to_a)
+ iseq = eval(EnvUtil.invoke_ruby(['-e', <<~'EOS'], '', true).first)
+ p RubyVM::InstructionSequence.of("\x00".method(:unpack)).to_a
+ EOS
+ insns = collect_insns(iseq)
mark_tested_insn(:opt_invokebuiltin_delegate_leave, used_insns: insns)
assert_eval_with_jit('print "\x00".unpack("c")', stdout: '[0]', success_count: 1)
end
+ def test_compile_insn_checkmatch
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '"world"', insns: %i[checkmatch])
+ begin;
+ ary = %w(hello good-bye)
+ case 'hello'
+ when *ary
+ 'world'
+ end
+ end;
+ end
+
def test_jit_output
out, err = eval_with_jit('5.times { puts "MJIT" }', verbose: 1, min_calls: 5)
assert_equal("MJIT\n" * 5, out)
@@ -610,6 +627,46 @@ class TestJIT < Test::Unit::TestCase
assert_match(/^Successful MJIT finish$/, err)
end
+ def test_nothing_to_unload_with_jit_wait
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'hello', success_count: 11, max_cache: 10, ignorable_patterns: MAX_CACHE_PATTERNS)
+ begin;
+ def a1() a2() end
+ def a2() a3() end
+ def a3() a4() end
+ def a4() a5() end
+ def a5() a6() end
+ def a6() a7() end
+ def a7() a8() end
+ def a8() a9() end
+ def a9() a10() end
+ def a10() a11() end
+ def a11() print('hello') end
+ a1
+ end;
+ end
+
+ def test_unload_units_on_fiber
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'hello', success_count: 12, max_cache: 10, ignorable_patterns: MAX_CACHE_PATTERNS)
+ begin;
+ def a1() a2(false); a2(true) end
+ def a2(a) a3(a) end
+ def a3(a) a4(a) end
+ def a4(a) a5(a) end
+ def a5(a) a6(a) end
+ def a6(a) a7(a) end
+ def a7(a) a8(a) end
+ def a8(a) a9(a) end
+ def a9(a) a10(a) end
+ def a10(a)
+ if a
+ Fiber.new { a11 }.resume
+ end
+ end
+ def a11() print('hello') end
+ a1
+ end;
+ end
+
def test_unload_units_and_compaction
Dir.mktmpdir("jit_test_unload_units_") do |dir|
# MIN_CACHE_SIZE is 10
@@ -636,30 +693,40 @@ class TestJIT < Test::Unit::TestCase
debug_info = %Q[stdout:\n"""\n#{out}\n"""\n\nstderr:\n"""\n#{err}"""\n]
assert_equal('012345678910', out, debug_info)
compactions, errs = err.lines.partition do |l|
- l.match?(/\AJIT compaction \(\d+\.\dms\): Compacted \d+ methods ->/)
+ l.match?(/\AJIT compaction \(\d+\.\dms\): Compacted \d+ methods /)
end
10.times do |i|
assert_match(/\A#{JIT_SUCCESS_PREFIX}: mjit#{i}@\(eval\):/, errs[i], debug_info)
end
- assert_equal("Too many JIT code -- 1 units unloaded\n", errs[10], debug_info)
- assert_match(/\A#{JIT_SUCCESS_PREFIX}: mjit10@\(eval\):/, errs[11], debug_info)
+ assert_equal("No units can be unloaded -- incremented max-cache-size to 11 for --jit-wait\n", errs[10], debug_info)
+ assert_match(/\A#{JIT_SUCCESS_PREFIX}: mjit10@\(eval\):/, errs[11], debug_info)
# On --jit-wait, when the number of JIT-ed code reaches --jit-max-cache,
# it should trigger compaction.
unless RUBY_PLATFORM.match?(/mswin|mingw/) # compaction is not supported on Windows yet
- assert_equal(3, compactions.size, debug_info)
+ assert_equal(1, compactions.size, debug_info)
end
if RUBY_PLATFORM.match?(/mswin/)
# "Permission Denied" error is preventing to remove so file on AppVeyor/RubyCI.
skip 'Removing so file is randomly failing on AppVeyor/RubyCI mswin due to Permission Denied.'
else
- # verify .o files are deleted on unload_units
+ # verify .c files are deleted on unload_units
assert_send([Dir, :empty?, dir], debug_info)
end
end
end
+ def test_newarraykwsplat_on_stack
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "[nil, [{:type=>:development}]]\n", success_count: 1, insns: %i[newarraykwsplat])
+ begin;
+ def arr
+ [nil, [:type => :development]]
+ end
+ p arr
+ end;
+ end
+
def test_local_stack_on_exception
assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '3', success_count: 2)
begin;
@@ -719,6 +786,57 @@ class TestJIT < Test::Unit::TestCase
end;
end
+ def test_inlined_builtin_methods
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '', success_count: 1, min_calls: 2)
+ begin;
+ def test
+ float = 0.0
+ float.abs
+ float.-@
+ float.zero?
+ end
+ test
+ test
+ end;
+ end
+
+ def test_inlined_c_method
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aaa", success_count: 2, recompile_count: 1, min_calls: 2)
+ begin;
+ def test(obj, recursive: nil)
+ if recursive
+ test(recursive)
+ end
+ obj.to_s
+ end
+
+ print(test('a')) # set #to_s cc to String#to_s (expecting C method)
+ print(test('a')) # JIT with #to_s cc: String#to_s
+ # update #to_s cd->cc to Symbol#to_s, then go through the Symbol#to_s cd->cc
+ # after checking receiver class using inlined #to_s cc with String#to_s.
+ print(test('a', recursive: :foo))
+ end;
+ end
+
+ def test_inlined_exivar
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aaa", success_count: 3, recompile_count: 1, min_calls: 2)
+ begin;
+ class Foo < Hash
+ def initialize
+ @a = :a
+ end
+
+ def bar
+ @a
+ end
+ end
+
+ print(Foo.new.bar)
+ print(Foo.new.bar) # compile #initialize, #bar -> recompile #bar
+ print(Foo.new.bar) # compile #bar with exivar
+ end;
+ end
+
def test_inlined_undefined_ivar
assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "bbb", success_count: 3, min_calls: 3)
begin;
@@ -734,11 +852,9 @@ class TestJIT < Test::Unit::TestCase
end
end
- verbose, $VERBOSE = $VERBOSE, false # suppress "instance variable @b not initialized"
print(Foo.new.bar)
print(Foo.new.bar)
print(Foo.new.bar)
- $VERBOSE = verbose
end;
end
@@ -764,6 +880,18 @@ class TestJIT < Test::Unit::TestCase
end;
end
+ def test_inlined_getconstant
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '11', success_count: 1, min_calls: 2)
+ begin;
+ FOO = 1
+ def const
+ FOO
+ end
+ print const
+ print const
+ end;
+ end
+
def test_attr_reader
assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "4nil\nnil\n6", success_count: 2, min_calls: 2)
begin;
@@ -828,6 +956,44 @@ class TestJIT < Test::Unit::TestCase
end;
end
+ def test_heap_promotion_of_ivar_in_the_middle_of_jit
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "true\ntrue\n", success_count: 2, min_calls: 2)
+ begin;
+ class A
+ def initialize
+ @iv0 = nil
+ @iv1 = []
+ @iv2 = nil
+ end
+
+ def test(add)
+ @iv0.nil?
+ @iv2.nil?
+ add_ivar if add
+ @iv1.empty?
+ end
+
+ def add_ivar
+ @iv3 = nil
+ end
+ end
+
+ a = A.new
+ p a.test(false)
+ p a.test(true)
+ end;
+ end
+
+ def test_jump_to_precompiled_branch
+ assert_eval_with_jit("#{<<~'begin;'}\n#{<<~'end;'}", stdout: ".0", success_count: 1, min_calls: 1)
+ begin;
+ def test(foo)
+ ".#{foo unless foo == 1}" if true
+ end
+ print test(0)
+ end;
+ end
+
def test_clean_so
if RUBY_PLATFORM.match?(/mswin/)
skip 'Removing so file is randomly failing on AppVeyor/RubyCI mswin due to Permission Denied.'
@@ -881,7 +1047,7 @@ class TestJIT < Test::Unit::TestCase
def test_frame_omitted_inlining
assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "true\ntrue\ntrue\n", success_count: 1, min_calls: 2)
begin;
- class Numeric
+ class Integer
remove_method :zero?
def zero?
self == 0
@@ -907,6 +1073,10 @@ class TestJIT < Test::Unit::TestCase
end;
end
+ def test_builtin_frame_omitted_inlining
+ assert_eval_with_jit('0.zero?; 0.zero?; 3.times { p 0.zero? }', stdout: "true\ntrue\ntrue\n", success_count: 1, min_calls: 2)
+ end
+
def test_program_counter_with_regexpmatch
assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aa", success_count: 1)
begin;
@@ -935,6 +1105,30 @@ class TestJIT < Test::Unit::TestCase
end;
end
+ def test_mjit_pause_wait
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '', success_count: 0, min_calls: 1)
+ begin;
+ RubyVM::MJIT.pause
+ proc {}.call
+ end;
+ end
+
+ def test_not_cancel_by_tracepoint_class
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", success_count: 1, min_calls: 2)
+ begin;
+ TracePoint.new(:class) {}.enable
+ 2.times {}
+ end;
+ end
+
+ def test_cancel_by_tracepoint
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", success_count: 0, min_calls: 2)
+ begin;
+ TracePoint.new(:line) {}.enable
+ 2.times {}
+ end;
+ end
+
def test_caller_locations_without_catch_table
out, _ = eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", min_calls: 1)
begin;
@@ -996,13 +1190,14 @@ class TestJIT < Test::Unit::TestCase
end
# Shorthand for normal test cases
- def assert_eval_with_jit(script, stdout: nil, success_count:, min_calls: 1, insns: [], uplevel: 1)
- out, err = eval_with_jit(script, verbose: 1, min_calls: min_calls)
- actual = err.scan(/^#{JIT_SUCCESS_PREFIX}:/).size
- # Add --jit-verbose=2 logs for cl.exe because compiler's error message is suppressed
- # for cl.exe with --jit-verbose=1. See `start_process` in mjit_worker.c.
- if RUBY_PLATFORM.match?(/mswin/) && success_count != actual
- out2, err2 = eval_with_jit(script, verbose: 2, min_calls: min_calls)
+ def assert_eval_with_jit(script, stdout: nil, success_count:, recompile_count: nil, min_calls: 1, max_cache: 1000, insns: [], uplevel: 1, ignorable_patterns: [])
+ out, err = eval_with_jit(script, verbose: 1, min_calls: min_calls, max_cache: max_cache)
+ success_actual = err.scan(/^#{JIT_SUCCESS_PREFIX}:/).size
+ recompile_actual = err.scan(/^#{JIT_RECOMPILE_PREFIX}:/).size
+ # Add --mjit-verbose=2 logs for cl.exe because compiler's error message is suppressed
+ # for cl.exe with --mjit-verbose=1. See `start_process` in mjit_worker.c.
+ if RUBY_PLATFORM.match?(/mswin/) && success_count != success_actual
+ out2, err2 = eval_with_jit(script, verbose: 2, min_calls: min_calls, max_cache: max_cache)
end
# Make sure that the script has insns expected to be tested
@@ -1011,18 +1206,24 @@ class TestJIT < Test::Unit::TestCase
mark_tested_insn(insn, used_insns: used_insns, uplevel: uplevel + 3)
end
+ suffix = "script:\n#{code_block(script)}\nstderr:\n#{code_block(err)}#{(
+ "\nstdout(verbose=2 retry):\n#{code_block(out2)}\nstderr(verbose=2 retry):\n#{code_block(err2)}" if out2 || err2
+ )}"
assert_equal(
- success_count, actual,
- "Expected #{success_count} times of JIT success, but succeeded #{actual} times.\n\n"\
- "script:\n#{code_block(script)}\nstderr:\n#{code_block(err)}#{(
- "\nstdout(verbose=2 retry):\n#{code_block(out2)}\nstderr(verbose=2 retry):\n#{code_block(err2)}" if out2 || err2
- )}",
+ success_count, success_actual,
+ "Expected #{success_count} times of JIT success, but succeeded #{success_actual} times.\n\n#{suffix}",
)
+ if recompile_count
+ assert_equal(
+ recompile_count, recompile_actual,
+ "Expected #{success_count} times of JIT recompile, but recompiled #{success_actual} times.\n\n#{suffix}",
+ )
+ end
if stdout
assert_equal(stdout, out, "Expected stdout #{out.inspect} to match #{stdout.inspect} with script:\n#{code_block(script)}")
end
err_lines = err.lines.reject! do |l|
- l.chomp.empty? || l.match?(/\A#{JIT_SUCCESS_PREFIX}/) || IGNORABLE_PATTERNS.any? { |pat| pat.match?(l) }
+ l.chomp.empty? || l.match?(/\A#{JIT_SUCCESS_PREFIX}/) || (IGNORABLE_PATTERNS + ignorable_patterns).any? { |pat| pat.match?(l) }
end
unless err_lines.empty?
warn err_lines.join(''), uplevel: uplevel
@@ -1030,7 +1231,9 @@ class TestJIT < Test::Unit::TestCase
end
def mark_tested_insn(insn, used_insns:, uplevel: 1)
- unless used_insns.include?(insn)
+ # Currently, this check emits a false-positive warning against opt_regexpmatch2,
+ # so the insn is excluded explicitly. See https://bugs.ruby-lang.org/issues/18269
+ if !used_insns.include?(insn) && insn != :opt_regexpmatch2
$stderr.puts
warn "'#{insn}' insn is not included in the script. Actual insns are: #{used_insns.join(' ')}\n", uplevel: uplevel
end
diff --git a/test/ruby/test_jit_debug.rb b/test/ruby/test_jit_debug.rb
new file mode 100644
index 0000000000..b8dc9416ef
--- /dev/null
+++ b/test/ruby/test_jit_debug.rb
@@ -0,0 +1,17 @@
+require_relative 'test_jit'
+
+return unless defined?(TestJIT)
+return if ENV.key?('APPVEYOR')
+return if ENV.key?('RUBYCI_NICKNAME')
+return if ENV['RUBY_DEBUG']&.include?('ci') # ci.rvm.jp
+return if /mswin/ =~ RUBY_PLATFORM
+
+class TestJITDebug < TestJIT
+ @@test_suites.delete TestJIT if self.respond_to? :on_parallel_worker?
+
+ def setup
+ super
+ # let `#eval_with_jit` use --mjit-debug
+ @mjit_debug = true
+ end
+end
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
index 874b09bcfa..9094259bc2 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -24,9 +24,7 @@ class TestKeywordArguments < Test::Unit::TestCase
def test_f2
assert_equal([:xyz, "foo", 424242], f2(:xyz))
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `f2'/m) do
- assert_equal([{"bar"=>42}, "foo", 424242], f2("bar"=>42))
- end
+ assert_raise(ArgumentError) { f2("bar"=>42) }
end
@@ -192,6 +190,218 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(["bar", 111111], f[str: "bar", num: 111111])
end
+ def test_keyword_splat_new
+ kw = {}
+ h = {a: 1}
+
+ def self.assert_equal_not_same(kw, res)
+ assert_instance_of(Hash, res)
+ assert_equal(kw, res)
+ assert_not_same(kw, res)
+ end
+
+ def self.y(**kw) kw end
+ m = method(:y)
+ assert_equal(false, y(**{}).frozen?)
+ assert_equal_not_same(kw, y(**kw))
+ assert_equal_not_same(h, y(**h))
+ assert_equal(false, send(:y, **{}).frozen?)
+ assert_equal_not_same(kw, send(:y, **kw))
+ assert_equal_not_same(h, send(:y, **h))
+ assert_equal(false, public_send(:y, **{}).frozen?)
+ assert_equal_not_same(kw, public_send(:y, **kw))
+ assert_equal_not_same(h, public_send(:y, **h))
+ assert_equal(false, m.(**{}).frozen?)
+ assert_equal_not_same(kw, m.(**kw))
+ assert_equal_not_same(h, m.(**h))
+ assert_equal(false, m.send(:call, **{}).frozen?)
+ assert_equal_not_same(kw, m.send(:call, **kw))
+ assert_equal_not_same(h, m.send(:call, **h))
+
+ m = method(:send)
+ assert_equal(false, m.(:y, **{}).frozen?)
+ assert_equal_not_same(kw, m.(:y, **kw))
+ assert_equal_not_same(h, m.(:y, **h))
+ assert_equal(false, m.send(:call, :y, **{}).frozen?)
+ assert_equal_not_same(kw, m.send(:call, :y, **kw))
+ assert_equal_not_same(h, m.send(:call, :y, **h))
+
+ singleton_class.send(:remove_method, :y)
+ define_singleton_method(:y) { |**kw| kw }
+ m = method(:y)
+ assert_equal(false, y(**{}).frozen?)
+ assert_equal_not_same(kw, y(**kw))
+ assert_equal_not_same(h, y(**h))
+ assert_equal(false, send(:y, **{}).frozen?)
+ assert_equal_not_same(kw, send(:y, **kw))
+ assert_equal_not_same(h, send(:y, **h))
+ assert_equal(false, public_send(:y, **{}).frozen?)
+ assert_equal_not_same(kw, public_send(:y, **kw))
+ assert_equal_not_same(h, public_send(:y, **h))
+ assert_equal(false, m.(**{}).frozen?)
+ assert_equal_not_same(kw, m.(**kw))
+ assert_equal_not_same(h, m.(**h))
+ assert_equal(false, m.send(:call, **{}).frozen?)
+ assert_equal_not_same(kw, m.send(:call, **kw))
+ assert_equal_not_same(h, m.send(:call, **h))
+
+ y = lambda { |**kw| kw }
+ m = y.method(:call)
+ assert_equal(false, y.(**{}).frozen?)
+ assert_equal_not_same(kw, y.(**kw))
+ assert_equal_not_same(h, y.(**h))
+ assert_equal(false, y.send(:call, **{}).frozen?)
+ assert_equal_not_same(kw, y.send(:call, **kw))
+ assert_equal_not_same(h, y.send(:call, **h))
+ assert_equal(false, y.public_send(:call, **{}).frozen?)
+ assert_equal_not_same(kw, y.public_send(:call, **kw))
+ assert_equal_not_same(h, y.public_send(:call, **h))
+ assert_equal(false, m.(**{}).frozen?)
+ assert_equal_not_same(kw, m.(**kw))
+ assert_equal_not_same(h, m.(**h))
+ assert_equal(false, m.send(:call, **{}).frozen?)
+ assert_equal_not_same(kw, m.send(:call, **kw))
+ assert_equal_not_same(h, m.send(:call, **h))
+
+ y = :y.to_proc
+ m = y.method(:call)
+ assert_equal(false, y.(self, **{}).frozen?)
+ assert_equal_not_same(kw, y.(self, **kw))
+ assert_equal_not_same(h, y.(self, **h))
+ assert_equal(false, y.send(:call, self, **{}).frozen?)
+ assert_equal_not_same(kw, y.send(:call, self, **kw))
+ assert_equal_not_same(h, y.send(:call, self, **h))
+ assert_equal(false, y.public_send(:call, self, **{}).frozen?)
+ assert_equal_not_same(kw, y.public_send(:call, self, **kw))
+ assert_equal_not_same(h, y.public_send(:call, self, **h))
+ assert_equal(false, m.(self, **{}).frozen?)
+ assert_equal_not_same(kw, m.(self, **kw))
+ assert_equal_not_same(h, m.(self, **h))
+ assert_equal(false, m.send(:call, self, **{}).frozen?)
+ assert_equal_not_same(kw, m.send(:call, self, **kw))
+ assert_equal_not_same(h, m.send(:call, self, **h))
+
+ c = Class.new do
+ def y(**kw) kw end
+ end
+ o = c.new
+ def o.y(**kw) super end
+ m = o.method(:y)
+ assert_equal(false, o.y(**{}).frozen?)
+ assert_equal_not_same(kw, o.y(**kw))
+ assert_equal_not_same(h, o.y(**h))
+ assert_equal(false, o.send(:y, **{}).frozen?)
+ assert_equal_not_same(kw, o.send(:y, **kw))
+ assert_equal_not_same(h, o.send(:y, **h))
+ assert_equal(false, o.public_send(:y, **{}).frozen?)
+ assert_equal_not_same(kw, o.public_send(:y, **kw))
+ assert_equal_not_same(h, o.public_send(:y, **h))
+ assert_equal(false, m.(**{}).frozen?)
+ assert_equal_not_same(kw, m.(**kw))
+ assert_equal_not_same(h, m.(**h))
+ assert_equal(false, m.send(:call, **{}).frozen?)
+ assert_equal_not_same(kw, m.send(:call, **kw))
+ assert_equal_not_same(h, m.send(:call, **h))
+
+ o.singleton_class.send(:remove_method, :y)
+ def o.y(**kw) super(**kw) end
+ assert_equal(false, o.y(**{}).frozen?)
+ assert_equal_not_same(kw, o.y(**kw))
+ assert_equal_not_same(h, o.y(**h))
+ assert_equal(false, o.send(:y, **{}).frozen?)
+ assert_equal_not_same(kw, o.send(:y, **kw))
+ assert_equal_not_same(h, o.send(:y, **h))
+ assert_equal(false, o.public_send(:y, **{}).frozen?)
+ assert_equal_not_same(kw, o.public_send(:y, **kw))
+ assert_equal_not_same(h, o.public_send(:y, **h))
+ assert_equal(false, m.(**{}).frozen?)
+ assert_equal_not_same(kw, m.(**kw))
+ assert_equal_not_same(h, m.(**h))
+ assert_equal(false, m.send(:call, **{}).frozen?)
+ assert_equal_not_same(kw, m.send(:call, **kw))
+ assert_equal_not_same(h, m.send(:call, **h))
+
+ c = Class.new do
+ def method_missing(_, **kw) kw end
+ end
+ o = c.new
+ def o.y(**kw) super end
+ m = o.method(:y)
+ assert_equal(false, o.y(**{}).frozen?)
+ assert_equal_not_same(kw, o.y(**kw))
+ assert_equal_not_same(h, o.y(**h))
+ assert_equal(false, o.send(:y, **{}).frozen?)
+ assert_equal_not_same(kw, o.send(:y, **kw))
+ assert_equal_not_same(h, o.send(:y, **h))
+ assert_equal(false, o.public_send(:y, **{}).frozen?)
+ assert_equal_not_same(kw, o.public_send(:y, **kw))
+ assert_equal_not_same(h, o.public_send(:y, **h))
+ assert_equal(false, m.(**{}).frozen?)
+ assert_equal_not_same(kw, m.(**kw))
+ assert_equal_not_same(h, m.(**h))
+ assert_equal(false, m.send(:call, **{}).frozen?)
+ assert_equal_not_same(kw, m.send(:call, **kw))
+ assert_equal_not_same(h, m.send(:call, **h))
+
+ o.singleton_class.send(:remove_method, :y)
+ def o.y(**kw) super(**kw) end
+ assert_equal(false, o.y(**{}).frozen?)
+ assert_equal_not_same(kw, o.y(**kw))
+ assert_equal_not_same(h, o.y(**h))
+ assert_equal(false, o.send(:y, **{}).frozen?)
+ assert_equal_not_same(kw, o.send(:y, **kw))
+ assert_equal_not_same(h, o.send(:y, **h))
+ assert_equal(false, o.public_send(:y, **{}).frozen?)
+ assert_equal_not_same(kw, o.public_send(:y, **kw))
+ assert_equal_not_same(h, o.public_send(:y, **h))
+ assert_equal(false, m.(**{}).frozen?)
+ assert_equal_not_same(kw, m.(**kw))
+ assert_equal_not_same(h, m.(**h))
+ assert_equal(false, m.send(:call, **{}).frozen?)
+ assert_equal_not_same(kw, m.send(:call, **kw))
+ assert_equal_not_same(h, m.send(:call, **h))
+
+ c = Class.new do
+ attr_reader :kw
+ def initialize(**kw) @kw = kw end
+ end
+ m = c.method(:new)
+ assert_equal(false, c.new(**{}).kw.frozen?)
+ assert_equal_not_same(kw, c.new(**kw).kw)
+ assert_equal_not_same(h, c.new(**h).kw)
+ assert_equal(false, c.send(:new, **{}).kw.frozen?)
+ assert_equal_not_same(kw, c.send(:new, **kw).kw)
+ assert_equal_not_same(h, c.send(:new, **h).kw)
+ assert_equal(false, c.public_send(:new, **{}).kw.frozen?)
+ assert_equal_not_same(kw, c.public_send(:new, **kw).kw)
+ assert_equal_not_same(h, c.public_send(:new, **h).kw)
+ assert_equal(false, m.(**{}).kw.frozen?)
+ assert_equal_not_same(kw, m.(**kw).kw)
+ assert_equal_not_same(h, m.(**h).kw)
+ assert_equal(false, m.send(:call, **{}).kw.frozen?)
+ assert_equal_not_same(kw, m.send(:call, **kw).kw)
+ assert_equal_not_same(h, m.send(:call, **h).kw)
+
+ singleton_class.send(:attr_writer, :y)
+ m = method(:y=)
+ assert_equal_not_same(h, send(:y=, **h))
+ assert_equal_not_same(h, public_send(:y=, **h))
+ assert_equal_not_same(h, m.(**h))
+ assert_equal_not_same(h, m.send(:call, **h))
+
+ singleton_class.send(:remove_method, :y)
+ def self.method_missing(_, **kw) kw end
+ assert_equal(false, y(**{}).frozen?)
+ assert_equal_not_same(kw, y(**kw))
+ assert_equal_not_same(h, y(**h))
+ assert_equal(false, send(:y, **{}).frozen?)
+ assert_equal_not_same(kw, send(:y, **kw))
+ assert_equal_not_same(h, send(:y, **h))
+ assert_equal(false, public_send(:y, **{}).frozen?)
+ assert_equal_not_same(kw, public_send(:y, **kw))
+ assert_equal_not_same(h, public_send(:y, **h))
+ end
+
def test_regular_kwsplat
kw = {}
h = {:a=>1}
@@ -224,12 +434,8 @@ class TestKeywordArguments < Test::Unit::TestCase
def c.m(args)
args
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, c.m(**{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, c.m(**kw))
- end
+ assert_raise(ArgumentError) { c.m(**{}) }
+ assert_raise(ArgumentError) { c.m(**kw) }
assert_equal(kw, c.m(kw, **kw))
assert_equal(h, c.m(**h))
assert_equal(h, c.m(a: 1))
@@ -248,39 +454,21 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.m(**h2))
assert_equal(h3, c.m(**h3))
assert_equal(h3, c.m(a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for `m'/m) do
- assert_equal(h, c.m(h))
- end
+ assert_raise(ArgumentError) { c.m(h) }
assert_raise(ArgumentError) { c.m(h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `m'/m) do
- assert_raise(ArgumentError) { c.m(h3) }
- end
+ assert_raise(ArgumentError) { c.m(h3) }
c.singleton_class.remove_method(:m)
def c.m(arg, **args)
[arg, args]
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- c.m(**{})
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- c.m(**kw)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], c.m(**h))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], c.m(a: 1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h2, kw], c.m(**h2))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], c.m(**h3))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], c.m(a: 1, **h2))
- end
+ assert_raise(ArgumentError) { c.m(**{}) }
+ assert_raise(ArgumentError) { c.m(**kw) }
+ assert_raise(ArgumentError) { c.m(**h) }
+ assert_raise(ArgumentError) { c.m(a: 1) }
+ assert_raise(ArgumentError) { c.m(**h2) }
+ assert_raise(ArgumentError) { c.m(**h3) }
+ assert_raise(ArgumentError) { c.m(a: 1, **h2) }
assert_equal([h, kw], c.m(h))
assert_equal([h2, kw], c.m(h2))
assert_equal([h3, kw], c.m(h3))
@@ -296,13 +484,9 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, h2], c.m(**h2))
assert_equal([1, h3], c.m(**h3))
assert_equal([1, h3], c.m(a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for `m'/m) do
- assert_equal([1, h], c.m(h))
- end
+ assert_equal([h, kw], c.m(h))
assert_equal([h2, kw], c.m(h2))
- assert_warn(/The last argument is split into positional and keyword parameters.*for `m'/m) do
- assert_equal([h2, h], c.m(h3))
- end
+ assert_equal([h3, kw], c.m(h3))
end
def test_implicit_super_kwsplat
@@ -313,19 +497,9 @@ class TestKeywordArguments < Test::Unit::TestCase
sc = Class.new
c = sc.new
- redef = -> do
- if defined?(c.m)
- class << c
- remove_method(:m)
- end
- end
- eval <<-END
- def c.m(*args, **kw)
- super(*args, **kw)
- end
- END
+ def c.m(*args, **kw)
+ super(*args, **kw)
end
- redef[]
sc.class_eval do
def m(*args)
args
@@ -357,13 +531,8 @@ class TestKeywordArguments < Test::Unit::TestCase
args
end
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, c.m(**{}))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, c.m(**kw))
- end
+ assert_raise(ArgumentError) { c.m(**{}) }
+ assert_raise(ArgumentError) { c.m(**kw) }
assert_equal(h, c.m(**h))
assert_equal(h, c.m(a: 1))
assert_equal(h2, c.m(**h2))
@@ -383,13 +552,9 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.m(**h2))
assert_equal(h3, c.m(**h3))
assert_equal(h3, c.m(a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for `m'/m) do
- assert_equal(h, c.m(h))
- end
+ assert_raise(ArgumentError) { c.m(h) }
assert_raise(ArgumentError) { c.m(h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `m'/m) do
- assert_raise(ArgumentError) { c.m(h3) }
- end
+ assert_raise(ArgumentError) { c.m(h3) }
sc.class_eval do
remove_method(:m)
@@ -397,34 +562,13 @@ class TestKeywordArguments < Test::Unit::TestCase
[arg, args]
end
end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- c.m(**{})
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- c.m(**kw)
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], c.m(**h))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], c.m(a: 1))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h2, kw], c.m(**h2))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], c.m(**h3))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], c.m(a: 1, **h2))
- end
+ assert_raise(ArgumentError) { c.m(**{}) }
+ assert_raise(ArgumentError) { c.m(**kw) }
+ assert_raise(ArgumentError) { c.m(**h) }
+ assert_raise(ArgumentError) { c.m(a: 1) }
+ assert_raise(ArgumentError) { c.m(**h2) }
+ assert_raise(ArgumentError) { c.m(**h3) }
+ assert_raise(ArgumentError) { c.m(a: 1, **h2) }
sc.class_eval do
remove_method(:m)
@@ -449,19 +593,9 @@ class TestKeywordArguments < Test::Unit::TestCase
sc = Class.new
c = sc.new
- redef = -> do
- if defined?(c.m)
- class << c
- remove_method(:m)
- end
- end
- eval <<-END
- def c.m(*args, **kw)
- super(*args, **kw)
- end
- END
+ def c.m(*args, **kw)
+ super(*args, **kw)
end
- redef[]
sc.class_eval do
def m(*args)
args
@@ -493,13 +627,8 @@ class TestKeywordArguments < Test::Unit::TestCase
args
end
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, c.m(**{}))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, c.m(**kw))
- end
+ assert_raise(ArgumentError) { c.m(**{}) }
+ assert_raise(ArgumentError) { c.m(**kw) }
assert_equal(h, c.m(**h))
assert_equal(h, c.m(a: 1))
assert_equal(h2, c.m(**h2))
@@ -519,13 +648,9 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.m(**h2))
assert_equal(h3, c.m(**h3))
assert_equal(h3, c.m(a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for `m'/m) do
- assert_equal(h, c.m(h))
- end
+ assert_raise(ArgumentError) { c.m(h) }
assert_raise(ArgumentError) { c.m(h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `m'/m) do
- assert_raise(ArgumentError) { c.m(h3) }
- end
+ assert_raise(ArgumentError) { c.m(h3) }
sc.class_eval do
remove_method(:m)
@@ -533,34 +658,13 @@ class TestKeywordArguments < Test::Unit::TestCase
[arg, args]
end
end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- c.m(**{})
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- c.m(**kw)
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], c.m(**h))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], c.m(a: 1))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h2, kw], c.m(**h2))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], c.m(**h3))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], c.m(a: 1, **h2))
- end
+ assert_raise(ArgumentError) { c.m(**{}) }
+ assert_raise(ArgumentError) { c.m(**kw) }
+ assert_raise(ArgumentError) { c.m(**h) }
+ assert_raise(ArgumentError) { c.m(a: 1) }
+ assert_raise(ArgumentError) { c.m(**h2) }
+ assert_raise(ArgumentError) { c.m(**h3) }
+ assert_raise(ArgumentError) { c.m(a: 1, **h2) }
sc.class_eval do
remove_method(:m)
@@ -592,12 +696,8 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_raise(ArgumentError) { f[**h3] }
f = ->(a) { a }
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, f[**{}])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, f[**kw])
- end
+ assert_raise(ArgumentError) { f[**{}] }
+ assert_raise(ArgumentError) { f[**kw] }
assert_equal(h, f[**h])
assert_equal(h, f[a: 1])
assert_equal(h2, f[**h2])
@@ -612,36 +712,18 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, f[**h2])
assert_equal(h3, f[**h3])
assert_equal(h3, f[a: 1, **h2])
- assert_warn(/The last argument is used as the keyword parameter.*for `\[\]'/m) do
- assert_equal(h, f[h])
- end
+ assert_raise(ArgumentError) { f[h] }
assert_raise(ArgumentError) { f[h2] }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `\[\]'/m) do
- assert_raise(ArgumentError) { f[h3] }
- end
+ assert_raise(ArgumentError) { f[h3] }
f = ->(a, **x) { [a,x] }
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `\[\]'/m) do
- assert_equal([{}, {}], f[**{}])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `\[\]'/m) do
- assert_equal([{}, {}], f[**kw])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `\[\]'/m) do
- assert_equal([h, {}], f[**h])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `\[\]'/m) do
- assert_equal([h, {}], f[a: 1])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `\[\]'/m) do
- assert_equal([h2, {}], f[**h2])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `\[\]'/m) do
- assert_equal([h3, {}], f[**h3])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `\[\]'/m) do
- assert_equal([h3, {}], f[a: 1, **h2])
- end
+ assert_raise(ArgumentError) { f[**{}] }
+ assert_raise(ArgumentError) { f[**kw] }
+ assert_raise(ArgumentError) { f[**h] }
+ assert_raise(ArgumentError) { f[a: 1] }
+ assert_raise(ArgumentError) { f[**h2] }
+ assert_raise(ArgumentError) { f[**h3] }
+ assert_raise(ArgumentError) { f[a: 1, **h2] }
f = ->(a=1, **x) { [a, x] }
assert_equal([1, kw], f[**{}])
@@ -670,12 +752,8 @@ class TestKeywordArguments < Test::Unit::TestCase
f = ->(a) { a }
f = f.method(:call)
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, f[**{}])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, f[**kw])
- end
+ assert_raise(ArgumentError) { f[**{}] }
+ assert_raise(ArgumentError) { f[**kw] }
assert_equal(h, f[**h])
assert_equal(h, f[a: 1])
assert_equal(h2, f[**h2])
@@ -691,37 +769,19 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, f[**h2])
assert_equal(h3, f[**h3])
assert_equal(h3, f[a: 1, **h2])
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(h, f[h])
- end
+ assert_raise(ArgumentError) { f[h] }
assert_raise(ArgumentError) { f[h2] }
- assert_warn(/The last argument is split into positional and keyword parameters.*for method/m) do
- assert_raise(ArgumentError) { f[h3] }
- end
+ assert_raise(ArgumentError) { f[h3] }
f = ->(a, **x) { [a,x] }
f = f.method(:call)
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([{}, {}], f[**{}])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([{}, {}], f[**kw])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, {}], f[**h])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, {}], f[a: 1])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h2, {}], f[**h2])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, {}], f[**h3])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, {}], f[a: 1, **h2])
- end
+ assert_raise(ArgumentError) { f[**{}] }
+ assert_raise(ArgumentError) { f[**kw] }
+ assert_raise(ArgumentError) { f[**h] }
+ assert_raise(ArgumentError) { f[a: 1] }
+ assert_raise(ArgumentError) { f[**h2] }
+ assert_raise(ArgumentError) { f[**h3] }
+ assert_raise(ArgumentError) { f[a: 1, **h2] }
f = ->(a=1, **x) { [a, x] }
f = f.method(:call)
@@ -751,12 +811,8 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_raise(ArgumentError) { t.new(**h3, &f).value }
f = ->(a) { a }
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, t.new(**{}, &f).value)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, t.new(**kw, &f).value)
- end
+ assert_raise(ArgumentError) { t.new(**{}, &f).value }
+ assert_raise(ArgumentError) { t.new(**kw, &f).value }
assert_equal(h, t.new(**h, &f).value)
assert_equal(h, t.new(a: 1, &f).value)
assert_equal(h2, t.new(**h2, &f).value)
@@ -771,36 +827,18 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, t.new(**h2, &f).value)
assert_equal(h3, t.new(**h3, &f).value)
assert_equal(h3, t.new(a: 1, **h2, &f).value)
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(h, t.new(h, &f).value)
- end
+ assert_raise(ArgumentError) { t.new(h, &f).value }
assert_raise(ArgumentError) { t.new(h2, &f).value }
- assert_warn(/The last argument is split into positional and keyword parameters.*for method/m) do
- assert_raise(ArgumentError) { t.new(h3, &f).value }
- end
+ assert_raise(ArgumentError) { t.new(h3, &f).value }
f = ->(a, **x) { [a,x] }
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([{}, {}], t.new(**{}, &f).value)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([{}, {}], t.new(**kw, &f).value)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, {}], t.new(**h, &f).value)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, {}], t.new(a: 1, &f).value)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h2, {}], t.new(**h2, &f).value)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, {}], t.new(**h3, &f).value)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, {}], t.new(a: 1, **h2, &f).value)
- end
+ assert_raise(ArgumentError) { t.new(**{}, &f).value }
+ assert_raise(ArgumentError) { t.new(**kw, &f).value }
+ assert_raise(ArgumentError) { t.new(**h, &f).value }
+ assert_raise(ArgumentError) { t.new(a: 1, &f).value }
+ assert_raise(ArgumentError) { t.new(**h2, &f).value }
+ assert_raise(ArgumentError) { t.new(**h3, &f).value }
+ assert_raise(ArgumentError) { t.new(a: 1, **h2, &f).value }
f = ->(a=1, **x) { [a, x] }
assert_equal([1, kw], t.new(**{}, &f).value)
@@ -830,12 +868,8 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_raise(ArgumentError) { t.new(&f).resume(**h3) }
f = ->(a) { a }
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, t.new(&f).resume(**{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, t.new(&f).resume(**kw))
- end
+ assert_raise(ArgumentError) { t.new(&f).resume(**{}) }
+ assert_raise(ArgumentError) { t.new(&f).resume(**kw) }
assert_equal(h, t.new(&f).resume(**h))
assert_equal(h, t.new(&f).resume(a: 1))
assert_equal(h2, t.new(&f).resume(**h2))
@@ -850,36 +884,18 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, t.new(&f).resume(**h2))
assert_equal(h3, t.new(&f).resume(**h3))
assert_equal(h3, t.new(&f).resume(a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(h, t.new(&f).resume(h))
- end
+ assert_raise(ArgumentError) { t.new(&f).resume(h) }
assert_raise(ArgumentError) { t.new(&f).resume(h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for method/m) do
- assert_raise(ArgumentError) { t.new(&f).resume(h3) }
- end
+ assert_raise(ArgumentError) { t.new(&f).resume(h3) }
f = ->(a, **x) { [a,x] }
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([{}, {}], t.new(&f).resume(**{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([{}, {}], t.new(&f).resume(**kw))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, {}], t.new(&f).resume(**h))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, {}], t.new(&f).resume(a: 1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h2, {}], t.new(&f).resume(**h2))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, {}], t.new(&f).resume(**h3))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, {}], t.new(&f).resume(a: 1, **h2))
- end
+ assert_raise(ArgumentError) { t.new(&f).resume(**{}) }
+ assert_raise(ArgumentError) { t.new(&f).resume(**kw) }
+ assert_raise(ArgumentError) { t.new(&f).resume(**h) }
+ assert_raise(ArgumentError) { t.new(&f).resume(a: 1) }
+ assert_raise(ArgumentError) { t.new(&f).resume(**h2) }
+ assert_raise(ArgumentError) { t.new(&f).resume(**h3) }
+ assert_raise(ArgumentError) { t.new(&f).resume(a: 1, **h2) }
f = ->(a=1, **x) { [a, x] }
assert_equal([1, kw], t.new(&f).resume(**{}))
@@ -907,12 +923,8 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_raise(ArgumentError) { g.new(&f).each(**h3) }
f = ->(_, a) { a }
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, g.new(&f).each(**{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, g.new(&f).each(**kw))
- end
+ assert_raise(ArgumentError) { g.new(&f).each(**{}) }
+ assert_raise(ArgumentError) { g.new(&f).each(**kw) }
assert_equal(h, g.new(&f).each(**h))
assert_equal(h, g.new(&f).each(a: 1))
assert_equal(h2, g.new(&f).each(**h2))
@@ -927,36 +939,18 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, g.new(&f).each(**h2))
assert_equal(h3, g.new(&f).each(**h3))
assert_equal(h3, g.new(&f).each(a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(h, g.new(&f).each(h))
- end
+ assert_raise(ArgumentError) { g.new(&f).each(h) }
assert_raise(ArgumentError) { g.new(&f).each(h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for method/m) do
- assert_raise(ArgumentError) { g.new(&f).each(h3) }
- end
+ assert_raise(ArgumentError) { g.new(&f).each(h3) }
f = ->(_, a, **x) { [a,x] }
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([{}, {}], g.new(&f).each(**{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([{}, {}], g.new(&f).each(**kw))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, {}], g.new(&f).each(**h))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, {}], g.new(&f).each(a: 1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h2, {}], g.new(&f).each(**h2))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, {}], g.new(&f).each(**h3))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, {}], g.new(&f).each(a: 1, **h2))
- end
+ assert_raise(ArgumentError) { g.new(&f).each(**{}) }
+ assert_raise(ArgumentError) { g.new(&f).each(**kw) }
+ assert_raise(ArgumentError) { g.new(&f).each(**h) }
+ assert_raise(ArgumentError) { g.new(&f).each(a: 1) }
+ assert_raise(ArgumentError) { g.new(&f).each(**h2) }
+ assert_raise(ArgumentError) { g.new(&f).each(**h3) }
+ assert_raise(ArgumentError) { g.new(&f).each(a: 1, **h2) }
f = ->(_, a=1, **x) { [a, x] }
assert_equal([1, kw], g.new(&f).each(**{}))
@@ -984,12 +978,8 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_raise(ArgumentError) { g.new{|y| y.yield(**h3)}.each(&f) }
f = ->(a) { a }
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, g.new{|y| y.yield(**{})}.each(&f))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, g.new{|y| y.yield(**kw)}.each(&f))
- end
+ assert_raise(ArgumentError) { g.new{|y| y.yield(**{})}.each(&f) }
+ assert_raise(ArgumentError) { g.new{|y| y.yield(**kw)}.each(&f) }
assert_equal(h, g.new{|y| y.yield(**h)}.each(&f))
assert_equal(h, g.new{|y| y.yield(a: 1)}.each(&f))
assert_equal(h2, g.new{|y| y.yield(**h2)}.each(&f))
@@ -1004,36 +994,18 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, g.new{|y| y.yield(**h2)}.each(&f))
assert_equal(h3, g.new{|y| y.yield(**h3)}.each(&f))
assert_equal(h3, g.new{|y| y.yield(a: 1, **h2)}.each(&f))
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(h, g.new{|y| y.yield(h)}.each(&f))
- end
+ assert_raise(ArgumentError) { g.new{|y| y.yield(h)}.each(&f) }
assert_raise(ArgumentError) { g.new{|y| y.yield(h2)}.each(&f) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for method/m) do
- assert_raise(ArgumentError) { g.new{|y| y.yield(h3)}.each(&f) }
- end
+ assert_raise(ArgumentError) { g.new{|y| y.yield(h3)}.each(&f) }
f = ->(a, **x) { [a,x] }
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([{}, {}], g.new{|y| y.yield(**{})}.each(&f))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([{}, {}], g.new{|y| y.yield(**kw)}.each(&f))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, {}], g.new{|y| y.yield(**h)}.each(&f))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, {}], g.new{|y| y.yield(a: 1)}.each(&f))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h2, {}], g.new{|y| y.yield(**h2)}.each(&f))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, {}], g.new{|y| y.yield(**h3)}.each(&f))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, {}], g.new{|y| y.yield(a: 1, **h2)}.each(&f))
- end
+ assert_raise(ArgumentError) { g.new{|y| y.yield(**{})}.each(&f) }
+ assert_raise(ArgumentError) { g.new{|y| y.yield(**kw)}.each(&f) }
+ assert_raise(ArgumentError) { g.new{|y| y.yield(**h)}.each(&f) }
+ assert_raise(ArgumentError) { g.new{|y| y.yield(a: 1)}.each(&f) }
+ assert_raise(ArgumentError) { g.new{|y| y.yield(**h2)}.each(&f) }
+ assert_raise(ArgumentError) { g.new{|y| y.yield(**h3)}.each(&f) }
+ assert_raise(ArgumentError) { g.new{|y| y.yield(a: 1, **h2)}.each(&f) }
f = ->(a=1, **x) { [a, x] }
assert_equal([1, kw], g.new{|y| y.yield(**{})}.each(&f))
@@ -1087,12 +1059,8 @@ class TestKeywordArguments < Test::Unit::TestCase
@args = args
end
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal(kw, c[**{}].args)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal(kw, c[**kw].args)
- end
+ assert_raise(ArgumentError) { c[**{}] }
+ assert_raise(ArgumentError) { c[**kw] }
assert_equal(h, c[**h].args)
assert_equal(h, c[a: 1].args)
assert_equal(h2, c[**h2].args)
@@ -1111,40 +1079,22 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c[**h2].args)
assert_equal(h3, c[**h3].args)
assert_equal(h3, c[a: 1, **h2].args)
- assert_warn(/The last argument is used as the keyword parameter.*for `initialize'/m) do
- assert_equal(h, c[h].args)
- end
+ assert_raise(ArgumentError) { c[h].args }
assert_raise(ArgumentError) { c[h2].args }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `initialize'/m) do
- assert_raise(ArgumentError) { c[h3].args }
- end
+ assert_raise(ArgumentError) { c[h3].args }
c = Class.new(sc) do
def initialize(arg, **args)
@args = [arg, args]
end
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal([kw, kw], c[**{}].args)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal([kw, kw], c[**kw].args)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal([h, kw], c[**h].args)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal([h, kw], c[a: 1].args)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal([h2, kw], c[**h2].args)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal([h3, kw], c[**h3].args)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal([h3, kw], c[a: 1, **h2].args)
- end
+ assert_raise(ArgumentError) { c[**{}].args }
+ assert_raise(ArgumentError) { c[**kw].args }
+ assert_raise(ArgumentError) { c[**h].args }
+ assert_raise(ArgumentError) { c[a: 1].args }
+ assert_raise(ArgumentError) { c[**h2].args }
+ assert_raise(ArgumentError) { c[**h3].args }
+ assert_raise(ArgumentError) { c[a: 1, **h2].args }
c = Class.new(sc) do
def initialize(arg=1, **args)
@@ -1199,12 +1149,8 @@ class TestKeywordArguments < Test::Unit::TestCase
@args = args
end
end.method(:new)
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal(kw, c[**{}].args)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal(kw, c[**kw].args)
- end
+ assert_raise(ArgumentError) { c[**{}] }
+ assert_raise(ArgumentError) { c[**kw] }
assert_equal(h, c[**h].args)
assert_equal(h, c[a: 1].args)
assert_equal(h2, c[**h2].args)
@@ -1223,40 +1169,22 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c[**h2].args)
assert_equal(h3, c[**h3].args)
assert_equal(h3, c[a: 1, **h2].args)
- assert_warn(/The last argument is used as the keyword parameter.*for `initialize'/m) do
- assert_equal(h, c[h].args)
- end
+ assert_raise(ArgumentError) { c[h].args }
assert_raise(ArgumentError) { c[h2].args }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `initialize'/m) do
- assert_raise(ArgumentError) { c[h3].args }
- end
+ assert_raise(ArgumentError) { c[h3].args }
c = Class.new(sc) do
def initialize(arg, **args)
@args = [arg, args]
end
end.method(:new)
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal([kw, kw], c[**{}].args)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal([kw, kw], c[**kw].args)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal([h, kw], c[**h].args)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal([h, kw], c[a: 1].args)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal([h2, kw], c[**h2].args)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal([h3, kw], c[**h3].args)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `initialize'/m) do
- assert_equal([h3, kw], c[a: 1, **h2].args)
- end
+ assert_raise(ArgumentError) { c[**{}].args }
+ assert_raise(ArgumentError) { c[**kw].args }
+ assert_raise(ArgumentError) { c[**h].args }
+ assert_raise(ArgumentError) { c[a: 1].args }
+ assert_raise(ArgumentError) { c[**h2].args }
+ assert_raise(ArgumentError) { c[**h3].args }
+ assert_raise(ArgumentError) { c[a: 1, **h2].args }
c = Class.new(sc) do
def initialize(arg=1, **args)
@@ -1304,12 +1232,8 @@ class TestKeywordArguments < Test::Unit::TestCase
def c.m(args)
args
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, c.method(:m)[**{}])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, c.method(:m)[**kw])
- end
+ assert_raise(ArgumentError) { c.method(:m)[**{}] }
+ assert_raise(ArgumentError) { c.method(:m)[**kw] }
assert_equal(h, c.method(:m)[**h])
assert_equal(h, c.method(:m)[a: 1])
assert_equal(h2, c.method(:m)[**h2])
@@ -1327,39 +1251,21 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.method(:m)[**h2])
assert_equal(h3, c.method(:m)[**h3])
assert_equal(h3, c.method(:m)[a: 1, **h2])
- assert_warn(/The last argument is used as the keyword parameter.*for `m'/m) do
- assert_equal(h, c.method(:m)[h])
- end
+ assert_raise(ArgumentError) { c.method(:m)[h] }
assert_raise(ArgumentError) { c.method(:m)[h2] }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `m'/m) do
- assert_raise(ArgumentError) { c.method(:m)[h3] }
- end
+ assert_raise(ArgumentError) { c.method(:m)[h3] }
c.singleton_class.remove_method(:m)
def c.m(arg, **args)
[arg, args]
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([kw, kw], c.method(:m)[**{}])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([kw, kw], c.method(:m)[**kw])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], c.method(:m)[**h])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], c.method(:m)[a: 1])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h2, kw], c.method(:m)[**h2])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], c.method(:m)[**h3])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], c.method(:m)[a: 1, **h2])
- end
+ assert_raise(ArgumentError) { c.method(:m)[**{}] }
+ assert_raise(ArgumentError) { c.method(:m)[**kw] }
+ assert_raise(ArgumentError) { c.method(:m)[**h] }
+ assert_raise(ArgumentError) { c.method(:m)[a: 1] }
+ assert_raise(ArgumentError) { c.method(:m)[**h2] }
+ assert_raise(ArgumentError) { c.method(:m)[**h3] }
+ assert_raise(ArgumentError) { c.method(:m)[a: 1, **h2] }
c.singleton_class.remove_method(:m)
def c.m(arg=1, **args)
@@ -1407,12 +1313,8 @@ class TestKeywordArguments < Test::Unit::TestCase
def c.m(args)
args
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, sc.instance_method(:m).bind_call(c, **{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, sc.instance_method(:m).bind_call(c, **kw))
- end
+ assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, **{}) }
+ assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, **kw) }
assert_equal(h, sc.instance_method(:m).bind_call(c, **h))
assert_equal(h, sc.instance_method(:m).bind_call(c, a: 1))
assert_equal(h2, sc.instance_method(:m).bind_call(c, **h2))
@@ -1430,39 +1332,21 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, sc.instance_method(:m).bind_call(c, **h2))
assert_equal(h3, sc.instance_method(:m).bind_call(c, **h3))
assert_equal(h3, sc.instance_method(:m).bind_call(c, a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for `m'/m) do
- assert_equal(h, sc.instance_method(:m).bind_call(c, h))
- end
+ assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, h) }
assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `m'/m) do
- assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, h3) }
- end
+ assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, h3) }
sc.remove_method(:m)
def c.m(arg, **args)
[arg, args]
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([kw, kw], sc.instance_method(:m).bind_call(c, **{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([kw, kw], sc.instance_method(:m).bind_call(c, **kw))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], sc.instance_method(:m).bind_call(c, **h))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], sc.instance_method(:m).bind_call(c, a: 1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h2, kw], sc.instance_method(:m).bind_call(c, **h2))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], sc.instance_method(:m).bind_call(c, **h3))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], sc.instance_method(:m).bind_call(c, a: 1, **h2))
- end
+ assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, **{}) }
+ assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, **kw) }
+ assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, **h) }
+ assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, a: 1) }
+ assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, **h2) }
+ assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, **h3) }
+ assert_raise(ArgumentError) { sc.instance_method(:m).bind_call(c, a: 1, **h2) }
sc.remove_method(:m)
def c.m(arg=1, **args)
@@ -1509,12 +1393,8 @@ class TestKeywordArguments < Test::Unit::TestCase
def c.m(args)
args
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, c.send(:m, **{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, c.send(:m, **kw))
- end
+ assert_raise(ArgumentError) { c.send(:m, **{}) }
+ assert_raise(ArgumentError) { c.send(:m, **kw) }
assert_equal(h, c.send(:m, **h))
assert_equal(h, c.send(:m, a: 1))
assert_equal(h2, c.send(:m, **h2))
@@ -1532,39 +1412,21 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.send(:m, **h2))
assert_equal(h3, c.send(:m, **h3))
assert_equal(h3, c.send(:m, a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for `m'/m) do
- assert_equal(h, c.send(:m, h))
- end
+ assert_raise(ArgumentError) { c.send(:m, h) }
assert_raise(ArgumentError) { c.send(:m, h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `m'/m) do
- assert_raise(ArgumentError) { c.send(:m, h3) }
- end
+ assert_raise(ArgumentError) { c.send(:m, h3) }
c.singleton_class.remove_method(:m)
def c.m(arg, **args)
[arg, args]
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- c.send(:m, **{})
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- c.send(:m, **kw)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], c.send(:m, **h))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], c.send(:m, a: 1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h2, kw], c.send(:m, **h2))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], c.send(:m, **h3))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], c.send(:m, a: 1, **h2))
- end
+ assert_raise(ArgumentError) { c.send(:m, **{}) }
+ assert_raise(ArgumentError) { c.send(:m, **kw) }
+ assert_raise(ArgumentError) { c.send(:m, **h) }
+ assert_raise(ArgumentError) { c.send(:m, a: 1) }
+ assert_raise(ArgumentError) { c.send(:m, **h2) }
+ assert_raise(ArgumentError) { c.send(:m, **h3) }
+ assert_raise(ArgumentError) { c.send(:m, a: 1, **h2) }
c.singleton_class.remove_method(:m)
def c.m(arg=1, **args)
@@ -1611,12 +1473,8 @@ class TestKeywordArguments < Test::Unit::TestCase
def c.m(args)
args
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, c.public_send(:m, **{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, c.public_send(:m, **kw))
- end
+ assert_raise(ArgumentError) { c.public_send(:m, **{}) }
+ assert_raise(ArgumentError) { c.public_send(:m, **kw) }
assert_equal(h, c.public_send(:m, **h))
assert_equal(h, c.public_send(:m, a: 1))
assert_equal(h2, c.public_send(:m, **h2))
@@ -1634,39 +1492,21 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.public_send(:m, **h2))
assert_equal(h3, c.public_send(:m, **h3))
assert_equal(h3, c.public_send(:m, a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for `m'/m) do
- assert_equal(h, c.public_send(:m, h))
- end
+ assert_raise(ArgumentError) { c.public_send(:m, h) }
assert_raise(ArgumentError) { c.public_send(:m, h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `m'/m) do
- assert_raise(ArgumentError) { c.public_send(:m, h3) }
- end
+ assert_raise(ArgumentError) { c.public_send(:m, h3) }
c.singleton_class.remove_method(:m)
def c.m(arg, **args)
[arg, args]
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- c.public_send(:m, **{})
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- c.public_send(:m, **kw)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], c.public_send(:m, **h))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], c.public_send(:m, a: 1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h2, kw], c.public_send(:m, **h2))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], c.public_send(:m, **h3))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], c.public_send(:m, a: 1, **h2))
- end
+ assert_raise(ArgumentError) { c.public_send(:m, **{}) }
+ assert_raise(ArgumentError) { c.public_send(:m, **kw) }
+ assert_raise(ArgumentError) { c.public_send(:m, **h) }
+ assert_raise(ArgumentError) { c.public_send(:m, a: 1) }
+ assert_raise(ArgumentError) { c.public_send(:m, **h2) }
+ assert_raise(ArgumentError) { c.public_send(:m, **h3) }
+ assert_raise(ArgumentError) { c.public_send(:m, a: 1, **h2) }
c.singleton_class.remove_method(:m)
def c.m(arg=1, **args)
@@ -1716,12 +1556,8 @@ class TestKeywordArguments < Test::Unit::TestCase
args
end
m = c.method(:send)
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, m.call(:m, **{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, m.call(:m, **kw))
- end
+ assert_raise(ArgumentError) { m.call(:m, **{}) }
+ assert_raise(ArgumentError) { m.call(:m, **kw) }
assert_equal(h, m.call(:m, **h))
assert_equal(h, m.call(:m, a: 1))
assert_equal(h2, m.call(:m, **h2))
@@ -1740,40 +1576,22 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, m.call(:m, **h2))
assert_equal(h3, m.call(:m, **h3))
assert_equal(h3, m.call(:m, a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for `m'/m) do
- assert_equal(h, m.call(:m, h))
- end
+ assert_raise(ArgumentError) { m.call(:m, h) }
assert_raise(ArgumentError) { m.call(:m, h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `m'/m) do
- assert_raise(ArgumentError) { m.call(:m, h3) }
- end
+ assert_raise(ArgumentError) { m.call(:m, h3) }
c.singleton_class.remove_method(:m)
def c.m(arg, **args)
[arg, args]
end
m = c.method(:send)
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- m.call(:m, **{})
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- m.call(:m, **kw)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], m.call(:m, **h))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], m.call(:m, a: 1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h2, kw], m.call(:m, **h2))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], m.call(:m, **h3))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], m.call(:m, a: 1, **h2))
- end
+ assert_raise(ArgumentError) { m.call(:m, **{}) }
+ assert_raise(ArgumentError) { m.call(:m, **kw) }
+ assert_raise(ArgumentError) { m.call(:m, **h) }
+ assert_raise(ArgumentError) { m.call(:m, a: 1) }
+ assert_raise(ArgumentError) { m.call(:m, **h2) }
+ assert_raise(ArgumentError) { m.call(:m, **h3) }
+ assert_raise(ArgumentError) { m.call(:m, a: 1, **h2) }
c.singleton_class.remove_method(:m)
def c.m(arg=1, **args)
@@ -1821,12 +1639,8 @@ class TestKeywordArguments < Test::Unit::TestCase
def c.m(args)
args
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, :m.to_proc.call(c, **{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, :m.to_proc.call(c, **kw))
- end
+ assert_raise(ArgumentError) { :m.to_proc.call(c, **{}) }
+ assert_raise(ArgumentError) { :m.to_proc.call(c, **kw) }
assert_equal(h, :m.to_proc.call(c, **h))
assert_equal(h, :m.to_proc.call(c, a: 1))
assert_equal(h2, :m.to_proc.call(c, **h2))
@@ -1844,39 +1658,21 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, :m.to_proc.call(c, **h2))
assert_equal(h3, :m.to_proc.call(c, **h3))
assert_equal(h3, :m.to_proc.call(c, a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for `m'/m) do
- assert_equal(h, :m.to_proc.call(c, h))
- end
+ assert_raise(ArgumentError) { :m.to_proc.call(c, h) }
assert_raise(ArgumentError) { :m.to_proc.call(c, h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `m'/m) do
- assert_raise(ArgumentError) { :m.to_proc.call(c, h3) }
- end
+ assert_raise(ArgumentError) { :m.to_proc.call(c, h3) }
c.singleton_class.remove_method(:m)
def c.m(arg, **args)
[arg, args]
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([kw, kw], :m.to_proc.call(c, **{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([kw, kw], :m.to_proc.call(c, **kw))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], :m.to_proc.call(c, **h))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], :m.to_proc.call(c, a: 1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h2, kw], :m.to_proc.call(c, **h2))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], :m.to_proc.call(c, **h3))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], :m.to_proc.call(c, a: 1, **h2))
- end
+ assert_raise(ArgumentError) { :m.to_proc.call(c, **{}) }
+ assert_raise(ArgumentError) { :m.to_proc.call(c, **kw) }
+ assert_raise(ArgumentError) { :m.to_proc.call(c, **h) }
+ assert_raise(ArgumentError) { :m.to_proc.call(c, a: 1) }
+ assert_raise(ArgumentError) { :m.to_proc.call(c, **h2) }
+ assert_raise(ArgumentError) { :m.to_proc.call(c, **h3) }
+ assert_raise(ArgumentError) { :m.to_proc.call(c, a: 1, **h2) }
c.singleton_class.remove_method(:m)
def c.m(arg=1, **args)
@@ -1924,12 +1720,8 @@ class TestKeywordArguments < Test::Unit::TestCase
def c.m(args)
args
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, m.call(c, **{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, m.call(c, **kw))
- end
+ assert_raise(ArgumentError) { m.call(c, **{}) }
+ assert_raise(ArgumentError) { m.call(c, **kw) }
assert_equal(h, m.call(c, **h))
assert_equal(h, m.call(c, a: 1))
assert_equal(h2, m.call(c, **h2))
@@ -1947,39 +1739,21 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, m.call(c, **h2))
assert_equal(h3, m.call(c, **h3))
assert_equal(h3, m.call(c, a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for `m'/m) do
- assert_equal(h, m.call(c, h))
- end
+ assert_raise(ArgumentError) { m.call(c, h) }
assert_raise(ArgumentError) { m.call(c, h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `m'/m) do
- assert_raise(ArgumentError) { m.call(c, h3) }
- end
+ assert_raise(ArgumentError) { m.call(c, h3) }
c.singleton_class.remove_method(:m)
def c.m(arg, **args)
[arg, args]
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([kw, kw], m.call(c, **{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([kw, kw], m.call(c, **kw))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], m.call(c, **h))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], m.call(c, a: 1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h2, kw], m.call(c, **h2))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], m.call(c, **h3))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], m.call(c, a: 1, **h2))
- end
+ assert_raise(ArgumentError) { m.call(c, **{}) }
+ assert_raise(ArgumentError) { m.call(c, **kw) }
+ assert_raise(ArgumentError) { m.call(c, **h) }
+ assert_raise(ArgumentError) { m.call(c, a: 1) }
+ assert_raise(ArgumentError) { m.call(c, **h2) }
+ assert_raise(ArgumentError) { m.call(c, **h3) }
+ assert_raise(ArgumentError) { m.call(c, a: 1, **h2) }
c.singleton_class.remove_method(:m)
def c.m(arg=1, **args)
@@ -2026,12 +1800,8 @@ class TestKeywordArguments < Test::Unit::TestCase
def c.method_missing(_, args)
args
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal(kw, c.m(**{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal(kw, c.m(**kw))
- end
+ assert_raise(ArgumentError) { c.m(**{}) }
+ assert_raise(ArgumentError) { c.m(**kw) }
assert_equal(h, c.m(**h))
assert_equal(h, c.m(a: 1))
assert_equal(h2, c.m(**h2))
@@ -2049,39 +1819,21 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.m(**h2))
assert_equal(h3, c.m(**h3))
assert_equal(h3, c.m(a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for `method_missing'/m) do
- assert_equal(h, c.m(h))
- end
+ assert_raise(ArgumentError) { c.m(h) }
assert_raise(ArgumentError) { c.m(h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `method_missing'/m) do
- assert_raise(ArgumentError) { c.m(h3) }
- end
+ assert_raise(ArgumentError) { c.m(h3) }
c.singleton_class.remove_method(:method_missing)
def c.method_missing(_, arg, **args)
[arg, args]
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([kw, kw], c.m(**{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([kw, kw], c.m(**kw))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h, kw], c.m(**h))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h, kw], c.m(a: 1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h2, kw], c.m(**h2))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h3, kw], c.m(**h3))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h3, kw], c.m(a: 1, **h2))
- end
+ assert_raise(ArgumentError) { c.m(**{}) }
+ assert_raise(ArgumentError) { c.m(**kw) }
+ assert_raise(ArgumentError) { c.m(**h) }
+ assert_raise(ArgumentError) { c.m(a: 1) }
+ assert_raise(ArgumentError) { c.m(**h2) }
+ assert_raise(ArgumentError) { c.m(**h3) }
+ assert_raise(ArgumentError) { c.m(a: 1, **h2) }
c.singleton_class.remove_method(:method_missing)
def c.method_missing(_, arg=1, **args)
@@ -2128,22 +1880,12 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_raise(ArgumentError) { c.m(**h3) }
assert_raise(ArgumentError) { c.m(a: 1, **h2) }
- redef = -> do
- c.singleton_class.remove_method(:method_missing)
- eval <<-END
- def c.method_missing(_, args)
- args
- end
- END
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal(kw, c.m(**{}))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal(kw, c.m(**kw))
+ c.singleton_class.remove_method(:method_missing)
+ def c.method_missing(_, args)
+ args
end
+ assert_raise(ArgumentError) { c.m(**{}) }
+ assert_raise(ArgumentError) { c.m(**kw) }
assert_equal(h, c.m(**h))
assert_equal(h, c.m(a: 1))
assert_equal(h2, c.m(**h2))
@@ -2161,50 +1903,21 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.m(**h2))
assert_equal(h3, c.m(**h3))
assert_equal(h3, c.m(a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for `m'/m) do
- assert_equal(h, c.m(h))
- end
+ assert_raise(ArgumentError) { c.m(h) }
assert_raise(ArgumentError) { c.m(h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `m'/m) do
- assert_raise(ArgumentError) { c.m(h3) }
- end
+ assert_raise(ArgumentError) { c.m(h3) }
- redef = -> do
- c.singleton_class.remove_method(:method_missing)
- eval <<-END
- def c.method_missing(_, arg, **args)
- [arg, args]
- end
- END
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([kw, kw], c.m(**{}))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([kw, kw], c.m(**kw))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h, kw], c.m(**h))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h, kw], c.m(a: 1))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h2, kw], c.m(**h2))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h3, kw], c.m(**h3))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h3, kw], c.m(a: 1, **h2))
+ c.singleton_class.remove_method(:method_missing)
+ def c.method_missing(_, arg, **args)
+ [arg, args]
end
+ assert_raise(ArgumentError) { c.m(**{}) }
+ assert_raise(ArgumentError) { c.m(**kw) }
+ assert_raise(ArgumentError) { c.m(**h) }
+ assert_raise(ArgumentError) { c.m(a: 1) }
+ assert_raise(ArgumentError) { c.m(**h2) }
+ assert_raise(ArgumentError) { c.m(**h3) }
+ assert_raise(ArgumentError) { c.m(a: 1, **h2) }
c.singleton_class.remove_method(:method_missing)
def c.method_missing(_, arg=1, **args)
@@ -2252,12 +1965,8 @@ class TestKeywordArguments < Test::Unit::TestCase
def c.method_missing(_, args)
args
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal(kw, c.m(**{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal(kw, c.m(**kw))
- end
+ assert_raise(ArgumentError) { c.m(**{}) }
+ assert_raise(ArgumentError) { c.m(**kw) }
assert_equal(h, c.m(**h))
assert_equal(h, c.m(a: 1))
assert_equal(h2, c.m(**h2))
@@ -2275,39 +1984,21 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.m(**h2))
assert_equal(h3, c.m(**h3))
assert_equal(h3, c.m(a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for `method_missing'/m) do
- assert_equal(h, c.m(h))
- end
+ assert_raise(ArgumentError) { c.m(h) }
assert_raise(ArgumentError) { c.m(h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `method_missing'/m) do
- assert_raise(ArgumentError) { c.m(h3) }
- end
+ assert_raise(ArgumentError) { c.m(h3) }
c.singleton_class.remove_method(:method_missing)
def c.method_missing(_, arg, **args)
[arg, args]
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([kw, kw], c.m(**{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([kw, kw], c.m(**kw))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h, kw], c.m(**h))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h, kw], c.m(a: 1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h2, kw], c.m(**h2))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h3, kw], c.m(**h3))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h3, kw], c.m(a: 1, **h2))
- end
+ assert_raise(ArgumentError) { c.m(**{}) }
+ assert_raise(ArgumentError) { c.m(**kw) }
+ assert_raise(ArgumentError) { c.m(**h) }
+ assert_raise(ArgumentError) { c.m(a: 1) }
+ assert_raise(ArgumentError) { c.m(**h2) }
+ assert_raise(ArgumentError) { c.m(**h3) }
+ assert_raise(ArgumentError) { c.m(a: 1, **h2) }
c.singleton_class.remove_method(:method_missing)
def c.method_missing(_, arg=1, **args)
@@ -2344,12 +2035,8 @@ class TestKeywordArguments < Test::Unit::TestCase
class << c
define_method(:m) {|arg| arg }
end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, c.m(**{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, c.m(**kw))
- end
+ assert_raise(ArgumentError) { c.m(**{}) }
+ assert_raise(ArgumentError) { c.m(**kw) }
assert_equal(h, c.m(**h))
assert_equal(h, c.m(a: 1))
assert_equal(h2, c.m(**h2))
@@ -2379,39 +2066,21 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.m(**h2))
assert_equal(h3, c.m(**h3))
assert_equal(h3, c.m(a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter/m) do
- assert_equal(h, c.m(h))
- end
+ assert_raise(ArgumentError) { c.m(h) }
assert_raise(ArgumentError) { c.m(h2) }
- assert_warn(/The last argument is split into positional and keyword parameters/m) do
- assert_raise(ArgumentError) { c.m(h3) }
- end
+ assert_raise(ArgumentError) { c.m(h3) }
c = Object.new
class << c
define_method(:m) {|arg, **opt| [arg, opt] }
end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([kw, kw], c.m(**{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([kw, kw], c.m(**kw))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h, kw], c.m(**h))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h, kw], c.m(a: 1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h2, kw], c.m(**h2))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h3, kw], c.m(**h3))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h3, kw], c.m(a: 1, **h2))
- end
+ assert_raise(ArgumentError) { c.m(**{}) }
+ assert_raise(ArgumentError) { c.m(**kw) }
+ assert_raise(ArgumentError) { c.m(**h) }
+ assert_raise(ArgumentError) { c.m(a: 1) }
+ assert_raise(ArgumentError) { c.m(**h2) }
+ assert_raise(ArgumentError) { c.m(**h3) }
+ assert_raise(ArgumentError) { c.m(a: 1, **h2) }
c = Object.new
class << c
@@ -2429,23 +2098,15 @@ class TestKeywordArguments < Test::Unit::TestCase
class << c
define_method(:m) {|*args, **opt| [args, opt] }
end
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal([[], h], c.m(h))
- end
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal([[h], h], c.m(h, h))
- end
+ assert_equal([[h], kw], c.m(h))
+ assert_equal([[h, h], kw], c.m(h, h))
c = Object.new
class << c
define_method(:m) {|arg=nil, a: nil| [arg, a] }
end
- assert_warn(/The last argument is split into positional and keyword parameters.*for method/m) do
- assert_equal([h2, 1], c.m(h3))
- end
- assert_warn(/The last argument is split into positional and keyword parameters.*for method/m) do
- assert_equal([h2, 1], c.m(**h3))
- end
+ assert_equal([h3, nil], c.m(h3))
+ assert_raise(ArgumentError) { c.m(**h3) }
end
def test_define_method_method_kwsplat
@@ -2472,12 +2133,8 @@ class TestKeywordArguments < Test::Unit::TestCase
define_method(:m) {|arg| arg }
end
m = c.method(:m)
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, m.call(**{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, m.call(**kw))
- end
+ assert_raise(ArgumentError) { m.call(**{}) }
+ assert_raise(ArgumentError) { m.call(**kw) }
assert_equal(h, m.call(**h))
assert_equal(h, m.call(a: 1))
assert_equal(h2, m.call(**h2))
@@ -2509,40 +2166,22 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, m.call(**h2))
assert_equal(h3, m.call(**h3))
assert_equal(h3, m.call(a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter/m) do
- assert_equal(h, m.call(h))
- end
+ assert_raise(ArgumentError) { m.call(h) }
assert_raise(ArgumentError) { m.call(h2) }
- assert_warn(/The last argument is split into positional and keyword parameters/m) do
- assert_raise(ArgumentError) { m.call(h3) }
- end
+ assert_raise(ArgumentError) { m.call(h3) }
c = Object.new
class << c
define_method(:m) {|arg, **opt| [arg, opt] }
end
m = c.method(:m)
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([kw, kw], m.call(**{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([kw, kw], m.call(**kw))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h, kw], m.call(**h))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h, kw], m.call(a: 1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h2, kw], m.call(**h2))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h3, kw], m.call(**h3))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h3, kw], m.call(a: 1, **h2))
- end
+ assert_raise(ArgumentError) { m.call(**{}) }
+ assert_raise(ArgumentError) { m.call(**kw) }
+ assert_raise(ArgumentError) { m.call(**h) }
+ assert_raise(ArgumentError) { m.call(a: 1) }
+ assert_raise(ArgumentError) { m.call(**h2) }
+ assert_raise(ArgumentError) { m.call(**h3) }
+ assert_raise(ArgumentError) { m.call(a: 1, **h2) }
c = Object.new
class << c
@@ -2562,24 +2201,16 @@ class TestKeywordArguments < Test::Unit::TestCase
define_method(:m) {|*args, **opt| [args, opt] }
end
m = c.method(:m)
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal([[], h], m.call(h))
- end
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal([[h], h], m.call(h, h))
- end
+ assert_equal([[h], kw], m.call(h))
+ assert_equal([[h, h], kw], m.call(h, h))
c = Object.new
class << c
define_method(:m) {|arg=nil, a: nil| [arg, a] }
end
m = c.method(:m)
- assert_warn(/The last argument is split into positional and keyword parameters.*for method/m) do
- assert_equal([h2, 1], m.call(h3))
- end
- assert_warn(/The last argument is split into positional and keyword parameters.*for method/m) do
- assert_equal([h2, 1], m.call(**h3))
- end
+ assert_equal([h3, nil], m.call(h3))
+ assert_raise(ArgumentError) { m.call(**h3) }
end
def test_attr_reader_kwsplat
@@ -2631,12 +2262,8 @@ class TestKeywordArguments < Test::Unit::TestCase
class << c
attr_writer :m
end
- assert_warn(/The keyword argument for `m=' is passed as the last hash parameter/) do
- c.send(:m=, **{})
- end
- assert_warn(/The keyword argument for `m=' is passed as the last hash parameter/) do
- c.send(:m=, **kw)
- end
+ assert_raise(ArgumentError) { c.send(:m=, **{}) }
+ assert_raise(ArgumentError) { c.send(:m=, **kw) }
assert_equal(h, c.send(:m=, **h))
assert_equal(h, c.send(:m=, a: 1))
assert_equal(h2, c.send(:m=, **h2))
@@ -2663,12 +2290,8 @@ class TestKeywordArguments < Test::Unit::TestCase
attr_writer :m
end
m = c.method(:m=)
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- m.call(**{})
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- m.call(**kw)
- end
+ assert_raise(ArgumentError) { m.call(**{}) }
+ assert_raise(ArgumentError) { m.call(**kw) }
assert_equal(h, m.call(**h))
assert_equal(h, m.call(a: 1))
assert_equal(h2, m.call(**h2))
@@ -2691,13 +2314,9 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([[1], h1], foo.call(1, :a=>1, &->(*args, **kw){[args, kw]}))
assert_equal([1, h1], foo.call(1, :a=>1, &->(*args){args}))
- assert_warn(/The last argument is used as the keyword parameter/) do
- assert_equal([[1], h1], foo.call(1, {:a=>1}, &->(*args, **kw){[args, kw]}))
- end
+ assert_equal([[1, h1], {}], foo.call(1, {:a=>1}, &->(*args, **kw){[args, kw]}))
assert_equal([1, h1], foo.call(1, {:a=>1}, &->(*args){args}))
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h1, {}], foo.call(:a=>1, &->(arg, **kw){[arg, kw]}))
- end
+ assert_raise(ArgumentError) { foo.call(:a=>1, &->(arg, **kw){[arg, kw]}) }
assert_equal(h1, foo.call(:a=>1, &->(arg){arg}))
[->(){}, ->(arg){}, ->(*args, **kw){}, ->(*args, k: 1){}, ->(*args, k: ){}].each do |pr|
@@ -2724,6 +2343,12 @@ class TestKeywordArguments < Test::Unit::TestCase
end
def test_ruby2_keywords
+ assert_raise(ArgumentError) do
+ Class.new do
+ ruby2_keywords
+ end
+ end
+
c = Class.new do
ruby2_keywords def foo(meth, *args)
send(meth, *args)
@@ -2741,6 +2366,11 @@ class TestKeywordArguments < Test::Unit::TestCase
baz(*args)
end
+ define_method(:block_splat) {|*args| }
+ ruby2_keywords :block_splat, def foo_bar_after_bmethod(*args)
+ bar(*args)
+ end
+
ruby2_keywords def foo_baz2(*args)
baz(*args)
baz(*args)
@@ -2781,6 +2411,13 @@ class TestKeywordArguments < Test::Unit::TestCase
args
end
+ def empty_method
+ end
+
+ def opt(arg = :opt)
+ arg
+ end
+
ruby2_keywords def foo_dbar(*args)
dbar(*args)
end
@@ -2789,6 +2426,16 @@ class TestKeywordArguments < Test::Unit::TestCase
dbaz(*args)
end
+ ruby2_keywords def clear_last_empty_method(*args)
+ args.last.clear
+ empty_method(*args)
+ end
+
+ ruby2_keywords def clear_last_opt(*args)
+ args.last.clear
+ opt(*args)
+ end
+
define_method(:dbar) do |*args, **kw|
[args, kw]
end
@@ -2876,6 +2523,7 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, h1], o.foo(:foo_baz, 1, :a=>1))
assert_equal([[1], h1], o.foo_foo_bar(1, :a=>1))
assert_equal([1, h1], o.foo_foo_baz(1, :a=>1))
+ assert_equal([[1], h1], o.foo_bar_after_bmethod(1, :a=>1))
assert_equal([[1], h1], o.foo(:bar, 1, **h1))
assert_equal([1, h1], o.foo(:baz, 1, **h1))
@@ -2891,6 +2539,7 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, h1], o.foo(:foo_baz, 1, **h1))
assert_equal([[1], h1], o.foo_foo_bar(1, **h1))
assert_equal([1, h1], o.foo_foo_baz(1, **h1))
+ assert_equal([[1], h1], o.foo_bar_after_bmethod(1, **h1))
assert_equal([[h1], {}], o.foo(:bar, h1, **{}))
assert_equal([h1], o.foo(:baz, h1, **{}))
@@ -2906,23 +2555,17 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([h1], o.foo(:foo_baz, h1, **{}))
assert_equal([[h1], {}], o.foo_foo_bar(h1, **{}))
assert_equal([h1], o.foo_foo_baz(h1, **{}))
+ assert_equal([[h1], {}], o.foo_bar_after_bmethod(h1, **{}))
- assert_warn(/The last argument is used as the keyword parameter.* for `bar'/m) do
- assert_equal([[1], h1], o.foo(:bar, 1, h1))
- end
+ assert_equal([[1, h1], {}], o.foo(:bar, 1, h1))
assert_equal([1, h1], o.foo(:baz, 1, h1))
- assert_warn(/The last argument is used as the keyword parameter.* for `bar'/m) do
- assert_equal([[1], h1], o.bfoo(:bar, 1, h1))
- end
+ assert_equal([[1, h1], {}], o.bfoo(:bar, 1, h1))
assert_equal([1, h1], o.bfoo(:baz, 1, h1))
- assert_warn(/The last argument is used as the keyword parameter.* for `bar'/m) do
- assert_equal([[1], h1], o.store_foo(:bar, 1, h1))
- end
+ assert_equal([[1, h1], {}], o.store_foo(:bar, 1, h1))
assert_equal([1, h1], o.store_foo(:baz, 1, h1))
- assert_warn(/The last argument is used as the keyword parameter.* for `bar'/m) do
- assert_equal([[1], h1], o.foo_bar(1, h1))
- end
+ assert_equal([[1, h1], {}], o.foo_bar(1, h1))
assert_equal([1, h1], o.foo_baz(1, h1))
+ assert_equal([[1, h1], {}], o.foo_bar_after_bmethod(1, h1))
assert_equal([[1, h1, 1], {}], o.foo_mod(:bar, 1, :a=>1))
assert_equal([1, h1, 1], o.foo_mod(:baz, 1, :a=>1))
@@ -2934,10 +2577,10 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([[1, h1, 1], {}], o.foo_bar_mod(1, **h1))
assert_equal([1, h1, 1], o.foo_baz_mod(1, **h1))
- assert_equal([[h1, {}, 1], {}], o.foo_mod(:bar, h1, **{}))
- assert_equal([h1, {}, 1], o.foo_mod(:baz, h1, **{}))
- assert_equal([[h1, {}, 1], {}], o.foo_bar_mod(h1, **{}))
- assert_equal([h1, {}, 1], o.foo_baz_mod(h1, **{}))
+ assert_equal([[h1, 1], {}], o.foo_mod(:bar, h1, **{}))
+ assert_equal([h1, 1], o.foo_mod(:baz, h1, **{}))
+ assert_equal([[h1, 1], {}], o.foo_bar_mod(h1, **{}))
+ assert_equal([h1, 1], o.foo_baz_mod(h1, **{}))
assert_equal([[1, h1, 1], {}], o.foo_mod(:bar, 1, h1))
assert_equal([1, h1, 1], o.foo_mod(:baz, 1, h1))
@@ -2971,43 +2614,29 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([[h1], {}], o.foo_dbar(h1, **{}))
assert_equal([h1], o.foo_dbaz(h1, **{}))
- assert_warn(/The last argument is used as the keyword parameter.* for method/m) do
- assert_equal([[1], h1], o.foo(:dbar, 1, h1))
- end
+ assert_equal([[1, h1], {}], o.foo(:dbar, 1, h1))
assert_equal([1, h1], o.foo(:dbaz, 1, h1))
- assert_warn(/The last argument is used as the keyword parameter.* for method/m) do
- assert_equal([[1], h1], o.bfoo(:dbar, 1, h1))
- end
+ assert_equal([[1, h1], {}], o.bfoo(:dbar, 1, h1))
assert_equal([1, h1], o.bfoo(:dbaz, 1, h1))
- assert_warn(/The last argument is used as the keyword parameter.* for method/m) do
- assert_equal([[1], h1], o.store_foo(:dbar, 1, h1))
- end
+ assert_equal([[1, h1], {}], o.store_foo(:dbar, 1, h1))
assert_equal([1, h1], o.store_foo(:dbaz, 1, h1))
- assert_warn(/The last argument is used as the keyword parameter.* for method/m) do
- assert_equal([[1], h1], o.foo_dbar(1, h1))
- end
+ assert_equal([[1, h1], {}], o.foo_dbar(1, h1))
assert_equal([1, h1], o.foo_dbaz(1, h1))
assert_equal([[1], h1], o.block(1, :a=>1))
assert_equal([[1], h1], o.block(1, **h1))
- assert_warn(/The last argument is used as the keyword parameter.* for `call'/m) do
- assert_equal([[1], h1], o.block(1, h1))
- end
+ assert_equal([[1, h1], {}], o.block(1, h1))
assert_equal([[h1], {}], o.block(h1, **{}))
assert_equal([[1], h1], o.cfunc(1, :a=>1))
assert_equal([[1], h1], o.cfunc(1, **h1))
- assert_warn(/The last argument is used as the keyword parameter.* for `initialize'/m) do
- assert_equal([[1], h1], o.cfunc(1, h1))
- end
+ assert_equal([[1, h1], {}], o.cfunc(1, h1))
assert_equal([[h1], {}], o.cfunc(h1, **{}))
o = mmkw.new
assert_equal([[:b, 1], h1], o.b(1, :a=>1))
assert_equal([[:b, 1], h1], o.b(1, **h1))
- assert_warn(/The last argument is used as the keyword parameter.* for `method_missing'/m) do
- assert_equal([[:b, 1], h1], o.b(1, h1))
- end
+ assert_equal([[:b, 1, h1], {}], o.b(1, h1))
assert_equal([[:b, h1], {}], o.b(h1, **{}))
o = mmnokw.new
@@ -3019,9 +2648,7 @@ class TestKeywordArguments < Test::Unit::TestCase
o = implicit_super.new
assert_equal([[1], h1], o.bar(1, :a=>1))
assert_equal([[1], h1], o.bar(1, **h1))
- assert_warn(/The last argument is used as the keyword parameter.* for `bar'/m) do
- assert_equal([[1], h1], o.bar(1, h1))
- end
+ assert_equal([[1, h1], {}], o.bar(1, h1))
assert_equal([[h1], {}], o.bar(h1, **{}))
assert_equal([1, h1], o.baz(1, :a=>1))
@@ -3032,9 +2659,7 @@ class TestKeywordArguments < Test::Unit::TestCase
o = explicit_super.new
assert_equal([[1], h1], o.bar(1, :a=>1))
assert_equal([[1], h1], o.bar(1, **h1))
- assert_warn(/The last argument is used as the keyword parameter.* for `bar'/m) do
- assert_equal([[1], h1], o.bar(1, h1))
- end
+ assert_equal([[1, h1], {}], o.bar(1, h1))
assert_equal([[h1], {}], o.bar(h1, **{}))
assert_equal([1, h1], o.baz(1, :a=>1))
@@ -3042,19 +2667,11 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, h1], o.baz(1, h1))
assert_equal([h1], o.baz(h1, **{}))
- c.class_eval do
- remove_method(:bar)
- def bar(*args, **kw)
- [args, kw]
- end
- end
- assert_warn(/The last argument is used as the keyword parameter.* for `bar'/m) do
- assert_equal([[1], h1], o.foo(:pass_bar, 1, :a=>1))
- end
+ assert_equal([[1, h1], {}], o.foo(:pass_bar, 1, :a=>1))
+ assert_equal([[1, h1], {}], o.foo(:pass_cfunc, 1, :a=>1))
- assert_warn(/The last argument is used as the keyword parameter.* for `initialize'/m) do
- assert_equal([[1], h1], o.foo(:pass_cfunc, 1, :a=>1))
- end
+ assert_equal(:opt, o.clear_last_opt(a: 1))
+ assert_nothing_raised(ArgumentError) { o.clear_last_empty_method(a: 1) }
assert_warn(/Skipping set of ruby2_keywords flag for bar \(method accepts keywords or method does not accept argument splat\)/) do
assert_nil(c.send(:ruby2_keywords, :bar))
@@ -3142,25 +2759,14 @@ class TestKeywordArguments < Test::Unit::TestCase
end
assert_equal(c, [c].dig(0, **{}))
assert_equal(c, [c].dig(0, **kw))
- assert_warn(/The last argument is used as the keyword parameter.*for `dig'/m) do
- assert_equal(h, [c].dig(0, **h))
- end
- assert_warn(/The last argument is used as the keyword parameter.*for `dig'/m) do
- assert_equal(h, [c].dig(0, a: 1))
- end
- assert_warn(/The last argument is split into positional and keyword parameters.*for `dig'/m) do
- assert_raise(ArgumentError) { [c].dig(0, **h3) }
- end
- assert_warn(/The last argument is split into positional and keyword parameters.*for `dig'/m) do
- assert_raise(ArgumentError) { [c].dig(0, a: 1, **h2) }
- end
- assert_warn(/The last argument is used as the keyword parameter.*for `dig'/m) do
- assert_equal(h, [c].dig(0, h))
- end
+ assert_raise(ArgumentError) { [c].dig(0, **h) }
+ assert_raise(ArgumentError) { [c].dig(0, a: 1) }
+ assert_raise(ArgumentError) { [c].dig(0, **h2) }
+ assert_raise(ArgumentError) { [c].dig(0, **h3) }
+ assert_raise(ArgumentError) { [c].dig(0, a: 1, **h2) }
+ assert_raise(ArgumentError) { [c].dig(0, h) }
assert_raise(ArgumentError) { [c].dig(0, h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `dig'/m) do
- assert_raise(ArgumentError) { [c].dig(0, h3) }
- end
+ assert_raise(ArgumentError) { [c].dig(0, h3) }
c.singleton_class.remove_method(:dig)
def c.dig(arg, **args)
@@ -3180,26 +2786,14 @@ class TestKeywordArguments < Test::Unit::TestCase
end
assert_equal(c, [c].dig(0, **{}))
assert_equal(c, [c].dig(0, **kw))
- assert_warn(/The last argument is used as the keyword parameter.*for `dig'/m) do
- assert_equal([1, h], [c].dig(0, **h))
- end
- assert_warn(/The last argument is used as the keyword parameter.*for `dig'/m) do
- assert_equal([1, h], [c].dig(0, a: 1))
- end
+ assert_equal([h, kw], [c].dig(0, **h))
+ assert_equal([h, kw], [c].dig(0, a: 1))
assert_equal([h2, kw], [c].dig(0, **h2))
- assert_warn(/The last argument is split into positional and keyword parameters.*for `dig'/m) do
- assert_equal([h2, h], [c].dig(0, **h3))
- end
- assert_warn(/The last argument is split into positional and keyword parameters.*for `dig'/m) do
- assert_equal([h2, h], [c].dig(0, a: 1, **h2))
- end
- assert_warn(/The last argument is used as the keyword parameter.*for `dig'/m) do
- assert_equal([1, h], [c].dig(0, h))
- end
+ assert_equal([h3, kw], [c].dig(0, **h3))
+ assert_equal([h3, kw], [c].dig(0, a: 1, **h2))
+ assert_equal([h, {}], [c].dig(0, h))
assert_equal([h2, kw], [c].dig(0, h2))
- assert_warn(/The last argument is split into positional and keyword parameters.*for `dig'/m) do
- assert_equal([h2, h], [c].dig(0, h3))
- end
+ assert_equal([h3, kw], [c].dig(0, h3))
assert_equal([h, kw], [c].dig(0, h, **{}))
assert_equal([h2, kw], [c].dig(0, h2, **{}))
assert_equal([h3, kw], [c].dig(0, h3, **{}))
@@ -3252,25 +2846,14 @@ class TestKeywordArguments < Test::Unit::TestCase
end
assert_equal(c, [c].dig(0, **{}))
assert_equal(c, [c].dig(0, **kw))
- assert_warn(/The last argument is used as the keyword parameter.*for `method_missing'/m) do
- assert_equal(h, [c].dig(0, **h))
- end
- assert_warn(/The last argument is used as the keyword parameter.*for `method_missing'/m) do
- assert_equal(h, [c].dig(0, a: 1))
- end
- assert_warn(/The last argument is split into positional and keyword parameters.*for `method_missing'/m) do
- assert_raise(ArgumentError) { [c].dig(0, **h3) }
- end
- assert_warn(/The last argument is split into positional and keyword parameters.*for `method_missing'/m) do
- assert_raise(ArgumentError) { [c].dig(0, a: 1, **h2) }
- end
- assert_warn(/The last argument is used as the keyword parameter.*for `method_missing'/m) do
- assert_equal(h, [c].dig(0, h))
- end
+ assert_raise(ArgumentError) { [c].dig(0, **h) }
+ assert_raise(ArgumentError) { [c].dig(0, a: 1) }
+ assert_raise(ArgumentError) { [c].dig(0, **h2) }
+ assert_raise(ArgumentError) { [c].dig(0, **h3) }
+ assert_raise(ArgumentError) { [c].dig(0, a: 1, **h2) }
+ assert_raise(ArgumentError) { [c].dig(0, h) }
assert_raise(ArgumentError) { [c].dig(0, h2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `method_missing'/m) do
- assert_raise(ArgumentError) { [c].dig(0, h3) }
- end
+ assert_raise(ArgumentError) { [c].dig(0, h3) }
c.singleton_class.remove_method(:method_missing)
def c.method_missing(_, arg, **args)
@@ -3290,26 +2873,14 @@ class TestKeywordArguments < Test::Unit::TestCase
end
assert_equal(c, [c].dig(0, **{}))
assert_equal(c, [c].dig(0, **kw))
- assert_warn(/The last argument is used as the keyword parameter.*for `method_missing'/m) do
- assert_equal([1, h], [c].dig(0, **h))
- end
- assert_warn(/The last argument is used as the keyword parameter.*for `method_missing'/m) do
- assert_equal([1, h], [c].dig(0, a: 1))
- end
+ assert_equal([h, kw], [c].dig(0, **h))
+ assert_equal([h, kw], [c].dig(0, a: 1))
assert_equal([h2, kw], [c].dig(0, **h2))
- assert_warn(/The last argument is split into positional and keyword parameters.*for `method_missing'/m) do
- assert_equal([h2, h], [c].dig(0, **h3))
- end
- assert_warn(/The last argument is split into positional and keyword parameters.*for `method_missing'/m) do
- assert_equal([h2, h], [c].dig(0, a: 1, **h2))
- end
- assert_warn(/The last argument is used as the keyword parameter.*for `method_missing'/m) do
- assert_equal([1, h], [c].dig(0, h))
- end
+ assert_equal([h3, kw], [c].dig(0, **h3))
+ assert_equal([h3, kw], [c].dig(0, a: 1, **h2))
+ assert_equal([h, kw], [c].dig(0, h))
assert_equal([h2, kw], [c].dig(0, h2))
- assert_warn(/The last argument is split into positional and keyword parameters.*for `method_missing'/m) do
- assert_equal([h2, h], [c].dig(0, h3))
- end
+ assert_equal([h3, kw], [c].dig(0, h3))
assert_equal([h, kw], [c].dig(0, h, **{}))
assert_equal([h2, kw], [c].dig(0, h2, **{}))
assert_equal([h3, kw], [c].dig(0, h3, **{}))
@@ -3342,12 +2913,8 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_raise(ArgumentError) { c.to_enum(:each, a: 1, **h2, &m).size }
m = ->(args){ args }
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, c.to_enum(:each, **{}, &m).size)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal(kw, c.to_enum(:each, **kw, &m).size)
- end
+ assert_raise(ArgumentError) { c.to_enum(:each, **{}, &m).size }
+ assert_raise(ArgumentError) { c.to_enum(:each, **kw, &m).size }
assert_equal(kw, c.to_enum(:each, kw, **kw, &m).size)
assert_equal(h, c.to_enum(:each, **h, &m).size)
assert_equal(h, c.to_enum(:each, a: 1, &m).size)
@@ -3363,36 +2930,18 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.to_enum(:each, **h2, &m).size)
assert_equal(h3, c.to_enum(:each, **h3, &m).size)
assert_equal(h3, c.to_enum(:each, a: 1, **h2, &m).size)
- assert_warn(/The last argument is used as the keyword parameter/m) do
- assert_equal(h, c.to_enum(:each, h, &m).size)
- end
+ assert_raise(ArgumentError) { c.to_enum(:each, h, &m).size }
assert_raise(ArgumentError) { c.to_enum(:each, h2, &m).size }
- assert_warn(/The last argument is split into positional and keyword parameters/m) do
- assert_raise(ArgumentError) { c.to_enum(:each, h3, &m).size }
- end
+ assert_raise(ArgumentError) { c.to_enum(:each, h3, &m).size }
m = ->(arg, **args){ [arg, args] }
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- c.to_enum(:each, **{}, &m).size
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- c.to_enum(:each, **kw, &m).size
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h, kw], c.to_enum(:each, **h, &m).size)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h, kw], c.to_enum(:each, a: 1, &m).size)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h2, kw], c.to_enum(:each, **h2, &m).size)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h3, kw], c.to_enum(:each, **h3, &m).size)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/m) do
- assert_equal([h3, kw], c.to_enum(:each, a: 1, **h2, &m).size)
- end
+ assert_raise(ArgumentError) { c.to_enum(:each, **{}, &m).size }
+ assert_raise(ArgumentError) { c.to_enum(:each, **kw, &m).size }
+ assert_raise(ArgumentError) { c.to_enum(:each, **h, &m).size }
+ assert_raise(ArgumentError) { c.to_enum(:each, a: 1, &m).size }
+ assert_raise(ArgumentError) { c.to_enum(:each, **h2, &m).size }
+ assert_raise(ArgumentError) { c.to_enum(:each, **h3, &m).size }
+ assert_raise(ArgumentError) { c.to_enum(:each, a: 1, **h2, &m).size }
assert_equal([h, kw], c.to_enum(:each, h, &m).size)
assert_equal([h2, kw], c.to_enum(:each, h2, &m).size)
assert_equal([h3, kw], c.to_enum(:each, h3, &m).size)
@@ -3405,13 +2954,9 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, h2], c.to_enum(:each, **h2, &m).size)
assert_equal([1, h3], c.to_enum(:each, **h3, &m).size)
assert_equal([1, h3], c.to_enum(:each, a: 1, **h2, &m).size)
- assert_warn(/The last argument is used as the keyword parameter/m) do
- assert_equal([1, h], c.to_enum(:each, h, &m).size)
- end
+ assert_equal([h, kw], c.to_enum(:each, h, &m).size)
assert_equal([h2, kw], c.to_enum(:each, h2, &m).size)
- assert_warn(/The last argument is split into positional and keyword parameters/m) do
- assert_equal([h2, h], c.to_enum(:each, h3, &m).size)
- end
+ assert_equal([h3, kw], c.to_enum(:each, h3, &m).size)
end
def test_instance_exec_kwsplat
@@ -3440,12 +2985,8 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_raise(ArgumentError) { c.instance_exec(a: 1, **h2, &m) }
m = ->(args) { args }
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal(kw, c.instance_exec(**{}, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal(kw, c.instance_exec(**kw, &m))
- end
+ assert_raise(ArgumentError) { c.instance_exec(**{}, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**kw, &m) }
assert_equal(kw, c.instance_exec(kw, **kw, &m))
assert_equal(h, c.instance_exec(**h, &m))
assert_equal(h, c.instance_exec(a: 1, &m))
@@ -3461,36 +3002,18 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.instance_exec(**h2, &m))
assert_equal(h3, c.instance_exec(**h3, &m))
assert_equal(h3, c.instance_exec(a: 1, **h2, &m))
- assert_warn(/The last argument is used as the keyword parameter/) do
- assert_equal(h, c.instance_exec(h, &m))
- end
+ assert_raise(ArgumentError) { c.instance_exec(h, &m) }
assert_raise(ArgumentError) { c.instance_exec(h2, &m) }
- assert_warn(/The last argument is split into positional and keyword parameters/) do
- assert_raise(ArgumentError) { c.instance_exec(h3, &m) }
- end
+ assert_raise(ArgumentError) { c.instance_exec(h3, &m) }
m = ->(arg, **args) { [arg, args] }
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- c.instance_exec(**{}, &m)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- c.instance_exec(**kw, &m)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, kw], c.instance_exec(**h, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, kw], c.instance_exec(a: 1, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h2, kw], c.instance_exec(**h2, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, kw], c.instance_exec(**h3, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, kw], c.instance_exec(a: 1, **h2, &m))
- end
+ assert_raise(ArgumentError) { c.instance_exec(**{}, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**kw, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**h, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(a: 1, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**h2, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**h3, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(a: 1, **h2, &m) }
assert_equal([h, kw], c.instance_exec(h, &m))
assert_equal([h2, kw], c.instance_exec(h2, &m))
assert_equal([h3, kw], c.instance_exec(h3, &m))
@@ -3503,13 +3026,9 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, h2], c.instance_exec(**h2, &m))
assert_equal([1, h3], c.instance_exec(**h3, &m))
assert_equal([1, h3], c.instance_exec(a: 1, **h2, &m))
- assert_warn(/The last argument is used as the keyword parameter/m) do
- assert_equal([1, h], c.instance_exec(h, &m))
- end
+ assert_equal([h, kw], c.instance_exec(h, &m))
assert_equal([h2, kw], c.instance_exec(h2, &m))
- assert_warn(/The last argument is split into positional and keyword parameters/m) do
- assert_equal([h2, h], c.instance_exec(h3, &m))
- end
+ assert_equal([h3, kw], c.instance_exec(h3, &m))
end
def test_instance_exec_method_kwsplat
@@ -3548,12 +3067,8 @@ class TestKeywordArguments < Test::Unit::TestCase
args
end
m = c.method(:m)
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal(kw, c.instance_exec(**{}, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal(kw, c.instance_exec(**kw, &m))
- end
+ assert_raise(ArgumentError) { c.instance_exec(**{}, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**kw, &m) }
assert_equal(kw, c.instance_exec(kw, **kw, &m))
assert_equal(h, c.instance_exec(**h, &m))
assert_equal(h, c.instance_exec(a: 1, &m))
@@ -3573,40 +3088,22 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.instance_exec(**h2, &m))
assert_equal(h3, c.instance_exec(**h3, &m))
assert_equal(h3, c.instance_exec(a: 1, **h2, &m))
- assert_warn(/The last argument is used as the keyword parameter/) do
- assert_equal(h, c.instance_exec(h, &m))
- end
+ assert_raise(ArgumentError) { c.instance_exec(h, &m) }
assert_raise(ArgumentError) { c.instance_exec(h2, &m) }
- assert_warn(/The last argument is split into positional and keyword parameters/) do
- assert_raise(ArgumentError) { c.instance_exec(h3, &m) }
- end
+ assert_raise(ArgumentError) { c.instance_exec(h3, &m) }
c.singleton_class.remove_method(:m)
def c.m(arg, **args)
[arg, args]
end
m = c.method(:m)
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- c.instance_exec(**{}, &m)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- c.instance_exec(**kw, &m)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, kw], c.instance_exec(**h, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, kw], c.instance_exec(a: 1, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h2, kw], c.instance_exec(**h2, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, kw], c.instance_exec(**h3, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, kw], c.instance_exec(a: 1, **h2, &m))
- end
+ assert_raise(ArgumentError) { c.instance_exec(**{}, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**kw, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**h, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(a: 1, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**h2, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**h3, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(a: 1, **h2, &m) }
assert_equal([h, kw], c.instance_exec(h, &m))
assert_equal([h2, kw], c.instance_exec(h2, &m))
assert_equal([h3, kw], c.instance_exec(h3, &m))
@@ -3623,13 +3120,9 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, h2], c.instance_exec(**h2, &m))
assert_equal([1, h3], c.instance_exec(**h3, &m))
assert_equal([1, h3], c.instance_exec(a: 1, **h2, &m))
- assert_warn(/The last argument is used as the keyword parameter/m) do
- assert_equal([1, h], c.instance_exec(h, &m))
- end
+ assert_equal([h, kw], c.instance_exec(h, &m))
assert_equal([h2, kw], c.instance_exec(h2, &m))
- assert_warn(/The last argument is split into positional and keyword parameters/) do
- assert_equal([h2, h], c.instance_exec(h3, &m))
- end
+ assert_equal([h3, kw], c.instance_exec(h3, &m))
end
def test_instance_exec_define_method_kwsplat
@@ -3668,12 +3161,8 @@ class TestKeywordArguments < Test::Unit::TestCase
args
end
m = c.method(:m)
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal(kw, c.instance_exec(**{}, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal(kw, c.instance_exec(**kw, &m))
- end
+ assert_raise(ArgumentError) { c.instance_exec(**{}, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**kw, &m) }
assert_equal(kw, c.instance_exec(kw, **kw, &m))
assert_equal(h, c.instance_exec(**h, &m))
assert_equal(h, c.instance_exec(a: 1, &m))
@@ -3693,40 +3182,22 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.instance_exec(**h2, &m))
assert_equal(h3, c.instance_exec(**h3, &m))
assert_equal(h3, c.instance_exec(a: 1, **h2, &m))
- assert_warn(/The last argument is used as the keyword parameter/) do
- assert_equal(h, c.instance_exec(h, &m))
- end
+ assert_raise(ArgumentError) { c.instance_exec(h, &m) }
assert_raise(ArgumentError) { c.instance_exec(h2, &m) }
- assert_warn(/The last argument is split into positional and keyword parameters/) do
- assert_raise(ArgumentError) { c.instance_exec(h3, &m) }
- end
+ assert_raise(ArgumentError) { c.instance_exec(h3, &m) }
c.singleton_class.remove_method(:m)
c.define_singleton_method(:m) do |arg, **args|
[arg, args]
end
m = c.method(:m)
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- c.instance_exec(**{}, &m)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- c.instance_exec(**kw, &m)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, kw], c.instance_exec(**h, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, kw], c.instance_exec(a: 1, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h2, kw], c.instance_exec(**h2, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, kw], c.instance_exec(**h3, &m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, kw], c.instance_exec(a: 1, **h2, &m))
- end
+ assert_raise(ArgumentError) { c.instance_exec(**{}, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**kw, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**h, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(a: 1, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**h2, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(**h3, &m) }
+ assert_raise(ArgumentError) { c.instance_exec(a: 1, **h2, &m) }
assert_equal([h, kw], c.instance_exec(h, &m))
assert_equal([h2, kw], c.instance_exec(h2, &m))
assert_equal([h3, kw], c.instance_exec(h3, &m))
@@ -3743,13 +3214,9 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, h2], c.instance_exec(**h2, &m))
assert_equal([1, h3], c.instance_exec(**h3, &m))
assert_equal([1, h3], c.instance_exec(a: 1, **h2, &m))
- assert_warn(/The last argument is used as the keyword parameter/m) do
- assert_equal([1, h], c.instance_exec(h, &m))
- end
+ assert_equal([h, kw], c.instance_exec(h, &m))
assert_equal([h2, kw], c.instance_exec(h2, &m))
- assert_warn(/The last argument is split into positional and keyword parameters/) do
- assert_equal([h2, h], c.instance_exec(h3, &m))
- end
+ assert_equal([h3, kw], c.instance_exec(h3, &m))
end
def test_instance_exec_sym_proc_kwsplat
@@ -3785,12 +3252,8 @@ class TestKeywordArguments < Test::Unit::TestCase
def c.m(args)
args
end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal(kw, c.instance_exec(c, **{}, &:m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal(kw, c.instance_exec(c, **kw, &:m))
- end
+ assert_raise(ArgumentError) { c.instance_exec(c, **{}, &:m) }
+ assert_raise(ArgumentError) { c.instance_exec(c, **kw, &:m) }
assert_equal(kw, c.instance_exec(c, kw, **kw, &:m))
assert_equal(h, c.instance_exec(c, **h, &:m))
assert_equal(h, c.instance_exec(c, a: 1, &:m))
@@ -3809,39 +3272,21 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal(h2, c.instance_exec(c, **h2, &:m))
assert_equal(h3, c.instance_exec(c, **h3, &:m))
assert_equal(h3, c.instance_exec(c, a: 1, **h2, &:m))
- assert_warn(/The last argument is used as the keyword parameter/) do
- assert_equal(h, c.instance_exec(c, h, &:m))
- end
+ assert_raise(ArgumentError) { c.instance_exec(c, h, &:m) }
assert_raise(ArgumentError) { c.instance_exec(c, h2, &:m) }
- assert_warn(/The last argument is split into positional and keyword parameters/) do
- assert_raise(ArgumentError) { c.instance_exec(c, h3, &:m) }
- end
+ assert_raise(ArgumentError) { c.instance_exec(c, h3, &:m) }
c.singleton_class.remove_method(:m)
def c.m(arg, **args)
[arg, args]
end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- c.instance_exec(c, **{}, &:m)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- c.instance_exec(c, **kw, &:m)
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, kw], c.instance_exec(c, **h, &:m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h, kw], c.instance_exec(c, a: 1, &:m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h2, kw], c.instance_exec(c, **h2, &:m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, kw], c.instance_exec(c, **h3, &:m))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter/) do
- assert_equal([h3, kw], c.instance_exec(c, a: 1, **h2, &:m))
- end
+ assert_raise(ArgumentError) { c.instance_exec(c, **{}, &:m) }
+ assert_raise(ArgumentError) { c.instance_exec(c, **kw, &:m) }
+ assert_raise(ArgumentError) { c.instance_exec(c, **h, &:m) }
+ assert_raise(ArgumentError) { c.instance_exec(c, a: 1, &:m) }
+ assert_raise(ArgumentError) { c.instance_exec(c, **h2, &:m) }
+ assert_raise(ArgumentError) { c.instance_exec(c, **h3, &:m) }
+ assert_raise(ArgumentError) { c.instance_exec(c, a: 1, **h2, &:m) }
assert_equal([h, kw], c.instance_exec(c, h, &:m))
assert_equal([h2, kw], c.instance_exec(c, h2, &:m))
assert_equal([h3, kw], c.instance_exec(c, h3, &:m))
@@ -3857,13 +3302,9 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, h2], c.instance_exec(c, **h2, &:m))
assert_equal([1, h3], c.instance_exec(c, **h3, &:m))
assert_equal([1, h3], c.instance_exec(c, a: 1, **h2, &:m))
- assert_warn(/The last argument is used as the keyword parameter/m) do
- assert_equal([1, h], c.instance_exec(c, h, &:m))
- end
+ assert_equal([h, kw], c.instance_exec(c, h, &:m))
assert_equal([h2, kw], c.instance_exec(c, h2, &:m))
- assert_warn(/The last argument is split into positional and keyword parameters/) do
- assert_equal([h2, h], c.instance_exec(c, h3, &:m))
- end
+ assert_equal([h3, kw], c.instance_exec(c, h3, &:m))
end
def test_rb_yield_block_kwsplat
@@ -3902,12 +3343,8 @@ class TestKeywordArguments < Test::Unit::TestCase
def c.c(args)
args
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do
- assert_equal(kw, c.m(:c, **{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do
- assert_equal(kw, c.m(:c, **kw))
- end
+ assert_raise(ArgumentError) { c.m(:c, **{}) }
+ assert_raise(ArgumentError) { c.m(:c, **kw) }
assert_equal(kw, c.m(:c, kw, **kw))
assert_equal(h, c.m(:c, **h))
assert_equal(h, c.m(:c, a: 1))
@@ -3927,39 +3364,21 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([h2, h2], c.m(:c, **h2, &m))
assert_equal([h3, h3], c.m(:c, **h3, &m))
assert_equal([h3, h3], c.m(:c, a: 1, **h2, &m))
- assert_warn(/The last argument is used as the keyword parameter.*for `c'/m) do
- assert_equal([h, h], c.m(:c, h, &m))
- end
+ assert_raise(ArgumentError) { c.m(:c, h, &m) }
assert_raise(ArgumentError) { c.m(:c, h2, &m) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `c'/m) do
- assert_raise(ArgumentError) { c.m(:c, h3, &m) }
- end
+ assert_raise(ArgumentError) { c.m(:c, h3, &m) }
c.singleton_class.remove_method(:c)
def c.c(arg, **args)
[arg, args]
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do
- assert_equal([kw, kw], c.m(:c, **{}))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do
- assert_equal([kw, kw], c.m(:c, **kw))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do
- assert_equal([h, kw], c.m(:c, **h))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do
- assert_equal([h, kw], c.m(:c, a: 1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do
- assert_equal([h2, kw], c.m(:c, **h2))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do
- assert_equal([h3, kw], c.m(:c, **h3))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `c'/m) do
- assert_equal([h3, kw], c.m(:c, a: 1, **h2))
- end
+ assert_raise(ArgumentError) { c.m(:c, **{}, &m) }
+ assert_raise(ArgumentError) { c.m(:c, **kw, &m) }
+ assert_raise(ArgumentError) { c.m(:c, **h, &m) }
+ assert_raise(ArgumentError) { c.m(:c, a: 1, &m) }
+ assert_raise(ArgumentError) { c.m(:c, **h2, &m) }
+ assert_raise(ArgumentError) { c.m(:c, **h3, &m) }
+ assert_raise(ArgumentError) { c.m(:c, a: 1, **h2, &m) }
assert_equal([h, kw], c.m(:c, h))
assert_equal([h2, kw], c.m(:c, h2))
assert_equal([h3, kw], c.m(:c, h3))
@@ -3975,13 +3394,9 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, h2], c.m(:c, **h2))
assert_equal([1, h3], c.m(:c, **h3))
assert_equal([1, h3], c.m(:c, a: 1, **h2))
- assert_warn(/The last argument is used as the keyword parameter.*for `c'/m) do
- assert_equal([1, h], c.m(:c, h))
- end
+ assert_equal([h, kw], c.m(:c, h))
assert_equal([h2, kw], c.m(:c, h2))
- assert_warn(/The last argument is split into positional and keyword parameters.*for `c'/m) do
- assert_equal([h2, h], c.m(:c, h3))
- end
+ assert_equal([h3, kw], c.m(:c, h3))
end
def p1
@@ -4109,25 +3524,21 @@ class TestKeywordArguments < Test::Unit::TestCase
def test_rest_keyrest
bug7665 = '[ruby-core:51278]'
bug8463 = '[ruby-core:55203] [Bug #8463]'
- expect = [*%w[foo bar], {zzz: 42}]
- assert_warn(/The last argument is used as the keyword parameter.* for `rest_keyrest'/m) do
- assert_equal(expect, rest_keyrest(*expect), bug7665)
- end
+ a = [*%w[foo bar], {zzz: 42}]
+ splat_expect = a + [{}]
+ nonsplat_expect = [a, {}]
+ assert_equal(splat_expect, rest_keyrest(*a), bug7665)
+ assert_equal(nonsplat_expect, rest_keyrest(a), bug7665)
+
pr = proc {|*args, **opt| next *args, opt}
- assert_warn(/The last argument is used as the keyword parameter.* for `call'/m) do
- assert_equal(expect, pr.call(*expect), bug7665)
- end
- assert_warn(/The last argument is used as the keyword parameter.* for `call'/m) do
- assert_equal(expect, pr.call(expect), bug8463)
- end
+ assert_equal(splat_expect, pr.call(*a), bug7665)
+ assert_equal(nonsplat_expect, pr.call(a), bug8463)
+
pr = proc {|a, *b, **opt| next a, *b, opt}
- assert_warn(/The last argument is used as the keyword parameter.* for `call'/m) do
- assert_equal(expect, pr.call(expect), bug8463)
- end
+ assert_equal(splat_expect, pr.call(a), bug8463)
+
pr = proc {|a, **opt| next a, opt}
- assert_warn(/The last argument is used as the keyword parameter.* for `call'/m) do
- assert_equal(expect.values_at(0, -1), pr.call(expect), bug8463)
- end
+ assert_equal(splat_expect.values_at(0, -1), pr.call(splat_expect), bug8463)
end
def req_plus_keyword(x, **h)
@@ -4142,16 +3553,10 @@ class TestKeywordArguments < Test::Unit::TestCase
[a, h]
end
- def test_keyword_split
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `req_plus_keyword'/m) do
- assert_equal([{:a=>1}, {}], req_plus_keyword(:a=>1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `req_plus_keyword'/m) do
- assert_equal([{"a"=>1}, {}], req_plus_keyword("a"=>1))
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `req_plus_keyword'/m) do
- assert_equal([{"a"=>1, :a=>1}, {}], req_plus_keyword("a"=>1, :a=>1))
- end
+ def test_keyword_no_split
+ assert_raise(ArgumentError) { req_plus_keyword(:a=>1) }
+ assert_raise(ArgumentError) { req_plus_keyword("a"=>1) }
+ assert_raise(ArgumentError) { req_plus_keyword("a"=>1, :a=>1) }
assert_equal([{:a=>1}, {}], req_plus_keyword({:a=>1}))
assert_equal([{"a"=>1}, {}], req_plus_keyword({"a"=>1}))
assert_equal([{"a"=>1, :a=>1}, {}], req_plus_keyword({"a"=>1, :a=>1}))
@@ -4159,24 +3564,16 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, {:a=>1}], opt_plus_keyword(:a=>1))
assert_equal([1, {"a"=>1}], opt_plus_keyword("a"=>1))
assert_equal([1, {"a"=>1, :a=>1}], opt_plus_keyword("a"=>1, :a=>1))
- assert_warn(/The last argument is used as the keyword parameter.* for `opt_plus_keyword'/m) do
- assert_equal([1, {:a=>1}], opt_plus_keyword({:a=>1}))
- end
+ assert_equal([{:a=>1}, {}], opt_plus_keyword({:a=>1}))
assert_equal([{"a"=>1}, {}], opt_plus_keyword({"a"=>1}))
- assert_warn(/The last argument is split into positional and keyword parameters.* for `opt_plus_keyword'/m) do
- assert_equal([{"a"=>1}, {:a=>1}], opt_plus_keyword({"a"=>1, :a=>1}))
- end
+ assert_equal([{"a"=>1, :a=>1}, {}], opt_plus_keyword({"a"=>1, :a=>1}))
assert_equal([[], {:a=>1}], splat_plus_keyword(:a=>1))
assert_equal([[], {"a"=>1}], splat_plus_keyword("a"=>1))
assert_equal([[], {"a"=>1, :a=>1}], splat_plus_keyword("a"=>1, :a=>1))
- assert_warn(/The last argument is used as the keyword parameter.* for `splat_plus_keyword'/m) do
- assert_equal([[], {:a=>1}], splat_plus_keyword({:a=>1}))
- end
+ assert_equal([[{:a=>1}], {}], splat_plus_keyword({:a=>1}))
assert_equal([[{"a"=>1}], {}], splat_plus_keyword({"a"=>1}))
- assert_warn(/The last argument is split into positional and keyword parameters.* for `splat_plus_keyword'/m) do
- assert_equal([[{"a"=>1}], {:a=>1}], splat_plus_keyword({"a"=>1, :a=>1}))
- end
+ assert_equal([[{"a"=>1, :a=>1}], {}], splat_plus_keyword({"a"=>1, :a=>1}))
end
def test_bare_kwrest
@@ -4313,6 +3710,25 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([42, {:bar=>"x"}], b.new.foo(42), bug8236)
end
+ def test_super_with_keyword_kwrest
+ base = Class.new do
+ def foo(**h)
+ h
+ end
+ end
+ a = Class.new(base) do
+ attr_reader :h
+ def foo(a:, b:, **h)
+ @h = h
+ super
+ end
+ end
+
+ o = a.new
+ assert_equal({a: 1, b: 2, c: 3}, o.foo(a: 1, b: 2, c: 3))
+ assert_equal({c: 3}, o.h)
+ end
+
def test_zsuper_only_named_kwrest
bug8416 = '[ruby-core:55033] [Bug #8416]'
base = Class.new do
@@ -4321,11 +3737,15 @@ class TestKeywordArguments < Test::Unit::TestCase
end
end
a = Class.new(base) do
+ attr_reader :h
def foo(**h)
+ @h = h
super
end
end
- assert_equal({:bar=>"x"}, a.new.foo(bar: "x"), bug8416)
+ o = a.new
+ assert_equal({:bar=>"x"}, o.foo(bar: "x"), bug8416)
+ assert_equal({:bar=>"x"}, o.h)
end
def test_zsuper_only_anonymous_kwrest
@@ -4355,15 +3775,12 @@ class TestKeywordArguments < Test::Unit::TestCase
end
def test_precedence_of_keyword_arguments_with_post_argument
- bug8993 = '[ruby-core:57706] [Bug #8993]'
a = Class.new do
def foo(a, b, c=1, *d, e, f:2, **g)
[a, b, c, d, e, f, g]
end
end
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `foo'/m) do
- assert_equal([1, 2, 1, [], {:f=>5}, 2, {}], a.new.foo(1, 2, f:5), bug8993)
- end
+ assert_raise(ArgumentError) { a.new.foo(1, 2, f:5) }
end
def test_splat_keyword_nondestructive
@@ -4390,18 +3807,12 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal({a: 1, b: 2}, m1(**o, **o2) {|x| break x}, bug9898)
end
- def test_implicit_hash_conversion
- bug10016 = '[ruby-core:63593] [Bug #10016]'
-
+ def test_no_implicit_hash_conversion
o = Object.new
def o.to_hash() { k: 9 } end
assert_equal([1, 42, [], o, :key, {}, nil], f9(1, o))
- assert_warn(/The last argument is used as the keyword parameter.* for `m1'/m) do
- assert_equal([1, 9], m1(1, o) {|a, k: 0| break [a, k]}, bug10016)
- end
- assert_warn(/The last argument is used as the keyword parameter.* for `m1'/m) do
- assert_equal([1, 9], m1(1, o, &->(a, k: 0) {break [a, k]}), bug10016)
- end
+ assert_equal([1, 0], m1(1, o) {|a, k: 0| break [a, k]})
+ assert_raise(ArgumentError) { m1(1, o, &->(a, k: 0) {break [a, k]}) }
end
def test_splat_hash
@@ -4412,7 +3823,7 @@ class TestKeywordArguments < Test::Unit::TestCase
def m.f3(**a) a; end
def m.f4(*a) a; end
o = {a: 1}
- assert_raise_with_message(ArgumentError, /unknown keyword: :a/) {
+ assert_raise_with_message(ArgumentError, /wrong number of arguments \(given 1, expected 0\)/) {
m.f(**o)
}
o = {}
@@ -4520,7 +3931,7 @@ class TestKeywordArguments < Test::Unit::TestCase
def test_nonsymbol_key
result = m(["a" => 10]) { |a = nil, **b| [a, b] }
- assert_equal([{"a" => 10}, {}], result)
+ assert_equal([[{"a" => 10}], {}], result)
end
def method_for_test_to_hash_call_during_setup_complex_parameters k1:, k2:, **rest_kw
@@ -4654,6 +4065,12 @@ class TestKeywordArguments < Test::Unit::TestCase
def test_splat_empty_hash_with_block_passing
assert_valid_syntax("bug15087(**{}, &nil)")
end
+
+ def test_do_not_use_newarraykwsplat
+ assert_equal([42, "foo", 424242], f2(*[], 42, **{}))
+ a = [1, 2, 3]
+ assert_equal([[1,2,3,4,5,6], "foo", 424242, {:k=>:k}], f7(*a, 4,5,6, k: :k))
+ end
end
class TestKeywordArgumentsSymProcRefinements < Test::Unit::TestCase
@@ -4699,22 +4116,12 @@ class TestKeywordArgumentsSymProcRefinements < Test::Unit::TestCase
assert_raise(ArgumentError) { c.call(**h3, &:m) }
assert_raise(ArgumentError) { c.call(a: 1, **h2, &:m) }
- redef = -> do
- c.singleton_class.remove_method(:m)
- eval <<-END
- def c.m(args)
- args
- end
- END
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, c.call(**{}, &:m))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal(kw, c.call(**kw, &:m))
+ c.singleton_class.remove_method(:m)
+ def c.m(args)
+ args
end
+ assert_raise(ArgumentError) { c.call(**{}, &:m) }
+ assert_raise(ArgumentError) { c.call(**kw, &:m) }
assert_equal(h, c.call(**h, &:m))
assert_equal(h, c.call(a: 1, &:m))
assert_equal(h2, c.call(**h2, &:m))
@@ -4732,50 +4139,21 @@ class TestKeywordArgumentsSymProcRefinements < Test::Unit::TestCase
assert_equal(h2, c.call(**h2, &:m))
assert_equal(h3, c.call(**h3, &:m))
assert_equal(h3, c.call(a: 1, **h2, &:m))
- assert_warn(/The last argument is used as the keyword parameter.*for `call'/m) do
- assert_equal(h, c.call(h, &:m))
- end
+ assert_raise(ArgumentError) { c.call(h, &:m) }
assert_raise(ArgumentError) { c.call(h2, &:m) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `call'/m) do
- assert_raise(ArgumentError) { c.call(h3, &:m) }
- end
+ assert_raise(ArgumentError) { c.call(h3, &:m) }
- redef = -> do
- c.singleton_class.remove_method(:m)
- eval <<-END
- def c.m(arg, **args)
- [arg, args]
- end
- END
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([kw, kw], c.call(**{}, &:m))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([kw, kw], c.call(**kw, &:m))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], c.call(**h, &:m))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h, kw], c.call(a: 1, &:m))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h2, kw], c.call(**h2, &:m))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], c.call(**h3, &:m))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
- assert_equal([h3, kw], c.call(a: 1, **h2, &:m))
+ c.singleton_class.remove_method(:m)
+ def c.m(arg, **args)
+ [arg, args]
end
+ assert_raise(ArgumentError) { c.call(**{}, &:m) }
+ assert_raise(ArgumentError) { c.call(**kw, &:m) }
+ assert_raise(ArgumentError) { c.call(**h, &:m) }
+ assert_raise(ArgumentError) { c.call(a: 1, &:m) }
+ assert_raise(ArgumentError) { c.call(**h2, &:m) }
+ assert_raise(ArgumentError) { c.call(**h3, &:m) }
+ assert_raise(ArgumentError) { c.call(a: 1, **h2, &:m) }
c.singleton_class.remove_method(:m)
def c.m(arg=1, **args)
@@ -4819,22 +4197,12 @@ class TestKeywordArgumentsSymProcRefinements < Test::Unit::TestCase
assert_raise(ArgumentError) { c.call(**h3, &:m) }
assert_raise(ArgumentError) { c.call(a: 1, **h2, &:m) }
- redef = -> do
- c.singleton_class.remove_method(:method_missing)
- eval <<-END
- def c.method_missing(_, args)
- args
- end
- END
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal(kw, c.call(**{}, &:m))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal(kw, c.call(**kw, &:m))
+ c.singleton_class.remove_method(:method_missing)
+ def c.method_missing(_, args)
+ args
end
+ assert_raise(ArgumentError) { c.call(**{}, &:m) }
+ assert_raise(ArgumentError) { c.call(**kw, &:m) }
assert_equal(h, c.call(**h, &:m))
assert_equal(h, c.call(a: 1, &:m))
assert_equal(h2, c.call(**h2, &:m))
@@ -4852,50 +4220,21 @@ class TestKeywordArgumentsSymProcRefinements < Test::Unit::TestCase
assert_equal(h2, c.call(**h2, &:m))
assert_equal(h3, c.call(**h3, &:m))
assert_equal(h3, c.call(a: 1, **h2, &:m))
- assert_warn(/The last argument is used as the keyword parameter.*for `call'/m) do
- assert_equal(h, c.call(h, &:m2))
- end
+ assert_raise(ArgumentError) { c.call(h, &:m2) }
assert_raise(ArgumentError) { c.call(h2, &:m2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `call'/m) do
- assert_raise(ArgumentError) { c.call(h3, &:m2) }
- end
+ assert_raise(ArgumentError) { c.call(h3, &:m2) }
- redef = -> do
- c.singleton_class.remove_method(:method_missing)
- eval <<-END
- def c.method_missing(_, arg, **args)
- [arg, args]
- end
- END
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([kw, kw], c.call(**{}, &:m))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([kw, kw], c.call(**kw, &:m))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h, kw], c.call(**h, &:m))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h, kw], c.call(a: 1, &:m))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h2, kw], c.call(**h2, &:m))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h3, kw], c.call(**h3, &:m))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h3, kw], c.call(a: 1, **h2, &:m))
+ c.singleton_class.remove_method(:method_missing)
+ def c.method_missing(_, arg, **args)
+ [arg, args]
end
+ assert_raise(ArgumentError) { c.call(**{}, &:m2) }
+ assert_raise(ArgumentError) { c.call(**kw, &:m2) }
+ assert_raise(ArgumentError) { c.call(**h, &:m2) }
+ assert_raise(ArgumentError) { c.call(a: 1, &:m2) }
+ assert_raise(ArgumentError) { c.call(**h2, &:m2) }
+ assert_raise(ArgumentError) { c.call(**h3, &:m2) }
+ assert_raise(ArgumentError) { c.call(a: 1, **h2, &:m2) }
c.singleton_class.remove_method(:method_missing)
def c.method_missing(_, arg=1, **args)
@@ -4939,22 +4278,12 @@ class TestKeywordArgumentsSymProcRefinements < Test::Unit::TestCase
assert_raise(ArgumentError) { c.call(**h3, &:m2) }
assert_raise(ArgumentError) { c.call(a: 1, **h2, &:m2) }
- redef = -> do
- c.singleton_class.remove_method(:method_missing)
- eval <<-END
- def c.method_missing(_, args)
- args
- end
- END
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal(kw, c.call(**{}, &:m2))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal(kw, c.call(**kw, &:m2))
+ c.singleton_class.remove_method(:method_missing)
+ def c.method_missing(_, args)
+ args
end
+ assert_raise(ArgumentError) { c.call(**{}, &:m2) }
+ assert_raise(ArgumentError) { c.call(**kw, &:m2) }
assert_equal(h, c.call(**h, &:m2))
assert_equal(h, c.call(a: 1, &:m2))
assert_equal(h2, c.call(**h2, &:m2))
@@ -4972,50 +4301,21 @@ class TestKeywordArgumentsSymProcRefinements < Test::Unit::TestCase
assert_equal(h2, c.call(**h2, &:m2))
assert_equal(h3, c.call(**h3, &:m2))
assert_equal(h3, c.call(a: 1, **h2, &:m2))
- assert_warn(/The last argument is used as the keyword parameter.*for `call'/m) do
- assert_equal(h, c.call(h, &:m2))
- end
+ assert_raise(ArgumentError) { c.call(h, &:m2) }
assert_raise(ArgumentError) { c.call(h2, &:m2) }
- assert_warn(/The last argument is split into positional and keyword parameters.*for `call'/m) do
- assert_raise(ArgumentError) { c.call(h3, &:m2) }
- end
+ assert_raise(ArgumentError) { c.call(h3, &:m2) }
- redef = -> do
- c.singleton_class.remove_method(:method_missing)
- eval <<-END
- def c.method_missing(_, arg, **args)
- [arg, args]
- end
- END
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([kw, kw], c.call(**{}, &:m2))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([kw, kw], c.call(**kw, &:m2))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h, kw], c.call(**h, &:m2))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h, kw], c.call(a: 1, &:m2))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h2, kw], c.call(**h2, &:m2))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h3, kw], c.call(**h3, &:m2))
- end
- redef[]
- assert_warn(/The keyword argument is passed as the last hash parameter.* for `method_missing'/m) do
- assert_equal([h3, kw], c.call(a: 1, **h2, &:m2))
+ c.singleton_class.remove_method(:method_missing)
+ def c.method_missing(_, arg, **args)
+ [arg, args]
end
+ assert_raise(ArgumentError) { c.call(**{}, &:m2) }
+ assert_raise(ArgumentError) { c.call(**kw, &:m2) }
+ assert_raise(ArgumentError) { c.call(**h, &:m2) }
+ assert_raise(ArgumentError) { c.call(a: 1, &:m2) }
+ assert_raise(ArgumentError) { c.call(**h2, &:m2) }
+ assert_raise(ArgumentError) { c.call(**h3, &:m2) }
+ assert_raise(ArgumentError) { c.call(a: 1, **h2, &:m2) }
c.singleton_class.remove_method(:method_missing)
def c.method_missing(_, arg=1, **args)
@@ -5045,4 +4345,27 @@ class TestKeywordArgumentsSymProcRefinements < Test::Unit::TestCase
mock.new.foo
end
end
+
+ def test_splat_fixnum
+ bug16603 = '[ruby-core:97047] [Bug #16603]'
+ assert_raise(TypeError, bug16603) { p(**42) }
+ assert_raise(TypeError, bug16603) { p(k:1, **42) }
+ end
+
+ def test_value_omission
+ f = ->(**kwargs) { kwargs }
+ x = 1
+ y = 2
+ assert_equal({x: 1, y: 2}, f.call(x:, y:))
+ assert_equal({x: 1, y: 2, z: 3}, f.call(x:, y:, z: 3))
+ assert_equal({one: 1, two: 2}, f.call(one:, two:))
+ end
+
+ private def one
+ 1
+ end
+
+ private def two
+ 2
+ end
end
diff --git a/test/ruby/test_lambda.rb b/test/ruby/test_lambda.rb
index b9412d4540..9949fab8c7 100644
--- a/test/ruby/test_lambda.rb
+++ b/test/ruby/test_lambda.rb
@@ -74,6 +74,135 @@ class TestLambdaParameters < Test::Unit::TestCase
assert_raise(ArgumentError, bug9605) {proc(&plus).call [1,2]}
end
+ def test_proc_inside_lambda_inside_method_return_inside_lambda_inside_method
+ def self.a
+ -> do
+ p = Proc.new{return :a}
+ p.call
+ end.call
+ end
+ assert_equal(:a, a)
+
+ def self.b
+ lambda do
+ p = Proc.new{return :b}
+ p.call
+ end.call
+ end
+ assert_equal(:b, b)
+ end
+
+ def test_proc_inside_lambda_inside_method_return_inside_lambda_outside_method
+ def self.a
+ -> do
+ p = Proc.new{return :a}
+ p.call
+ end
+ end
+ assert_equal(:a, a.call)
+
+ def self.b
+ lambda do
+ p = Proc.new{return :b}
+ p.call
+ end
+ end
+ assert_equal(:b, b.call)
+ end
+
+ def test_proc_inside_lambda_inside_method_return_outside_lambda_inside_method
+ def self.a
+ -> do
+ Proc.new{return :a}
+ end.call.call
+ end
+ assert_raise(LocalJumpError) {a}
+
+ def self.b
+ lambda do
+ Proc.new{return :b}
+ end.call.call
+ end
+ assert_raise(LocalJumpError) {b}
+ end
+
+ def test_proc_inside_lambda_inside_method_return_outside_lambda_outside_method
+ def self.a
+ -> do
+ Proc.new{return :a}
+ end
+ end
+ assert_raise(LocalJumpError) {a.call.call}
+
+ def self.b
+ lambda do
+ Proc.new{return :b}
+ end
+ end
+ assert_raise(LocalJumpError) {b.call.call}
+ end
+
+ def test_proc_inside_lambda2_inside_method_return_outside_lambda1_inside_method
+ def self.a
+ -> do
+ -> do
+ Proc.new{return :a}
+ end.call.call
+ end.call
+ end
+ assert_raise(LocalJumpError) {a}
+
+ def self.b
+ lambda do
+ lambda do
+ Proc.new{return :a}
+ end.call.call
+ end.call
+ end
+ assert_raise(LocalJumpError) {b}
+ end
+
+ def test_proc_inside_lambda_toplevel
+ assert_separately [], <<~RUBY
+ lambda{
+ $g = proc{ return :pr }
+ }.call
+ begin
+ $g.call
+ rescue LocalJumpError
+ # OK!
+ else
+ raise
+ end
+ RUBY
+ end
+
+ def pass_along(&block)
+ lambda(&block)
+ end
+
+ def pass_along2(&block)
+ pass_along(&block)
+ end
+
+ def test_create_non_lambda_for_proc_one_level
+ prev_warning, Warning[:deprecated] = Warning[:deprecated], false
+ f = pass_along {}
+ refute_predicate(f, :lambda?, '[Bug #15620]')
+ assert_nothing_raised(ArgumentError) { f.call(:extra_arg) }
+ ensure
+ Warning[:deprecated] = prev_warning
+ end
+
+ def test_create_non_lambda_for_proc_two_levels
+ prev_warning, Warning[:deprecated] = Warning[:deprecated], false
+ f = pass_along2 {}
+ refute_predicate(f, :lambda?, '[Bug #15620]')
+ assert_nothing_raised(ArgumentError) { f.call(:extra_arg) }
+ ensure
+ Warning[:deprecated] = prev_warning
+ end
+
def test_instance_exec
bug12568 = '[ruby-core:76300] [Bug #12568]'
assert_nothing_raised(ArgumentError, bug12568) do
diff --git a/test/ruby/test_lazy_enumerator.rb b/test/ruby/test_lazy_enumerator.rb
index 6e5c1714a5..2116d0ee31 100644
--- a/test/ruby/test_lazy_enumerator.rb
+++ b/test/ruby/test_lazy_enumerator.rb
@@ -160,6 +160,10 @@ class TestLazyEnumerator < Test::Unit::TestCase
assert_equal([{?a=>97}, {?b=>98}, {?c=>99}], [?a, ?b, ?c].lazy.flat_map {|x| {x=>x.ord}}.force)
end
+ def test_flat_map_take
+ assert_equal([1,2]*3, [[1,2]].cycle.lazy.take(3).flat_map {|x| x}.to_a)
+ end
+
def test_reject
a = Step.new(1..6)
assert_equal(4, a.reject {|x| x < 4}.first)
@@ -678,4 +682,8 @@ EOS
ary = (0..Float::INFINITY).lazy.with_index.take(2).to_a
assert_equal([[0, 0], [1, 1]], ary)
end
+
+ def test_with_index_size
+ assert_equal(3, Enumerator::Lazy.new([1, 2, 3], 3){|y, v| y << v}.with_index.size)
+ end
end
diff --git a/test/ruby/test_literal.rb b/test/ruby/test_literal.rb
index 7f4a329c4a..99dd3a0c56 100644
--- a/test/ruby/test_literal.rb
+++ b/test/ruby/test_literal.rb
@@ -26,7 +26,7 @@ class TestRubyLiteral < Test::Unit::TestCase
assert_equal '5', 0b101.inspect
assert_instance_of Integer, 0b101
assert_raise(SyntaxError) { eval("0b") }
- assert_equal '123456789012345678901234567890', 123456789012345678901234567890.inspect
+ assert_equal '123456789012345678901234567890', 123456789012345678901234567890.to_s
assert_instance_of Integer, 123456789012345678901234567890
assert_instance_of Float, 1.3
assert_equal '2', eval("0x00+2").inspect
@@ -187,14 +187,14 @@ class TestRubyLiteral < Test::Unit::TestCase
if defined?(RubyVM::InstructionSequence.compile_option) and
RubyVM::InstructionSequence.compile_option.key?(:debug_frozen_string_literal)
def test_debug_frozen_string
- src = 'n = 1; _="foo#{n ? "-#{n}" : ""}"'; f = "test.rb"; n = 1
+ src = '_="foo-1"'; f = "test.rb"; n = 1
opt = {frozen_string_literal: true, debug_frozen_string_literal: true}
str = RubyVM::InstructionSequence.compile(src, f, f, n, **opt).eval
assert_equal("foo-1", str)
assert_predicate(str, :frozen?)
assert_raise_with_message(FrozenError, /created at #{Regexp.quote(f)}:#{n}/) {
str << "x"
- }
+ } unless ENV['RUBY_ISEQ_DUMP_DEBUG']
end
def test_debug_frozen_string_in_array_literal
@@ -461,17 +461,46 @@ class TestRubyLiteral < Test::Unit::TestCase
def test_hash_duplicated_key
h = EnvUtil.suppress_warning do
- eval <<~end
+ eval "#{<<-"begin;"}\n#{<<-'end;'}"
+ begin;
# This is a syntax that renders warning at very early stage.
# eval used to delay warning, to be suppressible by EnvUtil.
{"a" => 100, "b" => 200, "a" => 300, "a" => 400}
- end
+ end;
end
assert_equal(2, h.size)
assert_equal(400, h['a'])
assert_equal(200, h['b'])
assert_nil(h['c'])
assert_equal(nil, h.key('300'))
+
+ a = []
+ h = EnvUtil.suppress_warning do
+ eval <<~end
+ # This is a syntax that renders warning at very early stage.
+ # eval used to delay warning, to be suppressible by EnvUtil.
+ {"a" => a.push(100).last, "b" => a.push(200).last, "a" => a.push(300).last, "a" => a.push(400).last}
+ end
+ end
+ assert_equal({'a' => 400, 'b' => 200}, h)
+ assert_equal([100, 200, 300, 400], a)
+
+ assert_all_assertions_foreach(
+ "duplicated literal key",
+ ':foo',
+ '"a"',
+ '1000',
+ '1.0',
+ '1_000_000_000_000_000_000_000',
+ '1.0r',
+ '1.0i',
+ '1.72723e-77',
+ '//',
+ ) do |key|
+ assert_warning(/key #{Regexp.quote(eval(key).inspect)} is duplicated/) do
+ eval("{#{key} => :bar, #{key} => :foo}")
+ end
+ end
end
def test_hash_frozen_key_id
@@ -488,6 +517,30 @@ class TestRubyLiteral < Test::Unit::TestCase
assert_equal(100, h['a'])
end
+ FOO = "foo"
+
+ def test_hash_value_omission
+ x = 1
+ y = 2
+ assert_equal({x: 1, y: 2}, {x:, y:})
+ assert_equal({x: 1, y: 2, z: 3}, {x:, y:, z: 3})
+ assert_equal({one: 1, two: 2}, {one:, two:})
+ b = binding
+ b.local_variable_set(:if, "if")
+ b.local_variable_set(:self, "self")
+ assert_equal({FOO: "foo", if: "if", self: "self"},
+ eval('{FOO:, if:, self:}', b))
+ assert_syntax_error('{"#{x}":}', /'\}'/)
+ end
+
+ private def one
+ 1
+ end
+
+ private def two
+ 2
+ end
+
def test_range
assert_instance_of Range, (1..2)
assert_equal(1..2, 1..2)
diff --git a/test/ruby/test_m17n.rb b/test/ruby/test_m17n.rb
index 6c7d0e6bae..c793520ac4 100644
--- a/test/ruby/test_m17n.rb
+++ b/test/ruby/test_m17n.rb
@@ -299,6 +299,9 @@ class TestM17N < Test::Unit::TestCase
orig_v, $VERBOSE = $VERBOSE, false
orig_int, Encoding.default_internal = Encoding.default_internal, nil
orig_ext = Encoding.default_external
+
+ skip "https://bugs.ruby-lang.org/issues/18338"
+
o = Object.new
Encoding.default_external = Encoding::UTF_16BE
@@ -310,6 +313,7 @@ class TestM17N < Test::Unit::TestCase
def o.inspect
"abc".encode(Encoding.default_external)
end
+
assert_equal '[abc]', [o].inspect
Encoding.default_external = Encoding::US_ASCII
@@ -1324,10 +1328,14 @@ class TestM17N < Test::Unit::TestCase
end
def test_env
- locale_encoding = Encoding.find("locale")
+ if RUBY_PLATFORM =~ /bccwin|mswin|mingw/
+ env_encoding = Encoding::UTF_8
+ else
+ env_encoding = Encoding.find("locale")
+ end
ENV.each {|k, v|
- assert_equal(locale_encoding, k.encoding, k)
- assert_equal(locale_encoding, v.encoding, v)
+ assert_equal(env_encoding, k.encoding, proc {"key(#{k.encoding})=#{k.dump}"})
+ assert_equal(env_encoding, v.encoding, proc {"key(#{k.encoding})=#{k.dump}\n" "value(#{v.encoding})=#{v.dump}"})
}
end
diff --git a/test/ruby/test_m17n_comb.rb b/test/ruby/test_m17n_comb.rb
index cfb8bff882..e48a1948be 100644
--- a/test/ruby/test_m17n_comb.rb
+++ b/test/ruby/test_m17n_comb.rb
@@ -593,6 +593,21 @@ class TestM17NComb < Test::Unit::TestCase
}
end
+ def test_str_casecmp?
+ strings = STRINGS.dup
+ strings.push(
+ # prevent wrong single byte optimization
+ "\xC0".force_encoding("ISO-8859-1"),
+ "\xE0".force_encoding("ISO-8859-1"),
+ )
+ combination(strings, strings) {|s1, s2|
+ #puts "#{encdump(s1)}.casecmp(#{encdump(s2)})"
+ next unless s1.valid_encoding? && s2.valid_encoding? && Encoding.compatible?(s1, s2)
+ r = s1.casecmp?(s2)
+ assert_equal(s1.downcase(:fold) == s2.downcase(:fold), r)
+ }
+ end
+
def test_str_center
combination(STRINGS, [0,1,2,3,10]) {|s1, width|
t = s1.center(width)
@@ -751,8 +766,14 @@ class TestM17NComb < Test::Unit::TestCase
# glibc 2.16 or later denies salt contained other than [0-9A-Za-z./] #7312
# we use this check to test strict and non-strict behavior separately #11045
strict_crypt = if defined? Etc::CS_GNU_LIBC_VERSION
- glibcver = Etc.confstr(Etc::CS_GNU_LIBC_VERSION).scan(/\d+/).map(&:to_i)
- (glibcver <=> [2, 16]) >= 0
+ begin
+ confstr = Etc.confstr(Etc::CS_GNU_LIBC_VERSION)
+ rescue Errno::EINVAL
+ false
+ else
+ glibcver = confstr.scan(/\d+/).map(&:to_i)
+ (glibcver <=> [2, 16]) >= 0
+ end
end
def test_str_crypt
diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb
index f300710d2c..361d18dd4b 100644
--- a/test/ruby/test_marshal.rb
+++ b/test/ruby/test_marshal.rb
@@ -8,7 +8,6 @@ class TestMarshal < Test::Unit::TestCase
def setup
@verbose = $VERBOSE
- $VERBOSE = nil
end
def teardown
@@ -59,6 +58,8 @@ class TestMarshal < Test::Unit::TestCase
TestMarshal.instance_eval { remove_const :StructOrNot }
TestMarshal.const_set :StructOrNot, Class.new
assert_raise(TypeError, "[ruby-dev:31709]") { Marshal.load(s) }
+ ensure
+ TestMarshal.instance_eval { remove_const :StructOrNot }
end
def test_struct_invalid_members
@@ -67,6 +68,8 @@ class TestMarshal < Test::Unit::TestCase
Marshal.load("\004\bIc&TestMarshal::StructInvalidMembers\006:\020__members__\"\bfoo")
TestMarshal::StructInvalidMembers.members
}
+ ensure
+ TestMarshal.instance_eval { remove_const :StructInvalidMembers }
end
class C
@@ -157,20 +160,29 @@ class TestMarshal < Test::Unit::TestCase
end
def test_change_class_name
+ self.class.__send__(:remove_const, :C3) if self.class.const_defined?(:C3)
eval("class C3; def _dump(s); 'foo'; end; end")
m = Marshal.dump(C3.new)
assert_raise(TypeError) { Marshal.load(m) }
+ self.class.__send__(:remove_const, :C3)
eval("C3 = nil")
assert_raise(TypeError) { Marshal.load(m) }
+ ensure
+ self.class.__send__(:remove_const, :C3) if self.class.const_defined?(:C3)
end
def test_change_struct
+ self.class.__send__(:remove_const, :C3) if self.class.const_defined?(:C3)
eval("C3 = Struct.new(:foo, :bar)")
m = Marshal.dump(C3.new("FOO", "BAR"))
+ self.class.__send__(:remove_const, :C3)
eval("C3 = Struct.new(:foo)")
assert_raise(TypeError) { Marshal.load(m) }
+ self.class.__send__(:remove_const, :C3)
eval("C3 = Struct.new(:foo, :baz)")
assert_raise(TypeError) { Marshal.load(m) }
+ ensure
+ self.class.__send__(:remove_const, :C3) if self.class.const_defined?(:C3)
end
class C4
@@ -542,7 +554,7 @@ class TestMarshal < Test::Unit::TestCase
end
class TestForRespondToFalse
- def respond_to?(a)
+ def respond_to?(a, priv = false)
false
end
end
@@ -570,7 +582,7 @@ class TestMarshal < Test::Unit::TestCase
end
def test_continuation
- require "continuation"
+ EnvUtil.suppress_warning {require "continuation"}
c = Bug9523.new
assert_raise_with_message(RuntimeError, /Marshal\.dump reentered at marshal_dump/) do
Marshal.dump(c)
@@ -596,7 +608,8 @@ class TestMarshal < Test::Unit::TestCase
end
def test_unloadable_data
- c = eval("class Unloadable\u{23F0 23F3}<Time;;self;end")
+ name = "Unloadable\u{23F0 23F3}"
+ c = eval("class #{name} < Time;;self;end")
c.class_eval {
alias _dump_data _dump
undef _dump
@@ -605,10 +618,16 @@ class TestMarshal < Test::Unit::TestCase
assert_raise_with_message(TypeError, /Unloadable\u{23F0 23F3}/) {
Marshal.load(d)
}
+
+ # cleanup
+ self.class.class_eval do
+ remove_const name
+ end
end
def test_unloadable_userdef
- c = eval("class Userdef\u{23F0 23F3}<Time;self;end")
+ name = "Userdef\u{23F0 23F3}"
+ c = eval("class #{name} < Time;self;end")
class << c
undef _load
end
@@ -616,6 +635,11 @@ class TestMarshal < Test::Unit::TestCase
assert_raise_with_message(TypeError, /Userdef\u{23F0 23F3}/) {
Marshal.load(d)
}
+
+ # cleanup
+ self.class.class_eval do
+ remove_const name
+ end
end
def test_unloadable_usrmarshal
@@ -631,15 +655,16 @@ class TestMarshal < Test::Unit::TestCase
def test_no_internal_ids
opt = %w[--disable=gems]
- args = [opt, 'Marshal.dump("",STDOUT)', true, true, encoding: Encoding::ASCII_8BIT]
- out, err, status = EnvUtil.invoke_ruby(*args)
+ args = [opt, 'Marshal.dump("",STDOUT)', true, true]
+ kw = {encoding: Encoding::ASCII_8BIT}
+ out, err, status = EnvUtil.invoke_ruby(*args, **kw)
assert_empty(err)
assert_predicate(status, :success?)
expected = out
opt << "--enable=frozen-string-literal"
opt << "--debug=frozen-string-literal"
- out, err, status = EnvUtil.invoke_ruby(*args)
+ out, err, status = EnvUtil.invoke_ruby(*args, **kw)
assert_empty(err)
assert_predicate(status, :success?)
assert_equal(expected, out)
@@ -651,6 +676,23 @@ class TestMarshal < Test::Unit::TestCase
assert_equal(['X', 'X'], Marshal.load(Marshal.dump(obj), ->(v) { v == str ? v.upcase : v }))
end
+ def test_marshal_proc_string_encoding
+ string = "foo"
+ payload = Marshal.dump(string)
+ Marshal.load(payload, ->(v) {
+ if v.is_a?(String)
+ assert_equal(string, v)
+ assert_equal(string.encoding, v.encoding)
+ end
+ v
+ })
+ end
+
+ def test_marshal_proc_freeze
+ object = { foo: [42, "bar"] }
+ assert_equal object, Marshal.load(Marshal.dump(object), :freeze.to_proc)
+ end
+
def test_marshal_load_extended_class_crash
assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
begin;
@@ -753,4 +795,136 @@ class TestMarshal < Test::Unit::TestCase
Marshal.dump(obj)
end
end
+
+ ruby2_keywords def ruby2_keywords_hash(*a)
+ a.last
+ end
+
+ def ruby2_keywords_test(key: 1)
+ key
+ end
+
+ def test_marshal_with_ruby2_keywords_hash
+ flagged_hash = ruby2_keywords_hash(key: 42)
+ data = Marshal.dump(flagged_hash)
+ hash = Marshal.load(data)
+ assert_equal(42, ruby2_keywords_test(*[hash]))
+
+ hash2 = Marshal.load(data.sub(/\x06K(?=T\z)/, "\x08KEY"))
+ assert_raise(ArgumentError, /\(given 1, expected 0\)/) {
+ ruby2_keywords_test(*[hash2])
+ }
+ hash2 = Marshal.load(data.sub(/:\x06K(?=T\z)/, "I\\&\x06:\x0dencoding\"\x0aUTF-7"))
+ assert_raise(ArgumentError, /\(given 1, expected 0\)/) {
+ ruby2_keywords_test(*[hash2])
+ }
+ end
+
+ def test_invalid_byte_sequence_symbol
+ data = Marshal.dump(:K)
+ data = data.sub(/:\x06K/, "I\\&\x06:\x0dencoding\"\x0dUTF-16LE")
+ assert_raise(ArgumentError, /UTF-16LE: "\\x4B"/) {
+ Marshal.load(data)
+ }
+ end
+
+ def exception_test
+ raise
+ end
+
+ def test_marshal_exception
+ begin
+ exception_test
+ rescue => e
+ e2 = Marshal.load(Marshal.dump(e))
+ assert_equal(e.message, e2.message)
+ assert_equal(e.backtrace, e2.backtrace)
+ assert_nil(e2.backtrace_locations) # temporal
+ end
+ end
+
+ def nameerror_test
+ unknown_method
+ end
+
+ def test_marshal_nameerror
+ begin
+ nameerror_test
+ rescue NameError => e
+ e2 = Marshal.load(Marshal.dump(e))
+ assert_equal(e.message.lines.first.chomp, e2.message.lines.first)
+ assert_equal(e.name, e2.name)
+ assert_equal(e.backtrace, e2.backtrace)
+ assert_nil(e2.backtrace_locations) # temporal
+ end
+ end
+
+ class TestMarshalFreezeProc < Test::Unit::TestCase
+ include MarshalTestLib
+
+ def encode(o)
+ Marshal.dump(o)
+ end
+
+ def decode(s)
+ Marshal.load(s, :freeze.to_proc)
+ end
+ end
+
+ def _test_hash_compared_by_identity(h)
+ h.compare_by_identity
+ h["a" + "0"] = 1
+ h["a" + "0"] = 2
+ h = Marshal.load(Marshal.dump(h))
+ assert_predicate(h, :compare_by_identity?)
+ a = h.to_a
+ assert_equal([["a0", 1], ["a0", 2]], a.sort)
+ assert_not_same(a[1][0], a[0][0])
+ end
+
+ def test_hash_compared_by_identity
+ _test_hash_compared_by_identity(Hash.new)
+ end
+
+ def test_hash_default_compared_by_identity
+ _test_hash_compared_by_identity(Hash.new(true))
+ end
+
+ class TestMarshalFreeze < Test::Unit::TestCase
+ include MarshalTestLib
+
+ def encode(o)
+ Marshal.dump(o)
+ end
+
+ def decode(s)
+ Marshal.load(s, freeze: true)
+ end
+
+ def test_return_objects_are_frozen
+ source = ["foo", {}, /foo/, 1..2]
+ objects = decode(encode(source))
+ assert_equal source, objects
+ assert_predicate objects, :frozen?
+ objects.each do |obj|
+ assert_predicate obj, :frozen?
+ end
+ end
+
+ def test_proc_returned_object_are_not_frozen
+ source = ["foo", {}, /foo/, 1..2]
+ objects = Marshal.load(encode(source), ->(o) { o.dup }, freeze: true)
+ assert_equal source, objects
+ refute_predicate objects, :frozen?
+ objects.each do |obj|
+ refute_predicate obj, :frozen?
+ end
+ end
+
+ def test_modules_and_classes_are_not_frozen
+ _objects = Marshal.load(encode([Object, Kernel]), freeze: true)
+ refute_predicate Object, :frozen?
+ refute_predicate Kernel, :frozen?
+ end
+ end
end
diff --git a/test/ruby/test_math.rb b/test/ruby/test_math.rb
index 5cc12bcfeb..73f44c6ae3 100644
--- a/test/ruby/test_math.rb
+++ b/test/ruby/test_math.rb
@@ -73,9 +73,9 @@ class TestMath < Test::Unit::TestCase
check(1 * Math::PI / 4, Math.acos( 1.0 / Math.sqrt(2)))
check(2 * Math::PI / 4, Math.acos( 0.0))
check(4 * Math::PI / 4, Math.acos(-1.0))
- assert_raise(Math::DomainError) { Math.acos(+1.0 + Float::EPSILON) }
- assert_raise(Math::DomainError) { Math.acos(-1.0 - Float::EPSILON) }
- assert_raise(Math::DomainError) { Math.acos(2.0) }
+ assert_raise_with_message(Math::DomainError, /\bacos\b/) { Math.acos(+1.0 + Float::EPSILON) }
+ assert_raise_with_message(Math::DomainError, /\bacos\b/) { Math.acos(-1.0 - Float::EPSILON) }
+ assert_raise_with_message(Math::DomainError, /\bacos\b/) { Math.acos(2.0) }
end
def test_asin
@@ -83,9 +83,9 @@ class TestMath < Test::Unit::TestCase
check( 1 * Math::PI / 4, Math.asin( 1.0 / Math.sqrt(2)))
check( 2 * Math::PI / 4, Math.asin( 1.0))
check(-2 * Math::PI / 4, Math.asin(-1.0))
- assert_raise(Math::DomainError) { Math.asin(+1.0 + Float::EPSILON) }
- assert_raise(Math::DomainError) { Math.asin(-1.0 - Float::EPSILON) }
- assert_raise(Math::DomainError) { Math.asin(2.0) }
+ assert_raise_with_message(Math::DomainError, /\basin\b/) { Math.asin(+1.0 + Float::EPSILON) }
+ assert_raise_with_message(Math::DomainError, /\basin\b/) { Math.asin(-1.0 - Float::EPSILON) }
+ assert_raise_with_message(Math::DomainError, /\basin\b/) { Math.asin(2.0) }
end
def test_atan
@@ -119,8 +119,8 @@ class TestMath < Test::Unit::TestCase
check(0, Math.acosh(1))
check(1, Math.acosh((Math::E ** 1 + Math::E ** -1) / 2))
check(2, Math.acosh((Math::E ** 2 + Math::E ** -2) / 2))
- assert_raise(Math::DomainError) { Math.acosh(1.0 - Float::EPSILON) }
- assert_raise(Math::DomainError) { Math.acosh(0) }
+ assert_raise_with_message(Math::DomainError, /\bacosh\b/) { Math.acosh(1.0 - Float::EPSILON) }
+ assert_raise_with_message(Math::DomainError, /\bacosh\b/) { Math.acosh(0) }
end
def test_asinh
@@ -135,8 +135,8 @@ class TestMath < Test::Unit::TestCase
check(2, Math.atanh(Math.sinh(2) / Math.cosh(2)))
assert_nothing_raised { assert_infinity(Math.atanh(1)) }
assert_nothing_raised { assert_infinity(-Math.atanh(-1)) }
- assert_raise(Math::DomainError) { Math.atanh(+1.0 + Float::EPSILON) }
- assert_raise(Math::DomainError) { Math.atanh(-1.0 - Float::EPSILON) }
+ assert_raise_with_message(Math::DomainError, /\batanh\b/) { Math.atanh(+1.0 + Float::EPSILON) }
+ assert_raise_with_message(Math::DomainError, /\batanh\b/) { Math.atanh(-1.0 - Float::EPSILON) }
end
def test_exp
@@ -157,10 +157,14 @@ class TestMath < Test::Unit::TestCase
assert_nothing_raised { assert_infinity(Math.log(1.0/0)) }
assert_nothing_raised { assert_infinity(-Math.log(+0.0)) }
assert_nothing_raised { assert_infinity(-Math.log(-0.0)) }
- assert_raise(Math::DomainError) { Math.log(-1.0) }
+ assert_raise_with_message(Math::DomainError, /\blog\b/) { Math.log(-1.0) }
+ assert_raise_with_message(Math::DomainError, /\blog\b/) { Math.log(-Float::EPSILON) }
assert_raise(TypeError) { Math.log(1,nil) }
- assert_raise(Math::DomainError, '[ruby-core:62309] [ruby-Bug #9797]') { Math.log(1.0, -1.0) }
+ assert_raise_with_message(Math::DomainError, /\blog\b/, '[ruby-core:62309] [ruby-Bug #9797]') { Math.log(1.0, -1.0) }
+ assert_raise_with_message(Math::DomainError, /\blog\b/) { Math.log(1.0, -Float::EPSILON) }
assert_nothing_raised { assert_nan(Math.log(0.0, 0.0)) }
+ assert_nothing_raised { assert_nan(Math.log(Float::NAN)) }
+ assert_nothing_raised { assert_nan(Math.log(1.0, Float::NAN)) }
end
def test_log2
@@ -172,7 +176,9 @@ class TestMath < Test::Unit::TestCase
assert_nothing_raised { assert_infinity(Math.log2(1.0/0)) }
assert_nothing_raised { assert_infinity(-Math.log2(+0.0)) }
assert_nothing_raised { assert_infinity(-Math.log2(-0.0)) }
- assert_raise(Math::DomainError) { Math.log2(-1.0) }
+ assert_raise_with_message(Math::DomainError, /\blog2\b/) { Math.log2(-1.0) }
+ assert_raise_with_message(Math::DomainError, /\blog2\b/) { Math.log2(-Float::EPSILON) }
+ assert_nothing_raised { assert_nan(Math.log2(Float::NAN)) }
end
def test_log10
@@ -184,7 +190,9 @@ class TestMath < Test::Unit::TestCase
assert_nothing_raised { assert_infinity(Math.log10(1.0/0)) }
assert_nothing_raised { assert_infinity(-Math.log10(+0.0)) }
assert_nothing_raised { assert_infinity(-Math.log10(-0.0)) }
- assert_raise(Math::DomainError) { Math.log10(-1.0) }
+ assert_raise_with_message(Math::DomainError, /\blog10\b/) { Math.log10(-1.0) }
+ assert_raise_with_message(Math::DomainError, /\blog10\b/) { Math.log10(-Float::EPSILON) }
+ assert_nothing_raised { assert_nan(Math.log10(Float::NAN)) }
end
def test_sqrt
@@ -193,7 +201,9 @@ class TestMath < Test::Unit::TestCase
check(2, Math.sqrt(4))
assert_nothing_raised { assert_infinity(Math.sqrt(1.0/0)) }
assert_equal("0.0", Math.sqrt(-0.0).to_s) # insure it is +0.0, not -0.0
- assert_raise(Math::DomainError) { Math.sqrt(-1.0) }
+ assert_raise_with_message(Math::DomainError, /\bsqrt\b/) { Math.sqrt(-1.0) }
+ assert_raise_with_message(Math::DomainError, /\bsqrt\b/) { Math.sqrt(-Float::EPSILON) }
+ assert_nothing_raised { assert_nan(Math.sqrt(Float::NAN)) }
end
def test_cbrt
@@ -201,8 +211,11 @@ class TestMath < Test::Unit::TestCase
check(-2, Math.cbrt(-8))
check(3, Math.cbrt(27))
check(-0.1, Math.cbrt(-0.001))
+ check(0.0, Math.cbrt(0.0))
assert_nothing_raised { assert_infinity(Math.cbrt(1.0/0)) }
assert_operator(Math.cbrt(1.0 - Float::EPSILON), :<=, 1.0)
+ assert_nothing_raised { assert_nan(Math.sqrt(Float::NAN)) }
+ assert_nothing_raised { assert_nan(Math.cbrt(Float::NAN)) }
end
def test_frexp
@@ -211,6 +224,7 @@ class TestMath < Test::Unit::TestCase
assert_float_and_int([0.5, 1], Math.frexp(1.0))
assert_float_and_int([0.5, 2], Math.frexp(2.0))
assert_float_and_int([0.75, 2], Math.frexp(3.0))
+ assert_nan(Math.frexp(Float::NAN)[0])
end
def test_ldexp
@@ -228,11 +242,13 @@ class TestMath < Test::Unit::TestCase
def test_erf
check(0, Math.erf(0))
check(1, Math.erf(1.0 / 0.0))
+ assert_nan(Math.erf(Float::NAN))
end
def test_erfc
check(1, Math.erfc(0))
check(0, Math.erfc(1.0 / 0.0))
+ assert_nan(Math.erfc(Float::NAN))
end
def test_gamma
@@ -257,11 +273,13 @@ class TestMath < Test::Unit::TestCase
assert_infinity(Math.gamma(i-1), "Math.gamma(#{i-1}) should be INF")
end
- assert_raise(Math::DomainError) { Math.gamma(-Float::INFINITY) }
+ assert_raise_with_message(Math::DomainError, /\bgamma\b/) { Math.gamma(-Float::INFINITY) }
+ assert_raise_with_message(Math::DomainError, /\bgamma\b/) { Math.gamma(-1.0) }
x = Math.gamma(-0.0)
mesg = "Math.gamma(-0.0) should be -INF"
assert_infinity(x, mesg)
assert_predicate(x, :negative?, mesg)
+ assert_nan(Math.gamma(Float::NAN))
end
def test_lgamma
@@ -277,12 +295,14 @@ class TestMath < Test::Unit::TestCase
assert_float_and_int([Math.log(15 * sqrt_pi / 8), 1], Math.lgamma(3.5))
assert_float_and_int([Math.log(6), 1], Math.lgamma(4))
- assert_raise(Math::DomainError) { Math.lgamma(-Float::INFINITY) }
+ assert_raise_with_message(Math::DomainError, /\blgamma\b/) { Math.lgamma(-Float::INFINITY) }
x, sign = Math.lgamma(-0.0)
mesg = "Math.lgamma(-0.0) should be [INF, -1]"
assert_infinity(x, mesg)
assert_predicate(x, :positive?, mesg)
assert_equal(-1, sign, mesg)
+ x, sign = Math.lgamma(Float::NAN)
+ assert_nan(x)
end
def test_fixnum_to_f
diff --git a/test/ruby/test_memory_view.rb b/test/ruby/test_memory_view.rb
new file mode 100644
index 0000000000..5a39084d18
--- /dev/null
+++ b/test/ruby/test_memory_view.rb
@@ -0,0 +1,341 @@
+require "-test-/memory_view"
+require "rbconfig/sizeof"
+
+class TestMemoryView < Test::Unit::TestCase
+ NATIVE_ENDIAN = MemoryViewTestUtils::NATIVE_ENDIAN
+ LITTLE_ENDIAN = :little_endian
+ BIG_ENDIAN = :big_endian
+
+ %I(SHORT INT INT16 INT32 INT64 INTPTR LONG LONG_LONG FLOAT DOUBLE).each do |type|
+ name = :"#{type}_ALIGNMENT"
+ const_set(name, MemoryViewTestUtils.const_get(name))
+ end
+
+ def test_rb_memory_view_register_duplicated
+ assert_warning(/Duplicated registration of memory view to/) do
+ MemoryViewTestUtils.register(MemoryViewTestUtils::ExportableString)
+ end
+ end
+
+ def test_rb_memory_view_register_nonclass
+ assert_raise(TypeError) do
+ MemoryViewTestUtils.register(Object.new)
+ end
+ end
+
+ def sizeof(type)
+ RbConfig::SIZEOF[type.to_s]
+ end
+
+ def test_rb_memory_view_item_size_from_format
+ [
+ [nil, 1], ['c', 1], ['C', 1],
+ ['n', 2], ['v', 2],
+ ['l', 4], ['L', 4], ['N', 4], ['V', 4], ['f', 4], ['e', 4], ['g', 4],
+ ['q', 8], ['Q', 8], ['d', 8], ['E', 8], ['G', 8],
+ ['s', sizeof(:short)], ['S', sizeof(:short)], ['s!', sizeof(:short)], ['S!', sizeof(:short)],
+ ['i', sizeof(:int)], ['I', sizeof(:int)], ['i!', sizeof(:int)], ['I!', sizeof(:int)],
+ ['l!', sizeof(:long)], ['L!', sizeof(:long)],
+ ['q!', sizeof('long long')], ['Q!', sizeof('long long')],
+ ['j', sizeof(:intptr_t)], ['J', sizeof(:intptr_t)],
+ ].each do |format, expected|
+ actual, err = MemoryViewTestUtils.item_size_from_format(format)
+ assert_nil(err)
+ assert_equal(expected, actual, "rb_memory_view_item_size_from_format(#{format || 'NULL'}) == #{expected}")
+ end
+ end
+
+ def test_rb_memory_view_item_size_from_format_composed
+ actual, = MemoryViewTestUtils.item_size_from_format("ccc")
+ assert_equal(3, actual)
+
+ actual, = MemoryViewTestUtils.item_size_from_format("c3")
+ assert_equal(3, actual)
+
+ actual, = MemoryViewTestUtils.item_size_from_format("fd")
+ assert_equal(12, actual)
+
+ actual, = MemoryViewTestUtils.item_size_from_format("fx2d")
+ assert_equal(14, actual)
+ end
+
+ def test_rb_memory_view_item_size_from_format_with_spaces
+ # spaces should be ignored
+ actual, = MemoryViewTestUtils.item_size_from_format("f x2 d")
+ assert_equal(14, actual)
+ end
+
+ def test_rb_memory_view_item_size_from_format_error
+ assert_equal([-1, "a"], MemoryViewTestUtils.item_size_from_format("ccca"))
+ assert_equal([-1, "a"], MemoryViewTestUtils.item_size_from_format("ccc4a"))
+ end
+
+ def test_rb_memory_view_parse_item_format
+ total_size, members, err = MemoryViewTestUtils.parse_item_format("ccc2f3x2d4q!<")
+ assert_equal(58, total_size)
+ assert_nil(err)
+ assert_equal([
+ {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 0, size: 1, repeat: 1},
+ {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 1, size: 1, repeat: 1},
+ {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 2, size: 1, repeat: 2},
+ {format: 'f', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 4, size: 4, repeat: 3},
+ {format: 'd', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 18, size: 8, repeat: 4},
+ {format: 'q', native_size_p: true, endianness: :little_endian, offset: 50, size: sizeof('long long'), repeat: 1}
+ ],
+ members)
+ end
+
+ def test_rb_memory_view_parse_item_format_with_alignment_signle
+ [
+ ["c", false, NATIVE_ENDIAN, 1, 1, 1],
+ ["C", false, NATIVE_ENDIAN, 1, 1, 1],
+ ["s", false, NATIVE_ENDIAN, SHORT_ALIGNMENT, sizeof(:short), 1],
+ ["S", false, NATIVE_ENDIAN, SHORT_ALIGNMENT, sizeof(:short), 1],
+ ["s!", true, NATIVE_ENDIAN, SHORT_ALIGNMENT, sizeof(:short), 1],
+ ["S!", true, NATIVE_ENDIAN, SHORT_ALIGNMENT, sizeof(:short), 1],
+ ["n", false, :big_endian, INT16_ALIGNMENT, sizeof(:int16_t), 1],
+ ["v", false, :little_endian, INT16_ALIGNMENT, sizeof(:int16_t), 1],
+ ["i", false, NATIVE_ENDIAN, INT_ALIGNMENT, sizeof(:int), 1],
+ ["I", false, NATIVE_ENDIAN, INT_ALIGNMENT, sizeof(:int), 1],
+ ["i!", true, NATIVE_ENDIAN, INT_ALIGNMENT, sizeof(:int), 1],
+ ["I!", true, NATIVE_ENDIAN, INT_ALIGNMENT, sizeof(:int), 1],
+ ["l", false, NATIVE_ENDIAN, INT32_ALIGNMENT, sizeof(:int32_t), 1],
+ ["L", false, NATIVE_ENDIAN, INT32_ALIGNMENT, sizeof(:int32_t), 1],
+ ["l!", true, NATIVE_ENDIAN, LONG_ALIGNMENT, sizeof(:long), 1],
+ ["L!", true, NATIVE_ENDIAN, LONG_ALIGNMENT, sizeof(:long), 1],
+ ["N", false, :big_endian, INT32_ALIGNMENT, sizeof(:int32_t), 1],
+ ["V", false, :little_endian, INT32_ALIGNMENT, sizeof(:int32_t), 1],
+ ["f", false, NATIVE_ENDIAN, FLOAT_ALIGNMENT, sizeof(:float), 1],
+ ["e", false, :little_endian, FLOAT_ALIGNMENT, sizeof(:float), 1],
+ ["g", false, :big_endian, FLOAT_ALIGNMENT, sizeof(:float), 1],
+ ["q", false, NATIVE_ENDIAN, INT64_ALIGNMENT, sizeof(:int64_t), 1],
+ ["Q", false, NATIVE_ENDIAN, INT64_ALIGNMENT, sizeof(:int64_t), 1],
+ ["q!", true, NATIVE_ENDIAN, LONG_LONG_ALIGNMENT, sizeof("long long"), 1],
+ ["Q!", true, NATIVE_ENDIAN, LONG_LONG_ALIGNMENT, sizeof("long long"), 1],
+ ["d", false, NATIVE_ENDIAN, DOUBLE_ALIGNMENT, sizeof(:double), 1],
+ ["E", false, :little_endian, DOUBLE_ALIGNMENT, sizeof(:double), 1],
+ ["G", false, :big_endian, DOUBLE_ALIGNMENT, sizeof(:double), 1],
+ ["j", false, NATIVE_ENDIAN, INTPTR_ALIGNMENT, sizeof(:intptr_t), 1],
+ ["J", false, NATIVE_ENDIAN, INTPTR_ALIGNMENT, sizeof(:intptr_t), 1],
+ ].each do |type, native_size_p, endianness, alignment, size, repeat, total_size|
+ total_size, members, err = MemoryViewTestUtils.parse_item_format("|c#{type}")
+ assert_nil(err)
+
+ padding_size = alignment - 1
+ expected_total_size = 1 + padding_size + size
+ assert_equal(expected_total_size, total_size)
+
+ expected_result = [
+ {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 0, size: 1, repeat: 1},
+ {format: type[0], native_size_p: native_size_p, endianness: endianness, offset: alignment, size: size, repeat: repeat},
+ ]
+ assert_equal(expected_result, members)
+ end
+ end
+
+ def alignment_padding(total_size, alignment)
+ res = total_size % alignment
+ if res > 0
+ alignment - res
+ else
+ 0
+ end
+ end
+
+ def test_rb_memory_view_parse_item_format_with_alignment_total_size_with_tail_padding
+ total_size, _members, err = MemoryViewTestUtils.parse_item_format("|lqc")
+ assert_nil(err)
+
+ expected_total_size = sizeof(:int32_t)
+ expected_total_size += alignment_padding(expected_total_size, INT32_ALIGNMENT)
+ expected_total_size += sizeof(:int64_t)
+ expected_total_size += alignment_padding(expected_total_size, INT64_ALIGNMENT)
+ expected_total_size += 1
+ expected_total_size += alignment_padding(expected_total_size, INT64_ALIGNMENT)
+ assert_equal(expected_total_size, total_size)
+ end
+
+ def test_rb_memory_view_parse_item_format_with_alignment_compound
+ total_size, members, err = MemoryViewTestUtils.parse_item_format("|ccc2f3x2d4cq!<")
+ assert_nil(err)
+
+ expected_total_size = 1 + 1 + 1*2
+ expected_total_size += alignment_padding(expected_total_size, FLOAT_ALIGNMENT)
+ expected_total_size += sizeof(:float)*3 + 1*2
+ expected_total_size += alignment_padding(expected_total_size, DOUBLE_ALIGNMENT)
+ expected_total_size += sizeof(:double)*4 + 1
+ expected_total_size += alignment_padding(expected_total_size, LONG_LONG_ALIGNMENT)
+ expected_total_size += sizeof("long long")
+ assert_equal(expected_total_size, total_size)
+
+ expected_result = [
+ {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 0, size: 1, repeat: 1},
+ {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 1, size: 1, repeat: 1},
+ {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 2, size: 1, repeat: 2},
+ ]
+ offset = 4
+
+ res = offset % FLOAT_ALIGNMENT
+ offset += FLOAT_ALIGNMENT - res if res > 0
+ expected_result << {format: 'f', native_size_p: false, endianness: NATIVE_ENDIAN, offset: offset, size: 4, repeat: 3}
+ offset += 12
+
+ offset += 2 # 2x
+
+ res = offset % DOUBLE_ALIGNMENT
+ offset += DOUBLE_ALIGNMENT - res if res > 0
+ expected_result << {format: 'd', native_size_p: false, endianness: NATIVE_ENDIAN, offset: offset, size: 8, repeat: 4}
+ offset += 32
+
+ expected_result << {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset: offset, size: 1, repeat: 1}
+ offset += 1
+
+ res = offset % LONG_LONG_ALIGNMENT
+ offset += LONG_LONG_ALIGNMENT - res if res > 0
+ expected_result << {format: 'q', native_size_p: true, endianness: :little_endian, offset: offset, size: 8, repeat: 1}
+
+ assert_equal(expected_result, members)
+ end
+
+ def test_rb_memory_view_extract_item_members
+ m = MemoryViewTestUtils
+ assert_equal(1, m.extract_item_members([1].pack("c"), "c"))
+ assert_equal([1, 2], m.extract_item_members([1, 2].pack("ii"), "ii"))
+ assert_equal([1, 2, 3], m.extract_item_members([1, 2, 3].pack("cls"), "cls"))
+ end
+
+ def test_rb_memory_view_extract_item_members_endianness
+ m = MemoryViewTestUtils
+ assert_equal([0x0102, 0x0304], m.extract_item_members([1, 2, 3, 4].pack("c*"), "S>2"))
+ assert_equal([0x0102, 0x0304], m.extract_item_members([1, 2, 3, 4].pack("c*"), "n2"))
+ assert_equal([0x0201, 0x0403], m.extract_item_members([1, 2, 3, 4].pack("c*"), "S<2"))
+ assert_equal([0x0201, 0x0403], m.extract_item_members([1, 2, 3, 4].pack("c*"), "v2"))
+ assert_equal(0x01020304, m.extract_item_members([1, 2, 3, 4].pack("c*"), "L>"))
+ assert_equal(0x01020304, m.extract_item_members([1, 2, 3, 4].pack("c*"), "N"))
+ assert_equal(0x04030201, m.extract_item_members([1, 2, 3, 4].pack("c*"), "L<"))
+ assert_equal(0x04030201, m.extract_item_members([1, 2, 3, 4].pack("c*"), "V"))
+ assert_equal(0x0102030405060708, m.extract_item_members([1, 2, 3, 4, 5, 6, 7, 8].pack("c*"), "Q>"))
+ assert_equal(0x0807060504030201, m.extract_item_members([1, 2, 3, 4, 5, 6, 7, 8].pack("c*"), "Q<"))
+ end
+
+ def test_rb_memory_view_extract_item_members_float
+ m = MemoryViewTestUtils
+ packed = [1.23].pack("f")
+ assert_equal(packed.unpack("f")[0], m.extract_item_members(packed, "f"))
+ end
+
+ def test_rb_memory_view_extract_item_members_float_endianness
+ m = MemoryViewTestUtils
+ hi, lo = [1.23].pack("f").unpack("L")[0].divmod(0x10000)
+ packed = [lo, hi].pack("S*")
+ assert_equal(packed.unpack("e")[0], m.extract_item_members(packed, "e"))
+ packed = [hi, lo].pack("S*")
+ assert_equal(packed.unpack("g")[0], m.extract_item_members(packed, "g"))
+ end
+
+ def test_rb_memory_view_extract_item_members_doble
+ m = MemoryViewTestUtils
+ packed = [1.23].pack("d")
+ assert_equal(1.23, m.extract_item_members(packed, "d"))
+ end
+
+ def test_rb_memory_view_extract_item_members_doble_endianness
+ m = MemoryViewTestUtils
+ hi, lo = [1.23].pack("d").unpack("Q")[0].divmod(0x10000)
+ packed = [lo, hi].pack("L*")
+ assert_equal(packed.unpack("E")[0], m.extract_item_members(packed, "E"))
+ packed = [hi, lo].pack("L*")
+ assert_equal(packed.unpack("G")[0], m.extract_item_members(packed, "G"))
+ end
+
+ def test_rb_memory_view_available_p
+ es = MemoryViewTestUtils::ExportableString.new("ruby")
+ assert_equal(true, MemoryViewTestUtils.available?(es))
+ es = MemoryViewTestUtils::ExportableString.new(nil)
+ assert_equal(false, MemoryViewTestUtils.available?(es))
+ end
+
+ def test_ref_count_with_exported_object
+ es = MemoryViewTestUtils::ExportableString.new("ruby")
+ assert_equal(1, MemoryViewTestUtils.ref_count_while_exporting(es, 1))
+ assert_equal(2, MemoryViewTestUtils.ref_count_while_exporting(es, 2))
+ assert_equal(10, MemoryViewTestUtils.ref_count_while_exporting(es, 10))
+ assert_nil(MemoryViewTestUtils.ref_count_while_exporting(es, 0))
+ end
+
+ def test_rb_memory_view_init_as_byte_array
+ # ExportableString's memory view is initialized by rb_memory_view_init_as_byte_array
+ es = MemoryViewTestUtils::ExportableString.new("ruby")
+ memory_view_info = MemoryViewTestUtils.get_memory_view_info(es)
+ assert_equal({
+ obj: es,
+ byte_size: 4,
+ readonly: true,
+ format: nil,
+ item_size: 1,
+ ndim: 1,
+ shape: nil,
+ strides: nil,
+ sub_offsets: nil
+ },
+ memory_view_info)
+ end
+
+ def test_rb_memory_view_get_with_memory_view_unavailable_object
+ es = MemoryViewTestUtils::ExportableString.new(nil)
+ memory_view_info = MemoryViewTestUtils.get_memory_view_info(es)
+ assert_nil(memory_view_info)
+ end
+
+ def test_rb_memory_view_fill_contiguous_strides
+ row_major_strides = MemoryViewTestUtils.fill_contiguous_strides(3, 8, [2, 3, 4], true)
+ assert_equal([96, 32, 8],
+ row_major_strides)
+
+ column_major_strides = MemoryViewTestUtils.fill_contiguous_strides(3, 8, [2, 3, 4], false)
+ assert_equal([8, 16, 48],
+ column_major_strides)
+ end
+
+ def test_rb_memory_view_get_item_pointer_single_member
+ buf = [ 1, 2, 3, 4,
+ 5, 6, 7, 8,
+ 9, 10, 11, 12 ].pack("l!*")
+ shape = [3, 4]
+ mv = MemoryViewTestUtils::MultiDimensionalView.new(buf, "l!", shape, nil)
+ assert_equal(1, mv[[0, 0]])
+ assert_equal(4, mv[[0, 3]])
+ assert_equal(6, mv[[1, 1]])
+ assert_equal(10, mv[[2, 1]])
+ end
+
+ def test_rb_memory_view_get_item_pointer_multiple_members
+ buf = [ 1, 2, 3, 4, 5, 6, 7, 8,
+ -1, -2, -3, -4, -5, -6, -7, -8].pack("s*")
+ shape = [2, 4]
+ strides = [4*sizeof(:short)*2, sizeof(:short)*2]
+ mv = MemoryViewTestUtils::MultiDimensionalView.new(buf, "ss", shape, strides)
+ assert_equal([1, 2], mv[[0, 0]])
+ assert_equal([5, 6], mv[[0, 2]])
+ assert_equal([-1, -2], mv[[1, 0]])
+ assert_equal([-7, -8], mv[[1, 3]])
+ end
+
+ def test_ractor
+ assert_in_out_err([], <<-"end;", ["[5, 6]", "[-7, -8]"], [])
+ require "-test-/memory_view"
+ require "rbconfig/sizeof"
+ $VERBOSE = nil
+ r = Ractor.new RbConfig::SIZEOF["short"] do |sizeof_short|
+ buf = [ 1, 2, 3, 4, 5, 6, 7, 8,
+ -1, -2, -3, -4, -5, -6, -7, -8].pack("s*")
+ shape = [2, 4]
+ strides = [4*sizeof_short*2, sizeof_short*2]
+ mv = MemoryViewTestUtils::MultiDimensionalView.new(buf, "ss", shape, strides)
+ p mv[[0, 2]]
+ mv[[1, 3]]
+ end
+ p r.take
+ end;
+ end
+end
diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb
index bb506f1258..ac50a9d0b0 100644
--- a/test/ruby/test_method.rb
+++ b/test/ruby/test_method.rb
@@ -5,7 +5,6 @@ require 'test/unit'
class TestMethod < Test::Unit::TestCase
def setup
@verbose = $VERBOSE
- $VERBOSE = nil
end
def teardown
@@ -104,6 +103,12 @@ class TestMethod < Test::Unit::TestCase
assert_raise(TypeError) do
um.bind(Base.new)
end
+
+ # cleanup
+ Derived.class_eval do
+ remove_method :foo
+ def foo() :derived; end
+ end
end
def test_callee
@@ -459,6 +464,23 @@ class TestMethod < Test::Unit::TestCase
c3.class_eval { alias bar foo }
m3 = c3.new.method(:bar)
assert_equal("#<Method: #{c3.inspect}(#{c.inspect})#bar(foo)() #{__FILE__}:#{line_no}>", m3.inspect, bug7806)
+
+ bug15608 = '[ruby-core:91570] [Bug #15608]'
+ c4 = Class.new(c)
+ c4.class_eval { alias bar foo }
+ o = c4.new
+ o.singleton_class
+ m4 = o.method(:bar)
+ assert_equal("#<Method: #{c4.inspect}(#{c.inspect})#bar(foo)() #{__FILE__}:#{line_no}>", m4.inspect, bug15608)
+
+ bug17428 = '[ruby-core:101635] [Bug #17428]'
+ assert_equal("#<Method: #<Class:String>(Module)#prepend(*)>", String.method(:prepend).inspect, bug17428)
+
+ c5 = Class.new(String)
+ m = Module.new{def prepend; end; alias prep prepend}; line_no = __LINE__
+ c5.extend(m)
+ c6 = Class.new(c5)
+ assert_equal("#<Method: #<Class:#{c6.inspect}>(#{m.inspect})#prep(prepend)() #{__FILE__}:#{line_no}>", c6.method(:prep).inspect, bug17428)
end
def test_callee_top_level
@@ -556,7 +578,7 @@ class TestMethod < Test::Unit::TestCase
assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyreq, :e], [:key, :f], [:keyrest, :o]], method(:mk8).parameters)
assert_equal([[:nokey]], method(:mnk).parameters)
# pending
- assert_equal([[:rest, :*], [:block, :&]], method(:mf).parameters)
+ assert_equal([[:rest, :*], [:keyrest, :**], [:block, :&]], method(:mf).parameters)
end
def test_unbound_parameters
@@ -582,7 +604,7 @@ class TestMethod < Test::Unit::TestCase
assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyreq, :e], [:key, :f], [:keyrest, :o]], self.class.instance_method(:mk8).parameters)
assert_equal([[:nokey]], self.class.instance_method(:mnk).parameters)
# pending
- assert_equal([[:rest, :*], [:block, :&]], self.class.instance_method(:mf).parameters)
+ assert_equal([[:rest, :*], [:keyrest, :**], [:block, :&]], self.class.instance_method(:mf).parameters)
end
def test_bmethod_bound_parameters
@@ -790,7 +812,9 @@ class TestMethod < Test::Unit::TestCase
assert_instance_of String, __dir__
assert_equal(File.dirname(File.realpath(__FILE__)), __dir__)
bug8436 = '[ruby-core:55123] [Bug #8436]'
- assert_equal(__dir__, eval("__dir__", binding), bug8436)
+ file, line = *binding.source_location
+ file = File.realpath(file)
+ assert_equal(__dir__, eval("__dir__", binding, file, line), bug8436)
bug8662 = '[ruby-core:56099] [Bug #8662]'
assert_equal("arbitrary", eval("__dir__", binding, "arbitrary/file.rb"), bug8662)
assert_equal("arbitrary", Object.new.instance_eval("__dir__", "arbitrary/file.rb"), bug8662)
@@ -812,7 +836,7 @@ class TestMethod < Test::Unit::TestCase
assert_equal(c, c.instance_method(:foo).owner)
assert_equal(c, x.method(:foo).owner)
assert_equal(x.singleton_class, x.method(:bar).owner)
- assert_not_equal(x.method(:foo), x.method(:bar), bug7613)
+ assert_equal(x.method(:foo), x.method(:bar), bug7613)
assert_equal(c, x.method(:zot).owner, bug7993)
assert_equal(c, c.instance_method(:zot).owner, bug7993)
end
@@ -1021,7 +1045,7 @@ class TestMethod < Test::Unit::TestCase
assert_equal(sm, im.clone.bind(o).super_method)
end
- def test_super_method_removed
+ def test_super_method_removed_public
c1 = Class.new {private def foo; end}
c2 = Class.new(c1) {public :foo}
c3 = Class.new(c2) {def foo; end}
@@ -1031,20 +1055,35 @@ class TestMethod < Test::Unit::TestCase
assert_nil(m, Feature9781)
end
+ def test_super_method_removed_regular
+ c1 = Class.new { def foo; end }
+ c2 = Class.new(c1) { def foo; end }
+ assert_equal c1.instance_method(:foo), c2.instance_method(:foo).super_method
+ c1.remove_method :foo
+ assert_equal nil, c2.instance_method(:foo).super_method
+ end
+
def test_prepended_public_zsuper
- mod = EnvUtil.labeled_module("Mod") {private def foo; :ok end}
- mods = [mod]
+ mod = EnvUtil.labeled_module("Mod") {private def foo; [:ok] end}
obj = Object.new.extend(mod)
+
class << obj
public :foo
end
- 2.times do |i|
- mods.unshift(mod = EnvUtil.labeled_module("Mod#{i}") {def foo; end})
- obj.singleton_class.prepend(mod)
- end
+
+ mod1 = EnvUtil.labeled_module("Mod1") {def foo; [:mod1] + super end}
+ obj.singleton_class.prepend(mod1)
+
+ mod2 = EnvUtil.labeled_module("Mod2") {def foo; [:mod2] + super end}
+ obj.singleton_class.prepend(mod2)
+
m = obj.method(:foo)
- assert_equal(mods, mods.map {m.owner.tap {m = m.super_method}})
- assert_nil(m)
+ assert_equal mod2, m.owner
+ assert_equal mod1, m.super_method.owner
+ assert_equal obj.singleton_class, m.super_method.super_method.owner
+ assert_equal nil, m.super_method.super_method.super_method
+
+ assert_equal [:mod2, :mod1, :ok], obj.foo
end
def test_super_method_with_prepended_module
@@ -1064,6 +1103,258 @@ class TestMethod < Test::Unit::TestCase
'[ruby-core:85231] [Bug #14421]'
end
+ def test_super_method_alias
+ c0 = Class.new do
+ def m1
+ [:C0_m1]
+ end
+ def m2
+ [:C0_m2]
+ end
+ end
+
+ c1 = Class.new(c0) do
+ def m1
+ [:C1_m1] + super
+ end
+ alias m2 m1
+ end
+
+ c2 = Class.new(c1) do
+ def m2
+ [:C2_m2] + super
+ end
+ end
+ o1 = c2.new
+ assert_equal([:C2_m2, :C1_m1, :C0_m1], o1.m2)
+
+ m = o1.method(:m2)
+ assert_equal([:C2_m2, :C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:C0_m1], m.call)
+
+ assert_nil(m.super_method)
+ end
+
+ def test_super_method_alias_to_prepended_module
+ m = Module.new do
+ def m1
+ [:P_m1] + super
+ end
+
+ def m2
+ [:P_m2] + super
+ end
+ end
+
+ c0 = Class.new do
+ def m1
+ [:C0_m1]
+ end
+ end
+
+ c1 = Class.new(c0) do
+ def m1
+ [:C1_m1] + super
+ end
+ prepend m
+ alias m2 m1
+ end
+
+ o1 = c1.new
+ assert_equal([:P_m2, :P_m1, :C1_m1, :C0_m1], o1.m2)
+
+ m = o1.method(:m2)
+ assert_equal([:P_m2, :P_m1, :C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:P_m1, :C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:C1_m1, :C0_m1], m.call)
+
+ m = m.super_method
+ assert_equal([:C0_m1], m.call)
+
+ assert_nil(m.super_method)
+ end
+
+ # Bug 17780
+ def test_super_method_module_alias
+ m = Module.new do
+ def foo
+ end
+ alias :f :foo
+ end
+
+ method = m.instance_method(:f)
+ super_method = method.super_method
+ assert_nil(super_method)
+ end
+
+ def test_method_visibility_predicates
+ v = Visibility.new
+ assert_equal(true, v.method(:mv1).public?)
+ assert_equal(true, v.method(:mv2).private?)
+ assert_equal(true, v.method(:mv3).protected?)
+ assert_equal(false, v.method(:mv2).public?)
+ assert_equal(false, v.method(:mv3).private?)
+ assert_equal(false, v.method(:mv1).protected?)
+ end
+
+ def test_unbound_method_visibility_predicates
+ assert_equal(true, Visibility.instance_method(:mv1).public?)
+ assert_equal(true, Visibility.instance_method(:mv2).private?)
+ assert_equal(true, Visibility.instance_method(:mv3).protected?)
+ assert_equal(false, Visibility.instance_method(:mv2).public?)
+ assert_equal(false, Visibility.instance_method(:mv3).private?)
+ assert_equal(false, Visibility.instance_method(:mv1).protected?)
+ end
+
+ # Bug 18435
+ def test_instance_methods_owner_consistency
+ a = Module.new { def method1; end }
+
+ b = Class.new do
+ include a
+ protected :method1
+ end
+
+ assert_equal [:method1], b.instance_methods(false)
+ assert_equal b, b.instance_method(:method1).owner
+ end
+
+ def test_zsuper_method_removed
+ a = EnvUtil.labeled_class('A') do
+ private
+ def foo(arg = nil)
+ 1
+ end
+ end
+ line = __LINE__ - 4
+
+ b = EnvUtil.labeled_class('B', a) do
+ public :foo
+ end
+
+ unbound = b.instance_method(:foo)
+
+ assert_equal unbound, b.public_instance_method(:foo)
+ assert_equal "#<UnboundMethod: B(A)#foo(arg=...) #{__FILE__}:#{line}>", unbound.inspect
+ assert_equal [[:opt, :arg]], unbound.parameters
+
+ a.remove_method(:foo)
+
+ assert_equal "#<UnboundMethod: B(A)#foo(arg=...) #{__FILE__}:#{line}>", unbound.inspect
+ assert_equal [[:opt, :arg]], unbound.parameters
+
+ obj = b.new
+ assert_equal 1, unbound.bind_call(obj)
+
+ assert_include b.instance_methods(false), :foo
+ link = 'https://github.com/ruby/ruby/pull/6467#issuecomment-1262159088'
+ assert_raise(NameError, link) { b.instance_method(:foo) }
+ # For #test_method_list below, otherwise we get the same error as just above
+ b.remove_method(:foo)
+ end
+
+ def test_zsuper_method_removed_higher_method
+ a0 = EnvUtil.labeled_class('A0') do
+ def foo(arg1 = nil, arg2 = nil)
+ 0
+ end
+ end
+ line0 = __LINE__ - 4
+ a0_foo = a0.instance_method(:foo)
+
+ a = EnvUtil.labeled_class('A', a0) do
+ private
+ def foo(arg = nil)
+ 1
+ end
+ end
+ line = __LINE__ - 4
+
+ b = EnvUtil.labeled_class('B', a) do
+ public :foo
+ end
+
+ unbound = b.instance_method(:foo)
+
+ assert_equal a0_foo, unbound.super_method
+
+ a.remove_method(:foo)
+
+ assert_equal "#<UnboundMethod: B(A)#foo(arg=...) #{__FILE__}:#{line}>", unbound.inspect
+ assert_equal [[:opt, :arg]], unbound.parameters
+ assert_equal a0_foo, unbound.super_method
+
+ obj = b.new
+ assert_equal 1, unbound.bind_call(obj)
+
+ assert_include b.instance_methods(false), :foo
+ assert_equal "#<UnboundMethod: B(A0)#foo(arg1=..., arg2=...) #{__FILE__}:#{line0}>", b.instance_method(:foo).inspect
+ end
+
+ def test_zsuper_method_redefined_bind_call
+ c0 = EnvUtil.labeled_class('C0') do
+ def foo
+ [:foo]
+ end
+ end
+
+ c1 = EnvUtil.labeled_class('C1', c0) do
+ def foo
+ super + [:bar]
+ end
+ end
+ m1 = c1.instance_method(:foo)
+
+ c2 = EnvUtil.labeled_class('C2', c1) do
+ private :foo
+ end
+
+ assert_equal [:foo], c2.private_instance_methods(false)
+ m2 = c2.instance_method(:foo)
+
+ c1.class_exec do
+ def foo
+ [:bar2]
+ end
+ end
+
+ m3 = c2.instance_method(:foo)
+ c = c2.new
+ assert_equal [:foo, :bar], m1.bind_call(c)
+ assert_equal c1, m1.owner
+ assert_equal [:foo, :bar], m2.bind_call(c)
+ assert_equal c2, m2.owner
+ assert_equal [:bar2], m3.bind_call(c)
+ assert_equal c2, m3.owner
+ end
+
+ # Bug #18751
+ def method_equality_visbility_alias
+ c = Class.new do
+ class << self
+ alias_method :n, :new
+ private :new
+ end
+ end
+
+ assert_equal c.method(:n), c.method(:new)
+
+ assert_not_equal c.method(:n), Class.method(:new)
+ assert_equal c.method(:n) == Class.instance_method(:new).bind(c)
+
+ assert_not_equal c.method(:new), Class.method(:new)
+ assert_equal c.method(:new), Class.instance_method(:new).bind(c)
+ end
+
def rest_parameter(*rest)
rest
end
@@ -1100,17 +1391,22 @@ class TestMethod < Test::Unit::TestCase
assert_equal([:bar, :foo], b.local_variables.sort, bug11012)
end
- class MethodInMethodClass
- def m1
- def m2
- end
+ MethodInMethodClass_Setup = -> do
+ remove_const :MethodInMethodClass if defined? MethodInMethodClass
- self.class.send(:define_method, :m3){} # [Bug #11754]
+ class MethodInMethodClass
+ def m1
+ def m2
+ end
+ self.class.send(:define_method, :m3){} # [Bug #11754]
+ end
+ private
end
- private
end
def test_method_in_method_visibility_should_be_public
+ MethodInMethodClass_Setup.call
+
assert_equal([:m1].sort, MethodInMethodClass.public_instance_methods(false).sort)
assert_equal([].sort, MethodInMethodClass.private_instance_methods(false).sort)
@@ -1160,6 +1456,42 @@ class TestMethod < Test::Unit::TestCase
assert_separately [], "RubyVM::InstructionSequence.compile_option = {trace_instruction: false}\n" + body
end
+ def test_zsuper_private_override_instance_method
+ assert_separately(%w(--disable-gems), <<-'end;', timeout: 30)
+ # Bug #16942 [ruby-core:98691]
+ module M
+ def x
+ end
+ end
+
+ module M2
+ prepend Module.new
+ include M
+ private :x
+ end
+
+ ::Object.prepend(M2)
+
+ m = Object.instance_method(:x)
+ assert_equal M2, m.owner
+ end;
+ end
+
+ def test_override_optimized_method_on_class_using_prepend
+ assert_separately(%w(--disable-gems), <<-'end;', timeout: 30)
+ # Bug #17725 [ruby-core:102884]
+ $VERBOSE = nil
+ String.prepend(Module.new)
+ class String
+ def + other
+ 'blah blah'
+ end
+ end
+
+ assert_equal('blah blah', 'a' + 'b')
+ end;
+ end
+
def test_eqq
assert_operator(0.method(:<), :===, 5)
assert_not_operator(0.method(:<), :===, -5)
@@ -1266,4 +1598,8 @@ class TestMethod < Test::Unit::TestCase
assert_operator nummodule, :>, 0
assert_operator nummethod, :>, 0
end
+
+ def test_invalidating_CC_ASAN
+ assert_ruby_status(['-e', 'using Module.new'])
+ end
end
diff --git a/test/ruby/test_method_cache.rb b/test/ruby/test_method_cache.rb
new file mode 100644
index 0000000000..2ed89e47bf
--- /dev/null
+++ b/test/ruby/test_method_cache.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+require 'test/unit'
+
+class TestMethodCache < Test::Unit::TestCase
+ def test_undef
+ # clear same
+ c0 = Class.new do
+ def foo; end
+ undef foo
+ end
+
+ assert_raise(NoMethodError) do
+ c0.new.foo
+ end
+
+ c0.class_eval do
+ def foo; :ok; end
+ end
+
+ assert_equal :ok, c0.new.foo
+ end
+
+ def test_undef_with_subclasses
+ # with subclasses
+ c0 = Class.new do
+ def foo; end
+ undef foo
+ end
+
+ _c1 = Class.new(c0)
+
+ assert_raise(NoMethodError) do
+ c0.new.foo
+ end
+
+ c0.class_eval do
+ def foo; :ok; end
+ end
+
+ assert_equal :ok, c0.new.foo
+ end
+
+ def test_undef_with_subclasses_complicated
+ c0 = Class.new{ def foo; end }
+ c1 = Class.new(c0){ undef foo }
+ c2 = Class.new(c1)
+ c3 = Class.new(c2)
+ _c4 = Class.new(c3)
+
+ assert_raise(NoMethodError) do
+ c3.new.foo
+ end
+
+ c2.class_eval do
+ def foo; :c2; end
+ end
+
+ assert_raise(NoMethodError) do
+ c1.new.foo
+ end
+
+ assert_equal :c2, c3.new.foo
+ end
+
+ def test_negative_cache_with_and_without_subclasses
+ c0 = Class.new{}
+ c1 = Class.new(c0){}
+ c0.new.foo rescue nil
+ c1.new.foo rescue nil
+ c1.module_eval{ def foo = :c1 }
+ c0.module_eval{ def foo = :c0 }
+
+ assert_equal :c0, c0.new.foo
+ end
+end
+
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index 3ddfcd9c4f..b5414d139e 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -27,11 +27,13 @@ class TestModule < Test::Unit::TestCase
def setup
@verbose = $VERBOSE
- $VERBOSE = nil
+ @deprecated = Warning[:deprecated]
+ Warning[:deprecated] = true
end
def teardown
$VERBOSE = @verbose
+ Warning[:deprecated] = @deprecated
end
def test_LT_0
@@ -85,8 +87,11 @@ class TestModule < Test::Unit::TestCase
private :user3
end
- module Other
- def other
+ OtherSetup = -> do
+ remove_const :Other if defined? ::TestModule::Other
+ module Other
+ def other
+ end
end
end
@@ -220,6 +225,8 @@ class TestModule < Test::Unit::TestCase
@@class_eval = 'b'
def test_class_eval
+ OtherSetup.call
+
Other.class_eval("CLASS_EVAL = 1")
assert_equal(1, Other::CLASS_EVAL)
assert_include(Other.constants, :CLASS_EVAL)
@@ -260,7 +267,7 @@ class TestModule < Test::Unit::TestCase
].each do |name, msg|
expected = "wrong constant name %s" % name
msg = "#{msg}#{': ' if msg}wrong constant name #{name.dump}"
- assert_raise_with_message(NameError, expected, "#{msg} to #{m}") do
+ assert_raise_with_message(NameError, Regexp.compile(Regexp.quote(expected)), "#{msg} to #{m}") do
yield name
end
end
@@ -294,6 +301,8 @@ class TestModule < Test::Unit::TestCase
end
def test_nested_get
+ OtherSetup.call
+
assert_equal Other, Object.const_get([self.class, 'Other'].join('::'))
assert_equal User::USER, self.class.const_get([User, 'USER'].join('::'))
assert_raise(NameError) {
@@ -302,6 +311,8 @@ class TestModule < Test::Unit::TestCase
end
def test_nested_get_symbol
+ OtherSetup.call
+
const = [self.class, Other].join('::').to_sym
assert_raise(NameError) {Object.const_get(const)}
@@ -328,6 +339,8 @@ class TestModule < Test::Unit::TestCase
end
def test_nested_defined
+ OtherSetup.call
+
assert_send([Object, :const_defined?, [self.class.name, 'Other'].join('::')])
assert_send([self.class, :const_defined?, 'User::USER'])
assert_not_send([self.class, :const_defined?, 'User::Foo'])
@@ -335,6 +348,8 @@ class TestModule < Test::Unit::TestCase
end
def test_nested_defined_symbol
+ OtherSetup.call
+
const = [self.class, Other].join('::').to_sym
assert_raise(NameError) {Object.const_defined?(const)}
@@ -360,6 +375,8 @@ class TestModule < Test::Unit::TestCase
end
def test_const_set
+ OtherSetup.call
+
assert_not_operator(Other, :const_defined?, :KOALA)
Other.const_set(:KOALA, 99)
assert_operator(Other, :const_defined?, :KOALA)
@@ -387,19 +404,20 @@ class TestModule < Test::Unit::TestCase
assert_equal([:MIXIN, :USER], User.constants.sort)
end
- def test_self_initialize_copy
- bug9535 = '[ruby-dev:47989] [Bug #9535]'
- m = Module.new do
- def foo
- :ok
- end
- initialize_copy(self)
+ def test_initialize_copy
+ mod = Module.new { define_method(:foo) {:first} }
+ klass = Class.new { include mod }
+ instance = klass.new
+ assert_equal(:first, instance.foo)
+ new_mod = Module.new { define_method(:foo) { :second } }
+ assert_raise(TypeError) do
+ mod.send(:initialize_copy, new_mod)
end
- assert_equal(:ok, Object.new.extend(m).foo, bug9535)
+ 4.times { GC.start }
+ assert_equal(:first, instance.foo) # [BUG] unreachable
end
def test_initialize_copy_empty
- bug9813 = '[ruby-dev:48182] [Bug #9813]'
m = Module.new do
def x
end
@@ -409,15 +427,66 @@ class TestModule < Test::Unit::TestCase
assert_equal([:x], m.instance_methods)
assert_equal([:@x], m.instance_variables)
assert_equal([:X], m.constants)
- m.module_eval do
- initialize_copy(Module.new)
+ assert_raise(TypeError) do
+ m.module_eval do
+ initialize_copy(Module.new)
+ end
+ end
+
+ m = Class.new(Module) do
+ def initialize_copy(other)
+ # leave uninitialized
+ end
+ end.new.dup
+ c = Class.new
+ assert_operator(c.include(m), :<, m)
+ cp = Module.instance_method(:initialize_copy)
+ assert_raise(TypeError) do
+ cp.bind_call(m, Module.new)
+ end
+ end
+
+ class Bug18185 < Module
+ module InstanceMethods
+ end
+ attr_reader :ancestor_list
+ def initialize
+ @ancestor_list = ancestors
+ include InstanceMethods
end
- assert_empty(m.instance_methods, bug9813)
- assert_empty(m.instance_variables, bug9813)
- assert_empty(m.constants, bug9813)
+ class Foo
+ attr_reader :key
+ def initialize(key:)
+ @key = key
+ end
+ end
+ end
+
+ def test_module_subclass_initialize
+ mod = Bug18185.new
+ c = Class.new(Bug18185::Foo) do
+ include mod
+ end
+ anc = c.ancestors
+ assert_include(anc, mod)
+ assert_equal(1, anc.count(BasicObject), ->{anc.inspect})
+ b = c.new(key: 1)
+ assert_equal(1, b.key)
+ assert_not_include(mod.ancestor_list, BasicObject)
+ end
+
+ def test_module_collected_extended_object
+ m1 = labeled_module("m1")
+ m2 = labeled_module("m2")
+ Object.new.extend(m1)
+ GC.start
+ m1.include(m2)
+ assert_equal([m1, m2], m1.ancestors)
end
def test_dup
+ OtherSetup.call
+
bug6454 = '[ruby-core:45132]'
a = Module.new
@@ -459,6 +528,169 @@ class TestModule < Test::Unit::TestCase
assert_raise(ArgumentError) { Module.new { include } }
end
+ def test_include_before_initialize
+ m = Class.new(Module) do
+ def initialize(...)
+ include Enumerable
+ super
+ end
+ end.new
+ assert_equal(true, m < Enumerable)
+ end
+
+ def test_prepend_self
+ m = Module.new
+ assert_equal([m], m.ancestors)
+ m.prepend(m) rescue nil
+ assert_equal([m], m.ancestors)
+ end
+
+ def test_bug17590
+ m = Module.new
+ c = Class.new
+ c.prepend(m)
+ c.include(m)
+ m.prepend(m) rescue nil
+ m2 = Module.new
+ m2.prepend(m)
+ c.include(m2)
+
+ assert_equal([m, c, m2] + Object.ancestors, c.ancestors)
+ end
+
+ def test_prepend_works_with_duped_classes
+ m = Module.new
+ a = Class.new do
+ def b; 2 end
+ prepend m
+ end
+ a2 = a.dup.new
+ a.class_eval do
+ alias _b b
+ def b; 1 end
+ end
+ assert_equal(2, a2.b)
+ end
+
+ def test_gc_prepend_chain
+ assert_separately([], <<-EOS)
+ 10000.times { |i|
+ m1 = Module.new do
+ def foo; end
+ end
+ m2 = Module.new do
+ prepend m1
+ def bar; end
+ end
+ m3 = Module.new do
+ def baz; end
+ prepend m2
+ end
+ Class.new do
+ prepend m3
+ end
+ }
+ GC.start
+ EOS
+ end
+
+ def test_refine_module_then_include
+ assert_separately([], "#{<<~"end;"}\n")
+ module M
+ end
+ class C
+ include M
+ end
+ module RefinementBug
+ refine M do
+ def refined_method
+ :rm
+ end
+ end
+ end
+ using RefinementBug
+
+ class A
+ include M
+ end
+
+ assert_equal(:rm, C.new.refined_method)
+ end;
+ end
+
+ def test_include_into_module_already_included
+ c = Class.new{def foo; [:c] end}
+ modules = lambda do ||
+ sub = Class.new(c){def foo; [:sc] + super end}
+ [
+ Module.new{def foo; [:m1] + super end},
+ Module.new{def foo; [:m2] + super end},
+ Module.new{def foo; [:m3] + super end},
+ sub,
+ sub.new
+ ]
+ end
+
+ m1, m2, m3, sc, o = modules.call
+ assert_equal([:sc, :c], o.foo)
+ sc.include m1
+ assert_equal([:sc, :m1, :c], o.foo)
+ m1.include m2
+ assert_equal([:sc, :m1, :m2, :c], o.foo)
+ m2.include m3
+ assert_equal([:sc, :m1, :m2, :m3, :c], o.foo)
+
+ m1, m2, m3, sc, o = modules.call
+ sc.prepend m1
+ assert_equal([:m1, :sc, :c], o.foo)
+ m1.include m2
+ assert_equal([:m1, :m2, :sc, :c], o.foo)
+ m2.include m3
+ assert_equal([:m1, :m2, :m3, :sc, :c], o.foo)
+
+ m1, m2, m3, sc, o = modules.call
+ sc.include m2
+ assert_equal([:sc, :m2, :c], o.foo)
+ sc.prepend m1
+ assert_equal([:m1, :sc, :m2, :c], o.foo)
+ m1.include m2
+ assert_equal([:m1, :sc, :m2, :c], o.foo)
+ m1.include m3
+ assert_equal([:m1, :m3, :sc, :m2, :c], o.foo)
+
+ m1, m2, m3, sc, o = modules.call
+ sc.include m3
+ sc.include m2
+ assert_equal([:sc, :m2, :m3, :c], o.foo)
+ sc.prepend m1
+ assert_equal([:m1, :sc, :m2, :m3, :c], o.foo)
+ m1.include m2
+ m1.include m3
+ assert_equal([:m1, :sc, :m2, :m3, :c], o.foo)
+
+ m1, m2, m3, sc, o = modules.call
+ assert_equal([:sc, :c], o.foo)
+ sc.prepend m1
+ assert_equal([:m1, :sc, :c], o.foo)
+ m1.prepend m2
+ assert_equal([:m2, :m1, :sc, :c], o.foo)
+ m2.prepend m3
+ assert_equal([:m3, :m2, :m1, :sc, :c], o.foo)
+ m1, m2, m3, sc, o = modules.call
+ sc.include m1
+ assert_equal([:sc, :m1, :c], o.foo)
+ sc.prepend m2
+ assert_equal([:m2, :sc, :m1, :c], o.foo)
+ sc.prepend m3
+ assert_equal([:m3, :m2, :sc, :m1, :c], o.foo)
+ m1, m2, m3, sc, o = modules.call
+ sc.include m1
+ assert_equal([:sc, :m1, :c], o.foo)
+ m2.prepend m3
+ m1.include m2
+ assert_equal([:sc, :m1, :m3, :m2, :c], o.foo)
+ end
+
def test_included_modules
assert_equal([], Mixin.included_modules)
assert_equal([Mixin], User.included_modules)
@@ -469,6 +701,61 @@ class TestModule < Test::Unit::TestCase
assert_equal([Comparable, Kernel], String.included_modules - mixins)
end
+ def test_included_modules_with_prepend
+ m1 = Module.new
+ m2 = Module.new
+ m3 = Module.new
+
+ m2.prepend m1
+ m3.include m2
+ assert_equal([m1, m2], m3.included_modules)
+ end
+
+ def test_include_with_prepend
+ c = Class.new{def m; [:c] end}
+ p = Module.new{def m; [:p] + super end}
+ q = Module.new{def m; [:q] + super end; include p}
+ r = Module.new{def m; [:r] + super end; prepend q}
+ s = Module.new{def m; [:s] + super end; include r}
+ a = Class.new(c){def m; [:a] + super end; prepend p; include s}
+ assert_equal([:p, :a, :s, :q, :r, :c], a.new.m)
+ end
+
+ def test_prepend_after_include
+ c = Class.new{def m; [:c] end}
+ sc = Class.new(c){def m; [:sc] + super end}
+ m = Module.new{def m; [:m] + super end}
+ sc.include m
+ sc.prepend m
+ sc.prepend m
+ assert_equal([:m, :sc, :m, :c], sc.new.m)
+
+ c = Class.new{def m; [:c] end}
+ sc = Class.new(c){def m; [:sc] + super end}
+ m0 = Module.new{def m; [:m0] + super end}
+ m1 = Module.new{def m; [:m1] + super end}
+ m1.prepend m0
+ sc.include m1
+ sc.prepend m1
+ assert_equal([:m0, :m1, :sc, :m0, :m1, :c], sc.new.m)
+ sc.prepend m
+ assert_equal([:m, :m0, :m1, :sc, :m0, :m1, :c], sc.new.m)
+ sc.prepend m1
+ assert_equal([:m, :m0, :m1, :sc, :m0, :m1, :c], sc.new.m)
+
+
+ c = Class.new{def m; [:c] end}
+ sc = Class.new(c){def m; [:sc] + super end}
+ m0 = Module.new{def m; [:m0] + super end}
+ m1 = Module.new{def m; [:m1] + super end}
+ m1.include m0
+ sc.include m1
+ sc.prepend m
+ sc.prepend m1
+ sc.prepend m1
+ assert_equal([:m1, :m0, :m, :sc, :m1, :m0, :c], sc.new.m)
+ end
+
def test_instance_methods
assert_equal([:user, :user2], User.instance_methods(false).sort)
assert_equal([:user, :user2, :mixin].sort, User.instance_methods(true).sort)
@@ -519,6 +806,11 @@ class TestModule < Test::Unit::TestCase
assert !c.method_defined?(:userx, false)
c.define_method(:userx){}
assert c.method_defined?(:userx, false)
+
+ # cleanup
+ User.class_eval do
+ remove_const :FOO
+ end
end
def module_exec_aux
@@ -549,6 +841,14 @@ class TestModule < Test::Unit::TestCase
def dynamically_added_method_4; end
end
assert_method_defined?(User, :dynamically_added_method_4)
+
+ # cleanup
+ User.class_eval do
+ remove_method :dynamically_added_method_1
+ remove_method :dynamically_added_method_2
+ remove_method :dynamically_added_method_3
+ remove_method :dynamically_added_method_4
+ end
end
def test_module_eval
@@ -592,13 +892,13 @@ class TestModule < Test::Unit::TestCase
n = Module.new
m.const_set(:N, n)
assert_nil(m.name)
- assert_nil(n.name)
+ assert_match(/::N$/, n.name)
assert_equal([:N], m.constants)
m.module_eval("module O end")
assert_equal([:N, :O], m.constants.sort)
m.module_eval("class C; end")
assert_equal([:C, :N, :O], m.constants.sort)
- assert_nil(m::N.name)
+ assert_match(/::N$/, m::N.name)
assert_match(/\A#<Module:.*>::O\z/, m::O.name)
assert_match(/\A#<Module:.*>::C\z/, m::C.name)
self.class.const_set(:M, m)
@@ -608,12 +908,19 @@ class TestModule < Test::Unit::TestCase
assert_equal(prefix+"C", m.const_get(:C).name)
c = m.class_eval("Bug15891 = Class.new.freeze")
assert_equal(prefix+"Bug15891", c.name)
+ ensure
+ self.class.class_eval {remove_const(:M)}
end
def test_private_class_method
assert_raise(ExpectedException) { AClass.cm1 }
assert_raise(ExpectedException) { AClass.cm3 }
assert_equal("cm1cm2cm3", AClass.cm2)
+
+ c = Class.new(AClass)
+ c.class_eval {private_class_method [:cm1, :cm2]}
+ assert_raise(NoMethodError, /private method/) {c.cm1}
+ assert_raise(NoMethodError, /private method/) {c.cm2}
end
def test_private_instance_methods
@@ -636,6 +943,11 @@ class TestModule < Test::Unit::TestCase
assert_equal("cm1", MyClass.cm1)
assert_equal("cm1cm2cm3", MyClass.cm2)
assert_raise(ExpectedException) { eval "MyClass.cm3" }
+
+ c = Class.new(AClass)
+ c.class_eval {public_class_method [:cm1, :cm2]}
+ assert_equal("cm1", c.cm1)
+ assert_equal("cm1cm2cm3", c.cm2)
end
def test_public_instance_methods
@@ -643,12 +955,116 @@ class TestModule < Test::Unit::TestCase
assert_equal([:bClass1], BClass.public_instance_methods(false))
end
+ def test_s_public
+ o = (c = Class.new(AClass)).new
+ assert_raise(NoMethodError, /private method/) {o.aClass1}
+ assert_raise(NoMethodError, /protected method/) {o.aClass2}
+ c.class_eval {public :aClass1}
+ assert_equal(:aClass1, o.aClass1)
+
+ o = (c = Class.new(AClass)).new
+ c.class_eval {public :aClass1, :aClass2}
+ assert_equal(:aClass1, o.aClass1)
+ assert_equal(:aClass2, o.aClass2)
+
+ o = (c = Class.new(AClass)).new
+ c.class_eval {public [:aClass1, :aClass2]}
+ assert_equal(:aClass1, o.aClass1)
+ assert_equal(:aClass2, o.aClass2)
+
+ o = AClass.new
+ assert_equal(:aClass, o.aClass)
+ assert_raise(NoMethodError, /private method/) {o.aClass1}
+ assert_raise(NoMethodError, /protected method/) {o.aClass2}
+ end
+
+ def test_s_private
+ o = (c = Class.new(AClass)).new
+ assert_equal(:aClass, o.aClass)
+ c.class_eval {private :aClass}
+ assert_raise(NoMethodError, /private method/) {o.aClass}
+
+ o = (c = Class.new(AClass)).new
+ c.class_eval {private :aClass, :aClass2}
+ assert_raise(NoMethodError, /private method/) {o.aClass}
+ assert_raise(NoMethodError, /private method/) {o.aClass2}
+
+ o = (c = Class.new(AClass)).new
+ c.class_eval {private [:aClass, :aClass2]}
+ assert_raise(NoMethodError, /private method/) {o.aClass}
+ assert_raise(NoMethodError, /private method/) {o.aClass2}
+
+ o = AClass.new
+ assert_equal(:aClass, o.aClass)
+ assert_raise(NoMethodError, /private method/) {o.aClass1}
+ assert_raise(NoMethodError, /protected method/) {o.aClass2}
+ end
+
+ def test_s_protected
+ aclass = Class.new(AClass) do
+ def _aClass(o) o.aClass; end
+ def _aClass1(o) o.aClass1; end
+ def _aClass2(o) o.aClass2; end
+ end
+
+ o = (c = Class.new(aclass)).new
+ assert_equal(:aClass, o.aClass)
+ c.class_eval {protected :aClass}
+ assert_raise(NoMethodError, /protected method/) {o.aClass}
+ assert_equal(:aClass, c.new._aClass(o))
+
+ o = (c = Class.new(aclass)).new
+ c.class_eval {protected :aClass, :aClass1}
+ assert_raise(NoMethodError, /protected method/) {o.aClass}
+ assert_raise(NoMethodError, /protected method/) {o.aClass1}
+ assert_equal(:aClass, c.new._aClass(o))
+ assert_equal(:aClass1, c.new._aClass1(o))
+
+ o = (c = Class.new(aclass)).new
+ c.class_eval {protected [:aClass, :aClass1]}
+ assert_raise(NoMethodError, /protected method/) {o.aClass}
+ assert_raise(NoMethodError, /protected method/) {o.aClass1}
+ assert_equal(:aClass, c.new._aClass(o))
+ assert_equal(:aClass1, c.new._aClass1(o))
+
+ o = AClass.new
+ assert_equal(:aClass, o.aClass)
+ assert_raise(NoMethodError, /private method/) {o.aClass1}
+ assert_raise(NoMethodError, /protected method/) {o.aClass2}
+ end
+
+ def test_visibility_method_return_value
+ no_arg_results = nil
+ c = Module.new do
+ singleton_class.send(:public, :public, :private, :protected, :module_function)
+ def foo; end
+ def bar; end
+ no_arg_results = [public, private, protected, module_function]
+ end
+
+ assert_equal([nil]*4, no_arg_results)
+
+ assert_equal(:foo, c.private(:foo))
+ assert_equal(:foo, c.public(:foo))
+ assert_equal(:foo, c.protected(:foo))
+ assert_equal(:foo, c.module_function(:foo))
+
+ assert_equal([:foo, :bar], c.private(:foo, :bar))
+ assert_equal([:foo, :bar], c.public(:foo, :bar))
+ assert_equal([:foo, :bar], c.protected(:foo, :bar))
+ assert_equal([:foo, :bar], c.module_function(:foo, :bar))
+ end
+
def test_s_constants
c1 = Module.constants
Object.module_eval "WALTER = 99"
c2 = Module.constants
assert_equal([:WALTER], c2 - c1)
+ Object.class_eval do
+ remove_const :WALTER
+ end
+
assert_equal([], Module.constants(true))
assert_equal([], Module.constants(false))
@@ -711,14 +1127,19 @@ class TestModule < Test::Unit::TestCase
end
def test_attr_obsoleted_flag
- c = Class.new
- c.class_eval do
+ c = Class.new do
+ extend Test::Unit::Assertions
+ extend Test::Unit::CoreAssertions
def initialize
@foo = :foo
@bar = :bar
end
- attr :foo, true
- attr :bar, false
+ assert_deprecated_warning(/optional boolean argument/) do
+ attr :foo, true
+ end
+ assert_deprecated_warning(/optional boolean argument/) do
+ attr :bar, false
+ end
end
o = c.new
assert_equal(true, o.respond_to?(:foo))
@@ -763,6 +1184,7 @@ class TestModule < Test::Unit::TestCase
assert_equal(:foo, c2.const_get(:Foo))
assert_raise(NameError) { c2.const_get(:Foo, false) }
+ c1.__send__(:remove_const, :Foo)
eval("c1::Foo = :foo")
assert_raise(NameError) { c1::Bar }
assert_raise(NameError) { c2::Bar }
@@ -960,6 +1382,28 @@ class TestModule < Test::Unit::TestCase
assert_raise(NameError) do
c.instance_eval { attr_reader :"." }
end
+
+ assert_equal([:a], c.class_eval { attr :a })
+ assert_equal([:b, :c], c.class_eval { attr :b, :c })
+ assert_equal([:d], c.class_eval { attr_reader :d })
+ assert_equal([:e, :f], c.class_eval { attr_reader :e, :f })
+ assert_equal([:g=], c.class_eval { attr_writer :g })
+ assert_equal([:h=, :i=], c.class_eval { attr_writer :h, :i })
+ assert_equal([:j, :j=], c.class_eval { attr_accessor :j })
+ assert_equal([:k, :k=, :l, :l=], c.class_eval { attr_accessor :k, :l })
+ end
+
+ def test_alias_method
+ c = Class.new do
+ def foo; :foo end
+ end
+ o = c.new
+ assert_respond_to(o, :foo)
+ assert_not_respond_to(o, :bar)
+ r = c.class_eval {alias_method :bar, :foo}
+ assert_respond_to(o, :bar)
+ assert_equal(:foo, o.bar)
+ assert_equal(:bar, r)
end
def test_undef
@@ -1109,13 +1553,25 @@ class TestModule < Test::Unit::TestCase
end
def test_top_public_private
- assert_in_out_err([], <<-INPUT, %w([:foo] [:bar]), [])
+ assert_in_out_err([], <<-INPUT, %w([:foo] [:bar] [:bar,\ :foo] [] [:bar,\ :foo] []), [])
private
def foo; :foo; end
public
def bar; :bar; end
p self.private_methods.grep(/^foo$|^bar$/)
p self.methods.grep(/^foo$|^bar$/)
+
+ private :foo, :bar
+ p self.private_methods.grep(/^foo$|^bar$/).sort
+
+ public :foo, :bar
+ p self.private_methods.grep(/^foo$|^bar$/).sort
+
+ private [:foo, :bar]
+ p self.private_methods.grep(/^foo$|^bar$/).sort
+
+ public [:foo, :bar]
+ p self.private_methods.grep(/^foo$|^bar$/).sort
INPUT
end
@@ -1576,21 +2032,31 @@ class TestModule < Test::Unit::TestCase
c = Class.new
c.const_set(:FOO, "foo")
c.deprecate_constant(:FOO)
- assert_warn(/deprecated/) {c::FOO}
- assert_warn(/#{c}::FOO is deprecated/) {Class.new(c)::FOO}
+ assert_warn(/deprecated/) do
+ Warning[:deprecated] = true
+ c::FOO
+ end
+ assert_warn(/#{c}::FOO is deprecated/) do
+ Warning[:deprecated] = true
+ Class.new(c)::FOO
+ end
bug12382 = '[ruby-core:75505] [Bug #12382]'
- assert_warn(/deprecated/, bug12382) {c.class_eval "FOO"}
- end
-
- NIL = nil
- FALSE = false
- deprecate_constant(:NIL, :FALSE)
-
- def test_deprecate_nil_constant
- w = EnvUtil.verbose_warning {2.times {FALSE}}
- assert_equal(1, w.scan("::FALSE").size, w)
- w = EnvUtil.verbose_warning {2.times {NIL}}
- assert_equal(1, w.scan("::NIL").size, w)
+ assert_warn(/deprecated/, bug12382) do
+ Warning[:deprecated] = true
+ c.class_eval "FOO"
+ end
+ assert_warn('') do
+ Warning[:deprecated] = false
+ c::FOO
+ end
+ assert_warn('') do
+ Warning[:deprecated] = false
+ Class.new(c)::FOO
+ end
+ assert_warn('') do
+ Warning[:deprecated] = false
+ c.class_eval "FOO"
+ end
end
def test_constants_with_private_constant
@@ -1830,7 +2296,7 @@ class TestModule < Test::Unit::TestCase
assert_equal([:c2, :m0, :m1, :m2, :c0], c2.new.x)
m3 = labeled_module("m3") {include m1; prepend m1}
- assert_equal([m3, m0, m1], m3.ancestors)
+ assert_equal([m0, m1, m3, m0, m1], m3.ancestors)
m3 = labeled_module("m3") {prepend m1; include m1}
assert_equal([m0, m1, m3], m3.ancestors)
m3 = labeled_module("m3") {prepend m1; prepend m1}
@@ -1911,6 +2377,137 @@ class TestModule < Test::Unit::TestCase
assert_equal(0, 1 / 2)
end
+ def test_visibility_after_refine_and_visibility_change_with_origin_class
+ m = Module.new
+ c = Class.new do
+ def x; :x end
+ end
+ c.prepend(m)
+ Module.new do
+ refine c do
+ def x; :y end
+ end
+ end
+
+ o1 = c.new
+ o2 = c.new
+ assert_equal(:x, o1.public_send(:x))
+ assert_equal(:x, o2.public_send(:x))
+ o1.singleton_class.send(:private, :x)
+ o2.singleton_class.send(:public, :x)
+
+ assert_raise(NoMethodError) { o1.public_send(:x) }
+ assert_equal(:x, o2.public_send(:x))
+ end
+
+ def test_visibility_after_multiple_refine_and_visibility_change_with_origin_class
+ m = Module.new
+ c = Class.new do
+ def x; :x end
+ end
+ c.prepend(m)
+ Module.new do
+ refine c do
+ def x; :y end
+ end
+ end
+ Module.new do
+ refine c do
+ def x; :z end
+ end
+ end
+
+ o1 = c.new
+ o2 = c.new
+ assert_equal(:x, o1.public_send(:x))
+ assert_equal(:x, o2.public_send(:x))
+ o1.singleton_class.send(:private, :x)
+ o2.singleton_class.send(:public, :x)
+
+ assert_raise(NoMethodError) { o1.public_send(:x) }
+ assert_equal(:x, o2.public_send(:x))
+ end
+
+ def test_visibility_after_refine_and_visibility_change_without_origin_class
+ c = Class.new do
+ def x; :x end
+ end
+ Module.new do
+ refine c do
+ def x; :y end
+ end
+ end
+ o1 = c.new
+ o2 = c.new
+ o1.singleton_class.send(:private, :x)
+ o2.singleton_class.send(:public, :x)
+ assert_raise(NoMethodError) { o1.public_send(:x) }
+ assert_equal(:x, o2.public_send(:x))
+ end
+
+ def test_visibility_after_multiple_refine_and_visibility_change_without_origin_class
+ c = Class.new do
+ def x; :x end
+ end
+ Module.new do
+ refine c do
+ def x; :y end
+ end
+ end
+ Module.new do
+ refine c do
+ def x; :z end
+ end
+ end
+ o1 = c.new
+ o2 = c.new
+ o1.singleton_class.send(:private, :x)
+ o2.singleton_class.send(:public, :x)
+ assert_raise(NoMethodError) { o1.public_send(:x) }
+ assert_equal(:x, o2.public_send(:x))
+ end
+
+ def test_visibility_after_refine_and_visibility_change_with_superclass
+ c = Class.new do
+ def x; :x end
+ end
+ sc = Class.new(c)
+ Module.new do
+ refine sc do
+ def x; :y end
+ end
+ end
+ o1 = sc.new
+ o2 = sc.new
+ o1.singleton_class.send(:private, :x)
+ o2.singleton_class.send(:public, :x)
+ assert_raise(NoMethodError) { o1.public_send(:x) }
+ assert_equal(:x, o2.public_send(:x))
+ end
+
+ def test_visibility_after_multiple_refine_and_visibility_change_with_superclass
+ c = Class.new do
+ def x; :x end
+ end
+ sc = Class.new(c)
+ Module.new do
+ refine sc do
+ def x; :y end
+ end
+ end
+ Module.new do
+ refine sc do
+ def x; :z end
+ end
+ end
+ o1 = sc.new
+ o2 = sc.new
+ o1.singleton_class.send(:private, :x)
+ o2.singleton_class.send(:public, :x)
+ assert_raise(NoMethodError) { o1.public_send(:x) }
+ assert_equal(:x, o2.public_send(:x))
+ end
+
def test_prepend_visibility
bug8005 = '[ruby-core:53106] [Bug #8005]'
c = Class.new do
@@ -1953,6 +2550,33 @@ class TestModule < Test::Unit::TestCase
assert_include(im, mixin, bug8025)
end
+ def test_prepended_module_with_super_and_alias
+ bug16736 = '[Bug #16736]'
+
+ a = labeled_class("A") do
+ def m; "A"; end
+ end
+ m = labeled_module("M") do
+ prepend Module.new
+
+ def self.included(base)
+ base.alias_method :base_m, :m
+ end
+
+ def m
+ super + "M"
+ end
+
+ def m2
+ base_m
+ end
+ end
+ b = labeled_class("B", a) do
+ include m
+ end
+ assert_equal("AM", b.new.m2, bug16736)
+ end
+
def test_prepend_super_in_alias
bug7842 = '[Bug #7842]'
@@ -2071,6 +2695,22 @@ class TestModule < Test::Unit::TestCase
assert_equal([:@@bar], m2.class_variables(false))
end
+ def test_class_variable_in_dup_class
+ a = Class.new do
+ @@a = 'A'
+ def a=(x)
+ @@a = x
+ end
+ def a
+ @@a
+ end
+ end
+
+ b = a.dup
+ b.new.a = 'B'
+ assert_equal 'A', a.new.a, '[ruby-core:17019]'
+ end
+
Bug6891 = '[ruby-core:47241]'
def test_extend_module_with_protected_method
@@ -2147,6 +2787,9 @@ class TestModule < Test::Unit::TestCase
class AttrTest
class << self
attr_accessor :cattr
+ def reset
+ self.cattr = nil
+ end
end
attr_accessor :iattr
def ivar
@@ -2156,7 +2799,7 @@ class TestModule < Test::Unit::TestCase
def test_uninitialized_instance_variable
a = AttrTest.new
- assert_warning(/instance variable @ivar not initialized/) do
+ assert_warning('') do
assert_nil(a.ivar)
end
a.instance_variable_set(:@ivar, 42)
@@ -2165,7 +2808,7 @@ class TestModule < Test::Unit::TestCase
end
name = "@\u{5909 6570}"
- assert_warning(/instance variable #{name} not initialized/) do
+ assert_warning('') do
assert_nil(a.instance_eval(name))
end
end
@@ -2189,6 +2832,8 @@ class TestModule < Test::Unit::TestCase
assert_warning '' do
assert_equal(42, AttrTest.cattr)
end
+
+ AttrTest.reset
end
def test_uninitialized_attr_non_object
@@ -2292,31 +2937,6 @@ class TestModule < Test::Unit::TestCase
assert_raise(NoMethodError, bug8284) {Object.remove_const}
end
- def test_include_module_with_constants_does_not_invalidate_method_cache
- assert_in_out_err([], <<-RUBY, %w(123 456 true), [])
- A = 123
-
- class Foo
- def self.a
- A
- end
- end
-
- module M
- A = 456
- end
-
- puts Foo.a
- starting = RubyVM.stat[:global_method_state]
-
- Foo.send(:include, M)
-
- ending = RubyVM.stat[:global_method_state]
- puts Foo.a
- puts starting == ending
- RUBY
- end
-
def test_return_value_of_define_method
retvals = []
Class.new.class_eval do
@@ -2353,9 +2973,67 @@ class TestModule < Test::Unit::TestCase
}
end
+ def test_prepend_constant_lookup
+ m = Module.new do
+ const_set(:C, :m)
+ end
+ c = Class.new do
+ const_set(:C, :c)
+ prepend m
+ end
+ sc = Class.new(c)
+ # Situation from [Bug #17887]
+ assert_equal(sc.ancestors.take(3), [sc, m, c])
+ assert_equal(:m, sc.const_get(:C))
+ assert_equal(:m, sc::C)
+
+ assert_equal(:c, c::C)
+
+ m.send(:remove_const, :C)
+ assert_equal(:c, sc.const_get(:C))
+ assert_equal(:c, sc::C)
+
+ # Same ancestors, built with include instead of prepend
+ m = Module.new do
+ const_set(:C, :m)
+ end
+ c = Class.new do
+ const_set(:C, :c)
+ end
+ sc = Class.new(c) do
+ include m
+ end
+
+ assert_equal(sc.ancestors.take(3), [sc, m, c])
+ assert_equal(:m, sc.const_get(:C))
+ assert_equal(:m, sc::C)
+
+ m.send(:remove_const, :C)
+ assert_equal(:c, sc.const_get(:C))
+ assert_equal(:c, sc::C)
+
+ # Situation from [Bug #17887], but with modules
+ m = Module.new do
+ const_set(:C, :m)
+ end
+ m2 = Module.new do
+ const_set(:C, :m2)
+ prepend m
+ end
+ c = Class.new do
+ include m2
+ end
+ assert_equal(c.ancestors.take(3), [c, m, m2])
+ assert_equal(:m, c.const_get(:C))
+ assert_equal(:m, c::C)
+ end
+
def test_inspect_segfault
bug_10282 = '[ruby-core:65214] [Bug #10282]'
- assert_separately [], <<-RUBY
+ assert_separately [], "#{<<~"begin;"}\n#{<<~'end;'}"
+ bug_10282 = "#{bug_10282}"
+ begin;
+ line = __LINE__ + 2
module ShallowInspect
def shallow_inspect
"foo"
@@ -2372,9 +3050,9 @@ class TestModule < Test::Unit::TestCase
A.prepend InspectIsShallow
- expect = "#<Method: A(ShallowInspect)#inspect(shallow_inspect)() -:7>"
- assert_equal expect, A.new.method(:inspect).inspect, "#{bug_10282}"
- RUBY
+ expect = "#<Method: A(ShallowInspect)#inspect(shallow_inspect)() -:#{line}>"
+ assert_equal expect, A.new.method(:inspect).inspect, bug_10282
+ end;
end
def test_define_method_with_unbound_method
@@ -2486,6 +3164,62 @@ class TestModule < Test::Unit::TestCase
assert_equal :M2, CloneTestC2.new.foo, '[Bug #15877]'
end
+ def test_clone_freeze
+ m = Module.new.freeze
+ assert_predicate m.clone, :frozen?
+ assert_not_predicate m.clone(freeze: false), :frozen?
+ end
+
+ def test_module_name_in_singleton_method
+ s = Object.new.singleton_class
+ mod = s.const_set(:Foo, Module.new)
+ assert_match(/::Foo$/, mod.name, '[Bug #14895]')
+ end
+
+ def test_iclass_memory_leak
+ # [Bug #19550]
+ assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true)
+ code = proc do
+ mod = Module.new
+ Class.new do
+ include mod
+ end
+ end
+ 1_000.times(&code)
+ PREP
+ 3_000_000.times(&code)
+ CODE
+ end
+
+ def test_complemented_method_entry_memory_leak
+ # [Bug #19894]
+ assert_no_memory_leak([], <<~PREP, <<~CODE, rss: true)
+ code = proc do
+ $c = Class.new do
+ def foo; end
+ end
+
+ $m = Module.new do
+ refine $c do
+ def foo; end
+ end
+ end
+
+ Class.new do
+ using $m
+
+ def initialize
+ o = $c.new
+ o.method(:foo).unbind
+ end
+ end.new
+ end
+ 1_000.times(&code)
+ PREP
+ 100_000.times(&code)
+ CODE
+ end
+
private
def assert_top_method_is_private(method)
diff --git a/test/ruby/test_name_error.rb b/test/ruby/test_name_error.rb
new file mode 100644
index 0000000000..f0402de4b9
--- /dev/null
+++ b/test/ruby/test_name_error.rb
@@ -0,0 +1,156 @@
+require 'test/unit'
+
+class TestNameError < Test::Unit::TestCase
+ def test_new_default
+ error = NameError.new
+ assert_equal("NameError", error.message)
+ end
+
+ def test_new_message
+ error = NameError.new("Message")
+ assert_equal("Message", error.message)
+ end
+
+ def test_new_name
+ error = NameError.new("Message")
+ assert_nil(error.name)
+
+ error = NameError.new("Message", :foo)
+ assert_equal(:foo, error.name)
+ end
+
+ def test_new_receiver
+ receiver = Object.new
+
+ error = NameError.new
+ assert_raise(ArgumentError) {error.receiver}
+ assert_equal("NameError", error.message)
+
+ error = NameError.new(receiver: receiver)
+ assert_equal(["NameError", receiver],
+ [error.message, error.receiver])
+
+ error = NameError.new("Message", :foo, receiver: receiver)
+ assert_equal(["Message", receiver, :foo],
+ [error.message, error.receiver, error.name])
+ end
+
+ PrettyObject =
+ Class.new(BasicObject) do
+ alias object_id __id__
+ def pretty_inspect; "`obj'"; end
+ alias inspect pretty_inspect
+ end
+
+ def test_info_const
+ obj = PrettyObject.new
+
+ e = assert_raise(NameError) {
+ obj.instance_eval("Object")
+ }
+ assert_equal(:Object, e.name)
+
+ e = assert_raise(NameError) {
+ BasicObject::X
+ }
+ assert_same(BasicObject, e.receiver)
+ assert_equal(:X, e.name)
+ end
+
+ def test_info_const_name
+ mod = Module.new do
+ def self.name
+ "ModuleName"
+ end
+
+ def self.inspect
+ raise "<unusable info>"
+ end
+ end
+ assert_raise_with_message(NameError, /ModuleName/) {mod::DOES_NOT_EXIST}
+ end
+
+ def test_info_method
+ obj = PrettyObject.new
+
+ e = assert_raise(NameError) {
+ obj.instance_eval {foo}
+ }
+ assert_equal(:foo, e.name)
+ assert_same(obj, e.receiver)
+
+ e = assert_raise(NoMethodError) {
+ obj.foo(1, 2)
+ }
+ assert_equal(:foo, e.name)
+ assert_equal([1, 2], e.args)
+ assert_same(obj, e.receiver)
+ assert_not_predicate(e, :private_call?)
+
+ e = assert_raise(NoMethodError) {
+ obj.instance_eval {foo(1, 2)}
+ }
+ assert_equal(:foo, e.name)
+ assert_equal([1, 2], e.args)
+ assert_same(obj, e.receiver)
+ assert_predicate(e, :private_call?)
+ end
+
+ def test_info_local_variables
+ obj = PrettyObject.new
+ def obj.test(a, b=nil, *c, &d)
+ e = a
+ 1.times {|f| g = foo; g}
+ e
+ end
+
+ e = assert_raise(NameError) {
+ obj.test(3)
+ }
+ assert_equal(:foo, e.name)
+ assert_same(obj, e.receiver)
+ assert_equal(%i[a b c d e f g], e.local_variables.sort)
+ end
+
+ def test_info_method_missing
+ obj = PrettyObject.new
+ def obj.method_missing(*)
+ super
+ end
+
+ e = assert_raise(NoMethodError) {
+ obj.foo(1, 2)
+ }
+ assert_equal(:foo, e.name)
+ assert_equal([1, 2], e.args)
+ assert_same(obj, e.receiver)
+ assert_not_predicate(e, :private_call?)
+
+ e = assert_raise(NoMethodError) {
+ obj.instance_eval {foo(1, 2)}
+ }
+ assert_equal(:foo, e.name)
+ assert_equal([1, 2], e.args)
+ assert_same(obj, e.receiver)
+ assert_predicate(e, :private_call?)
+ end
+
+ def test_info_parent_iseq_mark
+ assert_separately(['-', File.join(__dir__, 'bug-11928.rb')], <<-'end;')
+ -> {require ARGV[0]}.call
+ end;
+ end
+
+ def test_large_receiver_inspect
+ receiver = Class.new do
+ def self.inspect
+ 'A' * 120
+ end
+ end
+
+ error = assert_raise(NameError) do
+ receiver::FOO
+ end
+ assert_match(/\Auninitialized constant #{'A' * 120}::FOO$/, error.message)
+ end
+end
diff --git a/test/ruby/test_nomethod_error.rb b/test/ruby/test_nomethod_error.rb
new file mode 100644
index 0000000000..321b7ccab2
--- /dev/null
+++ b/test/ruby/test_nomethod_error.rb
@@ -0,0 +1,109 @@
+require 'test/unit'
+
+class TestNoMethodError < Test::Unit::TestCase
+ def test_new_default
+ error = NoMethodError.new
+ assert_equal("NoMethodError", error.message)
+ end
+
+ def test_new_message
+ error = NoMethodError.new("Message")
+ assert_equal("Message", error.message)
+ end
+
+ def test_new_name
+ error = NoMethodError.new("Message")
+ assert_nil(error.name)
+
+ error = NoMethodError.new("Message", :foo)
+ assert_equal(:foo, error.name)
+ end
+
+ def test_new_name_args
+ error = NoMethodError.new("Message", :foo)
+ assert_nil(error.args)
+
+ error = NoMethodError.new("Message", :foo, [1, 2])
+ assert_equal([:foo, [1, 2]], [error.name, error.args])
+ end
+
+ def test_new_name_args_priv
+ error = NoMethodError.new("Message", :foo, [1, 2])
+ assert_not_predicate(error, :private_call?)
+
+ error = NoMethodError.new("Message", :foo, [1, 2], true)
+ assert_equal([:foo, [1, 2], true],
+ [error.name, error.args, error.private_call?])
+ end
+
+ def test_new_receiver
+ receiver = Object.new
+
+ error = NoMethodError.new
+ assert_raise(ArgumentError) {error.receiver}
+
+ error = NoMethodError.new(receiver: receiver)
+ assert_equal(receiver, error.receiver)
+
+ error = NoMethodError.new("Message")
+ assert_raise(ArgumentError) {error.receiver}
+
+ error = NoMethodError.new("Message", receiver: receiver)
+ assert_equal(["Message", receiver],
+ [error.message, error.receiver])
+
+ error = NoMethodError.new("Message", :foo)
+ assert_raise(ArgumentError) {error.receiver}
+
+ msg = "Message"
+
+ error = NoMethodError.new("Message", :foo, receiver: receiver)
+ assert_match msg, error.message
+ assert_equal :foo, error.name
+ assert_equal receiver, error.receiver
+
+ error = NoMethodError.new("Message", :foo, [1, 2])
+ assert_raise(ArgumentError) {error.receiver}
+
+ error = NoMethodError.new("Message", :foo, [1, 2], receiver: receiver)
+ assert_match msg, error.message
+ assert_equal :foo, error.name
+ assert_equal [1, 2], error.args
+ assert_equal receiver, error.receiver
+
+ error = NoMethodError.new("Message", :foo, [1, 2], true)
+ assert_raise(ArgumentError) {error.receiver}
+
+ error = NoMethodError.new("Message", :foo, [1, 2], true, receiver: receiver)
+ assert_equal :foo, error.name
+ assert_equal [1, 2], error.args
+ assert_equal receiver, error.receiver
+ assert error.private_call?, "private_call? was false."
+ end
+
+ def test_message_encoding
+ bug3237 = '[ruby-core:29948]'
+ str = "\u2600"
+ id = :"\u2604"
+ msg = "undefined method `#{id}' for \"#{str}\":String"
+ assert_raise_with_message(NoMethodError, Regexp.compile(Regexp.quote(msg)), bug3237) do
+ str.__send__(id)
+ end
+ end
+
+ def test_to_s
+ pre = Module.new do
+ def name
+ BasicObject.new
+ end
+ end
+ mod = Module.new
+ mod.singleton_class.prepend(pre)
+
+ err = assert_raise(NoMethodError) do
+ mod.this_method_does_not_exist
+ end
+
+ assert_match(/undefined method.+this_method_does_not_exist.+for.+Module/, err.to_s)
+ end
+end
diff --git a/test/ruby/test_notimp.rb b/test/ruby/test_notimp.rb
deleted file mode 100644
index b069154cfc..0000000000
--- a/test/ruby/test_notimp.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-# frozen_string_literal: false
-require 'test/unit'
-require 'timeout'
-require 'tmpdir'
-
-class TestNotImplement < Test::Unit::TestCase
- def test_respond_to_fork
- assert_include(Process.methods, :fork)
- if /linux/ =~ RUBY_PLATFORM
- assert_equal(true, Process.respond_to?(:fork))
- end
- end
-
- def test_respond_to_lchmod
- assert_include(File.methods, :lchmod)
- if /linux/ =~ RUBY_PLATFORM
- assert_equal(false, File.respond_to?(:lchmod))
- end
- if /freebsd/ =~ RUBY_PLATFORM
- assert_equal(true, File.respond_to?(:lchmod))
- end
- end
-
- def test_call_fork
- GC.start
- pid = nil
- ps =
- case RUBY_PLATFORM
- when /linux/ # assume Linux Distribution uses procps
- proc {`ps -eLf #{pid}`}
- when /freebsd/
- proc {`ps -lH #{pid}`}
- when /darwin/
- proc {`ps -lM #{pid}`}
- else
- proc {`ps -l #{pid}`}
- end
- assert_nothing_raised(Timeout::Error, ps) do
- EnvUtil.timeout(20) {
- pid = fork {}
- Process.wait pid
- pid = nil
- }
- end
- ensure
- if pid
- Process.kill(:KILL, pid)
- Process.wait pid
- end
- end if Process.respond_to?(:fork)
-
- def test_call_lchmod
- if File.respond_to?(:lchmod)
- Dir.mktmpdir {|d|
- f = "#{d}/f"
- g = "#{d}/g"
- File.open(f, "w") {}
- File.symlink f, g
- newmode = 0444
- File.lchmod newmode, "#{d}/g"
- snew = File.lstat(g)
- assert_equal(newmode, snew.mode & 0777)
- }
- end
- end
-
- def test_method_inspect_fork
- m = Process.method(:fork)
- if Process.respond_to?(:fork)
- assert_not_match(/not-implemented/, m.inspect)
- else
- assert_match(/not-implemented/, m.inspect)
- end
- end
-
- def test_method_inspect_lchmod
- m = File.method(:lchmod)
- if File.respond_to?(:lchmod)
- assert_not_match(/not-implemented/, m.inspect)
- else
- assert_match(/not-implemented/, m.inspect)
- end
- end
-
-end
diff --git a/test/ruby/test_numeric.rb b/test/ruby/test_numeric.rb
index 797fd209af..ab492743f6 100644
--- a/test/ruby/test_numeric.rb
+++ b/test/ruby/test_numeric.rb
@@ -200,6 +200,14 @@ class TestNumeric < Test::Unit::TestCase
assert_nil(a <=> :foo)
end
+ def test_float_round_ndigits
+ bug14635 = "[ruby-core:86323]"
+ f = 0.5
+ 31.times do |i|
+ assert_equal(0.5, f.round(i+1), bug14635 + " (argument: #{i+1})")
+ end
+ end
+
def test_floor_ceil_round_truncate
a = Class.new(Numeric) do
def to_f; 1.5; end
@@ -229,6 +237,15 @@ class TestNumeric < Test::Unit::TestCase
assert_equal(-1, a.truncate)
end
+ def test_floor_ceil_ndigits
+ bug17183 = "[ruby-core:100090]"
+ f = 291.4
+ 31.times do |i|
+ assert_equal(291.4, f.floor(i+1), bug17183)
+ assert_equal(291.4, f.ceil(i+1), bug17183)
+ end
+ end
+
def assert_step(expected, (from, *args), inf: false)
kw = args.last.is_a?(Hash) ? args.pop : {}
enum = from.step(*args, **kw)
@@ -267,12 +284,7 @@ class TestNumeric < Test::Unit::TestCase
assert_raise(ArgumentError) { 1.step(10, "1") { } }
assert_raise(ArgumentError) { 1.step(10, "1").size }
assert_raise(TypeError) { 1.step(10, nil) { } }
- assert_nothing_raised { 1.step(10, 0).size }
assert_nothing_raised { 1.step(10, nil).size }
- assert_nothing_raised { 1.step(by: 0, to: nil) }
- assert_nothing_raised { 1.step(by: 0, to: nil).size }
- assert_nothing_raised { 1.step(by: 0) }
- assert_nothing_raised { 1.step(by: 0).size }
assert_nothing_raised { 1.step(by: nil) }
assert_nothing_raised { 1.step(by: nil).size }
@@ -292,13 +304,29 @@ class TestNumeric < Test::Unit::TestCase
assert_raise(ArgumentError, bug9811) { 1.step(10, 1, by: 11) {} }
assert_raise(ArgumentError, bug9811) { 1.step(10, 1, by: 11).size }
-
- e = assert_warn(/The last argument is used as the keyword parameter/) {
- 1.step(10, {by: "1"})
- }
- assert_warn('') {
- assert_raise(ArgumentError) {e.size}
- }
+ feature15573 = "[ruby-core:91324] [Feature #15573]"
+ assert_raise(ArgumentError, feature15573) { 1.step(10, 0) }
+ assert_raise(ArgumentError, feature15573) { 1.step(10, by: 0) }
+ assert_raise(ArgumentError, feature15573) { 1.step(10, 0) { break } }
+ assert_raise(ArgumentError, feature15573) { 1.step(10, by: 0) { break } }
+ assert_raise(ArgumentError, feature15573) { 42.step(by: 0, to: -Float::INFINITY) }
+ assert_raise(ArgumentError, feature15573) { 42.step(by: 0, to: 42.5) }
+ assert_raise(ArgumentError, feature15573) { 4.2.step(by: 0.0) }
+ assert_raise(ArgumentError, feature15573) { 4.2.step(by: -0.0) }
+ assert_raise(ArgumentError, feature15573) { 42.step(by: 0.0, to: 44) }
+ assert_raise(ArgumentError, feature15573) { 42.step(by: 0.0, to: 0) }
+ assert_raise(ArgumentError, feature15573) { 42.step(by: -0.0, to: 44) }
+ assert_raise(ArgumentError, feature15573) { bignum.step(by: 0) }
+ assert_raise(ArgumentError, feature15573) { bignum.step(by: 0.0) }
+ assert_raise(ArgumentError, feature15573) { bignum.step(by: 0, to: bignum+1) }
+ assert_raise(ArgumentError, feature15573) { bignum.step(by: 0, to: 0) }
+
+ e = 1.step(10, {by: "1"})
+ assert_raise(TypeError) {e.next}
+ assert_raise(TypeError) {e.size}
+ e = 1.step(to: "10")
+ assert_raise(ArgumentError) {e.next}
+ assert_raise(ArgumentError) {e.size}
assert_equal(bignum*2+1, (-bignum).step(bignum, 1).size)
assert_equal(bignum*2, (-bignum).step(bignum-1, 1).size)
@@ -325,8 +353,6 @@ class TestNumeric < Test::Unit::TestCase
assert_step [], [2, 1, 3]
assert_step [], [-2, -1, -3]
- assert_step [3, 3, 3, 3], [3, by: 0], inf: true
- assert_step [3, 3, 3, 3], [3, by: 0, to: 42], inf: true
assert_step [10], [10, 1, -bignum]
assert_step [], [1, 0, Float::INFINITY]
@@ -336,19 +362,6 @@ class TestNumeric < Test::Unit::TestCase
assert_step [10, 11, 12, 13], [10], inf: true
assert_step [10, 9, 8, 7], [10, by: -1], inf: true
assert_step [10, 9, 8, 7], [10, by: -1, to: nil], inf: true
-
- assert_step [42, 42, 42, 42], [42, by: 0, to: -Float::INFINITY], inf: true
- assert_step [42, 42, 42, 42], [42, by: 0, to: 42.5], inf: true
- assert_step [4.2, 4.2, 4.2, 4.2], [4.2, by: 0.0], inf: true
- assert_step [4.2, 4.2, 4.2, 4.2], [4.2, by: -0.0], inf: true
- assert_step [42.0, 42.0, 42.0, 42.0], [42, by: 0.0, to: 44], inf: true
- assert_step [42.0, 42.0, 42.0, 42.0], [42, by: 0.0, to: 0], inf: true
- assert_step [42.0, 42.0, 42.0, 42.0], [42, by: -0.0, to: 44], inf: true
-
- assert_step [bignum]*4, [bignum, by: 0], inf: true
- assert_step [bignum]*4, [bignum, by: 0.0], inf: true
- assert_step [bignum]*4, [bignum, by: 0, to: bignum+1], inf: true
- assert_step [bignum]*4, [bignum, by: 0, to: 0], inf: true
end
def test_step_bug15537
@@ -391,6 +404,18 @@ class TestNumeric < Test::Unit::TestCase
end;
end
+ def test_remainder_infinity
+ assert_equal(4, 4.remainder(Float::INFINITY))
+ assert_equal(4, 4.remainder(-Float::INFINITY))
+ assert_equal(-4, -4.remainder(Float::INFINITY))
+ assert_equal(-4, -4.remainder(-Float::INFINITY))
+
+ assert_equal(4.2, 4.2.remainder(Float::INFINITY))
+ assert_equal(4.2, 4.2.remainder(-Float::INFINITY))
+ assert_equal(-4.2, -4.2.remainder(Float::INFINITY))
+ assert_equal(-4.2, -4.2.remainder(-Float::INFINITY))
+ end
+
def test_comparison_comparable
bug12864 = '[ruby-core:77713] [Bug #12864]'
@@ -438,6 +463,26 @@ class TestNumeric < Test::Unit::TestCase
assert_equal(12, 12.pow(1, 10000000001), '[Bug #14259]')
assert_equal(12, 12.pow(1, 10000000002), '[Bug #14259]')
assert_equal(17298641040, 12.pow(72387894339363242, 243682743764), '[Bug #14259]')
+
+ integers = [-2, -1, 0, 1, 2, 3, 6, 1234567890123456789]
+ integers.each do |i|
+ assert_equal(0, i.pow(0, 1), '[Bug #17257]')
+ assert_equal(1, i.pow(0, 2))
+ assert_equal(1, i.pow(0, 3))
+ assert_equal(1, i.pow(0, 6))
+ assert_equal(1, i.pow(0, 1234567890123456789))
+
+ assert_equal(0, i.pow(0, -1))
+ assert_equal(-1, i.pow(0, -2))
+ assert_equal(-2, i.pow(0, -3))
+ assert_equal(-5, i.pow(0, -6))
+ assert_equal(-1234567890123456788, i.pow(0, -1234567890123456789))
+ end
+
+ assert_equal(0, 0.pow(2, 1))
+ assert_equal(0, 0.pow(3, 1))
+ assert_equal(0, 2.pow(3, 1))
+ assert_equal(0, -2.pow(3, 1))
end
end
diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb
index add5b9fb15..83208bbcdb 100644
--- a/test/ruby/test_object.rb
+++ b/test/ruby/test_object.rb
@@ -5,7 +5,6 @@ require 'test/unit'
class TestObject < Test::Unit::TestCase
def setup
@verbose = $VERBOSE
- $VERBOSE = nil
end
def teardown
@@ -47,15 +46,27 @@ class TestObject < Test::Unit::TestCase
a = Object.new
def a.b; 2 end
+ c = a.clone
+ assert_equal(false, c.frozen?)
+ assert_equal(false, a.frozen?)
+ assert_equal(2, c.b)
+
+ c = a.clone(freeze: true)
+ assert_equal(true, c.frozen?)
+ assert_equal(false, a.frozen?)
+ assert_equal(2, c.b)
+
a.freeze
c = a.clone
assert_equal(true, c.frozen?)
+ assert_equal(true, a.frozen?)
assert_equal(2, c.b)
assert_raise(ArgumentError) {a.clone(freeze: [])}
d = a.clone(freeze: false)
def d.e; 3; end
assert_equal(false, d.frozen?)
+ assert_equal(true, a.frozen?)
assert_equal(2, d.b)
assert_equal(3, d.e)
@@ -75,6 +86,30 @@ class TestObject < Test::Unit::TestCase
assert_raise_with_message(ArgumentError, /\u{1f4a9}/) do
Object.new.clone(freeze: x)
end
+
+ c = Class.new do
+ attr_reader :f
+ end
+ o = c.new
+ def o.initialize_clone(_, freeze: true)
+ @f = freeze
+ super
+ end
+ clone = o.clone
+ assert_kind_of c, clone
+ assert_equal true, clone.f
+ clone = o.clone(freeze: false)
+ assert_kind_of c, clone
+ assert_equal false, clone.f
+
+ class << o
+ remove_method(:initialize_clone)
+ end
+ def o.initialize_clone(_)
+ super
+ end
+ assert_kind_of c, o.clone
+ assert_raise(ArgumentError) { o.clone(freeze: false) }
end
def test_init_dupclone
@@ -324,6 +359,7 @@ class TestObject < Test::Unit::TestCase
o = Object.new
def o.to_s; 1; end
assert_raise(TypeError) { String(o) }
+ o.singleton_class.remove_method(:to_s)
def o.to_s; "o"; end
assert_equal("o", String(o))
def o.to_str; "O"; end
@@ -336,6 +372,7 @@ class TestObject < Test::Unit::TestCase
o = Object.new
def o.to_a; 1; end
assert_raise(TypeError) { Array(o) }
+ o.singleton_class.remove_method(:to_a)
def o.to_a; [1]; end
assert_equal([1], Array(o))
def o.to_ary; [2]; end
@@ -353,6 +390,7 @@ class TestObject < Test::Unit::TestCase
o = Object.new
def o.to_hash; {a: 1, b: 2}; end
assert_equal({a: 1, b: 2}, Hash(o))
+ o.singleton_class.remove_method(:to_hash)
def o.to_hash; 9; end
assert_raise(TypeError) { Hash(o) }
end
@@ -361,6 +399,7 @@ class TestObject < Test::Unit::TestCase
o = Object.new
def o.to_i; nil; end
assert_raise(TypeError) { Integer(o) }
+ o.singleton_class.remove_method(:to_i)
def o.to_i; 42; end
assert_equal(42, Integer(o))
def o.respond_to?(*) false; end
@@ -595,7 +634,7 @@ class TestObject < Test::Unit::TestCase
called = []
p.singleton_class.class_eval do
- define_method(:respond_to?) do |a|
+ define_method(:respond_to?) do |a, priv = false|
called << [:respond_to?, a]
false
end
@@ -738,7 +777,7 @@ class TestObject < Test::Unit::TestCase
e = assert_raise(NoMethodError) {
o.never_defined_test_no_superclass_method
}
- assert_equal(m1, e.message, bug2312)
+ assert_equal(m1.lines.first, e.message.lines.first, bug2312)
end
def test_superclass_method
@@ -954,13 +993,4 @@ class TestObject < Test::Unit::TestCase
end
EOS
end
-
- def test_matcher
- assert_warning(/deprecated Object#=~ is called on Object/) do
- assert_equal(Object.new =~ 42, nil)
- end
- assert_warning(/deprecated Object#=~ is called on Array/) do
- assert_equal([] =~ 42, nil)
- end
- end
end
diff --git a/test/ruby/test_objectspace.rb b/test/ruby/test_objectspace.rb
index c352b75b70..e0f9eecd11 100644
--- a/test/ruby/test_objectspace.rb
+++ b/test/ruby/test_objectspace.rb
@@ -35,6 +35,36 @@ End
deftest_id2ref(false)
deftest_id2ref(nil)
+ def test_id2ref_liveness
+ assert_normal_exit <<-EOS
+ ids = []
+ 10.times{
+ 1_000.times{
+ ids << 'hello'.object_id
+ }
+ objs = ids.map{|id|
+ begin
+ ObjectSpace._id2ref(id)
+ rescue RangeError
+ nil
+ end
+ }
+ GC.start
+ objs.each{|e| e.inspect}
+ }
+ EOS
+ end
+
+ def test_id2ref_invalid_argument
+ msg = /no implicit conversion/
+ assert_raise_with_message(TypeError, msg) {ObjectSpace._id2ref(nil)}
+ assert_raise_with_message(TypeError, msg) {ObjectSpace._id2ref(false)}
+ assert_raise_with_message(TypeError, msg) {ObjectSpace._id2ref(true)}
+ assert_raise_with_message(TypeError, msg) {ObjectSpace._id2ref(:a)}
+ assert_raise_with_message(TypeError, msg) {ObjectSpace._id2ref("0")}
+ assert_raise_with_message(TypeError, msg) {ObjectSpace._id2ref(Object.new)}
+ end
+
def test_count_objects
h = {}
ObjectSpace.count_objects(h)
@@ -131,6 +161,40 @@ End
END
end
+ def test_exception_in_finalizer
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", [], /finalizing \(RuntimeError\)/)
+ begin;
+ ObjectSpace.define_finalizer(Object.new) {raise "finalizing"}
+ end;
+ end
+
+ def test_finalizer_thread_raise
+ GC.disable
+ fzer = proc do |id|
+ sleep 0.2
+ end
+ 2.times do
+ o = Object.new
+ ObjectSpace.define_finalizer(o, fzer)
+ end
+
+ my_error = Class.new(RuntimeError)
+ begin
+ main_th = Thread.current
+ Thread.new do
+ sleep 0.1
+ main_th.raise(my_error)
+ end
+ GC.start
+ puts "After GC"
+ sleep(10)
+ assert(false)
+ rescue my_error
+ end
+ ensure
+ GC.enable
+ end
+
def test_each_object
klass = Class.new
new_obj = klass.new
@@ -155,7 +219,7 @@ End
assert_same(new_obj, found[0])
end
- def test_each_object_no_gabage
+ def test_each_object_no_garbage
assert_separately([], <<-End)
GC.disable
eval('begin; 1.times{}; rescue; ensure; end')
@@ -203,4 +267,11 @@ End
assert_kind_of(meta, sclass)
assert_include(ObjectSpace.each_object(meta).to_a, sclass)
end
+
+ def test_each_object_with_allocation
+ assert_normal_exit(<<-End)
+ list = []
+ ObjectSpace.each_object { |o| list << Object.new }
+ End
+ end
end
diff --git a/test/ruby/test_optimization.rb b/test/ruby/test_optimization.rb
index b42314b765..43795d150c 100644
--- a/test/ruby/test_optimization.rb
+++ b/test/ruby/test_optimization.rb
@@ -150,6 +150,64 @@ class TestRubyOptimization < Test::Unit::TestCase
assert_redefine_method('String', '-@', 'assert_nil(-"foo")')
end
+ def test_array_min
+ assert_equal 1, [1, 2, 4].min
+ assert_redefine_method('Array', 'min', 'assert_nil([1, 2, 4].min)')
+ assert_redefine_method('Array', 'min', 'assert_nil([1 + 0, 2, 4].min)')
+ end
+
+ def test_array_max
+ assert_equal 4, [1, 2, 4].max
+ assert_redefine_method('Array', 'max', 'assert_nil([1, 2, 4].max)')
+ assert_redefine_method('Array', 'max', 'assert_nil([1 + 0, 2, 4].max)')
+ end
+
+ def test_trace_optimized_methods
+ bug14870 = "[ruby-core:87638]"
+ expected = [:-@, :max, :min, :+, :-, :*, :/, :%, :==, :<, :<=, :>, :>=, :<<,
+ :&, :|, :[], :[]=, :length, :empty?, :nil?, :succ, :!, :=~]
+ [:c_call, :c_return].each do |type|
+ methods = []
+ tp = TracePoint.new(type) { |tp| methods << tp.method_id }
+ tp.enable do
+ x = "a"; x = -x
+ [1].max
+ [1].min
+ x = 42 + 2
+ x = 42 - 2
+ x = 42 * 2
+ x = 42 / 2
+ x = 42 % 2
+ y = x == 42
+ y = x < 42
+ y = x <= 42
+ y = x > 42
+ y = x >= 42
+ x = x << 1
+ x = x & 1
+ x = x | 1
+ x = []; x[1]
+ x[1] = 2
+ x.length
+ x.empty?
+ x.nil?
+ x = 1; x.succ
+ !x
+ x = 'a'; x =~ /a/
+ x = y
+ end
+ assert_equal(expected, methods, bug14870)
+ end
+
+ methods = []
+ tp = TracePoint.new(:c_call, :c_return) { |tp| methods << tp.method_id }
+ tp.enable do
+ x = 1
+ x != 42
+ end
+ assert_equal([:!=, :==, :==, :!=], methods, bug14870)
+ end
+
def test_string_freeze_saves_memory
n = 16384
data = '.'.freeze
@@ -452,7 +510,7 @@ class TestRubyOptimization < Test::Unit::TestCase
end
def test_tailcall_not_to_grow_stack
- skip 'currently JIT-ed code always creates a new stack frame' if RubyVM::MJIT.enabled?
+ skip 'currently JIT-ed code always creates a new stack frame' if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
bug16161 = '[ruby-core:94881]'
tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
@@ -701,16 +759,16 @@ class TestRubyOptimization < Test::Unit::TestCase
def test_block_parameter_should_not_create_objects
assert_separately [], <<-END
- #
def foo &b
end
h1 = {}; h2 = {}
- ObjectSpace.count_objects(h1) # reharsal
+ ObjectSpace.count_objects(h1) # rehearsal
+ GC.start; GC.disable # to disable GC while foo{}
ObjectSpace.count_objects(h1)
foo{}
ObjectSpace.count_objects(h2)
- assert_equal 0, h2[:TOTAL] - h1[:TOTAL]
+ assert_equal 0, h2[:T_DATA] - h1[:T_DATA] # Proc is T_DATA
END
end
@@ -845,4 +903,34 @@ class TestRubyOptimization < Test::Unit::TestCase
raise "END"
end;
end
+
+ class Objtostring
+ end
+
+ def test_objtostring
+ assert_raise(NoMethodError){"#{BasicObject.new}"}
+ assert_redefine_method('Symbol', 'to_s', <<-'end')
+ assert_match %r{\A#<Symbol:0x[0-9a-f]+>\z}, "#{:foo}"
+ end
+ assert_redefine_method('NilClass', 'to_s', <<-'end')
+ assert_match %r{\A#<NilClass:0x[0-9a-f]+>\z}, "#{nil}"
+ end
+ assert_redefine_method('TrueClass', 'to_s', <<-'end')
+ assert_match %r{\A#<TrueClass:0x[0-9a-f]+>\z}, "#{true}"
+ end
+ assert_redefine_method('FalseClass', 'to_s', <<-'end')
+ assert_match %r{\A#<FalseClass:0x[0-9a-f]+>\z}, "#{false}"
+ end
+ assert_redefine_method('Integer', 'to_s', <<-'end')
+ (-1..10).each { |i|
+ assert_match %r{\A#<Integer:0x[0-9a-f]+>\z}, "#{i}"
+ }
+ end
+ assert_equal "TestRubyOptimization::Objtostring", "#{Objtostring}"
+ assert_match %r{\A#<Class:0x[0-9a-f]+>\z}, "#{Class.new}"
+ assert_match %r{\A#<Module:0x[0-9a-f]+>\z}, "#{Module.new}"
+ o = Object.new
+ def o.to_s; 1; end
+ assert_match %r{\A#<Object:0x[0-9a-f]+>\z}, "#{o}"
+ end
end
diff --git a/test/ruby/test_pack.rb b/test/ruby/test_pack.rb
index 60edd00186..9738f82b7e 100644
--- a/test/ruby/test_pack.rb
+++ b/test/ruby/test_pack.rb
@@ -638,6 +638,14 @@ EXPECTED
end;
end
+ def test_bug_18343
+ bug18343 = '[ruby-core:106096] [Bug #18343]'
+ assert_separately(%W[- #{bug18343}], <<-'end;')
+ bug = ARGV.shift
+ assert_raise(ArgumentError, bug){[0].pack('c', {})}
+ end;
+ end
+
def test_pack_unpack_m0
assert_equal("", [""].pack("m0"))
assert_equal("AA==", ["\0"].pack("m0"))
@@ -764,7 +772,7 @@ EXPECTED
$VERBOSE = true
- _, err = capture_io do
+ _, err = capture_output do
assert_equal "\000", [0].pack("*U")
end
@@ -783,7 +791,7 @@ EXPECTED
$VERBOSE = true
- _, err = capture_io do
+ _, err = capture_output do
assert_equal [0], "\000".unpack("*U")
end
@@ -869,4 +877,30 @@ EXPECTED
assert_equal "hogefuga", "aG9nZWZ1Z2E=".unpack1("m")
assert_equal "01000001", "A".unpack1("B*")
end
+
+ def test_unpack1_offset
+ assert_equal 65, "ZA".unpack1("C", offset: 1)
+ assert_equal "01000001", "YZA".unpack1("B*", offset: 2)
+ assert_nil "abc".unpack1("C", offset: 3)
+ assert_raise_with_message(ArgumentError, /offset can't be negative/) {
+ "a".unpack1("C", offset: -1)
+ }
+ assert_raise_with_message(ArgumentError, /offset outside of string/) {
+ "a".unpack1("C", offset: 2)
+ }
+ assert_nil "a".unpack1("C", offset: 1)
+ end
+
+ def test_unpack_offset
+ assert_equal [65], "ZA".unpack("C", offset: 1)
+ assert_equal ["01000001"], "YZA".unpack("B*", offset: 2)
+ assert_equal [nil, nil, nil], "abc".unpack("CCC", offset: 3)
+ assert_raise_with_message(ArgumentError, /offset can't be negative/) {
+ "a".unpack("C", offset: -1)
+ }
+ assert_raise_with_message(ArgumentError, /offset outside of string/) {
+ "a".unpack("C", offset: 2)
+ }
+ assert_equal [nil], "a".unpack("C", offset: 1)
+ end
end
diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb
index cb379ebe18..2841e20f6d 100644
--- a/test/ruby/test_parse.rb
+++ b/test/ruby/test_parse.rb
@@ -6,7 +6,6 @@ require 'stringio'
class TestParse < Test::Unit::TestCase
def setup
@verbose = $VERBOSE
- $VERBOSE = nil
end
def teardown
@@ -399,7 +398,6 @@ class TestParse < Test::Unit::TestCase
def test_arg2
o = Object.new
-
assert_nothing_raised do
eval <<-END, nil, __FILE__, __LINE__+1
def o.foo(a=42,*r,z,&b); b.call(r.inject(a*1000+z*100, :+)); end
@@ -411,6 +409,7 @@ class TestParse < Test::Unit::TestCase
assert_equal(-42100, o.foo(1) {|x| -x })
assert_raise(ArgumentError) { o.foo() }
+ o = Object.new
assert_nothing_raised do
eval <<-END, nil, __FILE__, __LINE__+1
def o.foo(a=42,z,&b); b.call(a*1000+z*100); end
@@ -420,6 +419,7 @@ class TestParse < Test::Unit::TestCase
assert_equal(-42100, o.foo(1) {|x| -x } )
assert_raise(ArgumentError) { o.foo() }
+ o = Object.new
assert_nothing_raised do
eval <<-END, nil, __FILE__, __LINE__+1
def o.foo(*r,z,&b); b.call(r.inject(z*100, :+)); end
@@ -562,6 +562,21 @@ class TestParse < Test::Unit::TestCase
assert_syntax_error("\"\\M-\x01\"", 'Invalid escape character syntax')
assert_syntax_error("\"\\M-\\C-\x01\"", 'Invalid escape character syntax')
assert_syntax_error("\"\\C-\\M-\x01\"", 'Invalid escape character syntax')
+
+ e = assert_syntax_error('"\c\u0000"', 'Invalid escape character syntax')
+ assert_equal(' ^~~~'"\n", e.message.lines.last)
+ e = assert_syntax_error('"\c\U0000"', 'Invalid escape character syntax')
+ assert_equal(' ^~~~'"\n", e.message.lines.last)
+
+ e = assert_syntax_error('"\C-\u0000"', 'Invalid escape character syntax')
+ assert_equal(' ^~~~~'"\n", e.message.lines.last)
+ e = assert_syntax_error('"\C-\U0000"', 'Invalid escape character syntax')
+ assert_equal(' ^~~~~'"\n", e.message.lines.last)
+
+ e = assert_syntax_error('"\M-\u0000"', 'Invalid escape character syntax')
+ assert_equal(' ^~~~~'"\n", e.message.lines.last)
+ e = assert_syntax_error('"\M-\U0000"', 'Invalid escape character syntax')
+ assert_equal(' ^~~~~'"\n", e.message.lines.last)
end
def test_question
@@ -599,6 +614,9 @@ class TestParse < Test::Unit::TestCase
assert_syntax_error('%s', /unterminated quoted string/)
assert_syntax_error('%ss', /unknown type/)
assert_syntax_error('%z()', /unknown type/)
+ assert_syntax_error("%\u3042", /unknown type/)
+ assert_syntax_error("%q\u3042", /unknown type/)
+ assert_syntax_error("%", /unterminated quoted string/)
end
def test_symbol
@@ -666,6 +684,15 @@ FOO
def test_magic_comment
x = nil
+
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+# coding: utf-8
+x = __ENCODING__
+ END
+ end
+ assert_equal(Encoding.find("UTF-8"), x)
+
assert_nothing_raised do
eval <<-END, nil, __FILE__, __LINE__+1
# coding = utf-8
@@ -680,6 +707,14 @@ x = __ENCODING__
x = __ENCODING__
END
end
+
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
+# xxxx : coding sjis
+x = __ENCODING__
+ END
+ end
+ assert_equal(__ENCODING__, x)
end
def test_utf8_bom
@@ -722,13 +757,13 @@ x = __ENCODING__
end
def test_float
- assert_equal(1.0/0, eval("1e10000"))
+ assert_predicate(assert_warning(/out of range/) {eval("1e10000")}, :infinite?)
assert_syntax_error('1_E', /trailing `_'/)
assert_syntax_error('1E1E1', /unexpected constant/)
end
def test_global_variable
- assert_equal(nil, eval('$-x'))
+ assert_equal(nil, assert_warning(/not initialized/) {eval('$-x')})
assert_equal(nil, eval('alias $preserve_last_match $&'))
assert_equal(nil, eval('alias $& $test_parse_foobarbazqux'))
$test_parse_foobarbazqux = nil
@@ -821,13 +856,13 @@ x = __ENCODING__
end
def test_assign_in_conditional
- assert_nothing_raised do
+ assert_warning(/`= literal' in conditional/) do
eval <<-END, nil, __FILE__, __LINE__+1
(x, y = 1, 2) ? 1 : 2
END
end
- assert_nothing_raised do
+ assert_warning(/`= literal' in conditional/) do
eval <<-END, nil, __FILE__, __LINE__+1
if @x = true
1
@@ -839,13 +874,13 @@ x = __ENCODING__
end
def test_literal_in_conditional
- assert_nothing_raised do
+ assert_warning(/string literal in condition/) do
eval <<-END, nil, __FILE__, __LINE__+1
"foo" ? 1 : 2
END
end
- assert_nothing_raised do
+ assert_warning(/regex literal in condition/) do
x = "bar"
eval <<-END, nil, __FILE__, __LINE__+1
/foo#{x}baz/ ? 1 : 2
@@ -858,13 +893,13 @@ x = __ENCODING__
END
end
- assert_nothing_raised do
+ assert_warning(/string literal in flip-flop/) do
eval <<-END, nil, __FILE__, __LINE__+1
("foo".."bar") ? 1 : 2
END
end
- assert_nothing_raised do
+ assert_warning(/literal in condition/) do
x = "bar"
eval <<-END, nil, __FILE__, __LINE__+1
:"foo#{"x"}baz" ? 1 : 2
@@ -895,6 +930,10 @@ x = __ENCODING__
assert_no_warning(/shadowing outer local variable/) {eval("a=1; tap {|a|}")}
end
+ def test_shadowing_private_local_variable
+ assert_equal 1, eval("_ = 1; [[2]].each{ |(_)| }; _")
+ end
+
def test_unused_variable
o = Object.new
assert_warning(/assigned but unused variable/) {o.instance_eval("def foo; a=1; nil; end")}
@@ -1009,7 +1048,7 @@ x = __ENCODING__
end;
assert_syntax_error("def\nf(000)end", /^ \^~~/)
- assert_syntax_error("def\nf(&)end", /^ \^/)
+ assert_syntax_error("def\nf(&0)end", /^ \^/)
end
def test_method_location_in_rescue
@@ -1045,6 +1084,32 @@ x = __ENCODING__
end;
end
+ def test_heredoc_interpolation
+ var = 1
+
+ v1 = <<~HEREDOC
+ something
+ #{"/#{var}"}
+ HEREDOC
+
+ v2 = <<~HEREDOC
+ something
+ #{_other = "/#{var}"}
+ HEREDOC
+
+ v3 = <<~HEREDOC
+ something
+ #{("/#{var}")}
+ HEREDOC
+
+ assert_equal "something\n/1\n", v1
+ assert_equal "something\n/1\n", v2
+ assert_equal "something\n/1\n", v3
+ assert_equal v1, v2
+ assert_equal v2, v3
+ assert_equal v1, v3
+ end
+
def test_unexpected_token_error
assert_syntax_error('"x"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', /unexpected/)
end
@@ -1101,6 +1166,10 @@ x = __ENCODING__
assert_syntax_error("def m\n\C-z""end", /unexpected/)
end
+ def test_unexpected_eof
+ assert_syntax_error('unless', /^ \^\Z/)
+ end
+
def test_location_of_invalid_token
assert_syntax_error('class xxx end', /^ \^~~\Z/)
end
@@ -1166,6 +1235,130 @@ x = __ENCODING__
assert_valid_syntax('let () { m(a) do; end }')
end
+ def test_void_value_in_rhs
+ w = "void value expression"
+ ["x = return 1", "x = return, 1", "x = 1, return", "x, y = return"].each do |code|
+ ex = assert_syntax_error(code, w)
+ assert_equal(1, ex.message.scan(w).size, ->{"same #{w.inspect} warning should be just once\n#{w.message}"})
+ end
+ end
+
+ def eval_separately(code)
+ Class.new.class_eval(code)
+ end
+
+ def assert_raise_separately(error, message, code)
+ assert_raise_with_message(error, message) do
+ eval_separately(code)
+ end
+ end
+
+ def assert_ractor_shareable(obj)
+ assert Ractor.shareable?(obj), ->{"Expected #{mu_pp(obj)} to be ractor shareable"}
+ end
+
+ def assert_not_ractor_shareable(obj)
+ assert !Ractor.shareable?(obj), ->{"Expected #{mu_pp(obj)} not to be ractor shareable"}
+ end
+
+ def test_shareable_constant_value_invalid
+ assert_warning(/invalid value/) do
+ assert_valid_syntax("# shareable_constant_value: invalid-option", verbose: true)
+ end
+ end
+
+ def test_shareable_constant_value_ignored
+ assert_warning(/ignored/) do
+ assert_valid_syntax("nil # shareable_constant_value: true", verbose: true)
+ end
+ end
+
+ def test_shareable_constant_value_simple
+ obj = [['unsharable_value']]
+ a, b, c = eval_separately("#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ # shareable_constant_value: experimental_everything
+ A = [[1]]
+ # shareable_constant_value: none
+ B = [[2]]
+ # shareable_constant_value: literal
+ C = [["shareable", "constant#{nil}"]]
+ D = A
+
+ [A, B, C]
+ end;
+ assert_ractor_shareable(a)
+ assert_not_ractor_shareable(b)
+ assert_ractor_shareable(c)
+ assert_equal([1], a[0])
+ assert_ractor_shareable(a[0])
+
+ a, obj = eval_separately(<<~'end;')
+ # shareable_constant_value: experimental_copy
+ obj = [["unshareable"]]
+ A = obj
+ [A, obj]
+ end;
+
+ assert_ractor_shareable(a)
+ assert_not_ractor_shareable(obj)
+ assert_equal obj, a
+ assert !obj.equal?(a)
+ end
+
+ def test_shareable_constant_value_nested
+ a, b = eval_separately("#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ # shareable_constant_value: none
+ class X
+ # shareable_constant_value: experimental_everything
+ var = [[1]]
+ A = var
+ end
+ B = []
+ [X::A, B]
+ end;
+ assert_ractor_shareable(a)
+ assert_not_ractor_shareable(b)
+ assert_equal([1], a[0])
+ assert_ractor_shareable(a[0])
+ end
+
+ def test_shareable_constant_value_unshareable_literal
+ assert_raise_separately(Ractor::IsolationError, /unshareable/,
+ "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ # shareable_constant_value: literal
+ C = ["Not " + "shareable"]
+ end;
+ end
+
+ def test_shareable_constant_value_nonliteral
+ assert_raise_separately(Ractor::IsolationError, /unshareable/, "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ # shareable_constant_value: literal
+ var = [:not_frozen]
+ C = var
+ end;
+
+ assert_raise_separately(Ractor::IsolationError, /unshareable/, "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ # shareable_constant_value: literal
+ D = begin [] end
+ end;
+ end
+
+ def test_shareable_constant_value_unfrozen
+ assert_raise_separately(Ractor::Error, /does not freeze object correctly/,
+ "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ # shareable_constant_value: experimental_everything
+ o = Object.new
+ def o.freeze; self; end
+ C = [o]
+ end;
+ end
+
=begin
def test_past_scope_variable
assert_warning(/past scope/) {catch {|tag| eval("BEGIN{throw tag}; tap {a = 1}; a")}}
diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb
index 5308ec3281..4c203fb4f9 100644
--- a/test/ruby/test_pattern_matching.rb
+++ b/test/ruby/test_pattern_matching.rb
@@ -1,9 +1,28 @@
# frozen_string_literal: true
require 'test/unit'
-verbose, $VERBOSE = $VERBOSE, nil # suppress "warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!"
+experimental, Warning[:experimental] = Warning[:experimental], false # suppress "warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!"
eval "\n#{<<~'END_of_GUARD'}", binding, __FILE__, __LINE__
class TestPatternMatching < Test::Unit::TestCase
+ class NullFormatter
+ def message_for(corrections)
+ ""
+ end
+ end
+
+ def setup
+ if defined?(DidYouMean)
+ @original_formatter = DidYouMean.formatter
+ DidYouMean.formatter = NullFormatter.new
+ end
+ end
+
+ def teardown
+ if defined?(DidYouMean)
+ DidYouMean.formatter = @original_formatter
+ end
+ end
+
class C
class << self
attr_accessor :keys
@@ -92,7 +111,8 @@ class TestPatternMatching < Test::Unit::TestCase
end
assert_block do
- verbose, $VERBOSE = $VERBOSE, nil # suppress "warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!"
+ # suppress "warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!"
+ experimental, Warning[:experimental] = Warning[:experimental], false
eval(%q{
case true
in a
@@ -100,7 +120,7 @@ class TestPatternMatching < Test::Unit::TestCase
end
})
ensure
- $VERBOSE = verbose
+ Warning[:experimental] = experimental
end
assert_block do
@@ -263,6 +283,7 @@ class TestPatternMatching < Test::Unit::TestCase
assert_block do
case 0
in a
+ assert_equal(0, a)
true
in a
flunk
@@ -270,7 +291,7 @@ class TestPatternMatching < Test::Unit::TestCase
end
assert_syntax_error(%q{
- 0 in [a, a]
+ 0 => [a, a]
}, /duplicated variable name/)
end
@@ -398,6 +419,53 @@ END
a == 0
end
end
+
+ assert_block do
+ @a = /a/
+ case 'abc'
+ in ^@a
+ true
+ end
+ end
+
+ assert_block do
+ @@TestPatternMatching = /a/
+ case 'abc'
+ in ^@@TestPatternMatching
+ true
+ end
+ end
+
+ assert_block do
+ $TestPatternMatching = /a/
+ case 'abc'
+ in ^$TestPatternMatching
+ true
+ end
+ end
+ end
+
+ def test_pin_operator_expr_pattern
+ assert_block do
+ case 'abc'
+ in ^(/a/)
+ true
+ end
+ end
+
+ assert_block do
+ case {name: '2.6', released_at: Time.new(2018, 12, 25)}
+ in {released_at: ^(Time.new(2010)..Time.new(2020))}
+ true
+ end
+ end
+
+ assert_block do
+ case 0
+ in ^(0+0)
+ true
+ end
+ end
end
def test_array_pattern
@@ -470,6 +538,7 @@ END
[[0], C.new([0])].all? do |i|
case i
in *a, 0, 1
+ raise a # suppress "unused variable: a" warning
else
true
end
@@ -636,6 +705,7 @@ END
assert_block do
case []
in [0, *a]
+ raise a # suppress "unused variable: a" warning
else
true
end
@@ -651,6 +721,7 @@ END
assert_block do
case [0]
in [0, *a, 1]
+ raise a # suppress "unused variable: a" warning
else
true
end
@@ -695,6 +766,7 @@ END
assert_block do
case []
in [0, *a]
+ raise a # suppress "unused variable: a" warning
else
true
end
@@ -730,6 +802,74 @@ END
end
end
+ def test_find_pattern
+ [0, 1, 2] => [*, 1 => a, *]
+ assert_equal(1, a)
+
+ [0, 1, 2] => [*a, 1 => b, *c]
+ assert_equal([0], a)
+ assert_equal(1, b)
+ assert_equal([2], c)
+
+ assert_block do
+ case [0, 1, 2]
+ in [*, 9, *]
+ false
+ else
+ true
+ end
+ end
+
+ assert_block do
+ case [0, 1, 2]
+ in [*, Integer, String, *]
+ false
+ else
+ true
+ end
+ end
+
+ [0, 1, 2] => [*a, 1 => b, 2 => c, *d]
+ assert_equal([0], a)
+ assert_equal(1, b)
+ assert_equal(2, c)
+ assert_equal([], d)
+
+ case [0, 1, 2]
+ in *, 1 => a, *;
+ assert_equal(1, a)
+ end
+
+ assert_block do
+ case [0, 1, 2]
+ in String(*, 1, *)
+ false
+ in Array(*, 1, *)
+ true
+ end
+ end
+
+ assert_block do
+ case [0, 1, 2]
+ in String[*, 1, *]
+ false
+ in Array[*, 1, *]
+ true
+ end
+ end
+
+ # https://bugs.ruby-lang.org/issues/17534
+ assert_block do
+ case [0, 1, 2]
+ in x
+ x = x # avoid a warning "assigned but unused variable - x"
+ true
+ in [*, 2, *]
+ false
+ end
+ end
+ end
+
def test_hash_pattern
assert_block do
[{}, C.new({})].all? do |i|
@@ -791,6 +931,7 @@ END
[{}, C.new({})].all? do |i|
case i
in a:
+ raise a # suppress "unused variable: a" warning
else
true
end
@@ -873,6 +1014,8 @@ END
[{}, C.new({})].all? do |i|
case i
in a:, **b
+ raise a # suppress "unused variable: a" warning
+ raise b # suppress "unused variable: b" warning
else
true
end
@@ -920,6 +1063,7 @@ END
[{a: 0}, C.new({a: 0})].all? do |i|
case i
in a:, **nil
+ assert_equal(0, a)
true
end
end
@@ -929,6 +1073,7 @@ END
[{a: 0, b: 1}, C.new({a: 0, b: 1})].all? do |i|
case i
in a:, **nil
+ assert_equal(0, a)
else
true
end
@@ -1031,6 +1176,34 @@ END
end
end
+ assert_block do
+ case {a: 0, b: 1}
+ in {a: 1,}
+ false
+ in {a:,}
+ _a = a
+ true
+ end
+ end
+
+ assert_block do
+ case {a: 0}
+ in {a: 1
+ }
+ false
+ in {a:
+ 2}
+ false
+ in a: {b:}, c:
+ _b = b
+ p c
+ in {a:
+ }
+ _a = a
+ true
+ end
+ end
+
assert_syntax_error(%q{
case _
in "a-b":
@@ -1066,6 +1239,10 @@ END
end
end
+ def test_nomatchingpatternerror
+ assert_equal(StandardError, NoMatchingPatternError.superclass)
+ end
+
def test_invalid_syntax
assert_syntax_error(%q{
case 0
@@ -1129,6 +1306,7 @@ END
assert_block do
case C.new({a: 0, b: 0, c: 0})
in {a: 0, b:}
+ assert_equal(0, b)
C.keys == [:a, :b]
end
end
@@ -1136,6 +1314,7 @@ END
assert_block do
case C.new({a: 0, b: 0, c: 0})
in {a: 0, b:, **}
+ assert_equal(0, b)
C.keys == [:a, :b]
end
end
@@ -1143,6 +1322,8 @@ END
assert_block do
case C.new({a: 0, b: 0, c: 0})
in {a: 0, b:, **r}
+ assert_equal(0, b)
+ assert_equal({c: 0}, r)
C.keys == nil
end
end
@@ -1157,6 +1338,7 @@ END
assert_block do
case C.new({a: 0, b: 0, c: 0})
in {**r}
+ assert_equal({a: 0, b: 0, c: 0}, r)
C.keys == nil
end
end
@@ -1164,6 +1346,94 @@ END
################################################################
+ class CDeconstructCache
+ def initialize(v)
+ @v = v
+ end
+
+ def deconstruct
+ @v.shift
+ end
+ end
+
+ def test_deconstruct_cache
+ assert_block do
+ case CDeconstructCache.new([[0]])
+ in [1]
+ in [0]
+ true
+ end
+ end
+
+ assert_block do
+ case CDeconstructCache.new([[0, 1]])
+ in [1,]
+ in [0,]
+ true
+ end
+ end
+
+ assert_block do
+ case CDeconstructCache.new([[[0]]])
+ in [[1]]
+ in [[*a]]
+ a == [0]
+ end
+ end
+
+ assert_block do
+ case CDeconstructCache.new([[0]])
+ in [x] if x > 0
+ in [0]
+ true
+ end
+ end
+
+ assert_block do
+ case CDeconstructCache.new([[0]])
+ in []
+ in [1] | [0]
+ true
+ end
+ end
+
+ assert_block do
+ case CDeconstructCache.new([[0]])
+ in [1] => _
+ in [0] => _
+ true
+ end
+ end
+
+ assert_block do
+ case CDeconstructCache.new([[0]])
+ in C[0]
+ in CDeconstructCache[0]
+ true
+ end
+ end
+
+ assert_block do
+ case [CDeconstructCache.new([[0], [1]])]
+ in [[1]]
+ false
+ in [[1]]
+ true
+ end
+ end
+
+ assert_block do
+ case CDeconstructCache.new([[0, :a, 1]])
+ in [*, String => x, *]
+ false
+ in [*, Symbol => x, *]
+ x == :a
+ end
+ end
+ end
+
+ ################################################################
+
class TestPatternMatchingRefinements < Test::Unit::TestCase
class C1
def deconstruct
@@ -1249,6 +1519,8 @@ END
s = Struct.new(:a, :b, keyword_init: true)
case s[a: 0, b: 1]
in a:, c:
+ raise a # suppress "unused variable: a" warning
+ raise c # suppress "unused variable: c" warning
flunk
in a:, b:, c:
flunk
@@ -1260,20 +1532,178 @@ END
################################################################
- def test_modifier_in
- 1 in a
+ def test_one_line
+ 1 => a
assert_equal 1, a
assert_raise(NoMatchingPatternError) do
- {a: 1} in {a: 0}
+ {a: 1} => {a: 0}
+ end
+
+ [1, 2] => a, b
+ assert_equal 1, a
+ assert_equal 2, b
+
+ {a: 1} => a:
+ assert_equal 1, a
+
+ assert_equal true, (1 in 1)
+ assert_equal false, (1 in 2)
+ end
+
+ def assert_experimental_warning(code)
+ w = Warning[:experimental]
+
+ Warning[:experimental] = false
+ assert_warn('') {eval(code)}
+
+ Warning[:experimental] = true
+ assert_warn(/is experimental/) {eval(code)}
+ ensure
+ Warning[:experimental] = w
+ end
+
+ def test_experimental_warning
+ assert_experimental_warning("case [0]; in [*, 0, *]; end")
+ end
+
+ def test_bug18990
+ {a: 0} => a:
+ assert_equal 0, a
+ {a: 0} => a:
+ assert_equal 0, a
+
+ {a: 0} in a:
+ assert_equal 0, a
+ {a: 0} in a:
+ assert_equal 0, a
+ end
+
+ ################################################################
+
+ def test_single_pattern_error_value_pattern
+ assert_raise_with_message(NoMatchingPatternError, "0: 1 === 0 does not return true") do
+ 0 => 1
+ end
+ end
+
+ def test_single_pattern_error_array_pattern
+ assert_raise_with_message(NoMatchingPatternError, "[]: Hash === [] does not return true") do
+ [] => Hash[]
+ end
+
+ assert_raise_with_message(NoMatchingPatternError, "0: 0 does not respond to #deconstruct") do
+ 0 => []
+ end
+
+ assert_raise_with_message(NoMatchingPatternError, "[0]: [0] length mismatch (given 1, expected 0)") do
+ [0] => []
+ end
+
+ assert_raise_with_message(NoMatchingPatternError, "[]: [] length mismatch (given 0, expected 1+)") do
+ [] => [_, *]
+ end
+
+ assert_raise_with_message(NoMatchingPatternError, "[0, 0]: 1 === 0 does not return true") do
+ [0, 0] => [0, 1]
+ end
+
+ assert_raise_with_message(NoMatchingPatternError, "[0, 0]: 1 === 0 does not return true") do
+ [0, 0] => [*, 0, 1]
+ end
+ end
+
+ def test_single_pattern_error_find_pattern
+ assert_raise_with_message(NoMatchingPatternError, "[]: Hash === [] does not return true") do
+ [] => Hash[*, _, *]
+ end
+
+ assert_raise_with_message(NoMatchingPatternError, "0: 0 does not respond to #deconstruct") do
+ 0 => [*, _, *]
+ end
+
+ assert_raise_with_message(NoMatchingPatternError, "[]: [] length mismatch (given 0, expected 1+)") do
+ [] => [*, _, *]
+ end
+
+ assert_raise_with_message(NoMatchingPatternError, "[0]: [0] does not match to find pattern") do
+ [0] => [*, 1, *]
+ end
+
+ assert_raise_with_message(NoMatchingPatternError, "[0]: [0] does not match to find pattern") do
+ [0] => [*, {a:}, *]
+ raise a # suppress "unused variable: a" warning
+ end
+ end
+
+ def test_single_pattern_error_hash_pattern
+ assert_raise_with_message(NoMatchingPatternError, "{}: Array === {} does not return true") do
+ {} => Array[a:]
+ raise a # suppress "unused variable: a" warning
+ end
+
+ assert_raise_with_message(NoMatchingPatternError, "0: 0 does not respond to #deconstruct_keys") do
+ 0 => {a:}
+ raise a # suppress "unused variable: a" warning
+ end
+
+ assert_raise_with_message(NoMatchingPatternKeyError, "{:a=>0}: key not found: :aa") do
+ {a: 0} => {aa:}
+ raise aa # suppress "unused variable: aa" warning
+ rescue NoMatchingPatternKeyError => e
+ assert_equal({a: 0}, e.matchee)
+ assert_equal(:aa, e.key)
+ raise e
+ end
+
+ assert_raise_with_message(NoMatchingPatternKeyError, "{:a=>{:b=>0}}: key not found: :bb") do
+ {a: {b: 0}} => {a: {bb:}}
+ raise bb # suppress "unused variable: bb" warning
+ rescue NoMatchingPatternKeyError => e
+ assert_equal({b: 0}, e.matchee)
+ assert_equal(:bb, e.key)
+ raise e
+ end
+
+ assert_raise_with_message(NoMatchingPatternError, "{:a=>0}: 1 === 0 does not return true") do
+ {a: 0} => {a: 1}
+ end
+
+ assert_raise_with_message(NoMatchingPatternError, "{:a=>0}: {:a=>0} is not empty") do
+ {a: 0} => {}
+ end
+
+ assert_raise_with_message(NoMatchingPatternError, "[{:a=>0}]: rest of {:a=>0} is not empty") do
+ [{a: 0}] => [{**nil}]
+ end
+ end
+
+ def test_single_pattern_error_as_pattern
+ assert_raise_with_message(NoMatchingPatternError, "[0]: 1 === 0 does not return true") do
+ case [0]
+ in [1] => _
+ end
+ end
+ end
+
+ def test_single_pattern_error_alternative_pattern
+ assert_raise_with_message(NoMatchingPatternError, "0: 2 === 0 does not return true") do
+ 0 => 1 | 2
+ end
+ end
+
+ def test_single_pattern_error_guard_clause
+ assert_raise_with_message(NoMatchingPatternError, "0: guard clause does not return true") do
+ case 0
+ in _ if false
+ end
+ end
+
+ assert_raise_with_message(NoMatchingPatternError, "0: guard clause does not return true") do
+ case 0
+ in _ unless true
+ end
end
- assert_syntax_error("if {} in {a:}; end", /void value expression/)
- assert_syntax_error(%q{
- 1 in a, b
- }, /unexpected/, '[ruby-core:95098]')
- assert_syntax_error(%q{
- 1 in a:
- }, /unexpected/, '[ruby-core:95098]')
end
end
END_of_GUARD
-$VERBOSE = verbose
+Warning[:experimental] = experimental
diff --git a/test/ruby/test_primitive.rb b/test/ruby/test_primitive.rb
index 19af44ad32..f1db934000 100644
--- a/test/ruby/test_primitive.rb
+++ b/test/ruby/test_primitive.rb
@@ -26,24 +26,31 @@ class TestRubyPrimitive < Test::Unit::TestCase
assert_equal 4, c
end
- C = 1
- class A
- Const = 1
- class B
- Const = 2
- class C
- Const = 3
- def const
- Const
+ C_Setup = -> do
+ remove_const :C if defined? ::TestRubyPrimitive::C
+ remove_const :A if defined? ::TestRubyPrimitive::A
+
+ C = 1
+ class A
+ Const = 1
+ class B
+ Const = 2
+ class C
+ Const = 3
+ def const
+ Const
+ end
end
end
end
+ (1..2).map {
+ A::B::C::Const
+ }
end
- (1..2).map {
- A::B::C::Const
- }
def test_constant
+ C_Setup.call
+
assert_equal 1, C
assert_equal 1, C
assert_equal 1, A::Const
@@ -145,42 +152,60 @@ class TestRubyPrimitive < Test::Unit::TestCase
assert_equal 7, ($test_ruby_primitive_gvar = 7)
end
- class A7
- @@c = 1
- def m
- @@c += 1
+ A7_Setup = -> do
+ remove_const :A7 if defined? TestRubyPrimitive::A7
+
+ class A7
+ @@c = 1
+ def m
+ @@c += 1
+ end
end
end
def test_cvar_from_instance_method
+ A7_Setup.call
+
assert_equal 2, A7.new.m
assert_equal 3, A7.new.m
assert_equal 4, A7.new.m
end
- class A8
- @@c = 1
- class << self
- def m
- @@c += 1
+ A8_Setup = -> do
+ remove_const :A8 if defined? TestRubyPrimitive::A8
+
+ class A8
+ @@c = 1
+ class << self
+ def m
+ @@c += 1
+ end
end
end
end
def test_cvar_from_singleton_method
+ A8_Setup.call
+
assert_equal 2, A8.m
assert_equal 3, A8.m
assert_equal 4, A8.m
end
- class A9
- @@c = 1
- def self.m
- @@c += 1
+ A9_Setup = -> do
+ remove_const :A8 if defined? TestRubyPrimitive::A8
+
+ class A9
+ @@c = 1
+ def self.m
+ @@c += 1
+ end
end
end
def test_cvar_from_singleton_method2
+ A9_Setup.call
+
assert_equal 2, A9.m
assert_equal 3, A9.m
assert_equal 4, A9.m
@@ -199,6 +224,9 @@ class TestRubyPrimitive < Test::Unit::TestCase
@iv += 2
assert_equal 4, @iv
+ # init @@cv
+ @@cv = nil
+
@@cv ||= 1
assert_equal 1, @@cv
@@cv &&= 2
diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb
index ea3fe823f0..51872e49be 100644
--- a/test/ruby/test_proc.rb
+++ b/test/ruby/test_proc.rb
@@ -4,7 +4,6 @@ require 'test/unit'
class TestProc < Test::Unit::TestCase
def setup
@verbose = $VERBOSE
- $VERBOSE = nil
end
def teardown
@@ -53,16 +52,14 @@ class TestProc < Test::Unit::TestCase
assert_equal(5, x)
end
- def assert_arity(n)
+ def assert_arity(n, &block)
meta = class << self; self; end
- b = assert_warn(/Capturing the given block using Proc\.new is deprecated/) do
- Proc.new
- end
+ b = Proc.new(&block)
meta.class_eval {
- remove_method(:foo) if method_defined?(:foo)
- define_method(:foo, b)
+ remove_method(:foo_arity) if method_defined?(:foo_arity)
+ define_method(:foo_arity, b)
}
- assert_equal(n, method(:foo).arity)
+ assert_equal(n, method(:foo_arity).arity)
end
def test_arity
@@ -139,6 +136,14 @@ class TestProc < Test::Unit::TestCase
lambda { x }
end
+ def m_nest0(&block)
+ block
+ end
+
+ def m_nest(&block)
+ [m_nest0(&block), m_nest0(&block)]
+ end
+
def test_eq
a = m(1)
b = m(2)
@@ -150,6 +155,17 @@ class TestProc < Test::Unit::TestCase
a = lambda {|x| lambda {} }.call(1)
b = lambda {}
assert_not_equal(a, b, "[ruby-dev:22601]")
+
+ assert_equal(*m_nest{}, "[ruby-core:84583] Feature #14627")
+ end
+
+ def test_hash
+ def self.capture(&block)
+ block
+ end
+
+ procs = Array.new(1000){capture{:foo }}
+ assert_operator(procs.map(&:hash).uniq.size, :>=, 500)
end
def test_block_par
@@ -263,7 +279,7 @@ class TestProc < Test::Unit::TestCase
def test_curry_given_blocks
b = lambda {|x, y, &blk| blk.call(x + y) }.curry
- b = b.call(2) { raise }
+ b = assert_warning(/given block not used/) {b.call(2) { raise }}
b = b.call(3) {|x| x + 4 }
assert_equal(9, b)
end
@@ -273,7 +289,7 @@ class TestProc < Test::Unit::TestCase
assert_equal(false, l.lambda?)
assert_equal(false, l.curry.lambda?, '[ruby-core:24127]')
assert_equal(false, proc(&l).lambda?)
- assert_equal(false, lambda(&l).lambda?)
+ assert_equal(false, assert_deprecated_warning {lambda(&l)}.lambda?)
assert_equal(false, Proc.new(&l).lambda?)
l = lambda {}
assert_equal(true, l.lambda?)
@@ -283,6 +299,49 @@ class TestProc < Test::Unit::TestCase
assert_equal(true, Proc.new(&l).lambda?)
end
+ def self.helper_test_warn_lamda_with_passed_block &b
+ lambda(&b)
+ end
+
+ def self.def_lambda_warning name, warn
+ define_method(name, proc do
+ prev = Warning[:deprecated]
+ assert_warn warn do
+ Warning[:deprecated] = true
+ yield
+ end
+ ensure
+ Warning[:deprecated] = prev
+ end)
+ end
+
+ def_lambda_warning 'test_lambda_warning_normal', '' do
+ lambda{}
+ end
+
+ def_lambda_warning 'test_lambda_warning_pass_lambda', '' do
+ b = lambda{}
+ lambda(&b)
+ end
+
+ def_lambda_warning 'test_lambda_warning_pass_symbol_proc', '' do
+ lambda(&:to_s)
+ end
+
+ def_lambda_warning 'test_lambda_warning_pass_proc', /deprecated/ do
+ b = proc{}
+ lambda(&b)
+ end
+
+ def_lambda_warning 'test_lambda_warning_pass_block', /deprecated/ do
+ helper_test_warn_lamda_with_passed_block{}
+ end
+
+ def_lambda_warning 'test_lambda_warning_pass_block_symbol_proc', '' do
+ # Symbol#to_proc returns lambda
+ helper_test_warn_lamda_with_passed_block(&:to_s)
+ end
+
def test_curry_ski_fib
s = proc {|f, g, x| f[x][g[x]] }.curry
k = proc {|x, y| x }.curry
@@ -383,7 +442,7 @@ class TestProc < Test::Unit::TestCase
def test_proc_lambda
assert_raise(ArgumentError) { proc }
- assert_raise(ArgumentError) { lambda }
+ assert_raise(ArgumentError) { assert_warn(/deprecated/) {lambda} }
o = Object.new
def o.foo
@@ -391,14 +450,18 @@ class TestProc < Test::Unit::TestCase
1.times { b = lambda }
b
end
- assert_raise(ArgumentError) {o.foo { :foo }.call}
+ assert_raise(ArgumentError) do
+ assert_deprecated_warning {o.foo { :foo }}.call
+ end
- def o.foo(&b)
+ def o.bar(&b)
b = nil
1.times { b = lambda }
b
end
- assert_raise(ArgumentError) {o.foo { :foo }.call}
+ assert_raise(ArgumentError) do
+ assert_deprecated_warning {o.bar { :foo }}.call
+ end
end
def test_arity2
@@ -784,6 +847,33 @@ class TestProc < Test::Unit::TestCase
assert_equal [[1, 2], Proc, :x], (pr.call(1, 2){|x| x})
end
+ def test_proc_args_only_rest
+ pr = proc {|*c| c }
+ assert_equal [], pr.call()
+ assert_equal [1], pr.call(1)
+ assert_equal [[1]], pr.call([1])
+ assert_equal [1, 2], pr.call(1,2)
+ assert_equal [[1, 2]], pr.call([1,2])
+ end
+
+ def test_proc_args_rest_kw
+ pr = proc {|*c, a: 1| [c, a] }
+ assert_equal [[], 1], pr.call()
+ assert_equal [[1], 1], pr.call(1)
+ assert_equal [[[1]], 1], pr.call([1])
+ assert_equal [[1, 2], 1], pr.call(1,2)
+ assert_equal [[[1, 2]], 1], pr.call([1,2])
+ end
+
+ def test_proc_args_rest_kwsplat
+ pr = proc {|*c, **kw| [c, kw] }
+ assert_equal [[], {}], pr.call()
+ assert_equal [[1], {}], pr.call(1)
+ assert_equal [[[1]], {}], pr.call([1])
+ assert_equal [[1, 2], {}], pr.call(1,2)
+ assert_equal [[[1, 2]], {}], pr.call([1,2])
+ end
+
def test_proc_args_pos_rest_post_block
pr = proc {|a,b,*c,d,e,&f|
[a, b, c, d, e, f.class, f&&f.call(:x)]
@@ -1087,6 +1177,36 @@ class TestProc < Test::Unit::TestCase
assert_equal([1,2,[3],4,5], r, "[ruby-core:19485]")
end
+ def test_proc_autosplat
+ def self.a(arg, kw)
+ yield arg
+ yield arg, **kw
+ yield arg, kw
+ end
+
+ arr = []
+ a([1,2,3], {}) do |arg1, arg2=0|
+ arr << [arg1, arg2]
+ end
+ assert_equal([[1, 2], [[1, 2, 3], 0], [[1, 2, 3], {}]], arr)
+
+ arr = []
+ a([1,2,3], a: 1) do |arg1, arg2=0|
+ arr << [arg1, arg2]
+ end
+ assert_equal([[1, 2], [[1, 2, 3], {a: 1}], [[1, 2, 3], {a: 1}]], arr)
+ end
+
+ def test_proc_single_arg_with_keywords_accepted_and_yielded
+ def self.a
+ yield [], **{a: 1}
+ end
+ res = a do |arg, **opts|
+ [arg, opts]
+ end
+ assert_equal([[], {a: 1}], res)
+ end
+
def test_parameters
assert_equal([], proc {}.parameters)
assert_equal([], proc {||}.parameters)
@@ -1376,16 +1496,6 @@ class TestProc < Test::Unit::TestCase
end;
end
- def method_for_test_proc_without_block_for_symbol
- assert_warn(/Capturing the given block using Kernel#proc is deprecated/) do
- binding.eval('proc')
- end
- end
-
- def test_proc_without_block_for_symbol
- assert_equal('1', method_for_test_proc_without_block_for_symbol(&:to_s).call(1), '[Bug #14782]')
- end
-
def test_compose
f = proc {|x| x * 2}
g = proc {|x| x + 1}
@@ -1413,9 +1523,13 @@ class TestProc < Test::Unit::TestCase
def test_compose_with_lambda
f = lambda {|x| x * 2}
g = lambda {|x| x}
+ not_lambda = proc {|x| x}
assert_predicate((f << g), :lambda?)
assert_predicate((g >> f), :lambda?)
+ assert_predicate((not_lambda << f), :lambda?)
+ assert_not_predicate((f << not_lambda), :lambda?)
+ assert_not_predicate((not_lambda >> f), :lambda?)
end
def test_compose_with_method
@@ -1427,6 +1541,7 @@ class TestProc < Test::Unit::TestCase
assert_equal(6, (f << g).call(2))
assert_equal(5, (f >> g).call(2))
+ assert_predicate((f << g), :lambda?)
end
def test_compose_with_callable
@@ -1438,6 +1553,7 @@ class TestProc < Test::Unit::TestCase
assert_equal(6, (f << g).call(2))
assert_equal(5, (f >> g).call(2))
+ assert_predicate((f << g), :lambda?)
end
def test_compose_with_noncallable
@@ -1477,6 +1593,63 @@ class TestProc < Test::Unit::TestCase
assert_equal(42, Module.new { extend self
def m1(&b) b end; def m2(); m1 { next 42 } end }.m2.call)
end
+
+ def test_isolate
+ assert_raise_with_message ArgumentError, /\(a\)/ do
+ a = :a
+ Proc.new{p a}.isolate
+ end
+
+ assert_raise_with_message ArgumentError, /\(a\)/ do
+ a = :a
+ 1.times{
+ Proc.new{p a}.isolate
+ }
+ end
+
+ assert_raise_with_message ArgumentError, /yield/ do
+ Proc.new{yield}.isolate
+ end
+
+
+ name = "\u{2603 26a1}"
+ assert_raise_with_message(ArgumentError, /\(#{name}\)/) do
+ eval("#{name} = :#{name}; Proc.new {p #{name}}").isolate
+ end
+
+ # binding
+
+ :a.tap{|a|
+ :b.tap{|b|
+ Proc.new{
+ :c.tap{|c|
+ assert_equal :c, eval('c')
+
+ assert_raise_with_message SyntaxError, /\`a\'/ do
+ eval('p a')
+ end
+
+ assert_raise_with_message SyntaxError, /\`b\'/ do
+ eval('p b')
+ end
+
+ assert_raise_with_message SyntaxError, /can not yield from isolated Proc/ do
+ eval('p yield')
+ end
+
+ assert_equal :c, binding.local_variable_get(:c)
+
+ assert_raise_with_message NameError, /local variable \`a\' is not defined/ do
+ binding.local_variable_get(:a)
+ end
+
+ assert_equal [:c], local_variables
+ assert_equal [:c], binding.local_variables
+ }
+ }.isolate.call
+ }
+ }
+ end if proc{}.respond_to? :isolate
end
class TestProcKeywords < Test::Unit::TestCase
@@ -1485,29 +1658,15 @@ class TestProcKeywords < Test::Unit::TestCase
g = ->(kw) { kw.merge(:a=>2) }
assert_equal(2, (f >> g).call(a: 3)[:a])
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(1, (f << g).call(a: 3)[:a])
- end
+ assert_raise(ArgumentError) { (f << g).call(a: 3)[:a] }
assert_equal(2, (f >> g).call(a: 3)[:a])
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(1, (f << g).call({a: 3})[:a])
- end
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(2, (f >> g).call({a: 3})[:a])
- end
+ assert_raise(ArgumentError) { (f << g).call({a: 3})[:a] }
+ assert_raise(ArgumentError) { (f >> g).call({a: 3})[:a] }
assert_equal(2, (g << f).call(a: 3)[:a])
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(1, (g >> f).call(a: 3)[:a])
- end
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(2, (g << f).call({a: 3})[:a])
- end
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(1, (g >> f).call({a: 3})[:a])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.*The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(1, (f << g).call(**{})[:a])
- end
+ assert_raise(ArgumentError) { (g >> f).call(a: 3)[:a] }
+ assert_raise(ArgumentError) { (g << f).call({a: 3})[:a] }
+ assert_raise(ArgumentError) { (g >> f).call({a: 3})[:a] }
+ assert_raise(ArgumentError) { (f << g).call(**{})[:a] }
assert_equal(2, (f >> g).call(**{})[:a])
end
@@ -1515,29 +1674,15 @@ class TestProcKeywords < Test::Unit::TestCase
f = ->(**kw) { kw.merge(:a=>1) }.method(:call)
g = ->(kw) { kw.merge(:a=>2) }.method(:call)
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(1, (f << g).call(a: 3)[:a])
- end
+ assert_raise(ArgumentError) { (f << g).call(a: 3)[:a] }
assert_equal(2, (f >> g).call(a: 3)[:a])
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(1, (f << g).call({a: 3})[:a])
- end
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(2, (f >> g).call({a: 3})[:a])
- end
+ assert_raise(ArgumentError) { (f << g).call({a: 3})[:a] }
+ assert_raise(ArgumentError) { (f >> g).call({a: 3})[:a] }
assert_equal(2, (g << f).call(a: 3)[:a])
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(1, (g >> f).call(a: 3)[:a])
- end
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(2, (g << f).call({a: 3})[:a])
- end
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(1, (g >> f).call({a: 3})[:a])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.*The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(1, (f << g).call(**{})[:a])
- end
+ assert_raise(ArgumentError) { (g >> f).call(a: 3)[:a] }
+ assert_raise(ArgumentError) { (g << f).call({a: 3})[:a] }
+ assert_raise(ArgumentError) { (g >> f).call({a: 3})[:a] }
+ assert_raise(ArgumentError) { (f << g).call(**{})[:a] }
assert_equal(2, (f >> g).call(**{})[:a])
end
@@ -1549,29 +1694,15 @@ class TestProcKeywords < Test::Unit::TestCase
def g.<<(f) to_proc << f end
def g.>>(f) to_proc >> f end
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(1, (f << g).call(a: 3)[:a])
- end
+ assert_raise(ArgumentError) { (f << g).call(a: 3)[:a] }
assert_equal(2, (f >> g).call(a: 3)[:a])
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(1, (f << g).call({a: 3})[:a])
- end
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(2, (f >> g).call({a: 3})[:a])
- end
+ assert_raise(ArgumentError) { (f << g).call({a: 3})[:a] }
+ assert_raise(ArgumentError) { (f >> g).call({a: 3})[:a] }
assert_equal(2, (g << f).call(a: 3)[:a])
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(1, (g >> f).call(a: 3)[:a])
- end
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(2, (g << f).call({a: 3})[:a])
- end
- assert_warn(/The last argument is used as the keyword parameter.*for method/m) do
- assert_equal(1, (g >> f).call({a: 3})[:a])
- end
- assert_warn(/The keyword argument is passed as the last hash parameter.*for `call'/m) do
- assert_equal(1, (f << g).call(**{})[:a])
- end
+ assert_raise(ArgumentError) { (g >> f).call(a: 3)[:a] }
+ assert_raise(ArgumentError) { (g << f).call({a: 3})[:a] }
+ assert_raise(ArgumentError) { (g >> f).call({a: 3})[:a] }
+ assert_raise(ArgumentError) { (f << g).call(**{})[:a] }
assert_equal(2, (f >> g).call(**{})[:a])
f = ->(kw) { kw.merge(:a=>1) }
@@ -1582,29 +1713,15 @@ class TestProcKeywords < Test::Unit::TestCase
def g.>>(f) to_proc >> f end
assert_equal(1, (f << g).call(a: 3)[:a])
- assert_warn(/The last argument is used as the keyword parameter.*for `call'/m) do
- assert_equal(2, (f >> g).call(a: 3)[:a])
- end
- assert_warn(/The last argument is used as the keyword parameter.*for `call'/m) do
- assert_equal(1, (f << g).call({a: 3})[:a])
- end
- assert_warn(/The last argument is used as the keyword parameter.*for `call'/m) do
- assert_equal(2, (f >> g).call({a: 3})[:a])
- end
- assert_warn(/The last argument is used as the keyword parameter.*for `call'/m) do
- assert_equal(2, (g << f).call(a: 3)[:a])
- end
+ assert_raise(ArgumentError) { (f >> g).call(a: 3)[:a] }
+ assert_raise(ArgumentError) { (f << g).call({a: 3})[:a] }
+ assert_raise(ArgumentError) { (f >> g).call({a: 3})[:a] }
+ assert_raise(ArgumentError) { (g << f).call(a: 3)[:a] }
assert_equal(1, (g >> f).call(a: 3)[:a])
- assert_warn(/The last argument is used as the keyword parameter.*for `call'/m) do
- assert_equal(2, (g << f).call({a: 3})[:a])
- end
- assert_warn(/The last argument is used as the keyword parameter.*for `call'/m) do
- assert_equal(1, (g >> f).call({a: 3})[:a])
- end
+ assert_raise(ArgumentError) { (g << f).call({a: 3})[:a] }
+ assert_raise(ArgumentError) { (g >> f).call({a: 3})[:a] }
assert_equal(1, (f << g).call(**{})[:a])
- assert_warn(/The keyword argument is passed as the last hash parameter.*The last argument is used as the keyword parameter.*for `call'/m) do
- assert_equal(2, (f >> g).call(**{})[:a])
- end
+ assert_raise(ArgumentError) { (f >> g).call(**{})[:a] }
end
end
diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb
index e0cb49b8ef..30427aeec1 100644
--- a/test/ruby/test_process.rb
+++ b/test/ruby/test_process.rb
@@ -208,39 +208,39 @@ class TestProcess < Test::Unit::TestCase
n = max
IO.popen([RUBY, "-e",
- "p Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io|
- assert_equal("[#{n}, #{n}]\n", io.read)
+ "puts Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io|
+ assert_equal("#{n}\n#{n}\n", io.read)
}
n = 0
IO.popen([RUBY, "-e",
- "p Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io|
- assert_equal("[#{n}, #{n}]\n", io.read)
+ "puts Process.getrlimit(:CORE)", :rlimit_core=>n]) {|io|
+ assert_equal("#{n}\n#{n}\n", io.read)
}
n = max
IO.popen([RUBY, "-e",
- "p Process.getrlimit(:CORE)", :rlimit_core=>[n]]) {|io|
- assert_equal("[#{n}, #{n}]", io.read.chomp)
+ "puts Process.getrlimit(:CORE)", :rlimit_core=>[n]]) {|io|
+ assert_equal("#{n}\n#{n}\n", io.read)
}
m, n = 0, max
IO.popen([RUBY, "-e",
- "p Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io|
- assert_equal("[#{m}, #{n}]", io.read.chomp)
+ "puts Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io|
+ assert_equal("#{m}\n#{n}\n", io.read)
}
m, n = 0, 0
IO.popen([RUBY, "-e",
- "p Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io|
- assert_equal("[#{m}, #{n}]", io.read.chomp)
+ "puts Process.getrlimit(:CORE)", :rlimit_core=>[m,n]]) {|io|
+ assert_equal("#{m}\n#{n}\n", io.read)
}
n = max
IO.popen([RUBY, "-e",
- "p Process.getrlimit(:CORE), Process.getrlimit(:CPU)",
+ "puts Process.getrlimit(:CORE), Process.getrlimit(:CPU)",
:rlimit_core=>n, :rlimit_cpu=>3600]) {|io|
- assert_equal("[#{n}, #{n}]\n[3600, 3600]", io.read.chomp)
+ assert_equal("#{n}\n#{n}\n""3600\n3600\n", io.read)
}
assert_raise(ArgumentError) do
@@ -261,6 +261,18 @@ class TestProcess < Test::Unit::TestCase
}
end
+ def test_overwrite_ENV
+ assert_separately([],"#{<<~"begin;"}\n#{<<~"end;"}")
+ BUG = "[ruby-core:105223] [Bug #18164]"
+ begin;
+ $VERBOSE = nil
+ ENV = {}
+ pid = spawn({}, *#{TRUECOMMAND.inspect})
+ ENV.replace({})
+ assert_kind_of(Integer, pid, BUG)
+ end;
+ end
+
MANDATORY_ENVS = %w[RUBYLIB MJIT_SEARCH_BUILD_DIR]
case RbConfig::CONFIG['target_os']
when /linux/
@@ -338,6 +350,13 @@ class TestProcess < Test::Unit::TestCase
ensure
ENV["hmm"] = old
end
+
+ assert_raise_with_message(ArgumentError, /fo=fo/) {
+ system({"fo=fo"=>"ha"}, *ENVCOMMAND)
+ }
+ assert_raise_with_message(ArgumentError, /\u{30c0}=\u{30e1}/) {
+ system({"\u{30c0}=\u{30e1}"=>"ha"}, *ENVCOMMAND)
+ }
end
def test_execopt_env_path
@@ -1418,6 +1437,8 @@ class TestProcess < Test::Unit::TestCase
assert_equal(s.to_i >> 1, s >> 1)
assert_equal(false, s.stopped?)
assert_equal(nil, s.stopsig)
+
+ assert_equal(s, Marshal.load(Marshal.dump(s)))
end
end
@@ -1435,6 +1456,8 @@ class TestProcess < Test::Unit::TestCase
assert_equal(expected,
[s.exited?, s.signaled?, s.stopped?, s.success?],
"[s.exited?, s.signaled?, s.stopped?, s.success?]")
+
+ assert_equal(s, Marshal.load(Marshal.dump(s)))
end
end
@@ -1449,6 +1472,27 @@ class TestProcess < Test::Unit::TestCase
"[s.exited?, s.signaled?, s.stopped?, s.success?]")
assert_equal("#<Process::Status: pid #{ s.pid } SIGQUIT (signal #{ s.termsig })>",
s.inspect.sub(/ \(core dumped\)(?=>\z)/, ''))
+
+ assert_equal(s, Marshal.load(Marshal.dump(s)))
+ end
+ end
+
+ def test_status_fail
+ ret = Process::Status.wait($$)
+ assert_instance_of(Process::Status, ret)
+ assert_equal(-1, ret.pid)
+ end
+
+
+ def test_status_wait
+ IO.popen([RUBY, "-e", "gets"], "w") do |io|
+ pid = io.pid
+ assert_nil(Process::Status.wait(pid, Process::WNOHANG))
+ io.puts
+ ret = Process::Status.wait(pid)
+ assert_instance_of(Process::Status, ret)
+ assert_equal(pid, ret.pid)
+ assert_predicate(ret, :exited?)
end
end
@@ -1598,6 +1642,34 @@ class TestProcess < Test::Unit::TestCase
rescue NotImplementedError
end
+ if Process::UID.respond_to?(:from_name)
+ def test_uid_from_name
+ if u = Etc.getpwuid(Process.uid)
+ assert_equal(Process.uid, Process::UID.from_name(u.name), u.name)
+ end
+ assert_raise_with_message(ArgumentError, /\u{4e0d 5b58 5728}/) {
+ Process::UID.from_name("\u{4e0d 5b58 5728}")
+ }
+ end
+ end
+
+ if Process::GID.respond_to?(:from_name) && !RUBY_PLATFORM.include?("android")
+ def test_gid_from_name
+ if g = Etc.getgrgid(Process.gid)
+ assert_equal(Process.gid, Process::GID.from_name(g.name), g.name)
+ end
+ expected_excs = [ArgumentError]
+ expected_excs << Errno::ENOENT if defined?(Errno::ENOENT)
+ expected_excs << Errno::ESRCH if defined?(Errno::ESRCH) # WSL 2 actually raises Errno::ESRCH
+ expected_excs << Errno::EBADF if defined?(Errno::EBADF)
+ expected_excs << Errno::EPERM if defined?(Errno::EPERM)
+ exc = assert_raise(*expected_excs) do
+ Process::GID.from_name("\u{4e0d 5b58 5728}") # fu son zai ("absent" in Kanji)
+ end
+ assert_match(/\u{4e0d 5b58 5728}/, exc.message) if exc.is_a?(ArgumentError)
+ end
+ end
+
def test_uid_re_exchangeable_p
r = Process::UID.re_exchangeable?
assert_include([true, false], r)
@@ -1646,7 +1718,7 @@ class TestProcess < Test::Unit::TestCase
Process.wait pid
assert_send [sig_r, :wait_readable, 5], 'self-pipe not readable'
end
- if RubyVM::MJIT.enabled? # checking -DMJIT_FORCE_ENABLE. It may trigger extra SIGCHLD.
+ if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # checking -DMJIT_FORCE_ENABLE. It may trigger extra SIGCHLD.
assert_equal [true], signal_received.uniq, "[ruby-core:19744]"
else
assert_equal [true], signal_received, "[ruby-core:19744]"
@@ -1660,6 +1732,9 @@ class TestProcess < Test::Unit::TestCase
end
def test_no_curdir
+ if /solaris/i =~ RUBY_PLATFORM
+ skip "Temporary skip to avoid CI failures after commit to use realpath on required files"
+ end
with_tmpchdir {|d|
Dir.mkdir("vd")
status = nil
@@ -1711,6 +1786,7 @@ class TestProcess < Test::Unit::TestCase
min = 1_000 / (cmd.size + sep.size)
cmds = Array.new(min, cmd)
exs = [Errno::ENOENT]
+ exs << Errno::EINVAL if windows?
exs << Errno::E2BIG if defined?(Errno::E2BIG)
opts = {[STDOUT, STDERR]=>File::NULL}
opts[:rlimit_nproc] = 128 if defined?(Process::RLIMIT_NPROC)
@@ -1762,6 +1838,8 @@ class TestProcess < Test::Unit::TestCase
end
def test_daemon_noclose
+ pend "macOS 15 beta is not working with this test" if /darwin/ =~ RUBY_PLATFORM && /15/ =~ `sw_vers -productVersion`
+
data = IO.popen("-", "r+") do |f|
break f.read if f
Process.daemon(false, true)
@@ -2260,8 +2338,6 @@ EOS
pid = fork {Process.kill(:QUIT, parent)}
IO.popen([ruby, -'--disable=gems'], -'r+'){}
Process.wait(pid)
- $stdout.puts
- $stdout.flush
end
INPUT
end if defined?(fork)
@@ -2374,7 +2450,7 @@ EOS
rescue SystemCallError
w.syswrite("exec failed\n")
end
- q = Queue.new
+ q = Thread::Queue.new
th1 = Thread.new { i = 0; i += 1 while q.empty?; i }
th2 = Thread.new { j = 0; j += 1 while q.empty? && Thread.pass.nil?; j }
sleep 0.5
@@ -2383,7 +2459,7 @@ EOS
end
w.close
assert_equal "exec failed\n", r.gets
- vals = r.gets.chomp.split.map!(&:to_i)
+ vals = r.gets.split.map!(&:to_i)
assert_operator vals[0], :>, vals[1], vals.inspect
_, status = Process.waitpid2(pid)
end
@@ -2399,6 +2475,15 @@ EOS
r.close if r
end if defined?(fork)
+ def test_rescue_exec_fail
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_raise(Errno::ENOENT) do
+ exec("", in: "")
+ end
+ end;
+ end
+
def test_many_args
bug11418 = '[ruby-core:70251] [Bug #11418]'
assert_in_out_err([], <<-"end;", ["x"]*256, [], bug11418, timeout: 60)
@@ -2449,4 +2534,164 @@ EOS
Process.wait spawn(RUBY, "-e", "exit 13")
assert_same(Process.last_status, $?)
end
+
+ def test_last_status_failure
+ assert_nil system("sad")
+ assert_not_predicate $?, :success?
+ assert_equal $?.exitstatus, 127
+ end
+
+ def test_exec_failure_leaves_no_child
+ assert_raise(Errno::ENOENT) do
+ spawn('inexistent_command')
+ end
+ assert_empty(Process.waitall)
+ end
+
+ def test__fork
+ r, w = IO.pipe
+ pid = Process._fork
+ if pid == 0
+ begin
+ r.close
+ w << "ok: #$$"
+ w.close
+ ensure
+ exit!
+ end
+ else
+ w.close
+ assert_equal("ok: #{pid}", r.read)
+ r.close
+ Process.waitpid(pid)
+ end
+ end if Process.respond_to?(:_fork)
+
+ def test__fork_hook
+ %w(fork Process.fork).each do |method|
+ feature17795 = '[ruby-core:103400] [Feature #17795]'
+ assert_in_out_err([], <<-"end;", [], [], feature17795, timeout: 60) do |r, e|
+ module ForkHook
+ def _fork
+ p :before
+ ret = super
+ p :after
+ ret
+ end
+ end
+
+ Process.singleton_class.prepend(ForkHook)
+
+ pid = #{ method }
+ p pid
+ Process.waitpid(pid) if pid
+ end;
+ assert_equal([], e)
+ assert_equal(":before", r.shift)
+ assert_equal(":after", r.shift)
+ s = r.map {|s| s.chomp }.sort #=> [pid, ":after", "nil"]
+ assert_match(/^\d+$/, s[0]) # pid
+ assert_equal(":after", s[1])
+ assert_equal("nil", s[2])
+ end
+ end
+ end if Process.respond_to?(:_fork)
+
+ def test__fork_hook_popen
+ feature17795 = '[ruby-core:103400] [Feature #17795]'
+ assert_in_out_err([], <<-"end;", %w(:before :after :after foo bar), [], feature17795, timeout: 60)
+ module ForkHook
+ def _fork
+ p :before
+ ret = super
+ p :after
+ ret
+ end
+ end
+
+ Process.singleton_class.prepend(ForkHook)
+
+ IO.popen("-") {|io|
+ if !io
+ puts "foo"
+ else
+ puts io.read + "bar"
+ end
+ }
+ end;
+ end if Process.respond_to?(:_fork)
+
+ def test__fork_wrong_type_hook
+ feature17795 = '[ruby-core:103400] [Feature #17795]'
+ assert_in_out_err([], <<-"end;", ["OK"], [], feature17795, timeout: 60)
+ module ForkHook
+ def _fork
+ "BOO"
+ end
+ end
+
+ Process.singleton_class.prepend(ForkHook)
+
+ begin
+ fork
+ rescue TypeError
+ puts "OK"
+ end
+ end;
+ end if Process.respond_to?(:_fork)
+
+ def test_concurrent_group_and_pid_wait
+ # Use a pair of pipes that will make long_pid exit when this test exits, to avoid
+ # leaking temp processes.
+ long_rpipe, long_wpipe = IO.pipe
+ short_rpipe, short_wpipe = IO.pipe
+ # This process should run forever
+ long_pid = fork do
+ [short_rpipe, short_wpipe, long_wpipe].each(&:close)
+ long_rpipe.read
+ end
+ # This process will exit
+ short_pid = fork do
+ [long_rpipe, long_wpipe, short_wpipe].each(&:close)
+ short_rpipe.read
+ end
+ t1, t2, t3 = nil
+ EnvUtil.timeout(5) do
+ t1 = Thread.new do
+ Process.waitpid long_pid
+ end
+ # Wait for us to be blocking in a call to waitpid2
+ Thread.pass until t1.stop?
+ short_wpipe.close # Make short_pid exit
+
+ # The short pid has exited, so -1 should pick that up.
+ assert_equal short_pid, Process.waitpid(-1)
+
+ # Terminate t1 for the next phase of the test.
+ t1.kill
+ t1.join
+
+ t2 = Thread.new do
+ Process.waitpid -1
+ rescue Errno::ECHILD
+ nil
+ end
+ Thread.pass until t2.stop?
+ t3 = Thread.new do
+ Process.waitpid long_pid
+ rescue Errno::ECHILD
+ nil
+ end
+ Thread.pass until t3.stop?
+
+ # it's actually nondeterministic which of t2 or t3 will receive the wait (this
+ # nondeterminism comes from the behaviour of the underlying system calls)
+ long_wpipe.close
+ assert_equal [long_pid], [t2, t3].map(&:value).compact
+ end
+ ensure
+ [t1, t2, t3].each { _1&.kill rescue nil }
+ [t1, t2, t3].each { _1&.join rescue nil }
+ [long_rpipe, long_wpipe, short_rpipe, short_wpipe].each { _1&.close rescue nil }
+ end if defined?(fork)
end
diff --git a/test/ruby/test_rand.rb b/test/ruby/test_rand.rb
index 939d17bdf7..13b7329269 100644
--- a/test/ruby/test_rand.rb
+++ b/test/ruby/test_rand.rb
@@ -2,14 +2,13 @@
require 'test/unit'
class TestRand < Test::Unit::TestCase
- def assert_random_int(ws, m, init = 0)
+ def assert_random_int(m, init = 0, iterate: 5)
srand(init)
rnds = [Random.new(init)]
rnds2 = [rnds[0].dup]
rnds3 = [rnds[0].dup]
- ws.each_with_index do |w, i|
- w = w.to_i
- assert_equal(w, rand(m))
+ iterate.times do |i|
+ w = rand(m)
rnds.each do |rnd|
assert_equal(w, rnd.rand(m))
end
@@ -27,133 +26,97 @@ class TestRand < Test::Unit::TestCase
end
def test_mt
- assert_random_int(%w(1067595299 955945823 477289528 4107218783 4228976476),
- 0x100000000, 0x00000456_00000345_00000234_00000123)
+ assert_random_int(0x100000000, 0x00000456_00000345_00000234_00000123)
end
def test_0x3fffffff
- assert_random_int(%w(209652396 398764591 924231285 404868288 441365315),
- 0x3fffffff)
+ assert_random_int(0x3fffffff)
end
def test_0x40000000
- assert_random_int(%w(209652396 398764591 924231285 404868288 441365315),
- 0x40000000)
+ assert_random_int(0x40000000)
end
def test_0x40000001
- assert_random_int(%w(209652396 398764591 924231285 441365315 192771779),
- 0x40000001)
+ assert_random_int(0x40000001)
end
def test_0xffffffff
- assert_random_int(%w(2357136044 2546248239 3071714933 3626093760 2588848963),
- 0xffffffff)
+ assert_random_int(0xffffffff)
end
def test_0x100000000
- assert_random_int(%w(2357136044 2546248239 3071714933 3626093760 2588848963),
- 0x100000000)
+ assert_random_int(0x100000000)
end
def test_0x100000001
- assert_random_int(%w(2546248239 1277901399 243580376 1171049868 2051556033),
- 0x100000001)
+ assert_random_int(0x100000001)
end
def test_rand_0x100000000
- assert_random_int(%w(4119812344 3870378946 80324654 4294967296 410016213),
- 0x100000001, 311702798)
+ assert_random_int(0x100000001, 311702798)
end
def test_0x1000000000000
- assert_random_int(%w(11736396900911
- 183025067478208
- 197104029029115
- 130583529618791
- 180361239846611),
- 0x1000000000000)
+ assert_random_int(0x1000000000000)
end
def test_0x1000000000001
- assert_random_int(%w(187121911899765
- 197104029029115
- 180361239846611
- 236336749852452
- 208739549485656),
- 0x1000000000001)
+ assert_random_int(0x1000000000001)
end
def test_0x3fffffffffffffff
- assert_random_int(%w(900450186894289455
- 3969543146641149120
- 1895649597198586619
- 827948490035658087
- 3203365596207111891),
- 0x3fffffffffffffff)
+ assert_random_int(0x3fffffffffffffff)
end
def test_0x4000000000000000
- assert_random_int(%w(900450186894289455
- 3969543146641149120
- 1895649597198586619
- 827948490035658087
- 3203365596207111891),
- 0x4000000000000000)
+ assert_random_int(0x4000000000000000)
end
def test_0x4000000000000001
- assert_random_int(%w(900450186894289455
- 3969543146641149120
- 1895649597198586619
- 827948490035658087
- 2279347887019741461),
- 0x4000000000000001)
+ assert_random_int(0x4000000000000001)
end
def test_0x10000000000
- ws = %w(455570294424 1073054410371 790795084744 2445173525 1088503892627)
- assert_random_int(ws, 0x10000000000, 3)
+ assert_random_int(0x10000000000, 3)
end
def test_0x10000
- ws = %w(2732 43567 42613 52416 45891)
- assert_random_int(ws, 0x10000)
+ assert_random_int(0x10000)
+ end
+
+ def assert_same_numbers(type, *nums)
+ nums.each do |n|
+ assert_instance_of(type, n)
+ end
+ x = nums.shift
+ nums.each do |n|
+ assert_equal(x, n)
+ end
+ x
end
def test_types
- srand(0)
- rnd = Random.new(0)
- assert_equal(44, rand(100.0))
- assert_equal(44, rnd.rand(100))
- assert_equal(1245085576965981900420779258691, rand((2**100).to_f))
- assert_equal(1245085576965981900420779258691, rnd.rand(2**100))
- assert_equal(914679880601515615685077935113, rand(-(2**100).to_f))
+ o = Object.new
+ class << o
+ def to_int; 100; end
+ def class; Integer; end
+ end
srand(0)
- rnd = Random.new(0)
- assert_equal(997707939797331598305742933184, rand(2**100))
- assert_equal(997707939797331598305742933184, rnd.rand(2**100))
- assert_in_delta(0.602763376071644, rand((2**100).coerce(0).first),
- 0.000000000000001)
- assert_raise(ArgumentError) {rnd.rand((2**100).coerce(0).first)}
+ nums = [100.0, (2**100).to_f, (2**100), o, o, o].map do |m|
+ k = Integer
+ assert_kind_of(k, x = rand(m), m.inspect)
+ [m, k, x]
+ end
+ assert_kind_of(Integer, rand(-(2**100).to_f))
srand(0)
rnd = Random.new(0)
- assert_in_delta(0.548813503927325, rand(nil),
- 0.000000000000001)
- assert_in_delta(0.548813503927325, rnd.rand(),
- 0.000000000000001)
- srand(0)
- rnd = Random.new(0)
- o = Object.new
- def o.to_int; 100; end
- assert_equal(44, rand(o))
- assert_equal(44, rnd.rand(o))
- assert_equal(47, rand(o))
- assert_equal(47, rnd.rand(o))
- assert_equal(64, rand(o))
- assert_equal(64, rnd.rand(o))
+ rnd2 = Random.new(0)
+ nums.each do |m, k, x|
+ assert_same_numbers(m.class, Random.rand(m), rnd.rand(m), rnd2.rand(m))
+ end
end
def test_srand
@@ -163,10 +126,13 @@ class TestRand < Test::Unit::TestCase
srand(2**100)
rnd = Random.new(2**100)
- %w(3258412053).each {|w|
- assert_equal(w.to_i, rand(0x100000000))
- assert_equal(w.to_i, rnd.rand(0x100000000))
- }
+ r = 3.times.map do
+ assert_same_numbers(Integer, rand(0x100000000), rnd.rand(0x100000000))
+ end
+ srand(2**100)
+ r.each do |n|
+ assert_same_numbers(Integer, n, rand(0x100000000))
+ end
end
def test_shuffle
@@ -177,17 +143,17 @@ class TestRand < Test::Unit::TestCase
end
def test_big_seed
- assert_random_int(%w(2757555016), 0x100000000, 2**1000000-1)
+ assert_random_int(0x100000000, 2**1000000-1)
end
def test_random_gc
r = Random.new(0)
- %w(2357136044 2546248239 3071714933).each do |w|
- assert_equal(w.to_i, r.rand(0x100000000))
+ 3.times do
+ assert_kind_of(Integer, r.rand(0x100000000))
end
GC.start
- %w(3626093760 2588848963 3684848379).each do |w|
- assert_equal(w.to_i, r.rand(0x100000000))
+ 3.times do
+ assert_kind_of(Integer, r.rand(0x100000000))
end
end
@@ -223,179 +189,56 @@ class TestRand < Test::Unit::TestCase
def test_random_dup
r1 = Random.new(0)
r2 = r1.dup
- %w(2357136044 2546248239 3071714933).each do |w|
- assert_equal(w.to_i, r1.rand(0x100000000))
- end
- %w(2357136044 2546248239 3071714933).each do |w|
- assert_equal(w.to_i, r2.rand(0x100000000))
+ 3.times do
+ assert_same_numbers(Integer, r1.rand(0x100000000), r2.rand(0x100000000))
end
r2 = r1.dup
- %w(3626093760 2588848963 3684848379).each do |w|
- assert_equal(w.to_i, r1.rand(0x100000000))
- end
- %w(3626093760 2588848963 3684848379).each do |w|
- assert_equal(w.to_i, r2.rand(0x100000000))
+ 3.times do
+ assert_same_numbers(Integer, r1.rand(0x100000000), r2.rand(0x100000000))
end
end
- def test_random_state
- state = <<END
-3877134065023083674777481835852171977222677629000095857864323111193832400974413
-4782302161934463784850675209112299537259006497924090422596764895633625964527441
-6943943249411681406395713106007661119327771293929504639878577616749110507385924
-0173026285378896836022134086386136835407107422834685854738117043791709411958489
-3504364936306163473541948635570644161010981140452515307286926529085424765299100
-1255453260115310687580777474046203049197643434654645011966794531914127596390825
-0832232869378617194193100828000236737535657699356156021286278281306055217995213
-8911536025132779573429499813926910299964681785069915877910855089314686097947757
-2621451199734871158015842198110309034467412292693435515184023707918034746119728
-8223459645048255809852819129671833854560563104716892857257229121211527031509280
-2390605053896565646658122125171846129817536096211475312518457776328637574563312
-8113489216547503743508184872149896518488714209752552442327273883060730945969461
-6568672445225657265545983966820639165285082194907591432296265618266901318398982
-0560425129536975583916120558652408261759226803976460322062347123360444839683204
-9868507788028894111577023917218846128348302845774997500569465902983227180328307
-3735301552935104196244116381766459468172162284042207680945316590536094294865648
-5953156978630954893391701383648157037914019502853776972615500142898763385846315
-8457790690531675205213829055442306187692107777193071680668153335688203945049935
-3404449910419303330872435985327845889440458370416464132629866593538629877042969
-7589948685901343135964343582727302330074331803900821801076139161904900333497836
-6627028345784450211920229170280462474456504317367706849373957309797251052447898
-8436235456995644876515032202483449807136139063937892187299239634252391529822163
-9187055268750679730919937006195967184206072757082920250075756273182004964087790
-3812024063897424219316687828337007888030994779068081666133751070479581394604974
-6022215489604777611774245181115126041390161592199230774968475944753915533936834
-4740049163514318351045644344358598159463605453475585370226041981040238023241538
-4958436364776113598408428801867643946791659645708540669432995503575075657406359
-8086928867900590554805639837071298576728564946552163206007997000988745940681607
-4542883814997403673656291618517107133421335645430345871041730410669209035640945
-5024601618318371192091626092482640364669969307766919645222516407626038616667754
-5781148898846306894862390724358039251444333889446128074209417936830253204064223
-3424784857908022314095011879203259864909560830176189727132432100010493659154644
-8407326292884826469503093409465946018496358514999175268200846200025235441426140
-7783386235191526371372655894290440356560751680752191224383460972099834655086068
-9989413443881686756951804138910911737670495391762470293978321414964443502180391
-4665982575919524372985773336921990352313629822677022891307943536442258282401255
-5387646898976193134193506239982621725093291970351083631367582570375381334759004
-1784150668048523676387894646666460369896619585113435743180899362844070393586212
-5023920017185866399742380706352739465848708746963693663004068892056705603018655
-8686663087894205699555906146534549176352859823832196938386172810274748624517052
-8356758650653040545267425513047130342286119889879774951060662713807133125543465
-5104086026298674827575216701372513525846650644773241437066782037334367982012148
-7987782004646896468089089826267467005660035604553432197455616078731159778086155
-9443250946037119223468305483694093795324036812927501783593256716590840500905291
-2096608538205270323065573109227029887731553399324547696295234105140157179430410
-4003109602564833086703863221058381556776789018351726488797845637981974580864082
-1630093543020854542240690897858757985640869209737744458407777584279553258261599
-0246922348101147034463235613998979344685018577901996218099622190722307356620796
-5137485271371502385527388080824050288371607602101805675021790116223360483508538
-8832149997794718410946818866375912486788005950091851067237358294899771385995876
-7088239104394332452501033090159333224995108984871426750597513314521294001864578
-2353528356752869732412552685554334966798888534847483030947310518891788722418172
-6008607577773612004956373863580996793809969715725508939568919714424871639667201
-7922255031431159347210833846575355772055570279673262115911154370983086189948124
-4653677615895887099814174914248255026619941911735341818489822197472499295786997
-7728418516719104857455960900092226749725407204388193002835497055305427730656889
-1508308778869166073740855838213112709306743479676740893150000714099064468263284
-1873435518542972182497755500300784177067568586395485329021157235696300013490087
-2866571034916258390528533374944905429089028336079264760836949419754851422499614
-5732326011260304142074554782259843903215064144396140106592193961703288125005023
-5334375212799817540775536847622032852415253966587517800661605905489339306359573
-2234947905196298436841723673626428243649931398749552780311877734063703985375067
-1239508613417041942487245370152912391885566432830659640677893488723724763120121
-4111855277511356759926232894062814360449757490961653026194107761340614059045172
-1123363102660719217740126157997033682099769790976313166682432732518101889210276
-9574144065390305904944821051736021310524344626348851573631697771556587859836330
-6997324121866564283654784470215100159122764509197570402997911258816526554863326
-9877535269005418736225944874608987238997316999444215865249840762640949599725696
-0773083894168959823152054508672272612355108904098579447774398451678239199426513
-3439507737424049578587487505080347686371029156845461151278198605267053408259090
-3158676794894709281917034995611352710898103415304769654883981727681820369090169
-9295163908214854813365413456264812190842699054830709079275249714169405719140093
-1347572458245530016346604698682269779841803667099480215265926316505737171177810
-9969036572310084022695109125200937135540995157279354438704321290061646592229860
-0156566013602344870223183295508278359111174872740360473845615437106413256386849
-2286259982118315248148847764929974917157683083659364623458927512616369119194574
-2254080
-END
- state = state.split.join.to_i
- r = Random.new(0)
+ def test_random_bytes
srand(0)
- assert_equal(state, r.instance_eval { state })
- assert_equal(state, Random.instance_eval { state })
- r.rand(0x100)
- assert_equal(state, r.instance_eval { state })
- end
-
- def test_random_left
r = Random.new(0)
- assert_equal(1, r.instance_eval { left })
- r.rand(0x100)
- assert_equal(624, r.instance_eval { left })
- r.rand(0x100)
- assert_equal(623, r.instance_eval { left })
- srand(0)
- assert_equal(1, Random.instance_eval { left })
- rand(0x100)
- assert_equal(624, Random.instance_eval { left })
- rand(0x100)
- assert_equal(623, Random.instance_eval { left })
- end
- def test_random_bytes
- assert_random_bytes(Random.new(0))
- end
-
- def assert_random_bytes(r)
- srand(0)
assert_equal("", r.bytes(0))
assert_equal("", Random.bytes(0))
- x = "\xAC".force_encoding("ASCII-8BIT")
- assert_equal(x, r.bytes(1))
+
+ x = r.bytes(1)
+ assert_equal(1, x.bytesize)
assert_equal(x, Random.bytes(1))
- x = "/\xAA\xC4\x97u\xA6\x16\xB7\xC0\xCC".force_encoding("ASCII-8BIT")
- assert_equal(x, r.bytes(10))
+
+ x = r.bytes(10)
+ assert_equal(10, x.bytesize)
assert_equal(x, Random.bytes(10))
end
def test_random_range
srand(0)
r = Random.new(0)
- %w(9 5 8).each {|w|
- assert_equal(w.to_i, rand(5..9))
- assert_equal(w.to_i, r.rand(5..9))
- }
- %w(-237 731 383).each {|w|
- assert_equal(w.to_i, rand(-1000..1000))
- assert_equal(w.to_i, r.rand(-1000..1000))
- }
- %w(1267650600228229401496703205382
- 1267650600228229401496703205384
- 1267650600228229401496703205383).each do |w|
- assert_equal(w.to_i, rand(2**100+5..2**100+9))
- assert_equal(w.to_i, r.rand(2**100+5..2**100+9))
- end
-
- v = rand(3.1..4)
- assert_instance_of(Float, v, '[ruby-core:24679]')
- assert_include(3.1..4, v)
-
- v = r.rand(3.1..4)
- assert_instance_of(Float, v, '[ruby-core:24679]')
- assert_include(3.1..4, v)
-
now = Time.now
- assert_equal(now, rand(now..now))
- assert_equal(now, r.rand(now..now))
+ [5..9, -1000..1000, 2**100+5..2**100+9, 3.1..4, now..(now+2)].each do |range|
+ 3.times do
+ x = rand(range)
+ assert_instance_of(range.first.class, x)
+ assert_equal(x, r.rand(range))
+ assert_include(range, x)
+ end
+ end
end
def test_random_float
r = Random.new(0)
- assert_in_delta(0.5488135039273248, r.rand, 0.0001)
- assert_in_delta(0.7151893663724195, r.rand, 0.0001)
- assert_in_delta(0.6027633760716439, r.rand, 0.0001)
- assert_in_delta(1.0897663659937937, r.rand(2.0), 0.0001)
- assert_in_delta(5.3704626067153264e+29, r.rand((2**100).to_f), 10**25)
+ 3.times do
+ assert_include(0...1.0, r.rand)
+ end
+ [2.0, (2**100).to_f].each do |x|
+ range = 0...x
+ 3.times do
+ assert_include(range, r.rand(x), "rand(#{x})")
+ end
+ end
assert_raise(Errno::EDOM, Errno::ERANGE) { r.rand(1.0 / 0.0) }
assert_raise(Errno::EDOM, Errno::ERANGE) { r.rand(0.0 / 0.0) }
@@ -403,10 +246,11 @@ END
assert_raise(Errno::EDOM) {r.rand(..1)}
r = Random.new(0)
- assert_in_delta(1.5488135039273248, r.rand(1.0...2.0), 0.0001, '[ruby-core:24655]')
- assert_in_delta(1.7151893663724195, r.rand(1.0...2.0), 0.0001, '[ruby-core:24655]')
- assert_in_delta(7.027633760716439, r.rand(1.0...11.0), 0.0001, '[ruby-core:24655]')
- assert_in_delta(3.0897663659937937, r.rand(2.0...4.0), 0.0001, '[ruby-core:24655]')
+ [1.0...2.0, 1.0...11.0, 2.0...4.0].each do |range|
+ 3.times do
+ assert_include(range, r.rand(range), "[ruby-core:24655] rand(#{range})")
+ end
+ end
assert_nothing_raised {r.rand(-Float::MAX..Float::MAX)}
end
@@ -492,18 +336,6 @@ END
}
end
- def test_default
- r1 = Random::DEFAULT.dup
- r2 = Random::DEFAULT.dup
- 3.times do
- x0 = rand
- x1 = r1.rand
- x2 = r2.rand
- assert_equal(x0, x1)
- assert_equal(x0, x2)
- end
- end
-
def test_marshal
bug3656 = '[ruby-core:31622]'
assert_raise(TypeError, bug3656) {
@@ -560,16 +392,19 @@ END
end
def test_default_seed
- assert_separately([], <<-End)
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ verbose, $VERBOSE = $VERBOSE, nil
seed = Random::DEFAULT::seed
rand1 = Random::DEFAULT::rand
+ $VERBOSE = verbose
rand2 = Random.new(seed).rand
assert_equal(rand1, rand2)
srand seed
rand3 = rand
assert_equal(rand1, rand3)
- End
+ end;
end
def test_urandom
diff --git a/test/ruby/test_random_formatter.rb b/test/ruby/test_random_formatter.rb
new file mode 100644
index 0000000000..a5072099e1
--- /dev/null
+++ b/test/ruby/test_random_formatter.rb
@@ -0,0 +1,123 @@
+require 'test/unit'
+require 'random/formatter'
+
+module Random::Formatter
+ module FormatterTest
+ def test_random_bytes
+ assert_equal(16, @it.random_bytes.size)
+ assert_equal(Encoding::ASCII_8BIT, @it.random_bytes.encoding)
+ 65.times do |idx|
+ assert_equal(idx, @it.random_bytes(idx).size)
+ end
+ end
+
+ def test_hex
+ s = @it.hex
+ assert_equal(16 * 2, s.size)
+ assert_match(/\A\h+\z/, s)
+ 33.times do |idx|
+ s = @it.hex(idx)
+ assert_equal(idx * 2, s.size)
+ assert_match(/\A\h*\z/, s)
+ end
+ end
+
+ def test_hex_encoding
+ assert_equal(Encoding::US_ASCII, @it.hex.encoding)
+ end
+
+ def test_base64
+ assert_equal(16, @it.base64.unpack1('m*').size)
+ 17.times do |idx|
+ assert_equal(idx, @it.base64(idx).unpack1('m*').size)
+ end
+ end
+
+ def test_urlsafe_base64
+ safe = /[\n+\/]/
+ 65.times do |idx|
+ assert_not_match(safe, @it.urlsafe_base64(idx))
+ end
+ # base64 can include unsafe byte
+ assert((0..10000).any? {|idx| safe =~ @it.base64(idx)}, "None of base64(0..10000) is url-safe")
+ end
+
+ def test_random_number_float
+ 101.times do
+ v = @it.random_number
+ assert_in_range(0.0...1.0, v)
+ end
+ end
+
+ def test_random_number_float_by_zero
+ 101.times do
+ v = @it.random_number(0)
+ assert_in_range(0.0...1.0, v)
+ end
+ end
+
+ def test_random_number_int
+ 101.times do |idx|
+ next if idx.zero?
+ v = @it.random_number(idx)
+ assert_in_range(0...idx, v)
+ end
+ end
+
+ def test_uuid
+ uuid = @it.uuid
+ assert_equal(36, uuid.size)
+
+ # Check time_hi_and_version and clock_seq_hi_res bits (RFC 4122 4.4)
+ assert_equal('4', uuid[14])
+ assert_include(%w'8 9 a b', uuid[19])
+
+ assert_match(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/, uuid)
+ end
+
+ def test_alphanumeric
+ 65.times do |n|
+ an = @it.alphanumeric(n)
+ assert_match(/\A[0-9a-zA-Z]*\z/, an)
+ assert_equal(n, an.length)
+ end
+ end
+
+ def assert_in_range(range, result, mesg = nil)
+ assert(range.cover?(result), build_message(mesg, "Expected #{result} to be in #{range}"))
+ end
+ end
+
+ module NotDefaultTest
+ def test_random_number_not_default
+ msg = "random_number should not be affected by srand"
+ seed = srand(0)
+ x = @it.random_number(1000)
+ 10.times do|i|
+ srand(0)
+ return unless @it.random_number(1000) == x
+ end
+ srand(0)
+ assert_not_equal(x, @it.random_number(1000), msg)
+ ensure
+ srand(seed) if seed
+ end
+ end
+
+ class TestClassMethods < Test::Unit::TestCase
+ include FormatterTest
+
+ def setup
+ @it = Random
+ end
+ end
+
+ class TestInstanceMethods < Test::Unit::TestCase
+ include FormatterTest
+ include NotDefaultTest
+
+ def setup
+ @it = Random.new
+ end
+ end
+end
diff --git a/test/ruby/test_range.rb b/test/ruby/test_range.rb
index 800cee92cc..dc591b0604 100644
--- a/test/ruby/test_range.rb
+++ b/test/ruby/test_range.rb
@@ -81,6 +81,8 @@ class TestRange < Test::Unit::TestCase
assert_equal(nil, (2..1).min)
assert_equal(1, (1...2).min)
assert_equal(1, (1..).min)
+ assert_raise(RangeError) { (..1).min }
+ assert_raise(RangeError) { (...1).min }
assert_equal(1.0, (1.0..2.0).min)
assert_equal(nil, (2.0..1.0).min)
@@ -93,6 +95,8 @@ class TestRange < Test::Unit::TestCase
assert_equal([0,1,2], (0..10).min(3))
assert_equal([0,1], (0..1).min(3))
assert_equal([0,1,2], (0..).min(3))
+ assert_raise(RangeError) { (..1).min(3) }
+ assert_raise(RangeError) { (...1).min(3) }
assert_raise(RangeError) { (0..).min {|a, b| a <=> b } }
end
@@ -119,6 +123,15 @@ class TestRange < Test::Unit::TestCase
assert_equal([9,8,7], (0...10).max(3))
assert_raise(RangeError) { (1..).max(3) }
assert_raise(RangeError) { (1...).max(3) }
+
+ assert_raise(RangeError) { (..0).min {|a, b| a <=> b } }
+
+ assert_equal(2, (..2).max)
+ assert_raise(TypeError) { (...2).max }
+ assert_raise(TypeError) { (...2.0).max }
+
+ assert_equal(Float::INFINITY, (1..Float::INFINITY).max)
+ assert_nil((1..-Float::INFINITY).max)
end
def test_minmax
@@ -140,12 +153,18 @@ class TestRange < Test::Unit::TestCase
assert_equal([nil, nil], (0...0).minmax)
assert_equal([2, 1], (1..2).minmax{|a, b| b <=> a})
+
+ assert_equal(['a', 'c'], ('a'..'c').minmax)
+ assert_equal(['a', 'b'], ('a'...'c').minmax)
+
+ assert_equal([1, Float::INFINITY], (1..Float::INFINITY).minmax)
+ assert_equal([nil, nil], (1..-Float::INFINITY).minmax)
end
def test_initialize_twice
r = eval("1..2")
- assert_raise(NameError) { r.instance_eval { initialize 3, 4 } }
- assert_raise(NameError) { r.instance_eval { initialize_copy 3..4 } }
+ assert_raise(FrozenError) { r.instance_eval { initialize 3, 4 } }
+ assert_raise(FrozenError) { r.instance_eval { initialize_copy 3..4 } }
end
def test_uninitialized_range
@@ -252,8 +271,10 @@ class TestRange < Test::Unit::TestCase
assert_kind_of(Enumerator::ArithmeticSequence, (1..).step(2))
assert_raise(ArgumentError) { (0..10).step(-1) { } }
+ assert_raise(ArgumentError) { (0..10).step(0) }
assert_raise(ArgumentError) { (0..10).step(0) { } }
assert_raise(ArgumentError) { (0..).step(-1) { } }
+ assert_raise(ArgumentError) { (0..).step(0) }
assert_raise(ArgumentError) { (0..).step(0) { } }
a = []
@@ -284,6 +305,7 @@ class TestRange < Test::Unit::TestCase
(2**32-1 .. 2**32+1).step(2) {|x| a << x }
assert_equal([4294967295, 4294967297], a)
zero = (2**32).coerce(0).first
+ assert_raise(ArgumentError) { (2**32-1 .. 2**32+1).step(zero) }
assert_raise(ArgumentError) { (2**32-1 .. 2**32+1).step(zero) { } }
a = []
(2**32-1 .. ).step(2) {|x| a << x; break if a.size == 2 }
@@ -644,6 +666,35 @@ class TestRange < Test::Unit::TestCase
assert_not_operator(1..10, :cover?, 3...3)
assert_not_operator('aa'..'zz', :cover?, 'aa'...'zzz')
assert_not_operator(1..10, :cover?, 1...10.1)
+
+ assert_operator(..2, :cover?, 1)
+ assert_operator(..2, :cover?, 2)
+ assert_not_operator(..2, :cover?, 3)
+ assert_not_operator(...2, :cover?, 2)
+ assert_not_operator(..2, :cover?, "2")
+ assert_operator(..2, :cover?, ..2)
+ assert_operator(..2, :cover?, ...2)
+ assert_not_operator(..2, :cover?, .."2")
+ assert_not_operator(...2, :cover?, ..2)
+
+ assert_not_operator(2.., :cover?, 1)
+ assert_operator(2.., :cover?, 2)
+ assert_operator(2..., :cover?, 3)
+ assert_operator(2.., :cover?, 2)
+ assert_not_operator(2.., :cover?, "2")
+ assert_operator(2.., :cover?, 2..)
+ assert_operator(2.., :cover?, 2...)
+ assert_not_operator(2.., :cover?, "2"..)
+ assert_not_operator(2..., :cover?, 2..)
+ assert_operator(2..., :cover?, 3...)
+ assert_not_operator(2..., :cover?, 3..)
+ assert_not_operator(3.., :cover?, 2..)
+
+ assert_operator(nil..., :cover?, Object.new)
+ assert_operator(nil..., :cover?, nil...)
+ assert_operator(nil.., :cover?, nil...)
+ assert_not_operator(nil..., :cover?, nil..)
+ assert_not_operator(nil..., :cover?, 1..)
end
def test_beg_len
diff --git a/test/ruby/test_rational.rb b/test/ruby/test_rational.rb
index 301890b620..fe9de64c4c 100644
--- a/test/ruby/test_rational.rb
+++ b/test/ruby/test_rational.rb
@@ -128,6 +128,13 @@ class Rational_Test < Test::Unit::TestCase
assert_raise(TypeError){Rational(Object.new, Object.new)}
assert_raise(TypeError){Rational(1, Object.new)}
+ bug12485 = '[ruby-core:75995] [Bug #12485]'
+ o = Object.new
+ def o.to_int; 1; end
+ assert_equal(1, Rational(o, 1), bug12485)
+ assert_equal(1, Rational(1, o), bug12485)
+ assert_equal(1, Rational(o, o), bug12485)
+
o = Object.new
def o.to_r; 1/42r; end
assert_equal(1/42r, Rational(o))
@@ -158,6 +165,14 @@ class Rational_Test < Test::Unit::TestCase
if (1.0/0).infinite?
assert_raise(FloatDomainError){Rational(1.0/0)}
end
+
+ bug16518 = "[ruby-core:96942] [Bug #16518]"
+ cls = Class.new(Numeric) do
+ def /(y); 42; end
+ def to_r; 1r; end
+ def to_int; 1; end
+ end
+ assert_equal(1/2r, Rational(cls.new, 2), bug16518)
end
def test_attr
@@ -598,6 +613,13 @@ class Rational_Test < Test::Unit::TestCase
assert_nothing_raised(TypeError, '[Bug #5020] [ruby-dev:44088]') do
Rational(1,2).coerce(Complex(1,1))
end
+
+ assert_raise(ZeroDivisionError) do
+ 1 / 0r.coerce(0+0i)[0]
+ end
+ assert_raise(ZeroDivisionError) do
+ 1 / 0r.coerce(0.0+0i)[0]
+ end
end
class ObjectX
@@ -834,6 +856,18 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(nil, Rational(1, Object.new, exception: false))
}
+ bug12485 = '[ruby-core:75995] [Bug #12485]'
+ assert_nothing_raised(RuntimeError, bug12485) {
+ o = Object.new
+ def o.to_int; raise; end
+ assert_equal(nil, Rational(o, exception: false))
+ }
+ assert_nothing_raised(RuntimeError, bug12485) {
+ o = Object.new
+ def o.to_int; raise; end
+ assert_equal(nil, Rational(1, o, exception: false))
+ }
+
o = Object.new;
def o.to_r; raise; end
assert_nothing_raised(RuntimeError) {
diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb
index 6fb04de5d6..19857b035c 100644
--- a/test/ruby/test_refinement.rb
+++ b/test/ruby/test_refinement.rb
@@ -225,6 +225,8 @@ class TestRefinement < Test::Unit::TestCase
end
end
def test_method_should_use_refinements
+ skip if Test::Unit::Runner.current_repeat_count > 0
+
foo = Foo.new
assert_raise(NameError) { foo.method(:z) }
assert_equal("FooExt#z", FooExtClient.method_z(foo).call)
@@ -246,6 +248,8 @@ class TestRefinement < Test::Unit::TestCase
end
end
def test_instance_method_should_use_refinements
+ skip if Test::Unit::Runner.current_repeat_count > 0
+
foo = Foo.new
assert_raise(NameError) { Foo.instance_method(:z) }
assert_equal("FooExt#z", FooExtClient.instance_method_z(foo).bind(foo).call)
@@ -743,6 +747,13 @@ class TestRefinement < Test::Unit::TestCase
end
end
+ def self.suppress_verbose
+ verbose, $VERBOSE = $VERBOSE, nil
+ yield
+ ensure
+ $VERBOSE = verbose
+ end
+
module IncludeIntoRefinement
class C
def bar
@@ -770,7 +781,9 @@ class TestRefinement < Test::Unit::TestCase
module M
refine C do
- include Mixin
+ TestRefinement.suppress_verbose do
+ include Mixin
+ end
def baz
return super << " M#baz"
@@ -833,7 +846,9 @@ class TestRefinement < Test::Unit::TestCase
module M
refine C do
- prepend Mixin
+ TestRefinement.suppress_verbose do
+ prepend Mixin
+ end
def baz
return super << " M#baz"
@@ -916,7 +931,7 @@ class TestRefinement < Test::Unit::TestCase
#{PrependAfterRefine_CODE}
undef PrependAfterRefine
}
- }, timeout: 30
+ }, timeout: 60
end
def test_prepend_after_refine
@@ -1648,7 +1663,6 @@ class TestRefinement < Test::Unit::TestCase
def test_reopen_refinement_module
assert_separately([], <<-"end;")
- $VERBOSE = nil
class C
end
@@ -1665,6 +1679,7 @@ class TestRefinement < Test::Unit::TestCase
module R
refine C do
+ alias m m
def m
:bar
end
@@ -2382,6 +2397,281 @@ class TestRefinement < Test::Unit::TestCase
assert_equal(0, Bug13446::GenericEnumerable.new.sum)
end
+ def test_unbound_refine_method
+ a = EnvUtil.labeled_class("A") do
+ def foo
+ self.class
+ end
+ end
+ b = EnvUtil.labeled_class("B")
+ bar = EnvUtil.labeled_module("R") do
+ break refine a do
+ def foo
+ super
+ end
+ end
+ end
+ assert_raise(TypeError) do
+ bar.instance_method(:foo).bind(b.new)
+ end
+ end
+
+ def test_refine_frozen_class
+ verbose_bak, $VERBOSE = $VERBOSE, nil
+ singleton_class.instance_variable_set(:@x, self)
+ class << self
+ c = Class.new do
+ def foo
+ :cfoo
+ end
+ end
+ foo = Module.new do
+ refine c do
+ def foo
+ :rfoo
+ end
+ end
+ end
+ using foo
+ @x.assert_equal(:rfoo, c.new.foo)
+ c.freeze
+ foo.module_eval do
+ refine c do
+ def foo
+ :rfoo2
+ end
+ def bar
+ :rbar
+ end
+ end
+ end
+ @x.assert_equal(:rfoo2, c.new.foo)
+ @x.assert_equal(:rbar, c.new.bar, '[ruby-core:71391] [Bug #11669]')
+ end
+ ensure
+ $VERBOSE = verbose_bak
+ end
+
+ # [Bug #17386]
+ def test_prepended_with_method_cache
+ foo = Class.new do
+ def foo
+ :Foo
+ end
+ end
+
+ code = Module.new do
+ def foo
+ :Code
+ end
+ end
+
+ _ext = Module.new do
+ refine foo do
+ def foo; end
+ end
+ end
+
+ obj = foo.new
+
+ assert_equal :Foo, obj.foo
+ foo.prepend code
+ assert_equal :Code, obj.foo
+ end
+
+ # [Bug #17417]
+ def test_prepended_with_method_cache_17417
+ assert_normal_exit %q{
+ module M
+ def hoge; end
+ end
+
+ module R
+ refine Hash do
+ def except *args; end
+ end
+ end
+
+ h = {}
+ h.method(:except) # put it on pCMC
+ Hash.prepend(M)
+ h.method(:except)
+ }
+ end
+
+ def test_defining_after_cached
+ klass = Class.new
+ _refinement = Module.new { refine(klass) { def foo; end } }
+ klass.new.foo rescue nil # cache the refinement method entry
+ klass.define_method(:foo) { 42 }
+ assert_equal(42, klass.new.foo)
+ end
+
+ # [Bug #17806]
+ def test_two_refinements_for_prepended_class
+ assert_normal_exit %q{
+ module R1
+ refine Hash do
+ def foo; :r1; end
+ end
+ end
+
+ class Hash
+ prepend(Module.new)
+ end
+
+ class Hash
+ def foo; end
+ end
+
+ {}.method(:foo) # put it on pCMC
+
+ module R2
+ refine Hash do
+ def foo; :r2; end
+ end
+ end
+
+ {}.foo
+ }
+ end
+
+ # [Bug #17806]
+ def test_redefining_refined_for_prepended_class
+ klass = Class.new { def foo; end }
+ _refinement = Module.new do
+ refine(klass) { def foo; :refined; end }
+ end
+ klass.prepend(Module.new)
+ klass.new.foo # cache foo
+ klass.define_method(:foo) { :second }
+ assert_equal(:second, klass.new.foo)
+ end
+
+ class Bug18180
+ module M
+ refine Array do
+ def min; :min; end
+ def max; :max; end
+ end
+ end
+
+ using M
+
+ def t
+ [[1+0, 2, 4].min, [1, 2, 4].min, [1+0, 2, 4].max, [1, 2, 4].max]
+ end
+ end
+
+ def test_refine_array_min_max
+ assert_equal([:min, :min, :max, :max], Bug18180.new.t)
+ end
+
+ class Bug17822
+ module Ext
+ refine(Bug17822) do
+ def foo = :refined
+ end
+ end
+
+ private(def foo = :not_refined)
+
+ module Client
+ using Ext
+ def self.call_foo
+ Bug17822.new.foo
+ end
+ end
+ end
+
+ # [Bug #17822]
+ def test_privatizing_refined_method
+ assert_equal(:refined, Bug17822::Client.call_foo)
+ end
+
+ def test_ancestors
+ refinement = nil
+ as = nil
+ Module.new do
+ refine Array do
+ refinement = self
+ as = ancestors
+ end
+ end
+ assert_equal([refinement], as, "[ruby-core:86949] [Bug #14744]")
+ end
+
+ module TestImport
+ class A
+ def foo
+ "original"
+ end
+ end
+
+ module B
+ BAR = "bar"
+
+ def bar
+ "#{foo}:#{BAR}"
+ end
+ end
+
+ module C
+ refine A do
+ import_methods B
+
+ def foo
+ "refined"
+ end
+ end
+ end
+
+ module D
+ refine A do
+ TestRefinement.suppress_verbose do
+ include B
+ end
+
+ def foo
+ "refined"
+ end
+ end
+ end
+
+ module UsingC
+ using C
+
+ def self.call_bar
+ A.new.bar
+ end
+ end
+
+ module UsingD
+ using D
+
+ def self.call_bar
+ A.new.bar
+ end
+ end
+ end
+
+ def test_import_methods
+ assert_equal("refined:bar", TestImport::UsingC.call_bar)
+ assert_equal("original:bar", TestImport::UsingD.call_bar)
+
+ assert_raise(ArgumentError) do
+ Module.new do
+ refine Integer do
+ import_methods Enumerable
+ end
+ end
+ end
+ end
+
+ def test_inherit_singleton_methods_of_module
+ assert_equal([], Refinement.used_modules)
+ end
+
private
def eval_using(mod, s)
diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb
index a1d49c595a..16a6ed921f 100644
--- a/test/ruby/test_regexp.rb
+++ b/test/ruby/test_regexp.rb
@@ -5,7 +5,6 @@ require 'test/unit'
class TestRegexp < Test::Unit::TestCase
def setup
@verbose = $VERBOSE
- $VERBOSE = nil
end
def teardown
@@ -43,13 +42,14 @@ class TestRegexp < Test::Unit::TestCase
def test_yoshidam_net_20041111_1
s = "[\xC2\xA0-\xC3\xBE]"
- assert_match(Regexp.new(s, nil, "u"), "\xC3\xBE")
+ r = assert_deprecated_warning(/ignored/) {Regexp.new(s, nil, "u")}
+ assert_match(r, "\xC3\xBE")
end
def test_yoshidam_net_20041111_2
assert_raise(RegexpError) do
s = "[\xFF-\xFF]".force_encoding("utf-8")
- Regexp.new(s, nil, "u")
+ assert_warning(/ignored/) {Regexp.new(s, nil, "u")}
end
end
@@ -57,6 +57,17 @@ class TestRegexp < Test::Unit::TestCase
assert_equal('Ruby', 'Ruby'.sub(/[^a-z]/i, '-'))
end
+ def test_premature_end_char_property
+ ["\\p{",
+ "\\p{".dup.force_encoding("UTF-8"),
+ "\\p{".dup.force_encoding("US-ASCII")
+ ].each do |string|
+ assert_raise(RegexpError) do
+ Regexp.new(string)
+ end
+ end
+ end
+
def test_assert_normal_exit
# moved from knownbug. It caused core.
Regexp.union("a", "a")
@@ -161,6 +172,10 @@ class TestRegexp < Test::Unit::TestCase
s = "foo"
s[/(?<bar>o)/, "bar"] = "baz"
assert_equal("fbazo", s)
+
+ /.*/ =~ "abc"
+ "a".sub("a", "")
+ assert_raise(IndexError) {Regexp.last_match(:_id)}
end
def test_named_capture_with_nul
@@ -214,6 +229,17 @@ class TestRegexp < Test::Unit::TestCase
def test_assign_named_capture_to_reserved_word
/(?<nil>.)/ =~ "a"
assert_not_include(local_variables, :nil, "[ruby-dev:32675]")
+
+ def (obj = Object.new).test(s, nil: :ng)
+ /(?<nil>.)/ =~ s
+ binding.local_variable_get(:nil)
+ end
+ assert_equal("b", obj.test("b"))
+
+ tap do |nil: :ng|
+ /(?<nil>.)/ =~ "c"
+ assert_equal("c", binding.local_variable_get(:nil))
+ end
end
def test_assign_named_capture_to_const
@@ -239,6 +265,27 @@ class TestRegexp < Test::Unit::TestCase
assert_equal(re, re.match("foo").regexp)
end
+ def test_match_lambda_multithread
+ bug17507 = "[ruby-core:101901]"
+ str = "a-x-foo-bar-baz-z-b"
+
+ worker = lambda do
+ m = /foo-([A-Za-z0-9_\.]+)-baz/.match(str)
+ assert_equal("bar", m[1], bug17507)
+
+ # These two lines are needed to trigger the bug
+ File.exist? "/tmp"
+ str.gsub(/foo-bar-baz/, "foo-abc-baz")
+ end
+
+ def self. threaded_test(worker)
+ 6.times.map {Thread.new {10_000.times {worker.call}}}.each(&:join)
+ end
+
+ # The bug only occurs in a method calling a block/proc/lambda
+ threaded_test(worker)
+ end
+
def test_source
bug5484 = '[ruby-core:40364]'
assert_equal('', //.source)
@@ -420,6 +467,7 @@ class TestRegexp < Test::Unit::TestCase
assert_nil(m[5])
assert_raise(IndexError) { m[:foo] }
assert_raise(TypeError) { m[nil] }
+ assert_equal(["baz", nil], m[-2, 3])
end
def test_match_values_at
@@ -447,6 +495,40 @@ class TestRegexp < Test::Unit::TestCase
assert_equal("foobarbaz", m.string)
end
+ def test_match_matchsubstring
+ m = /(.)(.)(\d+)(\d)(\w)?/.match("THX1138.")
+ assert_equal("HX1138", m.match(0))
+ assert_equal("8", m.match(4))
+ assert_nil(m.match(5))
+
+ m = /\A\u3042(.)(.)?(.)\z/.match("\u3042\u3043\u3044")
+ assert_equal("\u3043", m.match(1))
+ assert_nil(m.match(2))
+ assert_equal("\u3044", m.match(3))
+
+ m = /(?<foo>.)(?<n>[^aeiou])?(?<bar>.+)/.match("hoge\u3042")
+ assert_equal("h", m.match(:foo))
+ assert_nil(m.match(:n))
+ assert_equal("oge\u3042", m.match(:bar))
+ end
+
+ def test_match_match_length
+ m = /(.)(.)(\d+)(\d)(\w)?/.match("THX1138.")
+ assert_equal(6, m.match_length(0))
+ assert_equal(1, m.match_length(4))
+ assert_nil(m.match_length(5))
+
+ m = /\A\u3042(.)(.)?(.)\z/.match("\u3042\u3043\u3044")
+ assert_equal(1, m.match_length(1))
+ assert_nil(m.match_length(2))
+ assert_equal(1, m.match_length(3))
+
+ m = /(?<foo>.)(?<n>[^aeiou])?(?<bar>.+)/.match("hoge\u3042")
+ assert_equal(1, m.match_length(:foo))
+ assert_nil(m.match_length(:n))
+ assert_equal(4, m.match_length(:bar))
+ end
+
def test_match_inspect
m = /(...)(...)(...)(...)?/.match("foobarbaz")
assert_equal('#<MatchData "foobarbaz" 1:"foo" 2:"bar" 3:"baz" 4:nil>', m.inspect)
@@ -454,7 +536,7 @@ class TestRegexp < Test::Unit::TestCase
def test_initialize
assert_raise(ArgumentError) { Regexp.new }
- assert_equal(/foo/, Regexp.new(/foo/, Regexp::IGNORECASE))
+ assert_equal(/foo/, assert_warning(/ignored/) {Regexp.new(/foo/, Regexp::IGNORECASE)})
assert_equal(Encoding.find("US-ASCII"), Regexp.new("b..", nil, "n").encoding)
assert_equal("bar", "foobarbaz"[Regexp.new("b..", nil, "n")])
@@ -470,6 +552,24 @@ class TestRegexp < Test::Unit::TestCase
assert_raise(RegexpError) { Regexp.new("((?<v>))\\g<0>") }
end
+ def test_match_control_meta_escape
+ assert_equal(0, /\c\xFF/ =~ "\c\xFF")
+ assert_equal(0, /\c\M-\xFF/ =~ "\c\M-\xFF")
+ assert_equal(0, /\C-\xFF/ =~ "\C-\xFF")
+ assert_equal(0, /\C-\M-\xFF/ =~ "\C-\M-\xFF")
+ assert_equal(0, /\M-\xFF/ =~ "\M-\xFF")
+ assert_equal(0, /\M-\C-\xFF/ =~ "\M-\C-\xFF")
+ assert_equal(0, /\M-\c\xFF/ =~ "\M-\c\xFF")
+
+ assert_nil(/\c\xFE/ =~ "\c\xFF")
+ assert_nil(/\c\M-\xFE/ =~ "\c\M-\xFF")
+ assert_nil(/\C-\xFE/ =~ "\C-\xFF")
+ assert_nil(/\C-\M-\xFE/ =~ "\C-\M-\xFF")
+ assert_nil(/\M-\xFE/ =~ "\M-\xFF")
+ assert_nil(/\M-\C-\xFE/ =~ "\M-\C-\xFF")
+ assert_nil(/\M-\c\xFE/ =~ "\M-\c\xFF")
+ end
+
def test_unescape
assert_raise(ArgumentError) { s = '\\'; /#{ s }/ }
assert_equal(/\xFF/n, /#{ s="\\xFF" }/n)
@@ -546,7 +646,10 @@ class TestRegexp < Test::Unit::TestCase
assert_equal("bc", /../.match('abc', -2)[0])
assert_nil(/../.match("abc", -4))
assert_nil(/../.match("abc", 4))
- assert_equal('\x', /../n.match("\u3042" + '\x', 1)[0])
+
+ # use eval because only one warning is shown for the same regexp literal
+ pat = eval('/../n')
+ assert_equal('\x', assert_warning(/binary regexp/) {pat.match("\u3042" + '\x', 1)}[0])
r = nil
/.../.match("abc") {|m| r = m[0] }
@@ -622,7 +725,7 @@ class TestRegexp < Test::Unit::TestCase
def test_dup
assert_equal(//, //.dup)
- assert_raise(TypeError) { //.instance_eval { initialize_copy(nil) } }
+ assert_raise(TypeError) { //.dup.instance_eval { initialize_copy(nil) } }
end
def test_regsub
@@ -646,21 +749,10 @@ class TestRegexp < Test::Unit::TestCase
assert_equal('foobazquux/foobazquux', result, bug8856)
end
- def test_KCODE
- assert_nil($KCODE)
- assert_nothing_raised { $KCODE = nil }
- assert_equal(false, $=)
- assert_nothing_raised { $= = nil }
- end
-
- def test_KCODE_warning
- assert_warning(/variable \$KCODE is no longer effective; ignored/) { $KCODE = nil }
- assert_warning(/variable \$KCODE is no longer effective/) { $KCODE = nil }
- end
-
- def test_ignorecase_warning
- assert_warning(/variable \$= is no longer effective; ignored/) { $= = nil }
- assert_warning(/variable \$= is no longer effective/) { $= }
+ def test_ignorecase
+ v = assert_deprecated_warning(/variable \$= is no longer effective/) { $= }
+ assert_equal(false, v)
+ assert_deprecated_warning(/variable \$= is no longer effective; ignored/) { $= = nil }
end
def test_match_setter
@@ -676,11 +768,16 @@ class TestRegexp < Test::Unit::TestCase
test = proc {|&blk| "abc".sub("a", ""); blk.call($~) }
bug10877 = '[ruby-core:68209] [Bug #10877]'
+ bug18160 = '[Bug #18160]'
test.call {|m| assert_raise_with_message(IndexError, /foo/, bug10877) {m["foo"]} }
key = "\u{3042}"
[Encoding::UTF_8, Encoding::Shift_JIS, Encoding::EUC_JP].each do |enc|
idx = key.encode(enc)
- test.call {|m| assert_raise_with_message(IndexError, /#{idx}/, bug10877) {m[idx]} }
+ pat = /#{idx}/
+ test.call {|m| assert_raise_with_message(IndexError, pat, bug10877) {m[idx]} }
+ test.call {|m| assert_raise_with_message(IndexError, pat, bug18160) {m.offset(idx)} }
+ test.call {|m| assert_raise_with_message(IndexError, pat, bug18160) {m.begin(idx)} }
+ test.call {|m| assert_raise_with_message(IndexError, pat, bug18160) {m.end(idx)} }
end
test.call {|m| assert_equal(/a/, m.regexp) }
test.call {|m| assert_equal("abc", m.string) }
@@ -722,11 +819,13 @@ class TestRegexp < Test::Unit::TestCase
end
def test_rindex_regexp
- assert_equal(3, "foobarbaz\u3042".rindex(/b../n, 5))
+ # use eval because only one warning is shown for the same regexp literal
+ pat = eval('/b../n')
+ assert_equal(3, assert_warning(/binary regexp/) {"foobarbaz\u3042".rindex(pat, 5)})
end
def assert_regexp(re, ss, fs = [], msg = nil)
- re = Regexp.new(re) unless re.is_a?(Regexp)
+ re = EnvUtil.suppress_warning {Regexp.new(re)} unless re.is_a?(Regexp)
ss = [ss] unless ss.is_a?(Array)
ss.each do |e, s|
s ||= e
@@ -759,7 +858,7 @@ class TestRegexp < Test::Unit::TestCase
check(/\A\80\z/, "80", ["\100", ""])
check(/\A\77\z/, "?")
check(/\A\78\z/, "\7" + '8', ["\100", ""])
- check(eval('/\A\Qfoo\E\z/'), "QfooE")
+ check(assert_warning(/Unknown escape/) {eval('/\A\Qfoo\E\z/')}, "QfooE")
check(/\Aa++\z/, "aaa")
check('\Ax]\z', "x]")
check(/x#foo/x, "x", "#foo")
@@ -803,8 +902,8 @@ class TestRegexp < Test::Unit::TestCase
check(/^(A+|B(?>\g<1>)*)[AC]$/, %w(AAAC BBBAAAAC), %w(BBBAAA))
check(/^()(?>\g<1>)*$/, "", "a")
check(/^(?>(?=a)(#{ "a" * 1000 }|))++$/, ["a" * 1000, "a" * 2000, "a" * 3000], ["", "a" * 500, "b" * 1000])
- check(eval('/^(?:a?)?$/'), ["", "a"], ["aa"])
- check(eval('/^(?:a+)?$/'), ["", "a", "aa"], ["ab"])
+ check(assert_warning(/nested repeat operator/) {eval('/^(?:a?)?$/')}, ["", "a"], ["aa"])
+ check(assert_warning(/nested repeat operator/) {eval('/^(?:a+)?$/')}, ["", "a", "aa"], ["ab"])
check(/^(?:a?)+?$/, ["", "a", "aa"], ["ab"])
check(/^a??[ab]/, [["a", "a"], ["a", "aa"], ["b", "b"], ["a", "ab"]], ["c"])
check(/^(?:a*){3,5}$/, ["", "a", "aa", "aaa", "aaaa", "aaaaa", "aaaaaa"], ["b"])
@@ -933,13 +1032,13 @@ class TestRegexp < Test::Unit::TestCase
def test_posix_bracket
check(/\A[[:alpha:]0]\z/, %w(0 a), %w(1 .))
- check(eval('/\A[[:^alpha:]0]\z/'), %w(0 1 .), "a")
- check(eval('/\A[[:alpha\:]]\z/'), %w(a l p h a :), %w(b 0 1 .))
- check(eval('/\A[[:alpha:foo]0]\z/'), %w(0 a), %w(1 .))
+ check(assert_warning(/duplicated range/) {eval('/\A[[:^alpha:]0]\z/')}, %w(0 1 .), "a")
+ check(assert_warning(/duplicated range/) {eval('/\A[[:alpha\:]]\z/')}, %w(a l p h a :), %w(b 0 1 .))
+ check(assert_warning(/duplicated range/) {eval('/\A[[:alpha:foo]0]\z/')}, %w(0 a), %w(1 .))
check(/\A[[:xdigit:]&&[:alpha:]]\z/, "a", %w(g 0))
check('\A[[:abcdefghijklmnopqrstu:]]+\z', "[]")
failcheck('[[:alpha')
- failcheck('[[:alpha:')
+ assert_warning(/duplicated range/) {failcheck('[[:alpha:')}
failcheck('[[:alp:]]')
assert_match(/\A[[:digit:]]+\z/, "\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19")
@@ -1106,13 +1205,17 @@ class TestRegexp < Test::Unit::TestCase
end
def test_regexp_popped
- assert_nothing_raised { eval("a = 1; /\#{ a }/; a") }
- assert_nothing_raised { eval("a = 1; /\#{ a }/o; a") }
+ EnvUtil.suppress_warning do
+ assert_nothing_raised { eval("a = 1; /\#{ a }/; a") }
+ assert_nothing_raised { eval("a = 1; /\#{ a }/o; a") }
+ end
end
def test_invalid_fragment
bug2547 = '[ruby-core:27374]'
- assert_raise(SyntaxError, bug2547) {eval('/#{"\\\\"}y/')}
+ assert_raise(SyntaxError, bug2547) do
+ assert_warning(/ignored/) {eval('/#{"\\\\"}y/')}
+ end
end
def test_dup_warn
@@ -1152,7 +1255,8 @@ class TestRegexp < Test::Unit::TestCase
def test_raw_hyphen_and_tk_char_type_after_range
bug6853 = '[ruby-core:47115]'
# use Regexp.new instead of literal to ignore a parser warning.
- check(Regexp.new('[0-1-\\s]'), [' ', '-'], ['2', 'a'], bug6853)
+ re = assert_warning(/without escape/) {Regexp.new('[0-1-\\s]')}
+ check(re, [' ', '-'], ['2', 'a'], bug6853)
end
def test_error_message_on_failed_conversion
@@ -1188,6 +1292,20 @@ class TestRegexp < Test::Unit::TestCase
}
end
+ def test_quantifier_reduction
+ assert_equal('aa', eval('/(a+?)*/').match('aa')[0])
+ assert_equal('aa', eval('/(?:a+?)*/').match('aa')[0])
+
+ quantifiers = %w'? * + ?? *? +?'
+ quantifiers.product(quantifiers) do |q1, q2|
+ EnvUtil.suppress_warning do
+ r1 = eval("/(a#{q1})#{q2}/").match('aa')[0]
+ r2 = eval("/(?:a#{q1})#{q2}/").match('aa')[0]
+ assert_equal(r1, r2)
+ end
+ end
+ end
+
def test_once
pr1 = proc{|i| /#{i}/o}
assert_equal(/0/, pr1.call(0))
@@ -1273,6 +1391,27 @@ class TestRegexp < Test::Unit::TestCase
assert_nil($1)
end
+ def test_backref_overrun
+ assert_raise_with_message(SyntaxError, /invalid backref number/) do
+ eval(%["".match(/(())(?<X>)((?(90000)))/)])
+ end
+ end
+
+ def test_invalid_group
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ assert_raise_with_message(RegexpError, /invalid conditional pattern/) do
+ Regexp.new("((?(1)x|x|)x)+")
+ end
+ end;
+ end
+
+ def test_bug18631
+ assert_kind_of MatchData, /(?<x>a)(?<x>aa)\k<x>/.match("aaaaa")
+ assert_kind_of MatchData, /(?<x>a)(?<x>aa)\k<x>/.match("aaaa")
+ assert_kind_of MatchData, /(?<x>a)(?<x>aa)\k<x>/.match("aaaab")
+ end
+
# This assertion is for porting x2() tests in testpy.py of Onigmo.
def assert_match_at(re, str, positions, msg = nil)
re = Regexp.new(re) unless re.is_a?(Regexp)
diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb
index 05dc18cd17..a71fe0932e 100644
--- a/test/ruby/test_require.rb
+++ b/test/ruby/test_require.rb
@@ -6,11 +6,27 @@ require 'tmpdir'
class TestRequire < Test::Unit::TestCase
def test_load_error_path
- filename = "should_not_exist"
- error = assert_raise(LoadError) do
- require filename
- end
- assert_equal filename, error.path
+ Tempfile.create(["should_not_exist", ".rb"]) {|t|
+ filename = t.path
+ t.close
+ File.unlink(filename)
+
+ error = assert_raise(LoadError) do
+ require filename
+ end
+ assert_equal filename, error.path
+
+ # with --disable=gems
+ assert_separately(["-", filename], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ filename = ARGV[0]
+ path = Struct.new(:to_path).new(filename)
+ error = assert_raise(LoadError) do
+ require path
+ end
+ assert_equal filename, error.path
+ end;
+ }
end
def test_require_invalid_shared_object
@@ -52,7 +68,8 @@ class TestRequire < Test::Unit::TestCase
def test_require_nonascii
bug3758 = '[ruby-core:31915]'
["\u{221e}", "\x82\xa0".force_encoding("cp932")].each do |path|
- assert_raise_with_message(LoadError, /#{path}\z/, bug3758) {require path}
+ e = assert_raise(LoadError, bug3758) {require path}
+ assert_operator(e.message, :end_with?, path, bug3758)
end
end
@@ -88,6 +105,7 @@ class TestRequire < Test::Unit::TestCase
end
def prepare_require_path(dir, encoding)
+ require 'enc/trans/single_byte'
Dir.mktmpdir {|tmp|
begin
require_path = File.join(tmp, dir, 'foo.rb').encode(encoding)
@@ -199,6 +217,7 @@ class TestRequire < Test::Unit::TestCase
end
def assert_syntax_error_backtrace
+ loaded_features = $LOADED_FEATURES.dup
Dir.mktmpdir do |tmp|
req = File.join(tmp, "test.rb")
File.write(req, ",\n")
@@ -208,12 +227,20 @@ class TestRequire < Test::Unit::TestCase
assert_not_nil(bt = e.backtrace, "no backtrace")
assert_not_empty(bt.find_all {|b| b.start_with? __FILE__}, proc {bt.inspect})
end
+ $LOADED_FEATURES.replace loaded_features
end
def test_require_syntax_error
assert_syntax_error_backtrace {|req| require req}
end
+ def test_require_syntax_error_rescued
+ assert_syntax_error_backtrace do |req|
+ assert_raise_with_message(SyntaxError, /unexpected/) {require req}
+ require req
+ end
+ end
+
def test_load_syntax_error
assert_syntax_error_backtrace {|req| load req}
end
@@ -357,19 +384,51 @@ class TestRequire < Test::Unit::TestCase
}
end
+ def test_load_into_module
+ Tempfile.create(["test_ruby_test_require", ".rb"]) {|t|
+ t.puts "def b; 1 end"
+ t.puts "class Foo"
+ t.puts " def c; 2 end"
+ t.puts "end"
+ t.close
+
+ m = Module.new
+ load(t.path, m)
+ assert_equal([:b], m.private_instance_methods(false))
+ c = Class.new do
+ include m
+ public :b
+ end
+ assert_equal(1, c.new.b)
+ assert_equal(2, m::Foo.new.c)
+ }
+ end
+
+ def test_load_wrap_nil
+ Dir.mktmpdir do |tmp|
+ File.write("#{tmp}/1.rb", "class LoadWrapNil; end\n")
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ path = ""#{tmp.dump}"/1.rb"
+ begin;
+ load path, nil
+ assert_instance_of(Class, LoadWrapNil)
+ end;
+ end
+ end
+
def test_load_ospath
bug = '[ruby-list:49994] path in ospath'
base = "test_load\u{3042 3044 3046 3048 304a}".encode(Encoding::Windows_31J)
path = nil
- Tempfile.create([base, ".rb"]) do |t|
- path = t.path
-
+ Dir.mktmpdir do |dir|
+ path = File.join(dir, base+".rb")
assert_raise_with_message(LoadError, /#{base}/) {
- load(File.join(File.dirname(path), base))
+ load(File.join(dir, base))
}
- t.puts "warn 'ok'"
- t.close
+ File.open(path, "w+b") do |t|
+ t.puts "warn 'ok'"
+ end
assert_include(path, base)
assert_warn("ok\n", bug) {
assert_nothing_raised(LoadError, bug) {
@@ -381,6 +440,8 @@ class TestRequire < Test::Unit::TestCase
def test_relative
load_path = $:.dup
+ loaded_featrures = $LOADED_FEATURES.dup
+
$:.delete(".")
Dir.mktmpdir do |tmp|
Dir.chdir(tmp) do
@@ -400,6 +461,7 @@ class TestRequire < Test::Unit::TestCase
end
ensure
$:.replace(load_path) if load_path
+ $LOADED_FEATURES.replace loaded_featrures
end
def test_relative_symlink
@@ -421,6 +483,32 @@ class TestRequire < Test::Unit::TestCase
}
end
+ def test_relative_symlink_realpath
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ Dir.mkdir "a"
+ File.open("a/a.rb", "w") {|f| f.puts 'require_relative "b"' }
+ File.open("a/b.rb", "w") {|f| f.puts '$t += 1' }
+ Dir.mkdir "b"
+ File.binwrite("c.rb", <<~RUBY)
+ $t = 0
+ $:.unshift(File.expand_path('../b', __FILE__))
+ require "b"
+ require "a"
+ print $t
+ RUBY
+ begin
+ File.symlink("../a/a.rb", "b/a.rb")
+ File.symlink("../a/b.rb", "b/b.rb")
+ result = IO.popen([EnvUtil.rubybin, "c.rb"], &:read)
+ assert_equal("1", result, "bug17885 [ruby-core:104010]")
+ rescue NotImplementedError, Errno::EACCES
+ skip "File.symlink is not implemented"
+ end
+ }
+ }
+ end
+
def test_frozen_loaded_features
bug3756 = '[ruby-core:31913]'
assert_in_out_err(['-e', '$LOADED_FEATURES.freeze; require "ostruct"'], "",
@@ -491,9 +579,6 @@ class TestRequire < Test::Unit::TestCase
assert_equal(true, (t1_res ^ t2_res), bug5754 + " t1:#{t1_res} t2:#{t2_res}")
assert_equal([:pre, :post], scratch, bug5754)
-
- assert_match(/circular require/, output)
- assert_match(/in #{__method__}'$/o, output)
}
ensure
$VERBOSE = verbose
@@ -518,6 +603,28 @@ class TestRequire < Test::Unit::TestCase
$".replace(features)
end
+ def test_default_loaded_features_encoding
+ Dir.mktmpdir {|tmp|
+ Dir.mkdir("#{tmp}/1")
+ Dir.mkdir("#{tmp}/2")
+ File.write("#{tmp}/1/bug18191-1.rb", "")
+ File.write("#{tmp}/2/bug18191-2.rb", "")
+ assert_separately(%W[-Eutf-8 -I#{tmp}/1 -], "#{<<~"begin;"}\n#{<<~'end;'}")
+ tmp = #{tmp.dump}"/2"
+ begin;
+ $:.unshift(tmp)
+ require "bug18191-1"
+ require "bug18191-2"
+ encs = [Encoding::US_ASCII, Encoding.find("filesystem")]
+ message = -> {
+ require "pp"
+ {filesystem: encs[1], **$".group_by(&:encoding)}.pretty_inspect
+ }
+ assert($".all? {|n| encs.include?(n.encoding)}, message)
+ end;
+ }
+ end
+
def test_require_changed_current_dir
bug7158 = '[ruby-core:47970]'
Dir.mktmpdir {|tmp|
@@ -698,8 +805,8 @@ class TestRequire < Test::Unit::TestCase
assert_in_out_err([{"RUBYOPT" => nil}, "-", script.path], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7530, timeout: 60)
begin;
PATH = ARGV.shift
- THREADS = 4
- ITERATIONS_PER_THREAD = 1000
+ THREADS = 30
+ ITERATIONS_PER_THREAD = 300
THREADS.times.map {
Thread.new do
@@ -757,6 +864,8 @@ class TestRequire < Test::Unit::TestCase
end if File.respond_to?(:mkfifo)
def test_loading_fifo_fd_leak
+ skip if RUBY_PLATFORM =~ /android/ # https://rubyci.org/logs/rubyci.s3.amazonaws.com/android29-x86_64/ruby-master/log/20200419T124100Z.fail.html.gz
+
Tempfile.create(%w'fifo .rb') {|f|
f.close
File.unlink(f.path)
@@ -824,6 +933,23 @@ class TestRequire < Test::Unit::TestCase
}
end
+ def test_provide_in_required_file
+ paths, loaded = $:.dup, $".dup
+ Dir.mktmpdir do |tmp|
+ provide = File.realdirpath("provide.rb", tmp)
+ File.write(File.join(tmp, "target.rb"), "raise __FILE__\n")
+ File.write(provide, '$" << '"'target.rb'\n")
+ $:.replace([tmp])
+ assert(require("provide"))
+ assert(!require("target"))
+ assert_equal($".pop, provide)
+ assert_equal($".pop, "target.rb")
+ end
+ ensure
+ $:.replace(paths)
+ $".replace(loaded)
+ end
+
if defined?($LOAD_PATH.resolve_feature_path)
def test_resolve_feature_path
paths, loaded = $:.dup, $".dup
@@ -842,5 +968,9 @@ class TestRequire < Test::Unit::TestCase
$:.replace(paths)
$".replace(loaded)
end
+
+ def test_resolve_feature_path_with_missing_feature
+ assert_nil($LOAD_PATH.resolve_feature_path("superkalifragilisticoespialidoso"))
+ end
end
end
diff --git a/test/ruby/test_require_lib.rb b/test/ruby/test_require_lib.rb
index 4af57173b8..6b2846c8fd 100644
--- a/test/ruby/test_require_lib.rb
+++ b/test/ruby/test_require_lib.rb
@@ -8,8 +8,7 @@ class TestRequireLib < Test::Unit::TestCase
# skip some problems
next if %r!/lib/(?:bundler|rubygems)\b! =~ lib
next if %r!/lib/(?:debug|mkmf)\.rb\z! =~ lib
- # skip because "in `<module:Maker>': undefined method `add_maker' for RSS::Maker:Module (NoMethodError)"
- next if %r!/lib/rss\b! =~ lib
+ next if %r!/lib/irb/ext/tracer\.rb\z! =~ lib
# skip many files that almost use no threads
next if TEST_RATIO < rand(0.0..1.0)
define_method "test_thread_size:#{lib}" do
diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb
index 496a51b970..aae2522fc6 100644
--- a/test/ruby/test_rubyoptions.rb
+++ b/test/ruby/test_rubyoptions.rb
@@ -7,9 +7,13 @@ require 'tempfile'
require_relative '../lib/jit_support'
class TestRubyOptions < Test::Unit::TestCase
+ def self.yjit_enabled? = defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled?
+
NO_JIT_DESCRIPTION =
- if RubyVM::MJIT.enabled? # checking -DMJIT_FORCE_ENABLE
- RUBY_DESCRIPTION.sub(/\+JIT /, '')
+ if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # checking -DMJIT_FORCE_ENABLE
+ RUBY_DESCRIPTION.sub(/\+MJIT /, '')
+ elsif yjit_enabled? # checking -DYJIT_FORCE_ENABLE
+ RUBY_DESCRIPTION.sub(/\+YJIT /, '')
else
RUBY_DESCRIPTION
end
@@ -66,15 +70,48 @@ class TestRubyOptions < Test::Unit::TestCase
end
end
+ def test_backtrace_limit
+ assert_in_out_err(%w(--backtrace-limit), "", [], /missing argument for --backtrace-limit/)
+ assert_in_out_err(%w(--backtrace-limit= 1), "", [], /missing argument for --backtrace-limit/)
+ assert_in_out_err(%w(--backtrace-limit=-1), "", [], /wrong limit for backtrace length/)
+ code = 'def f(n);n > 0 ? f(n-1) : raise;end;f(5)'
+ assert_in_out_err(%w(--backtrace-limit=1), code, [],
+ [/.*unhandled exception\n/, /^\tfrom .*\n/,
+ /^\t \.{3} \d+ levels\.{3}\n/])
+ assert_in_out_err(%w(--backtrace-limit=3), code, [],
+ [/.*unhandled exception\n/, *[/^\tfrom .*\n/]*3,
+ /^\t \.{3} \d+ levels\.{3}\n/])
+ assert_kind_of(Integer, Thread::Backtrace.limit)
+ assert_in_out_err(%w(--backtrace-limit=1), "p Thread::Backtrace.limit", ['1'], [])
+ end
def test_warning
save_rubyopt = ENV['RUBYOPT']
ENV['RUBYOPT'] = nil
assert_in_out_err(%w(-W0 -e) + ['p $-W'], "", %w(0), [])
assert_in_out_err(%w(-W1 -e) + ['p $-W'], "", %w(1), [])
- assert_in_out_err(%w(-Wx -e) + ['p $-W'], "", %w(1), [])
+ assert_in_out_err(%w(-Wx -e) + ['p $-W'], "", %w(2), [])
assert_in_out_err(%w(-W -e) + ['p $-W'], "", %w(2), [])
+ assert_in_out_err(%w(-We) + ['p $-W'], "", %w(2), [])
assert_in_out_err(%w(-w -W0 -e) + ['p $-W'], "", %w(0), [])
+ assert_in_out_err(%w(-W:deprecated -e) + ['p Warning[:deprecated]'], "", %w(true), [])
+ assert_in_out_err(%w(-W:no-deprecated -e) + ['p Warning[:deprecated]'], "", %w(false), [])
+ assert_in_out_err(%w(-W:experimental -e) + ['p Warning[:experimental]'], "", %w(true), [])
+ assert_in_out_err(%w(-W:no-experimental -e) + ['p Warning[:experimental]'], "", %w(false), [])
+ assert_in_out_err(%w(-W:qux), "", [], /unknown warning category: `qux'/)
+ assert_in_out_err(%w(-w -e) + ['p Warning[:deprecated]'], "", %w(true), [])
+ assert_in_out_err(%w(-W -e) + ['p Warning[:deprecated]'], "", %w(true), [])
+ assert_in_out_err(%w(-We) + ['p Warning[:deprecated]'], "", %w(true), [])
+ assert_in_out_err(%w(-e) + ['p Warning[:deprecated]'], "", %w(false), [])
+ code = 'puts "#{$VERBOSE}:#{Warning[:deprecated]}:#{Warning[:experimental]}"'
+ Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) do |t|
+ t.puts code
+ t.close
+ assert_in_out_err(["-r#{t.path}", '-e', code], "", %w(false:false:true false:false:true), [])
+ assert_in_out_err(["-r#{t.path}", '-w', '-e', code], "", %w(true:true:true true:true:true), [])
+ assert_in_out_err(["-r#{t.path}", '-W:deprecated', '-e', code], "", %w(false:true:true false:true:true), [])
+ assert_in_out_err(["-r#{t.path}", '-W:no-experimental', '-e', code], "", %w(false:false:false false:false:false), [])
+ end
ensure
ENV['RUBYOPT'] = save_rubyopt
end
@@ -100,16 +137,18 @@ class TestRubyOptions < Test::Unit::TestCase
VERSION_PATTERN_WITH_JIT =
case RUBY_ENGINE
when 'ruby'
- /^ruby #{q[RUBY_VERSION]}(?:[p ]|dev|rc).*? \+JIT \[#{q[RUBY_PLATFORM]}\]$/
+ /^ruby #{q[RUBY_VERSION]}(?:[p ]|dev|rc).*? \+MJIT \[#{q[RUBY_PLATFORM]}\]$/
else
VERSION_PATTERN
end
private_constant :VERSION_PATTERN_WITH_JIT
def test_verbose
- assert_in_out_err(["-vve", ""]) do |r, e|
+ assert_in_out_err([{'RUBY_YJIT_ENABLE' => nil}, "-vve", ""]) do |r, e|
assert_match(VERSION_PATTERN, r[0])
- if RubyVM::MJIT.enabled? && !mjit_force_enabled? # checking -DMJIT_FORCE_ENABLE
+ if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? && !mjit_force_enabled? # checking -DMJIT_FORCE_ENABLE
+ assert_equal(NO_JIT_DESCRIPTION, r[0])
+ elsif self.class.yjit_enabled? && !yjit_force_enabled? # checking -DYJIT_FORCE_ENABLE
assert_equal(NO_JIT_DESCRIPTION, r[0])
else
assert_equal(RUBY_DESCRIPTION, r[0])
@@ -170,9 +209,12 @@ class TestRubyOptions < Test::Unit::TestCase
end
def test_version
- assert_in_out_err(%w(--version)) do |r, e|
+ env = {'RUBY_YJIT_ENABLE' => nil} # unset in children
+ assert_in_out_err([env, '--version']) do |r, e|
assert_match(VERSION_PATTERN, r[0])
- if RubyVM::MJIT.enabled? # checking -DMJIT_FORCE_ENABLE
+ if ENV['RUBY_YJIT_ENABLE'] == '1'
+ assert_equal(NO_JIT_DESCRIPTION, r[0])
+ elsif defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? || self.class.yjit_enabled? # checking -D(M|Y)JIT_FORCE_ENABLE
assert_equal(EnvUtil.invoke_ruby(['-e', 'print RUBY_DESCRIPTION'], '', true).first, r[0])
else
assert_equal(RUBY_DESCRIPTION, r[0])
@@ -181,13 +223,19 @@ class TestRubyOptions < Test::Unit::TestCase
end
return if RbConfig::CONFIG["MJIT_SUPPORT"] == 'no'
+ return if yjit_force_enabled?
[
- %w(--version --jit --disable=jit),
- %w(--version --enable=jit --disable=jit),
- %w(--version --enable-jit --disable-jit),
+ %w(--version --mjit --disable=mjit),
+ %w(--version --enable=mjit --disable=mjit),
+ %w(--version --enable-mjit --disable-mjit),
+ *([
+ %w(--version --jit --disable=jit),
+ %w(--version --enable=jit --disable=jit),
+ %w(--version --enable-jit --disable-jit),
+ ] unless RUBY_PLATFORM.start_with?('x86_64-') && RUBY_PLATFORM !~ /mswin|mingw|msys/),
].each do |args|
- assert_in_out_err(args) do |r, e|
+ assert_in_out_err([env] + args) do |r, e|
assert_match(VERSION_PATTERN, r[0])
assert_match(NO_JIT_DESCRIPTION, r[0])
assert_equal([], e)
@@ -196,16 +244,21 @@ class TestRubyOptions < Test::Unit::TestCase
if JITSupport.supported?
[
- %w(--version --jit),
- %w(--version --enable=jit),
- %w(--version --enable-jit),
+ %w(--version --mjit),
+ %w(--version --enable=mjit),
+ %w(--version --enable-mjit),
+ *([
+ %w(--version --jit),
+ %w(--version --enable=jit),
+ %w(--version --enable-jit),
+ ] unless RUBY_PLATFORM.start_with?('x86_64-') && RUBY_PLATFORM !~ /mswin|mingw|msys/),
].each do |args|
- assert_in_out_err(args) do |r, e|
+ assert_in_out_err([env] + args) do |r, e|
assert_match(VERSION_PATTERN_WITH_JIT, r[0])
- if RubyVM::MJIT.enabled? # checking -DMJIT_FORCE_ENABLE
+ if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # checking -DMJIT_FORCE_ENABLE
assert_equal(RUBY_DESCRIPTION, r[0])
else
- assert_equal(EnvUtil.invoke_ruby(['--jit', '-e', 'print RUBY_DESCRIPTION'], '', true).first, r[0])
+ assert_equal(EnvUtil.invoke_ruby([env, '--mjit', '-e', 'print RUBY_DESCRIPTION'], '', true).first, r[0])
end
assert_equal([], e)
end
@@ -280,7 +333,7 @@ class TestRubyOptions < Test::Unit::TestCase
/unknown encoding name - test_ruby_test_rubyoptions_foobarbazqux \(RuntimeError\)/)
if /mswin|mingw|aix|android/ =~ RUBY_PLATFORM &&
- (str = "\u3042".force_encoding(Encoding.find("locale"))).valid_encoding?
+ (str = "\u3042".force_encoding(Encoding.find("external"))).valid_encoding?
# This result depends on locale because LANG=C doesn't affect locale
# on Windows.
# On AIX, the source encoding of stdin with LANG=C is ISO-8859-1,
@@ -328,6 +381,20 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err(%w(), "p $VERBOSE", ["true"])
assert_in_out_err(%w(-W1), "p $VERBOSE", ["false"])
assert_in_out_err(%w(-W0), "p $VERBOSE", ["nil"])
+ assert_in_out_err(%w(), "p Warning[:deprecated]", ["true"])
+ assert_in_out_err(%w(-W0), "p Warning[:deprecated]", ["false"])
+ assert_in_out_err(%w(-W1), "p Warning[:deprecated]", ["false"])
+ assert_in_out_err(%w(-W2), "p Warning[:deprecated]", ["true"])
+ ENV['RUBYOPT'] = '-W:deprecated'
+ assert_in_out_err(%w(), "p Warning[:deprecated]", ["true"])
+ ENV['RUBYOPT'] = '-W:no-deprecated'
+ assert_in_out_err(%w(), "p Warning[:deprecated]", ["false"])
+ ENV['RUBYOPT'] = '-W:experimental'
+ assert_in_out_err(%w(), "p Warning[:experimental]", ["true"])
+ ENV['RUBYOPT'] = '-W:no-experimental'
+ assert_in_out_err(%w(), "p Warning[:experimental]", ["false"])
+ ENV['RUBYOPT'] = '-W:qux'
+ assert_in_out_err(%w(), "", [], /unknown warning category: `qux'/)
ensure
if rubyopt_orig
ENV['RUBYOPT'] = rubyopt_orig
@@ -479,16 +546,18 @@ class TestRubyOptions < Test::Unit::TestCase
["case nil; when true", "end"],
["if false;", "end", "if true\nelse ", "end"],
["else", " end", "_ = if true\n"],
+ ["begin\n def f() = nil", "end"],
+ ["begin\n def self.f() = nil", "end"],
].each do
|b, e = 'end', pre = nil, post = nil|
src = ["#{pre}#{b}\n", " #{e}\n#{post}"]
k = b[/\A\s*(\S+)/, 1]
e = e[/\A\s*(\S+)/, 1]
- n = 2
- n += pre.count("\n") if pre
+ n = 1 + src[0].count("\n")
+ n1 = 1 + (pre ? pre.count("\n") : 0)
a.for("no directives with #{src}") do
- err = ["#{t.path}:#{n}: warning: mismatched indentations at '#{e}' with '#{k}' at #{n-1}"]
+ err = ["#{t.path}:#{n}: warning: mismatched indentations at '#{e}' with '#{k}' at #{n1}"]
t.rewind
t.truncate(0)
t.puts src
@@ -507,7 +576,7 @@ class TestRubyOptions < Test::Unit::TestCase
end
a.for("false and true directives with #{src}") do
- err = ["#{t.path}:#{n+2}: warning: mismatched indentations at '#{e}' with '#{k}' at #{n+1}"]
+ err = ["#{t.path}:#{n+2}: warning: mismatched indentations at '#{e}' with '#{k}' at #{n1+2}"]
t.rewind
t.truncate(0)
t.puts "# -*- warn-indent: false -*-"
@@ -529,7 +598,7 @@ class TestRubyOptions < Test::Unit::TestCase
end
a.for("BOM with #{src}") do
- err = ["#{t.path}:#{n}: warning: mismatched indentations at '#{e}' with '#{k}' at #{n-1}"]
+ err = ["#{t.path}:#{n}: warning: mismatched indentations at '#{e}' with '#{k}' at #{n1}"]
t.rewind
t.truncate(0)
t.print "\u{feff}"
@@ -691,6 +760,7 @@ class TestRubyOptions < Test::Unit::TestCase
%r(
(?:
--\sC\slevel\sbacktrace\sinformation\s-------------------------------------------\n
+ (?:Un(?:expected|supported|known)\s.*\n)*
(?:(?:.*\s)?\[0x\h+\].*\n|.*:\d+\n)*\n
)?
)x,
@@ -703,6 +773,8 @@ class TestRubyOptions < Test::Unit::TestCase
end
def assert_segv(args, message=nil)
+ skip if ENV['RUBY_ON_BUG']
+
test_stdin = ""
opt = SEGVTest::ExecOptions.dup
list = SEGVTest::ExpectedStderrList
@@ -821,11 +893,11 @@ class TestRubyOptions < Test::Unit::TestCase
def test_command_line_glob_nonascii
bug10555 = '[ruby-dev:48752] [Bug #10555]'
name = "\u{3042}.txt"
- expected = name.encode("locale") rescue "?.txt"
+ expected = name.encode("external") rescue "?.txt"
with_tmpchdir do |dir|
open(name, "w") {}
assert_in_out_err(["-e", "puts ARGV", "?.txt"], "", [expected], [],
- bug10555, encoding: "locale")
+ bug10555, encoding: "external")
end
end
@@ -860,7 +932,7 @@ class TestRubyOptions < Test::Unit::TestCase
with_tmpchdir do |dir|
Ougai.each {|f| open(f, "w") {}}
assert_in_out_err(["-Eutf-8", "-e", "puts ARGV", "*"], "", Ougai, encoding: "utf-8")
- ougai = Ougai.map {|f| f.encode("locale", replace: "?")}
+ ougai = Ougai.map {|f| f.encode("external", replace: "?")}
assert_in_out_err(["-e", "puts ARGV", "*.txt"], "", ougai)
end
end
@@ -996,11 +1068,11 @@ class TestRubyOptions < Test::Unit::TestCase
err = !freeze ? [] : debug ? with_debug_pat : wo_debug_pat
[
['"foo" << "bar"', err],
- ['"foo#{123}bar" << "bar"', err],
+ ['"foo#{123}bar" << "bar"', []],
['+"foo#{123}bar" << "bar"', []],
- ['-"foo#{123}bar" << "bar"', freeze && debug ? with_debug_pat : wo_debug_pat],
+ ['-"foo#{123}bar" << "bar"', wo_debug_pat],
].each do |code, expected|
- assert_in_out_err(opt, code, [], expected, [opt, code])
+ assert_in_out_err(opt, code, [], expected, "#{opt} #{code}")
end
end
end
@@ -1038,6 +1110,11 @@ class TestRubyOptions < Test::Unit::TestCase
end
end
+ def test_rubylib_invalid_encoding
+ env = {"RUBYLIB"=>"\xFF", "LOCALE"=>"en_US.UTF-8", "LC_ALL"=>"en_US.UTF-8"}
+ assert_ruby_status([env, "-e;"])
+ end
+
def test_null_script
skip "#{IO::NULL} is not a character device" unless File.chardev?(IO::NULL)
assert_in_out_err([IO::NULL], success: true)
@@ -1047,7 +1124,7 @@ class TestRubyOptions < Test::Unit::TestCase
# mswin uses prebuilt precompiled header. Thus it does not show a pch compilation log to check "-O0 -O1".
if JITSupport.supported? && !RUBY_PLATFORM.match?(/mswin/)
env = { 'MJIT_SEARCH_BUILD_DIR' => 'true' }
- assert_in_out_err([env, "--jit-debug=-O0 -O1", "--jit-verbose=2", "" ], "", [], /-O0 -O1/)
+ assert_in_out_err([env, "--disable-yjit", "--mjit-debug=-O0 -O1", "--mjit-verbose=2", "" ], "", [], /-O0 -O1/)
end
end
@@ -1056,4 +1133,8 @@ class TestRubyOptions < Test::Unit::TestCase
def mjit_force_enabled?
"#{RbConfig::CONFIG['CFLAGS']} #{RbConfig::CONFIG['CPPFLAGS']}".match?(/(\A|\s)-D ?MJIT_FORCE_ENABLE\b/)
end
+
+ def yjit_force_enabled?
+ "#{RbConfig::CONFIG['CFLAGS']} #{RbConfig::CONFIG['CPPFLAGS']}".match?(/(\A|\s)-D ?YJIT_FORCE_ENABLE\b/)
+ end
end
diff --git a/test/ruby/test_rubyvm.rb b/test/ruby/test_rubyvm.rb
index 7673d8dfbe..7d4588a165 100644
--- a/test/ruby/test_rubyvm.rb
+++ b/test/ruby/test_rubyvm.rb
@@ -4,15 +4,68 @@ require 'test/unit'
class TestRubyVM < Test::Unit::TestCase
def test_stat
assert_kind_of Hash, RubyVM.stat
- assert_kind_of Integer, RubyVM.stat[:global_method_state]
+ assert_kind_of Integer, RubyVM.stat[:global_constant_state]
RubyVM.stat(stat = {})
assert_not_empty stat
- assert_equal stat[:global_method_state], RubyVM.stat(:global_method_state)
+ assert_equal stat[:global_constant_state], RubyVM.stat(:global_constant_state)
end
def test_stat_unknown
assert_raise(ArgumentError){ RubyVM.stat(:unknown) }
assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {RubyVM.stat(:"\u{30eb 30d3 30fc}")}
end
+
+ def parse_and_compile
+ script = <<~RUBY
+ _a = 1
+ def foo
+ _b = 2
+ end
+ 1.times{
+ _c = 3
+ }
+ RUBY
+
+ ast = RubyVM::AbstractSyntaxTree.parse(script)
+ iseq = RubyVM::InstructionSequence.compile(script)
+
+ [ast, iseq]
+ end
+
+ def test_keep_script_lines
+ prev_conf = RubyVM.keep_script_lines
+
+ # keep
+ RubyVM.keep_script_lines = true
+
+ ast, iseq = *parse_and_compile
+
+ lines = ast.script_lines
+ assert_equal Array, lines.class
+
+ lines = iseq.script_lines
+ assert_equal Array, lines.class
+ iseq.each_child{|child|
+ assert_equal lines, child.script_lines
+ }
+ assert lines.frozen?
+
+ # don't keep
+ RubyVM.keep_script_lines = false
+
+ ast, iseq = *parse_and_compile
+
+ lines = ast.script_lines
+ assert_equal nil, lines
+
+ lines = iseq.script_lines
+ assert_equal nil, lines
+ iseq.each_child{|child|
+ assert_equal lines, child.script_lines
+ }
+
+ ensure
+ RubyVM.keep_script_lines = prev_conf
+ end
end
diff --git a/test/ruby/test_rubyvm_mjit.rb b/test/ruby/test_rubyvm_jit.rb
index ef7475670c..a3558d791c 100644
--- a/test/ruby/test_rubyvm_mjit.rb
+++ b/test/ruby/test_rubyvm_jit.rb
@@ -4,7 +4,7 @@ require_relative '../lib/jit_support'
return if RbConfig::CONFIG["MJIT_SUPPORT"] == 'no'
-class TestRubyVMMJIT < Test::Unit::TestCase
+class TestRubyVMJIT < Test::Unit::TestCase
include JITSupport
def setup
diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb
index 316e14e1ef..73d8aee6a2 100644
--- a/test/ruby/test_settracefunc.rb
+++ b/test/ruby/test_settracefunc.rb
@@ -3,17 +3,21 @@ require 'test/unit'
class TestSetTraceFunc < Test::Unit::TestCase
def setup
- @original_compile_option = RubyVM::InstructionSequence.compile_option
- RubyVM::InstructionSequence.compile_option = {
- :trace_instruction => true,
- :specialized_instruction => false
- }
+ if defined?(RubyVM)
+ @original_compile_option = RubyVM::InstructionSequence.compile_option
+ RubyVM::InstructionSequence.compile_option = {
+ :trace_instruction => true,
+ :specialized_instruction => false
+ }
+ end
@target_thread = Thread.current
end
def teardown
set_trace_func(nil)
- RubyVM::InstructionSequence.compile_option = @original_compile_option
+ if defined?(RubyVM)
+ RubyVM::InstructionSequence.compile_option = @original_compile_option
+ end
@target_thread = nil
end
@@ -46,6 +50,29 @@ class TestSetTraceFunc < Test::Unit::TestCase
assert_equal([], events)
end
+ def test_c_call_removed_method
+ # [Bug #19305]
+ klass = Class.new do
+ attr_writer :bar
+ alias_method :set_bar, :bar=
+ remove_method :bar=
+ end
+
+ obj = klass.new
+ method_id = nil
+ parameters = nil
+
+ TracePoint.new(:c_call) { |tp|
+ method_id = tp.method_id
+ parameters = tp.parameters
+ }.enable {
+ obj.set_bar(1)
+ }
+
+ assert_equal(:bar=, method_id)
+ assert_equal([[:req]], parameters)
+ end
+
def test_call
events = []
name = "#{self.class}\##{__method__}"
@@ -137,6 +164,10 @@ class TestSetTraceFunc < Test::Unit::TestCase
assert_equal(["c-call", 9, :set_trace_func, Kernel],
events.shift)
assert_equal([], events)
+
+ self.class.class_eval do
+ remove_const :Foo
+ end
end
def test_return # [ruby-dev:38701]
@@ -362,6 +393,11 @@ class TestSetTraceFunc < Test::Unit::TestCase
end
assert_equal([], events[:set])
assert_equal([], events[:add])
+
+ # cleanup
+ self.class.class_eval do
+ remove_const :ThreadTraceInnerClass
+ end
end
def test_trace_defined_method
@@ -380,7 +416,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
[["c-return", 3, :set_trace_func, Kernel],
["line", 6, __method__, self.class],
["call", 1, :foobar, FooBar],
- ["return", 6, :foobar, FooBar],
+ ["return", 1, :foobar, FooBar],
["line", 7, __method__, self.class],
["c-call", 7, :set_trace_func, Kernel]].each{|e|
assert_equal(e, events.shift)
@@ -472,8 +508,6 @@ class TestSetTraceFunc < Test::Unit::TestCase
[:c_call, 4, 'xyzzy', Integer, :times, 1, :outer, :nothing],
[:line, 4, 'xyzzy', self.class, method, self, nil, :nothing],
[:line, 5, 'xyzzy', self.class, method, self, :inner, :nothing],
- [:c_call, 5, 'xyzzy', Kernel, :tap, self, :inner, :nothing],
- [:c_return, 5, "xyzzy", Kernel, :tap, self, :inner, self],
[:c_return, 4, "xyzzy", Integer, :times, 1, :outer, 1],
[:line, 7, 'xyzzy', self.class, method, self, :outer, :nothing],
[:c_call, 7, "xyzzy", Class, :inherited, Object, :outer, :nothing],
@@ -499,8 +533,6 @@ class TestSetTraceFunc < Test::Unit::TestCase
[:call, 13, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
[:line, 14, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
[:line, 15, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, :nothing],
- [:c_call, 15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, :nothing],
- [:c_return,15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, xyzzy],
[:return, 16, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, xyzzy],
[:return, 12, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, xyzzy],
[:line, 20, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
@@ -555,6 +587,16 @@ class TestSetTraceFunc < Test::Unit::TestCase
}
end
+ # Bug #18264
+ def test_tracepoint_memory_leak
+ assert_no_memory_leak([], <<-PREP, <<-CODE, rss: true)
+code = proc { TracePoint.new(:line) { } }
+1_000.times(&code)
+PREP
+1_000_000.times(&code)
+CODE
+ end
+
def trace_by_set_trace_func
events = []
trace = nil
@@ -597,8 +639,6 @@ class TestSetTraceFunc < Test::Unit::TestCase
[:c_call, 4, 'xyzzy', Integer, :times, 1, :outer, :nothing],
[:line, 4, 'xyzzy', self.class, method, self, nil, :nothing],
[:line, 5, 'xyzzy', self.class, method, self, :inner, :nothing],
- [:c_call, 5, 'xyzzy', Kernel, :tap, self, :inner, :nothing],
- [:c_return, 5, "xyzzy", Kernel, :tap, self, :inner, self],
[:c_return, 4, "xyzzy", Integer, :times, 1, :outer, 1],
[:line, 7, 'xyzzy', self.class, method, self, :outer, :nothing],
[:c_call, 7, "xyzzy", Class, :inherited, Object, :outer, :nothing],
@@ -624,8 +664,6 @@ class TestSetTraceFunc < Test::Unit::TestCase
[:call, 13, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
[:line, 14, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
[:line, 15, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, :nothing],
- [:c_call, 15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, :nothing],
- [:c_return,15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, xyzzy],
[:return, 16, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, xyzzy],
[:return, 12, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, xyzzy],
[:line, 20, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
@@ -826,6 +864,56 @@ class TestSetTraceFunc < Test::Unit::TestCase
}
end
+ def test_tracepoint_attr
+ c = Class.new do
+ attr_accessor :x
+ alias y x
+ alias y= x=
+ end
+ obj = c.new
+
+ ar_meth = obj.method(:x)
+ aw_meth = obj.method(:x=)
+ aar_meth = obj.method(:y)
+ aaw_meth = obj.method(:y=)
+ events = []
+ trace = TracePoint.new(:c_call, :c_return){|tp|
+ next if !target_thread?
+ next if tp.path != __FILE__
+ next if tp.method_id == :call
+ case tp.event
+ when :c_call
+ assert_raise(RuntimeError) {tp.return_value}
+ events << [tp.event, tp.method_id, tp.callee_id]
+ when :c_return
+ events << [tp.event, tp.method_id, tp.callee_id, tp.return_value]
+ end
+ }
+ test_proc = proc do
+ obj.x = 1
+ obj.x
+ obj.y = 2
+ obj.y
+ aw_meth.call(1)
+ ar_meth.call
+ aaw_meth.call(2)
+ aar_meth.call
+ end
+ test_proc.call # populate call caches
+ trace.enable(&test_proc)
+ expected = [
+ [:c_call, :x=, :x=],
+ [:c_return, :x=, :x=, 1],
+ [:c_call, :x, :x],
+ [:c_return, :x, :x, 1],
+ [:c_call, :x=, :y=],
+ [:c_return, :x=, :y=, 2],
+ [:c_call, :x, :y],
+ [:c_return, :x, :y, 2],
+ ]
+ assert_equal(expected*2, events)
+ end
+
class XYZZYException < Exception; end
def method_test_tracepoint_raised_exception err
raise err
@@ -932,9 +1020,9 @@ class TestSetTraceFunc < Test::Unit::TestCase
when :line
assert_match(/ in /, str)
when :call, :c_call
- assert_match(/call \`/, str) # #<TracePoint:c_call `inherited'@../trunk/test.rb:11>
+ assert_match(/call \`/, str) # #<TracePoint:c_call `inherited' ../trunk/test.rb:11>
when :return, :c_return
- assert_match(/return \`/, str) # #<TracePoint:return `m'@../trunk/test.rb:3>
+ assert_match(/return \`/, str) # #<TracePoint:return `m' ../trunk/test.rb:3>
when /thread/
assert_match(/\#<Thread:/, str) # #<TracePoint:thread_end of #<Thread:0x87076c0>>
else
@@ -977,20 +1065,24 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_tracepoint_with_multithreads
assert_nothing_raised do
- TracePoint.new{
+ TracePoint.new(:line){
10.times{
Thread.pass
}
}.enable do
(1..10).map{
Thread.new{
- 1000.times{
+ 1_000.times{|i|
+ _a = i
}
}
}.each{|th|
th.join
}
end
+ _a = 1
+ _b = 2
+ _c = 3 # to make sure the deletion of unused TracePoints
end
end
@@ -1588,6 +1680,33 @@ class TestSetTraceFunc < Test::Unit::TestCase
assert_equal ev, :fiber_switch
}
+ # test for raise into resumable fiber
+ evs = []
+ f = nil
+ TracePoint.new(:raise, :fiber_switch){|tp|
+ next unless target_thread?
+ evs << [tp.event, Fiber.current]
+ }.enable{
+ f = Fiber.new{
+ Fiber.yield # will raise
+ Fiber.yield # unreachable
+ }
+ begin
+ f.resume
+ f.raise StopIteration
+ rescue StopIteration
+ evs << :rescued
+ end
+ }
+ assert_equal [:fiber_switch, f], evs[0], "initial resume"
+ assert_equal [:fiber_switch, Fiber.current], evs[1], "Fiber.yield"
+ assert_equal [:fiber_switch, f], evs[2], "fiber.raise"
+ assert_equal [:raise, f], evs[3], "fiber.raise"
+ assert_equal [:fiber_switch, Fiber.current], evs[4], "terminated with raise"
+ assert_equal [:raise, Fiber.current], evs[5], "terminated with raise"
+ assert_equal :rescued, evs[6]
+ assert_equal 7, evs.size
+
# test for transfer
evs = []
TracePoint.new(:fiber_switch){|tp|
@@ -1610,6 +1729,41 @@ class TestSetTraceFunc < Test::Unit::TestCase
evs.each{|ev|
assert_equal ev, :fiber_switch
}
+
+ # test for raise and from transferring fibers
+ evs = []
+ f1 = f2 = nil
+ TracePoint.new(:raise, :fiber_switch){|tp|
+ next unless target_thread?
+ evs << [tp.event, Fiber.current]
+ }.enable{
+ f1 = Fiber.new{
+ f2.transfer
+ f2.raise ScriptError
+ Fiber.yield :ok
+ }
+ f2 = Fiber.new{
+ f1.transfer
+ f1.transfer
+ }
+ begin
+ f1.resume
+ rescue ScriptError
+ evs << :rescued
+ end
+ }
+ assert_equal [:fiber_switch, f1], evs[0], "initial resume"
+ assert_equal [:fiber_switch, f2], evs[1], "f2.transfer"
+ assert_equal [:fiber_switch, f1], evs[2], "f1.transfer"
+ assert_equal [:fiber_switch, f2], evs[3], "f2.raise ScriptError"
+ assert_equal [:raise, f2], evs[4], "f2.raise ScriptError"
+ assert_equal [:fiber_switch, f1], evs[5], "f2 unhandled exception"
+ assert_equal [:raise, f1], evs[6], "f2 unhandled exception"
+ assert_equal [:fiber_switch, Fiber.current], evs[7], "f1 unhandled exception"
+ assert_equal [:raise, Fiber.current], evs[8], "f1 unhandled exception"
+ assert_equal :rescued, evs[9], "rescued everything"
+ assert_equal 10, evs.size
+
end
def test_tracepoint_callee_id
@@ -1672,7 +1826,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
TracePoint.new(:return, &capture_events).enable{
o.alias_m
}
- assert_equal [[:return, :m, :alias_m]], events
+ assert_equal [[:return, :tap, :tap], [:return, :m, :alias_m]], events
events.clear
o = Class.new{
@@ -1696,13 +1850,13 @@ class TestSetTraceFunc < Test::Unit::TestCase
events.clear
o = Class.new{
- alias alias_tap tap
- define_method(:m){alias_tap{return}}
+ alias alias_singleton_class singleton_class
+ define_method(:m){alias_singleton_class}
}.new
TracePoint.new(:c_return, &capture_events).enable{
o.m
}
- assert_equal [[:c_return, :tap, :alias_tap]], events
+ assert_equal [[:c_return, :singleton_class, :alias_singleton_class]], events
events.clear
c = Class.new{
@@ -1820,7 +1974,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
end
define_method(:f_break_defined) do
- return :f_break_defined
+ break :f_break_defined
end
define_method(:f_raise_defined) do
@@ -1841,27 +1995,44 @@ class TestSetTraceFunc < Test::Unit::TestCase
tp_return_value(:f_last_defined),
'[Bug #13369]'
- assert_equal [[:b_return, :f_return_defined, nil], # current limitation
+ assert_equal [[:b_return, :f_return_defined, :f_return_defined],
[:return, :f_return_defined, :f_return_defined]],
tp_return_value(:f_return_defined),
'[Bug #13369]'
- assert_equal [[:b_return, :f_break_defined, nil],
+ assert_equal [[:b_return, :f_break_defined, :f_break_defined],
[:return, :f_break_defined, :f_break_defined]],
tp_return_value(:f_break_defined),
'[Bug #13369]'
- assert_equal [[:b_return, :f_raise_defined, nil],
+ assert_equal [[:b_return, :f_raise_defined, f_raise_defined],
[:return, :f_raise_defined, f_raise_defined]],
tp_return_value(:f_raise_defined),
'[Bug #13369]'
- assert_equal [[:b_return, :f_break_in_rescue_defined, nil],
+ assert_equal [[:b_return, :f_break_in_rescue_defined, f_break_in_rescue_defined],
[:return, :f_break_in_rescue_defined, f_break_in_rescue_defined]],
tp_return_value(:f_break_in_rescue_defined),
'[Bug #13369]'
end
+ define_method(:just_yield) do |&block|
+ block.call
+ end
+
+ define_method(:unwind_multiple_bmethods) do
+ just_yield { return :unwind_multiple_bmethods }
+ end
+
+ def test_non_local_return_across_multiple_define_methods
+ assert_equal [[:b_return, :unwind_multiple_bmethods, nil],
+ [:b_return, :just_yield, nil],
+ [:return, :just_yield, nil],
+ [:b_return, :unwind_multiple_bmethods, :unwind_multiple_bmethods],
+ [:return, :unwind_multiple_bmethods, :unwind_multiple_bmethods]],
+ tp_return_value(:unwind_multiple_bmethods)
+ end
+
def f_iter
yield
end
@@ -1917,7 +2088,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_thread_add_trace_func
events = []
base_line = __LINE__
- q = Queue.new
+ q = Thread::Queue.new
t = Thread.new{
Thread.current.add_trace_func proc{|ev, file, line, *args|
events << [ev, line]
@@ -1943,7 +2114,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
# other thread
events = []
- m2t_q = Queue.new
+ m2t_q = Thread::Queue.new
t = Thread.new{
Thread.current.abort_on_exception = true
@@ -1958,7 +2129,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
Thread.pass until t.status == 'sleep'
# When MJIT thread exists, t.status becomes 'sleep' even if it does not reach m2t_q.pop.
# This sleep forces it to reach m2t_q.pop for --jit-wait.
- sleep 1 if RubyVM::MJIT.enabled?
+ sleep 1 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
t.add_trace_func proc{|ev, file, line, *args|
if file == __FILE__
@@ -2170,6 +2341,31 @@ class TestSetTraceFunc < Test::Unit::TestCase
assert_equal 'target_line is specified, but line event is not specified', e.message
end
+ def test_tracepoint_enable_with_target_line_two_times
+ events = []
+ line_0 = __LINE__
+ code1 = proc{
+ events << 1 # tp1
+ events << 2
+ events << 3 # tp2
+ }
+
+ tp1 = TracePoint.new(:line) do |tp|
+ events << :tp1
+ end
+ tp2 = TracePoint.new(:line) do |tp|
+ events << :tp2
+ end
+
+ tp1.enable(target: code1, target_line: line_0 + 2) do
+ tp2.enable(target: code1, target_line: line_0 + 4) do
+ # two hooks
+ code1.call
+ end
+ end
+ assert_equal [:tp1, 1, 2, :tp2, 3], events
+ end
+
def test_script_compiled
events = []
tp = TracePoint.new(:script_compiled){|tp|
@@ -2188,10 +2384,19 @@ class TestSetTraceFunc < Test::Unit::TestCase
[__FILE__+"/instance_eval", eval_script],
[__FILE__+"/class_eval", eval_script],
], events
+
events.clear
+ tp.enable{
+ begin
+ eval('a=')
+ rescue SyntaxError
+ end
+ }
+ assert_equal [], events, 'script_compiled event should not be invoked on compile error'
skip "TODO: test for requires"
+ events.clear
tp.enable{
require ''
require_relative ''
@@ -2219,8 +2424,8 @@ class TestSetTraceFunc < Test::Unit::TestCase
events << Thread.current
end
- q1 = Queue.new
- q2 = Queue.new
+ q1 = Thread::Queue.new
+ q2 = Thread::Queue.new
th = Thread.new{
q1 << :ok; q2.pop
@@ -2238,6 +2443,99 @@ class TestSetTraceFunc < Test::Unit::TestCase
assert_equal Array.new(2){th}, events
end
+ def test_return_bmethod_location
+ bug13392 = "[ruby-core:80515] incorrect bmethod return location"
+ actual = nil
+ obj = Object.new
+ expected = __LINE__ + 1
+ obj.define_singleton_method(:t){}
+ tp = TracePoint.new(:return) do
+ next unless target_thread?
+ actual = tp.lineno
+ end
+ tp.enable {obj.t}
+ assert_equal(expected, actual, bug13392)
+ end
+
+ def test_b_tracepoints_going_away
+ # test that call and return TracePoints continue to work
+ # when b_call and b_return TracePoints stop
+ events = []
+ record_events = ->(tp) do
+ next unless target_thread?
+ events << [tp.event, tp.method_id]
+ end
+
+ call_ret_tp = TracePoint.new(:call, :return, &record_events)
+ block_call_ret_tp = TracePoint.new(:b_call, :b_return, &record_events)
+
+ obj = Object.new
+ obj.define_singleton_method(:foo) {} # a bmethod
+
+ foo = obj.method(:foo)
+ call_ret_tp.enable(target: foo) do
+ block_call_ret_tp.enable(target: foo) do
+ obj.foo
+ end
+ obj.foo
+ end
+
+ assert_equal(
+ [
+ [:call, :foo],
+ [:b_call, :foo],
+ [:b_return, :foo],
+ [:return, :foo],
+ [:call, :foo],
+ [:return, :foo],
+ ],
+ events,
+ )
+ end
+
+ def test_target_different_bmethod_same_iseq
+ # make two bmethods that share the same block iseq
+ block = Proc.new {}
+ obj = Object.new
+ obj.define_singleton_method(:one, &block)
+ obj.define_singleton_method(:two, &block)
+
+ events = []
+ record_events = ->(tp) do
+ next unless target_thread?
+ events << [tp.event, tp.method_id]
+ end
+ tp_one = TracePoint.new(:call, :return, &record_events)
+ tp_two = TracePoint.new(:call, :return, &record_events)
+
+ tp_one.enable(target: obj.method(:one)) do
+ obj.one
+ obj.two # not targeted
+ end
+ assert_equal([[:call, :one], [:return, :one]], events)
+ events.clear
+
+ tp_one.enable(target: obj.method(:one)) do
+ obj.one
+ tp_two.enable(target: obj.method(:two)) do
+ obj.two
+ end
+ obj.two
+ obj.one
+ end
+ assert_equal(
+ [
+ [:call, :one],
+ [:return, :one],
+ [:call, :two],
+ [:return, :two],
+ [:call, :one],
+ [:return, :one],
+ ],
+ events
+ )
+ end
+
def test_return_event_with_rescue
obj = Object.new
def obj.example
@@ -2270,4 +2568,58 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_stat_exists
assert_instance_of Hash, TracePoint.stat
end
+
+ def test_tracepoint_opt_invokebuiltin_delegate_leave
+ code = 'puts RubyVM::InstructionSequence.of("\x00".method(:unpack)).disasm'
+ out = EnvUtil.invoke_ruby(['-e', code], '', true).first
+ assert_match(/^0000 opt_invokebuiltin_delegate_leave /, out)
+
+ event = eval(EnvUtil.invoke_ruby(['-e', <<~'EOS'], '', true).first)
+ TracePoint.new(:return) do |tp|
+ p [tp.event, tp.method_id]
+ end.enable do
+ "\x00".unpack("c")
+ end
+ EOS
+ assert_equal [:return, :unpack], event
+ end
+
+ def test_while_in_while
+ lines = []
+
+ TracePoint.new(:line){|tp|
+ next unless target_thread?
+ lines << tp.lineno
+ }.enable{
+ n = 3
+ while n > 0
+ n -= 1 while n > 0
+ end
+ }
+ assert_equal [__LINE__ - 5, __LINE__ - 4, __LINE__ - 3], lines, 'Bug #17868'
+ end
+
+ def test_allow_reentry
+ event_lines = []
+ _l1 = _l2 = _l3 = _l4 = nil
+ TracePoint.new(:line) do |tp|
+ next unless target_thread?
+
+ event_lines << tp.lineno
+ next if (__LINE__ + 2 .. __LINE__ + 4).cover?(tp.lineno)
+ TracePoint.allow_reentry do
+ _a = 1; _l3 = __LINE__
+ _b = 2; _l4 = __LINE__
+ end
+ end.enable do
+ _c = 3; _l1 = __LINE__
+ _d = 4; _l2 = __LINE__
+ end
+
+ assert_equal [_l1, _l3, _l4, _l2, _l3, _l4], event_lines
+
+ assert_raise RuntimeError do
+ TracePoint.allow_reentry{}
+ end
+ end
end
diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb
index 7986e9d141..f2e73eb58d 100644
--- a/test/ruby/test_sprintf.rb
+++ b/test/ruby/test_sprintf.rb
@@ -528,19 +528,4 @@ class TestSprintf < Test::Unit::TestCase
sprintf("%*s", RbConfig::LIMITS["INT_MIN"], "")
end
end
-
- def test_no_hidden_garbage
- skip unless Thread.list.size == 1
-
- fmt = [4, 2, 2].map { |x| "%0#{x}d" }.join('-') # defeats optimization
- ObjectSpace.count_objects(res = {}) # creates strings on first call
- GC.disable
- before = ObjectSpace.count_objects(res)[:T_STRING]
- val = sprintf(fmt, 1970, 1, 1)
- after = ObjectSpace.count_objects(res)[:T_STRING]
- assert_equal before + 1, after, 'only new string is the created one'
- assert_equal '1970-01-01', val
- ensure
- GC.enable
- end
end
diff --git a/test/ruby/test_stack.rb b/test/ruby/test_stack.rb
new file mode 100644
index 0000000000..763aeb6bc2
--- /dev/null
+++ b/test/ruby/test_stack.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'tmpdir'
+
+class TestStack < Test::Unit::TestCase
+ LARGE_VM_STACK_SIZE = 1024*1024*5
+ LARGE_MACHINE_STACK_SIZE = 1024*1024*10
+
+ def initialize(*)
+ super
+
+ @h_default = nil
+ @h_0 = nil
+ @h_large = nil
+ end
+
+ def invoke_ruby script, vm_stack_size: nil, machine_stack_size: nil
+ env = {}
+ env['RUBY_FIBER_VM_STACK_SIZE'] = vm_stack_size.to_s if vm_stack_size
+ env['RUBY_FIBER_MACHINE_STACK_SIZE'] = machine_stack_size.to_s if machine_stack_size
+ env['ASAN_OPTIONS'] = ENV['ASAN_OPTIONS'] if ENV['ASAN_OPTIONS']
+
+ stdout, stderr, status = EnvUtil.invoke_ruby([env, '-e', script], '', true, true, timeout: 30)
+ assert(!status.signaled?, FailDesc[status, nil, stderr])
+
+ return stdout
+ end
+
+ def h_default
+ @h_default ||= eval(invoke_ruby('p RubyVM::DEFAULT_PARAMS'))
+ end
+
+ def h_0
+ @h_0 ||= eval(invoke_ruby('p RubyVM::DEFAULT_PARAMS',
+ vm_stack_size: 0,
+ machine_stack_size: 0
+ ))
+ end
+
+ def h_large
+ @h_large ||= eval(invoke_ruby('p RubyVM::DEFAULT_PARAMS',
+ vm_stack_size: LARGE_VM_STACK_SIZE,
+ machine_stack_size: LARGE_MACHINE_STACK_SIZE
+ ))
+ end
+
+ def test_relative_stack_sizes
+ assert_operator(h_default[:fiber_vm_stack_size], :>, h_0[:fiber_vm_stack_size])
+ assert_operator(h_default[:fiber_vm_stack_size], :<, h_large[:fiber_vm_stack_size])
+ assert_operator(h_default[:fiber_machine_stack_size], :>=, h_0[:fiber_machine_stack_size])
+ assert_operator(h_default[:fiber_machine_stack_size], :<=, h_large[:fiber_machine_stack_size])
+ end
+
+ def test_vm_stack_size
+ script = '$stdout.sync=true; def rec; print "."; rec; end; Fiber.new{rec}.resume'
+
+ size_default = invoke_ruby(script).bytesize
+ assert_operator(size_default, :>, 0)
+
+ size_0 = invoke_ruby(script, vm_stack_size: 0).bytesize
+ assert_operator(size_default, :>, size_0)
+
+ size_large = invoke_ruby(script, vm_stack_size: LARGE_VM_STACK_SIZE).bytesize
+ assert_operator(size_default, :<, size_large)
+ end
+
+ # Depending on OS, machine stack size may not change size.
+ def test_machine_stack_size
+ return if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ script = '$stdout.sync=true; def rec; print "."; 1.times{1.times{1.times{rec}}}; end; Fiber.new{rec}.resume'
+
+ vm_stack_size = 1024 * 1024
+ size_default = invoke_ruby(script, vm_stack_size: vm_stack_size).bytesize
+
+ size_0 = invoke_ruby(script, vm_stack_size: vm_stack_size, machine_stack_size: 0).bytesize
+ assert_operator(size_default, :>=, size_0)
+
+ size_large = invoke_ruby(script, vm_stack_size: vm_stack_size, machine_stack_size: LARGE_MACHINE_STACK_SIZE).bytesize
+ assert_operator(size_default, :<=, size_large)
+ end
+end
diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb
index 0276d6a14d..6c00aa15f9 100644
--- a/test/ruby/test_string.rb
+++ b/test/ruby/test_string.rb
@@ -2,8 +2,6 @@
require 'test/unit'
class TestString < Test::Unit::TestCase
- ENUMERATOR_WANTARRAY = RUBY_VERSION >= "3.0.0"
-
WIDE_ENCODINGS = [
Encoding::UTF_16BE, Encoding::UTF_16LE,
Encoding::UTF_32BE, Encoding::UTF_32LE,
@@ -107,6 +105,16 @@ PREP
CODE
end
+ # Bug #18154
+ def test_initialize_nofree_memory_leak
+ assert_no_memory_leak([], <<-PREP, <<-CODE, rss: true)
+code = proc {0.to_s.__send__(:initialize, capacity: 10000)}
+1_000.times(&code)
+PREP
+100_000.times(&code)
+CODE
+ end
+
def test_AREF # '[]'
assert_equal("A", S("AooBar")[0])
assert_equal("B", S("FooBaB")[-1])
@@ -257,6 +265,7 @@ CODE
assert_not_equal(S("CAT"), S('cat'))
assert_not_equal(S("CaT"), S('cAt'))
+ assert_not_equal(S("cat\0""dog"), S("cat\0"))
o = Object.new
def o.to_str; end
@@ -387,6 +396,8 @@ CODE
end
def test_chomp
+ verbose, $VERBOSE = $VERBOSE, nil
+
assert_equal(S("hello"), S("hello").chomp("\n"))
assert_equal(S("hello"), S("hello\n").chomp("\n"))
save = $/
@@ -452,9 +463,12 @@ CODE
assert_equal("foo", s.chomp("\n"))
ensure
$/ = save
+ $VERBOSE = verbose
end
def test_chomp!
+ verbose, $VERBOSE = $VERBOSE, nil
+
a = S("hello")
a.chomp!(S("\n"))
@@ -511,6 +525,7 @@ CODE
s = S("").freeze
assert_raise_with_message(FrozenError, /frozen/) {s.chomp!}
+ $VERBOSE = nil # EnvUtil.suppress_warning resets $VERBOSE to the original state
s = S("ax")
o = Struct.new(:s).new(s)
@@ -519,6 +534,7 @@ CODE
"x"
end
assert_raise_with_message(FrozenError, /frozen/) {s.chomp!(o)}
+ $VERBOSE = nil # EnvUtil.suppress_warning resets $VERBOSE to the original state
s = S("hello")
assert_equal("hel", s.chomp!('lo'))
@@ -569,6 +585,7 @@ CODE
assert_equal("foo", s.chomp!("\n"))
ensure
$/ = save
+ $VERBOSE = verbose
end
def test_chop
@@ -688,6 +705,7 @@ CODE
@cls == String and
assert_no_memory_leak([], "s = ''; salt_proc = proc{#{(crypt_supports_des_crypt? ? '..' : good_salt).inspect}}", "#{<<~"begin;"}\n#{<<~'end;'}")
+
begin;
1000.times { s.crypt(-salt_proc.call).clear }
end;
@@ -751,6 +769,7 @@ CODE
assert_equal(S("hello"), S("hello").downcase)
assert_equal(S("hello"), S("HELLO").downcase)
assert_equal(S("abc hello 123"), S("abc HELLO 123").downcase)
+ assert_equal(S("h\0""ello"), S("h\0""ELLO").downcase)
end
def test_downcase!
@@ -763,6 +782,12 @@ CODE
a=S("hello")
assert_nil(a.downcase!)
assert_equal(S("hello"), a)
+
+ a = S("h\0""ELLO")
+ b = a.dup
+ assert_equal(S("h\0""ello"), a.downcase!)
+ assert_equal(S("h\0""ello"), a)
+ assert_equal(S("h\0""ELLO"), b)
end
def test_dump
@@ -859,6 +884,8 @@ CODE
end
def test_each
+ verbose, $VERBOSE = $VERBOSE, nil
+
save = $/
$/ = "\n"
res=[]
@@ -878,6 +905,7 @@ CODE
assert_equal(S("world"), res[1])
ensure
$/ = save
+ $VERBOSE = verbose
end
def test_each_byte
@@ -896,21 +924,15 @@ CODE
s = S("ABC")
assert_equal [65, 66, 67], s.bytes
- if ENUMERATOR_WANTARRAY
- assert_warn(/block not used/) {
- assert_equal [65, 66, 67], s.bytes {}
- }
- else
- res = []
- assert_equal s.object_id, s.bytes {|x| res << x }.object_id
- assert_equal(65, res[0])
- assert_equal(66, res[1])
- assert_equal(67, res[2])
- s = S("ABC")
- res = []
- assert_same s, s.bytes {|x| res << x }
- assert_equal [65, 66, 67], res
- end
+ res = []
+ assert_equal s.object_id, s.bytes {|x| res << x }.object_id
+ assert_equal(65, res[0])
+ assert_equal(66, res[1])
+ assert_equal(67, res[2])
+ s = S("ABC")
+ res = []
+ assert_same s, s.bytes {|x| res << x }
+ assert_equal [65, 66, 67], res
end
def test_each_codepoint
@@ -935,21 +957,15 @@ CODE
s = S("\u3042\u3044\u3046")
assert_equal [0x3042, 0x3044, 0x3046], s.codepoints
- if ENUMERATOR_WANTARRAY
- assert_warn(/block not used/) {
- assert_equal [0x3042, 0x3044, 0x3046], s.codepoints {}
- }
- else
- res = []
- assert_equal s.object_id, s.codepoints {|x| res << x }.object_id
- assert_equal(0x3042, res[0])
- assert_equal(0x3044, res[1])
- assert_equal(0x3046, res[2])
- s = S("ABC")
- res = []
- assert_same s, s.codepoints {|x| res << x }
- assert_equal [65, 66, 67], res
- end
+ res = []
+ assert_equal s.object_id, s.codepoints {|x| res << x }.object_id
+ assert_equal(0x3042, res[0])
+ assert_equal(0x3044, res[1])
+ assert_equal(0x3046, res[2])
+ s = S("ABC")
+ res = []
+ assert_same s, s.codepoints {|x| res << x }
+ assert_equal [65, 66, 67], res
end
def test_each_char
@@ -968,17 +984,11 @@ CODE
s = S("ABC")
assert_equal ["A", "B", "C"], s.chars
- if ENUMERATOR_WANTARRAY
- assert_warn(/block not used/) {
- assert_equal ["A", "B", "C"], s.chars {}
- }
- else
- res = []
- assert_equal s.object_id, s.chars {|x| res << x }.object_id
- assert_equal("A", res[0])
- assert_equal("B", res[1])
- assert_equal("C", res[2])
- end
+ res = []
+ assert_equal s.object_id, s.chars {|x| res << x }.object_id
+ assert_equal("A", res[0])
+ assert_equal("B", res[1])
+ assert_equal("C", res[2])
end
def test_each_grapheme_cluster
@@ -1039,22 +1049,18 @@ CODE
end
assert_equal ["a", "b", "c"], "abc".b.grapheme_clusters
- if ENUMERATOR_WANTARRAY
- assert_warn(/block not used/) {
- assert_equal ["A", "B", "C"], "ABC".grapheme_clusters {}
- }
- else
- s = "ABC".b
- res = []
- assert_same s, s.grapheme_clusters {|x| res << x }
- assert_equal(3, res.size)
- assert_equal("A", res[0])
- assert_equal("B", res[1])
- assert_equal("C", res[2])
- end
+ s = "ABC".b
+ res = []
+ assert_same s, s.grapheme_clusters {|x| res << x }
+ assert_equal(3, res.size)
+ assert_equal("A", res[0])
+ assert_equal("B", res[1])
+ assert_equal("C", res[2])
end
def test_each_line
+ verbose, $VERBOSE = $VERBOSE, nil
+
save = $/
$/ = "\n"
res=[]
@@ -1101,6 +1107,7 @@ CODE
end
ensure
$/ = save
+ $VERBOSE = verbose
end
def test_each_line_chomp
@@ -1158,16 +1165,10 @@ CODE
assert_equal ["hello\n", "world"], s.lines
assert_equal ["hello\nworld"], s.lines(nil)
- if ENUMERATOR_WANTARRAY
- assert_warn(/block not used/) {
- assert_equal ["hello\n", "world"], s.lines {}
- }
- else
- res = []
- assert_equal s.object_id, s.lines {|x| res << x }.object_id
- assert_equal(S("hello\n"), res[0])
- assert_equal(S("world"), res[1])
- end
+ res = []
+ assert_equal s.object_id, s.lines {|x| res << x }.object_id
+ assert_equal(S("hello\n"), res[0])
+ assert_equal(S("world"), res[1])
end
def test_empty?
@@ -1179,6 +1180,8 @@ CODE
assert_send([S("hello"), :end_with?, S("llo")])
assert_not_send([S("hello"), :end_with?, S("ll")])
assert_send([S("hello"), :end_with?, S("el"), S("lo")])
+ assert_send([S("hello"), :end_with?, S("")])
+ assert_not_send([S("hello"), :end_with?])
bug5536 = '[ruby-core:40623]'
assert_raise(TypeError, bug5536) {S("str").end_with? :not_convertible_to_string}
@@ -1331,6 +1334,8 @@ CODE
assert_nil("foo".index(//, -100))
assert_nil($~)
+
+ assert_equal(2, S("abcdbce").index(/b\Kc/))
end
def test_insert
@@ -1503,6 +1508,8 @@ CODE
assert_equal(3, "foo".rindex(//))
assert_equal([3, 3], $~.offset(0))
+
+ assert_equal(5, S("abcdbce").rindex(/b\Kc/))
end
def test_rjust
@@ -1594,8 +1601,10 @@ CODE
a = S("FooBar")
if @aref_slicebang_silent
assert_nil( a.slice!(6) )
+ assert_nil( a.slice!(6r) )
else
assert_raise(IndexError) { a.slice!(6) }
+ assert_raise(IndexError) { a.slice!(6r) }
end
assert_equal(S("FooBar"), a)
@@ -1768,13 +1777,6 @@ CODE
GC.start
assert_equal([], "".split, bug)
end;
-
- begin
- fs = $;
- assert_warn(/\$; will be deprecated/) {$; = " "}
- ensure
- EnvUtil.suppress_warning {$; = fs}
- end
end
def test_split_encoding
@@ -1816,6 +1818,11 @@ CODE
assert_equal("abc", s)
end
+ def test_split_lookbehind
+ assert_equal([S("ab"), S("d")], S("abcd").split(/(?<=b)c/))
+ assert_equal([S("ab"), S("d")], S("abcd").split(/b\Kc/))
+ end
+
def test_squeeze
assert_equal(S("abc"), S("aaabbbbccc").squeeze)
assert_equal(S("aa bb cc"), S("aa bb cc").squeeze(S(" ")))
@@ -1858,6 +1865,7 @@ CODE
def test_strip
assert_equal(S("x"), S(" x ").strip)
assert_equal(S("x"), S(" \n\r\t x \t\r\n\n ").strip)
+ assert_equal(S("x"), S("\x00x\x00").strip)
assert_equal("0b0 ".force_encoding("UTF-16BE"),
"\x00 0b0 ".force_encoding("UTF-16BE").strip)
@@ -1876,6 +1884,10 @@ CODE
assert_equal(S("x"), a.strip!)
assert_equal(S("x"), a)
+ a = S("\x00x\x00")
+ assert_equal(S("x"), a.strip!)
+ assert_equal(S("x"), a)
+
a = S("x")
assert_nil(a.strip!)
assert_equal(S("x") ,a)
@@ -2095,6 +2107,8 @@ CODE
def test_swapcase
assert_equal(S("hi&LOW"), S("HI&low").swapcase)
+ s = S("")
+ assert_not_same(s, s.swapcase)
end
def test_swapcase!
@@ -2330,6 +2344,7 @@ CODE
assert_equal(S("HELLO"), S("hello").upcase)
assert_equal(S("HELLO"), S("HELLO").upcase)
assert_equal(S("ABC HELLO 123"), S("abc HELLO 123").upcase)
+ assert_equal(S("H\0""ELLO"), S("H\0""ello").upcase)
end
def test_upcase!
@@ -2342,6 +2357,12 @@ CODE
a = S("HELLO")
assert_nil(a.upcase!)
assert_equal(S("HELLO"), a)
+
+ a = S("H\0""ello")
+ b = a.dup
+ assert_equal(S("H\0""ELLO"), a.upcase!)
+ assert_equal(S("H\0""ELLO"), a)
+ assert_equal(S("H\0""ello"), b)
end
def test_upto
@@ -2567,6 +2588,10 @@ CODE
hello = "hello"
hello.partition("hi").map(&:upcase!)
assert_equal("hello", hello, bug)
+
+ assert_equal(["", "", "foo"], "foo".partition(/^=*/))
+
+ assert_equal([S("ab"), S("c"), S("dbce")], S("abcdbce").partition(/b\Kc/))
end
def test_rpartition
@@ -2591,6 +2616,8 @@ CODE
hello = "hello"
hello.rpartition("hi").map(&:upcase!)
assert_equal("hello", hello, bug)
+
+ assert_equal([S("abcdb"), S("c"), S("e")], S("abcdbce").rpartition(/b\Kc/))
end
def test_setter
@@ -2658,6 +2685,7 @@ CODE
assert_equal(1, "FoO".casecmp("BaR"))
assert_equal(-1, "baR".casecmp("FoO"))
assert_equal(1, "\u3042B".casecmp("\u3042a"))
+ assert_equal(-1, "foo".casecmp("foo\0"))
assert_nil("foo".casecmp(:foo))
assert_nil("foo".casecmp(Object.new))
@@ -2672,6 +2700,7 @@ CODE
assert_equal(false, 'FoO'.casecmp?('BaR'))
assert_equal(false, 'baR'.casecmp?('FoO'))
assert_equal(true, 'äöü'.casecmp?('ÄÖÜ'))
+ assert_equal(false, "foo".casecmp?("foo\0"))
assert_nil("foo".casecmp?(:foo))
assert_nil("foo".casecmp?(Object.new))
@@ -2692,6 +2721,7 @@ CODE
def test_rstrip
assert_equal(" hello", " hello ".rstrip)
assert_equal("\u3042", "\u3042 ".rstrip)
+ assert_equal("\u3042", "\u3042\u0000".rstrip)
assert_raise(Encoding::CompatibilityError) { "\u3042".encode("ISO-2022-JP").rstrip }
end
@@ -2712,12 +2742,17 @@ CODE
assert_equal(nil, s4.rstrip!)
assert_equal("\u3042", s4)
+ s5 = S("\u3042\u0000")
+ assert_equal("\u3042", s5.rstrip!)
+ assert_equal("\u3042", s5)
+
assert_raise(Encoding::CompatibilityError) { "\u3042".encode("ISO-2022-JP").rstrip! }
end
def test_lstrip
assert_equal("hello ", " hello ".lstrip)
assert_equal("\u3042", " \u3042".lstrip)
+ assert_equal("hello ", "\x00hello ".lstrip)
end
def test_lstrip_bang
@@ -2736,6 +2771,11 @@ CODE
s4 = S("\u3042")
assert_equal(nil, s4.lstrip!)
assert_equal("\u3042", s4)
+
+ s5 = S("\u0000\u3042")
+ assert_equal("\u3042", s5.lstrip!)
+ assert_equal("\u3042", s5)
+
end
def test_delete_prefix
@@ -3141,6 +3181,22 @@ CODE
assert_same(str, -bar, "uminus deduplicates [Feature #13077]")
end
+ def test_uminus_frozen
+ # embedded
+ str1 = ("foobar" * 3).freeze
+ str2 = ("foobar" * 3).freeze
+ assert_not_same str1, str2
+ assert_same str1, -str1
+ assert_same str1, -str2
+
+ # regular
+ str1 = ("foobar" * 4).freeze
+ str2 = ("foobar" * 4).freeze
+ assert_not_same str1, str2
+ assert_same str1, -str1
+ assert_same str1, -str2
+ end
+
def test_uminus_no_freeze_not_bare
str = @cls.new("foo")
assert_instance_of(@cls, -str)
@@ -3176,6 +3232,12 @@ CODE
assert_not_predicate(data, :valid_encoding?)
assert_predicate(data[100..-1], :valid_encoding?)
end
+
+ def test_slice_bang_code_range
+ str = "[Bug #19739] ABC OÜ"
+ str.slice!(/ oü$/i)
+ assert_predicate str, :ascii_only?
+ end
end
class TestString2 < TestString
diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb
index 884fbe00ed..176e2ac5de 100644
--- a/test/ruby/test_struct.rb
+++ b/test/ruby/test_struct.rb
@@ -23,6 +23,10 @@ module TestStruct
test.bar = 47
assert_equal(47, test.bar)
+
+ @Struct.class_eval do
+ remove_const :Test
+ end
end
# [ruby-dev:26247] more than 10 struct members causes segmentation fault
@@ -113,11 +117,10 @@ module TestStruct
assert_equal @Struct::KeywordInitTrue.new(a: 1, b: 2).values, @Struct::KeywordInitFalse.new(1, 2).values
assert_equal "#{@Struct}::KeywordInitFalse", @Struct::KeywordInitFalse.inspect
assert_equal "#{@Struct}::KeywordInitTrue(keyword_init: true)", @Struct::KeywordInitTrue.inspect
- # eval is neede to prevent the warning duplication filter
- k = eval("Class.new(@Struct::KeywordInitFalse) {def initialize(**) end}")
- assert_warn(/The last argument is used as the keyword parameter/) {k.new(a: 1, b: 2)}
- k = Class.new(@Struct::KeywordInitTrue) {def initialize(**) end}
- assert_warn('') {k.new(a: 1, b: 2)}
+ # eval is needed to prevent the warning duplication filter
+ k = Class.new(@Struct::KeywordInitTrue) {def initialize(b, options); super(a: options, b: b); end}
+ o = assert_warn('') { k.new(42, {foo: 1, bar: 2}) }
+ assert_equal(1, o.a[:foo])
@Struct.instance_eval do
remove_const(:KeywordInitTrue)
@@ -135,6 +138,17 @@ module TestStruct
assert_equal(3, struct.new(a: 1, b: 2).c)
end
+ def test_struct_keyword_init_p
+ struct = @Struct.new(:a, :b, keyword_init: true)
+ assert_equal(true, struct.keyword_init?)
+
+ struct = @Struct.new(:a, :b, keyword_init: false)
+ assert_equal(false, struct.keyword_init?)
+
+ struct = @Struct.new(:a, :b)
+ assert_nil(struct.keyword_init?)
+ end
+
def test_initialize
klass = @Struct.new(:a)
assert_raise(ArgumentError) { klass.new(1, 2) }
@@ -146,6 +160,17 @@ module TestStruct
assert_equal 3, klass.new(1,2).total
end
+ def test_initialize_with_kw
+ klass = @Struct.new(:foo, :options) do
+ def initialize(foo, **options)
+ super(foo, options)
+ end
+ end
+ assert_equal({}, klass.new(42, **Hash.new).options)
+ x = assert_warn('') { klass.new(1, bar: 2) }
+ assert_equal 2, x.options[:bar]
+ end
+
def test_each
klass = @Struct.new(:a, :b)
o = klass.new(1, 2)
@@ -325,15 +350,31 @@ module TestStruct
end
def test_redefinition_warning
- @Struct.new("RedefinitionWarning")
+ @Struct.new(name = "RedefinitionWarning")
e = EnvUtil.verbose_warning do
@Struct.new("RedefinitionWarning")
end
assert_match(/redefining constant #@Struct::RedefinitionWarning/, e)
+
+ @Struct.class_eval do
+ remove_const name
+ end
+ end
+
+ def test_keyword_args_warning
+ warning = /warning: Passing only keyword arguments to Struct#initialize will behave differently from Ruby 3\.2\./
+ assert_warn(warning) { assert_equal({a: 1}, @Struct.new(:a).new(a: 1).a) }
+ assert_warn(warning) { assert_equal({a: 1}, @Struct.new(:a, keyword_init: nil).new(a: 1).a) }
+ assert_warn('') { assert_equal({a: 1}, @Struct.new(:a).new({a: 1}).a) }
+ assert_warn('') { assert_equal({a: 1}, @Struct.new(:a, :b).new(1, a: 1).b) }
+ assert_warn('') { assert_equal(1, @Struct.new(:a, keyword_init: true).new(a: 1).a) }
+ assert_warn('') { assert_equal({a: 1}, @Struct.new(:a, keyword_init: nil).new({a: 1}).a) }
+ assert_warn('') { assert_equal({a: 1}, @Struct.new(:a, keyword_init: false).new(a: 1).a) }
+ assert_warn('') { assert_equal({a: 1}, @Struct.new(:a, keyword_init: false).new({a: 1}).a) }
end
def test_nonascii
- struct_test = @Struct.new("R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}")
+ struct_test = @Struct.new(name = "R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}")
assert_equal(@Struct.const_get("R\u{e9}sum\u{e9}"), struct_test, '[ruby-core:24849]')
a = struct_test.new(42)
assert_equal("#<struct #@Struct::R\u{e9}sum\u{e9} r\u{e9}sum\u{e9}=42>", a.inspect, '[ruby-core:24849]')
@@ -343,6 +384,10 @@ module TestStruct
assert_nothing_raised(Encoding::CompatibilityError) do
assert_match(/redefining constant #@Struct::R\u{e9}sum\u{e9}/, e)
end
+
+ @Struct.class_eval do
+ remove_const name
+ end
end
def test_junk
@@ -444,6 +489,43 @@ module TestStruct
}
end
+ def test_public_send
+ klass = @Struct.new(:a)
+ x = klass.new(1)
+ assert_equal(1, x.public_send("a"))
+ assert_equal(42, x.public_send("a=", 42))
+ assert_equal(42, x.public_send("a"))
+ end
+
+ def test_arity
+ klass = @Struct.new(:a)
+ assert_equal 0, klass.instance_method(:a).arity
+ assert_equal 1, klass.instance_method(:a=).arity
+
+ klass.module_eval do
+ define_method(:b=, instance_method(:a=))
+ alias c= a=
+ end
+
+ assert_equal 1, klass.instance_method(:b=).arity
+ assert_equal 1, klass.instance_method(:c=).arity
+ end
+
+ def test_parameters
+ klass = @Struct.new(:a)
+ assert_equal [], klass.instance_method(:a).parameters
+ # NOTE: :_ may not be a spec.
+ assert_equal [[:req, :_]], klass.instance_method(:a=).parameters
+
+ klass.module_eval do
+ define_method(:b=, instance_method(:a=))
+ alias c= a=
+ end
+
+ assert_equal [[:req, :_]], klass.instance_method(:b=).parameters
+ assert_equal [[:req, :_]], klass.instance_method(:c=).parameters
+ end
+
class TopStruct < Test::Unit::TestCase
include TestStruct
diff --git a/test/ruby/test_super.rb b/test/ruby/test_super.rb
index bbfc581500..6a575b88c5 100644
--- a/test/ruby/test_super.rb
+++ b/test/ruby/test_super.rb
@@ -521,6 +521,43 @@ class TestSuper < Test::Unit::TestCase
assert_equal(%w[B A], result, bug9721)
end
+ # [Bug #18329]
+ def test_super_missing_prepended_module
+ a = Module.new do
+ def probe(*methods)
+ prepend(probing_module(methods))
+ end
+
+ def probing_module(methods)
+ Module.new do
+ methods.each do |method|
+ define_method(method) do |*args, **kwargs, &block|
+ super(*args, **kwargs, &block)
+ end
+ end
+ end
+ end
+ end
+
+ b = Class.new do
+ extend a
+
+ probe :danger!, :missing
+
+ def danger!; end
+ end
+
+ o = b.new
+ o.danger!
+ begin
+ original_gc_stress = GC.stress
+ GC.stress = true
+ 2.times { o.missing rescue NoMethodError }
+ ensure
+ GC.stress = original_gc_stress
+ end
+ end
+
def test_from_eval
bug10263 = '[ruby-core:65122] [Bug #10263a]'
a = Class.new do
@@ -583,4 +620,81 @@ class TestSuper < Test::Unit::TestCase
def test_super_with_modified_rest_parameter
assert_equal [13], TestFor_super_with_modified_rest_parameter.new.foo
end
+
+ def test_super_with_define_method
+ superklass1 = Class.new do
+ def foo; :foo; end
+ def bar; :bar; end
+ def boo; :boo; end
+ end
+ superklass2 = Class.new(superklass1) do
+ alias baz boo
+ def boo; :boo2; end
+ end
+ subklass = Class.new(superklass2)
+ [:foo, :bar, :baz, :boo].each do |sym|
+ subklass.define_method(sym){ super() }
+ end
+ assert_equal :foo, subklass.new.foo
+ assert_equal :bar, subklass.new.bar
+ assert_equal :boo, subklass.new.baz
+ assert_equal :boo2, subklass.new.boo
+ end
+
+ def test_super_attr_writer # [Bug #16785]
+ writer_class = Class.new do
+ attr_writer :test
+ end
+ superwriter_class = Class.new(writer_class) do
+ def initialize
+ @test = 1 # index: 1
+ end
+
+ def test=(test)
+ super(test)
+ end
+ end
+ inherited_class = Class.new(superwriter_class) do
+ def initialize
+ @a = nil
+ @test = 2 # index: 2
+ end
+ end
+
+ superwriter = superwriter_class.new
+ superwriter.test = 3 # set ic->index of superwriter_class#test= to 1
+
+ inherited = inherited_class.new
+ inherited.test = 4 # it may set 4 to index=1 while it should be index=2
+
+ assert_equal 3, superwriter.instance_variable_get(:@test)
+ assert_equal 4, inherited.instance_variable_get(:@test)
+ end
+
+ def test_super_attr_reader
+ reader_class = Class.new do
+ attr_reader :test
+ end
+ superreader_class = Class.new(reader_class) do
+ def initialize
+ @test = 1 # index: 1
+ end
+
+ def test
+ super
+ end
+ end
+ inherited_class = Class.new(superreader_class) do
+ def initialize
+ @a = nil
+ @test = 2 # index: 2
+ end
+ end
+
+ superreader = superreader_class.new
+ assert_equal 1, superreader.test # set ic->index of superreader_class#test to 1
+
+ inherited = inherited_class.new
+ assert_equal 2, inherited.test # it may read index=1 while it should be index=2
+ end
end
diff --git a/test/ruby/test_symbol.rb b/test/ruby/test_symbol.rb
index 660f2e1574..f7f17b8d67 100644
--- a/test/ruby/test_symbol.rb
+++ b/test/ruby/test_symbol.rb
@@ -105,6 +105,12 @@ class TestSymbol < Test::Unit::TestCase
end
end
+ def test_name
+ assert_equal("foo", :foo.name)
+ assert_same(:foo.name, :foo.name)
+ assert_predicate(:foo.name, :frozen?)
+ end
+
def test_to_proc
assert_equal %w(1 2 3), (1..3).map(&:to_s)
[
@@ -153,6 +159,14 @@ class TestSymbol < Test::Unit::TestCase
end;
end
+ def test_to_proc_lambda?
+ assert_predicate(:itself.to_proc, :lambda?)
+ end
+
+ def test_to_proc_arity
+ assert_equal(-2, :itself.to_proc.arity)
+ end
+
def test_to_proc_call_with_symbol_proc
first = 1
bug11594 = "[ruby-core:71088] [Bug #11594] corrupted the first local variable"
@@ -165,6 +179,9 @@ class TestSymbol < Test::Unit::TestCase
def _test_to_proc_arg_with_refinements_call(&block)
block.call TestToPRocArgWithRefinements.new
end
+ def _test_to_proc_with_refinements_call(&block)
+ block
+ end
using Module.new {
refine TestToPRocArgWithRefinements do
def hoge
@@ -176,6 +193,14 @@ class TestSymbol < Test::Unit::TestCase
assert_equal(:hoge, _test_to_proc_arg_with_refinements_call(&:hoge))
end
+ def test_to_proc_lambda_with_refinements
+ assert_predicate(_test_to_proc_with_refinements_call(&:hoge), :lambda?)
+ end
+
+ def test_to_proc_arity_with_refinements
+ assert_equal(-2, _test_to_proc_with_refinements_call(&:hoge).arity)
+ end
+
def self._test_to_proc_arg_with_refinements_call(&block)
block.call TestToPRocArgWithRefinements.new
end
@@ -219,11 +244,11 @@ class TestSymbol < Test::Unit::TestCase
begin;
bug11845 = '[ruby-core:72381] [Bug #11845]'
assert_nil(:class.to_proc.source_location, bug11845)
- assert_equal([[:rest]], :class.to_proc.parameters, bug11845)
+ assert_equal([[:req], [:rest]], :class.to_proc.parameters, bug11845)
c = Class.new {define_method(:klass, :class.to_proc)}
m = c.instance_method(:klass)
assert_nil(m.source_location, bug11845)
- assert_equal([[:rest]], m.parameters, bug11845)
+ assert_equal([[:req], [:rest]], m.parameters, bug11845)
end;
end
@@ -504,12 +529,14 @@ class TestSymbol < Test::Unit::TestCase
assert_nothing_raised(NoMethodError, bug10259) {obj.send("unagi=".intern, 1)}
end
- def test_symbol_fstr_leak
+ def test_symbol_fstr_memory_leak
bug10686 = '[ruby-core:67268] [Bug #10686]'
- x = x = 0
- assert_no_memory_leak([], '200_000.times { |i| i.to_s.to_sym }; GC.start', "#{<<-"begin;"}\n#{<<-"end;"}", bug10686, limit: 1.71, rss: true, timeout: 20)
+ assert_no_memory_leak([], "#{<<~"begin;"}\n#{<<~'else;'}", "#{<<~'end;'}", bug10686, limit: 1.71, rss: true, timeout: 20)
begin;
- 200_000.times { |i| (i + 200_000).to_s.to_sym }
+ n = 100_000
+ n.times { |i| i.to_s.to_sym }
+ else;
+ (2 * n).times { |i| (i + n).to_s.to_sym }
end;
end
diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb
index 286beb7074..53036cab3b 100644
--- a/test/ruby/test_syntax.rb
+++ b/test/ruby/test_syntax.rb
@@ -66,6 +66,81 @@ class TestSyntax < Test::Unit::TestCase
f&.close!
end
+ def test_script_lines_encoding
+ require 'tmpdir'
+ Dir.mktmpdir do |dir|
+ File.write(File.join(dir, "script_lines.rb"), "SCRIPT_LINES__ = {}\n")
+ assert_in_out_err(%w"-r./script_lines -w -Ke", "puts __ENCODING__.name",
+ %w"EUC-JP", /-K is specified/, chdir: dir)
+ end
+ end
+
+ def test_anonymous_block_forwarding
+ assert_syntax_error("def b; c(&); end", /no anonymous block parameter/)
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ def b(&); c(&) end
+ def c(&); yield 1 end
+ a = nil
+ b{|c| a = c}
+ assert_equal(1, a)
+
+ def inner
+ yield
+ end
+
+ def block_only(&)
+ inner(&)
+ end
+ assert_equal(1, block_only{1})
+
+ def pos(arg1, &)
+ inner(&)
+ end
+ assert_equal(2, pos(nil){2})
+
+ def pos_kwrest(arg1, **kw, &)
+ inner(&)
+ end
+ assert_equal(3, pos_kwrest(nil){3})
+
+ def no_kw(arg1, **nil, &)
+ inner(&)
+ end
+ assert_equal(4, no_kw(nil){4})
+
+ def rest_kw(*a, kwarg: 1, &)
+ inner(&)
+ end
+ assert_equal(5, rest_kw{5})
+
+ def kw(kwarg:1, &)
+ inner(&)
+ end
+ assert_equal(6, kw{6})
+
+ def pos_kw_kwrest(arg1, kwarg:1, **kw, &)
+ inner(&)
+ end
+ assert_equal(7, pos_kw_kwrest(nil){7})
+
+ def pos_rkw(arg1, kwarg1:, &)
+ inner(&)
+ end
+ assert_equal(8, pos_rkw(nil, kwarg1: nil){8})
+
+ def all(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, &)
+ inner(&)
+ end
+ assert_equal(9, all(nil, nil, nil, nil, okw1: nil, okw2: nil){9})
+
+ def all_kwrest(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, **kw, &)
+ inner(&)
+ end
+ assert_equal(10, all_kwrest(nil, nil, nil, nil, okw1: nil, okw2: nil){10})
+ end;
+ end
+
def test_newline_in_block_parameters
bug = '[ruby-dev:45292]'
["", "a", "a, b"].product(["", ";x", [";", "x"]]) do |params|
@@ -93,6 +168,17 @@ class TestSyntax < Test::Unit::TestCase
assert_valid_syntax("tap (proc do end)", __FILE__, bug9726)
end
+ def test_hash_kwsplat_hash
+ kw = {}
+ h = {a: 1}
+ assert_equal({}, {**{}})
+ assert_equal({}, {**kw})
+ assert_equal(h, {**h})
+ assert_equal(false, {**{}}.frozen?)
+ assert_equal(false, {**kw}.equal?(kw))
+ assert_equal(false, {**h}.equal?(h))
+ end
+
def test_array_kwsplat_hash
kw = {}
h = {a: 1}
@@ -182,26 +268,36 @@ class TestSyntax < Test::Unit::TestCase
h = {k3: 31}
assert_raise(ArgumentError) {o.kw(**h)}
h = {"k1"=>11, k2: 12}
- assert_warn(/The last argument is split into positional and keyword parameters.* for `kw'/m) do
- assert_raise(ArgumentError) {o.kw(**h)}
- end
+ assert_raise(ArgumentError) {o.kw(**h)}
end
def test_keyword_duplicated
bug10315 = '[ruby-core:65625] [Bug #10315]'
a = []
def a.add(x) push(x); x; end
- def a.f(k:) k; end
+ b = a.clone
+ def a.f(k:, **) k; end
+ def b.f(k:) k; end
a.clear
r = nil
- assert_warn(/duplicated/) {r = eval("a.f(k: a.add(1), k: a.add(2))")}
+ assert_warn(/duplicated/) {r = eval("b.f(k: b.add(1), k: b.add(2))")}
assert_equal(2, r)
- assert_equal([1, 2], a, bug10315)
+ assert_equal([1, 2], b, bug10315)
+ b.clear
+ r = nil
+ assert_warn(/duplicated/) {r = eval("a.f(k: a.add(1), j: a.add(2), k: a.add(3), k: a.add(4))")}
+ assert_equal(4, r)
+ assert_equal([1, 2, 3, 4], a)
a.clear
r = nil
- assert_warn(/duplicated/) {r = eval("a.f({k: a.add(1), k: a.add(2)})")}
+ assert_warn(/duplicated/) {r = eval("b.f(**{k: b.add(1), k: b.add(2)})")}
assert_equal(2, r)
- assert_equal([1, 2], a, bug10315)
+ assert_equal([1, 2], b, bug10315)
+ b.clear
+ r = nil
+ assert_warn(/duplicated/) {r = eval("a.f(**{k: a.add(1), j: a.add(2), k: a.add(3), k: a.add(4)})")}
+ assert_equal(4, r)
+ assert_equal([1, 2, 3, 4], a)
end
def test_keyword_empty_splat
@@ -610,6 +706,11 @@ WARN
def test_do_block_after_lambda
bug11380 = '[ruby-core:70067] [Bug #11380]'
assert_valid_syntax('p -> { :hello }, a: 1 do end', bug11380)
+
+ assert_valid_syntax('->(opt = (foo.[] bar)) {}')
+ assert_valid_syntax('->(opt = (foo.[]= bar)) {}')
+ assert_valid_syntax('->(opt = (foo.[] bar)) do end')
+ assert_valid_syntax('->(opt = (foo.[]= bar)) do end')
end
def test_reserved_method_no_args
@@ -951,9 +1052,14 @@ eom
def test_warning_for_cr
feature8699 = '[ruby-core:56240] [Feature #8699]'
- assert_warning(/encountered \\r/, feature8699) do
- eval("\r""__id__\r")
+ s = assert_warning(/encountered \\r/, feature8699) do
+ eval("'\r'\r")
end
+ assert_equal("\r", s)
+ s = assert_warning('') do
+ eval("'\r'\r\n")
+ end
+ assert_equal("\r", s)
end
def test_unexpected_fraction
@@ -984,6 +1090,19 @@ eom
assert_warn('') {eval("(1...)")}
assert_warn('') {eval("(1...\n2)")}
assert_warn('') {eval("{a: 1...\n2}")}
+
+ assert_warn(/\.\.\. at EOL/) do
+ assert_valid_syntax('foo.[]= ...', verbose: true)
+ end
+ assert_warn(/\.\.\. at EOL/) do
+ assert_valid_syntax('foo.[] ...', verbose: true)
+ end
+ assert_warn(/\.\.\. at EOL/) do
+ assert_syntax_error('foo.[]= bar, ...', /unexpected/, verbose: true)
+ end
+ assert_warn(/\.\.\. at EOL/) do
+ assert_syntax_error('foo.[] bar, ...', /unexpected/, verbose: true)
+ end
end
def test_too_big_nth_ref
@@ -1029,19 +1148,19 @@ eom
end
def test_warning_literal_in_condition
- assert_warn(/literal in condition/) do
+ assert_warn(/string literal in condition/) do
eval('1 if ""')
end
- assert_warn(/literal in condition/) do
+ assert_warn(/regex literal in condition/) do
eval('1 if //')
end
assert_warning(/literal in condition/) do
eval('1 if 1')
end
- assert_warning(/literal in condition/) do
+ assert_warning(/symbol literal in condition/) do
eval('1 if :foo')
end
- assert_warning(/literal in condition/) do
+ assert_warning(/symbol literal in condition/) do
eval('1 if :"#{"foo".upcase}"')
end
@@ -1374,6 +1493,15 @@ eom
assert_nil obj.test
end
+ def test_assignment_return_in_loop
+ obj = Object.new
+ def obj.test
+ x = nil
+ _y = (return until x unless x)
+ end
+ assert_nil obj.test, "[Bug #16695]"
+ end
+
def test_method_call_location
line = __LINE__+5
e = assert_raise(NoMethodError) do
@@ -1400,6 +1528,52 @@ eom
assert_equal(line, e.backtrace_locations[0].lineno)
end
+ def test_methoddef_endless
+ assert_valid_syntax('private def foo = 42')
+ assert_valid_syntax('private def foo() = 42')
+ assert_valid_syntax('private def inc(x) = x + 1')
+ assert_valid_syntax('private def obj.foo = 42')
+ assert_valid_syntax('private def obj.foo() = 42')
+ assert_valid_syntax('private def obj.inc(x) = x + 1')
+ k = Class.new do
+ class_eval('def rescued(x) = raise("to be caught") rescue "instance #{x}"')
+ class_eval('def self.rescued(x) = raise("to be caught") rescue "class #{x}"')
+ end
+ assert_equal("class ok", k.rescued("ok"))
+ assert_equal("instance ok", k.new.rescued("ok"))
+
+ error = /setter method cannot be defined in an endless method definition/
+ assert_syntax_error('def foo=() = 42', error)
+ assert_syntax_error('def obj.foo=() = 42', error)
+ assert_syntax_error('def foo=() = 42 rescue nil', error)
+ assert_syntax_error('def obj.foo=() = 42 rescue nil', error)
+ end
+
+ def test_methoddef_endless_command
+ assert_valid_syntax('def foo = puts "Hello"')
+ assert_valid_syntax('def foo() = puts "Hello"')
+ assert_valid_syntax('def foo(x) = puts x')
+ assert_valid_syntax('def obj.foo = puts "Hello"')
+ assert_valid_syntax('def obj.foo() = puts "Hello"')
+ assert_valid_syntax('def obj.foo(x) = puts x')
+ k = Class.new do
+ class_eval('def rescued(x) = raise "to be caught" rescue "instance #{x}"')
+ class_eval('def self.rescued(x) = raise "to be caught" rescue "class #{x}"')
+ end
+ assert_equal("class ok", k.rescued("ok"))
+ assert_equal("instance ok", k.new.rescued("ok"))
+
+ # Current technical limitation: cannot prepend "private" or something for command endless def
+ error = /syntax error, unexpected string literal/
+ error2 = /syntax error, unexpected local variable or method/
+ assert_syntax_error('private def foo = puts "Hello"', error)
+ assert_syntax_error('private def foo() = puts "Hello"', error)
+ assert_syntax_error('private def foo(x) = puts x', error2)
+ assert_syntax_error('private def obj.foo = puts "Hello"', error)
+ assert_syntax_error('private def obj.foo() = puts "Hello"', error)
+ assert_syntax_error('private def obj.foo(x) = puts x', error2)
+ end
+
def test_methoddef_in_cond
assert_valid_syntax('while def foo; tap do end; end; break; end')
assert_valid_syntax('while def foo a = tap do end; end; break; end')
@@ -1437,8 +1611,12 @@ eom
assert_syntax_error('-> {_1; -> {_2}}', /numbered parameter is already used/)
assert_syntax_error('-> {-> {_1}; _2}', /numbered parameter is already used/)
assert_syntax_error('proc {_1; _1 = nil}', /Can't assign to numbered parameter _1/)
- assert_warn(/`_1' is used as numbered parameter/) {eval('proc {_1 = nil}')}
- assert_warn(/`_2' is used as numbered parameter/) {eval('_2=1')}
+ assert_syntax_error('proc {_1 = nil}', /_1 is reserved for numbered parameter/)
+ assert_syntax_error('_2=1', /_2 is reserved for numbered parameter/)
+ assert_syntax_error('proc {|_3|}', /_3 is reserved for numbered parameter/)
+ assert_syntax_error('def x(_4) end', /_4 is reserved for numbered parameter/)
+ assert_syntax_error('def _5; end', /_5 is reserved for numbered parameter/)
+ assert_syntax_error('def self._6; end', /_6 is reserved for numbered parameter/)
assert_raise_with_message(NameError, /undefined local variable or method `_1'/) {
eval('_1')
}
@@ -1446,6 +1624,14 @@ eom
assert_valid_syntax("->{#{c};->{_1};end;_1}\n")
assert_valid_syntax("->{_1;#{c};->{_1};end}\n")
end
+
+ 1.times {
+ [
+ _1,
+ assert_equal([:a], eval("[:a].map{_1}")),
+ assert_raise(NameError) {eval("_1")},
+ ]
+ }
end
def test_value_expr_in_condition
@@ -1456,13 +1642,33 @@ eom
assert_valid_syntax("tap {a = (break unless true)}")
end
+ def test_tautological_condition
+ assert_valid_syntax("def f() return if false and invalid; nil end")
+ assert_valid_syntax("def f() return unless true or invalid; nil end")
+ end
+
def test_argument_forwarding
assert_valid_syntax('def foo(...) bar(...) end')
assert_valid_syntax('def foo(...) end')
+ assert_valid_syntax('def foo(a, ...) bar(...) end')
+ assert_valid_syntax("def foo ...\n bar(...)\nend")
+ assert_valid_syntax("def foo a, ...\n bar(...)\nend")
+ assert_valid_syntax("def foo b = 1, ...\n bar(...)\nend")
+ assert_valid_syntax("def foo ...; bar(...); end")
+ assert_valid_syntax("def foo a, ...; bar(...); end")
+ assert_valid_syntax("def foo b = 1, ...; bar(...); end")
+ assert_valid_syntax("(def foo ...\n bar(...)\nend)")
+ assert_valid_syntax("(def foo ...; bar(...); end)")
+ assert_valid_syntax('def ==(...) end')
+ assert_valid_syntax('def [](...) end')
+ assert_valid_syntax('def nil(...) end')
+ assert_valid_syntax('def true(...) end')
+ assert_valid_syntax('def false(...) end')
+ unexpected = /unexpected \.{3}/
assert_syntax_error('iter do |...| end', /unexpected/)
assert_syntax_error('iter {|...|}', /unexpected/)
- assert_syntax_error('->... {}', /unexpected/)
- assert_syntax_error('->(...) {}', /unexpected/)
+ assert_syntax_error('->... {}', unexpected)
+ assert_syntax_error('->(...) {}', unexpected)
assert_syntax_error('def foo(x, y, z) bar(...); end', /unexpected/)
assert_syntax_error('def foo(x, y, z) super(...); end', /unexpected/)
assert_syntax_error('def foo(...) yield(...); end', /unexpected/)
@@ -1482,7 +1688,11 @@ eom
[args, kws]
end
end
+ obj4 = obj1.clone
+ obj5 = obj1.clone
obj1.instance_eval('def foo(...) bar(...) end', __FILE__, __LINE__)
+ obj4.instance_eval("def foo ...\n bar(...)\n""end", __FILE__, __LINE__)
+ obj5.instance_eval("def foo ...; bar(...); end", __FILE__, __LINE__)
klass = Class.new {
def foo(*args, **kws, &block)
@@ -1511,25 +1721,177 @@ eom
end
obj3.instance_eval('def foo(...) bar(...) end', __FILE__, __LINE__)
- [obj1, obj2, obj3].each do |obj|
+ [obj1, obj2, obj3, obj4, obj5].each do |obj|
assert_warning('') {
assert_equal([[1, 2, 3], {k1: 4, k2: 5}], obj.foo(1, 2, 3, k1: 4, k2: 5) {|*x| x})
}
assert_warning('') {
assert_equal([[1, 2, 3], {k1: 4, k2: 5}], obj.foo(1, 2, 3, k1: 4, k2: 5))
}
- warning = "warning: The last argument is used as the keyword parameter"
- assert_warning(/\A\z|:(?!#{__LINE__+1})\d+: #{warning}/o) {
- assert_equal([[], {}], obj.foo({}) {|*x| x})
+ array = obj == obj3 ? [] : [{}]
+ assert_warning('') {
+ assert_equal([array, {}], obj.foo({}) {|*x| x})
}
- assert_warning(/\A\z|:(?!#{__LINE__+1})\d+: #{warning}/o) {
- assert_equal([[], {}], obj.foo({}))
+ assert_warning('') {
+ assert_equal([array, {}], obj.foo({}))
}
assert_equal(-1, obj.method(:foo).arity)
parameters = obj.method(:foo).parameters
assert_equal(:rest, parameters.dig(0, 0))
- assert_equal(:block, parameters.dig(1, 0))
+ assert_equal(:keyrest, parameters.dig(1, 0))
+ assert_equal(:block, parameters.dig(2, 0))
+ end
+ end
+
+ def test_argument_forwarding_with_leading_arguments
+ obj = Object.new
+ def obj.bar(*args, **kws, &block)
+ if block
+ block.call(args, kws)
+ else
+ [args, kws]
+ end
end
+ obj.instance_eval('def foo(_a, ...) bar(...) end', __FILE__, __LINE__)
+ assert_equal [[], {}], obj.foo(1)
+ assert_equal [[2], {}], obj.foo(1, 2)
+ assert_equal [[2, 3], {}], obj.foo(1, 2, 3)
+ assert_equal [[], {a: 1}], obj.foo(1, a: 1)
+ assert_equal [[2], {a: 1}], obj.foo(1, 2, a: 1)
+ assert_equal [[2, 3], {a: 1}], obj.foo(1, 2, 3, a: 1)
+ assert_equal [[2, 3], {a: 1}], obj.foo(1, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(...) bar(1, ...) end', __FILE__, __LINE__)
+ assert_equal [[1], {}], obj.foo
+ assert_equal [[1, 1], {}], obj.foo(1)
+ assert_equal [[1, 1, 2], {}], obj.foo(1, 2)
+ assert_equal [[1, 1, 2, 3], {}], obj.foo(1, 2, 3)
+ assert_equal [[1], {a: 1}], obj.foo(a: 1)
+ assert_equal [[1, 1], {a: 1}], obj.foo(1, a: 1)
+ assert_equal [[1, 1, 2], {a: 1}], obj.foo(1, 2, a: 1)
+ assert_equal [[1, 1, 2, 3], {a: 1}], obj.foo(1, 2, 3, a: 1)
+ assert_equal [[1, 1, 2, 3], {a: 1}], obj.foo(1, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(a, ...) bar(a, ...) end', __FILE__, __LINE__)
+ assert_equal [[4], {}], obj.foo(4)
+ assert_equal [[4, 2], {}], obj.foo(4, 2)
+ assert_equal [[4, 2, 3], {}], obj.foo(4, 2, 3)
+ assert_equal [[4], {a: 1}], obj.foo(4, a: 1)
+ assert_equal [[4, 2], {a: 1}], obj.foo(4, 2, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(_a, ...) bar(1, ...) end', __FILE__, __LINE__)
+ assert_equal [[1], {}], obj.foo(4)
+ assert_equal [[1, 2], {}], obj.foo(4, 2)
+ assert_equal [[1, 2, 3], {}], obj.foo(4, 2, 3)
+ assert_equal [[1], {a: 1}], obj.foo(4, a: 1)
+ assert_equal [[1, 2], {a: 1}], obj.foo(4, 2, a: 1)
+ assert_equal [[1, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1)
+ assert_equal [[1, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(_a, _b, ...) bar(...) end', __FILE__, __LINE__)
+ assert_equal [[], {}], obj.foo(4, 5)
+ assert_equal [[2], {}], obj.foo(4, 5, 2)
+ assert_equal [[2, 3], {}], obj.foo(4, 5, 2, 3)
+ assert_equal [[], {a: 1}], obj.foo(4, 5, a: 1)
+ assert_equal [[2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+ assert_equal [[2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+ assert_equal [[2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(_a, _b, ...) bar(1, ...) end', __FILE__, __LINE__)
+ assert_equal [[1], {}], obj.foo(4, 5)
+ assert_equal [[1, 2], {}], obj.foo(4, 5, 2)
+ assert_equal [[1, 2, 3], {}], obj.foo(4, 5, 2, 3)
+ assert_equal [[1], {a: 1}], obj.foo(4, 5, a: 1)
+ assert_equal [[1, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+ assert_equal [[1, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+ assert_equal [[1, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(_a, ...) bar(1, 2, ...) end', __FILE__, __LINE__)
+ assert_equal [[1, 2], {}], obj.foo(5)
+ assert_equal [[1, 2, 5], {}], obj.foo(4, 5)
+ assert_equal [[1, 2, 5, 2], {}], obj.foo(4, 5, 2)
+ assert_equal [[1, 2, 5, 2, 3], {}], obj.foo(4, 5, 2, 3)
+ assert_equal [[1, 2, 5], {a: 1}], obj.foo(4, 5, a: 1)
+ assert_equal [[1, 2, 5, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+ assert_equal [[1, 2, 5, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+ assert_equal [[1, 2, 5, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(a, b, ...) bar(b, a, ...) end', __FILE__, __LINE__)
+ assert_equal [[5, 4], {}], obj.foo(4, 5)
+ assert_equal [[5, 4, 2], {}], obj.foo(4, 5, 2)
+ assert_equal [[5, 4, 2, 3], {}], obj.foo(4, 5, 2, 3)
+ assert_equal [[5, 4], {a: 1}], obj.foo(4, 5, a: 1)
+ assert_equal [[5, 4, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+ assert_equal [[5, 4, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+ assert_equal [[5, 4, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(a, _b, ...) bar(a, ...) end', __FILE__, __LINE__)
+ assert_equal [[4], {}], obj.foo(4, 5)
+ assert_equal [[4, 2], {}], obj.foo(4, 5, 2)
+ assert_equal [[4, 2, 3], {}], obj.foo(4, 5, 2, 3)
+ assert_equal [[4], {a: 1}], obj.foo(4, 5, a: 1)
+ assert_equal [[4, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(a, ...) bar(a, 1, ...) end', __FILE__, __LINE__)
+ assert_equal [[4, 1], {}], obj.foo(4)
+ assert_equal [[4, 1, 5], {}], obj.foo(4, 5)
+ assert_equal [[4, 1, 5, 2], {}], obj.foo(4, 5, 2)
+ assert_equal [[4, 1, 5, 2, 3], {}], obj.foo(4, 5, 2, 3)
+ assert_equal [[4, 1, 5], {a: 1}], obj.foo(4, 5, a: 1)
+ assert_equal [[4, 1, 5, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+ assert_equal [[4, 1, 5, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+ assert_equal [[4, 1, 5, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval("def foo a, ...\n bar(a, ...)\n"" end", __FILE__, __LINE__)
+ assert_equal [[4], {}], obj.foo(4)
+ assert_equal [[4, 2], {}], obj.foo(4, 2)
+ assert_equal [[4, 2, 3], {}], obj.foo(4, 2, 3)
+ assert_equal [[4], {a: 1}], obj.foo(4, a: 1)
+ assert_equal [[4, 2], {a: 1}], obj.foo(4, 2, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval("def foo a, ...; bar(a, ...); end", __FILE__, __LINE__)
+ assert_equal [[4], {}], obj.foo(4)
+ assert_equal [[4, 2], {}], obj.foo(4, 2)
+ assert_equal [[4, 2, 3], {}], obj.foo(4, 2, 3)
+ assert_equal [[4], {a: 1}], obj.foo(4, a: 1)
+ assert_equal [[4, 2], {a: 1}], obj.foo(4, 2, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ exp = eval("-> (a: nil) {a...1}")
+ assert_equal 0...1, exp.call(a: 0)
+ end
+
+ def test_cdhash
+ assert_separately([], <<-RUBY)
+ n = case 1 when 2r then false else true end
+ assert_equal(n, true, '[ruby-core:103759] [Bug #17854]')
+ RUBY
+ assert_separately([], <<-RUBY)
+ n = case 3/2r when 1.5r then true else false end
+ assert_equal(n, true, '[ruby-core:103759] [Bug #17854]')
+ RUBY
+ assert_separately([], <<-RUBY)
+ n = case 1i when 1i then true else false end
+ assert_equal(n, true, '[ruby-core:103759] [Bug #17854]')
+ RUBY
end
private
diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb
index bf61a28f4d..41cee124be 100644
--- a/test/ruby/test_thread.rb
+++ b/test/ruby/test_thread.rb
@@ -230,9 +230,17 @@ class TestThread < Test::Unit::TestCase
assert_equal(t1, t3.value)
ensure
- t1&.kill
- t2&.kill
- t3&.kill
+ t1&.kill&.join
+ t2&.kill&.join
+ t3&.kill&.join
+ end
+
+ def test_join_argument_conversion
+ t = Thread.new {}
+ assert_raise(TypeError) {t.join(:foo)}
+
+ limit = Struct.new(:to_f, :count).new(0.05)
+ assert_same(t, t.join(limit))
end
{ 'FIXNUM_MAX' => RbConfig::LIMITS['FIXNUM_MAX'],
@@ -309,7 +317,7 @@ class TestThread < Test::Unit::TestCase
s += 1
end
Thread.pass until t.stop?
- sleep 1 if RubyVM::MJIT.enabled? # t.stop? behaves unexpectedly with --jit-wait
+ sleep 1 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled? # t.stop? behaves unexpectedly with --jit-wait
assert_equal(1, s)
t.wakeup
Thread.pass while t.alive?
@@ -490,6 +498,19 @@ class TestThread < Test::Unit::TestCase
end;
end
+ def test_ignore_deadlock
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ skip "can't trap a signal from another process on Windows"
+ end
+ assert_in_out_err([], <<-INPUT, %w(false :sig), [], :signal=>:INT, timeout: 1, timeout_error: nil)
+ p Thread.ignore_deadlock
+ q = Thread::Queue.new
+ trap(:INT){q.push :sig}
+ Thread.ignore_deadlock = true
+ p q.pop
+ INPUT
+ end
+
def test_status_and_stop_p
a = ::Thread.new {
Thread.current.report_on_exception = false
@@ -617,7 +638,7 @@ class TestThread < Test::Unit::TestCase
Thread.pass until t.stop?
assert_predicate(t, :alive?)
ensure
- t&.kill
+ t&.kill&.join
end
def test_mutex_deadlock
@@ -718,8 +739,8 @@ class TestThread < Test::Unit::TestCase
def make_handle_interrupt_test_thread1 flag
r = []
- ready_q = Queue.new
- done_q = Queue.new
+ ready_q = Thread::Queue.new
+ done_q = Thread::Queue.new
th = Thread.new{
begin
Thread.handle_interrupt(RuntimeError => flag){
@@ -795,14 +816,15 @@ class TestThread < Test::Unit::TestCase
end
def test_handle_interrupt_blocking
- r=:ng
- e=Class.new(Exception)
+ r = nil
+ q = Thread::Queue.new
+ e = Class.new(Exception)
th_s = Thread.current
- th = Thread.start{
+ th = Thread.start {
assert_raise(RuntimeError) {
Thread.handle_interrupt(Object => :on_blocking){
begin
- Thread.pass until r == :wait
+ q.pop
Thread.current.raise RuntimeError, "will raise in sleep"
r = :ok
sleep
@@ -812,15 +834,14 @@ class TestThread < Test::Unit::TestCase
}
}
}
- assert_raise(e) {r = :wait; sleep 0.2}
- th.join
- assert_equal(:ok,r)
+ assert_raise(e) {q << true; th.join}
+ assert_equal(:ok, r)
end
def test_handle_interrupt_and_io
assert_in_out_err([], <<-INPUT, %w(ok), [])
th_waiting = true
- q = Queue.new
+ q = Thread::Queue.new
t = Thread.new {
Thread.current.report_on_exception = false
@@ -1106,7 +1127,7 @@ q.pop
Thread.pass until mutex.locked?
assert_equal(mutex.owned?, false)
ensure
- th&.kill
+ th&.kill&.join
end
end
@@ -1133,7 +1154,9 @@ q.pop
env = {}
env['RUBY_THREAD_VM_STACK_SIZE'] = vm_stack_size.to_s if vm_stack_size
env['RUBY_THREAD_MACHINE_STACK_SIZE'] = machine_stack_size.to_s if machine_stack_size
- out, = EnvUtil.invoke_ruby([env, '-e', script], '', true, true)
+ out, err, status = EnvUtil.invoke_ruby([env, '-e', script], '', true, true)
+ assert_not_predicate(status, :signaled?, err)
+
use_length ? out.length : out
end
@@ -1150,6 +1173,7 @@ q.pop
"0 thread_machine_stack_size")
assert_operator(h_default[:thread_machine_stack_size], :<=, h_large[:thread_machine_stack_size],
"large thread_machine_stack_size")
+ assert_equal("ok", invoke_rec('print :ok', 1024 * 1024 * 100, nil, false))
end
def test_vm_machine_stack_size
@@ -1220,8 +1244,22 @@ q.pop
assert_predicate(status, :success?, bug9751)
end if Process.respond_to?(:fork)
+ def test_fork_value
+ bug18902 = "[Bug #18902]"
+ th = Thread.start { sleep 2 }
+ begin
+ pid = fork do
+ th.value
+ end
+ _, status = Process.wait2(pid)
+ assert_predicate(status, :success?, bug18902)
+ ensure
+ th.kill
+ end
+ end if Process.respond_to?(:fork)
+
def test_fork_while_locked
- m = Mutex.new
+ m = Thread::Mutex.new
thrs = []
3.times do |i|
thrs << Thread.new { m.synchronize { Process.waitpid2(fork{})[1] } }
@@ -1254,7 +1292,7 @@ q.pop
def test_fork_while_mutex_locked_by_forker
skip 'needs fork' unless Process.respond_to?(:fork)
- m = Mutex.new
+ m = Thread::Mutex.new
m.synchronize do
pid = fork do
exit!(2) unless m.locked?
@@ -1320,11 +1358,32 @@ q.pop
assert_equal("foo", c.new {Thread.current.name}.value, bug12290)
end
+ def test_thread_native_thread_id
+ skip "don't support native_thread_id" unless Thread.method_defined?(:native_thread_id)
+ assert_instance_of Integer, Thread.main.native_thread_id
+
+ th1 = Thread.start{sleep}
+
+ # newly created thread which doesn't run yet returns nil or integer
+ assert_include [NilClass, Integer], th1.native_thread_id.class
+
+ Thread.pass until th1.stop?
+
+ # After a thread starts (and execute `sleep`), it returns native_thread_id
+ assert_instance_of Integer, th1.native_thread_id
+
+ th1.wakeup
+ Thread.pass while th1.alive?
+
+ # dead thread returns nil
+ assert_nil th1.native_thread_id
+ end
+
def test_thread_interrupt_for_killed_thread
opts = { timeout: 5, timeout_error: nil }
# prevent SIGABRT from slow shutdown with MJIT
- opts[:reprieve] = 3 if RubyVM::MJIT.enabled?
+ opts[:reprieve] = 3 if defined?(RubyVM::MJIT) && RubyVM::MJIT.enabled?
assert_normal_exit(<<-_end, '[Bug #8996]', **opts)
Thread.report_on_exception = false
diff --git a/test/ruby/test_thread_cv.rb b/test/ruby/test_thread_cv.rb
index 38bcc3b8fa..88733419da 100644
--- a/test/ruby/test_thread_cv.rb
+++ b/test/ruby/test_thread_cv.rb
@@ -13,9 +13,10 @@ class TestThreadConditionVariable < Test::Unit::TestCase
end
def test_condvar_signal_and_wait
- mutex = Mutex.new
- condvar = ConditionVariable.new
+ mutex = Thread::Mutex.new
+ condvar = Thread::ConditionVariable.new
result = []
+ woken = nil
mutex.synchronize do
t = Thread.new do
mutex.synchronize do
@@ -25,18 +26,19 @@ class TestThreadConditionVariable < Test::Unit::TestCase
end
result << 0
- condvar.wait(mutex)
+ woken = condvar.wait(mutex)
result << 2
t.join
end
assert_equal([0, 1, 2], result)
+ assert(woken)
end
def test_condvar_wait_exception_handling
# Calling wait in the only thread running should raise a ThreadError of
# 'stopping only thread'
- mutex = Mutex.new
- condvar = ConditionVariable.new
+ mutex = Thread::Mutex.new
+ condvar = Thread::ConditionVariable.new
locked = false
thread = Thread.new do
@@ -61,8 +63,8 @@ class TestThreadConditionVariable < Test::Unit::TestCase
def test_condvar_wait_and_broadcast
nr_threads = 3
threads = Array.new
- mutex = Mutex.new
- condvar = ConditionVariable.new
+ mutex = Thread::Mutex.new
+ condvar = Thread::ConditionVariable.new
result = []
nr_threads.times do |i|
@@ -87,12 +89,15 @@ class TestThreadConditionVariable < Test::Unit::TestCase
end
assert_equal ["C1", "C1", "C1", "P1", "P2", "C2", "C2", "C2"], result
+ ensure
+ threads.each(&:kill)
+ threads.each(&:join)
end
def test_condvar_wait_deadlock
assert_in_out_err([], <<-INPUT, /\Afatal\nNo live threads left\. Deadlock/, [])
- mutex = Mutex.new
- cv = ConditionVariable.new
+ mutex = Thread::Mutex.new
+ cv = Thread::ConditionVariable.new
klass = nil
mesg = nil
@@ -112,8 +117,8 @@ INPUT
def test_condvar_wait_deadlock_2
nr_threads = 3
threads = Array.new
- mutex = Mutex.new
- condvar = ConditionVariable.new
+ mutex = Thread::Mutex.new
+ condvar = Thread::ConditionVariable.new
nr_threads.times do |i|
if (i != 0)
@@ -136,15 +141,16 @@ INPUT
end
def test_condvar_timed_wait
- mutex = Mutex.new
- condvar = ConditionVariable.new
+ mutex = Thread::Mutex.new
+ condvar = Thread::ConditionVariable.new
timeout = 0.3
locked = false
+ woken = true
t0 = Time.now
mutex.synchronize do
begin
- condvar.wait(mutex, timeout)
+ woken = condvar.wait(mutex, timeout)
ensure
locked = mutex.locked?
end
@@ -154,18 +160,19 @@ INPUT
assert_operator(timeout*0.9, :<, t)
assert(locked)
+ assert_nil(woken)
end
def test_condvar_nolock
- mutex = Mutex.new
- condvar = ConditionVariable.new
+ mutex = Thread::Mutex.new
+ condvar = Thread::ConditionVariable.new
assert_raise(ThreadError) {condvar.wait(mutex)}
end
def test_condvar_nolock_2
- mutex = Mutex.new
- condvar = ConditionVariable.new
+ mutex = Thread::Mutex.new
+ condvar = Thread::ConditionVariable.new
Thread.new do
assert_raise(ThreadError) {condvar.wait(mutex)}
@@ -173,8 +180,8 @@ INPUT
end
def test_condvar_nolock_3
- mutex = Mutex.new
- condvar = ConditionVariable.new
+ mutex = Thread::Mutex.new
+ condvar = Thread::ConditionVariable.new
Thread.new do
assert_raise(ThreadError) {condvar.wait(mutex, 0.1)}
@@ -182,22 +189,22 @@ INPUT
end
def test_condvar_empty_signal
- mutex = Mutex.new
- condvar = ConditionVariable.new
+ mutex = Thread::Mutex.new
+ condvar = Thread::ConditionVariable.new
assert_nothing_raised(Exception) { mutex.synchronize {condvar.signal} }
end
def test_condvar_empty_broadcast
- mutex = Mutex.new
- condvar = ConditionVariable.new
+ mutex = Thread::Mutex.new
+ condvar = Thread::ConditionVariable.new
assert_nothing_raised(Exception) { mutex.synchronize {condvar.broadcast} }
end
def test_dup
bug9440 = '[ruby-core:59961] [Bug #9440]'
- condvar = ConditionVariable.new
+ condvar = Thread::ConditionVariable.new
assert_raise(NoMethodError, bug9440) do
condvar.dup
end
@@ -207,7 +214,7 @@ INPUT
def test_dump
bug9674 = '[ruby-core:61677] [Bug #9674]'
- condvar = ConditionVariable.new
+ condvar = Thread::ConditionVariable.new
assert_raise_with_message(TypeError, /#{ConditionVariable}/, bug9674) do
Marshal.dump(condvar)
end
@@ -219,8 +226,8 @@ INPUT
end
def test_condvar_fork
- mutex = Mutex.new
- condvar = ConditionVariable.new
+ mutex = Thread::Mutex.new
+ condvar = Thread::ConditionVariable.new
thrs = (1..10).map do
Thread.new { mutex.synchronize { condvar.wait(mutex) } }
end
diff --git a/test/ruby/test_thread_queue.rb b/test/ruby/test_thread_queue.rb
index e46f0b2e8e..3fa0eae2c1 100644
--- a/test/ruby/test_thread_queue.rb
+++ b/test/ruby/test_thread_queue.rb
@@ -54,15 +54,37 @@ class TestThreadQueue < Test::Unit::TestCase
assert_equal 0, to_workers.size
end
+ def test_queue_initialize
+ e = Class.new do
+ include Enumerable
+ def initialize(list) @list = list end
+ def each(&block) @list.each(&block) end
+ end
+
+ all_assertions_foreach(nil,
+ [Array, "Array"],
+ [e, "Enumerable"],
+ [Struct.new(:to_a), "Array-like"],
+ ) do |a, type|
+ q = Thread::Queue.new(a.new([1,2,3]))
+ assert_equal(3, q.size, type)
+ assert_not_predicate(q, :empty?, type)
+ assert_equal(1, q.pop, type)
+ assert_equal(2, q.pop, type)
+ assert_equal(3, q.pop, type)
+ assert_predicate(q, :empty?, type)
+ end
+ end
+
def test_sized_queue_initialize
- q = SizedQueue.new(1)
+ q = Thread::SizedQueue.new(1)
assert_equal 1, q.max
- assert_raise(ArgumentError) { SizedQueue.new(0) }
- assert_raise(ArgumentError) { SizedQueue.new(-1) }
+ assert_raise(ArgumentError) { Thread::SizedQueue.new(0) }
+ assert_raise(ArgumentError) { Thread::SizedQueue.new(-1) }
end
def test_sized_queue_assign_max
- q = SizedQueue.new(2)
+ q = Thread::SizedQueue.new(2)
assert_equal(2, q.max)
q.max = 1
assert_equal(1, q.max)
@@ -82,7 +104,7 @@ class TestThreadQueue < Test::Unit::TestCase
end
def test_queue_pop_interrupt
- q = Queue.new
+ q = Thread::Queue.new
t1 = Thread.new { q.pop }
sleep 0.01 until t1.stop?
t1.kill.join
@@ -90,14 +112,14 @@ class TestThreadQueue < Test::Unit::TestCase
end
def test_queue_pop_non_block
- q = Queue.new
+ q = Thread::Queue.new
assert_raise_with_message(ThreadError, /empty/) do
q.pop(true)
end
end
def test_sized_queue_pop_interrupt
- q = SizedQueue.new(1)
+ q = Thread::SizedQueue.new(1)
t1 = Thread.new { q.pop }
sleep 0.01 until t1.stop?
t1.kill.join
@@ -105,14 +127,14 @@ class TestThreadQueue < Test::Unit::TestCase
end
def test_sized_queue_pop_non_block
- q = SizedQueue.new(1)
+ q = Thread::SizedQueue.new(1)
assert_raise_with_message(ThreadError, /empty/) do
q.pop(true)
end
end
def test_sized_queue_push_interrupt
- q = SizedQueue.new(1)
+ q = Thread::SizedQueue.new(1)
q.push(1)
assert_raise_with_message(ThreadError, /full/) do
q.push(2, true)
@@ -120,7 +142,7 @@ class TestThreadQueue < Test::Unit::TestCase
end
def test_sized_queue_push_non_block
- q = SizedQueue.new(1)
+ q = Thread::SizedQueue.new(1)
q.push(1)
t1 = Thread.new { q.push(2) }
sleep 0.01 until t1.stop?
@@ -131,13 +153,13 @@ class TestThreadQueue < Test::Unit::TestCase
def test_thr_kill
bug5343 = '[ruby-core:39634]'
Dir.mktmpdir {|d|
- timeout = 60
+ timeout = EnvUtil.apply_timeout_scale(60)
total_count = 250
begin
assert_normal_exit(<<-"_eom", bug5343, **{:timeout => timeout, :chdir=>d})
#{total_count}.times do |i|
open("test_thr_kill_count", "w") {|f| f.puts i }
- queue = Queue.new
+ queue = Thread::Queue.new
r, w = IO.pipe
th = Thread.start {
queue.push(nil)
@@ -156,20 +178,20 @@ class TestThreadQueue < Test::Unit::TestCase
end
def test_queue_push_return_value
- q = Queue.new
+ q = Thread::Queue.new
retval = q.push(1)
assert_same q, retval
end
def test_queue_clear_return_value
- q = Queue.new
+ q = Thread::Queue.new
retval = q.clear
assert_same q, retval
end
def test_sized_queue_clear
- # Fill queue, then test that SizedQueue#clear wakes up all waiting threads
- sq = SizedQueue.new(2)
+ # Fill queue, then test that Thread::SizedQueue#clear wakes up all waiting threads
+ sq = Thread::SizedQueue.new(2)
2.times { sq << 1 }
t1 = Thread.new do
@@ -190,19 +212,19 @@ class TestThreadQueue < Test::Unit::TestCase
end
def test_sized_queue_push_return_value
- q = SizedQueue.new(1)
+ q = Thread::SizedQueue.new(1)
retval = q.push(1)
assert_same q, retval
end
def test_sized_queue_clear_return_value
- q = SizedQueue.new(1)
+ q = Thread::SizedQueue.new(1)
retval = q.clear
assert_same q, retval
end
def test_sized_queue_throttle
- q = SizedQueue.new(1)
+ q = Thread::SizedQueue.new(1)
i = 0
consumer = Thread.new do
while q.pop
@@ -225,7 +247,7 @@ class TestThreadQueue < Test::Unit::TestCase
end
def test_queue_thread_raise
- q = Queue.new
+ q = Thread::Queue.new
th1 = Thread.new do
begin
q.pop
@@ -255,7 +277,7 @@ class TestThreadQueue < Test::Unit::TestCase
def test_dup
bug9440 = '[ruby-core:59961] [Bug #9440]'
- q = Queue.new
+ q = Thread::Queue.new
assert_raise(NoMethodError, bug9440) do
q.dup
end
@@ -265,12 +287,12 @@ class TestThreadQueue < Test::Unit::TestCase
def test_dump
bug9674 = '[ruby-core:61677] [Bug #9674]'
- q = Queue.new
+ q = Thread::Queue.new
assert_raise_with_message(TypeError, /#{Queue}/, bug9674) do
Marshal.dump(q)
end
- sq = SizedQueue.new(1)
+ sq = Thread::SizedQueue.new(1)
assert_raise_with_message(TypeError, /#{SizedQueue}/, bug9674) do
Marshal.dump(sq)
end
@@ -282,7 +304,7 @@ class TestThreadQueue < Test::Unit::TestCase
end
def test_close
- [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
+ [->{Thread::Queue.new}, ->{Thread::SizedQueue.new 3}].each do |qcreate|
q = qcreate.call
assert_equal false, q.closed?
q << :something
@@ -321,15 +343,15 @@ class TestThreadQueue < Test::Unit::TestCase
end
def test_queue_close_wakeup
- close_wakeup(15, 18){Queue.new}
+ close_wakeup(15, 18){Thread::Queue.new}
end
def test_size_queue_close_wakeup
- close_wakeup(5, 8){SizedQueue.new 9}
+ close_wakeup(5, 8){Thread::SizedQueue.new 9}
end
def test_sized_queue_one_closed_interrupt
- q = SizedQueue.new 1
+ q = Thread::SizedQueue.new 1
q << :one
t1 = Thread.new {
Thread.current.report_on_exception = false
@@ -346,7 +368,7 @@ class TestThreadQueue < Test::Unit::TestCase
# make sure that shutdown state is handled properly by empty? for the non-blocking case
def test_empty_non_blocking
- q = SizedQueue.new 3
+ q = Thread::SizedQueue.new 3
3.times{|i| q << i}
# these all block cos the queue is full
@@ -372,13 +394,13 @@ class TestThreadQueue < Test::Unit::TestCase
end
def test_sized_queue_closed_push_non_blocking
- q = SizedQueue.new 7
+ q = Thread::SizedQueue.new 7
q.close
assert_raise_with_message(ClosedQueueError, /queue closed/){q.push(non_block=true)}
end
def test_blocked_pushers
- q = SizedQueue.new 3
+ q = Thread::SizedQueue.new 3
prod_threads = 6.times.map do |i|
thr = Thread.new{
Thread.current.report_on_exception = false
@@ -424,9 +446,9 @@ class TestThreadQueue < Test::Unit::TestCase
end
def test_deny_pushers
- [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
+ [->{Thread::Queue.new}, ->{Thread::SizedQueue.new 3}].each do |qcreate|
q = qcreate[]
- synq = Queue.new
+ synq = Thread::Queue.new
prod_threads = 20.times.map do |i|
Thread.new {
synq.pop
@@ -444,7 +466,7 @@ class TestThreadQueue < Test::Unit::TestCase
# size should account for waiting pushers during shutdown
def sized_queue_size_close
- q = SizedQueue.new 4
+ q = Thread::SizedQueue.new 4
4.times{|i| q << i}
Thread.new{ q << 5 }
Thread.new{ q << 6 }
@@ -456,7 +478,7 @@ class TestThreadQueue < Test::Unit::TestCase
end
def test_blocked_pushers_empty
- q = SizedQueue.new 3
+ q = Thread::SizedQueue.new 3
prod_threads = 6.times.map do |i|
Thread.new{
Thread.current.report_on_exception = false
@@ -488,14 +510,14 @@ class TestThreadQueue < Test::Unit::TestCase
# test thread wakeup on one-element SizedQueue with close
def test_one_element_sized_queue
- q = SizedQueue.new 1
+ q = Thread::SizedQueue.new 1
t = Thread.new{ q.pop }
q.close
assert_nil t.value
end
def test_close_twice
- [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
+ [->{Thread::Queue.new}, ->{Thread::SizedQueue.new 3}].each do |qcreate|
q = qcreate[]
q.close
assert_nothing_raised(ClosedQueueError){q.close}
@@ -503,7 +525,7 @@ class TestThreadQueue < Test::Unit::TestCase
end
def test_queue_close_multi_multi
- q = SizedQueue.new rand(800..1200)
+ q = Thread::SizedQueue.new rand(800..1200)
count_items = rand(3000..5000)
count_producers = rand(10..20)
@@ -560,14 +582,19 @@ class TestThreadQueue < Test::Unit::TestCase
if ENV['APPVEYOR'] == 'True' && RUBY_PLATFORM.match?(/mswin/)
skip 'This test fails too often on AppVeyor vs140'
end
+ if RUBY_PLATFORM.match?(/mingw/)
+ skip 'This test fails too often on MinGW'
+ end
+
assert_in_out_err([], <<-INPUT, %w(INT INT exit), [])
- q = Queue.new
+ q = Thread::Queue.new
trap(:INT){
q.push 'INT'
}
Thread.new{
loop{
Process.kill :INT, $$
+ sleep 0.1
}
}
puts q.pop
@@ -577,8 +604,8 @@ class TestThreadQueue < Test::Unit::TestCase
end
def test_fork_while_queue_waiting
- q = Queue.new
- sq = SizedQueue.new(1)
+ q = Thread::Queue.new
+ sq = Thread::SizedQueue.new(1)
thq = Thread.new { q.pop }
thsq = Thread.new { sq.pop }
Thread.pass until thq.stop? && thsq.stop?
diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb
index 35e3172fb1..36c79273db 100644
--- a/test/ruby/test_time.rb
+++ b/test/ruby/test_time.rb
@@ -7,7 +7,6 @@ require 'delegate'
class TestTime < Test::Unit::TestCase
def setup
@verbose = $VERBOSE
- $VERBOSE = nil
end
def teardown
@@ -38,8 +37,11 @@ class TestTime < Test::Unit::TestCase
end
def test_new
+ assert_equal(Time.new(2000,1,1,0,0,0), Time.new(2000))
+ assert_equal(Time.new(2000,2,1,0,0,0), Time.new("2000", "Feb"))
assert_equal(Time.utc(2000,2,10), Time.new(2000,2,10, 11,0,0, 3600*11))
assert_equal(Time.utc(2000,2,10), Time.new(2000,2,9, 13,0,0, -3600*11))
+ assert_equal(Time.utc(2000,2,29,23,0,0), Time.new(2000, 3, 1, 0, 0, 0, 3600))
assert_equal(Time.utc(2000,2,10), Time.new(2000,2,10, 11,0,0, "+11:00"))
assert_equal(Rational(1,2), Time.new(2000,2,10, 11,0,5.5, "+11:00").subsec)
bug4090 = '[ruby-dev:42631]'
@@ -47,6 +49,13 @@ class TestTime < Test::Unit::TestCase
t = Time.new(*tm, "-12:00")
assert_equal([2001,2,28,23,59,30,-43200], [t.year, t.month, t.mday, t.hour, t.min, t.sec, t.gmt_offset], bug4090)
assert_raise(ArgumentError) { Time.new(2000,1,1, 0,0,0, "+01:60") }
+ msg = /invalid value for Integer/
+ assert_raise_with_message(ArgumentError, msg) { Time.new(2021, 1, 1, "+09:99") }
+ assert_raise_with_message(ArgumentError, msg) { Time.new(2021, 1, "+09:99") }
+ assert_raise_with_message(ArgumentError, msg) { Time.new(2021, "+09:99") }
+
+ assert_equal([0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], Time.new(2000, 1, 1, 0, 0, 0, "-00:00").to_a)
+ assert_equal([0, 0, 0, 2, 1, 2000, 0, 2, false, "UTC"], Time.new(2000, 1, 1, 24, 0, 0, "-00:00").to_a)
end
def test_time_add()
@@ -108,6 +117,10 @@ class TestTime < Test::Unit::TestCase
assert_equal(78796800, Time.utc(1972, 7, 1, 0, 0, 0).tv_sec)
assert_equal(78796801, Time.utc(1972, 7, 1, 0, 0, 1).tv_sec)
assert_equal(946684800, Time.utc(2000, 1, 1, 0, 0, 0).tv_sec)
+
+ # Giveup to try 2nd test because some state is changed.
+ skip if Test::Unit::Runner.current_repeat_count > 0
+
assert_equal(0x7fffffff, Time.utc(2038, 1, 19, 3, 14, 7).tv_sec)
assert_equal(0x80000000, Time.utc(2038, 1, 19, 3, 14, 8).tv_sec)
else
@@ -236,6 +249,10 @@ class TestTime < Test::Unit::TestCase
assert_equal(1, Time.at(0, 0.001).nsec)
end
+ def test_at_splat
+ assert_equal(Time.at(1, 2), Time.at(*[1, 2]))
+ end
+
def test_at_with_unit
assert_equal(123456789, Time.at(0, 123456789, :nanosecond).nsec)
assert_equal(123456789, Time.at(0, 123456789, :nsec).nsec)
@@ -375,6 +392,11 @@ class TestTime < Test::Unit::TestCase
end
end
+ def test_marshal_broken_month
+ data = "\x04\x08u:\tTime\r\x20\x7c\x1e\xc0\x00\x00\x00\x00"
+ assert_equal(Time.utc(2022, 4, 1), Marshal.load(data))
+ end
+
def test_marshal_distant_past
assert_marshal_roundtrip(Time.utc(1890, 1, 1))
assert_marshal_roundtrip(Time.utc(-4.5e9, 1, 1))
@@ -417,8 +439,10 @@ class TestTime < Test::Unit::TestCase
def o.to_int; 0; end
def o.to_r; nil; end
assert_raise(TypeError) { Time.gm(2000, 1, 1, 0, 0, o, :foo, :foo) }
+ class << o; remove_method(:to_r); end
def o.to_r; ""; end
assert_raise(TypeError) { Time.gm(2000, 1, 1, 0, 0, o, :foo, :foo) }
+ class << o; remove_method(:to_r); end
def o.to_r; Rational(11); end
assert_equal(11, Time.gm(2000, 1, 1, 0, 0, o).sec)
o = Object.new
@@ -431,6 +455,10 @@ class TestTime < Test::Unit::TestCase
assert_equal(-4427700000, Time.utc(-4427700000,12,1).year)
assert_equal(-2**30+10, Time.utc(-2**30+10,1,1).year)
+
+ assert_raise(ArgumentError) { Time.gm(2000, 1, -1) }
+ assert_raise(ArgumentError) { Time.gm(2000, 1, 2**30 + 1) }
+ assert_raise(ArgumentError) { Time.gm(2000, 1, -2**30 + 1) }
end
def test_time_interval
@@ -575,6 +603,10 @@ class TestTime < Test::Unit::TestCase
t2000 = get_t2000.localtime(9*3600) + 1/10r
assert_equal("2000-01-01 09:00:00.1 +0900", t2000.inspect)
+
+ t2000 = get_t2000
+ assert_equal("2000-01-01 09:12:00 +0912", t2000.localtime(9*3600+12*60).inspect)
+ assert_equal("2000-01-01 09:12:34 +091234", t2000.localtime(9*3600+12*60+34).inspect)
end
def assert_zone_encoding(time)
@@ -596,13 +628,12 @@ class TestTime < Test::Unit::TestCase
assert_nil(t.getlocal("+02:00").zone)
end
- def test_plus_minus_succ
+ def test_plus_minus
t2000 = get_t2000
# assert_raise(RangeError) { t2000 + 10000000000 }
# assert_raise(RangeError) t2000 - 3094168449 }
# assert_raise(RangeError) { t2000 + 1200798848 }
assert_raise(TypeError) { t2000 + Time.now }
- assert_equal(t2000 + 1, t2000.succ)
end
def test_plus_type
@@ -705,7 +736,9 @@ class TestTime < Test::Unit::TestCase
assert_equal("12:00:00 AM", t2000.strftime("%r"))
assert_equal("Sat 2000-01-01T00:00:00", t2000.strftime("%3a %FT%T"))
- assert_equal("", t2000.strftime(""))
+ assert_warning(/strftime called with empty format string/) do
+ assert_equal("", t2000.strftime(""))
+ end
assert_equal("foo\0bar\x0000\x0000\x0000", t2000.strftime("foo\0bar\0%H\0%M\0%S"))
assert_equal("foo" * 1000, t2000.strftime("foo" * 1000))
@@ -885,6 +918,13 @@ class TestTime < Test::Unit::TestCase
assert_equal(8192, Time.now.strftime('%8192z').size)
end
+ def test_strftime_wide_precision
+ t2000 = get_t2000
+ s = t2000.strftime("%28c")
+ assert_equal(28, s.size)
+ assert_equal(t2000.strftime("%c"), s.strip)
+ end
+
def test_strfimte_zoneoffset
t2000 = get_t2000
t = t2000.getlocal("+09:00:00")
@@ -898,6 +938,17 @@ class TestTime < Test::Unit::TestCase
assert_equal("+09:00", t.strftime("%:z"))
assert_equal("+09:00:01", t.strftime("%::z"))
assert_equal("+09:00:01", t.strftime("%:::z"))
+
+ assert_equal("+0000", t2000.strftime("%z"))
+ assert_equal("-0000", t2000.strftime("%-z"))
+ assert_equal("-00:00", t2000.strftime("%-:z"))
+ assert_equal("-00:00:00", t2000.strftime("%-::z"))
+
+ t = t2000.getlocal("+00:00")
+ assert_equal("+0000", t.strftime("%z"))
+ assert_equal("+0000", t.strftime("%-z"))
+ assert_equal("+00:00", t.strftime("%-:z"))
+ assert_equal("+00:00:00", t.strftime("%-::z"))
end
def test_strftime_padding
@@ -1054,6 +1105,11 @@ class TestTime < Test::Unit::TestCase
t2 = (t+0.123456789).ceil(4)
assert_equal([59,59,23, 31,12,1999, 5,365,false,"UTC"], t2.to_a)
assert_equal(Rational(1235,10000), t2.subsec)
+
+ time = Time.utc(2016, 4, 23, 0, 0, 0.123456789r)
+ assert_equal(time, time.ceil(9))
+ assert_equal(time, time.ceil(10))
+ assert_equal(time, time.ceil(11))
end
def test_getlocal_dont_share_eigenclass
@@ -1129,6 +1185,9 @@ class TestTime < Test::Unit::TestCase
end
def test_2038
+ # Giveup to try 2nd test because some state is changed.
+ skip if Test::Unit::Runner.current_repeat_count > 0
+
if no_leap_seconds?
assert_equal(0x80000000, Time.utc(2038, 1, 19, 3, 14, 8).tv_sec)
end
@@ -1187,6 +1246,30 @@ class TestTime < Test::Unit::TestCase
}
end
+ def test_getlocal_utc
+ t = Time.gm(2000)
+ assert_equal [00, 00, 00, 1, 1, 2000], (t1 = t.getlocal("UTC")).to_a[0, 6]
+ assert_predicate t1, :utc?
+ assert_equal [00, 00, 00, 1, 1, 2000], (t1 = t.getlocal("-0000")).to_a[0, 6]
+ assert_predicate t1, :utc?
+ assert_equal [00, 00, 00, 1, 1, 2000], (t1 = t.getlocal("+0000")).to_a[0, 6]
+ assert_not_predicate t1, :utc?
+ end
+
+ def test_getlocal_utc_offset
+ t = Time.gm(2000)
+ assert_equal [00, 30, 21, 31, 12, 1999], t.getlocal("-02:30").to_a[0, 6]
+ assert_equal [00, 00, 9, 1, 1, 2000], t.getlocal("+09:00").to_a[0, 6]
+ assert_equal [20, 29, 21, 31, 12, 1999], t.getlocal("-02:30:40").to_a[0, 6]
+ assert_equal [35, 10, 9, 1, 1, 2000], t.getlocal("+09:10:35").to_a[0, 6]
+ assert_equal [00, 30, 21, 31, 12, 1999], t.getlocal("-0230").to_a[0, 6]
+ assert_equal [00, 00, 9, 1, 1, 2000], t.getlocal("+0900").to_a[0, 6]
+ assert_equal [20, 29, 21, 31, 12, 1999], t.getlocal("-023040").to_a[0, 6]
+ assert_equal [35, 10, 9, 1, 1, 2000], t.getlocal("+091035").to_a[0, 6]
+ assert_raise(ArgumentError) {t.getlocal("-02:3040")}
+ assert_raise(ArgumentError) {t.getlocal("+0910:35")}
+ end
+
def test_getlocal_nil
now = Time.now
now2 = nil
@@ -1216,22 +1299,6 @@ class TestTime < Test::Unit::TestCase
assert_equal("366", t.strftime("%j"))
end
- def test_strftime_no_hidden_garbage
- skip unless Thread.list.size == 1
-
- fmt = %w(Y m d).map { |x| "%#{x}" }.join('-') # defeats optimization
- t = Time.at(0).getutc
- ObjectSpace.count_objects(res = {}) # creates strings on first call
- GC.disable
- before = ObjectSpace.count_objects(res)[:T_STRING]
- val = t.strftime(fmt)
- after = ObjectSpace.count_objects(res)[:T_STRING]
- assert_equal before + 1, after, 'only new string is the created one'
- assert_equal '1970-01-01', val
- ensure
- GC.enable
- end
-
def test_num_exact_error
bad = EnvUtil.labeled_class("BadValue").new
x = EnvUtil.labeled_class("Inexact") do
@@ -1245,6 +1312,7 @@ class TestTime < Test::Unit::TestCase
def test_memsize
# Time objects are common in some code, try to keep them small
skip "Time object size test" if /^(?:i.?86|x86_64)-linux/ !~ RUBY_PLATFORM
+ skip "GC is in debug" if GC::INTERNAL_CONSTANTS[:DEBUG]
require 'objspace'
t = Time.at(0)
size = GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb
index 83482eac65..26fe680acf 100644
--- a/test/ruby/test_time_tz.rb
+++ b/test/ruby/test_time_tz.rb
@@ -7,9 +7,9 @@ class TestTimeTZ < Test::Unit::TestCase
has_lisbon_tz = true
force_tz_test = ENV["RUBY_FORCE_TIME_TZ_TEST"] == "yes"
case RUBY_PLATFORM
- when /linux/
+ when /darwin|linux/
force_tz_test = true
- when /darwin|freebsd|openbsd/
+ when /freebsd|openbsd/
has_lisbon_tz = false
force_tz_test = true
end
@@ -95,6 +95,9 @@ class TestTimeTZ < Test::Unit::TestCase
CORRECT_KIRITIMATI_SKIP_1994 = with_tz("Pacific/Kiritimati") {
Time.local(1994, 12, 31, 0, 0, 0).year == 1995
}
+ CORRECT_SINGAPORE_1982 = with_tz("Asia/Singapore") {
+ "2022g" if Time.local(1981, 12, 31, 23, 59, 59).utc_offset == 8*3600
+ }
def time_to_s(t)
t.to_s
@@ -140,9 +143,12 @@ class TestTimeTZ < Test::Unit::TestCase
def test_asia_singapore
with_tz(tz="Asia/Singapore") {
- assert_time_constructor(tz, "1981-12-31 23:59:59 +0730", :local, [1981,12,31,23,59,59])
- assert_time_constructor(tz, "1982-01-01 00:30:00 +0800", :local, [1982,1,1,0,0,0])
- assert_time_constructor(tz, "1982-01-01 00:59:59 +0800", :local, [1982,1,1,0,29,59])
+ assert_time_constructor(tz, "1981-12-31 23:29:59 +0730", :local, [1981,12,31,23,29,59])
+ if CORRECT_SINGAPORE_1982
+ assert_time_constructor(tz, "1982-01-01 00:00:00 +0800", :local, [1981,12,31,23,30,00])
+ assert_time_constructor(tz, "1982-01-01 00:00:00 +0800", :local, [1982,1,1,0,0,0])
+ assert_time_constructor(tz, "1982-01-01 00:29:59 +0800", :local, [1982,1,1,0,29,59])
+ end
assert_time_constructor(tz, "1982-01-01 00:30:00 +0800", :local, [1982,1,1,0,30,0])
}
end
@@ -196,7 +202,7 @@ class TestTimeTZ < Test::Unit::TestCase
def test_europe_lisbon
with_tz("Europe/Lisbon") {
- assert_equal("LMT", Time.new(-0x1_0000_0000_0000_0000).zone)
+ assert_include(%w"LMT CET", Time.new(-0x1_0000_0000_0000_0000).zone)
}
end if has_lisbon_tz
@@ -219,7 +225,6 @@ class TestTimeTZ < Test::Unit::TestCase
def test_right_utc
with_tz(tz="right/UTC") {
- ::Bug::Time.reset_leap_second_info
assert_time_constructor(tz, "2008-12-31 23:59:59 UTC", :utc, [2008,12,31,23,59,59])
assert_time_constructor(tz, "2008-12-31 23:59:60 UTC", :utc, [2008,12,31,23,59,60])
assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,24,0,0])
@@ -229,25 +234,23 @@ class TestTimeTZ < Test::Unit::TestCase
def test_right_utc_switching
with_tz("UTC") { # ensure no leap second timezone
- ::Bug::Time.reset_leap_second_info
assert_equal(4102444800, Time.utc(2100,1,1,0,0,0).to_i)
with_tz(tz="right/UTC") {
assert_time_constructor(tz, "2008-12-31 23:59:59 UTC", :utc, [2008,12,31,23,59,59])
- assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,23,59,60])
+ assert_time_constructor(tz, "2008-12-31 23:59:60 UTC", :utc, [2008,12,31,23,59,60])
assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,24,0,0])
assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2009,1,1,0,0,0])
- assert_equal(4102444800, Time.utc(2100,1,1,0,0,0).to_i)
+ assert_not_equal(4102444800, Time.utc(2100,1,1,0,0,0).to_i)
}
}
with_tz("right/UTC") {
- ::Bug::Time.reset_leap_second_info
assert_not_equal(4102444800, Time.utc(2100,1,1,0,0,0).to_i)
with_tz(tz="UTC") {
assert_time_constructor(tz, "2008-12-31 23:59:59 UTC", :utc, [2008,12,31,23,59,59])
assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,23,59,60])
assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,24,0,0])
assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2009,1,1,0,0,0])
- assert_not_equal(4102444800, Time.utc(2100,1,1,0,0,0).to_i)
+ assert_equal(4102444800, Time.utc(2100,1,1,0,0,0).to_i)
}
}
end if has_right_tz
@@ -264,6 +267,8 @@ class TestTimeTZ < Test::Unit::TestCase
assert_predicate(Time.new(2019, 1, 1, 0, 0, 0, "UTC"), :utc?)
assert_predicate(Time.new(2019, 1, 1, 0, 0, 0, "utc"), :utc?)
assert_predicate(Time.new(2019, 1, 1, 0, 0, 0, "Z"), :utc?)
+ assert_predicate(Time.new(2019, 1, 1, 0, 0, 0, "-00:00"), :utc?)
+ assert_not_predicate(Time.new(2019, 1, 1, 0, 0, 0, "+00:00"), :utc?)
end
def test_military_names
@@ -376,7 +381,6 @@ class TestTimeTZ < Test::Unit::TestCase
mesg = "#{mesg_utc}.localtime"
define_method(gen_test_name(tz)) {
with_tz(tz) {
- ::Bug::Time.reset_leap_second_info
t = nil
assert_nothing_raised(mesg) { t = Time.utc(*u) }
assert_equal(expected_utc, time_to_s(t), mesg_utc)
@@ -452,9 +456,12 @@ America/Managua Fri Jan 1 06:00:00 1993 UTC = Fri Jan 1 01:00:00 1993 EST isd
America/Managua Wed Jan 1 04:59:59 1997 UTC = Tue Dec 31 23:59:59 1996 EST isdst=0 gmtoff=-18000
America/Managua Wed Jan 1 05:00:00 1997 UTC = Tue Dec 31 23:00:00 1996 CST isdst=0 gmtoff=-21600
Asia/Singapore Sun Aug 8 16:30:00 1965 UTC = Mon Aug 9 00:00:00 1965 SGT isdst=0 gmtoff=27000
-Asia/Singapore Thu Dec 31 16:29:59 1981 UTC = Thu Dec 31 23:59:59 1981 SGT isdst=0 gmtoff=27000
+Asia/Singapore Thu Dec 31 15:59:59 1981 UTC = Thu Dec 31 23:29:59 1981 SGT isdst=0 gmtoff=27000
Asia/Singapore Thu Dec 31 16:30:00 1981 UTC = Fri Jan 1 00:30:00 1982 SGT isdst=0 gmtoff=28800
End
+ gen_zdump_test <<'End' if CORRECT_SINGAPORE_1982
+Asia/Singapore Thu Dec 31 16:00:00 1981 UTC = Fri Jan 1 00:00:00 1982 SGT isdst=0 gmtoff=28800
+End
gen_zdump_test CORRECT_TOKYO_DST_1951 ? <<'End' + (CORRECT_TOKYO_DST_1951 < "2018f" ? <<'2018e' : <<'2018f') : <<'End'
Asia/Tokyo Sat May 5 14:59:59 1951 UTC = Sat May 5 23:59:59 1951 JST isdst=0 gmtoff=32400
Asia/Tokyo Sat May 5 15:00:00 1951 UTC = Sun May 6 01:00:00 1951 JDT isdst=1 gmtoff=36000
@@ -608,6 +615,15 @@ module TestTimeTZ::WithTZ
assert_equal([2018, 9, 1, 12, 0, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone])
h, m = (-utc_offset / 60).divmod(60)
assert_equal(time_class.utc(2018, 9, 1, 12+h, m, 0).to_i, t.to_i)
+ assert_equal(6, t.wday)
+ assert_equal(244, t.yday)
+ assert_equal(t, time_class.new(2018, 9, 1, 12, in: tzarg))
+ assert_raise(ArgumentError) {time_class.new(2018, 9, 1, 12, 0, 0, tzarg, in: tzarg)}
+ end
+
+ def subtest_hour24(time_class, tz, tzarg, tzname, abbr, utc_offset)
+ t = time_class.new(2000, 1, 1, 24, 0, 0, tzarg)
+ assert_equal([0, 0, 0, 2, 1, 2000], [t.sec, t.min, t.hour, t.mday, t.mon, t.year])
end
def subtest_now(time_class, tz, tzarg, tzname, abbr, utc_offset)
@@ -634,6 +650,7 @@ module TestTimeTZ::WithTZ
h, m = (utc_offset.abs / 60).divmod(60)
h = -h if utc_offset < 0
assert_equal("%+.2d%.2d %s" % [h, m, abbr], t.strftime("%z %Z"))
+ assert_equal("34 35 35", t.strftime("%U %V %W"))
end
def subtest_plus(time_class, tz, tzarg, tzname, abbr, utc_offset)
@@ -662,6 +679,12 @@ module TestTimeTZ::WithTZ
assert_equal(utc, t.to_i)
end
+ def subtest_to_a(time_class, tz, tzarg, tzname, abbr, utc_offset)
+ t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg)
+ ary = t.to_a
+ assert_equal(ary, [t.sec, t.min, t.hour, t.mday, t.mon, t.year, t.wday, t.yday, t.isdst, t.zone])
+ end
+
def subtest_marshal(time_class, tz, tzarg, tzname, abbr, utc_offset)
t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg)
t2 = Marshal.load(Marshal.dump(t))
@@ -747,6 +770,16 @@ class TestTimeTZ::DummyTZ < Test::Unit::TestCase
def self.make_timezone(tzname, abbr, utc_offset, abbr2 = nil, utc_offset2 = nil)
TestTimeTZ::TZ.new(tzname, abbr, utc_offset, abbr2, utc_offset2)
end
+
+ def test_fractional_second
+ x = Object.new
+ def x.local_to_utc(t); t + 8*3600; end
+ def x.utc_to_local(t); t - 8*3600; end
+
+ t1 = Time.new(2020,11,11,12,13,14.124r, '-08:00')
+ t2 = Time.new(2020,11,11,12,13,14.124r, x)
+ assert_equal(t1, t2)
+ end
end
begin
diff --git a/test/ruby/test_transcode.rb b/test/ruby/test_transcode.rb
index f405877dd5..c8b0034e06 100644
--- a/test/ruby/test_transcode.rb
+++ b/test/ruby/test_transcode.rb
@@ -126,6 +126,28 @@ class TestTranscode < Test::Unit::TestCase
assert_equal("D\xFCrst".force_encoding('iso-8859-2'), "D\xFCrst".encode('iso-8859-2', 'iso-8859-1'))
end
+ def test_encode_xml_multibyte
+ encodings = %w'UTF-8 UTF-16LE UTF-16BE UTF-32LE UTF-32BE'
+ encodings.each do |src_enc|
+ encodings.each do |dst_enc|
+ escaped = "<>".encode(src_enc).encode(dst_enc, :xml=>:text)
+ assert_equal("&lt;&gt;", escaped.encode('UTF-8'), "failed encoding #{src_enc} to #{dst_enc} with xml: :text")
+
+ escaped = '<">'.encode(src_enc).encode(dst_enc, :xml=>:attr)
+ assert_equal('"&lt;&quot;&gt;"', escaped.encode('UTF-8'), "failed encoding #{src_enc} to #{dst_enc} with xml: :attr")
+
+ escaped = "<>".encode(src_enc).force_encoding("UTF-8").encode(dst_enc, src_enc, :xml=>:text)
+ assert_equal("&lt;&gt;", escaped.encode('UTF-8'), "failed encoding #{src_enc} to #{dst_enc} with xml: :text")
+
+ escaped = '<">'.encode(src_enc).force_encoding("UTF-8").encode(dst_enc, src_enc, :xml=>:attr)
+ assert_equal('"&lt;&quot;&gt;"', escaped.encode('UTF-8'), "failed encoding #{src_enc} to #{dst_enc} with xml: :attr")
+ end
+ end
+ # regression test; U+6E7F (湿) uses the same bytes in ISO-2022-JP as "<>"
+ assert_equal( "&lt;&gt;\u6E7F", "<>\u6E7F".encode("ISO-2022-JP").encode("ISO-2022-JP", :xml=>:text).encode("UTF-8"))
+ assert_equal("\"&lt;&gt;\u6E7F\"", "<>\u6E7F".encode("ISO-2022-JP").encode("ISO-2022-JP", :xml=>:attr).encode("UTF-8"))
+ end
+
def test_ascii_range
encodings = [
'US-ASCII', 'ASCII-8BIT',
@@ -469,6 +491,25 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u00A0", "\xFF", 'IBM437') # non-breaking space
end
+ def test_IBM720
+ assert_raise(Encoding::UndefinedConversionError) { "\x80".encode("utf-8", 'IBM720') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x8F".encode("utf-8", 'IBM720') }
+ assert_raise(Encoding::UndefinedConversionError) { "\x90".encode("utf-8", 'IBM720') }
+ check_both_ways("\u0627", "\x9F", 'IBM720') # ا
+ check_both_ways("\u0628", "\xA0", 'IBM720') # ب
+ check_both_ways("\u00BB", "\xAF", 'IBM720') # »
+ check_both_ways("\u2591", "\xB0", 'IBM720') # ░
+ check_both_ways("\u2510", "\xBF", 'IBM720') # ┐
+ check_both_ways("\u2514", "\xC0", 'IBM720') # └
+ check_both_ways("\u2567", "\xCF", 'IBM720') # ╧
+ check_both_ways("\u2568", "\xD0", 'IBM720') # ╨
+ check_both_ways("\u2580", "\xDF", 'IBM720') # ▀
+ check_both_ways("\u0636", "\xE0", 'IBM720') # ض
+ check_both_ways("\u064A", "\xEF", 'IBM720') # ي
+ check_both_ways("\u2261", "\xF0", 'IBM720') # ≡
+ check_both_ways("\u00A0", "\xFF", 'IBM720') # non-breaking space
+ end
+
def test_IBM775
check_both_ways("\u0106", "\x80", 'IBM775') # Ć
check_both_ways("\u00C5", "\x8F", 'IBM775') # Å
@@ -2183,6 +2224,14 @@ class TestTranscode < Test::Unit::TestCase
assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback.method(:escape)))
end
+ def test_fallback_aref
+ fallback = Object.new
+ def fallback.[](x)
+ "U+%.4X" % x.unpack("U")
+ end
+ assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback))
+ end
+
bug8940 = '[ruby-core:57318] [Bug #8940]'
%w[UTF-32 UTF-16].each do |enc|
define_method("test_pseudo_encoding_inspect(#{enc})") do
@@ -2242,12 +2291,19 @@ class TestTranscode < Test::Unit::TestCase
"#{bug} coderange should not have side effects")
end
- def test_universal_newline
+ def test_newline_options
bug11324 = '[ruby-core:69841] [Bug #11324]'
usascii = Encoding::US_ASCII
s = "A\nB\r\nC".force_encoding(usascii)
assert_equal("A\nB\nC", s.encode(usascii, universal_newline: true), bug11324)
assert_equal("A\nB\nC", s.encode(usascii, universal_newline: true, undef: :replace), bug11324)
assert_equal("A\nB\nC", s.encode(usascii, universal_newline: true, undef: :replace, replace: ''), bug11324)
+ assert_equal("A\nB\nC", s.encode(usascii, newline: :universal))
+ assert_equal("A\nB\nC", s.encode(usascii, newline: :universal, undef: :replace))
+ assert_equal("A\nB\nC", s.encode(usascii, newline: :universal, undef: :replace, replace: ''))
+ assert_equal("A\rB\r\rC", s.encode(usascii, cr_newline: true))
+ assert_equal("A\rB\r\rC", s.encode(usascii, newline: :cr))
+ assert_equal("A\r\nB\r\r\nC", s.encode(usascii, crlf_newline: true))
+ assert_equal("A\r\nB\r\r\nC", s.encode(usascii, newline: :crlf))
end
end
diff --git a/test/ruby/test_undef.rb b/test/ruby/test_undef.rb
index e0add7c3ab..074b92be55 100644
--- a/test/ruby/test_undef.rb
+++ b/test/ruby/test_undef.rb
@@ -35,4 +35,20 @@ class TestUndef < Test::Unit::TestCase
end
end
end
+
+ def test_singleton_undef
+ klass = Class.new do
+ def foo
+ :ok
+ end
+ end
+
+ klass.new.foo
+
+ klass.new.instance_eval do
+ undef foo
+ end
+
+ klass.new.foo
+ end
end
diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb
index b053e11607..d425b43b0d 100644
--- a/test/ruby/test_variable.rb
+++ b/test/ruby/test_variable.rb
@@ -29,12 +29,39 @@ class TestVariable < Test::Unit::TestCase
@@rule = "Cronus" # modifies @@rule in Gods
include Olympians
def ruler4
- EnvUtil.suppress_warning {
- @@rule
- }
+ @@rule
end
end
+ Athena = Gods.clone
+
+ def test_cloned_classes_copy_cvar_cache
+ assert_equal "Cronus", Athena.new.ruler0
+ end
+
+ def test_setting_class_variable_on_module_through_inheritance
+ mod = Module.new
+ mod.class_variable_set(:@@foo, 1)
+ mod.freeze
+ c = Class.new { include(mod) }
+ assert_raise(FrozenError) { c.class_variable_set(:@@foo, 2) }
+ assert_raise(FrozenError) { c.class_eval("@@foo = 2") }
+ assert_equal(1, c.class_variable_get(:@@foo))
+ end
+
+ Zeus = Gods.clone
+
+ def test_cloned_allows_setting_cvar
+ Zeus.class_variable_set(:@@rule, "Athena")
+
+ god = Gods.new.ruler0
+ zeus = Zeus.new.ruler0
+
+ assert_equal "Cronus", god
+ assert_equal "Athena", zeus
+ assert_not_equal god.object_id, zeus.object_id
+ end
+
def test_singleton_class_included_class_variable
c = Class.new
c.extend(Olympians)
@@ -63,6 +90,67 @@ class TestVariable < Test::Unit::TestCase
assert_equal(1, o.singleton_class.class_variable_get(:@@foo))
end
+ def test_cvar_overtaken_by_parent_class
+ error = eval <<~EORB
+ class Parent
+ end
+
+ class Child < Parent
+ @@cvar = 1
+
+ def self.cvar
+ @@cvar
+ end
+ end
+
+ assert_equal 1, Child.cvar
+
+ class Parent
+ @@cvar = 2
+ end
+
+ assert_raise RuntimeError do
+ Child.cvar
+ end
+ EORB
+
+ assert_equal "class variable @@cvar of TestVariable::Child is overtaken by TestVariable::Parent", error.message
+ ensure
+ TestVariable.send(:remove_const, :Child) rescue nil
+ TestVariable.send(:remove_const, :Parent) rescue nil
+ end
+
+ def test_cvar_overtaken_by_module
+ error = eval <<~EORB
+ class ParentForModule
+ @@cvar = 1
+
+ def self.cvar
+ @@cvar
+ end
+ end
+
+ assert_equal 1, ParentForModule.cvar
+
+ module Mixin
+ @@cvar = 2
+ end
+
+ class ParentForModule
+ include Mixin
+ end
+
+ assert_raise RuntimeError do
+ ParentForModule.cvar
+ end
+ EORB
+
+ assert_equal "class variable @@cvar of TestVariable::ParentForModule is overtaken by TestVariable::Mixin", error.message
+ ensure
+ TestVariable.send(:remove_const, :Mixin) rescue nil
+ TestVariable.send(:remove_const, :ParentForModule) rescue nil
+ end
+
class IncludeRefinedModuleClassVariableNoWarning
module Mod
@@_test_include_refined_module_class_variable = true
@@ -107,7 +195,7 @@ class TestVariable < Test::Unit::TestCase
atlas = Titans.new
assert_equal("Cronus", atlas.ruler0)
assert_equal("Zeus", atlas.ruler3)
- assert_equal("Cronus", atlas.ruler4)
+ assert_raise(RuntimeError) { atlas.ruler4 }
assert_nothing_raised do
class << Gods
defined?(@@rule) && @@rule
diff --git a/test/ruby/test_vm_dump.rb b/test/ruby/test_vm_dump.rb
index 68f0fa7f27..679ce94b91 100644
--- a/test/ruby/test_vm_dump.rb
+++ b/test/ruby/test_vm_dump.rb
@@ -16,6 +16,6 @@ class TestVMDump < Test::Unit::TestCase
end
def test_darwin_invalid_access
- assert_darwin_vm_dump_works(['-rfiddle', '-eFiddle.dlunwrap(100).class'])
+ assert_darwin_vm_dump_works(['-rfiddle', '-eFiddle.dlunwrap(100).inspect'])
end
end
diff --git a/test/ruby/test_weakmap.rb b/test/ruby/test_weakmap.rb
index 3b9eef770a..5ca6b0fbdd 100644
--- a/test/ruby/test_weakmap.rb
+++ b/test/ruby/test_weakmap.rb
@@ -73,6 +73,15 @@ class TestWeakMap < Test::Unit::TestCase
@wm.inspect)
end
+ def test_inspect_garbage
+ 1000.times do |i|
+ @wm[i] = Object.new
+ @wm.inspect
+ end
+ assert_match(/\A\#<#{@wm.class.name}:[^:]++:(?:\s\d+\s=>\s\#<(?:Object|collected):[^:<>]*+>(?:,|>\z))+/,
+ @wm.inspect)
+ end
+
def test_each
m = __callee__[/test_(.*)/, 1]
x1 = Object.new
@@ -158,4 +167,31 @@ class TestWeakMap < Test::Unit::TestCase
assert_nothing_raised(FrozenError) {@wm[o] = 'foo'}
assert_nothing_raised(FrozenError) {@wm['foo'] = o}
end
+
+ def test_no_memory_leak
+ assert_no_memory_leak([], '', "#{<<~"begin;"}\n#{<<~'end;'}", "[Bug #19398]", rss: true, limit: 1.5, timeout: 60)
+ begin;
+ 1_000_000.times do
+ ObjectSpace::WeakMap.new
+ end
+ end;
+ end
+
+ def test_compaction_bug_19529
+ obj = Object.new
+ 100.times do |i|
+ GC.compact
+ @wm[i] = obj
+ end
+
+ assert_separately(%w(--disable-gems), <<-'end;')
+ wm = ObjectSpace::WeakMap.new
+ obj = Object.new
+ 100.times do
+ wm[Object.new] = obj
+ GC.start
+ end
+ GC.compact
+ end;
+ end
end
diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb
new file mode 100644
index 0000000000..48b3e4acea
--- /dev/null
+++ b/test/ruby/test_yjit.rb
@@ -0,0 +1,703 @@
+# frozen_string_literal: true
+require 'test/unit'
+require 'envutil'
+require 'tmpdir'
+
+return unless defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
+
+# Tests for YJIT with assertions on compilation and side exits
+# insipired by the MJIT tests in test/ruby/test_jit.rb
+class TestYJIT < Test::Unit::TestCase
+ def test_yjit_in_ruby_description
+ assert_includes(RUBY_DESCRIPTION, '+YJIT')
+ end
+
+ def test_yjit_in_version
+ [
+ %w(--version --yjit),
+ %w(--version --disable-yjit --yjit),
+ %w(--version --disable-yjit --enable-yjit),
+ %w(--version --disable-yjit --enable=yjit),
+ %w(--version --disable=yjit --yjit),
+ %w(--version --disable=yjit --enable-yjit),
+ %w(--version --disable=yjit --enable=yjit),
+ *([
+ %w(--version --jit),
+ %w(--version --disable-jit --jit),
+ %w(--version --disable-jit --enable-jit),
+ %w(--version --disable-jit --enable=jit),
+ %w(--version --disable=jit --yjit),
+ %w(--version --disable=jit --enable-jit),
+ %w(--version --disable=jit --enable=jit),
+ ] if RUBY_PLATFORM.start_with?('x86_64-') && RUBY_PLATFORM !~ /mswin|mingw|msys/),
+ ].each do |version_args|
+ assert_in_out_err(version_args) do |stdout, stderr|
+ assert_equal(RUBY_DESCRIPTION, stdout.first)
+ assert_equal([], stderr)
+ end
+ end
+ end
+
+ def test_command_line_switches
+ assert_in_out_err('--yjit-', '', [], /invalid option --yjit-/)
+ assert_in_out_err('--yjithello', '', [], /invalid option --yjithello/)
+ assert_in_out_err('--yjit-call-threshold', '', [], /--yjit-call-threshold needs an argument/)
+ assert_in_out_err('--yjit-call-threshold=', '', [], /--yjit-call-threshold needs an argument/)
+ assert_in_out_err('--yjit-greedy-versioning=1', '', [], /warning: argument to --yjit-greedy-versioning is ignored/)
+ end
+
+ def test_yjit_stats_and_v_no_error
+ _stdout, stderr, _status = EnvUtil.invoke_ruby(%w(-v --yjit-stats), '', true, true)
+ refute_includes(stderr, "NoMethodError")
+ end
+
+ def test_enable_from_env_var
+ yjit_child_env = {'RUBY_YJIT_ENABLE' => '1'}
+ assert_in_out_err([yjit_child_env, '--version'], '') do |stdout, stderr|
+ assert_equal(RUBY_DESCRIPTION, stdout.first)
+ assert_equal([], stderr)
+ end
+ assert_in_out_err([yjit_child_env, '-e puts RUBY_DESCRIPTION'], '', [RUBY_DESCRIPTION])
+ assert_in_out_err([yjit_child_env, '-e p RubyVM::YJIT.enabled?'], '', ['true'])
+ end
+
+ def test_compile_setclassvariable
+ script = 'class Foo; def self.foo; @@foo = 1; end; end; Foo.foo'
+ assert_compiles(script, insns: %i[setclassvariable], result: 1)
+ end
+
+ def test_compile_getclassvariable
+ script = 'class Foo; @@foo = 1; def self.foo; @@foo; end; end; Foo.foo'
+ assert_compiles(script, insns: %i[getclassvariable], result: 1)
+ end
+
+ def test_compile_putnil
+ assert_compiles('nil', insns: %i[putnil], result: nil)
+ end
+
+ def test_compile_putobject
+ assert_compiles('true', insns: %i[putobject], result: true)
+ assert_compiles('123', insns: %i[putobject], result: 123)
+ assert_compiles(':foo', insns: %i[putobject], result: :foo)
+ end
+
+ def test_compile_opt_not
+ assert_compiles('!false', insns: %i[opt_not], result: true)
+ assert_compiles('!nil', insns: %i[opt_not], result: true)
+ assert_compiles('!true', insns: %i[opt_not], result: false)
+ assert_compiles('![]', insns: %i[opt_not], result: false)
+ end
+
+ def test_compile_opt_newarray
+ assert_compiles('[]', insns: %i[newarray], result: [])
+ assert_compiles('[1+1]', insns: %i[newarray opt_plus], result: [2])
+ assert_compiles('[1,1+1,3,4,5,6]', insns: %i[newarray opt_plus], result: [1, 2, 3, 4, 5, 6])
+ end
+
+ def test_compile_opt_duparray
+ assert_compiles('[1]', insns: %i[duparray], result: [1])
+ assert_compiles('[1, 2, 3]', insns: %i[duparray], result: [1, 2, 3])
+ end
+
+ def test_compile_newrange
+ assert_compiles('s = 1; (s..5)', insns: %i[newrange], result: 1..5)
+ assert_compiles('s = 1; e = 5; (s..e)', insns: %i[newrange], result: 1..5)
+ assert_compiles('s = 1; (s...5)', insns: %i[newrange], result: 1...5)
+ assert_compiles('s = 1; (s..)', insns: %i[newrange], result: 1..)
+ assert_compiles('e = 5; (..e)', insns: %i[newrange], result: ..5)
+ end
+
+ def test_compile_duphash
+ assert_compiles('{ two: 2 }', insns: %i[duphash], result: { two: 2 })
+ end
+
+ def test_compile_newhash
+ assert_compiles('{}', insns: %i[newhash], result: {})
+ assert_compiles('{ two: 1 + 1 }', insns: %i[newhash], result: { two: 2 })
+ assert_compiles('{ 1 + 1 => :two }', insns: %i[newhash], result: { 2 => :two })
+ end
+
+ def test_compile_opt_nil_p
+ assert_compiles('nil.nil?', insns: %i[opt_nil_p], result: true)
+ assert_compiles('false.nil?', insns: %i[opt_nil_p], result: false)
+ assert_compiles('true.nil?', insns: %i[opt_nil_p], result: false)
+ assert_compiles('(-"").nil?', insns: %i[opt_nil_p], result: false)
+ assert_compiles('123.nil?', insns: %i[opt_nil_p], result: false)
+ end
+
+ def test_compile_eq_fixnum
+ assert_compiles('123 == 123', insns: %i[opt_eq], result: true)
+ assert_compiles('123 == 456', insns: %i[opt_eq], result: false)
+ end
+
+ def test_compile_eq_string
+ assert_compiles('-"" == -""', insns: %i[opt_eq], result: true)
+ assert_compiles('-"foo" == -"foo"', insns: %i[opt_eq], result: true)
+ assert_compiles('-"foo" == -"bar"', insns: %i[opt_eq], result: false)
+ end
+
+ def test_compile_eq_symbol
+ assert_compiles(':foo == :foo', insns: %i[opt_eq], result: true)
+ assert_compiles(':foo == :bar', insns: %i[opt_eq], result: false)
+ assert_compiles(':foo == "foo".to_sym', insns: %i[opt_eq], result: true)
+ end
+
+ def test_compile_eq_object
+ assert_compiles(<<~RUBY, insns: %i[opt_eq], result: false)
+ def eq(a, b)
+ a == b
+ end
+
+ eq(Object.new, Object.new)
+ RUBY
+
+ assert_compiles(<<~RUBY, insns: %i[opt_eq], result: true)
+ def eq(a, b)
+ a == b
+ end
+
+ obj = Object.new
+ eq(obj, obj)
+ RUBY
+ end
+
+ def test_compile_eq_arbitrary_class
+ assert_compiles(<<~RUBY, insns: %i[opt_eq], result: "yes")
+ def eq(a, b)
+ a == b
+ end
+
+ class Foo
+ def ==(other)
+ "yes"
+ end
+ end
+
+ eq(Foo.new, Foo.new)
+ eq(Foo.new, Foo.new)
+ RUBY
+ end
+
+ def test_compile_opt_lt
+ assert_compiles('1 < 2', insns: %i[opt_lt])
+ assert_compiles('"a" < "b"', insns: %i[opt_lt])
+ end
+
+ def test_compile_opt_le
+ assert_compiles('1 <= 2', insns: %i[opt_le])
+ assert_compiles('"a" <= "b"', insns: %i[opt_le])
+ end
+
+ def test_compile_opt_gt
+ assert_compiles('1 > 2', insns: %i[opt_gt])
+ assert_compiles('"a" > "b"', insns: %i[opt_gt])
+ end
+
+ def test_compile_opt_ge
+ assert_compiles('1 >= 2', insns: %i[opt_ge])
+ assert_compiles('"a" >= "b"', insns: %i[opt_ge])
+ end
+
+ def test_compile_opt_plus
+ assert_compiles('1 + 2', insns: %i[opt_plus])
+ assert_compiles('"a" + "b"', insns: %i[opt_plus])
+ assert_compiles('[:foo] + [:bar]', insns: %i[opt_plus])
+ end
+
+ def test_compile_opt_minus
+ assert_compiles('1 - 2', insns: %i[opt_minus])
+ assert_compiles('[:foo, :bar] - [:bar]', insns: %i[opt_minus])
+ end
+
+ def test_compile_opt_or
+ assert_compiles('1 | 2', insns: %i[opt_or])
+ assert_compiles('[:foo] | [:bar]', insns: %i[opt_or])
+ end
+
+ def test_compile_opt_and
+ assert_compiles('1 & 2', insns: %i[opt_and])
+ assert_compiles('[:foo, :bar] & [:bar]', insns: %i[opt_and])
+ end
+
+ def test_compile_set_and_get_global
+ assert_compiles('$foo = 123; $foo', insns: %i[setglobal], result: 123)
+ end
+
+ def test_compile_putspecialobject
+ assert_compiles('-> {}', insns: %i[putspecialobject])
+ end
+
+ def test_compile_tostring
+ assert_no_exits('"i am a string #{true}"')
+ end
+
+ def test_compile_opt_aset
+ assert_compiles('[1,2,3][2] = 4', insns: %i[opt_aset])
+ assert_compiles('{}[:foo] = :bar', insns: %i[opt_aset])
+ assert_compiles('[1,2,3][0..-1] = []', insns: %i[opt_aset])
+ assert_compiles('"foo"[3] = "d"', insns: %i[opt_aset])
+ end
+
+ def test_compile_attr_set
+ assert_no_exits(<<~EORB)
+ class Foo
+ attr_accessor :bar
+ end
+
+ foo = Foo.new
+ foo.bar = 3
+ foo.bar = 3
+ foo.bar = 3
+ foo.bar = 3
+ EORB
+ end
+
+ def test_compile_regexp
+ assert_no_exits('/#{true}/')
+ end
+
+ def test_compile_dynamic_symbol
+ assert_compiles(':"#{"foo"}"', insns: %i[intern])
+ assert_compiles('s = "bar"; :"foo#{s}"', insns: %i[intern])
+ end
+
+ def test_getlocal_with_level
+ assert_compiles(<<~RUBY, insns: %i[getlocal opt_plus], result: [[7]])
+ def foo(foo, bar)
+ [1].map do |x|
+ [1].map do |y|
+ foo + bar
+ end
+ end
+ end
+
+ foo(5, 2)
+ RUBY
+ end
+
+ def test_setlocal_with_level
+ assert_no_exits(<<~RUBY)
+ def sum(arr)
+ sum = 0
+ arr.each do |x|
+ sum += x
+ end
+ sum
+ end
+
+ sum([1,2,3])
+ RUBY
+ end
+
+ def test_string_then_nil
+ assert_compiles(<<~RUBY, insns: %i[opt_nil_p], result: true)
+ def foo(val)
+ val.nil?
+ end
+
+ foo("foo")
+ foo(nil)
+ RUBY
+ end
+
+ def test_nil_then_string
+ assert_compiles(<<~RUBY, insns: %i[opt_nil_p], result: false)
+ def foo(val)
+ val.nil?
+ end
+
+ foo(nil)
+ foo("foo")
+ RUBY
+ end
+
+ def test_opt_length_in_method
+ assert_compiles(<<~RUBY, insns: %i[opt_length], result: 5)
+ def foo(str)
+ str.length
+ end
+
+ foo("hello, ")
+ foo("world")
+ RUBY
+ end
+
+ def test_opt_regexpmatch2
+ assert_compiles(<<~RUBY, insns: %i[opt_regexpmatch2], result: 0)
+ def foo(str)
+ str =~ /foo/
+ end
+
+ foo("foobar")
+ RUBY
+ end
+
+ def test_expandarray
+ assert_compiles(<<~'RUBY', insns: %i[expandarray], result: [1, 2])
+ a, b = [1, 2]
+ RUBY
+ end
+
+ def test_expandarray_nil
+ assert_compiles(<<~'RUBY', insns: %i[expandarray], result: [nil, nil])
+ a, b = nil
+ [a, b]
+ RUBY
+ end
+
+ def test_getspecial_backref
+ assert_compiles("'foo' =~ /(o)./; $&", insns: %i[getspecial], result: "oo")
+ assert_compiles("'foo' =~ /(o)./; $`", insns: %i[getspecial], result: "f")
+ assert_compiles("'foo' =~ /(o)./; $'", insns: %i[getspecial], result: "")
+ assert_compiles("'foo' =~ /(o)./; $+", insns: %i[getspecial], result: "o")
+ assert_compiles("'foo' =~ /(o)./; $1", insns: %i[getspecial], result: "o")
+ assert_compiles("'foo' =~ /(o)./; $2", insns: %i[getspecial], result: nil)
+ end
+
+ def test_compile_opt_getinlinecache
+ assert_compiles(<<~RUBY, insns: %i[opt_getinlinecache], result: 123, min_calls: 2)
+ def get_foo
+ FOO
+ end
+
+ FOO = 123
+
+ get_foo # warm inline cache
+ get_foo
+ RUBY
+ end
+
+ def test_opt_getinlinecache_slowpath
+ assert_compiles(<<~RUBY, exits: { opt_getinlinecache: 1 }, result: [42, 42, 1, 1], min_calls: 2)
+ class A
+ FOO = 42
+ class << self
+ def foo
+ _foo = nil
+ FOO
+ end
+ end
+ end
+
+ result = []
+
+ result << A.foo
+ result << A.foo
+
+ class << A
+ FOO = 1
+ end
+
+ result << A.foo
+ result << A.foo
+
+ result
+ RUBY
+ end
+
+ def test_string_interpolation
+ assert_compiles(<<~'RUBY', insns: %i[objtostring anytostring concatstrings], result: "foobar", min_calls: 2)
+ def make_str(foo, bar)
+ "#{foo}#{bar}"
+ end
+
+ make_str("foo", "bar")
+ make_str("foo", "bar")
+ RUBY
+ end
+
+ def test_string_interpolation_cast
+ assert_compiles(<<~'RUBY', insns: %i[objtostring anytostring concatstrings], result: "123")
+ def make_str(foo, bar)
+ "#{foo}#{bar}"
+ end
+
+ make_str(1, 23)
+ RUBY
+ end
+
+ def test_checkkeyword
+ assert_compiles(<<~'RUBY', insns: %i[checkkeyword], result: [2, 5])
+ def foo(foo: 1+1)
+ foo
+ end
+
+ [foo, foo(foo: 5)]
+ RUBY
+ end
+
+ def test_struct_aref
+ assert_compiles(<<~RUBY)
+ def foo(obj)
+ obj.foo
+ obj.bar
+ end
+
+ Foo = Struct.new(:foo, :bar)
+ foo(Foo.new(123))
+ foo(Foo.new(123))
+ RUBY
+ end
+
+ def test_struct_aset
+ assert_compiles(<<~RUBY)
+ def foo(obj)
+ obj.foo = 123
+ obj.bar = 123
+ end
+
+ Foo = Struct.new(:foo, :bar)
+ foo(Foo.new(123))
+ foo(Foo.new(123))
+ RUBY
+ end
+
+ def test_super_iseq
+ assert_compiles(<<~'RUBY', insns: %i[invokesuper opt_plus opt_mult], result: 15)
+ class A
+ def foo
+ 1 + 2
+ end
+ end
+
+ class B < A
+ def foo
+ super * 5
+ end
+ end
+
+ B.new.foo
+ RUBY
+ end
+
+ def test_super_cfunc
+ assert_compiles(<<~'RUBY', insns: %i[invokesuper], result: "Hello")
+ class Gnirts < String
+ def initialize
+ super(-"olleH")
+ end
+
+ def to_s
+ super().reverse
+ end
+ end
+
+ Gnirts.new.to_s
+ RUBY
+ end
+
+ # Tests calling a variadic cfunc with many args
+ def test_build_large_struct
+ assert_compiles(<<~RUBY, insns: %i[opt_send_without_block], min_calls: 2)
+ ::Foo = Struct.new(:a, :b, :c, :d, :e, :f, :g, :h)
+
+ def build_foo
+ ::Foo.new(:a, :b, :c, :d, :e, :f, :g, :h)
+ end
+
+ build_foo
+ build_foo
+ RUBY
+ end
+
+ def test_fib_recursion
+ assert_compiles(<<~'RUBY', insns: %i[opt_le opt_minus opt_plus opt_send_without_block], result: 34)
+ def fib(n)
+ return n if n <= 1
+ fib(n-1) + fib(n-2)
+ end
+
+ fib(9)
+ RUBY
+ end
+
+ def test_optarg_and_kwarg
+ assert_no_exits(<<~'RUBY')
+ def opt_and_kwarg(a, b=nil, c: nil)
+ end
+
+ 2.times do
+ opt_and_kwarg(1, 2, c: 3)
+ end
+ RUBY
+ end
+
+ def test_ctx_different_mappings
+ # regression test simplified from URI::Generic#hostname=
+ assert_compiles(<<~'RUBY', frozen_string_literal: true)
+ def foo(v)
+ !(v&.start_with?('[')) && v&.index(':')
+ end
+
+ foo(nil)
+ foo("example.com")
+ RUBY
+ end
+
+ def test_no_excessive_opt_getinlinecache_invalidation
+ assert_compiles(<<~'RUBY', exits: :any, result: :ok)
+ objects = [Object.new, Object.new]
+
+ objects.each do |o|
+ class << o
+ def foo
+ Object
+ end
+ end
+ end
+
+ 9000.times {
+ objects[0].foo
+ objects[1].foo
+ }
+
+ stats = RubyVM::YJIT.runtime_stats
+ return :ok unless stats[:all_stats]
+ return :ok if stats[:invalidation_count] < 10
+
+ :fail
+ RUBY
+ end
+
+ def assert_no_exits(script)
+ assert_compiles(script)
+ end
+
+ ANY = Object.new
+ def assert_compiles(test_script, insns: [], min_calls: 1, stdout: nil, exits: {}, result: ANY, frozen_string_literal: nil)
+ reset_stats = <<~RUBY
+ RubyVM::YJIT.runtime_stats
+ RubyVM::YJIT.reset_stats!
+ RUBY
+
+ write_results = <<~RUBY
+ stats = RubyVM::YJIT.runtime_stats
+
+ def collect_blocks(blocks)
+ blocks.sort_by(&:address).map { |b| [b.iseq_start_index, b.iseq_end_index] }
+ end
+
+ def collect_iseqs(iseq)
+ iseq_array = iseq.to_a
+ insns = iseq_array.last.grep(Array)
+ blocks = RubyVM::YJIT.blocks_for(iseq)
+ h = {
+ name: iseq_array[5],
+ insns: insns,
+ blocks: collect_blocks(blocks),
+ }
+ arr = [h]
+ iseq.each_child { |c| arr.concat collect_iseqs(c) }
+ arr
+ end
+
+ iseq = RubyVM::InstructionSequence.of(_test_proc)
+ IO.open(3).write Marshal.dump({
+ result: #{result == ANY ? "nil" : "result"},
+ stats: stats,
+ iseqs: collect_iseqs(iseq),
+ disasm: iseq.disasm
+ })
+ RUBY
+
+ script = <<~RUBY
+ #{"# frozen_string_literal: true" if frozen_string_literal}
+ _test_proc = -> {
+ #{test_script}
+ }
+ #{reset_stats}
+ result = _test_proc.call
+ #{write_results}
+ RUBY
+
+ status, out, err, stats = eval_with_jit(script, min_calls: min_calls)
+
+ assert status.success?, "exited with status #{status.to_i}, stderr:\n#{err}"
+
+ assert_equal stdout.chomp, out.chomp if stdout
+
+ unless ANY.equal?(result)
+ assert_equal result, stats[:result]
+ end
+
+ runtime_stats = stats[:stats]
+ iseqs = stats[:iseqs]
+ disasm = stats[:disasm]
+
+ # Only available when RUBY_DEBUG enabled
+ if runtime_stats[:all_stats]
+ recorded_exits = runtime_stats.select { |k, v| k.to_s.start_with?("exit_") }
+ recorded_exits = recorded_exits.reject { |k, v| v == 0 }
+
+ recorded_exits.transform_keys! { |k| k.to_s.gsub("exit_", "").to_sym }
+ if exits != :any && exits != recorded_exits
+ flunk "Expected #{exits.empty? ? "no" : exits.inspect} exits" \
+ ", but got\n#{recorded_exits.inspect}"
+ end
+ end
+
+ # Only available when RUBY_DEBUG enabled
+ if runtime_stats[:all_stats]
+ missed_insns = insns.dup
+ all_compiled_blocks = {}
+ iseqs.each do |iseq|
+ compiled_blocks = iseq[:blocks].map { |from, to| (from...to) }
+ all_compiled_blocks[iseq[:name]] = compiled_blocks
+ compiled_insns = iseq[:insns]
+ next_idx = 0
+ compiled_insns.map! do |insn|
+ # TODO: not sure this is accurate for determining insn size
+ idx = next_idx
+ next_idx += insn.length
+ [idx, *insn]
+ end
+
+ compiled_insns.each do |idx, op, *arguments|
+ next unless missed_insns.include?(op)
+ next unless compiled_blocks.any? { |block| block === idx }
+
+ # This instruction was compiled
+ missed_insns.delete(op)
+ end
+ end
+
+ unless missed_insns.empty?
+ flunk "Expected to compile instructions #{missed_insns.join(", ")} but didn't.\nCompiled ranges: #{all_compiled_blocks.inspect}\niseq:\n#{disasm}"
+ end
+ end
+ end
+
+ def eval_with_jit(script, min_calls: 1, timeout: 1000)
+ args = [
+ "--disable-gems",
+ "--yjit-call-threshold=#{min_calls}",
+ "--yjit-stats"
+ ]
+ args << "-e" << script
+ stats_r, stats_w = IO.pipe
+ out, err, status = EnvUtil.invoke_ruby(args,
+ '', true, true, timeout: timeout, ios: {3 => stats_w}
+ )
+ stats_w.close
+ stats = stats_r.read
+ stats = Marshal.load(stats) if !stats.empty?
+ stats_r.close
+ [status, out, err, stats]
+ end
+
+ def test_bug_19316
+ n = 2 ** 64
+ # foo's extra param and the splats are relevant
+ assert_compiles(<<~'RUBY', result: [[n, -n], [n, -n]], exits: :any)
+ def foo(_, a, b, c)
+ [a & b, ~c]
+ end
+
+ n = 2 ** 64
+ args = [0, -n, n, n-1]
+
+ GC.stress = true
+ [foo(*args), foo(*args)]
+ RUBY
+ end
+end