summaryrefslogtreecommitdiff
path: root/test/ruby
diff options
context:
space:
mode:
Diffstat (limited to 'test/ruby')
-rw-r--r--test/ruby/allpairs.rb2
-rw-r--r--test/ruby/beginmainend.rb5
-rw-r--r--test/ruby/bug-11928.rb14
-rw-r--r--test/ruby/bug-13526.rb22
-rw-r--r--test/ruby/enc/test_big5.rb1
-rw-r--r--test/ruby/enc/test_case_comprehensive.rb307
-rw-r--r--test/ruby/enc/test_case_mapping.rb221
-rw-r--r--test/ruby/enc/test_case_options.rb81
-rw-r--r--test/ruby/enc/test_cp949.rb1
-rw-r--r--test/ruby/enc/test_emoji.rb7
-rw-r--r--test/ruby/enc/test_emoji_breaks.rb119
-rw-r--r--test/ruby/enc/test_euc_jp.rb3
-rw-r--r--test/ruby/enc/test_euc_kr.rb9
-rw-r--r--test/ruby/enc/test_euc_tw.rb1
-rw-r--r--test/ruby/enc/test_gb18030.rb1
-rw-r--r--test/ruby/enc/test_gbk.rb1
-rw-r--r--test/ruby/enc/test_grapheme_breaks.rb92
-rw-r--r--test/ruby/enc/test_iso_8859.rb7
-rw-r--r--test/ruby/enc/test_koi8.rb1
-rw-r--r--test/ruby/enc/test_regex_casefold.rb120
-rw-r--r--test/ruby/enc/test_shift_jis.rb5
-rw-r--r--test/ruby/enc/test_utf16.rb123
-rw-r--r--test/ruby/enc/test_utf32.rb71
-rw-r--r--test/ruby/enc/test_windows_1251.rb1
-rw-r--r--test/ruby/enc/test_windows_1252.rb26
-rw-r--r--test/ruby/endblockwarn_rb12
-rw-r--r--test/ruby/envutil.rb190
-rw-r--r--test/ruby/lbtest.rb17
-rw-r--r--test/ruby/marshaltestlib.rb149
-rw-r--r--test/ruby/sentence.rb1
-rw-r--r--test/ruby/test_alias.rb149
-rw-r--r--test/ruby/test_argf.rb910
-rw-r--r--test/ruby/test_arithmetic_sequence.rb464
-rw-r--r--test/ruby/test_arity.rb70
-rw-r--r--test/ruby/test_array.rb1370
-rw-r--r--test/ruby/test_assignment.rb98
-rw-r--r--test/ruby/test_ast.rb283
-rw-r--r--test/ruby/test_autoload.rb361
-rw-r--r--test/ruby/test_backtrace.rb332
-rw-r--r--test/ruby/test_basicinstructions.rb54
-rw-r--r--test/ruby/test_beginendblock.rb226
-rw-r--r--test/ruby/test_bignum.rb504
-rw-r--r--test/ruby/test_call.rb83
-rw-r--r--test/ruby/test_case.rb63
-rw-r--r--test/ruby/test_class.rb469
-rw-r--r--test/ruby/test_clone.rb36
-rw-r--r--test/ruby/test_comparable.rb45
-rw-r--r--test/ruby/test_complex.rb806
-rw-r--r--test/ruby/test_complex2.rb3
-rw-r--r--test/ruby/test_complexrational.rb35
-rw-r--r--test/ruby/test_condition.rb1
-rw-r--r--test/ruby/test_const.rb26
-rw-r--r--test/ruby/test_continuation.rb76
-rw-r--r--test/ruby/test_default_gems.rb15
-rw-r--r--test/ruby/test_defined.rb224
-rw-r--r--test/ruby/test_dir.rb328
-rw-r--r--test/ruby/test_dir_m17n.rb389
-rw-r--r--test/ruby/test_econv.rb79
-rw-r--r--test/ruby/test_encoding.rb40
-rw-r--r--test/ruby/test_enum.rb843
-rw-r--r--test/ruby/test_enumerator.rb429
-rw-r--r--test/ruby/test_env.rb266
-rw-r--r--test/ruby/test_eval.rb374
-rw-r--r--test/ruby/test_exception.rb1205
-rw-r--r--test/ruby/test_fiber.rb266
-rw-r--r--test/ruby/test_file.rb466
-rw-r--r--test/ruby/test_file_exhaustive.rb1743
-rw-r--r--test/ruby/test_fixnum.rb180
-rw-r--r--test/ruby/test_flip.rb84
-rw-r--r--test/ruby/test_float.rb611
-rw-r--r--test/ruby/test_fnmatch.rb221
-rw-r--r--test/ruby/test_gc.rb397
-rw-r--r--test/ruby/test_hash.rb1191
-rw-r--r--test/ruby/test_ifunless.rb17
-rw-r--r--test/ruby/test_integer.rb528
-rw-r--r--test/ruby/test_integer_comb.rb112
-rw-r--r--test/ruby/test_io.rb3348
-rw-r--r--test/ruby/test_io_m17n.rb1631
-rw-r--r--test/ruby/test_iseq.rb539
-rw-r--r--test/ruby/test_iterator.rb55
-rw-r--r--test/ruby/test_jit.rb1047
-rw-r--r--test/ruby/test_key_error.rb42
-rw-r--r--test/ruby/test_keyword.rb748
-rw-r--r--test/ruby/test_lambda.rb129
-rw-r--r--test/ruby/test_lazy_enumerator.rb581
-rw-r--r--test/ruby/test_literal.rb371
-rw-r--r--test/ruby/test_m17n.rb529
-rw-r--r--test/ruby/test_m17n_comb.rb399
-rw-r--r--test/ruby/test_marshal.rb471
-rw-r--r--test/ruby/test_math.rb183
-rw-r--r--test/ruby/test_metaclass.rb1
-rw-r--r--test/ruby/test_method.rb825
-rw-r--r--test/ruby/test_mixed_unicode_escapes.rb11
-rw-r--r--test/ruby/test_module.rb1763
-rw-r--r--test/ruby/test_not.rb13
-rw-r--r--test/ruby/test_notimp.rb50
-rw-r--r--test/ruby/test_numeric.rb397
-rw-r--r--test/ruby/test_object.rb666
-rw-r--r--test/ruby/test_objectspace.rb148
-rw-r--r--test/ruby/test_optimization.rb785
-rw-r--r--test/ruby/test_pack.rb418
-rw-r--r--test/ruby/test_parse.rb590
-rw-r--r--test/ruby/test_path.rb21
-rw-r--r--test/ruby/test_pipe.rb33
-rw-r--r--test/ruby/test_primitive.rb23
-rw-r--r--test/ruby/test_proc.rb786
-rw-r--r--test/ruby/test_process.rb1591
-rw-r--r--test/ruby/test_rand.rb209
-rw-r--r--test/ruby/test_range.rb622
-rw-r--r--test/ruby/test_rational.rb800
-rw-r--r--test/ruby/test_rational2.rb1
-rw-r--r--test/ruby/test_readpartial.rb9
-rw-r--r--test/ruby/test_refinement.rb2263
-rw-r--r--test/ruby/test_regexp.rb583
-rw-r--r--test/ruby/test_require.rb877
-rw-r--r--test/ruby/test_rubyoptions.rb887
-rw-r--r--test/ruby/test_rubyvm.rb18
-rw-r--r--test/ruby/test_rubyvm_mjit.rb75
-rw-r--r--test/ruby/test_settracefunc.rb1918
-rw-r--r--test/ruby/test_signal.rb318
-rw-r--r--test/ruby/test_sleep.rb10
-rw-r--r--test/ruby/test_sprintf.rb256
-rw-r--r--test/ruby/test_sprintf_comb.rb47
-rw-r--r--test/ruby/test_string.rb1666
-rw-r--r--test/ruby/test_stringchar.rb56
-rw-r--r--test/ruby/test_struct.rb316
-rw-r--r--test/ruby/test_super.rb435
-rw-r--r--test/ruby/test_symbol.rb447
-rw-r--r--test/ruby/test_syntax.rb1363
-rw-r--r--test/ruby/test_system.rb112
-rw-r--r--test/ruby/test_thread.rb1242
-rw-r--r--test/ruby/test_thread_cv.rb245
-rw-r--r--test/ruby/test_thread_queue.rb619
-rw-r--r--test/ruby/test_threadgroup.rb57
-rw-r--r--test/ruby/test_time.rb740
-rw-r--r--test/ruby/test_time_tz.rb501
-rw-r--r--test/ruby/test_trace.rb13
-rw-r--r--test/ruby/test_transcode.rb376
-rw-r--r--test/ruby/test_undef.rb3
-rw-r--r--test/ruby/test_unicode_escape.rb23
-rw-r--r--test/ruby/test_variable.rb85
-rw-r--r--test/ruby/test_vm_dump.rb21
-rw-r--r--test/ruby/test_weakmap.rb144
-rw-r--r--test/ruby/test_whileuntil.rb13
-rw-r--r--test/ruby/test_yield.rb59
-rw-r--r--test/ruby/ut_eof.rb5
146 files changed, 47059 insertions, 6651 deletions
diff --git a/test/ruby/allpairs.rb b/test/ruby/allpairs.rb
index 6cb2729b19..e5893e252a 100644
--- a/test/ruby/allpairs.rb
+++ b/test/ruby/allpairs.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
module AllPairs
module_function
@@ -66,7 +67,6 @@ module AllPairs
def each_index(*vs)
n = vs.length
max_v = vs.max
- prime = make_prime(max_v)
h = {}
make_large_block(max_v, n) {|row|
row = vs.zip(row).map {|v, i| i % v }
diff --git a/test/ruby/beginmainend.rb b/test/ruby/beginmainend.rb
index 6cdfb15ea6..b6de5d65fd 100644
--- a/test/ruby/beginmainend.rb
+++ b/test/ruby/beginmainend.rb
@@ -1,8 +1,7 @@
-errout = ARGV.shift
-
+# frozen_string_literal: false
BEGIN {
puts "b1"
- local_begin1 = "local_begin1"
+ # local_begin1 = "local_begin1"
$global_begin1 = "global_begin1"
ConstBegin1 = "ConstBegin1"
}
diff --git a/test/ruby/bug-11928.rb b/test/ruby/bug-11928.rb
new file mode 100644
index 0000000000..72b3b0f8ed
--- /dev/null
+++ b/test/ruby/bug-11928.rb
@@ -0,0 +1,14 @@
+class Segfault
+ at_exit { Segfault.new.segfault }
+
+ define_method 'segfault' do
+ n = 11928
+ v = nil
+ i = 0
+ while i < n
+ i += 1
+ v = (foo rescue $!).local_variables
+ end
+ assert_equal(%i[i n v], v.sort)
+ end
+end
diff --git a/test/ruby/bug-13526.rb b/test/ruby/bug-13526.rb
new file mode 100644
index 0000000000..50c6c67a7d
--- /dev/null
+++ b/test/ruby/bug-13526.rb
@@ -0,0 +1,22 @@
+# From https://bugs.ruby-lang.org/issues/13526#note-1
+
+Thread.report_on_exception = true
+
+sleep if $load
+$load = true
+
+n = 10
+threads = Array.new(n) do
+ Thread.new do
+ begin
+ autoload :Foo, File.expand_path(__FILE__)
+ Thread.pass
+ Foo
+ ensure
+ Thread.pass
+ end
+ end
+end
+
+Thread.pass until threads.all?(&:stop?)
+1000.times { Thread.pass }
diff --git a/test/ruby/enc/test_big5.rb b/test/ruby/enc/test_big5.rb
index e8fe0270a8..5dcf93e8e3 100644
--- a/test/ruby/enc/test_big5.rb
+++ b/test/ruby/enc/test_big5.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require "test/unit"
class TestBig5 < Test::Unit::TestCase
diff --git a/test/ruby/enc/test_case_comprehensive.rb b/test/ruby/enc/test_case_comprehensive.rb
new file mode 100644
index 0000000000..bde47017a2
--- /dev/null
+++ b/test/ruby/enc/test_case_comprehensive.rb
@@ -0,0 +1,307 @@
+# frozen_string_literal: true
+# Copyright © 2016 Martin J. Dürst (duerst@it.aoyama.ac.jp)
+
+require "test/unit"
+
+class TestComprehensiveCaseMapping < 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") ? "#{path}/ucd" : path
+
+ def self.hex2utf8(s)
+ s.split(' ').map { |c| c.to_i(16) }.pack('U*')
+ end
+
+ def self.expand_filename(basename)
+ File.expand_path("#{UNICODE_DATA_PATH}/#{basename}.txt", __dir__)
+ end
+
+ def self.data_files_available?
+ %w[UnicodeData CaseFolding SpecialCasing].all? do |f|
+ File.exist?(expand_filename(f))
+ end
+ end
+
+ def test_data_files_available
+ unless TestComprehensiveCaseMapping.data_files_available?
+ skip "Unicode data files not available in #{UNICODE_DATA_PATH}."
+ end
+ end
+end
+
+TestComprehensiveCaseMapping.data_files_available? and class TestComprehensiveCaseMapping
+ (CaseTest = Struct.new(:method_name, :attributes, :first_data, :follow_data)).class_eval do
+ def initialize(method_name, attributes, first_data, follow_data=first_data)
+ super
+ end
+ end
+
+ def self.read_data_file(filename)
+ IO.foreach(expand_filename(filename), encoding: Encoding::ASCII_8BIT) do |line|
+ if $. == 1
+ if filename == 'UnicodeData'
+ elsif line.start_with?("# #{filename}-#{UNICODE_VERSION}.txt")
+ else
+ raise "File Version Mismatch"
+ end
+ end
+ next if /\A(?:[\#@]|\s*\z)|Surrogate/.match?(line)
+ data = line.chomp.split('#')[0].split(/;\s*/, 15)
+ code = data[0].to_i(16).chr(Encoding::UTF_8)
+ yield code, data
+ end
+ end
+
+ def self.read_data
+ @@codepoints = []
+
+ downcase = Hash.new { |h, c| c }
+ upcase = Hash.new { |h, c| c }
+ titlecase = Hash.new { |h, c| c }
+ casefold = Hash.new { |h, c| c }
+ swapcase = Hash.new { |h, c| c }
+ turkic_upcase = Hash.new { |h, c| upcase[c] }
+ turkic_downcase = Hash.new { |h, c| downcase[c] }
+ turkic_titlecase = Hash.new { |h, c| titlecase[c] }
+ turkic_swapcase = Hash.new { |h, c| swapcase[c] }
+ ascii_upcase = Hash.new { |h, c| /\A[a-zA-Z]\z/.match?(c) ? upcase[c] : c }
+ ascii_downcase = Hash.new { |h, c| /\A[a-zA-Z]\z/.match?(c) ? downcase[c] : c }
+ ascii_titlecase = Hash.new { |h, c| /\A[a-zA-Z]\z/.match?(c) ? titlecase[c] : c }
+ ascii_swapcase = Hash.new { |h, c| /\A[a-z]\z/.match?(c) ? upcase[c] : (/\A[A-Z]\z/.match?(c) ? downcase[c] : c) }
+
+ read_data_file('UnicodeData') do |code, data|
+ @@codepoints << code
+ upcase[code] = hex2utf8 data[12] unless data[12].empty?
+ downcase[code] = hex2utf8 data[13] unless data[13].empty?
+ if code>="\u1C90" and code<="\u1CBF" # exception for Georgian: use lowercase for titlecase
+ titlecase[code] = hex2utf8(data[13]) unless data[13].empty?
+ else
+ titlecase[code] = hex2utf8 data[14] unless data[14].empty?
+ end
+ end
+ read_data_file('CaseFolding') do |code, data|
+ casefold[code] = hex2utf8(data[2]) if data[1] =~ /^[CF]$/
+ end
+
+ read_data_file('SpecialCasing') do |code, data|
+ case data[4]
+ when ''
+ upcase[code] = hex2utf8 data[3]
+ downcase[code] = hex2utf8 data[1]
+ titlecase[code] = hex2utf8 data[2]
+ when /\Atr\s*/
+ if data[4]!='tr After_I'
+ turkic_upcase[code] = hex2utf8 data[3]
+ turkic_downcase[code] = hex2utf8 data[1]
+ turkic_titlecase[code] = hex2utf8 data[2]
+ end
+ end
+ end
+
+ @@codepoints.each do |c|
+ if upcase[c] != c
+ if downcase[c] != c
+ swapcase[c] = turkic_swapcase[c] =
+ case c
+ when "\u01C5" then "\u0064\u017D"
+ when "\u01C8" then "\u006C\u004A"
+ when "\u01CB" then "\u006E\u004A"
+ when "\u01F2" then "\u0064\u005A"
+ else # Greek
+ downcase[upcase[c][0]] + "\u0399"
+ end
+ else
+ swapcase[c] = upcase[c]
+ turkic_swapcase[c] = turkic_upcase[c]
+ end
+ else
+ if downcase[c] != c
+ swapcase[c] = downcase[c]
+ turkic_swapcase[c] = turkic_downcase[c]
+ end
+ end
+ end
+
+ [
+ CaseTest.new(:downcase, [], downcase),
+ CaseTest.new(:upcase, [], upcase),
+ CaseTest.new(:capitalize, [], titlecase, downcase),
+ CaseTest.new(:swapcase, [], swapcase),
+ CaseTest.new(:downcase, [:fold], casefold),
+ CaseTest.new(:upcase, [:turkic], turkic_upcase),
+ CaseTest.new(:downcase, [:turkic], turkic_downcase),
+ CaseTest.new(:capitalize, [:turkic], turkic_titlecase, turkic_downcase),
+ CaseTest.new(:swapcase, [:turkic], turkic_swapcase),
+ CaseTest.new(:upcase, [:ascii], ascii_upcase),
+ CaseTest.new(:downcase, [:ascii], ascii_downcase),
+ CaseTest.new(:capitalize, [:ascii], ascii_titlecase, ascii_downcase),
+ CaseTest.new(:swapcase, [:ascii], ascii_swapcase),
+ ]
+ end
+
+ def self.all_tests
+ @@tests ||= read_data
+ rescue Errno::ENOENT
+ @@tests ||= []
+ end
+
+ def self.generate_unicode_case_mapping_tests(encoding)
+ all_tests.each do |test|
+ attributes = test.attributes.map(&:to_s).join '-'
+ attributes.prepend '_' unless attributes.empty?
+ define_method "test_#{encoding}_#{test.method_name}#{attributes}" do
+ @@codepoints.each do |code|
+ source = code.encode(encoding) * 5
+ target = "#{test.first_data[code]}#{test.follow_data[code]*4}".encode(encoding)
+ result = source.__send__(test.method_name, *test.attributes)
+ assert_equal target, result,
+ proc{"from #{code*5} (#{source.dump}) expected #{target.dump} but was #{result.dump}"}
+ end
+ end
+ end
+ end
+
+ def self.generate_case_mapping_tests(encoding)
+ all_tests
+ # preselect codepoints to speed up testing for small encodings
+ codepoints = @@codepoints.select do |code|
+ begin
+ code.encode(encoding)
+ true
+ rescue Encoding::UndefinedConversionError
+ false
+ end
+ end
+ all_tests.each do |test|
+ attributes = test.attributes.map(&:to_s).join '-'
+ attributes.prepend '_' unless attributes.empty?
+ define_method "test_#{encoding}_#{test.method_name}#{attributes}" do
+ codepoints.each do |code|
+ begin
+ source = code.encode(encoding) * 5
+ begin
+ target = "#{test.first_data[code]}#{test.follow_data[code]*4}".encode(encoding)
+ rescue Encoding::UndefinedConversionError
+ if test.first_data[code]=="i\u0307" or test.follow_data[code]=="i\u0307" # explicit dot above
+ first_data = test.first_data[code]=="i\u0307" ? 'i' : test.first_data[code]
+ follow_data = test.follow_data[code]=="i\u0307" ? 'i' : test.follow_data[code]
+ target = "#{first_data}#{follow_data*4}".encode(encoding)
+ elsif code =~ /i|I/ # special case for Turkic
+ raise
+ else
+ target = source
+ end
+ end
+ result = source.send(test.method_name, *test.attributes)
+ assert_equal target, result,
+ proc{"from #{code*5} (#{source.dump}) expected #{target.dump} but was #{result.dump}"}
+ rescue Encoding::UndefinedConversionError
+ end
+ end
+ end
+ end
+ end
+
+ # test for encodings that don't yet (or will never) deal with non-ASCII characters
+ def self.generate_ascii_only_case_mapping_tests(encoding)
+ all_tests
+ # preselect codepoints to speed up testing for small encodings
+ codepoints = @@codepoints.select do |code|
+ begin
+ code.encode(encoding)
+ true
+ rescue Encoding::UndefinedConversionError
+ false
+ end
+ end
+ define_method "test_#{encoding}_upcase" do
+ codepoints.each do |code|
+ begin
+ source = code.encode(encoding) * 5
+ target = source.tr 'a-z', 'A-Z'
+ result = source.upcase
+ assert_equal target, result,
+ "from #{code*5} (#{source.dump}) expected #{target.dump} but was #{result.dump}"
+ rescue Encoding::UndefinedConversionError
+ end
+ end
+ end
+ define_method "test_#{encoding}_downcase" do
+ codepoints.each do |code|
+ begin
+ source = code.encode(encoding) * 5
+ target = source.tr 'A-Z', 'a-z'
+ result = source.downcase
+ assert_equal target, result,
+ "from #{code*5} (#{source.dump}) expected #{target.dump} but was #{result.dump}"
+ rescue Encoding::UndefinedConversionError
+ end
+ end
+ end
+ define_method "test_#{encoding}_capitalize" do
+ codepoints.each do |code|
+ begin
+ source = code.encode(encoding) * 5
+ target = source[0].tr('a-z', 'A-Z') + source[1..-1].tr('A-Z', 'a-z')
+ result = source.capitalize
+ assert_equal target, result,
+ "from #{code*5} (#{source.dump}) expected #{target.dump} but was #{result.dump}"
+ rescue Encoding::UndefinedConversionError
+ end
+ end
+ end
+ define_method "test_#{encoding}_swapcase" do
+ codepoints.each do |code|
+ begin
+ source = code.encode(encoding) * 5
+ target = source.tr('a-zA-Z', 'A-Za-z')
+ result = source.swapcase
+ assert_equal target, result,
+ "from #{code*5} (#{source.dump}) expected #{target.dump} but was #{result.dump}"
+ rescue Encoding::UndefinedConversionError
+ end
+ end
+ end
+ end
+
+ generate_case_mapping_tests 'US-ASCII'
+ generate_case_mapping_tests 'ASCII-8BIT'
+ generate_case_mapping_tests 'ISO-8859-1'
+ generate_case_mapping_tests 'ISO-8859-2'
+ generate_case_mapping_tests 'ISO-8859-3'
+ generate_case_mapping_tests 'ISO-8859-4'
+ generate_case_mapping_tests 'ISO-8859-5'
+ generate_case_mapping_tests 'ISO-8859-6'
+ generate_case_mapping_tests 'ISO-8859-7'
+ generate_case_mapping_tests 'ISO-8859-8'
+ generate_case_mapping_tests 'ISO-8859-9'
+ generate_case_mapping_tests 'ISO-8859-10'
+ generate_case_mapping_tests 'ISO-8859-11'
+ generate_case_mapping_tests 'ISO-8859-13'
+ generate_case_mapping_tests 'ISO-8859-14'
+ generate_case_mapping_tests 'ISO-8859-15'
+ generate_case_mapping_tests 'ISO-8859-16'
+ generate_ascii_only_case_mapping_tests 'KOI8-R'
+ generate_ascii_only_case_mapping_tests 'KOI8-U'
+ generate_ascii_only_case_mapping_tests 'Big5'
+ generate_ascii_only_case_mapping_tests 'EUC-JP'
+ generate_ascii_only_case_mapping_tests 'EUC-KR'
+ generate_ascii_only_case_mapping_tests 'GB18030'
+ generate_ascii_only_case_mapping_tests 'GB2312'
+ generate_ascii_only_case_mapping_tests 'GBK'
+ generate_ascii_only_case_mapping_tests 'Shift_JIS'
+ generate_ascii_only_case_mapping_tests 'Windows-31J'
+ generate_case_mapping_tests 'Windows-1250'
+ generate_case_mapping_tests 'Windows-1251'
+ generate_case_mapping_tests 'Windows-1252'
+ generate_case_mapping_tests 'Windows-1253'
+ generate_case_mapping_tests 'Windows-1254'
+ generate_case_mapping_tests 'Windows-1255'
+ generate_ascii_only_case_mapping_tests 'Windows-1256'
+ generate_case_mapping_tests 'Windows-1257'
+ generate_unicode_case_mapping_tests 'UTF-8'
+ generate_unicode_case_mapping_tests 'UTF-16BE'
+ generate_unicode_case_mapping_tests 'UTF-16LE'
+ generate_unicode_case_mapping_tests 'UTF-32BE'
+ generate_unicode_case_mapping_tests 'UTF-32LE'
+end
diff --git a/test/ruby/enc/test_case_mapping.rb b/test/ruby/enc/test_case_mapping.rb
new file mode 100644
index 0000000000..aa20531783
--- /dev/null
+++ b/test/ruby/enc/test_case_mapping.rb
@@ -0,0 +1,221 @@
+# Copyright © 2016 Kimihito Matsui (松井 仁人) and Martin J. Dürst (duerst@it.aoyama.ac.jp)
+
+require "test/unit"
+
+# preliminary tests, using as a guard
+# to test new implementation strategy
+class TestCaseMappingPreliminary < Test::Unit::TestCase
+ # checks, including idempotence and non-modification; not always guaranteed
+ def check_upcase_properties(expected, start, *flags)
+ assert_equal expected, start.upcase(*flags)
+ temp = start.dup
+ assert_equal expected, temp.upcase!(*flags) unless expected==temp
+ assert_equal nil, temp.upcase!(*flags) if expected==temp
+ assert_equal expected, expected.upcase(*flags)
+ temp = expected.dup
+ assert_nil temp.upcase!(*flags)
+ end
+
+ def check_downcase_properties(expected, start, *flags)
+ assert_equal expected, start.downcase(*flags)
+ temp = start.dup
+ assert_equal expected, temp.downcase!(*flags) unless expected==temp
+ assert_equal nil, temp.downcase!(*flags) if expected==temp
+ assert_equal expected, expected.downcase(*flags)
+ temp = expected.dup
+ assert_nil temp.downcase!(*flags)
+ end
+
+ def check_capitalize_properties(expected, start, *flags)
+ assert_equal expected, start.capitalize(*flags)
+ temp = start.dup
+ assert_equal expected, temp.capitalize!(*flags) unless expected==temp
+ assert_equal nil, temp.capitalize!(*flags) if expected==temp
+ assert_equal expected, expected.capitalize(*flags)
+ temp = expected.dup
+ assert_nil temp.capitalize!(*flags)
+ end
+
+ def check_capitalize_suffixes(lower, upper)
+ while upper.length > 1
+ lower = lower[1..-1]
+ check_capitalize_properties upper[0]+lower, upper
+ upper = upper[1..-1]
+ end
+ end
+
+ # different properties; careful: roundtrip isn't always guaranteed
+ def check_swapcase_properties(expected, start, *flags)
+ assert_equal expected, start.swapcase(*flags)
+ temp = start
+ assert_equal expected, temp.swapcase!(*flags)
+ assert_equal start, start.swapcase(*flags).swapcase(*flags)
+ assert_equal expected, expected.swapcase(*flags).swapcase(*flags)
+ end
+
+ def test_ascii
+ check_downcase_properties 'yukihiro matsumoto (matz)', 'Yukihiro MATSUMOTO (MATZ)'
+ check_upcase_properties 'YUKIHIRO MATSUMOTO (MATZ)', 'yukihiro matsumoto (matz)'
+ check_capitalize_properties 'Yukihiro matsumoto (matz)', 'yukihiro MATSUMOTO (MATZ)'
+ check_swapcase_properties 'yUKIHIRO matsumoto (MAtz)', 'Yukihiro MATSUMOTO (maTZ)'
+ end
+
+ def test_invalid
+ assert_raise(ArgumentError, "Should not be possible to upcase invalid string.") { "\xEB".force_encoding('UTF-8').upcase }
+ assert_raise(ArgumentError, "Should not be possible to downcase invalid string.") { "\xEB".force_encoding('UTF-8').downcase }
+ assert_raise(ArgumentError, "Should not be possible to capitalize invalid string.") { "\xEB".force_encoding('UTF-8').capitalize }
+ assert_raise(ArgumentError, "Should not be possible to swapcase invalid string.") { "\xEB".force_encoding('UTF-8').swapcase }
+ end
+
+ def test_general
+ check_downcase_properties 'résumé dürst ĭñŧėřŋãţijňőńæłĩżàťïōņ', 'RÉSUMÉ DÜRST ĬÑŦĖŘŊÃŢIJŇŐŃÆŁĨŻÀŤÏŌŅ'
+ check_upcase_properties 'RÉSUMÉ DÜRST ĬÑŦĖŘŊÃŢIJŇŐŃÆŁĨŻÀŤÏŌŅ', 'résumé dürst ĭñŧėřŋãţijňőńæłĩżàťïōņ'
+ check_capitalize_suffixes 'résumé dürst ĭñŧėřŋãţijňőńæłĩżàťïōņ', 'RÉSUMÉ DÜRST ĬÑŦĖŘŊÃŢIJŇŐŃÆŁĨŻÀŤÏŌŅ'
+ check_swapcase_properties 'résumé DÜRST ĭñŧėřŊÃŢIJŇŐŃæłĩżàťïōņ', 'RÉSUMÉ dürst ĬÑŦĖŘŋãţijňőńÆŁĨŻÀŤÏŌŅ'
+ end
+
+ def test_one_way_upcase
+ check_upcase_properties 'ΜΜΜΜΜ', 'µµµµµ' # MICRO SIGN -> Greek Mu
+ check_downcase_properties 'µµµµµ', 'µµµµµ' # MICRO SIGN -> Greek Mu
+ check_capitalize_properties 'Μµµµµ', 'µµµµµ' # MICRO SIGN -> Greek Mu
+ check_capitalize_properties 'Μµµµµ', 'µµµµµ', :turkic # MICRO SIGN -> Greek Mu
+ check_capitalize_properties 'H̱ẖẖẖẖ', 'ẖẖẖẖẖ'
+ check_capitalize_properties 'Βϐϐϐϐ', 'ϐϐϐϐϐ'
+ check_capitalize_properties 'Θϑϑϑϑ', 'ϑϑϑϑϑ'
+ check_capitalize_properties 'Φϕ', 'ϕϕ'
+ check_capitalize_properties 'Πϖ', 'ϖϖ'
+ check_capitalize_properties 'Κϰ', 'ϰϰ'
+ check_capitalize_properties 'Ρϱϱ', 'ϱϱϱ'
+ check_capitalize_properties 'Εϵ', 'ϵϵ'
+ check_capitalize_properties 'Ιͅͅͅͅ', 'ͅͅͅͅͅ'
+ check_capitalize_properties 'Sſſſſ', 'ſſſſſ'
+ end
+
+ def test_various
+ check_upcase_properties 'Μ', 'µ' # MICRO SIGN -> Greek Mu
+ check_downcase_properties 'µµµµµ', 'µµµµµ' # MICRO SIGN
+ check_capitalize_properties 'Ss', 'ß'
+ check_upcase_properties 'SS', 'ß'
+ end
+
+ def test_cherokee
+ check_downcase_properties "\uab70\uab71\uab72\uab73\uab74\uab75\uab76\uab77\uab78\uab79", 'ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩ'
+ check_upcase_properties 'ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩ', "\uab70\uab71\uab72\uab73\uab74\uab75\uab76\uab77\uab78\uab79"
+ check_capitalize_suffixes "\uab70\uab71\uab72\uab73\uab74\uab75\uab76\uab77\uab78\uab79", 'ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩ'
+ assert_equal 'ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩ', 'ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩ'.downcase(:fold)
+ assert_equal 'ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩ', "\uab70\uab71\uab72\uab73\uab74\uab75\uab76\uab77\uab78\uab79".downcase(:fold)
+ end
+
+ def test_titlecase
+ check_downcase_properties 'dz dž lj nj', 'Dz Dž Lj Nj'
+ check_downcase_properties 'dz dž lj nj', 'DZ DŽ LJ NJ'
+ check_upcase_properties 'DZ DŽ LJ NJ', 'Dz Dž Lj Nj'
+ check_upcase_properties 'DZ DŽ LJ NJ', 'dz dž lj nj'
+ check_capitalize_properties 'Dz', 'DZ'
+ check_capitalize_properties 'Dž', 'DŽ'
+ check_capitalize_properties 'Lj', 'LJ'
+ check_capitalize_properties 'Nj', 'NJ'
+ check_capitalize_properties 'Dz', 'dz'
+ check_capitalize_properties 'Dž', 'dž'
+ check_capitalize_properties 'Lj', 'lj'
+ check_capitalize_properties 'Nj', 'nj'
+ end
+
+ def test_swapcase
+ assert_equal 'dZ', 'Dz'.swapcase
+ assert_equal 'dŽ', 'Dž'.swapcase
+ assert_equal 'lJ', 'Lj'.swapcase
+ assert_equal 'nJ', 'Nj'.swapcase
+ assert_equal 'ἀΙ', 'ᾈ'.swapcase
+ assert_equal 'ἣΙ', 'ᾛ'.swapcase
+ assert_equal 'ὧΙ', 'ᾯ'.swapcase
+ assert_equal 'αΙ', 'ᾼ'.swapcase
+ assert_equal 'ηΙ', 'ῌ'.swapcase
+ assert_equal 'ωΙ', 'ῼ'.swapcase
+ end
+
+ def test_ascii_option
+ check_downcase_properties 'yukihiro matsumoto (matz)', 'Yukihiro MATSUMOTO (MATZ)', :ascii
+ check_upcase_properties 'YUKIHIRO MATSUMOTO (MATZ)', 'yukihiro matsumoto (matz)', :ascii
+ check_capitalize_properties 'Yukihiro matsumoto (matz)', 'yukihiro MATSUMOTO (MATZ)', :ascii
+ check_swapcase_properties 'yUKIHIRO matsumoto (MAtz)', 'Yukihiro MATSUMOTO (maTZ)', :ascii
+ check_downcase_properties 'yukİhİro matsumoto (matz)', 'YUKİHİRO MATSUMOTO (MATZ)', :ascii
+ check_downcase_properties 'rÉsumÉ dÜrst ĬÑŦĖŘŊÃŢIJŇŐŃÆŁĨŻÀŤĬŌŅ', 'RÉSUMÉ DÜRST ĬÑŦĖŘŊÃŢIJŇŐŃÆŁĨŻÀŤĬŌŅ', :ascii
+ check_swapcase_properties 'rÉsumÉ dÜrst ĬÑŦĖŘŊÃŢIJŇŐŃÆŁĨŻÀŤĬŌŅ', 'RÉSUMÉ DÜRST ĬÑŦĖŘŊÃŢIJŇŐŃÆŁĨŻÀŤĬŌŅ', :ascii
+ end
+
+ def test_fold_option
+ check_downcase_properties 'ss', 'ß', :fold
+ check_downcase_properties 'fifl', 'fifl', :fold
+ check_downcase_properties 'σ', 'ς', :fold
+ check_downcase_properties 'μ', 'µ', :fold # MICRO SIGN -> Greek mu
+ end
+
+ def test_turcic
+ check_downcase_properties 'yukihiro matsumoto (matz)', 'Yukihiro MATSUMOTO (MATZ)', :turkic
+ check_upcase_properties 'YUKİHİRO MATSUMOTO (MATZ)', 'Yukihiro Matsumoto (matz)', :turkic
+ check_downcase_properties "yuki\u0307hi\u0307ro matsumoto (matz)", 'YUKİHİRO MATSUMOTO (MATZ)'
+ end
+
+ def test_greek
+ check_downcase_properties 'αβγδεζηθικλμνξοπρστυφχψω', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ'
+ check_upcase_properties 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ', 'αβγδεζηθικλμνξοπρστυφχψω'
+ end
+
+ # This test checks against problems when changing the order of mapping results
+ # in some of the entries of the unfolding table (related to
+ # https://bugs.ruby-lang.org/issues/12990).
+ def test_reorder_unfold
+ # GREEK SMALL LETTER IOTA
+ assert_equal 0, "\u03B9" =~ /\u0345/i
+ assert_equal 0, "\u0345" =~ /\u03B9/i
+ assert_equal 0, "\u03B9" =~ /\u0399/i
+ assert_equal 0, "\u0399" =~ /\u03B9/i
+ assert_equal 0, "\u03B9" =~ /\u1fbe/i
+ assert_equal 0, "\u1fbe" =~ /\u03B9/i
+
+ # GREEK SMALL LETTER MU
+ assert_equal 0, "\u03BC" =~ /\u00B5/i
+ assert_equal 0, "\u00B5" =~ /\u03BC/i
+ assert_equal 0, "\u03BC" =~ /\u039C/i
+ assert_equal 0, "\u039C" =~ /\u03BC/i
+
+ # CYRILLIC SMALL LETTER MONOGRAPH UK
+ assert_equal 0, "\uA64B" =~ /\u1c88/i
+ assert_equal 0, "\u1c88" =~ /\uA64B/i
+ assert_equal 0, "\uA64B" =~ /\ua64A/i
+ assert_equal 0, "\ua64A" =~ /\uA64B/i
+ end
+
+ def test_georgian_canary
+ message = "Reexamine implementation of Georgian in String#capitalize"
+ assert_equal false, "\u1CBB".match?(/\p{assigned}/), message
+ assert_equal false, "\u1CBC".match?(/\p{assigned}/), message
+ end
+
+ def test_georgian_unassigned
+ message = "Unassigned codepoints should not be converted"
+ assert_equal "\u1CBB", "\u1CBB".capitalize, message
+ assert_equal "\u1CBC", "\u1CBC".capitalize, message
+ end
+
+ def test_georgian_capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u1C90\u1C91\u1C92".capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u1C90\u1C91\u10D2".capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u1C90\u10D1\u1C92".capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u1C90\u10D1\u10D2".capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u10D0\u1C91\u1C92".capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u10D0\u1C91\u10D2".capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u10D0\u10D1\u1C92".capitalize
+ assert_equal "\u10D0\u10D1\u10D2", "\u10D0\u10D1\u10D2".capitalize
+ end
+
+ def no_longer_a_test_buffer_allocations
+ assert_equal 'TURKISH*ı'*10, ('I'*10).downcase(:turkic)
+ assert_equal 'TURKISH*ı'*100, ('I'*100).downcase(:turkic)
+ assert_equal 'TURKISH*ı'*1_000, ('I'*1_000).downcase(:turkic)
+ assert_equal 'TURKISH*ı'*10_000, ('I'*10_000).downcase(:turkic)
+ assert_equal 'TURKISH*ı'*100_000, ('I'*100_000).downcase(:turkic)
+ assert_equal 'TURKISH*ı'*1_000_000, ('I'*1_000_000).downcase(:turkic)
+ end
+end
diff --git a/test/ruby/enc/test_case_options.rb b/test/ruby/enc/test_case_options.rb
new file mode 100644
index 0000000000..e9bf50fcfc
--- /dev/null
+++ b/test/ruby/enc/test_case_options.rb
@@ -0,0 +1,81 @@
+# Copyright © 2016 Kimihito Matsui (松井 仁人) and Martin J. Dürst (duerst@it.aoyama.ac.jp)
+
+require "test/unit"
+
+class TestCaseOptions < Test::Unit::TestCase
+ def assert_raise_functional_operations(arg, *options)
+ assert_raise(ArgumentError) { arg.upcase(*options) }
+ assert_raise(ArgumentError) { arg.downcase(*options) }
+ assert_raise(ArgumentError) { arg.capitalize(*options) }
+ assert_raise(ArgumentError) { arg.swapcase(*options) }
+ end
+
+ def assert_raise_bang_operations(arg, *options)
+ assert_raise(ArgumentError) { arg.upcase!(*options) }
+ assert_raise(ArgumentError) { arg.downcase!(*options) }
+ assert_raise(ArgumentError) { arg.capitalize!(*options) }
+ assert_raise(ArgumentError) { arg.swapcase!(*options) }
+ end
+
+ def assert_raise_both_types(*options)
+ assert_raise_functional_operations 'a', *options
+ assert_raise_bang_operations 'a', *options
+ assert_raise_functional_operations :a, *options
+ end
+
+ def test_option_errors
+ assert_raise_both_types :invalid
+ assert_raise_both_types :lithuanian, :turkic, :fold
+ assert_raise_both_types :fold, :fold
+ assert_raise_both_types :ascii, :fold
+ assert_raise_both_types :fold, :ascii
+ assert_raise_both_types :ascii, :turkic
+ assert_raise_both_types :turkic, :ascii
+ assert_raise_both_types :ascii, :lithuanian
+ assert_raise_both_types :lithuanian, :ascii
+ end
+
+ def assert_okay_functional_operations(arg, *options)
+ assert_nothing_raised { arg.upcase(*options) }
+ assert_nothing_raised { arg.downcase(*options) }
+ assert_nothing_raised { arg.capitalize(*options) }
+ assert_nothing_raised { arg.swapcase(*options) }
+ end
+
+ def assert_okay_bang_operations(arg, *options)
+ assert_nothing_raised { arg.upcase!(*options) }
+ assert_nothing_raised { arg.downcase!(*options) }
+ assert_nothing_raised { arg.capitalize!(*options) }
+ assert_nothing_raised { arg.swapcase!(*options) }
+ end
+
+ def assert_okay_both_types(*options)
+ assert_okay_functional_operations 'a', *options
+ assert_okay_bang_operations 'a', *options
+ assert_okay_functional_operations :a, *options
+ end
+
+ def test_options_okay
+ assert_okay_both_types
+ assert_okay_both_types :ascii
+ assert_okay_both_types :turkic
+ assert_okay_both_types :lithuanian
+ assert_okay_both_types :turkic, :lithuanian
+ assert_okay_both_types :lithuanian, :turkic
+ end
+
+ def test_operation_specific # :fold option only allowed on downcase
+ assert_nothing_raised { 'a'.downcase :fold }
+ assert_raise(ArgumentError) { 'a'.upcase :fold }
+ assert_raise(ArgumentError) { 'a'.capitalize :fold }
+ assert_raise(ArgumentError) { 'a'.swapcase :fold }
+ assert_nothing_raised { 'a'.downcase! :fold }
+ assert_raise(ArgumentError) { 'a'.upcase! :fold }
+ assert_raise(ArgumentError) { 'a'.capitalize! :fold }
+ assert_raise(ArgumentError) { 'a'.swapcase! :fold }
+ assert_nothing_raised { :a.downcase :fold }
+ assert_raise(ArgumentError) { :a.upcase :fold }
+ assert_raise(ArgumentError) { :a.capitalize :fold }
+ assert_raise(ArgumentError) { :a.swapcase :fold }
+ end
+end
diff --git a/test/ruby/enc/test_cp949.rb b/test/ruby/enc/test_cp949.rb
index e675c7b80c..0684162d5b 100644
--- a/test/ruby/enc/test_cp949.rb
+++ b/test/ruby/enc/test_cp949.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require "test/unit"
class TestCP949 < Test::Unit::TestCase
diff --git a/test/ruby/enc/test_emoji.rb b/test/ruby/enc/test_emoji.rb
index 90144cffff..330ff70cb9 100644
--- a/test/ruby/enc/test_emoji.rb
+++ b/test/ruby/enc/test_emoji.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
module Emoji
@@ -42,7 +43,7 @@ module Emoji
def test_encoding_name
%w(UTF8-DoCoMo
SJIS-DoCoMo).each do |n|
- assert Encoding.name_list.include?(n), "encoding not found: #{n}"
+ assert_include Encoding.name_list, n, "encoding not found: #{n}"
end
end
@@ -126,7 +127,7 @@ module Emoji
SJIS-KDDI
ISO-2022-JP-KDDI
stateless-ISO-2022-JP-KDDI).each do |n|
- assert Encoding.name_list.include?(n), "encoding not found: #{n}"
+ assert_include Encoding.name_list, n, "encoding not found: #{n}"
end
end
@@ -250,7 +251,7 @@ module Emoji
def test_encoding_name
%w(UTF8-SoftBank
SJIS-SoftBank).each do |n|
- assert Encoding.name_list.include?(n), "encoding not found: #{n}"
+ assert_include Encoding.name_list, n, "encoding not found: #{n}"
end
end
diff --git a/test/ruby/enc/test_emoji_breaks.rb b/test/ruby/enc/test_emoji_breaks.rb
new file mode 100644
index 0000000000..37a29640d8
--- /dev/null
+++ b/test/ruby/enc/test_emoji_breaks.rb
@@ -0,0 +1,119 @@
+# frozen_string_literal: true
+# Copyright © 2018 Martin J. Dürst (duerst@it.aoyama.ac.jp)
+
+require "test/unit"
+
+class BreakTest
+ attr_reader :string, :comment, :filename, :line_number, :type, :shortname
+
+ def initialize (filename, line_number, data, comment='')
+ @filename = filename
+ @line_number = line_number
+ @comment = comment.gsub(/\s+/, ' ').strip
+ if filename=='emoji-test'
+ codes, @type = data.split(/\s*;\s*/)
+ @shortname = ''
+ else
+ codes, @type, @shortname = data.split(/\s*;\s*/)
+ end
+ @type = @type.gsub(/\s+/, ' ').strip
+ @shortname = @shortname.gsub(/\s+/, ' ').strip
+ @string = codes.split(/\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
+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__)
+
+ def self.expand_filename(basename)
+ File.expand_path("#{EMOJI_DATA_PATH}/#{basename}.txt", __dir__)
+ end
+
+ def self.data_files_available?
+ EMOJI_DATA_FILES.all? do |f|
+ File.exist?(expand_filename(f))
+ end
+ end
+
+ def test_data_files_available
+ unless TestEmojiBreaks.data_files_available?
+ skip "Emoji data files not available in #{EMOJI_DATA_PATH}."
+ end
+ end
+end
+
+TestEmojiBreaks.data_files_available? and class TestEmojiBreaks
+ def read_data
+ tests = []
+ EMOJI_DATA_FILES.each do |filename|
+ version_mismatch = true
+ file_tests = []
+ IO.foreach(TestEmojiBreaks.expand_filename(filename), 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'
+ end
+ raise "File Version Mismatch" if version_mismatch
+ tests += file_tests
+ end
+ tests
+ end
+
+ def all_tests
+ @@tests ||= read_data
+ rescue Errno::ENOENT
+ @@tests ||= []
+ end
+
+ def test_single_emoji
+ all_tests.each do |test|
+ expected = [test.string]
+ actual = test.string.each_grapheme_cluster.to_a
+ assert_equal expected, actual,
+ "file: #{test.filename}, line #{test.line_number}, " +
+ "type: #{test.type}, shortname: #{test.shortname}, comment: #{test.comment}"
+ end
+ end
+
+ def test_embedded_emoji
+ all_tests.each do |test|
+ expected = ["\t", test.string, "\t"]
+ actual = "\t#{test.string}\t".each_grapheme_cluster.to_a
+ assert_equal expected, actual,
+ "file: #{test.filename}, line #{test.line_number}, " +
+ "type: #{test.type}, shortname: #{test.shortname}, comment: #{test.comment}"
+ end
+ end
+
+ # test some pseodorandom combinations of emoji
+ def test_mixed_emoji
+ srand 0
+ length = all_tests.length
+ step = 503 # use a prime number
+ all_tests.each do |test1|
+ start = rand step
+ start.step(by: step, to: length-1) do |t2|
+ test2 = all_tests[t2]
+ # exclude skin tones, because they glue to previous grapheme clusters
+ next if (0x1F3FB..0x1F3FF).include? test2.string.ord
+ expected = [test1.string, test2.string]
+ actual = (test1.string+test2.string).each_grapheme_cluster.to_a
+ assert_equal expected, actual,
+ "file1: #{test1.filename}, line1 #{test1.line_number}, " +
+ "file2: #{test2.filename}, line2 #{test2.line_number},\n" +
+ "type1: #{test1.type}, shortname1: #{test1.shortname}, comment1: #{test1.comment},\n" +
+ "type2: #{test2.type}, shortname2: #{test2.shortname}, comment2: #{test2.comment}"
+ end
+ end
+ end
+end
diff --git a/test/ruby/enc/test_euc_jp.rb b/test/ruby/enc/test_euc_jp.rb
index 1ccc55ccb9..4aec69e4db 100644
--- a/test/ruby/enc/test_euc_jp.rb
+++ b/test/ruby/enc/test_euc_jp.rb
@@ -1,11 +1,12 @@
# vim: set fileencoding=euc-jp
+# frozen_string_literal: false
require "test/unit"
class TestEUC_JP < Test::Unit::TestCase
def test_mbc_case_fold
assert_match(/()(a)\1\2/i, "aA")
- assert_no_match(/()(a)\1\2/i, "aA")
+ assert_match(/()(a)\1\2/i, "aA")
end
def test_property
diff --git a/test/ruby/enc/test_euc_kr.rb b/test/ruby/enc/test_euc_kr.rb
index 087bc795f7..c9de2cc4e1 100644
--- a/test/ruby/enc/test_euc_kr.rb
+++ b/test/ruby/enc/test_euc_kr.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require "test/unit"
class TestEucKr < Test::Unit::TestCase
@@ -25,4 +26,12 @@ class TestEucKr < Test::Unit::TestCase
def test_left_adjust_char_head
assert_equal(s("\xa1\xa1"), s("\xa1\xa1\xa1\xa1").chop)
end
+
+ def test_euro_sign
+ assert_equal("\u{20ac}", s("\xa2\xe6").encode("utf-8"))
+ end
+
+ def test_registered_mark
+ assert_equal("\u{00ae}", s("\xa2\xe7").encode("utf-8"))
+ end
end
diff --git a/test/ruby/enc/test_euc_tw.rb b/test/ruby/enc/test_euc_tw.rb
index f36d86b088..649b1b81c6 100644
--- a/test/ruby/enc/test_euc_tw.rb
+++ b/test/ruby/enc/test_euc_tw.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require "test/unit"
class TestEucTw < Test::Unit::TestCase
diff --git a/test/ruby/enc/test_gb18030.rb b/test/ruby/enc/test_gb18030.rb
index f379504d48..76ac785951 100644
--- a/test/ruby/enc/test_gb18030.rb
+++ b/test/ruby/enc/test_gb18030.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require "test/unit"
class TestGB18030 < Test::Unit::TestCase
diff --git a/test/ruby/enc/test_gbk.rb b/test/ruby/enc/test_gbk.rb
index d6dc5d6d1b..2e541b5821 100644
--- a/test/ruby/enc/test_gbk.rb
+++ b/test/ruby/enc/test_gbk.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require "test/unit"
class TestGBK < Test::Unit::TestCase
diff --git a/test/ruby/enc/test_grapheme_breaks.rb b/test/ruby/enc/test_grapheme_breaks.rb
new file mode 100644
index 0000000000..7f6c776113
--- /dev/null
+++ b/test/ruby/enc/test_grapheme_breaks.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+# Copyright © 2018 Martin J. Dürst (duerst@it.aoyama.ac.jp)
+
+require "test/unit"
+
+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
+ 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
+ GRAPHEME_BREAK_TEST_FILE = File.expand_path("#{UNICODE_DATA_PATH}/GraphemeBreakTest.txt", __dir__)
+
+ def self.file_available?
+ File.exist? GRAPHEME_BREAK_TEST_FILE
+ end
+
+ def test_data_files_available
+ unless TestGraphemeBreaksFromFile.file_available?
+ 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"
+ end
+ next if /\A#/.match? line
+ tests << BreakTest.new($., *line.chomp.split('#')) rescue 'whatever'
+ end
+ 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}"
+ 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
+ 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}, after last removal, expected '#{expected}', " +
+ "but got '#{string}', comment: #{test.comment}"
+ end
+ end
+end
diff --git a/test/ruby/enc/test_iso_8859.rb b/test/ruby/enc/test_iso_8859.rb
index 64cc7cd76d..ed663be243 100644
--- a/test/ruby/enc/test_iso_8859.rb
+++ b/test/ruby/enc/test_iso_8859.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestISO8859 < Test::Unit::TestCase
@@ -28,13 +29,15 @@ class TestISO8859 < Test::Unit::TestCase
end
def test_iso_8859_3
+ # todo: decide on behavior, test, and fix implementation re. İ and ı (0xA9/0xB9)
+ # treating them as case equivalents is definitely an error
eval(%q(# encoding: iso8859-3
assert_match(/^(\xdf)\1$/i, "\xdf\xdf")
assert_match(/^(\xdf)\1$/i, "ssss")
assert_match(/^[\xdfz]+$/i, "sszzsszz")
assert_match(/^SS$/i, "\xdf")
assert_match(/^Ss$/i, "\xdf")
- [0xa1, 0xa6, *(0xa9..0xac), 0xaf].each do |c|
+ [0xa1, 0xa6, *(0xaa..0xac), 0xaf].each do |c|
c1 = c.chr("iso8859-3")
c2 = (c + 0x10).chr("iso8859-3")
assert_match(/^(#{ c1 })\1$/i, c2 + c1)
@@ -120,7 +123,7 @@ class TestISO8859 < Test::Unit::TestCase
assert_match(/^[\xdfz]+$/i, "sszzsszz")
assert_match(/^SS$/i, "\xdf")
assert_match(/^Ss$/i, "\xdf")
- ([*(0xc0..0xdc)] - [0xd7]).each do |c|
+ ([*(0xc0..0xde)] - [0xd7, 0xdd]).each do |c|
c1 = c.chr("iso8859-9")
c2 = (c + 0x20).chr("iso8859-9")
assert_match(/^(#{ c1 })\1$/i, c2 + c1)
diff --git a/test/ruby/enc/test_koi8.rb b/test/ruby/enc/test_koi8.rb
index ce2d8925ea..4a4d233e8d 100644
--- a/test/ruby/enc/test_koi8.rb
+++ b/test/ruby/enc/test_koi8.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require "test/unit"
class TestKOI8 < Test::Unit::TestCase
diff --git a/test/ruby/enc/test_regex_casefold.rb b/test/ruby/enc/test_regex_casefold.rb
new file mode 100644
index 0000000000..2b252bd441
--- /dev/null
+++ b/test/ruby/enc/test_regex_casefold.rb
@@ -0,0 +1,120 @@
+# Copyright Kimihito Matsui (松井 仁人) and Martin J. Dürst (duerst@it.aoyama.ac.jp)
+
+require "test/unit"
+
+class TestCaseFold < 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") ? "#{path}/ucd" : path
+ CaseTest = Struct.new :source, :target, :kind, :line
+
+ def check_downcase_properties(expected, start, *flags)
+ assert_equal expected, start.downcase(*flags)
+ temp = start
+ assert_equal expected, temp.downcase!(*flags)
+ assert_equal expected, expected.downcase(*flags)
+ temp = expected
+ assert_nil temp.downcase!(*flags)
+ end
+
+ def read_tests
+ IO.readlines("#{UNICODE_DATA_PATH}/CaseFolding.txt", encoding: Encoding::ASCII_8BIT)
+ .collect.with_index { |linedata, linenumber| [linenumber.to_i+1, linedata.chomp] }
+ .reject { |number, data| data =~ /^(#|$)/ }
+ .collect do |linenumber, linedata|
+ data, _ = linedata.split(/#\s*/)
+ code, kind, result, _ = data.split(/;\s*/)
+ CaseTest.new code.to_i(16).chr('UTF-8'),
+ result.split(/ /).collect { |hex| hex.to_i(16) }.pack('U*'),
+ kind, linenumber
+ end.select { |test| test.kind=='C' }
+ end
+
+ def to_codepoints(string)
+ string.codepoints.collect { |cp| cp.to_s(16).upcase.rjust(4, '0') }
+ end
+
+ def setup
+ @@tests ||= read_tests
+ rescue Errno::ENOENT => e
+ @@tests ||= []
+ skip e.message
+ end
+
+ def self.generate_test_casefold(encoding)
+ define_method "test_mbc_case_fold_#{encoding}" do
+ @@tests.each do |test|
+ begin
+ source = test.source.encode encoding
+ target = test.target.encode encoding
+ assert_equal 5, "12345#{target}67890" =~ /#{source}/i,
+ "12345#{to_codepoints(target)}67890 and /#{to_codepoints(source)}/ do not match case-insensitive " +
+ "(CaseFolding.txt line #{test[:line]})"
+ rescue Encoding::UndefinedConversionError
+ end
+ end
+ end
+
+ define_method "test_get_case_fold_codes_by_str_#{encoding}" do
+ @@tests.each do |test|
+ begin
+ source = test.source.encode encoding
+ target = test.target.encode encoding
+ assert_equal 5, "12345#{source}67890" =~ /#{target}/i,
+ "12345#{to_codepoints(source)}67890 and /#{to_codepoints(target)}/ do not match case-insensitive " +
+ "(CaseFolding.txt line #{test[:line]}), " +
+ "error may also be triggered by mbc_case_fold"
+ rescue Encoding::UndefinedConversionError
+ end
+ end
+ end
+
+ define_method "test_apply_all_case_fold_#{encoding}" do
+ @@tests.each do |test|
+ begin
+ source = test.source.encode encoding
+ target = test.target.encode encoding
+ reg = '\p{Upper}'
+ regexp = Regexp.compile reg.encode(encoding)
+ regexpi = Regexp.compile reg.encode(encoding), Regexp::IGNORECASE
+ assert_equal 5, "12345#{target}67890" =~ regexpi,
+ "12345#{to_codepoints(target)}67890 and /#{reg}/i do not match " +
+ "(CaseFolding.txt line #{test[:line]})"
+ rescue Encoding::UndefinedConversionError
+ source = source
+ regexp = regexp
+ end
+ end
+ end
+ end
+
+ def test_downcase_fold
+ @@tests.each do |test|
+ check_downcase_properties test.target, test.source, :fold
+ end
+ end
+
+ # start with good encodings only
+ generate_test_casefold 'US-ASCII'
+ generate_test_casefold 'ISO-8859-1'
+ generate_test_casefold 'ISO-8859-2'
+ generate_test_casefold 'ISO-8859-3'
+ generate_test_casefold 'ISO-8859-4'
+ generate_test_casefold 'ISO-8859-5'
+ generate_test_casefold 'ISO-8859-6'
+ # generate_test_casefold 'ISO-8859-7'
+ generate_test_casefold 'ISO-8859-8'
+ generate_test_casefold 'ISO-8859-9'
+ generate_test_casefold 'ISO-8859-10'
+ generate_test_casefold 'ISO-8859-11'
+ generate_test_casefold 'ISO-8859-13'
+ generate_test_casefold 'ISO-8859-14'
+ generate_test_casefold 'ISO-8859-15'
+ generate_test_casefold 'ISO-8859-16'
+ generate_test_casefold 'Windows-1250'
+ # generate_test_casefold 'Windows-1251'
+ generate_test_casefold 'Windows-1252'
+ generate_test_casefold 'koi8-r'
+ generate_test_casefold 'koi8-u'
+end
diff --git a/test/ruby/enc/test_shift_jis.rb b/test/ruby/enc/test_shift_jis.rb
index f81cb7801c..059992d167 100644
--- a/test/ruby/enc/test_shift_jis.rb
+++ b/test/ruby/enc/test_shift_jis.rb
@@ -1,11 +1,12 @@
# vim: set fileencoding=shift_jis
+# frozen_string_literal: false
require "test/unit"
class TestShiftJIS < Test::Unit::TestCase
def test_mbc_case_fold
assert_match(/()(a)\1\2/i, "aA")
- assert_no_match(/()(a)\1\2/i, "a`A")
+ assert_match(/()(a)\1\2/i, "a`A")
end
def test_property
@@ -22,6 +23,6 @@ class TestShiftJIS < Test::Unit::TestCase
s = ""
s << 0x82a9
assert_equal("", s)
- assert_raise(ArgumentError) { s << 0x82 }
+ assert_raise(RangeError) { s << 0x82 }
end
end
diff --git a/test/ruby/enc/test_utf16.rb b/test/ruby/enc/test_utf16.rb
index 90a8314067..e08f2ea14e 100644
--- a/test/ruby/enc/test_utf16.rb
+++ b/test/ruby/enc/test_utf16.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestUTF16 < Test::Unit::TestCase
@@ -49,65 +50,77 @@ class TestUTF16 < Test::Unit::TestCase
#{encdump expected} expected but not equal to
#{encdump actual}.
EOT
- assert_block(full_message) { expected == actual }
+ assert_equal(expected, actual, full_message)
end
# tests start
def test_utf16be_valid_encoding
- [
- "\x00\x00",
- "\xd7\xff",
- "\xd8\x00\xdc\x00",
- "\xdb\xff\xdf\xff",
- "\xe0\x00",
- "\xff\xff",
- ].each {|s|
- s.force_encoding("utf-16be")
- assert_equal(true, s.valid_encoding?, "#{encdump s}.valid_encoding?")
- }
- [
- "\x00",
- "\xd7",
- "\xd8\x00",
- "\xd8\x00\xd8\x00",
- "\xdc\x00",
- "\xdc\x00\xd8\x00",
- "\xdc\x00\xdc\x00",
- "\xe0",
- "\xff",
- ].each {|s|
- s.force_encoding("utf-16be")
- assert_equal(false, s.valid_encoding?, "#{encdump s}.valid_encoding?")
- }
+ all_assertions do |a|
+ [
+ "\x00\x00",
+ "\xd7\xff",
+ "\xd8\x00\xdc\x00",
+ "\xdb\xff\xdf\xff",
+ "\xe0\x00",
+ "\xff\xff",
+ ].each {|s|
+ s.force_encoding("utf-16be")
+ a.for(s) {
+ assert_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ [
+ "\x00",
+ "\xd7",
+ "\xd8\x00",
+ "\xd8\x00\xd8\x00",
+ "\xdc\x00",
+ "\xdc\x00\xd8\x00",
+ "\xdc\x00\xdc\x00",
+ "\xe0",
+ "\xff",
+ ].each {|s|
+ s.force_encoding("utf-16be")
+ a.for(s) {
+ assert_not_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ end
end
def test_utf16le_valid_encoding
- [
- "\x00\x00",
- "\xff\xd7",
- "\x00\xd8\x00\xdc",
- "\xff\xdb\xff\xdf",
- "\x00\xe0",
- "\xff\xff",
- ].each {|s|
- s.force_encoding("utf-16le")
- assert_equal(true, s.valid_encoding?, "#{encdump s}.valid_encoding?")
- }
- [
- "\x00",
- "\xd7",
- "\x00\xd8",
- "\x00\xd8\x00\xd8",
- "\x00\xdc",
- "\x00\xdc\x00\xd8",
- "\x00\xdc\x00\xdc",
- "\xe0",
- "\xff",
- ].each {|s|
- s.force_encoding("utf-16le")
- assert_equal(false, s.valid_encoding?, "#{encdump s}.valid_encoding?")
- }
+ all_assertions do |a|
+ [
+ "\x00\x00",
+ "\xff\xd7",
+ "\x00\xd8\x00\xdc",
+ "\xff\xdb\xff\xdf",
+ "\x00\xe0",
+ "\xff\xff",
+ ].each {|s|
+ s.force_encoding("utf-16le")
+ a.for(s) {
+ assert_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ [
+ "\x00",
+ "\xd7",
+ "\x00\xd8",
+ "\x00\xd8\x00\xd8",
+ "\x00\xdc",
+ "\x00\xdc\x00\xd8",
+ "\x00\xdc\x00\xdc",
+ "\xe0",
+ "\xff",
+ ].each {|s|
+ s.force_encoding("utf-16le")
+ a.for(s) {
+ assert_not_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ end
end
def test_strftime
@@ -122,7 +135,7 @@ EOT
def test_sym_eq
s = "aa".force_encoding("utf-16le")
- assert(s.intern != :aa, "#{encdump s}.intern != :aa")
+ assert_not_equal(:aa, s.intern, "#{encdump s}.intern != :aa")
end
def test_compatible
@@ -253,10 +266,10 @@ EOT
def test_succ
s = "\xff\xff".force_encoding("utf-16be")
- assert(s.succ.valid_encoding?, "#{encdump s}.succ.valid_encoding?")
+ assert_predicate(s.succ, :valid_encoding?, "#{encdump s}.succ.valid_encoding?")
s = "\xdb\xff\xdf\xff".force_encoding("utf-16be")
- assert(s.succ.valid_encoding?, "#{encdump s}.succ.valid_encoding?")
+ assert_predicate(s.succ, :valid_encoding?, "#{encdump s}.succ.valid_encoding?")
end
def test_regexp_union
@@ -366,7 +379,7 @@ EOT
def test_regexp_escape
s = "\0*".force_encoding("UTF-16BE")
r = Regexp.new(Regexp.escape(s))
- assert(r =~ s, "#{encdump(r)} =~ #{encdump(s)}")
+ assert_match(r, s, "#{encdump(r)} =~ #{encdump(s)}")
end
def test_casecmp2
diff --git a/test/ruby/enc/test_utf32.rb b/test/ruby/enc/test_utf32.rb
index 3d4a458512..76379abca0 100644
--- a/test/ruby/enc/test_utf32.rb
+++ b/test/ruby/enc/test_utf32.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestUTF32 < Test::Unit::TestCase
@@ -15,7 +16,7 @@ class TestUTF32 < Test::Unit::TestCase
#{encdump expected} expected but not equal to
#{encdump actual}.
EOT
- assert_block(full_message) { expected == actual }
+ assert_equal(expected, actual, full_message)
end
def test_substr
@@ -89,5 +90,73 @@ EOT
assert_equal(sl, "a".ord.chr("utf-32le"))
assert_equal(sb, "a".ord.chr("utf-32be"))
end
+
+ def test_utf32be_valid_encoding
+ all_assertions do |a|
+ [
+ "\x00\x00\x00\x00",
+ "\x00\x00\x00a",
+ "\x00\x00\x30\x40",
+ "\x00\x00\xd7\xff",
+ "\x00\x00\xe0\x00",
+ "\x00\x00\xff\xff",
+ "\x00\x10\xff\xff",
+ ].each {|s|
+ s.force_encoding("utf-32be")
+ a.for(s) {
+ assert_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ [
+ "a",
+ "\x00a",
+ "\x00\x00a",
+ "\x00\x00\xd8\x00",
+ "\x00\x00\xdb\xff",
+ "\x00\x00\xdc\x00",
+ "\x00\x00\xdf\xff",
+ "\x00\x11\x00\x00",
+ ].each {|s|
+ s.force_encoding("utf-32be")
+ a.for(s) {
+ assert_not_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ end
+ end
+
+ def test_utf32le_valid_encoding
+ all_assertions do |a|
+ [
+ "\x00\x00\x00\x00",
+ "a\x00\x00\x00",
+ "\x40\x30\x00\x00",
+ "\xff\xd7\x00\x00",
+ "\x00\xe0\x00\x00",
+ "\xff\xff\x00\x00",
+ "\xff\xff\x10\x00",
+ ].each {|s|
+ s.force_encoding("utf-32le")
+ a.for(s) {
+ assert_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ [
+ "a",
+ "a\x00",
+ "a\x00\x00",
+ "\x00\xd8\x00\x00",
+ "\xff\xdb\x00\x00",
+ "\x00\xdc\x00\x00",
+ "\xff\xdf\x00\x00",
+ "\x00\x00\x11\x00",
+ ].each {|s|
+ s.force_encoding("utf-32le")
+ a.for(s) {
+ assert_not_predicate(s, :valid_encoding?, "#{encdump s}.valid_encoding?")
+ }
+ }
+ end
+ end
end
diff --git a/test/ruby/enc/test_windows_1251.rb b/test/ruby/enc/test_windows_1251.rb
index 6fbf3159a1..002dbaa3cc 100644
--- a/test/ruby/enc/test_windows_1251.rb
+++ b/test/ruby/enc/test_windows_1251.rb
@@ -1,4 +1,5 @@
# encoding:windows-1251
+# frozen_string_literal: false
require "test/unit"
diff --git a/test/ruby/enc/test_windows_1252.rb b/test/ruby/enc/test_windows_1252.rb
new file mode 100644
index 0000000000..f264cba759
--- /dev/null
+++ b/test/ruby/enc/test_windows_1252.rb
@@ -0,0 +1,26 @@
+# encoding:windows-1252
+# frozen_string_literal: false
+
+require "test/unit"
+
+class TestWindows1252 < Test::Unit::TestCase
+ def test_stset
+ assert_match(/^(\xdf)\1$/i, "\xdf\xdf")
+ assert_match(/^(\xdf)\1$/i, "ssss")
+ # assert_match(/^(\xdf)\1$/i, "\xdfss") # this must be bug...
+ assert_match(/^[\xdfz]+$/i, "sszzsszz")
+ assert_match(/^SS$/i, "\xdf")
+ assert_match(/^Ss$/i, "\xdf")
+ end
+
+ def test_windows_1252
+ [0x8a, 0x8c, 0x8e, *0xc0..0xd6, *0xd8..0xde, 0x9f].zip([0x9a, 0x9c, 0x9e, *0xe0..0xf6, *0xf8..0xfe, 0xff]).each do |c1, c2|
+ c1 = c1.chr("windows-1252")
+ c2 = c2.chr("windows-1252")
+ assert_match(/^(#{ c1 })\1$/i, c2 + c1)
+ assert_match(/^(#{ c2 })\1$/i, c1 + c2)
+ assert_match(/^[#{ c1 }]+$/i, c2 + c1)
+ assert_match(/^[#{ c2 }]+$/i, c1 + c2)
+ end
+ end
+end
diff --git a/test/ruby/endblockwarn_rb b/test/ruby/endblockwarn_rb
deleted file mode 100644
index 7b7f97f597..0000000000
--- a/test/ruby/endblockwarn_rb
+++ /dev/null
@@ -1,12 +0,0 @@
-def end1
- END {}
-end
-
-end1
-
-eval <<EOE
- def end2
- END {}
- end
-EOE
-
diff --git a/test/ruby/envutil.rb b/test/ruby/envutil.rb
deleted file mode 100644
index 978bf770bf..0000000000
--- a/test/ruby/envutil.rb
+++ /dev/null
@@ -1,190 +0,0 @@
-require "open3"
-require "timeout"
-
-module EnvUtil
- def rubybin
- unless ENV["RUBYOPT"]
-
- end
- if ruby = ENV["RUBY"]
- return ruby
- end
- ruby = "ruby"
- rubyexe = ruby+".exe"
- 3.times do
- if File.exist? ruby and File.executable? ruby and !File.directory? ruby
- return File.expand_path(ruby)
- end
- if File.exist? rubyexe and File.executable? rubyexe
- return File.expand_path(rubyexe)
- end
- ruby = File.join("..", ruby)
- end
- if defined?(RbConfig.ruby)
- RbConfig.ruby
- else
- "ruby"
- end
- end
- module_function :rubybin
-
- LANG_ENVS = %w"LANG LC_ALL LC_CTYPE"
-
- def invoke_ruby(args, stdin_data="", capture_stdout=false, capture_stderr=false, opt={})
- in_c, in_p = IO.pipe
- out_p, out_c = IO.pipe if capture_stdout
- err_p, err_c = IO.pipe if capture_stderr
- opt = opt.dup
- opt[:in] = in_c
- opt[:out] = out_c if capture_stdout
- opt[:err] = err_c if capture_stderr
- if enc = opt.delete(:encoding)
- out_p.set_encoding(enc) if out_p
- err_p.set_encoding(enc) if err_p
- end
- c = "C"
- child_env = {}
- LANG_ENVS.each {|lc| child_env[lc] = c}
- if Array === args and Hash === args.first
- child_env.update(args.shift)
- end
- args = [args] if args.kind_of?(String)
- pid = spawn(child_env, EnvUtil.rubybin, *args, opt)
- in_c.close
- out_c.close if capture_stdout
- err_c.close if capture_stderr
- if block_given?
- return yield in_p, out_p, err_p
- else
- th_stdout = Thread.new { out_p.read } if capture_stdout
- th_stderr = Thread.new { err_p.read } if capture_stderr
- in_p.write stdin_data.to_str
- in_p.close
- timeout = opt.fetch(:timeout, 10)
- if (!capture_stdout || th_stdout.join(timeout)) && (!capture_stderr || th_stderr.join(timeout))
- stdout = th_stdout.value if capture_stdout
- stderr = th_stderr.value if capture_stderr
- else
- raise Timeout::Error
- end
- out_p.close if capture_stdout
- err_p.close if capture_stderr
- Process.wait pid
- status = $?
- return stdout, stderr, status
- end
- ensure
- [in_c, in_p, out_c, out_p, err_c, err_p].each do |io|
- io.close if io && !io.closed?
- end
- [th_stdout, th_stderr].each do |th|
- (th.kill; th.join) if th
- end
- end
- module_function :invoke_ruby
-
- alias rubyexec invoke_ruby
- class << self
- alias rubyexec invoke_ruby
- end
-
- def verbose_warning
- class << (stderr = "")
- alias write <<
- end
- stderr, $stderr, verbose, $VERBOSE = $stderr, stderr, $VERBOSE, true
- yield stderr
- ensure
- stderr, $stderr, $VERBOSE = $stderr, stderr, verbose
- return stderr
- end
- module_function :verbose_warning
-
- def under_gc_stress
- stress, GC.stress = GC.stress, true
- yield
- ensure
- GC.stress = stress
- end
- module_function :under_gc_stress
-end
-
-module Test
- module Unit
- module Assertions
- public
- def assert_normal_exit(testsrc, message = '', opt = {})
- stdout, stderr, status = EnvUtil.invoke_ruby(%W'-W0', testsrc, true, true, opt)
- pid = status.pid
- faildesc = proc do
- signo = status.termsig
- signame = Signal.list.invert[signo]
- sigdesc = "signal #{signo}"
- if signame
- sigdesc = "SIG#{signame} (#{sigdesc})"
- end
- if status.coredump?
- sigdesc << " (core dumped)"
- end
- full_message = ''
- if !message.empty?
- full_message << message << "\n"
- end
- if msg.empty?
- full_message << "pid #{pid} killed by #{sigdesc}"
- else
- msg << "\n" if /\n\z/ !~ msg
- full_message << "pid #{pid} killed by #{sigdesc}\n#{msg.gsub(/^/, '| ')}"
- end
- full_message
- end
- assert_block(faildesc) { !status.signaled? }
- end
-
- def assert_in_out_err(args, test_stdin = "", test_stdout = [], test_stderr = [], message = nil, opt={})
- stdout, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, true, true, opt)
- if block_given?
- yield(stdout.lines.map {|l| l.chomp }, stderr.lines.map {|l| l.chomp })
- else
- if test_stdout.is_a?(Regexp)
- assert_match(test_stdout, stdout, message)
- else
- assert_equal(test_stdout, stdout.lines.map {|l| l.chomp }, message)
- end
- if test_stderr.is_a?(Regexp)
- assert_match(test_stderr, stderr, message)
- else
- assert_equal(test_stderr, stderr.lines.map {|l| l.chomp }, message)
- end
- status
- end
- end
-
- def assert_ruby_status(args, test_stdin="", message=nil, opt={})
- stdout, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, false, false, opt)
- m = message ? "#{message} (#{status.inspect})" : "ruby exit status is not success: #{status.inspect}"
- assert(status.success?, m)
- end
-
- end
- end
-end
-
-begin
- require 'rbconfig'
-rescue LoadError
-else
- module RbConfig
- @ruby = EnvUtil.rubybin
- class << self
- undef ruby if method_defined?(:ruby)
- attr_reader :ruby
- end
- dir = File.dirname(ruby)
- name = File.basename(ruby, CONFIG['EXEEXT'])
- CONFIG['bindir'] = dir
- CONFIG['ruby_install_name'] = name
- CONFIG['RUBY_INSTALL_NAME'] = name
- Gem::ConfigMap[:bindir] = dir if defined?(Gem)
- end
-end
diff --git a/test/ruby/lbtest.rb b/test/ruby/lbtest.rb
index df7872dc76..c7822c9e9a 100644
--- a/test/ruby/lbtest.rb
+++ b/test/ruby/lbtest.rb
@@ -1,9 +1,9 @@
-require 'thread'
+# frozen_string_literal: false
class LocalBarrier
def initialize(n)
- @wait = Queue.new
- @done = Queue.new
+ @wait = Thread::Queue.new
+ @done = Thread::Queue.new
@keeper = begin_keeper(n)
end
@@ -35,14 +35,15 @@ lb = LocalBarrier.new(n)
(n - 1).times do |i|
Thread.start do
- sleep((rand(n) + 1) / 10.0)
- puts "#{i}: done"
+ sleep((rand(n) + 1) / 100.0)
+ print "#{i}: done\n"
lb.sync
- puts "#{i}: cont"
+ print "#{i}: cont\n"
end
end
lb.sync
-puts "#{n-1}: cone"
+print "#{n-1}: cont\n"
+# lb.join # [ruby-dev:30653]
-puts "exit."
+print "exit.\n"
diff --git a/test/ruby/marshaltestlib.rb b/test/ruby/marshaltestlib.rb
index 39471aac64..5c48a8d853 100644
--- a/test/ruby/marshaltestlib.rb
+++ b/test/ruby/marshaltestlib.rb
@@ -1,6 +1,7 @@
# coding: utf-8
+# frozen_string_literal: false
module MarshalTestLib
- # include this module to a Test::Unit::TestCase and definde encode(o) and
+ # include this module to a Test::Unit::TestCase and define encode(o) and
# decode(s) methods. e.g.
#
# def encode(o)
@@ -40,6 +41,14 @@ module MarshalTestLib
end
end
+ def marshal_equal_with_ancestry(o1, msg = nil)
+ marshal_equal(o1, msg) do |o|
+ ancestry = o.singleton_class.ancestors
+ ancestry[ancestry.index(o.singleton_class)] = :singleton_class
+ ancestry
+ end
+ end
+
class MyObject; def initialize(v) @v = v end; attr_reader :v; end
def test_object
o1 = Object.new
@@ -54,24 +63,26 @@ module MarshalTestLib
def test_object_extend
o1 = Object.new
o1.extend(Mod1)
- marshal_equal(o1) { |o|
- (class << self; self; end).ancestors
- }
+ marshal_equal_with_ancestry(o1)
o1.extend(Mod2)
- marshal_equal(o1) { |o|
- (class << self; self; end).ancestors
- }
+ marshal_equal_with_ancestry(o1)
end
def test_object_subclass_extend
o1 = MyObject.new(2)
o1.extend(Mod1)
- marshal_equal(o1) { |o|
- (class << self; self; end).ancestors
- }
+ marshal_equal_with_ancestry(o1)
o1.extend(Mod2)
- marshal_equal(o1) { |o|
- (class << self; self; end).ancestors
+ marshal_equal_with_ancestry(o1)
+ end
+
+ def test_object_prepend
+ bug8041 = '[ruby-core:53202] [Bug #8041]'
+
+ o1 = MyObject.new(42)
+ o1.singleton_class.class_eval {prepend Mod1}
+ assert_nothing_raised(ArgumentError, bug8041) {
+ marshal_equal_with_ancestry(o1, bug8041)
}
end
@@ -99,7 +110,9 @@ module MarshalTestLib
class MyException < Exception; def initialize(v, *args) super(*args); @v = v; end; attr_reader :v; end
def test_exception
marshal_equal(Exception.new('foo')) {|o| o.message}
- marshal_equal(assert_raise(NoMethodError) {no_such_method()}) {|o| o.message}
+ obj = Object.new
+ e = assert_raise(NoMethodError) {obj.no_such_method()}
+ marshal_equal(e) {|o| o.message}
end
def test_exception_subclass
@@ -141,25 +154,17 @@ module MarshalTestLib
def test_hash_extend
o1 = Hash.new
o1.extend(Mod1)
- marshal_equal(o1) { |o|
- (class << self; self; end).ancestors
- }
+ marshal_equal_with_ancestry(o1)
o1.extend(Mod2)
- marshal_equal(o1) { |o|
- (class << self; self; end).ancestors
- }
+ marshal_equal_with_ancestry(o1)
end
def test_hash_subclass_extend
o1 = MyHash.new(2)
o1.extend(Mod1)
- marshal_equal(o1) { |o|
- (class << self; self; end).ancestors
- }
+ marshal_equal_with_ancestry(o1)
o1.extend(Mod2)
- marshal_equal(o1) { |o|
- (class << self; self; end).ancestors
- }
+ marshal_equal_with_ancestry(o1)
end
def test_bignum
@@ -178,22 +183,6 @@ module MarshalTestLib
marshal_equal(0x3fff_ffff)
end
- def test_fixnum_ivar
- o1 = 1
- o1.instance_eval { @iv = 2 }
- marshal_equal(o1) {|o| o.instance_eval { @iv }}
- ensure
- 1.instance_eval { remove_instance_variable("@iv") }
- end
-
- def test_fixnum_ivar_self
- o1 = 1
- o1.instance_eval { @iv = 1 }
- marshal_equal(o1) {|o| o.instance_eval { @iv }}
- ensure
- 1.instance_eval { remove_instance_variable("@iv") }
- end
-
def test_float
marshal_equal(-1.0)
marshal_equal(0.0)
@@ -207,30 +196,6 @@ module MarshalTestLib
marshal_equal(NegativeZero) {|o| 1.0/o}
end
- def test_float_ivar
- o1 = 1.23
- o1.instance_eval { @iv = 1 }
- marshal_equal(o1) {|o| o.instance_eval { @iv }}
- end
-
- def test_float_ivar_self
- o1 = 5.5
- o1.instance_eval { @iv = o1 }
- marshal_equal(o1) {|o| o.instance_eval { @iv }}
- end
-
- def test_float_extend
- o1 = 0.0/0.0
- o1.extend(Mod1)
- marshal_equal(o1) { |o|
- (class << self; self; end).ancestors
- }
- o1.extend(Mod2)
- marshal_equal(o1) { |o|
- (class << self; self; end).ancestors
- }
- end
-
class MyRange < Range; def initialize(v, *args) super(*args); @v = v; end end
def test_range
marshal_equal(1..2)
@@ -277,7 +242,7 @@ module MarshalTestLib
str = MyString.new(10, "b")
str.instance_eval { @v = str }
marshal_equal(str) { |o|
- assert_equal(o.__id__, o.instance_eval { @v }.__id__)
+ assert_same(o, o.instance_eval { @v })
o.instance_eval { @v }
}
end
@@ -287,36 +252,17 @@ module MarshalTestLib
o.extend(Mod1)
str = MyString.new(o, "c")
marshal_equal(str) { |v|
- assert(v.instance_eval { @v }.kind_of?(Mod1))
+ assert_kind_of(Mod1, v.instance_eval { @v })
}
end
MyStruct = Struct.new("MyStruct", :a, :b)
- if RUBY_VERSION < "1.8.0"
- # Struct#== is not defined in ruby/1.6
- class MyStruct
- def ==(rhs)
- return true if __id__ == rhs.__id__
- return false unless rhs.is_a?(::Struct)
- return false if self.class != rhs.class
- members.each do |member|
- return false if self.__send__(member) != rhs.__send__(member)
- end
- return true
- end
- end
- end
class MySubStruct < MyStruct; def initialize(v, *args) super(*args); @v = v; end end
def test_struct
marshal_equal(MyStruct.new(1,2))
end
def test_struct_subclass
- if RUBY_VERSION < "1.8.0"
- # Substruct instance cannot be dumped in ruby/1.6
- # ::Marshal.dump(MySubStruct.new(10, 1, 2)) #=> uninitialized struct
- return false
- end
marshal_equal(MySubStruct.new(10,1,2))
end
@@ -329,13 +275,9 @@ module MarshalTestLib
def test_struct_subclass_extend
o1 = MyStruct.new
o1.extend(Mod1)
- marshal_equal(o1) { |o|
- (class << self; self; end).ancestors
- }
+ marshal_equal_with_ancestry(o1)
o1.extend(Mod2)
- marshal_equal(o1) { |o|
- (class << self; self; end).ancestors
- }
+ marshal_equal_with_ancestry(o1)
end
def test_symbol
@@ -426,6 +368,11 @@ module MarshalTestLib
o = Object.new
def o.m() end
assert_raise(TypeError) { marshaltest(o) }
+
+ bug8043 = '[ruby-core:53206] [Bug #8043]'
+ class << o; prepend Mod1; end
+ assert_raise(TypeError, bug8043) {marshaltest(o)}
+
o = Object.new
c = class << o
@v = 1
@@ -444,7 +391,7 @@ module MarshalTestLib
o = Object.new
o.extend Mod1
o.extend Mod2
- marshal_equal(o) {|obj| class << obj; ancestors end}
+ marshal_equal_with_ancestry(o)
o = Object.new
o.extend Module.new
assert_raise(TypeError) { marshaltest(o) }
@@ -457,7 +404,7 @@ module MarshalTestLib
o = ""
o.extend Mod1
o.extend Mod2
- marshal_equal(o) {|obj| class << obj; ancestors end}
+ marshal_equal_with_ancestry(o)
o = ""
o.extend Module.new
assert_raise(TypeError) { marshaltest(o) }
@@ -485,20 +432,6 @@ module MarshalTestLib
end
MyStruct2 = Struct.new(:a, :b)
- if RUBY_VERSION < "1.8.0"
- # Struct#== is not defined in ruby/1.6
- class MyStruct2
- def ==(rhs)
- return true if __id__ == rhs.__id__
- return false unless rhs.is_a?(::Struct)
- return false if self.class != rhs.class
- members.each do |member|
- return false if self.__send__(member) != rhs.__send__(member)
- end
- return true
- end
- end
- end
def test_struct_toplevel
o = MyStruct2.new(1,2)
marshal_equal(o)
diff --git a/test/ruby/sentence.rb b/test/ruby/sentence.rb
index 50f42d6885..28fb5d1cf8 100644
--- a/test/ruby/sentence.rb
+++ b/test/ruby/sentence.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
# == sentence library
#
# = Features
diff --git a/test/ruby/test_alias.rb b/test/ruby/test_alias.rb
index 6320121bce..e81636fa43 100644
--- a/test/ruby/test_alias.rb
+++ b/test/ruby/test_alias.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestAlias < Test::Unit::TestCase
@@ -52,16 +53,6 @@ class TestAlias < Test::Unit::TestCase
end
end
- def test_JVN_83768862
- d = lambda {
- $SAFE = 4
- dclass = Class.new(C)
- dclass.send(:alias_method, :mm, :m)
- dclass.new
- }.call
- assert_raise(SecurityError) { d.mm }
- end
-
def test_nonexistmethod
assert_raise(NameError){
Class.new{
@@ -104,4 +95,142 @@ class TestAlias < Test::Unit::TestCase
end
assert_equal(:ok, d.new.bar)
end
+
+ module SuperInAliasedModuleMethod
+ module M
+ def foo
+ super << :M
+ end
+
+ alias bar foo
+ end
+
+ class Base
+ def foo
+ [:Base]
+ end
+ end
+
+ class Derived < Base
+ include M
+ end
+ end
+
+ # [ruby-dev:46028]
+ def test_super_in_aliased_module_method # fails in 1.8
+ assert_equal([:Base, :M], SuperInAliasedModuleMethod::Derived.new.bar)
+ end
+
+ def test_alias_wb_miss
+ assert_normal_exit "#{<<-"begin;"}\n#{<<-'end;'}"
+ begin;
+ require 'stringio'
+ GC.verify_internal_consistency
+ GC.start
+ class StringIO
+ alias_method :read_nonblock, :sysread
+ end
+ GC.verify_internal_consistency
+ end;
+ end
+
+ def test_cyclic_zsuper
+ bug9475 = '[ruby-core:60431] [Bug #9475]'
+
+ a = Module.new do
+ def foo
+ "A"
+ end
+ end
+
+ b = Class.new do
+ include a
+ attr_reader :b
+
+ def foo
+ @b ||= 0
+ raise SystemStackError if (@b += 1) > 1
+ # "foo from B"
+ super + "B"
+ end
+ end
+
+ c = Class.new(b) do
+ alias orig_foo foo
+
+ def foo
+ # "foo from C"
+ orig_foo + "C"
+ end
+ end
+
+ b.class_eval do
+ alias orig_foo foo
+ attr_reader :b2
+
+ def foo
+ @b2 ||= 0
+ raise SystemStackError if (@b2 += 1) > 1
+ # "foo from B (again)"
+ orig_foo + "B2"
+ end
+ end
+
+ assert_nothing_raised(SystemStackError, bug9475) do
+ assert_equal("ABC", c.new.foo, bug9475)
+ end
+ end
+
+ def test_alias_in_module
+ bug9663 = '[ruby-core:61635] [Bug #9663]'
+
+ assert_separately(['-', bug9663], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ bug = ARGV[0]
+
+ m = Module.new do
+ alias orig_to_s to_s
+ end
+
+ o = Object.new.extend(m)
+ assert_equal(o.to_s, o.orig_to_s, bug)
+ end;
+ end
+
+ class C0; def foo; end; end
+ class C1 < C0; alias bar foo; end
+
+ def test_alias_method_equation
+ obj = C1.new
+ assert_equal(obj.method(:bar), obj.method(:foo))
+ assert_equal(obj.method(:foo), obj.method(:bar))
+ end
+
+ def test_alias_class_method_added
+ name = nil
+ k = Class.new {
+ def foo;end
+ def self.method_added(mid)
+ @name = instance_method(mid).original_name
+ end
+ alias bar foo
+ name = @name
+ }
+ assert_equal(:foo, k.instance_method(:bar).original_name)
+ assert_equal(:foo, name)
+ end
+
+ def test_alias_module_method_added
+ name = nil
+ k = Module.new {
+ def foo;end
+ def self.method_added(mid)
+ @name = instance_method(mid).original_name
+ end
+ alias bar foo
+ name = @name
+ }
+ assert_equal(:foo, k.instance_method(:bar).original_name)
+ assert_equal(:foo, name)
+ end
end
diff --git a/test/ruby/test_argf.rb b/test/ruby/test_argf.rb
index ffd5096789..a76bdccf45 100644
--- a/test/ruby/test_argf.rb
+++ b/test/ruby/test_argf.rb
@@ -1,51 +1,53 @@
+# frozen_string_literal: false
require 'test/unit'
require 'timeout'
require 'tmpdir'
require 'tempfile'
-require_relative 'envutil'
+require 'fileutils'
class TestArgf < Test::Unit::TestCase
def setup
- @t1 = Tempfile.new("argf-foo")
+ @tmpdir = Dir.mktmpdir
+ @tmp_count = 0
+ @t1 = make_tempfile0("argf-foo")
@t1.binmode
@t1.puts "1"
@t1.puts "2"
@t1.close
- @t2 = Tempfile.new("argf-bar")
+ @t2 = make_tempfile0("argf-bar")
@t2.binmode
@t2.puts "3"
@t2.puts "4"
@t2.close
- @t3 = Tempfile.new("argf-baz")
+ @t3 = make_tempfile0("argf-baz")
@t3.binmode
@t3.puts "5"
@t3.puts "6"
@t3.close
- @tmps = [@t1, @t2, @t3]
end
def teardown
- @tmps.each {|t|
- bak = t.path + ".bak"
- File.unlink bak if File.file? bak
- t.close(true)
- }
+ FileUtils.rmtree(@tmpdir)
+ end
+
+ def make_tempfile0(basename)
+ @tmp_count += 1
+ open("#{@tmpdir}/#{basename}-#{@tmp_count}", "w")
end
- def make_tempfile
- t = Tempfile.new("argf-qux")
+ def make_tempfile(basename = "argf-qux")
+ t = make_tempfile0(basename)
t.puts "foo"
t.puts "bar"
t.puts "baz"
t.close
- @tmps << t
t
end
- def ruby(*args)
+ def ruby(*args, external_encoding: Encoding::UTF_8)
args = ['-e', '$>.write($<.read)'] if args.empty?
ruby = EnvUtil.rubybin
- f = IO.popen([ruby] + args, 'r+')
+ f = IO.popen([ruby] + args, 'r+', external_encoding: external_encoding)
yield(f)
ensure
f.close unless !f || f.closed?
@@ -55,7 +57,7 @@ class TestArgf < Test::Unit::TestCase
/cygwin|mswin|mingw|bccwin/ =~ RUBY_PLATFORM
end
- def assert_src_expected(line, src, args = nil)
+ def assert_src_expected(src, args = nil, line: caller_locations(1, 1)[0].lineno+1)
args ||= [@t1.path, @t2.path, @t3.path]
expected = src.split(/^/)
ruby('-e', src, *args) do |f|
@@ -69,79 +71,108 @@ class TestArgf < Test::Unit::TestCase
end
def test_argf
- assert_src_expected(__LINE__+1, <<-'SRC')
+ assert_src_expected("#{<<~"{#"}\n#{<<~'};'}")
+ {#
a = ARGF
b = a.dup
p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["1", 1, "1", 1]
p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["2", 2, "2", 2]
a.rewind
b.rewind
- p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["1", 1, "1", 3]
- p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["2", 2, "2", 4]
- p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["3", 3, "3", 5]
- p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["4", 4, "4", 6]
- p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["5", 5, "5", 7]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["1", 1, "1", 1]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["2", 2, "2", 2]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["3", 3, "3", 3]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["4", 4, "4", 4]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["5", 5, "5", 5]
a.rewind
b.rewind
- p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["5", 5, "5", 8]
- p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["6", 6, "6", 9]
- SRC
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["5", 5, "5", 5]
+ p [a.gets.chomp, a.lineno, b.gets.chomp, b.lineno] #=> ["6", 6, "6", 6]
+ };
end
def test_lineno
- assert_src_expected(__LINE__+1, <<-'SRC')
+ assert_src_expected("#{<<~"{#"}\n#{<<~'};'}")
+ {#
a = ARGF
- a.gets; p $. #=> 1
- a.gets; p $. #=> 2
- a.gets; p $. #=> 3
- a.rewind; p $. #=> 3
- a.gets; p $. #=> 3
- a.gets; p $. #=> 4
- a.rewind; p $. #=> 4
- a.gets; p $. #=> 3
- a.lineno = 1000; p $. #=> 1000
- a.gets; p $. #=> 1001
- a.gets; p $. #=> 1002
+ a.gets; p($.) #=> 1
+ a.gets; p($.) #=> 2
+ a.gets; p($.) #=> 3
+ a.rewind; p($.) #=> 3
+ a.gets; p($.) #=> 3
+ a.gets; p($.) #=> 4
+ a.rewind; p($.) #=> 4
+ a.gets; p($.) #=> 3
+ a.lineno = 1000; p($.) #=> 1000
+ a.gets; p($.) #=> 1001
+ a.gets; p($.) #=> 1002
$. = 2000
- a.gets; p $. #=> 2001
- a.gets; p $. #=> 2001
- SRC
+ a.gets; p($.) #=> 2001
+ a.gets; p($.) #=> 2001
+ };
end
def test_lineno2
- assert_src_expected(__LINE__+1, <<-'SRC')
+ assert_src_expected("#{<<~"{#"}\n#{<<~'};'}")
+ {#
a = ARGF.dup
- a.gets; p $. #=> 1
- a.gets; p $. #=> 2
- a.gets; p $. #=> 1
- a.rewind; p $. #=> 1
- a.gets; p $. #=> 1
- a.gets; p $. #=> 2
- a.gets; p $. #=> 1
- a.lineno = 1000; p $. #=> 1
- a.gets; p $. #=> 2
- a.gets; p $. #=> 2
+ a.gets; p($.) #=> 1
+ a.gets; p($.) #=> 2
+ a.gets; p($.) #=> 1
+ a.rewind; p($.) #=> 1
+ a.gets; p($.) #=> 1
+ a.gets; p($.) #=> 2
+ a.gets; p($.) #=> 1
+ a.lineno = 1000; p($.) #=> 1
+ a.gets; p($.) #=> 2
+ a.gets; p($.) #=> 2
$. = 2000
- a.gets; p $. #=> 2000
- a.gets; p $. #=> 2000
- SRC
+ a.gets; p($.) #=> 2000
+ a.gets; p($.) #=> 2000
+ };
end
def test_lineno3
- assert_in_out_err(["-", @t1.path, @t2.path], <<-INPUT, %w"1 1 1 2 2 2 3 3 1 4 4 2", [], "[ruby-core:25205]")
+ expected = %w"1 1 1 2 2 2 3 3 1 4 4 2"
+ assert_in_out_err(["-", @t1.path, @t2.path],
+ "#{<<~"{#"}\n#{<<~'};'}", expected, [], "[ruby-core:25205]")
+ {#
ARGF.each do |line|
puts [$., ARGF.lineno, ARGF.file.lineno]
end
- INPUT
+ };
+ end
+
+ def test_new_lineno_each
+ f = ARGF.class.new(@t1.path, @t2.path, @t3.path)
+ result = []
+ f.each {|line| result << [f.lineno, line]; break if result.size == 3}
+ assert_equal(3, f.lineno)
+ assert_equal((1..3).map {|i| [i, "#{i}\n"]}, result)
+
+ f.rewind
+ assert_equal(2, f.lineno)
+ ensure
+ f.close
+ end
+
+ def test_new_lineno_each_char
+ f = ARGF.class.new(@t1.path, @t2.path, @t3.path)
+ f.each_char.to_a
+ assert_equal(0, f.lineno)
+ ensure
+ f.close
end
def test_inplace
- assert_in_out_err(["-", @t1.path, @t2.path, @t3.path], <<-INPUT, [], [])
+ assert_in_out_err(["-", @t1.path, @t2.path, @t3.path],
+ "#{<<~"{#"}\n#{<<~'};'}")
+ {#
ARGF.inplace_mode = '.bak'
while line = ARGF.gets
puts line.chomp + '.new'
end
- INPUT
+ };
assert_equal("1.new\n2.new\n", File.read(@t1.path))
assert_equal("3.new\n4.new\n", File.read(@t2.path))
assert_equal("5.new\n6.new\n", File.read(@t3.path))
@@ -151,7 +182,9 @@ class TestArgf < Test::Unit::TestCase
end
def test_inplace2
- assert_in_out_err(["-", @t1.path, @t2.path, @t3.path], <<-INPUT, [], [])
+ assert_in_out_err(["-", @t1.path, @t2.path, @t3.path],
+ "#{<<~"{#"}\n#{<<~'};'}")
+ {#
ARGF.inplace_mode = '.bak'
puts ARGF.gets.chomp + '.new'
puts ARGF.gets.chomp + '.new'
@@ -165,7 +198,7 @@ class TestArgf < Test::Unit::TestCase
p ARGF.inplace_mode
ARGF.inplace_mode = nil
puts ARGF.gets.chomp + '.new'
- INPUT
+ };
assert_equal("1.new\n2.new\n\".bak\"\n3.new\n4.new\nnil\n", File.read(@t1.path))
assert_equal("3\n4\n", File.read(@t2.path))
assert_equal("5.new\n\".bak\"\n6.new\n", File.read(@t3.path))
@@ -175,7 +208,9 @@ class TestArgf < Test::Unit::TestCase
end
def test_inplace3
- assert_in_out_err(["-i.bak", "-", @t1.path, @t2.path, @t3.path], <<-INPUT, [], [])
+ assert_in_out_err(["-i.bak", "-", @t1.path, @t2.path, @t3.path],
+ "#{<<~"{#"}\n#{<<~'};'}")
+ {#
puts ARGF.gets.chomp + '.new'
puts ARGF.gets.chomp + '.new'
p $-i
@@ -188,7 +223,7 @@ class TestArgf < Test::Unit::TestCase
p $-i
$-i = nil
puts ARGF.gets.chomp + '.new'
- INPUT
+ };
assert_equal("1.new\n2.new\n\".bak\"\n3.new\n4.new\nnil\n", File.read(@t1.path))
assert_equal("3\n4\n", File.read(@t2.path))
assert_equal("5.new\n\".bak\"\n6.new\n", File.read(@t3.path))
@@ -200,33 +235,56 @@ class TestArgf < Test::Unit::TestCase
def test_inplace_rename_impossible
t = make_tempfile
- assert_in_out_err(["-", t.path], <<-INPUT) do |r, e|
- ARGF.inplace_mode = '/\\\\'
- while line = ARGF.gets
- puts line.chomp + '.new'
- end
- INPUT
- if no_safe_rename
- assert_equal([], e)
- assert_equal([], r)
- assert_equal("foo.new\nbar.new\nbaz.new\n", File.read(t.path))
- else
- assert_match(/Can't rename .* to .*: .*. skipping file/, e.first) #'
- assert_equal([], r)
- assert_equal("foo\nbar\nbaz\n", File.read(t.path))
- end
+ assert_in_out_err(["-", t.path], "#{<<~"{#"}\n#{<<~'};'}") do |r, e|
+ {#
+ ARGF.inplace_mode = '/\\\\:'
+ while line = ARGF.gets
+ puts line.chomp + '.new'
+ end
+ };
+ assert_match(/Can't rename .* to .*: .*. skipping file/, e.first) #'
+ assert_equal([], r)
+ assert_equal("foo\nbar\nbaz\n", File.read(t.path))
+ end
+
+ base = "argf-\u{30c6 30b9 30c8}"
+ name = "#{@tmpdir}/#{base}"
+ File.write(name, "foo")
+ argf = ARGF.class.new(name)
+ argf.inplace_mode = '/\\:'
+ assert_warning(/#{base}/) {argf.gets}
+ end
+
+ def test_inplace_nonascii
+ ext = Encoding.default_external or
+ skip "no default external encoding"
+ t = nil
+ ["\u{3042}", "\u{e9}"].any? do |n|
+ t = make_tempfile(n.encode(ext))
+ rescue Encoding::UndefinedConversionError
end
+ t or skip "no name to test"
+ assert_in_out_err(["-i.bak", "-", t.path],
+ "#{<<~"{#"}\n#{<<~'};'}")
+ {#
+ puts ARGF.gets.chomp + '.new'
+ puts ARGF.gets.chomp + '.new'
+ puts ARGF.gets.chomp + '.new'
+ };
+ assert_equal("foo.new\n""bar.new\n""baz.new\n", File.read(t.path))
+ assert_equal("foo\n""bar\n""baz\n", File.read(t.path + ".bak"))
end
def test_inplace_no_backup
t = make_tempfile
- assert_in_out_err(["-", t.path], <<-INPUT) do |r, e|
- ARGF.inplace_mode = ''
- while line = ARGF.gets
- puts line.chomp + '.new'
- end
- INPUT
+ assert_in_out_err(["-", t.path], "#{<<~"{#"}\n#{<<~'};'}") do |r, e|
+ {#
+ ARGF.inplace_mode = ''
+ while line = ARGF.gets
+ puts line.chomp + '.new'
+ end
+ };
if no_safe_rename
assert_match(/Can't do inplace edit without backup/, e.join) #'
else
@@ -240,67 +298,125 @@ class TestArgf < Test::Unit::TestCase
def test_inplace_dup
t = make_tempfile
- assert_in_out_err(["-", t.path], <<-INPUT, [], [])
+ assert_in_out_err(["-", t.path], "#{<<~"{#"}\n#{<<~'};'}", [], [])
+ {#
ARGF.inplace_mode = '.bak'
f = ARGF.dup
while line = f.gets
puts line.chomp + '.new'
end
- INPUT
+ };
assert_equal("foo.new\nbar.new\nbaz.new\n", File.read(t.path))
end
def test_inplace_stdin
- t = make_tempfile
-
- assert_in_out_err(["-", "-"], <<-INPUT, [], /Can't do inplace edit for stdio; skipping/)
+ assert_in_out_err(["-", "-"], "#{<<~"{#"}\n#{<<~'};'}", [], /Can't do inplace edit for stdio; skipping/)
+ {#
ARGF.inplace_mode = '.bak'
f = ARGF.dup
while line = f.gets
puts line.chomp + '.new'
end
- INPUT
+ };
end
def test_inplace_stdin2
- t = make_tempfile
-
- assert_in_out_err(["-"], <<-INPUT, [], /Can't do inplace edit for stdio/)
+ assert_in_out_err(["-"], "#{<<~"{#"}\n#{<<~'};'}", [], /Can't do inplace edit for stdio/)
+ {#
ARGF.inplace_mode = '.bak'
while line = ARGF.gets
puts line.chomp + '.new'
end
- INPUT
+ };
+ end
+
+ def test_inplace_invalid_backup
+ assert_raise(ArgumentError, '[ruby-dev:50272] [Bug #13960]') {
+ ARGF.inplace_mode = "a\0"
+ }
+ end
+
+ def test_inplace_to_path
+ base = "argf-test"
+ name = "#{@tmpdir}/#{base}"
+ File.write(name, "foo")
+ stdout = $stdout
+ argf = ARGF.class.new(Struct.new(:to_path).new(name))
+ begin
+ result = argf.gets
+ ensure
+ $stdout = stdout
+ argf.close
+ end
+ assert_equal("foo", result)
+ end
+
+ def test_inplace_ascii_incompatible_path
+ base = "argf-\u{30c6 30b9 30c8}"
+ name = "#{@tmpdir}/#{base}"
+ File.write(name, "foo")
+ stdout = $stdout
+ argf = ARGF.class.new(name.encode(Encoding::UTF_16LE))
+ assert_raise(Encoding::CompatibilityError) do
+ argf.gets
+ end
+ ensure
+ $stdout = stdout
+ end
+
+ def test_inplace_suffix_encoding
+ base = "argf-\u{30c6 30b9 30c8}"
+ name = "#{@tmpdir}/#{base}"
+ suffix = "-bak"
+ File.write(name, "foo")
+ stdout = $stdout
+ argf = ARGF.class.new(name)
+ argf.inplace_mode = suffix.encode(Encoding::UTF_16LE)
+ begin
+ argf.each do |s|
+ puts "+"+s
+ end
+ ensure
+ $stdout.close unless $stdout == stdout
+ $stdout = stdout
+ end
+ assert_file.exist?(name)
+ assert_equal("+foo\n", File.read(name))
+ assert_file.not_exist?(name+"-")
+ assert_file.exist?(name+suffix)
+ assert_equal("foo", File.read(name+suffix))
end
def test_encoding
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- p ARGF.external_encoding.is_a?(Encoding)
- p ARGF.internal_encoding.is_a?(Encoding)
- ARGF.gets
- p ARGF.external_encoding.is_a?(Encoding)
- p ARGF.internal_encoding
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ p ARGF.external_encoding.is_a?(Encoding)
+ p ARGF.internal_encoding.is_a?(Encoding)
+ ARGF.gets
+ p ARGF.external_encoding.is_a?(Encoding)
+ p ARGF.internal_encoding
+ };
assert_equal("true\ntrue\ntrue\nnil\n", f.read)
end
end
def test_tell
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- begin
- ARGF.binmode
- loop do
- p ARGF.tell
- p ARGF.gets
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ begin
+ ARGF.binmode
+ loop do
+ p ARGF.tell
+ p ARGF.gets
+ end
+ rescue ArgumentError
+ puts "end"
end
- rescue ArgumentError
- puts "end"
- end
- SRC
+ };
a = f.read.split("\n")
[0, 2, 4, 2, 4, 2, 4].map {|i| i.to_s }.
- zip((1..6).map {|i| '"' + i.to_s + '\n"' } + ["nil"]).flatten.
- each do |x|
+ zip((1..6).map {|i| '"' + i.to_s + '\n"' } + ["nil"]).flatten.
+ each do |x|
assert_equal(x, a.shift)
end
assert_equal('end', a.shift)
@@ -308,7 +424,8 @@ class TestArgf < Test::Unit::TestCase
end
def test_seek
- assert_src_expected(__LINE__+1, <<-'SRC')
+ assert_src_expected("#{<<~"{#"}\n#{<<~'};'}")
+ {#
ARGF.seek(4)
p ARGF.gets #=> "3\n"
ARGF.seek(0, IO::SEEK_END)
@@ -320,11 +437,12 @@ class TestArgf < Test::Unit::TestCase
rescue
puts "end" #=> end
end
- SRC
+ };
end
def test_set_pos
- assert_src_expected(__LINE__+1, <<-'SRC')
+ assert_src_expected("#{<<~"{#"}\n#{<<~'};'}")
+ {#
ARGF.pos = 4
p ARGF.gets #=> "3\n"
ARGF.pos = 4
@@ -336,11 +454,12 @@ class TestArgf < Test::Unit::TestCase
rescue
puts "end" #=> end
end
- SRC
+ };
end
def test_rewind
- assert_src_expected(__LINE__+1, <<-'SRC')
+ assert_src_expected("#{<<~"{#"}\n#{<<~'};'}")
+ {#
ARGF.pos = 4
ARGF.rewind
p ARGF.gets #=> "1\n"
@@ -355,28 +474,29 @@ class TestArgf < Test::Unit::TestCase
rescue
puts "end" #=> end
end
- SRC
+ };
end
def test_fileno
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- p ARGF.fileno
- ARGF.gets
- ARGF.gets
- p ARGF.fileno
- ARGF.gets
- ARGF.gets
- p ARGF.fileno
- ARGF.gets
- ARGF.gets
- p ARGF.fileno
- ARGF.gets
- begin
- ARGF.fileno
- rescue
- puts "end"
- end
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ p ARGF.fileno
+ ARGF.gets
+ ARGF.gets
+ p ARGF.fileno
+ ARGF.gets
+ ARGF.gets
+ p ARGF.fileno
+ ARGF.gets
+ ARGF.gets
+ p ARGF.fileno
+ ARGF.gets
+ begin
+ ARGF.fileno
+ rescue
+ puts "end"
+ end
+ };
a = f.read.split("\n")
fd1, fd2, fd3, fd4, tag = a
assert_match(/^\d+$/, fd1)
@@ -388,12 +508,13 @@ class TestArgf < Test::Unit::TestCase
end
def test_to_io
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- 8.times do
- p ARGF.to_io
- ARGF.gets
- end
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ 8.times do
+ p ARGF.to_io
+ ARGF.gets
+ end
+ };
a = f.read.split("\n")
f11, f12, f13, f21, f22, f31, f32, f4 = a
assert_equal(f11, f12)
@@ -407,27 +528,28 @@ class TestArgf < Test::Unit::TestCase
end
def test_eof
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- begin
- 8.times do
- p ARGF.eof?
- ARGF.gets
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ begin
+ 8.times do
+ p ARGF.eof?
+ ARGF.gets
+ end
+ rescue IOError
+ puts "end"
end
- rescue IOError
- puts "end"
- end
- SRC
+ };
a = f.read.split("\n")
(%w(false) + (%w(false true) * 3) + %w(end)).each do |x|
assert_equal(x, a.shift)
end
end
- t1 = Tempfile.new("argf-foo")
+ t1 = open("#{@tmpdir}/argf-hoge", "w")
t1.binmode
t1.puts "foo"
t1.close
- t2 = Tempfile.new("argf-bar")
+ t2 = open("#{@tmpdir}/argf-moge", "w")
t2.binmode
t2.puts "bar"
t2.close
@@ -443,54 +565,71 @@ class TestArgf < Test::Unit::TestCase
end
def test_read2
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = ""
- ARGF.read(8, s)
- p s
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = ""
+ ARGF.read(8, s)
+ p s
+ };
+ assert_equal("\"1\\n2\\n3\\n4\\n\"\n", f.read)
+ end
+ end
+
+ def test_read2_with_not_empty_buffer
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = "0123456789"
+ ARGF.read(8, s)
+ p s
+ };
assert_equal("\"1\\n2\\n3\\n4\\n\"\n", f.read)
end
end
def test_read3
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- nil while ARGF.gets
- p ARGF.read
- p ARGF.read(0, "")
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ nil while ARGF.gets
+ p ARGF.read
+ p ARGF.read(0, "")
+ };
assert_equal("nil\n\"\"\n", f.read)
end
end
def test_readpartial
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = ""
- begin
- loop do
- s << ARGF.readpartial(1)
- t = ""; ARGF.readpartial(1, t); s << t
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = ""
+ begin
+ loop do
+ s << ARGF.readpartial(1)
+ t = ""; ARGF.readpartial(1, t); s << t
+ # not empty buffer
+ u = "abcdef"; ARGF.readpartial(1, u); s << u
+ end
+ rescue EOFError
+ puts s
end
- rescue EOFError
- puts s
- end
- SRC
+ };
assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
end
end
def test_readpartial2
- ruby('-e', <<-SRC) do |f|
- s = ""
- begin
- loop do
- s << ARGF.readpartial(1)
- t = ""; ARGF.readpartial(1, t); s << t
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}") do |f|
+ {#
+ s = ""
+ begin
+ loop do
+ s << ARGF.readpartial(1)
+ t = ""; ARGF.readpartial(1, t); s << t
+ end
+ rescue EOFError
+ $stdout.binmode
+ puts s
end
- rescue EOFError
- $stdout.binmode
- puts s
- end
- SRC
+ };
f.binmode
f.puts("foo")
f.puts("bar")
@@ -500,66 +639,83 @@ class TestArgf < Test::Unit::TestCase
end
end
+ def test_readpartial_eof_twice
+ ruby('-W1', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path) do |f|
+ {#
+ $stderr = $stdout
+ print ARGF.readpartial(256)
+ ARGF.readpartial(256) rescue p($!.class)
+ ARGF.readpartial(256) rescue p($!.class)
+ };
+ assert_equal("1\n2\nEOFError\nEOFError\n", f.read)
+ end
+ end
+
def test_getc
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = ""
- while c = ARGF.getc
- s << c
- end
- puts s
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = ""
+ while c = ARGF.getc
+ s << c
+ end
+ puts s
+ };
assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
end
end
def test_getbyte
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = []
- while c = ARGF.getbyte
- s << c
- end
- p s
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = []
+ while c = ARGF.getbyte
+ s << c
+ end
+ p s
+ };
assert_equal("[49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10]\n", f.read)
end
end
def test_readchar
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = ""
- begin
- while c = ARGF.readchar
- s << c
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = ""
+ begin
+ while c = ARGF.readchar
+ s << c
+ end
+ rescue EOFError
+ puts s
end
- rescue EOFError
- puts s
- end
- SRC
+ };
assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
end
end
def test_readbyte
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- begin
- s = []
- while c = ARGF.readbyte
- s << c
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ begin
+ s = []
+ while c = ARGF.readbyte
+ s << c
+ end
+ rescue EOFError
+ p s
end
- rescue EOFError
- p s
- end
- SRC
+ };
assert_equal("[49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10]\n", f.read)
end
end
def test_each_line
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = []
- ARGF.each_line {|l| s << l }
- p s
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = []
+ ARGF.each_line {|l| s << l }
+ p s
+ };
assert_equal("[\"1\\n\", \"2\\n\", \"3\\n\", \"4\\n\", \"5\\n\", \"6\\n\"]\n", f.read)
end
end
@@ -570,32 +726,35 @@ class TestArgf < Test::Unit::TestCase
end
def test_each_byte
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = []
- ARGF.each_byte {|c| s << c }
- p s
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = []
+ ARGF.each_byte {|c| s << c }
+ p s
+ };
assert_equal("[49, 10, 50, 10, 51, 10, 52, 10, 53, 10, 54, 10]\n", f.read)
end
end
def test_each_char
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = ""
- ARGF.each_char {|c| s << c }
- puts s
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ s = ""
+ ARGF.each_char {|c| s << c }
+ puts s
+ };
assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
end
end
def test_filename
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- begin
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ begin
+ puts ARGF.filename.dump
+ end while ARGF.gets
puts ARGF.filename.dump
- end while ARGF.gets
- puts ARGF.filename.dump
- SRC
+ };
a = f.read.split("\n")
assert_equal(@t1.path.dump, a.shift)
assert_equal(@t1.path.dump, a.shift)
@@ -609,12 +768,13 @@ class TestArgf < Test::Unit::TestCase
end
def test_filename2
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- begin
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ begin
+ puts $FILENAME.dump
+ end while ARGF.gets
puts $FILENAME.dump
- end while ARGF.gets
- puts $FILENAME.dump
- SRC
+ };
a = f.read.split("\n")
assert_equal(@t1.path.dump, a.shift)
assert_equal(@t1.path.dump, a.shift)
@@ -628,12 +788,13 @@ class TestArgf < Test::Unit::TestCase
end
def test_file
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- begin
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ begin
+ puts ARGF.file.path.dump
+ end while ARGF.gets
puts ARGF.file.path.dump
- end while ARGF.gets
- puts ARGF.file.path.dump
- SRC
+ };
a = f.read.split("\n")
assert_equal(@t1.path.dump, a.shift)
assert_equal(@t1.path.dump, a.shift)
@@ -647,43 +808,117 @@ class TestArgf < Test::Unit::TestCase
end
def test_binmode
+ bug5268 = '[ruby-core:39234]'
+ open(@t3.path, "wb") {|f| f.write "5\r\n6\r\n"}
ruby('-e', "ARGF.binmode; STDOUT.binmode; puts ARGF.read", @t1.path, @t2.path, @t3.path) do |f|
f.binmode
- assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
+ assert_equal("1\n2\n3\n4\n5\r\n6\r\n", f.read, bug5268)
end
end
+ def test_textmode
+ bug5268 = '[ruby-core:39234]'
+ open(@t3.path, "wb") {|f| f.write "5\r\n6\r\n"}
+ ruby('-e', "STDOUT.binmode; puts ARGF.read", @t1.path, @t2.path, @t3.path) do |f|
+ f.binmode
+ assert_equal("1\n2\n3\n4\n5\n6\n", f.read, bug5268)
+ end
+ end unless IO::BINARY.zero?
+
def test_skip
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- ARGF.skip
- puts ARGF.gets
- ARGF.skip
- puts ARGF.read
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ ARGF.skip
+ puts ARGF.gets
+ ARGF.skip
+ puts ARGF.read
+ };
assert_equal("1\n3\n4\n5\n6\n", f.read)
end
end
+ def test_skip_in_each_line
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ ARGF.each_line {|l| print l; ARGF.skip}
+ };
+ assert_equal("1\n3\n5\n", f.read, '[ruby-list:49185]')
+ end
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ ARGF.each_line {|l| ARGF.skip; puts [l, ARGF.gets].map {|s| s ? s.chomp : s.inspect}.join("+")}
+ };
+ assert_equal("1+3\n4+5\n6+nil\n", f.read, '[ruby-list:49185]')
+ end
+ end
+
+ def test_skip_in_each_byte
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ ARGF.each_byte {|l| print l; ARGF.skip}
+ };
+ assert_equal("135".unpack("C*").join(""), f.read, '[ruby-list:49185]')
+ end
+ end
+
+ def test_skip_in_each_char
+ [[@t1, "\u{3042}"], [@t2, "\u{3044}"], [@t3, "\u{3046}"]].each do |f, s|
+ File.write(f.path, s, mode: "w:utf-8")
+ end
+ ruby('-Eutf-8', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ ARGF.each_char {|l| print l; ARGF.skip}
+ };
+ assert_equal("\u{3042 3044 3046}", f.read, '[ruby-list:49185]')
+ end
+ end
+
+ def test_skip_in_each_codepoint
+ [[@t1, "\u{3042}"], [@t2, "\u{3044}"], [@t3, "\u{3046}"]].each do |f, s|
+ File.write(f.path, s, mode: "w:utf-8")
+ end
+ ruby('-Eutf-8', '-Eutf-8', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ ARGF.each_codepoint {|l| printf "%x:", l; ARGF.skip}
+ };
+ assert_equal("3042:3044:3046:", f.read, '[ruby-list:49185]')
+ end
+ end
+
def test_close
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- ARGF.close
- puts ARGF.read
- SRC
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ ARGF.close
+ puts ARGF.read
+ };
assert_equal("3\n4\n5\n6\n", f.read)
end
end
+ def test_close_replace
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}") do |f|
+ paths = ['#{@t1.path}', '#{@t2.path}', '#{@t3.path}']
+ {#
+ ARGF.close
+ ARGV.replace paths
+ puts ARGF.read
+ };
+ assert_equal("1\n2\n3\n4\n5\n6\n", f.read)
+ end
+ end
+
def test_closed
- ruby('-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- 3.times do
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ 3.times do
+ p ARGF.closed?
+ ARGF.gets
+ ARGF.gets
+ end
p ARGF.closed?
ARGF.gets
- ARGF.gets
- end
- p ARGF.closed?
- ARGF.gets
- p ARGF.closed?
- SRC
+ p ARGF.closed?
+ };
assert_equal("false\nfalse\nfalse\nfalse\ntrue\n", f.read)
end
end
@@ -694,4 +929,149 @@ class TestArgf < Test::Unit::TestCase
assert_equal([@t1.path, @t2.path, @t3.path].inspect, f.gets.chomp)
end
end
+
+ def test_readlines_limit_0
+ bug4024 = '[ruby-dev:42538]'
+ t = make_tempfile
+ argf = ARGF.class.new(t.path)
+ begin
+ assert_raise(ArgumentError, bug4024) do
+ argf.readlines(0)
+ end
+ ensure
+ argf.close
+ end
+ end
+
+ def test_each_line_limit_0
+ bug4024 = '[ruby-dev:42538]'
+ t = make_tempfile
+ argf = ARGF.class.new(t.path)
+ begin
+ assert_raise(ArgumentError, bug4024) do
+ argf.each_line(0).next
+ end
+ ensure
+ argf.close
+ end
+ end
+
+ def test_unreadable
+ bug4274 = '[ruby-core:34446]'
+ paths = (1..2).map do
+ t = Tempfile.new("bug4274-")
+ path = t.path
+ t.close!
+ path
+ end
+ argf = ARGF.class.new(*paths)
+ paths.each do |path|
+ assert_raise_with_message(Errno::ENOENT, /- #{Regexp.quote(path)}\z/) {argf.gets}
+ end
+ assert_nil(argf.gets, bug4274)
+ end
+
+ def test_readlines_twice
+ bug5952 = '[ruby-dev:45160]'
+ assert_ruby_status(["-e", "2.times {STDIN.tty?; readlines}"], "", bug5952)
+ 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)
+ end
+ 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))
+ end
+ 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
+ end
+
+ def test_codepoints
+ ruby('-W1', '-e', "#{<<~"{#"}\n#{<<~'};'}", @t1.path, @t2.path, @t3.path) do |f|
+ {#
+ $stderr = $stdout
+ print Marshal.dump(ARGF.codepoints.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
+
+ def test_read_nonblock
+ ruby('-e', "#{<<~"{#"}\n#{<<~'};'}") do |f|
+ {#
+ $stdout.sync = true
+ :wait_readable == ARGF.read_nonblock(1, "", exception: false) or
+ abort "did not return :wait_readable"
+
+ begin
+ ARGF.read_nonblock(1)
+ abort 'fail to raise IO::WaitReadable'
+ rescue IO::WaitReadable
+ end
+ puts 'starting select'
+
+ IO.select([ARGF]) == [[ARGF], [], []] or
+ abort 'did not awaken for readability (before byte)'
+
+ buf = ''
+ buf.object_id == ARGF.read_nonblock(1, buf).object_id or
+ abort "read destination buffer failed"
+ print buf
+
+ IO.select([ARGF]) == [[ARGF], [], []] or
+ abort 'did not awaken for readability (before EOF)'
+
+ ARGF.read_nonblock(1, buf, exception: false) == nil or
+ abort "EOF should return nil if exception: false"
+
+ begin
+ ARGF.read_nonblock(1, buf)
+ abort 'fail to raise IO::WaitReadable'
+ rescue EOFError
+ puts 'done with eof'
+ end
+ };
+ f.sync = true
+ assert_equal "starting select\n", f.gets
+ f.write('.') # wake up from IO.select
+ assert_equal '.', f.read(1)
+ f.close_write
+ assert_equal "done with eof\n", f.gets
+ end
+ end
+
+ def test_wrong_type
+ assert_separately([], "#{<<~"{#"}\n#{<<~'};'}")
+ {#
+ bug11610 = '[ruby-core:71140] [Bug #11610]'
+ ARGV[0] = nil
+ assert_raise(TypeError, bug11610) {gets}
+ };
+ end
end
diff --git a/test/ruby/test_arithmetic_sequence.rb b/test/ruby/test_arithmetic_sequence.rb
new file mode 100644
index 0000000000..143906f4e2
--- /dev/null
+++ b/test/ruby/test_arithmetic_sequence.rb
@@ -0,0 +1,464 @@
+# frozen_string_literal: false
+require 'test/unit'
+
+class TestArithmeticSequence < Test::Unit::TestCase
+ def test_new
+ assert_raise(NoMethodError) { Enumerator::ArithmeticSequence.new }
+ end
+
+ def test_allocate
+ assert_raise(TypeError) { Enumerator::ArithmeticSequence.allocate }
+ end
+
+ def test_begin
+ assert_equal(1, 1.step.begin)
+ assert_equal(1, 1.step(10).begin)
+ assert_equal(1, 1.step(to: 10).begin)
+ assert_equal(1, 1.step(nil).begin)
+ assert_equal(1, 1.step(to: nil).begin)
+ assert_equal(1, 1.step(by: 2).begin)
+ assert_equal(1, 1.step(by: -1).begin)
+ assert_equal(1, 1.step(by: nil).begin)
+ assert_equal(1, 1.step(10, 2).begin)
+ assert_equal(1, 1.step(10, by: 2).begin)
+ assert_equal(1, 1.step(to: 10, by: 2).begin)
+ assert_equal(10, 10.step(to: 1, by: -1).begin)
+ assert_equal(10, 10.step(to: 1, by: -2).begin)
+ assert_equal(10, 10.step(to: -1, by: -2).begin)
+ assert_equal(10.0, 10.0.step(to: -1.0, by: -2.0).begin)
+ end
+
+ def test_end
+ assert_equal(nil, 1.step.end)
+ assert_equal(10, 1.step(10).end)
+ assert_equal(10, 1.step(to: 10).end)
+ assert_equal(nil, 1.step(nil).end)
+ assert_equal(nil, 1.step(to: nil).end)
+ assert_equal(nil, 1.step(by: 2).end)
+ assert_equal(nil, 1.step(by: -1).end)
+ assert_equal(nil, 1.step(by: nil).end)
+ assert_equal(10, 1.step(10, 2).end)
+ assert_equal(10, 1.step(10, by: 2).end)
+ assert_equal(10, 1.step(to: 10, by: 2).end)
+ assert_equal(1, 10.step(to: 1, by: -1).end)
+ assert_equal(1, 10.step(to: 1, by: -2).end)
+ assert_equal(-1, 10.step(to: -1, by: -2).end)
+ assert_equal(-1.0, 10.0.step(to: -1.0, by: -2.0).end)
+ end
+
+ def test_exclude_end_p
+ assert_equal(false, 1.step.exclude_end?)
+ assert_equal(false, 1.step(10).exclude_end?)
+ assert_equal(false, 1.step(to: 10).exclude_end?)
+ assert_equal(false, 1.step(nil).exclude_end?)
+ assert_equal(false, 1.step(to: nil).exclude_end?)
+ assert_equal(false, 1.step(by: 2).exclude_end?)
+ assert_equal(false, 1.step(by: -1).exclude_end?)
+ assert_equal(false, 1.step(by: nil).exclude_end?)
+ assert_equal(false, 1.step(10, 2).exclude_end?)
+ assert_equal(false, 1.step(10, by: 2).exclude_end?)
+ assert_equal(false, 1.step(to: 10, by: 2).exclude_end?)
+ assert_equal(false, 10.step(to: 1, by: -1).exclude_end?)
+ assert_equal(false, 10.step(to: 1, by: -2).exclude_end?)
+ assert_equal(false, 10.step(to: -1, by: -2).exclude_end?)
+ end
+
+ def test_step
+ assert_equal(1, 1.step.step)
+ assert_equal(1, 1.step(10).step)
+ assert_equal(1, 1.step(to: 10).step)
+ assert_equal(1, 1.step(nil).step)
+ assert_equal(1, 1.step(to: nil).step)
+ assert_equal(2, 1.step(by: 2).step)
+ assert_equal(-1, 1.step(by: -1).step)
+ assert_equal(1, 1.step(by: nil).step)
+ assert_equal(2, 1.step(10, 2).step)
+ assert_equal(2, 1.step(10, by: 2).step)
+ assert_equal(2, 1.step(to: 10, by: 2).step)
+ assert_equal(-1, 10.step(to: 1, by: -1).step)
+ assert_equal(-2, 10.step(to: 1, by: -2).step)
+ assert_equal(-2, 10.step(to: -1, by: -2).step)
+ assert_equal(-2.0, 10.0.step(to: -1.0, by: -2.0).step)
+ end
+
+ def test_eq
+ seq = 1.step
+ assert_equal(seq, seq)
+ assert_equal(seq, 1.step)
+ assert_equal(seq, 1.step(nil))
+ end
+
+ def test_eqq
+ seq = 1.step
+ assert_operator(seq, :===, seq)
+ assert_operator(seq, :===, 1.step)
+ assert_operator(seq, :===, 1.step(nil))
+ end
+
+ def test_eql_p
+ seq = 1.step
+ assert_operator(seq, :eql?, seq)
+ assert_operator(seq, :eql?, 1.step)
+ assert_operator(seq, :eql?, 1.step(nil))
+ end
+
+ def test_hash
+ seq = 1.step
+ assert_equal(seq.hash, seq.hash)
+ assert_equal(seq.hash, 1.step.hash)
+ assert_equal(seq.hash, 1.step(nil).hash)
+ assert_kind_of(String, seq.hash.to_s)
+ end
+
+ def test_first
+ seq = 1.step
+ assert_equal(1, seq.first)
+ assert_equal([1], seq.first(1))
+ assert_equal([1, 2, 3], seq.first(3))
+
+ seq = 1.step(by: 2)
+ assert_equal(1, seq.first)
+ assert_equal([1], seq.first(1))
+ assert_equal([1, 3, 5], seq.first(3))
+
+ seq = 10.step(by: -2)
+ assert_equal(10, seq.first)
+ assert_equal([10], seq.first(1))
+ assert_equal([10, 8, 6], seq.first(3))
+
+ seq = 1.step(by: 4)
+ assert_equal([1, 5, 9], seq.first(3))
+
+ seq = 1.step(10, by: 4)
+ assert_equal([1, 5, 9], seq.first(5))
+
+ seq = 1.step(0)
+ assert_equal(nil, seq.first)
+ assert_equal([], seq.first(1))
+ assert_equal([], seq.first(3))
+
+ seq = 1.step(10, by: -1)
+ assert_equal(nil, seq.first)
+ 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))
+ assert_equal([10.0, 8.0, 6.0], seq.first(3))
+ end
+
+ def test_first_bug15518
+ bug15518 = '[Bug #15518]'
+ seq = (1 .. 10.0).step(1)
+ five_float_classes = Array.new(5) { Float }
+ assert_equal(five_float_classes, seq.first(5).map(&:class), bug15518)
+ assert_equal([1.0, 2.0, 3.0, 4.0, 5.0], seq.first(5), bug15518)
+ seq = (1 .. Float::INFINITY).step(1)
+ assert_equal(five_float_classes, seq.first(5).map(&:class), bug15518)
+ assert_equal([1.0, 2.0, 3.0, 4.0, 5.0], seq.first(5), bug15518)
+ seq = (1 .. Float::INFINITY).step(1r)
+ assert_equal(five_float_classes, seq.first(5).map(&:class), bug15518)
+ assert_equal([1.0, 2.0, 3.0, 4.0, 5.0], seq.first(5), bug15518)
+ end
+
+ def test_last
+ seq = 1.step(10)
+ assert_equal(10, seq.last)
+ assert_equal([10], seq.last(1))
+ assert_equal([8, 9, 10], seq.last(3))
+
+ seq = 1.step(10, 2)
+ assert_equal(9, seq.last)
+ assert_equal([9], seq.last(1))
+ assert_equal([5, 7, 9], seq.last(3))
+
+ seq = 10.step(1, -2)
+ assert_equal(2, seq.last)
+ assert_equal([2], seq.last(1))
+ assert_equal([6, 4, 2], seq.last(3))
+
+ seq = 10.step(-1, -2)
+ assert_equal(0, seq.last)
+
+ seq = 1.step(10, 4)
+ assert_equal([1, 5, 9], seq.last(5))
+
+ seq = 10.step(1)
+ assert_equal(nil, seq.last)
+ assert_equal([], seq.last(1))
+ assert_equal([], seq.last(5))
+
+ seq = 1.step(10, -1)
+ assert_equal(nil, seq.last)
+ assert_equal([], seq.last(1))
+ assert_equal([], seq.last(5))
+
+ seq = (1..10).step
+ assert_equal(10, seq.last)
+ assert_equal([10], seq.last(1))
+ assert_equal([8, 9, 10], seq.last(3))
+
+ seq = (1...10).step
+ assert_equal(9, seq.last)
+ assert_equal([9], seq.last(1))
+ assert_equal([7, 8, 9], seq.last(3))
+
+ seq = 10.0.step(-3.0, by: -2.0)
+ assert_equal(-2.0, seq.last)
+ assert_equal([-2.0], seq.last(1))
+ assert_equal([2.0, 0.0, -2.0], seq.last(3))
+ end
+
+ def test_last_with_float
+ res = (1..3).step(2).last(2.0)
+ assert_equal([1, 3], res)
+ assert_instance_of Integer, res[0]
+ assert_instance_of Integer, res[1]
+
+ res = (1..3).step(2).last(5.0)
+ assert_equal([1, 3], res)
+ assert_instance_of Integer, res[0]
+ assert_instance_of Integer, res[1]
+ end
+
+ def test_last_with_rational
+ res = (1..3).step(2).last(2r)
+ assert_equal([1, 3], res)
+ assert_instance_of Integer, res[0]
+ assert_instance_of Integer, res[1]
+
+ res = (1..3).step(2).last(10/2r)
+ assert_equal([1, 3], res)
+ assert_instance_of Integer, res[0]
+ assert_instance_of Integer, res[1]
+ 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)
+ assert_equal([1, 3, 5, 7, 9], (1..10).step(2).to_a)
+ assert_equal([10, 8, 6, 4, 2], 10.step(1, by: -2).to_a)
+ assert_equal([10, 8, 6, 4, 2], (10..1).step(-2).to_a)
+ assert_equal([10.0, 8.0, 6.0, 4.0, 2.0], (10.0..1.0).step(-2.0).to_a)
+ end
+
+ def test_to_a_bug15444
+ seq = ((1/10r)..(1/2r)).step(1/10r)
+ assert_num_equal_type([1/10r, 1/5r, 3/10r, 2/5r, 1/2r], seq.to_a,
+ '[ruby-core:90648] [Bug #15444]')
+ end
+
+ def test_last_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)
+
+ seq = 10.step(1, -2)
+ assert_equal([[10, 8, 6], [4, 2]], seq.each_slice(3).to_a)
+ end
+
+ def test_cons
+ seq = 1.step(10, 2)
+ assert_equal([[1, 3, 5], [3, 5, 7], [5, 7, 9]], seq.each_cons(3).to_a)
+
+ seq = 10.step(1, -2)
+ assert_equal([[10, 8, 6], [8, 6, 4], [6, 4, 2]], seq.each_cons(3).to_a)
+ end
+
+ def test_with_index
+ seq = 1.step(6, 2)
+ assert_equal([[1, 0], [3, 1], [5, 2]], seq.with_index.to_a)
+ assert_equal([[1, 10], [3, 11], [5, 12]], seq.with_index(10).to_a)
+
+ seq = 10.step(5, -2)
+ assert_equal([[10, 0], [8, 1], [6, 2]], seq.with_index.to_a)
+ assert_equal([[10, 10], [8, 11], [6, 12]], seq.with_index(10).to_a)
+ end
+
+ def test_with_object
+ obj = [0, 1]
+ seq = 1.step(10, 2)
+ ret = seq.each_with_object(obj) do |i, memo|
+ memo[0] += i
+ memo[1] *= i
+ end
+ assert_same(obj, ret)
+ assert_equal([25, 945], ret)
+
+ obj = [0, 1]
+ seq = 10.step(1, -2)
+ ret = seq.each_with_object(obj) do |i, memo|
+ memo[0] += i
+ memo[1] *= i
+ end
+ assert_same(obj, ret)
+ assert_equal([30, 3840], ret)
+ end
+
+ def test_next
+ seq = 1.step(10, 2)
+ [1, 3, 5, 7, 9].each do |i|
+ assert_equal(i, seq.next)
+ end
+
+ seq = 10.step(1, -2)
+ [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
+ seq = ((1/10r)..(1/2r)).step(1/10r)
+ assert_equal(1/10r, seq.next, '[ruby-core:90648] [Bug #15444]')
+ end
+
+ def test_next_rewind
+ seq = 1.step(6, 2)
+ assert_equal(1, seq.next)
+ assert_equal(3, seq.next)
+ seq.rewind
+ assert_equal(1, seq.next)
+ assert_equal(3, seq.next)
+ assert_equal(5, seq.next)
+ assert_raise(StopIteration) { seq.next }
+
+ seq = 10.step(5, -2)
+ assert_equal(10, seq.next)
+ assert_equal(8, seq.next)
+ seq.rewind
+ assert_equal(10, seq.next)
+ assert_equal(8, seq.next)
+ assert_equal(6, seq.next)
+ assert_raise(StopIteration) { seq.next }
+ end
+
+ def test_next_after_stopiteration
+ seq = 1.step(2, 2)
+ assert_equal(1, seq.next)
+ assert_raise(StopIteration) { seq.next }
+ assert_raise(StopIteration) { seq.next }
+ seq.rewind
+ assert_equal(1, seq.next)
+ assert_raise(StopIteration) { seq.next }
+ assert_raise(StopIteration) { seq.next }
+ end
+
+ def test_stop_result
+ seq = 1.step(2, 2)
+ res = seq.each {}
+ assert_equal(1, seq.next)
+ exc = assert_raise(StopIteration) { seq.next }
+ assert_equal(res, exc.result)
+ end
+
+ def test_peek
+ seq = 1.step(2, 2)
+ assert_equal(1, seq.peek)
+ assert_equal(1, seq.peek)
+ assert_equal(1, seq.next)
+ assert_raise(StopIteration) { seq.peek }
+ assert_raise(StopIteration) { seq.peek }
+
+ seq = 10.step(9, -2)
+ assert_equal(10, seq.peek)
+ assert_equal(10, seq.peek)
+ assert_equal(10, seq.next)
+ assert_raise(StopIteration) { seq.peek }
+ assert_raise(StopIteration) { seq.peek }
+ end
+
+ def test_next_values
+ seq = 1.step(2, 2)
+ assert_equal([1], seq.next_values)
+ end
+
+ def test_peek_values
+ seq = 1.step(2, 2)
+ assert_equal([1], seq.peek_values)
+ end
+
+ def test_num_step_inspect
+ assert_equal('(1.step)', 1.step.inspect)
+ assert_equal('(1.step(10))', 1.step(10).inspect)
+ assert_equal('(1.step(10, 2))', 1.step(10, 2).inspect)
+ assert_equal('(1.step(10, by: 2))', 1.step(10, by: 2).inspect)
+ assert_equal('(1.step(by: 2))', 1.step(by: 2).inspect)
+ end
+
+ def test_range_step_inspect
+ assert_equal('((1..).step)', (1..).step.inspect)
+ assert_equal('((1..10).step)', (1..10).step.inspect)
+ assert_equal('((1..10).step(2))', (1..10).step(2).inspect)
+ end
+
+ def test_num_step_size
+ assert_equal(10, 1.step(10).size)
+ assert_equal(5, 1.step(10, 2).size)
+ assert_equal(4, 1.step(10, 3).size)
+ assert_equal(1, 1.step(10, 10).size)
+ assert_equal(0, 1.step(0).size)
+ assert_equal(Float::INFINITY, 1.step.size)
+
+ assert_equal(10, 10.step(1, -1).size)
+ assert_equal(5, 10.step(1, -2).size)
+ assert_equal(4, 10.step(1, -3).size)
+ assert_equal(1, 10.step(1, -10).size)
+ assert_equal(0, 1.step(2, -1).size)
+ assert_equal(Float::INFINITY, 1.step(by: -1).size)
+ end
+
+ def test_range_step_size
+ assert_equal(10, (1..10).step.size)
+ assert_equal(9, (1...10).step.size)
+ assert_equal(5, (1..10).step(2).size)
+ assert_equal(5, (1...10).step(2).size)
+ assert_equal(4, (1...9).step(2).size)
+ assert_equal(Float::INFINITY, (1..).step.size)
+
+ assert_equal(10, (10..1).step(-1).size)
+ assert_equal(9, (10...1).step(-1).size)
+ assert_equal(5, (10..1).step(-2).size)
+ assert_equal(5, (10...1).step(-2).size)
+ assert_equal(4, (10...2).step(-2).size)
+ assert_equal(Float::INFINITY, (1..).step(-1).size)
+ end
+
+ def assert_num_equal_type(ary1, ary2, message=nil)
+ assert_equal(ary1.length, ary2.length, message)
+ ary1.zip(ary2) do |e1, e2|
+ assert_equal(e1.class, e2.class, message)
+ if e1.is_a? Complex
+ assert_equal(e1.real, e2.real, message)
+ assert_equal(e1.imag, e2.imag, message)
+ else
+ assert_equal(e1, e2, message)
+ end
+ end
+ end
+
+ def test_complex
+ assert_num_equal_type([1, 1+1i, 1+2i], (1..).step(1i).take(3))
+ assert_num_equal_type([1, 1+1.0i, 1+2.0i], (1..).step(1.0i).take(3))
+ assert_num_equal_type([0.0, 0.0+1.0i, 0.0+2.0i], (0.0..).step(1.0i).take(3))
+ assert_num_equal_type([0.0+0.0i, 0.0+1.0i, 0.0+2.0i], (0.0i..).step(1.0i).take(3))
+ end
+
+ def test_sum
+ assert_equal([1, 3, 5, 7, 9].sum, (1..10).step(2).sum)
+ assert_equal([1.0, 2.5, 4.0, 5.5, 7.0, 8.5, 10.0].sum, (1.0..10.0).step(1.5).sum)
+ assert_equal([1/2r, 1r, 3/2r, 2, 5/2r, 3, 7/2r, 4].sum, ((1/2r)...(9/2r)).step(1/2r).sum)
+ end
+end
diff --git a/test/ruby/test_arity.rb b/test/ruby/test_arity.rb
new file mode 100644
index 0000000000..b98248f603
--- /dev/null
+++ b/test/ruby/test_arity.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: false
+require 'test/unit'
+
+class TestArity < Test::Unit::TestCase
+ def err_mess(method_proc = nil, argc = 0)
+ args = (1..argc).to_a
+ assert_raise_with_message(ArgumentError, /wrong number of arguments \(.*\b(\d+)\b.* (\d\S*?)\)/) do
+ case method_proc
+ when nil
+ yield
+ when Symbol
+ method(method_proc).call(*args)
+ else
+ method_proc.call(*args)
+ end
+ end
+ [$1, $2]
+ end
+
+ def a
+ end
+
+ def b(a, b, c, d=1, e=2, f, g, h, i, &block)
+ end
+
+ def c(a, b, c, d=1, e=2, *rest)
+ end
+
+ def d(a, b: 42)
+ end
+
+ def e(a, b:42, **c)
+ end
+
+ def f(a, b, c=1, *rest, d: 3)
+ end
+
+ def test_method_err_mess
+ assert_equal %w[1 0], err_mess(:a, 1)
+ assert_equal %w[10 7..9], err_mess(:b, 10)
+ assert_equal %w[2 3+], err_mess(:c, 2)
+ assert_equal %w[2 1], err_mess(:d, 2)
+ assert_equal %w[0 1], err_mess(:d, 0)
+ assert_equal %w[2 1], err_mess(:e, 2)
+ assert_equal %w[0 1], err_mess(:e, 0)
+ assert_equal %w[1 2+], err_mess(:f, 1)
+ end
+
+ def test_proc_err_mess
+ assert_equal %w[0 1..2], err_mess(->(b, c=42){}, 0)
+ assert_equal %w[1 2+], err_mess(->(a, b, c=42, *d){}, 1)
+ assert_equal %w[3 4+], err_mess(->(a, b, *c, d, e){}, 3)
+ assert_equal %w[3 1..2], err_mess(->(b, c=42){}, 3)
+ assert_equal %w[1 0], err_mess(->(&block){}, 1)
+ # Double checking:
+ p = Proc.new{|b, c=42| :ok}
+ assert_equal :ok, p.call(1, 2, 3)
+ assert_equal :ok, p.call
+ end
+
+ def test_message_change_issue_6085
+ assert_equal %w[3 1..2], err_mess{ SignalException.new(1, "", nil) }
+ assert_equal %w[1 0], err_mess{ Hash.new(1){} }
+ assert_equal %w[3 1..2], err_mess{ Module.send :define_method, 1, 2, 3 }
+ assert_equal %w[1 2], err_mess{ "".sub!(//) }
+ assert_equal %w[0 1..2], err_mess{ "".sub!{} }
+ assert_equal %w[0 1+], err_mess{ exec }
+ assert_equal %w[0 1+], err_mess{ Struct.new }
+ end
+end
diff --git a/test/ruby/test_array.rb b/test/ruby/test_array.rb
index e8edcc25e3..8d48b86ea9 100644
--- a/test/ruby/test_array.rb
+++ b/test/ruby/test_array.rb
@@ -1,5 +1,8 @@
+# coding: US-ASCII
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
+require "delegate"
+require "rbconfig/sizeof"
class TestArray < Test::Unit::TestCase
def setup
@@ -12,6 +15,17 @@ class TestArray < Test::Unit::TestCase
$VERBOSE = @verbose
end
+ def test_percent_i
+ assert_equal([:foo, :bar], %i[foo bar])
+ assert_equal([:"\"foo"], %i["foo])
+ end
+
+ def test_percent_I
+ x = 10
+ assert_equal([:foo, :b10], %I[foo b#{x}])
+ assert_equal([:"\"foo10"], %I["foo#{x}])
+ end
+
def test_0_literal
assert_equal([1, 2, 3, 4], [1, 2] + [3, 4])
assert_equal([1, 2, 1, 2], [1, 2] * 2)
@@ -27,15 +41,17 @@ class TestArray < Test::Unit::TestCase
assert_equal(2, x[2])
assert_equal([1, 2, 3], x[1..3])
assert_equal([1, 2, 3], x[1,3])
+ assert_equal([3, 4, 5], x[3..])
x[0, 2] = 10
- assert(x[0] == 10 && x[1] == 2)
+ assert_equal([10, 2, 3, 4, 5], x)
x[0, 0] = -1
- assert(x[0] == -1 && x[1] == 10)
+ assert_equal([-1, 10, 2, 3, 4, 5], x)
x[-1, 1] = 20
- assert(x[-1] == 20 && x.pop == 20)
+ assert_equal(20, x[-1])
+ assert_equal(20, x.pop)
end
def test_array_andor_0
@@ -85,7 +101,7 @@ class TestArray < Test::Unit::TestCase
end
def test_misc_0
- assert(defined? "a".chomp)
+ assert(defined? "a".chomp, '"a".chomp is not defined')
assert_equal(["a", "b", "c"], "abc".scan(/./))
assert_equal([["1a"], ["2b"], ["3c"]], "1a2b3c".scan(/(\d.)/))
# non-greedy match
@@ -130,14 +146,17 @@ class TestArray < Test::Unit::TestCase
assert_equal(1, x.first)
assert_equal([1], x.first(1))
assert_equal([1, 2, 3], x.first(3))
+ assert_raise_with_message(ArgumentError, /0\.\.1/) {x.first(1, 2)}
assert_equal(5, x.last)
assert_equal([5], x.last(1))
assert_equal([3, 4, 5], x.last(3))
+ assert_raise_with_message(ArgumentError, /0\.\.1/) {x.last(1, 2)}
assert_equal(1, x.shift)
assert_equal([2, 3, 4], x.shift(3))
assert_equal([5], x)
+ assert_raise_with_message(ArgumentError, /0\.\.1/) {x.first(1, 2)}
assert_equal([2, 3, 4, 5], x.unshift(2, 3, 4))
assert_equal([1, 2, 3, 4, 5], x.unshift(1))
@@ -146,6 +165,7 @@ class TestArray < Test::Unit::TestCase
assert_equal(5, x.pop)
assert_equal([3, 4], x.pop(2))
assert_equal([1, 2], x)
+ assert_raise_with_message(ArgumentError, /0\.\.1/) {x.pop(1, 2)}
assert_equal([1, 2, 3, 4], x.push(3, 4))
assert_equal([1, 2, 3, 4, 5], x.push(5))
@@ -155,6 +175,7 @@ class TestArray < Test::Unit::TestCase
def test_find_all_0
assert_respond_to([], :find_all)
assert_respond_to([], :select) # Alias
+ assert_respond_to([], :filter) # Alias
assert_equal([], [].find_all{ |obj| obj == "foo"})
x = ["foo", "bar", "baz", "baz", 1, 2, 3, 3, 4]
@@ -183,6 +204,8 @@ class TestArray < Test::Unit::TestCase
assert_equal([0, 1, 2, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(3...4){|i| i+10})
assert_equal([0, 1, 12, 13, 14, 5], [0, 1, 2, 3, 4, 5].fill(2..-2){|i| i+10})
assert_equal([0, 1, 12, 13, 4, 5], [0, 1, 2, 3, 4, 5].fill(2...-2){|i| i+10})
+ assert_equal([0, 1, 2, 13, 14, 15], [0, 1, 2, 3, 4, 5].fill(3..){|i| i+10})
+ assert_equal([0, 1, 2, 13, 14, 15], [0, 1, 2, 3, 4, 5].fill(3...){|i| i+10})
end
# From rubicon
@@ -209,6 +232,13 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[], @cls[ 1, 2, 3 ] & @cls[ 4, 5, 6 ])
end
+ def test_AND_big_array # '&'
+ assert_equal(@cls[1, 3], @cls[ 1, 1, 3, 5 ]*64 & @cls[ 1, 2, 3 ]*64)
+ assert_equal(@cls[], @cls[ 1, 1, 3, 5 ]*64 & @cls[ ])
+ assert_equal(@cls[], @cls[ ] & @cls[ 1, 2, 3 ]*64)
+ assert_equal(@cls[], @cls[ 1, 2, 3 ]*64 & @cls[ 4, 5, 6 ]*64)
+ end
+
def test_MUL # '*'
assert_equal(@cls[], @cls[]*3)
assert_equal(@cls[1, 1, 1], @cls[1]*3)
@@ -232,19 +262,45 @@ class TestArray < Test::Unit::TestCase
def test_MINUS # '-'
assert_equal(@cls[], @cls[1] - @cls[1])
assert_equal(@cls[1], @cls[1, 2, 3, 4, 5] - @cls[2, 3, 4, 5])
- # Ruby 1.8 feature change
- #assert_equal(@cls[1], @cls[1, 2, 1, 3, 1, 4, 1, 5] - @cls[2, 3, 4, 5])
assert_equal(@cls[1, 1, 1, 1], @cls[1, 2, 1, 3, 1, 4, 1, 5] - @cls[2, 3, 4, 5])
a = @cls[]
1000.times { a << 1 }
assert_equal(1000, a.length)
- #assert_equal(@cls[1], a - @cls[2])
assert_equal(@cls[1] * 1000, a - @cls[2])
- #assert_equal(@cls[1], @cls[1, 2, 1] - @cls[2])
assert_equal(@cls[1, 1], @cls[1, 2, 1] - @cls[2])
assert_equal(@cls[1, 2, 3], @cls[1, 2, 3] - @cls[4, 5, 6])
end
+ def test_MINUS_big_array # '-'
+ assert_equal(@cls[1]*64, @cls[1, 2, 3, 4, 5]*64 - @cls[2, 3, 4, 5]*64)
+ assert_equal(@cls[1, 1, 1, 1]*64, @cls[1, 2, 1, 3, 1, 4, 1, 5]*64 - @cls[2, 3, 4, 5]*64)
+ a = @cls[]
+ 1000.times { a << 1 }
+ assert_equal(1000, a.length)
+ assert_equal(@cls[1] * 1000, a - @cls[2])
+ end
+
+ def test_difference
+ assert_equal(@cls[], @cls[1].difference(@cls[1]))
+ assert_equal(@cls[1], @cls[1, 2, 3, 4, 5].difference(@cls[2, 3, 4, 5]))
+ assert_equal(@cls[1, 1], @cls[1, 2, 1].difference(@cls[2]))
+ assert_equal(@cls[1, 1, 1, 1], @cls[1, 2, 1, 3, 1, 4, 1, 5].difference(@cls[2, 3, 4, 5]))
+ assert_equal(@cls[], @cls[1, 2, 3, 4].difference(@cls[1], @cls[2], @cls[3], @cls[4]))
+ a = [1]
+ assert_equal(@cls[1], a.difference(@cls[2], @cls[2]))
+ assert_equal(@cls[], a.difference(@cls[1]))
+ assert_equal(@cls[1], a)
+ end
+
+ def test_difference_big_array
+ assert_equal(@cls[1]*64, (@cls[1, 2, 3, 4, 5] * 64).difference(@cls[2, 3, 4] * 64, @cls[3, 5] * 64))
+ assert_equal(@cls[1, 1, 1, 1]*64, (@cls[1, 2, 1, 3, 1, 4, 1, 5] * 64).difference(@cls[2, 3, 4, 5] * 64))
+ a = @cls[1] * 1000
+ assert_equal(@cls[1] * 1000, a.difference(@cls[2], @cls[2]))
+ assert_equal(@cls[], a.difference(@cls[1]))
+ assert_equal(@cls[1] * 1000, a)
+ end
+
def test_LSHIFT # '<<'
a = @cls[]
a << 1
@@ -270,17 +326,17 @@ class TestArray < Test::Unit::TestCase
end
def test_EQUAL # '=='
- assert(@cls[] == @cls[])
- assert(@cls[1] == @cls[1])
- assert(@cls[1, 1, 2, 2] == @cls[1, 1, 2, 2])
- assert(@cls[1.0, 1.0, 2.0, 2.0] == @cls[1, 1, 2, 2])
+ assert_operator(@cls[], :==, @cls[])
+ assert_operator(@cls[1], :==, @cls[1])
+ assert_operator(@cls[1, 1, 2, 2], :==, @cls[1, 1, 2, 2])
+ assert_operator(@cls[1.0, 1.0, 2.0, 2.0], :==, @cls[1, 1, 2, 2])
end
def test_VERY_EQUAL # '==='
- assert(@cls[] === @cls[])
- assert(@cls[1] === @cls[1])
- assert(@cls[1, 1, 2, 2] === @cls[1, 1, 2, 2])
- assert(@cls[1.0, 1.0, 2.0, 2.0] === @cls[1, 1, 2, 2])
+ assert_operator(@cls[], :===, @cls[])
+ assert_operator(@cls[1], :===, @cls[1])
+ assert_operator(@cls[1, 1, 2, 2], :===, @cls[1, 1, 2, 2])
+ assert_operator(@cls[1.0, 1.0, 2.0, 2.0], :===, @cls[1, 1, 2, 2])
end
def test_AREF # '[]'
@@ -318,12 +374,11 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[99], a[-2..-2])
assert_equal(@cls[10, 11, 12], a[9..11])
+ assert_equal(@cls[98, 99, 100], a[97..])
assert_equal(@cls[10, 11, 12], a[-91..-89])
+ assert_equal(@cls[98, 99, 100], a[-3..])
assert_nil(a[10, -3])
- # Ruby 1.8 feature change:
- # Array#[size..x] returns [] instead of nil.
- #assert_nil(a[10..7])
assert_equal [], a[10..7]
assert_raise(TypeError) {a['cat']}
@@ -379,33 +434,33 @@ class TestArray < Test::Unit::TestCase
assert_equal(b, a[10..19] = b)
assert_equal(@cls[*(0..9).to_a] + b + @cls[*(20..99).to_a], a)
- # Ruby 1.8 feature change:
- # assigning nil does not remove elements.
-=begin
a = @cls[*(0..99).to_a]
assert_equal(nil, a[0,1] = nil)
- assert_equal(@cls[*(1..99).to_a], a)
+ assert_equal(@cls[nil] + @cls[*(1..99).to_a], a)
a = @cls[*(0..99).to_a]
assert_equal(nil, a[10,10] = nil)
- assert_equal(@cls[*(0..9).to_a] + @cls[*(20..99).to_a], a)
+ assert_equal(@cls[*(0..9).to_a] + @cls[nil] + @cls[*(20..99).to_a], a)
a = @cls[*(0..99).to_a]
assert_equal(nil, a[-1, 1] = nil)
- assert_equal(@cls[*(0..98).to_a], a)
+ assert_equal(@cls[*(0..98).to_a] + @cls[nil], a)
a = @cls[*(0..99).to_a]
assert_equal(nil, a[-10, 10] = nil)
- assert_equal(@cls[*(0..89).to_a], a)
+ assert_equal(@cls[*(0..89).to_a] + @cls[nil], a)
a = @cls[*(0..99).to_a]
assert_equal(nil, a[0,1000] = nil)
- assert_equal(@cls[] , a)
+ assert_equal(@cls[nil] , a)
a = @cls[*(0..99).to_a]
assert_equal(nil, a[10..19] = nil)
- assert_equal(@cls[*(0..9).to_a] + @cls[*(20..99).to_a], a)
-=end
+ assert_equal(@cls[*(0..9).to_a] + @cls[nil] + @cls[*(20..99).to_a], a)
+
+ a = @cls[*(0..99).to_a]
+ assert_equal(nil, a[10..] = nil)
+ assert_equal(@cls[*(0..9).to_a] + @cls[nil], a)
a = @cls[1, 2, 3]
a[1, 0] = a
@@ -414,6 +469,29 @@ class TestArray < Test::Unit::TestCase
a = @cls[1, 2, 3]
a[-1, 0] = a
assert_equal([1, 2, 1, 2, 3, 3], a)
+
+ a = @cls[]
+ a[5,0] = [5]
+ assert_equal([nil, nil, nil, nil, nil, 5], a)
+
+ a = @cls[1]
+ a[1,0] = [2]
+ assert_equal([1, 2], a)
+
+ a = @cls[1]
+ a[1,1] = [2]
+ assert_equal([1, 2], a)
+ end
+
+ def test_append
+ a = @cls[1, 2, 3]
+ assert_equal(@cls[1, 2, 3, 4, 5], a.append(4, 5))
+ assert_equal(@cls[1, 2, 3, 4, 5, nil], a.append(nil))
+
+ a.append
+ assert_equal @cls[1, 2, 3, 4, 5, nil], a
+ a.append 6, 7
+ assert_equal @cls[1, 2, 3, 4, 5, nil, 6, 7], a
end
def test_assoc
@@ -452,42 +530,39 @@ class TestArray < Test::Unit::TestCase
def test_clone
for taint in [ false, true ]
- for untrust in [ false, true ]
- for frozen in [ false, true ]
- a = @cls[*(0..99).to_a]
- a.taint if taint
- a.untrust if untrust
- a.freeze if frozen
- b = a.clone
-
- assert_equal(a, b)
- assert(a.__id__ != b.__id__)
- assert_equal(a.frozen?, b.frozen?)
- assert_equal(a.untrusted?, b.untrusted?)
- assert_equal(a.tainted?, b.tainted?)
- end
+ for frozen in [ false, true ]
+ a = @cls[*(0..99).to_a]
+ a.taint if taint
+ a.freeze if frozen
+ b = a.clone
+
+ assert_equal(a, b)
+ assert_not_equal(a.__id__, b.__id__)
+ assert_equal(a.frozen?, b.frozen?)
+ assert_equal(a.tainted?, b.tainted?)
end
end
end
def test_collect
a = @cls[ 1, 'cat', 1..1 ]
- assert_equal([ Fixnum, String, Range], a.collect {|e| e.class} )
+ assert_equal([ Integer, String, Range], a.collect {|e| e.class} )
assert_equal([ 99, 99, 99], a.collect { 99 } )
assert_equal([], @cls[].collect { 99 })
- # Ruby 1.9 feature change:
- # Enumerable#collect without block returns an Enumerator.
- #assert_equal([1, 2, 3], @cls[1, 2, 3].collect)
assert_kind_of Enumerator, @cls[1, 2, 3].collect
+
+ assert_raise(ArgumentError) {
+ assert_equal([[1, 2, 3]], [[1, 2, 3]].collect(&->(a, b, c) {[a, b, c]}))
+ }
end
# also update map!
def test_collect!
a = @cls[ 1, 'cat', 1..1 ]
- assert_equal([ Fixnum, String, Range], a.collect! {|e| e.class} )
- assert_equal([ Fixnum, String, Range], a)
+ assert_equal([ Integer, String, Range], a.collect! {|e| e.class} )
+ assert_equal([ Integer, String, Range], a)
a = @cls[ 1, 'cat', 1..1 ]
assert_equal([ 99, 99, 99], a.collect! { 99 } )
@@ -533,7 +608,9 @@ class TestArray < Test::Unit::TestCase
def test_concat
assert_equal(@cls[1, 2, 3, 4], @cls[1, 2].concat(@cls[3, 4]))
assert_equal(@cls[1, 2, 3, 4], @cls[].concat(@cls[1, 2, 3, 4]))
+ assert_equal(@cls[1, 2, 3, 4], @cls[1].concat(@cls[2, 3], [4]))
assert_equal(@cls[1, 2, 3, 4], @cls[1, 2, 3, 4].concat(@cls[]))
+ assert_equal(@cls[1, 2, 3, 4], @cls[1, 2, 3, 4].concat())
assert_equal(@cls[], @cls[].concat(@cls[]))
assert_equal(@cls[@cls[1, 2], @cls[3, 4]], @cls[@cls[1, 2]].concat(@cls[@cls[3, 4]]))
@@ -541,8 +618,12 @@ class TestArray < Test::Unit::TestCase
a.concat(a)
assert_equal([1, 2, 3, 1, 2, 3], a)
+ b = @cls[4, 5]
+ b.concat(b, b)
+ assert_equal([4, 5, 4, 5, 4, 5], b)
+
assert_raise(TypeError) { [0].concat(:foo) }
- assert_raise(RuntimeError) { [0].freeze.concat(:foo) }
+ assert_raise(FrozenError) { [0].freeze.concat(:foo) }
end
def test_count
@@ -552,6 +633,29 @@ class TestArray < Test::Unit::TestCase
assert_equal(3, a.count {|x| x % 2 == 1 })
assert_equal(2, a.count(1) {|x| x % 2 == 1 })
assert_raise(ArgumentError) { a.count(0, 1) }
+
+ bug8654 = '[ruby-core:56072]'
+ assert_in_out_err [], <<-EOS, ["0"], [], bug8654
+ a1 = []
+ a2 = Array.new(100) { |i| i }
+ a2.count do |i|
+ p i
+ a2.replace(a1) if i == 0
+ end
+ EOS
+
+ assert_in_out_err [], <<-EOS, ["[]", "0"], [], bug8654
+ ARY = Array.new(100) { |i| i }
+ class Integer
+ alias old_equal ==
+ def == other
+ ARY.replace([]) if self.equal?(0)
+ p ARY
+ self.equal?(other)
+ end
+ end
+ p ARY.count(42)
+ EOS
end
def test_delete
@@ -574,6 +678,14 @@ class TestArray < Test::Unit::TestCase
a = @cls[*('cab'..'cat').to_a]
assert_equal(99, a.delete('cup') { 99 } )
assert_equal(@cls[*('cab'..'cat').to_a], a)
+
+ o = Object.new
+ def o.==(other); true; end
+ o2 = Object.new
+ def o2.==(other); true; end
+ a = @cls[1, o, o2, 2]
+ assert_equal(o2, a.delete(42))
+ assert_equal([1, 2], a)
end
def test_delete_at
@@ -607,6 +719,11 @@ class TestArray < Test::Unit::TestCase
a = @cls[ 1, 2, 3, 4, 5 ]
assert_equal(a, a.delete_if { |i| i > 3 })
assert_equal(@cls[1, 2, 3], a)
+
+ bug2545 = '[ruby-core:27366]'
+ 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)
end
def test_dup
@@ -618,7 +735,7 @@ class TestArray < Test::Unit::TestCase
b = a.dup
assert_equal(a, b)
- assert(a.__id__ != b.__id__)
+ assert_not_equal(a.__id__, b.__id__)
assert_equal(false, b.frozen?)
assert_equal(a.tainted?, b.tainted?)
end
@@ -637,7 +754,7 @@ class TestArray < Test::Unit::TestCase
a = @cls[]
i = 0
a.each { |e|
- assert_equal(a[i], e)
+ assert(false, "Never get here")
i += 1
}
assert_equal(0, i)
@@ -657,7 +774,7 @@ class TestArray < Test::Unit::TestCase
a = @cls[]
i = 0
a.each_index { |ind|
- assert_equal(i, ind)
+ assert(false, "Never get here")
i += 1
}
assert_equal(0, i)
@@ -666,15 +783,15 @@ class TestArray < Test::Unit::TestCase
end
def test_empty?
- assert(@cls[].empty?)
- assert(!@cls[1].empty?)
+ assert_empty(@cls[])
+ assert_not_empty(@cls[1])
end
def test_eql?
- assert(@cls[].eql?(@cls[]))
- assert(@cls[1].eql?(@cls[1]))
- assert(@cls[1, 1, 2, 2].eql?(@cls[1, 1, 2, 2]))
- assert(!@cls[1.0, 1.0, 2.0, 2.0].eql?(@cls[1, 1, 2, 2]))
+ assert_send([@cls[], :eql?, @cls[]])
+ assert_send([@cls[1], :eql?, @cls[1]])
+ assert_send([@cls[1, 1, 2, 2], :eql?, @cls[1, 1, 2, 2]])
+ assert_not_send([@cls[1.0, 1.0, 2.0, 2.0], :eql?, @cls[1, 1, 2, 2]])
end
def test_fill
@@ -711,25 +828,52 @@ class TestArray < Test::Unit::TestCase
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[],
@cls[@cls[@cls[@cls[],@cls[]],@cls[@cls[]],@cls[]],@cls[@cls[@cls[]]]].flatten)
+ end
+ def test_flatten_wrong_argument
assert_raise(TypeError, "[ruby-dev:31197]") { [[]].flatten("") }
+ end
+ def test_flatten_taint
a6 = @cls[[1, 2], 3]
a6.taint
- a6.untrust
a7 = a6.flatten
assert_equal(true, a7.tainted?)
- assert_equal(true, a7.untrusted?)
+ end
+ def test_flatten_level0
a8 = @cls[[1, 2], 3]
a9 = a8.flatten(0)
assert_equal(a8, a9)
assert_not_same(a8, a9)
end
+ def test_flatten_splat
+ bug10748 = '[ruby-core:67637] [Bug #10748]'
+ o = Object.new
+ o.singleton_class.class_eval do
+ define_method(:to_ary) do
+ raise bug10748
+ end
+ end
+ a = @cls[@cls[o]]
+ assert_raise_with_message(RuntimeError, bug10748) {a.flatten}
+ assert_nothing_raised(RuntimeError, bug10748) {a.flatten(1)}
+ end
+
+ def test_flattern_singleton_class
+ bug12738 = '[ruby-dev:49781] [Bug #12738]'
+ a = [[0]]
+ class << a
+ def m; end
+ end
+ assert_raise(NoMethodError, bug12738) { a.flatten.m }
+ end
+
def test_flatten!
a1 = @cls[ 1, 2, 3]
a2 = @cls[ 5, 6 ]
@@ -742,16 +886,42 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[1, 2, 3, 4, 5, 6], a5.flatten!)
assert_nil(a5.flatten!(0), '[ruby-core:23382]')
assert_equal(@cls[1, 2, 3, 4, 5, 6], a5)
+ end
- assert_equal(@cls[], @cls[].flatten)
+ def test_flatten_empty!
+ assert_nil(@cls[].flatten!)
assert_equal(@cls[],
- @cls[@cls[@cls[@cls[],@cls[]],@cls[@cls[]],@cls[]],@cls[@cls[@cls[]]]].flatten)
+ @cls[@cls[@cls[@cls[],@cls[]],@cls[@cls[]],@cls[]],@cls[@cls[@cls[]]]].flatten!)
+ end
+ def test_flatten_level0!
assert_nil(@cls[].flatten!(0), '[ruby-core:23382]')
end
+ def test_flatten_splat!
+ bug10748 = '[ruby-core:67637] [Bug #10748]'
+ o = Object.new
+ o.singleton_class.class_eval do
+ define_method(:to_ary) do
+ raise bug10748
+ end
+ end
+ a = @cls[@cls[o]]
+ assert_raise_with_message(RuntimeError, bug10748) {a.flatten!}
+ assert_nothing_raised(RuntimeError, bug10748) {a.flatten!(1)}
+ end
+
+ def test_flattern_singleton_class!
+ bug12738 = '[ruby-dev:49781] [Bug #12738]'
+ a = [[0]]
+ class << a
+ def m; end
+ end
+ assert_nothing_raised(NameError, bug12738) { a.flatten!.m }
+ end
+
def test_flatten_with_callcc
- respond_to?(:callcc, true) or require 'continuation'
+ need_continuation
o = Object.new
def o.to_ary() callcc {|k| @cont = k; [1,2,3]} end
begin
@@ -764,8 +934,30 @@ class TestArray < Test::Unit::TestCase
assert_match(/reentered/, e.message, '[ruby-dev:34798]')
end
+ def test_flatten_respond_to_missing
+ bug11465 = '[ruby-core:70460] [Bug #11465]'
+
+ obj = Class.new do
+ def respond_to_missing?(method, stuff)
+ return false if method == :to_ary
+ super
+ end
+
+ def method_missing(*args)
+ super
+ end
+ end.new
+
+ ex = nil
+ trace = TracePoint.new(:raise) do |tp|
+ ex = tp.raised_exception
+ end
+ trace.enable {[obj].flatten}
+ assert_nil(ex, bug11465)
+ end
+
def test_permutation_with_callcc
- respond_to?(:callcc, true) or require 'continuation'
+ need_continuation
n = 1000
cont = nil
ary = [1,2,3]
@@ -782,7 +974,7 @@ class TestArray < Test::Unit::TestCase
end
def test_product_with_callcc
- respond_to?(:callcc, true) or require 'continuation'
+ need_continuation
n = 1000
cont = nil
ary = [1,2,3]
@@ -799,7 +991,7 @@ class TestArray < Test::Unit::TestCase
end
def test_combination_with_callcc
- respond_to?(:callcc, true) or require 'continuation'
+ need_continuation
n = 1000
cont = nil
ary = [1,2,3]
@@ -816,7 +1008,7 @@ class TestArray < Test::Unit::TestCase
end
def test_repeated_permutation_with_callcc
- respond_to?(:callcc, true) or require 'continuation'
+ need_continuation
n = 1000
cont = nil
ary = [1,2,3]
@@ -833,7 +1025,7 @@ class TestArray < Test::Unit::TestCase
end
def test_repeated_combination_with_callcc
- respond_to?(:callcc, true) or require 'continuation'
+ need_continuation
n = 1000
cont = nil
ary = [1,2,3]
@@ -853,18 +1045,20 @@ class TestArray < Test::Unit::TestCase
a1 = @cls[ 'cat', 'dog' ]
a2 = @cls[ 'cat', 'dog' ]
a3 = @cls[ 'dog', 'cat' ]
- assert(a1.hash == a2.hash)
- assert(a1.hash != a3.hash)
+ assert_equal(a1.hash, a2.hash)
+ assert_not_equal(a1.hash, a3.hash)
+ bug9231 = '[ruby-core:58993] [Bug #9231]'
+ assert_not_equal(false.hash, @cls[].hash, bug9231)
end
def test_include?
a = @cls[ 'cat', 99, /a/, @cls[ 1, 2, 3] ]
- assert(a.include?('cat'))
- assert(a.include?(99))
- assert(a.include?(/a/))
- assert(a.include?([1,2,3]))
- assert(!a.include?('ca'))
- assert(!a.include?([1,2]))
+ assert_include(a, 'cat')
+ assert_include(a, 99)
+ assert_include(a, /a/)
+ assert_include(a, [1,2,3])
+ assert_not_include(a, 'ca')
+ assert_not_include(a, [1,2])
end
def test_index
@@ -890,29 +1084,52 @@ class TestArray < Test::Unit::TestCase
a = @cls[]
assert_equal("", a.join)
assert_equal("", a.join(','))
+ assert_equal(Encoding::US_ASCII, a.join.encoding)
$, = ""
a = @cls[1, 2]
assert_equal("12", a.join)
+ assert_equal("12", a.join(nil))
assert_equal("1,2", a.join(','))
$, = ""
a = @cls[1, 2, 3]
assert_equal("123", a.join)
+ assert_equal("123", a.join(nil))
assert_equal("1,2,3", a.join(','))
$, = ":"
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", a.join(','))
$, = ""
a = @cls[1, 2, 3]
a.taint
- a.untrust
s = a.join
assert_equal(true, s.tainted?)
- assert_equal(true, s.untrusted?)
+
+ bug5902 = '[ruby-core:42161]'
+ sep = ":".taint
+
+ s = @cls[].join(sep)
+ assert_equal(false, s.tainted?, bug5902)
+ s = @cls[1].join(sep)
+ assert_equal(false, s.tainted?, bug5902)
+ s = @cls[1, 2].join(sep)
+ assert_equal(true, s.tainted?, bug5902)
+
+ 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)
+ bug5379 = '[ruby-core:39776]'
+ assert_equal(Encoding::US_ASCII, [[], u, nil].join.encoding, bug5379)
+ assert_equal(Encoding::UTF_8, [[], "\u3042", nil].join.encoding, bug5379)
ensure
$, = nil
end
@@ -934,8 +1151,8 @@ class TestArray < Test::Unit::TestCase
# also update collect!
def test_map!
a = @cls[ 1, 'cat', 1..1 ]
- assert_equal(@cls[ Fixnum, String, Range], a.map! {|e| e.class} )
- assert_equal(@cls[ Fixnum, String, Range], a)
+ assert_equal(@cls[ Integer, String, Range], a.map! {|e| e.class} )
+ assert_equal(@cls[ Integer, String, Range], a)
a = @cls[ 1, 'cat', 1..1 ]
assert_equal(@cls[ 99, 99, 99], a.map! { 99 } )
@@ -1040,13 +1257,18 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[], a)
end
+ def test_prepend
+ a = @cls[]
+ assert_equal(@cls['cat'], a.prepend('cat'))
+ assert_equal(@cls['dog', 'cat'], a.prepend('dog'))
+ assert_equal(@cls[nil, 'dog', 'cat'], a.prepend(nil))
+ assert_equal(@cls[@cls[1,2], nil, 'dog', 'cat'], a.prepend(@cls[1, 2]))
+ end
+
def test_push
a = @cls[1, 2, 3]
assert_equal(@cls[1, 2, 3, 4, 5], a.push(4, 5))
assert_equal(@cls[1, 2, 3, 4, 5, nil], a.push(nil))
- # Ruby 1.8 feature:
- # Array#push accepts any number of arguments.
- #assert_raise(ArgumentError, "a.push()") { a.push() }
a.push
assert_equal @cls[1, 2, 3, 4, 5, nil], a
a.push 6, 7
@@ -1079,6 +1301,70 @@ class TestArray < Test::Unit::TestCase
a = @cls[ 1, 2, 3, 4, 5 ]
assert_equal(a, a.reject! { |i| i > 3 })
assert_equal(@cls[1, 2, 3], a)
+
+ bug2545 = '[ruby-core:27366]'
+ 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)
+ end
+
+ def test_shared_array_reject!
+ c = []
+ b = [1, 2, 3, 4]
+ 3.times do
+ a = b.dup
+ c << a.dup
+
+ begin
+ a.reject! do |x|
+ case x
+ when 2 then true
+ when 3 then raise StandardError, 'Oops'
+ else false
+ end
+ end
+ rescue StandardError
+ end
+
+ c << a.dup
+ end
+
+ bug90781 = '[ruby-core:90781]'
+ assert_equal [[1, 2, 3, 4],
+ [1, 3, 4],
+ [1, 2, 3, 4],
+ [1, 3, 4],
+ [1, 2, 3, 4],
+ [1, 3, 4]], c, bug90781
+ end
+
+ def test_iseq_shared_array_reject!
+ c = []
+ 3.times do
+ a = [1, 2, 3, 4]
+ c << a.dup
+
+ begin
+ a.reject! do |x|
+ case x
+ when 2 then true
+ when 3 then raise StandardError, 'Oops'
+ else false
+ end
+ end
+ rescue StandardError
+ end
+
+ c << a.dup
+ end
+
+ bug90781 = '[ruby-core:90781]'
+ assert_equal [[1, 2, 3, 4],
+ [1, 3, 4],
+ [1, 2, 3, 4],
+ [1, 3, 4],
+ [1, 2, 3, 4],
+ [1, 3, 4]], c, bug90781
end
def test_replace
@@ -1091,10 +1377,10 @@ class TestArray < Test::Unit::TestCase
fa = a.dup.freeze
assert_nothing_raised(RuntimeError) { a.replace(a) }
- assert_raise(RuntimeError) { fa.replace(fa) }
+ assert_raise(FrozenError) { fa.replace(fa) }
assert_raise(ArgumentError) { fa.replace() }
assert_raise(TypeError) { a.replace(42) }
- assert_raise(RuntimeError) { fa.replace(42) }
+ assert_raise(FrozenError) { fa.replace(42) }
end
def test_reverse
@@ -1108,9 +1394,6 @@ class TestArray < Test::Unit::TestCase
a = @cls[*%w( dog cat bee ant )]
assert_equal(@cls[*%w(ant bee cat dog)], a.reverse!)
assert_equal(@cls[*%w(ant bee cat dog)], a)
- # Ruby 1.8 feature change:
- # Array#reverse always returns self.
- #assert_nil(@cls[].reverse!)
assert_equal @cls[], @cls[].reverse!
end
@@ -1126,6 +1409,7 @@ class TestArray < Test::Unit::TestCase
a = @cls[]
i = 0
a.reverse_each { |e|
+ i += 1
assert(false, "Never get here")
}
assert_equal(0, i)
@@ -1189,14 +1473,14 @@ class TestArray < Test::Unit::TestCase
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_nil(a.slice(-101..-1))
+ assert_nil(a.slice(-101..))
assert_nil(a.slice(10, -3))
- # Ruby 1.8 feature change:
- # Array#slice[size..x] always returns [].
- #assert_nil(a.slice(10..7))
assert_equal @cls[], a.slice(10..7)
end
@@ -1237,6 +1521,8 @@ class TestArray < Test::Unit::TestCase
assert_equal(nil, a.slice!(-6,2))
assert_equal(@cls[1, 2, 3, 4, 5], a)
+ assert_equal("[2, 3]", [1,2,3].slice!(1,10000).inspect, "moved from btest/knownbug")
+
assert_raise(ArgumentError) { @cls[1].slice! }
assert_raise(ArgumentError) { @cls[1].slice!(0, 0, 0) }
end
@@ -1281,7 +1567,7 @@ class TestArray < Test::Unit::TestCase
end
def test_sort_with_callcc
- respond_to?(:callcc, true) or require 'continuation'
+ need_continuation
n = 1000
cont = nil
ary = (1..100).to_a
@@ -1309,6 +1595,32 @@ class TestArray < Test::Unit::TestCase
end
end
+ def test_sort_bang_with_freeze
+ ary = []
+ o1 = Object.new
+ o1.singleton_class.class_eval {
+ define_method(:<=>) {|v|
+ ary.freeze
+ 1
+ }
+ }
+ o2 = o1.clone
+ ary << o1 << o2
+ orig = ary.dup
+ assert_raise(FrozenError, "frozen during comparison") {ary.sort!}
+ assert_equal(orig, ary, "must not be modified once frozen")
+ end
+
+ def test_short_heap_array_sort_bang_memory_leak
+ bug11332 = '[ruby-dev:49166] [Bug #11332]'
+ assert_no_memory_leak([], <<-PREP, <<-TEST, bug11332, limit: 1.3, timeout: 60)
+ def t; ary = [*1..5]; ary.pop(2); ary.sort!; end
+ 1.times {t}
+ PREP
+ 500000.times {t}
+ TEST
+ end
+
def test_to_a
a = @cls[ 1, 2, 3 ]
a_id = a.__id__
@@ -1336,7 +1648,7 @@ class TestArray < Test::Unit::TestCase
def o.to_ary
foo_bar()
end
- assert_match(/foo_bar/, assert_raise(NoMethodError) {a.concat(o)}.message)
+ assert_raise_with_message(NoMethodError, /foo_bar/) {a.concat(o)}
end
def test_to_s
@@ -1359,6 +1671,95 @@ class TestArray < Test::Unit::TestCase
$, = nil
end
+ StubToH = [
+ [:key, :value],
+ Object.new.tap do |kvp|
+ def kvp.to_ary
+ [:obtained, :via_to_ary]
+ end
+ end,
+ ]
+
+ def test_to_h
+ array = StubToH
+ assert_equal({key: :value, obtained: :via_to_ary}, array.to_h)
+
+ e = assert_raise(TypeError) {
+ [[:first_one, :ok], :not_ok].to_h
+ }
+ assert_equal "wrong element type Symbol at 1 (expected array)", e.message
+ array = [eval("class C\u{1f5ff}; self; end").new]
+ assert_raise_with_message(TypeError, /C\u{1f5ff}/) {array.to_h}
+ e = assert_raise(ArgumentError) {
+ [[:first_one, :ok], [1, 2], [:not_ok]].to_h
+ }
+ assert_equal "wrong array length at 2 (expected 2, was 1)", e.message
+ end
+
+ def test_to_h_block
+ array = StubToH
+ assert_equal({"key" => "value", "obtained" => "via_to_ary"},
+ array.to_h {|k, v| [k.to_s, v.to_s]})
+
+ assert_equal({first_one: :ok, not_ok: :ng},
+ [[:first_one, :ok], :not_ok].to_h {|k, v| [k, v || :ng]})
+
+ e = assert_raise(TypeError) {
+ [[:first_one, :ok], :not_ok].to_h {|k, v| v ? [k, v] : k}
+ }
+ assert_equal "wrong element type Symbol at 1 (expected array)", e.message
+ array = [1]
+ k = eval("class C\u{1f5ff}; self; end").new
+ assert_raise_with_message(TypeError, /C\u{1f5ff}/) {array.to_h {k}}
+ e = assert_raise(ArgumentError) {
+ [[:first_one, :ok], [1, 2], [:not_ok]].to_h {|kv| kv}
+ }
+ assert_equal "wrong array length at 2 (expected 2, was 1)", e.message
+ end
+
+ def test_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))
+ ary = %w(albatross dog horse)
+ assert_equal("albatross", ary.min)
+ assert_equal("dog", ary.min {|a,b| a.length <=> b.length })
+ assert_equal(1, [3,2,1].min)
+ assert_equal(%w[albatross dog], ary.min(2))
+ assert_equal(%w[dog horse],
+ ary.min(2) {|a,b| a.length <=> b.length })
+ assert_equal([13, 14], [20, 32, 32, 21, 30, 25, 29, 13, 14].min(2))
+ assert_equal([2, 4, 6, 7], [2, 4, 8, 6, 7].min(4))
+
+ class << (obj = Object.new)
+ def <=>(x) 1 <=> x end
+ def coerce(x) [x, 1] end
+ end
+ assert_same(obj, [obj, 1.0].min)
+ end
+
+ def test_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))
+ ary = %w(albatross dog horse)
+ assert_equal("horse", ary.max)
+ assert_equal("albatross", ary.max {|a,b| a.length <=> b.length })
+ assert_equal(1, [3,2,1].max{|a,b| b <=> a })
+ assert_equal(%w[horse dog], ary.max(2))
+ assert_equal(%w[albatross horse],
+ ary.max(2) {|a,b| a.length <=> b.length })
+ assert_equal([3, 2], [0, 0, 0, 0, 0, 0, 1, 3, 2].max(2))
+
+ class << (obj = Object.new)
+ def <=>(x) 1 <=> x end
+ def coerce(x) [x, 1] end
+ end
+ assert_same(obj, [obj, 1.0].max)
+ end
+
def test_uniq
a = []
b = a.uniq
@@ -1395,6 +1796,18 @@ class TestArray < Test::Unit::TestCase
assert_equal(d, c)
assert_equal(@cls[1, 2, 3], @cls[1, 2, 3].uniq)
+
+ a = %w(a a)
+ b = a.uniq
+ assert_equal(%w(a a), a)
+ assert(a.none?(&:frozen?))
+ assert_equal(%w(a), b)
+ assert(b.none?(&:frozen?))
+
+ bug9340 = "[ruby-core:59457]"
+ ary = [bug9340, bug9340.dup, bug9340.dup]
+ assert_equal 1, ary.uniq.size
+ assert_same bug9340, ary.uniq[0]
end
def test_uniq_with_block
@@ -1415,6 +1828,13 @@ class TestArray < Test::Unit::TestCase
assert_equal([1,3], a)
assert_equal([1], b)
assert_not_same(a, b)
+
+ a = %w(a a)
+ b = a.uniq {|v| v }
+ assert_equal(%w(a a), a)
+ assert(a.none?(&:frozen?))
+ assert_equal(%w(a), b)
+ assert(b.none?(&:frozen?))
end
def test_uniq!
@@ -1454,7 +1874,20 @@ class TestArray < Test::Unit::TestCase
f = a.dup.freeze
assert_raise(ArgumentError) { a.uniq!(1) }
assert_raise(ArgumentError) { f.uniq!(1) }
- assert_raise(RuntimeError) { f.uniq! }
+ assert_raise(FrozenError) { f.uniq! }
+
+ assert_nothing_raised do
+ a = [ {c: "b"}, {c: "r"}, {c: "w"}, {c: "g"}, {c: "g"} ]
+ a.sort_by!{|e| e[:c]}
+ a.uniq! {|e| e[:c]}
+ end
+
+ a = %w(a a)
+ b = a.uniq
+ assert_equal(%w(a a), a)
+ assert(a.none?(&:frozen?))
+ assert_equal(%w(a), b)
+ assert(b.none?(&:frozen?))
end
def test_uniq_bang_with_block
@@ -1476,6 +1909,21 @@ class TestArray < Test::Unit::TestCase
b = a.uniq! {|v| v.even? }
assert_equal([1,2], a)
assert_equal(nil, b)
+
+ a = %w(a a)
+ b = a.uniq! {|v| v }
+ assert_equal(%w(a), b)
+ assert_same(a, b)
+ assert b.none?(&:frozen?)
+ end
+
+ def test_uniq_bang_with_freeze
+ ary = [1,2]
+ orig = ary.dup
+ assert_raise(FrozenError, "frozen during comparison") {
+ ary.uniq! {|v| ary.freeze; 1}
+ }
+ assert_equal(orig, ary, "must not be modified once frozen")
end
def test_unshift
@@ -1486,6 +1934,17 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[@cls[1,2], nil, 'dog', 'cat'], a.unshift(@cls[1, 2]))
end
+ def test_unshift_frozen
+ bug15952 = '[Bug #15952]'
+ assert_raise(FrozenError, bug15952) do
+ a = [1] * 100
+ b = a[4..-1]
+ a.replace([1])
+ b.freeze
+ b.unshift("a")
+ end
+ end
+
def test_OR # '|'
assert_equal(@cls[], @cls[] | @cls[])
assert_equal(@cls[1], @cls[1] | @cls[])
@@ -1495,15 +1954,111 @@ class TestArray < Test::Unit::TestCase
assert_equal(@cls[1,2], @cls[1] | @cls[2])
assert_equal(@cls[1,2], @cls[1, 1] | @cls[2, 2])
assert_equal(@cls[1,2], @cls[1, 2] | @cls[1, 2])
+
+ a = %w(a b c)
+ b = %w(a b c d e)
+ c = a | b
+ assert_equal(c, b)
+ assert_not_same(c, b)
+ assert_equal(%w(a b c), a)
+ assert_equal(%w(a b c d e), b)
+ assert(a.none?(&:frozen?))
+ assert(b.none?(&:frozen?))
+ assert(c.none?(&:frozen?))
+ end
+
+ def test_OR_in_order
+ obj1, obj2 = Class.new do
+ attr_reader :name
+ def initialize(name) @name = name; end
+ def inspect; "test_OR_in_order(#{@name})"; end
+ def hash; 0; end
+ def eql?(a) true; end
+ break [new("1"), new("2")]
+ end
+ assert_equal([obj1], [obj1]|[obj2])
+ end
+
+ def test_OR_big_in_order
+ obj1, obj2 = Class.new do
+ attr_reader :name
+ def initialize(name) @name = name; end
+ def inspect; "test_OR_in_order(#{@name})"; end
+ def hash; 0; end
+ def eql?(a) true; end
+ break [new("1"), new("2")]
+ end
+ assert_equal([obj1], [obj1]*64|[obj2]*64)
+ end
+
+ def test_OR_big_array # '|'
+ assert_equal(@cls[1,2], @cls[1]*64 | @cls[2]*64)
+ assert_equal(@cls[1,2], @cls[1, 2]*64 | @cls[1, 2]*64)
+
+ a = (1..64).to_a
+ b = (1..128).to_a
+ c = a | b
+ assert_equal(c, b)
+ assert_not_same(c, b)
+ assert_equal((1..64).to_a, a)
+ assert_equal((1..128).to_a, b)
+ end
+
+ def test_union
+ assert_equal(@cls[], @cls[].union(@cls[]))
+ assert_equal(@cls[1], @cls[1].union(@cls[]))
+ assert_equal(@cls[1], @cls[].union(@cls[1]))
+ assert_equal(@cls[1], @cls[].union(@cls[], @cls[1]))
+ assert_equal(@cls[1], @cls[1].union(@cls[1]))
+ assert_equal(@cls[1], @cls[1].union(@cls[1], @cls[1], @cls[1]))
+
+ assert_equal(@cls[1,2], @cls[1].union(@cls[2]))
+ assert_equal(@cls[1,2], @cls[1, 1].union(@cls[2, 2]))
+ assert_equal(@cls[1,2], @cls[1, 2].union(@cls[1, 2]))
+ assert_equal(@cls[1,2], @cls[1, 1].union(@cls[1, 1], @cls[1, 2], @cls[2, 1], @cls[2, 2, 2]))
+
+ a = %w(a b c)
+ b = %w(a b c d e)
+ c = a.union(b)
+ assert_equal(c, b)
+ assert_not_same(c, b)
+ assert_equal(%w(a b c), a)
+ assert_equal(%w(a b c d e), b)
+ assert(a.none?(&:frozen?))
+ assert(b.none?(&:frozen?))
+ assert(c.none?(&:frozen?))
+ end
+
+ def test_union_big_array
+ assert_equal(@cls[1,2], (@cls[1]*64).union(@cls[2]*64))
+ assert_equal(@cls[1,2,3], (@cls[1, 2]*64).union(@cls[1, 2]*64, @cls[3]*60))
+
+ a = (1..64).to_a
+ b = (1..128).to_a
+ c = a | b
+ assert_equal(c, b)
+ assert_not_same(c, b)
+ assert_equal((1..64).to_a, a)
+ assert_equal((1..128).to_a, b)
end
def test_combination
- assert_equal(@cls[[]], @cls[1,2,3,4].combination(0).to_a)
- assert_equal(@cls[[1],[2],[3],[4]], @cls[1,2,3,4].combination(1).to_a)
- assert_equal(@cls[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]], @cls[1,2,3,4].combination(2).to_a)
- assert_equal(@cls[[1,2,3],[1,2,4],[1,3,4],[2,3,4]], @cls[1,2,3,4].combination(3).to_a)
- assert_equal(@cls[[1,2,3,4]], @cls[1,2,3,4].combination(4).to_a)
- assert_equal(@cls[], @cls[1,2,3,4].combination(5).to_a)
+ a = @cls[]
+ assert_equal(1, a.combination(0).size)
+ assert_equal(0, a.combination(1).size)
+ a = @cls[1,2,3,4]
+ assert_equal(1, a.combination(0).size)
+ assert_equal(4, a.combination(1).size)
+ assert_equal(6, a.combination(2).size)
+ assert_equal(4, a.combination(3).size)
+ assert_equal(1, a.combination(4).size)
+ assert_equal(0, a.combination(5).size)
+ assert_equal(@cls[[]], a.combination(0).to_a)
+ assert_equal(@cls[[1],[2],[3],[4]], a.combination(1).to_a)
+ assert_equal(@cls[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]], a.combination(2).to_a)
+ assert_equal(@cls[[1,2,3],[1,2,4],[1,3,4],[2,3,4]], a.combination(3).to_a)
+ assert_equal(@cls[[1,2,3,4]], a.combination(4).to_a)
+ assert_equal(@cls[], a.combination(5).to_a)
end
def test_product
@@ -1528,10 +2083,23 @@ class TestArray < Test::Unit::TestCase
acc = [1,2].product(*[o]*10)
assert_equal([1,2].product([3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4], [3,4]),
acc)
+
+ a = []
+ [1, 2].product([0, 1, 2, 3, 4][1, 4]) {|x| a << x }
+ a.all? {|x| assert_not_include(x, 0)}
end
def test_permutation
+ a = @cls[]
+ assert_equal(1, a.permutation(0).size)
+ assert_equal(0, a.permutation(1).size)
a = @cls[1,2,3]
+ assert_equal(1, a.permutation(0).size)
+ assert_equal(3, a.permutation(1).size)
+ assert_equal(6, a.permutation(2).size)
+ assert_equal(6, a.permutation(3).size)
+ assert_equal(0, a.permutation(4).size)
+ assert_equal(6, a.permutation.size)
assert_equal(@cls[[]], a.permutation(0).to_a)
assert_equal(@cls[[1],[2],[3]], a.permutation(1).to_a.sort)
assert_equal(@cls[[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]],
@@ -1549,10 +2117,31 @@ class TestArray < Test::Unit::TestCase
a.permutation {|x| b << x; a.replace(@cls[9, 8, 7, 6]) }
assert_equal(@cls[9, 8, 7, 6], a)
assert_equal(@cls[1, 2, 3, 4].permutation.to_a, b)
+
+ bug3708 = '[ruby-dev:42067]'
+ assert_equal(b, @cls[0, 1, 2, 3, 4][1, 4].permutation.to_a, bug3708)
+ end
+
+ def test_permutation_stack_error
+ bug9932 = '[ruby-core:63103] [Bug #9932]'
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}", timeout: 30)
+ bug = "#{bug9932}"
+ begin;
+ assert_nothing_raised(SystemStackError, bug) do
+ assert_equal(:ok, Array.new(100_000, nil).permutation {break :ok})
+ end
+ end;
end
def test_repeated_permutation
+ a = @cls[]
+ assert_equal(1, a.repeated_permutation(0).size)
+ assert_equal(0, a.repeated_permutation(1).size)
a = @cls[1,2]
+ assert_equal(1, a.repeated_permutation(0).size)
+ assert_equal(2, a.repeated_permutation(1).size)
+ assert_equal(4, a.repeated_permutation(2).size)
+ assert_equal(8, a.repeated_permutation(3).size)
assert_equal(@cls[[]], a.repeated_permutation(0).to_a)
assert_equal(@cls[[1],[2]], a.repeated_permutation(1).to_a.sort)
assert_equal(@cls[[1,1],[1,2],[2,1],[2,2]],
@@ -1571,10 +2160,30 @@ class TestArray < Test::Unit::TestCase
a.repeated_permutation(4) {|x| b << x; a.replace(@cls[9, 8, 7, 6]) }
assert_equal(@cls[9, 8, 7, 6], a)
assert_equal(@cls[1, 2, 3, 4].repeated_permutation(4).to_a, b)
+
+ a = @cls[0, 1, 2, 3, 4][1, 4].repeated_permutation(2)
+ assert_empty(a.reject {|x| !x.include?(0)})
+ end
+
+ def test_repeated_permutation_stack_error
+ assert_separately([], "#{<<-"begin;"}\n#{<<~'end;'}", timeout: 30)
+ begin;
+ assert_nothing_raised(SystemStackError) do
+ assert_equal(:ok, Array.new(100_000, nil).repeated_permutation(500_000) {break :ok})
+ end
+ end;
end
def test_repeated_combination
+ a = @cls[]
+ assert_equal(1, a.repeated_combination(0).size)
+ assert_equal(0, a.repeated_combination(1).size)
a = @cls[1,2,3]
+ assert_equal(1, a.repeated_combination(0).size)
+ assert_equal(3, a.repeated_combination(1).size)
+ assert_equal(6, a.repeated_combination(2).size)
+ assert_equal(10, a.repeated_combination(3).size)
+ assert_equal(15, a.repeated_combination(4).size)
assert_equal(@cls[[]], a.repeated_combination(0).to_a)
assert_equal(@cls[[1],[2],[3]], a.repeated_combination(1).to_a.sort)
assert_equal(@cls[[1,1],[1,2],[1,3],[2,2],[2,3],[3,3]],
@@ -1587,8 +2196,8 @@ class TestArray < Test::Unit::TestCase
[2,2,2,2],[2,2,2,3],[2,2,3,3],[2,3,3,3],[3,3,3,3]],
a.repeated_combination(4).to_a.sort)
assert_equal(@cls[], a.repeated_combination(-1).to_a)
- assert_equal("abcde".each_char.to_a.repeated_combination(5).map{|a|a.sort}.sort,
- "edcba".each_char.to_a.repeated_combination(5).map{|a|a.sort}.sort)
+ assert_equal("abcde".each_char.to_a.repeated_combination(5).map{|e|e.sort}.sort,
+ "edcba".each_char.to_a.repeated_combination(5).map{|e|e.sort}.sort)
assert_equal(@cls[].repeated_combination(0).to_a, @cls[[]])
assert_equal(@cls[].repeated_combination(1).to_a, @cls[])
@@ -1597,6 +2206,18 @@ class TestArray < Test::Unit::TestCase
a.repeated_combination(4) {|x| b << x; a.replace(@cls[9, 8, 7, 6]) }
assert_equal(@cls[9, 8, 7, 6], a)
assert_equal(@cls[1, 2, 3, 4].repeated_combination(4).to_a, b)
+
+ a = @cls[0, 1, 2, 3, 4][1, 4].repeated_combination(2)
+ assert_empty(a.reject {|x| !x.include?(0)})
+ end
+
+ def test_repeated_combination_stack_error
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}", timeout: 20)
+ begin;
+ assert_nothing_raised(SystemStackError) do
+ assert_equal(:ok, Array.new(100_000, nil).repeated_combination(500_000) {break :ok})
+ end
+ end;
end
def test_take
@@ -1619,19 +2240,6 @@ class TestArray < Test::Unit::TestCase
assert_equal([3,4,5,0], [1,2,3,4,5,0].drop_while {|i| i < 3 })
end
- def test_modify_check
- a = []
- a.freeze
- assert_raise(RuntimeError) { a.shift }
- a = [1, 2]
- assert_raise(SecurityError) do
- Thread.new do
- $SAFE = 4
- a.shift
- end.value
- end
- end
-
LONGP = [127, 63, 31, 15, 7].map {|x| 2**x-1 }.find do |x|
begin
[].first(x)
@@ -1668,13 +2276,14 @@ class TestArray < Test::Unit::TestCase
assert_raise(IndexError) { [0][LONGP] = 2 }
assert_raise(IndexError) { [0][(LONGP + 1) / 2 - 1] = 2 }
assert_raise(IndexError) { [0][LONGP..-1] = 2 }
+ assert_raise(IndexError) { [0][LONGP..] = 2 }
a = [0]
a[2] = 4
assert_equal([0, nil, 4], a)
assert_raise(ArgumentError) { [0][0, 0, 0] = 0 }
assert_raise(ArgumentError) { [0].freeze[0, 0, 0] = 0 }
assert_raise(TypeError) { [0][:foo] = 0 }
- assert_raise(RuntimeError) { [0].freeze[:foo] = 0 }
+ assert_raise(FrozenError) { [0].freeze[:foo] = 0 }
end
def test_first2
@@ -1682,6 +2291,11 @@ class TestArray < Test::Unit::TestCase
assert_raise(ArgumentError) { [0].first(-1) }
end
+ def test_last2
+ assert_equal([0], [0].last(2))
+ assert_raise(ArgumentError) { [0].last(-1) }
+ end
+
def test_shift2
assert_equal(0, ([0] * 16).shift)
# check
@@ -1689,15 +2303,18 @@ class TestArray < Test::Unit::TestCase
a[3] = 3
a.shift(2)
assert_equal([2, 3], a)
+
+ assert_equal([1,1,1], ([1] * 100).shift(3))
end
def test_unshift_error
- assert_raise(RuntimeError) { [].freeze.unshift('cat') }
- assert_raise(RuntimeError) { [].freeze.unshift() }
+ assert_raise(FrozenError) { [].freeze.unshift('cat') }
+ assert_raise(FrozenError) { [].freeze.unshift() }
end
def test_aref
assert_raise(ArgumentError) { [][0, 0, 0] }
+ assert_raise(TypeError) { [][(1..10).step(2)] }
end
def test_fetch
@@ -1750,9 +2367,11 @@ class TestArray < Test::Unit::TestCase
assert_equal([0], a.insert(1))
assert_equal([0, 1], a.insert(1, 1))
assert_raise(ArgumentError) { a.insert }
+ assert_raise(TypeError) { a.insert(Object.new) }
assert_equal([0, 1, 2], a.insert(-1, 2))
assert_equal([0, 1, 3, 2], a.insert(-2, 3))
- assert_raise(RuntimeError) { [0].freeze.insert(0)}
+ assert_raise_with_message(IndexError, /-6/) { a.insert(-6, 4) }
+ assert_raise(FrozenError) { [0].freeze.insert(0)}
assert_raise(ArgumentError) { [0].freeze.insert }
end
@@ -1778,7 +2397,9 @@ class TestArray < Test::Unit::TestCase
def test_values_at2
a = [0, 1, 2, 3, 4, 5]
assert_equal([1, 2, 3], a.values_at(1..3))
- assert_equal([], a.values_at(7..8))
+ assert_equal([nil, nil], a.values_at(7..8))
+ bug6203 = '[ruby-core:43678]'
+ assert_equal([4, 5, nil, nil], a.values_at(4..7), bug6203)
assert_equal([nil], a.values_at(2**31-1))
end
@@ -1790,7 +2411,6 @@ class TestArray < Test::Unit::TestCase
def test_select!
a = @cls[ 1, 2, 3, 4, 5 ]
assert_equal(nil, a.select! { true })
- assert_equal(a, a.keep_if { true })
assert_equal(@cls[1, 2, 3, 4, 5], a)
a = @cls[ 1, 2, 3, 4, 5 ]
@@ -1800,6 +2420,55 @@ class TestArray < Test::Unit::TestCase
a = @cls[ 1, 2, 3, 4, 5 ]
assert_equal(a, a.select! { |i| i > 3 })
assert_equal(@cls[4, 5], a)
+
+ bug10722 = '[ruby-dev:48805] [Bug #10722]'
+ a = @cls[ 5, 6, 7, 8, 9, 10 ]
+ r = a.select! {|i|
+ break i if i > 8
+ # assert_equal(a[0], i, "should be selected values only") if i == 7
+ i >= 7
+ }
+ assert_equal(9, r)
+ assert_equal(@cls[7, 8, 9, 10], a, bug10722)
+
+ bug13053 = '[ruby-core:78739] [Bug #13053] Array#select! can resize to negative size'
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ a.select! {|i| a.clear if i == 5; false }
+ assert_equal(0, a.size, bug13053)
+ end
+
+ # also select!
+ def test_keep_if
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(a, a.keep_if { true })
+ assert_equal(@cls[1, 2, 3, 4, 5], a)
+
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(a, a.keep_if { false })
+ assert_equal(@cls[], a)
+
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(a, a.keep_if { |i| i > 3 })
+ assert_equal(@cls[4, 5], a)
+ end
+
+ def test_filter
+ assert_equal([0, 2], [0, 1, 2, 3].filter {|x| x % 2 == 0 })
+ end
+
+ # alias for select!
+ def test_filter!
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(nil, a.filter! { true })
+ assert_equal(@cls[1, 2, 3, 4, 5], a)
+
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(a, a.filter! { false })
+ assert_equal(@cls[], a)
+
+ a = @cls[ 1, 2, 3, 4, 5 ]
+ assert_equal(a, a.filter! { |i| i > 3 })
+ assert_equal(@cls[4, 5], a)
end
def test_delete2
@@ -1812,6 +2481,22 @@ class TestArray < Test::Unit::TestCase
assert_equal([1, 3], [0, 1, 2, 3].reject {|x| x % 2 == 0 })
end
+ def test_reject_with_callcc
+ need_continuation
+ bug9727 = '[ruby-dev:48101] [Bug #9727]'
+ cont = nil
+ a = [*1..10].reject do |i|
+ callcc {|c| cont = c} if !cont and i == 10
+ false
+ end
+ if a.size < 1000
+ a.unshift(:x)
+ cont.call
+ end
+ assert_equal(1000, a.size, bug9727)
+ assert_equal([:x, *1..10], a.uniq, bug9727)
+ end
+
def test_zip
assert_equal([[1, :a, "a"], [2, :b, "b"], [3, nil, "c"]],
[1, 2, 3].zip([:a, :b], ["a", "b", "c", "d"]))
@@ -1821,13 +2506,22 @@ class TestArray < Test::Unit::TestCase
ary = Object.new
def ary.to_a; [1, 2]; end
- assert_raise(NoMethodError){ %w(a b).zip(ary) }
+ assert_raise(TypeError) {%w(a b).zip(ary)}
def ary.each; [3, 4].each{|e|yield e}; end
assert_equal([['a', 3], ['b', 4]], %w(a b).zip(ary))
def ary.to_ary; [5, 6]; end
assert_equal([['a', 5], ['b', 6]], %w(a b).zip(ary))
end
+ def test_zip_bug
+ bug8153 = "ruby-core:53650"
+ r = 1..1
+ def r.respond_to?(*)
+ super
+ end
+ assert_equal [[42, 1]], [42].zip(r), bug8153
+ end
+
def test_transpose
assert_equal([[1, :a], [2, :b], [3, :c]],
[[1, 2, 3], [:a, :b, :c]].transpose)
@@ -1854,11 +2548,18 @@ class TestArray < Test::Unit::TestCase
assert_not_equal([0, 1, 2], [0, 1, 3])
end
- def test_hash2
- a = []
- a << a
- assert_equal([[a]].hash, a.hash)
- assert_not_equal([a, a].hash, a.hash) # Implementation dependent
+ A = Array.new(3, &:to_s)
+ B = A.dup
+
+ def test_equal_resize
+ o = Object.new
+ def o.==(o)
+ A.clear
+ B.clear
+ true
+ end
+ A[1] = o
+ assert_equal(A, B)
end
def test_flatten_error
@@ -1870,22 +2571,78 @@ class TestArray < Test::Unit::TestCase
assert_raise(ArgumentError) { a.flatten!(1, 2) }
assert_raise(TypeError) { a.flatten!(:foo) }
assert_raise(ArgumentError) { f.flatten!(1, 2) }
- assert_raise(RuntimeError) { f.flatten! }
- assert_raise(RuntimeError) { f.flatten!(:foo) }
+ assert_raise(FrozenError) { f.flatten! }
+ assert_raise(FrozenError) { f.flatten!(:foo) }
end
def test_shuffle
100.times do
assert_equal([0, 1, 2], [2, 1, 0].shuffle.sort)
end
+
+ gen = Random.new(0)
+ assert_raise(ArgumentError) {[1, 2, 3].shuffle(1, random: gen)}
+ srand(0)
+ 100.times do
+ assert_equal([0, 1, 2].shuffle, [0, 1, 2].shuffle(random: gen))
+ end
+
+ assert_raise_with_message(ArgumentError, /unknown keyword/) do
+ [0, 1, 2].shuffle(xawqij: "a")
+ end
+ assert_raise_with_message(ArgumentError, /unknown keyword/) do
+ [0, 1, 2].shuffle!(xawqij: "a")
+ end
+ end
+
+ def test_shuffle_random
+ gen = proc do
+ 10000000
+ end
+ class << gen
+ alias rand call
+ end
+ assert_raise(RangeError) {
+ [*0..2].shuffle(random: gen)
+ }
+
+ ary = (0...10000).to_a
+ gen = proc do
+ ary.replace([])
+ 0.5
+ end
+ class << gen
+ alias rand call
+ end
+ assert_raise(RuntimeError) {ary.shuffle!(random: gen)}
+
+ zero = Object.new
+ def zero.to_int
+ 0
+ end
+ gen_to_int = proc do |max|
+ zero
+ end
+ class << gen_to_int
+ alias rand call
+ end
+ ary = (0...10000).to_a
+ assert_equal(ary.rotate, ary.shuffle(random: gen_to_int))
+
+ assert_raise(NoMethodError) {
+ ary.shuffle(random: Object.new)
+ }
+ assert_raise(NoMethodError) {
+ ary.shuffle!(random: Object.new)
+ }
end
def test_sample
100.times do
- assert([0, 1, 2].include?([2, 1, 0].sample))
+ assert_include([0, 1, 2], [2, 1, 0].sample)
samples = [2, 1, 0].sample(2)
samples.each{|sample|
- assert([0, 1, 2].include?(sample))
+ assert_include([0, 1, 2], sample)
}
end
@@ -1894,7 +2651,7 @@ class TestArray < Test::Unit::TestCase
(0..20).each do |n|
100.times do
b = a.sample(n)
- assert_equal([n, 18].min, b.uniq.size)
+ assert_equal([n, 18].min, b.size)
assert_equal(a, (a | b).sort)
assert_equal(b.sort, (a & b).sort)
end
@@ -1907,6 +2664,81 @@ class TestArray < Test::Unit::TestCase
end
assert_raise(ArgumentError, '[ruby-core:23374]') {[1, 2].sample(-1)}
+
+ gen = Random.new(0)
+ srand(0)
+ a = (1..18).to_a
+ (0..20).each do |n|
+ 100.times do |i|
+ assert_equal(a.sample(n), a.sample(n, random: gen), "#{i}/#{n}")
+ end
+ end
+
+ assert_raise_with_message(ArgumentError, /unknown keyword/) do
+ [0, 1, 2].sample(xawqij: "a")
+ end
+ end
+
+ def test_sample_random
+ ary = (0...10000).to_a
+ assert_raise(ArgumentError) {ary.sample(1, 2, random: nil)}
+ gen0 = proc do |max|
+ max/2
+ end
+ class << gen0
+ alias rand call
+ end
+ gen1 = proc do |max|
+ ary.replace([])
+ max/2
+ end
+ class << gen1
+ alias rand call
+ end
+ assert_equal(5000, ary.sample(random: gen0))
+ assert_nil(ary.sample(random: gen1))
+ assert_equal([], ary)
+ ary = (0...10000).to_a
+ assert_equal([5000], ary.sample(1, random: gen0))
+ assert_equal([], ary.sample(1, random: gen1))
+ assert_equal([], ary)
+ ary = (0...10000).to_a
+ assert_equal([5000, 4999], ary.sample(2, random: gen0))
+ assert_equal([], ary.sample(2, random: gen1))
+ assert_equal([], ary)
+ ary = (0...10000).to_a
+ assert_equal([5000, 4999, 5001], ary.sample(3, random: gen0))
+ assert_equal([], ary.sample(3, random: gen1))
+ assert_equal([], ary)
+ ary = (0...10000).to_a
+ assert_equal([5000, 4999, 5001, 4998], ary.sample(4, random: gen0))
+ assert_equal([], ary.sample(4, random: gen1))
+ assert_equal([], ary)
+ ary = (0...10000).to_a
+ assert_equal([5000, 4999, 5001, 4998, 5002, 4997, 5003, 4996, 5004, 4995], ary.sample(10, random: gen0))
+ assert_equal([], ary.sample(10, random: gen1))
+ assert_equal([], ary)
+ ary = (0...10000).to_a
+ assert_equal([5000, 0, 5001, 2, 5002, 4, 5003, 6, 5004, 8, 5005], ary.sample(11, random: gen0))
+ ary.sample(11, random: gen1) # implementation detail, may change in the future
+ assert_equal([], ary)
+
+ half = Object.new
+ def half.to_int
+ 5000
+ end
+ gen_to_int = proc do |max|
+ half
+ end
+ class << gen_to_int
+ alias rand call
+ end
+ ary = (0...10000).to_a
+ assert_equal(5000, ary.sample(random: gen_to_int))
+
+ assert_raise(NoMethodError) {
+ ary.sample(random: Object.new)
+ }
end
def test_cycle
@@ -1923,6 +2755,9 @@ class TestArray < Test::Unit::TestCase
a = []
[0, 1, 2].cycle(3) {|i| a << i }
assert_equal([0, 1, 2, 0, 1, 2, 0, 1, 2], a)
+
+ assert_equal(Float::INFINITY, a.cycle.size)
+ assert_equal(27, a.cycle(3).size)
end
def test_reverse_each2
@@ -1937,9 +2772,23 @@ class TestArray < Test::Unit::TestCase
end
def test_combination2
- assert_nothing_raised do
- (0..100).to_a.combination(50) { break }
- end
+ assert_equal(:called, (0..100).to_a.combination(50) { break :called }, "[ruby-core:29240] ... must be yielded even if 100C50 > signed integer")
+ end
+
+ def test_combination_clear
+ bug9939 = '[ruby-core:63149] [Bug #9939]'
+ assert_nothing_raised(bug9939) {
+ a = [*0..100]
+ a.combination(3) {|*,x| a.clear}
+ }
+
+ bug13052 = '[ruby-core:78738] [Bug #13052] Array#combination segfaults if the Array is modified during iteration'
+ assert_nothing_raised(bug13052) {
+ a = [*0..100]
+ a.combination(1) { a.clear }
+ a = [*0..100]
+ a.repeated_combination(1) { a.clear }
+ }
end
def test_product2
@@ -1957,17 +2806,15 @@ class TestArray < Test::Unit::TestCase
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) # embeded
- assert_equal(Array2, Array2[*(1..100)][1..99].class) #not embeded
+ assert_equal(Array2, Array2[1,2][0,1].class) # embedded
+ assert_equal(Array2, Array2[*(1..100)][1..99].class) #not embedded
end
def test_inspect
a = @cls[1, 2, 3]
a.taint
- a.untrust
s = a.inspect
assert_equal(true, s.tainted?)
- assert_equal(true, s.untrusted?)
end
def test_initialize2
@@ -1982,6 +2829,11 @@ class TestArray < Test::Unit::TestCase
b.replace(a)
assert_equal((1..10).to_a, a.shift(10))
assert_equal((11..100).to_a, a)
+
+ a = (1..30).to_a
+ assert_equal((1..3).to_a, a.shift(3))
+ # occupied
+ assert_equal((4..6).to_a, a.shift(3))
end
def test_replace_shared_ary
@@ -2058,9 +2910,241 @@ class TestArray < Test::Unit::TestCase
assert_equal([], a.rotate!(13))
assert_equal([], a.rotate!(-13))
a = [].freeze
- e = assert_raise(RuntimeError) {a.rotate!}
- assert_match(/can't modify frozen array/, e.message)
+ assert_raise_with_message(FrozenError, /can\'t modify frozen/) {a.rotate!}
a = [1,2,3]
assert_raise(ArgumentError) { a.rotate!(1, 1) }
end
+
+ def test_bsearch_typechecks_return_values
+ assert_raise(TypeError) do
+ [1, 2, 42, 100, 666].bsearch{ "not ok" }
+ end
+ c = eval("class C\u{309a 26a1 26c4 1f300};self;end")
+ assert_raise_with_message(TypeError, /C\u{309a 26a1 26c4 1f300}/) do
+ [0,1].bsearch {c.new}
+ end
+ assert_equal [1, 2, 42, 100, 666].bsearch{}, [1, 2, 42, 100, 666].bsearch{false}
+ end
+
+ def test_bsearch_with_no_block
+ enum = [1, 2, 42, 100, 666].bsearch
+ assert_nil enum.size
+ assert_equal 42, enum.each{|x| x >= 33 }
+ end
+
+ def test_bsearch_in_find_minimum_mode
+ a = [0, 4, 7, 10, 12]
+ assert_equal(4, a.bsearch {|x| x >= 4 })
+ assert_equal(7, a.bsearch {|x| x >= 6 })
+ assert_equal(0, a.bsearch {|x| x >= -1 })
+ assert_equal(nil, a.bsearch {|x| x >= 100 })
+ end
+
+ def test_bsearch_in_find_any_mode
+ a = [0, 4, 7, 10, 12]
+ assert_include([4, 7], a.bsearch {|x| 1 - x / 4 })
+ assert_equal(nil, a.bsearch {|x| 4 - x / 2 })
+ assert_equal(nil, a.bsearch {|x| 1 })
+ assert_equal(nil, a.bsearch {|x| -1 })
+
+ assert_include([4, 7], a.bsearch {|x| (1 - x / 4) * (2**100) })
+ assert_equal(nil, a.bsearch {|x| 1 * (2**100) })
+ assert_equal(nil, a.bsearch {|x| (-1) * (2**100) })
+
+ assert_include([4, 7], a.bsearch {|x| (2**100).coerce((1 - x / 4) * (2**100)).first })
+ end
+
+ def test_bsearch_index_typechecks_return_values
+ assert_raise(TypeError) do
+ [1, 2, 42, 100, 666].bsearch_index {"not ok"}
+ end
+ assert_equal [1, 2, 42, 100, 666].bsearch_index {}, [1, 2, 42, 100, 666].bsearch_index {false}
+ end
+
+ def test_bsearch_index_with_no_block
+ enum = [1, 2, 42, 100, 666].bsearch_index
+ assert_nil enum.size
+ assert_equal 2, enum.each{|x| x >= 33 }
+ end
+
+ def test_bsearch_index_in_find_minimum_mode
+ a = [0, 4, 7, 10, 12]
+ assert_equal(1, a.bsearch_index {|x| x >= 4 })
+ assert_equal(2, a.bsearch_index {|x| x >= 6 })
+ assert_equal(0, a.bsearch_index {|x| x >= -1 })
+ assert_equal(nil, a.bsearch_index {|x| x >= 100 })
+ end
+
+ def test_bsearch_index_in_find_any_mode
+ a = [0, 4, 7, 10, 12]
+ assert_include([1, 2], a.bsearch_index {|x| 1 - x / 4 })
+ assert_equal(nil, a.bsearch_index {|x| 4 - x / 2 })
+ assert_equal(nil, a.bsearch_index {|x| 1 })
+ assert_equal(nil, a.bsearch_index {|x| -1 })
+
+ assert_include([1, 2], a.bsearch_index {|x| (1 - x / 4) * (2**100) })
+ assert_equal(nil, a.bsearch_index {|x| 1 * (2**100) })
+ assert_equal(nil, a.bsearch_index {|x| (-1) * (2**100) })
+
+ assert_include([1, 2], a.bsearch_index {|x| (2**100).coerce((1 - x / 4) * (2**100)).first })
+ end
+
+ def test_shared_marking
+ reduce = proc do |s|
+ s.gsub(/(verify_internal_consistency_reachable_i:\sWB\smiss\s\S+\s\(T_ARRAY\)\s->\s)\S+\s\((proc|T_NONE)\)\n
+ \K(?:\1\S+\s\(\2\)\n)*/x) do
+ "...(snip #{$&.count("\n")} lines)...\n"
+ end
+ end
+ begin
+ assert_normal_exit(<<-EOS, '[Bug #9718]', timeout: 5, stdout_filter: reduce)
+ queue = []
+ 50.times do
+ 10_000.times do
+ queue << lambda{}
+ end
+ GC.start(full_mark: false, immediate_sweep: true)
+ GC.verify_internal_consistency
+ queue.shift.call
+ end
+ EOS
+ rescue Timeout::Error => e
+ skip e.message
+ end
+ end
+
+ sizeof_long = [0].pack("l!").size
+ sizeof_voidp = [""].pack("p").size
+ if sizeof_long < sizeof_voidp
+ ARY_MAX = (1<<(8*sizeof_long-1)) / sizeof_voidp - 1
+ Bug11235 = '[ruby-dev:49043] [Bug #11235]'
+
+ def test_push_over_ary_max
+ assert_separately(['-', ARY_MAX.to_s, Bug11235], "#{<<~"begin;"}\n#{<<~'end;'}", timeout: 30)
+ begin;
+ a = Array.new(ARGV[0].to_i)
+ assert_raise(IndexError, ARGV[1]) {0x1000.times {a.push(1)}}
+ end;
+ end
+
+ def test_unshift_over_ary_max
+ assert_separately(['-', ARY_MAX.to_s, Bug11235], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ a = Array.new(ARGV[0].to_i)
+ assert_raise(IndexError, ARGV[1]) {0x1000.times {a.unshift(1)}}
+ end;
+ end
+
+ def test_splice_over_ary_max
+ assert_separately(['-', ARY_MAX.to_s, Bug11235], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ a = Array.new(ARGV[0].to_i)
+ assert_raise(IndexError, ARGV[1]) {a[0, 0] = Array.new(0x1000)}
+ end;
+ end
+ end
+
+ def test_dig
+ h = @cls[@cls[{a: 1}], 0]
+ assert_equal(1, h.dig(0, 0, :a))
+ assert_nil(h.dig(2, 0))
+ assert_raise(TypeError) {h.dig(1, 0)}
+ end
+
+ FIXNUM_MIN = RbConfig::LIMITS['FIXNUM_MIN']
+ FIXNUM_MAX = RbConfig::LIMITS['FIXNUM_MAX']
+
+ def assert_typed_equal(e, v, cls, msg=nil)
+ assert_kind_of(cls, v, msg)
+ assert_equal(e, v, msg)
+ end
+
+ def assert_int_equal(e, v, msg=nil)
+ assert_typed_equal(e, v, Integer, msg)
+ end
+
+ def assert_rational_equal(e, v, msg=nil)
+ assert_typed_equal(e, v, Rational, msg)
+ end
+
+ def assert_float_equal(e, v, msg=nil)
+ assert_typed_equal(e, v, Float, msg)
+ end
+
+ def assert_complex_equal(e, v, msg=nil)
+ assert_typed_equal(e, v, Complex, msg)
+ end
+
+ def test_sum
+ assert_int_equal(0, [].sum)
+ assert_int_equal(3, [3].sum)
+ assert_int_equal(8, [3, 5].sum)
+ assert_int_equal(15, [3, 5, 7].sum)
+ assert_rational_equal(8r, [3, 5r].sum)
+ assert_float_equal(15.0, [3, 5, 7.0].sum)
+ assert_float_equal(15.0, [3, 5r, 7.0].sum)
+ assert_complex_equal(8r + 1i, [3, 5r, 1i].sum)
+ assert_complex_equal(15.0 + 1i, [3, 5r, 7.0, 1i].sum)
+
+ assert_int_equal(2*FIXNUM_MAX, Array.new(2, FIXNUM_MAX).sum)
+ assert_int_equal(2*(FIXNUM_MAX+1), Array.new(2, FIXNUM_MAX+1).sum)
+ assert_int_equal(10*FIXNUM_MAX, Array.new(10, FIXNUM_MAX).sum)
+ assert_int_equal(0, ([FIXNUM_MAX, 1, -FIXNUM_MAX, -1]*10).sum)
+ assert_int_equal(FIXNUM_MAX*10, ([FIXNUM_MAX+1, -1]*10).sum)
+ assert_int_equal(2*FIXNUM_MIN, Array.new(2, FIXNUM_MIN).sum)
+
+ assert_float_equal(0.0, [].sum(0.0))
+ assert_float_equal(3.0, [3].sum(0.0))
+ assert_float_equal(3.5, [3].sum(0.5))
+ assert_float_equal(8.5, [3.5, 5].sum)
+ assert_float_equal(10.5, [2, 8.5].sum)
+ assert_float_equal((FIXNUM_MAX+1).to_f, [FIXNUM_MAX, 1, 0.0].sum)
+ assert_float_equal((FIXNUM_MAX+1).to_f, [0.0, FIXNUM_MAX+1].sum)
+
+ assert_rational_equal(3/2r, [1/2r, 1].sum)
+ assert_rational_equal(5/6r, [1/2r, 1/3r].sum)
+
+ assert_equal(2.0+3.0i, [2.0, 3.0i].sum)
+
+ assert_int_equal(13, [1, 2].sum(10))
+ assert_int_equal(16, [1, 2].sum(10) {|v| v * 2 })
+
+ yielded = []
+ three = SimpleDelegator.new(3)
+ ary = [1, 2.0, three]
+ assert_float_equal(12.0, ary.sum {|x| yielded << x; x * 2 })
+ assert_equal(ary, yielded)
+
+ assert_raise(TypeError) { [Object.new].sum }
+
+ large_number = 100000000
+ small_number = 1e-9
+ until (large_number + small_number) == large_number
+ small_number /= 10
+ end
+ assert_float_equal(large_number+(small_number*10), [large_number, *[small_number]*10].sum)
+ assert_float_equal(large_number+(small_number*10), [large_number/1r, *[small_number]*10].sum)
+ assert_float_equal(large_number+(small_number*11), [small_number, large_number/1r, *[small_number]*10].sum)
+ assert_float_equal(small_number, [large_number, small_number, -large_number].sum)
+ assert_equal(+Float::INFINITY, [+Float::INFINITY].sum)
+ assert_equal(+Float::INFINITY, [0.0, +Float::INFINITY].sum)
+ assert_equal(+Float::INFINITY, [+Float::INFINITY, 0.0].sum)
+ assert_equal(-Float::INFINITY, [-Float::INFINITY].sum)
+ assert_equal(-Float::INFINITY, [0.0, -Float::INFINITY].sum)
+ assert_equal(-Float::INFINITY, [-Float::INFINITY, 0.0].sum)
+ assert_predicate([-Float::INFINITY, Float::INFINITY].sum, :nan?)
+
+ assert_equal("abc", ["a", "b", "c"].sum(""))
+ assert_equal([1, [2], 3], [[1], [[2]], [3]].sum([]))
+
+ assert_raise(TypeError) {[0].sum("")}
+ assert_raise(TypeError) {[1].sum("")}
+ end
+
+ private
+ def need_continuation
+ unless respond_to?(:callcc, true)
+ EnvUtil.suppress_warning {require 'continuation'}
+ end
+ end
end
diff --git a/test/ruby/test_assignment.rb b/test/ruby/test_assignment.rb
index e38b20b285..c1f8c46890 100644
--- a/test/ruby/test_assignment.rb
+++ b/test/ruby/test_assignment.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestAssignment < Test::Unit::TestCase
@@ -17,7 +18,9 @@ class TestAssignment < Test::Unit::TestCase
cc = 5
cc &&=44
assert_equal(44, cc)
+ end
+ def test_assign_simple
a = nil; assert_nil(a)
a = 1; assert_equal(1, a)
a = []; assert_equal([], a)
@@ -28,7 +31,9 @@ class TestAssignment < Test::Unit::TestCase
a = [*[]]; assert_equal([], a)
a = [*[1]]; assert_equal([1], a)
a = [*[1,2]]; assert_equal([1,2], a)
+ end
+ def test_assign_splat
a = *[]; assert_equal([], a)
a = *[1]; assert_equal([1], a)
a = *[nil]; assert_equal([nil], a)
@@ -37,7 +42,9 @@ class TestAssignment < Test::Unit::TestCase
a = *[*[]]; assert_equal([], a)
a = *[*[1]]; assert_equal([1], a)
a = *[*[1,2]]; assert_equal([1,2], a)
+ end
+ def test_assign_ary
*a = nil; assert_equal([nil], a)
*a = 1; assert_equal([1], a)
*a = []; assert_equal([], a)
@@ -48,7 +55,9 @@ class TestAssignment < Test::Unit::TestCase
*a = [*[]]; assert_equal([], a)
*a = [*[1]]; assert_equal([1], a)
*a = [*[1,2]]; assert_equal([1,2], a)
+ end
+ def test_assign_ary_splat
*a = *[]; assert_equal([], a)
*a = *[1]; assert_equal([1], a)
*a = *[nil]; assert_equal([nil], a)
@@ -57,7 +66,9 @@ class TestAssignment < Test::Unit::TestCase
*a = *[*[]]; assert_equal([], a)
*a = *[*[1]]; assert_equal([1], a)
*a = *[*[1,2]]; assert_equal([1,2], a)
+ end
+ def test_massign_simple
a,b,*c = nil; assert_equal([nil,nil,[]], [a,b,c])
a,b,*c = 1; assert_equal([1,nil,[]], [a,b,c])
a,b,*c = []; assert_equal([nil,nil,[]], [a,b,c])
@@ -68,7 +79,9 @@ class TestAssignment < Test::Unit::TestCase
a,b,*c = [*[]]; assert_equal([nil,nil,[]], [a,b,c])
a,b,*c = [*[1]]; assert_equal([1,nil,[]], [a,b,c])
a,b,*c = [*[1,2]]; assert_equal([1,2,[]], [a,b,c])
+ 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])
a,b,*c = *[nil]; assert_equal([nil,nil,[]], [a,b,c])
@@ -77,7 +90,9 @@ class TestAssignment < Test::Unit::TestCase
a,b,*c = *[*[]]; assert_equal([nil,nil,[]], [a,b,c])
a,b,*c = *[*[1]]; assert_equal([1,nil,[]], [a,b,c])
a,b,*c = *[*[1,2]]; assert_equal([1,2,[]], [a,b,c])
+ end
+ def test_assign_abbreviated
bug2050 = '[ruby-core:25629]'
a = Hash.new {[]}
b = [1, 2]
@@ -87,6 +102,47 @@ class TestAssignment < Test::Unit::TestCase
assert_equal([1, 2, 3, [1, 2, 3]], a[:x], bug2050)
end
+ def test_assign_private_self
+ bug11096 = '[ruby-core:68984] [Bug #11096]'
+
+ o = Object.new
+ class << o
+ private
+ def foo; 42; end
+ def [](i); 42; end
+ def foo=(a); 42; end
+ def []=(i, a); 42; end
+ end
+
+ assert_raise(NoMethodError) {
+ o.instance_eval {o.foo = 1}
+ }
+ assert_nothing_raised(NoMethodError) {
+ assert_equal(1, o.instance_eval {self.foo = 1})
+ }
+
+ assert_raise(NoMethodError) {
+ o.instance_eval {o[0] = 1}
+ }
+ assert_nothing_raised(NoMethodError) {
+ assert_equal(1, o.instance_eval {self[0] = 1})
+ }
+
+ assert_raise(NoMethodError, bug11096) {
+ o.instance_eval {self.foo += 1}
+ }
+ assert_raise(NoMethodError, bug11096) {
+ o.instance_eval {self.foo &&= 1}
+ }
+
+ assert_raise(NoMethodError, bug11096) {
+ o.instance_eval {self[0] += 1}
+ }
+ assert_raise(NoMethodError, bug11096) {
+ o.instance_eval {self[0] &&= 1}
+ }
+ end
+
def test_yield
def f; yield(nil); end; f {|a| assert_nil(a)}; undef f
def f; yield(1); end; f {|a| assert_equal(1, a)}; undef f
@@ -424,11 +480,10 @@ class TestAssignment < Test::Unit::TestCase
assert_equal 1, a
assert_equal [2, 3], b
- # not supported yet
- #a, *b, c = 1, 2, 3, 4
- #assert_equal 1, a
- #assert_equal [2,3], b
- #assert_equal 4, c
+ a, *b, c = 1, 2, 3, 4
+ assert_equal 1, a
+ assert_equal [2,3], b
+ assert_equal 4, c
a = 1, 2
assert_equal [1, 2], a
@@ -496,6 +551,11 @@ class TestAssignment < Test::Unit::TestCase
a, b = Base::A, Base::B
assert_equal [3,4], [a,b]
end
+
+ def test_massign_in_cond
+ result = eval("if (a, b = MyObj.new); [a, b]; end", nil, __FILE__, __LINE__)
+ assert_equal [[1,2],[3,4]], result
+ end
end
require_relative 'sentence'
@@ -692,4 +752,32 @@ class TestAssignmentGen < Test::Unit::TestCase
check(assign)
}
end
+
+ def test_optimized_aset
+ bug9448 = Class.new do
+ def []=(key, new_value)
+ '[ruby-core:60071] [Bug #9448]'
+ end
+ end
+ o = bug9448.new
+ assert_equal("ok", o['current'] = "ok")
+ end
+
+ def test_massign_aref_lhs_splat
+ bug11970 = '[ruby-core:72777] [Bug #11970]'
+ h = {}
+ k = [:key]
+ h[*k], = ["ok", "ng"]
+ assert_equal("ok", h[:key], bug11970)
+ end
+
+ def test_chainged_assign_command
+ all_assertions do |a|
+ asgn = %w'= +='
+ asgn.product(asgn) do |a1, a2|
+ stmt = "a #{a1} b #{a2} raise 'x'"
+ a.for(stmt) {assert_valid_syntax(stmt)}
+ end
+ end
+ end
end
diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb
new file mode 100644
index 0000000000..3ba67e69a2
--- /dev/null
+++ b/test/ruby/test_ast.rb
@@ -0,0 +1,283 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'tempfile'
+
+class RubyVM
+ module AbstractSyntaxTree
+ class Node
+ class CodePosition
+ include Comparable
+ attr_reader :lineno, :column
+ def initialize(lineno, column)
+ @lineno = lineno
+ @column = column
+ end
+
+ def <=>(other)
+ case
+ when lineno < other.lineno
+ -1
+ when lineno == other.lineno
+ column <=> other.column
+ when lineno > other.lineno
+ 1
+ end
+ end
+ end
+
+ def beg_pos
+ CodePosition.new(first_lineno, first_column)
+ end
+
+ def end_pos
+ CodePosition.new(last_lineno, last_column)
+ end
+
+ alias to_s inspect
+ end
+ end
+end
+
+class TestAst < Test::Unit::TestCase
+ class Helper
+ attr_reader :errors
+
+ def initialize(path)
+ @path = path
+ @errors = []
+ @debug = false
+ end
+
+ def validate_range
+ @errors = []
+ validate_range0(ast)
+
+ @errors.empty?
+ end
+
+ def validate_not_cared
+ @errors = []
+ validate_not_cared0(ast)
+
+ @errors.empty?
+ end
+
+ def ast
+ return @ast if defined?(@ast)
+ @ast = RubyVM::AbstractSyntaxTree.parse_file(@path)
+ end
+
+ private
+
+ def validate_range0(node)
+ beg_pos, end_pos = node.beg_pos, node.end_pos
+ children = node.children.grep(RubyVM::AbstractSyntaxTree::Node)
+
+ return true if children.empty?
+ # These NODE_D* has NODE_ARRAY as nd_next->nd_next whose last locations
+ # we can not update when item is appended.
+ return true if [:DSTR, :DXSTR, :DREGX, :DSYM].include? node.type
+
+ min = children.map(&:beg_pos).min
+ max = children.map(&:end_pos).max
+
+ unless beg_pos <= min
+ @errors << { type: :min_validation_error, min: min, beg_pos: beg_pos, node: node }
+ end
+
+ unless max <= end_pos
+ @errors << { type: :max_validation_error, max: max, end_pos: end_pos, node: node }
+ end
+
+ p "#{node} => #{children}" if @debug
+
+ children.each do |child|
+ p child if @debug
+ validate_range0(child)
+ end
+ end
+
+ def validate_not_cared0(node)
+ beg_pos, end_pos = node.beg_pos, node.end_pos
+ children = node.children.grep(RubyVM::AbstractSyntaxTree::Node)
+
+ @errors << { type: :first_lineno, node: node } if beg_pos.lineno == 0
+ @errors << { type: :first_column, node: node } if beg_pos.column == -1
+ @errors << { type: :last_lineno, node: node } if end_pos.lineno == 0
+ @errors << { type: :last_column, node: node } if end_pos.column == -1
+
+ children.each {|c| validate_not_cared0(c) }
+ end
+ end
+
+ SRCDIR = File.expand_path("../../..", __FILE__)
+
+ Dir.glob("test/**/*.rb", base: SRCDIR).each do |path|
+ define_method("test_ranges:#{path}") do
+ helper = Helper.new("#{SRCDIR}/#{path}")
+ helper.validate_range
+
+ assert_equal([], helper.errors)
+ end
+ end
+
+ Dir.glob("test/**/*.rb", base: SRCDIR).each do |path|
+ define_method("test_not_cared:#{path}") do
+ helper = Helper.new("#{SRCDIR}/#{path}")
+ helper.validate_not_cared
+
+ assert_equal([], helper.errors)
+ end
+ end
+
+ def test_allocate
+ assert_raise(TypeError) {RubyVM::AbstractSyntaxTree::Node.allocate}
+ end
+
+ def test_parse_argument_error
+ assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(0)}
+ assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(nil)}
+ assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(false)}
+ assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(true)}
+ assert_raise(TypeError) {RubyVM::AbstractSyntaxTree.parse(:foo)}
+ end
+
+ def test_column_with_long_heredoc_identifier
+ term = "A"*257
+ ast = RubyVM::AbstractSyntaxTree.parse("<<-#{term}\n""ddddddd\n#{term}\n")
+ node = ast.children[2]
+ assert_equal(:STR, node.type)
+ assert_equal(0, node.first_column)
+ end
+
+ def test_column_of_heredoc
+ node = RubyVM::AbstractSyntaxTree.parse("<<-SRC\nddddddd\nSRC\n").children[2]
+ assert_equal(:STR, node.type)
+ assert_equal(0, node.first_column)
+ assert_equal(6, node.last_column)
+
+ node = RubyVM::AbstractSyntaxTree.parse("<<SRC\nddddddd\nSRC\n").children[2]
+ assert_equal(:STR, node.type)
+ assert_equal(0, node.first_column)
+ assert_equal(5, node.last_column)
+ end
+
+ def test_parse_raises_syntax_error
+ assert_raise_with_message(SyntaxError, /\bend\b/) do
+ RubyVM::AbstractSyntaxTree.parse("end")
+ end
+ end
+
+ def test_parse_file_raises_syntax_error
+ Tempfile.create(%w"test_ast .rb") do |f|
+ f.puts "end"
+ f.close
+ assert_raise_with_message(SyntaxError, /\bend\b/) do
+ RubyVM::AbstractSyntaxTree.parse_file(f.path)
+ end
+ end
+ end
+
+ def test_of
+ proc = Proc.new { 1 + 2 }
+ method = self.method(__method__)
+
+ node_proc = RubyVM::AbstractSyntaxTree.of(proc)
+ node_method = RubyVM::AbstractSyntaxTree.of(method)
+
+ 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;'}"
+ begin;
+ SCRIPT_LINES__ = {}
+ assert_instance_of(RubyVM::AbstractSyntaxTree::Node, RubyVM::AbstractSyntaxTree.of(proc {|x| x}))
+ end;
+ tmp.close
+ assert_separately(["-", tmp.path], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ load ARGV[0]
+ assert_empty(SCRIPT_LINES__)
+ end;
+ end
+ end
+
+ def test_scope_local_variables
+ node = RubyVM::AbstractSyntaxTree.parse("x = 0")
+ lv, _, body = *node.children
+ assert_equal([:x], lv)
+ assert_equal(:LASGN, body.type)
+ end
+
+ def test_call
+ node = RubyVM::AbstractSyntaxTree.parse("nil.foo")
+ _, _, body = *node.children
+ assert_equal(:CALL, body.type)
+ recv, mid, args = body.children
+ assert_equal(:NIL, recv.type)
+ assert_equal(:foo, mid)
+ assert_nil(args)
+ end
+
+ def test_fcall
+ node = RubyVM::AbstractSyntaxTree.parse("foo()")
+ _, _, body = *node.children
+ assert_equal(:FCALL, body.type)
+ mid, args = body.children
+ assert_equal(:foo, mid)
+ assert_nil(args)
+ end
+
+ def test_vcall
+ node = RubyVM::AbstractSyntaxTree.parse("foo")
+ _, _, body = *node.children
+ assert_equal(:VCALL, body.type)
+ mid, args = body.children
+ assert_equal(:foo, mid)
+ assert_nil(args)
+ end
+
+ def test_defn
+ node = RubyVM::AbstractSyntaxTree.parse("def a; end")
+ _, _, body = *node.children
+ assert_equal(:DEFN, body.type)
+ mid, defn = body.children
+ assert_equal(:a, mid)
+ assert_equal(:SCOPE, defn.type)
+ end
+
+ def test_defs
+ node = RubyVM::AbstractSyntaxTree.parse("def a.b; end")
+ _, _, 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)
+ end
+
+ def test_dstr
+ node = RubyVM::AbstractSyntaxTree.parse('"foo#{1}bar"')
+ _, _, body = *node.children
+ assert_equal(:DSTR, body.type)
+ head, body = body.children
+ assert_equal("foo", head)
+ assert_equal(:EVSTR, body.type)
+ body, = body.children
+ assert_equal(:LIT, body.type)
+ assert_equal([1], body.children)
+ 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
+end
diff --git a/test/ruby/test_autoload.rb b/test/ruby/test_autoload.rb
index c2039086cf..1d2d1af00c 100644
--- a/test/ruby/test_autoload.rb
+++ b/test/ruby/test_autoload.rb
@@ -1,12 +1,365 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
+require 'tempfile'
class TestAutoload < Test::Unit::TestCase
def test_autoload_so
- # Continuation is always available, unless excluded intentionally.
+ # Date is always available, unless excluded intentionally.
assert_in_out_err([], <<-INPUT, [], [])
- autoload :Continuation, "continuation"
- begin Continuation; rescue LoadError; end
+ autoload :Date, "date"
+ begin Date; rescue LoadError; end
INPUT
end
+
+ def test_non_realpath_in_loadpath
+ require 'tmpdir'
+ tmpdir = Dir.mktmpdir('autoload')
+ tmpdirs = [tmpdir]
+ tmpdirs.unshift(tmpdir + '/foo')
+ Dir.mkdir(tmpdirs[0])
+ tmpfiles = [tmpdir + '/foo.rb', tmpdir + '/foo/bar.rb']
+ open(tmpfiles[0] , 'w') do |f|
+ f.puts <<-INPUT
+$:.unshift(File.expand_path('..', __FILE__)+'/./foo')
+module Foo
+ autoload :Bar, 'bar'
+end
+p Foo::Bar
+ INPUT
+ end
+ open(tmpfiles[1], 'w') do |f|
+ f.puts 'class Foo::Bar; end'
+ end
+ assert_in_out_err([tmpfiles[0]], "", ["Foo::Bar"], [])
+ ensure
+ File.unlink(*tmpfiles) rescue nil if tmpfiles
+ tmpdirs.each {|dir| Dir.rmdir(dir)}
+ end
+
+ def test_autoload_p
+ bug4565 = '[ruby-core:35679]'
+
+ require 'tmpdir'
+ Dir.mktmpdir('autoload') {|tmpdir|
+ tmpfile = tmpdir + '/foo.rb'
+ tmpfile2 = tmpdir + '/bar.rb'
+ a = Module.new do
+ autoload :X, tmpfile
+ autoload :Y, tmpfile2
+ end
+ b = Module.new do
+ include a
+ end
+ assert_equal(true, a.const_defined?(:X))
+ assert_equal(true, b.const_defined?(:X))
+ assert_equal(tmpfile, a.autoload?(:X), bug4565)
+ assert_equal(tmpfile, b.autoload?(:X), bug4565)
+ assert_equal(true, a.const_defined?("Y"))
+ assert_equal(true, b.const_defined?("Y"))
+ assert_equal(tmpfile2, a.autoload?("Y"))
+ assert_equal(tmpfile2, b.autoload?("Y"))
+ }
+ end
+
+ def test_autoload_with_unqualified_file_name # [ruby-core:69206]
+ lp = $LOAD_PATH.dup
+ lf = $LOADED_FEATURES.dup
+
+ Dir.mktmpdir('autoload') { |tmpdir|
+ $LOAD_PATH << tmpdir
+
+ Dir.chdir(tmpdir) do
+ eval <<-END
+ class ::Object
+ module A
+ autoload :C, 'b'
+ end
+ end
+ END
+
+ File.open('b.rb', 'w') {|file| file.puts 'module A; class C; end; end'}
+ assert_kind_of Class, ::A::C
+ end
+ }
+ ensure
+ $LOAD_PATH.replace lp
+ $LOADED_FEATURES.replace lf
+ Object.send(:remove_const, :A) if Object.const_defined?(:A)
+ end
+
+ def test_require_explicit
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts 'class Object; AutoloadTest = 1; end'
+ file.close
+ add_autoload(file.path)
+ begin
+ assert_nothing_raised do
+ assert(require file.path)
+ assert_equal(1, ::AutoloadTest)
+ end
+ ensure
+ remove_autoload_constant
+ end
+ }
+ end
+
+ def test_threaded_accessing_constant
+ # Suppress "warning: loading in progress, circular require considered harmful"
+ EnvUtil.default_warning {
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts 'sleep 0.5; class AutoloadTest; X = 1; end'
+ file.close
+ add_autoload(file.path)
+ begin
+ assert_nothing_raised do
+ t1 = Thread.new { ::AutoloadTest::X }
+ t2 = Thread.new { ::AutoloadTest::X }
+ [t1, t2].each(&:join)
+ end
+ ensure
+ remove_autoload_constant
+ end
+ }
+ }
+ end
+
+ def test_threaded_accessing_inner_constant
+ # Suppress "warning: loading in progress, circular require considered harmful"
+ EnvUtil.default_warning {
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts 'class AutoloadTest; sleep 0.5; X = 1; end'
+ file.close
+ add_autoload(file.path)
+ begin
+ assert_nothing_raised do
+ t1 = Thread.new { ::AutoloadTest::X }
+ t2 = Thread.new { ::AutoloadTest::X }
+ [t1, t2].each(&:join)
+ end
+ ensure
+ remove_autoload_constant
+ end
+ }
+ }
+ end
+
+ def test_nameerror_when_autoload_did_not_define_the_constant
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts ''
+ file.close
+ add_autoload(file.path)
+ begin
+ assert_raise(NameError) do
+ AutoloadTest
+ end
+ ensure
+ remove_autoload_constant
+ end
+ }
+ end
+
+ def test_override_autoload
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts ''
+ file.close
+ add_autoload(file.path)
+ begin
+ eval %q(class AutoloadTest; end)
+ assert_equal(Class, AutoloadTest.class)
+ ensure
+ remove_autoload_constant
+ end
+ }
+ end
+
+ def test_override_while_autoloading
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts 'class AutoloadTest; sleep 0.5; end'
+ file.close
+ add_autoload(file.path)
+ begin
+ # while autoloading...
+ t = Thread.new { AutoloadTest }
+ sleep 0.1
+ # override it
+ EnvUtil.suppress_warning {
+ eval %q(AutoloadTest = 1)
+ }
+ t.join
+ assert_equal(1, AutoloadTest)
+ ensure
+ remove_autoload_constant
+ end
+ }
+ end
+
+ def ruby_impl_require
+ Kernel.module_eval do
+ alias old_require require
+ end
+ called_with = []
+ Kernel.send :define_method, :require do |path|
+ called_with << path
+ old_require path
+ end
+ yield called_with
+ ensure
+ Kernel.module_eval do
+ undef require
+ alias require old_require
+ undef old_require
+ end
+ end
+
+ def test_require_implemented_in_ruby_is_called
+ ruby_impl_require do |called_with|
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts 'class AutoloadTest; end'
+ file.close
+ add_autoload(file.path)
+ begin
+ assert(Object::AutoloadTest)
+ ensure
+ remove_autoload_constant
+ end
+ assert_equal [file.path], called_with
+ }
+ end
+ end
+
+ def test_autoload_while_autoloading
+ ruby_impl_require do |called_with|
+ Tempfile.create(%w(a .rb)) do |a|
+ Tempfile.create(%w(b .rb)) do |b|
+ a.puts "require '#{b.path}'; class AutoloadTest; end"
+ b.puts "class AutoloadTest; module B; end; end"
+ [a, b].each(&:flush)
+ add_autoload(a.path)
+ begin
+ assert(Object::AutoloadTest)
+ ensure
+ remove_autoload_constant
+ end
+ assert_equal [a.path, b.path], called_with
+ end
+ end
+ end
+ end
+
+ def test_bug_13526
+ script = File.join(__dir__, 'bug-13526.rb')
+ assert_ruby_status([script], '', '[ruby-core:81016] [Bug #13526]')
+ end
+
+ def test_autoload_private_constant
+ Dir.mktmpdir('autoload') do |tmpdir|
+ File.write(tmpdir+"/zzz.rb", "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class AutoloadTest
+ ZZZ = :ZZZ
+ private_constant :ZZZ
+ end
+ end;
+ assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-'end;'}")
+ bug = '[ruby-core:85516] [Bug #14469]'
+ begin;
+ class AutoloadTest
+ autoload :ZZZ, "zzz.rb"
+ end
+ assert_raise(NameError, bug) {AutoloadTest::ZZZ}
+ end;
+ end
+ end
+
+ def test_autoload_deprecate_constant
+ Dir.mktmpdir('autoload') do |tmpdir|
+ File.write(tmpdir+"/zzz.rb", "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class AutoloadTest
+ ZZZ = :ZZZ
+ deprecate_constant :ZZZ
+ end
+ end;
+ assert_separately(%W[-I #{tmpdir}], "#{<<-"begin;"}\n#{<<-'end;'}")
+ bug = '[ruby-core:85516] [Bug #14469]'
+ begin;
+ class AutoloadTest
+ autoload :ZZZ, "zzz.rb"
+ end
+ assert_warning(/ZZZ is deprecated/, bug) {AutoloadTest::ZZZ}
+ end;
+ end
+ end
+
+ def test_autoload_fork
+ EnvUtil.default_warning do
+ Tempfile.create(['autoload', '.rb']) {|file|
+ file.puts 'sleep 0.3; class AutoloadTest; end'
+ file.close
+ add_autoload(file.path)
+ begin
+ thrs = []
+ 3.times do
+ thrs << Thread.new { AutoloadTest; nil }
+ thrs << Thread.new { fork { AutoloadTest } }
+ end
+ thrs.each(&:join)
+ thrs.each do |th|
+ pid = th.value or next
+ _, status = Process.waitpid2(pid)
+ assert_predicate status, :success?
+ end
+ ensure
+ remove_autoload_constant
+ assert_nil $!, '[ruby-core:86410] [Bug #14634]'
+ end
+ }
+ end
+ end if Process.respond_to?(:fork)
+
+ def test_autoload_same_file
+ Dir.mktmpdir('autoload') do |tmpdir|
+ File.write("#{tmpdir}/b.rb", "#{<<~'begin;'}\n#{<<~'end;'}")
+ begin;
+ module Foo; end
+ module Bar; end
+ end;
+ 3.times do # timing-dependent, needs a few times to hit [Bug #14742]
+ assert_separately(%W[-I #{tmpdir}], "#{<<-'begin;'}\n#{<<-'end;'}")
+ begin;
+ autoload :Foo, 'b'
+ autoload :Bar, 'b'
+ t1 = Thread.new do Foo end
+ t2 = Thread.new do Bar end
+ t1.join
+ t2.join
+ bug = '[ruby-core:86935] [Bug #14742]'
+ assert_instance_of Module, t1.value, bug
+ assert_instance_of Module, t2.value, bug
+ end;
+ end
+ end
+ end
+
+ def test_no_leak
+ assert_no_memory_leak([], '', <<~'end;', 'many autoloads', timeout: 30)
+ 200000.times do |i|
+ m = Module.new
+ m.instance_eval do
+ autoload :Foo, 'x'
+ autoload :Bar, i.to_s
+ end
+ end
+ end;
+ end
+
+ def add_autoload(path)
+ (@autoload_paths ||= []) << path
+ ::Object.class_eval {autoload(:AutoloadTest, path)}
+ end
+
+ def remove_autoload_constant
+ $".replace($" - @autoload_paths)
+ ::Object.class_eval {remove_const(:AutoloadTest)}
+ end
end
diff --git a/test/ruby/test_backtrace.rb b/test/ruby/test_backtrace.rb
new file mode 100644
index 0000000000..0730b5d1c5
--- /dev/null
+++ b/test/ruby/test_backtrace.rb
@@ -0,0 +1,332 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'tempfile'
+
+class TestBacktrace < Test::Unit::TestCase
+ def test_exception
+ bt = Fiber.new{
+ begin
+ raise
+ rescue => e
+ e.backtrace
+ end
+ }.resume
+ assert_equal(1, bt.size)
+ assert_match(/.+:\d+:.+/, bt[0])
+ end
+
+ def helper_test_exception_backtrace_locations
+ raise
+ end
+
+ def test_exception_backtrace_locations
+ backtrace, backtrace_locations = Fiber.new{
+ begin
+ raise
+ rescue => e
+ [e.backtrace, e.backtrace_locations]
+ end
+ }.resume
+ assert_equal(backtrace, backtrace_locations.map{|e| e.to_s})
+
+ backtrace, backtrace_locations = Fiber.new{
+ begin
+ begin
+ helper_test_exception_backtrace_locations
+ rescue
+ raise
+ end
+ rescue => e
+ [e.backtrace, e.backtrace_locations]
+ end
+ }.resume
+ assert_equal(backtrace, backtrace_locations.map{|e| e.to_s})
+ end
+
+ def call_helper_test_exception_backtrace_locations
+ helper_test_exception_backtrace_locations(:bad_argument)
+ end
+
+ def test_argument_error_backtrace_locations
+ backtrace, backtrace_locations = Fiber.new{
+ begin
+ helper_test_exception_backtrace_locations(1)
+ rescue ArgumentError => e
+ [e.backtrace, e.backtrace_locations]
+ end
+ }.resume
+ assert_equal(backtrace, backtrace_locations.map{|e| e.to_s})
+
+ backtrace, backtrace_locations = Fiber.new{
+ begin
+ call_helper_test_exception_backtrace_locations
+ rescue ArgumentError => e
+ [e.backtrace, e.backtrace_locations]
+ end
+ }.resume
+ assert_equal(backtrace, backtrace_locations.map{|e| e.to_s})
+ end
+
+ def test_caller_lev
+ cs = []
+ Fiber.new{
+ Proc.new{
+ cs << caller(0)
+ cs << caller(1)
+ cs << caller(2)
+ cs << caller(3)
+ cs << caller(4)
+ cs << caller(5)
+ }.call
+ }.resume
+ assert_equal(2, cs[0].size)
+ assert_equal(1, cs[1].size)
+ assert_equal(0, cs[2].size)
+ assert_equal(nil, cs[3])
+ assert_equal(nil, cs[4])
+
+ #
+ max = 7
+ rec = lambda{|n|
+ if n > 0
+ 1.times{
+ rec[n-1]
+ }
+ else
+ (max*3).times{|i|
+ total_size = caller(0).size
+ c = caller(i)
+ if c
+ assert_equal(total_size - i, caller(i).size, "[ruby-dev:45673]")
+ end
+ }
+ end
+ }
+ Fiber.new{
+ rec[max]
+ }.resume
+ end
+
+ def test_caller_lev_and_n
+ m = 10
+ rec = lambda{|n|
+ if n < 0
+ (m*6).times{|lev|
+ (m*6).times{|i|
+ t = caller(0).size
+ r = caller(lev, i)
+ r = r.size if r.respond_to? :size
+
+ # STDERR.puts [t, lev, i, r].inspect
+ if i == 0
+ assert_equal(0, r, [t, lev, i, r].inspect)
+ elsif t < lev
+ assert_equal(nil, r, [t, lev, i, r].inspect)
+ else
+ if t - lev > i
+ assert_equal(i, r, [t, lev, i, r].inspect)
+ else
+ assert_equal(t - lev, r, [t, lev, i, r].inspect)
+ end
+ end
+ }
+ }
+ else
+ rec[n-1]
+ end
+ }
+ rec[m]
+ end
+
+ def test_caller_with_nil_length
+ assert_equal caller(0), caller(0, nil)
+ end
+
+ def test_caller_locations
+ cs = caller(0); locs = caller_locations(0).map{|loc|
+ loc.to_s
+ }
+ assert_equal(cs, locs)
+ end
+
+ def test_caller_locations_with_range
+ cs = caller(0,2); locs = caller_locations(0..1).map { |loc|
+ loc.to_s
+ }
+ assert_equal(cs, locs)
+ end
+
+ def test_caller_locations_to_s_inspect
+ cs = caller(0); locs = caller_locations(0)
+ cs.zip(locs){|str, loc|
+ assert_equal(str, loc.to_s)
+ assert_equal(str.inspect, loc.inspect)
+ }
+ end
+
+ def test_caller_locations_path
+ loc, = caller_locations(0, 1)
+ assert_equal(__FILE__, loc.path)
+ Tempfile.create(%w"caller_locations .rb") do |f|
+ f.puts "caller_locations(0, 1)[0].tap {|loc| puts loc.path}"
+ f.close
+ dir, base = File.split(f.path)
+ assert_in_out_err(["-C", dir, base], "", [base])
+ end
+ end
+
+ def test_caller_locations_absolute_path
+ loc, = caller_locations(0, 1)
+ assert_equal(__FILE__, loc.absolute_path)
+ Tempfile.create(%w"caller_locations .rb") do |f|
+ f.puts "caller_locations(0, 1)[0].tap {|loc| puts loc.absolute_path}"
+ f.close
+ assert_in_out_err(["-C", *File.split(f.path)], "", [File.realpath(f.path)])
+ end
+ end
+
+ def test_caller_locations_lineno
+ loc, = caller_locations(0, 1)
+ assert_equal(__LINE__-1, loc.lineno)
+ Tempfile.create(%w"caller_locations .rb") do |f|
+ f.puts "caller_locations(0, 1)[0].tap {|loc| puts loc.lineno}"
+ f.close
+ assert_in_out_err(["-C", *File.split(f.path)], "", ["1"])
+ end
+ end
+
+ def test_caller_locations_base_label
+ assert_equal("#{__method__}", caller_locations(0, 1)[0].base_label)
+ loc, = tap {break caller_locations(0, 1)}
+ assert_equal("#{__method__}", loc.base_label)
+ begin
+ raise
+ rescue
+ assert_equal("#{__method__}", caller_locations(0, 1)[0].base_label)
+ end
+ end
+
+ def test_caller_locations_label
+ assert_equal("#{__method__}", caller_locations(0, 1)[0].label)
+ loc, = tap {break caller_locations(0, 1)}
+ assert_equal("block in #{__method__}", loc.label)
+ begin
+ raise
+ rescue
+ assert_equal("rescue in #{__method__}", caller_locations(0, 1)[0].label)
+ end
+ end
+
+ def th_rec q, n=10
+ if n > 1
+ th_rec q, n-1
+ else
+ q.pop
+ end
+ end
+
+ def test_thread_backtrace
+ begin
+ q = Thread::Queue.new
+ th = Thread.new{
+ th_rec q
+ }
+ sleep 0.5
+ th_backtrace = th.backtrace
+ th_locations = th.backtrace_locations
+
+ assert_equal(10, th_backtrace.count{|e| e =~ /th_rec/})
+ assert_equal(th_backtrace, th_locations.map{|e| e.to_s})
+ assert_equal(th_backtrace, th.backtrace(0))
+ assert_equal(th_locations.map{|e| e.to_s},
+ th.backtrace_locations(0).map{|e| e.to_s})
+ th_backtrace.size.times{|n|
+ assert_equal(n, th.backtrace(0, n).size)
+ assert_equal(n, th.backtrace_locations(0, n).size)
+ }
+ n = th_backtrace.size
+ assert_equal(n, th.backtrace(0, n + 1).size)
+ assert_equal(n, th.backtrace_locations(0, n + 1).size)
+ ensure
+ q << true
+ th.join
+ end
+ end
+
+ def test_thread_backtrace_locations_with_range
+ begin
+ q = Thread::Queue.new
+ th = Thread.new{
+ th_rec q
+ }
+ sleep 0.5
+ bt = th.backtrace(0,2)
+ locs = th.backtrace_locations(0..1).map { |loc|
+ loc.to_s
+ }
+ assert_equal(bt, locs)
+ ensure
+ q << true
+ th.join
+ end
+ end
+
+ def test_core_backtrace_alias
+ obj = BasicObject.new
+ e = assert_raise(NameError) do
+ class << obj
+ alias foo bar
+ end
+ end
+ assert_not_match(/\Acore#/, e.backtrace_locations[0].base_label)
+ end
+
+ def test_core_backtrace_undef
+ obj = BasicObject.new
+ e = assert_raise(NameError) do
+ class << obj
+ undef foo
+ end
+ end
+ assert_not_match(/\Acore#/, e.backtrace_locations[0].base_label)
+ end
+
+ def test_core_backtrace_hash_merge
+ e = assert_raise(TypeError) do
+ {**nil}
+ end
+ assert_not_match(/\Acore#/, e.backtrace_locations[0].base_label)
+ end
+
+ def test_notty_backtrace
+ err = ["-:1:in `<main>': unhandled exception"]
+ assert_in_out_err([], "raise", [], err)
+
+ err = ["-:2:in `foo': foo! (RuntimeError)",
+ "\tfrom -:4:in `<main>'"]
+ assert_in_out_err([], <<-"end;", [], err)
+ def foo
+ raise "foo!"
+ end
+ foo
+ end;
+
+ err = ["-:7:in `rescue in bar': bar! (RuntimeError)",
+ "\tfrom -:4:in `bar'",
+ "\tfrom -:9:in `<main>'",
+ "-:2:in `foo': foo! (RuntimeError)",
+ "\tfrom -:5:in `bar'",
+ "\tfrom -:9:in `<main>'"]
+ assert_in_out_err([], <<-"end;", [], err)
+ def foo
+ raise "foo!"
+ end
+ def bar
+ foo
+ rescue
+ raise "bar!"
+ end
+ bar
+ end;
+ end
+end
diff --git a/test/ruby/test_basicinstructions.rb b/test/ruby/test_basicinstructions.rb
index ff14e4a7a6..ab32ee54e2 100644
--- a/test/ruby/test_basicinstructions.rb
+++ b/test/ruby/test_basicinstructions.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
ConstTest = 3
@@ -85,7 +86,9 @@ class TestBasicInstructions < Test::Unit::TestCase
s = "OK"
prev = nil
3.times do
- assert_equal prev.object_id, (prev ||= /#{s}/o).object_id if prev
+ re = /#{s}/o
+ assert_same prev, re if prev
+ prev = re
end
end
@@ -114,7 +117,6 @@ class TestBasicInstructions < Test::Unit::TestCase
assert_equal({1=>2}, {1=>2})
assert_equal({1=>2, 3=>4}, {1=>2, 3=>4})
assert_equal({1=>2, 3=>4}, {3=>4, 1=>2})
- # assert_equal({1=>2, 3=>4}, {1,2, 3,4}) # 1.9 doesn't support
assert_equal({"key"=>"val"}, {"key"=>"val"})
end
@@ -208,9 +210,9 @@ class TestBasicInstructions < Test::Unit::TestCase
assert_raise(NameError) { a }
assert_raise(NameError) { b }
assert_raise(NameError) { c }
- a = "NOT OK"
- b = "NOT OK"
- c = "NOT OK"
+ a = a = "NOT OK"
+ b = b = "NOT OK"
+ c = c = "NOT OK"
end
class Const
@@ -500,6 +502,7 @@ class TestBasicInstructions < Test::Unit::TestCase
class OP
attr_reader :x
+ attr_accessor :foo
def x=(x)
@x = x
:Bug1996
@@ -600,6 +603,19 @@ class TestBasicInstructions < Test::Unit::TestCase
assert_equal 4, x[0]
end
+ def test_send_opassign
+ return if defined?(RUBY_ENGINE) and RUBY_ENGINE != "ruby"
+
+ bug7773 = '[ruby-core:51821]'
+ x = OP.new
+ assert_equal 42, x.foo = 42, bug7773
+ assert_equal 42, x.foo, bug7773
+ assert_equal (-6), x.send(:foo=, -6), bug7773
+ assert_equal (-6), x.foo, bug7773
+ assert_equal :Bug1996, x.send(:x=, :case_when_setter_returns_other_value), bug7773
+ assert_equal :case_when_setter_returns_other_value, x.x, bug7773
+ end
+
def test_backref
/re/ =~ 'not match'
assert_nil $~
@@ -632,7 +648,7 @@ class TestBasicInstructions < Test::Unit::TestCase
assert_equal 'i', $~[9]
assert_equal 'x', $`
assert_equal 'abcdefghi', $&
- assert_equal 'y', $'
+ assert_equal "y", $'
assert_equal 'i', $+
assert_equal 'a', $1
assert_equal 'b', $2
@@ -662,19 +678,45 @@ class TestBasicInstructions < Test::Unit::TestCase
end
def test_array_splat
+ feature1125 = '[ruby-core:21901]'
+
a = []
assert_equal [], [*a]
assert_equal [1], [1, *a]
+ assert_not_same(a, [*a], feature1125)
a = [2]
assert_equal [2], [*a]
assert_equal [1, 2], [1, *a]
+ assert_not_same(a, [*a], feature1125)
a = [2, 3]
assert_equal [2, 3], [*a]
assert_equal [1, 2, 3], [1, *a]
+ assert_not_same(a, [*a], feature1125)
a = nil
assert_equal [], [*a]
assert_equal [1], [1, *a]
end
+ def test_special_const_instance_variables
+ assert_separately(%w(-W0), <<-INPUT, timeout: 60)
+ module M
+ def get
+ # we can not set instance variables on special const objects.
+ # However, we can access instance variables with default value (nil).
+ @ivar
+ end
+ end
+ class Integer; include M; end
+ class Float; include M; end
+ class Symbol; include M; end
+ class FalseClass; include M; end
+ class TrueClass; include M; end
+ class NilClass; include M; end
+
+ [123, 1.2, :sym, false, true, nil].each{|obj|
+ assert_equal(nil, obj.get)
+ }
+ INPUT
+ end
end
diff --git a/test/ruby/test_beginendblock.rb b/test/ruby/test_beginendblock.rb
index 0540f5df1c..eb8394864f 100644
--- a/test/ruby/test_beginendblock.rb
+++ b/test/ruby/test_beginendblock.rb
@@ -1,127 +1,179 @@
+# frozen_string_literal: false
require 'test/unit'
-require 'tempfile'
-require 'timeout'
-require_relative 'envutil'
class TestBeginEndBlock < Test::Unit::TestCase
DIR = File.dirname(File.expand_path(__FILE__))
- def q(content)
- "\"#{content}\""
- end
-
def test_beginendblock
- ruby = EnvUtil.rubybin
target = File.join(DIR, 'beginmainend.rb')
- result = IO.popen([ruby, target]){|io|io.read}
- assert_equal(%w(b1 b2-1 b2 main b3-1 b3 b4 e1 e4 e3 e2 e4-2 e4-1 e1-1 e4-1-1), result.split)
-
- input = Tempfile.new(self.class.name)
- inputpath = input.path
- input.close
- result = IO.popen([ruby, "-n", "-eBEGIN{p :begin}", "-eEND{p :end}", inputpath]){|io|io.read}
- assert_equal(%w(:begin), result.split)
- result = IO.popen([ruby, "-p", "-eBEGIN{p :begin}", "-eEND{p :end}", inputpath]){|io|io.read}
- assert_equal(%w(:begin), result.split)
- input.open
- input.puts "foo\nbar"
- input.close
- result = IO.popen([ruby, "-n", "-eBEGIN{p :begin}", "-eEND{p :end}", inputpath]){|io|io.read}
- assert_equal(%w(:begin :end), result.split)
- result = IO.popen([ruby, "-p", "-eBEGIN{p :begin}", "-eEND{p :end}", inputpath]){|io|io.read}
- assert_equal(%w(:begin foo bar :end), result.split)
+ assert_in_out_err([target], '', %w(b1 b2-1 b2 main b3-1 b3 b4 e1 e1-1 e4 e4-2 e4-1 e4-1-1 e3 e2))
+
+ assert_in_out_err(["-n", "-eBEGIN{p :begin}", "-eEND{p :end}"], '', %w(:begin))
+ assert_in_out_err(["-p", "-eBEGIN{p :begin}", "-eEND{p :end}"], '', %w(:begin))
+ assert_in_out_err(["-n", "-eBEGIN{p :begin}", "-eEND{p :end}"], "foo\nbar\n", %w(:begin :end))
+ assert_in_out_err(["-p", "-eBEGIN{p :begin}", "-eEND{p :end}"], "foo\nbar\n", %w(:begin foo bar :end))
end
def test_begininmethod
- assert_raise(SyntaxError) do
+ assert_raise_with_message(SyntaxError, /BEGIN is permitted only at toplevel/) do
eval("def foo; BEGIN {}; end")
end
- assert_raise(SyntaxError) do
+ assert_raise_with_message(SyntaxError, /BEGIN is permitted only at toplevel/) do
eval('eval("def foo; BEGIN {}; end")')
end
end
def test_begininclass
- assert_raise(SyntaxError) do
+ assert_raise_with_message(SyntaxError, /BEGIN is permitted only at toplevel/) do
eval("class TestBeginEndBlock; BEGIN {}; end")
end
end
def test_endblockwarn
- ruby = EnvUtil.rubybin
- # Use Tempfile to create temporary file path.
- launcher = Tempfile.new(self.class.name)
- errout = Tempfile.new(self.class.name)
-
- launcher << <<EOF
-# -*- coding: #{ruby.encoding.name} -*-
-errout = ARGV.shift
-STDERR.reopen(File.open(errout, "w"))
-STDERR.sync = true
-Dir.chdir(#{q(DIR)})
-system("#{ruby}", "endblockwarn_rb")
-EOF
- launcher.close
- launcherpath = launcher.path
- errout.close
- erroutpath = errout.path
- system(ruby, launcherpath, erroutpath)
- expected = <<EOW
-endblockwarn_rb:2: warning: END in method; use at_exit
-(eval):2: warning: END in method; use at_exit
-EOW
- assert_equal(expected, File.read(erroutpath))
- # expecting Tempfile to unlink launcher and errout file.
+ assert_in_out_err([], "#{<<~"begin;"}#{<<~'end;'}", [], ['-:2: warning: END in method; use at_exit'])
+ begin;
+ def end1
+ END {}
+ end
+ end;
+ end
+
+ def test_endblockwarn_in_eval
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", [], ['(eval):2: warning: END in method; use at_exit'])
+ begin;
+ eval <<-EOE
+ def end2
+ END {}
+ end
+ EOE
+ end;
end
def test_raise_in_at_exit
- ruby = EnvUtil.rubybin
- out = IO.popen([ruby, '-e', 'STDERR.reopen(STDOUT)',
- '-e', 'at_exit{raise %[SomethingBad]}',
- '-e', 'raise %[SomethingElse]']) {|f|
- f.read
- }
- assert_match(/SomethingBad/, out, "[ruby-core:9675]")
- assert_match(/SomethingElse/, out, "[ruby-core:9675]")
+ args = ['-e', 'at_exit{raise %[SomethingBad]}',
+ '-e', 'raise %[SomethingElse]']
+ expected = [:*, /SomethingBad/, :*, /SomethingElse/, :*]
+ status = assert_in_out_err(args, '', [], expected, "[ruby-core:9675]")
+ assert_not_predicate(status, :success?)
+ end
+
+ def test_exitcode_in_at_exit
+ bug8501 = '[ruby-core:55365] [Bug #8501]'
+ args = ['-e', 'o = Object.new; def o.inspect; raise "[Bug #8501]"; end',
+ '-e', 'at_exit{o.nope}']
+ status = assert_in_out_err(args, '', [], /undefined method `nope'/, bug8501)
+ assert_not_predicate(status, :success?, bug8501)
end
- def test_should_propagate_exit_code
+ def test_propagate_exit_code
ruby = EnvUtil.rubybin
assert_equal false, system(ruby, '-e', 'at_exit{exit 2}')
assert_equal 2, $?.exitstatus
assert_nil $?.termsig
end
- def test_should_propagate_signaled
- ruby = EnvUtil.rubybin
- out = IO.popen(
- [ruby,
- '-e', 'STDERR.reopen(STDOUT)',
- '-e', 'at_exit{Process.kill(:INT, $$); sleep 5 }']) {|f|
- timeout(10) {
- f.read
- }
- }
- assert_match(/Interrupt$/, out)
+ def test_propagate_signaled
+ status = assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", [], /Interrupt$/)
+ begin;
+ trap(:INT, "DEFAULT")
+ at_exit{Process.kill(:INT, $$)}
+ end;
Process.kill(0, 0) rescue return # check if signal works
- assert_nil $?.exitstatus
- assert_equal Signal.list["INT"], $?.termsig
+ assert_nil status.exitstatus
+ assert_equal Signal.list["INT"], status.termsig
end
def test_endblock_raise
- ruby = EnvUtil.rubybin
- out = IO.popen(
- [ruby,
- '-e', 'class C; def write(x); puts x; STDOUT.flush; sleep 0.01; end; end',
- '-e', '$stderr = C.new',
- '-e', 'END {raise "e1"}; END {puts "e2"}',
- '-e', 'END {raise "e3"}; END {puts "e4"}',
- '-e', 'END {raise "e5"}; END {puts "e6"}']) {|f|
- Thread.new {sleep 5; Process.kill :KILL, f.pid}
- f.read
- }
- assert_match(/e1/, out)
- assert_match(/e6/, out)
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", %w(e6 e4 e2), [:*, /e5/, :*, /e3/, :*, /e1/, :*])
+ begin;
+ END {raise "e1"}; END {puts "e2"}
+ END {raise "e3"}; END {puts "e4"}
+ END {raise "e5"}; END {puts "e6"}
+ end;
+ end
+
+ def test_nested_at_exit
+ expected = [ "outer3",
+ "outer2_begin",
+ "outer2_end",
+ "inner2",
+ "outer1_begin",
+ "outer1_end",
+ "inner1",
+ "outer0" ]
+
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", expected, [], "[ruby-core:35237]")
+ begin;
+ at_exit { puts :outer0 }
+ at_exit { puts :outer1_begin; at_exit { puts :inner1 }; puts :outer1_end }
+ at_exit { puts :outer2_begin; at_exit { puts :inner2 }; puts :outer2_end }
+ at_exit { puts :outer3 }
+ end;
+ end
+
+ def test_rescue_at_exit
+ bug5218 = '[ruby-core:43173][Bug #5218]'
+ cmd = [
+ "raise 'X' rescue nil",
+ "nil",
+ "exit(42)",
+ ]
+ %w[at_exit END].each do |ex|
+ out, err, status = EnvUtil.invoke_ruby(cmd.map {|s|["-e", "#{ex} {#{s}}"]}.flatten, "", true, true)
+ assert_equal(["", "", 42], [out, err, status.exitstatus], "#{bug5218}: #{ex}")
+ end
+ end
+
+ def test_callcc_at_exit
+ bug9110 = '[ruby-core:58329][Bug #9110]'
+ assert_ruby_status([], "#{<<~"begin;"}\n#{<<~'end;'}", bug9110)
+ begin;
+ require "continuation"
+ c = nil
+ at_exit { c.call }
+ at_exit { callcc {|_c| c = _c } }
+ end;
+ end
+
+ def test_errinfo_at_exit
+ bug12302 = '[ruby-core:75038] [Bug #12302]'
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", %w[2:exit 1:exit], [], bug12302)
+ begin;
+ at_exit do
+ puts "1:#{$!}"
+ end
+
+ at_exit do
+ puts "2:#{$!}"
+ raise 'x' rescue nil
+ end
+
+ at_exit do
+ exit
+ end
+ end;
+ end
+
+ if defined?(fork)
+ def test_internal_errinfo_at_exit
+ # TODO: use other than break-in-fork to throw an internal
+ # error info.
+ error, pid, status = IO.pipe do |r, w|
+ pid = fork do
+ r.close
+ STDERR.reopen(w)
+ at_exit do
+ $!.class
+ end
+ break
+ end
+ w.close
+ [r.read, *Process.wait2(pid)]
+ end
+ assert_not_predicate(status, :success?)
+ assert_not_predicate(status, :signaled?)
+ assert_match(/unexpected break/, error)
+ end
end
end
diff --git a/test/ruby/test_bignum.rb b/test/ruby/test_bignum.rb
index 5cff48c4d7..58d63a7c29 100644
--- a/test/ruby/test_bignum.rb
+++ b/test/ruby/test_bignum.rb
@@ -1,12 +1,45 @@
+# frozen_string_literal: false
require 'test/unit'
+begin
+ require '-test-/integer'
+rescue LoadError
+else
class TestBignum < Test::Unit::TestCase
+ FIXNUM_MIN = RbConfig::LIMITS['FIXNUM_MIN']
+ FIXNUM_MAX = RbConfig::LIMITS['FIXNUM_MAX']
+
+ BIGNUM_MIN = FIXNUM_MAX + 1
+ b = BIGNUM_MIN
+
+ f = BIGNUM_MIN
+ n = 0
+ until f == 0
+ f >>= 1
+ n += 1
+ 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
+
def setup
@verbose = $VERBOSE
$VERBOSE = nil
@fmax = Float::MAX.to_i
@fmax2 = @fmax * 2
- @big = (1 << 63) - 1
+ @big = (1 << BIGNUM_MIN_BITS) - 1
end
def teardown
@@ -23,20 +56,37 @@ class TestBignum < Test::Unit::TestCase
return f
end
+ def test_prepare
+ assert_bignum(@big)
+ assert_bignum(T_ZERO)
+ assert_bignum(T_ONE)
+ assert_bignum(T_MONE)
+ assert_bignum(T31)
+ assert_bignum(T31P)
+ assert_bignum(T32)
+ assert_bignum(T32P)
+ assert_bignum(T64)
+ assert_bignum(T64P)
+ assert_bignum(T1024)
+ assert_bignum(T1024P)
+ end
+
def test_bignum
$x = fact(40)
assert_equal($x, $x)
assert_equal($x, fact(40))
- assert($x < $x+2)
- assert($x > $x-2)
+ assert_operator($x, :<, $x+2)
+ assert_operator($x, :>, $x-2)
assert_equal(815915283247897734345611269596115894272000000000, $x)
assert_not_equal(815915283247897734345611269596115894272000000001, $x)
assert_equal(815915283247897734345611269596115894272000000001, $x+1)
assert_equal(335367096786357081410764800000, $x/fact(20))
$x = -$x
assert_equal(-815915283247897734345611269596115894272000000000, $x)
- assert_equal(2-(2**32), -(2**32-2))
- assert_equal(2**32 - 5, (2**32-3)-2)
+
+ b = 2*BIGNUM_MIN
+ assert_equal(2-b, -(b-2))
+ assert_equal(b - 5, (b-3)-2)
for i in 1000..1014
assert_equal(2 ** i, 1 << i)
@@ -106,19 +156,10 @@ class TestBignum < Test::Unit::TestCase
assert_equal("nd075ib45k86g" ,18446744073709551616.to_s(31), "[ruby-core:10686]")
assert_equal("1777777777777777777777" ,18446744073709551615.to_s(8))
assert_equal("-1777777777777777777777" ,-18446744073709551615.to_s(8))
+ assert_match(/\A10{99}1\z/, (10**100+1).to_s)
+ assert_match(/\A10{900}9{100}\z/, (10**1000+(10**100-1)).to_s)
end
-
- T_ZERO = (2**32).coerce(0).first
- T_ONE = (2**32).coerce(1).first
- T_MONE = (2**32).coerce(-1).first
- T31 = 2**31 # 2147483648
- T31P = T31 - 1 # 2147483647
- T32 = 2**32 # 4294967296
- T32P = T32 - 1 # 4294967295
- T64 = 2**64 # 18446744073709551616
- T64P = T64 - 1 # 18446744073709551615
-
def test_big_2comp
assert_equal("-4294967296", (~T32P).to_s)
assert_equal("..f00000000", "%x" % -T32)
@@ -180,21 +221,24 @@ class TestBignum < Test::Unit::TestCase
end
def test_cmp
- assert(T31P > 1)
- assert(T31P < 2147483648.0)
- assert(T31P < T64P)
- assert(T64P > T31P)
+ assert_operator(T31P, :>, 1)
+ assert_operator(T31P, :<, 2147483648.0)
+ assert_operator(T31P, :<, T64P)
+ assert_operator(T64P, :>, T31P)
assert_raise(ArgumentError) { T31P < "foo" }
+ assert_operator(T64, :<, (1.0/0.0))
+ assert_not_operator(T64, :>, (1.0/0.0))
end
def test_eq
- assert(T31P != 1)
- assert(T31P == 2147483647.0)
- assert(T31P != "foo")
+ assert_not_equal(T31P, 1)
+ assert_equal(T31P, 2147483647.0)
+ assert_not_equal(T31P, "foo")
+ assert_not_equal(2**77889, (1.0/0.0), '[ruby-core:31603]')
end
def test_eql
- assert(T31P.eql?(T31P))
+ assert_send([T31P, :eql?, T31P])
end
def test_convert
@@ -239,18 +283,149 @@ class TestBignum < Test::Unit::TestCase
assert_equal(2**180, (2**80) * o)
end
+ def test_positive_p
+ assert_predicate(T_ONE, :positive?)
+ assert_not_predicate(T_MONE, :positive?)
+ assert_not_predicate(T_ZERO, :positive?)
+ end
+
+ def test_negative_p
+ assert_not_predicate(T_ONE, :negative?)
+ assert_predicate(T_MONE, :negative?)
+ assert_not_predicate(T_ZERO, :negative?)
+ end
+
def test_mul_balance
assert_equal(3**7000, (3**5000) * (3**2000))
end
+ def test_mul_large_numbers
+ a = %w[
+ 32580286268570032115047167942578356789222410206194227403993117616454027392
+ 62501901985861926098797067562795526004375784403965882943322008991129440928
+ 33855888840298794008677656280486901895499985197580043127115026675632969396
+ 55040226415022070581995493731570435346323030715226718346725312551631168110
+ 83966158581772380474470605428802018934282425947323171408377505151988776271
+ 85865548747366001752375899635539662017095652855537225416899242508164949615
+ 96848508410008685252121247181772953744297349638273854170932226446528911938
+ 03430429031094465344063914822790537339912760237589085026016396616506014081
+ 53557719631183538265614091691713138728177917059624255801026099255450058876
+ 97412698978242128457751836011774504753020608663272925708049430557191193188
+ 23212591809241860763625985763438355314593186083254640117460724730431447842
+ 15432124830037389073162094304199742919767272162759192882136828372588787906
+ 96027938532441670018954643423581446981760344524184231299785949158765352788
+ 38452309862972527623669323263424418781899966895996672291193305401609553502
+ 63893514163147729201340204483973131948541009975283778189609285614445485714
+ 63843850089417416331356938086609682943037801440660232801570877143192251897
+ 63026816485314923378023904237699794122181407920355722922555234540701118607
+ 37971417665315821995516986204709574657462370947443531049033704997194647442
+ 13711787319587466437795542850136751816475182349380345341647976135081955799
+ 56787050815348701001765730577514591032367920292271016649813170789854524395
+ 72571698998841196411826453893352760318867994518757872432266374568779920489
+ 55597104558927387008506485038236352630863481679853742412042588244086070827
+ 43705456833283086410967648483312972903432798923897357373793064381177468258
+ 69131640408147806442422254638590386673344704147156793990832671592488742473
+ 31524606724894164324227362735271650556732855509929890983919463699819116427
+ ].join.to_i
+ b = %w[
+ 31519454770031243652776765515030872050264386564379909299874378289835540661
+ 99756262835346828114038365624177182230027040172583473561802565238817167503
+ 85144159132462819032164726177606533272071955542237648482852154879445654746
+ 25061253606344846225905712926863168413666058602449408307586532461776530803
+ 56810626880722653177544008166119272373179841889454920521993413902672848145
+ 77974951972342194855267960390195830413354782136431833731467699250684103370
+ 98571305167189174270854698169136844578685346745340041520068176478277580590
+ 43810457765638903028049263788987034217272442328962400931269515791911786205
+ 15357047519615932249418012945178659435259428163356223753159488306813844040
+ 93609959555018799309373542926110109744437994067754004273450659607204900586
+ 28878103661124568217617766580438460505513654179249613168352070584906185237
+ 34829991855182473813233425492094534396541544295119674419522772382981982574
+ 64708442087451070125274285088681225122475041996116377707892328889948526913
+ 82239084041628877737628853240361038273348062246951097300286513836140601495
+ 63604611754185656404194406869925540477185577643853560887894081047256701731
+ 66884554460428760857958761948461476977864005799494946578017758268987123749
+ 85937011490156431231903167442071541493304390639100774497107347884381581049
+ 85451663323551635322518839895028929788021096587229364219084708576998525298
+ 39594168681411529110089531428721005176467479027585291807482375043729783455
+ 35827667428080449919778142400266842990117940984804919512360370451936835708
+ 76338722049621773169385978521438867493162717866679193103745711403152099047
+ 27294943901673885707639094215339506973982546487889199083181789561917985023
+ 82368442718514694400160954955539704757794969665555505203532944598698824542
+ 00599461848630034847211204029842422678421808487300084850702007663003230882
+ 16645745324467830796203354080471008809087072562876681588151822072260738003
+ ].join.to_i
+ c = %w[
+ 10269128594368631269792194698469828812223242061960065022209211719149714886
+ 03494742299892841188636314745174778237781513956755034582435818316155459882
+ 71422025990633195596790290038198841087091600598192959108790192789550336119
+ 13849937951116346796903163312950010689963716629093190601532313463306463573
+ 64436438673379454947908896258675634478867189655764364639888427350090856831
+ 84369949421175534994092429682748078316130135651006102162888937624830856951
+ 64818150356583421988135211585954838926347035741143424980258821170351244310
+ 33072045488402539147707418016613224788469923473310249137422855065567940804
+ 75231970365923936034328561426062696074717204901606475826224235014948198414
+ 19979210494282212322919438926816203585575357874850252052656098969732107129
+ 30639419804565653489687198910271702181183420960744232756057631336661646896
+ 48734093497394719644969417287962767186599484579769717220518657324467736902
+ 16947995288312851432262922140679347615046098863974141226499783975470926697
+ 95970415188661518504275964397022973192968233221707696639386238428211541334
+ 69925631385166494600401675904803418143232703594169525858261988389529181035
+ 06048776134746377586210180203524132714354779486439559392942733781343640971
+ 02430607931736785273011780813863748280091795277451796799961887248262211653
+ 38966967509803488282644299584920109534552889962877144862747797551711984992
+ 00726518175235286668236031649728858774545087668286506201943248842967749907
+ 05345423019480534625965140632428736051632750698608916592720742728646191514
+ 86268964807395494825321744802493138032936406889713953832376411900451422777
+ 06372983421062172556566901346288286168790235741528630664513209619789835729
+ 36999522461733403414326366959273556098219489572448083984779946889707480205
+ 42459898495081687425132939473146331452400120169525968892769310016015870148
+ 66821361032541586130017904207971120217385522074967066199941112154460026348
+ 07223950375610474071278649031647998546085807777970592429037128484222394216
+ 33776560239741740193444702279919018283324070210090106960567819910943036248
+ 16660475627526085805165023447934326510232828674828006752369603151390527384
+ 16810180735871644266726954590262010744712519045524839388305761859432443670
+ 05188791334908140831469790180096209292338569623252372975043915954675335333
+ 66614002146554533771788633057869340167604765688639181655208751680821446276
+ 75871494160208888666798836473728725968253820774671626436794492530356258709
+ 62318715778035246655925307167306434486713879511272648637608703497794724929
+ 54912261106702913491290913962825303534484477936036071463820553314826894581
+ 36951927032835690160443252405644718368516656317176848748544135126122940034
+ 68454782581240953957381976073459570718038035358630417744490242611126043987
+ 89191812971310096496208294948623403471433467614886863238916702384858514703
+ 24327715474804343531844042107910755966152655912676456945146277848606406879
+ 49724219295823540160221752189725460676360350860849986313532861445465771187
+ 86822806696323658053947125253562001971534265078959827450518368635828010637
+ 91977444206363529864361796188661941906329947840521598310396004328950804758
+ 79728679236044038853668859284513594307352133390781441610395116807369310560
+ 35193762565748328526426224069629084264376146174383444988110993194030351064
+ 29660536743256949099972314033972121470913480844652490838985461134989129492
+ 75577567064571716731774820127381261057956083604361635892088585967074514802
+ 51958582645785905276289980534832170529946494815794770854644518463332458915
+ 77572397432680871220602513555535017751714443325264019171753694163676670792
+ 04353584782364068773777058727187323211012094819929720407636607815292764459
+ 21851731257845562153822058534043916834839514338448582518847879059020959697
+ 90538105704766415685100946308842788321400392381169436435078204622400475281
+ ].join.to_i
+ assert_equal(c, a*b, '[ruby-core:48552]')
+ end
+
def test_divrem
assert_equal(0, T32 / T64)
end
+ def test_divide
+ bug5490 = '[ruby-core:40429]'
+ assert_raise(ZeroDivisionError, bug5490) {T1024./(0)}
+ assert_equal(Float::INFINITY, T1024./(0.0), bug5490)
+ end
+
def test_div
assert_equal(T32.to_f, T32 / 1.0)
assert_raise(TypeError) { T32 / "foo" }
assert_equal(0x20000000, 0x40000001.div(2.0), "[ruby-dev:34553]")
+ bug5490 = '[ruby-core:40429]'
+ assert_raise(ZeroDivisionError, bug5490) {T1024.div(0)}
+ assert_raise(ZeroDivisionError, bug5490) {T1024.div(0.0)}
end
def test_idiv
@@ -273,6 +448,8 @@ class TestBignum < Test::Unit::TestCase
end
def test_quo
+ assert_kind_of(Float, T32.quo(1.0))
+
assert_equal(T32.to_f, T32.quo(1))
assert_equal(T32.to_f, T32.quo(1.0))
assert_equal(T32.to_f, T32.quo(T_ONE))
@@ -280,11 +457,11 @@ class TestBignum < Test::Unit::TestCase
assert_raise(TypeError) { T32.quo("foo") }
assert_equal(1024**1024, (1024**1024).quo(1))
- assert_equal(1024**1024, (1024**1024).quo(1.0))
+ assert_equal(Float::INFINITY, (1024**1024).quo(1.0))
assert_equal(1024**1024*2, (1024**1024*2).quo(1))
inf = 1 / 0.0; nan = inf / inf
- assert((1024**1024*2).quo(nan).nan?)
+ assert_send([(1024**1024*2).quo(nan), :nan?])
end
def test_pow
@@ -296,6 +473,9 @@ class TestBignum < Test::Unit::TestCase
### rational changes the behavior of Bignum#**
#assert_raise(TypeError) { T32**"foo" }
assert_raise(TypeError, ArgumentError) { T32**"foo" }
+
+ feature3429 = '[ruby-core:30735]'
+ assert_kind_of(Integer, (2 ** 7830457), feature3429)
end
def test_and
@@ -309,6 +489,7 @@ class TestBignum < Test::Unit::TestCase
assert_equal(T32 + T31, T32 | T31)
assert_equal(-T31, (-T32) | (-T31))
assert_equal(T64 + T32, T32 | T64)
+ assert_equal(FIXNUM_MAX, T_ZERO | FIXNUM_MAX)
end
def test_xor
@@ -318,34 +499,83 @@ class TestBignum < Test::Unit::TestCase
assert_equal(T64 + T32, T32 ^ T64)
end
+ class DummyNumeric < Numeric
+ def to_int
+ 1
+ end
+ end
+
+ def test_and_with_float
+ assert_raise(TypeError) { T1024 & 1.5 }
+ end
+
+ def test_and_with_rational
+ assert_raise(TypeError, "#1792") { T1024 & Rational(3, 2) }
+ end
+
+ def test_and_with_nonintegral_numeric
+ assert_raise(TypeError, "#1792") { T1024 & DummyNumeric.new }
+ end
+
+ def test_or_with_float
+ assert_raise(TypeError) { T1024 | 1.5 }
+ end
+
+ def test_or_with_rational
+ assert_raise(TypeError, "#1792") { T1024 | Rational(3, 2) }
+ end
+
+ def test_or_with_nonintegral_numeric
+ assert_raise(TypeError, "#1792") { T1024 | DummyNumeric.new }
+ end
+
+ def test_xor_with_float
+ assert_raise(TypeError) { T1024 ^ 1.5 }
+ end
+
+ def test_xor_with_rational
+ assert_raise(TypeError, "#1792") { T1024 ^ Rational(3, 2) }
+ end
+
+ def test_xor_with_nonintegral_numeric
+ assert_raise(TypeError, "#1792") { T1024 ^ DummyNumeric.new }
+ end
+
def test_shift2
- assert_equal(2**33, (2**32) << 1)
- assert_equal(2**31, (2**32) << -1)
- assert_equal(2**33, (2**32) << 1.0)
- assert_equal(2**31, (2**32) << -1.0)
- assert_equal(2**33, (2**32) << T_ONE)
- assert_equal(2**31, (2**32) << T_MONE)
- assert_equal(2**31, (2**32) >> 1)
- assert_equal(2**33, (2**32) >> -1)
- assert_equal(2**31, (2**32) >> 1.0)
- assert_equal(2**33, (2**32) >> -1.0)
- assert_equal(2**31, (2**32) >> T_ONE)
- assert_equal(2**33, (2**32) >> T_MONE)
- assert_equal( 0, (2**32) >> (2**32))
- assert_equal(-1, -(2**32) >> (2**32))
- assert_equal( 0, (2**32) >> 128)
- assert_equal(-1, -(2**32) >> 128)
- assert_equal( 0, (2**31) >> 32)
- assert_equal(-1, -(2**31) >> 32)
+ b = BIGNUM_MIN_BITS
+ n = BIGNUM_MIN << 1
+ assert_equal(2**(b+1), n << 1)
+ assert_equal(2**(b-1), n << -1)
+ assert_equal(2**(b+1), n << 1.0)
+ assert_equal(2**(b-1), n << -1.0)
+ assert_equal(2**(b+1), n << T_ONE)
+ assert_equal(2**(b-1), n << T_MONE)
+ assert_equal(2**(b-1), n >> 1)
+ assert_equal(2**(b+1), n >> -1)
+ assert_equal(2**(b-1), n >> 1.0)
+ assert_equal(2**(b+1), n >> -1.0)
+ assert_equal(2**(b-1), n >> T_ONE)
+ assert_equal(2**(b+1), n >> T_MONE)
+ assert_equal( 0, n >> n)
+ assert_equal(-1, -n >> n)
+ assert_equal( 0, n >> (b*4))
+ assert_equal(-1, -n >> (b*4))
+ assert_equal( 0, (n/2) >> b)
+ assert_equal(-1, -(n/2) >> b)
+ end
+
+ def test_shift_bigshift
+ big = 2**300
+ assert_equal(2**65538 / (2**65537), 2**65538 >> big.coerce(65537).first)
end
def test_aref
- assert_equal(0, (2**32)[0])
- assert_equal(0, (2**32)[2**32])
- assert_equal(0, (2**32)[-(2**32)])
- assert_equal(0, (2**32)[T_ZERO])
- assert_equal(0, (-(2**64))[0])
- assert_equal(1, (-2**256)[256])
+ assert_equal(0, BIGNUM_MIN[0])
+ assert_equal(0, BIGNUM_MIN[BIGNUM_MIN])
+ assert_equal(0, BIGNUM_MIN[-BIGNUM_MIN])
+ assert_equal(0, BIGNUM_MIN[T_ZERO])
+ assert_equal(0, (-(BIGNUM_MIN*BIGNUM_MIN))[0])
+ assert_equal(1, (-2**(BIGNUM_MIN_BITS*4))[BIGNUM_MIN_BITS*4])
end
def test_hash
@@ -355,6 +585,8 @@ class TestBignum < Test::Unit::TestCase
def test_coerce
assert_equal([T64P, T31P], T31P.coerce(T64P))
assert_raise(TypeError) { T31P.coerce(nil) }
+ obj = eval("class C\u{1f5ff}; self; end").new
+ assert_raise_with_message(TypeError, /C\u{1f5ff}/) { T31P.coerce(obj) }
end
def test_abs
@@ -362,44 +594,81 @@ class TestBignum < Test::Unit::TestCase
end
def test_size
- assert(T31P.size.is_a?(Integer))
+ assert_kind_of(Integer, T31P.size)
end
def test_odd
- assert_equal(true, (2**32+1).odd?)
- assert_equal(false, (2**32).odd?)
+ assert_equal(true, (BIGNUM_MIN+1).odd?)
+ assert_equal(false, BIGNUM_MIN.odd?)
end
def test_even
- assert_equal(false, (2**32+1).even?)
- assert_equal(true, (2**32).even?)
+ assert_equal(false, (BIGNUM_MIN+1).even?)
+ assert_equal(true, BIGNUM_MIN.even?)
end
- def interrupt
+ def test_interrupt_during_to_s
+ if defined?(Integer::GMP_VERSION)
+ return # GMP doesn't support interrupt during an operation.
+ end
time = Time.now
start_flag = false
end_flag = false
+ num = (65536 ** 65536)
+ q = Queue.new
thread = Thread.new do
- start_flag = true
- yield
- end_flag = true
+ assert_raise(RuntimeError) {
+ q << true
+ num.to_s
+ end_flag = true
+ }
end
- sleep 1
+ q.pop # sync
thread.raise
- thread.join rescue nil
- start_flag && !end_flag && Time.now - time < 10
+ thread.join
+ time = Time.now - time
+ skip "too fast cpu" if end_flag
+ assert_operator(time, :<, 10)
end
- def test_interrupt
- assert(interrupt { (65536 ** 65536).to_s })
+ def test_interrupt_during_bigdivrem
+ if defined?(Integer::GMP_VERSION)
+ return # GMP doesn't support interrupt during an operation.
+ end
+ return unless Process.respond_to?(:kill)
+ begin
+ trace = []
+ oldtrap = Signal.trap(:INT) {|sig| trace << :int }
+ a = 456 ** 100
+ b = 123 ** 100
+ c = nil
+ 100.times do |n|
+ a **= 3
+ b **= 3
+ trace.clear
+ th = Thread.new do
+ sleep 0.1; Process.kill :INT, $$
+ sleep 0.1; Process.kill :INT, $$
+ end
+ c = a / b
+ trace << :end
+ th.join
+ if trace == [:int, :int, :end]
+ assert_equal(a / b, c)
+ return
+ end
+ end
+ skip "cannot create suitable test case"
+ ensure
+ Signal.trap(:INT, oldtrap) if oldtrap
+ end
end
def test_too_big_to_s
- if (big = 2**31-1).is_a?(Fixnum)
+ if (big = 2**31-1).fixnum?
return
end
- e = assert_raise(RangeError) {(1 << big).to_s}
- assert_match(/too big to convert/, e.message)
+ assert_raise_with_message(RangeError, /too big to convert/) {(1 << big).to_s}
end
def test_fix_fdiv
@@ -419,7 +688,7 @@ class TestBignum < Test::Unit::TestCase
def test_float_fdiv
b = 1E+300.to_i
assert_equal(b, (b ** 2).fdiv(b))
- assert(@big.fdiv(0.0 / 0.0).nan?)
+ assert_send([@big.fdiv(0.0 / 0.0), :nan?])
assert_in_delta(1E+300, (10**500).fdiv(1E+200), 1E+285)
end
@@ -427,5 +696,104 @@ class TestBignum < Test::Unit::TestCase
o = Object.new
def o.coerce(x); [x, 2**100]; end
assert_equal((2**200).to_f, (2**300).fdiv(o))
+ o = Object.new
+ def o.coerce(x); [self, x]; end
+ def o.fdiv(x); 1; end
+ assert_equal(1.0, (2**300).fdiv(o))
+ end
+
+ def test_singleton_method
+ # this test assumes 32bit/64bit platform
+ assert_raise(TypeError) { a = 1 << 64; def a.foo; end }
+ end
+
+ def test_frozen
+ assert_equal(true, (2**100).frozen?)
+ end
+
+ def test_bitwise_and_with_integer_mimic_object
+ def (obj = Object.new).to_int
+ 10
+ end
+ assert_raise(TypeError, '[ruby-core:39491]') { T1024 & obj }
+
+ def obj.coerce(other)
+ [other, 10]
+ end
+ assert_equal(T1024 & 10, T1024 & obj)
+ end
+
+ def test_bitwise_or_with_integer_mimic_object
+ def (obj = Object.new).to_int
+ 10
+ end
+ assert_raise(TypeError, '[ruby-core:39491]') { T1024 | obj }
+
+ def obj.coerce(other)
+ [other, 10]
+ end
+ assert_equal(T1024 | 10, T1024 | obj)
+ end
+
+ def test_bitwise_xor_with_integer_mimic_object
+ def (obj = Object.new).to_int
+ 10
+ end
+ assert_raise(TypeError, '[ruby-core:39491]') { T1024 ^ obj }
+
+ def obj.coerce(other)
+ [other, 10]
+ end
+ assert_equal(T1024 ^ 10, T1024 ^ obj)
+ end
+
+ def test_digits
+ assert_equal([90, 78, 56, 34, 12], 1234567890.to_bignum.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))
+ end
+
+ def test_digits_for_negative_numbers
+ assert_raise(Math::DomainError) { -11.digits(T1024P) }
+ assert_raise(Math::DomainError) { (-T1024P).digits }
+ assert_raise(Math::DomainError) { (-T1024P).digits(T1024P) }
+ 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) }
end
+
+ def test_digits_for_non_integral_base_numbers
+ assert_equal([11], 11.digits(T128P.to_r))
+ assert_equal([11], 11.digits(T128P.to_f))
+
+ t1024p_digits_in_t32 = [T32P]*32
+ assert_equal(t1024p_digits_in_t32, T1024P.digits(T32.to_r))
+ assert_equal(t1024p_digits_in_t32, T1024P.digits(T32.to_f))
+
+ assert_raise(RangeError) { T128P.digits(10+1i) }
+ end
+
+ def test_digits_for_non_numeric_base_argument
+ assert_raise(TypeError) { T1024P.digits("10") }
+ assert_raise(TypeError) { T1024P.digits("a") }
+ end
+
+ def test_finite_p
+ assert_predicate(T1024P, :finite?)
+ assert_predicate(-T1024P, :finite?)
+ end
+
+ def test_infinite_p
+ assert_nil(T1024P.infinite?)
+ assert_nil((-T1024P).infinite?)
+ end
+end
end
diff --git a/test/ruby/test_call.rb b/test/ruby/test_call.rb
index 8f861d96a1..2a1b671cac 100644
--- a/test/ruby/test_call.rb
+++ b/test/ruby/test_call.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestCall < Test::Unit::TestCase
@@ -16,4 +17,86 @@ class TestCall < Test::Unit::TestCase
assert_equal([1, 2, 3, 4], aaa(1, 2, 3, 4))
assert_equal([1, 2, 3, 4], aaa(1, *[2, 3, 4]))
end
+
+ def test_callinfo
+ bug9622 = '[ruby-core:61422] [Bug #9622]'
+ o = Class.new do
+ def foo(*args)
+ bar(:foo, *args)
+ end
+ def bar(name)
+ name
+ end
+ end.new
+ e = assert_raise(ArgumentError) {o.foo(100)}
+ assert_nothing_raised(ArgumentError) {o.foo}
+ assert_raise_with_message(ArgumentError, e.message, bug9622) {o.foo(100)}
+ end
+
+ def test_safe_call
+ s = Struct.new(:x, :y, :z)
+ o = s.new("x")
+ assert_equal("X", o.x&.upcase)
+ assert_nil(o.y&.upcase)
+ assert_equal("x", o.x)
+ o&.x = 6
+ assert_equal(6, o.x)
+ o&.x *= 7
+ assert_equal(42, o.x)
+ o&.y = 5
+ assert_equal(5, o.y)
+ o&.z ||= 6
+ assert_equal(6, o.z)
+
+ o = nil
+ assert_nil(o&.x)
+ assert_nothing_raised(NoMethodError) {o&.x = raise}
+ assert_nothing_raised(NoMethodError) {o&.x *= raise}
+ assert_nothing_raised(NoMethodError) {o&.x *= raise; nil}
+ end
+
+ def test_safe_call_evaluate_arguments_only_method_call_is_made
+ count = 0
+ proc = proc { count += 1; 1 }
+ s = Struct.new(:x, :y)
+ o = s.new(["a", "b", "c"])
+
+ o.y&.at(proc.call)
+ assert_equal(0, count)
+
+ o.x&.at(proc.call)
+ assert_equal(1, count)
+ end
+
+ def test_safe_call_block_command
+ assert_nil(("a".sub! "b" do end&.foo 1))
+ end
+
+ def test_safe_call_block_call
+ assert_nil(("a".sub! "b" do end&.foo))
+ end
+
+ def test_safe_call_block_call_brace
+ assert_nil(("a".sub! "b" do end&.foo {}))
+ assert_nil(("a".sub! "b" do end&.foo do end))
+ end
+
+ def test_safe_call_block_call_command
+ assert_nil(("a".sub! "b" do end&.foo 1 do end))
+ end
+
+ def test_invalid_safe_call
+ h = nil
+ assert_raise(NoMethodError) {
+ h[:foo] = nil
+ }
+ end
+
+ def test_call_splat_order
+ bug12860 = '[ruby-core:77701] [Bug# 12860]'
+ ary = [1, 2]
+ assert_equal([1, 2, 1], aaa(*ary, ary.shift), bug12860)
+ ary = [1, 2]
+ assert_equal([0, 1, 2, 1], aaa(0, *ary, ary.shift), bug12860)
+ end
end
diff --git a/test/ruby/test_case.rb b/test/ruby/test_case.rb
index 98498dada6..77612a8945 100644
--- a/test/ruby/test_case.rb
+++ b/test/ruby/test_case.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil.rb'
class TestCase < Test::Unit::TestCase
def test_case
@@ -81,7 +81,66 @@ class TestCase < Test::Unit::TestCase
EOS
assert_in_out_err(['-e', <<-EOS], '', %w[42], [])
- class Fixnum; undef ===; def ===(o); p 42; true; end; end; case 1; when 1; end
+ class Integer; undef ===; def ===(o); p 42; true; end; end; case 1; when 1; end
EOS
end
+
+ def test_optimization
+ case 1
+ when 0.9, 1.1
+ assert(false)
+ when 1.0
+ assert(true)
+ else
+ assert(false)
+ end
+ case 536870912
+ when 536870911.9, 536870912.1
+ assert(false)
+ when 536870912.0
+ assert(true)
+ else
+ assert(false)
+ end
+ end
+
+ def test_method_missing
+ flag = false
+
+ case 1
+ when Class.new(BasicObject) { def method_missing(*) true end }.new
+ flag = true
+ end
+
+ assert(flag)
+ end
+
+ def test_nomethoderror
+ assert_raise(NoMethodError) {
+ case 1
+ when Class.new(BasicObject) { }.new
+ end
+ }
+ end
+
+ module NilEqq
+ refine NilClass do
+ def === other
+ false
+ end
+ end
+ end
+
+ class NilEqqClass
+ using NilEqq
+
+ def eqq(a)
+ case a; when nil then nil; else :not_nil; end
+ end
+ end
+
+
+ def test_deoptimize_nil
+ assert_equal :not_nil, NilEqqClass.new.eqq(nil)
+ end
end
diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb
index c81f0752d4..2ab1e7f266 100644
--- a/test/ruby/test_class.rb
+++ b/test/ruby/test_class.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestClass < Test::Unit::TestCase
# ------------------
@@ -46,9 +46,9 @@ class TestClass < Test::Unit::TestCase
assert_same(Class, c.class)
assert_same(Object, c.superclass)
- c = Class.new(Fixnum)
+ c = Class.new(Integer)
assert_same(Class, c.class)
- assert_same(Fixnum, c.superclass)
+ assert_same(Integer, c.superclass)
end
def test_00_new_basic
@@ -89,7 +89,7 @@ class TestClass < Test::Unit::TestCase
end
end
- def test_instanciate_singleton_class
+ def test_instantiate_singleton_class
c = class << Object.new; self; end
assert_raise(TypeError) { c.new }
end
@@ -105,6 +105,32 @@ class TestClass < Test::Unit::TestCase
end
end
+ def test_extend_object
+ c = Class.new
+ assert_raise(TypeError) do
+ Module.instance_method(:extend_object).bind(c).call(Object.new)
+ end
+ end
+
+ def test_append_features
+ c = Class.new
+ assert_raise(TypeError) do
+ Module.instance_method(:append_features).bind(c).call(Module.new)
+ end
+ end
+
+ def test_prepend_features
+ c = Class.new
+ assert_raise(TypeError) do
+ Module.instance_method(:prepend_features).bind(c).call(Module.new)
+ end
+ end
+
+ def test_module_specific_methods
+ assert_empty(Class.private_instance_methods(true) &
+ [:module_function, :extend_object, :append_features, :prepend_features])
+ end
+
def test_method_redefinition
feature2155 = '[ruby-dev:39400]'
@@ -118,23 +144,21 @@ class TestClass < Test::Unit::TestCase
assert_match(/:#{line}: warning: method redefined; discarding old foo/, stderr)
assert_match(/:#{line-1}: warning: previous definition of foo/, stderr, feature2155)
- stderr = EnvUtil.verbose_warning do
+ assert_warning '' do
Class.new do
def foo; end
alias bar foo
def foo; end
end
end
- assert_equal("", stderr)
- stderr = EnvUtil.verbose_warning do
+ assert_warning '' do
Class.new do
def foo; end
alias bar foo
alias bar foo
end
end
- assert_equal("", stderr)
line = __LINE__+4
stderr = EnvUtil.verbose_warning do
@@ -146,22 +170,20 @@ class TestClass < Test::Unit::TestCase
assert_match(/:#{line}: warning: method redefined; discarding old foo/, stderr)
assert_match(/:#{line-1}: warning: previous definition of foo/, stderr, feature2155)
- stderr = EnvUtil.verbose_warning do
+ assert_warning '' do
Class.new do
define_method(:foo) do end
alias bar foo
alias bar foo
end
end
- assert_equal("", stderr)
- stderr = EnvUtil.verbose_warning do
+ assert_warning '' do
Class.new do
def foo; end
undef foo
end
end
- assert_equal("", stderr)
end
def test_check_inheritable
@@ -172,6 +194,9 @@ class TestClass < Test::Unit::TestCase
assert_raise(TypeError) { Class.new(c) }
assert_raise(TypeError) { Class.new(Class) }
assert_raise(TypeError) { eval("class Foo < Class; end") }
+ m = "M\u{1f5ff}"
+ o = Class.new {break eval("class #{m}; self; end.new")}
+ assert_raise_with_message(TypeError, /#{m}/) {Class.new(o)}
end
def test_initialize_copy
@@ -187,6 +212,8 @@ class TestClass < Test::Unit::TestCase
def test_singleton_class
assert_raise(TypeError) { 1.extend(Module.new) }
+ assert_raise(TypeError) { 1.0.extend(Module.new) }
+ assert_raise(TypeError) { (2.0**1000).extend(Module.new) }
assert_raise(TypeError) { :foo.extend(Module.new) }
assert_in_out_err([], <<-INPUT, %w(:foo :foo true true), [])
@@ -203,6 +230,13 @@ class TestClass < Test::Unit::TestCase
def test_uninitialized
assert_raise(TypeError) { Class.allocate.new }
assert_raise(TypeError) { Class.allocate.superclass }
+ bug6863 = '[ruby-core:47148]'
+ assert_raise(TypeError, bug6863) { Class.new(Class.allocate) }
+
+ allocator = Class.instance_method(:allocate)
+ assert_raise_with_message(TypeError, /prohibited/) {
+ allocator.bind(Rational).call
+ }
end
def test_nonascii_name
@@ -212,13 +246,28 @@ class TestClass < Test::Unit::TestCase
assert_equal("TestClass::C\u{df}", c.name, '[ruby-core:24600]')
end
- def test_invalid_jump_from_class_definition
+ def test_invalid_next_from_class_definition
assert_raise(SyntaxError) { eval("class C; next; end") }
+ end
+
+ def test_invalid_break_from_class_definition
assert_raise(SyntaxError) { eval("class C; break; end") }
+ end
+
+ def test_invalid_redo_from_class_definition
assert_raise(SyntaxError) { eval("class C; redo; end") }
+ end
+
+ def test_invalid_retry_from_class_definition
assert_raise(SyntaxError) { eval("class C; retry; end") }
+ end
+
+ def test_invalid_return_from_class_definition
assert_raise(SyntaxError) { eval("class C; return; end") }
- assert_raise(SyntaxError) { eval("class C; yield; end") }
+ end
+
+ def test_invalid_yield_from_class_definition
+ assert_raise(LocalJumpError) { eval("class C; yield; end") }
end
def test_clone
@@ -236,4 +285,396 @@ class TestClass < Test::Unit::TestCase
copy.send(:include, mod)
assert_equal("mod#foo", copy.new.foo)
end
+
+ def test_nested_class_removal
+ assert_normal_exit('File.__send__(:remove_const, :Stat); at_exit{File.stat(".")}; GC.start')
+ end
+
+ class PrivateClass
+ end
+ private_constant :PrivateClass
+
+ def test_redefine_private_class
+ assert_raise(NameError) do
+ eval("class ::TestClass::PrivateClass; end")
+ end
+ eval <<-END
+ class ::TestClass
+ class PrivateClass
+ def foo; 42; end
+ end
+ end
+ END
+ assert_equal(42, PrivateClass.new.foo)
+ end
+
+ StrClone = String.clone
+ Class.new(StrClone)
+
+ def test_cloned_class
+ bug5274 = StrClone.new("[ruby-dev:44460]")
+ assert_equal(bug5274, Marshal.load(Marshal.dump(bug5274)))
+ end
+
+ def test_cannot_reinitialize_class_with_initialize_copy # [ruby-core:50869]
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", ["Object"], [])
+ begin;
+ class Class
+ def initialize_copy(*); super; end
+ end
+
+ class A; end
+ class B; end
+
+ A.send(:initialize_copy, Class.new(B)) rescue nil
+
+ p A.superclass
+ end;
+ end
+
+ module M
+ C = 1
+
+ def self.m
+ C
+ end
+ end
+
+ def test_constant_access_from_method_in_cloned_module # [ruby-core:47834]
+ m = M.dup
+ assert_equal 1, m::C
+ assert_equal 1, m.m
+ end
+
+ def test_invalid_superclass
+ assert_raise(TypeError) do
+ eval <<-'end;'
+ class C < nil
+ end
+ end;
+ end
+
+ assert_raise(TypeError) do
+ eval <<-'end;'
+ class C < false
+ end
+ end;
+ end
+
+ assert_raise(TypeError) do
+ eval <<-'end;'
+ class C < true
+ end
+ end;
+ end
+
+ assert_raise(TypeError) do
+ eval <<-'end;'
+ class C < 0
+ end
+ end;
+ end
+
+ assert_raise(TypeError) do
+ eval <<-'end;'
+ class C < ""
+ end
+ end;
+ end
+
+ m = Module.new
+ n = "M\u{1f5ff}"
+ c = m.module_eval "class #{n}; new; end"
+ assert_raise_with_message(TypeError, /#{n}/) {
+ eval <<-"end;"
+ class C < c
+ end
+ end;
+ }
+ assert_raise_with_message(TypeError, /#{n}/) {
+ Class.new(c)
+ }
+ assert_raise_with_message(TypeError, /#{n}/) {
+ m.module_eval "class #{n} < Class.new; end"
+ }
+ end
+
+ define_method :test_invalid_reset_superclass do
+ class A; end
+ class SuperclassCannotBeReset < A
+ end
+ assert_equal A, SuperclassCannotBeReset.superclass
+
+ assert_raise_with_message(TypeError, /superclass mismatch/) {
+ class SuperclassCannotBeReset < String
+ end
+ }
+
+ assert_raise_with_message(TypeError, /superclass mismatch/, "[ruby-core:75446]") {
+ class SuperclassCannotBeReset < Object
+ end
+ }
+
+ assert_equal A, SuperclassCannotBeReset.superclass
+ end
+
+ def test_cloned_singleton_method_added
+ bug5283 = '[ruby-dev:44477]'
+ added = []
+ c = Class.new
+ c.singleton_class.class_eval do
+ define_method(:singleton_method_added) {|mid| added << [self, mid]}
+ def foo; :foo; end
+ end
+ added.clear
+ d = c.clone
+ assert_empty(added.grep(->(k) {c == k[0]}), bug5283)
+ 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)
+ assert_not_predicate(self.class, :singleton_class?, feature7609)
+ end
+
+ def test_freeze_to_s
+ assert_nothing_raised("[ruby-core:41858] [Bug #5828]") {
+ Class.new.freeze.clone.to_s
+ }
+ end
+
+ def test_singleton_class_of_frozen_object
+ obj = Object.new
+ c = obj.singleton_class
+ obj.freeze
+ assert_raise_with_message(FrozenError, /frozen object/) {
+ c.class_eval {def f; end}
+ }
+ end
+
+ def test_singleton_class_message
+ c = Class.new.freeze
+ assert_raise_with_message(FrozenError, /frozen Class/) {
+ def c.f; end
+ }
+ end
+
+ def test_singleton_class_should_has_own_namespace
+ # CONST in singleton class
+ objs = []
+ $i = 0
+
+ 2.times{
+ objs << obj = Object.new
+ class << obj
+ CONST = ($i += 1)
+ def foo
+ CONST
+ end
+ end
+ }
+ assert_equal(1, objs[0].foo, '[Bug #10943]')
+ assert_equal(2, objs[1].foo, '[Bug #10943]')
+
+ # CONST in block in singleton class
+ objs = []
+ $i = 0
+
+ 2.times{
+ objs << obj = Object.new
+ class << obj
+ 1.times{
+ CONST = ($i += 1)
+ }
+ def foo
+ [nil].map{
+ CONST
+ }
+ end
+ end
+ }
+ assert_equal([1], objs[0].foo, '[Bug #10943]')
+ assert_equal([2], objs[1].foo, '[Bug #10943]')
+
+ # class def in singleton class
+ objs = []
+ $xs = []
+ $i = 0
+
+ 2.times{
+ objs << obj = Object.new
+ class << obj
+ CONST = ($i += 1)
+ class X
+ $xs << self
+ CONST = ($i += 1)
+ def foo
+ CONST
+ end
+ end
+
+ def x
+ X
+ end
+ end
+ }
+ assert_not_equal($xs[0], $xs[1], '[Bug #10943]')
+ assert_not_equal(objs[0].x, objs[1].x, '[Bug #10943]')
+ assert_equal(2, $xs[0]::CONST, '[Bug #10943]')
+ assert_equal(2, $xs[0].new.foo, '[Bug #10943]')
+ assert_equal(4, $xs[1]::CONST, '[Bug #10943]')
+ assert_equal(4, $xs[1].new.foo, '[Bug #10943]')
+
+ # class def in block in singleton class
+ objs = []
+ $xs = []
+ $i = 0
+
+ 2.times{
+ objs << obj = Object.new
+ class << obj
+ 1.times{
+ CONST = ($i += 1)
+ }
+ 1.times{
+ class X
+ $xs << self
+ CONST = ($i += 1)
+ def foo
+ CONST
+ end
+ end
+
+ def x
+ X
+ end
+ }
+ end
+ }
+ assert_not_equal($xs[0], $xs[1], '[Bug #10943]')
+ assert_not_equal(objs[0].x, objs[1].x, '[Bug #10943]')
+ assert_equal(2, $xs[0]::CONST, '[Bug #10943]')
+ assert_equal(2, $xs[0].new.foo, '[Bug #10943]')
+ assert_equal(4, $xs[1]::CONST, '[Bug #10943]')
+ assert_equal(4, $xs[1].new.foo, '[Bug #10943]')
+
+ # method def in singleton class
+ ms = []
+ ps = $test_singleton_class_shared_cref_ps = []
+ 2.times{
+ ms << Module.new do
+ class << self
+ $test_singleton_class_shared_cref_ps << Proc.new{
+ def xyzzy
+ self
+ end
+ }
+ end
+ end
+ }
+
+ ps.each{|p| p.call} # define xyzzy methods for each singleton classes
+ ms.each{|m|
+ assert_equal(m, m.xyzzy, "Bug #10871")
+ }
+ end
+
+ def test_namescope_error_message
+ m = Module.new
+ o = m.module_eval "class A\u{3042}; self; end.new"
+ assert_raise_with_message(TypeError, /A\u{3042}/) {
+ o::Foo
+ }
+ end
+
+ def test_redefinition_mismatch
+ m = Module.new
+ m.module_eval "A = 1"
+ assert_raise_with_message(TypeError, /is not a class/) {
+ m.module_eval "class A; end"
+ }
+ n = "M\u{1f5ff}"
+ m.module_eval "#{n} = 42"
+ assert_raise_with_message(TypeError, "#{n} is not a class") {
+ m.module_eval "class #{n}; end"
+ }
+
+ assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}")
+ begin;
+ Date = (class C\u{1f5ff}; self; end).new
+ assert_raise_with_message(TypeError, /C\u{1f5ff}/) {
+ require 'date'
+ }
+ end;
+ end
+
+ def test_should_not_expose_singleton_class_without_metaclass
+ assert_normal_exit "#{<<~"begin;"}\n#{<<~'end;'}", '[Bug #11740]'
+ begin;
+ klass = Class.new(Array)
+ # The metaclass of +klass+ should handle #bla since it should inherit methods from meta:meta:Array
+ def (Array.singleton_class).bla; :bla; end
+ hidden = ObjectSpace.each_object(Class).find { |c| klass.is_a? c and c.inspect.include? klass.inspect }
+ raise unless hidden.nil?
+ end;
+
+ assert_normal_exit "#{<<~"begin;"}\n#{<<~'end;'}", '[Bug #11740]'
+ begin;
+ klass = Class.new(Array)
+ klass.singleton_class
+ # The metaclass of +klass+ should handle #bla since it should inherit methods from meta:meta:Array
+ def (Array.singleton_class).bla; :bla; end
+ hidden = ObjectSpace.each_object(Class).find { |c| klass.is_a? c and c.inspect.include? klass.inspect }
+ raise if hidden.nil?
+ end;
+
+ end
end
diff --git a/test/ruby/test_clone.rb b/test/ruby/test_clone.rb
index c5e2469d10..321feb07c7 100644
--- a/test/ruby/test_clone.rb
+++ b/test/ruby/test_clone.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestClone < Test::Unit::TestCase
@@ -25,4 +26,39 @@ class TestClone < Test::Unit::TestCase
assert_equal([M003, M002, M001], M003.ancestors)
end
+
+ def test_user_flags
+ assert_separately([], <<-EOS)
+ #
+ class Array
+ undef initialize_copy
+ def initialize_copy(*); end
+ end
+ x = [1, 2, 3].clone
+ assert_equal [], x, '[Bug #14847]'
+ EOS
+
+ assert_separately([], <<-EOS)
+ #
+ class Array
+ undef initialize_copy
+ def initialize_copy(*); end
+ end
+ x = [1,2,3,4,5,6,7][1..-2].clone
+ x.push(1,1,1,1,1)
+ assert_equal [1, 1, 1, 1, 1], x, '[Bug #14847]'
+ EOS
+
+ assert_separately([], <<-EOS)
+ #
+ class Hash
+ undef initialize_copy
+ def initialize_copy(*); end
+ end
+ h = {}
+ h.default_proc = proc { raise }
+ h = h.clone
+ assert_equal nil, h[:not_exist], '[Bug #14847]'
+ EOS
+ end
end
diff --git a/test/ruby/test_comparable.rb b/test/ruby/test_comparable.rb
index 00ce6b485a..94c05d5f91 100644
--- a/test/ruby/test_comparable.rb
+++ b/test/ruby/test_comparable.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestComparable < Test::Unit::TestCase
@@ -17,8 +18,18 @@ class TestComparable < Test::Unit::TestCase
assert_equal(true, @o == nil)
cmp->(x) do 1; end
assert_equal(false, @o == nil)
- cmp->(x) do raise; end
+ cmp->(x) do nil; end
assert_equal(false, @o == nil)
+
+ cmp->(x) do raise NotImplementedError, "Not a RuntimeError" end
+ assert_raise(NotImplementedError) { @o == nil }
+
+ bug7688 = 'Comparable#== should not silently rescue' \
+ 'any Exception [ruby-core:51389] [Bug #7688]'
+ cmp->(x) do raise StandardError end
+ assert_raise(StandardError, bug7688) { @o == nil }
+ cmp->(x) do "bad value"; end
+ assert_raise(ArgumentError, bug7688) { @o == nil }
end
def test_gt
@@ -65,8 +76,40 @@ class TestComparable < Test::Unit::TestCase
assert_equal(true, @o.between?(0, 0))
end
+ def test_clamp
+ cmp->(x) do 0 <=> x end
+ assert_equal(1, @o.clamp(1, 2))
+ assert_equal(-1, @o.clamp(-2, -1))
+ assert_equal(@o, @o.clamp(-1, 3))
+
+ assert_equal(1, @o.clamp(1, 1))
+ assert_equal(@o, @o.clamp(0, 0))
+
+ assert_raise_with_message(ArgumentError, 'min argument must be smaller than max argument') {
+ @o.clamp(2, 1)
+ }
+ end
+
def test_err
assert_raise(ArgumentError) { 1.0 < nil }
assert_raise(ArgumentError) { 1.0 < Object.new }
+ e = EnvUtil.labeled_class("E\u{30a8 30e9 30fc}")
+ assert_raise_with_message(ArgumentError, /E\u{30a8 30e9 30fc}/) {
+ 1.0 < e.new
+ }
+ end
+
+ def test_inversed_compare
+ bug7870 = '[ruby-core:52305] [Bug #7870]'
+ assert_nothing_raised(SystemStackError, bug7870) {
+ assert_nil(Time.new <=> "")
+ }
+ end
+
+ def test_no_cmp
+ bug9003 = '[ruby-core:57736] [Bug #9003]'
+ assert_nothing_raised(SystemStackError, bug9003) {
+ @o <=> @o.dup
+ }
end
end
diff --git a/test/ruby/test_complex.rb b/test/ruby/test_complex.rb
index f6d65de6de..0161ce8ffe 100644
--- a/test/ruby/test_complex.rb
+++ b/test/ruby/test_complex.rb
@@ -1,16 +1,14 @@
+# frozen_string_literal: false
require 'test/unit'
class ComplexSub < Complex; end
class Complex_Test < Test::Unit::TestCase
- def setup
- @rational = defined?(Rational)
- if @rational
- @keiju = Rational.instance_variables.include?(:@RCS_ID)
- end
- seps = [File::SEPARATOR, File::ALT_SEPARATOR].compact.map{|x| Regexp.escape(x)}.join("|")
- @unify = $".grep(/(?:^|#{seps})mathn(?:\.(?:rb|so))?/).size != 0
+ def test_rationalize
+ assert_equal(1.quo(3), Complex(1/3.0, 0).rationalize, '[ruby-core:38885]')
+ assert_equal(1.quo(5), Complex(0.2, 0).rationalize, '[ruby-core:38885]')
+ assert_equal(5.quo(2), Complex(2.5, 0).rationalize(0), '[ruby-core:40667]')
end
def test_compsub
@@ -18,24 +16,20 @@ class Complex_Test < Test::Unit::TestCase
assert_kind_of(Numeric, c)
- if @unify
- assert_instance_of(Fixnum, c)
- else
- assert_instance_of(ComplexSub, c)
+ assert_instance_of(ComplexSub, c)
- c2 = c + 1
- assert_instance_of(ComplexSub, c2)
- c2 = c - 1
- assert_instance_of(ComplexSub, c2)
+ c2 = c + 1
+ assert_instance_of(ComplexSub, c2)
+ c2 = c - 1
+ assert_instance_of(ComplexSub, c2)
- c3 = c - c2
- assert_instance_of(ComplexSub, c3)
+ c3 = c - c2
+ assert_instance_of(ComplexSub, c3)
- s = Marshal.dump(c)
- c5 = Marshal.load(s)
- assert_equal(c, c5)
- assert_instance_of(ComplexSub, c5)
- end
+ s = Marshal.dump(c)
+ c5 = Marshal.load(s)
+ assert_equal(c, c5)
+ assert_instance_of(ComplexSub, c5)
c1 = Complex(1)
assert_equal(c1.hash, c.hash, '[ruby-dev:38850]')
@@ -47,19 +41,19 @@ class Complex_Test < Test::Unit::TestCase
c2 = Complex(0)
c3 = Complex(1)
- assert_equal(true, c.eql?(c2))
- assert_equal(false, c.eql?(c3))
+ assert_operator(c, :eql?, c2)
+ assert_not_operator(c, :eql?, c3)
- if @unify
- assert_equal(true, c.eql?(0))
- else
- assert_equal(false, c.eql?(0))
- end
+ assert_not_operator(c, :eql?, 0)
end
def test_hash
- assert_instance_of(Fixnum, Complex(1,2).hash)
- assert_instance_of(Fixnum, Complex(1.0,2.0).hash)
+ h = Complex(1,2).hash
+ assert_kind_of(Integer, h)
+ assert_nothing_raised {h.to_s}
+ h = Complex(1.0,2.0).hash
+ assert_kind_of(Integer, h)
+ assert_nothing_raised {h.to_s}
h = {}
h[Complex(0)] = 0
@@ -85,10 +79,7 @@ class Complex_Test < Test::Unit::TestCase
def test_freeze
c = Complex(1)
- c.freeze
- unless @unify
- assert_equal(true, c.frozen?)
- end
+ assert_predicate(c, :frozen?)
assert_instance_of(String, c.to_s)
end
@@ -127,9 +118,7 @@ class Complex_Test < Test::Unit::TestCase
assert_equal(Complex(1),Complex(1))
assert_equal(Complex(1),Complex('1'))
assert_equal(Complex(3.0,3.0),Complex('3.0','3.0'))
- if @rational && !@keiju
- assert_equal(Complex(1,1),Complex('3/3','3/3'))
- end
+ assert_equal(Complex(1,1),Complex('3/3','3/3'))
assert_raise(TypeError){Complex(nil)}
assert_raise(TypeError){Complex(Object.new)}
assert_raise(ArgumentError){Complex()}
@@ -203,49 +192,14 @@ class Complex_Test < Test::Unit::TestCase
def test_attr2
c = Complex(1)
- if @unify
-=begin
- assert_equal(true, c.finite?)
- assert_equal(false, c.infinite?)
- assert_equal(false, c.nan?)
- assert_equal(true, c.integer?)
- assert_equal(false, c.float?)
- assert_equal(true, c.rational?)
-=end
- assert_equal(true, c.real?)
-=begin
- assert_equal(false, c.complex?)
- assert_equal(true, c.exact?)
- assert_equal(false, c.inexact?)
-=end
- else
-=begin
- assert_equal(true, c.finite?)
- assert_equal(false, c.infinite?)
- assert_equal(false, c.nan?)
- assert_equal(false, c.integer?)
- assert_equal(false, c.float?)
- assert_equal(false, c.rational?)
-=end
- assert_equal(false, c.real?)
-=begin
- assert_equal(true, c.complex?)
- assert_equal(true, c.exact?)
- assert_equal(false, c.inexact?)
-=end
- end
-
-=begin
- assert_equal(0, Complex(0).sign)
- assert_equal(1, Complex(2).sign)
- assert_equal(-1, Complex(-2).sign)
-=end
+ assert_not_predicate(c, :integer?)
+ assert_not_predicate(c, :real?)
- assert_equal(true, Complex(0).zero?)
- assert_equal(true, Complex(0,0).zero?)
- assert_equal(false, Complex(1,0).zero?)
- assert_equal(false, Complex(0,1).zero?)
- assert_equal(false, Complex(1,1).zero?)
+ assert_predicate(Complex(0), :zero?)
+ assert_predicate(Complex(0,0), :zero?)
+ assert_not_predicate(Complex(1,0), :zero?)
+ assert_not_predicate(Complex(0,1), :zero?)
+ assert_not_predicate(Complex(1,1), :zero?)
assert_equal(nil, Complex(0).nonzero?)
assert_equal(nil, Complex(0,0).nonzero?)
@@ -261,6 +215,7 @@ 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))
end
def test_uplus
@@ -299,12 +254,6 @@ class Complex_Test < Test::Unit::TestCase
assert_equal('0.0', c.real.to_s)
assert_equal('0.0', c.imag.to_s)
end
-
-=begin
- assert_equal(0, Complex(0).negate)
- assert_equal(-2, Complex(2).negate)
- assert_equal(2, Complex(-2).negate)
-=end
end
def test_add
@@ -316,10 +265,41 @@ class Complex_Test < Test::Unit::TestCase
assert_equal(Complex(3,2), c + 2)
assert_equal(Complex(3.0,2), c + 2.0)
- if @rational
- assert_equal(Complex(Rational(3,1),Rational(2)), c + Rational(2))
- assert_equal(Complex(Rational(5,3),Rational(2)), c + Rational(2,3))
- end
+ assert_equal(Complex(Rational(3,1),Rational(2)), c + Rational(2))
+ assert_equal(Complex(Rational(5,3),Rational(2)), c + Rational(2,3))
+ end
+
+ def test_add_with_redefining_int_plus
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Integer
+ remove_method :+
+ def +(other); 42; end
+ end
+ a = Complex(1, 2) + Complex(0, 1)
+ puts a == Complex(42, 42)
+ end;
+ end
+
+ def test_add_with_redefining_float_plus
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Float
+ remove_method :+
+ def +(other); 42.0; end
+ end
+ a = Complex(1.0, 2.0) + Complex(0, 1)
+ puts a == Complex(42.0, 42.0)
+ end;
+ end
+
+ def test_add_with_redefining_rational_plus
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Rational
+ remove_method :+
+ def +(other); 355/113r; end
+ end
+ a = Complex(1r, 2r) + Complex(0, 1)
+ puts a == Complex(355/113r, 355/113r)
+ end;
end
def test_sub
@@ -331,10 +311,41 @@ class Complex_Test < Test::Unit::TestCase
assert_equal(Complex(-1,2), c - 2)
assert_equal(Complex(-1.0,2), c - 2.0)
- if @rational
- assert_equal(Complex(Rational(-1,1),Rational(2)), c - Rational(2))
- assert_equal(Complex(Rational(1,3),Rational(2)), c - Rational(2,3))
- end
+ assert_equal(Complex(Rational(-1,1),Rational(2)), c - Rational(2))
+ assert_equal(Complex(Rational(1,3),Rational(2)), c - Rational(2,3))
+ end
+
+ def test_sub_with_redefining_int_minus
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Integer
+ remove_method :-
+ def -(other); 42; end
+ end
+ a = Complex(1, 2) - Complex(0, 1)
+ puts a == Complex(42, 42)
+ end;
+ end
+
+ def test_sub_with_redefining_float_minus
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Float
+ remove_method :-
+ def -(other); 42.0; end
+ end
+ a = Complex(1.0, 2.0) - Complex(0, 1)
+ puts a == Complex(42.0, 42.0)
+ end;
+ end
+
+ def test_sub_with_redefining_rational_minus
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Rational
+ remove_method :-
+ def -(other); 355/113r; end
+ end
+ a = Complex(1r, 2r) - Complex(0, 1)
+ puts a == Complex(355/113r, 355/113r)
+ end;
end
def test_mul
@@ -346,24 +357,58 @@ class Complex_Test < Test::Unit::TestCase
assert_equal(Complex(2,4), c * 2)
assert_equal(Complex(2.0,4.0), c * 2.0)
- if @rational
- assert_equal(Complex(Rational(2,1),Rational(4)), c * Rational(2))
- assert_equal(Complex(Rational(2,3),Rational(4,3)), c * Rational(2,3))
- end
+ assert_equal(Complex(Rational(2,1),Rational(4)), c * Rational(2))
+ assert_equal(Complex(Rational(2,3),Rational(4,3)), c * Rational(2,3))
+ c = Complex(Float::INFINITY, 0)
+ assert_equal(Complex(Float::INFINITY, 0), c * Complex(1, 0))
+ assert_equal(Complex(0, Float::INFINITY), c * Complex(0, 1))
+ c = Complex(0, Float::INFINITY)
+ assert_equal(Complex(0, Float::INFINITY), c * Complex(1, 0))
+ assert_equal(Complex(-Float::INFINITY, 0), c * Complex(0, 1))
+
+ assert_equal(Complex(-0.0, -0.0), Complex(-0.0, 0) * Complex(0, 0))
+ end
+
+ def test_mul_with_redefining_int_mult
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Integer
+ remove_method :*
+ def *(other); 42; end
+ end
+ a = Complex(2, 0) * Complex(1, 2)
+ puts a == Complex(0, 84)
+ end;
+ end
+
+ def test_mul_with_redefining_float_mult
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Float
+ remove_method :*
+ def *(other); 42.0; end
+ end
+ a = Complex(2.0, 0.0) * Complex(1, 2)
+ puts a == Complex(0.0, 84.0)
+ end;
+ end
+
+
+ def test_mul_with_redefining_rational_mult
+ assert_in_out_err([], <<-'end;', ['true'], [])
+ class Rational
+ remove_method :*
+ def *(other); 355/113r; end
+ end
+ a = Complex(2r, 0r) * Complex(1, 2)
+ puts a == Complex(0r, 2*355/113r)
+ end;
end
def test_div
c = Complex(1,2)
c2 = Complex(2,3)
- if @rational
- assert_equal(Complex(Rational(8,13),Rational(1,13)), c / c2)
- else
- r = c / c2
- assert_in_delta(0.615, r.real, 0.001)
- assert_in_delta(0.076, r.imag, 0.001)
- end
+ assert_equal(Complex(Rational(8,13),Rational(1,13)), c / c2)
c = Complex(1.0,2.0)
c2 = Complex(2.0,3.0)
@@ -375,30 +420,25 @@ class Complex_Test < Test::Unit::TestCase
c = Complex(1,2)
c2 = Complex(2,3)
- if @rational
- assert_equal(Complex(Rational(1,2),1), c / 2)
- else
- assert_equal(Complex(0.5,1.0), c / 2)
- end
+ assert_equal(Complex(Rational(1,2),1), c / 2)
assert_equal(Complex(0.5,1.0), c / 2.0)
- if @rational
- assert_equal(Complex(Rational(1,2),Rational(1)), c / Rational(2))
- assert_equal(Complex(Rational(3,2),Rational(3)), c / Rational(2,3))
- end
+ assert_equal(Complex(Rational(1,2),Rational(1)), c / Rational(2))
+ assert_equal(Complex(Rational(3,2),Rational(3)), c / Rational(2,3))
+
+ c = Complex(1)
+ r = c / c
+ assert_instance_of(Complex, r)
+ assert_equal(1, r)
+ assert_predicate(r.real, :integer?)
+ assert_predicate(r.imag, :integer?)
end
def test_quo
c = Complex(1,2)
c2 = Complex(2,3)
- if @rational
- assert_equal(Complex(Rational(8,13),Rational(1,13)), c.quo(c2))
- else
- r = c.quo(c2)
- assert_in_delta(0.615, r.real, 0.001)
- assert_in_delta(0.076, r.imag, 0.001)
- end
+ assert_equal(Complex(Rational(8,13),Rational(1,13)), c.quo(c2))
c = Complex(1.0,2.0)
c2 = Complex(2.0,3.0)
@@ -410,17 +450,11 @@ class Complex_Test < Test::Unit::TestCase
c = Complex(1,2)
c2 = Complex(2,3)
- if @rational
- assert_equal(Complex(Rational(1,2),1), c.quo(2))
- else
- assert_equal(Complex(0.5,1.0), c.quo(2))
- end
+ assert_equal(Complex(Rational(1,2),1), c.quo(2))
assert_equal(Complex(0.5,1.0), c.quo(2.0))
- if @rational
- assert_equal(Complex(Rational(1,2),Rational(1)), c / Rational(2))
- assert_equal(Complex(Rational(3,2),Rational(3)), c / Rational(2,3))
- end
+ assert_equal(Complex(Rational(1,2),Rational(1)), c / Rational(2))
+ assert_equal(Complex(Rational(3,2),Rational(3)), c / Rational(2,3))
end
def test_fdiv
@@ -454,13 +488,8 @@ class Complex_Test < Test::Unit::TestCase
assert_in_delta(-0.179, r.imag, 0.001)
assert_equal(Complex(-3,4), c ** 2)
- if @rational && !@keiju
- assert_equal(Complex(Rational(-3,25),Rational(-4,25)), c ** -2)
- else
- r = c ** -2
- assert_in_delta(-0.12, r.real, 0.001)
- assert_in_delta(-0.16, r.imag, 0.001)
- end
+ assert_equal(Complex(Rational(-3,25),Rational(-4,25)), c ** -2)
+
r = c ** 2.0
assert_in_delta(-3.0, r.real, 0.001)
assert_in_delta(4.0, r.imag, 0.001)
@@ -469,21 +498,21 @@ class Complex_Test < Test::Unit::TestCase
assert_in_delta(-0.12, r.real, 0.001)
assert_in_delta(-0.16, r.imag, 0.001)
- if @rational && !@keiju
- assert_equal(Complex(-3,4), c ** Rational(2))
-#=begin
- assert_equal(Complex(Rational(-3,25),Rational(-4,25)),
- c ** Rational(-2)) # why failed?
-#=end
+ assert_equal(Complex(-3,4), c ** Rational(2))
+ assert_equal(Complex(Rational(-3,25),Rational(-4,25)),
+ c ** Rational(-2)) # why failed?
- r = c ** Rational(2,3)
- assert_in_delta(1.264, r.real, 0.001)
- assert_in_delta(1.150, r.imag, 0.001)
+ r = c ** Rational(2,3)
+ assert_in_delta(1.264, r.real, 0.001)
+ assert_in_delta(1.150, r.imag, 0.001)
- r = c ** Rational(-2,3)
- assert_in_delta(0.432, r.real, 0.001)
- assert_in_delta(-0.393, r.imag, 0.001)
- end
+ r = c ** Rational(-2,3)
+ assert_in_delta(0.432, r.real, 0.001)
+ assert_in_delta(-0.393, r.imag, 0.001)
+
+ c = Complex(0.0, -888888888888888.0)**8888
+ assert_not_predicate(c.real, :nan?)
+ assert_not_predicate(c.imag, :nan?)
end
def test_cmp
@@ -493,19 +522,19 @@ class Complex_Test < Test::Unit::TestCase
end
def test_eqeq
- assert(Complex(1,0) == Complex(1))
- assert(Complex(-1,0) == Complex(-1))
+ assert_equal(Complex(1), Complex(1,0))
+ assert_equal(Complex(-1), Complex(-1,0))
- assert_equal(false, Complex(2,1) == Complex(1))
- assert_equal(true, Complex(2,1) != Complex(1))
- assert_equal(false, Complex(1) == nil)
- assert_equal(false, Complex(1) == '')
+ assert_not_equal(Complex(1), Complex(2,1))
+ assert_operator(Complex(2,1), :!=, Complex(1))
+ assert_not_equal(nil, Complex(1))
+ assert_not_equal('', Complex(1))
nan = 0.0 / 0
if nan.nan? && nan != nan
- assert_equal(false, Complex(nan, 0) == Complex(nan, 0))
- assert_equal(false, Complex(0, nan) == Complex(0, nan))
- assert_equal(false, Complex(nan, nan) == Complex(nan, nan))
+ assert_not_equal(Complex(nan, 0), Complex(nan, 0))
+ assert_not_equal(Complex(0, nan), Complex(0, nan))
+ assert_not_equal(Complex(nan, nan), Complex(nan, nan))
end
end
@@ -515,17 +544,25 @@ class Complex_Test < Test::Unit::TestCase
assert_equal([Complex(Rational(2)),Complex(1)],
Complex(1).coerce(Rational(2)))
assert_equal([Complex(2),Complex(1)], Complex(1).coerce(Complex(2)))
+
+ obj = eval("class C\u{1f5ff}; self; end").new
+ assert_raise_with_message(TypeError, /C\u{1f5ff}/) { Complex(1).coerce(obj) }
+ end
+
+ class ObjectX
+ def +(x) Rational(1) end
+ alias - +
+ alias * +
+ alias / +
+ alias quo +
+ alias ** +
+ def coerce(x) [x, Complex(1)] end
end
- def test_unify
- if @unify
- assert_instance_of(Fixnum, Complex(1,2) + Complex(-1,-2))
- assert_instance_of(Fixnum, Complex(1,2) - Complex(1,2))
- assert_instance_of(Fixnum, Complex(1,2) * 0)
- assert_instance_of(Fixnum, Complex(1,2) / Complex(1,2))
-# assert_instance_of(Fixnum, Complex(1,2).div(Complex(1,2)))
- assert_instance_of(Fixnum, Complex(1,2).quo(Complex(1,2)))
-# assert_instance_of(Fixnum, Complex(1,2) ** 0) # mathn's bug
+ def test_coerce2
+ x = ObjectX.new
+ %w(+ - * / quo **).each do |op|
+ assert_kind_of(Numeric, Complex(1).__send__(op, x))
end
end
@@ -550,8 +587,6 @@ class Complex_Test < Test::Unit::TestCase
assert_in_delta(1.107, r[1], 0.001)
assert_equal(Complex(1,-2), c.conjugate)
assert_equal(Complex(1,-2), c.conj)
-# assert_equal(Complex(1,-2), ~c)
-# assert_equal(5, c * ~c)
assert_equal(Complex(1,2), c.numerator)
assert_equal(1, c.denominator)
@@ -579,23 +614,21 @@ class Complex_Test < Test::Unit::TestCase
assert_equal('1.0-2.0i', Complex(1.0,-2.0).to_s)
assert_equal('-1.0-2.0i', Complex(-1.0,-2.0).to_s)
- if @rational && !@unify && !@keiju
- assert_equal('0+2/1i', Complex(0,Rational(2)).to_s)
- assert_equal('0-2/1i', Complex(0,Rational(-2)).to_s)
- assert_equal('1+2/1i', Complex(1,Rational(2)).to_s)
- assert_equal('-1+2/1i', Complex(-1,Rational(2)).to_s)
- assert_equal('-1-2/1i', Complex(-1,Rational(-2)).to_s)
- assert_equal('1-2/1i', Complex(1,Rational(-2)).to_s)
- assert_equal('-1-2/1i', Complex(-1,Rational(-2)).to_s)
-
- assert_equal('0+2/3i', Complex(0,Rational(2,3)).to_s)
- assert_equal('0-2/3i', Complex(0,Rational(-2,3)).to_s)
- assert_equal('1+2/3i', Complex(1,Rational(2,3)).to_s)
- assert_equal('-1+2/3i', Complex(-1,Rational(2,3)).to_s)
- assert_equal('-1-2/3i', Complex(-1,Rational(-2,3)).to_s)
- assert_equal('1-2/3i', Complex(1,Rational(-2,3)).to_s)
- assert_equal('-1-2/3i', Complex(-1,Rational(-2,3)).to_s)
- end
+ assert_equal('0+2/1i', Complex(0,Rational(2)).to_s)
+ assert_equal('0-2/1i', Complex(0,Rational(-2)).to_s)
+ assert_equal('1+2/1i', Complex(1,Rational(2)).to_s)
+ assert_equal('-1+2/1i', Complex(-1,Rational(2)).to_s)
+ assert_equal('-1-2/1i', Complex(-1,Rational(-2)).to_s)
+ assert_equal('1-2/1i', Complex(1,Rational(-2)).to_s)
+ assert_equal('-1-2/1i', Complex(-1,Rational(-2)).to_s)
+
+ assert_equal('0+2/3i', Complex(0,Rational(2,3)).to_s)
+ assert_equal('0-2/3i', Complex(0,Rational(-2,3)).to_s)
+ assert_equal('1+2/3i', Complex(1,Rational(2,3)).to_s)
+ assert_equal('-1+2/3i', Complex(-1,Rational(2,3)).to_s)
+ assert_equal('-1-2/3i', Complex(-1,Rational(-2,3)).to_s)
+ assert_equal('1-2/3i', Complex(1,Rational(-2,3)).to_s)
+ assert_equal('-1-2/3i', Complex(-1,Rational(-2,3)).to_s)
nan = 0.0 / 0
inf = 1.0 / 0
@@ -617,21 +650,32 @@ class Complex_Test < Test::Unit::TestCase
def test_marshal
c = Complex(1,2)
- c.instance_eval{@ivar = 9}
s = Marshal.dump(c)
c2 = Marshal.load(s)
assert_equal(c, c2)
- assert_equal(9, c2.instance_variable_get(:@ivar))
assert_instance_of(Complex, c2)
- if @rational
- c = Complex(Rational(1,2),Rational(2,3))
+ c = Complex(Rational(1,2),Rational(2,3))
+
+ s = Marshal.dump(c)
+ c2 = Marshal.load(s)
+ assert_equal(c, c2)
+ assert_instance_of(Complex, c2)
+
+ bug3656 = '[ruby-core:31622]'
+ c = Complex(1,2)
+ assert_predicate(c, :frozen?)
+ result = c.marshal_load([2,3]) rescue :fail
+ assert_equal(:fail, result, bug3656)
+ assert_equal(Complex(1,2), c)
+ end
- s = Marshal.dump(c)
- c2 = Marshal.load(s)
- assert_equal(c, c2)
- assert_instance_of(Complex, c2)
+ def test_marshal_compatibility
+ bug6625 = '[ruby-core:45775]'
+ dump = "\x04\x08o:\x0cComplex\x07:\x0a@reali\x06:\x0b@imagei\x07"
+ assert_nothing_raised(bug6625) do
+ assert_equal(Complex(1, 2), Marshal.load(dump), bug6625)
end
end
@@ -667,6 +711,15 @@ class Complex_Test < Test::Unit::TestCase
assert_equal(Complex(0.0,3.0), '3.0i'.to_c)
assert_equal(Complex(0.0,-3.0), '-3.0i'.to_c)
+ assert_equal(Complex(5.1), '5.1'.to_c)
+ assert_equal(Complex(-5.2), '-5.2'.to_c)
+ assert_equal(Complex(5.3,3.4), '5.3+3.4i'.to_c)
+ assert_equal(Complex(-5.5,3.6), '-5.5+3.6i'.to_c)
+ assert_equal(Complex(5.3,-3.4), '5.3-3.4i'.to_c)
+ assert_equal(Complex(-5.5,-3.6), '-5.5-3.6i'.to_c)
+ assert_equal(Complex(0.0,3.1), '3.1i'.to_c)
+ assert_equal(Complex(0.0,-3.2), '-3.2i'.to_c)
+
assert_equal(Complex(5.0), '5e0'.to_c)
assert_equal(Complex(-5.0), '-5e0'.to_c)
assert_equal(Complex(5.0,3.0), '5e0+3e0i'.to_c)
@@ -676,6 +729,15 @@ class Complex_Test < Test::Unit::TestCase
assert_equal(Complex(0.0,3.0), '3e0i'.to_c)
assert_equal(Complex(0.0,-3.0), '-3e0i'.to_c)
+ assert_equal(Complex(5e1), '5e1'.to_c)
+ assert_equal(Complex(-5e2), '-5e2'.to_c)
+ assert_equal(Complex(5e3,3e4), '5e003+3e4i'.to_c)
+ assert_equal(Complex(-5e5,3e6), '-5e5+3e006i'.to_c)
+ assert_equal(Complex(5e3,-3e4), '5e003-3e4i'.to_c)
+ assert_equal(Complex(-5e5,-3e6), '-5e5-3e006i'.to_c)
+ assert_equal(Complex(0.0,3e1), '3e1i'.to_c)
+ assert_equal(Complex(0.0,-3e2), '-3e2i'.to_c)
+
assert_equal(Complex(0.33), '.33'.to_c)
assert_equal(Complex(0.33), '0.33'.to_c)
assert_equal(Complex(-0.33), '-.33'.to_c)
@@ -718,6 +780,15 @@ class Complex_Test < Test::Unit::TestCase
assert_equal(Complex(0.0,3.0), Complex('3.0i'))
assert_equal(Complex(0.0,-3.0), Complex('-3.0i'))
+ assert_equal(Complex(5.1), Complex('5.1'))
+ assert_equal(Complex(-5.2), Complex('-5.2'))
+ assert_equal(Complex(5.3,3.4), Complex('5.3+3.4i'))
+ assert_equal(Complex(-5.5,3.6), Complex('-5.5+3.6i'))
+ assert_equal(Complex(5.3,-3.4), Complex('5.3-3.4i'))
+ assert_equal(Complex(-5.5,-3.6), Complex('-5.5-3.6i'))
+ assert_equal(Complex(0.0,3.1), Complex('3.1i'))
+ assert_equal(Complex(0.0,-3.2), Complex('-3.2i'))
+
assert_equal(Complex(5.0), Complex('5e0'))
assert_equal(Complex(-5.0), Complex('-5e0'))
assert_equal(Complex(5.0,3.0), Complex('5e0+3e0i'))
@@ -727,6 +798,15 @@ class Complex_Test < Test::Unit::TestCase
assert_equal(Complex(0.0,3.0), Complex('3e0i'))
assert_equal(Complex(0.0,-3.0), Complex('-3e0i'))
+ assert_equal(Complex(5e1), Complex('5e1'))
+ assert_equal(Complex(-5e2), Complex('-5e2'))
+ assert_equal(Complex(5e3,3e4), Complex('5e003+3e4i'))
+ assert_equal(Complex(-5e5,3e6), Complex('-5e5+3e006i'))
+ assert_equal(Complex(5e3,-3e4), Complex('5e003-3e4i'))
+ assert_equal(Complex(-5e5,-3e6), Complex('-5e5-3e006i'))
+ assert_equal(Complex(0.0,3e1), Complex('3e1i'))
+ assert_equal(Complex(0.0,-3e2), Complex('-3e2i'))
+
assert_equal(Complex(0.33), Complex('.33'))
assert_equal(Complex(0.33), Complex('0.33'))
assert_equal(Complex(-0.33), Complex('-.33'))
@@ -760,57 +840,81 @@ class Complex_Test < Test::Unit::TestCase
assert_raise(ArgumentError){ Complex('5+3i_')}
assert_raise(ArgumentError){ Complex('5+3ix')}
- if @rational && defined?(''.to_r)
- assert_equal(Complex(Rational(1,5)), '1/5'.to_c)
- assert_equal(Complex(Rational(-1,5)), '-1/5'.to_c)
- assert_equal(Complex(Rational(1,5),3), '1/5+3i'.to_c)
- assert_equal(Complex(Rational(1,5),-3), '1/5-3i'.to_c)
- assert_equal(Complex(Rational(-1,5),3), '-1/5+3i'.to_c)
- assert_equal(Complex(Rational(-1,5),-3), '-1/5-3i'.to_c)
- assert_equal(Complex(Rational(1,5),Rational(3,2)), '1/5+3/2i'.to_c)
- assert_equal(Complex(Rational(1,5),Rational(-3,2)), '1/5-3/2i'.to_c)
- assert_equal(Complex(Rational(-1,5),Rational(3,2)), '-1/5+3/2i'.to_c)
- assert_equal(Complex(Rational(-1,5),Rational(-3,2)), '-1/5-3/2i'.to_c)
- assert_equal(Complex(Rational(1,5),Rational(3,2)), '1/5+3/2i'.to_c)
- assert_equal(Complex(Rational(1,5),Rational(-3,2)), '1/5-3/2i'.to_c)
- assert_equal(Complex(Rational(-1,5),Rational(3,2)), '-1/5+3/2i'.to_c)
- assert_equal(Complex(Rational(-1,5),Rational(-3,2)), '-1/5-3/2i'.to_c)
- assert_equal(Complex.polar(Rational(1,5),Rational(3,2)), Complex('1/5@3/2'))
- assert_equal(Complex.polar(Rational(-1,5),Rational(-3,2)), Complex('-1/5@-3/2'))
- end
+ assert_equal(Complex(Rational(1,5)), '1/5'.to_c)
+ assert_equal(Complex(Rational(-1,5)), '-1/5'.to_c)
+ assert_equal(Complex(Rational(1,5),3), '1/5+3i'.to_c)
+ assert_equal(Complex(Rational(1,5),-3), '1/5-3i'.to_c)
+ assert_equal(Complex(Rational(-1,5),3), '-1/5+3i'.to_c)
+ assert_equal(Complex(Rational(-1,5),-3), '-1/5-3i'.to_c)
+ assert_equal(Complex(Rational(1,5),Rational(3,2)), '1/5+3/2i'.to_c)
+ assert_equal(Complex(Rational(1,5),Rational(-3,2)), '1/5-3/2i'.to_c)
+ assert_equal(Complex(Rational(-1,5),Rational(3,2)), '-1/5+3/2i'.to_c)
+ assert_equal(Complex(Rational(-1,5),Rational(-3,2)), '-1/5-3/2i'.to_c)
+ assert_equal(Complex(Rational(1,5),Rational(3,2)), '1/5+3/2i'.to_c)
+ assert_equal(Complex(Rational(1,5),Rational(-3,2)), '1/5-3/2i'.to_c)
+ assert_equal(Complex(Rational(-1,5),Rational(3,2)), '-1/5+3/2i'.to_c)
+ assert_equal(Complex(Rational(-1,5),Rational(-3,2)), '-1/5-3/2i'.to_c)
+ assert_equal(Complex.polar(Rational(1,5),Rational(3,2)), Complex('1/5@3/2'))
+ assert_equal(Complex.polar(Rational(-1,5),Rational(-3,2)), Complex('-1/5@-3/2'))
end
+ def test_Complex_without_exception
+ assert_nothing_raised(ArgumentError){
+ assert_equal(nil, Complex('5x', exception: false))
+ }
+ assert_nothing_raised(ArgumentError){
+ assert_equal(nil, Complex(nil, exception: false))
+ }
+ assert_nothing_raised(ArgumentError){
+ assert_equal(nil, Complex(Object.new, exception: false))
+ }
+ assert_nothing_raised(ArgumentError){
+ assert_equal(nil, Complex(1, nil, exception: false))
+ }
+ assert_nothing_raised(ArgumentError){
+ assert_equal(nil, Complex(1, Object.new, exception: false))
+ }
+
+ o = Object.new
+ def o.to_c; raise; end
+ assert_nothing_raised(ArgumentError){
+ assert_equal(nil, Complex(o, exception: false))
+ }
+ assert_nothing_raised(ArgumentError){
+ assert_equal(nil, Complex(1, o, exception: false))
+ }
+ end
+
def test_respond
c = Complex(1,1)
- assert_equal(false, c.respond_to?(:%))
- assert_equal(false, c.respond_to?(:<))
- assert_equal(false, c.respond_to?(:<=))
- assert_equal(false, c.respond_to?(:<=>))
- assert_equal(false, c.respond_to?(:>))
- assert_equal(false, c.respond_to?(:>=))
- assert_equal(false, c.respond_to?(:between?))
- assert_equal(false, c.respond_to?(:div))
- assert_equal(false, c.respond_to?(:divmod))
- assert_equal(false, c.respond_to?(:floor))
- assert_equal(false, c.respond_to?(:ceil))
- assert_equal(false, c.respond_to?(:modulo))
- assert_equal(false, c.respond_to?(:remainder))
- assert_equal(false, c.respond_to?(:round))
- assert_equal(false, c.respond_to?(:step))
- assert_equal(false, c.respond_to?(:tunrcate))
-
- assert_equal(false, c.respond_to?(:positive?))
- assert_equal(false, c.respond_to?(:negative?))
-# assert_equal(false, c.respond_to?(:sign))
-
- assert_equal(false, c.respond_to?(:quotient))
- assert_equal(false, c.respond_to?(:quot))
- assert_equal(false, c.respond_to?(:quotrem))
-
- assert_equal(false, c.respond_to?(:gcd))
- assert_equal(false, c.respond_to?(:lcm))
- assert_equal(false, c.respond_to?(:gcdlcm))
+ assert_not_respond_to(c, :%)
+ assert_not_respond_to(c, :<=>)
+ assert_not_respond_to(c, :div)
+ assert_not_respond_to(c, :divmod)
+ assert_not_respond_to(c, :floor)
+ assert_not_respond_to(c, :ceil)
+ assert_not_respond_to(c, :modulo)
+ assert_not_respond_to(c, :remainder)
+ assert_not_respond_to(c, :round)
+ assert_not_respond_to(c, :step)
+ assert_not_respond_to(c, :tunrcate)
+
+ assert_not_respond_to(c, :positive?)
+ assert_not_respond_to(c, :negative?)
+ assert_not_respond_to(c, :sign)
+
+ assert_not_respond_to(c, :quotient)
+ assert_not_respond_to(c, :quot)
+ assert_not_respond_to(c, :quotrem)
+
+ assert_not_respond_to(c, :gcd)
+ assert_not_respond_to(c, :lcm)
+ assert_not_respond_to(c, :gcdlcm)
+
+ (Comparable.instance_methods(false) - Complex.instance_methods(false)).each do |n|
+ assert_not_respond_to(c, n, "Complex##{n}")
+ end
end
def test_to_i
@@ -828,12 +932,10 @@ class Complex_Test < Test::Unit::TestCase
end
def test_to_r
- if @rational && !@keiju
- assert_equal(Rational(3), Complex(3).to_r)
- assert_equal(Rational(3), Rational(Complex(3)))
- assert_raise(RangeError){Complex(3,2).to_r}
-# assert_raise(RangeError){Rational(Complex(3,2))}
- end
+ assert_equal(Rational(3), Complex(3).to_r)
+ assert_equal(Rational(3), Rational(Complex(3)))
+ assert_raise(RangeError){Complex(3,2).to_r}
+ assert_raise(RangeError){Rational(Complex(3,2))}
end
def test_to_c
@@ -849,10 +951,8 @@ class Complex_Test < Test::Unit::TestCase
c = 1.1.to_c
assert_equal([1.1, 0], [c.real, c.imag])
- if @rational
- c = Rational(1,2).to_c
- assert_equal([Rational(1,2), 0], [c.real, c.imag])
- end
+ c = Rational(1,2).to_c
+ assert_equal([Rational(1,2), 0], [c.real, c.imag])
c = Complex(1,2).to_c
assert_equal([1, 2], [c.real, c.imag])
@@ -865,9 +965,45 @@ class Complex_Test < Test::Unit::TestCase
end
end
+ def test_finite_p
+ assert_predicate(1+1i, :finite?)
+ assert_predicate(1-1i, :finite?)
+ assert_predicate(-1+1i, :finite?)
+ assert_predicate(-1-1i, :finite?)
+ assert_not_predicate(Float::INFINITY + 1i, :finite?)
+ assert_not_predicate(Complex(1, Float::INFINITY), :finite?)
+ assert_predicate(Complex(Float::MAX, 0.0), :finite?)
+ assert_predicate(Complex(0.0, Float::MAX), :finite?)
+ assert_predicate(Complex(Float::MAX, Float::MAX), :finite?)
+ assert_not_predicate(Complex(Float::NAN, 0), :finite?)
+ assert_not_predicate(Complex(0, Float::NAN), :finite?)
+ assert_not_predicate(Complex(Float::NAN, Float::NAN), :finite?)
+ end
+
+ def test_infinite_p
+ assert_nil((1+1i).infinite?)
+ assert_nil((1-1i).infinite?)
+ assert_nil((-1+1i).infinite?)
+ assert_nil((-1-1i).infinite?)
+ assert_equal(1, (Float::INFINITY + 1i).infinite?)
+ assert_equal(1, (Float::INFINITY - 1i).infinite?)
+ assert_equal(1, (-Float::INFINITY + 1i).infinite?)
+ assert_equal(1, (-Float::INFINITY - 1i).infinite?)
+ assert_equal(1, Complex(1, Float::INFINITY).infinite?)
+ assert_equal(1, Complex(-1, Float::INFINITY).infinite?)
+ assert_equal(1, Complex(1, -Float::INFINITY).infinite?)
+ assert_equal(1, Complex(-1, -Float::INFINITY).infinite?)
+ assert_nil(Complex(Float::MAX, 0.0).infinite?)
+ assert_nil(Complex(0.0, Float::MAX).infinite?)
+ assert_nil(Complex(Float::MAX, Float::MAX).infinite?)
+ assert_nil(Complex(Float::NAN, 0).infinite?)
+ assert_nil(Complex(0, Float::NAN).infinite?)
+ assert_nil(Complex(Float::NAN, Float::NAN).infinite?)
+ end
+
def test_supp
- assert_equal(true, 1.real?)
- assert_equal(true, 1.1.real?)
+ assert_predicate(1, :real?)
+ assert_predicate(1.1, :real?)
assert_equal(1, 1.real)
assert_equal(0, 1.imag)
@@ -897,9 +1033,9 @@ class Complex_Test < Test::Unit::TestCase
if (0.0/0).nan?
nan = 0.0/0
- assert(nan.arg.equal?(nan))
- assert(nan.angle.equal?(nan))
- assert(nan.phase.equal?(nan))
+ assert_same(nan, nan.arg)
+ assert_same(nan, nan.angle)
+ assert_same(nan, nan.phase)
end
assert_equal(Math::PI, -1.arg)
@@ -936,134 +1072,13 @@ class Complex_Test < Test::Unit::TestCase
assert_equal(1.1, 1.1.conj)
assert_equal(-1.1, -1.1.conj)
- if @rational
- assert_equal(Complex(Rational(1,2),Rational(1)), Complex(1,2).quo(2))
- else
- assert_equal(Complex(0.5,1.0), Complex(1,2).quo(2))
- end
-
-=begin
- if @rational && !@keiju
- assert_equal(Complex(Rational(1,2),Rational(1)), Complex(1,2).quo(2))
- end
-=end
+ assert_equal(Complex(Rational(1,2),Rational(1)), Complex(1,2).quo(2))
assert_equal(0.5, 1.fdiv(2))
assert_equal(5000000000.0, 10000000000.fdiv(2))
assert_equal(0.5, 1.0.fdiv(2))
- if @rational
- assert_equal(0.25, Rational(1,2).fdiv(2))
- end
+ assert_equal(0.25, Rational(1,2).fdiv(2))
assert_equal(Complex(0.5,1.0), Complex(1,2).quo(2))
-
- unless $".grep(/(\A|\/)complex/).empty?
- assert_equal(Complex(0,2), Math.sqrt(-4.0))
-# assert_equal(true, Math.sqrt(-4.0).inexact?)
- assert_equal(Complex(0,2), Math.sqrt(-4))
-# assert_equal(true, Math.sqrt(-4).exact?)
- if @rational
- assert_equal(Complex(0,2), Math.sqrt(Rational(-4)))
-# assert_equal(true, Math.sqrt(Rational(-4)).exact?)
- end
-
- assert_equal(Complex(0,3), Math.sqrt(-9.0))
-# assert_equal(true, Math.sqrt(-9.0).inexact?)
- assert_equal(Complex(0,3), Math.sqrt(-9))
-# assert_equal(true, Math.sqrt(-9).exact?)
- if @rational
- assert_equal(Complex(0,3), Math.sqrt(Rational(-9)))
-# assert_equal(true, Math.sqrt(Rational(-9)).exact?)
- end
-
- c = Math.sqrt(Complex(1, 2))
- assert_in_delta(1.272, c.real, 0.001)
- assert_in_delta(0.786, c.imag, 0.001)
-
- c = Math.sqrt(-9)
- assert_in_delta(0.0, c.real, 0.001)
- assert_in_delta(3.0, c.imag, 0.001)
-
- c = Math.exp(Complex(1, 2))
- assert_in_delta(-1.131, c.real, 0.001)
- assert_in_delta(2.471, c.imag, 0.001)
-
- c = Math.sin(Complex(1, 2))
- assert_in_delta(3.165, c.real, 0.001)
- assert_in_delta(1.959, c.imag, 0.001)
-
- c = Math.cos(Complex(1, 2))
- assert_in_delta(2.032, c.real, 0.001)
- assert_in_delta(-3.051, c.imag, 0.001)
-
- c = Math.tan(Complex(1, 2))
- assert_in_delta(0.033, c.real, 0.001)
- assert_in_delta(1.014, c.imag, 0.001)
-
- c = Math.sinh(Complex(1, 2))
- assert_in_delta(-0.489, c.real, 0.001)
- assert_in_delta(1.403, c.imag, 0.001)
-
- c = Math.cosh(Complex(1, 2))
- assert_in_delta(-0.642, c.real, 0.001)
- assert_in_delta(1.068, c.imag, 0.001)
-
- c = Math.tanh(Complex(1, 2))
- assert_in_delta(1.166, c.real, 0.001)
- assert_in_delta(-0.243, c.imag, 0.001)
-
- c = Math.log(Complex(1, 2))
- assert_in_delta(0.804, c.real, 0.001)
- assert_in_delta(1.107, c.imag, 0.001)
-
- c = Math.log(Complex(1, 2), Math::E)
- assert_in_delta(0.804, c.real, 0.001)
- assert_in_delta(1.107, c.imag, 0.001)
-
- c = Math.log(-1)
- assert_in_delta(0.0, c.real, 0.001)
- assert_in_delta(Math::PI, c.imag, 0.001)
-
- c = Math.log(8, 2)
- assert_in_delta(3.0, c.real, 0.001)
- assert_in_delta(0.0, c.imag, 0.001)
-
- c = Math.log(-8, -2)
- assert_in_delta(1.092, c.real, 0.001)
- assert_in_delta(-0.420, c.imag, 0.001)
-
- c = Math.log10(Complex(1, 2))
- assert_in_delta(0.349, c.real, 0.001)
- assert_in_delta(0.480, c.imag, 0.001)
-
- c = Math.asin(Complex(1, 2))
- assert_in_delta(0.427, c.real, 0.001)
- assert_in_delta(1.528, c.imag, 0.001)
-
- c = Math.acos(Complex(1, 2))
- assert_in_delta(1.143, c.real, 0.001)
- assert_in_delta(-1.528, c.imag, 0.001)
-
- c = Math.atan(Complex(1, 2))
- assert_in_delta(1.338, c.real, 0.001)
- assert_in_delta(0.402, c.imag, 0.001)
-
- c = Math.atan2(Complex(1, 2), 1)
- assert_in_delta(1.338, c.real, 0.001)
- assert_in_delta(0.402, c.imag, 0.001)
-
- c = Math.asinh(Complex(1, 2))
- assert_in_delta(1.469, c.real, 0.001)
- assert_in_delta(1.063, c.imag, 0.001)
-
- c = Math.acosh(Complex(1, 2))
- assert_in_delta(1.528, c.real, 0.001)
- assert_in_delta(1.143, c.imag, 0.001)
-
- c = Math.atanh(Complex(1, 2))
- assert_in_delta(0.173, c.real, 0.001)
- assert_in_delta(1.178, c.imag, 0.001)
- end
-
end
def test_ruby19
@@ -1073,13 +1088,40 @@ class Complex_Test < Test::Unit::TestCase
end
def test_fixed_bug
- if @rational && !@keiju
- assert_equal(Complex(1), 1 ** Complex(1))
- end
+ assert_equal(Complex(1), 1 ** Complex(1))
assert_equal('-1.0-0.0i', Complex(-1.0, -0.0).to_s)
+ assert_in_delta(Math::PI, Complex(-0.0).arg, 0.001)
+ assert_equal(Complex(2e3, 2e4), '2e3+2e4i'.to_c)
+ assert_raise(ArgumentError){ Complex('--8i')}
end
def test_known_bug
end
+ def test_canonicalize_internal
+ obj = Class.new(Numeric) do
+ attr_accessor :real
+ alias real? real
+ end.new
+ obj.real = true
+ c = Complex.rect(obj, 1);
+ obj.real = false
+ c = c.conj
+ assert_equal(obj, c.real)
+ assert_equal(-1, c.imag)
+ end
+
+ def test_canonicalize_polar
+ obj = Class.new(Numeric) do
+ def initialize
+ @x = 2
+ end
+ def real?
+ (@x -= 1) > 0
+ end
+ end.new
+ assert_raise(TypeError) do
+ Complex.polar(1, obj)
+ end
+ end
end
diff --git a/test/ruby/test_complex2.rb b/test/ruby/test_complex2.rb
index 4e960c3e36..594fc3f45a 100644
--- a/test/ruby/test_complex2.rb
+++ b/test/ruby/test_complex2.rb
@@ -1,9 +1,10 @@
+# frozen_string_literal: false
require 'test/unit'
class Complex_Test2 < Test::Unit::TestCase
def test_kumi
- return unless defined?(Rational)
+ skip unless defined?(Rational)
assert_equal(Complex(1, 0), +Complex(1, 0))
assert_equal(Complex(-1, 0), -Complex(1, 0))
diff --git a/test/ruby/test_complexrational.rb b/test/ruby/test_complexrational.rb
index 47c535fca0..0360f5ee42 100644
--- a/test/ruby/test_complexrational.rb
+++ b/test/ruby/test_complexrational.rb
@@ -1,9 +1,10 @@
+# frozen_string_literal: false
require 'test/unit'
class ComplexRational_Test < Test::Unit::TestCase
def test_rat_srat
- return unless defined?(Rational)
+ skip unless defined?(Rational)
c = SimpleRat(1,3)
cc = Rational(3,2)
@@ -74,8 +75,8 @@ class ComplexRational_Test < Test::Unit::TestCase
assert_equal(0, Rational(2,3) <=> SimpleRat(2,3))
assert_equal(0, SimpleRat(2,3) <=> Rational(2,3))
- assert(Rational(2,3) == SimpleRat(2,3))
- assert(SimpleRat(2,3) == Rational(2,3))
+ assert_equal(Rational(2,3), SimpleRat(2,3))
+ assert_equal(SimpleRat(2,3), Rational(2,3))
assert_equal(SimpleRat, (c + 0).class)
assert_equal(SimpleRat, (c - 0).class)
@@ -88,7 +89,7 @@ class ComplexRational_Test < Test::Unit::TestCase
end
def test_comp_srat
- return unless defined?(Rational)
+ skip unless defined?(Rational)
c = Complex(SimpleRat(2,3),SimpleRat(1,2))
cc = Complex(Rational(3,2),Rational(2,1))
@@ -168,10 +169,10 @@ class ComplexRational_Test < Test::Unit::TestCase
assert_equal([Float,Float],
(cc ** c).instance_eval{[real.class, imag.class]})
- assert(Complex(SimpleRat(2,3),SimpleRat(3,2)) ==
- Complex(Rational(2,3),Rational(3,2)))
- assert(Complex(Rational(2,3),Rational(3,2)) ==
- Complex(SimpleRat(2,3),SimpleRat(3,2)))
+ assert_equal(Complex(SimpleRat(2,3),SimpleRat(3,2)),
+ Complex(Rational(2,3),Rational(3,2)))
+ assert_equal(Complex(Rational(2,3),Rational(3,2)),
+ Complex(SimpleRat(2,3),SimpleRat(3,2)))
assert_equal([SimpleRat,SimpleRat],
(c + 0).instance_eval{[real.class, imag.class]})
@@ -213,10 +214,10 @@ class SimpleRat < Numeric
def numerator() @num end
def denominator() @den end
- def +@ () self end
- def -@ () self.class.new(-@num, @den) end
+ def +@() self end
+ def -@() self.class.new(-@num, @den) end
- def + (o)
+ def +(o)
case o
when SimpleRat, Rational
a = @num * o.denominator
@@ -232,7 +233,7 @@ class SimpleRat < Numeric
end
end
- def - (o)
+ def -(o)
case o
when SimpleRat, Rational
a = @num * o.denominator
@@ -248,7 +249,7 @@ class SimpleRat < Numeric
end
end
- def * (o)
+ def *(o)
case o
when SimpleRat, Rational
a = @num * o.numerator
@@ -272,7 +273,7 @@ class SimpleRat < Numeric
self.class.new(a, b)
when Integer
if o == 0
- raise raise ZeroDivisionError, "divided by zero"
+ raise ZeroDivisionError, "divided by zero"
end
self.quo(self.class.new(o))
when Float
@@ -333,7 +334,7 @@ class SimpleRat < Numeric
def divmod(o) [div(o), modulo(o)] end
def quotrem(o) [quot(o), remainder(o)] end
- def ** (o)
+ def **(o)
case o
when SimpleRat, Rational
Float(self) ** o
@@ -356,7 +357,7 @@ class SimpleRat < Numeric
end
end
- def <=> (o)
+ def <=>(o)
case o
when SimpleRat, Rational
a = @num * o.denominator
@@ -372,7 +373,7 @@ class SimpleRat < Numeric
end
end
- def == (o)
+ def ==(o)
begin
(self <=> o) == 0
rescue
diff --git a/test/ruby/test_condition.rb b/test/ruby/test_condition.rb
index ba2e0688f3..ab0ffc4b6a 100644
--- a/test/ruby/test_condition.rb
+++ b/test/ruby/test_condition.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestCondition < Test::Unit::TestCase
diff --git a/test/ruby/test_const.rb b/test/ruby/test_const.rb
index 3708a5a0ca..8784e0e988 100644
--- a/test/ruby/test_const.rb
+++ b/test/ruby/test_const.rb
@@ -1,3 +1,5 @@
+# -*- coding: us-ascii -*-
+# frozen_string_literal: false
require 'test/unit'
class TestConst < Test::Unit::TestCase
@@ -35,7 +37,7 @@ class TestConst < Test::Unit::TestCase
self.class.class_eval {
include Const2
}
- STDERR.print "intentionally redefines TEST3, TEST4\n" if $VERBOSE
+ # STDERR.print "intentionally redefines TEST3, TEST4\n" if $VERBOSE
assert defined?(TEST1)
assert_equal 1, TEST1
assert defined?(TEST2)
@@ -45,4 +47,26 @@ class TestConst < Test::Unit::TestCase
assert defined?(TEST4)
assert_equal 8, TEST4
end
+
+ def test_redefinition
+ c = Class.new
+ name = "X\u{5b9a 6570}"
+ c.const_set(name, 1)
+ prev_line = __LINE__ - 1
+ assert_warning(<<-WARNING) {c.const_set(name, 2)}
+#{__FILE__}:#{__LINE__-1}: warning: already initialized constant #{c}::#{name}
+#{__FILE__}:#{prev_line}: warning: previous definition of #{name} was here
+WARNING
+ end
+
+ def test_redefinition_memory_leak
+ code = <<-PRE
+350000.times { FOO = :BAR }
+PRE
+ assert_no_memory_leak(%w[-W0 -], '', code, 'redefined constant', timeout: 30)
+ end
+
+ def test_toplevel_lookup
+ assert_raise(NameError, '[Feature #11547]') {TestConst::Object}
+ end
end
diff --git a/test/ruby/test_continuation.rb b/test/ruby/test_continuation.rb
index c719db8c35..8c62d20840 100644
--- a/test/ruby/test_continuation.rb
+++ b/test/ruby/test_continuation.rb
@@ -1,7 +1,7 @@
+# frozen_string_literal: false
require 'test/unit'
-require 'continuation'
+EnvUtil.suppress_warning {require 'continuation'}
require 'fiber'
-require_relative 'envutil'
class TestContinuation < Test::Unit::TestCase
def test_create
@@ -37,9 +37,11 @@ class TestContinuation < Test::Unit::TestCase
def test_error
cont = callcc{|c| c}
- assert_raise(RuntimeError){
- Thread.new{cont.call}.join
- }
+ Thread.new{
+ assert_raise(RuntimeError){
+ cont.call
+ }
+ }.join
assert_raise(LocalJumpError){
callcc
}
@@ -77,5 +79,67 @@ class TestContinuation < Test::Unit::TestCase
}, '[ruby-dev:34802]'
end
-end
+ def tracing_with_set_trace_func
+ orig_thread = Thread.current
+ cont = nil
+ func = lambda do |*args|
+ if orig_thread == Thread.current
+ if cont
+ @memo += 1
+ c = cont
+ cont = nil
+ begin
+ c.call(nil)
+ rescue RuntimeError
+ set_trace_func(nil)
+ end
+ end
+ end
+ end
+ cont = callcc { |cc| cc }
+ if cont
+ set_trace_func(func)
+ else
+ set_trace_func(nil)
+ end
+ end
+
+ def _test_tracing_with_set_trace_func
+ @memo = 0
+ tracing_with_set_trace_func
+ tracing_with_set_trace_func
+ tracing_with_set_trace_func
+ assert_equal 0, @memo
+ end
+
+ def tracing_with_thread_set_trace_func
+ cont = nil
+ func = lambda do |*args|
+ if cont
+ @memo += 1
+ c = cont
+ cont = nil
+ begin
+ c.call(nil)
+ rescue RuntimeError
+ Thread.current.set_trace_func(nil)
+ end
+ end
+ end
+ cont = callcc { |cc| cc }
+ if cont
+ Thread.current.set_trace_func(func)
+ else
+ Thread.current.set_trace_func(nil)
+ end
+ end
+
+ def test_tracing_with_thread_set_trace_func
+ @memo = 0
+ tracing_with_thread_set_trace_func
+ tracing_with_thread_set_trace_func
+ tracing_with_thread_set_trace_func
+ assert_equal 3, @memo
+ end
+end
diff --git a/test/ruby/test_default_gems.rb b/test/ruby/test_default_gems.rb
new file mode 100644
index 0000000000..837f7571ea
--- /dev/null
+++ b/test/ruby/test_default_gems.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: false
+require 'rubygems'
+
+class TestDefaultGems < Test::Unit::TestCase
+
+ def test_validate_gemspec
+ srcdir = File.expand_path('../../..', __FILE__)
+ Dir.glob("#{srcdir}/{lib,ext}/**/*.gemspec").map do |src|
+ assert_nothing_raised do
+ raise("invalid spec in #{src}") unless Gem::Specification.load(src)
+ end
+ end
+ end
+
+end
diff --git a/test/ruby/test_defined.rb b/test/ruby/test_defined.rb
index 07485bd2cc..e1571d5714 100644
--- a/test/ruby/test_defined.rb
+++ b/test/ruby/test_defined.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestDefined < Test::Unit::TestCase
@@ -22,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?($`)
@@ -66,23 +107,198 @@ class TestDefined < Test::Unit::TestCase
/a/ =~ 'a'
assert_equal 'global-variable', defined?($&)
assert_equal 'global-variable', defined?($`)
- assert_equal 'global-variable', defined?($')
+ assert_equal 'global-variable', defined?($') # '
assert_equal nil, defined?($+)
assert_equal nil, defined?($1)
assert_equal nil, defined?($2)
/(a)/ =~ 'a'
assert_equal 'global-variable', defined?($&)
assert_equal 'global-variable', defined?($`)
- assert_equal 'global-variable', defined?($')
+ assert_equal 'global-variable', defined?($') # '
assert_equal 'global-variable', defined?($+)
assert_equal 'global-variable', defined?($1)
assert_equal nil, defined?($2)
/(a)b/ =~ 'ab'
assert_equal 'global-variable', defined?($&)
assert_equal 'global-variable', defined?($`)
- assert_equal 'global-variable', defined?($')
+ assert_equal 'global-variable', defined?($') # '
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_empty_paren_expr
+ bug8224 = '[ruby-core:54024] [Bug #8224]'
+ (1..3).each do |level|
+ expr = "("*level+")"*level
+ assert_equal("nil", eval("defined? #{expr}"), "#{bug8224} defined? #{expr}")
+ assert_equal("nil", eval("defined?(#{expr})"), "#{bug8224} defined?(#{expr})")
+ end
+ end
+
+ def test_defined_empty_paren_arg
+ assert_nil(defined?(p () + 1))
+ end
+
+ def test_defined_impl_specific
+ feature7035 = '[ruby-core:47558]' # not spec
+ assert_predicate(defined?(Foo), :frozen?, feature7035)
+ assert_same(defined?(Foo), defined?(Array), feature7035)
+ end
+
+ class TestAutoloadedSuperclass
+ autoload :A, "a"
+ end
+
+ class TestAutoloadedSubclass < TestAutoloadedSuperclass
+ def a?
+ defined?(A)
+ end
+ end
+
+ def test_autoloaded_subclass
+ bug = "[ruby-core:35509]"
+
+ x = TestAutoloadedSuperclass.new
+ class << x
+ def a?; defined?(A); end
+ end
+ assert_equal("constant", x.a?, bug)
+
+ assert_equal("constant", TestAutoloadedSubclass.new.a?, bug)
+ end
+
+ class TestAutoloadedNoload
+ autoload :A, "a"
+ def a?
+ defined?(A)
+ end
+ def b?
+ defined?(A::B)
+ end
+ end
+
+ def test_autoloaded_noload
+ loaded = $".dup
+ $".clear
+ loadpath = $:.dup
+ $:.clear
+ x = TestAutoloadedNoload.new
+ assert_equal("constant", x.a?)
+ assert_nil(x.b?)
+ assert_equal([], $")
+ ensure
+ $".replace(loaded)
+ $:.replace(loadpath)
+ end
+
+ def test_exception
+ bug5786 = '[ruby-dev:45021]'
+ assert_nil(defined?(raise("[Bug#5786]")::A), bug5786)
+ end
+
+ def test_define_method
+ bug6644 = '[ruby-core:45831]'
+ a = Class.new do
+ def self.def_f!;
+ singleton_class.send(:define_method, :f) { defined? super }
+ end
+ end
+ aa = Class.new(a)
+ a.def_f!
+ assert_nil(a.f)
+ assert_nil(aa.f)
+ aa.def_f!
+ assert_equal("super", aa.f, bug6644)
+ assert_nil(a.f, bug6644)
+ end
+
+ def test_super_in_included_method
+ c0 = Class.new do
+ def m
+ end
+ end
+ m1 = Module.new do
+ def m
+ defined?(super)
+ end
+ end
+ c = Class.new(c0) do include m1
+ def m
+ super
+ end
+ end
+ assert_equal("super", c.new.m)
+ end
+
+ def test_super_in_block
+ bug8367 = '[ruby-core:54769] [Bug #8367]'
+ c = Class.new do
+ def x; end
+ end
+
+ m = Module.new do
+ def b; yield; end
+ def x; b {return defined?(super)}; end
+ end
+
+ o = c.new
+ o.extend(m)
+ assert_equal("super", o.x, bug8367)
+ end
+
+ def test_super_toplevel
+ assert_separately([], "assert_nil(defined?(super))")
+ end
+
+ class ExampleRespondToMissing
+ attr_reader :called
+
+ def initialize
+ @called = false
+ end
+
+ def respond_to_missing? *args
+ @called = true
+ false
+ end
+
+ def existing_method
+ end
+
+ def func_defined_existing_func
+ defined?(existing_method())
+ end
+
+ def func_defined_non_existing_func
+ defined?(non_existing_method())
+ end
+ end
+
+ def test_method_by_respond_to_missing
+ bug_11211 = '[Bug #11211]'
+ obj = ExampleRespondToMissing.new
+ assert_equal("method", defined?(obj.existing_method), bug_11211)
+ assert_equal(false, obj.called, bug_11211)
+ assert_equal(nil, defined?(obj.non_existing_method), bug_11211)
+ assert_equal(true, obj.called, bug_11211)
+
+ bug_11212 = '[Bug #11212]'
+ obj = ExampleRespondToMissing.new
+ assert_equal("method", obj.func_defined_existing_func, bug_11212)
+ assert_equal(false, obj.called, bug_11212)
+ assert_equal(nil, obj.func_defined_non_existing_func, bug_11212)
+ assert_equal(true, obj.called, bug_11212)
+ end
+
+ def test_top_level_constant_not_defined
+ assert_nil(defined?(TestDefined::Object))
+ end
end
diff --git a/test/ruby/test_dir.rb b/test/ruby/test_dir.rb
index 236fd991db..5196c08f81 100644
--- a/test/ruby/test_dir.rb
+++ b/test/ruby/test_dir.rb
@@ -1,21 +1,23 @@
+# frozen_string_literal: false
require 'test/unit'
require 'tmpdir'
require 'fileutils'
-require 'pathname'
class TestDir < Test::Unit::TestCase
def setup
@verbose = $VERBOSE
$VERBOSE = nil
- @root = Pathname.new(Dir.mktmpdir('__test_dir__')).realpath.to_s
+ @root = File.realpath(Dir.mktmpdir('__test_dir__'))
@nodir = File.join(@root, "dummy")
- for i in ?a..?z
+ @dirs = []
+ for i in "a".."z"
if i.ord % 2 == 0
FileUtils.touch(File.join(@root, i))
else
FileUtils.mkdir(File.join(@root, i))
+ @dirs << File.join(i, "")
end
end
end
@@ -43,15 +45,6 @@ class TestDir < Test::Unit::TestCase
end
end
- def test_JVN_13947696
- b = lambda {
- d = Dir.open('.')
- $SAFE = 4
- d.close
- }
- assert_raise(SecurityError) { b.call }
- end
-
def test_nodir
assert_raise(Errno::ENOENT) { Dir.open(@nodir) }
end
@@ -90,12 +83,6 @@ class TestDir < Test::Unit::TestCase
d.rewind
b = (0..5).map { d.read }
assert_equal(a, b)
- assert_raise(SecurityError) do
- Thread.new do
- $SAFE = 4
- d.rewind
- end.join
- end
ensure
d.close
end
@@ -142,19 +129,23 @@ class TestDir < Test::Unit::TestCase
def test_close
d = Dir.open(@root)
d.close
+ assert_nothing_raised(IOError) { d.close }
assert_raise(IOError) { d.read }
end
def test_glob
- assert_equal((%w(. ..) + (?a..?z).to_a).map{|f| File.join(@root, f) },
+ 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,
+ assert_equal([@root] + ("a".."z").map {|f| File.join(@root, f) }.sort,
Dir.glob([@root, File.join(@root, "*")]).sort)
- assert_equal([@root] + (?a..?z).map {|f| File.join(@root, f) }.sort,
- Dir.glob(@root + "\0\0\0" + File.join(@root, "*")).sort)
+ assert_warning(/nul-separated patterns/) do
+ assert_equal([@root] + ("a".."z").map {|f| File.join(@root, f) }.sort,
+ Dir.glob(@root + "\0\0\0" + File.join(@root, "*")).sort)
+ end
- assert_equal((?a..?z).step(2).map {|f| File.join(File.join(@root, f), "") }.sort,
+ assert_equal(("a".."z").step(2).map {|f| File.join(File.join(@root, f), "") }.sort,
Dir.glob(File.join(@root, "*/")).sort)
+ assert_equal([File.join(@root, '//a')], Dir.glob(@root + '//a'))
FileUtils.touch(File.join(@root, "{}"))
assert_equal(%w({} a).map{|f| File.join(@root, f) },
@@ -163,16 +154,149 @@ 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) }.sort, Dir.glob(File.join(@root, '[abc/def]')).sort)
- d = "\u{3042}\u{3044}".encode("utf-16le")
- assert_raise(Encoding::CompatibilityError) {Dir.glob(d)}
- m = Class.new {define_method(:to_path) {d}}
- assert_raise(Encoding::CompatibilityError) {Dir.glob(m.new)}
+ open(File.join(@root, "}}{}"), "wb") {}
+ open(File.join(@root, "}}a"), "wb") {}
+ assert_equal(%w(}}{} }}a).map {|f| File.join(@root, f)}, Dir.glob(File.join(@root, '}}{\{\},a}')))
+ assert_equal(%w(}}{} }}a b c).map {|f| File.join(@root, f)}, Dir.glob(File.join(@root, '{\}\}{\{\},a},b,c}')))
+ assert_raise(ArgumentError) {
+ Dir.glob([[@root, File.join(@root, "*")].join("\0")])
+ }
+ end
+
+ def test_glob_recursive
+ bug6977 = '[ruby-core:47418]'
+ bug8006 = '[ruby-core:53108] [Bug #8006]'
+ Dir.chdir(@root) do
+ assert_include(Dir.glob("a/**/*", File::FNM_DOTMATCH), "a/.", bug8006)
+
+ 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)
+ assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/c/d/e/f"), bug6977)
+ assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/b/c/d/e/f"), bug6977)
+ assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/c/?/e/f"), bug6977)
+ assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/c/**/d/e/f"), bug6977)
+ assert_equal(["a/b/c/d/e/f"], Dir.glob("a/**/c/**/d/e/f"), bug6977)
+
+ bug8283 = '[ruby-core:54387] [Bug #8283]'
+ 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(["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)
+ end
+ end
+
+ def test_glob_recursive_directory
+ Dir.chdir(@root) do
+ ['d', 'e'].each do |path|
+ FileUtils.mkdir_p("c/#{path}/a/b/c")
+ FileUtils.touch("c/#{path}/a/a.file")
+ FileUtils.touch("c/#{path}/a/b/b.file")
+ FileUtils.touch("c/#{path}/a/b/c/c.file")
+ end
+ 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)
+ end
+ end
+
+ def test_glob_starts_with_brace
+ Dir.chdir(@root) do
+ bug15649 = '[ruby-core:91728] [Bug #15649]'
+ assert_equal(["#{@root}/a", "#{@root}/b"],
+ Dir.glob("{#{@root}/a,#{@root}/b}"), bug15649)
+ end
+ end
+
+ if Process.const_defined?(:RLIMIT_NOFILE)
+ def test_glob_too_may_open_files
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}", chdir: @root)
+ begin;
+ n = 16
+ Process.setrlimit(Process::RLIMIT_NOFILE, n)
+ files = []
+ begin
+ n.times {files << File.open('b')}
+ rescue Errno::EMFILE, Errno::ENFILE => e
+ end
+ assert_raise(e.class) {
+ Dir.glob('*')
+ }
+ end;
+ end
+ end
+
+ def test_glob_base
+ files = %w[a/foo.c c/bar.c]
+ files.each {|n| File.write(File.join(@root, n), "")}
+ 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})
+ end
+
+ def test_glob_base_dir
+ files = %w[a/foo.c c/bar.c]
+ files.each {|n| File.write(File.join(@root, n), "")}
+ 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(%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}})
+ end
+
+ def assert_entries(entries, children_only = false)
+ entries.sort!
+ expected = ("a".."z").to_a
+ expected = %w(. ..) + expected unless children_only
+ assert_equal(expected, entries)
+ end
+
+ def test_entries
+ assert_entries(Dir.open(@root) {|dir| dir.entries})
+ assert_entries(Dir.entries(@root).to_a)
+ assert_raise(ArgumentError) {Dir.entries(@root+"\0")}
end
def test_foreach
- assert_equal(Dir.foreach(@root).to_a.sort, %w(. ..) + (?a..?z).to_a)
+ 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}
+ 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")}
+ 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}
end
def test_dir_enc
@@ -195,20 +319,158 @@ class TestDir < Test::Unit::TestCase
end
end
+ def test_unknown_keywords
+ bug8060 = '[ruby-dev:47152] [Bug #8060]'
+ assert_raise_with_message(ArgumentError, /unknown keyword/, bug8060) do
+ Dir.open(@root, xawqij: "a") {}
+ end
+ end
+
def test_symlink
begin
- ["dummy", *?a..?z].each do |f|
+ ["dummy", *"a".."z"].each do |f|
File.symlink(File.join(@root, f),
File.join(@root, "symlink-#{ f }"))
end
- rescue NotImplementedError
+ rescue NotImplementedError, Errno::EACCES
return
end
- assert_equal([*?a..?z, *"symlink-a".."symlink-z"].each_slice(2).map {|f, _| File.join(@root, f + "/") }.sort,
+ 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)
end
+ def test_glob_metachar
+ bug8597 = '[ruby-core:55764] [Bug #8597]'
+ assert_empty(Dir.glob(File.join(@root, "<")), bug8597)
+ end
+
+ def test_glob_cases
+ feature5994 = "[ruby-core:42469] [Feature #5994]"
+ feature5994 << "\nDir.glob should return the filename with actual cases on the filesystem"
+ Dir.chdir(File.join(@root, "a")) do
+ open("FileWithCases", "w") {}
+ return unless File.exist?("filewithcases")
+ assert_equal(%w"FileWithCases", Dir.glob("filewithcases"), feature5994)
+ end
+ Dir.chdir(@root) do
+ assert_equal(%w"a/FileWithCases", Dir.glob("A/filewithcases"), feature5994)
+ end
+ end
+
+ def test_glob_super_root
+ bug9648 = '[ruby-core:61552] [Bug #9648]'
+ roots = Dir.glob("/*")
+ assert_equal(roots.map {|n| "/..#{n}"}, Dir.glob("/../*"), bug9648)
+ end
+
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ def test_glob_legacy_short_name
+ bug10819 = '[ruby-core:67954] [Bug #10819]'
+ bug11206 = '[ruby-core:69435] [Bug #11206]'
+ skip unless /\A\w:/ =~ ENV["ProgramFiles"]
+ short = "#$&/PROGRA~1"
+ skip unless File.directory?(short)
+ entries = Dir.glob("#{short}/Common*")
+ assert_not_empty(entries, bug10819)
+ long = File.expand_path(short)
+ assert_equal(Dir.glob("#{long}/Common*"), entries, bug10819)
+ wild = short.sub(/1\z/, '*')
+ assert_not_include(Dir.glob(wild), long, bug11206)
+ assert_include(Dir.glob(wild, File::FNM_SHORTNAME), long, bug10819)
+ assert_empty(entries - Dir.glob("#{wild}/Common*", File::FNM_SHORTNAME), bug10819)
+ end
+ end
+
+ def test_home
+ env_home = ENV["HOME"]
+ env_logdir = ENV["LOGDIR"]
+ ENV.delete("HOME")
+ ENV.delete("LOGDIR")
+
+ ENV["HOME"] = @nodir
+ assert_nothing_raised(ArgumentError) do
+ assert_equal(@nodir, Dir.home)
+ end
+ assert_nothing_raised(ArgumentError) do
+ assert_equal(@nodir, Dir.home(""))
+ end
+ if user = ENV["USER"]
+ tilde = windows? ? "~" : "~#{user}"
+ assert_nothing_raised(ArgumentError) do
+ assert_equal(File.expand_path(tilde), Dir.home(user))
+ end
+ end
+ %W[no:such:user \u{7559 5b88}:\u{756a}].each do |user|
+ assert_raise_with_message(ArgumentError, /#{user}/) {Dir.home(user)}
+ end
+ ensure
+ ENV["HOME"] = env_home
+ ENV["LOGDIR"] = env_logdir
+ end
+
+ def test_symlinks_not_resolved
+ Dir.mktmpdir do |dirname|
+ Dir.chdir(dirname) do
+ begin
+ File.symlink('some-dir', 'dir-symlink')
+ rescue NotImplementedError, Errno::EACCES
+ return
+ end
+
+ 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
+ end
+ end
+ end
+
+ def test_fileno
+ Dir.open(".") {|d|
+ if d.respond_to? :fileno
+ assert_kind_of(Integer, d.fileno)
+ else
+ assert_raise(NotImplementedError) { d.fileno }
+ end
+ }
+ end
+
+ def test_empty?
+ assert_not_send([Dir, :empty?, @root])
+ a = File.join(@root, "a")
+ assert_send([Dir, :empty?, a])
+ %w[A .dot].each do |tmp|
+ tmp = File.join(a, tmp)
+ open(tmp, "w") {}
+ assert_not_send([Dir, :empty?, a])
+ File.delete(tmp)
+ assert_send([Dir, :empty?, a])
+ Dir.mkdir(tmp)
+ assert_not_send([Dir, :empty?, a])
+ Dir.rmdir(tmp)
+ assert_send([Dir, :empty?, a])
+ end
+ assert_raise(Errno::ENOENT) {Dir.empty?(@nodir)}
+ assert_not_send([Dir, :empty?, File.join(@root, "b")])
+ assert_raise(ArgumentError) {Dir.empty?(@root+"\0")}
+ end
+
+ def test_glob_gc_for_fd
+ assert_separately(["-C", @root], "#{<<-"begin;"}\n#{<<-"end;"}", timeout: 3)
+ begin;
+ Process.setrlimit(Process::RLIMIT_NOFILE, 50)
+ begin
+ tap {tap {tap {(0..100).map {open(IO::NULL)}}}}
+ rescue Errno::EMFILE
+ end
+ list = Dir.glob("*").sort
+ assert_not_empty(list)
+ assert_equal([*"a".."z"], list)
+ end;
+ end if defined?(Process::RLIMIT_NOFILE)
end
diff --git a/test/ruby/test_dir_m17n.rb b/test/ruby/test_dir_m17n.rb
index 81accb7f93..7584074c7e 100644
--- a/test/ruby/test_dir_m17n.rb
+++ b/test/ruby/test_dir_m17n.rb
@@ -1,6 +1,7 @@
+# frozen_string_literal: false
require 'test/unit'
require 'tmpdir'
-require_relative 'envutil'
+require '-test-/file'
class TestDir_M17N < Test::Unit::TestCase
def with_tmpdir
@@ -11,74 +12,108 @@ class TestDir_M17N < Test::Unit::TestCase
}
end
+ def assert_raw_file_name(code, encoding)
+ with_tmpdir { |dir|
+ 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)
+ 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")
+ 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
+ end
+ assert_include(ents, filename)
+ EOS
+ }
+ end
+
## UTF-8 default_external, no default_internal
def test_filename_extutf8
with_tmpdir {|d|
- assert_ruby_status(%w[-EUTF-8], <<-'EOS', nil, :chdir=>d)
+ assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
filename = "\u3042"
File.open(filename, "w") {}
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", opts)
- exit ents.include?(filename)
+ assert_include(ents, filename)
EOS
}
end
def test_filename_extutf8_invalid
- skip "ruby on windows doesn't support invalid utf-8 path" if /mswin|mingw/ =~ RUBY_PLATFORM
+ return if /cygwin/ =~ RUBY_PLATFORM
+ # High Sierra's APFS cannot use invalid filenames
+ return if Bug::File::Fs.fsname(Dir.tmpdir) == "apfs"
with_tmpdir {|d|
- assert_ruby_status(%w[-EASCII-8BIT], <<-'EOS', nil, :chdir=>d)
+ assert_separately(%w[-EASCII-8BIT], <<-'EOS', :chdir=>d)
filename = "\xff".force_encoding("ASCII-8BIT") # invalid byte sequence as UTF-8
File.open(filename, "w") {}
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", opts)
- exit ents.include?(filename) || ((RUBY_PLATFORM =~ /darwin/) != nil && ents.include?("%FF"))
+ filename = "%FF" if /darwin/ =~ RUBY_PLATFORM && ents.include?("%FF")
+ assert_include(ents, filename)
EOS
- assert_ruby_status(%w[-EUTF-8], <<-'EOS', nil, :chdir=>d)
+ assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
filename = "\xff".force_encoding("UTF-8") # invalid byte sequence as UTF-8
File.open(filename, "w") {}
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", opts)
- exit ents.include?(filename) || ((RUBY_PLATFORM =~ /darwin/) != nil && ents.include?("%FF"))
+ filename = "%FF" if /darwin/ =~ RUBY_PLATFORM && ents.include?("%FF")
+ assert_include(ents, filename)
EOS
}
- end
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
def test_filename_as_bytes_extutf8
with_tmpdir {|d|
- assert_ruby_status(%w[-EUTF-8], <<-'EOS', nil, :chdir=>d)
+ assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
filename = "\xc2\xa1".force_encoding("utf-8")
File.open(filename, "w") {}
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", opts)
- exit ents.include?(filename)
+ assert_include(ents, filename)
EOS
- assert_ruby_status(%w[-EUTF-8], <<-'EOS', nil, :chdir=>d)
- if /mswin|mingw/ =~ RUBY_PLATFORM
+ assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
+ if /mswin|mingw|darwin/ =~ RUBY_PLATFORM
filename = "\x8f\xa2\xc2".force_encoding("euc-jp")
else
filename = "\xc2\xa1".force_encoding("euc-jp")
end
- begin
+ assert_nothing_raised(Errno::ENOENT) do
open(filename) {}
- exit true
- rescue Errno::ENOENT
- exit false
end
EOS
- skip "no meaning test on windows" if /mswin|mingw/ =~ RUBY_PLATFORM
- assert_ruby_status(%w[-EUTF-8], <<-'EOS', nil, :chdir=>d)
- filename1 = "\xc2\xa1".force_encoding("utf-8")
- filename2 = "\xc2\xa1".force_encoding("euc-jp")
- filename3 = filename1.encode("euc-jp")
- filename4 = filename2.encode("utf-8")
- s1 = File.stat(filename1) rescue nil
- s2 = File.stat(filename2) rescue nil
- s3 = File.stat(filename3) rescue nil
- s4 = File.stat(filename4) rescue nil
- exit((s1 && s2 && !s3 && !s4) ? true : false)
- EOS
+ # no meaning test on windows
+ unless /mswin|mingw|darwin/ =~ RUBY_PLATFORM
+ assert_separately(%W[-EUTF-8], <<-'EOS', :chdir=>d)
+ filename1 = "\xc2\xa1".force_encoding("utf-8")
+ filename2 = "\xc2\xa1".force_encoding("euc-jp")
+ filename3 = filename1.encode("euc-jp")
+ filename4 = filename2.encode("utf-8")
+ assert_file.stat(filename1)
+ assert_file.stat(filename2)
+ assert_file.not_exist?(filename3)
+ assert_file.not_exist?(filename4)
+ EOS
+ end
}
end
@@ -86,26 +121,23 @@ class TestDir_M17N < Test::Unit::TestCase
def test_filename_extutf8_inteucjp_representable
with_tmpdir {|d|
- assert_ruby_status(%w[-EUTF-8], <<-'EOS', nil, :chdir=>d)
+ assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
filename = "\u3042"
File.open(filename, "w") {}
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", opts)
- exit ents.include?(filename)
+ assert_include(ents, filename)
EOS
- assert_ruby_status(%w[-EUTF-8:EUC-JP], <<-'EOS', nil, :chdir=>d)
+ assert_separately(%w[-EUTF-8:EUC-JP], <<-'EOS', :chdir=>d)
filename = "\xA4\xA2".force_encoding("euc-jp")
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", opts)
- exit ents.include?(filename)
+ assert_include(ents, filename)
EOS
- assert_ruby_status(%w[-EUTF-8:EUC-JP], <<-'EOS', nil, :chdir=>d)
+ assert_separately(%w[-EUTF-8:EUC-JP], <<-'EOS', :chdir=>d)
filename = "\xA4\xA2".force_encoding("euc-jp")
- begin
+ assert_nothing_raised(Errno::ENOENT) do
open(filename) {}
- exit true
- rescue Errno::ENOENT
- exit false
end
EOS
}
@@ -113,30 +145,31 @@ class TestDir_M17N < Test::Unit::TestCase
def test_filename_extutf8_inteucjp_unrepresentable
with_tmpdir {|d|
- assert_ruby_status(%w[-EUTF-8], <<-'EOS', nil, :chdir=>d)
+ assert_separately(%w[-EUTF-8], <<-'EOS', :chdir=>d)
filename1 = "\u2661" # WHITE HEART SUIT which is not representable in EUC-JP
filename2 = "\u3042" # HIRAGANA LETTER A which is representable in EUC-JP
File.open(filename1, "w") {}
File.open(filename2, "w") {}
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", opts)
- exit ents.include?(filename1) && ents.include?(filename2)
+ assert_include(ents, filename1)
+ assert_include(ents, filename2)
EOS
- assert_ruby_status(%w[-EUTF-8:EUC-JP], <<-'EOS', nil, :chdir=>d)
+ assert_separately(%w[-EUTF-8:EUC-JP], <<-'EOS', :chdir=>d)
filename1 = "\u2661" # WHITE HEART SUIT which is not representable in EUC-JP
filename2 = "\xA4\xA2".force_encoding("euc-jp") # HIRAGANA LETTER A in EUC-JP
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", opts)
- exit ents.include?(filename1) && ents.include?(filename2)
+ assert_include(ents, filename1)
+ assert_include(ents, filename2)
EOS
- assert_ruby_status(%w[-EUTF-8:EUC-JP], <<-'EOS', nil, :chdir=>d)
+ assert_separately(%w[-EUTF-8:EUC-JP], <<-'EOS', :chdir=>d)
filename1 = "\u2661" # WHITE HEART SUIT which is not representable in EUC-JP
filename2 = "\u3042" # HIRAGANA LETTER A which is representable in EUC-JP
filename3 = "\xA4\xA2".force_encoding("euc-jp") # HIRAGANA LETTER A in EUC-JP
- s1 = File.stat(filename1) rescue nil
- s2 = File.stat(filename2) rescue nil
- s3 = File.stat(filename3) rescue nil
- exit((s1 && s2 && s3) ? true : false)
+ assert_file.stat(filename1)
+ assert_file.stat(filename2)
+ assert_file.stat(filename3)
EOS
}
end
@@ -144,73 +177,273 @@ class TestDir_M17N < Test::Unit::TestCase
## others
def test_filename_bytes_euc_jp
+ return if /cygwin/ =~ RUBY_PLATFORM
with_tmpdir {|d|
- assert_ruby_status(%w[-EEUC-JP], <<-'EOS', nil, :chdir=>d)
+ 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.each {|e| e.force_encoding("ASCII-8BIT") }
- exit ents.include?(filename.force_encoding("ASCII-8BIT")) ||
- ((RUBY_PLATFORM =~ /darwin/) != nil && ents.include?("%A4%A2".force_encoding("ASCII-8BIT")))
+ if /darwin/ =~ RUBY_PLATFORM
+ filename = filename.encode("utf-8")
+ end
+ assert_include(ents, filename.force_encoding("ASCII-8BIT"))
EOS
}
end
def test_filename_euc_jp
+ return if /cygwin/ =~ RUBY_PLATFORM
with_tmpdir {|d|
- assert_ruby_status(%w[-EEUC-JP], <<-'EOS', nil, :chdir=>d)
+ 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)
- exit ents.include?(filename) || ((RUBY_PLATFORM =~ /darwin/) != nil && ents.include?("%A4%A2".force_encoding("euc-jp")))
+ if /darwin/ =~ RUBY_PLATFORM
+ filename = filename.encode("utf-8").force_encoding("euc-jp")
+ end
+ assert_include(ents, filename)
EOS
- assert_ruby_status(%w[-EASCII-8BIT], <<-'EOS', nil, :chdir=>d)
- filename = "\xA4\xA2"
+ 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)
- exit ents.include?(filename) ||
- ((RUBY_PLATFORM =~ /darwin/) != nil && ents.include?("%A4%A2".force_encoding("ASCII-8BIT"))) ||
- ((RUBY_PLATFORM =~ /mswin|mingw/) != nil && ents.include?("\x82\xA0".force_encoding("ASCII-8BIT")))
+ 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
+ end
+ end
+ assert_include(ents, filename)
EOS
}
end
- def test_filename_utf8_raw_name
- with_tmpdir {|d|
- assert_ruby_status(%w[-EUTF-8], <<-'EOS', nil, :chdir=>d)
- filename = "\u3042".force_encoding("utf-8")
- File.open(filename, "w") {}
- opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
- ents = Dir.entries(".", opts)
- exit ents.include?(filename)
- EOS
- assert_ruby_status(%w[-EASCII-8BIT], <<-'EOS', nil, :chdir=>d)
- filename = "\u3042".force_encoding("ASCII-8BIT")
- opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
- ents = Dir.entries(".", opts)
- exit ents.include?(filename) || ((RUBY_PLATFORM =~ /mswin|mingw/) != nil && ents.include?("\x82\xA0".force_encoding("ASCII-8BIT")))
- EOS
- }
+ def test_filename_utf8_raw_jp_name
+ assert_raw_file_name(0x3042, "UTF-8")
+ end
+
+ def test_filename_utf8_raw_windows_1251_name
+ assert_raw_file_name(0x0424, "UTF-8")
+ end
+
+ def test_filename_utf8_raw_windows_1252_name
+ assert_raw_file_name(0x00c6, "UTF-8")
end
def test_filename_ext_euc_jp_and_int_utf_8
+ return if /cygwin/ =~ RUBY_PLATFORM
with_tmpdir {|d|
- assert_ruby_status(%w[-EEUC-JP], <<-'EOS', nil, :chdir=>d)
+ 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)
- exit ents.include?(filename) || ((RUBY_PLATFORM =~ /darwin/) != nil && ents.include?("%A4%A2".force_encoding("euc-jp")))
+ if /darwin/ =~ RUBY_PLATFORM
+ filename = filename.encode("utf-8", "euc-jp").force_encoding("euc-jp")
+ end
+ assert_include(ents, filename)
EOS
- assert_ruby_status(%w[-EEUC-JP:UTF-8], <<-'EOS', nil, :chdir=>d)
+ assert_separately(%w[-EEUC-JP:UTF-8], <<-'EOS', :chdir=>d)
filename = "\u3042"
opts = {:encoding => Encoding.default_external} if /mswin|mingw/ =~ RUBY_PLATFORM
ents = Dir.entries(".", opts)
- exit ents.include?(filename) || ((RUBY_PLATFORM =~ /darwin/) != nil && ents.include?("%A4%A2"))
+ if /darwin/ =~ RUBY_PLATFORM
+ filename = filename.force_encoding("euc-jp")
+ end
+ assert_include(ents, filename)
EOS
}
end
-end
+ def test_error_nonascii
+ bug6071 = '[ruby-dev:45279]'
+ paths = ["\u{3042}".encode("sjis"), "\u{ff}".encode("iso-8859-1")]
+ encs = with_tmpdir {
+ paths.map {|path|
+ Dir.open(path) rescue $!.message.encoding
+ }
+ }
+ assert_equal(paths.map(&:encoding), encs, bug6071)
+ end
+
+ def test_inspect_nonascii
+ bug6072 = '[ruby-dev:45280]'
+ paths = ["\u{3042}".encode("sjis"), "\u{ff}".encode("iso-8859-1")]
+ encs = with_tmpdir {
+ paths.map {|path|
+ Dir.mkdir(path)
+ Dir.open(path) {|d| d.inspect.encoding}
+ }
+ }
+ assert_equal(paths.map(&:encoding), encs, bug6072)
+ end
+
+ def test_glob_incompatible
+ d = "\u{3042}\u{3044}".encode("utf-16le")
+ assert_raise(Encoding::CompatibilityError) {Dir.glob(d)}
+ m = Class.new {define_method(:to_path) {d}}
+ assert_raise(Encoding::CompatibilityError) {Dir.glob(m.new)}
+ end
+
+ def test_glob_compose
+ bug7267 = '[ruby-core:48745] [Bug #7267]'
+
+ pp = Object.new.extend(Test::Unit::Assertions)
+ def pp.mu_pp(str) #:nodoc:
+ str.dump
+ end
+
+ with_tmpdir {|d|
+ orig = %W"d\u{e9}tente x\u{304c 304e 3050 3052 3054}"
+ orig.each {|n| open(n, "w") {}}
+ orig.each do |o|
+ n = Dir.glob("#{o[0..0]}*")[0]
+ pp.assert_equal(o, n, bug7267)
+ end
+ }
+ end
+
+ def with_enc_path
+ with_tmpdir do |d|
+ names = %W"\u{391 392 393 394 395} \u{3042 3044 3046 3048 304a}"
+ names.each do |dir|
+ EnvUtil.with_default_external(Encoding::UTF_8) do
+ Dir.mkdir(dir) rescue next
+ begin
+ yield(dir)
+ ensure
+ File.chmod(0700, dir)
+ end
+ end
+ end
+ end
+ end
+
+ def test_glob_warning_opendir
+ with_enc_path do |dir|
+ open("#{dir}/x", "w") {}
+ File.chmod(0300, dir)
+ next if File.readable?(dir)
+ assert_warning(/#{dir}/) do
+ Dir.glob("#{dir}/*")
+ end
+ end
+ end
+
+ def test_glob_warning_match_all
+ with_enc_path do |dir|
+ open("#{dir}/x", "w") {}
+ File.chmod(0000, dir)
+ next if File.readable?(dir)
+ assert_warning(/#{dir}/) do
+ Dir.glob("#{dir}/x")
+ end
+ end
+ end
+
+ def test_glob_warning_match_dir
+ with_enc_path do |dir|
+ Dir.mkdir("#{dir}/x")
+ File.chmod(0000, dir)
+ next if File.readable?(dir)
+ assert_warning(/#{dir}/) do
+ Dir.glob("#{dir}/x/")
+ end
+ end
+ end
+
+ def test_glob_escape_multibyte
+ name = "\x81\\".force_encoding(Encoding::Shift_JIS)
+ with_tmpdir do
+ open(name, "w") {} rescue next
+ match, = Dir.glob("#{name}*")
+ next unless match and match.encoding == Encoding::Shift_JIS
+ assert_equal([name], Dir.glob("\\#{name}*"))
+ end
+ end
+
+ def test_glob_encoding
+ with_tmpdir do
+ list = %W"file_one.ext file_two.ext \u{6587 4ef6}1.txt \u{6587 4ef6}2.txt"
+ list.each {|f| open(f, "w") {}}
+ a = "file_one*".force_encoding Encoding::IBM437
+ b = "file_two*".force_encoding Encoding::EUC_JP
+ assert_equal([a, b].map(&:encoding), Dir[a, b].map(&:encoding))
+ if Bug::File::Fs.fsname(Dir.pwd) == "apfs"
+ # High Sierra's APFS cannot use filenames with undefined character
+ dir = "\u{76EE}"
+ else
+ dir = "\u{76EE 5F551}"
+ end
+ Dir.mkdir(dir)
+ list << dir
+ bug12081 = '[ruby-core:73868] [Bug #12081]'
+ a = "*".force_encoding("us-ascii")
+ result = Dir[a].map {|n|
+ if n.encoding == Encoding::ASCII_8BIT ||
+ n.encoding == Encoding::ISO_8859_1 ||
+ !n.valid_encoding?
+ n.force_encoding(Encoding::UTF_8)
+ else
+ n.encode(Encoding::UTF_8)
+ end
+ }
+ assert_equal(list, result.sort!, bug12081)
+ end
+ end
+
+ PP = Object.new.extend(Test::Unit::Assertions)
+ def PP.mu_pp(ary) #:nodoc:
+ '[' << ary.map {|str| "#{str.dump}(#{str.encoding})"}.join(', ') << ']'
+ end
+
+ def test_entries_compose
+ bug7267 = '[ruby-core:48745] [Bug #7267]'
+
+ with_tmpdir {|d|
+ orig = %W"d\u{e9}tente x\u{304c 304e 3050 3052 3054}"
+ 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}
+ ents.sort!
+ PP.assert_equal(orig, ents, bug7267)
+ }
+ end
+
+ def test_pwd
+ orig = %W"d\u{e9}tente x\u{304c 304e 3050 3052 3054}"
+ 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
+ expected << n
+ with_tmpdir {
+ Dir.mkdir(o)
+ results << File.basename(Dir.chdir(o) {Dir.pwd})
+ }
+ }
+ PP.assert_equal(expected, results)
+ end
+end
diff --git a/test/ruby/test_econv.rb b/test/ruby/test_econv.rb
index 00682f69cd..6f098db454 100644
--- a/test/ruby/test_econv.rb
+++ b/test/ruby/test_econv.rb
@@ -1,14 +1,11 @@
+# frozen_string_literal: false
require 'test/unit'
class TestEncodingConverter < Test::Unit::TestCase
def check_ec(edst, esrc, eres, dst, src, ec, off, len, opts=nil)
res = ec.primitive_convert(src, dst, off, len, opts)
- assert_equal([edst.dup.force_encoding("ASCII-8BIT"),
- esrc.dup.force_encoding("ASCII-8BIT"),
- eres],
- [dst.dup.force_encoding("ASCII-8BIT"),
- src.dup.force_encoding("ASCII-8BIT"),
- res])
+ assert_equal([edst.b, esrc.b, eres],
+ [dst.b, src.b, res])
end
def assert_econv(converted, eres, obuf_bytesize, ec, consumed, rest, opts=nil)
@@ -22,8 +19,8 @@ class TestEncodingConverter < Test::Unit::TestCase
def assert_errinfo(e_res, e_enc1, e_enc2, e_error_bytes, e_readagain_bytes, ec)
assert_equal([e_res, e_enc1, e_enc2,
- e_error_bytes && e_error_bytes.dup.force_encoding("ASCII-8BIT"),
- e_readagain_bytes && e_readagain_bytes.dup.force_encoding("ASCII-8BIT")],
+ e_error_bytes&.b,
+ e_readagain_bytes&.b],
ec.primitive_errinfo)
end
@@ -43,9 +40,9 @@ class TestEncodingConverter < Test::Unit::TestCase
def test_asciicompat_encoding_iso2022jp
acenc = Encoding::Converter.asciicompat_encoding("ISO-2022-JP")
- str = "\e$B~~\(B".force_encoding("iso-2022-jp")
+ str = "\e$B~~\e(B".force_encoding("iso-2022-jp")
str2 = str.encode(acenc)
- str3 = str.encode("ISO-2022-JP")
+ str3 = str2.encode("ISO-2022-JP")
assert_equal(str, str3)
end
@@ -85,8 +82,8 @@ class TestEncodingConverter < Test::Unit::TestCase
}
encoding_list = Encoding.list.map {|e| e.name }
- assert(!encoding_list.include?(name1))
- assert(!encoding_list.include?(name2))
+ assert_not_include(encoding_list, name1)
+ assert_not_include(encoding_list, name2)
end
def test_newline_converter_with_ascii_incompatible
@@ -146,7 +143,7 @@ class TestEncodingConverter < Test::Unit::TestCase
def test_nil_source_buffer
ec = Encoding::Converter.new("UTF-8", "EUC-JP")
- ret = ec.primitive_convert(nil, dst="", nil, 10)
+ ret = ec.primitive_convert(nil, "", nil, 10)
assert_equal(:finished, ret)
end
@@ -449,6 +446,16 @@ class TestEncodingConverter < Test::Unit::TestCase
assert_econv("abc\rdef", :finished, 50, ec, "abc\ndef", "")
end
+ def test_no_universal_newline1
+ ec = Encoding::Converter.new("UTF-8", "EUC-JP", universal_newline: false)
+ assert_econv("abc\r\ndef", :finished, 50, ec, "abc\r\ndef", "")
+ end
+
+ def test_no_universal_newline2
+ ec = Encoding::Converter.new("", "", universal_newline: false)
+ assert_econv("abc\r\ndef", :finished, 50, ec, "abc\r\ndef", "")
+ end
+
def test_after_output
ec = Encoding::Converter.new("UTF-8", "EUC-JP")
a = ["", "abc\u{3042}def", ec, nil, 100, :after_output=>true]
@@ -464,44 +471,44 @@ class TestEncodingConverter < Test::Unit::TestCase
def test_errinfo_invalid_euc_jp
ec = Encoding::Converter.new("EUC-JP", "Shift_JIS")
- ec.primitive_convert(src="\xff", dst="", nil, 10)
+ ec.primitive_convert("\xff", "", nil, 10)
assert_errinfo(:invalid_byte_sequence, "EUC-JP", "Shift_JIS", "\xFF", "", ec)
end
def test_errinfo_invalid_euc_jp2
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- ec.primitive_convert(src="\xff", dst="", nil, 10)
+ ec.primitive_convert("\xff", "", nil, 10)
assert_errinfo(:invalid_byte_sequence, "EUC-JP", "UTF-8", "\xFF", "", ec)
end
def test_errinfo_undefined_hiragana
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- ec.primitive_convert(src="\xa4\xa2", dst="", nil, 10)
+ ec.primitive_convert("\xa4\xa2", "", nil, 10)
assert_errinfo(:undefined_conversion, "UTF-8", "ISO-8859-1", "\xE3\x81\x82", "", ec)
end
def test_errinfo_invalid_partial_character
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- ec.primitive_convert(src="\xa4", dst="", nil, 10)
+ ec.primitive_convert("\xa4", "", nil, 10)
assert_errinfo(:incomplete_input, "EUC-JP", "UTF-8", "\xA4", "", ec)
end
def test_errinfo_valid_partial_character
ec = Encoding::Converter.new("EUC-JP", "ISO-8859-1")
- ec.primitive_convert(src="\xa4", dst="", nil, 10, :partial_input=>true)
+ ec.primitive_convert("\xa4", "", nil, 10, :partial_input=>true)
assert_errinfo(:source_buffer_empty, nil, nil, nil, nil, ec)
end
def test_errinfo_invalid_utf16be
ec = Encoding::Converter.new("UTF-16BE", "UTF-8")
- ec.primitive_convert(src="\xd8\x00\x00@", dst="", nil, 10)
+ ec.primitive_convert(src="\xd8\x00\x00@", "", nil, 10)
assert_errinfo(:invalid_byte_sequence, "UTF-16BE", "UTF-8", "\xD8\x00", "\x00", ec)
assert_equal("@", src)
end
def test_errinfo_invalid_utf16le
ec = Encoding::Converter.new("UTF-16LE", "UTF-8")
- ec.primitive_convert(src="\x00\xd8@\x00", dst="", nil, 10)
+ ec.primitive_convert(src="\x00\xd8@\x00", "", nil, 10)
assert_errinfo(:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "@\x00", ec)
assert_equal("", src)
end
@@ -588,7 +595,7 @@ class TestEncodingConverter < Test::Unit::TestCase
def test_putback2
ec = Encoding::Converter.new("utf-16le", "euc-jp")
- ret = ec.primitive_convert(src="\x00\xd8\x21\x00", dst="", nil, nil)
+ ret = ec.primitive_convert("\x00\xd8\x21\x00", "", nil, nil)
assert_equal(:invalid_byte_sequence, ret)
assert_equal("\x00".force_encoding("utf-16le"), ec.putback(1))
assert_equal("\x21".force_encoding("utf-16le"), ec.putback(1))
@@ -673,6 +680,7 @@ class TestEncodingConverter < Test::Unit::TestCase
ec = Encoding::Converter.new("utf-8", "euc-jp")
assert_raise(Encoding::InvalidByteSequenceError) { ec.convert("a\x80") }
assert_raise(Encoding::UndefinedConversionError) { ec.convert("\ufffd") }
+ assert_predicate(ec.convert("abc".taint), :tainted?)
ret = ec.primitive_convert(nil, "", nil, nil)
assert_equal(:finished, ret)
assert_raise(ArgumentError) { ec.convert("a") }
@@ -694,20 +702,20 @@ class TestEncodingConverter < Test::Unit::TestCase
def test_last_error1
ec = Encoding::Converter.new("sjis", "euc-jp")
assert_equal(nil, ec.last_error)
- assert_equal(:incomplete_input, ec.primitive_convert(src="fo\x81", dst="", nil, nil))
+ assert_equal(:incomplete_input, ec.primitive_convert("fo\x81", "", nil, nil))
assert_kind_of(Encoding::InvalidByteSequenceError, ec.last_error)
end
def test_last_error2
ec = Encoding::Converter.new("sjis", "euc-jp")
- assert_equal("fo", ec.convert(src="fo\x81"))
+ assert_equal("fo", ec.convert("fo\x81"))
assert_raise(Encoding::InvalidByteSequenceError) { ec.finish }
assert_kind_of(Encoding::InvalidByteSequenceError, ec.last_error)
end
def test_us_ascii
ec = Encoding::Converter.new("UTF-8", "US-ASCII")
- ec.primitive_convert(src="\u{3042}", dst="")
+ ec.primitive_convert("\u{3042}", "")
err = ec.last_error
assert_kind_of(Encoding::UndefinedConversionError, err)
assert_equal("\u{3042}", err.error_char)
@@ -715,7 +723,7 @@ class TestEncodingConverter < Test::Unit::TestCase
def test_88591
ec = Encoding::Converter.new("UTF-8", "ISO-8859-1")
- ec.primitive_convert(src="\u{3042}", dst="")
+ ec.primitive_convert("\u{3042}", "")
err = ec.last_error
assert_kind_of(Encoding::UndefinedConversionError, err)
assert_equal("\u{3042}", err.error_char)
@@ -892,4 +900,25 @@ class TestEncodingConverter < Test::Unit::TestCase
"".encode("euc-jp", :undef => :replace, :replace => broken)
}
end
+
+ def test_newline_option
+ ec1 = Encoding::Converter.new("", "", universal_newline: true)
+ ec2 = Encoding::Converter.new("", "", newline: :universal)
+ assert_equal(ec1, ec2)
+ assert_raise_with_message(ArgumentError, /\u{3042}/) {
+ Encoding::Converter.new("", "", newline: "\u{3042}".to_sym)
+ }
+ end
+
+ def test_default_external
+ Encoding.list.grep(->(enc) {/\AISO-8859-\d+\z/i =~ enc.name}) do |enc|
+ assert_separately(%W[--disable=gems -d - #{enc.name}], <<-EOS, ignore_stderr: true)
+ Encoding.default_external = ext = ARGV[0]
+ Encoding.default_internal = int ='utf-8'
+ assert_nothing_raised do
+ Encoding::Converter.new(ext, int)
+ end
+ EOS
+ end
+ end
end
diff --git a/test/ruby/test_encoding.rb b/test/ruby/test_encoding.rb
index 46b86ccabf..8f73a8fce1 100644
--- a/test/ruby/test_encoding.rb
+++ b/test/ruby/test_encoding.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestEncoding < Test::Unit::TestCase
@@ -13,6 +13,7 @@ class TestEncoding < Test::Unit::TestCase
assert_equal(e, Encoding.find(e.name.upcase))
assert_equal(e, Encoding.find(e.name.capitalize))
assert_equal(e, Encoding.find(e.name.downcase))
+ assert_equal(e, Encoding.find(e))
end
end
@@ -21,7 +22,7 @@ class TestEncoding < Test::Unit::TestCase
aliases.each do |a, en|
e = Encoding.find(a)
assert_equal(e.name, en)
- assert(e.names.include?(a))
+ assert_include(e.names, a)
end
end
@@ -33,6 +34,9 @@ class TestEncoding < Test::Unit::TestCase
assert_raise(TypeError) { e.dup }
assert_raise(TypeError) { e.clone }
assert_equal(e.object_id, Marshal.load(Marshal.dump(e)).object_id)
+ assert_not_predicate(e, :tainted?)
+ Marshal.load(Marshal.dump(e).taint)
+ assert_not_predicate(e, :tainted?, '[ruby-core:71793] [Bug #11760]')
end
end
@@ -41,7 +45,7 @@ class TestEncoding < Test::Unit::TestCase
assert_nothing_raised{Encoding.find("locale")}
assert_nothing_raised{Encoding.find("filesystem")}
- if /(?:ms|dar)win/ !~ RUBY_PLATFORM
+ if /(?:ms|dar)win|mingw/ !~ RUBY_PLATFORM
# Unix's filesystem encoding is default_external
assert_ruby_status(%w[-EUTF-8:EUC-JP], <<-'EOS')
exit Encoding.find("filesystem") == Encoding::UTF_8
@@ -49,6 +53,9 @@ class TestEncoding < Test::Unit::TestCase
exit Encoding.find("filesystem") == Encoding::EUC_JP
EOS
end
+
+ bug5150 = '[ruby-dev:44327]'
+ assert_raise(TypeError, bug5150) {Encoding.find(1)}
end
def test_replicate
@@ -81,8 +88,8 @@ class TestEncoding < Test::Unit::TestCase
def test_aliases
assert_instance_of(Hash, Encoding.aliases)
Encoding.aliases.each do |k, v|
- assert(Encoding.name_list.include?(k))
- assert(Encoding.name_list.include?(v))
+ assert_include(Encoding.name_list, k)
+ assert_include(Encoding.name_list, v)
assert_instance_of(String, k)
assert_instance_of(String, v)
end
@@ -95,4 +102,27 @@ class TestEncoding < Test::Unit::TestCase
str2 = Marshal.load(Marshal.dump(str2))
assert_equal(str, str2, '[ruby-dev:38596]')
end
+
+ def test_compatible_p
+ ua = "abc".force_encoding(Encoding::UTF_8)
+ assert_equal(Encoding::UTF_8, Encoding.compatible?(ua, :abc))
+ assert_equal(nil, Encoding.compatible?(ua, 1))
+ bin = "a".force_encoding(Encoding::ASCII_8BIT)
+ asc = "b".force_encoding(Encoding::US_ASCII)
+ assert_equal(Encoding::ASCII_8BIT, Encoding.compatible?(bin, asc))
+ bin = "\xff".force_encoding(Encoding::ASCII_8BIT).to_sym
+ asc = "b".force_encoding(Encoding::ASCII_8BIT)
+ assert_equal(Encoding::ASCII_8BIT, Encoding.compatible?(bin, asc))
+ assert_equal(Encoding::UTF_8, Encoding.compatible?("\u{3042}".to_sym, ua.to_sym))
+ end
+
+ def test_errinfo_after_autoload
+ assert_separately(%w[--disable=gems], "#{<<~"begin;"}\n#{<<~'end;'}")
+ bug9038 = '[ruby-core:57949] [Bug #9038]'
+ begin;
+ assert_raise_with_message(SyntaxError, /unknown regexp option - Q/, bug9038) {
+ eval("/regexp/sQ")
+ }
+ end;
+ end
end
diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb
index c500cee4c3..568fa0ea8d 100644
--- a/test/ruby/test_enum.rb
+++ b/test/ruby/test_enum.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: false
require 'test/unit'
-require 'continuation'
+EnvUtil.suppress_warning {require 'continuation'}
+require 'stringio'
class TestEnumerable < Test::Unit::TestCase
def setup
@@ -12,6 +14,16 @@ class TestEnumerable < Test::Unit::TestCase
yield 3
yield 1
yield 2
+ self
+ end
+ end
+ @empty = Object.new
+ class << @empty
+ attr_reader :block
+ include Enumerable
+ def each(&block)
+ @block = block
+ self
end
end
@verbose = $VERBOSE
@@ -22,11 +34,33 @@ class TestEnumerable < Test::Unit::TestCase
$VERBOSE = @verbose
end
+ def test_grep_v
+ assert_equal([3], @obj.grep_v(1..2))
+ a = []
+ @obj.grep_v(2) {|x| a << x }
+ assert_equal([1, 3, 1], a)
+
+ a = []
+ lambda = ->(x, i) {a << [x, i]}
+ @obj.each_with_index.grep_v(proc{|x,i|x!=2}, &lambda)
+ assert_equal([[2, 1], [2, 4]], a)
+ end
+
def test_grep
assert_equal([1, 2, 1, 2], @obj.grep(1..2))
a = []
@obj.grep(2) {|x| a << x }
assert_equal([2, 2], a)
+
+ bug5801 = '[ruby-dev:45041]'
+ @empty.grep(//)
+ block = @empty.block
+ assert_nothing_raised(bug5801) {100.times {block.call}}
+
+ a = []
+ lambda = ->(x, i) {a << [x, i]}
+ @obj.each_with_index.grep(proc{|x,i|x==2}, &lambda)
+ assert_equal([[2, 1], [2, 4]], a)
end
def test_count
@@ -52,6 +86,8 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal(2, @obj.find {|x| x % 2 == 0 })
assert_equal(nil, @obj.find {|x| false })
assert_equal(:foo, @obj.find(proc { :foo }) {|x| false })
+ cond = ->(x, i) { x % 2 == 0 }
+ assert_equal([2, 1], @obj.each_with_index.find(&cond))
end
def test_find_index
@@ -64,44 +100,233 @@ class TestEnumerable < Test::Unit::TestCase
def test_find_all
assert_equal([1, 3, 1], @obj.find_all {|x| x % 2 == 1 })
+ cond = ->(x, i) { x % 2 == 1 }
+ assert_equal([[1, 0], [3, 2], [1, 3]], @obj.each_with_index.find_all(&cond))
end
def test_reject
assert_equal([2, 3, 2], @obj.reject {|x| x < 2 })
+ cond = ->(x, i) {x < 2}
+ assert_equal([[2, 1], [3, 2], [2, 4]], @obj.each_with_index.reject(&cond))
end
def test_to_a
assert_equal([1, 2, 3, 1, 2], @obj.to_a)
end
+ def test_to_a_size_symbol
+ sym = Object.new
+ class << sym
+ include Enumerable
+ def each
+ self
+ end
+
+ def size
+ :size
+ end
+ end
+ assert_equal([], sym.to_a)
+ end
+
+ def test_to_a_size_infinity
+ inf = Object.new
+ class << inf
+ include Enumerable
+ def each
+ self
+ end
+
+ def size
+ Float::INFINITY
+ end
+ end
+ assert_equal([], inf.to_a)
+ end
+
+ StubToH = Object.new.tap do |obj|
+ def obj.each(*args)
+ yield(*args)
+ yield [:key, :value]
+ yield :other_key, :other_value
+ kvp = Object.new
+ def kvp.to_ary
+ [:obtained, :via_to_ary]
+ end
+ yield kvp
+ end
+ obj.extend Enumerable
+ obj.freeze
+ end
+
+ def test_to_h
+ obj = StubToH
+
+ assert_equal({
+ :hello => :world,
+ :key => :value,
+ :other_key => :other_value,
+ :obtained => :via_to_ary,
+ }, obj.to_h(:hello, :world))
+
+ e = assert_raise(TypeError) {
+ obj.to_h(:not_an_array)
+ }
+ assert_equal "wrong element type Symbol (expected array)", e.message
+
+ e = assert_raise(ArgumentError) {
+ obj.to_h([1])
+ }
+ assert_equal "element has wrong array length (expected 2, was 1)", e.message
+ end
+
+ def test_to_h_block
+ obj = StubToH
+
+ assert_equal({
+ "hello" => "world",
+ "key" => "value",
+ "other_key" => "other_value",
+ "obtained" => "via_to_ary",
+ }, obj.to_h(:hello, :world) {|k, v| [k.to_s, v.to_s]})
+
+ e = assert_raise(TypeError) {
+ obj.to_h {:not_an_array}
+ }
+ assert_equal "wrong element type Symbol (expected array)", e.message
+
+ e = assert_raise(ArgumentError) {
+ obj.to_h {[1]}
+ }
+ assert_equal "element has wrong array length (expected 2, was 1)", e.message
+ end
+
def test_inject
assert_equal(12, @obj.inject {|z, x| z * x })
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(nil, @empty.inject() {9})
+ end
+
+ FIXNUM_MIN = RbConfig::LIMITS['FIXNUM_MIN']
+ FIXNUM_MAX = RbConfig::LIMITS['FIXNUM_MAX']
+
+ def test_inject_array_mul
+ assert_equal(nil, [].inject(:*))
+ assert_equal(5, [5].inject(:*))
+ assert_equal(35, [5, 7].inject(:*))
+ assert_equal(3, [].inject(3, :*))
+ assert_equal(15, [5].inject(3, :*))
+ assert_equal(105, [5, 7].inject(3, :*))
+ end
+
+ def test_inject_array_plus
+ assert_equal(3, [3].inject(:+))
+ assert_equal(8, [3, 5].inject(:+))
+ 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(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((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_array_op_redefined
+ assert_separately([], "#{<<~"end;"}\n""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, [1,2,3].inject(op), bug)
+ ensure
+ Integer.class_eval do
+ undef_method op
+ alias_method op, :orig
+ end
+ end
+ end;
+ end
+
+ def test_inject_array_op_private
+ assert_separately([], "#{<<~"end;"}\n""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
+ [1,2,3].inject(op)
+ ensure
+ Integer.class_eval do
+ public op
+ end
+ end
+ end
+ end;
end
def test_partition
assert_equal([[1, 3, 1], [2, 2]], @obj.partition {|x| x % 2 == 1 })
+ cond = ->(x, i) { x % 2 == 1 }
+ assert_equal([[[1, 0], [3, 2], [1, 3]], [[2, 1], [2, 4]]], @obj.each_with_index.partition(&cond))
end
def test_group_by
h = { 1 => [1, 1], 2 => [2, 2], 3 => [3] }
assert_equal(h, @obj.group_by {|x| x })
+
+ h = {1=>[[1, 0], [1, 3]], 2=>[[2, 1], [2, 4]], 3=>[[3, 2]]}
+ cond = ->(x, i) { x }
+ assert_equal(h, @obj.each_with_index.group_by(&cond))
end
def test_first
assert_equal(1, @obj.first)
assert_equal([1, 2, 3], @obj.first(3))
+ assert_nil(@empty.first)
+ assert_equal([], @empty.first(10))
+
+ bug5801 = '[ruby-dev:45041]'
+ assert_in_out_err([], <<-'end;', [], /unexpected break/, bug5801)
+ empty = Object.new
+ class << empty
+ attr_reader :block
+ include Enumerable
+ def each(&block)
+ @block = block
+ self
+ end
+ end
+ empty.first
+ empty.block.call
+ end;
end
def test_sort
assert_equal([1, 1, 2, 2, 3], @obj.sort)
+ assert_equal([3, 2, 2, 1, 1], @obj.sort {|x, y| y <=> x })
end
def test_sort_by
assert_equal([3, 2, 2, 1, 1], @obj.sort_by {|x| -x })
+ assert_equal((1..300).to_a.reverse, (1..300).sort_by {|x| -x })
+
+ cond = ->(x, i) { [-x, i] }
+ assert_equal([[3, 2], [2, 1], [2, 4], [1, 0], [1, 3]], @obj.each_with_index.sort_by(&cond))
end
def test_all
@@ -109,6 +334,25 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal(false, @obj.all? {|x| x < 3 })
assert_equal(true, @obj.all?)
assert_equal(false, [true, true, false].all?)
+ assert_equal(true, [].all?)
+ assert_equal(true, @empty.all?)
+ assert_equal(true, @obj.all?(Fixnum))
+ assert_equal(false, @obj.all?(1..2))
+ end
+
+ def test_all_with_unused_block
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ [1, 2].all?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ (1..2).all?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ 3.times.all?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ {a: 1, b: 2}.all?([:b, 2]) {|x| x == 4 }
+ EOS
end
def test_any
@@ -116,46 +360,126 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal(false, @obj.any? {|x| x > 3 })
assert_equal(true, @obj.any?)
assert_equal(false, [false, false, false].any?)
+ assert_equal(false, [].any?)
+ assert_equal(false, @empty.any?)
+ assert_equal(true, @obj.any?(1..2))
+ assert_equal(false, @obj.any?(Float))
+ assert_equal(false, [1, 42].any?(Float))
+ assert_equal(true, [1, 4.2].any?(Float))
+ assert_equal(false, {a: 1, b: 2}.any?(->(kv) { kv == [:foo, 42] }))
+ assert_equal(true, {a: 1, b: 2}.any?(->(kv) { kv == [:b, 2] }))
+ end
+
+ def test_any_with_unused_block
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ [1, 23].any?(1) {|x| x == 1 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ (1..2).any?(34) {|x| x == 2 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ 3.times.any?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ {a: 1, b: 2}.any?([:b, 2]) {|x| x == 4 }
+ EOS
end
def test_one
assert(@obj.one? {|x| x == 3 })
assert(!(@obj.one? {|x| x == 1 }))
assert(!(@obj.one? {|x| x == 4 }))
+ assert(@obj.one?(3..4))
+ assert(!(@obj.one?(1..2)))
+ assert(!(@obj.one?(4..5)))
assert(%w{ant bear cat}.one? {|word| word.length == 4})
assert(!(%w{ant bear cat}.one? {|word| word.length > 4}))
assert(!(%w{ant bear cat}.one? {|word| word.length < 4}))
+ assert(%w{ant bear cat}.one?(/b/))
+ assert(!(%w{ant bear cat}.one?(/t/)))
assert(!([ nil, true, 99 ].one?))
assert([ nil, true, false ].one?)
+ assert(![].one?)
+ assert(!@empty.one?)
+ assert([ nil, true, 99 ].one?(Integer))
+ end
+
+ def test_one_with_unused_block
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ [1, 2].one?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ (1..2).one?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ 3.times.one?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ {a: 1, b: 2}.one?([:b, 2]) {|x| x == 4 }
+ EOS
end
def test_none
assert(@obj.none? {|x| x == 4 })
assert(!(@obj.none? {|x| x == 1 }))
assert(!(@obj.none? {|x| x == 3 }))
+ assert(@obj.none?(4..5))
+ assert(!(@obj.none?(1..3)))
assert(%w{ant bear cat}.none? {|word| word.length == 5})
assert(!(%w{ant bear cat}.none? {|word| word.length >= 4}))
+ assert(%w{ant bear cat}.none?(/d/))
+ assert(!(%w{ant bear cat}.none?(/b/)))
assert([].none?)
assert([nil].none?)
assert([nil,false].none?)
+ assert(![nil,false,true].none?)
+ assert(@empty.none?)
+ end
+
+ def test_none_with_unused_block
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ [1, 2].none?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ (1..2).none?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ 3.times.none?(1) {|x| x == 3 }
+ EOS
+ assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"]
+ {a: 1, b: 2}.none?([:b, 2]) {|x| x == 4 }
+ EOS
end
def test_min
assert_equal(1, @obj.min)
assert_equal(3, @obj.min {|a,b| b <=> a })
- ary = %w(albatross dog horse)
- assert_equal("albatross", ary.min)
- assert_equal("dog", ary.min {|a,b| a.length <=> b.length })
- assert_equal(1, [3,2,1].min)
+ cond = ->((a, ia), (b, ib)) { (b <=> a).nonzero? or ia <=> ib }
+ assert_equal([3, 2], @obj.each_with_index.min(&cond))
+ enum = %w(albatross dog horse).to_enum
+ assert_equal("albatross", enum.min)
+ assert_equal("dog", enum.min {|a,b| a.length <=> b.length })
+ assert_equal(1, [3,2,1].to_enum.min)
+ assert_equal(%w[albatross dog], enum.min(2))
+ assert_equal(%w[dog horse],
+ enum.min(2) {|a,b| a.length <=> b.length })
+ assert_equal([13, 14], [20, 32, 32, 21, 30, 25, 29, 13, 14].to_enum.min(2))
+ assert_equal([2, 4, 6, 7], [2, 4, 8, 6, 7].to_enum.min(4))
end
def test_max
assert_equal(3, @obj.max)
assert_equal(1, @obj.max {|a,b| b <=> a })
- ary = %w(albatross dog horse)
- assert_equal("horse", ary.max)
- assert_equal("albatross", ary.max {|a,b| a.length <=> b.length })
- assert_equal(1, [3,2,1].max{|a,b| b <=> a })
+ cond = ->((a, ia), (b, ib)) { (b <=> a).nonzero? or ia <=> ib }
+ assert_equal([1, 3], @obj.each_with_index.max(&cond))
+ enum = %w(albatross dog horse).to_enum
+ assert_equal("horse", enum.max)
+ assert_equal("albatross", enum.max {|a,b| a.length <=> b.length })
+ assert_equal(1, [3,2,1].to_enum.max{|a,b| b <=> a })
+ assert_equal(%w[horse dog], enum.max(2))
+ assert_equal(%w[albatross horse],
+ enum.max(2) {|a,b| a.length <=> b.length })
+ assert_equal([3, 2], [0, 0, 0, 0, 0, 0, 1, 3, 2].to_enum.max(2))
end
def test_minmax
@@ -167,24 +491,35 @@ class TestEnumerable < Test::Unit::TestCase
assert_equal([1, 3], [2,3,1].minmax)
assert_equal([3, 1], [2,3,1].minmax {|a,b| b <=> a })
assert_equal([1, 3], [2,2,3,3,1,1].minmax)
+ assert_equal([nil, nil], [].minmax)
end
def test_min_by
assert_equal(3, @obj.min_by {|x| -x })
+ cond = ->(x, i) { -x }
+ assert_equal([3, 2], @obj.each_with_index.min_by(&cond))
a = %w(albatross dog horse)
assert_equal("dog", a.min_by {|x| x.length })
assert_equal(3, [2,3,1].min_by {|x| -x })
+ assert_equal(%w[dog horse], a.min_by(2) {|x| x.length })
+ assert_equal([13, 14], [20, 32, 32, 21, 30, 25, 29, 13, 14].min_by(2) {|x| x})
end
def test_max_by
assert_equal(1, @obj.max_by {|x| -x })
+ cond = ->(x, i) { -x }
+ assert_equal([1, 0], @obj.each_with_index.max_by(&cond))
a = %w(albatross dog horse)
assert_equal("albatross", a.max_by {|x| x.length })
assert_equal(1, [2,3,1].max_by {|x| -x })
+ assert_equal(%w[albatross horse], a.max_by(2) {|x| x.length })
+ assert_equal([3, 2], [0, 0, 0, 0, 0, 0, 1, 3, 2].max_by(2) {|x| x})
end
def test_minmax_by
assert_equal([3, 1], @obj.minmax_by {|x| -x })
+ cond = ->(x, i) { -x }
+ assert_equal([[3, 2], [1, 0]], @obj.each_with_index.minmax_by(&cond))
a = %w(albatross dog horse)
assert_equal(["dog", "albatross"], a.minmax_by {|x| x.length })
assert_equal([3, 1], [2,3,1].minmax_by {|x| -x })
@@ -232,25 +567,77 @@ class TestEnumerable < Test::Unit::TestCase
def test_each_entry
assert_equal([1, 2, 3], [1, 2, 3].each_entry.to_a)
assert_equal([1, [1, 2]], Foo.new.each_entry.to_a)
+ a = []
+ cond = ->(x, i) { a << x }
+ @obj.each_with_index.each_entry(&cond)
+ assert_equal([1, 2, 3, 1, 2], a)
+ end
+
+ def test_each_slice
+ ary = []
+ (1..10).each_slice(3) {|a| ary << a}
+ assert_equal([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]], ary)
+
+ bug9749 = '[ruby-core:62060] [Bug #9749]'
+ ary.clear
+ (1..10).each_slice(3, &lambda {|a, *| ary << a})
+ assert_equal([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]], ary, bug9749)
+
+ ary.clear
+ (1..10).each_slice(10) {|a| ary << a}
+ assert_equal([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]], ary)
+
+ ary.clear
+ (1..10).each_slice(11) {|a| ary << a}
+ assert_equal([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]], ary)
+ end
+
+ def test_each_cons
+ ary = []
+ (1..5).each_cons(3) {|a| ary << a}
+ assert_equal([[1, 2, 3], [2, 3, 4], [3, 4, 5]], ary)
+
+ bug9749 = '[ruby-core:62060] [Bug #9749]'
+ ary.clear
+ (1..5).each_cons(3, &lambda {|a, *| ary << a})
+ assert_equal([[1, 2, 3], [2, 3, 4], [3, 4, 5]], ary, bug9749)
+
+ ary.clear
+ (1..5).each_cons(5) {|a| ary << a}
+ assert_equal([[1, 2, 3, 4, 5]], ary)
+
+ ary.clear
+ (1..5).each_cons(6) {|a| ary << a}
+ assert_empty(ary)
end
def test_zip
assert_equal([[1,1],[2,2],[3,3],[1,1],[2,2]], @obj.zip(@obj))
+ assert_equal([["a",1],["b",2],["c",3]], ["a", "b", "c"].zip(@obj))
+
a = []
- @obj.zip([:a, :b, :c]) {|x,y| a << [x, y] }
+ result = @obj.zip([:a, :b, :c]) {|x,y| a << [x, y] }
+ assert_nil result
assert_equal([[1,:a],[2,:b],[3,:c],[1,nil],[2,nil]], a)
a = []
+ cond = ->((x, i), y) { a << [x, y, i] }
+ @obj.each_with_index.zip([:a, :b, :c], &cond)
+ assert_equal([[1,:a,0],[2,:b,1],[3,:c,2],[1,nil,3],[2,nil,4]], a)
+
+ a = []
@obj.zip({a: "A", b: "B", c: "C"}) {|x,y| a << [x, y] }
assert_equal([[1,[:a,"A"]],[2,[:b,"B"]],[3,[:c,"C"]],[1,nil],[2,nil]], a)
ary = Object.new
def ary.to_a; [1, 2]; end
- assert_raise(NoMethodError){ %w(a b).zip(ary) }
+ assert_raise(TypeError) {%w(a b).zip(ary)}
def ary.each; [3, 4].each{|e|yield e}; end
assert_equal([[1, 3], [2, 4], [3, nil], [1, nil], [2, nil]], @obj.zip(ary))
def ary.to_ary; [5, 6]; end
assert_equal([[1, 5], [2, 6], [3, nil], [1, nil], [2, nil]], @obj.zip(ary))
+ obj = eval("class C\u{1f5ff}; self; end").new
+ assert_raise_with_message(TypeError, /C\u{1f5ff}/) {(1..1).zip(obj)}
end
def test_take
@@ -259,6 +646,13 @@ class TestEnumerable < Test::Unit::TestCase
def test_take_while
assert_equal([1,2], @obj.take_while {|x| x <= 2})
+ cond = ->(x, i) {x <= 2}
+ assert_equal([[1, 0], [2, 1]], @obj.each_with_index.take_while(&cond))
+
+ bug5801 = '[ruby-dev:45040]'
+ @empty.take_while {true}
+ block = @empty.block
+ assert_nothing_raised(bug5801) {100.times {block.call}}
end
def test_drop
@@ -267,10 +661,19 @@ class TestEnumerable < Test::Unit::TestCase
def test_drop_while
assert_equal([3,1,2], @obj.drop_while {|x| x <= 2})
+ cond = ->(x, i) {x <= 2}
+ assert_equal([[3, 2], [1, 3], [2, 4]], @obj.each_with_index.drop_while(&cond))
end
def test_cycle
assert_equal([1,2,3,1,2,1,2,3,1,2], @obj.cycle.take(10))
+ a = []
+ @obj.cycle(2) {|x| a << x}
+ assert_equal([1,2,3,1,2,1,2,3,1,2], a)
+ a = []
+ cond = ->(x, i) {a << x}
+ @obj.each_with_index.cycle(2, &cond)
+ assert_equal([1,2,3,1,2,1,2,3,1,2], a)
end
def test_callcc
@@ -305,12 +708,41 @@ class TestEnumerable < Test::Unit::TestCase
[o, o, o].sort_by {|x| x }
c.call
end
+
+ assert_raise_with_message(RuntimeError, /reentered/) do
+ i = 0
+ c = nil
+ o = Object.new
+ class << o; self; end.class_eval do
+ define_method(:<=>) do |x|
+ callcc {|c2| c ||= c2 }
+ i += 1
+ 0
+ end
+ end
+ [o, o].min(1)
+ assert_operator(i, :<=, 5, "infinite loop")
+ c.call
+ end
end
def test_reverse_each
assert_equal([2,1,3,2,1], @obj.reverse_each.to_a)
end
+ def test_reverse_each_memory_corruption
+ bug16354 = '[ruby-dev:50867]'
+ assert_normal_exit %q{
+ size = 1000
+ (0...size).reverse_each do |i|
+ i.inspect
+ ObjectSpace.each_object(Array) do |a|
+ a.clear if a.length == size
+ end
+ end
+ }, bug16354
+ end
+
def test_chunk
e = [].chunk {|elt| true }
assert_equal([], e.to_a)
@@ -318,22 +750,6 @@ class TestEnumerable < Test::Unit::TestCase
e = @obj.chunk {|elt| elt & 2 == 0 ? false : true }
assert_equal([[false, [1]], [true, [2, 3]], [false, [1]], [true, [2]]], e.to_a)
- e = @obj.chunk(acc: 0) {|elt, h| h[:acc] += elt; h[:acc].even? }
- assert_equal([[false, [1,2]], [true, [3]], [false, [1,2]]], e.to_a)
- assert_equal([[false, [1,2]], [true, [3]], [false, [1,2]]], e.to_a) # this tests h is duplicated.
-
- hs = [{}]
- e = [:foo].chunk(hs[0]) {|elt, h|
- hs << h
- true
- }
- assert_equal([[true, [:foo]]], e.to_a)
- assert_equal([[true, [:foo]]], e.to_a)
- assert_equal([{}, {}, {}], hs)
- assert_not_same(hs[0], hs[1])
- assert_not_same(hs[0], hs[2])
- assert_not_same(hs[1], hs[2])
-
e = @obj.chunk {|elt| elt < 3 ? :_alone : true }
assert_equal([[:_alone, [1]],
[:_alone, [2]],
@@ -351,6 +767,12 @@ class TestEnumerable < Test::Unit::TestCase
e = @obj.chunk {|elt| :_foo }
assert_raise(RuntimeError) { e.to_a }
+
+ e = @obj.chunk.with_index {|elt, i| elt - i }
+ assert_equal([[1, [1, 2, 3]],
+ [-2, [1, 2]]], e.to_a)
+
+ assert_equal(4, (0..3).chunk.size)
end
def test_slice_before
@@ -363,25 +785,362 @@ class TestEnumerable < Test::Unit::TestCase
e = @obj.slice_before {|elt| elt.odd? }
assert_equal([[1,2], [3], [1,2]], e.to_a)
- e = @obj.slice_before(acc: 0) {|elt, h| h[:acc] += elt; h[:acc].even? }
- assert_equal([[1,2], [3,1,2]], e.to_a)
- assert_equal([[1,2], [3,1,2]], e.to_a) # this tests h is duplicated.
+ ss = %w[abc defg h ijk l mno pqr st u vw xy z]
+ assert_equal([%w[abc defg h], %w[ijk l], %w[mno], %w[pqr st u vw xy z]],
+ ss.slice_before(/\A...\z/).to_a)
+ assert_warning("") {ss.slice_before(/\A...\z/).to_a}
+ end
+
+ def test_slice_after0
+ assert_raise(ArgumentError) { [].slice_after }
+ end
+
+ def test_slice_after1
+ e = [].slice_after {|a| flunk "should not be called" }
+ assert_equal([], e.to_a)
+
+ e = [1,2].slice_after(1)
+ assert_equal([[1], [2]], e.to_a)
+
+ e = [1,2].slice_after(3)
+ assert_equal([[1, 2]], e.to_a)
+
+ [true, false].each {|b|
+ block_results = [true, b]
+ e = [1,2].slice_after {|a| block_results.shift }
+ assert_equal([[1], [2]], e.to_a)
+ assert_equal([], block_results)
+
+ block_results = [false, b]
+ e = [1,2].slice_after {|a| block_results.shift }
+ assert_equal([[1, 2]], e.to_a)
+ assert_equal([], block_results)
+ }
+ end
+
+ def test_slice_after_both_pattern_and_block
+ assert_raise(ArgumentError) { [].slice_after(1) {|a| true } }
+ end
+
+ def test_slice_after_continuation_lines
+ lines = ["foo\n", "bar\\\n", "baz\n", "\n", "qux\n"]
+ e = lines.slice_after(/[^\\]\n\z/)
+ assert_equal([["foo\n"], ["bar\\\n", "baz\n"], ["\n", "qux\n"]], e.to_a)
+ end
- hs = [{}]
- e = [:foo].slice_before(hs[0]) {|elt, h|
- hs << h
+ def test_slice_before_empty_line
+ lines = ["foo", "", "bar"]
+ e = lines.slice_after(/\A\s*\z/)
+ assert_equal([["foo", ""], ["bar"]], e.to_a)
+ end
+
+ def test_slice_when_0
+ e = [].slice_when {|a, b| flunk "should not be called" }
+ assert_equal([], e.to_a)
+ end
+
+ def test_slice_when_1
+ e = [1].slice_when {|a, b| flunk "should not be called" }
+ assert_equal([[1]], e.to_a)
+ end
+
+ def test_slice_when_2
+ e = [1,2].slice_when {|a,b|
+ assert_equal(1, a)
+ assert_equal(2, b)
true
}
- assert_equal([[:foo]], e.to_a)
- assert_equal([[:foo]], e.to_a)
- assert_equal([{}, {}, {}], hs)
- assert_not_same(hs[0], hs[1])
- assert_not_same(hs[0], hs[2])
- assert_not_same(hs[1], hs[2])
+ assert_equal([[1], [2]], e.to_a)
- ss = %w[abc defg h ijk l mno pqr st u vw xy z]
- assert_equal([%w[abc defg h], %w[ijk l], %w[mno], %w[pqr st u vw xy z]],
- ss.slice_before(/\A...\z/).to_a)
+ e = [1,2].slice_when {|a,b|
+ assert_equal(1, a)
+ assert_equal(2, b)
+ false
+ }
+ assert_equal([[1, 2]], e.to_a)
+ end
+
+ def test_slice_when_3
+ block_invocations = [
+ lambda {|a, b|
+ assert_equal(1, a)
+ assert_equal(2, b)
+ true
+ },
+ lambda {|a, b|
+ assert_equal(2, a)
+ assert_equal(3, b)
+ false
+ }
+ ]
+ e = [1,2,3].slice_when {|a,b|
+ block_invocations.shift.call(a, b)
+ }
+ assert_equal([[1], [2, 3]], e.to_a)
+ assert_equal([], block_invocations)
+ end
+
+ def test_slice_when_noblock
+ assert_raise(ArgumentError) { [].slice_when }
+ end
+
+ def test_slice_when_contiguously_increasing_integers
+ e = [1,4,9,10,11,12,15,16,19,20,21].slice_when {|i, j| i+1 != j }
+ assert_equal([[1], [4], [9,10,11,12], [15,16], [19,20,21]], e.to_a)
+ end
+
+ def test_chunk_while_contiguously_increasing_integers
+ e = [1,4,9,10,11,12,15,16,19,20,21].chunk_while {|i, j| i+1 == j }
+ assert_equal([[1], [4], [9,10,11,12], [15,16], [19,20,21]], e.to_a)
+ end
+
+ def test_detect
+ @obj = ('a'..'z')
+ assert_equal('c', @obj.detect {|x| x == 'c' })
+
+ proc = Proc.new {|x| x == 'c' }
+ assert_equal('c', @obj.detect(&proc))
+
+ lambda = ->(x) { x == 'c' }
+ assert_equal('c', @obj.detect(&lambda))
+
+ assert_equal(['c',2], @obj.each_with_index.detect {|x, i| x == 'c' })
+
+ proc2 = Proc.new {|x, i| x == 'c' }
+ assert_equal(['c',2], @obj.each_with_index.detect(&proc2))
+
+ bug9605 = '[ruby-core:61340]'
+ lambda2 = ->(x, i) { x == 'c' }
+ assert_equal(['c',2], @obj.each_with_index.detect(&lambda2), bug9605)
+ end
+
+ def test_select
+ @obj = ('a'..'z')
+ assert_equal(['c'], @obj.select {|x| x == 'c' })
+
+ proc = Proc.new {|x| x == 'c' }
+ assert_equal(['c'], @obj.select(&proc))
+
+ lambda = ->(x) { x == 'c' }
+ assert_equal(['c'], @obj.select(&lambda))
+
+ assert_equal([['c',2]], @obj.each_with_index.select {|x, i| x == 'c' })
+
+ proc2 = Proc.new {|x, i| x == 'c' }
+ assert_equal([['c',2]], @obj.each_with_index.select(&proc2))
+
+ bug9605 = '[ruby-core:61340]'
+ lambda2 = ->(x, i) { x == 'c' }
+ assert_equal([['c',2]], @obj.each_with_index.select(&lambda2), bug9605)
+ end
+
+ def test_map
+ @obj = ('a'..'e')
+ assert_equal(['A', 'B', 'C', 'D', 'E'], @obj.map {|x| x.upcase })
+
+ proc = Proc.new {|x| x.upcase }
+ assert_equal(['A', 'B', 'C', 'D', 'E'], @obj.map(&proc))
+
+ lambda = ->(x) { x.upcase }
+ assert_equal(['A', 'B', 'C', 'D', 'E'], @obj.map(&lambda))
+
+ assert_equal([['A',0], ['B',1], ['C',2], ['D',3], ['E',4]],
+ @obj.each_with_index.map {|x, i| [x.upcase, i] })
+
+ proc2 = Proc.new {|x, i| [x.upcase, i] }
+ assert_equal([['A',0], ['B',1], ['C',2], ['D',3], ['E',4]],
+ @obj.each_with_index.map(&proc2))
+
+ lambda2 = ->(x, i) { [x.upcase, i] }
+ assert_equal([['A',0], ['B',1], ['C',2], ['D',3], ['E',4]],
+ @obj.each_with_index.map(&lambda2))
+
+ hash = { a: 'hoge', b: 'fuga' }
+ lambda = -> (k, v) { "#{k}:#{v}" }
+ assert_equal ["a:hoge", "b:fuga"], hash.map(&lambda)
end
+ def test_flat_map
+ @obj = [[1,2], [3,4]]
+ assert_equal([2,4,6,8], @obj.flat_map {|i| i.map{|j| j*2} })
+
+ proc = Proc.new {|i| i.map{|j| j*2} }
+ assert_equal([2,4,6,8], @obj.flat_map(&proc))
+
+ lambda = ->(i) { i.map{|j| j*2} }
+ assert_equal([2,4,6,8], @obj.flat_map(&lambda))
+
+ assert_equal([[1,2],0,[3,4],1],
+ @obj.each_with_index.flat_map {|x, i| [x,i] })
+
+ proc2 = Proc.new {|x, i| [x,i] }
+ assert_equal([[1,2],0,[3,4],1],
+ @obj.each_with_index.flat_map(&proc2))
+
+ lambda2 = ->(x, i) { [x,i] }
+ assert_equal([[1,2],0,[3,4],1],
+ @obj.each_with_index.flat_map(&lambda2))
+ end
+
+ def assert_typed_equal(e, v, cls, msg=nil)
+ assert_kind_of(cls, v, msg)
+ assert_equal(e, v, msg)
+ end
+
+ def assert_int_equal(e, v, msg=nil)
+ assert_typed_equal(e, v, Integer, msg)
+ end
+
+ def assert_rational_equal(e, v, msg=nil)
+ assert_typed_equal(e, v, Rational, msg)
+ end
+
+ def assert_float_equal(e, v, msg=nil)
+ assert_typed_equal(e, v, Float, msg)
+ end
+
+ def assert_complex_equal(e, v, msg=nil)
+ assert_typed_equal(e, v, Complex, msg)
+ end
+
+ def test_sum
+ class << (enum = Object.new)
+ include Enumerable
+ def each
+ yield 3
+ yield 5
+ yield 7
+ end
+ end
+ assert_int_equal(15, enum.sum)
+
+ assert_int_equal(0, [].each.sum)
+ assert_int_equal(3, [3].each.sum)
+ assert_int_equal(8, [3, 5].each.sum)
+ assert_int_equal(15, [3, 5, 7].each.sum)
+ assert_rational_equal(8r, [3, 5r].each.sum)
+ assert_float_equal(15.0, [3, 5, 7.0].each.sum)
+ assert_float_equal(15.0, [3, 5r, 7.0].each.sum)
+ assert_complex_equal(8r + 1i, [3, 5r, 1i].each.sum)
+ assert_complex_equal(15.0 + 1i, [3, 5r, 7.0, 1i].each.sum)
+
+ assert_int_equal(2*FIXNUM_MAX, Array.new(2, FIXNUM_MAX).each.sum)
+ assert_int_equal(2*(FIXNUM_MAX+1), Array.new(2, FIXNUM_MAX+1).each.sum)
+ assert_int_equal(10*FIXNUM_MAX, Array.new(10, FIXNUM_MAX).each.sum)
+ assert_int_equal(0, ([FIXNUM_MAX, 1, -FIXNUM_MAX, -1]*10).each.sum)
+ assert_int_equal(FIXNUM_MAX*10, ([FIXNUM_MAX+1, -1]*10).each.sum)
+ assert_int_equal(2*FIXNUM_MIN, Array.new(2, FIXNUM_MIN).each.sum)
+
+ assert_float_equal(0.0, [].each.sum(0.0))
+ assert_float_equal(3.0, [3].each.sum(0.0))
+ assert_float_equal(3.5, [3].each.sum(0.5))
+ assert_float_equal(8.5, [3.5, 5].each.sum)
+ assert_float_equal(10.5, [2, 8.5].each.sum)
+ assert_float_equal((FIXNUM_MAX+1).to_f, [FIXNUM_MAX, 1, 0.0].each.sum)
+ assert_float_equal((FIXNUM_MAX+1).to_f, [0.0, FIXNUM_MAX+1].each.sum)
+
+ assert_rational_equal(3/2r, [1/2r, 1].each.sum)
+ assert_rational_equal(5/6r, [1/2r, 1/3r].each.sum)
+
+ assert_equal(2.0+3.0i, [2.0, 3.0i].each.sum)
+
+ assert_int_equal(13, [1, 2].each.sum(10))
+ assert_int_equal(16, [1, 2].each.sum(10) {|v| v * 2 })
+
+ yielded = []
+ three = SimpleDelegator.new(3)
+ ary = [1, 2.0, three]
+ assert_float_equal(12.0, ary.each.sum {|x| yielded << x; x * 2 })
+ assert_equal(ary, yielded)
+
+ assert_raise(TypeError) { [Object.new].each.sum }
+
+ large_number = 100000000
+ small_number = 1e-9
+ until (large_number + small_number) == large_number
+ small_number /= 10
+ end
+ assert_float_equal(large_number+(small_number*10), [large_number, *[small_number]*10].each.sum)
+ assert_float_equal(large_number+(small_number*10), [large_number/1r, *[small_number]*10].each.sum)
+ assert_float_equal(large_number+(small_number*11), [small_number, large_number/1r, *[small_number]*10].each.sum)
+ assert_float_equal(small_number, [large_number, small_number, -large_number].each.sum)
+
+ k = Class.new do
+ include Enumerable
+ def initialize(*values)
+ @values = values
+ end
+ def each(&block)
+ @values.each(&block)
+ end
+ end
+ assert_equal(+Float::INFINITY, k.new(0.0, +Float::INFINITY).sum)
+ assert_equal(+Float::INFINITY, k.new(+Float::INFINITY, 0.0).sum)
+ assert_equal(-Float::INFINITY, k.new(0.0, -Float::INFINITY).sum)
+ assert_equal(-Float::INFINITY, k.new(-Float::INFINITY, 0.0).sum)
+ assert_predicate(k.new(-Float::INFINITY, Float::INFINITY).sum, :nan?)
+
+ assert_equal("abc", ["a", "b", "c"].each.sum(""))
+ assert_equal([1, [2], 3], [[1], [[2]], [3]].each.sum([]))
+ end
+
+ def test_hash_sum
+ histogram = { 1 => 6, 2 => 4, 3 => 3, 4 => 7, 5 => 5, 6 => 4 }
+ assert_equal(100, histogram.sum {|v, n| v * n })
+ end
+
+ def test_range_sum
+ assert_int_equal(55, (1..10).sum)
+ assert_float_equal(55.0, (1..10).sum(0.0))
+ assert_int_equal(90, (5..10).sum {|v| v * 2 })
+ assert_float_equal(90.0, (5..10).sum(0.0) {|v| v * 2 })
+ assert_int_equal(0, (2..0).sum)
+ assert_int_equal(5, (2..0).sum(5))
+ assert_int_equal(2, (2..2).sum)
+ assert_int_equal(42, (2...2).sum(42))
+
+ not_a_range = Class.new do
+ include Enumerable # Defines the `#sum` method
+ def each
+ yield 2
+ yield 4
+ yield 6
+ end
+
+ def begin; end
+ def end; end
+ end
+ assert_equal(12, not_a_range.new.sum)
+ end
+
+ def test_uniq
+ src = [1, 1, 1, 1, 2, 2, 3, 4, 5, 6]
+ assert_equal([1, 2, 3, 4, 5, 6], src.uniq.to_a)
+ olympics = {
+ 1896 => 'Athens',
+ 1900 => 'Paris',
+ 1904 => 'Chicago',
+ 1906 => 'Athens',
+ 1908 => 'Rome',
+ }
+ assert_equal([[1896, "Athens"], [1900, "Paris"], [1904, "Chicago"], [1908, "Rome"]],
+ olympics.uniq{|k,v| v})
+ assert_equal([1, 2, 3, 4, 5, 10], (1..100).uniq{|x| (x**2) % 10 }.first(6))
+ assert_equal([1, [1, 2]], Foo.new.to_enum.uniq)
+ end
+
+ def test_transient_heap_sort_by
+ klass = Class.new do
+ include Comparable
+ attr_reader :i
+ def initialize e
+ @i = e
+ end
+ def <=> other
+ GC.start
+ i <=> other.i
+ end
+ end
+ assert_equal [1, 2, 3, 4, 5], (1..5).sort_by{|e| klass.new e}
+ end
end
diff --git a/test/ruby/test_enumerator.rb b/test/ruby/test_enumerator.rb
index 0ee33ca6f1..efcfef580f 100644
--- a/test/ruby/test_enumerator.rb
+++ b/test/ruby/test_enumerator.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestEnumerator < Test::Unit::TestCase
@@ -9,10 +10,13 @@ class TestEnumerator < Test::Unit::TestCase
a.each {|x| yield x }
end
end
+ @sized = @obj.clone
+ def @sized.size
+ 42
+ end
end
def enum_test obj
- i = 0
obj.map{|e|
e
}.sort
@@ -21,7 +25,7 @@ class TestEnumerator < Test::Unit::TestCase
def test_iterators
assert_equal [0, 1, 2], enum_test(3.times)
assert_equal [:x, :y, :z], enum_test([:x, :y, :z].each)
- assert_equal [[:x, 1], [:y, 2]], enum_test({:x=>1, :y=>2})
+ assert_equal [[:x, 1], [:y, 2]], enum_test({:x=>1, :y=>2}.each)
end
## Enumerator as Iterator
@@ -43,7 +47,15 @@ class TestEnumerator < Test::Unit::TestCase
}
end
- def test_nested_itaration
+ def test_loop_return_value
+ assert_equal nil, loop { break }
+ assert_equal 42, loop { break 42 }
+
+ e = Enumerator.new { |y| y << 1; y << 2; :stopped }
+ assert_equal :stopped, loop { e.next while true }
+ end
+
+ def test_nested_iteration
def (o = Object.new).each
yield :ok1
yield [:ok2, :x].each.next
@@ -57,9 +69,22 @@ class TestEnumerator < Test::Unit::TestCase
def test_initialize
assert_equal([1, 2, 3], @obj.to_enum(:foo, 1, 2, 3).to_a)
- assert_equal([1, 2, 3], Enumerator.new(@obj, :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_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
+ # warning: Enumerator.new without a block is deprecated; use Object#to_enum
+ enum.__send__(:initialize, @obj, :foo)
+ end
+ }
end
def test_initialize_copy
@@ -78,6 +103,7 @@ class TestEnumerator < Test::Unit::TestCase
1.times do
foo = [1,2,3].to_enum
GC.start
+ foo
end
GC.start
end
@@ -97,6 +123,36 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal([[1,5],[2,6],[3,7]], @obj.to_enum(:foo, 1, 2, 3).with_index(5).to_a)
end
+ def test_with_index_large_offset
+ bug8010 = '[ruby-dev:47131] [Bug #8010]'
+ s = 1 << (8*1.size-2)
+ assert_equal([[1,s],[2,s+1],[3,s+2]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_a, bug8010)
+ s <<= 1
+ assert_equal([[1,s],[2,s+1],[3,s+2]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_a, bug8010)
+ end
+
+ def test_with_index_nonnum_offset
+ bug8010 = '[ruby-dev:47131] [Bug #8010]'
+ s = Object.new
+ def s.to_int; 1 end
+ assert_equal([[1,1],[2,2],[3,3]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_a, bug8010)
+ end
+
+ def test_with_index_string_offset
+ bug8010 = '[ruby-dev:47131] [Bug #8010]'
+ assert_raise(TypeError, bug8010){ @obj.to_enum(:foo, 1, 2, 3).with_index('1').to_a }
+ end
+
+ def test_with_index_dangling_memo
+ bug9178 = '[ruby-core:58692] [Bug #9178]'
+ assert_separately([], <<-"end;")
+ bug = "#{bug9178}"
+ e = [1].to_enum(:chunk).with_index {|c,i| i == 5}
+ assert_kind_of(Enumerator, e)
+ assert_equal([false, [1]], e.to_a[0], bug)
+ end;
+ end
+
def test_with_object
obj = [0, 1]
ret = (1..10).each.with_object(obj) {|i, memo|
@@ -238,6 +294,21 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal([1,2], e.next_values)
end
+ def test_each_arg
+ o = Object.new
+ def o.each(ary)
+ ary << 1
+ yield
+ end
+ ary = []
+ e = o.to_enum { 1 }
+ assert_equal(1, e.size)
+ e_arg = e.each(ary)
+ assert_equal(nil, e_arg.size)
+ e_arg.next
+ assert_equal([1], ary)
+ end
+
def test_feed
o = Object.new
def o.each(ary)
@@ -336,7 +407,13 @@ class TestEnumerator < Test::Unit::TestCase
e = (0..10).each_cons(2)
assert_equal("#<Enumerator: 0..10:each_cons(2)>", e.inspect)
- e = Enumerator.new {|y| x = y.yield; 10 }
+ e = (0..10).each_with_object({})
+ assert_equal("#<Enumerator: 0..10:each_with_object({})>", e.inspect)
+
+ e = (0..10).each_with_object(a: 1)
+ assert_equal("#<Enumerator: 0..10:each_with_object(a: 1)>", e.inspect)
+
+ e = Enumerator.new {|y| y.yield; 10 }
assert_match(/\A#<Enumerator: .*:each>/, e.inspect)
a = []
@@ -346,6 +423,20 @@ class TestEnumerator < Test::Unit::TestCase
e.inspect)
end
+ def test_inspect_verbose
+ bug6214 = '[ruby-dev:45449]'
+ assert_warning("", bug6214) { "".bytes.inspect }
+ assert_warning("", bug6214) { [].lazy.inspect }
+ end
+
+ def test_inspect_encoding
+ c = Class.new{define_method("\u{3042}"){}}
+ e = c.new.enum_for("\u{3042}")
+ s = assert_nothing_raised(Encoding::CompatibilityError) {break e.inspect}
+ assert_equal(Encoding::UTF_8, s.encoding)
+ assert_match(/\A#<Enumerator: .*:\u{3042}>\z/, s)
+ end
+
def test_generator
# note: Enumerator::Generator is a class just for internal
g = Enumerator::Generator.new {|y| y << 1 << 2 << 3; :foo }
@@ -356,6 +447,30 @@ class TestEnumerator < Test::Unit::TestCase
a = []
assert_equal(:foo, g2.each {|x| a << x })
assert_equal([1, 2, 3], a)
+
+ g.freeze
+ assert_raise(FrozenError) {
+ g.__send__ :initialize, proc { |y| y << 4 << 5 }
+ }
+
+ g = Enumerator::Generator.new(proc {|y| y << 4 << 5; :foo })
+ a = []
+ assert_equal(:foo, g.each {|x| a << x })
+ assert_equal([4, 5], a)
+
+ assert_raise(LocalJumpError) {Enumerator::Generator.new}
+ assert_raise(TypeError) {Enumerator::Generator.new(1)}
+ obj = eval("class C\u{1f5ff}; self; end").new
+ assert_raise_with_message(TypeError, /C\u{1f5ff}/) {
+ Enumerator::Generator.new(obj)
+ }
+ end
+
+ def test_generator_args
+ g = Enumerator::Generator.new {|y, x| y << 1 << 2 << 3; x }
+ a = []
+ assert_equal(:bar, g.each(:bar) {|x| a << x })
+ assert_equal([1, 2, 3], a)
end
def test_yielder
@@ -370,8 +485,310 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal([1], y.yield(1))
assert_equal([1, 2], y.yield(2))
assert_equal([1, 2, 3], y.yield(3))
+ assert_equal([1, 2, 3, 4], y.yield(4, 5))
+
+ a = []
+ y = Enumerator::Yielder.new {|*x| a.concat(x) }
+ assert_equal([1], y.yield(1))
+ assert_equal([1, 2, 3], y.yield(2, 3))
assert_raise(LocalJumpError) { Enumerator::Yielder.new }
end
-end
+ def test_size
+ assert_equal nil, Enumerator.new{}.size
+ assert_equal 42, Enumerator.new(->{42}){}.size
+ obj = Object.new
+ def obj.call; 42; end
+ assert_equal 42, Enumerator.new(obj){}.size
+ assert_equal 42, Enumerator.new(42){}.size
+ assert_equal 1 << 70, Enumerator.new(1 << 70){}.size
+ assert_equal Float::INFINITY, Enumerator.new(Float::INFINITY){}.size
+ assert_equal nil, Enumerator.new(nil){}.size
+ assert_raise(TypeError) { Enumerator.new("42"){} }
+
+ assert_equal nil, @obj.to_enum(:foo, 0, 1).size
+ assert_equal 2, @obj.to_enum(:foo, 0, 1){ 2 }.size
+ end
+
+ def test_size_for_enum_created_by_enumerators
+ enum = to_enum{ 42 }
+ assert_equal 42, enum.with_index.size
+ assert_equal 42, enum.with_object(:foo).size
+ end
+
+ def test_size_for_enum_created_from_array
+ arr = %w[hello world]
+ %i[each each_with_index reverse_each sort_by! sort_by map map!
+ keep_if reject! reject select! select filter! filter delete_if].each do |method|
+ assert_equal arr.size, arr.send(method).size
+ end
+ end
+
+ def test_size_for_enum_created_from_enumerable
+ %i[find_all reject map flat_map partition group_by sort_by min_by max_by
+ minmax_by each_with_index reverse_each each_entry].each do |method|
+ assert_equal nil, @obj.send(method).size
+ assert_equal 42, @sized.send(method).size
+ end
+ assert_equal nil, @obj.each_with_object(nil).size
+ assert_equal 42, @sized.each_with_object(nil).size
+ end
+
+ def test_size_for_enum_created_from_hash
+ h = {a: 1, b: 2, c: 3}
+ methods = %i[delete_if reject reject! select select! filter filter! keep_if each each_key each_pair]
+ enums = methods.map {|method| h.send(method)}
+ s = enums.group_by(&:size)
+ assert_equal([3], s.keys, ->{s.reject!{|k| k==3}.inspect})
+ h[:d] = 4
+ s = enums.group_by(&:size)
+ assert_equal([4], s.keys, ->{s.reject!{|k| k==4}.inspect})
+ end
+
+ def test_size_for_enum_created_from_env
+ %i[each_pair reject! delete_if select select! filter filter! keep_if].each do |method|
+ assert_equal ENV.size, ENV.send(method).size
+ end
+ end
+
+ def test_size_for_enum_created_from_struct
+ s = Struct.new(:foo, :bar, :baz).new(1, 2)
+ %i[each each_pair select].each do |method|
+ assert_equal 3, s.send(method).size
+ end
+ end
+
+ def check_consistency_for_combinatorics(method)
+ [ [], [:a, :b, :c, :d, :e] ].product([-2, 0, 2, 5, 6]) do |array, arg|
+ assert_equal array.send(method, arg).to_a.size, array.send(method, arg).size,
+ "inconsistent size for #{array}.#{method}(#{arg})"
+ end
+ end
+
+ def test_size_for_array_combinatorics
+ check_consistency_for_combinatorics(:permutation)
+ assert_equal 24, [0, 1, 2, 4].permutation.size
+ assert_equal 2933197128679486453788761052665610240000000,
+ (1..42).to_a.permutation(30).size # 1.upto(42).inject(:*) / 1.upto(12).inject(:*)
+
+ check_consistency_for_combinatorics(:combination)
+ assert_equal 28258808871162574166368460400,
+ (1..100).to_a.combination(42).size
+ # 1.upto(100).inject(:*) / 1.upto(42).inject(:*) / 1.upto(58).inject(:*)
+
+ check_consistency_for_combinatorics(:repeated_permutation)
+ assert_equal 291733167875766667063796853374976,
+ (1..42).to_a.repeated_permutation(20).size # 42 ** 20
+
+ check_consistency_for_combinatorics(:repeated_combination)
+ assert_equal 28258808871162574166368460400,
+ (1..59).to_a.repeated_combination(42).size
+ # 1.upto(100).inject(:*) / 1.upto(42).inject(:*) / 1.upto(58).inject(:*)
+ end
+
+ def test_size_for_cycle
+ assert_equal Float::INFINITY, [:foo].cycle.size
+ assert_equal 10, [:foo, :bar].cycle(5).size
+ assert_equal 0, [:foo, :bar].cycle(-10).size
+ assert_equal Float::INFINITY, {foo: 1}.cycle.size
+ assert_equal 10, {foo: 1, bar: 2}.cycle(5).size
+ assert_equal 0, {foo: 1, bar: 2}.cycle(-10).size
+ assert_equal 0, [].cycle.size
+ assert_equal 0, [].cycle(5).size
+ assert_equal 0, {}.cycle.size
+ assert_equal 0, {}.cycle(5).size
+
+ assert_equal nil, @obj.cycle.size
+ assert_equal nil, @obj.cycle(5).size
+ assert_equal Float::INFINITY, @sized.cycle.size
+ assert_equal 126, @sized.cycle(3).size
+ assert_equal Float::INFINITY, [].to_enum { 42 }.cycle.size
+ assert_equal 0, [].to_enum { 0 }.cycle.size
+
+ assert_raise(TypeError) {[].to_enum { 0 }.cycle("").size}
+ end
+
+ def test_size_for_loops
+ assert_equal Float::INFINITY, loop.size
+ assert_equal 42, 42.times.size
+ end
+
+ def test_size_for_each_slice
+ assert_equal nil, @obj.each_slice(3).size
+ assert_equal 6, @sized.each_slice(7).size
+ assert_equal 5, @sized.each_slice(10).size
+ assert_equal 1, @sized.each_slice(70).size
+ assert_raise(ArgumentError){ @obj.each_slice(0).size }
+ end
+
+ def test_size_for_each_cons
+ assert_equal nil, @obj.each_cons(3).size
+ assert_equal 33, @sized.each_cons(10).size
+ assert_equal 0, @sized.each_cons(70).size
+ assert_raise(ArgumentError){ @obj.each_cons(0).size }
+ end
+
+ def test_size_for_step
+ assert_equal 42, 5.step(46).size
+ assert_equal 4, 1.step(10, 3).size
+ assert_equal 3, 1.step(9, 3).size
+ assert_equal 0, 1.step(-11).size
+ assert_equal 0, 1.step(-11, 2).size
+ assert_equal 7, 1.step(-11, -2).size
+ assert_equal 7, 1.step(-11.1, -2).size
+ assert_equal 0, 42.step(Float::INFINITY, -2).size
+ assert_equal 1, 42.step(55, Float::INFINITY).size
+ assert_equal 1, 42.step(Float::INFINITY, Float::INFINITY).size
+ assert_equal 14, 0.1.step(4.2, 0.3).size
+ assert_equal Float::INFINITY, 42.step(Float::INFINITY, 2).size
+
+ assert_equal 10, (1..10).step.size
+ assert_equal 4, (1..10).step(3).size
+ assert_equal 3, (1...10).step(3).size
+ assert_equal Float::INFINITY, (42..Float::INFINITY).step(2).size
+ assert_equal 0, (1..10).step(-2).size
+ end
+
+ def test_size_for_downup_to
+ assert_equal 0, 1.upto(-100).size
+ assert_equal 102, 1.downto(-100).size
+ assert_equal Float::INFINITY, 42.upto(Float::INFINITY).size
+ end
+
+ def test_size_for_string
+ assert_equal 5, 'hello'.each_byte.size
+ assert_equal 5, 'hello'.each_char.size
+ assert_equal 5, 'hello'.each_codepoint.size
+ end
+
+ def test_peek_for_enumerator_objects
+ e = 2.times
+ assert_equal(0, e.peek)
+ e.next
+ assert_equal(1, e.peek)
+ e.next
+ assert_raise(StopIteration) { e.peek }
+ end
+
+ def test_uniq
+ u = [0, 1, 0, 1].to_enum.lazy.uniq
+ assert_equal([0, 1], u.force)
+ assert_equal([0, 1], u.force)
+ end
+
+ def test_enum_chain_and_plus
+ r = 1..5
+
+ e1 = r.chain()
+ assert_kind_of(Enumerator::Chain, e1)
+ assert_equal(5, e1.size)
+ ary = []
+ e1.each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5], ary)
+
+ e2 = r.chain([6, 7, 8])
+ assert_kind_of(Enumerator::Chain, e2)
+ assert_equal(8, e2.size)
+ ary = []
+ e2.each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5, 6, 7, 8], ary)
+
+ e3 = r.chain([6, 7], 8.step)
+ assert_kind_of(Enumerator::Chain, e3)
+ assert_equal(Float::INFINITY, e3.size)
+ ary = []
+ e3.take(10).each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ary)
+
+ # `a + b + c` should not return `Enumerator::Chain.new(a, b, c)`
+ # because it is expected that `(a + b).each` be called.
+ e4 = e2.dup
+ class << e4
+ attr_reader :each_is_called
+ def each
+ super
+ @each_is_called = true
+ end
+ end
+ e5 = e4 + 9.step
+ assert_kind_of(Enumerator::Chain, e5)
+ assert_equal(Float::INFINITY, e5.size)
+ ary = []
+ e5.take(10).each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ary)
+ assert_equal(true, e4.each_is_called)
+ end
+
+ def test_chained_enums
+ a = (1..5).each
+
+ e0 = Enumerator::Chain.new()
+ assert_kind_of(Enumerator::Chain, e0)
+ assert_equal(0, e0.size)
+ ary = []
+ e0.each { |x| ary << x }
+ assert_equal([], ary)
+
+ e1 = Enumerator::Chain.new(a)
+ assert_kind_of(Enumerator::Chain, e1)
+ assert_equal(5, e1.size)
+ ary = []
+ e1.each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5], ary)
+
+ e2 = Enumerator::Chain.new(a, [6, 7, 8])
+ assert_kind_of(Enumerator::Chain, e2)
+ assert_equal(8, e2.size)
+ ary = []
+ e2.each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5, 6, 7, 8], ary)
+
+ e3 = Enumerator::Chain.new(a, [6, 7], 8.step)
+ assert_kind_of(Enumerator::Chain, e3)
+ assert_equal(Float::INFINITY, e3.size)
+ ary = []
+ e3.take(10).each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ary)
+
+ e4 = Enumerator::Chain.new(a, Enumerator.new { |y| y << 6 << 7 << 8 })
+ assert_kind_of(Enumerator::Chain, e4)
+ assert_equal(nil, e4.size)
+ ary = []
+ e4.each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5, 6, 7, 8], ary)
+
+ e5 = Enumerator::Chain.new(e1, e2)
+ assert_kind_of(Enumerator::Chain, e5)
+ assert_equal(13, e5.size)
+ ary = []
+ e5.each { |x| ary << x }
+ assert_equal([1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 7, 8], ary)
+
+ rewound = []
+ e1.define_singleton_method(:rewind) { rewound << object_id }
+ e2.define_singleton_method(:rewind) { rewound << object_id }
+ e5.rewind
+ assert_equal(rewound, [e2.object_id, e1.object_id])
+
+ rewound = []
+ a = [1]
+ e6 = Enumerator::Chain.new(a)
+ a.define_singleton_method(:rewind) { rewound << object_id }
+ e6.rewind
+ assert_equal(rewound, [])
+
+ assert_equal(
+ '#<Enumerator::Chain: [' +
+ '#<Enumerator::Chain: [' +
+ '#<Enumerator: 1..5:each>' +
+ ']>, ' +
+ '#<Enumerator::Chain: [' +
+ '#<Enumerator: 1..5:each>, ' +
+ '[6, 7, 8]' +
+ ']>' +
+ ']>',
+ e5.inspect
+ )
+ end
+end
diff --git a/test/ruby/test_env.rb b/test/ruby/test_env.rb
index 060c50e68e..e9fb2524f6 100644
--- a/test/ruby/test_env.rb
+++ b/test/ruby/test_env.rb
@@ -1,8 +1,24 @@
+# frozen_string_literal: false
require 'test/unit'
class TestEnv < Test::Unit::TestCase
IGNORE_CASE = /bccwin|mswin|mingw/ =~ RUBY_PLATFORM
PATH_ENV = "PATH"
+ INVALID_ENVVARS = [
+ "foo\0bar",
+ "\xa1\xa1".force_encoding(Encoding::UTF_16LE),
+ "foo".force_encoding(Encoding::ISO_2022_JP),
+ ]
+
+ def assert_invalid_env(msg = nil)
+ all_assertions(msg) do |a|
+ INVALID_ENVVARS.each do |v|
+ a.for(v) do
+ assert_raise(ArgumentError) {yield v}
+ end
+ end
+ end
+ end
def setup
@verbose = $VERBOSE
@@ -30,6 +46,7 @@ class TestEnv < Test::Unit::TestCase
end
ENV['TEST'] = 'bar'
assert_equal('bar', ENV['TEST'])
+ assert_predicate(ENV['TEST'], :tainted?)
if IGNORE_CASE
assert_equal('bar', ENV['test'])
else
@@ -37,7 +54,7 @@ class TestEnv < Test::Unit::TestCase
end
assert_raise(TypeError) {
- tmp = ENV[1]
+ ENV[1]
}
assert_raise(TypeError) {
ENV[1] = 'foo'
@@ -87,56 +104,63 @@ class TestEnv < Test::Unit::TestCase
end
def test_delete
- assert_raise(ArgumentError) { ENV.delete("foo\0bar") }
+ assert_invalid_env {|v| ENV.delete(v)}
assert_nil(ENV.delete("TEST"))
assert_nothing_raised { ENV.delete(PATH_ENV) }
end
def test_getenv
- assert_raise(ArgumentError) { ENV["foo\0bar"] }
+ assert_invalid_env {|v| ENV[v]}
ENV[PATH_ENV] = ""
assert_equal("", ENV[PATH_ENV])
+ assert_predicate(ENV[PATH_ENV], :tainted?)
+ assert_nil(ENV[""])
end
def test_fetch
ENV["test"] = "foo"
assert_equal("foo", ENV.fetch("test"))
ENV.delete("test")
- assert_raise(KeyError) { ENV.fetch("test") }
+ feature8649 = '[ruby-core:56062] [Feature #8649]'
+ e = assert_raise_with_message(KeyError, 'key not found: "test"', feature8649) do
+ ENV.fetch("test")
+ end
+ assert_same(ENV, e.receiver)
+ assert_equal("test", e.key)
assert_equal("foo", ENV.fetch("test", "foo"))
assert_equal("bar", ENV.fetch("test") { "bar" })
- assert_equal("bar", ENV.fetch("test", "foo") { "bar" })
- assert_raise(ArgumentError) { ENV.fetch("foo\0bar") }
+ EnvUtil.suppress_warning do
+ assert_equal("bar", ENV.fetch("test", "foo") { "bar" })
+ end
+ assert_invalid_env {|v| ENV.fetch(v)}
assert_nothing_raised { ENV.fetch(PATH_ENV, "foo") }
ENV[PATH_ENV] = ""
assert_equal("", ENV.fetch(PATH_ENV))
+ assert_predicate(ENV.fetch(PATH_ENV), :tainted?)
end
def test_aset
- assert_raise(SecurityError) do
- Thread.new do
- $SAFE = 4
- ENV["test"] = "foo"
- end.join
- end
assert_nothing_raised { ENV["test"] = nil }
assert_equal(nil, ENV["test"])
- assert_raise(ArgumentError) { ENV["foo\0bar"] = "test" }
- assert_raise(ArgumentError) { ENV["test"] = "foo\0bar" }
- if /netbsd/ =~ RUBY_PLATFORM
+ assert_invalid_env {|v| ENV[v] = "test"}
+ assert_invalid_env {|v| ENV["test"] = v}
+
+ begin
+ # setenv(3) allowed the name includes '=',
+ # but POSIX.1-2001 says it should fail with EINVAL.
+ # see also http://togetter.com/li/22380
ENV["foo=bar"] = "test"
assert_equal("test", ENV["foo=bar"])
assert_equal("test", ENV["foo"])
- else
- assert_raise(Errno::EINVAL) { ENV["foo=bar"] = "test" }
+ rescue Errno::EINVAL
end
+
ENV[PATH_ENV] = "/tmp/".taint
assert_equal("/tmp/", ENV[PATH_ENV])
end
def test_keys
- a = nil
- assert_block { a = ENV.keys }
+ a = ENV.keys
assert_kind_of(Array, a)
a.each {|k| assert_kind_of(String, k) }
end
@@ -146,8 +170,7 @@ class TestEnv < Test::Unit::TestCase
end
def test_values
- a = nil
- assert_block { a = ENV.values }
+ a = ENV.values
assert_kind_of(Array, a)
a.each {|k| assert_kind_of(String, k) }
end
@@ -172,6 +195,10 @@ class TestEnv < Test::Unit::TestCase
ENV.each_pair {|k, v| h2[k] = v }
assert_equal(h1, h2)
+ assert_nil(ENV.reject! {|k, v| IGNORE_CASE ? k.upcase == "TEST" : k == "test" })
+ end
+
+ def test_delete_if
h1 = {}
ENV.each_pair {|k, v| h1[k] = v }
ENV["test"] = "foo"
@@ -179,6 +206,8 @@ class TestEnv < Test::Unit::TestCase
h2 = {}
ENV.each_pair {|k, v| h2[k] = v }
assert_equal(h1, h2)
+
+ assert_equal(ENV, ENV.delete_if {|k, v| IGNORE_CASE ? k.upcase == "TEST" : k == "test" })
end
def test_select_bang
@@ -190,6 +219,22 @@ class TestEnv < Test::Unit::TestCase
ENV.each_pair {|k, v| h2[k] = v }
assert_equal(h1, h2)
+ assert_nil(ENV.select! {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" })
+ end
+
+ def test_filter_bang
+ h1 = {}
+ ENV.each_pair {|k, v| h1[k] = v }
+ ENV["test"] = "foo"
+ ENV.filter! {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" }
+ h2 = {}
+ ENV.each_pair {|k, v| h2[k] = v }
+ assert_equal(h1, h2)
+
+ assert_nil(ENV.filter! {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" })
+ end
+
+ def test_keep_if
h1 = {}
ENV.each_pair {|k, v| h1[k] = v }
ENV["test"] = "foo"
@@ -197,6 +242,8 @@ class TestEnv < Test::Unit::TestCase
h2 = {}
ENV.each_pair {|k, v| h2[k] = v }
assert_equal(h1, h2)
+
+ assert_equal(ENV, ENV.keep_if {|k, v| IGNORE_CASE ? k.upcase != "TEST" : k != "test" })
end
def test_values_at
@@ -219,6 +266,32 @@ class TestEnv < Test::Unit::TestCase
end
end
+ def test_filter
+ ENV["test"] = "foo"
+ h = ENV.filter {|k| IGNORE_CASE ? k.upcase == "TEST" : k == "test" }
+ assert_equal(1, h.size)
+ k = h.keys.first
+ v = h.values.first
+ if IGNORE_CASE
+ assert_equal("TEST", k.upcase)
+ assert_equal("FOO", v.upcase)
+ else
+ assert_equal("test", k)
+ assert_equal("foo", v)
+ end
+ end
+
+ def test_slice
+ ENV.clear
+ ENV["foo"] = "bar"
+ ENV["baz"] = "qux"
+ ENV["bar"] = "rab"
+ assert_equal({}, ENV.slice())
+ assert_equal({}, ENV.slice(""))
+ assert_equal({}, ENV.slice("unknown"))
+ assert_equal({"foo"=>"bar", "baz"=>"qux"}, ENV.slice("foo", "baz"))
+ end
+
def test_clear
ENV.clear
assert_equal(0, ENV.size)
@@ -267,16 +340,16 @@ class TestEnv < Test::Unit::TestCase
def test_empty_p
ENV.clear
- assert(ENV.empty?)
+ assert_predicate(ENV, :empty?)
ENV["test"] = "foo"
- assert(!ENV.empty?)
+ assert_not_predicate(ENV, :empty?)
end
def test_has_key
- assert(!ENV.has_key?("test"))
+ assert_not_send([ENV, :has_key?, "test"])
ENV["test"] = "foo"
- assert(ENV.has_key?("test"))
- assert_raise(ArgumentError) { ENV.has_key?("foo\0bar") }
+ assert_send([ENV, :has_key?, "test"])
+ assert_invalid_env {|v| ENV.has_key?(v)}
end
def test_assoc
@@ -290,14 +363,16 @@ class TestEnv < Test::Unit::TestCase
assert_equal("test", k)
assert_equal("foo", v)
end
- assert_raise(ArgumentError) { ENV.assoc("foo\0bar") }
+ assert_invalid_env {|var| ENV.assoc(var)}
+ assert_predicate(v, :tainted?)
+ assert_equal(Encoding.find("locale"), v.encoding)
end
def test_has_value2
ENV.clear
- assert(!ENV.has_value?("foo"))
+ assert_not_send([ENV, :has_value?, "foo"])
ENV["test"] = "foo"
- assert(ENV.has_value?("foo"))
+ assert_send([ENV, :has_value?, "foo"])
end
def test_rassoc
@@ -322,6 +397,12 @@ class TestEnv < Test::Unit::TestCase
assert_equal(h, ENV.to_hash)
end
+ def test_to_h
+ assert_equal(ENV.to_hash, ENV.to_h)
+ assert_equal(ENV.map {|k, v| ["$#{k}", v.size]}.to_h,
+ ENV.to_h {|k, v| ["$#{k}", v.size]})
+ end
+
def test_reject
h1 = {}
ENV.each_pair {|k, v| h1[k] = v }
@@ -359,6 +440,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
@@ -374,4 +457,129 @@ class TestEnv < Test::Unit::TestCase
ENV.update({"baz"=>"quux","a"=>"b"}) {|k, v1, v2| v1 ? k + "_" + v1 + "_" + v2 : v2 }
check(ENV.to_hash.to_a, [%w(foo bar), %w(baz baz_qux_quux), %w(a b)])
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"])
+ else
+ assert_nothing_raised { ENV["foo"] = huge_value }
+ assert_equal(huge_value, ENV["foo"])
+ end
+ end
+
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ def windows_version
+ @windows_version ||= %x[ver][/Version (\d+)/, 1].to_i
+ end
+
+ def test_win32_blocksize
+ keys = []
+ len = 32767 - ENV.to_a.flatten.inject(1) {|r,e| r + e.bytesize + 1}
+ val = "bar" * 1000
+ key = nil
+ while (len -= val.size + (key="foo#{len}").size + 2) > 0
+ keys << key
+ ENV[key] = val
+ end
+ if windows_version < 6
+ 1.upto(12) {|i|
+ assert_raise(Errno::EINVAL) { ENV[key] = val }
+ }
+ else
+ 1.upto(12) {|i|
+ assert_nothing_raised(Errno::EINVAL) { ENV[key] = val }
+ }
+ end
+ ensure
+ keys.each {|k| ENV.delete(k)}
+ end
+ end
+
+ def test_frozen
+ ENV[PATH_ENV] = "/"
+ ENV.each do |k, v|
+ assert_predicate(k, :frozen?)
+ assert_predicate(v, :frozen?)
+ end
+ ENV.each_key do |k|
+ assert_predicate(k, :frozen?)
+ end
+ ENV.each_value do |v|
+ assert_predicate(v, :frozen?)
+ end
+ ENV.each_key do |k|
+ assert_predicate(ENV[k], :frozen?, "[#{k.dump}]")
+ assert_predicate(ENV.fetch(k), :frozen?, "fetch(#{k.dump})")
+ end
+ end
+
+ def test_shared_substring
+ 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]
+ assert_equal("T#{n}", e0, bug12475)
+ assert_nil(e1, bug12475)
+ end
+
+ if RUBY_PLATFORM =~ /bccwin|mswin|mingw/
+ def test_memory_leak_aset
+ bug9977 = '[ruby-dev:48323] [Bug #9977]'
+ assert_no_memory_leak([], <<-'end;', "5_000.times(&doit)", bug9977, limit: 2.0)
+ ENV.clear
+ k = 'FOO'
+ v = (ENV[k] = 'bar'*5000 rescue 'bar'*1500)
+ doit = proc {ENV[k] = v}
+ 500.times(&doit)
+ end;
+ end
+
+ def test_memory_leak_select
+ bug9978 = '[ruby-dev:48325] [Bug #9978]'
+ assert_no_memory_leak([], <<-'end;', "5_000.times(&doit)", bug9978, limit: 2.0)
+ ENV.clear
+ k = 'FOO'
+ (ENV[k] = 'bar'*5000 rescue 'bar'*1500)
+ doit = proc {ENV.select {break}}
+ 500.times(&doit)
+ end;
+ end
+
+ def test_memory_crash_select
+ assert_normal_exit(<<-'end;')
+ 1000.times {ENV["FOO#{i}"] = 'bar'}
+ ENV.select {ENV.clear}
+ end;
+ end
+
+ def test_memory_leak_shift
+ bug9983 = '[ruby-dev:48332] [Bug #9983]'
+ assert_no_memory_leak([], <<-'end;', "5_000.times(&doit)", bug9983, limit: 2.0)
+ ENV.clear
+ k = 'FOO'
+ v = (ENV[k] = 'bar'*5000 rescue 'bar'*1500)
+ doit = proc {ENV[k] = v; ENV.shift}
+ 500.times(&doit)
+ 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
+ end
+ end
end
diff --git a/test/ruby/test_eval.rb b/test/ruby/test_eval.rb
index a6900e075e..9cb69ddc37 100644
--- a/test/ruby/test_eval.rb
+++ b/test/ruby/test_eval.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestEval < Test::Unit::TestCase
@@ -127,72 +127,87 @@ class TestEval < Test::Unit::TestCase
}
end
- def forall_TYPE(mid)
- objects = [Object.new, [], nil, true, false, 77, :sym] # TODO: check
+ def test_module_eval_block_symbol
+ assert_equal "Math", Math.module_eval(&:to_s)
+ end
+
+ def forall_TYPE
+ objects = [Object.new, [], nil, true, false] # TODO: check
objects.each do |obj|
- obj.instance_variable_set :@ivar, 12
- send mid, obj
+ obj.instance_variable_set :@ivar, 12 unless obj.frozen?
+ yield obj
end
end
def test_instance_eval_string_basic
- forall_TYPE :instance_eval_string_basic_i
- end
-
- def instance_eval_string_basic_i(o)
- assert_equal nil, o.instance_eval("nil")
- assert_equal true, o.instance_eval("true")
- assert_equal false, o.instance_eval("false")
- assert_equal o, o.instance_eval("self")
- assert_equal 1, o.instance_eval("1")
- assert_equal :sym, o.instance_eval(":sym")
-
- assert_equal 11, o.instance_eval("11")
- assert_equal 12, o.instance_eval("@ivar")
- assert_equal 13, o.instance_eval("@@cvar")
- assert_equal 14, o.instance_eval("$gvar__eval")
- assert_equal 15, o.instance_eval("Const")
- assert_equal 16, o.instance_eval("7 + 9")
- assert_equal 17, o.instance_eval("17.to_i")
- assert_equal "18", o.instance_eval(%q("18"))
- assert_equal "19", o.instance_eval(%q("1#{9}"))
-
- 1.times {
- assert_equal 12, o.instance_eval("@ivar")
- assert_equal 13, o.instance_eval("@@cvar")
- assert_equal 14, o.instance_eval("$gvar__eval")
- assert_equal 15, o.instance_eval("Const")
- }
+ forall_TYPE do |o|
+ assert_equal nil, o.instance_eval("nil")
+ assert_equal true, o.instance_eval("true")
+ assert_equal false, o.instance_eval("false")
+ assert_equal o, o.instance_eval("self")
+ assert_equal 1, o.instance_eval("1")
+ assert_equal :sym, o.instance_eval(":sym")
+
+ assert_equal 11, o.instance_eval("11")
+ assert_equal 12, o.instance_eval("@ivar") unless o.frozen?
+ assert_equal 13, o.instance_eval("@@cvar")
+ assert_equal 14, o.instance_eval("$gvar__eval")
+ assert_equal 15, o.instance_eval("Const")
+ assert_equal 16, o.instance_eval("7 + 9")
+ assert_equal 17, o.instance_eval("17.to_i")
+ assert_equal "18", o.instance_eval(%q("18"))
+ assert_equal "19", o.instance_eval(%q("1#{9}"))
+
+ 1.times {
+ assert_equal 12, o.instance_eval("@ivar") unless o.frozen?
+ assert_equal 13, o.instance_eval("@@cvar")
+ assert_equal 14, o.instance_eval("$gvar__eval")
+ assert_equal 15, o.instance_eval("Const")
+ }
+ end
end
def test_instance_eval_block_basic
- forall_TYPE :instance_eval_block_basic_i
- end
-
- def instance_eval_block_basic_i(o)
- assert_equal nil, o.instance_eval { nil }
- assert_equal true, o.instance_eval { true }
- assert_equal false, o.instance_eval { false }
- assert_equal o, o.instance_eval { self }
- assert_equal 1, o.instance_eval { 1 }
- assert_equal :sym, o.instance_eval { :sym }
-
- assert_equal 11, o.instance_eval { 11 }
- assert_equal 12, o.instance_eval { @ivar }
- assert_equal 13, o.instance_eval { @@cvar }
- assert_equal 14, o.instance_eval { $gvar__eval }
- assert_equal 15, o.instance_eval { Const }
- assert_equal 16, o.instance_eval { 7 + 9 }
- assert_equal 17, o.instance_eval { 17.to_i }
- assert_equal "18", o.instance_eval { "18" }
- assert_equal "19", o.instance_eval { "1#{9}" }
+ forall_TYPE do |o|
+ assert_equal nil, o.instance_eval { nil }
+ assert_equal true, o.instance_eval { true }
+ assert_equal false, o.instance_eval { false }
+ assert_equal o, o.instance_eval { self }
+ assert_equal 1, o.instance_eval { 1 }
+ assert_equal :sym, o.instance_eval { :sym }
+
+ assert_equal 11, o.instance_eval { 11 }
+ assert_equal 12, o.instance_eval { @ivar } unless o.frozen?
+ assert_equal 13, o.instance_eval { @@cvar }
+ assert_equal 14, o.instance_eval { $gvar__eval }
+ assert_equal 15, o.instance_eval { Const }
+ assert_equal 16, o.instance_eval { 7 + 9 }
+ assert_equal 17, o.instance_eval { 17.to_i }
+ assert_equal "18", o.instance_eval { "18" }
+ assert_equal "19", o.instance_eval { "1#{9}" }
+
+ 1.times {
+ assert_equal 12, o.instance_eval { @ivar } unless o.frozen?
+ assert_equal 13, o.instance_eval { @@cvar }
+ assert_equal 14, o.instance_eval { $gvar__eval }
+ assert_equal 15, o.instance_eval { Const }
+ }
+ end
+ end
- 1.times {
- assert_equal 12, o.instance_eval { @ivar }
- assert_equal 13, o.instance_eval { @@cvar }
- assert_equal 14, o.instance_eval { $gvar__eval }
- assert_equal 15, o.instance_eval { Const }
- }
+ def test_instance_eval_block_self
+ # instance_eval(&block)'s self must not be sticky (jruby/jruby#2060)
+ pr = proc { self }
+ assert_equal self, pr.call
+ o = Object.new
+ assert_equal o, o.instance_eval(&pr)
+ assert_equal self, pr.call
+ end
+
+ def test_instance_eval_block_symbol
+ forall_TYPE do |o|
+ assert_equal o.to_s, o.instance_eval(&:to_s)
+ end
end
def test_instance_eval_cvar
@@ -204,29 +219,61 @@ class TestEval < Test::Unit::TestCase
end
end
+ def test_instance_eval_method
+ bug2788 = '[ruby-core:28324]'
+ [Object.new, [], nil, true, false].each do |o|
+ assert_nothing_raised(TypeError, "#{bug2788} (#{o.inspect})") do
+ o.instance_eval {
+ def defd_using_instance_eval() :ok end
+ }
+ end
+ assert_equal(:ok, o.defd_using_instance_eval)
+ class << o
+ remove_method :defd_using_instance_eval
+ end
+ end
+ end
+
+ def test_instance_eval_on_argf_singleton_class
+ bug8188 = '[ruby-core:53839] [Bug #8188]'
+ assert_warning('', bug8188) do
+ ARGF.singleton_class.instance_eval{}
+ end
+ end
+
+ class Foo
+ Bar = 2
+ end
+
+ def test_instance_eval_const
+ bar = nil
+ assert_nothing_raised(NameError) do
+ bar = Foo.new.instance_eval("Bar")
+ end
+ assert_equal(2, bar)
+ end
+
#
# From ruby/test/ruby/test_eval.rb
#
- def test_ev
- local1 = "local1"
+ def make_test_binding
+ local1 = local1 = "local1"
lambda {
- local2 = "local2"
+ local2 = local2 = "local2"
return binding
}.call
end
def test_eval_orig
assert_nil(eval(""))
- $bad=false
- eval 'while false; $bad = true; print "foo\n" end'
- assert(!$bad)
+ bad=false
+ eval 'while false; bad = true; print "foo\n" end'
+ assert(!bad)
- assert(eval('TRUE'))
+ assert(eval('Object'))
assert(eval('true'))
- assert(!eval('NIL'))
assert(!eval('nil'))
- assert(!eval('FALSE'))
assert(!eval('false'))
$foo = 'assert(true)'
@@ -238,53 +285,40 @@ class TestEval < Test::Unit::TestCase
assert_equal('assert(true)', eval("$foo"))
assert_equal(true, eval("true"))
- i = 5
+ i = i = 5
assert(eval("i == 5"))
assert_equal(5, eval("i"))
assert(eval("defined? i"))
- $x = test_ev
- assert_equal("local1", eval("local1", $x)) # normal local var
- assert_equal("local2", eval("local2", $x)) # nested local var
- $bad = true
+ x = make_test_binding
+ assert_equal("local1", eval("local1", x)) # normal local var
+ assert_equal("local2", eval("local2", x)) # nested local var
+ bad = true
begin
p eval("local1")
rescue NameError # must raise error
- $bad = false
+ bad = false
end
- assert(!$bad)
+ assert(!bad)
# !! use class_eval to avoid nested definition
- self.class.class_eval %q(
+ x = self.class.class_eval %q(
module EvTest
EVTEST1 = 25
evtest2 = 125
- $x = binding
+ evtest2 = evtest2
+ binding
end
)
- assert_equal(25, eval("EVTEST1", $x)) # constant in module
- assert_equal(125, eval("evtest2", $x)) # local var in module
- $bad = true
+ assert_equal(25, eval("EVTEST1", x)) # constant in module
+ assert_equal(125, eval("evtest2", x)) # local var in module
+ bad = true
begin
eval("EVTEST1")
rescue NameError # must raise error
- $bad = false
- end
- assert(!$bad)
-
- if false
- # Ruby 2.0 doesn't see Proc as Binding
- x = proc{}
- eval "i4 = 1", x
- assert_equal(1, eval("i4", x))
- x = proc{proc{}}.call
- eval "i4 = 22", x
- assert_equal(22, eval("i4", x))
- $x = []
- x = proc{proc{}}.call
- eval "(0..9).each{|i5| $x[i5] = proc{i5*2}}", x
- assert_equal(8, $x[4].call)
+ bad = false
end
+ assert(!bad)
x = binding
eval "i = 1", x
@@ -292,10 +326,10 @@ class TestEval < Test::Unit::TestCase
x = proc{binding}.call
eval "i = 22", x
assert_equal(22, eval("i", x))
- $x = []
+ t = []
x = proc{binding}.call
- eval "(0..9).each{|i5| $x[i5] = proc{i5*2}}", x
- assert_equal(8, $x[4].call)
+ eval "(0..9).each{|i5| t[i5] = proc{i5*2}}", x
+ assert_equal(8, t[4].call)
x = proc{binding}.call
eval "for i6 in 1..1; j6=i6; end", x
assert(eval("defined? i6", x))
@@ -305,34 +339,13 @@ class TestEval < Test::Unit::TestCase
p = binding
eval "foo11 = 1", p
foo22 = 5
- proc{foo11=22}.call
+ proc{foo11=22;foo11}.call
proc{foo22=55}.call
# assert_equal(eval("foo11"), eval("foo11", p))
# assert_equal(1, eval("foo11"))
assert_equal(eval("foo22"), eval("foo22", p))
assert_equal(55, eval("foo22"))
}.call
-
- if false
- # Ruby 2.0 doesn't see Proc as Binding
- p1 = proc{i7 = 0; proc{i7}}.call
- assert_equal(0, p1.call)
- eval "i7=5", p1
- assert_equal(5, p1.call)
- assert(!defined?(i7))
- end
-
- if false
- # Ruby 2.0 doesn't see Proc as Binding
- p1 = proc{i7 = 0; proc{i7}}.call
- i7 = nil
- assert_equal(0, p1.call)
- eval "i7=1", p1
- assert_equal(1, p1.call)
- eval "i7=5", p1
- assert_equal(5, p1.call)
- assert_nil(i7)
- end
end
def test_nil_instance_eval_cvar
@@ -352,10 +365,10 @@ class TestEval < Test::Unit::TestCase
def test_cvar_scope_with_instance_eval
# TODO: check
- Fixnum.class_eval "@@test_cvar_scope_with_instance_eval = 1" # depends on [ruby-dev:24229]
+ Integer.class_eval "@@test_cvar_scope_with_instance_eval = 1" # depends on [ruby-dev:24229]
@@test_cvar_scope_with_instance_eval = 4
assert_equal(4, 1.instance_eval("@@test_cvar_scope_with_instance_eval"), "[ruby-dev:24223]")
- Fixnum.__send__(:remove_class_variable, :@@test_cvar_scope_with_instance_eval)
+ Integer.__send__(:remove_class_variable, :@@test_cvar_scope_with_instance_eval)
end
def test_eval_and_define_method
@@ -384,6 +397,24 @@ class TestEval < Test::Unit::TestCase
assert_equal("ok", x)
end
+ def test_define_method_toplevel
+ feature6609 = '[ruby-core:45715]'
+ main = eval("self", TOPLEVEL_BINDING)
+ assert_nothing_raised(NoMethodError, feature6609) do
+ main.instance_eval do
+ define_method("feature6609_block") {feature6609}
+ end
+ end
+ assert_equal(feature6609, feature6609_block)
+
+ assert_nothing_raised(NoMethodError, feature6609) do
+ main.instance_eval do
+ define_method("feature6609_method", Object.instance_method(:feature6609_block))
+ end
+ end
+ assert_equal(feature6609, feature6609_method)
+ end
+
def test_eval_using_integer_as_binding
assert_raise(TypeError) { eval("", 1) }
end
@@ -392,16 +423,6 @@ class TestEval < Test::Unit::TestCase
assert_raise(RuntimeError) { eval("raise ''") }
end
- def test_eval_using_untainted_binding_under_safe4
- assert_raise(SecurityError) do
- Thread.new do
- b = binding
- $SAFE = 4
- eval("", b)
- end.join
- end
- end
-
def test_eval_with_toplevel_binding # [ruby-dev:37142]
ruby("-e", "x = 0; eval('p x', TOPLEVEL_BINDING)") do |f|
f.close_write
@@ -415,4 +436,93 @@ class TestEval < Test::Unit::TestCase
assert_raise(ArgumentError) {eval("__ENCODING__".encode("utf-32be"))}
assert_raise(ArgumentError) {eval("__ENCODING__".encode("utf-32le"))}
end
+
+ def test_instance_eval_method_proc
+ bug3860 = Class.new do
+ def initialize(a);
+ @a=a
+ end
+ def get(*args)
+ @a
+ end
+ end
+ foo = bug3860.new 1
+ foo_pr = foo.method(:get).to_proc
+ result = foo.instance_eval(&foo_pr)
+ assert_equal(1, result, 'Bug #3786, Bug #3860, [ruby-core:32501]')
+ end
+
+ def test_file_encoding
+ fname = "\u{3042}".encode("euc-jp")
+ assert_equal(fname, eval("__FILE__", nil, fname, 1))
+ end
+
+ def test_eval_location_fstring
+ o = Object.new
+ o.instance_eval "def foo() end", "generated code"
+ o.instance_eval "def bar() end", "generated code"
+
+ a, b = o.method(:foo).source_location[0],
+ o.method(:bar).source_location[0]
+
+ assert_same a, b
+ end
+
+ def test_eval_location_binding
+ assert_warning(/__FILE__ in eval/) do
+ assert_equal(__FILE__, eval("__FILE__", binding))
+ end
+ end
+
+ def test_fstring_instance_eval
+ bug = "[ruby-core:78116] [Bug #12930]".freeze
+ assert_same bug, (bug.instance_eval {self})
+ assert_raise(FrozenError) {
+ bug.instance_eval {@ivar = true}
+ }
+ end
+
+ def test_gced_binding_block
+ assert_normal_exit %q{
+ def m
+ binding
+ end
+ GC.stress = true
+ b = nil
+ tap do
+ b = m {}
+ end
+ 0.times.to_a
+ b.eval('yield')
+ }, '[Bug #10368]'
+ end
+
+ def test_gced_eval_location
+ Dir.mktmpdir do |d|
+ File.write("#{d}/2.rb", "")
+ File.write("#{d}/1.rb", "require_relative '2'\n""__FILE__\n")
+ file = "1.rb"
+ path = File.expand_path(file, d)
+ assert_equal(path, eval(File.read(path), nil, File.expand_path(file, d)))
+ assert_equal(path, eval(File.read(path), nil, File.expand_path(file, d)))
+ end
+ end
+
+ def orphan_proc
+ proc {eval("return :ng")}
+ end
+
+ def orphan_lambda
+ lambda {eval("return :ok")}
+ end
+
+ def test_return_in_eval_proc
+ x = orphan_proc
+ assert_raise(LocalJumpError) {x.call}
+ end
+
+ def test_return_in_eval_lambda
+ x = orphan_lambda
+ assert_equal(:ok, x.call)
+ end
end
diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb
index 18ecb404be..de249c9b7c 100644
--- a/test/ruby/test_exception.rb
+++ b/test/ruby/test_exception.rb
@@ -1,42 +1,46 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
+require 'tempfile'
class TestException < Test::Unit::TestCase
- def test_exception
+ def test_exception_rescued
begin
raise "this must be handled"
assert(false)
rescue
assert(true)
end
+ end
- $bad = true
+ def test_exception_retry
+ bad = true
begin
raise "this must be handled no.2"
rescue
- if $bad
- $bad = false
+ if bad
+ bad = false
retry
- assert(false)
end
+ assert(!bad)
end
assert(true)
+ end
- # exception in rescue clause
- $string = "this must be handled no.3"
- e = assert_raise(RuntimeError) do
+ def test_exception_in_rescue
+ string = "this must be handled no.3"
+ assert_raise_with_message(RuntimeError, string) do
begin
raise "exception in rescue clause"
rescue
- raise $string
+ raise string
end
assert(false)
end
- assert_equal($string, e.message)
+ end
- # exception in ensure clause
- $string = "exception in ensure clause"
- e = assert_raise(RuntimeError) do
+ def test_exception_in_ensure
+ string = "exception in ensure clause"
+ assert_raise_with_message(RuntimeError, string) do
begin
raise "this must be handled no.4"
ensure
@@ -46,55 +50,148 @@ class TestException < Test::Unit::TestCase
end
assert(false)
end
- assert_equal($string, e.message)
+ end
- $bad = true
+ def test_exception_ensure
+ bad = true
begin
begin
raise "this must be handled no.5"
ensure
- $bad = false
+ bad = false
end
rescue
end
- assert(!$bad)
+ assert(!bad)
+ end
- $bad = true
+ def test_exception_ensure_2 # just duplication?
+ bad = true
begin
begin
raise "this must be handled no.6"
ensure
- $bad = false
+ bad = false
end
rescue
end
- assert(!$bad)
+ assert(!bad)
+ end
+
+ def test_errinfo_in_debug
+ bug9568 = EnvUtil.labeled_class("[ruby-core:61091] [Bug #9568]", RuntimeError) do
+ def to_s
+ require '\0'
+ rescue LoadError
+ self.class.to_s
+ end
+ end
- $bad = true
+ err = EnvUtil.verbose_warning do
+ assert_raise(bug9568) do
+ $DEBUG, debug = true, $DEBUG
+ begin
+ raise bug9568
+ ensure
+ $DEBUG = debug
+ end
+ end
+ end
+ assert_include(err, bug9568.to_s)
+ end
+
+ def test_errinfo_encoding_in_debug
+ exc = Module.new {break class_eval("class C\u{30a8 30e9 30fc} < RuntimeError; self; end".encode(Encoding::EUC_JP))}
+ exc.inspect
+
+ err = EnvUtil.verbose_warning do
+ assert_raise(exc) do
+ $DEBUG, debug = true, $DEBUG
+ begin
+ raise exc
+ ensure
+ $DEBUG = debug
+ end
+ end
+ end
+ assert_include(err, exc.to_s)
+ end
+
+ def test_break_ensure
+ bad = true
while true
begin
break
ensure
- $bad = false
+ bad = false
end
end
- assert(!$bad)
+ assert(!bad)
+ end
+
+ def test_catch_no_throw
+ assert_equal(:foo, catch {:foo})
+ end
+
+ def test_catch_throw
+ result = catch(:foo) {
+ loop do
+ loop do
+ throw :foo, true
+ break
+ end
+ assert(false, "should not reach here")
+ end
+ false
+ }
+ assert(result)
+ end
+
+ def test_catch_throw_noarg
+ assert_nothing_raised(UncaughtThrowError) {
+ result = catch {|obj|
+ throw obj, :ok
+ assert(false, "should not reach here")
+ }
+ assert_equal(:ok, result)
+ }
+ end
+
+ def test_uncaught_throw
+ tag = nil
+ e = assert_raise_with_message(UncaughtThrowError, /uncaught throw/) {
+ catch("foo") {|obj|
+ tag = obj.dup
+ throw tag, :ok
+ assert(false, "should not reach here")
+ }
+ assert(false, "should not reach here")
+ }
+ assert_not_nil(tag)
+ assert_same(tag, e.tag)
+ assert_equal(:ok, e.value)
+ end
- assert(catch(:foo) {
- loop do
- loop do
- throw :foo, true
- break
- end
- break
- assert(false) # should no reach here
- end
- false
- })
+ def test_catch_throw_in_require
+ bug7185 = '[ruby-dev:46234]'
+ Tempfile.create(["dep", ".rb"]) {|t|
+ t.puts("throw :extdep, 42")
+ t.close
+ assert_equal(42, assert_throw(:extdep, bug7185) {require t.path}, bug7185)
+ }
+ end
+ def test_throw_false
+ bug12743 = '[ruby-core:77229] [Bug #12743]'
+ Thread.start {
+ e = assert_raise_with_message(UncaughtThrowError, /false/, bug12743) {
+ throw false
+ }
+ assert_same(false, e.tag, bug12743)
+ }.join
end
- def test_else
+ def test_else_no_exception
begin
assert(true)
rescue
@@ -102,7 +199,9 @@ class TestException < Test::Unit::TestCase
else
assert(true)
end
+ end
+ def test_else_raised
begin
assert(true)
raise
@@ -112,7 +211,9 @@ class TestException < Test::Unit::TestCase
else
assert(false)
end
+ end
+ def test_else_nested_no_exception
begin
assert(true)
begin
@@ -128,7 +229,9 @@ class TestException < Test::Unit::TestCase
else
assert(true)
end
+ end
+ def test_else_nested_rescued
begin
assert(true)
begin
@@ -146,7 +249,9 @@ class TestException < Test::Unit::TestCase
else
assert(true)
end
+ end
+ def test_else_nested_unrescued
begin
assert(true)
begin
@@ -164,7 +269,9 @@ class TestException < Test::Unit::TestCase
else
assert(false)
end
+ end
+ def test_else_nested_rescued_reraise
begin
assert(true)
begin
@@ -192,6 +299,26 @@ class TestException < Test::Unit::TestCase
assert_raise(ArgumentError) { raise 1, 1, 1, 1 }
end
+ def test_type_error_message_encoding
+ c = eval("Module.new do break class C\u{4032}; self; end; end")
+ o = c.new
+ assert_raise_with_message(TypeError, /C\u{4032}/) do
+ ""[o]
+ end
+ c.class_eval {def to_int; self; end}
+ assert_raise_with_message(TypeError, /C\u{4032}/) do
+ ""[o]
+ end
+ c.class_eval {def to_a; self; end}
+ assert_raise_with_message(TypeError, /C\u{4032}/) do
+ [*o]
+ end
+ obj = eval("class C\u{1f5ff}; self; end").new
+ assert_raise_with_message(TypeError, /C\u{1f5ff}/) do
+ Class.new {include obj}
+ end
+ end
+
def test_errat
assert_in_out_err([], "p $@", %w(nil), [])
@@ -224,28 +351,11 @@ class TestException < Test::Unit::TestCase
INPUT
end
- def test_safe4
- cmd = proc{raise SystemExit}
- safe0_p = proc{|*args| args}
-
- test_proc = proc {
- $SAFE = 4
- begin
- cmd.call
- rescue SystemExit => e
- safe0_p["SystemExit: #{e.inspect}"]
- raise e
- rescue Exception => e
- safe0_p["Exception (NOT SystemExit): #{e.inspect}"]
- raise e
- end
- }
- assert_raise(SystemExit, '[ruby-dev:38760]') {test_proc.call}
- end
-
def test_thread_signal_location
- stdout, stderr, status = EnvUtil.invoke_ruby("-d", <<-RUBY, false, true)
+ skip
+ _, stderr, _ = EnvUtil.invoke_ruby(%w"--disable-gems -d", <<-RUBY, false, true)
Thread.start do
+ Thread.current.report_on_exception = false
begin
Process.kill(:INT, $$)
ensure
@@ -278,6 +388,17 @@ end.join
assert_equal(e.inspect, e.new.inspect)
end
+ def test_to_s
+ e = StandardError.new("foo")
+ assert_equal("foo", e.to_s)
+
+ def (s = Object.new).to_s
+ "bar"
+ end
+ e = StandardError.new(s)
+ assert_equal("bar", e.to_s)
+ end
+
def test_set_backtrace
e = Exception.new
@@ -296,20 +417,984 @@ end.join
exit
rescue SystemExit => e
end
- assert(e.success?)
+ assert_send([e, :success?], "success by default")
+
+ begin
+ exit(true)
+ rescue SystemExit => e
+ end
+ assert_send([e, :success?], "true means success")
+
+ begin
+ exit(false)
+ rescue SystemExit => e
+ end
+ assert_not_send([e, :success?], "false means failure")
begin
abort
rescue SystemExit => e
end
- assert(!e.success?)
+ assert_not_send([e, :success?], "abort means failure")
end
def test_nomethoderror
bug3237 = '[ruby-core:29948]'
str = "\u2600"
id = :"\u2604"
- e = assert_raise(NoMethodError) {str.__send__(id)}
- assert_equal("undefined method `#{id}' for #{str.inspect}:String", e.message, bug3237)
+ 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
+
+ def test_too_many_args_in_eval
+ bug5720 = '[ruby-core:41520]'
+ arg_string = (0...140000).to_a.join(", ")
+ assert_raise(SystemStackError, bug5720) {eval "raise(#{arg_string})"}
+ end
+
+ def test_systemexit_new
+ e0 = SystemExit.new
+ assert_equal(0, e0.status)
+ assert_equal("SystemExit", e0.message)
+ ei = SystemExit.new(3)
+ assert_equal(3, ei.status)
+ assert_equal("SystemExit", ei.message)
+ es = SystemExit.new("msg")
+ assert_equal(0, es.status)
+ assert_equal("msg", es.message)
+ eis = SystemExit.new(7, "msg")
+ assert_equal(7, eis.status)
+ assert_equal("msg", eis.message)
+
+ bug5728 = '[ruby-dev:44951]'
+ et = SystemExit.new(true)
+ assert_equal(true, et.success?, bug5728)
+ assert_equal("SystemExit", et.message, bug5728)
+ ef = SystemExit.new(false)
+ assert_equal(false, ef.success?, bug5728)
+ assert_equal("SystemExit", ef.message, bug5728)
+ ets = SystemExit.new(true, "msg")
+ assert_equal(true, ets.success?, bug5728)
+ assert_equal("msg", ets.message, bug5728)
+ efs = SystemExit.new(false, "msg")
+ assert_equal(false, efs.success?, bug5728)
+ assert_equal("msg", efs.message, bug5728)
+ end
+
+ def test_exception_in_name_error_to_str
+ bug5575 = '[ruby-core:41612]'
+ Tempfile.create(["test_exception_in_name_error_to_str", ".rb"]) do |t|
+ t.puts <<-EOC
+ begin
+ BasicObject.new.inspect
+ rescue
+ $!.inspect
+ end
+ EOC
+ t.close
+ assert_nothing_raised(NameError, bug5575) do
+ load(t.path)
+ end
+ end
+ end
+
+ def test_equal
+ bug5865 = '[ruby-core:41979]'
+ assert_equal(RuntimeError.new("a"), RuntimeError.new("a"), bug5865)
+ assert_not_equal(RuntimeError.new("a"), StandardError.new("a"), bug5865)
+ end
+
+ def test_exception_in_exception_equal
+ bug5865 = '[ruby-core:41979]'
+ Tempfile.create(["test_exception_in_exception_equal", ".rb"]) do |t|
+ t.puts <<-EOC
+ o = Object.new
+ def o.exception(arg)
+ end
+ _ = RuntimeError.new("a") == o
+ EOC
+ t.close
+ assert_nothing_raised(ArgumentError, bug5865) do
+ load(t.path)
+ end
+ end
+ end
+
+ Bug4438 = '[ruby-core:35364]'
+
+ def test_rescue_single_argument
+ assert_raise(TypeError, Bug4438) do
+ begin
+ raise
+ rescue 1
+ end
+ end
+ end
+
+ def test_rescue_splat_argument
+ assert_raise(TypeError, Bug4438) do
+ begin
+ raise
+ rescue *Array(1)
+ end
+ end
+ end
+
+ def test_to_s_taintness_propagation
+ for exc in [Exception, NameError]
+ m = "abcdefg"
+ e = exc.new(m)
+ e.taint
+ s = e.to_s
+ assert_equal(false, m.tainted?,
+ "#{exc}#to_s should not propagate taintness")
+ assert_equal(false, s.tainted?,
+ "#{exc}#to_s should not propagate taintness")
+ end
+
+ o = Object.new
+ def o.to_str
+ "foo"
+ end
+ o.taint
+ e = NameError.new(o)
+ s = e.to_s
+ assert_equal(false, s.tainted?)
+ end
+
+ def m
+ m(&->{return 0})
+ 42
+ end
+
+ def test_stackoverflow
+ feature6216 = '[ruby-core:43794] [Feature #6216]'
+ e = assert_raise(SystemStackError, feature6216) {m}
+ level = e.backtrace.size
+ assert_operator(level, :>, 10, feature6216)
+
+ feature6216 = '[ruby-core:63377] [Feature #6216]'
+ e = assert_raise(SystemStackError, feature6216) {raise e}
+ assert_equal(level, e.backtrace.size, feature6216)
+ end
+
+ def test_machine_stackoverflow
+ bug9109 = '[ruby-dev:47804] [Bug #9109]'
+ assert_separately(%w[--disable-gem], <<-SRC)
+ assert_raise(SystemStackError, #{bug9109.dump}) {
+ h = {a: ->{h[:a].call}}
+ h[:a].call
+ }
+ SRC
+ rescue SystemStackError
+ end
+
+ def test_machine_stackoverflow_by_define_method
+ bug9454 = '[ruby-core:60113] [Bug #9454]'
+ assert_separately(%w[--disable-gem], <<-SRC)
+ assert_raise(SystemStackError, #{bug9454.dump}) {
+ define_method(:foo) {self.foo}
+ self.foo
+ }
+ SRC
+ rescue SystemStackError
+ end
+
+ def test_machine_stackoverflow_by_trace
+ assert_normal_exit("#{<<-"begin;"}\n#{<<~"end;"}", timeout: 60)
+ begin;
+ require 'timeout'
+ require 'tracer'
+ class HogeError < StandardError
+ def to_s
+ message.upcase # disable tailcall optimization
+ end
+ end
+ Tracer.stdout = open(IO::NULL, "w")
+ begin
+ Timeout.timeout(5) do
+ Tracer.on
+ HogeError.new.to_s
+ end
+ rescue Timeout::Error
+ # ok. there are no SEGV or critical error
+ rescue SystemStackError => e
+ # ok.
+ end
+ end;
+ end
+
+ def test_cause
+ msg = "[Feature #8257]"
+ cause = nil
+ e = assert_raise(StandardError) {
+ begin
+ raise msg
+ rescue => e
+ cause = e.cause
+ raise StandardError
+ end
+ }
+ assert_nil(cause, msg)
+ cause = e.cause
+ assert_instance_of(RuntimeError, cause, msg)
+ assert_equal(msg, cause.message, msg)
+ end
+
+ def test_cause_reraised
+ msg = "[Feature #8257]"
+ e = assert_raise(RuntimeError) {
+ begin
+ raise msg
+ rescue => e
+ raise e
+ end
+ }
+ assert_not_same(e, e.cause, "#{msg}: should not be recursive")
+ end
+
+ def test_cause_raised_in_rescue
+ a = nil
+ e = assert_raise_with_message(RuntimeError, 'b') {
+ begin
+ raise 'a'
+ rescue => a
+ begin
+ raise 'b'
+ rescue => b
+ assert_same(a, b.cause)
+ begin
+ raise 'c'
+ rescue
+ raise b
+ end
+ end
+ end
+ }
+ assert_same(a, e.cause, 'cause should not be overwritten by reraise')
+ end
+
+ def test_cause_at_raised
+ a = nil
+ e = assert_raise_with_message(RuntimeError, 'b') {
+ begin
+ raise 'a'
+ rescue => a
+ b = RuntimeError.new('b')
+ assert_nil(b.cause)
+ begin
+ raise 'c'
+ rescue
+ raise b
+ end
+ end
+ }
+ assert_equal('c', e.cause.message, 'cause should be the exception at raised')
+ assert_same(a, e.cause.cause)
+ end
+
+ def test_cause_at_end
+ errs = [
+ /-: unexpected return\n/,
+ /.*undefined local variable or method `n'.*\n/,
+ ]
+ assert_in_out_err([], <<-'end;', [], errs)
+ END{n}; END{return}
+ end;
+ end
+
+ def test_raise_with_cause
+ msg = "[Feature #8257]"
+ cause = ArgumentError.new("foobar")
+ e = assert_raise(RuntimeError) {raise msg, cause: cause}
+ assert_same(cause, e.cause)
+ end
+
+ def test_cause_with_no_arguments
+ cause = ArgumentError.new("foobar")
+ assert_raise_with_message(ArgumentError, /with no arguments/) do
+ raise cause: cause
+ end
+ end
+
+ def test_raise_with_cause_in_rescue
+ e = assert_raise_with_message(RuntimeError, 'b') {
+ begin
+ raise 'a'
+ rescue => a
+ begin
+ raise 'b'
+ rescue => b
+ assert_same(a, b.cause)
+ begin
+ raise 'c'
+ rescue
+ raise b, cause: ArgumentError.new('d')
+ end
+ end
+ end
+ }
+ assert_equal('d', e.cause.message, 'cause option should be honored always')
+ assert_nil(e.cause.cause)
+ end
+
+ def test_cause_thread_no_cause
+ bug12741 = '[ruby-core:77222] [Bug #12741]'
+
+ x = Thread.current
+ a = false
+ y = Thread.start do
+ Thread.pass until a
+ x.raise "stop"
+ end
+
+ begin
+ raise bug12741
+ rescue
+ e = assert_raise_with_message(RuntimeError, "stop") do
+ a = true
+ sleep 1
+ end
+ end
+ assert_nil(e.cause)
+ ensure
+ y.join
+ end
+
+ def test_cause_thread_with_cause
+ bug12741 = '[ruby-core:77222] [Bug #12741]'
+
+ x = Thread.current
+ q = Queue.new
+ y = Thread.start do
+ q.pop
+ begin
+ raise "caller's cause"
+ rescue
+ x.raise "stop"
+ end
+ end
+
+ begin
+ raise bug12741
+ rescue
+ e = assert_raise_with_message(RuntimeError, "stop") do
+ q.push(true)
+ sleep 1
+ end
+ ensure
+ y.join
+ end
+ assert_equal("caller's cause", e.cause.message)
+ end
+
+ def test_unknown_option
+ bug = '[ruby-core:63203] [Feature #8257] should pass unknown options'
+
+ exc = Class.new(RuntimeError) do
+ attr_reader :arg
+ def initialize(msg = nil)
+ @arg = msg
+ super(msg)
+ end
+ end
+
+ e = assert_raise(exc, bug) {raise exc, "foo" => "bar", foo: "bar"}
+ assert_equal({"foo" => "bar", foo: "bar"}, e.arg, bug)
+
+ e = assert_raise(exc, bug) {raise exc, "foo" => "bar", foo: "bar", cause: RuntimeError.new("zzz")}
+ assert_equal({"foo" => "bar", foo: "bar"}, e.arg, bug)
+
+ e = assert_raise(exc, bug) {raise exc, {}}
+ assert_equal({}, e.arg, bug)
+ end
+
+ def test_circular_cause
+ bug13043 = '[ruby-core:78688] [Bug #13043]'
+ begin
+ begin
+ raise "error 1"
+ ensure
+ orig_error = $!
+ begin
+ raise "error 2"
+ rescue => err
+ raise orig_error
+ end
+ end
+ rescue => x
+ end
+ assert_equal(orig_error, x)
+ assert_equal(orig_error, err.cause)
+ assert_nil(orig_error.cause, bug13043)
+ end
+
+ def test_cause_with_frozen_exception
+ exc = ArgumentError.new("foo").freeze
+ assert_raise_with_message(ArgumentError, exc.message) {
+ raise exc, cause: RuntimeError.new("bar")
+ }
+ end
+
+ 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
+ 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_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}
+
+ error = NoMethodError.new("Message", :foo, receiver: receiver)
+ assert_equal(["Message", :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(["Message", :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
+ 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;
+ end
+
+ def test_output_string_encoding
+ # "\x82\xa0" in cp932 is "\u3042" (Japanese hiragana 'a')
+ # change $stderr to force calling rb_io_write() instead of fwrite()
+ assert_in_out_err(["-Eutf-8:cp932"], '# coding: cp932
+$stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
+ assert_equal 1, outs.size
+ assert_equal 0, errs.size
+ err = outs.first.force_encoding('utf-8')
+ assert err.valid_encoding?, 'must be valid encoding'
+ assert_match %r/\u3042/, err
+ end
+ end
+
+ def test_multibyte_and_newline
+ bug10727 = '[ruby-core:67473] [Bug #10727]'
+ assert_in_out_err([], <<-'end;', [], /\u{306b 307b 3093 3054} \(E\)\n\u{6539 884c}/, bug10727, encoding: "UTF-8")
+ class E < StandardError
+ def initialize
+ super("\u{306b 307b 3093 3054}\n\u{6539 884c}")
+ end
+ end
+ raise E
+ end;
+ end
+
+ def test_method_missing_reason_clear
+ bug10969 = '[ruby-core:68515] [Bug #10969]'
+ a = Class.new {def method_missing(*) super end}.new
+ assert_raise(NameError) {a.instance_eval("foo")}
+ assert_raise(NoMethodError, bug10969) {a.public_send("bar", true)}
+ end
+
+ def test_message_of_name_error
+ assert_raise_with_message(NameError, /\Aundefined method `foo' for module `#<Module:.*>'$/) do
+ Module.new do
+ module_function :foo
+ end
+ end
+ end
+
+ def capture_warning_warn
+ verbose = $VERBOSE
+ warning = []
+
+ ::Warning.class_eval do
+ alias_method :warn2, :warn
+ remove_method :warn
+
+ define_method(:warn) do |str|
+ warning << str
+ end
+ end
+
+ $VERBOSE = true
+ yield
+
+ return warning
+ ensure
+ $VERBOSE = verbose
+
+ ::Warning.class_eval do
+ remove_method :warn
+ alias_method :warn, :warn2
+ remove_method :warn2
+ end
+ end
+
+ def test_warning_warn
+ warning = capture_warning_warn {@a}
+ assert_match(/instance variable @a 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_kernel_warn_uplevel
+ warning = capture_warning_warn {warn("test warning", uplevel: 0)}
+ assert_equal("#{__FILE__}:#{__LINE__-1}: warning: test warning\n", warning[0])
+ def (obj = Object.new).w(n) warn("test warning", uplevel: n) end
+ warning = capture_warning_warn {obj.w(0)}
+ assert_equal("#{__FILE__}:#{__LINE__-2}: warning: test warning\n", warning[0])
+ warning = capture_warning_warn {obj.w(1)}
+ assert_equal("#{__FILE__}:#{__LINE__-1}: warning: test warning\n", warning[0])
+ assert_raise(ArgumentError) {warn("test warning", uplevel: -1)}
+ assert_in_out_err(["-e", "warn 'ok', uplevel: 1"], '', [], /warning:/)
+ end
+
+ def test_warning_warn_invalid_argument
+ assert_raise(TypeError) do
+ ::Warning.warn nil
+ end
+ assert_raise(TypeError) do
+ ::Warning.warn 1
+ end
+ assert_raise(Encoding::CompatibilityError) do
+ ::Warning.warn "\x00a\x00b\x00c".force_encoding("utf-16be")
+ end
+ end
+
+ def test_warning_warn_circular_require_backtrace
+ warning = nil
+ path = nil
+ Tempfile.create(%w[circular .rb]) do |t|
+ path = File.realpath(t.path)
+ basename = File.basename(path)
+ t.puts "require '#{basename}'"
+ t.close
+ $LOAD_PATH.push(File.dirname(t))
+ warning = capture_warning_warn {require basename}
+ ensure
+ $LOAD_PATH.pop
+ $LOADED_FEATURES.delete(t)
+ end
+ assert_equal(1, warning.size)
+ assert_match(/circular require/, warning.first)
+ assert_match(/^\tfrom #{Regexp.escape(path)}:1:/, warning.first)
+ end
+
+ def test_warning_warn_super
+ assert_in_out_err(%[-W0], "#{<<~"{#"}\n#{<<~'};'}", [], /instance variable @a not initialized/)
+ {#
+ module Warning
+ def warn(message)
+ super
+ end
+ end
+
+ $VERBOSE = true
+ @a
+ };
+ end
+
+ def test_undefined_backtrace
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ class Exception
+ undef backtrace
+ end
+
+ assert_raise(RuntimeError) {
+ raise RuntimeError, "hello"
+ }
+ end;
+ end
+
+ def test_redefined_backtrace
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ $exc = nil
+
+ class Exception
+ undef backtrace
+ def backtrace
+ $exc = self
+ end
+ end
+
+ e = assert_raise(RuntimeError) {
+ raise RuntimeError, "hello"
+ }
+ assert_same(e, $exc)
+ end;
+ end
+
+ def test_blocking_backtrace
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class Bug < RuntimeError
+ def backtrace
+ IO.readlines(IO::NULL)
+ end
+ end
+ bug = Bug.new '[ruby-core:85939] [Bug #14577]'
+ n = 10000
+ i = 0
+ n.times do
+ begin
+ raise bug
+ rescue Bug
+ i += 1
+ end
+ end
+ assert_equal(n, i)
+ end;
+ end
+
+ def test_wrong_backtrace
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ class Exception
+ undef backtrace
+ def backtrace(a)
+ end
+ end
+
+ assert_raise(RuntimeError) {
+ raise RuntimeError, "hello"
+ }
+ end;
+
+ error_class = Class.new(StandardError) do
+ def backtrace; :backtrace; end
+ end
+ begin
+ raise error_class
+ rescue error_class => e
+ assert_raise(TypeError) {$@}
+ assert_raise(TypeError) {e.full_message}
+ end
+ end
+
+ def test_backtrace_in_eval
+ bug = '[ruby-core:84434] [Bug #14229]'
+ assert_in_out_err(['-e', 'eval("raise")'], "", [], /^\(eval\):1:/, bug)
+ end
+
+ def test_full_message
+ message = RuntimeError.new("testerror").full_message
+ assert_operator(message, :end_with?, "\n")
+
+ test_method = "def foo; raise 'testerror'; end"
+
+ out1, err1, status1 = EnvUtil.invoke_ruby(['-e', "#{test_method}; begin; foo; rescue => e; puts e.full_message; end"], '', true, true)
+ assert_predicate(status1, :success?)
+ assert_empty(err1, "expected nothing wrote to $stdout by #full_message")
+
+ _, err2, status1 = EnvUtil.invoke_ruby(['-e', "#{test_method}; begin; foo; end"], '', true, true)
+ assert_equal(err2, out1)
+
+ e = RuntimeError.new("a\n")
+ message = assert_nothing_raised(ArgumentError, proc {e.pretty_inspect}) do
+ e.full_message
+ end
+ assert_operator(message, :end_with?, "\n")
+ message = message.gsub(/\e\[[\d;]*m/, '')
+ assert_not_operator(message, :end_with?, "\n\n")
+ e = RuntimeError.new("a\n\nb\n\nc")
+ message = assert_nothing_raised(ArgumentError, proc {e.pretty_inspect}) do
+ e.full_message
+ end
+ assert_all?(message.lines) do |m|
+ /\e\[\d[;\d]*m[^\e]*\n/ !~ m
+ end
+
+ e = RuntimeError.new("testerror")
+ message = e.full_message(highlight: false)
+ assert_not_match(/\e/, message)
+
+ bt = ["test:100", "test:99", "test:98", "test:1"]
+ e = assert_raise(RuntimeError) {raise RuntimeError, "testerror", bt}
+
+ bottom = "test:100: testerror (RuntimeError)\n"
+ top = "test:1\n"
+ remark = "Traceback (most recent call last):"
+
+ message = e.full_message(highlight: false, order: :top)
+ assert_not_match(/\e/, message)
+ assert_operator(message.count("\n"), :>, 2)
+ assert_operator(message, :start_with?, bottom)
+ assert_operator(message, :end_with?, top)
+
+ message = e.full_message(highlight: false, order: :bottom)
+ assert_not_match(/\e/, message)
+ assert_operator(message.count("\n"), :>, 2)
+ assert_operator(message, :start_with?, remark)
+ assert_operator(message, :end_with?, bottom)
+
+ assert_raise_with_message(ArgumentError, /:top or :bottom/) {
+ e.full_message(highlight: false, order: :middle)
+ }
+
+ message = e.full_message(highlight: true)
+ assert_match(/\e/, message)
+
+ message = e.full_message
+ 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
+ end
+
+ def test_exception_in_message
+ code = "#{<<~"begin;"}\n#{<<~'end;'}"
+ begin;
+ class Bug14566 < StandardError
+ def message; raise self.class; end
+ end
+ raise Bug14566
+ end;
+ assert_in_out_err([], code, [], /Bug14566/, success: false, timeout: 2)
+ end
+
+ def test_non_exception_cause
+ assert_raise_with_message(TypeError, /exception/) do
+ raise "foo", cause: 1
+ end;
+ end
+
+ def test_circular_cause_handle
+ assert_raise_with_message(ArgumentError, /circular cause/) do
+ begin
+ raise "error 1"
+ rescue => e1
+ raise "error 2" rescue raise e1, cause: $!
+ end
+ end;
+ end
+
+ def test_super_in_method_missing
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ $VERBOSE = nil
+ class Object
+ def method_missing(name, *args, &block)
+ super
+ end
+ end
+
+ bug14670 = '[ruby-dev:50522] [Bug #14670]'
+ assert_raise_with_message(NoMethodError, /`foo'/, bug14670) do
+ Object.new.foo
+ end
+ end;
end
end
diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb
index edfe55a1d3..e6b89ffa45 100644
--- a/test/ruby/test_fiber.rb
+++ b/test/ruby/test_fiber.rb
@@ -1,11 +1,11 @@
+# frozen_string_literal: false
require 'test/unit'
require 'fiber'
-require 'continuation'
-require_relative './envutil'
+EnvUtil.suppress_warning {require 'continuation'}
+require 'tmpdir'
class TestFiber < Test::Unit::TestCase
def test_normal
- f = Fiber.current
assert_equal(:ok2,
Fiber.new{|e|
assert_equal(:ok1, e)
@@ -34,50 +34,55 @@ class TestFiber < Test::Unit::TestCase
end
def test_many_fibers
- max = 10000
+ max = 10_000
assert_equal(max, max.times{
Fiber.new{}
})
+ GC.start # force collect created fibers
assert_equal(max,
max.times{|i|
Fiber.new{
}.resume
}
)
+ GC.start # force collect created fibers
end
def test_many_fibers_with_threads
- max = 1000
- @cnt = 0
- (1..100).map{|ti|
- Thread.new{
- max.times{|i|
- Fiber.new{
- @cnt += 1
- }.resume
+ assert_normal_exit <<-SRC, timeout: 60
+ max = 1000
+ @cnt = 0
+ (1..100).map{|ti|
+ Thread.new{
+ max.times{|i|
+ Fiber.new{
+ @cnt += 1
+ }.resume
+ }
}
+ }.each{|t|
+ t.join
}
- }.each{|t|
- t.join
- }
- assert_equal(:ok, :ok)
+ SRC
end
def test_error
assert_raise(ArgumentError){
Fiber.new # Fiber without block
}
- assert_raise(FiberError){
- f = Fiber.new{}
- Thread.new{f.resume}.join # Fiber yielding across thread
- }
+ f = Fiber.new{}
+ Thread.new{
+ assert_raise(FiberError){ # Fiber yielding across thread
+ f.resume
+ }
+ }.join
assert_raise(FiberError){
f = Fiber.new{}
f.resume
f.resume
}
assert_raise(RuntimeError){
- f = Fiber.new{
+ Fiber.new{
@c = callcc{|c| @c = c}
}.resume
@c.call # cross fiber callcc
@@ -115,7 +120,7 @@ class TestFiber < Test::Unit::TestCase
end
def test_throw
- assert_raise(ArgumentError){
+ assert_raise(UncaughtThrowError){
Fiber.new do
throw :a
end.resume
@@ -188,6 +193,221 @@ class TestFiber < Test::Unit::TestCase
f2 = Fiber.new{ f1.resume }
f1.transfer
}, '[ruby-dev:40833]'
+ assert_normal_exit %q{
+ require 'fiber'
+ Fiber.new{}.resume
+ 1.times{Fiber.current.transfer}
+ }
end
-end
+ def test_resume_root_fiber
+ Thread.new do
+ assert_raise(FiberError) do
+ Fiber.current.resume
+ end
+ end.join
+ end
+
+ def test_gc_root_fiber
+ bug4612 = '[ruby-core:35891]'
+
+ assert_normal_exit %q{
+ require 'fiber'
+ GC.stress = true
+ Thread.start{ Fiber.current; nil }.join
+ GC.start
+ }, bug4612
+ end
+
+ def test_mark_fiber
+ bug13875 = '[ruby-core:82681]'
+
+ assert_normal_exit %q{
+ GC.stress = true
+ up = 1.upto(10)
+ down = 10.downto(1)
+ up.zip(down) {|a, b| a + b == 11 or fail 'oops'}
+ }, bug13875
+ end
+
+ def test_no_valid_cfp
+ bug5083 = '[ruby-dev:44208]'
+ assert_equal([], Fiber.new(&Module.method(:nesting)).resume, bug5083)
+ assert_instance_of(Class, Fiber.new(&Class.new.method(:undef_method)).resume(:to_s), bug5083)
+ end
+
+ def test_prohibit_resume_transferred_fiber
+ assert_raise(FiberError){
+ root_fiber = Fiber.current
+ f = Fiber.new{
+ root_fiber.transfer
+ }
+ f.transfer
+ f.resume
+ }
+ assert_raise(FiberError){
+ g=nil
+ f=Fiber.new{
+ g.resume
+ g.resume
+ }
+ g=Fiber.new{
+ f.resume
+ f.resume
+ }
+ f.transfer
+ }
+ end
+
+ def test_fork_from_fiber
+ skip 'fork not supported' unless Process.respond_to?(:fork)
+ pid = nil
+ bug5700 = '[ruby-core:41456]'
+ assert_nothing_raised(bug5700) do
+ Fiber.new do
+ pid = fork do
+ xpid = nil
+ Fiber.new {
+ xpid = fork do
+ # enough to trigger GC on old root fiber
+ count = 10000
+ count = 1000 if /openbsd/i =~ RUBY_PLATFORM
+ count.times do
+ Fiber.new {}.transfer
+ Fiber.new { Fiber.yield }
+ end
+ exit!(0)
+ end
+ }.transfer
+ _, status = Process.waitpid2(xpid)
+ exit!(status.success?)
+ end
+ end.resume
+ end
+ pid, status = Process.waitpid2(pid)
+ assert_equal(0, status.exitstatus, bug5700)
+ assert_equal(false, status.signaled?, bug5700)
+ end
+
+ def test_exit_in_fiber
+ bug5993 = '[ruby-dev:45218]'
+ assert_nothing_raised(bug5993) do
+ Thread.new{ Fiber.new{ Thread.exit }.resume; raise "unreachable" }.join
+ end
+ end
+
+ def test_fatal_in_fiber
+ assert_in_out_err(["-r-test-/fatal/rb_fatal", "-e", <<-EOS], "", [], /ok/)
+ Fiber.new{
+ 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|
+ EnvUtil.invoke_ruby([env, '-e', script], '', true, true, chdir: tmpdir, timeout: 30)
+ }
+ 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 * 10, 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 * 10, 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"
+ m1 = $~
+ m2 = nil
+ Fiber.new do
+ /b/ =~ "b"
+ m2 = $~
+ end.resume
+ assert_equal("b", m2[0])
+ assert_equal(m1, $~, bug7678)
+ end
+
+ def test_separate_lastline
+ bug7678 = '[ruby-core:51331]'
+ $_ = s1 = "outer"
+ s2 = nil
+ Fiber.new do
+ s2 = "inner"
+ end.resume
+ assert_equal("inner", s2)
+ assert_equal(s1, $_, bug7678)
+ end
+
+ def test_new_symbol_proc
+ bug = '[ruby-core:80147] [Bug #13313]'
+ assert_ruby_status([], "#{<<-"begin;"}\n#{<<-'end;'}", bug)
+ begin;
+ exit("1" == Fiber.new(&:to_s).resume(1))
+ end;
+ end
+
+ def test_to_s
+ f = Fiber.new do
+ assert_match(/resumed/, f.to_s)
+ Fiber.yield
+ end
+ assert_match(/created/, f.to_s)
+ f.resume
+ assert_match(/suspended/, f.to_s)
+ f.resume
+ assert_match(/terminated/, f.to_s)
+ assert_match(/resumed/, Fiber.current.to_s)
+ end
+
+ def test_create_fiber_in_new_thread
+ ret = Thread.new{
+ Thread.new{
+ Fiber.new{Fiber.yield :ok}.resume
+ }.value
+ }.value
+ assert_equal :ok, ret, '[Bug #14642]'
+ end
+
+ def test_machine_stack_gc
+ assert_normal_exit <<-RUBY, '[Bug #14561]', timeout: 10
+ enum = Enumerator.new { |y| y << 1 }
+ thread = Thread.new { enum.peek }
+ thread.join
+ sleep 5 # pause until thread cache wait time runs out. Native thread exits.
+ GC.start
+ RUBY
+ end
+end
diff --git a/test/ruby/test_file.rb b/test/ruby/test_file.rb
index ba9549fda5..3deab76e93 100644
--- a/test/ruby/test_file.rb
+++ b/test/ruby/test_file.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: false
require 'test/unit'
require 'tempfile'
+require "-test-/file"
require_relative 'ut_eof'
class TestFile < Test::Unit::TestCase
@@ -28,114 +30,200 @@ class TestFile < Test::Unit::TestCase
include TestEOF
def open_file(content)
- f = Tempfile.new("test-eof")
- f << content
- f.rewind
- yield f
+ Tempfile.create("test-eof") {|f|
+ f << content
+ f.rewind
+ yield f
+ }
end
alias open_file_rw open_file
include TestEOF::Seek
+ def test_empty_file_bom
+ bug6487 = '[ruby-core:45203]'
+ Tempfile.create(__method__.to_s) {|f|
+ assert_file.exist?(f.path)
+ assert_nothing_raised(bug6487) {File.read(f.path, mode: 'r:utf-8')}
+ assert_nothing_raised(bug6487) {File.read(f.path, mode: 'r:bom|utf-8')}
+ }
+ end
+
+ def assert_bom(bytes, name)
+ bug6487 = '[ruby-core:45203]'
+
+ Tempfile.create(name.to_s) {|f|
+ f.sync = true
+ expected = ""
+ result = nil
+ bytes[0...-1].each do |x|
+ f.write x
+ f.write ' '
+ f.pos -= 1
+ expected << x
+ assert_nothing_raised(bug6487) {result = File.read(f.path, mode: 'rb:bom|utf-8')}
+ assert_equal("#{expected} ".force_encoding("utf-8"), result)
+ end
+ f.write bytes[-1]
+ assert_nothing_raised(bug6487) {result = File.read(f.path, mode: 'rb:bom|utf-8')}
+ assert_equal '', result, "valid bom"
+ }
+ end
+
+ def test_bom_8
+ assert_bom(["\xEF", "\xBB", "\xBF"], __method__)
+ end
+
+ def test_bom_16be
+ assert_bom(["\xFE", "\xFF"], __method__)
+ end
+
+ def test_bom_16le
+ assert_bom(["\xFF", "\xFE"], __method__)
+ end
+
+ def test_bom_32be
+ assert_bom(["\0", "\0", "\xFE", "\xFF"], __method__)
+ end
+
+ def test_bom_32le
+ assert_bom(["\xFF", "\xFE\0\0"], __method__)
+ end
+
def test_truncate_wbuf
- f = Tempfile.new("test-truncate")
- f.print "abc"
- f.truncate(0)
- f.print "def"
- f.flush
- assert_equal("\0\0\0def", File.read(f.path), "[ruby-dev:24191]")
- f.close
+ Tempfile.create("test-truncate") {|f|
+ f.print "abc"
+ f.truncate(0)
+ f.print "def"
+ f.flush
+ assert_equal("\0\0\0def", File.read(f.path), "[ruby-dev:24191]")
+ }
end
def test_truncate_rbuf
- f = Tempfile.new("test-truncate")
- f.puts "abc"
- f.puts "def"
- f.close
- f.open
- assert_equal("abc\n", f.gets)
- f.truncate(3)
- assert_equal(nil, f.gets, "[ruby-dev:24197]")
+ Tempfile.create("test-truncate") {|f|
+ f.puts "abc"
+ f.puts "def"
+ f.rewind
+ assert_equal("abc\n", f.gets)
+ f.truncate(3)
+ assert_equal(nil, f.gets, "[ruby-dev:24197]")
+ }
end
def test_truncate_beyond_eof
- f = Tempfile.new("test-truncate")
- f.print "abc"
- f.truncate 10
- assert_equal("\0" * 7, f.read(100), "[ruby-dev:24532]")
+ Tempfile.create("test-truncate") {|f|
+ f.print "abc"
+ f.truncate 10
+ assert_equal("\0" * 7, f.read(100), "[ruby-dev:24532]")
+ }
+ end
+
+ def test_truncate_size
+ Tempfile.create("test-truncate") do |f|
+ q1 = Thread::Queue.new
+ q2 = Thread::Queue.new
+
+ th = Thread.new do
+ data = ''
+ 64.times do |i|
+ data << i.to_s
+ f.rewind
+ f.print data
+ f.truncate(data.bytesize)
+ q1.push data.bytesize
+ q2.pop
+ end
+ q1.push nil
+ end
+
+ while size = q1.pop
+ assert_equal size, File.size(f.path)
+ assert_equal size, f.size
+ q2.push true
+ end
+ th.join
+ end
end
def test_read_all_extended_file
[nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
- f = Tempfile.new("test-extended-file", mode)
- assert_nil(f.getc)
- f.print "a"
- f.rewind
- assert_equal("a", f.read, "mode = <#{mode}>")
+ Tempfile.create("test-extended-file", mode) {|f|
+ assert_nil(f.getc)
+ f.print "a"
+ f.rewind
+ assert_equal("a", f.read, "mode = <#{mode}>")
+ }
end
end
def test_gets_extended_file
[nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
- f = Tempfile.new("test-extended-file", mode)
- assert_nil(f.getc)
- f.print "a"
- f.rewind
- assert_equal("a", f.gets("a"), "mode = <#{mode}>")
+ Tempfile.create("test-extended-file", mode) {|f|
+ assert_nil(f.getc)
+ f.print "a"
+ f.rewind
+ assert_equal("a", f.gets("a"), "mode = <#{mode}>")
+ }
end
end
def test_gets_para_extended_file
[nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
- f = Tempfile.new("test-extended-file", mode)
- assert_nil(f.getc)
- f.print "\na"
- f.rewind
- assert_equal("a", f.gets(""), "mode = <#{mode}>")
+ Tempfile.create("test-extended-file", mode) {|f|
+ assert_nil(f.getc)
+ f.print "\na"
+ f.rewind
+ assert_equal("a", f.gets(""), "mode = <#{mode}>")
+ }
end
end
def test_each_char_extended_file
[nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
- f = Tempfile.new("test-extended-file", mode)
- assert_nil(f.getc)
- f.print "a"
- f.rewind
- result = []
- f.each_char {|b| result << b }
- assert_equal([?a], result, "mode = <#{mode}>")
+ Tempfile.create("test-extended-file", mode) {|f|
+ assert_nil(f.getc)
+ f.print "a"
+ f.rewind
+ result = []
+ f.each_char {|b| result << b }
+ assert_equal([?a], result, "mode = <#{mode}>")
+ }
end
end
def test_each_byte_extended_file
[nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
- f = Tempfile.new("test-extended-file", mode)
- assert_nil(f.getc)
- f.print "a"
- f.rewind
- result = []
- f.each_byte {|b| result << b.chr }
- assert_equal([?a], result, "mode = <#{mode}>")
+ Tempfile.create("test-extended-file", mode) {|f|
+ assert_nil(f.getc)
+ f.print "a"
+ f.rewind
+ result = []
+ f.each_byte {|b| result << b.chr }
+ assert_equal([?a], result, "mode = <#{mode}>")
+ }
end
end
def test_getc_extended_file
[nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
- f = Tempfile.new("test-extended-file", mode)
- assert_nil(f.getc)
- f.print "a"
- f.rewind
- assert_equal(?a, f.getc, "mode = <#{mode}>")
+ Tempfile.create("test-extended-file", mode) {|f|
+ assert_nil(f.getc)
+ f.print "a"
+ f.rewind
+ assert_equal(?a, f.getc, "mode = <#{mode}>")
+ }
end
end
def test_getbyte_extended_file
[nil, {:textmode=>true}, {:binmode=>true}].each do |mode|
- f = Tempfile.new("test-extended-file", mode)
- assert_nil(f.getc)
- f.print "a"
- f.rewind
- assert_equal(?a, f.getbyte.chr, "mode = <#{mode}>")
+ Tempfile.create("test-extended-file", mode) {|f|
+ assert_nil(f.getc)
+ f.print "a"
+ f.rewind
+ assert_equal(?a, f.getbyte.chr, "mode = <#{mode}>")
+ }
end
end
@@ -161,7 +249,7 @@ class TestFile < Test::Unit::TestCase
def test_realpath
Dir.mktmpdir('rubytest-realpath') {|tmpdir|
realdir = File.realpath(tmpdir)
- tst = realdir.sub(/#{Regexp.escape(File::SEPARATOR)}/, '\0\0\0')
+ tst = realdir + (File::SEPARATOR*3 + ".")
assert_equal(realdir, File.realpath(tst))
assert_equal(realdir, File.realpath(".", tst))
if File::ALT_SEPARATOR
@@ -171,14 +259,264 @@ class TestFile < Test::Unit::TestCase
}
end
+ def test_realpath_encoding
+ fsenc = Encoding.find("filesystem")
+ nonascii = "\u{0391 0410 0531 10A0 05d0 2C00 3042}"
+ tst = "A"
+ nonascii.each_char {|c| tst << c.encode(fsenc) rescue nil}
+ Dir.mktmpdir('rubytest-realpath') {|tmpdir|
+ realdir = File.realpath(tmpdir)
+ open(File.join(tmpdir, tst), "w") {}
+ a = File.join(tmpdir, "x")
+ begin
+ File.symlink(tst, a)
+ rescue Errno::EACCES, Errno::EPERM
+ skip "need privilege"
+ end
+ assert_equal(File.join(realdir, tst), File.realpath(a))
+ File.unlink(a)
+
+ tst = "A" + nonascii
+ open(File.join(tmpdir, tst), "w") {}
+ File.symlink(tst, a)
+ assert_equal(File.join(realdir, tst), File.realpath(a.encode("UTF-8")))
+ }
+ end
+
+ def test_realpath_taintedness
+ Dir.mktmpdir('rubytest-realpath') {|tmpdir|
+ dir = File.realpath(tmpdir).untaint
+ File.write(File.join(dir, base = "test.file"), '')
+ base.taint
+ dir.taint
+ assert_predicate(File.realpath(base, dir), :tainted?)
+ base.untaint
+ dir.taint
+ assert_predicate(File.realpath(base, dir), :tainted?)
+ base.taint
+ dir.untaint
+ assert_predicate(File.realpath(base, dir), :tainted?)
+ base.untaint
+ dir.untaint
+ assert_predicate(File.realpath(base, dir), :tainted?)
+ assert_predicate(Dir.chdir(dir) {File.realpath(base)}, :tainted?)
+ }
+ end
+
+ def test_realpath_special_symlink
+ IO.pipe do |r, w|
+ if File.pipe?(path = "/dev/fd/#{r.fileno}")
+ assert_file.identical?(File.realpath(path), path)
+ end
+ end
+ end
+
def test_realdirpath
Dir.mktmpdir('rubytest-realdirpath') {|tmpdir|
realdir = File.realpath(tmpdir)
- tst = realdir.sub(/#{Regexp.escape(File::SEPARATOR)}/, '\0\0\0')
+ tst = realdir + (File::SEPARATOR*3 + ".")
assert_equal(realdir, File.realdirpath(tst))
assert_equal(realdir, File.realdirpath(".", tst))
assert_equal(File.join(realdir, "foo"), File.realdirpath("foo", tst))
}
+ begin
+ result = File.realdirpath("bar", "//:/foo")
+ rescue SystemCallError
+ else
+ if result.start_with?("//")
+ assert_equal("//:/foo/bar", result)
+ end
+ end
+ end
+
+ def test_realdirpath_junction
+ Dir.mktmpdir('rubytest-realpath') {|tmpdir|
+ Dir.chdir(tmpdir) do
+ Dir.mkdir('foo')
+ skip "cannot run mklink" unless system('mklink /j bar foo > nul')
+ assert_equal(File.realpath('foo'), File.realpath('bar'))
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_utime_with_minus_time_segv
+ bug5596 = '[ruby-dev:44838]'
+ assert_in_out_err([], <<-EOS, [bug5596], [])
+ require "tempfile"
+ t = Time.at(-1)
+ begin
+ Tempfile.create('test_utime_with_minus_time_segv') {|f|
+ File.utime(t, t, f)
+ }
+ rescue
+ end
+ puts '#{bug5596}'
+ EOS
+ end
+
+ def test_utime
+ bug6385 = '[ruby-core:44776]'
+
+ mod_time_contents = Time.at 1306527039
+
+ file = Tempfile.new("utime")
+ file.close
+ path = file.path
+
+ File.utime(File.atime(path), mod_time_contents, path)
+ stats = File.stat(path)
+
+ file.open
+ file_mtime = file.mtime
+ file.close(true)
+
+ assert_equal(mod_time_contents, file_mtime, bug6385)
+ assert_equal(mod_time_contents, stats.mtime, bug6385)
+ end
+
+ def test_stat
+ tb = Process.clock_gettime(Process::CLOCK_REALTIME)
+ Tempfile.create("stat") {|file|
+ tb = (tb + Process.clock_gettime(Process::CLOCK_REALTIME)) / 2
+ file.close
+ path = file.path
+
+ t0 = Process.clock_gettime(Process::CLOCK_REALTIME)
+ File.write(path, "foo")
+ sleep 2
+ File.write(path, "bar")
+ sleep 2
+ File.read(path)
+ File.chmod(0644, path)
+ sleep 2
+ File.read(path)
+
+ delta = 1
+ stat = File.stat(path)
+ assert_in_delta tb, stat.birthtime.to_f, delta
+ assert_in_delta t0+2, stat.mtime.to_f, delta
+ if stat.birthtime != stat.ctime
+ assert_in_delta t0+4, stat.ctime.to_f, delta
+ end
+ if /mswin|mingw/ !~ RUBY_PLATFORM && !Bug::File::Fs.noatime?(path)
+ # Windows delays updating atime
+ assert_in_delta t0+6, stat.atime.to_f, delta
+ end
+ }
+ rescue NotImplementedError
+ end
+
+ def test_stat_inode
+ assert_not_equal 0, File.stat(__FILE__).ino
+ end
+
+ def test_chmod_m17n
+ bug5671 = '[ruby-dev:44898]'
+ Dir.mktmpdir('test-file-chmod-m17n-') do |tmpdir|
+ file = File.join(tmpdir, "\u3042")
+ File.open(file, 'w'){}
+ assert_equal(File.chmod(0666, file), 1, bug5671)
+ end
+ end
+
+ def test_file_open_permissions
+ Dir.mktmpdir(__method__.to_s) do |tmpdir|
+ tmp = File.join(tmpdir, 'x')
+ File.open(tmp, :mode => IO::RDWR | IO::CREAT | IO::BINARY,
+ :encoding => Encoding::ASCII_8BIT) do |x|
+
+ assert_predicate(x, :autoclose?)
+ assert_equal Encoding::ASCII_8BIT, x.external_encoding
+ x.write 'hello'
+
+ x.seek 0, IO::SEEK_SET
+
+ assert_equal 'hello', x.read
+
+ end
+ end
+ end
+
+ def test_file_open_double_mode
+ assert_raise_with_message(ArgumentError, 'mode specified twice') {
+ File.open("a", 'w', :mode => 'rw+')
+ }
+ end
+
+ def test_file_share_delete
+ Dir.mktmpdir(__method__.to_s) do |tmpdir|
+ tmp = File.join(tmpdir, 'x')
+ File.open(tmp, mode: IO::WRONLY | IO::CREAT | IO::BINARY | IO::SHARE_DELETE) do |f|
+ assert_file.exist?(tmp)
+ assert_nothing_raised do
+ File.unlink(tmp)
+ end
+ end
+ assert_file.not_exist?(tmp)
+ end
+ end
+
+ def test_conflicting_encodings
+ Dir.mktmpdir(__method__.to_s) do |tmpdir|
+ tmp = File.join(tmpdir, 'x')
+ File.open(tmp, 'wb', :encoding => Encoding::EUC_JP) do |x|
+ assert_equal Encoding::EUC_JP, x.external_encoding
+ end
+ end
+ end
+
+ def test_untainted_path
+ bug5374 = '[ruby-core:39745]'
+ cwd = ("./"*40+".".taint).dup.untaint
+ in_safe = proc {|safe| $SAFE = safe; File.stat(cwd)}
+ assert_not_send([cwd, :tainted?])
+ (0..1).each do |level|
+ assert_nothing_raised(SecurityError, bug5374) {in_safe[level]}
+ end
+ ensure
+ $SAFE = 0
+ end
+
+ if /(bcc|ms|cyg)win|mingw|emx/ =~ RUBY_PLATFORM
+ def test_long_unc
+ feature3399 = '[ruby-core:30623]'
+ path = File.expand_path(__FILE__)
+ path.sub!(%r'\A//', 'UNC/')
+ assert_nothing_raised(Errno::ENOENT, feature3399) do
+ File.stat("//?/#{path}")
+ end
+ end
end
+ def test_open_nul
+ Dir.mktmpdir(__method__.to_s) do |tmpdir|
+ path = File.join(tmpdir, "foo")
+ assert_raise(ArgumentError) do
+ open(path + "\0bar", "w") {}
+ end
+ assert_file.not_exist?(path)
+ end
+ end
+
+ def test_open_tempfile_path
+ Dir.mktmpdir(__method__.to_s) do |tmpdir|
+ begin
+ io = File.open(tmpdir, File::RDWR | File::TMPFILE)
+ rescue Errno::EINVAL
+ skip 'O_TMPFILE not supported (EINVAL)'
+ rescue Errno::EISDIR
+ skip 'O_TMPFILE not supported (EISDIR)'
+ rescue Errno::EOPNOTSUPP
+ skip 'O_TMPFILE not supported (EOPNOTSUPP)'
+ end
+
+ io.write "foo"
+ io.flush
+ assert_equal 3, io.size
+ assert_raise(IOError) { io.path }
+ ensure
+ io&.close
+ end
+ end if File::Constants.const_defined?(:TMPFILE)
+
end
diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb
index 35d6bcff14..7d4eeb6d59 100644
--- a/test/ruby/test_file_exhaustive.rb
+++ b/test/ruby/test_file_exhaustive.rb
@@ -1,8 +1,15 @@
+# frozen_string_literal: false
require "test/unit"
require "fileutils"
require "tmpdir"
+require "socket"
+require '-test-/file'
class TestFileExhaustive < Test::Unit::TestCase
+ DRIVE = Dir.pwd[%r'\A(?:[a-z]:|//[^/]+/[^/]+)'i]
+ POSIX = /cygwin|mswin|bccwin|mingw|emx/ !~ RUBY_PLATFORM
+ NTFS = !(/mingw|mswin|bccwin/ !~ RUBY_PLATFORM)
+
def assert_incompatible_encoding
d = "\u{3042}\u{3044}".encode("utf-16le")
assert_raise(Encoding::CompatibilityError) {yield d}
@@ -13,63 +20,193 @@ class TestFileExhaustive < Test::Unit::TestCase
def setup
@dir = Dir.mktmpdir("rubytest-file")
File.chown(-1, Process.gid, @dir)
- @file = make_tmp_filename("file")
- @zerofile = make_tmp_filename("zerofile")
+ end
+
+ def teardown
+ GC.start
+ FileUtils.remove_entry_secure @dir
+ end
+
+ def make_tmp_filename(prefix)
+ "#{@dir}/#{prefix}.test"
+ end
+
+ def rootdir
+ return @rootdir if defined? @rootdir
+ @rootdir = "#{DRIVE}/"
+ @rootdir
+ end
+
+ def nofile
+ return @nofile if defined? @nofile
@nofile = make_tmp_filename("nofile")
- @symlinkfile = make_tmp_filename("symlinkfile")
- @hardlinkfile = make_tmp_filename("hardlinkfile")
- make_file("foo", @file)
+ @nofile
+ end
+
+ def make_file(content, file)
+ open(file, "w") {|fh| fh << content }
+ end
+
+ def zerofile
+ return @zerofile if defined? @zerofile
+ @zerofile = make_tmp_filename("zerofile")
make_file("", @zerofile)
- @time = Time.now
+ @zerofile
+ end
+
+ def regular_file
+ return @file if defined? @file
+ @file = make_tmp_filename("file")
+ make_file("foo", @file)
+ @file
+ end
+
+ def utf8_file
+ return @utf8file if defined? @utf8file
+ @utf8file = make_tmp_filename("\u3066\u3059\u3068")
+ make_file("foo", @utf8file)
+ @utf8file
+ end
+
+ def notownedfile
+ return @notownedfile if defined? @notownedfile
+ if Process.euid != 0
+ @notownedfile = '/'
+ else
+ @notownedfile = nil
+ end
+ @notownedfile
+ end
+
+ def suidfile
+ return @suidfile if defined? @suidfile
+ if POSIX
+ @suidfile = make_tmp_filename("suidfile")
+ make_file("", @suidfile)
+ File.chmod 04500, @suidfile
+ @suidfile
+ else
+ @suidfile = nil
+ end
+ end
+
+ def sgidfile
+ return @sgidfile if defined? @sgidfile
+ if POSIX
+ @sgidfile = make_tmp_filename("sgidfile")
+ make_file("", @sgidfile)
+ File.chmod 02500, @sgidfile
+ @sgidfile
+ else
+ @sgidfile = nil
+ end
+ end
+
+ def stickyfile
+ return @stickyfile if defined? @stickyfile
+ if POSIX
+ @stickyfile = make_tmp_filename("stickyfile")
+ Dir.mkdir(@stickyfile)
+ File.chmod 01500, @stickyfile
+ @stickyfile
+ else
+ @stickyfile = nil
+ end
+ end
+
+ def symlinkfile
+ return @symlinkfile if defined? @symlinkfile
+ @symlinkfile = make_tmp_filename("symlinkfile")
begin
- File.symlink(@file, @symlinkfile)
- rescue NotImplementedError
+ File.symlink(regular_file, @symlinkfile)
+ rescue NotImplementedError, Errno::EACCES, Errno::EPERM
@symlinkfile = nil
end
+ @symlinkfile
+ end
+
+ def hardlinkfile
+ return @hardlinkfile if defined? @hardlinkfile
+ @hardlinkfile = make_tmp_filename("hardlinkfile")
begin
- File.link(@file, @hardlinkfile)
+ File.link(regular_file, @hardlinkfile)
rescue NotImplementedError, Errno::EINVAL # EINVAL for Windows Vista
@hardlinkfile = nil
end
+ @hardlinkfile
end
- def teardown
- GC.start
- FileUtils.remove_entry_secure @dir
+ def fifo
+ return @fifo if defined? @fifo
+ if POSIX
+ fn = make_tmp_filename("fifo")
+ File.mkfifo(fn)
+ @fifo = fn
+ else
+ @fifo = nil
+ end
+ @fifo
end
- def make_file(content, file = @file)
- open(file, "w") {|fh| fh << content }
+ def socket
+ return @socket if defined? @socket
+ if defined? UNIXServer
+ socket = make_tmp_filename("s")
+ UNIXServer.open(socket).close
+ @socket = socket
+ else
+ @socket = nil
+ end
end
- def make_tmp_filename(prefix)
- @hardlinkfile = @dir + "/" + prefix + File.basename(__FILE__) + ".#{$$}.test"
+ def chardev
+ return @chardev if defined? @chardev
+ @chardev = File::NULL == "/dev/null" ? "/dev/null" : nil
+ @chardev
end
- def test_path
- file = @file
+ def blockdev
+ return @blockdev if defined? @blockdev
+ if /linux/ =~ RUBY_PLATFORM
+ @blockdev = %w[/dev/loop0 /dev/sda /dev/vda /dev/xvda1].find {|f| File.exist? f }
+ else
+ @blockdev = nil
+ end
+ @blockdev
+ end
- assert_equal(file, File.open(file) {|f| f.path})
- assert_equal(file, File.path(file))
- o = Object.new
- class << o; self; end.class_eval do
- define_method(:to_path) { file }
+ def test_path
+ [regular_file, utf8_file].each do |file|
+ assert_equal(file, File.open(file) {|f| f.path})
+ assert_equal(file, File.path(file))
+ o = Object.new
+ class << o; self; end.class_eval do
+ define_method(:to_path) { file }
+ end
+ assert_equal(file, File.path(o))
end
- assert_equal(file, File.path(o))
end
def assert_integer(n)
- assert(n.is_a?(Integer), n.inspect + " is not Fixnum.")
+ assert_kind_of(Integer, n)
end
def assert_integer_or_nil(n)
- assert(n.is_a?(Integer) || n.equal?(nil), n.inspect + " is neither Fixnum nor nil.")
+ msg = ->{"#{n.inspect} is neither Integer nor nil."}
+ if n
+ assert_kind_of(Integer, n, msg)
+ else
+ assert_nil(n, msg)
+ end
end
def test_stat
- sleep(@time - Time.now + 1.1)
- make_file("foo", @file + "2")
- fs1, fs2 = File.stat(@file), File.stat(@file + "2")
+ fn1 = regular_file
+ hardlinkfile
+ sleep(1.1)
+ fn2 = fn1 + "2"
+ make_file("foo", fn2)
+ fs1, fs2 = File.stat(fn1), File.stat(fn2)
assert_nothing_raised do
assert_equal(0, fs1 <=> fs1)
assert_equal(-1, fs1 <=> fs2)
@@ -86,7 +223,7 @@ class TestFileExhaustive < Test::Unit::TestCase
unless /emx|mswin|mingw/ =~ RUBY_PLATFORM
# on Windows, nlink is always 1. but this behavior will be changed
# in the future.
- assert_equal(@hardlinkfile ? 2 : 1, fs1.nlink)
+ assert_equal(hardlinkfile ? 2 : 1, fs1.nlink)
end
assert_integer(fs1.uid)
assert_integer(fs1.gid)
@@ -98,236 +235,414 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_kind_of(Time, fs1.ctime)
assert_kind_of(String, fs1.inspect)
end
- assert_raise(Errno::ENOENT) { File.stat(@nofile) }
- assert_kind_of(File::Stat, File.open(@file) {|f| f.stat})
- assert_raise(Errno::ENOENT) { File.lstat(@nofile) }
- assert_kind_of(File::Stat, File.open(@file) {|f| f.lstat})
+ assert_raise(Errno::ENOENT) { File.stat(nofile) }
+ assert_kind_of(File::Stat, File.open(fn1) {|f| f.stat})
+ assert_raise(Errno::ENOENT) { File.lstat(nofile) }
+ assert_kind_of(File::Stat, File.open(fn1) {|f| f.lstat})
+ end
+
+ def test_stat_drive_root
+ assert_nothing_raised { File.stat(DRIVE + "/") }
+ assert_nothing_raised { File.stat(DRIVE + "/.") }
+ assert_nothing_raised { File.stat(DRIVE + "/..") }
+ assert_raise(Errno::ENOENT) { File.stat(DRIVE + "/...") }
+ # want to test the root of empty drive, but there is no method to test it...
+ end if DRIVE
+
+ def test_stat_dotted_prefix
+ Dir.mktmpdir do |dir|
+ prefix = File.join(dir, "...a")
+ Dir.mkdir(prefix)
+ assert_file.exist?(prefix)
+
+ assert_nothing_raised { File.stat(prefix) }
+
+ Dir.chdir(dir) do
+ assert_nothing_raised { File.stat(File.basename(prefix)) }
+ end
+ end
+ end if NTFS
+
+ def test_lstat
+ return unless symlinkfile
+ assert_equal(false, File.stat(symlinkfile).symlink?)
+ assert_equal(true, File.lstat(symlinkfile).symlink?)
+ f = File.new(symlinkfile)
+ assert_equal(false, f.stat.symlink?)
+ assert_equal(true, f.lstat.symlink?)
+ f.close
end
def test_directory_p
- assert(File.directory?(@dir))
- assert(!(File.directory?(@dir+"/...")))
- assert(!(File.directory?(@file)))
- assert(!(File.directory?(@nofile)))
+ assert_file.directory?(@dir)
+ assert_file.not_directory?(@dir+"/...")
+ assert_file.not_directory?(regular_file)
+ assert_file.not_directory?(utf8_file)
+ assert_file.not_directory?(nofile)
end
- def test_pipe_p ## xxx
- assert(!(File.pipe?(@dir)))
- assert(!(File.pipe?(@file)))
- assert(!(File.pipe?(@nofile)))
+ def test_pipe_p
+ assert_file.not_pipe?(@dir)
+ assert_file.not_pipe?(regular_file)
+ assert_file.not_pipe?(utf8_file)
+ assert_file.not_pipe?(nofile)
+ assert_file.pipe?(fifo) if fifo
end
def test_symlink_p
- assert(!(File.symlink?(@dir)))
- assert(!(File.symlink?(@file)))
- assert(File.symlink?(@symlinkfile)) if @symlinkfile
- assert(!(File.symlink?(@hardlinkfile))) if @hardlinkfile
- assert(!(File.symlink?(@nofile)))
+ assert_file.not_symlink?(@dir)
+ assert_file.not_symlink?(regular_file)
+ assert_file.not_symlink?(utf8_file)
+ assert_file.symlink?(symlinkfile) if symlinkfile
+ assert_file.not_symlink?(hardlinkfile) if hardlinkfile
+ assert_file.not_symlink?(nofile)
end
- def test_socket_p ## xxx
- assert(!(File.socket?(@dir)))
- assert(!(File.socket?(@file)))
- assert(!(File.socket?(@nofile)))
+ def test_socket_p
+ assert_file.not_socket?(@dir)
+ assert_file.not_socket?(regular_file)
+ assert_file.not_socket?(utf8_file)
+ assert_file.not_socket?(nofile)
+ assert_file.socket?(socket) if socket
end
- def test_blockdev_p ## xxx
- assert(!(File.blockdev?(@dir)))
- assert(!(File.blockdev?(@file)))
- assert(!(File.blockdev?(@nofile)))
+ def test_blockdev_p
+ assert_file.not_blockdev?(@dir)
+ assert_file.not_blockdev?(regular_file)
+ assert_file.not_blockdev?(utf8_file)
+ assert_file.not_blockdev?(nofile)
+ assert_file.blockdev?(blockdev) if blockdev
end
- def test_chardev_p ## xxx
- assert(!(File.chardev?(@dir)))
- assert(!(File.chardev?(@file)))
- assert(!(File.chardev?(@nofile)))
+ def test_chardev_p
+ assert_file.not_chardev?(@dir)
+ assert_file.not_chardev?(regular_file)
+ assert_file.not_chardev?(utf8_file)
+ assert_file.not_chardev?(nofile)
+ assert_file.chardev?(chardev) if chardev
end
def test_exist_p
- assert(File.exist?(@dir))
- assert(File.exist?(@file))
- assert(!(File.exist?(@nofile)))
+ assert_file.exist?(@dir)
+ assert_file.exist?(regular_file)
+ assert_file.exist?(utf8_file)
+ assert_file.not_exist?(nofile)
end
def test_readable_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
return if Process.euid == 0
- File.chmod(0200, @file)
- assert(!(File.readable?(@file)))
- File.chmod(0600, @file)
- assert(File.readable?(@file))
- assert(!(File.readable?(@nofile)))
- end
+ File.chmod(0200, regular_file)
+ assert_file.not_readable?(regular_file)
+ File.chmod(0600, regular_file)
+ assert_file.readable?(regular_file)
+
+ File.chmod(0200, utf8_file)
+ assert_file.not_readable?(utf8_file)
+ File.chmod(0600, utf8_file)
+ assert_file.readable?(utf8_file)
+
+ assert_file.not_readable?(nofile)
+ end if POSIX
def test_readable_real_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
return if Process.euid == 0
- File.chmod(0200, @file)
- assert(!(File.readable_real?(@file)))
- File.chmod(0600, @file)
- assert(File.readable_real?(@file))
- assert(!(File.readable_real?(@nofile)))
- end
+ File.chmod(0200, regular_file)
+ assert_file.not_readable_real?(regular_file)
+ File.chmod(0600, regular_file)
+ assert_file.readable_real?(regular_file)
+
+ File.chmod(0200, utf8_file)
+ assert_file.not_readable_real?(utf8_file)
+ File.chmod(0600, utf8_file)
+ assert_file.readable_real?(utf8_file)
+
+ assert_file.not_readable_real?(nofile)
+ end if POSIX
def test_world_readable_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
- File.chmod(0006, @file)
- assert(File.world_readable?(@file))
- File.chmod(0060, @file)
- assert(!(File.world_readable?(@file)))
- File.chmod(0600, @file)
- assert(!(File.world_readable?(@file)))
- assert(!(File.world_readable?(@nofile)))
- end
+ File.chmod(0006, regular_file)
+ assert_file.world_readable?(regular_file)
+ File.chmod(0060, regular_file)
+ assert_file.not_world_readable?(regular_file)
+ File.chmod(0600, regular_file)
+ assert_file.not_world_readable?(regular_file)
+
+ File.chmod(0006, utf8_file)
+ assert_file.world_readable?(utf8_file)
+ File.chmod(0060, utf8_file)
+ assert_file.not_world_readable?(utf8_file)
+ File.chmod(0600, utf8_file)
+ assert_file.not_world_readable?(utf8_file)
+
+ assert_file.not_world_readable?(nofile)
+ end if POSIX
def test_writable_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
return if Process.euid == 0
- File.chmod(0400, @file)
- assert(!(File.writable?(@file)))
- File.chmod(0600, @file)
- assert(File.writable?(@file))
- assert(!(File.writable?(@nofile)))
- end
+ File.chmod(0400, regular_file)
+ assert_file.not_writable?(regular_file)
+ File.chmod(0600, regular_file)
+ assert_file.writable?(regular_file)
+
+ File.chmod(0400, utf8_file)
+ assert_file.not_writable?(utf8_file)
+ File.chmod(0600, utf8_file)
+ assert_file.writable?(utf8_file)
+
+ assert_file.not_writable?(nofile)
+ end if POSIX
def test_writable_real_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
return if Process.euid == 0
- File.chmod(0400, @file)
- assert(!(File.writable_real?(@file)))
- File.chmod(0600, @file)
- assert(File.writable_real?(@file))
- assert(!(File.writable_real?(@nofile)))
- end
+ File.chmod(0400, regular_file)
+ assert_file.not_writable_real?(regular_file)
+ File.chmod(0600, regular_file)
+ assert_file.writable_real?(regular_file)
+
+ File.chmod(0400, utf8_file)
+ assert_file.not_writable_real?(utf8_file)
+ File.chmod(0600, utf8_file)
+ assert_file.writable_real?(utf8_file)
+
+ assert_file.not_writable_real?(nofile)
+ end if POSIX
def test_world_writable_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
- File.chmod(0006, @file)
- assert(File.world_writable?(@file))
- File.chmod(0060, @file)
- assert(!(File.world_writable?(@file)))
- File.chmod(0600, @file)
- assert(!(File.world_writable?(@file)))
- assert(!(File.world_writable?(@nofile)))
- end
+ File.chmod(0006, regular_file)
+ assert_file.world_writable?(regular_file)
+ File.chmod(0060, regular_file)
+ assert_file.not_world_writable?(regular_file)
+ File.chmod(0600, regular_file)
+ assert_file.not_world_writable?(regular_file)
+
+ File.chmod(0006, utf8_file)
+ assert_file.world_writable?(utf8_file)
+ File.chmod(0060, utf8_file)
+ assert_file.not_world_writable?(utf8_file)
+ File.chmod(0600, utf8_file)
+ assert_file.not_world_writable?(utf8_file)
+
+ assert_file.not_world_writable?(nofile)
+ end if POSIX
def test_executable_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
- File.chmod(0100, @file)
- assert(File.executable?(@file))
- File.chmod(0600, @file)
- assert(!(File.executable?(@file)))
- assert(!(File.executable?(@nofile)))
- end
+ File.chmod(0100, regular_file)
+ assert_file.executable?(regular_file)
+ File.chmod(0600, regular_file)
+ assert_file.not_executable?(regular_file)
+
+ File.chmod(0100, utf8_file)
+ assert_file.executable?(utf8_file)
+ File.chmod(0600, utf8_file)
+ assert_file.not_executable?(utf8_file)
+
+ assert_file.not_executable?(nofile)
+ end if POSIX
def test_executable_real_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
- File.chmod(0100, @file)
- assert(File.executable_real?(@file))
- File.chmod(0600, @file)
- assert(!(File.executable_real?(@file)))
- assert(!(File.executable_real?(@nofile)))
- end
+ File.chmod(0100, regular_file)
+ assert_file.executable_real?(regular_file)
+ File.chmod(0600, regular_file)
+ assert_file.not_executable_real?(regular_file)
+
+ File.chmod(0100, utf8_file)
+ assert_file.executable_real?(utf8_file)
+ File.chmod(0600, utf8_file)
+ assert_file.not_executable_real?(utf8_file)
+
+ assert_file.not_executable_real?(nofile)
+ end if POSIX
def test_file_p
- assert(!(File.file?(@dir)))
- assert(File.file?(@file))
- assert(!(File.file?(@nofile)))
+ assert_file.not_file?(@dir)
+ assert_file.file?(regular_file)
+ assert_file.file?(utf8_file)
+ assert_file.not_file?(nofile)
end
def test_zero_p
assert_nothing_raised { File.zero?(@dir) }
- assert(!(File.zero?(@file)))
- assert(File.zero?(@zerofile))
- assert(!(File.zero?(@nofile)))
+ assert_file.not_zero?(regular_file)
+ assert_file.not_zero?(utf8_file)
+ assert_file.zero?(zerofile)
+ assert_file.not_zero?(nofile)
+ end
+
+ def test_empty_p
+ assert_nothing_raised { File.empty?(@dir) }
+ assert_file.not_empty?(regular_file)
+ assert_file.not_empty?(utf8_file)
+ assert_file.empty?(zerofile)
+ assert_file.not_empty?(nofile)
end
def test_size_p
assert_nothing_raised { File.size?(@dir) }
- assert_equal(3, File.size?(@file))
- assert(!(File.size?(@zerofile)))
- assert(!(File.size?(@nofile)))
+ assert_equal(3, File.size?(regular_file))
+ assert_equal(3, File.size?(utf8_file))
+ assert_file.not_size?(zerofile)
+ assert_file.not_size?(nofile)
+ end
+
+ def test_owned_p
+ assert_file.owned?(regular_file)
+ assert_file.owned?(utf8_file)
+ assert_file.not_owned?(notownedfile) if notownedfile
+ end if POSIX
+
+ def test_grpowned_p ## xxx
+ assert_file.grpowned?(regular_file)
+ assert_file.grpowned?(utf8_file)
+ end if POSIX
+
+ def io_open(file_name)
+ # avoid File.open since we do not want #to_path
+ io = IO.for_fd(IO.sysopen(file_name))
+ yield io
+ ensure
+ io&.close
+ end
+
+ def test_suid
+ assert_file.not_setuid?(regular_file)
+ assert_file.not_setuid?(utf8_file)
+ if suidfile
+ assert_file.setuid?(suidfile)
+ io_open(suidfile) { |io| assert_file.setuid?(io) }
+ end
+ end
+
+ def test_sgid
+ assert_file.not_setgid?(regular_file)
+ assert_file.not_setgid?(utf8_file)
+ if sgidfile
+ assert_file.setgid?(sgidfile)
+ io_open(sgidfile) { |io| assert_file.setgid?(io) }
+ end
+ end
+
+ def test_sticky
+ assert_file.not_sticky?(regular_file)
+ assert_file.not_sticky?(utf8_file)
+ if stickyfile
+ assert_file.sticky?(stickyfile)
+ io_open(stickyfile) { |io| assert_file.sticky?(io) }
+ end
end
- def test_owned_p ## xxx
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
- assert(File.owned?(@file))
- assert(File.grpowned?(@file))
+ def test_path_identical_p
+ assert_file.identical?(regular_file, regular_file)
+ assert_file.not_identical?(regular_file, zerofile)
+ assert_file.not_identical?(regular_file, nofile)
+ assert_file.not_identical?(nofile, regular_file)
end
- def test_suid_sgid_sticky ## xxx
- assert(!(File.setuid?(@file)))
- assert(!(File.setgid?(@file)))
- assert(!(File.sticky?(@file)))
+ def path_identical_p(file)
+ [regular_file, utf8_file].each do |file|
+ assert_file.identical?(file, file)
+ assert_file.not_identical?(file, zerofile)
+ assert_file.not_identical?(file, nofile)
+ assert_file.not_identical?(nofile, file)
+ end
end
- def test_identical_p
- assert(File.identical?(@file, @file))
- assert(!(File.identical?(@file, @zerofile)))
- assert(!(File.identical?(@file, @nofile)))
- assert(!(File.identical?(@nofile, @file)))
+ def test_io_identical_p
+ [regular_file, utf8_file].each do |file|
+ open(file) {|f|
+ assert_file.identical?(f, f)
+ assert_file.identical?(file, f)
+ assert_file.identical?(f, file)
+ }
+ end
+ end
+
+ def test_closed_io_identical_p
+ [regular_file, utf8_file].each do |file|
+ io = open(file) {|f| f}
+ assert_raise(IOError) {
+ File.identical?(file, io)
+ }
+ File.unlink(file)
+ assert_file.not_exist?(file)
+ end
end
def test_s_size
assert_integer(File.size(@dir))
- assert_equal(3, File.size(@file))
- assert_equal(0, File.size(@zerofile))
- assert_raise(Errno::ENOENT) { File.size(@nofile) }
+ assert_equal(3, File.size(regular_file))
+ assert_equal(3, File.size(utf8_file))
+ assert_equal(0, File.size(zerofile))
+ assert_raise(Errno::ENOENT) { File.size(nofile) }
end
def test_ftype
assert_equal("directory", File.ftype(@dir))
- assert_equal("file", File.ftype(@file))
- assert_equal("link", File.ftype(@symlinkfile)) if @symlinkfile
- assert_equal("file", File.ftype(@hardlinkfile)) if @hardlinkfile
- assert_raise(Errno::ENOENT) { File.ftype(@nofile) }
+ assert_equal("file", File.ftype(regular_file))
+ assert_equal("file", File.ftype(utf8_file))
+ assert_equal("link", File.ftype(symlinkfile)) if symlinkfile
+ assert_equal("file", File.ftype(hardlinkfile)) if hardlinkfile
+ assert_raise(Errno::ENOENT) { File.ftype(nofile) }
end
def test_atime
- t1 = File.atime(@file)
- t2 = File.open(@file) {|f| f.atime}
- assert_kind_of(Time, t1)
- assert_kind_of(Time, t2)
- assert_equal(t1, t2)
- assert_raise(Errno::ENOENT) { File.atime(@nofile) }
+ [regular_file, utf8_file].each do |file|
+ t1 = File.atime(file)
+ t2 = File.open(file) {|f| f.atime}
+ assert_kind_of(Time, t1)
+ assert_kind_of(Time, t2)
+ # High Sierra's APFS can handle nano-sec precise.
+ # t1 value is difference from t2 on APFS.
+ if Bug::File::Fs.fsname(Dir.tmpdir) == "apfs"
+ assert_equal(t1.to_i, t2.to_i)
+ else
+ assert_equal(t1, t2)
+ end
+ end
+ assert_raise(Errno::ENOENT) { File.atime(nofile) }
end
def test_mtime
- t1 = File.mtime(@file)
- t2 = File.open(@file) {|f| f.mtime}
- assert_kind_of(Time, t1)
- assert_kind_of(Time, t2)
- assert_equal(t1, t2)
- assert_raise(Errno::ENOENT) { File.mtime(@nofile) }
+ [regular_file, utf8_file].each do |file|
+ t1 = File.mtime(file)
+ t2 = File.open(file) {|f| f.mtime}
+ assert_kind_of(Time, t1)
+ assert_kind_of(Time, t2)
+ assert_equal(t1, t2)
+ end
+ assert_raise(Errno::ENOENT) { File.mtime(nofile) }
end
def test_ctime
- t1 = File.ctime(@file)
- t2 = File.open(@file) {|f| f.ctime}
- assert_kind_of(Time, t1)
- assert_kind_of(Time, t2)
- assert_equal(t1, t2)
- assert_raise(Errno::ENOENT) { File.ctime(@nofile) }
+ [regular_file, utf8_file].each do |file|
+ t1 = File.ctime(file)
+ t2 = File.open(file) {|f| f.ctime}
+ assert_kind_of(Time, t1)
+ assert_kind_of(Time, t2)
+ assert_equal(t1, t2)
+ end
+ assert_raise(Errno::ENOENT) { File.ctime(nofile) }
end
def test_chmod
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
- assert_equal(1, File.chmod(0444, @file))
- assert_equal(0444, File.stat(@file).mode % 01000)
- assert_equal(0, File.open(@file) {|f| f.chmod(0222)})
- assert_equal(0222, File.stat(@file).mode % 01000)
- File.chmod(0600, @file)
- assert_raise(Errno::ENOENT) { File.chmod(0600, @nofile) }
- end
+ [regular_file, utf8_file].each do |file|
+ assert_equal(1, File.chmod(0444, file))
+ assert_equal(0444, File.stat(file).mode % 01000)
+ assert_equal(0, File.open(file) {|f| f.chmod(0222)})
+ assert_equal(0222, File.stat(file).mode % 01000)
+ File.chmod(0600, file)
+ end
+ assert_raise(Errno::ENOENT) { File.chmod(0600, nofile) }
+ end if POSIX
def test_lchmod
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
- assert_equal(1, File.lchmod(0444, @file))
- assert_equal(0444, File.stat(@file).mode % 01000)
- File.lchmod(0600, @file)
- assert_raise(Errno::ENOENT) { File.lchmod(0600, @nofile) }
+ [regular_file, utf8_file].each do |file|
+ assert_equal(1, File.lchmod(0444, file))
+ assert_equal(0444, File.stat(file).mode % 01000)
+ File.lchmod(0600, regular_file)
+ end
+ assert_raise(Errno::ENOENT) { File.lchmod(0600, nofile) }
rescue NotImplementedError
- end
+ end if POSIX
def test_chown ## xxx
end
@@ -336,114 +651,633 @@ class TestFileExhaustive < Test::Unit::TestCase
end
def test_symlink
- return unless @symlinkfile
- assert_equal("link", File.ftype(@symlinkfile))
- assert_raise(Errno::EEXIST) { File.symlink(@file, @file) }
+ return unless symlinkfile
+ assert_equal("link", File.ftype(symlinkfile))
+ assert_raise(Errno::EEXIST) { File.symlink(regular_file, regular_file) }
+ assert_raise(Errno::EEXIST) { File.symlink(utf8_file, utf8_file) }
end
def test_utime
t = Time.local(2000)
- File.utime(t + 1, t + 2, @zerofile)
- assert_equal(t + 1, File.atime(@zerofile))
- assert_equal(t + 2, File.mtime(@zerofile))
+ File.utime(t + 1, t + 2, zerofile)
+ assert_equal(t + 1, File.atime(zerofile))
+ assert_equal(t + 2, File.mtime(zerofile))
end
- def test_hardlink
- return unless @hardlinkfile
- assert_equal("file", File.ftype(@hardlinkfile))
- assert_raise(Errno::EEXIST) { File.link(@file, @file) }
+ def test_utime_symlinkfile
+ return unless symlinkfile
+ t = Time.local(2000)
+ stat = File.lstat(symlinkfile)
+ assert_equal(1, File.utime(t, t, symlinkfile))
+ assert_equal(t, File.stat(regular_file).atime)
+ assert_equal(t, File.stat(regular_file).mtime)
end
- def test_symlink2
- return unless @symlinkfile
- assert_equal(@file, File.readlink(@symlinkfile))
- assert_raise(Errno::EINVAL) { File.readlink(@file) }
- assert_raise(Errno::ENOENT) { File.readlink(@nofile) }
+ def test_lutime
+ return unless File.respond_to?(:lutime)
+ return unless symlinkfile
+
+ r = File.stat(regular_file)
+ t = Time.local(2000)
+ File.lutime(t + 1, t + 2, symlinkfile)
+ rescue NotImplementedError => e
+ skip(e.message)
+ else
+ stat = File.stat(regular_file)
+ assert_equal(r.atime, stat.atime)
+ assert_equal(r.mtime, stat.mtime)
+
+ stat = File.lstat(symlinkfile)
+ assert_equal(t + 1, stat.atime)
+ assert_equal(t + 2, stat.mtime)
+ end
+
+ def test_hardlink
+ return unless hardlinkfile
+ assert_equal("file", File.ftype(hardlinkfile))
+ assert_raise(Errno::EEXIST) { File.link(regular_file, regular_file) }
+ assert_raise(Errno::EEXIST) { File.link(utf8_file, utf8_file) }
+ end
+
+ def test_readlink
+ return unless symlinkfile
+ assert_equal(regular_file, File.readlink(symlinkfile))
+ assert_raise(Errno::EINVAL) { File.readlink(regular_file) }
+ assert_raise(Errno::EINVAL) { File.readlink(utf8_file) }
+ assert_raise(Errno::ENOENT) { File.readlink(nofile) }
+ if fs = Encoding.find("filesystem")
+ assert_equal(fs, File.readlink(symlinkfile).encoding)
+ end
rescue NotImplementedError
end
+ def test_readlink_long_path
+ return unless symlinkfile
+ bug9157 = '[ruby-core:58592] [Bug #9157]'
+ assert_separately(["-", symlinkfile, bug9157], "#{<<~begin}#{<<~"end;"}")
+ begin
+ symlinkfile, bug9157 = *ARGV
+ 100.step(1000, 100) do |n|
+ File.unlink(symlinkfile)
+ link = "foo"*n
+ begin
+ File.symlink(link, symlinkfile)
+ rescue Errno::ENAMETOOLONG
+ break
+ end
+ assert_equal(link, File.readlink(symlinkfile), bug9157)
+ end
+ end;
+ end
+
+ if NTFS
+ def test_readlink_junction
+ base = File.basename(nofile)
+ err = IO.popen(%W"cmd.exe /c mklink /j #{base} .", chdir: @dir, err: %i[child out], &:read)
+ skip err unless $?.success?
+ assert_equal(@dir, File.readlink(nofile))
+ end
+
+ def test_realpath_mount_point
+ vol = IO.popen(["mountvol", DRIVE, "/l"], &:read).strip
+ Dir.mkdir(mnt = File.join(@dir, mntpnt = "mntpnt"))
+ system("mountvol", mntpnt, vol, chdir: @dir)
+ assert_equal(mnt, File.realpath(mnt))
+ ensure
+ system("mountvol", mntpnt, "/d", chdir: @dir)
+ end
+ end
+
def test_unlink
- assert_equal(1, File.unlink(@file))
- make_file("foo", @file)
- assert_raise(Errno::ENOENT) { File.unlink(@nofile) }
+ assert_equal(1, File.unlink(regular_file))
+ make_file("foo", regular_file)
+
+ assert_equal(1, File.unlink(utf8_file))
+ make_file("foo", utf8_file)
+
+ assert_raise(Errno::ENOENT) { File.unlink(nofile) }
end
def test_rename
- assert_equal(0, File.rename(@file, @nofile))
- assert(!(File.exist?(@file)))
- assert(File.exist?(@nofile))
- assert_equal(0, File.rename(@nofile, @file))
- assert_raise(Errno::ENOENT) { File.rename(@nofile, @file) }
+ [regular_file, utf8_file].each do |file|
+ assert_equal(0, File.rename(file, nofile))
+ assert_file.not_exist?(file)
+ assert_file.exist?(nofile)
+ assert_equal(0, File.rename(nofile, file))
+ assert_raise(Errno::ENOENT) { File.rename(nofile, file) }
+ end
end
def test_umask
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
prev = File.umask(0777)
assert_equal(0777, File.umask)
- open(@nofile, "w") { }
- assert_equal(0, File.stat(@nofile).mode % 01000)
- File.unlink(@nofile)
+ open(nofile, "w") { }
+ assert_equal(0, File.stat(nofile).mode % 01000)
+ File.unlink(nofile)
assert_equal(0777, File.umask(prev))
assert_raise(ArgumentError) { File.umask(0, 1, 2) }
- end
+ end if POSIX
def test_expand_path
- assert_equal(@file, File.expand_path(File.basename(@file), File.dirname(@file)))
- if /cygwin|mingw|mswin|bccwin/ =~ RUBY_PLATFORM
- assert_equal(@file, File.expand_path(@file + " "))
- assert_equal(@file, File.expand_path(@file + "."))
- assert_equal(@file, File.expand_path(@file + "::$DATA"))
+ assert_equal(regular_file, File.expand_path(File.basename(regular_file), File.dirname(regular_file)))
+ assert_equal(utf8_file, File.expand_path(File.basename(utf8_file), File.dirname(utf8_file)))
+ end
+
+ if NTFS
+ def test_expand_path_ntfs
+ [regular_file, utf8_file].each do |file|
+ assert_equal(file, File.expand_path(file + " "))
+ assert_equal(file, File.expand_path(file + "."))
+ assert_equal(file, File.expand_path(file + "::$DATA"))
+ end
+ assert_match(/\Ac:\//i, File.expand_path('c:'), '[ruby-core:31591]')
+ assert_match(/\Ac:\//i, File.expand_path('c:foo', 'd:/bar'))
+ assert_match(/\Ae:\//i, File.expand_path('e:foo', 'd:/bar'))
+ assert_match(%r'\Ac:/bar/foo\z'i, File.expand_path('c:foo', 'c:/bar'))
end
- assert_kind_of(String, File.expand_path("~"))
- unless /mingw|mswin/ =~ RUBY_PLATFORM
- assert_raise(ArgumentError) { File.expand_path("~foo_bar_baz_unknown_user_wahaha") }
- assert_raise(ArgumentError) { File.expand_path("~foo_bar_baz_unknown_user_wahaha", "/") }
+ end
+
+ case RUBY_PLATFORM
+ when /darwin/
+ def test_expand_path_hfs
+ ["\u{feff}", *"\u{2000}"..."\u{2100}"].each do |c|
+ file = regular_file + c
+ full_path = File.expand_path(file)
+ mesg = proc {File.basename(full_path).dump}
+ begin
+ open(file) {}
+ rescue
+ # High Sierra's APFS cannot use filenames with undefined character
+ next if Bug::File::Fs.fsname(Dir.tmpdir) == "apfs"
+ assert_equal(file, full_path, mesg)
+ else
+ assert_equal(regular_file, full_path, mesg)
+ end
+ end
+ end
+ end
+
+ if DRIVE
+ def test_expand_path_absolute
+ assert_match(%r"\Az:/foo\z"i, File.expand_path('/foo', "z:/bar"))
+ assert_match(%r"\A//host/share/foo\z"i, File.expand_path('/foo', "//host/share/bar"))
+ assert_match(%r"\A#{DRIVE}/foo\z"i, File.expand_path('/foo'))
end
+ else
+ def test_expand_path_absolute
+ assert_equal("/foo", File.expand_path('/foo'))
+ end
+ end
+
+ def test_expand_path_memsize
+ bug9934 = '[ruby-core:63114] [Bug #9934]'
+ require "objspace"
+ path = File.expand_path("/foo")
+ assert_operator(ObjectSpace.memsize_of(path), :<=, path.bytesize + GC::INTERNAL_CONSTANTS[:RVALUE_SIZE], bug9934)
+ path = File.expand_path("/a"*25)
+ assert_equal(path.bytesize+1 + GC::INTERNAL_CONSTANTS[:RVALUE_SIZE], ObjectSpace.memsize_of(path), bug9934)
+ end
+
+ def test_expand_path_encoding
+ drive = (DRIVE ? 'C:' : '')
+ if Encoding.find("filesystem") == Encoding::CP1251
+ a = "#{drive}/\u3042\u3044\u3046\u3048\u304a".encode("cp932")
+ else
+ a = "#{drive}/\u043f\u0440\u0438\u0432\u0435\u0442".encode("cp1251")
+ end
+ assert_equal(a, File.expand_path(a))
+ a = "#{drive}/\225\\\\"
+ if File::ALT_SEPARATOR == '\\'
+ [%W"cp437 #{drive}/\225", %W"cp932 #{drive}/\225\\"]
+ elsif File.directory?("#{@dir}/\\")
+ [%W"cp437 /\225", %W"cp932 /\225\\"]
+ else
+ [["cp437", a], ["cp932", a]]
+ end.each do |cp, expected|
+ assert_equal(expected.force_encoding(cp), File.expand_path(a.dup.force_encoding(cp)), cp)
+ end
+
+ path = "\u3042\u3044\u3046\u3048\u304a".encode("EUC-JP")
+ assert_equal("#{Dir.pwd}/#{path}".encode("CP932"), File.expand_path(path).encode("CP932"))
+
+ path = "\u3042\u3044\u3046\u3048\u304a".encode("CP51932")
+ assert_equal("#{Dir.pwd}/#{path}", File.expand_path(path))
assert_incompatible_encoding {|d| File.expand_path(d)}
end
+ def test_expand_path_encoding_filesystem
+ home = ENV["HOME"]
+ ENV["HOME"] = "#{DRIVE}/UserHome"
+
+ path = "~".encode("US-ASCII")
+ dir = "C:/".encode("IBM437")
+ fs = Encoding.find("filesystem")
+
+ assert_equal fs, File.expand_path(path).encoding
+ assert_equal fs, File.expand_path(path, dir).encoding
+ ensure
+ ENV["HOME"] = home
+ end
+
+ UnknownUserHome = "~foo_bar_baz_unknown_user_wahaha".freeze
+
+ def test_expand_path_home
+ assert_kind_of(String, File.expand_path("~")) if ENV["HOME"]
+ assert_raise(ArgumentError) { File.expand_path(UnknownUserHome) }
+ assert_raise(ArgumentError) { File.expand_path(UnknownUserHome, "/") }
+ begin
+ bug3630 = '[ruby-core:31537]'
+ home = ENV["HOME"]
+ home_drive = ENV["HOMEDRIVE"]
+ home_path = ENV["HOMEPATH"]
+ user_profile = ENV["USERPROFILE"]
+ ENV["HOME"] = nil
+ ENV["HOMEDRIVE"] = nil
+ ENV["HOMEPATH"] = nil
+ ENV["USERPROFILE"] = nil
+ ENV["HOME"] = "~"
+ assert_raise(ArgumentError, bug3630) { File.expand_path("~") }
+ ENV["HOME"] = "."
+ assert_raise(ArgumentError, bug3630) { File.expand_path("~") }
+ ensure
+ ENV["HOME"] = home
+ ENV["HOMEDRIVE"] = home_drive
+ ENV["HOMEPATH"] = home_path
+ ENV["USERPROFILE"] = user_profile
+ end
+ end
+
+ def test_expand_path_home_dir_string
+ home = ENV["HOME"]
+ new_home = "#{DRIVE}/UserHome"
+ ENV["HOME"] = new_home
+ bug8034 = "[ruby-core:53168]"
+
+ assert_equal File.join(new_home, "foo"), File.expand_path("foo", "~"), bug8034
+ assert_equal File.join(new_home, "bar", "foo"), File.expand_path("foo", "~/bar"), bug8034
+
+ assert_raise(ArgumentError) { File.expand_path(".", UnknownUserHome) }
+ assert_nothing_raised(ArgumentError) { File.expand_path("#{DRIVE}/", UnknownUserHome) }
+ ENV["HOME"] = "#{DRIVE}UserHome"
+ assert_raise(ArgumentError) { File.expand_path("~") }
+ ensure
+ ENV["HOME"] = home
+ end
+
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ def test_expand_path_home_memory_leak_in_path
+ assert_no_memory_leak_at_expand_path_home('', 'in path')
+ end
+
+ def test_expand_path_home_memory_leak_in_base
+ assert_no_memory_leak_at_expand_path_home('".",', 'in base')
+ end
+
+ def assert_no_memory_leak_at_expand_path_home(arg, message)
+ prep = 'ENV["HOME"] = "foo"*100'
+ assert_no_memory_leak([], prep, <<-TRY, "memory leaked at non-absolute home #{message}")
+ 10000.times do
+ begin
+ File.expand_path(#{arg}"~/a")
+ rescue ArgumentError => e
+ next
+ ensure
+ abort("ArgumentError (non-absolute home) expected") unless e
+ end
+ end
+ GC.start
+ TRY
+ end
+ end
+
+
+ def test_expand_path_remove_trailing_alternative_data
+ assert_equal File.join(rootdir, "aaa"), File.expand_path("#{rootdir}/aaa::$DATA")
+ assert_equal File.join(rootdir, "aa:a"), File.expand_path("#{rootdir}/aa:a:$DATA")
+ assert_equal File.join(rootdir, "aaa:$DATA"), File.expand_path("#{rootdir}/aaa:$DATA")
+ end if DRIVE
+
+ def test_expand_path_resolve_empty_string_current_directory
+ assert_equal(Dir.pwd, File.expand_path(""))
+ end
+
+ def test_expand_path_resolve_dot_current_directory
+ assert_equal(Dir.pwd, File.expand_path("."))
+ end
+
+ def test_expand_path_resolve_file_name_relative_current_directory
+ assert_equal(File.join(Dir.pwd, "foo"), File.expand_path("foo"))
+ end
+
+ def test_ignore_nil_dir_string
+ assert_equal(File.join(Dir.pwd, "foo"), File.expand_path("foo", nil))
+ end
+
+ def test_expand_path_resolve_file_name_and_dir_string_relative
+ assert_equal(File.join(Dir.pwd, "bar", "foo"),
+ File.expand_path("foo", "bar"))
+ end
+
+ def test_expand_path_cleanup_dots_file_name
+ bug = "[ruby-talk:18512]"
+
+ assert_equal(File.join(Dir.pwd, ".a"), File.expand_path(".a"), bug)
+ assert_equal(File.join(Dir.pwd, "..a"), File.expand_path("..a"), bug)
+
+ if DRIVE
+ # cleanup dots only on Windows
+ assert_equal(File.join(Dir.pwd, "a"), File.expand_path("a."), bug)
+ assert_equal(File.join(Dir.pwd, "a"), File.expand_path("a.."), bug)
+ else
+ assert_equal(File.join(Dir.pwd, "a."), File.expand_path("a."), bug)
+ assert_equal(File.join(Dir.pwd, "a.."), File.expand_path("a.."), bug)
+ end
+ end
+
+ def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_a_complete_path
+ assert_equal(@dir, File.expand_path("", "#{@dir}"))
+ assert_equal(File.join(@dir, "a"), File.expand_path("a", "#{@dir}"))
+ assert_equal(File.join(@dir, "a"), File.expand_path("../a", "#{@dir}/xxx"))
+ assert_equal(rootdir, File.expand_path(".", "#{rootdir}"))
+ end
+
+ def test_expand_path_ignores_supplied_dir_if_path_contains_a_drive_letter
+ assert_equal(rootdir, File.expand_path(rootdir, "D:/"))
+ end if DRIVE
+
+ def test_expand_path_removes_trailing_slashes_from_absolute_path
+ assert_equal(File.join(rootdir, "foo"), File.expand_path("#{rootdir}foo/"))
+ assert_equal(File.join(rootdir, "foo.rb"), File.expand_path("#{rootdir}foo.rb/"))
+ end
+
+ def test_expand_path_removes_trailing_spaces_from_absolute_path
+ assert_equal(File.join(rootdir, "a"), File.expand_path("#{rootdir}a "))
+ end if DRIVE
+
+ def test_expand_path_converts_a_pathname_which_starts_with_a_slash_using_dir_s_drive
+ assert_match(%r"\Az:/foo\z"i, File.expand_path('/foo', "z:/bar"))
+ end if DRIVE
+
+ def test_expand_path_converts_a_pathname_which_starts_with_a_slash_and_unc_pathname
+ assert_equal("//foo", File.expand_path('//foo', "//bar"))
+ assert_equal("//bar/foo", File.expand_path('/foo', "//bar"))
+ assert_equal("//foo", File.expand_path('//foo', "/bar"))
+ end if DRIVE
+
+ def test_expand_path_converts_a_dot_with_unc_dir
+ assert_equal("//", File.expand_path('.', "//"))
+ end
+
+ def test_expand_path_preserves_unc_path_root
+ assert_equal("//", File.expand_path("//"))
+ assert_equal("//", File.expand_path("//."))
+ assert_equal("//", File.expand_path("//.."))
+ end
+
+ def test_expand_path_converts_a_pathname_which_starts_with_a_slash_using_host_share
+ assert_match(%r"\A//host/share/foo\z"i, File.expand_path('/foo', "//host/share/bar"))
+ end if DRIVE
+
+ def test_expand_path_converts_a_pathname_which_starts_with_a_slash_using_a_current_drive
+ assert_match(%r"\A#{DRIVE}/foo\z"i, File.expand_path('/foo'))
+ end
+
+ def test_expand_path_returns_tainted_strings_or_not
+ assert_equal(true, File.expand_path('foo').tainted?)
+ assert_equal(true, File.expand_path('foo'.taint).tainted?)
+ assert_equal(true, File.expand_path('/foo'.taint).tainted?)
+ assert_equal(true, File.expand_path('foo', 'bar').tainted?)
+ assert_equal(true, File.expand_path('foo', '/bar'.taint).tainted?)
+ assert_equal(true, File.expand_path('foo'.taint, '/bar').tainted?)
+ assert_equal(true, File.expand_path('~').tainted?) if ENV["HOME"]
+
+ if DRIVE
+ assert_equal(true, File.expand_path('/foo').tainted?)
+ assert_equal(false, File.expand_path('//foo').tainted?)
+ assert_equal(true, File.expand_path('C:/foo'.taint).tainted?)
+ assert_equal(false, File.expand_path('C:/foo').tainted?)
+ assert_equal(true, File.expand_path('foo', '/bar').tainted?)
+ assert_equal(true, File.expand_path('foo', 'C:/bar'.taint).tainted?)
+ assert_equal(true, File.expand_path('foo'.taint, 'C:/bar').tainted?)
+ assert_equal(false, File.expand_path('foo', 'C:/bar').tainted?)
+ assert_equal(false, File.expand_path('C:/foo/../bar').tainted?)
+ assert_equal(false, File.expand_path('foo', '//bar').tainted?)
+ else
+ assert_equal(false, File.expand_path('/foo').tainted?)
+ assert_equal(false, File.expand_path('foo', '/bar').tainted?)
+ end
+ end
+
+ def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_home_as_base
+ old_home = ENV["HOME"]
+ home = ENV["HOME"] = "#{DRIVE}/UserHome"
+ assert_equal(home, File.expand_path("~"))
+ assert_equal(home, File.expand_path("~", "C:/FooBar"))
+ assert_equal(File.join(home, "a"), File.expand_path("~/a", "C:/FooBar"))
+ ensure
+ ENV["HOME"] = old_home
+ end
+
+ def test_expand_path_converts_a_pathname_to_an_absolute_pathname_using_unc_home
+ old_home = ENV["HOME"]
+ unc_home = ENV["HOME"] = "//UserHome"
+ assert_equal(unc_home, File.expand_path("~"))
+ ensure
+ ENV["HOME"] = old_home
+ end if DRIVE
+
+ def test_expand_path_does_not_modify_a_home_string_argument
+ old_home = ENV["HOME"]
+ home = ENV["HOME"] = "#{DRIVE}/UserHome"
+ str = "~/a"
+ assert_equal("#{home}/a", File.expand_path(str))
+ assert_equal("~/a", str)
+ ensure
+ ENV["HOME"] = old_home
+ end
+
+ def test_expand_path_raises_argument_error_for_any_supplied_username
+ bug = '[ruby-core:39597]'
+ assert_raise(ArgumentError, bug) { File.expand_path("~anything") }
+ end if DRIVE
+
+ def test_expand_path_for_existent_username
+ user = ENV['USER']
+ skip "ENV['USER'] is not set" unless user
+ assert_equal(ENV['HOME'], File.expand_path("~#{user}"))
+ end unless DRIVE
+
+ def test_expand_path_error_for_nonexistent_username
+ user = "\u{3086 3046 3066 3044}:\u{307F 3084 304A 3046}"
+ assert_raise_with_message(ArgumentError, /#{user}/) {File.expand_path("~#{user}")}
+ end unless DRIVE
+
+ def test_expand_path_error_for_non_absolute_home
+ old_home = ENV["HOME"]
+ ENV["HOME"] = "./UserHome"
+ assert_raise_with_message(ArgumentError, /non-absolute home/) {File.expand_path("~")}
+ ensure
+ ENV["HOME"] = old_home
+ end
+
+ def test_expand_path_raises_a_type_error_if_not_passed_a_string_type
+ assert_raise(TypeError) { File.expand_path(1) }
+ assert_raise(TypeError) { File.expand_path(nil) }
+ assert_raise(TypeError) { File.expand_path(true) }
+ end
+
+ def test_expand_path_expands_dot_dir
+ assert_equal("#{DRIVE}/dir", File.expand_path("#{DRIVE}/./dir"))
+ end
+
+ def test_expand_path_does_not_expand_wildcards
+ assert_equal("#{DRIVE}/*", File.expand_path("./*", "#{DRIVE}/"))
+ assert_equal("#{Dir.pwd}/*", File.expand_path("./*", Dir.pwd))
+ assert_equal("#{DRIVE}/?", File.expand_path("./?", "#{DRIVE}/"))
+ assert_equal("#{Dir.pwd}/?", File.expand_path("./?", Dir.pwd))
+ end if DRIVE
+
+ def test_expand_path_does_not_modify_the_string_argument
+ str = "./a/b/../c"
+ assert_equal("#{Dir.pwd}/a/c", File.expand_path(str, Dir.pwd))
+ assert_equal("./a/b/../c", str)
+ end
+
+ def test_expand_path_returns_a_string_when_passed_a_string_subclass
+ sub = Class.new(String)
+ str = sub.new "./a/b/../c"
+ path = File.expand_path(str, Dir.pwd)
+ assert_equal("#{Dir.pwd}/a/c", path)
+ assert_instance_of(String, path)
+ end
+
+ def test_expand_path_accepts_objects_that_have_a_to_path_method
+ klass = Class.new { def to_path; "a/b/c"; end }
+ obj = klass.new
+ assert_equal("#{Dir.pwd}/a/b/c", File.expand_path(obj))
+ end
+
+ def test_expand_path_with_drive_letter
+ bug10858 = '[ruby-core:68130] [Bug #10858]'
+ assert_match(%r'/bar/foo\z'i, File.expand_path('z:foo', 'bar'), bug10858)
+ assert_equal('z:/bar/foo', File.expand_path('z:foo', '/bar'), bug10858)
+ end if DRIVE
+
+ if /darwin/ =~ RUBY_PLATFORM and Encoding.find("filesystem") == Encoding::UTF_8
+ def test_expand_path_compose
+ pp = Object.new.extend(Test::Unit::Assertions)
+ def pp.mu_pp(str) #:nodoc:
+ str.dump
+ end
+
+ Dir.mktmpdir do |dir|
+ Dir.chdir(dir) do
+ orig = %W"d\u{e9}tente x\u{304c 304e 3050 3052 3054}"
+ orig.each do |o|
+ Dir.mkdir(o)
+ n = Dir.chdir(o) {File.expand_path(".")}
+ pp.assert_equal(o, File.basename(n))
+ end
+ end
+ end
+ end
+ end
+
def test_basename
- assert_equal(File.basename(@file).sub(/\.test$/, ""), File.basename(@file, ".test"))
+ assert_equal(File.basename(regular_file).sub(/\.test$/, ""), File.basename(regular_file, ".test"))
+ assert_equal(File.basename(utf8_file).sub(/\.test$/, ""), File.basename(utf8_file, ".test"))
assert_equal("", s = File.basename(""))
- assert(!s.frozen?, '[ruby-core:24199]')
+ assert_not_predicate(s, :frozen?, '[ruby-core:24199]')
assert_equal("foo", s = File.basename("foo"))
- assert(!s.frozen?, '[ruby-core:24199]')
+ assert_not_predicate(s, :frozen?, '[ruby-core:24199]')
assert_equal("foo", File.basename("foo", ".ext"))
assert_equal("foo", File.basename("foo.ext", ".ext"))
assert_equal("foo", File.basename("foo.ext", ".*"))
- if /cygwin|mingw|mswin|bccwin/ =~ RUBY_PLATFORM
- basename = File.basename(@file)
- assert_equal(basename, File.basename(@file + " "))
- assert_equal(basename, File.basename(@file + "."))
- assert_equal(basename, File.basename(@file + "::$DATA"))
- basename.chomp!(".test")
- assert_equal(basename, File.basename(@file + " ", ".test"))
- assert_equal(basename, File.basename(@file + ".", ".test"))
- assert_equal(basename, File.basename(@file + "::$DATA", ".test"))
- assert_equal(basename, File.basename(@file + " ", ".*"))
- assert_equal(basename, File.basename(@file + ".", ".*"))
- assert_equal(basename, File.basename(@file + "::$DATA", ".*"))
+ end
+
+ if NTFS
+ def test_basename_strip
+ [regular_file, utf8_file].each do |file|
+ basename = File.basename(file)
+ assert_equal(basename, File.basename(file + " "))
+ assert_equal(basename, File.basename(file + "."))
+ assert_equal(basename, File.basename(file + "::$DATA"))
+ basename.chomp!(".test")
+ assert_equal(basename, File.basename(file + " ", ".test"))
+ assert_equal(basename, File.basename(file + ".", ".test"))
+ assert_equal(basename, File.basename(file + "::$DATA", ".test"))
+ assert_equal(basename, File.basename(file + " ", ".*"))
+ assert_equal(basename, File.basename(file + ".", ".*"))
+ assert_equal(basename, File.basename(file + "::$DATA", ".*"))
+ end
end
+ else
+ def test_basename_strip
+ [regular_file, utf8_file].each do |file|
+ basename = File.basename(file)
+ assert_equal(basename + " ", File.basename(file + " "))
+ assert_equal(basename + ".", File.basename(file + "."))
+ assert_equal(basename + "::$DATA", File.basename(file + "::$DATA"))
+ assert_equal(basename + " ", File.basename(file + " ", ".test"))
+ assert_equal(basename + ".", File.basename(file + ".", ".test"))
+ assert_equal(basename + "::$DATA", File.basename(file + "::$DATA", ".test"))
+ assert_equal(basename, File.basename(file + ".", ".*"))
+ basename.chomp!(".test")
+ assert_equal(basename, File.basename(file + " ", ".*"))
+ assert_equal(basename, File.basename(file + "::$DATA", ".*"))
+ end
+ end
+ end
+
+ if File::ALT_SEPARATOR == '\\'
+ def test_basename_backslash
+ a = "foo/\225\\\\"
+ [%W"cp437 \225", %W"cp932 \225\\"].each do |cp, expected|
+ assert_equal(expected.force_encoding(cp), File.basename(a.dup.force_encoding(cp)), cp)
+ end
+ end
+ end
+ def test_basename_encoding
assert_incompatible_encoding {|d| File.basename(d)}
assert_incompatible_encoding {|d| File.basename(d, ".*")}
assert_raise(Encoding::CompatibilityError) {File.basename("foo.ext", ".*".encode("utf-16le"))}
+
+ s = "foo\x93_a".force_encoding("cp932")
+ assert_equal(s, File.basename(s, "_a"))
+
+ s = "\u4032.\u3024"
+ assert_equal(s, File.basename(s, ".\x95\\".force_encoding("cp932")))
end
def test_dirname
- assert(@file.start_with?(File.dirname(@file)))
+ assert_equal(@dir, File.dirname(regular_file))
+ assert_equal(@dir, File.dirname(utf8_file))
assert_equal(".", File.dirname(""))
+ end
+
+ def test_dirname_encoding
assert_incompatible_encoding {|d| File.dirname(d)}
end
+ if File::ALT_SEPARATOR == '\\'
+ def test_dirname_backslash
+ a = "\225\\\\foo"
+ [%W"cp437 \225", %W"cp932 \225\\"].each do |cp, expected|
+ assert_equal(expected.force_encoding(cp), File.dirname(a.dup.force_encoding(cp)), cp)
+ end
+ end
+ end
+
def test_extname
- assert_equal(".test", File.extname(@file))
+ assert_equal(".test", File.extname(regular_file))
+ assert_equal(".test", File.extname(utf8_file))
prefixes = ["", "/", ".", "/.", "bar/.", "/bar/."]
infixes = ["", " ", "."]
infixes2 = infixes + [".ext "]
appendixes = [""]
- if /cygwin|mingw|mswin|bccwin/ =~ RUBY_PLATFORM
+ if NTFS
appendixes << " " << "." << "::$DATA" << "::$DATA.bar"
end
prefixes.each do |prefix|
@@ -465,58 +1299,164 @@ class TestFileExhaustive < Test::Unit::TestCase
end
def test_split
- d, b = File.split(@file)
- assert_equal(File.dirname(@file), d)
- assert_equal(File.basename(@file), b)
+ [regular_file, utf8_file].each do |file|
+ d, b = File.split(file)
+ assert_equal(File.dirname(file), d)
+ assert_equal(File.basename(file), b)
+ end
end
def test_join
s = "foo" + File::SEPARATOR + "bar" + File::SEPARATOR + "baz"
assert_equal(s, File.join("foo", "bar", "baz"))
assert_equal(s, File.join(["foo", "bar", "baz"]))
+
o = Object.new
def o.to_path; "foo"; end
assert_equal(s, File.join(o, "bar", "baz"))
assert_equal(s, File.join("foo" + File::SEPARATOR, "bar", File::SEPARATOR + "baz"))
end
+ def test_join_alt_separator
+ if File::ALT_SEPARATOR == '\\'
+ a = "\225\\"
+ b = "foo"
+ [%W"cp437 \225\\foo", %W"cp932 \225\\/foo"].each do |cp, expected|
+ assert_equal(expected.force_encoding(cp), File.join(a.dup.force_encoding(cp), b.dup.force_encoding(cp)), cp)
+ end
+ end
+ end
+
+ def test_join_ascii_incompatible
+ bug7168 = '[ruby-core:48012]'
+ names = %w"a b".map {|s| s.encode(Encoding::UTF_16LE)}
+ assert_raise(Encoding::CompatibilityError, bug7168) {File.join(*names)}
+ assert_raise(Encoding::CompatibilityError, bug7168) {File.join(names)}
+
+ a = Object.new
+ b = names[1]
+ names = [a, "b"]
+ a.singleton_class.class_eval do
+ define_method(:to_path) do
+ names[1] = b
+ "a"
+ end
+ end
+ assert_raise(Encoding::CompatibilityError, bug7168) {File.join(names)}
+ end
+
+ def test_join_with_changed_separator
+ assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}")
+ bug = '[ruby-core:79579] [Bug #13223]'
+ begin;
+ class File
+ remove_const :Separator
+ remove_const :SEPARATOR
+ end
+ GC.start
+ assert_equal("hello/world", File.join("hello", "world"), bug)
+ end;
+ end
+
def test_truncate
- assert_equal(0, File.truncate(@file, 1))
- assert(File.exist?(@file))
- assert_equal(1, File.size(@file))
- assert_equal(0, File.truncate(@file, 0))
- assert(File.exist?(@file))
- assert(File.zero?(@file))
- make_file("foo", @file)
- assert_raise(Errno::ENOENT) { File.truncate(@nofile, 0) }
-
- f = File.new(@file, "w")
- assert_equal(0, f.truncate(2))
- assert(File.exist?(@file))
- assert_equal(2, File.size(@file))
- assert_equal(0, f.truncate(0))
- assert(File.exist?(@file))
- assert(File.zero?(@file))
- f.close
- make_file("foo", @file)
+ [regular_file, utf8_file].each do |file|
+ assert_equal(0, File.truncate(file, 1))
+ assert_file.exist?(file)
+ assert_equal(1, File.size(file))
+ assert_equal(0, File.truncate(file, 0))
+ assert_file.exist?(file)
+ assert_file.zero?(file)
+ make_file("foo", file)
+ assert_raise(Errno::ENOENT) { File.truncate(nofile, 0) }
+
+ f = File.new(file, "w")
+ assert_equal(0, f.truncate(2))
+ assert_file.exist?(file)
+ assert_equal(2, File.size(file))
+ assert_equal(0, f.truncate(0))
+ assert_file.exist?(file)
+ assert_file.zero?(file)
+ f.close
+ make_file("foo", file)
+
+ assert_raise(IOError) { File.open(file) {|ff| ff.truncate(0)} }
+ end
+ rescue NotImplementedError
+ end
- assert_raise(IOError) { File.open(@file) {|ff| ff.truncate(0)} }
+ def test_flock_exclusive
+ File.open(regular_file, "r+") do |f|
+ f.flock(File::LOCK_EX)
+ assert_separately(["-rtimeout", "-", regular_file], "#{<<~begin}#{<<~"end;"}")
+ begin
+ open(ARGV[0], "r") do |f|
+ Timeout.timeout(0.1) do
+ assert(!f.flock(File::LOCK_SH|File::LOCK_NB))
+ end
+ end
+ end;
+ assert_separately(["-rtimeout", "-", regular_file], "#{<<~begin}#{<<~"end;"}")
+ begin
+ open(ARGV[0], "r") do |f|
+ assert_raise(Timeout::Error) do
+ Timeout.timeout(0.1) do
+ f.flock(File::LOCK_SH)
+ end
+ end
+ end
+ end;
+ f.flock(File::LOCK_UN)
+ end
rescue NotImplementedError
end
- def test_flock ## xxx
- f = File.new(@file, "r+")
- f.flock(File::LOCK_EX)
- f.flock(File::LOCK_SH)
- f.flock(File::LOCK_UN)
- f.close
+ def test_flock_shared
+ File.open(regular_file, "r+") do |f|
+ f.flock(File::LOCK_SH)
+ assert_separately(["-rtimeout", "-", regular_file], "#{<<~begin}#{<<~"end;"}")
+ begin
+ open(ARGV[0], "r") do |f|
+ Timeout.timeout(0.1) do
+ assert(f.flock(File::LOCK_SH))
+ end
+ end
+ end;
+ assert_separately(["-rtimeout", "-", regular_file], "#{<<~begin}#{<<~"end;"}")
+ begin
+ open(ARGV[0], "r+") do |f|
+ assert_raise(Timeout::Error) do
+ Timeout.timeout(0.1) do
+ f.flock(File::LOCK_EX)
+ end
+ end
+ end
+ end;
+ f.flock(File::LOCK_UN)
+ end
rescue NotImplementedError
end
def test_test
- sleep(@time - Time.now + 1.1)
- make_file("foo", @file + "2")
- [@dir, @file, @zerofile, @symlinkfile, @hardlinkfile].compact.each do |f|
+ fn1 = regular_file
+ hardlinkfile
+ sleep(1.1)
+ fn2 = fn1 + "2"
+ make_file("foo", fn2)
+ [
+ @dir,
+ fn1,
+ zerofile,
+ notownedfile,
+ suidfile,
+ sgidfile,
+ stickyfile,
+ symlinkfile,
+ hardlinkfile,
+ chardev,
+ blockdev,
+ 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))
@@ -543,28 +1483,31 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_equal(File.executable_real?(f), test(?X, f))
assert_equal(File.zero?(f), test(?z, f))
end
- assert_equal(false, test(?-, @dir, @file))
- assert_equal(true, test(?-, @file, @file))
- assert_equal(true, test(?=, @file, @file))
- assert_equal(false, test(?>, @file, @file))
- assert_equal(false, test(?<, @file, @file))
+ assert_equal(false, test(?-, @dir, fn1))
+ assert_equal(true, test(?-, fn1, fn1))
+ assert_equal(true, test(?=, fn1, fn1))
+ assert_equal(false, test(?>, fn1, fn1))
+ assert_equal(false, test(?<, fn1, fn1))
unless /cygwin/ =~ RUBY_PLATFORM
- assert_equal(false, test(?=, @file, @file + "2"))
- assert_equal(false, test(?>, @file, @file + "2"))
- assert_equal(true, test(?>, @file + "2", @file))
- assert_equal(true, test(?<, @file, @file + "2"))
- assert_equal(false, test(?<, @file + "2", @file))
+ assert_equal(false, test(?=, fn1, fn2))
+ assert_equal(false, test(?>, fn1, fn2))
+ assert_equal(true, test(?>, fn2, fn1))
+ assert_equal(true, test(?<, fn1, fn2))
+ assert_equal(false, test(?<, fn2, fn1))
end
assert_raise(ArgumentError) { test }
- assert_raise(Errno::ENOENT) { test(?A, @nofile) }
+ assert_raise(Errno::ENOENT) { test(?A, nofile) }
assert_raise(ArgumentError) { test(?a) }
assert_raise(ArgumentError) { test("\0".ord) }
end
def test_stat_init
- sleep(@time - Time.now + 1.1)
- make_file("foo", @file + "2")
- fs1, fs2 = File::Stat.new(@file), File::Stat.new(@file + "2")
+ fn1 = regular_file
+ hardlinkfile
+ sleep(1.1)
+ fn2 = fn1 + "2"
+ make_file("foo", fn2)
+ fs1, fs2 = File::Stat.new(fn1), File::Stat.new(fn2)
assert_nothing_raised do
assert_equal(0, fs1 <=> fs1)
assert_equal(-1, fs1 <=> fs2)
@@ -578,11 +1521,7 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_integer_or_nil(fs1.rdev_minor)
assert_integer(fs1.ino)
assert_integer(fs1.mode)
- unless /emx|mswin|mingw/ =~ RUBY_PLATFORM
- # on Windows, nlink is always 1. but this behavior will be changed
- # in the future.
- assert_equal(@hardlinkfile ? 2 : 1, fs1.nlink)
- end
+ assert_equal(hardlinkfile ? 2 : 1, fs1.nlink)
assert_integer(fs1.uid)
assert_integer(fs1.gid)
assert_equal(3, fs1.size)
@@ -593,179 +1532,197 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_kind_of(Time, fs1.ctime)
assert_kind_of(String, fs1.inspect)
end
- assert_raise(Errno::ENOENT) { File::Stat.new(@nofile) }
- assert_kind_of(File::Stat, File::Stat.new(@file).dup)
+ assert_raise(Errno::ENOENT) { File::Stat.new(nofile) }
+ assert_kind_of(File::Stat, File::Stat.new(fn1).dup)
assert_raise(TypeError) do
- File::Stat.new(@file).instance_eval { initialize_copy(0) }
+ File::Stat.new(fn1).instance_eval { initialize_copy(0) }
+ end
+ end
+
+ def test_stat_new_utf8
+ assert_nothing_raised do
+ File::Stat.new(utf8_file)
end
end
def test_stat_ftype
assert_equal("directory", File::Stat.new(@dir).ftype)
- assert_equal("file", File::Stat.new(@file).ftype)
+ assert_equal("file", File::Stat.new(regular_file).ftype)
# File::Stat uses stat
- assert_equal("file", File::Stat.new(@symlinkfile).ftype) if @symlinkfile
- assert_equal("file", File::Stat.new(@hardlinkfile).ftype) if @hardlinkfile
+ assert_equal("file", File::Stat.new(symlinkfile).ftype) if symlinkfile
+ assert_equal("file", File::Stat.new(hardlinkfile).ftype) if hardlinkfile
end
def test_stat_directory_p
- assert(File::Stat.new(@dir).directory?)
- assert(!(File::Stat.new(@file).directory?))
+ assert_predicate(File::Stat.new(@dir), :directory?)
+ assert_not_predicate(File::Stat.new(regular_file), :directory?)
end
- def test_stat_pipe_p ## xxx
- assert(!(File::Stat.new(@dir).pipe?))
- assert(!(File::Stat.new(@file).pipe?))
+ def test_stat_pipe_p
+ assert_not_predicate(File::Stat.new(@dir), :pipe?)
+ assert_not_predicate(File::Stat.new(regular_file), :pipe?)
+ assert_predicate(File::Stat.new(fifo), :pipe?) if fifo
+ IO.pipe {|r, w|
+ assert_predicate(r.stat, :pipe?)
+ assert_predicate(w.stat, :pipe?)
+ }
end
def test_stat_symlink_p
- assert(!(File::Stat.new(@dir).symlink?))
- assert(!(File::Stat.new(@file).symlink?))
+ assert_not_predicate(File::Stat.new(@dir), :symlink?)
+ assert_not_predicate(File::Stat.new(regular_file), :symlink?)
# File::Stat uses stat
- assert(!(File::Stat.new(@symlinkfile).symlink?)) if @symlinkfile
- assert(!(File::Stat.new(@hardlinkfile).symlink?)) if @hardlinkfile
+ assert_not_predicate(File::Stat.new(symlinkfile), :symlink?) if symlinkfile
+ assert_not_predicate(File::Stat.new(hardlinkfile), :symlink?) if hardlinkfile
end
- def test_stat_socket_p ## xxx
- assert(!(File::Stat.new(@dir).socket?))
- assert(!(File::Stat.new(@file).socket?))
+ def test_stat_socket_p
+ assert_not_predicate(File::Stat.new(@dir), :socket?)
+ assert_not_predicate(File::Stat.new(regular_file), :socket?)
+ assert_predicate(File::Stat.new(socket), :socket?) if socket
end
- def test_stat_blockdev_p ## xxx
- assert(!(File::Stat.new(@dir).blockdev?))
- assert(!(File::Stat.new(@file).blockdev?))
+ def test_stat_blockdev_p
+ assert_not_predicate(File::Stat.new(@dir), :blockdev?)
+ assert_not_predicate(File::Stat.new(regular_file), :blockdev?)
+ assert_predicate(File::Stat.new(blockdev), :blockdev?) if blockdev
end
- def test_stat_chardev_p ## xxx
- assert(!(File::Stat.new(@dir).chardev?))
- assert(!(File::Stat.new(@file).chardev?))
+ 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
end
def test_stat_readable_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
return if Process.euid == 0
- File.chmod(0200, @file)
- assert(!(File::Stat.new(@file).readable?))
- File.chmod(0600, @file)
- assert(File::Stat.new(@file).readable?)
- end
+ File.chmod(0200, regular_file)
+ assert_not_predicate(File::Stat.new(regular_file), :readable?)
+ File.chmod(0600, regular_file)
+ assert_predicate(File::Stat.new(regular_file), :readable?)
+ end if POSIX
def test_stat_readable_real_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
return if Process.euid == 0
- File.chmod(0200, @file)
- assert(!(File::Stat.new(@file).readable_real?))
- File.chmod(0600, @file)
- assert(File::Stat.new(@file).readable_real?)
- end
+ File.chmod(0200, regular_file)
+ assert_not_predicate(File::Stat.new(regular_file), :readable_real?)
+ File.chmod(0600, regular_file)
+ assert_predicate(File::Stat.new(regular_file), :readable_real?)
+ end if POSIX
def test_stat_world_readable_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
- File.chmod(0006, @file)
- assert(File::Stat.new(@file).world_readable?)
- File.chmod(0060, @file)
- assert(!(File::Stat.new(@file).world_readable?))
- File.chmod(0600, @file)
- assert(!(File::Stat.new(@file).world_readable?))
- end
+ File.chmod(0006, regular_file)
+ assert_predicate(File::Stat.new(regular_file), :world_readable?)
+ File.chmod(0060, regular_file)
+ assert_not_predicate(File::Stat.new(regular_file), :world_readable?)
+ File.chmod(0600, regular_file)
+ assert_not_predicate(File::Stat.new(regular_file), :world_readable?)
+ end if POSIX
def test_stat_writable_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
return if Process.euid == 0
- File.chmod(0400, @file)
- assert(!(File::Stat.new(@file).writable?))
- File.chmod(0600, @file)
- assert(File::Stat.new(@file).writable?)
- end
+ File.chmod(0400, regular_file)
+ assert_not_predicate(File::Stat.new(regular_file), :writable?)
+ File.chmod(0600, regular_file)
+ assert_predicate(File::Stat.new(regular_file), :writable?)
+ end if POSIX
def test_stat_writable_real_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
return if Process.euid == 0
- File.chmod(0400, @file)
- assert(!(File::Stat.new(@file).writable_real?))
- File.chmod(0600, @file)
- assert(File::Stat.new(@file).writable_real?)
- end
+ File.chmod(0400, regular_file)
+ assert_not_predicate(File::Stat.new(regular_file), :writable_real?)
+ File.chmod(0600, regular_file)
+ assert_predicate(File::Stat.new(regular_file), :writable_real?)
+ end if POSIX
def test_stat_world_writable_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
- File.chmod(0006, @file)
- assert(File::Stat.new(@file).world_writable?)
- File.chmod(0060, @file)
- assert(!(File::Stat.new(@file).world_writable?))
- File.chmod(0600, @file)
- assert(!(File::Stat.new(@file).world_writable?))
- end
+ File.chmod(0006, regular_file)
+ assert_predicate(File::Stat.new(regular_file), :world_writable?)
+ File.chmod(0060, regular_file)
+ assert_not_predicate(File::Stat.new(regular_file), :world_writable?)
+ File.chmod(0600, regular_file)
+ assert_not_predicate(File::Stat.new(regular_file), :world_writable?)
+ end if POSIX
def test_stat_executable_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
- File.chmod(0100, @file)
- assert(File::Stat.new(@file).executable?)
- File.chmod(0600, @file)
- assert(!(File::Stat.new(@file).executable?))
- end
+ File.chmod(0100, regular_file)
+ assert_predicate(File::Stat.new(regular_file), :executable?)
+ File.chmod(0600, regular_file)
+ assert_not_predicate(File::Stat.new(regular_file), :executable?)
+ end if POSIX
def test_stat_executable_real_p
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
- File.chmod(0100, @file)
- assert(File::Stat.new(@file).executable_real?)
- File.chmod(0600, @file)
- assert(!(File::Stat.new(@file).executable_real?))
- end
+ File.chmod(0100, regular_file)
+ assert_predicate(File::Stat.new(regular_file), :executable_real?)
+ File.chmod(0600, regular_file)
+ assert_not_predicate(File::Stat.new(regular_file), :executable_real?)
+ end if POSIX
def test_stat_file_p
- assert(!(File::Stat.new(@dir).file?))
- assert(File::Stat.new(@file).file?)
+ assert_not_predicate(File::Stat.new(@dir), :file?)
+ assert_predicate(File::Stat.new(regular_file), :file?)
end
def test_stat_zero_p
assert_nothing_raised { File::Stat.new(@dir).zero? }
- assert(!(File::Stat.new(@file).zero?))
- assert(File::Stat.new(@zerofile).zero?)
+ assert_not_predicate(File::Stat.new(regular_file), :zero?)
+ assert_predicate(File::Stat.new(zerofile), :zero?)
end
def test_stat_size_p
assert_nothing_raised { File::Stat.new(@dir).size? }
- assert_equal(3, File::Stat.new(@file).size?)
- assert(!(File::Stat.new(@zerofile).size?))
+ assert_equal(3, File::Stat.new(regular_file).size?)
+ assert_not_predicate(File::Stat.new(zerofile), :size?)
end
- def test_stat_owned_p ## xxx
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
- assert(File::Stat.new(@file).owned?)
- assert(File::Stat.new(@file).grpowned?)
+ def test_stat_owned_p
+ assert_predicate(File::Stat.new(regular_file), :owned?)
+ assert_not_predicate(File::Stat.new(notownedfile), :owned?) if notownedfile
+ end if POSIX
+
+ def test_stat_grpowned_p ## xxx
+ assert_predicate(File::Stat.new(regular_file), :grpowned?)
+ end if POSIX
+
+ def test_stat_suid
+ assert_not_predicate(File::Stat.new(regular_file), :setuid?)
+ assert_predicate(File::Stat.new(suidfile), :setuid?) if suidfile
end
- def test_stat_suid_sgid_sticky ## xxx
- assert(!(File::Stat.new(@file).setuid?))
- assert(!(File::Stat.new(@file).setgid?))
- assert(!(File::Stat.new(@file).sticky?))
+ def test_stat_sgid
+ assert_not_predicate(File::Stat.new(regular_file), :setgid?)
+ assert_predicate(File::Stat.new(sgidfile), :setgid?) if sgidfile
+ end
+
+ def test_stat_sticky
+ assert_not_predicate(File::Stat.new(regular_file), :sticky?)
+ assert_predicate(File::Stat.new(stickyfile), :sticky?) if stickyfile
end
def test_stat_size
assert_integer(File::Stat.new(@dir).size)
- assert_equal(3, File::Stat.new(@file).size)
- assert_equal(0, File::Stat.new(@zerofile).size)
+ assert_equal(3, File::Stat.new(regular_file).size)
+ assert_equal(0, File::Stat.new(zerofile).size)
end
+ def test_stat_special_file
+ # test for special files such as pagefile.sys on Windows
+ assert_nothing_raised do
+ Dir::glob("C:/*.sys") {|f| File::Stat.new(f) }
+ end
+ end if DRIVE
+
def test_path_check
assert_nothing_raised { ENV["PATH"] }
end
- def test_find_file
- assert_raise(SecurityError) do
- Thread.new do
- $SAFE = 4
- load(@file)
- end.join
- end
- end
-
def test_size
- assert_equal(3, File.open(@file) {|f| f.size })
- File.open(@file, "a") do |f|
- f.write("bar")
- assert_equal(6, f.size)
+ [regular_file, utf8_file].each do |file|
+ assert_equal(3, File.open(file) {|f| f.size })
+ File.open(file, "a") do |f|
+ f.write("bar")
+ assert_equal(6, f.size)
+ end
end
end
diff --git a/test/ruby/test_fixnum.rb b/test/ruby/test_fixnum.rb
index 2aee65c211..bd18067dda 100644
--- a/test/ruby/test_fixnum.rb
+++ b/test/ruby/test_fixnum.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestFixnum < Test::Unit::TestCase
@@ -36,10 +37,14 @@ class TestFixnum < Test::Unit::TestCase
def test_plus
assert_equal(0x40000000, 0x3fffffff+1)
+ assert_equal(0x7ffffffe, 0x3fffffff+0x3fffffff)
assert_equal(0x4000000000000000, 0x3fffffffffffffff+1)
+ assert_equal(0x7ffffffffffffffe, 0x3fffffffffffffff+0x3fffffffffffffff)
assert_equal(-0x40000001, (-0x40000000)+(-1))
assert_equal(-0x4000000000000001, (-0x4000000000000000)+(-1))
+ assert_equal(-0x7ffffffe, (-0x3fffffff)+(-0x3fffffff))
assert_equal(-0x80000000, (-0x40000000)+(-0x40000000))
+ assert_equal(-0x8000000000000000, (-0x4000000000000000)+(-0x4000000000000000))
end
def test_sub
@@ -48,6 +53,8 @@ class TestFixnum < Test::Unit::TestCase
assert_equal(-0x40000001, (-0x40000000)-1)
assert_equal(-0x4000000000000001, (-0x4000000000000000)-1)
assert_equal(-0x80000000, (-0x40000000)-0x40000000)
+ assert_equal(0x7fffffffffffffff, 0x3fffffffffffffff-(-0x4000000000000000))
+ assert_equal(-0x8000000000000000, -0x4000000000000000-0x4000000000000000)
end
def test_mult
@@ -74,6 +81,7 @@ class TestFixnum < Test::Unit::TestCase
assert_equal(-0x4000000000000001, 0xc000000000000003/(-3))
assert_equal(0x40000000, (-0x40000000)/(-1), "[ruby-dev:31210]")
assert_equal(0x4000000000000000, (-0x4000000000000000)/(-1))
+ assert_raise(FloatDomainError) { 2.div(Float::NAN).nan? }
end
def test_mod
@@ -87,14 +95,21 @@ class TestFixnum < Test::Unit::TestCase
next if b == 0
q, r = a.divmod(b)
assert_equal(a, b*q+r)
- assert(r.abs < b.abs)
- assert(0 < b ? (0 <= r && r < b) : (b < r && r <= 0))
+ assert_operator(r.abs, :<, b.abs)
+ if 0 < b
+ assert_operator(r, :>=, 0)
+ assert_operator(r, :<, b)
+ else
+ assert_operator(r, :>, b)
+ assert_operator(r, :<=, 0)
+ end
assert_equal(q, a/b)
assert_equal(q, a.div(b))
assert_equal(r, a%b)
assert_equal(r, a.modulo(b))
}
}
+ assert_raise(FloatDomainError) { 2.divmod(Float::NAN) }
end
def test_not
@@ -157,6 +172,7 @@ class TestFixnum < Test::Unit::TestCase
assert_equal(0, 1 / (2**32))
assert_equal(0, 1.div(2**32))
+ assert_kind_of(Float, 1.quo(2.0))
assert_equal(0.5, 1.quo(2.0))
assert_equal(0.5, 1 / 2.0)
assert_equal(0, 1.div(2.0))
@@ -194,39 +210,143 @@ class TestFixnum < Test::Unit::TestCase
end
def test_cmp
- assert(1 != nil)
+ assert_operator(1, :!=, nil)
assert_equal(0, 1 <=> 1)
assert_equal(-1, 1 <=> 4294967296)
+ assert_equal(-1, 1 <=> 1 << 100)
assert_equal(0, 1 <=> 1.0)
assert_nil(1 <=> nil)
- assert(1.send(:>, 0))
- assert(!(1.send(:>, 1)))
- assert(!(1.send(:>, 2)))
- assert(!(1.send(:>, 4294967296)))
- assert(1.send(:>, 0.0))
- assert_raise(ArgumentError) { 1.send(:>, nil) }
-
- assert(1.send(:>=, 0))
- assert(1.send(:>=, 1))
- assert(!(1.send(:>=, 2)))
- assert(!(1.send(:>=, 4294967296)))
- assert(1.send(:>=, 0.0))
- assert_raise(ArgumentError) { 1.send(:>=, nil) }
-
- assert(!(1.send(:<, 0)))
- assert(!(1.send(:<, 1)))
- assert(1.send(:<, 2))
- assert(1.send(:<, 4294967296))
- assert(!(1.send(:<, 0.0)))
- assert_raise(ArgumentError) { 1.send(:<, nil) }
-
- assert(!(1.send(:<=, 0)))
- assert(1.send(:<=, 1))
- assert(1.send(:<=, 2))
- assert(1.send(:<=, 4294967296))
- assert(!(1.send(:<=, 0.0)))
- assert_raise(ArgumentError) { 1.send(:<=, nil) }
+ assert_operator(1, :>, 0)
+ assert_not_operator(1, :>, 1)
+ assert_not_operator(1, :>, 2)
+ assert_not_operator(1, :>, 4294967296)
+ assert_operator(1, :>, 0.0)
+ assert_raise(ArgumentError) { 1 > nil }
+
+ assert_operator(1, :>=, 0)
+ assert_operator(1, :>=, 1)
+ assert_not_operator(1, :>=, 2)
+ assert_not_operator(1, :>=, 4294967296)
+ assert_operator(1, :>=, 0.0)
+ assert_raise(ArgumentError) { 1 >= nil }
+
+ assert_not_operator(1, :<, 0)
+ assert_not_operator(1, :<, 1)
+ assert_operator(1, :<, 2)
+ assert_operator(1, :<, 4294967296)
+ assert_not_operator(1, :<, 0.0)
+ assert_raise(ArgumentError) { 1 < nil }
+
+ assert_not_operator(1, :<=, 0)
+ assert_operator(1, :<=, 1)
+ assert_operator(1, :<=, 2)
+ assert_operator(1, :<=, 4294967296)
+ assert_not_operator(1, :<=, 0.0)
+ assert_raise(ArgumentError) { 1 <= nil }
+ end
+
+ class DummyNumeric < Numeric
+ def to_int
+ 1
+ end
+ end
+
+ def test_and_with_float
+ assert_raise(TypeError) { 1 & 1.5 }
+ end
+
+ def test_and_with_rational
+ assert_raise(TypeError, "#1792") { 1 & Rational(3, 2) }
+ end
+
+ def test_and_with_nonintegral_numeric
+ assert_raise(TypeError, "#1792") { 1 & DummyNumeric.new }
+ end
+
+ def test_or_with_float
+ assert_raise(TypeError) { 1 | 1.5 }
+ end
+
+ def test_or_with_rational
+ assert_raise(TypeError, "#1792") { 1 | Rational(3, 2) }
+ end
+
+ def test_or_with_nonintegral_numeric
+ assert_raise(TypeError, "#1792") { 1 | DummyNumeric.new }
+ end
+
+ def test_xor_with_float
+ assert_raise(TypeError) { 1 ^ 1.5 }
+ end
+
+ def test_xor_with_rational
+ assert_raise(TypeError, "#1792") { 1 ^ Rational(3, 2) }
+ end
+
+ def test_xor_with_nonintegral_numeric
+ assert_raise(TypeError, "#1792") { 1 ^ DummyNumeric.new }
+ end
+
+ def test_singleton_method
+ assert_raise(TypeError) { a = 1; def a.foo; end }
+ end
+
+ def test_frozen
+ assert_equal(true, 1.frozen?)
+ end
+
+ def assert_eql(a, b, mess)
+ assert a.eql?(b), "expected #{a} & #{b} to be eql? #{mess}"
+ end
+
+ def test_power_of_1_and_minus_1
+ bug5715 = '[ruby-core:41498]'
+ big = 1 << 66
+ assert_eql 1, 1 ** -big , bug5715
+ assert_eql 1, (-1) ** -big , bug5715
+ assert_eql (-1), (-1) ** -(big+1), bug5715
+ end
+
+ def test_power_of_0
+ bug5713 = '[ruby-core:41494]'
+ big = 1 << 66
+ assert_raise(ZeroDivisionError, bug5713) { 0 ** -big }
+ assert_raise(ZeroDivisionError, bug5713) { 0 ** Rational(-2,3) }
+ end
+
+ def test_remainder
+ assert_equal(1, 5.remainder(4))
+ assert_predicate(4.remainder(Float::NAN), :nan?)
+ end
+
+ def test_zero_p
+ assert_predicate(0, :zero?)
+ assert_not_predicate(1, :zero?)
+ end
+
+ def test_positive_p
+ assert_predicate(1, :positive?)
+ assert_not_predicate(0, :positive?)
+ assert_not_predicate(-1, :positive?)
+ end
+
+ def test_negative_p
+ assert_predicate(-1, :negative?)
+ assert_not_predicate(0, :negative?)
+ assert_not_predicate(1, :negative?)
+ end
+
+ def test_finite_p
+ assert_predicate(1, :finite?)
+ assert_predicate(0, :finite?)
+ assert_predicate(-1, :finite?)
+ end
+
+ def test_infinite_p
+ assert_nil(1.infinite?)
+ assert_nil(0.infinite?)
+ assert_nil(-1.infinite?)
end
end
diff --git a/test/ruby/test_flip.rb b/test/ruby/test_flip.rb
new file mode 100644
index 0000000000..8ea6416bdb
--- /dev/null
+++ b/test/ruby/test_flip.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: false
+require 'test/unit'
+
+class TestFlip < Test::Unit::TestCase
+ def setup
+ @verbose_bak, $VERBOSE = $VERBOSE, nil
+ end
+
+ def teardown
+ $VERBOSE = @verbose_bak
+ end
+
+ def test_flip_flop
+ eval <<-END
+ assert_equal [4,5], (1..9).select {|n| true if (n==4)..(n==5)}
+ assert_equal [4,5], (1..9).select {|n| true if (n==4)...(n==5)}
+ assert_equal [2], (1..9).select {|n| true if (n==2)..(n%2).zero?}
+ assert_equal [2,3,4], (1..9).select {|n| true if (n==2)...(n%2).zero?}
+ assert_equal [4,5,7,8], (1..9).select {|n| true if (n==4)...(n==5) or (n==7)...(n==8)}
+ assert_equal [nil, 2, 3, 4, nil], (1..5).map {|x| x if (x==2..x==4)}
+ assert_equal [1, nil, nil, nil, 5], (1..5).map {|x| x if !(x==2..x==4)}
+ END
+ end
+
+ def test_hidden_key
+ bug6899 = '[ruby-core:47253]'
+ foo = "foor"
+ bar = "bar"
+ assert_nothing_raised(NotImplementedError, bug6899) do
+ 2000.times {eval %[(foo..bar) ? 1 : 2]}
+ end
+ foo = bar
+ end
+
+ def test_shared_eval
+ bug7671 = '[ruby-core:51296]'
+ vs = (1..9).to_a
+ eval("vs.select {|n| if n==2..n==16 then 1 end}")
+ v = eval("vs.select {|n| if n==3..n==6 then 1 end}")
+ assert_equal([*3..6], v, bug7671)
+ end
+
+ def test_shared_thread
+ ff = eval("proc {|n| true if n==3..n==5}")
+ v = 1..9
+ a = true
+ th = Thread.new {
+ v.select {|i|
+ Thread.pass while a
+ ff[i].tap {a = true}
+ }
+ }
+ v1 = v.select {|i|
+ Thread.pass until a
+ ff[i].tap {a = false}
+ }
+ v2 = th.value
+ expected = [3, 4, 5]
+ mesg = 'flip-flop should be separated per threads'
+ assert_equal(expected, v1, mesg)
+ assert_equal(expected, v2, mesg)
+ end
+
+ def test_input_line_number_range
+ bug12947 = '[ruby-core:78162] [Bug #12947]'
+ ary = b1 = b2 = nil
+ EnvUtil.suppress_warning do
+ b1 = eval("proc {|i| i if 2..4}")
+ b2 = eval("proc {|i| if 2..4; i; end}")
+ end
+ IO.pipe {|r, w|
+ th = Thread.start {(1..5).each {|i| w.puts i};w.close}
+ ary = r.map {|i| b1.call(i.chomp)}
+ th.join
+ }
+ assert_equal([nil, "2", "3", "4", nil], ary, bug12947)
+ IO.pipe {|r, w|
+ th = Thread.start {(1..5).each {|i| w.puts i};w.close}
+ ary = r.map {|i| b2.call(i.chomp)}
+ th.join
+ }
+ assert_equal([nil, "2", "3", "4", nil], ary, bug12947)
+ end
+end
diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb
index 03d9c94766..0b2e4df05b 100644
--- a/test/ruby/test_float.rb
+++ b/test/ruby/test_float.rb
@@ -1,6 +1,9 @@
+# frozen_string_literal: false
require 'test/unit'
class TestFloat < Test::Unit::TestCase
+ include EnvUtil
+
def test_float
assert_equal(2, 2.6.floor)
assert_equal(-3, (-2.6).floor)
@@ -10,18 +13,20 @@ class TestFloat < Test::Unit::TestCase
assert_equal(-2, (-2.6).truncate)
assert_equal(3, 2.6.round)
assert_equal(-2, (-2.4).truncate)
- assert((13.4 % 1 - 0.4).abs < 0.0001)
+ assert_in_delta(13.4 % 1, 0.4, 0.0001)
assert_equal(36893488147419111424,
36893488147419107329.0.to_i)
+ assert_equal(1185151044158398820374743613440,
+ 1.1851510441583988e+30.to_i)
end
def nan_test(x,y)
extend Test::Unit::Assertions
- assert(x != y)
- assert_equal(false, (x < y))
- assert_equal(false, (x > y))
- assert_equal(false, (x <= y))
- assert_equal(false, (x >= y))
+ assert_operator(x, :!=, y)
+ assert_not_operator(x, :<, y)
+ assert_not_operator(x, :>, y)
+ assert_not_operator(x, :<=, y)
+ assert_not_operator(x, :>=, y)
end
def test_nan
nan = Float::NAN
@@ -54,27 +59,76 @@ class TestFloat < Test::Unit::TestCase
assert_equal(a == b, b == a)
end
+ def test_cmp_int
+ 100.times {|i|
+ int0 = 1 << i
+ [int0, -int0].each {|int|
+ flt = int.to_f
+ bigger = int + 1
+ smaller = int - 1
+ assert_operator(flt, :==, int)
+ assert_operator(flt, :>, smaller)
+ assert_operator(flt, :>=, smaller)
+ assert_operator(flt, :<, bigger)
+ assert_operator(flt, :<=, bigger)
+ assert_equal(0, flt <=> int)
+ assert_equal(-1, flt <=> bigger)
+ assert_equal(1, flt <=> smaller)
+ assert_operator(int, :==, flt)
+ assert_operator(bigger, :>, flt)
+ assert_operator(bigger, :>=, flt)
+ assert_operator(smaller, :<, flt)
+ assert_operator(smaller, :<=, flt)
+ assert_equal(0, int <=> flt)
+ assert_equal(-1, smaller <=> flt)
+ assert_equal(1, bigger <=> flt)
+ [
+ [int, flt + 0.5, bigger],
+ [smaller, flt - 0.5, int]
+ ].each {|smaller2, flt2, bigger2|
+ next if flt2 == flt2.round
+ assert_operator(flt2, :!=, smaller2)
+ assert_operator(flt2, :!=, bigger2)
+ assert_operator(flt2, :>, smaller2)
+ assert_operator(flt2, :>=, smaller2)
+ assert_operator(flt2, :<, bigger2)
+ assert_operator(flt2, :<=, bigger2)
+ assert_equal(-1, flt2 <=> bigger2)
+ assert_equal(1, flt2 <=> smaller2)
+ assert_operator(smaller2, :!=, flt2)
+ assert_operator(bigger2, :!=, flt2)
+ assert_operator(bigger2, :>, flt2)
+ assert_operator(bigger2, :>=, flt2)
+ assert_operator(smaller2, :<, flt2)
+ assert_operator(smaller2, :<=, flt2)
+ assert_equal(-1, smaller2 <=> flt2)
+ assert_equal(1, bigger2 <=> flt2)
+ }
+ }
+ }
+ end
+
def test_strtod
a = Float("0")
- assert(a.abs < Float::EPSILON)
+ assert_in_delta(a, 0, Float::EPSILON)
a = Float("0.0")
- assert(a.abs < Float::EPSILON)
+ assert_in_delta(a, 0, Float::EPSILON)
a = Float("+0.0")
- assert(a.abs < Float::EPSILON)
+ assert_in_delta(a, 0, Float::EPSILON)
a = Float("-0.0")
- assert(a.abs < Float::EPSILON)
+ assert_in_delta(a, 0, Float::EPSILON)
a = Float("0.0000000000000000001")
- assert(a != 0.0)
+ assert_not_equal(0.0, a)
a = Float("+0.0000000000000000001")
- assert(a != 0.0)
+ assert_not_equal(0.0, a)
a = Float("-0.0000000000000000001")
- assert(a != 0.0)
+ assert_not_equal(0.0, a)
a = Float(".0")
- assert(a.abs < Float::EPSILON)
+ assert_in_delta(a, 0, Float::EPSILON)
a = Float("+.0")
- assert(a.abs < Float::EPSILON)
+ assert_in_delta(a, 0, Float::EPSILON)
a = Float("-.0")
- assert(a.abs < Float::EPSILON)
+ assert_in_delta(a, 0, Float::EPSILON)
assert_raise(ArgumentError){Float("0.")}
assert_raise(ArgumentError){Float("+0.")}
assert_raise(ArgumentError){Float("-0.")}
@@ -85,20 +139,56 @@ class TestFloat < Test::Unit::TestCase
assert_raise(ArgumentError){Float("-.")}
assert_raise(ArgumentError){Float("1e")}
assert_raise(ArgumentError){Float("1__1")}
+ assert_raise(ArgumentError){Float("1.")}
+ assert_raise(ArgumentError){Float("1.e+00")}
+ assert_raise(ArgumentError){Float("0x1.p+0")}
# add expected behaviour here.
assert_equal(10, Float("1_0"))
assert_equal([ 0.0].pack('G'), [Float(" 0x0p+0").to_f].pack('G'))
assert_equal([-0.0].pack('G'), [Float("-0x0p+0").to_f].pack('G'))
assert_equal(255.0, Float("0Xff"))
- assert_equal(255.5, Float("0Xff.8"))
- assert_equal(1.0, Float("0X1.P+0"))
assert_equal(1024.0, Float("0x1p10"))
assert_equal(1024.0, Float("0x1p+10"))
assert_equal(0.0009765625, Float("0x1p-10"))
assert_equal(2.6881171418161356e+43, Float("0x1.3494a9b171bf5p+144"))
assert_equal(-3.720075976020836e-44, Float("-0x1.a8c1f14e2af5dp-145"))
-
+ assert_equal(31.0*2**1019, Float("0x0."+("0"*268)+"1fp2099"))
+ assert_equal(31.0*2**1019, Float("0x0."+("0"*600)+"1fp3427"))
+ assert_equal(-31.0*2**1019, Float("-0x0."+("0"*268)+"1fp2099"))
+ assert_equal(-31.0*2**1019, Float("-0x0."+("0"*600)+"1fp3427"))
+ suppress_warning do
+ assert_equal(31.0*2**-1027, Float("0x1f"+("0"*268)+".0p-2099"))
+ assert_equal(31.0*2**-1027, Float("0x1f"+("0"*600)+".0p-3427"))
+ assert_equal(-31.0*2**-1027, Float("-0x1f"+("0"*268)+".0p-2099"))
+ assert_equal(-31.0*2**-1027, Float("-0x1f"+("0"*600)+".0p-3427"))
+ end
+
+ assert_equal(1.0e10, Float("1.0_"+"00000"*Float::DIG+"e10"))
+
+ z = "0" * (Float::DIG * 4 + 10)
+ all_assertions_foreach("long invalid string", "1.0", "1.0e", "1.0e-", "1.0e+") do |n|
+ 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
@@ -106,6 +196,8 @@ class TestFloat < Test::Unit::TestCase
assert_equal([-3, -0.5], 11.5.divmod(-4))
assert_equal([-3, 0.5], (-11.5).divmod(4))
assert_equal([2, -3.5], (-11.5).divmod(-4))
+ assert_raise(FloatDomainError) { Float::NAN.divmod(2) }
+ assert_raise(FloatDomainError) { Float::INFINITY.divmod(2) }
end
def test_div
@@ -113,6 +205,9 @@ class TestFloat < Test::Unit::TestCase
assert_equal(-3, 11.5.div(-4))
assert_equal(-3, (-11.5).div(4))
assert_equal(2, (-11.5).div(-4))
+ assert_raise(FloatDomainError) { 11.5.div(Float::NAN).nan? }
+ assert_raise(FloatDomainError) { Float::NAN.div(2).nan? }
+ assert_raise(FloatDomainError) { Float::NAN.div(11.5).nan? }
end
def test_modulo
@@ -127,6 +222,8 @@ class TestFloat < Test::Unit::TestCase
assert_equal(3.5, 11.5.remainder(-4))
assert_equal(-3.5, (-11.5).remainder(4))
assert_equal(-3.5, (-11.5).remainder(-4))
+ assert_predicate(Float::NAN.remainder(4), :nan?)
+ assert_predicate(4.remainder(Float::NAN), :nan?)
end
def test_to_s
@@ -153,6 +250,8 @@ class TestFloat < Test::Unit::TestCase
assert_equal(4.0, 2.0.send(:+, 2))
assert_equal(4.0, 2.0.send(:+, (2**32).coerce(2).first))
assert_equal(4.0, 2.0.send(:+, 2.0))
+ assert_equal(Float::INFINITY, 2.0.send(:+, Float::INFINITY))
+ assert_predicate(2.0.send(:+, Float::NAN), :nan?)
assert_raise(TypeError) { 2.0.send(:+, nil) }
end
@@ -160,6 +259,8 @@ class TestFloat < Test::Unit::TestCase
assert_equal(0.0, 2.0.send(:-, 2))
assert_equal(0.0, 2.0.send(:-, (2**32).coerce(2).first))
assert_equal(0.0, 2.0.send(:-, 2.0))
+ assert_equal(-Float::INFINITY, 2.0.send(:-, Float::INFINITY))
+ assert_predicate(2.0.send(:-, Float::NAN), :nan?)
assert_raise(TypeError) { 2.0.send(:-, nil) }
end
@@ -167,6 +268,7 @@ class TestFloat < Test::Unit::TestCase
assert_equal(4.0, 2.0.send(:*, 2))
assert_equal(4.0, 2.0.send(:*, (2**32).coerce(2).first))
assert_equal(4.0, 2.0.send(:*, 2.0))
+ assert_equal(Float::INFINITY, 2.0.send(:*, Float::INFINITY))
assert_raise(TypeError) { 2.0.send(:*, nil) }
end
@@ -174,6 +276,7 @@ class TestFloat < Test::Unit::TestCase
assert_equal(1.0, 2.0.send(:/, 2))
assert_equal(1.0, 2.0.send(:/, (2**32).coerce(2).first))
assert_equal(1.0, 2.0.send(:/, 2.0))
+ assert_equal(0.0, 2.0.send(:/, Float::INFINITY))
assert_raise(TypeError) { 2.0.send(:/, nil) }
end
@@ -184,6 +287,24 @@ class TestFloat < Test::Unit::TestCase
assert_raise(TypeError) { 2.0.send(:%, nil) }
end
+ def test_modulo3
+ bug6048 = '[ruby-core:42726]'
+ assert_equal(4.2, 4.2.send(:%, Float::INFINITY), bug6048)
+ assert_equal(4.2, 4.2 % Float::INFINITY, bug6048)
+ assert_is_minus_zero(-0.0 % 4.2)
+ assert_is_minus_zero(-0.0.send :%, 4.2)
+ assert_raise(ZeroDivisionError, bug6048) { 4.2.send(:%, 0.0) }
+ assert_raise(ZeroDivisionError, bug6048) { 4.2 % 0.0 }
+ assert_raise(ZeroDivisionError, bug6048) { 42.send(:%, 0) }
+ assert_raise(ZeroDivisionError, bug6048) { 42 % 0 }
+ end
+
+ def test_modulo4
+ assert_predicate((0.0).modulo(Float::NAN), :nan?)
+ assert_predicate((1.0).modulo(Float::NAN), :nan?)
+ assert_predicate(Float::INFINITY.modulo(1), :nan?)
+ end
+
def test_divmod2
assert_equal([1.0, 0.0], 2.0.divmod(2))
assert_equal([1.0, 0.0], 2.0.divmod((2**32).coerce(2).first))
@@ -207,15 +328,15 @@ class TestFloat < Test::Unit::TestCase
def test_eql
inf = Float::INFINITY
nan = Float::NAN
- assert(1.0.eql?(1.0))
- assert(inf.eql?(inf))
- assert(!(nan.eql?(nan)))
- assert(!(1.0.eql?(nil)))
+ assert_operator(1.0, :eql?, 1.0)
+ assert_operator(inf, :eql?, inf)
+ assert_not_operator(nan, :eql?, nan)
+ assert_not_operator(1.0, :eql?, nil)
- assert(1.0 == 1)
- assert(1.0 != 2**32)
- assert(1.0 != nan)
- assert(1.0 != nil)
+ assert_equal(1.0, 1)
+ assert_not_equal(1.0, 2**32)
+ assert_not_equal(1.0, nan)
+ assert_not_equal(1.0, nil)
end
def test_cmp
@@ -239,6 +360,20 @@ class TestFloat < Test::Unit::TestCase
assert_equal(-1, (Float::MAX.to_i*2) <=> inf)
assert_equal(1, (-Float::MAX.to_i*2) <=> -inf)
+ bug3609 = '[ruby-core:31470]'
+ def (pinf = Object.new).infinite?; +1 end
+ def (ninf = Object.new).infinite?; -1 end
+ def (fin = Object.new).infinite?; nil end
+ nonum = Object.new
+ assert_equal(0, inf <=> pinf, bug3609)
+ assert_equal(1, inf <=> fin, bug3609)
+ assert_equal(1, inf <=> ninf, bug3609)
+ assert_nil(inf <=> nonum, bug3609)
+ assert_equal(-1, -inf <=> pinf, bug3609)
+ assert_equal(-1, -inf <=> fin, bug3609)
+ assert_equal(0, -inf <=> ninf, bug3609)
+ assert_nil(-inf <=> nonum, bug3609)
+
assert_raise(ArgumentError) { 1.0 > nil }
assert_raise(ArgumentError) { 1.0 >= nil }
assert_raise(ArgumentError) { 1.0 < nil }
@@ -246,8 +381,32 @@ class TestFloat < Test::Unit::TestCase
end
def test_zero_p
- assert(0.0.zero?)
- assert(!(1.0.zero?))
+ assert_predicate(0.0, :zero?)
+ assert_not_predicate(1.0, :zero?)
+ end
+
+ def test_positive_p
+ assert_predicate(+1.0, :positive?)
+ assert_not_predicate(+0.0, :positive?)
+ assert_not_predicate(-0.0, :positive?)
+ assert_not_predicate(-1.0, :positive?)
+ assert_predicate(+(0.0.next_float), :positive?)
+ assert_not_predicate(-(0.0.next_float), :positive?)
+ assert_predicate(Float::INFINITY, :positive?)
+ assert_not_predicate(-Float::INFINITY, :positive?)
+ assert_not_predicate(Float::NAN, :positive?)
+ end
+
+ def test_negative_p
+ assert_predicate(-1.0, :negative?)
+ assert_not_predicate(-0.0, :negative?)
+ assert_not_predicate(+0.0, :negative?)
+ assert_not_predicate(+1.0, :negative?)
+ assert_predicate(-(0.0.next_float), :negative?)
+ assert_not_predicate(+(0.0.next_float), :negative?)
+ assert_predicate(-Float::INFINITY, :negative?)
+ assert_not_predicate(Float::INFINITY, :negative?)
+ assert_not_predicate(Float::NAN, :negative?)
end
def test_infinite_p
@@ -259,9 +418,9 @@ class TestFloat < Test::Unit::TestCase
def test_finite_p
inf = Float::INFINITY
- assert(!(inf.finite?))
- assert(!((-inf).finite?))
- assert(1.0.finite?)
+ assert_not_predicate(inf, :finite?)
+ assert_not_predicate(-inf, :finite?)
+ assert_predicate(1.0, :finite?)
end
def test_floor_ceil_round_truncate
@@ -290,11 +449,119 @@ class TestFloat < Test::Unit::TestCase
assert_raise(FloatDomainError) { inf.ceil }
assert_raise(FloatDomainError) { inf.round }
assert_raise(FloatDomainError) { inf.truncate }
+ end
+ def test_round_with_precision
assert_equal(1.100, 1.111.round(1))
assert_equal(1.110, 1.111.round(2))
assert_equal(11110.0, 11111.1.round(-1))
assert_equal(11100.0, 11111.1.round(-2))
+ assert_equal(-1.100, -1.111.round(1))
+ assert_equal(-1.110, -1.111.round(2))
+ assert_equal(-11110.0, -11111.1.round(-1))
+ assert_equal(-11100.0, -11111.1.round(-2))
+ assert_equal(0, 11111.1.round(-5))
+
+ assert_equal(10**300, 1.1e300.round(-300))
+ assert_equal(-10**300, -1.1e300.round(-300))
+ assert_equal(1.0e-300, 1.1e-300.round(300))
+ assert_equal(-1.0e-300, -1.1e-300.round(300))
+
+ bug5227 = '[ruby-core:39093]'
+ assert_equal(42.0, 42.0.round(308), bug5227)
+ assert_equal(1.0e307, 1.0e307.round(2), bug5227)
+
+ assert_raise(TypeError) {1.0.round("4")}
+ assert_raise(TypeError) {1.0.round(nil)}
+ def (prec = Object.new).to_int; 2; end
+ assert_equal(1.0, 0.998.round(prec))
+
+ assert_equal(+5.02, +5.015.round(2))
+ assert_equal(-5.02, -5.015.round(2))
+ assert_equal(+1.26, +1.255.round(2))
+ assert_equal(-1.26, -1.255.round(2))
+ end
+
+ def test_floor_with_precision
+ assert_equal(+0.0, +0.001.floor(1))
+ assert_equal(-0.1, -0.001.floor(1))
+ assert_equal(1.100, 1.111.floor(1))
+ assert_equal(1.110, 1.111.floor(2))
+ assert_equal(11110, 11119.9.floor(-1))
+ assert_equal(11100, 11100.0.floor(-2))
+ assert_equal(11100, 11199.9.floor(-2))
+ assert_equal(-1.200, -1.111.floor(1))
+ assert_equal(-1.120, -1.111.floor(2))
+ assert_equal(-11120, -11119.9.floor(-1))
+ assert_equal(-11100, -11100.0.floor(-2))
+ assert_equal(-11200, -11199.9.floor(-2))
+ assert_equal(0, 11111.1.floor(-5))
+
+ assert_equal(10**300, 1.1e300.floor(-300))
+ assert_equal(-2*10**300, -1.1e300.floor(-300))
+ assert_equal(1.0e-300, 1.1e-300.floor(300))
+ assert_equal(-2.0e-300, -1.1e-300.floor(300))
+
+ assert_equal(42.0, 42.0.floor(308))
+ assert_equal(1.0e307, 1.0e307.floor(2))
+
+ assert_raise(TypeError) {1.0.floor("4")}
+ assert_raise(TypeError) {1.0.floor(nil)}
+ def (prec = Object.new).to_int; 2; end
+ assert_equal(0.99, 0.998.floor(prec))
+ end
+
+ def test_ceil_with_precision
+ assert_equal(+0.1, +0.001.ceil(1))
+ assert_equal(-0.0, -0.001.ceil(1))
+ assert_equal(1.200, 1.111.ceil(1))
+ assert_equal(1.120, 1.111.ceil(2))
+ assert_equal(11120, 11111.1.ceil(-1))
+ assert_equal(11200, 11111.1.ceil(-2))
+ assert_equal(-1.100, -1.111.ceil(1))
+ assert_equal(-1.110, -1.111.ceil(2))
+ assert_equal(-11110, -11111.1.ceil(-1))
+ assert_equal(-11100, -11111.1.ceil(-2))
+ assert_equal(100000, 11111.1.ceil(-5))
+
+ assert_equal(2*10**300, 1.1e300.ceil(-300))
+ assert_equal(-10**300, -1.1e300.ceil(-300))
+ assert_equal(2.0e-300, 1.1e-300.ceil(300))
+ assert_equal(-1.0e-300, -1.1e-300.ceil(300))
+
+ assert_equal(42.0, 42.0.ceil(308))
+ assert_equal(1.0e307, 1.0e307.ceil(2))
+
+ assert_raise(TypeError) {1.0.ceil("4")}
+ assert_raise(TypeError) {1.0.ceil(nil)}
+ def (prec = Object.new).to_int; 2; end
+ assert_equal(0.99, 0.981.ceil(prec))
+ end
+
+ def test_truncate_with_precision
+ assert_equal(1.100, 1.111.truncate(1))
+ assert_equal(1.110, 1.111.truncate(2))
+ assert_equal(11110, 11119.9.truncate(-1))
+ assert_equal(11100, 11100.0.truncate(-2))
+ assert_equal(11100, 11199.9.truncate(-2))
+ assert_equal(-1.100, -1.111.truncate(1))
+ assert_equal(-1.110, -1.111.truncate(2))
+ assert_equal(-11110, -11111.1.truncate(-1))
+ assert_equal(-11100, -11111.1.truncate(-2))
+ assert_equal(0, 11111.1.truncate(-5))
+
+ assert_equal(10**300, 1.1e300.truncate(-300))
+ assert_equal(-10**300, -1.1e300.truncate(-300))
+ assert_equal(1.0e-300, 1.1e-300.truncate(300))
+ assert_equal(-1.0e-300, -1.1e-300.truncate(300))
+
+ assert_equal(42.0, 42.0.truncate(308))
+ assert_equal(1.0e307, 1.0e307.truncate(2))
+
+ assert_raise(TypeError) {1.0.truncate("4")}
+ assert_raise(TypeError) {1.0.truncate(nil)}
+ def (prec = Object.new).to_int; 2; end
+ assert_equal(0.99, 0.998.truncate(prec))
end
VS = [
@@ -411,33 +678,187 @@ class TestFloat < Test::Unit::TestCase
def test_round
VS.each {|f|
+ msg = "round(#{f})"
i = f.round
if f < 0
- assert_operator(i, :<, 0)
+ assert_operator(i, :<, 0, msg)
else
- assert_operator(i, :>, 0)
+ assert_operator(i, :>, 0, msg)
end
d = f - i
- assert_operator(-0.5, :<=, d)
- assert_operator(d, :<=, 0.5)
+ assert_operator(-0.5, :<=, d, msg)
+ assert_operator(d, :<=, 0.5, msg)
+ }
+ end
+
+ def test_round_half_even
+ assert_equal(12.0, 12.5.round(half: :even))
+ assert_equal(14.0, 13.5.round(half: :even))
+
+ assert_equal(2.2, 2.15.round(1, half: :even))
+ assert_equal(2.2, 2.25.round(1, half: :even))
+ assert_equal(2.4, 2.35.round(1, half: :even))
+
+ assert_equal(-2.2, -2.15.round(1, half: :even))
+ assert_equal(-2.2, -2.25.round(1, half: :even))
+ assert_equal(-2.4, -2.35.round(1, half: :even))
+
+ assert_equal(7.1364, 7.13645.round(4, half: :even))
+ assert_equal(7.1365, 7.1364501.round(4, half: :even))
+ assert_equal(7.1364, 7.1364499.round(4, half: :even))
+
+ assert_equal(-7.1364, -7.13645.round(4, half: :even))
+ assert_equal(-7.1365, -7.1364501.round(4, half: :even))
+ assert_equal(-7.1364, -7.1364499.round(4, half: :even))
+ end
+
+ def test_round_half_up
+ assert_equal(13.0, 12.5.round(half: :up))
+ assert_equal(14.0, 13.5.round(half: :up))
+
+ assert_equal(2.2, 2.15.round(1, half: :up))
+ assert_equal(2.3, 2.25.round(1, half: :up))
+ assert_equal(2.4, 2.35.round(1, half: :up))
+
+ assert_equal(-2.2, -2.15.round(1, half: :up))
+ assert_equal(-2.3, -2.25.round(1, half: :up))
+ assert_equal(-2.4, -2.35.round(1, half: :up))
+
+ assert_equal(7.1365, 7.13645.round(4, half: :up))
+ assert_equal(7.1365, 7.1364501.round(4, half: :up))
+ assert_equal(7.1364, 7.1364499.round(4, half: :up))
+
+ assert_equal(-7.1365, -7.13645.round(4, half: :up))
+ assert_equal(-7.1365, -7.1364501.round(4, half: :up))
+ assert_equal(-7.1364, -7.1364499.round(4, half: :up))
+ end
+
+ def test_round_half_down
+ assert_equal(12.0, 12.5.round(half: :down))
+ assert_equal(13.0, 13.5.round(half: :down))
+
+ assert_equal(2.1, 2.15.round(1, half: :down))
+ assert_equal(2.2, 2.25.round(1, half: :down))
+ assert_equal(2.3, 2.35.round(1, half: :down))
+
+ assert_equal(-2.1, -2.15.round(1, half: :down))
+ assert_equal(-2.2, -2.25.round(1, half: :down))
+ assert_equal(-2.3, -2.35.round(1, half: :down))
+
+ assert_equal(7.1364, 7.13645.round(4, half: :down))
+ assert_equal(7.1365, 7.1364501.round(4, half: :down))
+ assert_equal(7.1364, 7.1364499.round(4, half: :down))
+
+ assert_equal(-7.1364, -7.13645.round(4, half: :down))
+ assert_equal(-7.1365, -7.1364501.round(4, half: :down))
+ assert_equal(-7.1364, -7.1364499.round(4, half: :down))
+ end
+
+ def test_round_half_nil
+ assert_equal(13.0, 12.5.round(half: nil))
+ assert_equal(14.0, 13.5.round(half: nil))
+
+ assert_equal(2.2, 2.15.round(1, half: nil))
+ assert_equal(2.3, 2.25.round(1, half: nil))
+ assert_equal(2.4, 2.35.round(1, half: nil))
+
+ assert_equal(-2.2, -2.15.round(1, half: nil))
+ assert_equal(-2.3, -2.25.round(1, half: nil))
+ assert_equal(-2.4, -2.35.round(1, half: nil))
+
+ assert_equal(7.1365, 7.13645.round(4, half: nil))
+ assert_equal(7.1365, 7.1364501.round(4, half: nil))
+ assert_equal(7.1364, 7.1364499.round(4, half: nil))
+
+ assert_equal(-7.1365, -7.13645.round(4, half: nil))
+ assert_equal(-7.1365, -7.1364501.round(4, half: nil))
+ assert_equal(-7.1364, -7.1364499.round(4, half: nil))
+ end
+
+ def test_round_half_invalid
+ assert_raise_with_message(ArgumentError, /Object/) {
+ 1.0.round(half: Object)
+ }
+ assert_raise_with_message(ArgumentError, /xxx/) {
+ 1.0.round(half: "\0xxx")
}
end
def test_Float
assert_in_delta(0.125, Float("0.1_2_5"), 0.00001)
assert_in_delta(0.125, "0.1_2_5__".to_f, 0.00001)
- assert_equal(1, Float(([1] * 10000).join).infinite?)
- assert(!Float(([1] * 10000).join("_")).infinite?) # is it really OK?
+ assert_in_delta(0.0, "0_.125".to_f, 0.00001)
+ assert_in_delta(0.0, "0._125".to_f, 0.00001)
+ assert_in_delta(0.1, "0.1__2_5".to_f, 0.00001)
+ assert_in_delta(0.1, "0.1_e10".to_f, 0.00001)
+ assert_in_delta(0.1, "0.1e_10".to_f, 0.00001)
+ assert_in_delta(1.0, "0.1e1__0".to_f, 0.00001)
+ assert_equal(1, suppress_warning {Float(([1] * 10000).join)}.infinite?)
+ assert_not_predicate(Float(([1] * 10000).join("_")), :infinite?) # is it really OK?
assert_raise(ArgumentError) { Float("1.0\x001") }
- assert_equal(1, Float("1e10_00").infinite?)
+ assert_equal(15.9375, Float('0xf.fp0'))
+ assert_raise(ArgumentError) { Float('0x') }
+ assert_equal(15, Float('0xf'))
+ assert_equal(15, Float('0xfp0'))
+ assert_raise(ArgumentError) { Float('0xfp') }
+ assert_raise(ArgumentError) { Float('0xf.') }
+ assert_raise(ArgumentError) { Float('0xf.p') }
+ assert_raise(ArgumentError) { Float('0xf.p0') }
+ assert_raise(ArgumentError) { Float('0xf.f') }
+ assert_raise(ArgumentError) { Float('0xf.fp') }
+ assert_equal(Float::INFINITY, Float('0xf.fp1000000000000000'))
+ assert_equal(1, suppress_warning {Float("1e10_00")}.infinite?)
assert_raise(TypeError) { Float(nil) }
+ assert_raise(TypeError) { Float(:test) }
o = Object.new
def o.to_f; inf = Float::INFINITY; inf/inf; end
- assert(Float(o).nan?)
+ assert_predicate(Float(o), :nan?)
+ end
+
+ def test_invalid_str
+ bug4310 = '[ruby-core:34820]'
+ assert_raise(ArgumentError, bug4310) {under_gc_stress {Float('a'*10000)}}
+ end
+
+ def test_Float_with_exception_keyword
+ assert_raise(ArgumentError) {
+ Float(".", exception: true)
+ }
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(nil, Float(".", exception: false))
+ }
+ assert_raise(RangeError) {
+ Float(1i, exception: true)
+ }
+ assert_nothing_raised(RangeError) {
+ assert_equal(nil, Float(1i, exception: false))
+ }
+ assert_raise(TypeError) {
+ Float(nil, exception: true)
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Float(nil, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Float(:test, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Float(Object.new, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ o = Object.new
+ def o.to_f; 3.14; end
+ assert_equal(3.14, Float(o, exception: false))
+ }
+ assert_nothing_raised(RuntimeError) {
+ o = Object.new
+ def o.to_f; raise; end
+ assert_equal(nil, Float(o, exception: false))
+ }
end
def test_num2dbl
- assert_raise(TypeError) do
+ assert_raise(ArgumentError, "comparison of String with 0 failed") do
1.0.step(2.0, "0.5") {}
end
assert_raise(TypeError) do
@@ -450,4 +871,112 @@ class TestFloat < Test::Unit::TestCase
sleep(0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1)
end
end
+
+ def test_step
+ 1000.times do
+ a = rand
+ b = a+rand*1000
+ s = (b - a) / 10
+ assert_equal(11, (a..b).step(s).to_a.length)
+ end
+
+ (1.0..12.7).step(1.3).each do |n|
+ assert_operator(n, :<=, 12.7)
+ end
+
+ assert_equal([5.0, 4.0, 3.0, 2.0], 5.0.step(1.5, -1).to_a)
+ end
+
+ def test_step2
+ assert_equal([0.0], 0.0.step(1.0, Float::INFINITY).to_a)
+ end
+
+ def test_step_excl
+ 1000.times do
+ a = rand
+ b = a+rand*1000
+ s = (b - a) / 10
+ assert_equal(10, (a...b).step(s).to_a.length)
+ end
+
+ assert_equal([1.0, 2.9, 4.8, 6.699999999999999], (1.0...6.8).step(1.9).to_a)
+
+ e = 1+1E-12
+ (1.0 ... e).step(1E-16) do |n|
+ assert_operator(n, :<=, e)
+ end
+ end
+
+ def test_singleton_method
+ # flonum on 64bit platform
+ assert_raise(TypeError) { a = 1.0; def a.foo; end }
+ # always not flonum
+ assert_raise(TypeError) { a = Float::INFINITY; def a.foo; end }
+ end
+
+ def test_long_string
+ assert_separately([], <<-'end;')
+ assert_in_epsilon(10.0, ("1."+"1"*300000).to_f*9)
+ end;
+ end
+
+ def test_next_float
+ smallest = 0.0.next_float
+ assert_equal(-Float::MAX, (-Float::INFINITY).next_float)
+ assert_operator(-Float::MAX, :<, (-Float::MAX).next_float)
+ assert_equal(Float::EPSILON/2, (-1.0).next_float + 1.0)
+ assert_operator(0.0, :<, smallest)
+ assert_operator([0.0, smallest], :include?, smallest/2)
+ assert_equal(Float::EPSILON, 1.0.next_float - 1.0)
+ assert_equal(Float::INFINITY, Float::MAX.next_float)
+ assert_equal(Float::INFINITY, Float::INFINITY.next_float)
+ assert_predicate(Float::NAN.next_float, :nan?)
+ end
+
+ def test_prev_float
+ smallest = 0.0.next_float
+ assert_equal(-Float::INFINITY, (-Float::INFINITY).prev_float)
+ assert_equal(-Float::INFINITY, (-Float::MAX).prev_float)
+ assert_equal(-Float::EPSILON, (-1.0).prev_float + 1.0)
+ assert_equal(-smallest, 0.0.prev_float)
+ assert_operator([0.0, 0.0.prev_float], :include?, 0.0.prev_float/2)
+ assert_equal(-Float::EPSILON/2, 1.0.prev_float - 1.0)
+ assert_operator(Float::MAX, :>, Float::MAX.prev_float)
+ assert_equal(Float::MAX, Float::INFINITY.prev_float)
+ assert_predicate(Float::NAN.prev_float, :nan?)
+ end
+
+ def test_next_prev_float_zero
+ z = 0.0.next_float.prev_float
+ assert_equal(0.0, z)
+ assert_equal(Float::INFINITY, 1.0/z)
+ z = 0.0.prev_float.next_float
+ assert_equal(0.0, z)
+ assert_equal(-Float::INFINITY, 1.0/z)
+ end
+
+ def test_hash_0
+ bug10979 = '[ruby-core:68541] [Bug #10979]'
+ assert_equal(+0.0.hash, -0.0.hash)
+ assert_operator(+0.0, :eql?, -0.0)
+ h = {0.0 => bug10979}
+ assert_equal(bug10979, h[-0.0])
+ end
+
+ def test_aliased_quo_recursion
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ class Float
+ $VERBOSE = nil
+ alias / quo
+ end
+ assert_raise(NameError) do
+ begin
+ 1.0/2.0
+ rescue SystemStackError => e
+ raise SystemStackError, e.message
+ end
+ end
+ end;
+ end
end
diff --git a/test/ruby/test_fnmatch.rb b/test/ruby/test_fnmatch.rb
index e5f5ba6a4f..16f1076e48 100644
--- a/test/ruby/test_fnmatch.rb
+++ b/test/ruby/test_fnmatch.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestFnmatch < Test::Unit::TestCase
@@ -9,98 +10,160 @@ class TestFnmatch < Test::Unit::TestCase
assert_equal(t.include?(i.chr), !File.fnmatch("[!#{s}]", i.chr, File::FNM_DOTMATCH))
end
end
+
def test_fnmatch
- assert(File.fnmatch('\[1\]' , '[1]'), "[ruby-dev:22819]")
- assert(File.fnmatch('*?', 'a'), "[ruby-dev:22815]")
- assert(File.fnmatch('*/', 'a/'))
- assert(File.fnmatch('\[1\]' , '[1]', File::FNM_PATHNAME))
- assert(File.fnmatch('*?', 'a', File::FNM_PATHNAME))
- assert(File.fnmatch('*/', 'a/', File::FNM_PATHNAME))
+ assert_file.for("[ruby-dev:22819]").fnmatch('\[1\]' , '[1]')
+ assert_file.for("[ruby-dev:22815]").fnmatch('*?', 'a')
+ assert_file.fnmatch('*/', 'a/')
+ assert_file.fnmatch('\[1\]' , '[1]', File::FNM_PATHNAME)
+ assert_file.fnmatch('*?', 'a', File::FNM_PATHNAME)
+ assert_file.fnmatch('*/', 'a/', File::FNM_PATHNAME)
+ end
+
+ def test_text
# text
- assert(File.fnmatch('cat', 'cat'))
- assert(!File.fnmatch('cat', 'category'))
- assert(!File.fnmatch('cat', 'wildcat'))
+ assert_file.fnmatch('cat', 'cat')
+ assert_file.not_fnmatch('cat', 'category')
+ assert_file.not_fnmatch('cat', 'wildcat')
+ end
+
+ def test_any_one
# '?' matches any one character
- assert(File.fnmatch('?at', 'cat'))
- assert(File.fnmatch('c?t', 'cat'))
- assert(File.fnmatch('ca?', 'cat'))
- assert(File.fnmatch('?a?', 'cat'))
- assert(!File.fnmatch('c??t', 'cat'))
- assert(!File.fnmatch('??at', 'cat'))
- assert(!File.fnmatch('ca??', 'cat'))
+ assert_file.fnmatch('?at', 'cat')
+ assert_file.fnmatch('c?t', 'cat')
+ assert_file.fnmatch('ca?', 'cat')
+ assert_file.fnmatch('?a?', 'cat')
+ assert_file.not_fnmatch('c??t', 'cat')
+ assert_file.not_fnmatch('??at', 'cat')
+ assert_file.not_fnmatch('ca??', 'cat')
+ end
+
+ def test_any_chars
# '*' matches any number (including 0) of any characters
- assert(File.fnmatch('c*', 'cats'))
- assert(File.fnmatch('c*ts', 'cats'))
- assert(File.fnmatch('*ts', 'cats'))
- assert(File.fnmatch('*c*a*t*s*', 'cats'))
- assert(!File.fnmatch('c*t', 'cats'))
- assert(!File.fnmatch('*abc', 'abcabz'))
- assert(File.fnmatch('*abz', 'abcabz'))
- assert(!File.fnmatch('a*abc', 'abc'))
- assert(File.fnmatch('a*bc', 'abc'))
- assert(!File.fnmatch('a*bc', 'abcd'))
+ assert_file.fnmatch('c*', 'cats')
+ assert_file.fnmatch('c*ts', 'cats')
+ assert_file.fnmatch('*ts', 'cats')
+ assert_file.fnmatch('*c*a*t*s*', 'cats')
+ assert_file.not_fnmatch('c*t', 'cats')
+ assert_file.not_fnmatch('*abc', 'abcabz')
+ assert_file.fnmatch('*abz', 'abcabz')
+ assert_file.not_fnmatch('a*abc', 'abc')
+ assert_file.fnmatch('a*bc', 'abc')
+ assert_file.not_fnmatch('a*bc', 'abcd')
+ end
+
+ def test_char_class
# [seq] : matches any character listed between bracket
# [!seq] or [^seq] : matches any character except those listed between bracket
bracket_test("bd-gikl-mosv-x", "bdefgiklmosvwx")
+ end
+
+ def test_escape
# escaping character
- assert(File.fnmatch('\?', '?'))
- assert(!File.fnmatch('\?', '\?'))
- assert(!File.fnmatch('\?', 'a'))
- assert(!File.fnmatch('\?', '\a'))
- assert(File.fnmatch('\*', '*'))
- assert(!File.fnmatch('\*', '\*'))
- assert(!File.fnmatch('\*', 'cats'))
- assert(!File.fnmatch('\*', '\cats'))
- assert(File.fnmatch('\a', 'a'))
- assert(!File.fnmatch('\a', '\a'))
- assert(File.fnmatch('[a\-c]', 'a'))
- assert(File.fnmatch('[a\-c]', '-'))
- assert(File.fnmatch('[a\-c]', 'c'))
- assert(!File.fnmatch('[a\-c]', 'b'))
- assert(!File.fnmatch('[a\-c]', '\\'))
+ assert_file.fnmatch('\?', '?')
+ assert_file.not_fnmatch('\?', '\?')
+ assert_file.not_fnmatch('\?', 'a')
+ assert_file.not_fnmatch('\?', '\a')
+ assert_file.fnmatch('\*', '*')
+ assert_file.not_fnmatch('\*', '\*')
+ assert_file.not_fnmatch('\*', 'cats')
+ assert_file.not_fnmatch('\*', '\cats')
+ assert_file.fnmatch('\a', 'a')
+ assert_file.not_fnmatch('\a', '\a')
+ assert_file.fnmatch('[a\-c]', 'a')
+ assert_file.fnmatch('[a\-c]', '-')
+ assert_file.fnmatch('[a\-c]', 'c')
+ assert_file.not_fnmatch('[a\-c]', 'b')
+ assert_file.not_fnmatch('[a\-c]', '\\')
+ end
+
+ def test_fnm_escape
# escaping character loses its meaning if FNM_NOESCAPE is set
- assert(!File.fnmatch('\?', '?', File::FNM_NOESCAPE))
- assert(File.fnmatch('\?', '\?', File::FNM_NOESCAPE))
- assert(!File.fnmatch('\?', 'a', File::FNM_NOESCAPE))
- assert(File.fnmatch('\?', '\a', File::FNM_NOESCAPE))
- assert(!File.fnmatch('\*', '*', File::FNM_NOESCAPE))
- assert(File.fnmatch('\*', '\*', File::FNM_NOESCAPE))
- assert(!File.fnmatch('\*', 'cats', File::FNM_NOESCAPE))
- assert(File.fnmatch('\*', '\cats', File::FNM_NOESCAPE))
- assert(!File.fnmatch('\a', 'a', File::FNM_NOESCAPE))
- assert(File.fnmatch('\a', '\a', File::FNM_NOESCAPE))
- assert(File.fnmatch('[a\-c]', 'a', File::FNM_NOESCAPE))
- assert(!File.fnmatch('[a\-c]', '-', File::FNM_NOESCAPE))
- assert(File.fnmatch('[a\-c]', 'c', File::FNM_NOESCAPE))
- assert(File.fnmatch('[a\-c]', 'b', File::FNM_NOESCAPE)) # '\\' < 'b' < 'c'
- assert(File.fnmatch('[a\-c]', '\\', File::FNM_NOESCAPE))
+ assert_file.not_fnmatch('\?', '?', File::FNM_NOESCAPE)
+ assert_file.fnmatch('\?', '\?', File::FNM_NOESCAPE)
+ assert_file.not_fnmatch('\?', 'a', File::FNM_NOESCAPE)
+ assert_file.fnmatch('\?', '\a', File::FNM_NOESCAPE)
+ assert_file.not_fnmatch('\*', '*', File::FNM_NOESCAPE)
+ assert_file.fnmatch('\*', '\*', File::FNM_NOESCAPE)
+ assert_file.not_fnmatch('\*', 'cats', File::FNM_NOESCAPE)
+ assert_file.fnmatch('\*', '\cats', File::FNM_NOESCAPE)
+ assert_file.not_fnmatch('\a', 'a', File::FNM_NOESCAPE)
+ assert_file.fnmatch('\a', '\a', File::FNM_NOESCAPE)
+ assert_file.fnmatch('[a\-c]', 'a', File::FNM_NOESCAPE)
+ assert_file.not_fnmatch('[a\-c]', '-', File::FNM_NOESCAPE)
+ assert_file.fnmatch('[a\-c]', 'c', File::FNM_NOESCAPE)
+ assert_file.fnmatch('[a\-c]', 'b', File::FNM_NOESCAPE) # '\\' < 'b' < 'c'
+ assert_file.fnmatch('[a\-c]', '\\', File::FNM_NOESCAPE)
+ end
+
+ def test_fnm_casefold
# case is ignored if FNM_CASEFOLD is set
- assert(!File.fnmatch('cat', 'CAT'))
- assert(File.fnmatch('cat', 'CAT', File::FNM_CASEFOLD))
- assert(!File.fnmatch('[a-z]', 'D'))
- assert(File.fnmatch('[a-z]', 'D', File::FNM_CASEFOLD))
- assert(!File.fnmatch('[abc]', 'B'))
- assert(File.fnmatch('[abc]', 'B', File::FNM_CASEFOLD))
+ assert_file.not_fnmatch('cat', 'CAT')
+ assert_file.fnmatch('cat', 'CAT', File::FNM_CASEFOLD)
+ assert_file.not_fnmatch('[a-z]', 'D')
+ assert_file.fnmatch('[a-z]', 'D', File::FNM_CASEFOLD)
+ assert_file.not_fnmatch('[abc]', 'B')
+ assert_file.fnmatch('[abc]', 'B', File::FNM_CASEFOLD)
+ end
+
+ def test_fnm_pathname
# wildcard doesn't match '/' if FNM_PATHNAME is set
- assert(File.fnmatch('foo?boo', 'foo/boo'))
- assert(File.fnmatch('foo*', 'foo/boo'))
- assert(!File.fnmatch('foo?boo', 'foo/boo', File::FNM_PATHNAME))
- assert(!File.fnmatch('foo*', 'foo/boo', File::FNM_PATHNAME))
+ assert_file.fnmatch('foo?boo', 'foo/boo')
+ assert_file.fnmatch('foo*', 'foo/boo')
+ assert_file.not_fnmatch('foo?boo', 'foo/boo', File::FNM_PATHNAME)
+ assert_file.not_fnmatch('foo*', 'foo/boo', File::FNM_PATHNAME)
+ end
+
+ def test_fnm_dotmatch
# wildcard matches leading period if FNM_DOTMATCH is set
- assert(!File.fnmatch('*', '.profile'))
- assert(File.fnmatch('*', '.profile', File::FNM_DOTMATCH))
- assert(File.fnmatch('.*', '.profile'))
- assert(File.fnmatch('*', 'dave/.profile'))
- assert(File.fnmatch('*/*', 'dave/.profile'))
- assert(!File.fnmatch('*/*', 'dave/.profile', File::FNM_PATHNAME))
- assert(File.fnmatch('*/*', 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH))
+ assert_file.not_fnmatch('*', '.profile')
+ assert_file.fnmatch('*', '.profile', File::FNM_DOTMATCH)
+ assert_file.fnmatch('.*', '.profile')
+ assert_file.fnmatch('*', 'dave/.profile')
+ assert_file.fnmatch('*/*', 'dave/.profile')
+ assert_file.not_fnmatch('*/*', 'dave/.profile', File::FNM_PATHNAME)
+ assert_file.fnmatch('*/*', 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH)
+ end
+
+ def test_recursive
# recursive matching
- assert(File.fnmatch('**/foo', 'a/b/c/foo', File::FNM_PATHNAME))
- assert(File.fnmatch('**/foo', '/foo', File::FNM_PATHNAME))
- assert(!File.fnmatch('**/foo', 'a/.b/c/foo', File::FNM_PATHNAME))
- assert(File.fnmatch('**/foo', 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH))
- assert(File.fnmatch('**/foo', '/root/foo', File::FNM_PATHNAME))
- assert(File.fnmatch('**/foo', 'c:/root/foo', File::FNM_PATHNAME))
+ assert_file.fnmatch('**/foo', 'a/b/c/foo', File::FNM_PATHNAME)
+ assert_file.fnmatch('**/foo', '/foo', File::FNM_PATHNAME)
+ assert_file.not_fnmatch('**/foo', 'a/.b/c/foo', File::FNM_PATHNAME)
+ assert_file.fnmatch('**/foo', 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH)
+ assert_file.fnmatch('**/foo', '/root/foo', File::FNM_PATHNAME)
+ assert_file.fnmatch('**/foo', 'c:/root/foo', File::FNM_PATHNAME)
+ end
+
+ def test_extglob
+ feature5422 = '[ruby-core:40037]'
+ assert_file.for(feature5422).not_fnmatch?( "{.g,t}*", ".gem")
+ assert_file.for(feature5422).fnmatch?("{.g,t}*", ".gem", File::FNM_EXTGLOB)
+ end
+
+ def test_unmatched_encoding
+ bug7911 = '[ruby-dev:47069] [Bug #7911]'
+ path = "\u{3042}"
+ pattern_ascii = 'a'.encode('US-ASCII')
+ pattern_eucjp = path.encode('EUC-JP')
+ assert_nothing_raised(ArgumentError, bug7911) do
+ assert_file.not_fnmatch(pattern_ascii, path)
+ assert_file.not_fnmatch(pattern_eucjp, path)
+ assert_file.not_fnmatch(pattern_ascii, path, File::FNM_CASEFOLD)
+ assert_file.not_fnmatch(pattern_eucjp, path, File::FNM_CASEFOLD)
+ assert_file.fnmatch("{*,#{pattern_ascii}}", path, File::FNM_EXTGLOB)
+ assert_file.fnmatch("{*,#{pattern_eucjp}}", path, File::FNM_EXTGLOB)
+ end
end
+ def test_unicode
+ assert_file.fnmatch("[a-\u3042]*", "\u3042")
+ assert_file.not_fnmatch("[a-\u3042]*", "\u3043")
+ end
+
+ def test_nullchar
+ assert_raise(ArgumentError) {
+ File.fnmatch("a\0z", "a")
+ }
+ end
end
diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb
index 1bd3df4c1a..7a6309b6a3 100644
--- a/test/ruby/test_gc.rb
+++ b/test/ruby/test_gc.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestGc < Test::Unit::TestCase
@@ -12,10 +13,10 @@ class TestGc < Test::Unit::TestCase
GC.stress = false
assert_nothing_raised do
+ tmp = nil
1.upto(10000) {
tmp = [0,1,2,3,4,5,6,7,8,9]
}
- tmp = nil
end
l=nil
100000.times {
@@ -33,6 +34,10 @@ class TestGc < Test::Unit::TestCase
GC.stress = prev_stress
end
+ def use_rgengc?
+ GC::OPTS.include? 'USE_RGENGC'.freeze
+ end
+
def test_enable_disable
GC.enable
assert_equal(false, GC.enable)
@@ -46,9 +51,399 @@ class TestGc < Test::Unit::TestCase
GC.enable
end
+ def test_start_full_mark
+ return unless use_rgengc?
+
+ GC.start(full_mark: false)
+ assert_nil GC.latest_gc_info(:major_by)
+
+ GC.start(full_mark: true)
+ assert_not_nil GC.latest_gc_info(:major_by)
+ end
+
+ def test_start_immediate_sweep
+ GC.start(immediate_sweep: false)
+ assert_equal false, GC.latest_gc_info(:immediate_sweep)
+
+ GC.start(immediate_sweep: true)
+ assert_equal true, GC.latest_gc_info(:immediate_sweep)
+ end
+
def test_count
c = GC.count
GC.start
assert_operator(c, :<, GC.count)
end
+
+ def test_stat
+ res = GC.stat
+ assert_equal(false, res.empty?)
+ assert_kind_of(Integer, res[:count])
+
+ arg = Hash.new
+ res = GC.stat(arg)
+ assert_equal(arg, res)
+ assert_equal(false, res.empty?)
+ assert_kind_of(Integer, res[:count])
+
+ stat, count = {}, {}
+ GC.start
+ 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)
+ assert_equal(count[:FREE], stat[:heap_free_slots])
+ end
+
+ def test_stat_argument
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {GC.stat(:"\u{30eb 30d3 30fc}")}
+ end
+
+ def test_stat_single
+ stat = GC.stat
+ assert_equal stat[:count], GC.stat(:count)
+ assert_raise(ArgumentError){ GC.stat(:invalid) }
+ end
+
+ def test_stat_constraints
+ stat = GC.stat
+ assert_equal stat[:total_allocated_pages], stat[:heap_allocated_pages] + stat[:total_freed_pages]
+ assert_operator stat[:heap_sorted_length], :>=, stat[:heap_eden_pages] + stat[:heap_allocatable_pages], "stat is: " + stat.inspect
+ assert_equal stat[:heap_available_slots], stat[:heap_live_slots] + stat[:heap_free_slots] + stat[:heap_final_slots]
+ assert_equal stat[:heap_live_slots], stat[:total_allocated_objects] - stat[:total_freed_objects] - stat[:heap_final_slots]
+ assert_equal stat[:heap_allocated_pages], stat[:heap_eden_pages] + stat[:heap_tomb_pages]
+
+ if use_rgengc?
+ assert_equal stat[:count], stat[:major_gc_count] + stat[:minor_gc_count]
+ end
+ end
+
+ def test_latest_gc_info
+ assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom'
+ GC.start
+ count = GC.stat(:heap_free_slots) + GC.stat(:heap_allocatable_pages) * GC::INTERNAL_CONSTANTS[:HEAP_PAGE_OBJ_LIMIT]
+ count.times{ "a" + "b" }
+ assert_equal :newobj, GC.latest_gc_info[:gc_by]
+ eom
+
+ 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.stress = true
+ assert_equal :force, GC.latest_gc_info[:major_by]
+ ensure
+ GC.stress = false
+ end
+
+ def test_latest_gc_info_argument
+ info = {}
+ GC.latest_gc_info(info)
+
+ assert_not_empty info
+ assert_equal info[:gc_by], GC.latest_gc_info(:gc_by)
+ assert_raise(ArgumentError){ GC.latest_gc_info(:invalid) }
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {GC.latest_gc_info(:"\u{30eb 30d3 30fc}")}
+ end
+
+ def test_singleton_method
+ assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "[ruby-dev:42832]")
+ GC.stress = true
+ 10.times do
+ obj = Object.new
+ def obj.foo() end
+ def obj.bar() raise "obj.foo is called, but this is obj.bar" end
+ obj.foo
+ end
+ EOS
+ end
+
+ def test_singleton_method_added
+ assert_in_out_err(%w[--disable-gems], <<-EOS, [], [], "[ruby-dev:44436]")
+ class BasicObject
+ undef singleton_method_added
+ def singleton_method_added(mid)
+ raise
+ end
+ end
+ b = proc {}
+ class << b; end
+ b.clone rescue nil
+ GC.start
+ EOS
+ end
+
+ def test_gc_parameter
+ env = {
+ "RUBY_GC_MALLOC_LIMIT" => "60000000",
+ "RUBY_GC_HEAP_INIT_SLOTS" => "100000"
+ }
+ assert_normal_exit("exit", "[ruby-core:39777]", :child_env => env)
+
+ env = {
+ "RUBYOPT" => "",
+ "RUBY_GC_HEAP_INIT_SLOTS" => "100000"
+ }
+ assert_in_out_err([env, "-e", "exit"], "", [], [], "[ruby-core:39795]")
+ assert_in_out_err([env, "-W0", "-e", "exit"], "", [], [], "[ruby-core:39795]")
+ assert_in_out_err([env, "-W1", "-e", "exit"], "", [], [], "[ruby-core:39795]")
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_INIT_SLOTS=100000/, "[ruby-core:39795]")
+
+ env = {
+ "RUBY_GC_HEAP_GROWTH_FACTOR" => "2.0",
+ "RUBY_GC_HEAP_GROWTH_MAX_SLOTS" => "10000"
+ }
+ assert_normal_exit("exit", "", :child_env => env)
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_GROWTH_FACTOR=2.0/, "")
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_GROWTH_MAX_SLOTS=10000/, "[ruby-core:57928]")
+
+ env = {
+ "RUBY_GC_HEAP_INIT_SLOTS" => "100000",
+ "RUBY_GC_HEAP_FREE_SLOTS" => "10000",
+ "RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR" => "0.9",
+ }
+ assert_normal_exit("exit", "", :child_env => env)
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=0\.9/, "")
+
+ # 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",
+ "RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR" => "2.0"
+ }
+ assert_normal_exit("exit", "", :child_env => env)
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_MALLOC_LIMIT=6000000/, "")
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_MALLOC_LIMIT_MAX=16000000/, "")
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR=2.0/, "")
+
+ if use_rgengc?
+ env = {
+ "RUBY_GC_OLDMALLOC_LIMIT" => "60000000",
+ "RUBY_GC_OLDMALLOC_LIMIT_MAX" => "160000000",
+ "RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR" => "2.0"
+ }
+ assert_normal_exit("exit", "", :child_env => env)
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_OLDMALLOC_LIMIT=6000000/, "")
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_OLDMALLOC_LIMIT_MAX=16000000/, "")
+ assert_in_out_err([env, "-w", "-e", "exit"], "", [], /RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR=2.0/, "")
+ end
+ end
+
+ def test_profiler_enabled
+ GC::Profiler.enable
+ assert_equal(true, GC::Profiler.enabled?)
+ GC::Profiler.disable
+ assert_equal(false, GC::Profiler.enabled?)
+ ensure
+ GC::Profiler.disable
+ end
+
+ def test_profiler_clear
+ assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom', timeout: 30
+ GC::Profiler.enable
+
+ GC.start
+ assert_equal(1, GC::Profiler.raw_data.size)
+ GC::Profiler.clear
+ assert_equal(0, GC::Profiler.raw_data.size)
+
+ 200.times{ GC.start }
+ assert_equal(200, GC::Profiler.raw_data.size)
+ GC::Profiler.clear
+ assert_equal(0, GC::Profiler.raw_data.size)
+ eom
+ end
+
+ def test_profiler_total_time
+ GC::Profiler.enable
+ GC::Profiler.clear
+
+ GC.start
+ assert_operator(GC::Profiler.total_time, :>=, 0)
+ ensure
+ GC::Profiler.disable
+ end
+
+ def test_finalizing_main_thread
+ assert_in_out_err(%w[--disable-gems], <<-EOS, ["\"finalize\""], [], "[ruby-dev:46647]")
+ ObjectSpace.define_finalizer(Thread.main) { p 'finalize' }
+ EOS
+ end
+
+ def test_expand_heap
+ assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom'
+ GC.start
+ base_length = GC.stat[:heap_eden_pages]
+ (base_length * 500).times{ 'a' }
+ GC.start
+ base_length = GC.stat[:heap_eden_pages]
+ (base_length * 500).times{ 'a' }
+ GC.start
+ assert_in_epsilon base_length, (v = GC.stat[:heap_eden_pages]), 1/8r,
+ "invalid heap expanding (base_length: #{base_length}, GC.stat[:heap_eden_pages]: #{v})"
+
+ a = []
+ (base_length * 500).times{ a << 'a'; nil }
+ GC.start
+ assert_operator base_length, :<, GC.stat[:heap_eden_pages] + 1
+ eom
+ end
+
+ def test_gc_internals
+ assert_not_nil GC::INTERNAL_CONSTANTS[:HEAP_PAGE_OBJ_LIMIT]
+ assert_not_nil GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
+ end
+
+ def test_sweep_in_finalizer
+ bug9205 = '[ruby-core:58833] [Bug #9205]'
+ 2.times do
+ assert_ruby_status([], <<-'end;', bug9205, timeout: 60)
+ raise_proc = proc do |id|
+ GC.start
+ end
+ 1000.times do
+ ObjectSpace.define_finalizer(Object.new, raise_proc)
+ end
+ end;
+ end
+ end
+
+ def test_exception_in_finalizer
+ bug9168 = '[ruby-core:58652] [Bug #9168]'
+ assert_normal_exit(<<-'end;', bug9168, encoding: Encoding::ASCII_8BIT)
+ raise_proc = proc {raise}
+ 10000.times do
+ ObjectSpace.define_finalizer(Object.new, raise_proc)
+ Thread.handle_interrupt(RuntimeError => :immediate) {break}
+ Thread.handle_interrupt(RuntimeError => :on_blocking) {break}
+ Thread.handle_interrupt(RuntimeError => :never) {break}
+ end
+ end;
+ end
+
+ def test_interrupt_in_finalizer
+ bug10595 = '[ruby-core:66825] [Bug #10595]'
+ src = <<-'end;'
+ Signal.trap(:INT, 'DEFAULT')
+ pid = $$
+ Thread.start do
+ 10.times {
+ sleep 0.1
+ Process.kill("INT", pid) rescue break
+ }
+ end
+ f = proc {1000.times {}}
+ loop do
+ ObjectSpace.define_finalizer(Object.new, f)
+ end
+ end;
+ out, err, status = assert_in_out_err(["-e", src], "", [], [], bug10595, signal: :SEGV) do |*result|
+ break result
+ end
+ unless /mswin|mingw/ =~ RUBY_PLATFORM
+ assert_equal("INT", Signal.signame(status.termsig), bug10595)
+ end
+ assert_match(/Interrupt/, err.first, proc {err.join("\n")})
+ assert_empty(out)
+ end
+
+ def test_verify_internal_consistency
+ assert_nil(GC.verify_internal_consistency)
+ end
+
+ def test_gc_stress_on_realloc
+ assert_normal_exit(<<-'end;', '[Bug #9859]')
+ class C
+ def initialize
+ @a = nil
+ @b = nil
+ @c = nil
+ @d = nil
+ @e = nil
+ @f = nil
+ end
+ end
+
+ GC.stress = true
+ C.new
+ end;
+ end
+
+ def test_gc_disabled_start
+ begin
+ disabled = GC.disable
+ c = GC.count
+ GC.start
+ assert_equal 1, GC.count - c
+ ensure
+ GC.enable unless disabled
+ end
+ end
+
+ def test_vm_object
+ assert_normal_exit <<-'end', '[Bug #12583]'
+ ObjectSpace.each_object{|o| o.singleton_class rescue 0}
+ ObjectSpace.each_object{|o| case o when Module then o.instance_methods end}
+ end
+ end
+
+ def test_exception_in_finalizer_procs
+ result = []
+ c1 = proc do
+ result << :c1
+ raise
+ end
+ c2 = proc do
+ result << :c2
+ raise
+ end
+ tap {
+ tap {
+ 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
+
+ def test_exception_in_finalizer_method
+ @result = []
+ def self.c1(x)
+ @result << :c1
+ raise
+ end
+ def self.c2(x)
+ @result << :c2
+ raise
+ end
+ tap {
+ tap {
+ 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
diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb
index 782edc9a0c..e1b6e7257e 100644
--- a/test/ruby/test_hash.rb
+++ b/test/ruby/test_hash.rb
@@ -1,11 +1,13 @@
+# -*- coding: us-ascii -*-
+# frozen_string_literal: false
require 'test/unit'
-require 'continuation'
+EnvUtil.suppress_warning {require 'continuation'}
class TestHash < Test::Unit::TestCase
def test_hash
- x = {1=>2, 2=>4, 3=>6}
- y = {1=>2, 2=>4, 3=>6} # y = {1, 2, 2, 4, 3, 6} # 1.9 doesn't support
+ x = @cls[1=>2, 2=>4, 3=>6]
+ y = @cls[1=>2, 2=>4, 3=>6] # y = {1, 2, 2, 4, 3, 6} # 1.9 doesn't support
assert_equal(2, x[1])
@@ -19,8 +21,8 @@ class TestHash < Test::Unit::TestCase
end)
assert_equal(3, x.length)
- assert(x.has_key?(1))
- assert(x.has_value?(4))
+ assert_send([x, :has_key?, 1])
+ assert_send([x, :has_value?, 4])
assert_equal([4,6], x.values_at(2,3))
assert_equal({1=>2, 2=>4, 3=>6}, x)
@@ -77,7 +79,7 @@ class TestHash < Test::Unit::TestCase
# From rubicon
def setup
- @cls = Hash
+ @cls ||= Hash
@h = @cls[
1 => 'one', 2 => 'two', 3 => 'three',
self => 'self', true => 'true', nil => 'nil',
@@ -91,6 +93,36 @@ class TestHash < Test::Unit::TestCase
$VERBOSE = @verbose
end
+ def test_bad_initialize_copy
+ h = Class.new(Hash) {
+ def initialize_copy(h)
+ super(Object.new)
+ end
+ }.new
+ assert_raise(TypeError) { h.dup }
+ end
+
+ def test_clear_initialize_copy
+ h = @cls[1=>2]
+ h.instance_eval {initialize_copy({})}
+ assert_empty(h)
+ end
+
+ def test_self_initialize_copy
+ h = @cls[1=>2]
+ h.instance_eval {initialize_copy(h)}
+ assert_equal(2, h[1])
+ end
+
+ def test_dup_will_rehash
+ set1 = @cls[]
+ set2 = @cls[set1 => true]
+
+ set1[set1] = true
+
+ assert_equal set2, set2.dup
+ end
+
def test_s_AREF
h = @cls["a" => 100, "b" => 200]
assert_equal(100, h['a'])
@@ -101,6 +133,50 @@ class TestHash < Test::Unit::TestCase
assert_equal(100, h['a'])
assert_equal(200, h['b'])
assert_nil(h['c'])
+
+ h = @cls["a", 100, "b", 200]
+ assert_equal(100, h['a'])
+ assert_equal(200, h['b'])
+ assert_nil(h['c'])
+
+ h = @cls[[["a", 100], ["b", 200]]]
+ assert_equal(100, h['a'])
+ assert_equal(200, h['b'])
+ assert_nil(h['c'])
+
+ h = @cls[[["a", 100], ["b"], ["c", 300]]]
+ assert_equal(100, h['a'])
+ assert_equal(nil, h['b'])
+ assert_equal(300, h['c'])
+
+ h = @cls[[["a", 100], "b", ["c", 300]]]
+ assert_equal(100, h['a'])
+ assert_equal(nil, h['b'])
+ assert_equal(300, h['c'])
+ end
+
+ def test_s_AREF_duplicated_key
+ alist = [["a", 100], ["b", 200], ["a", 300], ["a", 400]]
+ h = @cls[alist]
+ 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'))
+ end
+
+ def test_s_AREF_frozen_key_id
+ key = "a".freeze
+ h = @cls[key, 100]
+ assert_equal(100, h['a'])
+ assert_same(key, *h.keys)
+ end
+
+ def test_s_AREF_key_tampering
+ key = "a".dup
+ h = @cls[key, 100]
+ key.upcase!
+ assert_equal(100, h['a'])
end
def test_s_new
@@ -113,7 +189,14 @@ class TestHash < Test::Unit::TestCase
assert_instance_of(@cls, h)
assert_equal('default', h.default)
assert_equal('default', h['spurious'])
+ end
+ def test_try_convert
+ assert_equal({1=>2}, Hash.try_convert({1=>2}))
+ assert_equal(nil, Hash.try_convert("1=>2"))
+ o = Object.new
+ def o.to_hash; {3=>4} end
+ assert_equal({3=>4}, Hash.try_convert(o))
end
def test_AREF # '[]'
@@ -183,22 +266,88 @@ class TestHash < Test::Unit::TestCase
assert_equal(256, h[z])
end
+ def test_AREF_fstring_key
+ h = {"abc" => 1}
+ before = GC.stat(:total_allocated_objects)
+ 5.times{ h["abc"] }
+ assert_equal before, GC.stat(:total_allocated_objects)
+ end
+
+ def test_ASET_fstring_key
+ a, b = {}, {}
+ assert_equal 1, a["abc"] = 1
+ assert_equal 1, b["abc"] = 1
+ assert_same a.keys[0], b.keys[0]
+ end
+
+ def test_ASET_fstring_non_literal_key
+ underscore = "_"
+ non_literal_strings = Proc.new{ ["abc#{underscore}def", "abc" * 5, "abc" + "def", "" << "ghi" << "jkl"] }
+
+ a, b = {}, {}
+ non_literal_strings.call.each do |string|
+ assert_equal 1, a[string] = 1
+ end
+
+ non_literal_strings.call.each do |string|
+ assert_equal 1, b[string] = 1
+ end
+
+ [a.keys, b.keys].transpose.each do |key_a, key_b|
+ assert_same key_a, key_b
+ end
+ end
+
+ def test_hash_aset_fstring_identity
+ h = {}.compare_by_identity
+ h['abc'] = 1
+ h['abc'] = 2
+ assert_equal 2, h.size, '[ruby-core:78783] [Bug #12855]'
+ end
+
+ def test_hash_aref_fstring_identity
+ h = {}.compare_by_identity
+ h['abc'] = 1
+ assert_nil h['abc'], '[ruby-core:78783] [Bug #12855]'
+ end
+
+ def test_NEWHASH_fstring_key
+ a = {"ABC" => :t}
+ b = {"ABC" => :t}
+ assert_same a.keys[0], b.keys[0]
+ assert_same "ABC".freeze, a.keys[0]
+ var = +'ABC'
+ c = { var => :t }
+ assert_same "ABC".freeze, c.keys[0]
+ end
+
+ def test_tainted_string_key
+ str = 'str'.taint
+ h = {}
+ h[str] = nil
+ key = h.keys.first
+ assert_predicate str, :tainted?
+ assert_not_predicate str, :frozen?
+ assert_predicate key, :tainted?
+ assert_predicate key, :frozen?
+ end
+
def test_EQUAL # '=='
h1 = @cls[ "a" => 1, "c" => 2 ]
h2 = @cls[ "a" => 1, "c" => 2, 7 => 35 ]
h3 = @cls[ "a" => 1, "c" => 2, 7 => 35 ]
h4 = @cls[ ]
- assert(h1 == h1)
- assert(h2 == h2)
- assert(h3 == h3)
- assert(h4 == h4)
- assert(!(h1 == h2))
- assert(h2 == h3)
- assert(!(h3 == h4))
+ assert_equal(h1, h1)
+ assert_equal(h2, h2)
+ assert_equal(h3, h3)
+ assert_equal(h4, h4)
+ assert_not_equal(h1, h2)
+ assert_equal(h2, h3)
+ assert_not_equal(h3, h4)
end
def test_clear
- assert(@h.size > 0)
+ assert_operator(@h.size, :>, 0)
@h.clear
assert_equal(0, @h.size)
assert_nil(@h[1])
@@ -206,20 +355,16 @@ class TestHash < Test::Unit::TestCase
def test_clone
for taint in [ false, true ]
- for untrust in [ false, true ]
- for frozen in [ false, true ]
- a = @h.clone
- a.taint if taint
- a.untrust if untrust
- a.freeze if frozen
- b = a.clone
-
- assert_equal(a, b)
- assert(a.__id__ != b.__id__)
- assert_equal(a.frozen?, b.frozen?)
- assert_equal(a.untrusted?, b.untrusted?)
- assert_equal(a.tainted?, b.tainted?)
- end
+ for frozen in [ false, true ]
+ a = @h.clone
+ a.taint if taint
+ a.freeze if frozen
+ b = a.clone
+
+ assert_equal(a, b)
+ assert_not_same(a, b)
+ assert_equal(a.frozen?, b.frozen?)
+ assert_equal(a.tainted?, b.tainted?)
end
end
end
@@ -291,31 +436,46 @@ class TestHash < Test::Unit::TestCase
end
def test_keep_if
- h = {1=>2,3=>4,5=>6}
+ h = @cls[1=>2,3=>4,5=>6]
assert_equal({3=>4,5=>6}, h.keep_if {|k, v| k + v >= 7 })
- h = {1=>2,3=>4,5=>6}
+ h = @cls[1=>2,3=>4,5=>6]
assert_equal({1=>2,3=>4,5=>6}, h.keep_if{true})
end
+ def test_compact
+ h = @cls[a: 1, b: nil, c: false, d: true, e: nil]
+ assert_equal({a: 1, c: false, d: true}, h.compact)
+ assert_equal({a: 1, b: nil, c: false, d: true, e: nil}, h)
+ assert_same(h, h.compact!)
+ assert_equal({a: 1, c: false, d: true}, h)
+ assert_nil(h.compact!)
+ end
+
def test_dup
for taint in [ false, true ]
- for untrust in [ false, true ]
- for frozen in [ false, true ]
- a = @h.dup
- a.taint if taint
- a.freeze if frozen
- b = a.dup
-
- assert_equal(a, b)
- assert(a.__id__ != b.__id__)
- assert_equal(false, b.frozen?)
- assert_equal(a.tainted?, b.tainted?)
- assert_equal(a.untrusted?, b.untrusted?)
- end
+ for frozen in [ false, true ]
+ a = @h.dup
+ a.taint if taint
+ a.freeze if frozen
+ b = a.dup
+
+ assert_equal(a, b)
+ assert_not_same(a, b)
+ assert_equal(false, b.frozen?)
+ assert_equal(a.tainted?, b.tainted?)
end
end
end
+ def test_dup_equality
+ h = @cls['k' => 'v']
+ assert_equal(h, h.dup)
+ h1 = @cls[h => 1]
+ assert_equal(h1, h1.dup)
+ h[1] = 2
+ assert_equal(h1, h1.dup)
+ end
+
def test_each
count = 0
@cls[].each { |k, v| count + 1 }
@@ -326,6 +486,11 @@ class TestHash < Test::Unit::TestCase
assert_equal(v, h.delete(k))
end
assert_equal(@cls[], h)
+
+ h = @cls[]
+ h[1] = 1
+ h[2] = 2
+ assert_equal([[1,1],[2,2]], h.each.to_a)
end
def test_each_key
@@ -368,13 +533,11 @@ class TestHash < Test::Unit::TestCase
end
def test_empty?
- assert(@cls[].empty?)
- assert(!@h.empty?)
+ assert_empty(@cls[])
+ assert_not_empty(@h)
end
def test_fetch
- assert_raise(KeyError) { @cls[].fetch(1) }
- assert_raise(KeyError) { @h.fetch('gumby') }
assert_equal('gumbygumby', @h.fetch('gumby') {|k| k * 2 })
assert_equal('pokey', @h.fetch('gumby', 'pokey'))
@@ -383,28 +546,38 @@ class TestHash < Test::Unit::TestCase
assert_equal('nil', @h.fetch(nil))
end
+ def test_fetch_error
+ assert_raise(KeyError) { @cls[].fetch(1) }
+ assert_raise(KeyError) { @h.fetch('gumby') }
+ e = assert_raise(KeyError) { @h.fetch('gumby'*20) }
+ assert_match(/key not found: "gumbygumby/, e.message)
+ assert_match(/\.\.\.\z/, e.message)
+ assert_same(@h, e.receiver)
+ assert_equal('gumby'*20, e.key)
+ end
+
def test_key2?
- assert(!@cls[].key?(1))
- assert(!@cls[].key?(nil))
- assert(@h.key?(nil))
- assert(@h.key?(1))
- assert(!@h.key?('gumby'))
+ assert_not_send([@cls[], :key?, 1])
+ assert_not_send([@cls[], :key?, nil])
+ assert_send([@h, :key?, nil])
+ assert_send([@h, :key?, 1])
+ assert_not_send([@h, :key?, 'gumby'])
end
def test_value?
- assert(!@cls[].value?(1))
- assert(!@cls[].value?(nil))
- assert(@h.value?('one'))
- assert(@h.value?(nil))
- assert(!@h.value?('gumby'))
+ assert_not_send([@cls[], :value?, 1])
+ assert_not_send([@cls[], :value?, nil])
+ assert_send([@h, :value?, 'one'])
+ assert_send([@h, :value?, nil])
+ assert_not_send([@h, :value?, 'gumby'])
end
def test_include?
- assert(!@cls[].include?(1))
- assert(!@cls[].include?(nil))
- assert(@h.include?(nil))
- assert(@h.include?(1))
- assert(!@h.include?('gumby'))
+ assert_not_send([@cls[], :include?, 1])
+ assert_not_send([@cls[], :include?, nil])
+ assert_send([@h, :include?, nil])
+ assert_send([@h, :include?, 1])
+ assert_not_send([@h, :include?, 'gumby'])
end
def test_key
@@ -418,11 +591,11 @@ class TestHash < Test::Unit::TestCase
def test_values_at
res = @h.values_at('dog', 'cat', 'horse')
- assert(res.length == 3)
+ assert_equal(3, res.length)
assert_equal([nil, nil, nil], res)
res = @h.values_at
- assert(res.length == 0)
+ assert_equal(0, res.length)
res = @h.values_at(3, 2, 1, nil)
assert_equal 4, res.length
@@ -433,6 +606,23 @@ class TestHash < Test::Unit::TestCase
assert_equal ['three', nil, 'one', 'nil'], res
end
+ def test_fetch_values
+ res = @h.fetch_values
+ assert_equal(0, res.length)
+
+ res = @h.fetch_values(3, 2, 1, nil)
+ assert_equal(4, res.length)
+ assert_equal %w( three two one nil ), res
+
+ e = assert_raise KeyError do
+ @h.fetch_values(3, 'invalid')
+ end
+ assert_same(@h, e.receiver)
+ assert_equal('invalid', e.key)
+
+ res = @h.fetch_values(3, 'invalid') { |k| k.upcase }
+ assert_equal %w( three INVALID ), res
+ end
def test_invert
h = @h.invert
@@ -441,21 +631,21 @@ class TestHash < Test::Unit::TestCase
assert_equal(nil, h['nil'])
h.each do |k, v|
- assert(@h.key?(v)) # not true in general, but works here
+ assert_send([@h, :key?, v]) # not true in general, but works here
end
h = @cls[ 'a' => 1, 'b' => 2, 'c' => 1].invert
assert_equal(2, h.length)
- assert(h[1] == 'a' || h[1] == 'c')
+ assert_include(%w[a c], h[1])
assert_equal('b', h[2])
end
def test_key?
- assert(!@cls[].key?(1))
- assert(!@cls[].key?(nil))
- assert(@h.key?(nil))
- assert(@h.key?(1))
- assert(!@h.key?('gumby'))
+ assert_not_send([@cls[], :key?, 1])
+ assert_not_send([@cls[], :key?, nil])
+ assert_send([@h, :key?, nil])
+ assert_send([@h, :key?, 1])
+ assert_not_send([@h, :key?, 'gumby'])
end
def test_keys
@@ -474,11 +664,11 @@ class TestHash < Test::Unit::TestCase
end
def test_member?
- assert(!@cls[].member?(1))
- assert(!@cls[].member?(nil))
- assert(@h.member?(nil))
- assert(@h.member?(1))
- assert(!@h.member?('gumby'))
+ assert_not_send([@cls[], :member?, 1])
+ assert_not_send([@cls[], :member?, nil])
+ assert_send([@h, :member?, nil])
+ assert_send([@h, :member?, 1])
+ assert_not_send([@h, :member?, 'gumby'])
end
def test_rehash
@@ -493,6 +683,8 @@ class TestHash < Test::Unit::TestCase
end
def test_reject
+ assert_equal({3=>4,5=>6}, @cls[1=>2,3=>4,5=>6].reject {|k, v| k + v < 7 })
+
base = @cls[ 1 => 'one', 2 => false, true => 'true', 'cat' => 99 ]
h1 = @cls[ 1 => 'one', 2 => false, true => 'true' ]
h2 = @cls[ 2 => false, 'cat' => 99 ]
@@ -509,6 +701,15 @@ class TestHash < Test::Unit::TestCase
assert_equal(h3, h.reject {|k,v| v })
assert_equal(base, h)
+
+ h.instance_variable_set(:@foo, :foo)
+ h.default = 42
+ h.taint
+ h = EnvUtil.suppress_warning {h.reject {false}}
+ assert_instance_of(Hash, h)
+ assert_not_predicate(h, :tainted?)
+ assert_nil(h.default)
+ assert_not_send([h, :instance_variable_defined?, :@foo])
end
def test_reject!
@@ -544,12 +745,30 @@ class TestHash < Test::Unit::TestCase
assert_nil(h[2])
end
+ def test_replace_bug9230
+ h = @cls[]
+ h.replace(@cls[])
+ assert_empty h
+
+ h = @cls[]
+ h.replace(@cls[].compare_by_identity)
+ assert_predicate(h, :compare_by_identity?)
+ end
+
+ def test_replace_bug15358
+ h1 = {}
+ h2 = {a:1,b:2,c:3,d:4,e:5}
+ h2.replace(h1)
+ GC.start
+ assert(true)
+ end
+
def test_shift
h = @h.dup
@h.length.times {
k, v = h.shift
- assert(@h.key?(k))
+ assert_send([@h, :key?, k])
assert_equal(@h[k], v)
}
@@ -616,15 +835,59 @@ class TestHash < Test::Unit::TestCase
h = @cls[ 1=>2, 3=>4, 5=>6 ]
h.taint
- h.untrust
a = h.to_a
assert_equal(true, a.tainted?)
- assert_equal(true, a.untrusted?)
end
def test_to_hash
h = @h.to_hash
assert_equal(@h, h)
+ assert_instance_of(@cls, h)
+ end
+
+ def test_to_h
+ h = @h.to_h
+ assert_equal(@h, h)
+ assert_instance_of(Hash, h)
+ end
+
+ def test_to_h_instance_variable
+ @h.instance_variable_set(:@x, 42)
+ h = @h.to_h
+ if @cls == Hash
+ assert_equal(42, h.instance_variable_get(:@x))
+ else
+ assert_not_send([h, :instance_variable_defined?, :@x])
+ end
+ end
+
+ def test_to_h_default_value
+ @h.default = :foo
+ h = @h.to_h
+ assert_equal(:foo, h.default)
+ end
+
+ def test_to_h_default_proc
+ @h.default_proc = ->(_,k) {"nope#{k}"}
+ h = @h.to_h
+ assert_equal("nope42", h[42])
+ end
+
+ def test_to_h_block
+ h = @h.to_h {|k, v| [k.to_s, v.to_s]}
+ assert_equal({
+ "1"=>"one", "2"=>"two", "3"=>"three", to_s=>"self",
+ "true"=>"true", ""=>"nil", "nil"=>""
+ },
+ h)
+ assert_instance_of(Hash, h)
+ end
+
+ def test_nil_to_h
+ h = nil.to_h
+ assert_equal({}, h)
+ assert_nil(h.default)
+ assert_nil(h.default_proc)
end
def test_to_s
@@ -656,11 +919,11 @@ class TestHash < Test::Unit::TestCase
end
def test_value2?
- assert(!@cls[].value?(1))
- assert(!@cls[].value?(nil))
- assert(@h.value?(nil))
- assert(@h.value?('one'))
- assert(!@h.value?('gumby'))
+ assert_not_send([@cls[], :value?, 1])
+ assert_not_send([@cls[], :value?, nil])
+ assert_send([@h, :value?, nil])
+ assert_send([@h, :value?, 'one'])
+ assert_not_send([@h, :value?, 'gumby'])
end
def test_values
@@ -673,36 +936,29 @@ class TestHash < Test::Unit::TestCase
assert_equal([], expected - vals)
end
- def test_security_check
- h = {}
- assert_raise(SecurityError) do
- Thread.new do
- $SAFE = 4
- h[1] = 1
- end.join
- end
- end
-
- def test_intialize_wrong_arguments
+ def test_initialize_wrong_arguments
assert_raise(ArgumentError) do
Hash.new(0) { }
end
end
def test_create
- assert_equal({1=>2, 3=>4}, Hash[[[1,2],[3,4]]])
+ assert_equal({1=>2, 3=>4}, @cls[[[1,2],[3,4]]])
assert_raise(ArgumentError) { Hash[0, 1, 2] }
- assert_equal({1=>2, 3=>4}, Hash[1,2,3,4])
+ assert_warning(/wrong element type Integer at 1 /) {@cls[[[1, 2], 3]]}
+ bug5406 = '[ruby-core:39945]'
+ assert_raise(ArgumentError, bug5406) { @cls[[[1, 2], [3, 4, 5]]] }
+ assert_equal({1=>2, 3=>4}, @cls[1,2,3,4])
o = Object.new
def o.to_hash() {1=>2} end
- assert_equal({1=>2}, Hash[o], "[ruby-dev:34555]")
+ assert_equal({1=>2}, @cls[o], "[ruby-dev:34555]")
end
def test_rehash2
- h = {1 => 2, 3 => 4}
+ h = @cls[1 => 2, 3 => 4]
assert_equal(h.dup, h.rehash)
assert_raise(RuntimeError) { h.each { h.rehash } }
- assert_equal({}, {}.rehash)
+ assert_equal({}, @cls[].rehash)
end
def test_fetch2
@@ -710,57 +966,141 @@ class TestHash < Test::Unit::TestCase
end
def test_default_proc
- h = Hash.new {|hh, k| hh + k + "baz" }
+ h = @cls.new {|hh, k| hh + k + "baz" }
assert_equal("foobarbaz", h.default_proc.call("foo", "bar"))
- h = {}
+ assert_nil(h.default_proc = nil)
+ assert_nil(h.default_proc)
+ h.default_proc = ->(_,_){ true }
+ assert_equal(true, h[:nope])
+ h = @cls[]
assert_nil(h.default_proc)
end
def test_shift2
- h = Hash.new {|hh, k| :foo }
+ h = @cls.new {|hh, k| :foo }
h[1] = 2
assert_equal([1, 2], h.shift)
assert_equal(:foo, h.shift)
assert_equal(:foo, h.shift)
- h = Hash.new(:foo)
+ h = @cls.new(:foo)
h[1] = 2
assert_equal([1, 2], h.shift)
assert_equal(:foo, h.shift)
assert_equal(:foo, h.shift)
- h = {1=>2}
+ h =@cls[1=>2]
h.each { assert_equal([1, 2], h.shift) }
end
+ def test_shift_none
+ h = @cls.new {|hh, k| "foo"}
+ def h.default(k = nil)
+ super.upcase
+ end
+ assert_equal("FOO", h.shift)
+ end
+
def test_reject_bang2
- assert_equal({1=>2}, {1=>2,3=>4}.reject! {|k, v| k + v == 7 })
- assert_nil({1=>2,3=>4}.reject! {|k, v| k == 5 })
- assert_nil({}.reject! { })
+ 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 })
+ assert_nil(@cls[].reject! { })
end
def test_select
- assert_equal({3=>4,5=>6}, {1=>2,3=>4,5=>6}.select {|k, v| k + v >= 7 })
+ assert_equal({3=>4,5=>6}, @cls[1=>2,3=>4,5=>6].select {|k, v| k + v >= 7 })
+
+ base = @cls[ 1 => 'one', '2' => false, true => 'true', 'cat' => 99 ]
+ h1 = @cls[ '2' => false, 'cat' => 99 ]
+ h2 = @cls[ 1 => 'one', true => 'true' ]
+ h3 = @cls[ 1 => 'one', true => 'true', 'cat' => 99 ]
+
+ h = base.dup
+ assert_equal(h, h.select { true })
+ assert_equal(@cls[], h.select { false })
+
+ h = base.dup
+ assert_equal(h1, h.select {|k,v| k.instance_of?(String) })
+
+ assert_equal(h2, h.select {|k,v| v.instance_of?(String) })
+
+ assert_equal(h3, h.select {|k,v| v })
+ assert_equal(base, h)
+
+ h.instance_variable_set(:@foo, :foo)
+ h.default = 42
+ h.taint
+ h = h.select {true}
+ assert_instance_of(Hash, h)
+ assert_not_predicate(h, :tainted?)
+ assert_nil(h.default)
+ assert_not_send([h, :instance_variable_defined?, :@foo])
end
def test_select!
- h = {1=>2,3=>4,5=>6}
+ 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 = {1=>2,3=>4,5=>6}
+ h = @cls[1=>2,3=>4,5=>6]
assert_equal(nil, h.select!{true})
end
+ def test_slice
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_equal({1=>2, 3=>4}, h.slice(1, 3))
+ assert_equal({}, h.slice(7))
+ assert_equal({}, h.slice)
+ assert_equal({}, {}.slice)
+ end
+
+ def test_filter
+ assert_equal({3=>4,5=>6}, @cls[1=>2,3=>4,5=>6].filter {|k, v| k + v >= 7 })
+
+ base = @cls[ 1 => 'one', '2' => false, true => 'true', 'cat' => 99 ]
+ h1 = @cls[ '2' => false, 'cat' => 99 ]
+ h2 = @cls[ 1 => 'one', true => 'true' ]
+ h3 = @cls[ 1 => 'one', true => 'true', 'cat' => 99 ]
+
+ h = base.dup
+ assert_equal(h, h.filter { true })
+ assert_equal(@cls[], h.filter { false })
+
+ h = base.dup
+ assert_equal(h1, h.filter {|k,v| k.instance_of?(String) })
+
+ assert_equal(h2, h.filter {|k,v| v.instance_of?(String) })
+
+ assert_equal(h3, h.filter {|k,v| v })
+ assert_equal(base, h)
+
+ h.instance_variable_set(:@foo, :foo)
+ h.default = 42
+ h.taint
+ h = h.filter {true}
+ assert_instance_of(Hash, h)
+ assert_not_predicate(h, :tainted?)
+ assert_nil(h.default)
+ assert_not_send([h, :instance_variable_defined?, :@foo])
+ end
+
+ def test_filter!
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_equal(h, h.filter! {|k, v| k + v >= 7 })
+ assert_equal({3=>4,5=>6}, h)
+ h = @cls[1=>2,3=>4,5=>6]
+ assert_equal(nil, h.filter!{true})
+ end
+
def test_clear2
- assert_equal({}, {1=>2,3=>4,5=>6}.clear)
- h = {1=>2,3=>4,5=>6}
+ assert_equal({}, @cls[1=>2,3=>4,5=>6].clear)
+ h = @cls[1=>2,3=>4,5=>6]
h.each { h.clear }
assert_equal({}, h)
end
def test_replace2
- h1 = Hash.new { :foo }
- h2 = {}
+ h1 = @cls.new { :foo }
+ h2 = @cls.new
h2.replace h1
assert_equal(:foo, h2[0])
@@ -768,73 +1108,127 @@ class TestHash < Test::Unit::TestCase
assert_raise(TypeError) { h2.replace(1) }
h2.freeze
assert_raise(ArgumentError) { h2.replace() }
- assert_raise(RuntimeError) { h2.replace(h1) }
- assert_raise(RuntimeError) { h2.replace(42) }
+ assert_raise(FrozenError) { h2.replace(h1) }
+ assert_raise(FrozenError) { h2.replace(42) }
end
def test_size2
- assert_equal(0, {}.size)
+ assert_equal(0, @cls[].size)
end
def test_equal2
- assert({} != 0)
+ assert_not_equal(0, @cls[])
o = Object.new
- def o.to_hash; {}; end
+ o.instance_variable_set(:@cls, @cls)
+ def o.to_hash; @cls[]; end
def o.==(x); true; end
- assert({} == o)
+ assert_equal({}, o)
def o.==(x); false; end
- assert({} != o)
+ assert_not_equal({}, o)
- h1 = {1=>2}; h2 = {3=>4}
- assert(h1 != h2)
- h1 = {1=>2}; h2 = {1=>4}
- assert(h1 != h2)
+ h1 = @cls[1=>2]; h2 = @cls[3=>4]
+ assert_not_equal(h1, h2)
+ h1 = @cls[1=>2]; h2 = @cls[1=>4]
+ assert_not_equal(h1, h2)
end
def test_eql
- assert(!({}.eql?(0)))
+ assert_not_send([@cls[], :eql?, 0])
o = Object.new
- def o.to_hash; {}; end
+ o.instance_variable_set(:@cls, @cls)
+ def o.to_hash; @cls[]; end
def o.eql?(x); true; end
- assert({}.eql?(o))
+ assert_send([@cls[], :eql?, o])
def o.eql?(x); false; end
- assert(!({}.eql?(o)))
+ assert_not_send([@cls[], :eql?, o])
end
def test_hash2
- assert_kind_of(Integer, {}.hash)
+ assert_kind_of(Integer, @cls[].hash)
+ h = @cls[1=>2]
+ h.shift
+ assert_equal({}.hash, h.hash, '[ruby-core:38650]')
+ bug9231 = '[ruby-core:58993] [Bug #9231]'
+ assert_not_equal(0, @cls[].hash, bug9231)
end
def test_update2
- h1 = {1=>2, 3=>4}
+ h1 = @cls[1=>2, 3=>4]
h2 = {1=>3, 5=>7}
h1.update(h2) {|k, v1, v2| k + v1 + v2 }
assert_equal({1=>6, 3=>4, 5=>7}, h1)
end
+ def test_update3
+ h1 = @cls[1=>2, 3=>4]
+ h1.update()
+ assert_equal({1=>2, 3=>4}, h1)
+ h2 = {1=>3, 5=>7}
+ h3 = {1=>1, 2=>4}
+ h1.update(h2, h3)
+ assert_equal({1=>1, 2=>4, 3=>4, 5=>7}, h1)
+ end
+
+ def test_update4
+ h1 = @cls[1=>2, 3=>4]
+ h1.update(){|k, v1, v2| k + v1 + v2 }
+ assert_equal({1=>2, 3=>4}, h1)
+ h2 = {1=>3, 5=>7}
+ h3 = {1=>1, 2=>4}
+ h1.update(h2, h3){|k, v1, v2| k + v1 + v2 }
+ assert_equal({1=>8, 2=>4, 3=>4, 5=>7}, h1)
+ end
+
def test_merge
- h1 = {1=>2, 3=>4}
+ h1 = @cls[1=>2, 3=>4]
h2 = {1=>3, 5=>7}
+ h3 = {1=>1, 2=>4}
+ assert_equal({1=>2, 3=>4}, h1.merge())
assert_equal({1=>3, 3=>4, 5=>7}, h1.merge(h2))
assert_equal({1=>6, 3=>4, 5=>7}, h1.merge(h2) {|k, v1, v2| k + v1 + v2 })
+ assert_equal({1=>1, 2=>4, 3=>4, 5=>7}, h1.merge(h2, h3))
+ assert_equal({1=>8, 2=>4, 3=>4, 5=>7}, h1.merge(h2, h3) {|k, v1, v2| k + v1 + v2 })
end
def test_assoc
- assert_equal([3,4], {1=>2, 3=>4, 5=>6}.assoc(3))
- assert_nil({1=>2, 3=>4, 5=>6}.assoc(4))
+ assert_equal([3,4], @cls[1=>2, 3=>4, 5=>6].assoc(3))
+ assert_nil(@cls[1=>2, 3=>4, 5=>6].assoc(4))
+ assert_equal([1.0,1], @cls[1.0=>1].assoc(1))
+ end
+
+ def test_assoc_compare_by_identity
+ h = @cls[]
+ h.compare_by_identity
+ h["a"] = 1
+ h["a".dup] = 2
+ assert_equal(["a",1], h.assoc("a"))
end
def test_rassoc
- assert_equal([3,4], {1=>2, 3=>4, 5=>6}.rassoc(4))
+ assert_equal([3,4], @cls[1=>2, 3=>4, 5=>6].rassoc(4))
assert_nil({1=>2, 3=>4, 5=>6}.rassoc(3))
end
def test_flatten
- assert_equal([[1], [2]], {[1] => [2]}.flatten)
+ assert_equal([[1], [2]], @cls[[1] => [2]].flatten)
+
+ a = @cls[1=> "one", 2 => [2,"two"], 3 => [3, ["three"]]]
+ assert_equal([1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten)
+ assert_equal([[1, "one"], [2, [2, "two"]], [3, [3, ["three"]]]], a.flatten(0))
+ assert_equal([1, "one", 2, [2, "two"], 3, [3, ["three"]]], a.flatten(1))
+ assert_equal([1, "one", 2, 2, "two", 3, 3, ["three"]], a.flatten(2))
+ assert_equal([1, "one", 2, 2, "two", 3, 3, "three"], a.flatten(3))
+ assert_equal([1, "one", 2, 2, "two", 3, 3, "three"], a.flatten(-1))
+ assert_raise(TypeError){ a.flatten(nil) }
+ end
+
+ def test_flatten_arity
+ a = @cls[1=> "one", 2 => [2,"two"], 3 => [3, ["three"]]]
+ assert_raise(ArgumentError){ a.flatten(1, 2) }
end
def test_callcc
- h = {1=>2}
+ h = @cls[1=>2]
c = nil
f = false
h.each { callcc {|c2| c = c2 } }
@@ -844,7 +1238,7 @@ class TestHash < Test::Unit::TestCase
end
assert_raise(RuntimeError) { h.each { h.rehash } }
- h = {1=>2}
+ h = @cls[1=>2]
c = nil
assert_raise(RuntimeError) do
h.each { callcc {|c2| c = c2 } }
@@ -853,15 +1247,95 @@ class TestHash < Test::Unit::TestCase
end
end
+ def test_callcc_iter_level
+ bug9105 = '[ruby-dev:47803] [Bug #9105]'
+ h = @cls[1=>2, 3=>4]
+ c = nil
+ f = false
+ h.each {callcc {|c2| c = c2}}
+ unless f
+ f = true
+ c.call
+ end
+ assert_nothing_raised(RuntimeError, bug9105) do
+ h.each {|i, j|
+ h.delete(i);
+ assert_not_equal(false, i, bug9105)
+ }
+ end
+ end
+
+ def test_callcc_escape
+ bug9105 = '[ruby-dev:47803] [Bug #9105]'
+ assert_nothing_raised(RuntimeError, bug9105) do
+ h=@cls[]
+ cnt=0
+ c = callcc {|cc|cc}
+ h[cnt] = true
+ h.each{|i|
+ cnt+=1
+ c.call if cnt == 1
+ }
+ end
+ end
+
+ def test_callcc_reenter
+ bug9105 = '[ruby-dev:47803] [Bug #9105]'
+ assert_nothing_raised(RuntimeError, bug9105) do
+ h = @cls[1=>2,3=>4]
+ c = nil
+ f = false
+ h.each { |i|
+ callcc {|c2| c = c2 } unless c
+ h.delete(1) if f
+ }
+ unless f
+ f = true
+ c.call
+ end
+ end
+ end
+
+ def test_threaded_iter_level
+ bug9105 = '[ruby-dev:47807] [Bug #9105]'
+ h = @cls[1=>2]
+ 2.times.map {
+ f = false
+ th = Thread.start {h.each {f = true; sleep}}
+ Thread.pass until f
+ Thread.pass until th.stop?
+ th
+ }.each {|th| th.run; th.join}
+ assert_nothing_raised(RuntimeError, bug9105) do
+ h[5] = 6
+ end
+ assert_equal(6, h[5], bug9105)
+ end
+
def test_compare_by_identity
a = "foo"
- assert(!{}.compare_by_identity?)
- h = { a => "bar" }
- assert(!h.compare_by_identity?)
+ assert_not_predicate(@cls[], :compare_by_identity?)
+ h = @cls[a => "bar"]
+ assert_not_predicate(h, :compare_by_identity?)
h.compare_by_identity
- assert(h.compare_by_identity?)
+ assert_predicate(h, :compare_by_identity?)
#assert_equal("bar", h[a])
assert_nil(h["foo"])
+
+ bug8703 = '[ruby-core:56256] [Bug #8703] copied identhash'
+ h.clear
+ assert_predicate(h.dup, :compare_by_identity?, bug8703)
+ end
+
+ def test_same_key
+ bug9646 = '[ruby-dev:48047] [Bug #9646] Infinite loop at Hash#each'
+ h = @cls[a=[], 1]
+ a << 1
+ h[[]] = 2
+ a.clear
+ cnt = 0
+ r = h.each{ break nil if (cnt+=1) > 100 }
+ assert_not_nil(r,bug9646)
end
class ObjWithHash
@@ -877,31 +1351,444 @@ class TestHash < Test::Unit::TestCase
end
def test_hash_hash
- assert_equal({0=>2,11=>1}.hash, {11=>1,0=>2}.hash)
+ assert_equal({0=>2,11=>1}.hash, @cls[11=>1,0=>2].hash)
o1 = ObjWithHash.new(0,1)
o2 = ObjWithHash.new(11,1)
- assert_equal({o1=>1,o2=>2}.hash, {o2=>2,o1=>1}.hash)
+ assert_equal({o1=>1,o2=>2}.hash, @cls[o2=>2,o1=>1].hash)
end
def test_hash_bignum_hash
x = 2<<(32-3)-1
- assert_equal({x=>1}.hash, {x=>1}.hash)
+ assert_equal({x=>1}.hash, @cls[x=>1].hash)
x = 2<<(64-3)-1
- assert_equal({x=>1}.hash, {x=>1}.hash)
+ assert_equal({x=>1}.hash, @cls[x=>1].hash)
o = Object.new
- def o.hash; 2<<100; end
- assert_equal({x=>1}.hash, {x=>1}.hash)
+ def o.hash; 2 << 100; end
+ assert_equal({o=>1}.hash, @cls[o=>1].hash)
end
- def test_hash_poped
- assert_nothing_raised { eval("a = 1; {a => a}; a") }
+ def test_hash_popped
+ assert_nothing_raised { eval("a = 1; @cls[a => a]; a") }
end
def test_recursive_key
- h = {}
+ h = @cls[]
assert_nothing_raised { h[h] = :foo }
h.rehash
assert_equal(:foo, h[h])
end
+
+ def test_inverse_hash
+ feature4262 = '[ruby-core:34334]'
+ [@cls[1=>2], @cls[123=>"abc"]].each do |h|
+ assert_not_equal(h.hash, h.invert.hash, feature4262)
+ end
+ end
+
+ def test_recursive_hash_value_struct
+ bug9151 = '[ruby-core:58567] [Bug #9151]'
+
+ s = Struct.new(:x) {def hash; [x,""].hash; end}
+ a = s.new
+ b = s.new
+ a.x = b
+ b.x = a
+ assert_nothing_raised(SystemStackError, bug9151) {a.hash}
+ assert_nothing_raised(SystemStackError, bug9151) {b.hash}
+
+ h = @cls[]
+ h[[a,"hello"]] = 1
+ assert_equal(1, h.size)
+ h[[b,"world"]] = 2
+ assert_equal(2, h.size)
+
+ obj = Object.new
+ h = @cls[a => obj]
+ assert_same(obj, h[b])
+ end
+
+ def test_recursive_hash_value_array
+ h = @cls[]
+ h[[[1]]] = 1
+ assert_equal(1, h.size)
+ h[[[2]]] = 1
+ assert_equal(2, h.size)
+
+ a = []
+ a << a
+
+ h = @cls[]
+ h[[a, 1]] = 1
+ assert_equal(1, h.size)
+ h[[a, 2]] = 2
+ assert_equal(2, h.size)
+ h[[a, a]] = 3
+ assert_equal(3, h.size)
+
+ obj = Object.new
+ h = @cls[a => obj]
+ assert_same(obj, h[[[a]]])
+ end
+
+ def test_recursive_hash_value_array_hash
+ h = @cls[]
+ rec = [h]
+ h[:x] = rec
+
+ obj = Object.new
+ h2 = {rec => obj}
+ [h, {x: rec}].each do |k|
+ k = [k]
+ assert_same(obj, h2[k], ->{k.inspect})
+ end
+ end
+
+ def test_recursive_hash_value_hash_array
+ h = @cls[]
+ rec = [h]
+ h[:x] = rec
+
+ obj = Object.new
+ h2 = {h => obj}
+ [rec, [h]].each do |k|
+ k = {x: k}
+ assert_same(obj, h2[k], ->{k.inspect})
+ end
+ end
+
+ def test_exception_in_rehash_memory_leak
+ return unless @cls == Hash
+
+ bug9187 = '[ruby-core:58728] [Bug #9187]'
+
+ prepare = <<-EOS
+ class Foo
+ def initialize
+ @raise = false
+ end
+
+ def hash
+ raise if @raise
+ @raise = true
+ return 0
+ end
+ end
+ h = {Foo.new => true}
+ EOS
+
+ code = <<-EOS
+ 10_0000.times do
+ h.rehash rescue nil
+ end
+ GC.start
+ EOS
+
+ assert_no_memory_leak([], prepare, code, bug9187)
+ end
+
+ def test_wrapper
+ bug9381 = '[ruby-core:59638] [Bug #9381]'
+
+ wrapper = Class.new do
+ def initialize(obj)
+ @obj = obj
+ end
+
+ def hash
+ @obj.hash
+ end
+
+ def eql?(other)
+ @obj.eql?(other)
+ end
+ end
+
+ bad = [
+ 5, true, false, nil,
+ 0.0, 1.72723e-77,
+ :foo, "dsym_#{self.object_id.to_s(16)}_#{Time.now.to_i.to_s(16)}".to_sym,
+ "str",
+ ].select do |x|
+ hash = {x => bug9381}
+ hash[wrapper.new(x)] != bug9381
+ end
+ assert_empty(bad, bug9381)
+ end
+
+ def assert_hash_random(obj, dump = obj.inspect)
+ a = [obj.hash.to_s]
+ 3.times {
+ assert_in_out_err(["-e", "print (#{dump}).hash"], "") do |r, e|
+ a += r
+ assert_equal([], e)
+ end
+ }
+ assert_not_equal([obj.hash.to_s], a.uniq)
+ assert_operator(a.uniq.size, :>, 2, proc {a.inspect})
+ end
+
+ def test_string_hash_random
+ assert_hash_random('abc')
+ end
+
+ def test_symbol_hash_random
+ assert_hash_random(:-)
+ assert_hash_random(:foo)
+ assert_hash_random("dsym_#{self.object_id.to_s(16)}_#{Time.now.to_i.to_s(16)}".to_sym)
+ end
+
+ def test_integer_hash_random
+ assert_hash_random(0)
+ assert_hash_random(+1)
+ assert_hash_random(-1)
+ assert_hash_random(+(1<<100))
+ assert_hash_random(-(1<<100))
+ end
+
+ def test_float_hash_random
+ assert_hash_random(0.0)
+ assert_hash_random(+1.0)
+ assert_hash_random(-1.0)
+ assert_hash_random(1.72723e-77)
+ assert_hash_random(Float::INFINITY, "Float::INFINITY")
+ end
+
+ def test_label_syntax
+ return unless @cls == Hash
+
+ feature4935 = '[ruby-core:37553] [Feature #4935]'
+ x = 'world'
+ hash = assert_nothing_raised(SyntaxError, feature4935) do
+ break eval(%q({foo: 1, "foo-bar": 2, "hello-#{x}": 3, 'hello-#{x}': 4, 'bar': {}}))
+ end
+ assert_equal({:foo => 1, :'foo-bar' => 2, :'hello-world' => 3, :'hello-#{x}' => 4, :bar => {}}, hash, feature4935)
+ x = x
+ end
+
+ def test_dig
+ h = @cls[a: @cls[b: [1, 2, 3]], c: 4]
+ assert_equal(1, h.dig(:a, :b, 0))
+ assert_nil(h.dig(:b, 1))
+ assert_raise(TypeError) {h.dig(:c, 1)}
+ o = Object.new
+ def o.dig(*args)
+ {dug: args}
+ end
+ h[:d] = o
+ assert_equal({dug: [:foo, :bar]}, h.dig(:d, :foo, :bar))
+ end
+
+ def test_dig_with_respond_to
+ bug12030 = '[ruby-core:73556] [Bug #12030]'
+ o = Object.new
+ def o.respond_to?(*args)
+ super
+ end
+ assert_raise(TypeError, bug12030) {{foo: o}.dig(:foo, :foo)}
+ end
+
+ def test_cmp
+ h1 = {a:1, b:2}
+ h2 = {a:1, b:2, c:3}
+
+ assert_operator(h1, :<=, h1)
+ assert_operator(h1, :<=, h2)
+ assert_not_operator(h2, :<=, h1)
+ assert_operator(h2, :<=, h2)
+
+ assert_operator(h1, :>=, h1)
+ assert_not_operator(h1, :>=, h2)
+ assert_operator(h2, :>=, h1)
+ assert_operator(h2, :>=, h2)
+
+ assert_not_operator(h1, :<, h1)
+ assert_operator(h1, :<, h2)
+ assert_not_operator(h2, :<, h1)
+ assert_not_operator(h2, :<, h2)
+
+ assert_not_operator(h1, :>, h1)
+ assert_not_operator(h1, :>, h2)
+ assert_operator(h2, :>, h1)
+ assert_not_operator(h2, :>, h2)
+ end
+
+ def test_cmp_samekeys
+ h1 = {a:1}
+ h2 = {a:2}
+
+ assert_operator(h1, :<=, h1)
+ assert_not_operator(h1, :<=, h2)
+ assert_not_operator(h2, :<=, h1)
+ assert_operator(h2, :<=, h2)
+
+ assert_operator(h1, :>=, h1)
+ assert_not_operator(h1, :>=, h2)
+ assert_not_operator(h2, :>=, h1)
+ assert_operator(h2, :>=, h2)
+
+ assert_not_operator(h1, :<, h1)
+ assert_not_operator(h1, :<, h2)
+ assert_not_operator(h2, :<, h1)
+ assert_not_operator(h2, :<, h2)
+
+ assert_not_operator(h1, :>, h1)
+ assert_not_operator(h1, :>, h2)
+ assert_not_operator(h2, :>, h1)
+ assert_not_operator(h2, :>, h2)
+ end
+
+ def test_to_proc
+ h = {
+ 1 => 10,
+ 2 => 20,
+ 3 => 30,
+ }
+
+ assert_equal([10, 20, 30], [1, 2, 3].map(&h))
+ end
+
+ def test_transform_keys
+ x = @cls[a: 1, b: 2, c: 3]
+ y = x.transform_keys {|k| :"#{k}!" }
+ assert_equal({a: 1, b: 2, c: 3}, x)
+ assert_equal({a!: 1, b!: 2, c!: 3}, y)
+
+ enum = x.transform_keys
+ assert_equal(x.size, enum.size)
+ assert_instance_of(Enumerator, enum)
+
+ y = x.transform_keys.with_index {|k, i| "#{k}.#{i}" }
+ assert_equal(%w(a.0 b.1 c.2), y.keys)
+ end
+
+ def test_transform_keys_bang
+ x = @cls[a: 1, b: 2, c: 3]
+ y = x.transform_keys! {|k| :"#{k}!" }
+ assert_equal({a!: 1, b!: 2, c!: 3}, x)
+ assert_same(x, y)
+
+ enum = x.transform_keys!
+ assert_equal(x.size, enum.size)
+ assert_instance_of(Enumerator, enum)
+
+ x.transform_keys!.with_index {|k, i| "#{k}.#{i}" }
+ assert_equal(%w(a!.0 b!.1 c!.2), x.keys)
+
+ x = @cls[1 => :a, -1 => :b]
+ x.transform_keys! {|k| -k }
+ assert_equal([-1, :a, 1, :b], x.flatten)
+
+ x = @cls[true => :a, false => :b]
+ x.transform_keys! {|k| !k }
+ assert_equal([false, :a, true, :b], x.flatten)
+ end
+
+ def test_transform_values
+ x = @cls[a: 1, b: 2, c: 3]
+ y = x.transform_values {|v| v ** 2 }
+ assert_equal([1, 4, 9], y.values_at(:a, :b, :c))
+ assert_not_same(x, y)
+
+ 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_bang
+ x = @cls[a: 1, b: 2, c: 3]
+ y = x.transform_values! {|v| v ** 2 }
+ assert_equal([1, 4, 9], y.values_at(:a, :b, :c))
+ assert_same(x, y)
+
+ 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))
+ end
+
+ def test_broken_hash_value
+ bug14218 = '[ruby-core:84395] [Bug #14218]'
+
+ assert_equal(0, 1_000_000.times.count{a=Object.new.hash; b=Object.new.hash; a < 0 && b < 0 && a + b > 0}, bug14218)
+ assert_equal(0, 1_000_000.times.count{a=Object.new.hash; b=Object.new.hash; 0 + a + b != 0 + b + a}, bug14218)
+ end
+
+ def test_reserved_hash_val
+ s = Struct.new(:hash)
+ h = {}
+ keys = [*0..8]
+ keys.each {|i| h[s.new(i)]=true}
+ msg = proc {h.inspect}
+ assert_equal(keys, h.keys.map(&:hash), msg)
+ end
+
+ class TestSubHash < TestHash
+ class SubHash < Hash
+ def reject(*)
+ super
+ end
+ end
+
+ def setup
+ @cls = SubHash
+ super
+ end
+ 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
end
diff --git a/test/ruby/test_ifunless.rb b/test/ruby/test_ifunless.rb
index bffc794512..f68e5154a2 100644
--- a/test/ruby/test_ifunless.rb
+++ b/test/ruby/test_ifunless.rb
@@ -1,14 +1,15 @@
+# frozen_string_literal: false
require 'test/unit'
-class TestIfunless < Test::Unit::TestCase
+class TestIfUnless < Test::Unit::TestCase
def test_if_unless
- $x = 'test';
- assert(if $x == $x then true else false end)
- $bad = false
- unless $x == $x
- $bad = true
+ x = 'test';
+ assert(if x == x then true else false end)
+ bad = false
+ unless x == x
+ bad = true
end
- assert(!$bad)
- assert(unless $x != $x then true else false end)
+ assert(!bad)
+ assert(unless x != x then true else false end)
end
end
diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb
index 7f8212ebf0..0f289d8ed9 100644
--- a/test/ruby/test_integer.rb
+++ b/test/ruby/test_integer.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestInteger < Test::Unit::TestCase
@@ -23,6 +24,32 @@ class TestInteger < Test::Unit::TestCase
rescue
nil
end, "[ruby-dev:32084] [ruby-dev:34547]")
+
+ x = EnvUtil.suppress_warning {2 ** -0x4000000000000000}
+ assert_in_delta(0.0, (x / 2), Float::EPSILON)
+
+ <<~EXPRS.each_line.with_index(__LINE__+1) do |expr, line|
+ crash01: 111r+11**-11111161111111
+ crash02: 1118111111111**-1111111111111111**1+1==11111
+ crash03: -1111111**-1111*11 - -1111111** -111111111
+ crash04: 1118111111111** -1111111111111111**1+11111111111**1 ===111
+ crash05: 11** -111155555555555555 -55 !=5-555
+ crash07: 1 + 111111111**-1111811111
+ crash08: 18111111111**-1111111111111111**1 + 1111111111**-1111**1
+ crash10: -7 - -1111111** -1111**11
+ crash12: 1118111111111** -1111111111111111**1 + 1111 - -1111111** -1111*111111111119
+ crash13: 1.0i - -1111111** -111111111
+ crash14: 11111**111111111**111111 * -11111111111111111111**-111111111111
+ crash15: ~1**1111 + -~1**~1**111
+ crash17: 11** -1111111**1111 /11i
+ crash18: 5555i**-5155 - -9111111**-1111**11
+ crash19: 111111*-11111111111111111111**-1111111111111111
+ crash20: 1111**111-11**-11111**11
+ crash21: 11**-10111111119-1i -1r
+ EXPRS
+ name, expr = expr.split(':', 2)
+ assert_ruby_status(%w"-W0", expr, name)
+ end
end
def test_lshift
@@ -35,9 +62,9 @@ class TestInteger < Test::Unit::TestCase
def test_rshift
# assert_equal(bdsize(0x40000001), (1 >> -0x40000001).size)
- assert((1 >> 0x80000000).zero?)
- assert((1 >> 0xffffffff).zero?)
- assert((1 >> 0x100000000).zero?)
+ assert_predicate((1 >> 0x80000000), :zero?)
+ assert_predicate((1 >> 0xffffffff), :zero?)
+ assert_predicate((1 >> 0x100000000), :zero?)
# assert_equal((1 << 0x40000000), (1 >> -0x40000000))
# assert_equal((1 << 0x40000001), (1 >> -0x40000001))
end
@@ -90,49 +117,98 @@ class TestInteger < Test::Unit::TestCase
assert_equal(2 ** 50, Integer(2.0 ** 50))
assert_raise(TypeError) { Integer(nil) }
- end
- def test_int_p
- assert(!(1.0.integer?))
- assert(1.integer?)
- end
+ bug14552 = '[ruby-core:85813]'
+ obj = Object.new
+ def obj.to_int; "str"; end
+ assert_raise(TypeError, bug14552) { Integer(obj) }
+ def obj.to_i; 42; end
+ assert_equal(42, Integer(obj), bug14552)
- def test_odd_p_even_p
- Fixnum.class_eval do
- alias odd_bak odd?
- alias even_bak even?
- remove_method :odd?, :even?
- end
+ obj = Object.new
+ def obj.to_i; "str"; end
+ assert_raise(TypeError) { Integer(obj) }
- assert(1.odd?)
- assert(!(2.odd?))
- assert(!(1.even?))
- assert(2.even?)
+ bug6192 = '[ruby-core:43566]'
+ assert_raise(Encoding::CompatibilityError, bug6192) {Integer("0".encode("utf-16be"))}
+ assert_raise(Encoding::CompatibilityError, bug6192) {Integer("0".encode("utf-16le"))}
+ assert_raise(Encoding::CompatibilityError, bug6192) {Integer("0".encode("utf-32be"))}
+ assert_raise(Encoding::CompatibilityError, bug6192) {Integer("0".encode("utf-32le"))}
+ assert_raise(Encoding::CompatibilityError, bug6192) {Integer("0".encode("iso-2022-jp"))}
- ensure
- Fixnum.class_eval do
- alias odd? odd_bak
- alias even? even_bak
- remove_method :odd_bak, :even_bak
- end
+ assert_raise_with_message(ArgumentError, /\u{1f4a1}/) {Integer("\u{1f4a1}")}
+
+ obj = Struct.new(:s).new(%w[42 not-an-integer])
+ def obj.to_str; s.shift; end
+ assert_equal(42, Integer(obj, 10))
+
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ class Float
+ undef to_int
+ def to_int; raise "conversion failed"; end
+ end
+ assert_equal (1 << 100), Integer((1 << 100).to_f)
+ assert_equal 1, Integer(1.0)
+ end;
end
- def test_succ
- assert_equal(2, 1.send(:succ))
+ def test_Integer_with_exception_keyword
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(nil, Integer("1z", exception: false))
+ }
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(nil, Integer(Object.new, exception: false))
+ }
+ assert_nothing_raised(ArgumentError) {
+ o = Object.new
+ def o.to_i; 42.5; end
+ assert_equal(nil, Integer(o, exception: false))
+ }
+ assert_nothing_raised(ArgumentError) {
+ o = Object.new
+ def o.to_i; raise; end
+ assert_equal(nil, Integer(o, exception: false))
+ }
+ assert_nothing_raised(ArgumentError) {
+ o = Object.new
+ def o.to_int; raise; end
+ assert_equal(nil, Integer(o, exception: false))
+ }
+ assert_nothing_raised(FloatDomainError) {
+ assert_equal(nil, Integer(Float::INFINITY, exception: false))
+ }
+ assert_nothing_raised(FloatDomainError) {
+ assert_equal(nil, Integer(-Float::INFINITY, exception: false))
+ }
+ assert_nothing_raised(FloatDomainError) {
+ assert_equal(nil, Integer(Float::NAN, exception: false))
+ }
- Fixnum.class_eval do
- alias succ_bak succ
- remove_method :succ
- end
+ assert_raise(ArgumentError) {
+ Integer("1z", exception: true)
+ }
+ assert_raise(TypeError) {
+ Integer(nil, exception: true)
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Integer(nil, exception: false))
+ }
- assert_equal(2, 1.succ)
- assert_equal(4294967297, 4294967296.succ)
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ class Integer;def method_missing(*);"";end;end
+ assert_equal(0, Integer("0", 2))
+ end;
+ end
- ensure
- Fixnum.class_eval do
- alias succ succ_bak
- remove_method :succ_bak
- end
+ def test_int_p
+ assert_not_predicate(1.0, :integer?)
+ assert_predicate(1, :integer?)
+ end
+
+ def test_succ
+ assert_equal(2, 1.send(:succ))
end
def test_chr
@@ -177,25 +253,365 @@ class TestInteger < Test::Unit::TestCase
end
end
+ def assert_int_equal(expected, result, mesg = nil)
+ assert_kind_of(Integer, result, mesg)
+ assert_equal(expected, result, mesg)
+ end
+
+ def assert_float_equal(expected, result, mesg = nil)
+ assert_kind_of(Float, result, mesg)
+ assert_equal(expected, result, mesg)
+ end
+
def test_round
- assert_equal(11111, 11111.round)
- assert_equal(Fixnum, 11111.round.class)
- assert_equal(11111, 11111.round(0))
- assert_equal(Fixnum, 11111.round(0).class)
-
- assert_equal(11111.0, 11111.round(1))
- assert_equal(Float, 11111.round(1).class)
- assert_equal(11111.0, 11111.round(2))
- assert_equal(Float, 11111.round(2).class)
-
- assert_equal(11110, 11111.round(-1))
- assert_equal(Fixnum, 11111.round(-1).class)
- assert_equal(11100, 11111.round(-2))
- assert_equal(Fixnum, 11111.round(-2).class)
-
- assert_equal(1111_1111_1111_1111_1111_1111_1111_1110, 1111_1111_1111_1111_1111_1111_1111_1111.round(-1))
- assert_equal(Bignum, 1111_1111_1111_1111_1111_1111_1111_1111.round(-1).class)
- assert_equal(-1111_1111_1111_1111_1111_1111_1111_1110, (-1111_1111_1111_1111_1111_1111_1111_1111).round(-1))
- assert_equal(Bignum, (-1111_1111_1111_1111_1111_1111_1111_1111).round(-1).class)
+ assert_int_equal(11111, 11111.round)
+ assert_int_equal(11111, 11111.round(0))
+
+ assert_int_equal(11111, 11111.round(1))
+ assert_int_equal(11111, 11111.round(2))
+
+ assert_int_equal(11110, 11111.round(-1))
+ assert_int_equal(11100, 11111.round(-2))
+ assert_int_equal(+200, +249.round(-2))
+ assert_int_equal(+300, +250.round(-2))
+ assert_int_equal(-200, -249.round(-2))
+ assert_int_equal(+200, +249.round(-2, half: :even))
+ assert_int_equal(+200, +250.round(-2, half: :even))
+ assert_int_equal(+300, +349.round(-2, half: :even))
+ assert_int_equal(+400, +350.round(-2, half: :even))
+ assert_int_equal(+200, +249.round(-2, half: :up))
+ assert_int_equal(+300, +250.round(-2, half: :up))
+ assert_int_equal(+300, +349.round(-2, half: :up))
+ assert_int_equal(+400, +350.round(-2, half: :up))
+ assert_int_equal(+200, +249.round(-2, half: :down))
+ assert_int_equal(+200, +250.round(-2, half: :down))
+ assert_int_equal(+300, +349.round(-2, half: :down))
+ assert_int_equal(+300, +350.round(-2, half: :down))
+ assert_int_equal(-300, -250.round(-2))
+ assert_int_equal(-200, -249.round(-2, half: :even))
+ assert_int_equal(-200, -250.round(-2, half: :even))
+ assert_int_equal(-300, -349.round(-2, half: :even))
+ assert_int_equal(-400, -350.round(-2, half: :even))
+ assert_int_equal(-200, -249.round(-2, half: :up))
+ assert_int_equal(-300, -250.round(-2, half: :up))
+ assert_int_equal(-300, -349.round(-2, half: :up))
+ assert_int_equal(-400, -350.round(-2, half: :up))
+ assert_int_equal(-200, -249.round(-2, half: :down))
+ assert_int_equal(-200, -250.round(-2, half: :down))
+ assert_int_equal(-300, -349.round(-2, half: :down))
+ assert_int_equal(-300, -350.round(-2, half: :down))
+ assert_int_equal(+30 * 10**70, (+25 * 10**70).round(-71))
+ assert_int_equal(-30 * 10**70, (-25 * 10**70).round(-71))
+ assert_int_equal(+20 * 10**70, (+25 * 10**70 - 1).round(-71))
+ assert_int_equal(-20 * 10**70, (-25 * 10**70 + 1).round(-71))
+ assert_int_equal(+40 * 10**70, (+35 * 10**70).round(-71))
+ assert_int_equal(-40 * 10**70, (-35 * 10**70).round(-71))
+ assert_int_equal(+30 * 10**70, (+35 * 10**70 - 1).round(-71))
+ assert_int_equal(-30 * 10**70, (-35 * 10**70 + 1).round(-71))
+ assert_int_equal(+20 * 10**70, (+25 * 10**70).round(-71, half: :even))
+ assert_int_equal(-20 * 10**70, (-25 * 10**70).round(-71, half: :even))
+ assert_int_equal(+20 * 10**70, (+25 * 10**70 - 1).round(-71, half: :even))
+ assert_int_equal(-20 * 10**70, (-25 * 10**70 + 1).round(-71, half: :even))
+ assert_int_equal(+40 * 10**70, (+35 * 10**70).round(-71, half: :even))
+ assert_int_equal(-40 * 10**70, (-35 * 10**70).round(-71, half: :even))
+ assert_int_equal(+30 * 10**70, (+35 * 10**70 - 1).round(-71, half: :even))
+ assert_int_equal(-30 * 10**70, (-35 * 10**70 + 1).round(-71, half: :even))
+ assert_int_equal(+30 * 10**70, (+25 * 10**70).round(-71, half: :up))
+ assert_int_equal(-30 * 10**70, (-25 * 10**70).round(-71, half: :up))
+ assert_int_equal(+20 * 10**70, (+25 * 10**70 - 1).round(-71, half: :up))
+ assert_int_equal(-20 * 10**70, (-25 * 10**70 + 1).round(-71, half: :up))
+ assert_int_equal(+40 * 10**70, (+35 * 10**70).round(-71, half: :up))
+ assert_int_equal(-40 * 10**70, (-35 * 10**70).round(-71, half: :up))
+ assert_int_equal(+30 * 10**70, (+35 * 10**70 - 1).round(-71, half: :up))
+ assert_int_equal(-30 * 10**70, (-35 * 10**70 + 1).round(-71, half: :up))
+ assert_int_equal(+20 * 10**70, (+25 * 10**70).round(-71, half: :down))
+ assert_int_equal(-20 * 10**70, (-25 * 10**70).round(-71, half: :down))
+ assert_int_equal(+20 * 10**70, (+25 * 10**70 - 1).round(-71, half: :down))
+ assert_int_equal(-20 * 10**70, (-25 * 10**70 + 1).round(-71, half: :down))
+ assert_int_equal(+30 * 10**70, (+35 * 10**70).round(-71, half: :down))
+ assert_int_equal(-30 * 10**70, (-35 * 10**70).round(-71, half: :down))
+ assert_int_equal(+30 * 10**70, (+35 * 10**70 - 1).round(-71, half: :down))
+ assert_int_equal(-30 * 10**70, (-35 * 10**70 + 1).round(-71, half: :down))
+
+ assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1110, 1111_1111_1111_1111_1111_1111_1111_1111.round(-1))
+ assert_int_equal(-1111_1111_1111_1111_1111_1111_1111_1110, (-1111_1111_1111_1111_1111_1111_1111_1111).round(-1))
+
+ assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1111, 1111_1111_1111_1111_1111_1111_1111_1111.round(1))
+ assert_int_equal(10**400, (10**400).round(1))
+ end
+
+ def test_floor
+ assert_int_equal(11111, 11111.floor)
+ assert_int_equal(11111, 11111.floor(0))
+
+ assert_int_equal(11111, 11111.floor(1))
+ assert_int_equal(11111, 11111.floor(2))
+
+ assert_int_equal(11110, 11110.floor(-1))
+ assert_int_equal(11110, 11119.floor(-1))
+ assert_int_equal(11100, 11100.floor(-2))
+ assert_int_equal(11100, 11199.floor(-2))
+ assert_int_equal(0, 11111.floor(-5))
+ assert_int_equal(+200, +299.floor(-2))
+ assert_int_equal(+300, +300.floor(-2))
+ assert_int_equal(-300, -299.floor(-2))
+ assert_int_equal(-300, -300.floor(-2))
+ assert_int_equal(+20 * 10**70, (+25 * 10**70).floor(-71))
+ assert_int_equal(-30 * 10**70, (-25 * 10**70).floor(-71))
+ assert_int_equal(+20 * 10**70, (+25 * 10**70 - 1).floor(-71))
+ assert_int_equal(-30 * 10**70, (-25 * 10**70 + 1).floor(-71))
+
+ assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1110, 1111_1111_1111_1111_1111_1111_1111_1111.floor(-1))
+ assert_int_equal(-1111_1111_1111_1111_1111_1111_1111_1120, (-1111_1111_1111_1111_1111_1111_1111_1111).floor(-1))
+
+ assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1111, 1111_1111_1111_1111_1111_1111_1111_1111.floor(1))
+ assert_int_equal(10**400, (10**400).floor(1))
+ end
+
+ def test_ceil
+ assert_int_equal(11111, 11111.ceil)
+ assert_int_equal(11111, 11111.ceil(0))
+
+ assert_int_equal(11111, 11111.ceil(1))
+ assert_int_equal(11111, 11111.ceil(2))
+
+ assert_int_equal(11110, 11110.ceil(-1))
+ assert_int_equal(11120, 11119.ceil(-1))
+ assert_int_equal(11200, 11101.ceil(-2))
+ assert_int_equal(11200, 11200.ceil(-2))
+ assert_int_equal(100000, 11111.ceil(-5))
+ assert_int_equal(300, 299.ceil(-2))
+ assert_int_equal(300, 300.ceil(-2))
+ assert_int_equal(-200, -299.ceil(-2))
+ assert_int_equal(-300, -300.ceil(-2))
+ assert_int_equal(+30 * 10**70, (+25 * 10**70).ceil(-71))
+ assert_int_equal(-20 * 10**70, (-25 * 10**70).ceil(-71))
+ assert_int_equal(+30 * 10**70, (+25 * 10**70 - 1).ceil(-71))
+ assert_int_equal(-20 * 10**70, (-25 * 10**70 + 1).ceil(-71))
+
+ assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1120, 1111_1111_1111_1111_1111_1111_1111_1111.ceil(-1))
+ assert_int_equal(-1111_1111_1111_1111_1111_1111_1111_1110, (-1111_1111_1111_1111_1111_1111_1111_1111).ceil(-1))
+
+ assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1111, 1111_1111_1111_1111_1111_1111_1111_1111.ceil(1))
+ assert_int_equal(10**400, (10**400).ceil(1))
+ end
+
+ def test_truncate
+ assert_int_equal(11111, 11111.truncate)
+ assert_int_equal(11111, 11111.truncate(0))
+
+ assert_int_equal(11111, 11111.truncate(1))
+ assert_int_equal(11111, 11111.truncate(2))
+
+ assert_int_equal(11110, 11110.truncate(-1))
+ assert_int_equal(11110, 11119.truncate(-1))
+ assert_int_equal(11100, 11100.truncate(-2))
+ assert_int_equal(11100, 11199.truncate(-2))
+ assert_int_equal(0, 11111.truncate(-5))
+ assert_int_equal(+200, +299.truncate(-2))
+ assert_int_equal(+300, +300.truncate(-2))
+ assert_int_equal(-200, -299.truncate(-2))
+ assert_int_equal(-300, -300.truncate(-2))
+ assert_int_equal(+20 * 10**70, (+25 * 10**70).truncate(-71))
+ assert_int_equal(-20 * 10**70, (-25 * 10**70).truncate(-71))
+ assert_int_equal(+20 * 10**70, (+25 * 10**70 - 1).truncate(-71))
+ assert_int_equal(-20 * 10**70, (-25 * 10**70 + 1).truncate(-71))
+
+ assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1110, 1111_1111_1111_1111_1111_1111_1111_1111.truncate(-1))
+ assert_int_equal(-1111_1111_1111_1111_1111_1111_1111_1110, (-1111_1111_1111_1111_1111_1111_1111_1111).truncate(-1))
+
+ assert_int_equal(1111_1111_1111_1111_1111_1111_1111_1111, 1111_1111_1111_1111_1111_1111_1111_1111.truncate(1))
+ assert_int_equal(10**400, (10**400).truncate(1))
+ end
+
+ MimicInteger = Struct.new(:to_int)
+ module CoercionToInt
+ def coerce(other)
+ [other, to_int]
+ end
+ end
+
+ def test_bitwise_and_with_integer_mimic_object
+ obj = MimicInteger.new(10)
+ assert_raise(TypeError, '[ruby-core:39491]') { 3 & obj }
+ obj.extend(CoercionToInt)
+ assert_equal(3 & 10, 3 & obj)
+ end
+
+ def test_bitwise_or_with_integer_mimic_object
+ obj = MimicInteger.new(10)
+ assert_raise(TypeError, '[ruby-core:39491]') { 3 | obj }
+ obj.extend(CoercionToInt)
+ assert_equal(3 | 10, 3 | obj)
+ end
+
+ def test_bitwise_xor_with_integer_mimic_object
+ obj = MimicInteger.new(10)
+ assert_raise(TypeError, '[ruby-core:39491]') { 3 ^ obj }
+ obj.extend(CoercionToInt)
+ assert_equal(3 ^ 10, 3 ^ obj)
+ end
+
+ module CoercionToSelf
+ def coerce(other)
+ [self.class.new(other), self]
+ end
+ end
+
+ def test_bitwise_and_with_integer_coercion
+ obj = Struct.new(:value) do
+ include(CoercionToSelf)
+ def &(other)
+ self.value & other.value
+ end
+ end.new(10)
+ assert_equal(3 & 10, 3 & obj)
+ end
+
+ def test_bitwise_or_with_integer_coercion
+ obj = Struct.new(:value) do
+ include(CoercionToSelf)
+ def |(other)
+ self.value | other.value
+ end
+ end.new(10)
+ assert_equal(3 | 10, 3 | obj)
+ end
+
+ def test_bitwise_xor_with_integer_coercion
+ obj = Struct.new(:value) do
+ include(CoercionToSelf)
+ def ^(other)
+ self.value ^ other.value
+ end
+ end.new(10)
+ assert_equal(3 ^ 10, 3 ^ obj)
+ end
+
+ def test_bit_length
+ assert_equal(13, (-2**12-1).bit_length)
+ assert_equal(12, (-2**12).bit_length)
+ assert_equal(12, (-2**12+1).bit_length)
+ assert_equal(9, -0x101.bit_length)
+ assert_equal(8, -0x100.bit_length)
+ assert_equal(8, -0xff.bit_length)
+ assert_equal(1, -2.bit_length)
+ assert_equal(0, -1.bit_length)
+ assert_equal(0, 0.bit_length)
+ assert_equal(1, 1.bit_length)
+ assert_equal(8, 0xff.bit_length)
+ assert_equal(9, 0x100.bit_length)
+ assert_equal(9, 0x101.bit_length)
+ assert_equal(12, (2**12-1).bit_length)
+ assert_equal(13, (2**12).bit_length)
+ assert_equal(13, (2**12+1).bit_length)
+
+ assert_equal(10001, (-2**10000-1).bit_length)
+ assert_equal(10000, (-2**10000).bit_length)
+ assert_equal(10000, (-2**10000+1).bit_length)
+ assert_equal(10000, (2**10000-1).bit_length)
+ assert_equal(10001, (2**10000).bit_length)
+ assert_equal(10001, (2**10000+1).bit_length)
+
+ 2.upto(1000) {|i|
+ n = 2**i
+ assert_equal(i+1, (-n-1).bit_length, "(#{-n-1}).bit_length")
+ assert_equal(i, (-n).bit_length, "(#{-n}).bit_length")
+ assert_equal(i, (-n+1).bit_length, "(#{-n+1}).bit_length")
+ assert_equal(i, (n-1).bit_length, "#{n-1}.bit_length")
+ assert_equal(i+1, (n).bit_length, "#{n}.bit_length")
+ assert_equal(i+1, (n+1).bit_length, "#{n+1}.bit_length")
+ }
+ end
+
+ def test_digits
+ assert_equal([0], 0.digits)
+ assert_equal([1], 1.digits)
+ 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))
+ end
+
+ def test_digits_for_negative_numbers
+ assert_raise(Math::DomainError) { -1.digits }
+ assert_raise(Math::DomainError) { -1234567890.digits }
+ assert_raise(Math::DomainError) { -1234567890.digits(100) }
+ assert_raise(Math::DomainError) { -1234567890.digits(13) }
+ end
+
+ def test_digits_for_invalid_base_numbers
+ assert_raise(ArgumentError) { 10.digits(-1) }
+ assert_raise(ArgumentError) { 10.digits(0) }
+ assert_raise(ArgumentError) { 10.digits(1) }
+ end
+
+ def test_digits_for_non_integral_base_numbers
+ assert_equal([1], 1.digits(10r))
+ assert_equal([1], 1.digits(10.0))
+ assert_raise(RangeError) { 10.digits(10+1i) }
+ end
+
+ def test_digits_for_non_numeric_base_argument
+ assert_raise(TypeError) { 10.digits("10") }
+ assert_raise(TypeError) { 10.digits("a") }
+
+ class << (o = Object.new)
+ def to_int
+ 10
+ end
+ end
+ assert_equal([0, 1], 10.digits(o))
+ end
+
+ def test_square_root
+ assert_raise(TypeError) {Integer.sqrt("x")}
+ assert_raise(Math::DomainError) {Integer.sqrt(-1)}
+ assert_equal(0, Integer.sqrt(0))
+ (1...4).each {|i| assert_equal(1, Integer.sqrt(i))}
+ (4...9).each {|i| assert_equal(2, Integer.sqrt(i))}
+ (9...16).each {|i| assert_equal(3, Integer.sqrt(i))}
+ (1..40).each do |i|
+ mesg = "10**#{i}"
+ s = Integer.sqrt(n = 10**i)
+ if i.even?
+ assert_equal(10**(i/2), Integer.sqrt(n), mesg)
+ else
+ assert_include((s**2)...(s+1)**2, n, mesg)
+ end
+ end
+ 50.step(400, 10) do |i|
+ exact = 10**(i/2)
+ x = 10**i
+ assert_equal(exact, Integer.sqrt(x), "10**#{i}")
+ assert_equal(exact, Integer.sqrt(x+1), "10**#{i}+1")
+ assert_equal(exact-1, Integer.sqrt(x-1), "10**#{i}-1")
+ end
+
+ bug13440 = '[ruby-core:80696] [Bug #13440]'
+ failures = []
+ 0.step(to: 50, by: 0.05) do |i|
+ n = (10**i).to_i
+ root = Integer.sqrt(n)
+ failures << n unless root*root <= n && (root+1)*(root+1) > n
+ end
+ assert_empty(failures, bug13440)
+
+ x = 0xffff_ffff_ffff_ffff
+ assert_equal(x, Integer.sqrt(x ** 2), "[ruby-core:95453]")
+ end
+
+ def test_fdiv
+ assert_equal(1.0, 1.fdiv(1))
+ assert_equal(0.5, 1.fdiv(2))
+ end
+
+ def test_obj_fdiv
+ o = Object.new
+ def o.coerce(x); [x, 0.5]; end
+ assert_equal(2.0, 1.fdiv(o))
+ o = Object.new
+ def o.coerce(x); [self, x]; end
+ def o.fdiv(x); 1; end
+ assert_equal(1.0, 1.fdiv(o))
end
end
diff --git a/test/ruby/test_integer_comb.rb b/test/ruby/test_integer_comb.rb
index c057deb36f..1ad13dd31b 100644
--- a/test/ruby/test_integer_comb.rb
+++ b/test/ruby/test_integer_comb.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestIntegerComb < Test::Unit::TestCase
@@ -106,28 +107,8 @@ class TestIntegerComb < Test::Unit::TestCase
]
#VS.map! {|v| 0x4000000000000000.coerce(v)[0] }
-
- min = -1
- min *= 2 while min.class == Fixnum
- FIXNUM_MIN = min/2
- max = 1
- max *= 2 while (max-1).class == Fixnum
- FIXNUM_MAX = max/2-1
-
- def test_fixnum_range
- assert_instance_of(Bignum, FIXNUM_MIN-1)
- assert_instance_of(Fixnum, FIXNUM_MIN)
- assert_instance_of(Fixnum, FIXNUM_MAX)
- assert_instance_of(Bignum, FIXNUM_MAX+1)
- end
-
- def check_class(n)
- if FIXNUM_MIN <= n && n <= FIXNUM_MAX
- assert_instance_of(Fixnum, n)
- else
- assert_instance_of(Bignum, n)
- end
- end
+ #VS.concat VS.find_all {|v| Fixnum === v }.map {|v| 0x4000000000000000.coerce(v)[0] }
+ #VS.sort! {|a, b| a.abs <=> b.abs }
def test_aref
VS.each {|a|
@@ -139,7 +120,7 @@ class TestIntegerComb < Test::Unit::TestCase
VS.each {|b|
c = nil
assert_nothing_raised("(#{a})[#{b}]") { c = a[b] }
- check_class(c)
+ assert_kind_of(Integer, c)
if b < 0
assert_equal(0, c, "(#{a})[#{b}]")
else
@@ -153,7 +134,7 @@ class TestIntegerComb < Test::Unit::TestCase
VS.each {|a|
VS.each {|b|
c = a + b
- check_class(c)
+ assert_kind_of(Integer, c)
assert_equal(b + a, c, "#{a} + #{b}")
assert_equal(a, c - b, "(#{a} + #{b}) - #{b}")
assert_equal(a-~b-1, c, "#{a} + #{b}") # Hacker's Delight
@@ -168,7 +149,7 @@ class TestIntegerComb < Test::Unit::TestCase
VS.each {|a|
VS.each {|b|
c = a - b
- check_class(c)
+ assert_kind_of(Integer, c)
assert_equal(a, c + b, "(#{a} - #{b}) + #{b}")
assert_equal(-b, c - a, "(#{a} - #{b}) - #{a}")
assert_equal(a+~b+1, c, "#{a} - #{b}") # Hacker's Delight
@@ -183,8 +164,9 @@ class TestIntegerComb < Test::Unit::TestCase
VS.each {|a|
VS.each {|b|
c = a * b
- check_class(c)
+ assert_kind_of(Integer, c)
assert_equal(b * a, c, "#{a} * #{b}")
+ assert_equal(b.send(:*, a), c, "#{a} * #{b}")
assert_equal(b, c / a, "(#{a} * #{b}) / #{a}") if a != 0
assert_equal(a.abs * b.abs, (a * b).abs, "(#{a} * #{b}).abs")
assert_equal((a-100)*(b-100)+(a-100)*100+(b-100)*100+10000, c, "#{a} * #{b}")
@@ -200,11 +182,17 @@ class TestIntegerComb < Test::Unit::TestCase
assert_raise(ZeroDivisionError) { a.divmod(b) }
else
q, r = a.divmod(b)
- check_class(q)
- check_class(r)
+ assert_kind_of(Integer, q)
+ assert_kind_of(Integer, r)
assert_equal(a, b*q+r)
- assert(r.abs < b.abs)
- assert(0 < b ? (0 <= r && r < b) : (b < r && r <= 0))
+ assert_operator(r.abs, :<, b.abs)
+ if 0 < b
+ assert_operator(r, :>=, 0)
+ assert_operator(r, :<, b)
+ else
+ assert_operator(r, :>, b)
+ assert_operator(r, :<=, 0)
+ end
assert_equal(q, a/b)
assert_equal(q, a.div(b))
assert_equal(r, a%b)
@@ -219,7 +207,7 @@ class TestIntegerComb < Test::Unit::TestCase
VS.each {|a|
small_values.each {|b|
c = a ** b
- check_class(c)
+ assert_kind_of(Integer, c)
d = 1
b.times { d *= a }
assert_equal(d, c, "(#{a}) ** #{b}")
@@ -235,7 +223,7 @@ class TestIntegerComb < Test::Unit::TestCase
def test_not
VS.each {|a|
b = ~a
- check_class(b)
+ assert_kind_of(Integer, b)
assert_equal(-1 ^ a, b, "~#{a}")
assert_equal(-a-1, b, "~#{a}") # Hacker's Delight
assert_equal(0, a & b, "#{a} & ~#{a}")
@@ -247,7 +235,7 @@ class TestIntegerComb < Test::Unit::TestCase
VS.each {|a|
VS.each {|b|
c = a | b
- check_class(c)
+ assert_kind_of(Integer, c)
assert_equal(b | a, c, "#{a} | #{b}")
assert_equal(a + b - (a&b), c, "#{a} | #{b}")
assert_equal((a & ~b) + b, c, "#{a} | #{b}") # Hacker's Delight
@@ -260,7 +248,7 @@ class TestIntegerComb < Test::Unit::TestCase
VS.each {|a|
VS.each {|b|
c = a & b
- check_class(c)
+ assert_kind_of(Integer, c)
assert_equal(b & a, c, "#{a} & #{b}")
assert_equal(a + b - (a|b), c, "#{a} & #{b}")
assert_equal((~a | b) - ~a, c, "#{a} & #{b}") # Hacker's Delight
@@ -273,7 +261,7 @@ class TestIntegerComb < Test::Unit::TestCase
VS.each {|a|
VS.each {|b|
c = a ^ b
- check_class(c)
+ assert_kind_of(Integer, c)
assert_equal(b ^ a, c, "#{a} ^ #{b}")
assert_equal((a|b)-(a&b), c, "#{a} ^ #{b}") # Hacker's Delight
assert_equal(b, c ^ a, "(#{a} ^ #{b}) ^ #{a}")
@@ -286,12 +274,12 @@ class TestIntegerComb < Test::Unit::TestCase
VS.each {|a|
small_values.each {|b|
c = a << b
- check_class(c)
+ assert_kind_of(Integer, c)
if 0 <= b
assert_equal(a, c >> b, "(#{a} << #{b}) >> #{b}")
assert_equal(a * 2**b, c, "#{a} << #{b}")
end
- 0.upto(c.size*8+10) {|nth|
+ 0.upto(c.bit_length+10) {|nth|
assert_equal(a[nth-b], c[nth], "(#{a} << #{b})[#{nth}]")
}
}
@@ -303,12 +291,12 @@ class TestIntegerComb < Test::Unit::TestCase
VS.each {|a|
small_values.each {|b|
c = a >> b
- check_class(c)
+ assert_kind_of(Integer, c)
if b <= 0
assert_equal(a, c << b, "(#{a} >> #{b}) << #{b}")
assert_equal(a * 2**(-b), c, "#{a} >> #{b}")
end
- 0.upto(c.size*8+10) {|nth|
+ 0.upto(c.bit_length+10) {|nth|
assert_equal(a[nth+b], c[nth], "(#{a} >> #{b})[#{nth}]")
}
}
@@ -318,7 +306,7 @@ class TestIntegerComb < Test::Unit::TestCase
def test_succ
VS.each {|a|
b = a.succ
- check_class(b)
+ assert_kind_of(Integer, b)
assert_equal(a+1, b, "(#{a}).succ")
assert_equal(a, b.pred, "(#{a}).succ.pred")
assert_equal(a, b-1, "(#{a}).succ - 1")
@@ -328,7 +316,7 @@ class TestIntegerComb < Test::Unit::TestCase
def test_pred
VS.each {|a|
b = a.pred
- check_class(b)
+ assert_kind_of(Integer, b)
assert_equal(a-1, b, "(#{a}).pred")
assert_equal(a, b.succ, "(#{a}).pred.succ")
assert_equal(a, b + 1, "(#{a}).pred + 1")
@@ -338,7 +326,7 @@ class TestIntegerComb < Test::Unit::TestCase
def test_unary_plus
VS.each {|a|
b = +a
- check_class(b)
+ assert_kind_of(Integer, b)
assert_equal(a, b, "+(#{a})")
}
end
@@ -346,7 +334,7 @@ class TestIntegerComb < Test::Unit::TestCase
def test_unary_minus
VS.each {|a|
b = -a
- check_class(b)
+ assert_kind_of(Integer, b)
assert_equal(0-a, b, "-(#{a})")
assert_equal(~a+1, b, "-(#{a})")
assert_equal(0, a+b, "#{a}+(-(#{a}))")
@@ -378,7 +366,7 @@ class TestIntegerComb < Test::Unit::TestCase
def test_abs
VS.each {|a|
b = a.abs
- check_class(b)
+ assert_kind_of(Integer, b)
if a < 0
assert_equal(-a, b, "(#{a}).abs")
else
@@ -390,7 +378,7 @@ class TestIntegerComb < Test::Unit::TestCase
def test_ceil
VS.each {|a|
b = a.ceil
- check_class(b)
+ assert_kind_of(Integer, b)
assert_equal(a, b, "(#{a}).ceil")
}
end
@@ -398,7 +386,7 @@ class TestIntegerComb < Test::Unit::TestCase
def test_floor
VS.each {|a|
b = a.floor
- check_class(b)
+ assert_kind_of(Integer, b)
assert_equal(a, b, "(#{a}).floor")
}
end
@@ -406,7 +394,7 @@ class TestIntegerComb < Test::Unit::TestCase
def test_round
VS.each {|a|
b = a.round
- check_class(b)
+ assert_kind_of(Integer, b)
assert_equal(a, b, "(#{a}).round")
}
end
@@ -414,7 +402,7 @@ class TestIntegerComb < Test::Unit::TestCase
def test_truncate
VS.each {|a|
b = a.truncate
- check_class(b)
+ assert_kind_of(Integer, b)
assert_equal(a, b, "(#{a}).truncate")
}
end
@@ -426,7 +414,7 @@ class TestIntegerComb < Test::Unit::TestCase
assert_raise(ZeroDivisionError) { a.divmod(b) }
else
r = a.remainder(b)
- check_class(r)
+ assert_kind_of(Integer, r)
if a < 0
assert_operator(-b.abs, :<, r, "#{a}.remainder(#{b})")
assert_operator(0, :>=, r, "#{a}.remainder(#{b})")
@@ -451,7 +439,7 @@ class TestIntegerComb < Test::Unit::TestCase
else
assert_equal(false, z, "(#{a}).zero?")
assert_equal(a, n, "(#{a}).nonzero?")
- check_class(n)
+ assert_kind_of(Integer, n)
end
assert(z ^ n, "(#{a}).zero? ^ (#{a}).nonzero?")
}
@@ -469,6 +457,30 @@ class TestIntegerComb < Test::Unit::TestCase
}
end
+ def test_allbits_p
+ VS.each {|a|
+ VS.each {|b|
+ assert_equal((a & b) == b, a.allbits?(b), "(#{a}).allbits?(#{b}")
+ }
+ }
+ end
+
+ def test_anybits_p
+ VS.each {|a|
+ VS.each {|b|
+ assert_equal((a & b) != 0, a.anybits?(b), "(#{a}).anybits?(#{b}")
+ }
+ }
+ end
+
+ def test_nobits_p
+ VS.each {|a|
+ VS.each {|b|
+ assert_equal((a & b) == 0, a.nobits?(b), "(#{a}).nobits?(#{b}")
+ }
+ }
+ end
+
def test_to_s
2.upto(36) {|radix|
VS.each {|a|
diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb
index 41d3640105..92fba2c04e 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -1,35 +1,116 @@
+# coding: US-ASCII
+# frozen_string_literal: false
require 'test/unit'
require 'tmpdir'
require "fcntl"
require 'io/nonblock'
+require 'pathname'
require 'socket'
require 'stringio'
require 'timeout'
require 'tempfile'
-require_relative 'envutil'
+require 'weakref'
class TestIO < Test::Unit::TestCase
- def have_close_on_exec?
- begin
+ module Feature
+ def have_close_on_exec?
$stdin.close_on_exec?
true
rescue NotImplementedError
false
end
+
+ def have_nonblock?
+ IO.method_defined?("nonblock=")
+ end
end
- def have_nonblock?
- IO.method_defined?("nonblock=")
+ include Feature
+ extend Feature
+
+ def pipe(wp, rp)
+ re, we = nil, nil
+ r, w = IO.pipe
+ rt = Thread.new do
+ begin
+ rp.call(r)
+ rescue Exception
+ r.close
+ re = $!
+ end
+ end
+ wt = Thread.new do
+ begin
+ wp.call(w)
+ rescue Exception
+ w.close
+ we = $!
+ end
+ end
+ flunk("timeout") unless wt.join(10) && rt.join(10)
+ ensure
+ w&.close
+ r&.close
+ (wt.kill; wt.join) if wt
+ (rt.kill; rt.join) if rt
+ raise we if we
+ raise re if re
+ end
+
+ def with_pipe
+ r, w = IO.pipe
+ begin
+ yield r, w
+ ensure
+ r.close
+ w.close
+ end
+ end
+
+ def with_read_pipe(content)
+ pipe(proc do |w|
+ w << content
+ w.close
+ end, proc do |r|
+ yield r
+ end)
+ end
+
+ def mkcdtmpdir
+ Dir.mktmpdir {|d|
+ Dir.chdir(d) {
+ yield
+ }
+ }
+ end
+
+ def trapping_usr2
+ @usr2_rcvd = 0
+ r, w = IO.pipe
+ trap(:USR2) do
+ w.write([@usr2_rcvd += 1].pack('L'))
+ end
+ yield r
+ ensure
+ trap(:USR2, "DEFAULT")
+ w&.close
+ r&.close
end
def test_pipe
r, w = IO.pipe
assert_instance_of(IO, r)
assert_instance_of(IO, w)
- w.print "abc"
- w.close
- assert_equal("abc", r.read)
- r.close
+ [
+ Thread.start{
+ w.print "abc"
+ w.close
+ },
+ Thread.start{
+ assert_equal("abc", r.read)
+ r.close
+ }
+ ].each{|thr| thr.join}
end
def test_pipe_block
@@ -38,16 +119,22 @@ class TestIO < Test::Unit::TestCase
x = [r,w]
assert_instance_of(IO, r)
assert_instance_of(IO, w)
- w.print "abc"
- w.close
- assert_equal("abc", r.read)
- assert(!r.closed?)
- assert(w.closed?)
+ [
+ Thread.start do
+ w.print "abc"
+ w.close
+ end,
+ Thread.start do
+ assert_equal("abc", r.read)
+ end
+ ].each{|thr| thr.join}
+ assert_not_predicate(r, :closed?)
+ assert_predicate(w, :closed?)
:foooo
}
assert_equal(:foooo, ret)
- assert(x[0].closed?)
- assert(x[1].closed?)
+ assert_predicate(x[0], :closed?)
+ assert_predicate(x[1], :closed?)
end
def test_pipe_block_close
@@ -58,107 +145,218 @@ class TestIO < Test::Unit::TestCase
r.close if (i&1) == 0
w.close if (i&2) == 0
}
- assert(x[0].closed?)
- assert(x[1].closed?)
+ assert_predicate(x[0], :closed?)
+ assert_predicate(x[1], :closed?)
}
end
def test_gets_rs
- # default_rs
- r, w = IO.pipe
- w.print "aaa\nbbb\n"
- w.close
- assert_equal "aaa\n", r.gets
- assert_equal "bbb\n", r.gets
- assert_nil r.gets
- r.close
-
- # nil
- r, w = IO.pipe
- w.print "a\n\nb\n\n"
- w.close
- assert_equal "a\n\nb\n\n", r.gets(nil)
- assert_nil r.gets("")
- r.close
-
- # "\377"
- r, w = IO.pipe('ascii-8bit')
- w.print "\377xyz"
- w.close
- r.binmode
- assert_equal("\377", r.gets("\377"), "[ruby-dev:24460]")
- r.close
-
- # ""
- r, w = IO.pipe
- w.print "a\n\nb\n\n"
- w.close
- assert_equal "a\n\n", r.gets(""), "[ruby-core:03771]"
- assert_equal "b\n\n", r.gets("")
- assert_nil r.gets("")
- r.close
+ rs = ":"
+ pipe(proc do |w|
+ w.print "aaa:bbb"
+ w.close
+ end, proc do |r|
+ assert_equal "aaa:", r.gets(rs)
+ assert_equal "bbb", r.gets(rs)
+ assert_nil r.gets(rs)
+ r.close
+ end)
+ end
+
+ def test_gets_default_rs
+ pipe(proc do |w|
+ w.print "aaa\nbbb\n"
+ w.close
+ end, proc do |r|
+ assert_equal "aaa\n", r.gets
+ assert_equal "bbb\n", r.gets
+ assert_nil r.gets
+ r.close
+ end)
+ end
+
+ def test_gets_rs_nil
+ pipe(proc do |w|
+ w.print "a\n\nb\n\n"
+ w.close
+ end, proc do |r|
+ assert_equal "a\n\nb\n\n", r.gets(nil)
+ assert_nil r.gets("")
+ r.close
+ end)
+ end
+
+ def test_gets_rs_377
+ pipe(proc do |w|
+ w.print "\377xyz"
+ w.close
+ end, proc do |r|
+ r.binmode
+ assert_equal("\377", r.gets("\377"), "[ruby-dev:24460]")
+ r.close
+ end)
+ end
+
+ def test_gets_paragraph
+ pipe(proc do |w|
+ w.print "a\n\nb\n\n"
+ w.close
+ end, proc do |r|
+ assert_equal "a\n\n", r.gets(""), "[ruby-core:03771]"
+ assert_equal "b\n\n", r.gets("")
+ assert_nil r.gets("")
+ r.close
+ end)
+ end
+
+ def test_gets_chomp_rs
+ rs = ":"
+ pipe(proc do |w|
+ w.print "aaa:bbb"
+ w.close
+ end, proc do |r|
+ assert_equal "aaa", r.gets(rs, chomp: true)
+ assert_equal "bbb", r.gets(rs, chomp: true)
+ assert_nil r.gets(rs, chomp: true)
+ r.close
+ end)
+ end
+
+ def test_gets_chomp_default_rs
+ pipe(proc do |w|
+ w.print "aaa\r\nbbb\nccc"
+ w.close
+ end, proc do |r|
+ assert_equal "aaa", r.gets(chomp: true)
+ assert_equal "bbb", r.gets(chomp: true)
+ assert_equal "ccc", r.gets(chomp: true)
+ assert_nil r.gets
+ r.close
+ end)
+
+ (0..3).each do |i|
+ pipe(proc do |w|
+ w.write("a" * ((4096 << i) - 4), "\r\n" "a\r\n")
+ w.close
+ end,
+ proc do |r|
+ r.gets
+ assert_equal "a", r.gets(chomp: true)
+ assert_nil r.gets
+ r.close
+ end)
+ end
+ end
+
+ def test_gets_chomp_rs_nil
+ pipe(proc do |w|
+ w.print "a\n\nb\n\n"
+ w.close
+ end, proc do |r|
+ assert_equal "a\n\nb\n", r.gets(nil, chomp: true)
+ assert_nil r.gets("")
+ r.close
+ end)
+ end
+
+ def test_gets_chomp_paragraph
+ pipe(proc do |w|
+ w.print "a\n\nb\n\n"
+ w.close
+ end, proc do |r|
+ assert_equal "a", r.gets("", chomp: true)
+ assert_equal "b", r.gets("", chomp: true)
+ assert_nil r.gets("", chomp: true)
+ r.close
+ end)
end
def test_gets_limit_extra_arg
- with_pipe {|r, w|
- r, w = IO.pipe
+ pipe(proc do |w|
w << "0123456789\n0123456789"
w.close
+ end, proc do |r|
assert_equal("0123456789\n0", r.gets(nil, 12))
assert_raise(TypeError) { r.gets(3,nil) }
- }
+ end)
end
# This test cause SEGV.
def test_ungetc
- r, w = IO.pipe
- w.close
- s = "a" * 1000
- assert_raise(IOError, "[ruby-dev:31650]") { 200.times { r.ungetc s } }
- ensure
- r.close
+ pipe(proc do |w|
+ w.close
+ end, proc do |r|
+ s = "a" * 1000
+ assert_raise(IOError, "[ruby-dev:31650]") { 200.times { r.ungetc s } }
+ end)
end
def test_ungetbyte
- t = make_tempfile
- t.open
- t.binmode
- t.ungetbyte(0x41)
- assert_equal(-1, t.pos)
- assert_equal(0x41, t.getbyte)
- t.rewind
- assert_equal(0, t.pos)
- t.ungetbyte("qux")
- assert_equal(-3, t.pos)
- assert_equal("quxfoo\n", t.gets)
- assert_equal(4, t.pos)
- t.set_encoding("utf-8")
- t.ungetbyte(0x89)
- t.ungetbyte(0x8e)
- t.ungetbyte("\xe7")
- t.ungetbyte("\xe7\xb4\x85")
- assert_equal(-2, t.pos)
- assert_equal("\u7d05\u7389bar\n", t.gets)
+ make_tempfile {|t|
+ t.open
+ t.binmode
+ t.ungetbyte(0x41)
+ assert_equal(-1, t.pos)
+ assert_equal(0x41, t.getbyte)
+ t.rewind
+ assert_equal(0, t.pos)
+ t.ungetbyte("qux")
+ assert_equal(-3, t.pos)
+ assert_equal("quxfoo\n", t.gets)
+ assert_equal(4, t.pos)
+ t.set_encoding("utf-8")
+ t.ungetbyte(0x89)
+ t.ungetbyte(0x8e)
+ t.ungetbyte("\xe7")
+ t.ungetbyte("\xe7\xb4\x85")
+ assert_equal(-2, t.pos)
+ assert_equal("\u7d05\u7389bar\n", t.gets)
+ }
end
def test_each_byte
- r, w = IO.pipe
- w << "abc def"
- w.close
- r.each_byte {|byte| break if byte == 32 }
- assert_equal("def", r.read, "[ruby-dev:31659]")
- ensure
- r.close
+ pipe(proc do |w|
+ w << "abc def"
+ w.close
+ end, proc do |r|
+ r.each_byte {|byte| break if byte == 32 }
+ assert_equal("def", r.read, "[ruby-dev:31659]")
+ end)
+ end
+
+ def test_each_byte_with_seek
+ make_tempfile {|t|
+ bug5119 = '[ruby-core:38609]'
+ i = 0
+ open(t.path) do |f|
+ f.each_byte {i = f.pos}
+ end
+ assert_equal(12, i, bug5119)
+ }
end
def test_each_codepoint
- t = make_tempfile
- bug2959 = '[ruby-core:28650]'
- a = ""
- File.open(t, 'rt') {|f|
- f.each_codepoint {|c| a << c}
+ make_tempfile {|t|
+ bug2959 = '[ruby-core:28650]'
+ a = ""
+ File.open(t, 'rt') {|f|
+ f.each_codepoint {|c| a << c}
+ }
+ assert_equal("foo\nbar\nbaz\n", a, bug2959)
+ }
+ end
+
+ def test_codepoints
+ make_tempfile {|t|
+ bug2959 = '[ruby-core:28650]'
+ a = ""
+ File.open(t, 'rt') {|f|
+ assert_warn(/deprecated/) {
+ f.codepoints {|c| a << c}
+ }
+ }
+ assert_equal("foo\nbar\nbaz\n", a, bug2959)
}
- assert_equal("foo\nbar\nbaz\n", a, bug2959)
end
def test_rubydev33072
@@ -170,165 +368,216 @@ class TestIO < Test::Unit::TestCase
end
end
- def with_pipe
- r, w = IO.pipe
- begin
- yield r, w
- ensure
- r.close unless r.closed?
- w.close unless w.closed?
- end
+ def with_srccontent(content = "baz")
+ src = "src"
+ mkcdtmpdir {
+ File.open(src, "w") {|f| f << content }
+ yield src, content
+ }
end
- def with_read_pipe(content)
- r, w = IO.pipe
- w << content
- w.close
- begin
- yield r
- ensure
- r.close
- end
+ def test_copy_stream_small
+ with_srccontent("foobar") {|src, content|
+ ret = IO.copy_stream(src, "dst")
+ assert_equal(content.bytesize, ret)
+ assert_equal(content, File.read("dst"))
+ }
end
- def mkcdtmpdir
- Dir.mktmpdir {|d|
- Dir.chdir(d) {
- yield
- }
+ def test_copy_stream_append
+ with_srccontent("foobar") {|src, content|
+ File.open('dst', 'ab') do |dst|
+ ret = IO.copy_stream(src, dst)
+ assert_equal(content.bytesize, ret)
+ assert_equal(content, File.read("dst"))
+ end
}
end
- def test_copy_stream
- mkcdtmpdir {
-
- content = "foobar"
- File.open("src", "w") {|f| f << content }
- ret = IO.copy_stream("src", "dst")
- assert_equal(content.bytesize, ret)
- assert_equal(content, File.read("dst"))
+ def test_copy_stream_smaller
+ with_srccontent {|src, content|
# overwrite by smaller file.
- content = "baz"
- File.open("src", "w") {|f| f << content }
- ret = IO.copy_stream("src", "dst")
+ dst = "dst"
+ File.open(dst, "w") {|f| f << "foobar"}
+
+ ret = IO.copy_stream(src, dst)
assert_equal(content.bytesize, ret)
- assert_equal(content, File.read("dst"))
+ assert_equal(content, File.read(dst))
- ret = IO.copy_stream("src", "dst", 2)
+ ret = IO.copy_stream(src, dst, 2)
assert_equal(2, ret)
- assert_equal(content[0,2], File.read("dst"))
+ assert_equal(content[0,2], File.read(dst))
- ret = IO.copy_stream("src", "dst", 0)
+ ret = IO.copy_stream(src, dst, 0)
assert_equal(0, ret)
- assert_equal("", File.read("dst"))
+ assert_equal("", File.read(dst))
- ret = IO.copy_stream("src", "dst", nil, 1)
+ ret = IO.copy_stream(src, dst, nil, 1)
assert_equal(content.bytesize-1, ret)
- assert_equal(content[1..-1], File.read("dst"))
+ assert_equal(content[1..-1], File.read(dst))
+ }
+ end
+ def test_copy_stream_noent
+ with_srccontent {|src, content|
assert_raise(Errno::ENOENT) {
IO.copy_stream("nodir/foo", "dst")
}
assert_raise(Errno::ENOENT) {
- IO.copy_stream("src", "nodir/bar")
+ IO.copy_stream(src, "nodir/bar")
}
+ }
+ end
- with_pipe {|r, w|
- ret = IO.copy_stream("src", w)
+ def test_copy_stream_pipe
+ with_srccontent {|src, content|
+ pipe(proc do |w|
+ ret = IO.copy_stream(src, w)
assert_equal(content.bytesize, ret)
w.close
+ end, proc do |r|
assert_equal(content, r.read)
- }
+ end)
+ }
+ end
+ def test_copy_stream_write_pipe
+ with_srccontent {|src, content|
with_pipe {|r, w|
w.close
- assert_raise(IOError) { IO.copy_stream("src", w) }
+ assert_raise(IOError) { IO.copy_stream(src, w) }
}
+ }
+ end
+
+ def with_pipecontent
+ mkcdtmpdir {
+ yield "abc"
+ }
+ end
- pipe_content = "abc"
+ def test_copy_stream_pipe_to_file
+ with_pipecontent {|pipe_content|
+ dst = "dst"
with_read_pipe(pipe_content) {|r|
- ret = IO.copy_stream(r, "dst")
+ ret = IO.copy_stream(r, dst)
assert_equal(pipe_content.bytesize, ret)
- assert_equal(pipe_content, File.read("dst"))
+ assert_equal(pipe_content, File.read(dst))
}
+ }
+ end
- with_read_pipe("abc") {|r1|
+ def test_copy_stream_read_pipe
+ with_pipecontent {|pipe_content|
+ with_read_pipe(pipe_content) {|r1|
assert_equal("a", r1.getc)
- with_pipe {|r2, w2|
+ pipe(proc do |w2|
w2.sync = false
w2 << "def"
ret = IO.copy_stream(r1, w2)
assert_equal(2, ret)
w2.close
+ end, proc do |r2|
assert_equal("defbc", r2.read)
- }
+ end)
}
- with_read_pipe("abc") {|r1|
+ with_read_pipe(pipe_content) {|r1|
assert_equal("a", r1.getc)
- with_pipe {|r2, w2|
+ pipe(proc do |w2|
w2.sync = false
w2 << "def"
ret = IO.copy_stream(r1, w2, 1)
assert_equal(1, ret)
w2.close
+ end, proc do |r2|
assert_equal("defb", r2.read)
- }
+ end)
}
- with_read_pipe("abc") {|r1|
+ with_read_pipe(pipe_content) {|r1|
assert_equal("a", r1.getc)
- with_pipe {|r2, w2|
+ pipe(proc do |w2|
ret = IO.copy_stream(r1, w2)
assert_equal(2, ret)
w2.close
+ end, proc do |r2|
assert_equal("bc", r2.read)
- }
+ end)
}
- with_read_pipe("abc") {|r1|
+ with_read_pipe(pipe_content) {|r1|
assert_equal("a", r1.getc)
- with_pipe {|r2, w2|
+ pipe(proc do |w2|
ret = IO.copy_stream(r1, w2, 1)
assert_equal(1, ret)
w2.close
+ end, proc do |r2|
assert_equal("b", r2.read)
- }
+ end)
}
- with_read_pipe("abc") {|r1|
+ with_read_pipe(pipe_content) {|r1|
assert_equal("a", r1.getc)
- with_pipe {|r2, w2|
+ pipe(proc do |w2|
ret = IO.copy_stream(r1, w2, 0)
assert_equal(0, ret)
w2.close
+ end, proc do |r2|
assert_equal("", r2.read)
- }
+ end)
}
- with_pipe {|r1, w1|
+ pipe(proc do |w1|
w1 << "abc"
+ w1 << "def"
+ w1.close
+ end, proc do |r1|
assert_equal("a", r1.getc)
- with_pipe {|r2, w2|
- w1 << "def"
- w1.close
+ pipe(proc do |w2|
ret = IO.copy_stream(r1, w2)
assert_equal(5, ret)
w2.close
+ end, proc do |r2|
assert_equal("bcdef", r2.read)
- }
- }
+ end)
+ end)
+ }
+ end
- with_pipe {|r, w|
- ret = IO.copy_stream("src", w, 1, 1)
+ def test_copy_stream_file_to_pipe
+ with_srccontent {|src, content|
+ pipe(proc do |w|
+ ret = IO.copy_stream(src, w, 1, 1)
assert_equal(1, ret)
w.close
+ end, proc do |r|
assert_equal(content[1,1], r.read)
- }
+ end)
+ }
+ end
+
+ 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 "multiple threads already active" if Thread.list.size > 1
+
+ msg = 'r58534 [ruby-core:80969] [Backport #13533]'
+ IO.pipe do |r,w|
+ r.nonblock = true
+ assert_cpu_usage_low(msg) do
+ th = Thread.new { IO.copy_stream(r, IO::NULL) }
+ sleep 0.1
+ w.close
+ th.join
+ end
+ end
+ end
- if have_nonblock?
+ def test_copy_stream_pipe_nonblock
+ mkcdtmpdir {
with_read_pipe("abc") {|r1|
assert_equal("a", r1.getc)
with_pipe {|r2, w2|
@@ -336,7 +585,6 @@ class TestIO < Test::Unit::TestCase
w2.nonblock = true
rescue Errno::EBADF
skip "nonblocking IO for pipe is not implemented"
- break
end
s = w2.syswrite("a" * 100000)
t = Thread.new { sleep 0.1; r2.read }
@@ -346,25 +594,51 @@ class TestIO < Test::Unit::TestCase
assert_equal("a" * s + "bc", t.value)
}
}
- end
+ }
+ end
+ end
+
+ def with_bigcontent
+ yield "abc" * 123456
+ end
- bigcontent = "abc" * 123456
- File.open("bigsrc", "w") {|f| f << bigcontent }
- ret = IO.copy_stream("bigsrc", "bigdst")
+ def with_bigsrc
+ mkcdtmpdir {
+ with_bigcontent {|bigcontent|
+ bigsrc = "bigsrc"
+ File.open("bigsrc", "w") {|f| f << bigcontent }
+ yield bigsrc, bigcontent
+ }
+ }
+ end
+
+ def test_copy_stream_bigcontent
+ with_bigsrc {|bigsrc, bigcontent|
+ ret = IO.copy_stream(bigsrc, "bigdst")
assert_equal(bigcontent.bytesize, ret)
assert_equal(bigcontent, File.read("bigdst"))
+ }
+ end
- File.unlink("bigdst")
- ret = IO.copy_stream("bigsrc", "bigdst", nil, 100)
+ def test_copy_stream_bigcontent_chop
+ with_bigsrc {|bigsrc, bigcontent|
+ ret = IO.copy_stream(bigsrc, "bigdst", nil, 100)
assert_equal(bigcontent.bytesize-100, ret)
assert_equal(bigcontent[100..-1], File.read("bigdst"))
+ }
+ end
- File.unlink("bigdst")
- ret = IO.copy_stream("bigsrc", "bigdst", 30000, 100)
+ def test_copy_stream_bigcontent_mid
+ with_bigsrc {|bigsrc, bigcontent|
+ ret = IO.copy_stream(bigsrc, "bigdst", 30000, 100)
assert_equal(30000, ret)
assert_equal(bigcontent[100, 30000], File.read("bigdst"))
+ }
+ end
- File.open("bigsrc") {|f|
+ def test_copy_stream_bigcontent_fpos
+ with_bigsrc {|bigsrc, bigcontent|
+ File.open(bigsrc) {|f|
begin
assert_equal(0, f.pos)
ret = IO.copy_stream(f, "bigdst", nil, 10)
@@ -376,19 +650,38 @@ class TestIO < Test::Unit::TestCase
assert_equal(bigcontent[30, 40], File.read("bigdst"))
assert_equal(0, f.pos)
rescue NotImplementedError
- #skip "pread(2) is not implemtented."
+ #skip "pread(2) is not implemented."
end
}
+ }
+ end
+ def test_copy_stream_closed_pipe
+ with_srccontent {|src,|
with_pipe {|r, w|
w.close
- assert_raise(IOError) { IO.copy_stream("src", w) }
+ assert_raise(IOError) { IO.copy_stream(src, w) }
}
+ }
+ end
- megacontent = "abc" * 1234567
- File.open("megasrc", "w") {|f| f << megacontent }
+ def with_megacontent
+ yield "abc" * 1234567
+ end
- if have_nonblock?
+ def with_megasrc
+ mkcdtmpdir {
+ with_megacontent {|megacontent|
+ megasrc = "megasrc"
+ File.open(megasrc, "w") {|f| f << megacontent }
+ yield megasrc, megacontent
+ }
+ }
+ end
+
+ if have_nonblock?
+ def test_copy_stream_megacontent_nonblock
+ with_megacontent {|megacontent|
with_pipe {|r1, w1|
with_pipe {|r2, w2|
begin
@@ -399,33 +692,48 @@ class TestIO < Test::Unit::TestCase
end
t1 = Thread.new { w1 << megacontent; w1.close }
t2 = Thread.new { r2.read }
- ret = IO.copy_stream(r1, w2)
- assert_equal(megacontent.bytesize, ret)
- w2.close
- t1.join
- assert_equal(megacontent, t2.value)
+ t3 = Thread.new {
+ ret = IO.copy_stream(r1, w2)
+ assert_equal(megacontent.bytesize, ret)
+ w2.close
+ }
+ _, t2_value, _ = assert_join_threads([t1, t2, t3])
+ assert_equal(megacontent, t2_value)
}
}
- end
+ }
+ end
+ end
+ def test_copy_stream_megacontent_pipe_to_file
+ with_megasrc {|megasrc, megacontent|
with_pipe {|r1, w1|
with_pipe {|r2, w2|
t1 = Thread.new { w1 << megacontent; w1.close }
t2 = Thread.new { r2.read }
- ret = IO.copy_stream(r1, w2)
- assert_equal(megacontent.bytesize, ret)
- w2.close
- t1.join
- assert_equal(megacontent, t2.value)
+ t3 = Thread.new {
+ ret = IO.copy_stream(r1, w2)
+ assert_equal(megacontent.bytesize, ret)
+ w2.close
+ }
+ _, t2_value, _ = assert_join_threads([t1, t2, t3])
+ assert_equal(megacontent, t2_value)
}
}
+ }
+ end
+ def test_copy_stream_megacontent_file_to_pipe
+ with_megasrc {|megasrc, megacontent|
with_pipe {|r, w|
- t = Thread.new { r.read }
- ret = IO.copy_stream("megasrc", w)
- assert_equal(megacontent.bytesize, ret)
- w.close
- assert_equal(megacontent, t.value)
+ t1 = Thread.new { r.read }
+ t2 = Thread.new {
+ ret = IO.copy_stream(megasrc, w)
+ assert_equal(megacontent.bytesize, ret)
+ w.close
+ }
+ t1_value, _ = assert_join_threads([t1, t2])
+ assert_equal(megacontent, t1_value)
}
}
end
@@ -433,15 +741,16 @@ class TestIO < Test::Unit::TestCase
def test_copy_stream_rbuf
mkcdtmpdir {
begin
- with_pipe {|r, w|
+ pipe(proc do |w|
File.open("foo", "w") {|f| f << "abcd" }
File.open("foo") {|f|
f.read(1)
assert_equal(3, IO.copy_stream(f, w, 10, 1))
}
w.close
+ end, proc do |r|
assert_equal("bcd", r.read)
- }
+ end)
rescue NotImplementedError
skip "pread(2) is not implemtented."
end
@@ -458,88 +767,145 @@ class TestIO < Test::Unit::TestCase
end
end
- def test_copy_stream_socket
- return unless defined? UNIXSocket
- mkcdtmpdir {
-
- content = "foobar"
- File.open("src", "w") {|f| f << content }
-
+ def test_copy_stream_socket1
+ with_srccontent("foobar") {|src, content|
with_socketpair {|s1, s2|
- ret = IO.copy_stream("src", s1)
+ ret = IO.copy_stream(src, s1)
assert_equal(content.bytesize, ret)
s1.close
assert_equal(content, s2.read)
}
+ }
+ end if defined? UNIXSocket
- bigcontent = "abc" * 123456
- File.open("bigsrc", "w") {|f| f << bigcontent }
-
+ def test_copy_stream_socket2
+ with_bigsrc {|bigsrc, bigcontent|
with_socketpair {|s1, s2|
- t = Thread.new { s2.read }
- ret = IO.copy_stream("bigsrc", s1)
- assert_equal(bigcontent.bytesize, ret)
- s1.close
- result = t.value
+ t1 = Thread.new { s2.read }
+ t2 = Thread.new {
+ ret = IO.copy_stream(bigsrc, s1)
+ assert_equal(bigcontent.bytesize, ret)
+ s1.close
+ }
+ result, _ = assert_join_threads([t1, t2])
assert_equal(bigcontent, result)
}
+ }
+ end if defined? UNIXSocket
+ def test_copy_stream_socket3
+ with_bigsrc {|bigsrc, bigcontent|
with_socketpair {|s1, s2|
- t = Thread.new { s2.read }
- ret = IO.copy_stream("bigsrc", s1, 10000)
- assert_equal(10000, ret)
- s1.close
- result = t.value
+ t1 = Thread.new { s2.read }
+ t2 = Thread.new {
+ ret = IO.copy_stream(bigsrc, s1, 10000)
+ assert_equal(10000, ret)
+ s1.close
+ }
+ result, _ = assert_join_threads([t1, t2])
assert_equal(bigcontent[0,10000], result)
}
+ }
+ end if defined? UNIXSocket
- File.open("bigsrc") {|f|
+ def test_copy_stream_socket4
+ with_bigsrc {|bigsrc, bigcontent|
+ File.open(bigsrc) {|f|
assert_equal(0, f.pos)
with_socketpair {|s1, s2|
- t = Thread.new { s2.read }
- ret = IO.copy_stream(f, s1, nil, 100)
- assert_equal(bigcontent.bytesize-100, ret)
- assert_equal(0, f.pos)
- s1.close
- result = t.value
+ t1 = Thread.new { s2.read }
+ t2 = Thread.new {
+ ret = IO.copy_stream(f, s1, nil, 100)
+ assert_equal(bigcontent.bytesize-100, ret)
+ assert_equal(0, f.pos)
+ s1.close
+ }
+ result, _ = assert_join_threads([t1, t2])
assert_equal(bigcontent[100..-1], result)
}
}
+ }
+ end if defined? UNIXSocket
- File.open("bigsrc") {|f|
+ def test_copy_stream_socket5
+ with_bigsrc {|bigsrc, bigcontent|
+ File.open(bigsrc) {|f|
assert_equal(bigcontent[0,100], f.read(100))
assert_equal(100, f.pos)
with_socketpair {|s1, s2|
- t = Thread.new { s2.read }
- ret = IO.copy_stream(f, s1)
- assert_equal(bigcontent.bytesize-100, ret)
- assert_equal(bigcontent.length, f.pos)
- s1.close
- result = t.value
+ t1 = Thread.new { s2.read }
+ t2 = Thread.new {
+ ret = IO.copy_stream(f, s1)
+ assert_equal(bigcontent.bytesize-100, ret)
+ assert_equal(bigcontent.length, f.pos)
+ s1.close
+ }
+ result, _ = assert_join_threads([t1, t2])
assert_equal(bigcontent[100..-1], result)
}
}
+ }
+ end if defined? UNIXSocket
+ def test_copy_stream_socket6
+ mkcdtmpdir {
megacontent = "abc" * 1234567
File.open("megasrc", "w") {|f| f << megacontent }
- if have_nonblock?
- with_socketpair {|s1, s2|
- begin
- s1.nonblock = true
- rescue Errno::EBADF
- skip "nonblocking IO for pipe is not implemented"
- end
- t = Thread.new { s2.read }
+ with_socketpair {|s1, s2|
+ begin
+ s1.nonblock = true
+ rescue Errno::EBADF
+ skip "nonblocking IO for pipe is not implemented"
+ end
+ t1 = Thread.new { s2.read }
+ t2 = Thread.new {
ret = IO.copy_stream("megasrc", s1)
assert_equal(megacontent.bytesize, ret)
s1.close
- result = t.value
- assert_equal(megacontent, result)
}
- end
+ result, _ = assert_join_threads([t1, t2])
+ assert_equal(megacontent, result)
+ }
}
- end
+ end if defined? UNIXSocket
+
+ def test_copy_stream_socket7
+ GC.start
+ mkcdtmpdir {
+ megacontent = "abc" * 1234567
+ File.open("megasrc", "w") {|f| f << megacontent }
+
+ with_socketpair {|s1, s2|
+ begin
+ s1.nonblock = true
+ rescue Errno::EBADF
+ skip "nonblocking IO for pipe is not implemented"
+ end
+ trapping_usr2 do |rd|
+ nr = 30
+ begin
+ pid = fork do
+ s1.close
+ IO.select([s2])
+ Process.kill(:USR2, Process.ppid)
+ buf = String.new(capacity: 16384)
+ nil while s2.read(16384, buf)
+ end
+ s2.close
+ nr.times do
+ assert_equal megacontent.bytesize, IO.copy_stream("megasrc", s1)
+ end
+ assert_equal(1, rd.read(4).unpack1('L'))
+ ensure
+ s1.close
+ _, status = Process.waitpid2(pid) if pid
+ end
+ assert_predicate(status, :success?)
+ end
+ }
+ }
+ end if defined? UNIXSocket and IO.method_defined?("nonblock=")
def test_copy_stream_strio
src = StringIO.new("abcd")
@@ -617,6 +983,73 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_copy_stream_strio_to_tempfile
+ bug11015 = '[ruby-core:68676] [Bug #11015]'
+ # StringIO to Tempfile
+ src = StringIO.new("abcd")
+ dst = Tempfile.new("baz")
+ ret = IO.copy_stream(src, dst)
+ assert_equal(4, ret)
+ pos = dst.pos
+ dst.rewind
+ assert_equal("abcd", dst.read)
+ assert_equal(4, pos, bug11015)
+ ensure
+ dst.close!
+ end
+
+ def test_copy_stream_pathname_to_pathname
+ bug11199 = '[ruby-dev:49008] [Bug #11199]'
+ mkcdtmpdir {
+ File.open("src", "w") {|f| f << "ok" }
+ src = Pathname.new("src")
+ dst = Pathname.new("dst")
+ IO.copy_stream(src, dst)
+ assert_equal("ok", IO.read("dst"), bug11199)
+ }
+ end
+
+ def test_copy_stream_write_in_binmode
+ bug8767 = '[ruby-core:56518] [Bug #8767]'
+ mkcdtmpdir {
+ EnvUtil.with_default_internal(Encoding::UTF_8) do
+ # StringIO to object with to_path
+ bytes = "\xDE\xAD\xBE\xEF".force_encoding(Encoding::ASCII_8BIT)
+ src = StringIO.new(bytes)
+ dst = Object.new
+ def dst.to_path
+ "qux"
+ end
+ assert_nothing_raised(bug8767) {
+ IO.copy_stream(src, dst)
+ }
+ assert_equal(bytes, File.binread("qux"), bug8767)
+ assert_equal(4, src.pos, bug8767)
+ end
+ }
+ end
+
+ def test_copy_stream_read_in_binmode
+ bug8767 = '[ruby-core:56518] [Bug #8767]'
+ mkcdtmpdir {
+ EnvUtil.with_default_internal(Encoding::UTF_8) do
+ # StringIO to object with to_path
+ bytes = "\xDE\xAD\xBE\xEF".force_encoding(Encoding::ASCII_8BIT)
+ File.binwrite("qux", bytes)
+ dst = StringIO.new
+ src = Object.new
+ def src.to_path
+ "qux"
+ end
+ assert_nothing_raised(bug8767) {
+ IO.copy_stream(src, dst)
+ }
+ assert_equal(bytes, dst.string.b, bug8767)
+ assert_equal(4, dst.pos, bug8767)
+ end
+ }
+ end
+
class Rot13IO
def initialize(io)
@io = io
@@ -686,28 +1119,30 @@ class TestIO < Test::Unit::TestCase
w.write "zz"
src = StringIO.new("abcd")
IO.copy_stream(src, w)
- t = Thread.new {
+ t1 = Thread.new {
w.close
}
- assert_equal("zzabcd", r.read)
- t.join
+ t2 = Thread.new { r.read }
+ _, result = assert_join_threads([t1, t2])
+ assert_equal("zzabcd", result)
}
end
def test_copy_stream_strio_rbuf
- with_pipe {|r, w|
+ pipe(proc do |w|
w << "abcd"
w.close
+ end, proc do |r|
assert_equal("a", r.read(1))
sio = StringIO.new
IO.copy_stream(r, sio)
assert_equal("bcd", sio.string)
- }
+ end)
end
def test_copy_stream_src_wbuf
mkcdtmpdir {
- with_pipe {|r, w|
+ pipe(proc do |w|
File.open("foe", "w+") {|f|
f.write "abcd\n"
f.rewind
@@ -716,17 +1151,41 @@ class TestIO < Test::Unit::TestCase
}
assert_equal("xycd\n", File.read("foe"))
w.close
+ end, proc do |r|
assert_equal("cd\n", r.read)
r.close
- }
+ end)
}
end
+ class Bug5237
+ attr_reader :count
+ def initialize
+ @count = 0
+ end
+
+ def read(bytes, buffer)
+ @count += 1
+ buffer.replace "this is a test"
+ nil
+ end
+ end
+
+ def test_copy_stream_broken_src_read_eof
+ src = Bug5237.new
+ dst = StringIO.new
+ assert_equal 0, src.count
+ th = Thread.new { IO.copy_stream(src, dst) }
+ flunk("timeout") unless th.join(10)
+ assert_equal 1, src.count
+ end
+
def test_copy_stream_dst_rbuf
mkcdtmpdir {
- with_pipe {|r, w|
+ pipe(proc do |w|
w << "xyz"
w.close
+ end, proc do |r|
File.open("fom", "w+b") {|f|
f.write "abcd\n"
f.rewind
@@ -735,40 +1194,40 @@ class TestIO < Test::Unit::TestCase
IO.copy_stream(r, f)
}
assert_equal("abxyz", File.read("fom"))
- }
+ end)
}
end
- def safe_4
- t = Thread.new do
- $SAFE = 4
- yield
- end
- unless t.join(10)
- t.kill
- flunk("timeout in safe_4")
- end
- end
-
- def pipe(wp, rp)
- r, w = IO.pipe
- rt = Thread.new { rp.call(r) }
- wt = Thread.new { wp.call(w) }
- flunk("timeout") unless rt.join(10) && wt.join(10)
- ensure
- r.close unless !r || r.closed?
- w.close unless !w || w.closed?
- (rt.kill; rt.join) if rt
- (wt.kill; wt.join) if wt
+ def test_copy_stream_to_duplex_io
+ result = IO.pipe {|a,w|
+ th = Thread.start {w.puts "yes"; w.close}
+ IO.popen([EnvUtil.rubybin, '-pe$_="#$.:#$_"'], "r+") {|b|
+ IO.copy_stream(a, b)
+ b.close_write
+ assert_join_threads([th])
+ b.read
+ }
+ }
+ assert_equal("1:yes\n", result)
end
def ruby(*args)
args = ['-e', '$>.write($<.read)'] if args.empty?
ruby = EnvUtil.rubybin
- f = IO.popen([ruby] + args, 'r+')
+ opts = {}
+ if defined?(Process::RLIMIT_NPROC)
+ lim = Process.getrlimit(Process::RLIMIT_NPROC)[1]
+ opts[:rlimit_nproc] = [lim, 2048].min
+ end
+ f = IO.popen([ruby] + args, 'r+', opts)
+ pid = f.pid
yield(f)
ensure
f.close unless !f || f.closed?
+ begin
+ Process.wait(pid)
+ rescue Errno::ECHILD, Errno::ESRCH
+ end
end
def test_try_convert
@@ -779,7 +1238,7 @@ class TestIO < Test::Unit::TestCase
def test_ungetc2
f = false
pipe(proc do |w|
- 0 until f
+ Thread.pass until f
w.write("1" * 10000)
w.close
end, proc do |r|
@@ -789,6 +1248,70 @@ class TestIO < Test::Unit::TestCase
end)
end
+ def test_write_with_multiple_arguments
+ pipe(proc do |w|
+ w.write("foo", "bar")
+ w.close
+ end, proc do |r|
+ assert_equal("foobar", r.read)
+ end)
+ end
+
+ def test_write_with_multiple_arguments_and_buffer
+ mkcdtmpdir do
+ line = "x"*9+"\n"
+ file = "test.out"
+ open(file, "wb") do |w|
+ w.write(line)
+ assert_equal(11, w.write(line, "\n"))
+ end
+ open(file, "rb") do |r|
+ assert_equal([line, line, "\n"], r.readlines)
+ end
+
+ line = "x"*99+"\n"
+ open(file, "wb") do |w|
+ w.write(line*81) # 8100 bytes
+ assert_equal(100, w.write("a"*99, "\n"))
+ end
+ open(file, "rb") do |r|
+ 81.times {assert_equal(line, r.gets)}
+ assert_equal("a"*99+"\n", r.gets)
+ end
+ end
+ end
+
+ def test_write_with_many_arguments
+ [1023, 1024].each do |n|
+ pipe(proc do |w|
+ w.write(*(["a"] * n))
+ w.close
+ end, proc do |r|
+ assert_equal("a" * n, r.read)
+ end)
+ end
+ end
+
+ def test_write_with_multiple_nonstring_arguments
+ assert_in_out_err([], "STDOUT.write(:foo, :bar)", ["foobar"])
+ end
+
+ def test_write_buffered_with_multiple_arguments
+ out, err, (_, status) = EnvUtil.invoke_ruby(["-e", "sleep 0.1;puts 'foo'"], "", true, true) do |_, o, e, i|
+ [o.read, e.read, Process.waitpid2(i)]
+ end
+ assert_predicate(status, :success?)
+ assert_equal("foo\n", out)
+ assert_empty(err)
+ end
+
+ def test_write_no_args
+ IO.pipe do |r, w|
+ assert_equal 0, w.write, '[ruby-core:86285] [Bug #14338]'
+ assert_equal :wait_readable, r.read_nonblock(1, exception: false)
+ end
+ end
+
def test_write_non_writable
with_pipe do |r, w|
assert_raise(IOError) do
@@ -799,44 +1322,39 @@ class TestIO < Test::Unit::TestCase
def test_dup
ruby do |f|
- f2 = f.dup
- f.puts "foo"
- f2.puts "bar"
- f.close_write
- f2.close_write
- assert_equal("foo\nbar\n", f.read)
- assert_equal("", f2.read)
+ begin
+ f2 = f.dup
+ f.puts "foo"
+ f2.puts "bar"
+ f.close_write
+ f2.close_write
+ assert_equal("foo\nbar\n", f.read)
+ assert_equal("", f2.read)
+ ensure
+ f2.close
+ end
end
end
def test_dup_many
- ruby('-e', <<-'End') {|f|
- ok = 0
+ opts = {}
+ opts[:rlimit_nofile] = 1024 if defined?(Process::RLIMIT_NOFILE)
+ assert_separately([], <<-'End', opts)
a = []
- begin
+ assert_raise(Errno::EMFILE, Errno::ENFILE, Errno::ENOMEM) do
loop {a << IO.pipe}
- rescue Errno::EMFILE, Errno::ENFILE, Errno::ENOMEM
- ok += 1
end
- print "no" if ok != 1
- begin
+ assert_raise(Errno::EMFILE, Errno::ENFILE, Errno::ENOMEM) do
loop {a << [a[-1][0].dup, a[-1][1].dup]}
- rescue Errno::EMFILE, Errno::ENFILE, Errno::ENOMEM
- ok += 1
end
- print "no" if ok != 2
- print "ok"
End
- assert_equal("ok", f.read)
- }
end
def test_inspect
with_pipe do |r, w|
assert_match(/^#<IO:fd \d+>$/, r.inspect)
- assert_raise(SecurityError) do
- safe_4 { r.inspect }
- end
+ r.freeze
+ assert_match(/^#<IO:fd \d+>$/, r.inspect)
end
end
@@ -856,7 +1374,7 @@ class TestIO < Test::Unit::TestCase
with_pipe do |r, w|
s = ""
t = Thread.new { r.readpartial(5, s) }
- 0 until s.size == 5
+ Thread.pass until t.stop?
assert_raise(RuntimeError) { s.clear }
w.write "foobarbaz"
w.close
@@ -875,6 +1393,27 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_readpartial_with_not_empty_buffer
+ pipe(proc do |w|
+ w.write "foob"
+ w.close
+ end, proc do |r|
+ r.readpartial(5, s = "01234567")
+ assert_equal("foob", s)
+ end)
+ end
+
+ def test_readpartial_buffer_error
+ with_pipe do |r, w|
+ s = ""
+ t = Thread.new { r.readpartial(5, s) }
+ Thread.pass until t.stop?
+ t.kill
+ t.value
+ assert_equal("", s)
+ end
+ end if /cygwin/ !~ RUBY_PLATFORM
+
def test_read
pipe(proc do |w|
w.write "foobarbaz"
@@ -891,7 +1430,7 @@ class TestIO < Test::Unit::TestCase
with_pipe do |r, w|
s = ""
t = Thread.new { r.read(5, s) }
- 0 until s.size == 5
+ Thread.pass until t.stop?
assert_raise(RuntimeError) { s.clear }
w.write "foobarbaz"
w.close
@@ -899,8 +1438,36 @@ class TestIO < Test::Unit::TestCase
end
end
+ def test_read_with_not_empty_buffer
+ pipe(proc do |w|
+ w.write "foob"
+ w.close
+ end, proc do |r|
+ r.read(nil, s = "01234567")
+ assert_equal("foob", s)
+ end)
+ end
+
+ def test_read_buffer_error
+ with_pipe do |r, w|
+ s = ""
+ t = Thread.new { r.read(5, s) }
+ Thread.pass until t.stop?
+ t.kill
+ t.value
+ assert_equal("", s)
+ end
+ with_pipe do |r, w|
+ s = "xxx"
+ t = Thread.new {r.read(2, s)}
+ Thread.pass until t.stop?
+ t.kill
+ t.value
+ assert_equal("xxx", s)
+ end
+ end if /cygwin/ !~ RUBY_PLATFORM
+
def test_write_nonblock
- skip "IO#write_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
pipe(proc do |w|
w.write_nonblock(1)
w.close
@@ -909,9 +1476,25 @@ class TestIO < Test::Unit::TestCase
end)
end
+ def test_read_nonblock_with_not_empty_buffer
+ with_pipe {|r, w|
+ w.write "foob"
+ w.close
+ r.read_nonblock(5, s = "01234567")
+ assert_equal("foob", s)
+ }
+ end
+
+ def test_write_nonblock_simple_no_exceptions
+ pipe(proc do |w|
+ w.write_nonblock('1', exception: false)
+ w.close
+ end, proc do |r|
+ assert_equal("1", r.read)
+ end)
+ end
+
def test_read_nonblock_error
- return if !have_nonblock?
- skip "IO#read_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
with_pipe {|r, w|
begin
r.read_nonblock 4096
@@ -919,11 +1502,40 @@ class TestIO < Test::Unit::TestCase
assert_kind_of(IO::WaitReadable, $!)
end
}
- end
+
+ with_pipe {|r, w|
+ begin
+ r.read_nonblock 4096, ""
+ rescue Errno::EWOULDBLOCK
+ assert_kind_of(IO::WaitReadable, $!)
+ end
+ }
+ end if have_nonblock?
+
+ def test_read_nonblock_no_exceptions
+ with_pipe {|r, w|
+ assert_equal :wait_readable, r.read_nonblock(4096, exception: false)
+ w.puts "HI!"
+ assert_equal "HI!\n", r.read_nonblock(4096, exception: false)
+ w.close
+ assert_equal nil, r.read_nonblock(4096, exception: false)
+ }
+ end if have_nonblock?
+
+ def test_read_nonblock_with_buffer_no_exceptions
+ with_pipe {|r, w|
+ assert_equal :wait_readable, r.read_nonblock(4096, "", exception: false)
+ w.puts "HI!"
+ buf = "buf"
+ value = r.read_nonblock(4096, buf, exception: false)
+ assert_equal value, "HI!\n"
+ assert_same(buf, value)
+ w.close
+ assert_equal nil, r.read_nonblock(4096, "", exception: false)
+ }
+ end if have_nonblock?
def test_write_nonblock_error
- return if !have_nonblock?
- skip "IO#write_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
with_pipe {|r, w|
begin
loop {
@@ -933,7 +1545,19 @@ class TestIO < Test::Unit::TestCase
assert_kind_of(IO::WaitWritable, $!)
end
}
- end
+ end if have_nonblock?
+
+ def test_write_nonblock_no_exceptions
+ with_pipe {|r, w|
+ loop {
+ ret = w.write_nonblock("a"*100000, exception: false)
+ if ret.is_a?(Symbol)
+ assert_equal :wait_writable, ret
+ break
+ end
+ }
+ }
+ end if have_nonblock?
def test_gets
pipe(proc do |w|
@@ -941,7 +1565,7 @@ class TestIO < Test::Unit::TestCase
w.close
end, proc do |r|
assert_equal("", r.gets(0))
- assert_equal("foobarbaz", s = r.gets(9))
+ assert_equal("foobarbaz", r.gets(9))
end)
end
@@ -950,6 +1574,9 @@ class TestIO < Test::Unit::TestCase
f.close_read
f.write "foobarbaz"
assert_raise(IOError) { f.read }
+ assert_nothing_raised(IOError) {f.close_read}
+ assert_nothing_raised(IOError) {f.close}
+ assert_nothing_raised(IOError) {f.close_read}
end
end
@@ -957,15 +1584,21 @@ class TestIO < Test::Unit::TestCase
with_pipe do |r, w|
r.close_read
assert_raise(Errno::EPIPE) { w.write "foobarbaz" }
+ assert_nothing_raised(IOError) {r.close_read}
+ assert_nothing_raised(IOError) {r.close}
+ assert_nothing_raised(IOError) {r.close_read}
end
end
- def test_close_read_security_error
- with_pipe do |r, w|
- assert_raise(SecurityError) do
- safe_4 { r.close_read }
- end
- end
+ def test_write_epipe_nosync
+ assert_separately([], <<-"end;")
+ r, w = IO.pipe
+ r.close
+ w.sync = false
+ assert_raise(Errno::EPIPE) {
+ loop { w.write "a" }
+ }
+ end;
end
def test_close_read_non_readable
@@ -981,84 +1614,113 @@ class TestIO < Test::Unit::TestCase
f.write "foobarbaz"
f.close_write
assert_equal("foobarbaz", f.read)
+ assert_nothing_raised(IOError) {f.close_write}
+ assert_nothing_raised(IOError) {f.close}
+ assert_nothing_raised(IOError) {f.close_write}
end
end
- def test_close_write_security_error
+ def test_close_write_non_readable
with_pipe do |r, w|
- assert_raise(SecurityError) do
- safe_4 { r.close_write }
+ assert_raise(IOError) do
+ r.close_write
end
end
end
- def test_close_write_non_readable
- with_pipe do |r, w|
- assert_raise(IOError) do
- r.close_write
+ def test_close_read_write_separately
+ bug = '[ruby-list:49598]'
+ (1..10).each do |i|
+ assert_nothing_raised(IOError, "#{bug} trying ##{i}") do
+ IO.popen(EnvUtil.rubybin, "r+") {|f|
+ th = Thread.new {f.close_write}
+ f.close_read
+ th.join
+ }
end
end
end
def test_pid
- r, w = IO.pipe
- assert_equal(nil, r.pid)
- assert_equal(nil, w.pid)
-
- pipe = IO.popen(EnvUtil.rubybin, "r+")
- pid1 = pipe.pid
- pipe.puts "p $$"
- pipe.close_write
- pid2 = pipe.read.chomp.to_i
- assert_equal(pid2, pid1)
- assert_equal(pid2, pipe.pid)
- pipe.close
+ IO.pipe {|r, w|
+ assert_equal(nil, r.pid)
+ assert_equal(nil, w.pid)
+ }
+
+ begin
+ pipe = IO.popen(EnvUtil.rubybin, "r+")
+ pid1 = pipe.pid
+ pipe.puts "p $$"
+ pipe.close_write
+ pid2 = pipe.read.chomp.to_i
+ assert_equal(pid2, pid1)
+ assert_equal(pid2, pipe.pid)
+ ensure
+ pipe.close
+ end
assert_raise(IOError) { pipe.pid }
end
+ def test_pid_after_close_read
+ pid1 = pid2 = nil
+ IO.popen("exit ;", "r+") do |io|
+ pid1 = io.pid
+ io.close_read
+ pid2 = io.pid
+ end
+ assert_not_nil(pid1)
+ assert_equal(pid1, pid2)
+ end
+
def make_tempfile
- t = Tempfile.new("foo")
+ t = Tempfile.new("test_io")
t.binmode
t.puts "foo"
t.puts "bar"
t.puts "baz"
t.close
- t
+ if block_given?
+ begin
+ yield t
+ ensure
+ t.close(true)
+ end
+ else
+ t
+ end
end
def test_set_lineno
- t = make_tempfile
-
- ruby("-e", <<-SRC, t.path) do |f|
- open(ARGV[0]) do |f|
- p $.
- f.gets; p $.
- f.gets; p $.
- f.lineno = 1000; p $.
- f.gets; p $.
- f.gets; p $.
- f.rewind; p $.
- f.gets; p $.
- f.gets; p $.
- f.gets; p $.
- f.gets; p $.
- end
- SRC
- assert_equal("0,1,2,2,1001,1001,1001,1,2,3,3", f.read.chomp.gsub("\n", ","))
- end
+ make_tempfile {|t|
+ assert_separately(["-", t.path], <<-SRC)
+ open(ARGV[0]) do |f|
+ assert_equal(0, $.)
+ f.gets; assert_equal(1, $.)
+ f.gets; assert_equal(2, $.)
+ f.lineno = 1000; assert_equal(2, $.)
+ f.gets; assert_equal(1001, $.)
+ f.gets; assert_equal(1001, $.)
+ f.rewind; assert_equal(1001, $.)
+ f.gets; assert_equal(1, $.)
+ f.gets; assert_equal(2, $.)
+ f.gets; assert_equal(3, $.)
+ f.gets; assert_equal(3, $.)
+ end
+ SRC
- pipe(proc do |w|
- w.puts "foo"
- w.puts "bar"
- w.puts "baz"
- w.close
- end, proc do |r|
- r.gets; assert_equal(1, $.)
- r.gets; assert_equal(2, $.)
- r.lineno = 1000; assert_equal(2, $.)
- r.gets; assert_equal(1001, $.)
- r.gets; assert_equal(1001, $.)
- end)
+ pipe(proc do |w|
+ w.puts "foo"
+ w.puts "bar"
+ w.puts "baz"
+ w.close
+ end, proc do |r|
+ r.gets; assert_equal(1, $.)
+ r.gets; assert_equal(2, $.)
+ r.lineno = 1000; assert_equal(2, $.)
+ r.gets; assert_equal(1001, $.)
+ r.gets; assert_equal(1001, $.)
+ end)
+ }
end
def test_readline
@@ -1090,21 +1752,28 @@ class TestIO < Test::Unit::TestCase
end
def test_lines
+ verbose, $VERBOSE = $VERBOSE, nil
pipe(proc do |w|
w.puts "foo"
w.puts "bar"
w.puts "baz"
w.close
end, proc do |r|
- e = r.lines
+ e = nil
+ assert_warn(/deprecated/) {
+ e = r.lines
+ }
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
pipe(proc do |w|
w.binmode
w.puts "foo"
@@ -1112,27 +1781,38 @@ class TestIO < Test::Unit::TestCase
w.puts "baz"
w.close
end, proc do |r|
- e = r.bytes
+ e = nil
+ assert_warn(/deprecated/) {
+ e = r.bytes
+ }
(%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
pipe(proc do |w|
w.puts "foo"
w.puts "bar"
w.puts "baz"
w.close
end, proc do |r|
- e = r.chars
+ e = nil
+ assert_warn(/deprecated/) {
+ e = r.chars
+ }
(%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
@@ -1166,8 +1846,9 @@ class TestIO < Test::Unit::TestCase
end
def test_close_on_exec
- skip "IO\#close_on_exec is not implemented." unless have_close_on_exec?
ruby do |f|
+ assert_equal(true, f.close_on_exec?)
+ f.close_on_exec = false
assert_equal(false, f.close_on_exec?)
f.close_on_exec = true
assert_equal(true, f.close_on_exec?)
@@ -1176,125 +1857,281 @@ class TestIO < Test::Unit::TestCase
end
with_pipe do |r, w|
+ assert_equal(true, r.close_on_exec?)
+ r.close_on_exec = false
assert_equal(false, r.close_on_exec?)
r.close_on_exec = true
assert_equal(true, r.close_on_exec?)
r.close_on_exec = false
assert_equal(false, r.close_on_exec?)
+ assert_equal(true, w.close_on_exec?)
+ w.close_on_exec = false
assert_equal(false, w.close_on_exec?)
w.close_on_exec = true
assert_equal(true, w.close_on_exec?)
w.close_on_exec = false
assert_equal(false, w.close_on_exec?)
end
+ end if have_close_on_exec?
+
+ def test_pos
+ make_tempfile {|t|
+ open(t.path, IO::RDWR|IO::CREAT|IO::TRUNC, 0600) do |f|
+ f.write "Hello"
+ assert_equal(5, f.pos)
+ end
+ open(t.path, IO::RDWR|IO::CREAT|IO::TRUNC, 0600) do |f|
+ f.sync = true
+ f.read
+ f.write "Hello"
+ assert_equal(5, f.pos)
+ end
+ }
end
- def test_close_security_error
- with_pipe do |r, w|
- assert_raise(SecurityError) do
- safe_4 { r.close }
+ def test_pos_with_getc
+ _bug6179 = '[ruby-core:43497]'
+ make_tempfile {|t|
+ ["", "t", "b"].each do |mode|
+ open(t.path, "w#{mode}") do |f|
+ f.write "0123456789\n"
+ end
+
+ open(t.path, "r#{mode}") do |f|
+ assert_equal 0, f.pos, "mode=r#{mode}"
+ assert_equal '0', f.getc, "mode=r#{mode}"
+ assert_equal 1, f.pos, "mode=r#{mode}"
+ assert_equal '1', f.getc, "mode=r#{mode}"
+ assert_equal 2, f.pos, "mode=r#{mode}"
+ assert_equal '2', f.getc, "mode=r#{mode}"
+ assert_equal 3, f.pos, "mode=r#{mode}"
+ assert_equal '3', f.getc, "mode=r#{mode}"
+ assert_equal 4, f.pos, "mode=r#{mode}"
+ assert_equal '4', f.getc, "mode=r#{mode}"
+ end
+ end
+ }
+ end
+
+ def can_seek_data(f)
+ if /linux/ =~ RUBY_PLATFORM
+ require "-test-/file"
+ # lseek(2)
+ case Bug::File::Fs.fsname(f.path)
+ when "btrfs"
+ return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,1]) >= 0
+ when "ocfs"
+ return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,2]) >= 0
+ when "xfs"
+ return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,5]) >= 0
+ when "ext4"
+ return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,8]) >= 0
+ when "tmpfs"
+ return true if (Etc.uname[:release].split('.').map(&:to_i) <=> [3,8]) >= 0
end
end
+ false
end
- def test_pos
- t = make_tempfile
+ def test_seek
+ make_tempfile {|t|
+ open(t.path) { |f|
+ f.seek(9)
+ assert_equal("az\n", f.read)
+ }
- open(t.path, IO::RDWR|IO::CREAT|IO::TRUNC, 0600) do |f|
- f.write "Hello"
- assert_equal(5, f.pos)
- end
- open(t.path, IO::RDWR|IO::CREAT|IO::TRUNC, 0600) do |f|
- f.sync = true
- f.read
- f.write "Hello"
- assert_equal(5, f.pos)
- end
+ open(t.path) { |f|
+ f.seek(9, IO::SEEK_SET)
+ assert_equal("az\n", f.read)
+ }
+
+ open(t.path) { |f|
+ f.seek(-4, IO::SEEK_END)
+ assert_equal("baz\n", f.read)
+ }
+
+ open(t.path) { |f|
+ assert_equal("foo\n", f.gets)
+ f.seek(2, IO::SEEK_CUR)
+ assert_equal("r\nbaz\n", f.read)
+ }
+
+ if defined?(IO::SEEK_DATA)
+ open(t.path) { |f|
+ break unless can_seek_data(f)
+ assert_equal("foo\n", f.gets)
+ f.seek(0, IO::SEEK_DATA)
+ assert_equal("foo\nbar\nbaz\n", f.read)
+ }
+ open(t.path, 'r+') { |f|
+ break unless can_seek_data(f)
+ f.seek(100*1024, IO::SEEK_SET)
+ f.print("zot\n")
+ f.seek(50*1024, IO::SEEK_DATA)
+ assert_operator(f.pos, :>=, 50*1024)
+ assert_match(/\A\0*zot\n\z/, f.read)
+ }
+ end
+
+ if defined?(IO::SEEK_HOLE)
+ open(t.path) { |f|
+ break unless can_seek_data(f)
+ assert_equal("foo\n", f.gets)
+ f.seek(0, IO::SEEK_HOLE)
+ assert_operator(f.pos, :>, 20)
+ f.seek(100*1024, IO::SEEK_HOLE)
+ assert_equal("", f.read)
+ }
+ end
+ }
end
- def test_sysseek
- t = make_tempfile
+ def test_seek_symwhence
+ make_tempfile {|t|
+ open(t.path) { |f|
+ f.seek(9, :SET)
+ assert_equal("az\n", f.read)
+ }
- open(t.path) do |f|
- f.sysseek(-4, IO::SEEK_END)
- assert_equal("baz\n", f.read)
- end
+ open(t.path) { |f|
+ f.seek(-4, :END)
+ assert_equal("baz\n", f.read)
+ }
- open(t.path) do |f|
- a = [f.getc, f.getc, f.getc]
- a.reverse_each {|c| f.ungetc c }
- assert_raise(IOError) { f.sysseek(1) }
- end
+ open(t.path) { |f|
+ assert_equal("foo\n", f.gets)
+ f.seek(2, :CUR)
+ assert_equal("r\nbaz\n", f.read)
+ }
+
+ if defined?(IO::SEEK_DATA)
+ open(t.path) { |f|
+ break unless can_seek_data(f)
+ assert_equal("foo\n", f.gets)
+ f.seek(0, :DATA)
+ assert_equal("foo\nbar\nbaz\n", f.read)
+ }
+ open(t.path, 'r+') { |f|
+ break unless can_seek_data(f)
+ f.seek(100*1024, :SET)
+ f.print("zot\n")
+ f.seek(50*1024, :DATA)
+ assert_operator(f.pos, :>=, 50*1024)
+ assert_match(/\A\0*zot\n\z/, f.read)
+ }
+ end
+
+ if defined?(IO::SEEK_HOLE)
+ open(t.path) { |f|
+ break unless can_seek_data(f)
+ assert_equal("foo\n", f.gets)
+ f.seek(0, :HOLE)
+ assert_operator(f.pos, :>, 20)
+ f.seek(100*1024, :HOLE)
+ assert_equal("", f.read)
+ }
+ end
+ }
end
- def test_syswrite
- t = make_tempfile
+ def test_sysseek
+ make_tempfile {|t|
+ open(t.path) do |f|
+ f.sysseek(-4, IO::SEEK_END)
+ assert_equal("baz\n", f.read)
+ end
- open(t.path, "w") do |f|
- o = Object.new
- def o.to_s; "FOO\n"; end
- f.syswrite(o)
- end
- assert_equal("FOO\n", File.read(t.path))
+ open(t.path) do |f|
+ a = [f.getc, f.getc, f.getc]
+ a.reverse_each {|c| f.ungetc c }
+ assert_raise(IOError) { f.sysseek(1) }
+ end
+ }
+ end
+
+ def test_syswrite
+ make_tempfile {|t|
+ open(t.path, "w") do |f|
+ o = Object.new
+ def o.to_s; "FOO\n"; end
+ f.syswrite(o)
+ end
+ assert_equal("FOO\n", File.read(t.path))
+ }
end
def test_sysread
- t = make_tempfile
+ make_tempfile {|t|
+ open(t.path) do |f|
+ a = [f.getc, f.getc, f.getc]
+ a.reverse_each {|c| f.ungetc c }
+ assert_raise(IOError) { f.sysread(1) }
+ end
+ }
+ end
- open(t.path) do |f|
- a = [f.getc, f.getc, f.getc]
- a.reverse_each {|c| f.ungetc c }
- assert_raise(IOError) { f.sysread(1) }
- end
+ def test_sysread_with_not_empty_buffer
+ pipe(proc do |w|
+ w.write "foob"
+ w.close
+ end, proc do |r|
+ r.sysread( 5, s = "01234567" )
+ assert_equal( "foob", s )
+ end)
end
def test_flag
- t = make_tempfile
+ make_tempfile {|t|
+ assert_raise(ArgumentError) do
+ open(t.path, "z") { }
+ end
- assert_raise(ArgumentError) do
- open(t.path, "z") { }
- end
+ assert_raise(ArgumentError) do
+ open(t.path, "rr") { }
+ end
- assert_raise(ArgumentError) do
- open(t.path, "rr") { }
- end
+ assert_raise(ArgumentError) do
+ open(t.path, "rbt") { }
+ end
+ }
end
def test_sysopen
- t = make_tempfile
+ make_tempfile {|t|
+ fd = IO.sysopen(t.path)
+ assert_kind_of(Integer, fd)
+ f = IO.for_fd(fd)
+ assert_equal("foo\nbar\nbaz\n", f.read)
+ f.close
- fd = IO.sysopen(t.path)
- assert_kind_of(Integer, fd)
- f = IO.for_fd(fd)
- assert_equal("foo\nbar\nbaz\n", f.read)
- f.close
+ fd = IO.sysopen(t.path, "w", 0666)
+ assert_kind_of(Integer, fd)
+ if defined?(Fcntl::F_GETFL)
+ f = IO.for_fd(fd)
+ else
+ f = IO.for_fd(fd, 0666)
+ end
+ f.write("FOO\n")
+ f.close
- fd = IO.sysopen(t.path, "w", 0666)
- assert_kind_of(Integer, fd)
- if defined?(Fcntl::F_GETFL)
+ fd = IO.sysopen(t.path, "r")
+ assert_kind_of(Integer, fd)
f = IO.for_fd(fd)
- else
- f = IO.for_fd(fd, 0666)
- end
- f.write("FOO\n")
- f.close
-
- fd = IO.sysopen(t.path, "r")
- assert_kind_of(Integer, fd)
- f = IO.for_fd(fd)
- assert_equal("FOO\n", f.read)
- f.close
+ assert_equal("FOO\n", f.read)
+ f.close
+ }
end
- def try_fdopen(fd, autoclose = true, level = 100)
+ def try_fdopen(fd, autoclose = true, level = 50)
if level > 0
- try_fdopen(fd, autoclose, level - 1)
- GC.start
- level
+ begin
+ 1.times {return try_fdopen(fd, autoclose, level - 1)}
+ ensure
+ GC.start
+ end
else
- IO.for_fd(fd, autoclose: autoclose)
- nil
+ WeakRef.new(IO.for_fd(fd, autoclose: autoclose))
end
end
@@ -1302,32 +2139,62 @@ class TestIO < Test::Unit::TestCase
feature2250 = '[ruby-core:26222]'
pre = 'ft2250'
- Tempfile.new(pre) do |t|
+ Dir.mktmpdir {|d|
+ t = open("#{d}/#{pre}", "w")
f = IO.for_fd(t.fileno)
assert_equal(true, f.autoclose?)
f.autoclose = false
assert_equal(false, f.autoclose?)
f.close
- assert_nothing_raised(Errno::EBADF) {t.close}
+ assert_nothing_raised(Errno::EBADF, feature2250) {t.close}
- t.open
+ t = open("#{d}/#{pre}", "w")
f = IO.for_fd(t.fileno, autoclose: false)
assert_equal(false, f.autoclose?)
f.autoclose = true
assert_equal(true, f.autoclose?)
f.close
- assert_raise(Errno::EBADF) {t.close}
- end
+ assert_raise(Errno::EBADF, feature2250) {t.close}
+ }
+ end
- Tempfile.new(pre) do |t|
- try_fdopen(t.fileno)
- assert_raise(Errno::EBADF) {t.close}
+ 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?
+
+ feature2250 = '[ruby-core:26222]'
+ pre = 'ft2250'
+ t = Tempfile.new(pre)
+ w = try_fdopen(t.fileno)
+ begin
+ w.close
+ begin
+ t.close
+ rescue Errno::EBADF
+ end
+ skip "expect IO object was GC'ed but not recycled yet"
+ rescue WeakRef::RefError
+ assert_raise(Errno::EBADF, feature2250) {t.close}
end
+ ensure
+ t&.close!
+ end
- Tempfile.new(pre) do |t|
- try_fdopen(f.fileno, false)
- assert_nothing_raised(Errno::EBADF) {t.close}
+ def test_autoclose_false_closed_by_finalizer
+ feature2250 = '[ruby-core:26222]'
+ pre = 'ft2250'
+ t = Tempfile.new(pre)
+ w = try_fdopen(t.fileno, false)
+ begin
+ w.close
+ t.close
+ skip "expect IO object was GC'ed but not recycled yet"
+ rescue WeakRef::RefError
+ assert_nothing_raised(Errno::EBADF, feature2250) {t.close}
end
+ ensure
+ t.close!
end
def test_open_redirect
@@ -1349,100 +2216,249 @@ class TestIO < Test::Unit::TestCase
end
end
- def test_reopen
- t = make_tempfile
-
- with_pipe do |r, w|
- assert_raise(SecurityError) do
- safe_4 { r.reopen(t.path) }
- end
+ def test_read_command
+ assert_equal("foo\n", IO.read("|echo foo"))
+ assert_raise(Errno::ENOENT, Errno::EINVAL) do
+ File.read("|#{EnvUtil.rubybin} -e puts")
end
-
- open(__FILE__) do |f|
- f.gets
- assert_nothing_raised {
- f.reopen(t.path)
- assert_equal("foo\n", f.gets)
- }
+ assert_raise(Errno::ENOENT, Errno::EINVAL) do
+ File.binread("|#{EnvUtil.rubybin} -e puts")
end
-
- open(__FILE__) do |f|
- f.gets
- f2 = open(t.path)
- f2.gets
- assert_nothing_raised {
- f.reopen(f2)
- assert_equal("bar\n", f.gets, '[ruby-core:24240]')
- }
+ assert_raise(Errno::ENOENT, Errno::EINVAL) do
+ Class.new(IO).read("|#{EnvUtil.rubybin} -e puts")
end
-
- open(__FILE__) do |f|
- f2 = open(t.path)
- f.reopen(f2)
- assert_equal("foo\n", f.gets)
- assert_equal("bar\n", f.gets)
- f.reopen(f2)
- assert_equal("baz\n", f.gets, '[ruby-dev:39479]')
+ assert_raise(Errno::ENOENT, Errno::EINVAL) do
+ Class.new(IO).binread("|#{EnvUtil.rubybin} -e puts")
end
end
+ def test_reopen
+ make_tempfile {|t|
+ open(__FILE__) do |f|
+ f.gets
+ assert_nothing_raised {
+ f.reopen(t.path)
+ assert_equal("foo\n", f.gets)
+ }
+ end
+
+ open(__FILE__) do |f|
+ f.gets
+ f2 = open(t.path)
+ begin
+ f2.gets
+ assert_nothing_raised {
+ f.reopen(f2)
+ assert_equal("bar\n", f.gets, '[ruby-core:24240]')
+ }
+ ensure
+ f2.close
+ end
+ end
+
+ open(__FILE__) do |f|
+ f2 = open(t.path)
+ begin
+ f.reopen(f2)
+ assert_equal("foo\n", f.gets)
+ assert_equal("bar\n", f.gets)
+ f.reopen(f2)
+ assert_equal("baz\n", f.gets, '[ruby-dev:39479]')
+ ensure
+ f2.close
+ end
+ end
+ }
+ end
+
def test_reopen_inherit
mkcdtmpdir {
- system(EnvUtil.rubybin, '-e', <<"End")
+ system(EnvUtil.rubybin, '-e', <<-"End")
f = open("out", "w")
STDOUT.reopen(f)
STDERR.reopen(f)
system(#{EnvUtil.rubybin.dump}, '-e', 'STDOUT.print "out"')
system(#{EnvUtil.rubybin.dump}, '-e', 'STDERR.print "err"')
-End
+ End
assert_equal("outerr", File.read("out"))
}
end
- def test_foreach
- a = []
- IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x }
- assert_equal(["foo\n", "bar\n", "baz\n"], a)
+ def test_reopen_stdio
+ mkcdtmpdir {
+ fname = 'bug11319'
+ File.write(fname, 'hello')
+ system(EnvUtil.rubybin, '-e', "STDOUT.reopen('#{fname}', 'w+')")
+ assert_equal('', File.read(fname))
+ }
+ end
+
+ def test_reopen_mode
+ feature7067 = '[ruby-core:47694]'
+ make_tempfile {|t|
+ open(__FILE__) do |f|
+ assert_nothing_raised {
+ f.reopen(t.path, "r")
+ assert_equal("foo\n", f.gets)
+ }
+ end
+
+ open(__FILE__) do |f|
+ assert_nothing_raised(feature7067) {
+ f.reopen(t.path, File::RDONLY)
+ assert_equal("foo\n", f.gets)
+ }
+ end
+ }
+ end
+ def test_reopen_opt
+ feature7103 = '[ruby-core:47806]'
+ make_tempfile {|t|
+ open(__FILE__) do |f|
+ assert_nothing_raised(feature7103) {
+ f.reopen(t.path, "r", binmode: true)
+ }
+ assert_equal("foo\n", f.gets)
+ end
+
+ open(__FILE__) do |f|
+ assert_nothing_raised(feature7103) {
+ f.reopen(t.path, autoclose: false)
+ }
+ assert_equal("foo\n", f.gets)
+ end
+ }
+ end
+
+ def make_tempfile_for_encoding
t = make_tempfile
+ open(t.path, "rb+:utf-8") {|f| f.puts "\u7d05\u7389bar\n"}
+ if block_given?
+ yield t
+ else
+ t
+ end
+ ensure
+ t&.close(true) if block_given?
+ end
- a = []
- IO.foreach(t.path) {|x| a << x }
- assert_equal(["foo\n", "bar\n", "baz\n"], a)
+ def test_reopen_encoding
+ make_tempfile_for_encoding {|t|
+ open(__FILE__) {|f|
+ f.reopen(t.path, "r:utf-8")
+ s = f.gets
+ assert_equal(Encoding::UTF_8, s.encoding)
+ assert_equal("\u7d05\u7389bar\n", s)
+ }
- a = []
- IO.foreach(t.path, {:mode => "r" }) {|x| a << x }
- assert_equal(["foo\n", "bar\n", "baz\n"], a)
+ open(__FILE__) {|f|
+ f.reopen(t.path, "r:UTF-8:EUC-JP")
+ s = f.gets
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ assert_equal("\xB9\xC8\xB6\xCCbar\n".force_encoding(Encoding::EUC_JP), s)
+ }
+ }
+ end
- a = []
- IO.foreach(t.path, {:open_args => [] }) {|x| a << x }
- assert_equal(["foo\n", "bar\n", "baz\n"], a)
+ def test_reopen_opt_encoding
+ feature7103 = '[ruby-core:47806]'
+ make_tempfile_for_encoding {|t|
+ open(__FILE__) {|f|
+ assert_nothing_raised(feature7103) {f.reopen(t.path, encoding: "ASCII-8BIT")}
+ s = f.gets
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_equal("\xe7\xb4\x85\xe7\x8e\x89bar\n", s)
+ }
+
+ open(__FILE__) {|f|
+ assert_nothing_raised(feature7103) {f.reopen(t.path, encoding: "UTF-8:EUC-JP")}
+ s = f.gets
+ assert_equal(Encoding::EUC_JP, s.encoding)
+ assert_equal("\xB9\xC8\xB6\xCCbar\n".force_encoding(Encoding::EUC_JP), s)
+ }
+ }
+ end
+
+ bug11320 = '[ruby-core:69780] [Bug #11320]'
+ ["UTF-8", "EUC-JP", "Shift_JIS"].each do |enc|
+ define_method("test_reopen_nonascii(#{enc})") do
+ mkcdtmpdir do
+ fname = "\u{30eb 30d3 30fc}".encode(enc)
+ File.write(fname, '')
+ assert_file.exist?(fname)
+ stdin = $stdin.dup
+ begin
+ assert_nothing_raised(Errno::ENOENT, "#{bug11320}: #{enc}") {
+ $stdin.reopen(fname, 'r')
+ }
+ ensure
+ $stdin.reopen(stdin)
+ stdin.close
+ end
+ end
+ end
+ end
+ def test_foreach
a = []
- IO.foreach(t.path, {:open_args => ["r"] }) {|x| a << x }
+ IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x }
assert_equal(["foo\n", "bar\n", "baz\n"], a)
a = []
- IO.foreach(t.path, "b") {|x| a << x }
- assert_equal(["foo\nb", "ar\nb", "az\n"], a)
+ IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :zot'", :open_args => ["r"]) {|x| a << x }
+ assert_equal(["zot\n"], a)
- a = []
- IO.foreach(t.path, 3) {|x| a << x }
- assert_equal(["foo", "\n", "bar", "\n", "baz", "\n"], a)
+ make_tempfile {|t|
+ a = []
+ IO.foreach(t.path) {|x| a << x }
+ assert_equal(["foo\n", "bar\n", "baz\n"], a)
- a = []
- IO.foreach(t.path, "b", 3) {|x| a << x }
- assert_equal(["foo", "\nb", "ar\n", "b", "az\n"], a)
+ a = []
+ IO.foreach(t.path, {:mode => "r" }) {|x| a << x }
+ assert_equal(["foo\n", "bar\n", "baz\n"], a)
+
+ a = []
+ IO.foreach(t.path, {:open_args => [] }) {|x| a << x }
+ assert_equal(["foo\n", "bar\n", "baz\n"], a)
+
+ a = []
+ IO.foreach(t.path, {:open_args => ["r"] }) {|x| a << x }
+ assert_equal(["foo\n", "bar\n", "baz\n"], a)
+ a = []
+ IO.foreach(t.path, "b") {|x| a << x }
+ assert_equal(["foo\nb", "ar\nb", "az\n"], a)
+
+ a = []
+ IO.foreach(t.path, 3) {|x| a << x }
+ assert_equal(["foo", "\n", "bar", "\n", "baz", "\n"], a)
+
+ a = []
+ IO.foreach(t.path, "b", 3) {|x| a << x }
+ assert_equal(["foo", "\nb", "ar\n", "b", "az\n"], a)
+
+ bug = '[ruby-dev:31525]'
+ assert_raise(ArgumentError, bug) {IO.foreach}
+
+ a = nil
+ assert_nothing_raised(ArgumentError, bug) {a = IO.foreach(t.path).to_a}
+ assert_equal(["foo\n", "bar\n", "baz\n"], a, bug)
+
+ bug6054 = '[ruby-dev:45267]'
+ assert_raise_with_message(IOError, /not opened for reading/, bug6054) do
+ IO.foreach(t.path, mode:"w").next
+ end
+ }
end
def test_s_readlines
- t = make_tempfile
-
- assert_equal(["foo\n", "bar\n", "baz\n"], IO.readlines(t.path))
- assert_equal(["foo\nb", "ar\nb", "az\n"], IO.readlines(t.path, "b"))
- assert_equal(["fo", "o\n", "ba", "r\n", "ba", "z\n"], IO.readlines(t.path, 2))
- assert_equal(["fo", "o\n", "b", "ar", "\nb", "az", "\n"], IO.readlines(t.path, "b", 2))
+ make_tempfile {|t|
+ assert_equal(["foo\n", "bar\n", "baz\n"], IO.readlines(t.path))
+ assert_equal(["foo\nb", "ar\nb", "az\n"], IO.readlines(t.path, "b"))
+ assert_equal(["fo", "o\n", "ba", "r\n", "ba", "z\n"], IO.readlines(t.path, 2))
+ assert_equal(["fo", "o\n", "b", "ar", "\nb", "az", "\n"], IO.readlines(t.path, "b", 2))
+ }
end
def test_printf
@@ -1455,9 +2471,11 @@ End
end
def test_print
- t = make_tempfile
-
- assert_in_out_err(["-", t.path], "print while $<.gets", %w(foo bar baz), [])
+ make_tempfile {|t|
+ assert_in_out_err(["-", t.path],
+ "print while $<.gets",
+ %w(foo bar baz), [])
+ }
end
def test_print_separators
@@ -1502,6 +2520,36 @@ End
end)
end
+ def test_puts_parallel
+ skip "not portable"
+ pipe(proc do |w|
+ threads = []
+ 100.times do
+ threads << Thread.new { w.puts "hey" }
+ end
+ threads.each(&:join)
+ w.close
+ end, proc do |r|
+ assert_equal("hey\n" * 100, r.read)
+ end)
+ end
+
+ def test_puts_old_write
+ capture = String.new
+ def capture.write(str)
+ self << str
+ end
+
+ capture.clear
+ assert_warning(/[.#]write is outdated/) do
+ stdout, $stdout = $stdout, capture
+ puts "hey"
+ ensure
+ $stdout = stdout
+ end
+ assert_equal("hey\n", capture)
+ end
+
def test_display
pipe(proc do |w|
"foo".display(w)
@@ -1517,35 +2565,58 @@ End
assert_raise(TypeError) { $> = Object.new }
assert_in_out_err([], "$> = $stderr\nputs 'foo'", [], %w(foo))
+
+ assert_separately(%w[-Eutf-8], "#{<<~"begin;"}\n#{<<~"end;"}")
+ begin;
+ alias $\u{6a19 6e96 51fa 529b} $stdout
+ x = eval("class X\u{307b 3052}; self; end".encode("euc-jp"))
+ assert_raise_with_message(TypeError, /\\$\u{6a19 6e96 51fa 529b} must.*, X\u{307b 3052} given/) do
+ $\u{6a19 6e96 51fa 529b} = x.new
+ end
+ end;
end
def test_initialize
return unless defined?(Fcntl::F_GETFL)
- t = make_tempfile
-
- fd = IO.sysopen(t.path, "w")
- assert_kind_of(Integer, fd)
- %w[r r+ w+ a+].each do |mode|
- assert_raise(Errno::EINVAL, "#{mode} [ruby-dev:38571]") {IO.new(fd, mode)}
- end
- f = IO.new(fd, "w")
- f.write("FOO\n")
- f.close
+ make_tempfile {|t|
+ fd = IO.sysopen(t.path, "w")
+ assert_kind_of(Integer, fd)
+ %w[r r+ w+ a+].each do |mode|
+ assert_raise(Errno::EINVAL, "#{mode} [ruby-dev:38571]") {IO.new(fd, mode)}
+ end
+ f = IO.new(fd, "w")
+ f.write("FOO\n")
+ f.close
- assert_equal("FOO\n", File.read(t.path))
+ assert_equal("FOO\n", File.read(t.path))
+ }
end
def test_reinitialize
- t = make_tempfile
- f = open(t.path)
- assert_raise(RuntimeError) do
- f.instance_eval { initialize }
- end
+ make_tempfile {|t|
+ f = open(t.path)
+ begin
+ assert_raise(RuntimeError) do
+ f.instance_eval { initialize }
+ end
+ ensure
+ f.close
+ end
+ }
end
def test_new_with_block
- assert_in_out_err([], "r, w = IO.pipe; IO.new(r) {}", [], /^.+$/)
+ assert_in_out_err([], "r, w = IO.pipe; r.autoclose=false; IO.new(r.fileno) {}.close", [], /^.+$/)
+ n = "IO\u{5165 51fa 529b}"
+ c = eval("class #{n} < IO; self; end")
+ IO.pipe do |r, w|
+ assert_warning(/#{n}/) {
+ r.autoclose=false
+ io = c.new(r.fileno) {}
+ io.close
+ }
+ end
end
def test_readline2
@@ -1567,11 +2638,11 @@ End
end
def test_s_read
- t = make_tempfile
-
- assert_equal("foo\nbar\nbaz\n", File.read(t.path))
- assert_equal("foo\nba", File.read(t.path, 6))
- assert_equal("bar\n", File.read(t.path, 4, 4))
+ make_tempfile {|t|
+ assert_equal("foo\nbar\nbaz\n", File.read(t.path))
+ assert_equal("foo\nba", File.read(t.path, 6))
+ assert_equal("bar\n", File.read(t.path, 4, 4))
+ }
end
def test_uninitialized
@@ -1580,8 +2651,6 @@ End
def test_nofollow
# O_NOFOLLOW is not standard.
- return if /freebsd|linux/ !~ RUBY_PLATFORM
- return unless defined? File::NOFOLLOW
mkcdtmpdir {
open("file", "w") {|f| f << "content" }
begin
@@ -1596,17 +2665,1194 @@ End
File.foreach("slnk", :open_args=>[File::RDONLY|File::NOFOLLOW]) {}
}
}
- end
+ end if /freebsd|linux/ =~ RUBY_PLATFORM and defined? File::NOFOLLOW
def test_tainted
- t = make_tempfile
- assert(File.read(t.path, 4).tainted?, '[ruby-dev:38826]')
- assert(File.open(t.path) {|f| f.read(4)}.tainted?, '[ruby-dev:38826]')
+ make_tempfile {|t|
+ assert_predicate(File.read(t.path, 4), :tainted?, '[ruby-dev:38826]')
+ assert_predicate(File.open(t.path) {|f| f.read(4)}, :tainted?, '[ruby-dev:38826]')
+ }
end
def test_binmode_after_closed
- t = make_tempfile
+ make_tempfile {|t|
+ assert_raise(IOError) {t.binmode}
+ }
+ end
+
+ def test_DATA_binmode
+ assert_separately([], <<-SRC)
+assert_not_predicate(DATA, :binmode?)
+__END__
+ SRC
+ end
+
+ def test_threaded_flush
+ bug3585 = '[ruby-core:31348]'
+ src = "#{<<~"begin;"}\n#{<<~'end;'}"
+ begin;
+ t = Thread.new { sleep 3 }
+ Thread.new {sleep 1; t.kill; p 'hi!'}
+ t.join
+ end;
+ 10.times.map do
+ Thread.start do
+ assert_in_out_err([], src, timeout: 20) {|stdout, stderr|
+ assert_no_match(/hi.*hi/, stderr.join, bug3585)
+ }
+ end
+ end.each {|th| th.join}
+ end
+
+ def test_flush_in_finalizer1
+ bug3910 = '[ruby-dev:42341]'
+ tmp = Tempfile.open("bug3910") {|t|
+ path = t.path
+ t.close
+ fds = []
+ assert_nothing_raised(TypeError, bug3910) do
+ 500.times {
+ f = File.open(path, "w")
+ f.instance_variable_set(:@test_flush_in_finalizer1, true)
+ fds << f.fileno
+ f.print "hoge"
+ }
+ end
+ t
+ }
+ ensure
+ ObjectSpace.each_object(File) {|f|
+ if f.instance_variables.include?(:@test_flush_in_finalizer1)
+ f.close
+ end
+ }
+ tmp.close!
+ end
+
+ def test_flush_in_finalizer2
+ bug3910 = '[ruby-dev:42341]'
+ Tempfile.open("bug3910") {|t|
+ path = t.path
+ t.close
+ begin
+ 1.times do
+ io = open(path,"w")
+ io.instance_variable_set(:@test_flush_in_finalizer2, true)
+ io.print "hoge"
+ end
+ assert_nothing_raised(TypeError, bug3910) do
+ GC.start
+ end
+ ensure
+ ObjectSpace.each_object(File) {|f|
+ if f.instance_variables.include?(:@test_flush_in_finalizer2)
+ f.close
+ end
+ }
+ end
+ t.close!
+ }
+ end
+
+ def test_readlines_limit_0
+ bug4024 = '[ruby-dev:42538]'
+ make_tempfile {|t|
+ open(t.path, "r") do |io|
+ assert_raise(ArgumentError, bug4024) do
+ io.readlines(0)
+ end
+ end
+ }
+ end
+
+ def test_each_line_limit_0
+ bug4024 = '[ruby-dev:42538]'
+ make_tempfile {|t|
+ open(t.path, "r") do |io|
+ assert_raise(ArgumentError, bug4024) do
+ io.each_line(0).next
+ end
+ end
+ }
+ end
+
+ def os_and_fs(path)
+ uname = Etc.uname
+ os = "#{uname[:sysname]} #{uname[:release]}"
+
+ fs = nil
+ if uname[:sysname] == 'Linux'
+ # [ruby-dev:45703] Old Linux's fadvise() doesn't work on tmpfs.
+ mount = `mount`
+ mountpoints = []
+ mount.scan(/ on (\S+) type (\S+) /) {
+ mountpoints << [$1, $2]
+ }
+ mountpoints.sort_by {|mountpoint, fstype| mountpoint.length }.reverse_each {|mountpoint, fstype|
+ if path == mountpoint
+ fs = fstype
+ break
+ end
+ mountpoint += "/" if %r{/\z} !~ mountpoint
+ if path.start_with?(mountpoint)
+ fs = fstype
+ break
+ end
+ }
+ end
+
+ if fs
+ "#{fs} on #{os}"
+ else
+ os
+ end
+ end
+
+ def test_advise
+ make_tempfile {|tf|
+ assert_raise(ArgumentError, "no arguments") { tf.advise }
+ %w{normal random sequential willneed dontneed noreuse}.map(&:to_sym).each do |adv|
+ [[0,0], [0, 20], [400, 2]].each do |offset, len|
+ open(tf.path) do |t|
+ ret = assert_nothing_raised(lambda { os_and_fs(tf.path) }) {
+ begin
+ t.advise(adv, offset, len)
+ rescue Errno::EINVAL => e
+ if /linux/ =~ RUBY_PLATFORM && (Etc.uname[:release].split('.').map(&:to_i) <=> [3,6]) < 0
+ next # [ruby-core:65355] tmpfs is not supported
+ else
+ raise e
+ end
+ end
+ }
+ assert_nil(ret)
+ assert_raise(ArgumentError, "superfluous arguments") do
+ t.advise(adv, offset, len, offset)
+ end
+ assert_raise(TypeError, "wrong type for first argument") do
+ t.advise(adv.to_s, offset, len)
+ end
+ assert_raise(TypeError, "wrong type for last argument") do
+ t.advise(adv, offset, Array(len))
+ end
+ assert_raise(RangeError, "last argument too big") do
+ t.advise(adv, offset, 9999e99)
+ end
+ end
+ assert_raise(IOError, "closed file") do
+ make_tempfile {|tf2|
+ tf2.advise(adv.to_sym, offset, len)
+ }
+ end
+ end
+ end
+ }
+ end
+
+ def test_invalid_advise
+ feature4204 = '[ruby-dev:42887]'
+ make_tempfile {|tf|
+ %W{Normal rand glark will_need zzzzzzzzzzzz \u2609}.map(&:to_sym).each do |adv|
+ [[0,0], [0, 20], [400, 2]].each do |offset, len|
+ open(tf.path) do |t|
+ assert_raise_with_message(NotImplementedError, /#{Regexp.quote(adv.inspect)}/, feature4204) { t.advise(adv, offset, len) }
+ end
+ end
+ end
+ }
+ end
+
+ def test_fcntl_lock_linux
+ pad = 0
+ Tempfile.create(self.class.name) do |f|
+ r, w = IO.pipe
+ pid = fork do
+ r.close
+ lock = [Fcntl::F_WRLCK, IO::SEEK_SET, pad, 12, 34, 0].pack("s!s!i!L!L!i!")
+ f.fcntl Fcntl::F_SETLKW, lock
+ w.syswrite "."
+ sleep
+ end
+ w.close
+ assert_equal ".", r.read(1)
+ r.close
+ pad = 0
+ getlock = [Fcntl::F_WRLCK, 0, pad, 0, 0, 0].pack("s!s!i!L!L!i!")
+ f.fcntl Fcntl::F_GETLK, getlock
+
+ ptype, whence, pad, start, len, lockpid = getlock.unpack("s!s!i!L!L!i!")
+
+ assert_equal(ptype, Fcntl::F_WRLCK)
+ assert_equal(whence, IO::SEEK_SET)
+ assert_equal(start, 12)
+ assert_equal(len, 34)
+ assert_equal(pid, lockpid)
+
+ Process.kill :TERM, pid
+ Process.waitpid2(pid)
+ end
+ end if /x86_64-linux/ =~ RUBY_PLATFORM and # A binary form of struct flock depend on platform
+ [nil].pack("p").bytesize == 8 # unless x32 platform.
+
+ def test_fcntl_lock_freebsd
+ start = 12
+ len = 34
+ sysid = 0
+ Tempfile.create(self.class.name) do |f|
+ r, w = IO.pipe
+ pid = fork do
+ r.close
+ lock = [start, len, 0, Fcntl::F_WRLCK, IO::SEEK_SET, sysid].pack("qqis!s!i!")
+ f.fcntl Fcntl::F_SETLKW, lock
+ w.syswrite "."
+ sleep
+ end
+ w.close
+ assert_equal ".", r.read(1)
+ r.close
+
+ getlock = [0, 0, 0, Fcntl::F_WRLCK, 0, 0].pack("qqis!s!i!")
+ f.fcntl Fcntl::F_GETLK, getlock
+
+ start, len, lockpid, ptype, whence, sysid = getlock.unpack("qqis!s!i!")
+
+ assert_equal(ptype, Fcntl::F_WRLCK)
+ assert_equal(whence, IO::SEEK_SET)
+ assert_equal(start, 12)
+ assert_equal(len, 34)
+ assert_equal(pid, lockpid)
+
+ Process.kill :TERM, pid
+ Process.waitpid2(pid)
+ end
+ end if /freebsd/ =~ RUBY_PLATFORM # A binary form of struct flock depend on platform
+
+ def test_fcntl_dupfd
+ Tempfile.create(self.class.name) do |f|
+ fd = f.fcntl(Fcntl::F_DUPFD, 63)
+ begin
+ assert_operator(fd, :>=, 63)
+ ensure
+ IO.for_fd(fd).close
+ end
+ end
+ end
+
+ def test_cross_thread_close_fd
+ with_pipe do |r,w|
+ read_thread = Thread.new do
+ begin
+ r.read(1)
+ rescue => e
+ e
+ end
+ end
+
+ sleep(0.1) until read_thread.stop?
+ r.close
+ read_thread.join
+ assert_kind_of(IOError, read_thread.value)
+ end
+ end
+
+ def test_cross_thread_close_stdio
+ assert_separately([], <<-'end;')
+ IO.pipe do |r,w|
+ $stdin.reopen(r)
+ r.close
+ read_thread = Thread.new do
+ begin
+ $stdin.read(1)
+ rescue IOError => e
+ e
+ end
+ end
+ sleep(0.1) until read_thread.stop?
+ $stdin.close
+ assert_kind_of(IOError, read_thread.value)
+ end
+ end;
+ end
+
+ def test_single_exception_on_close
+ a = []
+ t = []
+ 10.times do
+ r, w = IO.pipe
+ a << [r, w]
+ t << Thread.new do
+ while r.gets
+ end rescue IOError
+ Thread.current.pending_interrupt?
+ end
+ end
+ a.each do |r, w|
+ w.write(-"\n")
+ w.close
+ r.close
+ end
+ t.each do |th|
+ assert_equal false, th.value, '[ruby-core:81581] [Bug #13632]'
+ end
+ end
+
+ def test_open_mode
+ feature4742 = "[ruby-core:36338]"
+ bug6055 = '[ruby-dev:45268]'
+
+ mkcdtmpdir do
+ assert_not_nil(f = File.open('symbolic', 'w'))
+ f.close
+ assert_not_nil(f = File.open('numeric', File::WRONLY|File::TRUNC|File::CREAT))
+ f.close
+ assert_not_nil(f = File.open('hash-symbolic', :mode => 'w'))
+ f.close
+ assert_not_nil(f = File.open('hash-numeric', :mode => File::WRONLY|File::TRUNC|File::CREAT), feature4742)
+ f.close
+ assert_nothing_raised(bug6055) {f = File.open('hash-symbolic', binmode: true)}
+ f.close
+ end
+ end
+
+ def test_s_write
+ mkcdtmpdir do
+ path = "test_s_write"
+ File.write(path, "foo\nbar\nbaz")
+ assert_equal("foo\nbar\nbaz", File.read(path))
+ File.write(path, "FOO", 0)
+ assert_equal("FOO\nbar\nbaz", File.read(path))
+ File.write(path, "BAR")
+ assert_equal("BAR", File.read(path))
+ File.write(path, "\u{3042}", mode: "w", encoding: "EUC-JP")
+ assert_equal("\u{3042}".encode("EUC-JP"), File.read(path, encoding: "EUC-JP"))
+ File.delete path
+ assert_equal(6, File.write(path, 'string', 2))
+ File.delete path
+ assert_raise(Errno::EINVAL) { File.write('nonexisting','string', -2) }
+ assert_equal(6, File.write(path, 'string'))
+ assert_equal(3, File.write(path, 'sub', 1))
+ assert_equal("ssubng", File.read(path))
+ File.delete path
+ assert_equal(3, File.write(path, "foo", encoding: "UTF-8"))
+ File.delete path
+ assert_equal(3, File.write(path, "foo", 0, encoding: "UTF-8"))
+ assert_equal("foo", File.read(path))
+ assert_equal(1, File.write(path, "f", 1, encoding: "UTF-8"))
+ assert_equal("ffo", File.read(path))
+ File.delete path
+ assert_equal(1, File.write(path, "f", 1, encoding: "UTF-8"))
+ 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) {
+ File.write(path, "foo", Object.new => Object.new)
+ }
+ end
+ end
+
+ def test_s_binread_does_not_leak_with_invalid_offset
+ assert_raise(Errno::EINVAL) { IO.binread(__FILE__, 0, -1) }
+ end
+
+ def test_s_binwrite
+ mkcdtmpdir do
+ path = "test_s_binwrite"
+ File.binwrite(path, "foo\nbar\nbaz")
+ assert_equal("foo\nbar\nbaz", File.read(path))
+ File.binwrite(path, "FOO", 0)
+ assert_equal("FOO\nbar\nbaz", File.read(path))
+ File.binwrite(path, "BAR")
+ assert_equal("BAR", File.read(path))
+ File.binwrite(path, "\u{3042}")
+ assert_equal("\u{3042}".force_encoding("ASCII-8BIT"), File.binread(path))
+ File.delete path
+ assert_equal(6, File.binwrite(path, 'string', 2))
+ File.delete path
+ assert_equal(6, File.binwrite(path, 'string'))
+ assert_equal(3, File.binwrite(path, 'sub', 1))
+ assert_equal("ssubng", File.binread(path))
+ assert_equal(6, File.size(path))
+ assert_raise(Errno::EINVAL) { File.binwrite('nonexisting', 'string', -2) }
+ assert_nothing_raised(TypeError) { File.binwrite(path, "string", mode: "w", encoding: "EUC-JP") }
+ end
+ end
+
+ def test_race_between_read
+ Tempfile.create("test") {|file|
+ begin
+ path = file.path
+ file.close
+ write_file = File.open(path, "wt")
+ read_file = File.open(path, "rt")
+
+ threads = []
+ 10.times do |i|
+ threads << Thread.new {write_file.print(i)}
+ threads << Thread.new {read_file.read}
+ end
+ assert_join_threads(threads)
+ assert(true, "[ruby-core:37197]")
+ ensure
+ read_file.close
+ write_file.close
+ end
+ }
+ end
+
+ def test_warn
+ assert_warning "warning\n" do
+ warn "warning"
+ end
+
+ assert_warning '' do
+ warn
+ end
+
+ assert_warning "[Feature #5029]\n[ruby-core:38070]\n" do
+ warn "[Feature #5029]", "[ruby-core:38070]"
+ end
+ end
+
+ def test_cloexec
+ return unless defined? Fcntl::FD_CLOEXEC
+ open(__FILE__) {|f|
+ assert_predicate(f, :close_on_exec?)
+ g = f.dup
+ begin
+ assert_predicate(g, :close_on_exec?)
+ f.reopen(g)
+ assert_predicate(f, :close_on_exec?)
+ ensure
+ g.close
+ end
+ g = IO.new(f.fcntl(Fcntl::F_DUPFD))
+ begin
+ assert_predicate(g, :close_on_exec?)
+ ensure
+ g.close
+ end
+ }
+ IO.pipe {|r,w|
+ assert_predicate(r, :close_on_exec?)
+ assert_predicate(w, :close_on_exec?)
+ }
+ end
+
+ def test_ioctl_linux
+ # Alpha, mips, sparc and ppc have an another ioctl request number scheme.
+ # So, hardcoded 0x80045200 may fail.
+ assert_nothing_raised do
+ File.open('/dev/urandom'){|f1|
+ entropy_count = ""
+ # RNDGETENTCNT(0x80045200) mean "get entropy count".
+ f1.ioctl(0x80045200, entropy_count)
+ }
+ end
+
+ buf = ''
+ assert_nothing_raised do
+ fionread = 0x541B
+ File.open(__FILE__){|f1|
+ f1.ioctl(fionread, buf)
+ }
+ end
+ assert_equal(File.size(__FILE__), buf.unpack('i!')[0])
+ end if /^(?:i.?86|x86_64)-linux/ =~ RUBY_PLATFORM
+
+ def test_ioctl_linux2
+ return unless STDIN.tty? # stdin is not a terminal
+ begin
+ f = File.open('/dev/tty')
+ rescue Errno::ENOENT, Errno::ENXIO => e
+ skip e.message
+ else
+ tiocgwinsz=0x5413
+ winsize=""
+ assert_nothing_raised {
+ f.ioctl(tiocgwinsz, winsize)
+ }
+ ensure
+ f&.close
+ end
+ end if /^(?:i.?86|x86_64)-linux/ =~ RUBY_PLATFORM
+
+ def test_setpos
+ mkcdtmpdir {
+ File.open("tmp.txt", "wb") {|f|
+ f.puts "a"
+ f.puts "bc"
+ f.puts "def"
+ }
+ pos1 = pos2 = pos3 = nil
+ File.open("tmp.txt", "rb") {|f|
+ assert_equal("a\n", f.gets)
+ pos1 = f.pos
+ assert_equal("bc\n", f.gets)
+ pos2 = f.pos
+ assert_equal("def\n", f.gets)
+ pos3 = f.pos
+ assert_equal(nil, f.gets)
+ }
+ File.open("tmp.txt", "rb") {|f|
+ f.pos = pos1
+ assert_equal("bc\n", f.gets)
+ assert_equal("def\n", f.gets)
+ assert_equal(nil, f.gets)
+ }
+ File.open("tmp.txt", "rb") {|f|
+ f.pos = pos2
+ assert_equal("def\n", f.gets)
+ assert_equal(nil, f.gets)
+ }
+ File.open("tmp.txt", "rb") {|f|
+ f.pos = pos3
+ assert_equal(nil, f.gets)
+ }
+ File.open("tmp.txt", "rb") {|f|
+ f.pos = File.size("tmp.txt")
+ s = "not empty string "
+ assert_equal("", f.read(0,s))
+ }
+ }
+ end
+
+ def test_std_fileno
+ assert_equal(0, STDIN.fileno)
+ assert_equal(1, STDOUT.fileno)
+ assert_equal(2, STDERR.fileno)
+ assert_equal(0, $stdin.fileno)
+ assert_equal(1, $stdout.fileno)
+ assert_equal(2, $stderr.fileno)
+ end
+
+ def test_frozen_fileno
+ bug9865 = '[ruby-dev:48241] [Bug #9865]'
+ with_pipe do |r,w|
+ fd = r.fileno
+ assert_equal(fd, r.freeze.fileno, bug9865)
+ end
+ end
+
+ def test_frozen_autoclose
+ with_pipe do |r,w|
+ assert_equal(true, r.freeze.autoclose?)
+ end
+ end
+
+ def test_sysread_locktmp
+ bug6099 = '[ruby-dev:45297]'
+ buf = " " * 100
+ 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)
+ w.write(data)
+ Thread.pass while th.alive?
+ th.join
+ end
+ assert_equal(data, buf, bug6099)
+ end
+
+ def test_readpartial_locktmp
+ bug6099 = '[ruby-dev:45297]'
+ buf = " " * 100
+ data = "a" * 100
+ th = nil
+ with_pipe do |r,w|
+ r.nonblock = true
+ th = Thread.new {r.readpartial(100, buf)}
+
+ Thread.pass until th.stop?
+
+ 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)
+ th.join
+ end
+ assert_equal(data, buf, bug6099)
+ end
+
+ def test_advise_pipe
+ # we don't know if other platforms have a real posix_fadvise()
+ with_pipe do |r,w|
+ # Linux 2.6.15 and earlier returned EINVAL instead of ESPIPE
+ assert_raise(Errno::ESPIPE, Errno::EINVAL) {
+ r.advise(:willneed) or skip "fadvise(2) is not implemented"
+ }
+ assert_raise(Errno::ESPIPE, Errno::EINVAL) {
+ w.advise(:willneed) or skip "fadvise(2) is not implemented"
+ }
+ end
+ end if /linux/ =~ RUBY_PLATFORM
+
+ def assert_buffer_not_raise_shared_string_error
+ bug6764 = '[ruby-core:46586]'
+ bug9847 = '[ruby-core:62643] [Bug #9847]'
+ size = 28
+ data = [*"a".."z", *"A".."Z"].shuffle.join("")
+ t = Tempfile.new("test_io")
+ t.write(data)
t.close
- assert_raise(IOError) {t.binmode}
+ w = []
+ assert_nothing_raised(RuntimeError, bug6764) do
+ buf = ''
+ File.open(t.path, "r") do |r|
+ while yield(r, size, buf)
+ w << buf.dup
+ end
+ end
+ end
+ assert_equal(data, w.join(""), bug9847)
+ ensure
+ t.close!
+ end
+
+ def test_read_buffer_not_raise_shared_string_error
+ assert_buffer_not_raise_shared_string_error do |r, size, buf|
+ r.read(size, buf)
+ end
+ end
+
+ def test_sysread_buffer_not_raise_shared_string_error
+ assert_buffer_not_raise_shared_string_error do |r, size, buf|
+ begin
+ r.sysread(size, buf)
+ rescue EOFError
+ nil
+ end
+ end
+ end
+
+ def test_readpartial_buffer_not_raise_shared_string_error
+ assert_buffer_not_raise_shared_string_error do |r, size, buf|
+ begin
+ r.readpartial(size, buf)
+ rescue EOFError
+ nil
+ end
+ end
+ end
+
+ def test_puts_recursive_ary
+ bug5986 = '[ruby-core:42444]'
+ c = Class.new {
+ def to_ary
+ [self]
+ end
+ }
+ s = StringIO.new
+ s.puts(c.new)
+ assert_equal("[...]\n", s.string, bug5986)
+ end
+
+ def test_io_select_with_many_files
+ bug8080 = '[ruby-core:53349]'
+
+ assert_normal_exit %q{
+ require "tempfile"
+
+ # Unfortunately, ruby doesn't export FD_SETSIZE. then we assume it's 1024.
+ fd_setsize = 1024
+
+ # try to raise RLIM_NOFILE to >FD_SETSIZE
+ begin
+ Process.setrlimit(Process::RLIMIT_NOFILE, fd_setsize+20)
+ rescue Errno::EPERM
+ exit 0
+ end
+
+ tempfiles = []
+ (0..fd_setsize+1).map {|i|
+ tempfiles << Tempfile.open("test_io_select_with_many_files")
+ }
+
+ IO.select(tempfiles)
+ }, bug8080, timeout: 50
+ end if defined?(Process::RLIMIT_NOFILE)
+
+ def test_read_32bit_boundary
+ bug8431 = '[ruby-core:55098] [Bug #8431]'
+ make_tempfile {|t|
+ assert_separately(["-", bug8431, t.path], <<-"end;")
+ msg = ARGV.shift
+ f = open(ARGV[0], "rb")
+ f.seek(0xffff_ffff)
+ assert_nil(f.read(1), msg)
+ end;
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_write_32bit_boundary
+ bug8431 = '[ruby-core:55098] [Bug #8431]'
+ make_tempfile {|t|
+ def t.close(unlink_now = false)
+ # TODO: Tempfile should deal with this delay on Windows?
+ # NOTE: re-opening with O_TEMPORARY does not work.
+ path = self.path
+ ret = super
+ if unlink_now
+ begin
+ File.unlink(path)
+ rescue Errno::ENOENT
+ rescue Errno::EACCES
+ sleep(2)
+ retry
+ end
+ end
+ ret
+ end
+
+ begin
+ assert_separately(["-", bug8431, t.path], <<-"end;", timeout: 30)
+ msg = ARGV.shift
+ f = open(ARGV[0], "wb")
+ f.seek(0xffff_ffff)
+ begin
+ # this will consume very long time or fail by ENOSPC on a
+ # filesystem which sparse file is not supported
+ f.write('1')
+ pos = f.tell
+ rescue Errno::ENOSPC
+ skip "non-sparse file system"
+ rescue SystemCallError
+ else
+ assert_equal(0x1_0000_0000, pos, msg)
+ end
+ end;
+ rescue Timeout::Error
+ skip "Timeout because of slow file writing"
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_unlocktmp_ensure
+ bug8669 = '[ruby-core:56121] [Bug #8669]'
+
+ str = ""
+ IO.pipe {|r,|
+ t = Thread.new {
+ assert_raise(RuntimeError) {
+ r.read(nil, str)
+ }
+ }
+ sleep 0.1 until t.stop?
+ t.raise
+ sleep 0.1 while t.alive?
+ assert_nothing_raised(RuntimeError, bug8669) { str.clear }
+ t.join
+ }
+ end if /cygwin/ !~ RUBY_PLATFORM
+
+ def test_readpartial_unlocktmp_ensure
+ bug8669 = '[ruby-core:56121] [Bug #8669]'
+
+ str = ""
+ IO.pipe {|r, w|
+ t = Thread.new {
+ assert_raise(RuntimeError) {
+ r.readpartial(4096, str)
+ }
+ }
+ sleep 0.1 until t.stop?
+ t.raise
+ sleep 0.1 while t.alive?
+ assert_nothing_raised(RuntimeError, bug8669) { str.clear }
+ t.join
+ }
+ end if /cygwin/ !~ RUBY_PLATFORM
+
+ def test_readpartial_bad_args
+ IO.pipe do |r, w|
+ w.write '.'
+ buf = String.new
+ assert_raise(ArgumentError) { r.readpartial(1, buf, exception: false) }
+ assert_raise(TypeError) { r.readpartial(1, exception: false) }
+ assert_equal [[r],[],[]], IO.select([r], nil, nil, 1)
+ assert_equal '.', r.readpartial(1)
+ end
+ end
+
+ def test_sysread_unlocktmp_ensure
+ bug8669 = '[ruby-core:56121] [Bug #8669]'
+
+ str = ""
+ IO.pipe {|r, w|
+ t = Thread.new {
+ assert_raise(RuntimeError) {
+ r.sysread(4096, str)
+ }
+ }
+ sleep 0.1 until t.stop?
+ t.raise
+ sleep 0.1 while t.alive?
+ assert_nothing_raised(RuntimeError, bug8669) { str.clear }
+ t.join
+ }
+ end if /cygwin/ !~ RUBY_PLATFORM
+
+ def test_exception_at_close
+ bug10153 = '[ruby-core:64463] [Bug #10153] exception in close at the end of block'
+ assert_raise(Errno::EBADF, bug10153) do
+ IO.pipe do |r, w|
+ assert_nothing_raised {IO.open(w.fileno) {}}
+ end
+ end
+ end
+
+ def test_close_twice
+ open(__FILE__) {|f|
+ assert_equal(nil, f.close)
+ assert_equal(nil, f.close)
+ }
+ end
+
+ def test_close_uninitialized
+ io = IO.allocate
+ assert_raise(IOError) { io.close }
+ end
+
+ def test_open_fifo_does_not_block_other_threads
+ mkcdtmpdir {
+ File.mkfifo("fifo")
+ assert_separately([], <<-'EOS')
+ t1 = Thread.new {
+ open("fifo", "r") {|r|
+ r.read
+ }
+ }
+ t2 = Thread.new {
+ open("fifo", "w") {|w|
+ w.write "foo"
+ }
+ }
+ t1_value, _ = assert_join_threads([t1, t2])
+ assert_equal("foo", t1_value)
+ EOS
+ }
+ end if /mswin|mingw|bccwin|cygwin/ !~ RUBY_PLATFORM
+
+ def test_open_flag
+ make_tempfile do |t|
+ assert_raise(Errno::EEXIST){ open(t.path, File::WRONLY|File::CREAT, flags: File::EXCL){} }
+ assert_raise(Errno::EEXIST){ open(t.path, 'w', flags: File::EXCL){} }
+ assert_raise(Errno::EEXIST){ open(t.path, mode: 'w', flags: File::EXCL){} }
+ end
+ end
+
+ def test_open_flag_binary
+ make_tempfile do |t|
+ open(t.path, File::RDONLY, flags: File::BINARY) do |f|
+ assert_equal true, f.binmode?
+ end
+ open(t.path, 'r', flags: File::BINARY) do |f|
+ assert_equal true, f.binmode?
+ end
+ open(t.path, mode: 'r', flags: File::BINARY) do |f|
+ assert_equal true, f.binmode?
+ end
+ end
+ end if File::BINARY != 0
+
+ def test_exclusive_mode
+ make_tempfile do |t|
+ assert_raise(Errno::EEXIST){ open(t.path, 'wx'){} }
+ assert_raise(ArgumentError){ open(t.path, 'rx'){} }
+ assert_raise(ArgumentError){ open(t.path, 'ax'){} }
+ end
+ end
+
+ def test_race_gets_and_close
+ opt = { signal: :ABRT, timeout: 200 }
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}", opt)
+ bug13076 = '[ruby-core:78845] [Bug #13076]'
+ begin;
+ 10.times do |i|
+ a = []
+ t = []
+ 10.times do
+ r,w = IO.pipe
+ a << [r,w]
+ t << Thread.new do
+ begin
+ while r.gets
+ end
+ rescue IOError
+ end
+ end
+ end
+ a.each do |r,w|
+ w.puts "hoge"
+ w.close
+ r.close
+ end
+ t.each do |th|
+ assert_same(th, th.join(2), bug13076)
+ end
+ end
+ end;
+ end
+
+ def test_race_closed_stream
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ bug13158 = '[ruby-core:79262] [Bug #13158]'
+ closed = nil
+ q = Queue.new
+ IO.pipe do |r, w|
+ thread = Thread.new do
+ begin
+ q << true
+ assert_raise_with_message(IOError, /stream closed/) do
+ while r.gets
+ end
+ end
+ ensure
+ closed = r.closed?
+ end
+ end
+ q.pop
+ sleep 0.01 until thread.stop?
+ r.close
+ thread.join
+ assert_equal(true, closed, bug13158 + ': stream should be closed')
+ end
+ end;
+ end
+
+ if RUBY_ENGINE == "ruby" # implementation details
+ def test_foreach_rs_conversion
+ make_tempfile {|t|
+ a = []
+ rs = Struct.new(:count).new(0)
+ def rs.to_str; self.count += 1; "\n"; end
+ IO.foreach(t.path, rs) {|x| a << x }
+ assert_equal(["foo\n", "bar\n", "baz\n"], a)
+ assert_equal(1, rs.count)
+ }
+ end
+
+ def test_foreach_rs_invalid
+ make_tempfile {|t|
+ rs = Object.new
+ def rs.to_str; raise "invalid rs"; end
+ assert_raise(RuntimeError) do
+ IO.foreach(t.path, rs, mode:"w") {}
+ end
+ assert_equal(["foo\n", "bar\n", "baz\n"], IO.foreach(t.path).to_a)
+ }
+ end
+
+ def test_foreach_limit_conversion
+ make_tempfile {|t|
+ a = []
+ lim = Struct.new(:count).new(0)
+ def lim.to_int; self.count += 1; -1; end
+ IO.foreach(t.path, lim) {|x| a << x }
+ assert_equal(["foo\n", "bar\n", "baz\n"], a)
+ assert_equal(1, lim.count)
+ }
+ end
+
+ def test_foreach_limit_invalid
+ make_tempfile {|t|
+ lim = Object.new
+ def lim.to_int; raise "invalid limit"; end
+ assert_raise(RuntimeError) do
+ IO.foreach(t.path, lim, mode:"w") {}
+ end
+ assert_equal(["foo\n", "bar\n", "baz\n"], IO.foreach(t.path).to_a)
+ }
+ end
+
+ def test_readlines_rs_invalid
+ make_tempfile {|t|
+ rs = Object.new
+ def rs.to_str; raise "invalid rs"; end
+ assert_raise(RuntimeError) do
+ IO.readlines(t.path, rs, mode:"w")
+ end
+ assert_equal(["foo\n", "bar\n", "baz\n"], IO.readlines(t.path))
+ }
+ end
+
+ def test_readlines_limit_invalid
+ make_tempfile {|t|
+ lim = Object.new
+ def lim.to_int; raise "invalid limit"; end
+ assert_raise(RuntimeError) do
+ IO.readlines(t.path, lim, mode:"w")
+ end
+ assert_equal(["foo\n", "bar\n", "baz\n"], IO.readlines(t.path))
+ }
+ end
+
+ def test_closed_stream_in_rescue
+ assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ 10.times do
+ assert_nothing_raised(RuntimeError, /frozen IOError/) do
+ IO.pipe do |r, w|
+ th = Thread.start {r.close}
+ r.gets
+ rescue IOError
+ # swallow pending exceptions
+ begin
+ sleep 0.001
+ rescue IOError
+ retry
+ end
+ ensure
+ th.kill.join
+ end
+ 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
+
+ def test_pread
+ make_tempfile { |t|
+ open(t.path) do |f|
+ assert_equal("bar", f.pread(3, 4))
+ buf = "asdf"
+ assert_equal("bar", f.pread(3, 4, buf))
+ assert_equal("bar", buf)
+ assert_raise(EOFError) { f.pread(1, f.size) }
+ end
+ }
+ end if IO.method_defined?(:pread)
+
+ def test_pwrite
+ make_tempfile { |t|
+ open(t.path, IO::RDWR) do |f|
+ assert_equal(3, f.pwrite("ooo", 4))
+ assert_equal("ooo", f.pread(3, 4))
+ end
+ }
+ end if IO.method_defined?(:pread) and IO.method_defined?(:pwrite)
+ end
+
+ def test_select_exceptfds
+ if Etc.uname[:sysname] == 'SunOS' && Etc.uname[:release] == '5.11'
+ skip "Solaris 11 fails this"
+ end
+
+ TCPServer.open('localhost', 0) do |svr|
+ con = TCPSocket.new('localhost', svr.addr[1])
+ acc = svr.accept
+ assert_equal 5, con.send('hello', Socket::MSG_OOB)
+ set = IO.select(nil, nil, [acc], 30)
+ assert_equal([[], [], [acc]], set, 'IO#select exceptions array OK')
+ acc.close
+ con.close
+ end
+ end if Socket.const_defined?(:MSG_OOB)
+
+ def test_recycled_fd_close
+ dot = -'.'
+ IO.pipe do |sig_rd, sig_wr|
+ noex = Thread.new do # everything right and never see exceptions :)
+ until sig_rd.wait_readable(0)
+ IO.pipe do |r, w|
+ th = Thread.new { r.read(1) }
+ w.write(dot)
+
+ assert_same th, th.join(15), '"good" reader timeout'
+ assert_equal(dot, th.value)
+ end
+ end
+ sig_rd.read(4)
+ end
+ 1000.times do |i| # stupid things and make exceptions:
+ IO.pipe do |r,w|
+ th = Thread.new do
+ begin
+ while r.gets
+ end
+ rescue IOError => e
+ e
+ end
+ end
+ Thread.pass until th.stop?
+
+ r.close
+ assert_same th, th.join(30), '"bad" reader timeout'
+ assert_match(/stream closed/, th.value.message)
+ end
+ end
+ sig_wr.write 'done'
+ assert_same noex, noex.join(20), '"good" writer timeout'
+ assert_equal 'done', noex.value ,'r63216'
+ end
+ end
+
+ def test_select_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)
+ 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;
+ th = Thread.new do
+ Thread.handle_interrupt(StandardError => :on_blocking) do
+ begin
+ IO.select(rset, wset)
+ rescue
+ retry
+ end while true
+ end
+ end
+ 50_000.times do
+ Thread.pass until th.stop?
+ th.raise(exc)
+ end
+ th.kill
+ th.join
+ end;
+ end
+
+ def test_external_encoding_index
+ IO.pipe {|r, w|
+ assert_raise(TypeError) {Marshal.dump(r)}
+ assert_raise(TypeError) {Marshal.dump(w)}
+ }
end
end
diff --git a/test/ruby/test_io_m17n.rb b/test/ruby/test_io_m17n.rb
index 21b7941cbe..416a24ba6b 100644
--- a/test/ruby/test_io_m17n.rb
+++ b/test/ruby/test_io_m17n.rb
@@ -1,7 +1,9 @@
+# coding: US-ASCII
+# frozen_string_literal: false
require 'test/unit'
require 'tmpdir'
+require 'tempfile'
require 'timeout'
-require_relative 'envutil'
class TestIO_M17N < Test::Unit::TestCase
ENCS = [
@@ -19,6 +21,35 @@ class TestIO_M17N < Test::Unit::TestCase
}
end
+ def pipe(*args, wp, rp)
+ re, we = nil, nil
+ r, w = IO.pipe(*args)
+ rt = Thread.new do
+ begin
+ rp.call(r)
+ rescue Exception
+ r.close
+ re = $!
+ end
+ end
+ wt = Thread.new do
+ begin
+ wp.call(w)
+ rescue Exception
+ w.close
+ we = $!
+ end
+ end
+ flunk("timeout") unless wt.join(10) && rt.join(10)
+ ensure
+ w.close unless !w || w.closed?
+ r.close unless !r || r.closed?
+ (wt.kill; wt.join) if wt
+ (rt.kill; rt.join) if rt
+ raise we if we
+ raise re if re
+ end
+
def with_pipe(*args)
r, w = IO.pipe(*args)
begin
@@ -42,7 +73,7 @@ class TestIO_M17N < Test::Unit::TestCase
#{encdump expected} expected but not equal to
#{encdump actual}.
EOT
- assert_block(full_message) { expected == actual }
+ assert_equal(expected, actual, full_message)
end
def test_open_r
@@ -75,6 +106,42 @@ EOT
}
end
+ def test_open_r_ascii8bit
+ with_tmpdir {
+ generate_file('tmp', "")
+ EnvUtil.with_default_external(Encoding::ASCII_8BIT) do
+ EnvUtil.with_default_internal(Encoding::UTF_8) do
+ open("tmp", "r") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ open("tmp", "r:ascii-8bit") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ open("tmp", "r:ascii-8bit:utf-16") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ end
+ EnvUtil.with_default_internal(nil) do
+ open("tmp", "r") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ open("tmp", "r:ascii-8bit") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ open("tmp", "r:ascii-8bit:utf-16") {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ end
+ end
+ }
+ end
+
def test_open_r_enc_in_opt
with_tmpdir {
generate_file('tmp', "")
@@ -85,7 +152,27 @@ EOT
}
end
- def test_open_r_enc_in_opt2
+ def test_open_r_encname_in_opt
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "r", encoding: Encoding::EUC_JP) {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_r_ext_enc_in_opt
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "r", external_encoding: Encoding::EUC_JP) {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_r_ext_encname_in_opt
with_tmpdir {
generate_file('tmp', "")
open("tmp", "r", external_encoding: "euc-jp") {|f|
@@ -98,6 +185,16 @@ EOT
def test_open_r_enc_enc
with_tmpdir {
generate_file('tmp', "")
+ open("tmp", "r", external_encoding: Encoding::EUC_JP, internal_encoding: Encoding::UTF_8) {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(Encoding::UTF_8, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_r_encname_encname
+ with_tmpdir {
+ generate_file('tmp', "")
open("tmp", "r:euc-jp:utf-8") {|f|
assert_equal(Encoding::EUC_JP, f.external_encoding)
assert_equal(Encoding::UTF_8, f.internal_encoding)
@@ -105,7 +202,7 @@ EOT
}
end
- def test_open_r_enc_enc_in_opt
+ def test_open_r_encname_encname_in_opt
with_tmpdir {
generate_file('tmp', "")
open("tmp", "r", encoding: "euc-jp:utf-8") {|f|
@@ -115,7 +212,17 @@ EOT
}
end
- def test_open_r_enc_enc_in_opt2
+ def test_open_r_enc_enc_in_opt
+ with_tmpdir {
+ generate_file('tmp', "")
+ open("tmp", "r", external_encoding: Encoding::EUC_JP, internal_encoding: Encoding::UTF_8) {|f|
+ assert_equal(Encoding::EUC_JP, f.external_encoding)
+ assert_equal(Encoding::UTF_8, f.internal_encoding)
+ }
+ }
+ end
+
+ def test_open_r_externalencname_internalencname_in_opt
with_tmpdir {
generate_file('tmp', "")
open("tmp", "r", external_encoding: "euc-jp", internal_encoding: "utf-8") {|f|
@@ -206,13 +313,24 @@ EOT
}
end
+ def test_ignored_encoding_option
+ enc = "\u{30a8 30f3 30b3 30fc 30c7 30a3 30f3 30b0}"
+ pattern = /#{enc}/
+ assert_warning(pattern) {
+ open(IO::NULL, external_encoding: "us-ascii", encoding: enc) {}
+ }
+ assert_warning(pattern) {
+ open(IO::NULL, internal_encoding: "us-ascii", encoding: enc) {}
+ }
+ end
+
def test_io_new_enc
with_tmpdir {
generate_file("tmp", "\xa1")
fd = IO.sysopen("tmp")
f = IO.new(fd, "r:sjis")
begin
- assert_equal(Encoding::Shift_JIS, f.read.encoding)
+ assert_equal(Encoding::Windows_31J, f.read.encoding)
ensure
f.close
end
@@ -220,54 +338,68 @@ EOT
end
def test_s_pipe_invalid
- with_pipe("utf-8", "euc-jp", :invalid=>:replace) {|r, w|
- w << "\x80"
- w.close
- assert_equal("?", r.read)
- }
+ pipe("utf-8", "euc-jp", { :invalid=>:replace },
+ proc do |w|
+ w << "\x80"
+ w.close
+ end,
+ proc do |r|
+ assert_equal("?", r.read)
+ end)
end
def test_s_pipe_undef
- with_pipe("utf-8:euc-jp", :undef=>:replace) {|r, w|
- w << "\ufffd"
- w.close
- assert_equal("?", r.read)
- }
+ pipe("utf-8:euc-jp", { :undef=>:replace },
+ proc do |w|
+ w << "\ufffd"
+ w.close
+ end,
+ proc do |r|
+ assert_equal("?", r.read)
+ end)
end
def test_s_pipe_undef_replace_string
- with_pipe("utf-8:euc-jp", :undef=>:replace, :replace=>"X") {|r, w|
- w << "\ufffd"
- w.close
- assert_equal("X", r.read)
- }
+ pipe("utf-8:euc-jp", { :undef=>:replace, :replace=>"X" },
+ proc do |w|
+ w << "\ufffd"
+ w.close
+ end,
+ proc do |r|
+ assert_equal("X", r.read)
+ end)
end
def test_dup
- with_pipe("utf-8:euc-jp") {|r, w|
- w << "\u3042"
- w.close
- r2 = r.dup
- begin
- assert_equal("\xA4\xA2".force_encoding("euc-jp"), r2.read)
- ensure
- r2.close
- end
-
- }
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ w << "\u3042"
+ w.close
+ end,
+ proc do |r|
+ r2 = r.dup
+ begin
+ assert_equal("\xA4\xA2".force_encoding("euc-jp"), r2.read)
+ ensure
+ r2.close
+ end
+ end)
end
def test_dup_undef
- with_pipe("utf-8:euc-jp", :undef=>:replace) {|r, w|
- w << "\uFFFD"
- w.close
- r2 = r.dup
- begin
- assert_equal("?", r2.read)
- ensure
- r2.close
- end
- }
+ pipe("utf-8:euc-jp", { :undef=>:replace },
+ proc do |w|
+ w << "\uFFFD"
+ w.close
+ end,
+ proc do |r|
+ r2 = r.dup
+ begin
+ assert_equal("?", r2.read)
+ ensure
+ r2.close
+ end
+ end)
end
def test_stdin
@@ -331,47 +463,53 @@ EOT
end
def test_pipe_terminator_conversion
- with_pipe("euc-jp:utf-8") {|r, w|
- w.write "before \xa2\xa2 after"
- rs = "\xA2\xA2".encode("utf-8", "euc-jp")
- w.close
- timeout(1) {
- assert_equal("before \xa2\xa2".encode("utf-8", "euc-jp"),
- r.gets(rs))
- }
- }
+ rs = "\xA2\xA2".encode("utf-8", "euc-jp")
+ pipe("euc-jp:utf-8",
+ proc do |w|
+ w.write "before \xa2\xa2 after"
+ w.close
+ end,
+ proc do |r|
+ Timeout.timeout(1) {
+ assert_equal("before \xa2\xa2".encode("utf-8", "euc-jp"),
+ r.gets(rs))
+ }
+ end)
end
def test_pipe_conversion
- with_pipe("euc-jp:utf-8") {|r, w|
- w.write "\xa1\xa1"
- assert_equal("\xa1\xa1".encode("utf-8", "euc-jp"), r.getc)
- }
+ pipe("euc-jp:utf-8",
+ proc do |w|
+ w.write "\xa1\xa1"
+ end,
+ proc do |r|
+ assert_equal("\xa1\xa1".encode("utf-8", "euc-jp"), r.getc)
+ end)
end
def test_pipe_convert_partial_read
- with_pipe("euc-jp:utf-8") {|r, w|
- begin
- t = Thread.new {
- w.write "\xa1"
- sleep 0.1
- w.write "\xa1"
- }
- assert_equal("\xa1\xa1".encode("utf-8", "euc-jp"), r.getc)
- ensure
- t.join if t
- end
- }
+ pipe("euc-jp:utf-8",
+ proc do |w|
+ w.write "\xa1"
+ sleep 0.1
+ w.write "\xa1"
+ end,
+ proc do |r|
+ assert_equal("\xa1\xa1".encode("utf-8", "euc-jp"), r.getc)
+ end)
end
def test_getc_invalid
- with_pipe("euc-jp:utf-8") {|r, w|
- w << "\xa1xyz"
- w.close
- err = assert_raise(Encoding::InvalidByteSequenceError) { r.getc }
- assert_equal("\xA1".force_encoding("ascii-8bit"), err.error_bytes)
- assert_equal("xyz", r.read(10))
- }
+ pipe("euc-jp:utf-8",
+ proc do |w|
+ w << "\xa1xyz"
+ w.close
+ end,
+ proc do |r|
+ err = assert_raise(Encoding::InvalidByteSequenceError) { r.getc }
+ assert_equal("\xA1".force_encoding("ascii-8bit"), err.error_bytes)
+ assert_equal("xyz", r.read(10))
+ end)
end
def test_getc_stateful_conversion
@@ -389,14 +527,13 @@ EOT
with_tmpdir {
src = "\u3042"
generate_file('tmp', src)
- defext = Encoding.default_external
- Encoding.default_external = Encoding::UTF_8
- open("tmp", "rt") {|f|
- s = f.getc
- assert_equal(true, s.valid_encoding?)
- assert_equal("\u3042", s)
- }
- Encoding.default_external = defext
+ EnvUtil.with_default_external(Encoding::UTF_8) do
+ open("tmp", "rt") {|f|
+ s = f.getc
+ assert_equal(true, s.valid_encoding?)
+ assert_equal("\u3042", s)
+ }
+ end
}
end
@@ -404,17 +541,40 @@ EOT
with_tmpdir {
src = "\xE3\x81"
generate_file('tmp', src)
- defext = Encoding.default_external
- Encoding.default_external = Encoding::UTF_8
- open("tmp", "rt") {|f|
- s = f.getc
- assert_equal(false, s.valid_encoding?)
- assert_equal("\xE3".force_encoding("UTF-8"), s)
- s = f.getc
- assert_equal(false, s.valid_encoding?)
- assert_equal("\x81".force_encoding("UTF-8"), s)
+ EnvUtil.with_default_external(Encoding::UTF_8) do
+ open("tmp", "rt") {|f|
+ s = f.getc
+ assert_equal(false, s.valid_encoding?)
+ assert_equal("\xE3".force_encoding("UTF-8"), s)
+ s = f.getc
+ assert_equal(false, s.valid_encoding?)
+ assert_equal("\x81".force_encoding("UTF-8"), s)
+ }
+ end
+ }
+ end
+
+ def test_ungetc_int
+ with_tmpdir {
+ generate_file('tmp', "A")
+ s = open("tmp", "r:GB18030") {|f|
+ f.ungetc(0x8431A439)
+ f.read
+ }
+ assert_equal(Encoding::GB18030, s.encoding)
+ assert_str_equal(0x8431A439.chr("GB18030")+"A", s)
+ }
+ end
+
+ def test_ungetc_str
+ with_tmpdir {
+ generate_file('tmp', "A")
+ s = open("tmp", "r:GB18030") {|f|
+ f.ungetc(0x8431A439.chr("GB18030"))
+ f.read
}
- Encoding.default_external = defext
+ assert_equal(Encoding::GB18030, s.encoding)
+ assert_str_equal(0x8431A439.chr("GB18030")+"A", s)
}
end
@@ -573,185 +733,224 @@ EOT
utf8 = "\u6666"
eucjp = "\xb3\xa2".force_encoding("EUC-JP")
- with_pipe {|r,w|
- assert_equal(Encoding.default_external, r.external_encoding)
- assert_equal(nil, r.internal_encoding)
+ pipe(proc do |w|
w << utf8
w.close
+ end, proc do |r|
+ assert_equal(Encoding.default_external, r.external_encoding)
+ assert_equal(nil, r.internal_encoding)
s = r.read
assert_equal(Encoding.default_external, s.encoding)
assert_str_equal(utf8.dup.force_encoding(Encoding.default_external), s)
- }
-
- with_pipe("EUC-JP") {|r,w|
- assert_equal(Encoding::EUC_JP, r.external_encoding)
- assert_equal(nil, r.internal_encoding)
- w << eucjp
- w.close
- assert_equal(eucjp, r.read)
- }
-
- with_pipe("UTF-8") {|r,w|
- w << "a" * 1023 + "\u3042" + "a" * 1022
- w.close
- assert_equal(true, r.read.valid_encoding?)
- }
-
- with_pipe("UTF-8:EUC-JP") {|r,w|
- assert_equal(Encoding::UTF_8, r.external_encoding)
- assert_equal(Encoding::EUC_JP, r.internal_encoding)
- w << utf8
- w.close
- assert_equal(eucjp, r.read)
- }
-
- e = assert_raise(ArgumentError) {with_pipe("UTF-8", "UTF-8".encode("UTF-32BE")) {}}
- assert_match(/invalid name encoding/, e.message)
- e = assert_raise(ArgumentError) {with_pipe("UTF-8".encode("UTF-32BE")) {}}
- assert_match(/invalid name encoding/, e.message)
+ end)
+
+ pipe("EUC-JP",
+ proc do |w|
+ w << eucjp
+ w.close
+ end,
+ proc do |r|
+ assert_equal(Encoding::EUC_JP, r.external_encoding)
+ assert_equal(nil, r.internal_encoding)
+ assert_equal(eucjp, r.read)
+ end)
+
+ pipe("UTF-8",
+ proc do |w|
+ w << "a" * 1023 + "\u3042" + "a" * 1022
+ w.close
+ end,
+ proc do |r|
+ assert_equal(true, r.read.valid_encoding?)
+ end)
+
+ pipe("UTF-8:EUC-JP",
+ proc do |w|
+ w << utf8
+ w.close
+ end,
+ proc do |r|
+ assert_equal(Encoding::UTF_8, r.external_encoding)
+ assert_equal(Encoding::EUC_JP, r.internal_encoding)
+ assert_equal(eucjp, r.read)
+ end)
+
+ assert_raise_with_message(ArgumentError, /invalid name encoding/) do
+ with_pipe("UTF-8", "UTF-8".encode("UTF-32BE")) {}
+ end
+ assert_raise_with_message(ArgumentError, /invalid name encoding/) do
+ with_pipe("UTF-8".encode("UTF-32BE")) {}
+ end
ENCS.each {|enc|
- with_pipe(enc) {|r, w|
- w << "\xc2\xa1"
- w.close
- s = r.getc
- assert_equal(enc, s.encoding)
- }
+ pipe(enc,
+ proc do |w|
+ w << "\xc2\xa1"
+ w.close
+ end,
+ proc do |r|
+ s = r.getc
+ assert_equal(enc, s.encoding)
+ end)
}
ENCS.each {|enc|
next if enc == Encoding::ASCII_8BIT
next if enc == Encoding::UTF_8
- with_pipe("#{enc}:UTF-8") {|r, w|
- w << "\xc2\xa1"
- w.close
- s = r.read
- assert_equal(Encoding::UTF_8, s.encoding)
- assert_equal(s.encode("UTF-8"), s)
- }
+ pipe("#{enc}:UTF-8",
+ proc do |w|
+ w << "\xc2\xa1"
+ w.close
+ end,
+ proc do |r|
+ s = r.read
+ assert_equal(Encoding::UTF_8, s.encoding)
+ assert_equal(s.encode("UTF-8"), s)
+ end)
}
end
def test_marshal
- with_pipe("EUC-JP") {|r, w|
- data = 56225
- Marshal.dump(data, w)
- w.close
- result = nil
- assert_nothing_raised("[ruby-dev:33264]") { result = Marshal.load(r) }
- assert_equal(data, result)
- }
+ data = 56225
+ pipe("EUC-JP",
+ proc do |w|
+ Marshal.dump(data, w)
+ w.close
+ end,
+ proc do |r|
+ result = nil
+ assert_nothing_raised("[ruby-dev:33264]") { result = Marshal.load(r) }
+ assert_equal(data, result)
+ end)
end
def test_gets_nil
- with_pipe("UTF-8:EUC-JP") {|r, w|
- w << "\u{3042}"
- w.close
- result = r.gets(nil)
- assert_equal("\u{3042}".encode("euc-jp"), result)
- }
+ pipe("UTF-8:EUC-JP",
+ proc do |w|
+ w << "\u{3042}"
+ w.close
+ end,
+ proc do |r|
+ result = r.gets(nil)
+ assert_equal("\u{3042}".encode("euc-jp"), result)
+ end)
end
def test_gets_limit
- with_pipe("euc-jp") {|r, w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close
- assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.gets(1))
- }
- with_pipe("euc-jp") {|r, w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close
- assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.gets(2))
- }
- with_pipe("euc-jp") {|r, w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close
- assert_equal("\xa4\xa2\xa4\xa4".force_encoding("euc-jp"), r.gets(3))
- }
- with_pipe("euc-jp") {|r, w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close
- assert_equal("\xa4\xa2\xa4\xa4".force_encoding("euc-jp"), r.gets(4))
- }
- with_pipe("euc-jp") {|r, w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close
- assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6".force_encoding("euc-jp"), r.gets(5))
- }
- with_pipe("euc-jp") {|r, w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close
- assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6".force_encoding("euc-jp"), r.gets(6))
- }
- with_pipe("euc-jp") {|r, w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close
- assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6\n".force_encoding("euc-jp"), r.gets(7))
- }
- with_pipe("euc-jp") {|r, w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close
- assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6\n".force_encoding("euc-jp"), r.gets(8))
- }
- with_pipe("euc-jp") {|r, w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close
- assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6\n".force_encoding("euc-jp"), r.gets(9))
- }
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.gets(1)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.gets(2)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2\xa4\xa4".force_encoding("euc-jp"), r.gets(3)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2\xa4\xa4".force_encoding("euc-jp"), r.gets(4)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6".force_encoding("euc-jp"), r.gets(5)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6".force_encoding("euc-jp"), r.gets(6)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6\n".force_encoding("euc-jp"), r.gets(7)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6\n".force_encoding("euc-jp"), r.gets(8)) })
+ pipe("euc-jp",
+ proc {|w| w << "\xa4\xa2\xa4\xa4\xa4\xa6\n\xa4\xa8\xa4\xaa"; w.close },
+ proc {|r| assert_equal("\xa4\xa2\xa4\xa4\xa4\xa6\n".force_encoding("euc-jp"), r.gets(9)) })
end
def test_gets_invalid
- with_pipe("utf-8:euc-jp") {|r, w|
- before = "\u{3042}\u{3044}"
- invalid = "\x80".force_encoding("utf-8")
- after = "\u{3046}\u{3048}"
- w << before + invalid + after
- w.close
- err = assert_raise(Encoding::InvalidByteSequenceError) { r.gets }
- assert_equal(invalid.force_encoding("ascii-8bit"), err.error_bytes)
- assert_equal(after.encode("euc-jp"), r.gets)
- }
+ before = "\u{3042}\u{3044}"
+ invalid = "\x80".force_encoding("utf-8")
+ after = "\u{3046}\u{3048}"
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ w << before + invalid + after
+ w.close
+ end,
+ proc do |r|
+ err = assert_raise(Encoding::InvalidByteSequenceError) { r.gets }
+ assert_equal(invalid.force_encoding("ascii-8bit"), err.error_bytes)
+ assert_equal(after.encode("euc-jp"), r.gets)
+ end)
end
def test_getc_invalid2
- with_pipe("utf-8:euc-jp") {|r, w|
- before1 = "\u{3042}"
- before2 = "\u{3044}"
- invalid = "\x80".force_encoding("utf-8")
- after1 = "\u{3046}"
- after2 = "\u{3048}"
- w << before1 + before2 + invalid + after1 + after2
- w.close
- assert_equal(before1.encode("euc-jp"), r.getc)
- assert_equal(before2.encode("euc-jp"), r.getc)
- err = assert_raise(Encoding::InvalidByteSequenceError) { r.getc }
- assert_equal(invalid.force_encoding("ascii-8bit"), err.error_bytes)
- assert_equal(after1.encode("euc-jp"), r.getc)
- assert_equal(after2.encode("euc-jp"), r.getc)
- }
+ before1 = "\u{3042}"
+ before2 = "\u{3044}"
+ invalid = "\x80".force_encoding("utf-8")
+ after1 = "\u{3046}"
+ after2 = "\u{3048}"
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ w << before1 + before2 + invalid + after1 + after2
+ w.close
+ end,
+ proc do |r|
+ assert_equal(before1.encode("euc-jp"), r.getc)
+ assert_equal(before2.encode("euc-jp"), r.getc)
+ err = assert_raise(Encoding::InvalidByteSequenceError) { r.getc }
+ assert_equal(invalid.force_encoding("ascii-8bit"), err.error_bytes)
+ assert_equal(after1.encode("euc-jp"), r.getc)
+ assert_equal(after2.encode("euc-jp"), r.getc)
+ end)
end
def test_getc_invalid3
- with_pipe("utf-16le:euc-jp", binmode: true) {|r, w|
- before1 = "\x42\x30".force_encoding("utf-16le")
- before2 = "\x44\x30".force_encoding("utf-16le")
- invalid = "\x00\xd8".force_encoding("utf-16le")
- after1 = "\x46\x30".force_encoding("utf-16le")
- after2 = "\x48\x30".force_encoding("utf-16le")
- w << before1 + before2 + invalid + after1 + after2
- w.close
- assert_equal(before1.encode("euc-jp"), r.getc)
- assert_equal(before2.encode("euc-jp"), r.getc)
- err = assert_raise(Encoding::InvalidByteSequenceError) { r.getc }
- assert_equal(invalid.force_encoding("ascii-8bit"), err.error_bytes)
- assert_equal(after1.encode("euc-jp"), r.getc)
- assert_equal(after2.encode("euc-jp"), r.getc)
- }
+ before1 = "\x42\x30".force_encoding("utf-16le")
+ before2 = "\x44\x30".force_encoding("utf-16le")
+ invalid = "\x00\xd8".force_encoding("utf-16le")
+ after1 = "\x46\x30".force_encoding("utf-16le")
+ after2 = "\x48\x30".force_encoding("utf-16le")
+ pipe("utf-16le:euc-jp", { :binmode => true },
+ proc do |w|
+ w << before1 + before2 + invalid + after1 + after2
+ w.close
+ end,
+ proc do |r|
+ assert_equal(before1.encode("euc-jp"), r.getc)
+ assert_equal(before2.encode("euc-jp"), r.getc)
+ err = assert_raise(Encoding::InvalidByteSequenceError) { r.getc }
+ assert_equal(invalid.force_encoding("ascii-8bit"), err.error_bytes)
+ assert_equal(after1.encode("euc-jp"), r.getc)
+ assert_equal(after2.encode("euc-jp"), r.getc)
+ end)
end
def test_read_all
- with_pipe("utf-8:euc-jp") {|r, w|
- str = "\u3042\u3044"
- w << str
- w.close
- assert_equal(str.encode("euc-jp"), r.read)
- }
+ str = "\u3042\u3044"
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ w << str
+ w.close
+ end,
+ proc do |r|
+ assert_equal(str.encode("euc-jp"), r.read)
+ end)
end
def test_read_all_invalid
- with_pipe("utf-8:euc-jp") {|r, w|
- before = "\u{3042}\u{3044}"
- invalid = "\x80".force_encoding("utf-8")
- after = "\u{3046}\u{3048}"
- w << before + invalid + after
- w.close
- err = assert_raise(Encoding::InvalidByteSequenceError) { r.read }
- assert_equal(invalid.force_encoding("ascii-8bit"), err.error_bytes)
- assert_equal(after.encode("euc-jp"), r.read)
- }
+ before = "\u{3042}\u{3044}"
+ invalid = "\x80".force_encoding("utf-8")
+ after = "\u{3046}\u{3048}"
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ w << before + invalid + after
+ w.close
+ end,
+ proc do |r|
+ err = assert_raise(Encoding::InvalidByteSequenceError) { r.read }
+ assert_equal(invalid.force_encoding("ascii-8bit"), err.error_bytes)
+ assert_equal(after.encode("euc-jp"), r.read)
+ end)
end
def test_file_foreach
@@ -764,84 +963,131 @@ EOT
end
def test_set_encoding
- with_pipe("utf-8:euc-jp") {|r, w|
- s = "\u3042".force_encoding("ascii-8bit")
- s << "\x82\xa0".force_encoding("ascii-8bit")
- w << s
- w.close
- assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
- r.set_encoding("shift_jis:euc-jp")
- assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
- }
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ s = "\u3042".force_encoding("ascii-8bit")
+ s << "\x82\xa0".force_encoding("ascii-8bit")
+ w << s
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
+ r.set_encoding("shift_jis:euc-jp")
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
+ end)
end
def test_set_encoding2
- with_pipe("utf-8:euc-jp") {|r, w|
- s = "\u3042".force_encoding("ascii-8bit")
- s << "\x82\xa0".force_encoding("ascii-8bit")
- w << s
- w.close
- assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
- r.set_encoding("shift_jis", "euc-jp")
- assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
- }
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ s = "\u3042".force_encoding("ascii-8bit")
+ s << "\x82\xa0".force_encoding("ascii-8bit")
+ w << s
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
+ r.set_encoding("shift_jis", "euc-jp")
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
+ end)
end
def test_set_encoding_nil
- with_pipe("utf-8:euc-jp") {|r, w|
- s = "\u3042".force_encoding("ascii-8bit")
- s << "\x82\xa0".force_encoding("ascii-8bit")
- w << s
- w.close
- assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
- r.set_encoding(nil)
- assert_equal("\x82\xa0".force_encoding(Encoding.default_external), r.read)
- }
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ s = "\u3042".force_encoding("ascii-8bit")
+ s << "\x82\xa0".force_encoding("ascii-8bit")
+ w << s
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
+ r.set_encoding(nil)
+ assert_equal("\x82\xa0".force_encoding(Encoding.default_external), r.read)
+ end)
end
def test_set_encoding_enc
- with_pipe("utf-8:euc-jp") {|r, w|
- s = "\u3042".force_encoding("ascii-8bit")
- s << "\x82\xa0".force_encoding("ascii-8bit")
- w << s
- w.close
- assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
- r.set_encoding(Encoding::Shift_JIS)
- assert_equal("\x82\xa0".force_encoding(Encoding::Shift_JIS), r.getc)
- }
+ pipe("utf-8:euc-jp",
+ proc do |w|
+ s = "\u3042".force_encoding("ascii-8bit")
+ s << "\x82\xa0".force_encoding("ascii-8bit")
+ w << s
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\xa4\xa2".force_encoding("euc-jp"), r.getc)
+ r.set_encoding(Encoding::Shift_JIS)
+ assert_equal("\x82\xa0".force_encoding(Encoding::Shift_JIS), r.getc)
+ end)
end
def test_set_encoding_invalid
- with_pipe {|r, w|
- w << "\x80"
- w.close
- r.set_encoding("utf-8:euc-jp", :invalid=>:replace)
- assert_equal("?", r.read)
- }
+ pipe(proc do |w|
+ w << "\x80"
+ w.close
+ end,
+ proc do |r|
+ r.set_encoding("utf-8:euc-jp", :invalid=>:replace)
+ assert_equal("?", r.read)
+ end)
+ end
+
+ def test_set_encoding_identical
+ #bug5568 = '[ruby-core:40727]'
+ bug6324 = '[ruby-core:44455]'
+ open(__FILE__, "r") do |f|
+ assert_warning('', bug6324) {
+ f.set_encoding("eucjp:euc-jp")
+ }
+ assert_warning('', bug6324) {
+ f.set_encoding("eucjp", "euc-jp")
+ }
+ assert_warning('', bug6324) {
+ f.set_encoding(Encoding::EUC_JP, "euc-jp")
+ }
+ assert_warning('', bug6324) {
+ f.set_encoding("eucjp", Encoding::EUC_JP)
+ }
+ assert_warning('', bug6324) {
+ f.set_encoding(Encoding::EUC_JP, Encoding::EUC_JP)
+ }
+ nonstr = Object.new
+ def nonstr.to_str; "eucjp"; end
+ assert_warning('', bug6324) {
+ f.set_encoding(nonstr, nonstr)
+ }
+ end
end
def test_set_encoding_undef
- with_pipe {|r, w|
- w << "\ufffd"
- w.close
- r.set_encoding("utf-8", "euc-jp", :undef=>:replace)
- assert_equal("?", r.read)
- }
+ pipe(proc do |w|
+ w << "\ufffd"
+ w.close
+ end,
+ proc do |r|
+ r.set_encoding("utf-8", "euc-jp", :undef=>:replace)
+ assert_equal("?", r.read)
+ end)
end
def test_set_encoding_undef_replace
- with_pipe {|r, w|
- w << "\ufffd"
- w.close
- r.set_encoding("utf-8", "euc-jp", :undef=>:replace, :replace=>"ZZZ")
- assert_equal("ZZZ", r.read)
- }
- with_pipe {|r, w|
- w << "\ufffd"
- w.close
- r.set_encoding("utf-8:euc-jp", :undef=>:replace, :replace=>"ZZZ")
- assert_equal("ZZZ", r.read)
- }
+ pipe(proc do |w|
+ w << "\ufffd"
+ w.close
+ end,
+ proc do |r|
+ r.set_encoding("utf-8", "euc-jp", :undef=>:replace, :replace=>"ZZZ")
+ assert_equal("ZZZ", r.read)
+ end)
+ pipe(proc do |w|
+ w << "\ufffd"
+ w.close
+ end,
+ proc do |r|
+ r.set_encoding("utf-8:euc-jp", :undef=>:replace, :replace=>"ZZZ")
+ assert_equal("ZZZ", r.read)
+ end)
end
def test_set_encoding_binmode
@@ -872,63 +1118,113 @@ EOT
f.set_encoding("iso-2022-jp")
}
}
+ assert_nothing_raised {
+ open(__FILE__, "r", binmode: true) {|f|
+ assert_equal(Encoding::ASCII_8BIT, f.external_encoding)
+ f.set_encoding("iso-2022-jp")
+ }
+ }
+ assert_raise(ArgumentError) {
+ open(__FILE__, "rb", binmode: true) {|f|
+ f.set_encoding("iso-2022-jp")
+ }
+ }
+ assert_raise(ArgumentError) {
+ open(__FILE__, "rb", binmode: false) {|f|
+ f.set_encoding("iso-2022-jp")
+ }
+ }
end
- def test_write_conversion_fixenc
- with_pipe {|r, w|
- w.set_encoding("iso-2022-jp:utf-8")
- t = Thread.new { r.read.force_encoding("ascii-8bit") }
- w << "\u3042"
- w << "\u3044"
- w.close
- assert_equal("\e$B$\"$$\e(B".force_encoding("ascii-8bit"), t.value)
+ def test_set_encoding_unsupported
+ bug5567 = '[ruby-core:40726]'
+ IO.pipe do |r, w|
+ assert_nothing_raised(bug5567) do
+ assert_warning(/Unsupported/, bug5567) {r.set_encoding("fffffffffffxx")}
+ assert_warning(/Unsupported/, bug5567) {r.set_encoding("fffffffffffxx", "us-ascii")}
+ assert_warning(/Unsupported/, bug5567) {r.set_encoding("us-ascii", "fffffffffffxx")}
+ end
+ end
+ end
+
+ def test_textmode_twice
+ assert_raise(ArgumentError) {
+ open(__FILE__, "rt", textmode: true) {|f|
+ f.set_encoding("iso-2022-jp")
+ }
+ }
+ assert_raise(ArgumentError) {
+ open(__FILE__, "rt", textmode: false) {|f|
+ f.set_encoding("iso-2022-jp")
+ }
}
end
+ def test_write_conversion_fixenc
+ pipe(proc do |w|
+ w.set_encoding("iso-2022-jp:utf-8")
+ w << "\u3042"
+ w << "\u3044"
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\e$B$\"$$\e(B".force_encoding("ascii-8bit"),
+ r.read.force_encoding("ascii-8bit"))
+ end)
+ end
+
def test_write_conversion_anyenc_stateful
- with_pipe {|r, w|
- w.set_encoding("iso-2022-jp")
- t = Thread.new { r.read.force_encoding("ascii-8bit") }
- w << "\u3042"
- w << "\x82\xa2".force_encoding("sjis")
- w.close
- assert_equal("\e$B$\"$$\e(B".force_encoding("ascii-8bit"), t.value)
- }
+ pipe(proc do |w|
+ w.set_encoding("iso-2022-jp")
+ w << "\u3042"
+ w << "\x82\xa2".force_encoding("sjis")
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\e$B$\"$$\e(B".force_encoding("ascii-8bit"),
+ r.read.force_encoding("ascii-8bit"))
+ end)
end
def test_write_conversion_anyenc_stateless
- with_pipe {|r, w|
- w.set_encoding("euc-jp")
- t = Thread.new { r.read.force_encoding("ascii-8bit") }
- w << "\u3042"
- w << "\x82\xa2".force_encoding("sjis")
- w.close
- assert_equal("\xa4\xa2\xa4\xa4".force_encoding("ascii-8bit"), t.value)
- }
+ pipe(proc do |w|
+ w.set_encoding("euc-jp")
+ w << "\u3042"
+ w << "\x82\xa2".force_encoding("sjis")
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\xa4\xa2\xa4\xa4".force_encoding("ascii-8bit"),
+ r.read.force_encoding("ascii-8bit"))
+ end)
end
def test_write_conversion_anyenc_stateful_nosync
- with_pipe {|r, w|
- w.sync = false
- w.set_encoding("iso-2022-jp")
- t = Thread.new { r.read.force_encoding("ascii-8bit") }
- w << "\u3042"
- w << "\x82\xa2".force_encoding("sjis")
- w.close
- assert_equal("\e$B$\"$$\e(B".force_encoding("ascii-8bit"), t.value)
- }
+ pipe(proc do |w|
+ w.sync = false
+ w.set_encoding("iso-2022-jp")
+ w << "\u3042"
+ w << "\x82\xa2".force_encoding("sjis")
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\e$B$\"$$\e(B".force_encoding("ascii-8bit"),
+ r.read.force_encoding("ascii-8bit"))
+ end)
end
def test_read_stateful
- with_pipe("euc-jp:iso-2022-jp") {|r, w|
- w << "\xA4\xA2"
- w.close
- assert_equal("\e$B$\"\e(B".force_encoding("iso-2022-jp"), r.read)
- }
+ pipe("euc-jp:iso-2022-jp",
+ proc do |w|
+ w << "\xA4\xA2"
+ w.close
+ end,
+ proc do |r|
+ assert_equal("\e$B$\"\e(B".force_encoding("iso-2022-jp"), r.read)
+ end)
end
def test_stdin_external_encoding_with_reopen
- skip "passing non-stdio fds is not supported" if /mswin|mingw/ =~ RUBY_PLATFORM
with_tmpdir {
open("tst", "w+") {|f|
pid = spawn(EnvUtil.rubybin, '-e', <<-'End', 10=>f)
@@ -944,7 +1240,7 @@ EOT
assert_equal("\u3042".force_encoding("ascii-8bit"), result)
}
}
- end
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM # passing non-stdio fds is not supported
def test_popen_r_enc
IO.popen("#{EnvUtil.rubybin} -e 'putc 255'", "r:ascii-8bit") {|f|
@@ -1026,6 +1322,16 @@ EOT
}
end
+ def test_open_pipe_r_enc2
+ open("|#{EnvUtil.rubybin} -e 'putc \"\\u3042\"'", "r:UTF-8") {|f|
+ assert_equal(Encoding::UTF_8, f.external_encoding)
+ assert_equal(nil, f.internal_encoding)
+ s = f.read
+ assert_equal(Encoding::UTF_8, s.encoding)
+ assert_equal("\u3042", s)
+ }
+ end
+
def test_s_foreach_enc
with_tmpdir {
generate_file("t", "\xff")
@@ -1147,7 +1453,12 @@ EOT
end
def test_both_textmode_binmode
- assert_raise(ArgumentError) { open("not-exist", "r", :textmode=>true, :binmode=>true) }
+ bug5918 = '[ruby-core:42199]'
+ assert_raise(ArgumentError, bug5918) { open("not-exist", "r", :textmode=>true, :binmode=>true) }
+ assert_raise(ArgumentError, bug5918) { open("not-exist", "rt", :binmode=>true) }
+ assert_raise(ArgumentError, bug5918) { open("not-exist", "rt", :binmode=>false) }
+ assert_raise(ArgumentError, bug5918) { open("not-exist", "rb", :textmode=>true) }
+ assert_raise(ArgumentError, bug5918) { open("not-exist", "rb", :textmode=>false) }
end
def test_textmode_decode_universal_newline_read
@@ -1158,6 +1469,9 @@ EOT
open("t.crlf", "rt:euc-jp:utf-8") {|f| assert_equal("a\nb\nc\n", f.read) }
open("t.crlf", "rt") {|f| assert_equal("a\nb\nc\n", f.read) }
open("t.crlf", "r", :textmode=>true) {|f| assert_equal("a\nb\nc\n", f.read) }
+ open("t.crlf", "r", textmode: true, universal_newline: false) {|f|
+ assert_equal("a\r\nb\r\nc\r\n", f.read)
+ }
generate_file("t.cr", "a\rb\rc\r")
assert_equal("a\nb\nc\n", File.read("t.cr", mode:"rt:euc-jp:utf-8"))
@@ -1294,6 +1608,44 @@ EOT
}
end
+
+ def test_binmode_decode_universal_newline
+ with_tmpdir {
+ generate_file("t.txt", "a\n")
+ assert_raise(ArgumentError) {
+ open("t.txt", "rb", newline: :universal) {}
+ }
+ }
+ end
+
+ def test_default_mode_decode_universal_newline_gets
+ with_tmpdir {
+ generate_file("t.crlf", "a\r\nb\r\nc\r\n")
+ open("t.crlf", "r", newline: :universal) {|f|
+ assert_equal("a\n", f.gets)
+ assert_equal("b\n", f.gets)
+ assert_equal("c\n", f.gets)
+ assert_equal(nil, f.gets)
+ }
+
+ generate_file("t.cr", "a\rb\rc\r")
+ open("t.cr", "r", newline: :universal) {|f|
+ assert_equal("a\n", f.gets)
+ assert_equal("b\n", f.gets)
+ assert_equal("c\n", f.gets)
+ assert_equal(nil, f.gets)
+ }
+
+ generate_file("t.lf", "a\nb\nc\n")
+ open("t.lf", "r", newline: :universal) {|f|
+ assert_equal("a\n", f.gets)
+ assert_equal("b\n", f.gets)
+ assert_equal("c\n", f.gets)
+ assert_equal(nil, f.gets)
+ }
+ }
+ end
+
def test_read_newline_conversion_with_encoding_conversion
with_tmpdir {
generate_file("t.utf8.crlf", "a\r\nb\r\n")
@@ -1398,8 +1750,7 @@ EOT
args.each {|arg| f.print arg }
}
content = File.read("t", :mode=>"rb:ascii-8bit")
- assert_equal(expected.dup.force_encoding("ascii-8bit"),
- content.force_encoding("ascii-8bit"))
+ assert_equal(expected.b, content.b)
}
end
@@ -1414,7 +1765,7 @@ EOT
u16 = "\x85\x35\0\r\x00\xa2\0\r\0\n\0\n".force_encoding("utf-16be")
i = "\e$B\x42\x22\e(B\r\e$B\x21\x71\e(B\r\n\n".force_encoding("iso-2022-jp")
n = system_newline
- un = n.encode("utf-16be").force_encoding("ascii-8bit")
+ n.encode("utf-16be").force_encoding("ascii-8bit")
assert_write("a\rb\r#{n}c#{n}", "wt", a)
assert_write("\xc2\xa2", "wt", e)
@@ -1579,7 +1930,7 @@ EOT
with_tmpdir {
src = "\u3042\r\n"
generate_file("t.txt", src)
- srcbin = src.dup.force_encoding("ascii-8bit")
+ srcbin = src.b
open("t.txt", "rt:utf-8:euc-jp") {|f|
f.binmode
result = f.read
@@ -1731,19 +2082,105 @@ EOT
def test_strip_bom
with_tmpdir {
- text = "\uFEFFa"
+ text = "\uFEFF\u0100a"
+ stripped = "\u0100a"
%w/UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE/.each do |name|
path = '%s-bom.txt' % name
content = text.encode(name)
generate_file(path, content)
result = File.read(path, mode: 'rb:BOM|UTF-8')
- assert_equal(content[1].force_encoding("ascii-8bit"),
+ assert_equal(content[1..-1].force_encoding("ascii-8bit"),
result.force_encoding("ascii-8bit"))
+ result = File.read(path, mode: 'rb:BOM|UTF-8:UTF-8')
+ assert_equal(Encoding::UTF_8, result.encoding)
+ assert_equal(stripped, result)
end
bug3407 = '[ruby-core:30641]'
- result = File.read('UTF-8-bom.txt', encoding: 'BOM|UTF-8')
- assert_equal("a", result.force_encoding("ascii-8bit"), bug3407)
+ path = 'UTF-8-bom.txt'
+ result = File.read(path, encoding: 'BOM|UTF-8')
+ assert_equal(stripped.b, result.force_encoding("ascii-8bit"), bug3407)
+
+ bug8323 = '[ruby-core:54563] [Bug #8323]'
+ expected = (stripped.b + "\xff").force_encoding("utf-8")
+ open(path, 'ab') {|f| f.write("\xff")}
+ result = File.read(path, encoding: 'BOM|UTF-8')
+ assert_not_predicate(result, :valid_encoding?, bug8323)
+ assert_equal(expected, result, bug8323)
+ result = File.read(path, encoding: 'BOM|UTF-8:UTF-8')
+ assert_not_predicate(result, :valid_encoding?, bug8323)
+ assert_equal(expected, result, bug8323)
+
+ path = 'ascii.txt'
+ generate_file(path, stripped)
+ result = File.read(path, encoding: 'BOM|UTF-8')
+ assert_equal(stripped, result, bug8323)
+ result = File.read(path, encoding: 'BOM|UTF-8:UTF-8')
+ assert_equal(stripped, result, bug8323)
+ }
+ end
+
+ def test_bom_too_long_utfname
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_warn(/Unsupported encoding/) {
+ open(IO::NULL, "r:bom|utf-" + "x" * 10000) {}
+ }
+ end;
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_warn(/Unsupported encoding/) {
+ open(IO::NULL, encoding: "bom|utf-" + "x" * 10000) {}
+ }
+ end;
+ end
+
+ def test_bom_non_utf
+ enc = nil
+
+ assert_warn(/BOM/) {
+ open(__FILE__, "r:bom|us-ascii") {|f| enc = f.external_encoding}
+ }
+ assert_equal(Encoding::US_ASCII, enc)
+
+ enc = nil
+ assert_warn(/BOM/) {
+ open(__FILE__, "r", encoding: "bom|us-ascii") {|f| enc = f.external_encoding}
+ }
+ assert_equal(Encoding::US_ASCII, enc)
+
+ enc = nil
+ assert_warn(/BOM/) {
+ open(IO::NULL, "w:bom|us-ascii") {|f| enc = f.external_encoding}
+ }
+ assert_equal(Encoding::US_ASCII, enc)
+
+ enc = nil
+ assert_warn(/BOM/) {
+ open(IO::NULL, "w", encoding: "bom|us-ascii") {|f| enc = f.external_encoding}
+ }
+ assert_equal(Encoding::US_ASCII, enc)
+
+ tlhInganHol = "\u{f8e4 f8d9 f8d7 f8dc f8d0 f8db} \u{f8d6 f8dd f8d9}"
+ assert_warn(/#{tlhInganHol}/) {
+ EnvUtil.with_default_internal(nil) {
+ open(IO::NULL, "w:bom|#{tlhInganHol}") {|f| enc = f.external_encoding}
+ }
+ }
+ assert_nil(enc)
+ end
+
+ def test_bom_non_reading
+ with_tmpdir {
+ enc = nil
+ assert_nothing_raised(IOError) {
+ open("test", "w:bom|utf-8") {|f|
+ enc = f.external_encoding
+ f.print("abc")
+ }
+ }
+ assert_equal(Encoding::UTF_8, enc)
+ assert_equal("abc", File.binread("test"))
}
end
@@ -1774,64 +2211,496 @@ EOT
open("ff", "w") {|f| }
open("ff", "rt") {|f|
f.ungetc "a"
- assert(!f.eof?, "[ruby-dev:40506] (3)")
+ assert_not_predicate(f, :eof?, "[ruby-dev:40506] (3)")
}
}
end
def test_cbuf_select
- with_pipe("US-ASCII:UTF-8", :universal_newline => true) do |r, w|
- w << "\r\n"
- r.ungetc(r.getc)
- assert_equal([[r],[],[]], IO.select([r], nil, nil, 1))
- end
+ pipe("US-ASCII:UTF-8", { :universal_newline => true },
+ proc do |w|
+ w << "\r\n"
+ end,
+ proc do |r|
+ r.ungetc(r.getc)
+ assert_equal([[r],[],[]], IO.select([r], nil, nil, 1))
+ end)
end
def test_textmode_paragraphmode
- with_pipe("US-ASCII:UTF-8", :universal_newline => true) do |r, w|
- w << "a\n\n\nc".gsub(/\n/, "\r\n")
- w.close
- assert_equal("a\n\n", r.gets(""))
- assert_equal("c", r.gets(""), "[ruby-core:23723] (18)")
- end
+ pipe("US-ASCII:UTF-8", { :universal_newline => true },
+ proc do |w|
+ w << "a\n\n\nc".gsub(/\n/, "\r\n")
+ w.close
+ end,
+ proc do |r|
+ assert_equal("a\n\n", r.gets(""))
+ assert_equal("c", r.gets(""), "[ruby-core:23723] (18)")
+ end)
end
def test_textmode_paragraph_binaryread
- with_pipe("US-ASCII:UTF-8", :universal_newline => true) do |r, w|
- w << "a\n\n\ncdefgh".gsub(/\n/, "\r\n")
- w.close
- assert_equal("a\n\n", r.gets(""))
- assert_equal("c", r.getc)
- assert_equal("defgh", r.readpartial(10))
- end
+ pipe("US-ASCII:UTF-8", { :universal_newline => true },
+ proc do |w|
+ w << "a\n\n\ncdefgh".gsub(/\n/, "\r\n")
+ w.close
+ end,
+ proc do |r|
+ assert_equal("a\n\n", r.gets(""))
+ assert_equal("c", r.getc)
+ assert_equal("defgh", r.readpartial(10))
+ end)
end
def test_textmode_paragraph_nonasciicompat
bug3534 = ['[ruby-dev:41803]', '[Bug #3534]']
- r, w = IO.pipe
- [Encoding::UTF_32BE, Encoding::UTF_32LE,
- Encoding::UTF_16BE, Encoding::UTF_16LE,
- Encoding::UTF_8].each do |e|
- r.set_encoding(Encoding::US_ASCII, e)
- w.print(bug3534[0], "\n\n\n\n", bug3534[1], "\n")
- assert_equal((bug3534[0]+"\n\n").encode(e), r.gets(""), bug3534[0])
- assert_equal((bug3534[1]+"\n").encode(e), r.gets(), bug3534[1])
- end
+ IO.pipe {|r, w|
+ [Encoding::UTF_32BE, Encoding::UTF_32LE,
+ Encoding::UTF_16BE, Encoding::UTF_16LE,
+ Encoding::UTF_8].each do |e|
+ r.set_encoding(Encoding::US_ASCII, e)
+ wthr = Thread.new{ w.print(bug3534[0], "\n\n\n\n", bug3534[1], "\n") }
+ assert_equal((bug3534[0]+"\n\n").encode(e), r.gets(""), bug3534[0])
+ assert_equal((bug3534[1]+"\n").encode(e), r.gets(), bug3534[1])
+ wthr.join
+ end
+ }
end
def test_binmode_paragraph_nonasciicompat
bug3534 = ['[ruby-dev:41803]', '[Bug #3534]']
- r, w = IO.pipe
- r.binmode
- w.binmode
- [Encoding::UTF_32BE, Encoding::UTF_32LE,
- Encoding::UTF_16BE, Encoding::UTF_16LE,
- Encoding::UTF_8].each do |e|
- r.set_encoding(Encoding::US_ASCII, e)
- w.print(bug3534[0], "\n\n\n\n", bug3534[1], "\n")
- assert_equal((bug3534[0]+"\n\n").encode(e), r.gets(""), bug3534[0])
- assert_equal((bug3534[1]+"\n").encode(e), r.gets(), bug3534[1])
+ IO.pipe {|r, w|
+ r.binmode
+ w.binmode
+ [Encoding::UTF_32BE, Encoding::UTF_32LE,
+ Encoding::UTF_16BE, Encoding::UTF_16LE,
+ Encoding::UTF_8].each do |e|
+ r.set_encoding(Encoding::US_ASCII, e)
+ wthr = Thread.new{ w.print(bug3534[0], "\n\n\n\n", bug3534[1], "\n") }
+ assert_equal((bug3534[0]+"\n\n").encode(e), r.gets(""), bug3534[0])
+ assert_equal((bug3534[1]+"\n").encode(e), r.gets(), bug3534[1])
+ wthr.join
+ end
+ }
+ end
+
+ def test_puts_widechar
+ bug = '[ruby-dev:42212]'
+ pipe(Encoding::ASCII_8BIT,
+ proc do |w|
+ w.binmode
+ w.puts(0x010a.chr(Encoding::UTF_32BE))
+ w.puts(0x010a.chr(Encoding::UTF_16BE))
+ w.puts(0x0a01.chr(Encoding::UTF_32LE))
+ w.puts(0x0a01.chr(Encoding::UTF_16LE))
+ w.close
+ end,
+ proc do |r|
+ r.binmode
+ assert_equal("\x00\x00\x01\x0a\n", r.read(5), bug)
+ assert_equal("\x01\x0a\n", r.read(3), bug)
+ assert_equal("\x01\x0a\x00\x00\n", r.read(5), bug)
+ assert_equal("\x01\x0a\n", r.read(3), bug)
+ assert_equal("", r.read, bug)
+ r.close
+ end)
+ end
+
+ def test_getc_ascii_only
+ bug4557 = '[ruby-core:35630]'
+ c = with_tmpdir {
+ open("a", "wb") {|f| f.puts "a"}
+ open("a", "rt") {|f| f.getc}
+ }
+ assert_predicate(c, :ascii_only?, bug4557)
+ end
+
+ def test_getc_conversion
+ bug8516 = '[ruby-core:55444] [Bug #8516]'
+ c = with_tmpdir {
+ open("a", "wb") {|f| f.putc "\xe1"}
+ open("a", "r:iso-8859-1:utf-8") {|f| f.getc}
+ }
+ assert_not_predicate(c, :ascii_only?, bug8516)
+ assert_equal(1, c.size, bug8516)
+ end
+
+ def test_default_mode_on_dosish
+ with_tmpdir {
+ open("a", "w") {|f| f.write "\n"}
+ assert_equal("\r\n", IO.binread("a"))
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_default_mode_on_unix
+ with_tmpdir {
+ open("a", "w") {|f| f.write "\n"}
+ assert_equal("\n", IO.binread("a"))
+ }
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_text_mode
+ with_tmpdir {
+ open("a", "wb") {|f| f.write "\r\n"}
+ assert_equal("\n", open("a", "rt"){|f| f.read})
+ }
+ end
+
+ def test_binary_mode
+ with_tmpdir {
+ open("a", "wb") {|f| f.write "\r\n"}
+ assert_equal("\r\n", open("a", "rb"){|f| f.read})
+ }
+ end
+
+ def test_default_stdout_stderr_mode
+ with_pipe do |in_r, in_w|
+ with_pipe do |out_r, out_w|
+ pid = Process.spawn({}, EnvUtil.rubybin, in: in_r, out: out_w, err: out_w)
+ in_r.close
+ out_w.close
+ in_w.write <<-EOS
+ STDOUT.puts "abc"
+ STDOUT.flush
+ STDERR.puts "def"
+ STDERR.flush
+ EOS
+ in_w.close
+ Process.wait pid
+ assert_equal "abc\r\ndef\r\n", out_r.binmode.read
+ out_r.close
+ end
+ end
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_cr_decorator_on_stdout
+ with_pipe do |in_r, in_w|
+ with_pipe do |out_r, out_w|
+ pid = Process.spawn({}, EnvUtil.rubybin, in: in_r, out: out_w)
+ in_r.close
+ out_w.close
+ in_w.write <<-EOS
+ STDOUT.set_encoding('locale', nil, newline: :cr)
+ STDOUT.puts "abc"
+ STDOUT.flush
+ EOS
+ in_w.close
+ Process.wait pid
+ assert_equal "abc\r", out_r.binmode.read
+ out_r.close
+ end
end
end
-end
+ def test_lf_decorator_on_stdout
+ with_pipe do |in_r, in_w|
+ with_pipe do |out_r, out_w|
+ pid = Process.spawn({}, EnvUtil.rubybin, in: in_r, out: out_w)
+ in_r.close
+ out_w.close
+ in_w.write <<-EOS
+ STDOUT.set_encoding('locale', nil, newline: :lf)
+ STDOUT.puts "abc"
+ STDOUT.flush
+ EOS
+ in_w.close
+ Process.wait pid
+ assert_equal "abc\n", out_r.binmode.read
+ out_r.close
+ end
+ end
+ end
+
+ def test_crlf_decorator_on_stdout
+ with_pipe do |in_r, in_w|
+ with_pipe do |out_r, out_w|
+ pid = Process.spawn({}, EnvUtil.rubybin, in: in_r, out: out_w)
+ in_r.close
+ out_w.close
+ in_w.write <<-EOS
+ STDOUT.set_encoding('locale', nil, newline: :crlf)
+ STDOUT.puts "abc"
+ STDOUT.flush
+ EOS
+ in_w.close
+ Process.wait pid
+ assert_equal "abc\r\n", out_r.binmode.read
+ out_r.close
+ end
+ end
+ end
+
+ def test_binmode_with_pipe
+ with_pipe do |r, w|
+ src = "a\r\nb\r\nc\r\n"
+ w.binmode.write src
+ w.close
+
+ assert_equal("a", r.getc)
+ assert_equal("\n", r.getc)
+ r.binmode
+ assert_equal("b", r.getc)
+ assert_equal("\r", r.getc)
+ assert_equal("\n", r.getc)
+ assert_equal("c", r.getc)
+ assert_equal("\r", r.getc)
+ assert_equal("\n", r.getc)
+ assert_equal(nil, r.getc)
+ r.close
+ end
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_stdin_binmode
+ with_pipe do |in_r, in_w|
+ with_pipe do |out_r, out_w|
+ pid = Process.spawn({}, EnvUtil.rubybin, '-e', <<-'End', in: in_r, out: out_w)
+ STDOUT.binmode
+ STDOUT.write STDIN.getc
+ STDOUT.write STDIN.getc
+ STDIN.binmode
+ STDOUT.write STDIN.getc
+ STDOUT.write STDIN.getc
+ STDOUT.write STDIN.getc
+ STDOUT.write STDIN.getc
+ STDOUT.write STDIN.getc
+ STDOUT.write STDIN.getc
+ STDOUT.write STDIN.getc
+ End
+ in_r.close
+ out_w.close
+ src = "a\r\nb\r\nc\r\n"
+ in_w.binmode.write src
+ in_w.close
+ Process.wait pid
+ assert_equal "a\nb\r\nc\r\n", out_r.binmode.read
+ out_r.close
+ end
+ end
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_with_length
+ with_tmpdir {
+ str = "a\nb"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ assert_equal(str, f.read(3))
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_with_length_binmode
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ # read with length should be binary mode
+ assert_equal("a\r\n", f.read(3)) # binary
+ assert_equal("b\nc\n\n", f.read) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_gets_and_read_with_binmode
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n\n\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ assert_equal("a\n", f.gets) # text
+ assert_equal("b\r\n", f.read(3)) # binary
+ assert_equal("c\r\n", f.read(3)) # binary
+ assert_equal("\n\n", f.read) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_getc_and_read_with_binmode
+ with_tmpdir {
+ str = "a\r\nb\r\nc\n\n\r\n\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ assert_equal("a", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ assert_equal("b\r\n", f.read(3)) # binary
+ assert_equal("c\n\n\n\n", f.read) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_with_binmode_and_gets
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n\r\n"
+ open("tmp", "wb") { |f| f.write str }
+ open("tmp", "r") do |f|
+ assert_equal("a", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ assert_equal("b\r\n", f.read(3)) # binary
+ assert_equal("c\n", f.gets) # text
+ assert_equal("\n", f.gets) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_with_binmode_and_getc
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n\r\n"
+ open("tmp", "wb") { |f| f.write str }
+ open("tmp", "r") do |f|
+ assert_equal("a", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ assert_equal("b\r\n", f.read(3)) # binary
+ assert_equal("c", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ assert_equal("\n", f.getc) # text
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_write_with_binmode
+ with_tmpdir {
+ str = "a\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r+") do |f|
+ assert_equal("a\r\n", f.read(3)) # binary
+ f.write("b\n\n"); # text
+ f.rewind
+ assert_equal("a\nb\n\n", f.read) # text
+ f.rewind
+ assert_equal("a\r\nb\r\n\r\n", f.binmode.read) # binary
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_seek_with_setting_binmode
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n\r\n\n\n\n\n\n\n\n"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ assert_equal("a\n", f.gets) # text
+ assert_equal("b\r\n", f.read(3)) # binary
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_error_nonascii
+ bug6071 = '[ruby-dev:45279]'
+ paths = ["\u{3042}".encode("sjis"), "\u{ff}".encode("iso-8859-1")]
+ encs = with_tmpdir {
+ paths.map {|path|
+ open(path) rescue $!.message.encoding
+ }
+ }
+ assert_equal(paths.map(&:encoding), encs, bug6071)
+ end
+
+ def test_inspect_nonascii
+ bug6072 = '[ruby-dev:45280]'
+ paths = ["\u{3042}".encode("sjis"), "\u{ff}".encode("iso-8859-1")]
+ encs = with_tmpdir {
+ paths.map {|path|
+ open(path, "wb") {|f| f.inspect.encoding}
+ }
+ }
+ assert_equal(paths.map(&:encoding), encs, bug6072)
+ end
+
+ def test_pos_dont_move_cursor_position
+ bug6179 = '[ruby-core:43497]'
+ with_tmpdir {
+ str = "line one\r\nline two\r\nline three\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ assert_equal("line one\n", f.readline)
+ assert_equal(10, f.pos, bug6179)
+ assert_equal("line two\n", f.readline, bug6179)
+ assert_equal(20, f.pos, bug6179)
+ assert_equal("line three\n", f.readline, bug6179)
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_pos_with_buffer_end_cr
+ bug6401 = '[ruby-core:44874]'
+ with_tmpdir {
+ # Read buffer size is 8191. This generates '\r' at 8191.
+ lines = ["X" * 8187, "X"]
+ generate_file("tmp", lines.join("\r\n") + "\r\n")
+
+ open("tmp", "r") do |f|
+ lines.each do |line|
+ f.pos
+ assert_equal(line, f.readline.chomp, bug6401)
+ end
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_crlf_and_eof
+ bug6271 = '[ruby-core:44189]'
+ with_tmpdir {
+ str = "a\r\nb\r\nc\r\n"
+ generate_file("tmp", str)
+ open("tmp", "r") do |f|
+ i = 0
+ until f.eof?
+ assert_equal(str[i], f.read(1), bug6271)
+ i += 1
+ end
+ assert_equal(str.size, i, bug6271)
+ end
+ }
+ end if /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_read_with_buf_broken_ascii_only
+ a, b = IO.pipe
+ a.binmode
+ b.binmode
+ b.write("\xE2\x9C\x93")
+ b.close
+
+ buf = "".force_encoding("binary")
+ assert buf.ascii_only?, "should have been ascii_only?"
+ a.read(1, buf)
+ assert !buf.ascii_only?, "should not have been ascii_only?"
+ ensure
+ a.close rescue nil
+ b.close rescue nil
+ end
+
+ def test_each_codepoint_need_more
+ bug11444 = '[ruby-core:70379] [Bug #11444]'
+ tests = [
+ ["incomplete multibyte", "\u{1f376}".b[0,3], [], ["invalid byte sequence in UTF-8"]],
+ ["multibyte at boundary", "x"*8190+"\u{1f376}", ["1f376"], []],
+ ]
+ failure = []
+ ["bin", "text"].product(tests) do |mode, (test, data, out, err)|
+ code = <<-"end;"
+ c = nil
+ begin
+ open(ARGV[0], "r#{mode[0]}:utf-8") do |f|
+ f.each_codepoint{|i| c = i}
+ end
+ rescue ArgumentError => e
+ STDERR.puts e.message
+ else
+ printf "%x", c
+ end
+ end;
+ Tempfile.create("codepoint") do |f|
+ args = ['-e', code, f.path]
+ f.print data
+ f.close
+ begin
+ assert_in_out_err(args, "", out, err,
+ "#{bug11444}: #{test} in #{mode} mode",
+ timeout: 10)
+ rescue Exception => e
+ failure << e
+ end
+ end
+ end
+ unless failure.empty?
+ flunk failure.join("\n---\n")
+ end
+ end
+end
diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb
new file mode 100644
index 0000000000..9de241c485
--- /dev/null
+++ b/test/ruby/test_iseq.rb
@@ -0,0 +1,539 @@
+require 'test/unit'
+require 'tempfile'
+
+return
+
+class TestISeq < Test::Unit::TestCase
+ ISeq = RubyVM::InstructionSequence
+
+ def test_no_linenum
+ bug5894 = '[ruby-dev:45130]'
+ assert_normal_exit('p RubyVM::InstructionSequence.compile("1", "mac", "", 0).to_a', bug5894)
+ end
+
+ def compile(src, line = nil, opt = nil)
+ EnvUtil.suppress_warning do
+ ISeq.new(src, __FILE__, __FILE__, line, opt)
+ end
+ end
+
+ def lines src
+ body = compile(src).to_a[13]
+ body.find_all{|e| e.kind_of? Integer}
+ end
+
+ def test_allocate
+ assert_raise(TypeError) {ISeq.allocate}
+ end
+
+ def test_to_a_lines
+ src = <<-EOS
+ p __LINE__ # 1
+ p __LINE__ # 2
+ # 3
+ p __LINE__ # 4
+ EOS
+ assert_equal [1, 2, 4], lines(src)
+
+ src = <<-EOS
+ # 1
+ p __LINE__ # 2
+ # 3
+ p __LINE__ # 4
+ # 5
+ EOS
+ assert_equal [2, 4], lines(src)
+
+ src = <<-EOS
+ 1 # should be optimized out
+ 2 # should be optimized out
+ p __LINE__ # 3
+ p __LINE__ # 4
+ 5 # should be optimized out
+ 6 # should be optimized out
+ p __LINE__ # 7
+ 8 # should be optimized out
+ 9
+ EOS
+ assert_equal [3, 4, 7, 9], lines(src)
+ end
+
+ def test_unsupport_type
+ ary = compile("p").to_a
+ ary[9] = :foobar
+ assert_raise_with_message(TypeError, /:foobar/) {ISeq.load(ary)}
+ end if defined?(RubyVM::InstructionSequence.load)
+
+ def test_loaded_cdhash_mark
+ iseq = compile(<<-'end;', __LINE__+1)
+ def bug(kw)
+ case kw
+ when "false" then false
+ when "true" then true
+ when "nil" then nil
+ else raise("unhandled argument: #{kw.inspect}")
+ end
+ end
+ end;
+ assert_separately([], <<-"end;")
+ iseq = #{iseq.to_a.inspect}
+ RubyVM::InstructionSequence.load(iseq).eval
+ assert_equal(false, bug("false"))
+ GC.start
+ assert_equal(false, bug("false"))
+ end;
+ end if defined?(RubyVM::InstructionSequence.load)
+
+ def test_disasm_encoding
+ src = "\u{3042} = 1; \u{3042}; \u{3043}"
+ asm = compile(src).disasm
+ assert_equal(src.encoding, asm.encoding)
+ assert_predicate(asm, :valid_encoding?)
+ src.encode!(Encoding::Shift_JIS)
+ asm = compile(src).disasm
+ assert_equal(src.encoding, asm.encoding)
+ assert_predicate(asm, :valid_encoding?)
+
+ obj = Object.new
+ name = "\u{2603 26a1}"
+ obj.instance_eval("def #{name}; tap {}; end")
+ assert_include(RubyVM::InstructionSequence.of(obj.method(name)).disasm, name)
+ end
+
+ LINE_BEFORE_METHOD = __LINE__
+ def method_test_line_trace
+
+ _a = 1
+
+ _b = 2
+
+ end
+
+ def test_line_trace
+ iseq = compile \
+ %q{ a = 1
+ b = 2
+ c = 3
+ # d = 4
+ e = 5
+ # f = 6
+ g = 7
+
+ }
+ 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
+
+ result = []
+ TracePoint.new(:specified_line){|tp|
+ result << tp.lineno
+ }.enable{
+ iseq.eval
+ }
+ assert_equal([2, 5], result)
+
+ iseq = ISeq.of(self.class.instance_method(:method_test_line_trace))
+ assert_equal([LINE_BEFORE_METHOD + 3, LINE_BEFORE_METHOD + 5], iseq.line_trace_all)
+ end if false # TODO: now, it is only for C APIs.
+
+ LINE_OF_HERE = __LINE__
+ def test_location
+ iseq = ISeq.of(method(:test_location))
+
+ assert_equal(__FILE__, iseq.path)
+ assert_match(/#{__FILE__}/, iseq.absolute_path)
+ assert_equal("test_location", iseq.label)
+ assert_equal("test_location", iseq.base_label)
+ assert_equal(LINE_OF_HERE+1, iseq.first_lineno)
+
+ line = __LINE__
+ iseq = ISeq.of(Proc.new{})
+ assert_equal(__FILE__, iseq.path)
+ assert_match(/#{__FILE__}/, iseq.absolute_path)
+ assert_equal("test_location", iseq.base_label)
+ assert_equal("block in test_location", iseq.label)
+ assert_equal(line+1, iseq.first_lineno)
+ end
+
+ def test_label_fstring
+ c = Class.new{ def foobar() end }
+
+ a, b = eval("# encoding: us-ascii\n'foobar'.freeze"),
+ ISeq.of(c.instance_method(:foobar)).label
+ assert_same a, b
+ end
+
+ def test_disable_opt
+ src = "a['foo'] = a['bar']; 'a'.freeze"
+ body= compile(src, __LINE__, false).to_a[13]
+ body.each{|insn|
+ next unless Array === insn
+ op = insn.first
+ assert(!op.to_s.match(/^opt_/), "#{op}")
+ }
+ end
+
+ def test_invalid_source
+ bug11159 = '[ruby-core:69219] [Bug #11159]'
+ assert_raise(TypeError, bug11159) {compile(nil)}
+ assert_raise(TypeError, bug11159) {compile(:foo)}
+ assert_raise(TypeError, bug11159) {compile(1)}
+ end
+
+ def test_frozen_string_literal_compile_option
+ $f = 'f'
+ line = __LINE__ + 2
+ code = <<-'EOS'
+ ['foo', 'foo', "#{$f}foo", "#{'foo'}"]
+ EOS
+ 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?)
+ end
+
+ # Safe call chain is not optimized when Coverage is running.
+ # So we can test it only when Coverage is not running.
+ def test_safe_call_chain
+ src = "a&.a&.a&.a&.a&.a"
+ body = compile(src, __LINE__, {peephole_optimization: true}).to_a[13]
+ labels = body.select {|op, arg| op == :branchnil}.map {|op, arg| arg}
+ assert_equal(1, labels.uniq.size)
+ end if (!defined?(Coverage) || !Coverage.running?)
+
+ def test_parent_iseq_mark
+ assert_separately([], <<-'end;', timeout: 20)
+ ->{
+ ->{
+ ->{
+ eval <<-EOS
+ class Segfault
+ define_method :segfault do
+ x = nil
+ GC.disable
+ 1000.times do |n|
+ n.times do
+ x = (foo rescue $!).local_variables
+ end
+ GC.start
+ end
+ x
+ end
+ end
+ EOS
+ }.call
+ }.call
+ }.call
+ at_exit { assert_equal([:n, :x], Segfault.new.segfault.sort) }
+ end;
+ end
+
+ def test_syntax_error_message
+ feature11951 = '[Feature #11951]'
+
+ src, line = <<-'end;', __LINE__+1
+ def x@;end
+ def y@;end
+ end;
+ e1 = e2 = nil
+ m1 = EnvUtil.verbose_warning do
+ e1 = assert_raise(SyntaxError) do
+ eval(src, nil, __FILE__, line)
+ end
+ end
+ m2 = EnvUtil.verbose_warning do
+ e2 = assert_raise(SyntaxError) do
+ ISeq.new(src, __FILE__, __FILE__, line)
+ end
+ end
+ assert_equal([m1, e1.message], [m2, e2.message], feature11951)
+ e1, e2 = e1.message.lines
+ assert_send([e1, :start_with?, __FILE__])
+ assert_send([e2, :start_with?, __FILE__])
+ end
+
+ def test_compile_file_error
+ Tempfile.create(%w"test_iseq .rb") do |f|
+ f.puts "end"
+ f.close
+ path = f.path
+ assert_in_out_err(%W[- #{path}], "#{<<-"begin;"}\n#{<<-"end;"}", /unexpected end/, [], success: true)
+ begin;
+ path = ARGV[0]
+ begin
+ RubyVM::InstructionSequence.compile_file(path)
+ rescue SyntaxError => e
+ puts e.message
+ end
+ end;
+ end
+ end
+
+ def test_translate_by_object
+ assert_separately([], <<-"end;")
+ class Object
+ def translate
+ end
+ end
+ assert_equal(0, eval("0"))
+ end;
+ end
+
+ def test_inspect
+ %W[foo \u{30d1 30b9}].each do |name|
+ assert_match /@#{name}/, ISeq.compile("", name).inspect, name
+ m = ISeq.compile("class TestISeq::Inspect; def #{name}; end; instance_method(:#{name}); end").eval
+ assert_match /:#{name}@/, ISeq.of(m).inspect, name
+ end
+ end
+
+ def strip_lineno(source)
+ source.gsub(/^.*?: /, "")
+ end
+
+ def sample_iseq
+ ISeq.compile(strip_lineno(<<-EOS))
+ 1: class C
+ 2: def foo
+ 3: begin
+ 4: rescue
+ 5: p :rescue
+ 6: ensure
+ 7: p :ensure
+ 8: end
+ 9: end
+ 10: def bar
+ 11: 1.times{
+ 12: 2.times{
+ 13: }
+ 14: }
+ 15: end
+ 16: end
+ 17: class D < C
+ 18: end
+ EOS
+ end
+
+ def test_each_child
+ iseq = sample_iseq
+
+ collect_iseq = lambda{|iseq|
+ iseqs = []
+ iseq.each_child{|child_iseq|
+ iseqs << collect_iseq.call(child_iseq)
+ }
+ ["#{iseq.label}@#{iseq.first_lineno}", *iseqs.sort_by{|k, *| k}]
+ }
+
+ expected = ["<compiled>@1",
+ ["<class:C>@1",
+ ["bar@10", ["block in bar@11",
+ ["block (2 levels) in bar@12"]]],
+ ["foo@2", ["ensure in foo@2"],
+ ["rescue in foo@4"]]],
+ ["<class:D>@17"]]
+
+ assert_equal expected, collect_iseq.call(iseq)
+ end
+
+ def test_trace_points
+ collect_iseq = lambda{|iseq|
+ iseqs = []
+ iseq.each_child{|child_iseq|
+ iseqs << collect_iseq.call(child_iseq)
+ }
+ [["#{iseq.label}@#{iseq.first_lineno}", iseq.trace_points], *iseqs.sort_by{|k, *| k}]
+ }
+ assert_equal [["<compiled>@1", [[1, :line],
+ [17, :line]]],
+ [["<class:C>@1", [[1, :class],
+ [2, :line],
+ [10, :line],
+ [16, :end]]],
+ [["bar@10", [[10, :call],
+ [11, :line],
+ [15, :return]]],
+ [["block in bar@11", [[11, :b_call],
+ [12, :line],
+ [14, :b_return]]],
+ [["block (2 levels) in bar@12", [[12, :b_call],
+ [13, :b_return]]]]]],
+ [["foo@2", [[2, :call],
+ [4, :line],
+ [7, :line],
+ [9, :return]]],
+ [["ensure in foo@2", [[7, :line]]]],
+ [["rescue in foo@4", [[5, :line]]]]]],
+ [["<class:D>@17", [[17, :class],
+ [18, :end]]]]], collect_iseq.call(sample_iseq)
+ end
+
+ def test_empty_iseq_lineno
+ iseq = ISeq.compile(<<-EOS)
+ # 1
+ # 2
+ def foo # line 3 empty method
+ end # line 4
+ 1.time do # line 5 empty block
+ end # line 6
+ class C # line 7 empty class
+ end
+ EOS
+
+ iseq.each_child{|ci|
+ ary = ci.to_a
+ type = ary[9]
+ name = ary[5]
+ line = ary[13].first
+ case ary[9]
+ when :method
+ assert_equal "foo", name
+ assert_equal 3, line
+ when :class
+ assert_equal '<class:C>', name
+ assert_equal 7, line
+ when :block
+ assert_equal 'block in <compiled>', name
+ assert_equal 5, line
+ else
+ raise "unknown ary: " + ary.inspect
+ end
+ }
+ end
+
+ def hexdump(bin)
+ bin.unpack1("H*").gsub(/.{1,32}/) {|s|
+ "#{'%04x:' % $~.begin(0)}#{s.gsub(/../, " \\&").tap{|_|_[24]&&="-"}}\n"
+ }
+ end
+
+ def assert_iseq_to_binary(code, mesg = nil)
+ iseq = RubyVM::InstructionSequence.compile(code)
+ bin = assert_nothing_raised(mesg) do
+ iseq.to_binary
+ rescue RuntimeError => e
+ skip e.message if /compile with coverage/ =~ e.message
+ raise
+ end
+ 10.times do
+ bin2 = iseq.to_binary
+ assert_equal(bin, bin2, message(mesg) {diff hexdump(bin), hexdump(bin2)})
+ end
+ iseq2 = RubyVM::InstructionSequence.load_from_binary(bin)
+ a1 = iseq.to_a
+ a2 = iseq2.to_a
+ assert_equal(a1, a2, message(mesg) {diff iseq.disassemble, iseq2.disassemble})
+ iseq2
+ end
+
+ def test_to_binary_with_objects
+ assert_iseq_to_binary("[]"+100.times.map{|i|"<</#{i}/"}.join)
+ assert_iseq_to_binary("@x ||= (1..2)")
+ end
+
+ def test_to_binary_line_info
+ assert_iseq_to_binary("#{<<~"begin;"}\n#{<<~'end;'}", '[Bug #14660]').eval
+ begin;
+ class P
+ def p; end
+ def q; end
+ E = ""
+ N = "#{E}"
+ attr_reader :i
+ end
+ end;
+ end
+
+ def collect_from_binary_tracepoint_lines(tracepoint_type, filename)
+ iseq = RubyVM::InstructionSequence.compile(strip_lineno(<<-RUBY), filename)
+ class A
+ class B
+ 2.times {
+ def self.foo
+ a = 'good day'
+ raise
+ rescue
+ 'dear reader'
+ end
+ }
+ end
+ B.foo
+ end
+ RUBY
+
+ iseq_bin = iseq.to_binary
+ lines = []
+ TracePoint.new(tracepoint_type){|tp|
+ next unless tp.path == filename
+ lines << tp.lineno
+ }.enable{
+ ISeq.load_from_binary(iseq_bin).eval
+ }
+
+ lines
+ end
+
+ def test_to_binary_line_tracepoint
+ filename = "#{File.basename(__FILE__)}_#{__LINE__}"
+ lines = collect_from_binary_tracepoint_lines(:line, filename)
+
+ assert_equal [1, 2, 3, 4, 4, 12, 5, 6, 8], lines, '[Bug #14702]'
+ end
+
+ def test_to_binary_class_tracepoint
+ filename = "#{File.basename(__FILE__)}_#{__LINE__}"
+ lines = collect_from_binary_tracepoint_lines(:class, filename)
+
+ assert_equal [1, 2], lines, '[Bug #14702]'
+ end
+
+ def test_to_binary_end_tracepoint
+ filename = "#{File.basename(__FILE__)}_#{__LINE__}"
+ lines = collect_from_binary_tracepoint_lines(:end, filename)
+
+ assert_equal [11, 13], lines, '[Bug #14702]'
+ end
+
+ def test_to_binary_return_tracepoint
+ filename = "#{File.basename(__FILE__)}_#{__LINE__}"
+ lines = collect_from_binary_tracepoint_lines(:return, filename)
+
+ assert_equal [9], lines, '[Bug #14702]'
+ end
+
+ def test_to_binary_b_call_tracepoint
+ filename = "#{File.basename(__FILE__)}_#{__LINE__}"
+ lines = collect_from_binary_tracepoint_lines(:b_call, filename)
+
+ assert_equal [3, 3], lines, '[Bug #14702]'
+ end
+
+ def test_to_binary_b_return_tracepoint
+ filename = "#{File.basename(__FILE__)}_#{__LINE__}"
+ lines = collect_from_binary_tracepoint_lines(:b_return, filename)
+
+ assert_equal [10, 10], lines, '[Bug #14702]'
+ end
+
+ def test_iseq_of
+ [proc{},
+ method(:test_iseq_of),
+ RubyVM::InstructionSequence.compile("p 1", __FILE__)].each{|src|
+ iseq = RubyVM::InstructionSequence.of(src)
+ assert_equal __FILE__, iseq.path
+ }
+ end
+
+ def test_iseq_of_twice_for_same_code
+ [proc{},
+ method(:test_iseq_of_twice_for_same_code),
+ RubyVM::InstructionSequence.compile("p 1")].each{|src|
+ iseq1 = RubyVM::InstructionSequence.of(src)
+ iseq2 = RubyVM::InstructionSequence.of(src)
+
+ # ISeq objects should be same for same src
+ assert_equal iseq1.object_id, iseq2.object_id
+ }
+ end
+end
diff --git a/test/ruby/test_iterator.rb b/test/ruby/test_iterator.rb
index 362becc0e0..ebfb60354a 100644
--- a/test/ruby/test_iterator.rb
+++ b/test/ruby/test_iterator.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class Array
@@ -25,14 +26,14 @@ class TestIterator < Test::Unit::TestCase
end
def test_array
- $x = [1, 2, 3, 4]
- $y = []
+ x = [1, 2, 3, 4]
+ y = []
# iterator over array
- for i in $x
- $y.push i
+ for i in x
+ y.push i
end
- assert_equal($x, $y)
+ assert_equal(x, y)
end
def tt
@@ -73,42 +74,52 @@ class TestIterator < Test::Unit::TestCase
def test_break
done = true
loop{
- break
+ break if true
done = false # should not reach here
}
assert(done)
done = false
- $bad = false
+ bad = false
loop {
break if done
done = true
- next
- $bad = true # should not reach here
+ next if true
+ bad = true # should not reach here
}
- assert(!$bad)
+ assert(!bad)
done = false
- $bad = false
+ bad = false
loop {
break if done
done = true
- redo
- $bad = true # should not reach here
+ redo if true
+ bad = true # should not reach here
}
- assert(!$bad)
+ assert(!bad)
- $x = []
+ x = []
for i in 1 .. 7
- $x.push i
+ x.push i
end
- assert_equal(7, $x.size)
- assert_equal([1, 2, 3, 4, 5, 6, 7], $x)
+ assert_equal(7, x.size)
+ assert_equal([1, 2, 3, 4, 5, 6, 7], x)
+ end
+
+ def test_array_for_masgn
+ a = [Struct.new(:to_ary).new([1,2])]
+ x = []
+ a.each {|i,j|x << [i,j]}
+ assert_equal([[1,2]], x)
+ x = []
+ for i,j in a; x << [i,j]; end
+ assert_equal([[1,2]], x)
end
def test_append_method_to_built_in_class
- $x = [[1,2],[3,4],[5,6]]
- assert_equal($x.iter_test1{|x|x}, $x.iter_test2{|x|x})
+ x = [[1,2],[3,4],[5,6]]
+ assert_equal(x.iter_test1{|e|e}, x.iter_test2{|e|e})
end
class IterTest
@@ -278,6 +289,9 @@ class TestIterator < Test::Unit::TestCase
def proc_call(&b)
b.call
end
+ def proc_call2(b)
+ b.call
+ end
def proc_yield()
yield
end
@@ -299,6 +313,7 @@ class TestIterator < Test::Unit::TestCase
def test_ljump
assert_raise(LocalJumpError) {get_block{break}.call}
+ assert_raise(LocalJumpError) {proc_call2(get_block{break}){}}
# cannot use assert_nothing_raised due to passing block.
begin
diff --git a/test/ruby/test_jit.rb b/test/ruby/test_jit.rb
new file mode 100644
index 0000000000..fb63d4087c
--- /dev/null
+++ b/test/ruby/test_jit.rb
@@ -0,0 +1,1047 @@
+# frozen_string_literal: true
+require 'test/unit'
+require 'tmpdir'
+require_relative '../lib/jit_support'
+
+# Test for --jit option
+class TestJIT < Test::Unit::TestCase
+ include JITSupport
+
+ IGNORABLE_PATTERNS = [
+ /\ASuccessful MJIT finish\n\z/,
+ ]
+ MAX_CACHE_PATTERNS = [
+ /\AJIT 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
+ :getblockparamproxy,
+ :defineclass,
+ :opt_call_c_function,
+
+ # joke
+ :bitblt,
+ :answer,
+
+ # TODO: write tests for them
+ :reput,
+ :tracecoverage,
+ ]
+
+ def self.untested_insns
+ @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'
+ end
+
+ # ruby -w -Itest/lib test/ruby/test_jit.rb
+ if $VERBOSE && !defined?(@@at_exit_hooked)
+ at_exit do
+ unless TestJIT.untested_insns.empty?
+ warn "untested insns are found!: #{TestJIT.untested_insns.join(' ')}"
+ end
+ end
+ @@at_exit_hooked = true
+ end
+ end
+
+ def test_compile_insn_nop
+ assert_compile_once('nil rescue true', result_inspect: 'nil', insns: %i[nop])
+ end
+
+ def test_compile_insn_local
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1', insns: %i[setlocal_WC_0 getlocal_WC_0])
+ begin;
+ foo = 1
+ foo
+ end;
+
+ insns = %i[setlocal getlocal setlocal_WC_0 getlocal_WC_0 setlocal_WC_1 getlocal_WC_1]
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", success_count: 3, stdout: '168', insns: insns)
+ begin;
+ def foo
+ a = 0
+ [1, 2].each do |i|
+ a += i
+ [3, 4].each do |j|
+ a *= j
+ end
+ end
+ a
+ end
+
+ print foo
+ end;
+ end
+
+ def test_compile_insn_blockparam
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '3', success_count: 2, insns: %i[getblockparam setblockparam])
+ begin;
+ def foo(&b)
+ a = b
+ b = 2
+ a.call + 2
+ end
+
+ print foo { 1 }
+ end;
+ end
+
+ def test_compile_insn_getblockparamproxy
+ skip "support this in mjit_compile"
+ end
+
+ def test_compile_insn_getspecial
+ assert_compile_once('$1', result_inspect: 'nil', insns: %i[getspecial])
+ end
+
+ def test_compile_insn_setspecial
+ verbose_bak, $VERBOSE = $VERBOSE, nil
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: 'true', insns: %i[setspecial])
+ begin;
+ true if nil.nil?..nil.nil?
+ end;
+ ensure
+ $VERBOSE = verbose_bak
+ end
+
+ def test_compile_insn_instancevariable
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1', insns: %i[getinstancevariable setinstancevariable])
+ begin;
+ @foo = 1
+ @foo
+ end;
+
+ # optimized getinstancevariable call
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '33', success_count: 1, min_calls: 2)
+ begin;
+ class A
+ def initialize
+ @a = 1
+ @b = 2
+ end
+
+ def three
+ @a + @b
+ end
+ end
+
+ a = A.new
+ print(a.three) # set ic
+ print(a.three) # inlined ic
+ end;
+ end
+
+ def test_compile_insn_classvariable
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '1', success_count: 1, insns: %i[getclassvariable setclassvariable])
+ begin;
+ class Foo
+ def self.foo
+ @@foo = 1
+ @@foo
+ end
+ end
+
+ print Foo.foo
+ end;
+ end
+
+ def test_compile_insn_constant
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1', insns: %i[getconstant setconstant])
+ begin;
+ FOO = 1
+ FOO
+ end;
+ end
+
+ def test_compile_insn_global
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1', insns: %i[getglobal setglobal])
+ begin;
+ $foo = 1
+ $foo
+ end;
+ end
+
+ def test_compile_insn_putnil
+ assert_compile_once('nil', result_inspect: 'nil', insns: %i[putnil])
+ end
+
+ def test_compile_insn_putself
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'hello', success_count: 1, insns: %i[putself])
+ begin;
+ proc { print "hello" }.call
+ end;
+ end
+
+ def test_compile_insn_putobject
+ assert_compile_once('0', result_inspect: '0', insns: %i[putobject_INT2FIX_0_])
+ assert_compile_once('1', result_inspect: '1', insns: %i[putobject_INT2FIX_1_])
+ assert_compile_once('2', result_inspect: '2', insns: %i[putobject])
+ end
+
+ def test_compile_insn_putspecialobject_putiseq
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'hellohello', success_count: 2, insns: %i[putspecialobject putiseq])
+ begin;
+ print 2.times.map {
+ def method_definition
+ 'hello'
+ end
+ method_definition
+ }.join
+ 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;
+ end
+
+ def test_compile_insn_toregexp
+ assert_compile_once('/#{true}/ =~ "true"', result_inspect: '0', insns: %i[toregexp])
+ end
+
+ def test_compile_insn_newarray
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '[1, 2, 3]', insns: %i[newarray])
+ begin;
+ a, b, c = 1, 2, 3
+ [a, b, c]
+ end;
+ end
+
+ def test_compile_insn_intern_duparray
+ assert_compile_once('[:"#{0}"] + [1,2,3]', result_inspect: '[:"0", 1, 2, 3]', insns: %i[intern duparray])
+ end
+
+ def test_compile_insn_expandarray
+ assert_compile_once('y = [ true, false, nil ]; x, = y; x', result_inspect: 'true', insns: %i[expandarray])
+ end
+
+ def test_compile_insn_concatarray
+ assert_compile_once('["t", "r", *x = "u", "e"].join', result_inspect: '"true"', insns: %i[concatarray])
+ end
+
+ def test_compile_insn_splatarray
+ assert_compile_once('[*(1..2)]', result_inspect: '[1, 2]', insns: %i[splatarray])
+ end
+
+ def test_compile_insn_newhash
+ assert_compile_once('a = 1; { a: a }', result_inspect: '{:a=>1}', insns: %i[newhash])
+ end
+
+ def test_compile_insn_duphash
+ assert_compile_once('{ a: 1 }', result_inspect: '{:a=>1}', insns: %i[duphash])
+ end
+
+ def test_compile_insn_newrange
+ assert_compile_once('a = 1; 0..a', result_inspect: '0..1', insns: %i[newrange])
+ end
+
+ def test_compile_insn_pop
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1', insns: %i[pop])
+ begin;
+ a = false
+ b = 1
+ a || b
+ end;
+ end
+
+ def test_compile_insn_dup
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '3', insns: %i[dup])
+ begin;
+ a = 1
+ a&.+(2)
+ end;
+ end
+
+ def test_compile_insn_dupn
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: 'true', insns: %i[dupn])
+ begin;
+ klass = Class.new
+ klass::X ||= true
+ end;
+ end
+
+ def test_compile_insn_swap_topn
+ 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
+
+ def test_compile_insn_setn
+ assert_compile_once('[nil][0] = 1', result_inspect: '1', insns: %i[setn])
+ end
+
+ def test_compile_insn_adjuststack
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: 'true', insns: %i[adjuststack])
+ begin;
+ x = [true]
+ x[0] ||= nil
+ x[0]
+ end;
+ end
+
+ def test_compile_insn_defined
+ assert_compile_once('defined?(a)', result_inspect: 'nil', insns: %i[defined])
+ end
+
+ def test_compile_insn_checkkeyword
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'true', success_count: 1, insns: %i[checkkeyword])
+ begin;
+ def test(x: rand)
+ x
+ end
+ print test(x: true)
+ end;
+ end
+
+ def test_compile_insn_tracecoverage
+ skip "write test"
+ end
+
+ def test_compile_insn_defineclass
+ skip "support this in mjit_compile (low priority)"
+ end
+
+ def test_compile_insn_send
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '1', success_count: 2, insns: %i[send])
+ begin;
+ print proc { yield_self { 1 } }.call
+ end;
+ end
+
+ def test_compile_insn_opt_str_freeze
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '"foo"', insns: %i[opt_str_freeze])
+ begin;
+ 'foo'.freeze
+ end;
+ end
+
+ def test_compile_insn_opt_str_uminus
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '"bar"', insns: %i[opt_str_uminus])
+ begin;
+ -'bar'
+ end;
+ end
+
+ def test_compile_insn_opt_newarray_max
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '2', insns: %i[opt_newarray_max])
+ begin;
+ a = 1
+ b = 2
+ [a, b].max
+ end;
+ end
+
+ def test_compile_insn_opt_newarray_min
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1', insns: %i[opt_newarray_min])
+ begin;
+ a = 1
+ b = 2
+ [a, b].min
+ end;
+ end
+
+ def test_compile_insn_opt_send_without_block
+ assert_compile_once('print', result_inspect: 'nil', insns: %i[opt_send_without_block])
+ end
+
+ def test_compile_insn_invokesuper
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '3', success_count: 4, insns: %i[invokesuper])
+ begin;
+ mod = Module.new {
+ def test
+ super + 2
+ end
+ }
+ klass = Class.new {
+ prepend mod
+ def test
+ 1
+ end
+ }
+ print klass.new.test
+ end;
+ end
+
+ def test_compile_insn_invokeblock_leave
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '2', success_count: 2, insns: %i[invokeblock leave])
+ begin;
+ def foo
+ yield
+ end
+ print foo { 2 }
+ end;
+ end
+
+ def test_compile_insn_throw
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '4', success_count: 2, insns: %i[throw])
+ begin;
+ def test
+ proc do
+ if 1+1 == 1
+ return 3
+ else
+ return 4
+ end
+ 5
+ end.call
+ end
+ print test
+ end;
+ end
+
+ def test_compile_insn_jump_branchif
+ assert_compile_once("#{<<~"begin;"}\n#{<<~'end;'}", result_inspect: 'nil', insns: %i[jump branchif])
+ begin;
+ a = false
+ 1 + 1 while a
+ end;
+ end
+
+ def test_compile_insn_branchunless
+ assert_compile_once("#{<<~"begin;"}\n#{<<~'end;'}", result_inspect: '1', insns: %i[branchunless])
+ begin;
+ a = true
+ if a
+ 1
+ else
+ 2
+ end
+ end;
+ end
+
+ def test_compile_insn_branchnil
+ assert_compile_once("#{<<~"begin;"}\n#{<<~'end;'}", result_inspect: '3', insns: %i[branchnil])
+ begin;
+ a = 2
+ a&.+(1)
+ end;
+ end
+
+ def test_compile_insn_checktype
+ assert_compile_once("#{<<~"begin;"}\n#{<<~'end;'}", result_inspect: '"42"', insns: %i[checktype])
+ begin;
+ a = '2'
+ "4#{a}"
+ end;
+ end
+
+ def test_compile_insn_inlinecache
+ assert_compile_once('Struct', result_inspect: 'Struct', insns: %i[opt_getinlinecache opt_setinlinecache])
+ end
+
+ def test_compile_insn_once
+ assert_compile_once('/#{true}/o =~ "true" && $~.to_a', result_inspect: '["true"]', insns: %i[once])
+ end
+
+ def test_compile_insn_checkmatch_opt_case_dispatch
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '"world"', insns: %i[checkmatch opt_case_dispatch])
+ begin;
+ case 'hello'
+ when 'hello'
+ 'world'
+ end
+ end;
+ end
+
+ def test_compile_insn_opt_calc
+ assert_compile_once('4 + 2 - ((2 * 3 / 2) % 2)', result_inspect: '5', insns: %i[opt_plus opt_minus opt_mult opt_div opt_mod])
+ assert_compile_once('4.0 + 2.0 - ((2.0 * 3.0 / 2.0) % 2.0)', result_inspect: '5.0', insns: %i[opt_plus opt_minus opt_mult opt_div opt_mod])
+ assert_compile_once('4 + 2', result_inspect: '6')
+ end
+
+ def test_compile_insn_opt_cmp
+ assert_compile_once('(1 == 1) && (1 != 2)', result_inspect: 'true', insns: %i[opt_eq opt_neq])
+ end
+
+ def test_compile_insn_opt_rel
+ assert_compile_once('1 < 2 && 1 <= 1 && 2 > 1 && 1 >= 1', result_inspect: 'true', insns: %i[opt_lt opt_le opt_gt opt_ge])
+ end
+
+ def test_compile_insn_opt_ltlt
+ assert_compile_once('[1] << 2', result_inspect: '[1, 2]', insns: %i[opt_ltlt])
+ end
+
+ def test_compile_insn_opt_and
+ assert_compile_once('1 & 3', result_inspect: '1', insns: %i[opt_and])
+ end
+
+ def test_compile_insn_opt_or
+ assert_compile_once('1 | 3', result_inspect: '3', insns: %i[opt_or])
+ end
+
+ def test_compile_insn_opt_aref
+ # optimized call (optimized JIT) -> send call
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '21', success_count: 2, min_calls: 1, insns: %i[opt_aref])
+ begin;
+ obj = Object.new
+ def obj.[](h)
+ h
+ end
+
+ block = proc { |h| h[1] }
+ print block.call({ 1 => 2 })
+ print block.call(obj)
+ end;
+
+ # send call -> optimized call (send JIT) -> optimized call
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '122', success_count: 1, min_calls: 2)
+ begin;
+ obj = Object.new
+ def obj.[](h)
+ h
+ end
+
+ block = proc { |h| h[1] }
+ print block.call(obj)
+ print block.call({ 1 => 2 })
+ print block.call({ 1 => 2 })
+ end;
+ end
+
+ def test_compile_insn_opt_aref_with
+ assert_compile_once("{ '1' => 2 }['1']", result_inspect: '2', insns: %i[opt_aref_with])
+ end
+
+ def test_compile_insn_opt_aset
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '5', insns: %i[opt_aset opt_aset_with])
+ begin;
+ hash = { '1' => 2 }
+ (hash['2'] = 2) + (hash[1.to_s] = 3)
+ end;
+ end
+
+ def test_compile_insn_opt_length_size
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '4', insns: %i[opt_length opt_size])
+ begin;
+ array = [1, 2]
+ array.length + array.size
+ end;
+ end
+
+ def test_compile_insn_opt_empty_p
+ assert_compile_once('[].empty?', result_inspect: 'true', insns: %i[opt_empty_p])
+ end
+
+ def test_compile_insn_opt_succ
+ assert_compile_once('1.succ', result_inspect: '2', insns: %i[opt_succ])
+ end
+
+ def test_compile_insn_opt_not
+ assert_compile_once('!!true', result_inspect: 'true', insns: %i[opt_not])
+ end
+
+ def test_compile_insn_opt_regexpmatch1
+ assert_compile_once("/true/ =~ 'true'", result_inspect: '0', insns: %i[opt_regexpmatch1])
+ end
+
+ def test_compile_insn_opt_regexpmatch2
+ 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_jit_output
+ out, err = eval_with_jit('5.times { puts "MJIT" }', verbose: 1, min_calls: 5)
+ assert_equal("MJIT\n" * 5, out)
+ assert_match(/^#{JIT_SUCCESS_PREFIX}: block in <main>@-e:1 -> .+_ruby_mjit_p\d+u\d+\.c$/, err)
+ 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
+ out, err = eval_with_jit({"TMPDIR"=>dir}, "#{<<~"begin;"}\n#{<<~'end;'}", verbose: 1, min_calls: 1, max_cache: 10)
+ begin;
+ i = 0
+ while i < 11
+ eval(<<-EOS)
+ def mjit#{i}
+ print #{i}
+ end
+ mjit#{i}
+ EOS
+ i += 1
+ end
+
+ if defined?(fork)
+ # test the child does not try to delete files which are deleted by parent,
+ # and test possible deadlock on fork during MJIT unload and JIT compaction on child
+ Process.waitpid(Process.fork {})
+ end
+ end;
+
+ 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 ->/)
+ 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)
+
+ # 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)
+ end
+
+ if appveyor_mswin?
+ # "Permission Denied" error is preventing to remove so file on AppVeyor.
+ warn 'skipped to test directory emptiness in TestJIT#test_unload_units on AppVeyor mswin'
+ else
+ # verify .o files are deleted on unload_units
+ assert_send([Dir, :empty?, dir], debug_info)
+ end
+ end
+ end
+
+ def test_local_stack_on_exception
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '3', success_count: 2)
+ begin;
+ def b
+ raise
+ rescue
+ 2
+ end
+
+ def a
+ # Calling #b should be vm_exec, not direct mjit_exec.
+ # Otherwise `1` on local variable would be purged.
+ 1 + b
+ end
+
+ print a
+ end;
+ end
+
+ def test_local_stack_with_sp_motion_by_blockargs
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '1', success_count: 2)
+ begin;
+ def b(base)
+ 1
+ end
+
+ # This method is simple enough to have false in catch_except_p.
+ # So local_stack_p would be true in JIT compiler.
+ def a
+ m = method(:b)
+
+ # ci->flag has VM_CALL_ARGS_BLOCKARG and cfp->sp is moved in vm_caller_setup_arg_block.
+ # So, for this send insn, JIT-ed code should use cfp->sp instead of local variables for stack.
+ Module.module_eval(&m)
+ end
+
+ print a
+ end;
+ end
+
+ def test_catching_deep_exception
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '1', success_count: 4)
+ begin;
+ def catch_true(paths, prefixes) # catch_except_p: TRUE
+ prefixes.each do |prefix| # catch_except_p: TRUE
+ paths.each do |path| # catch_except_p: FALSE
+ return path
+ end
+ end
+ end
+
+ def wrapper(paths, prefixes)
+ catch_true(paths, prefixes)
+ end
+
+ print wrapper(['1'], ['2'])
+ end;
+ end
+
+ def test_inlined_undefined_ivar
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "bbb", success_count: 2, min_calls: 3)
+ begin;
+ class Foo
+ def initialize
+ @a = :a
+ end
+
+ def bar
+ if @b.nil?
+ @b = :b
+ end
+ end
+ end
+
+ print(Foo.new.bar)
+ print(Foo.new.bar)
+ print(Foo.new.bar)
+ end;
+ end
+
+ def test_inlined_setivar_frozen
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "FrozenError\n", success_count: 1, min_calls: 3)
+ begin;
+ class A
+ def a
+ @a = 1
+ end
+ end
+
+ a = A.new
+ a.a
+ a.a
+ a.a
+ a.freeze
+ begin
+ a.a
+ rescue FrozenError => e
+ p e.class
+ end
+ end;
+ end
+
+ def test_attr_reader
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "4nil\nnil\n6", success_count: 2, min_calls: 2)
+ begin;
+ class A
+ attr_reader :a, :b
+
+ def initialize
+ @a = 2
+ end
+
+ def test
+ a
+ end
+
+ def undefined
+ b
+ end
+ end
+
+ a = A.new
+ print(a.test * a.test)
+ p(a.undefined)
+ p(a.undefined)
+
+ # redefinition
+ class A
+ def test
+ 3
+ end
+ end
+
+ print(2 * a.test)
+ end;
+
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "true", success_count: 1, min_calls: 2)
+ begin;
+ class Hoge
+ attr_reader :foo
+
+ def initialize
+ @foo = []
+ @bar = nil
+ end
+ end
+
+ class Fuga < Hoge
+ def initialize
+ @bar = nil
+ @foo = []
+ end
+ end
+
+ def test(recv)
+ recv.foo.empty?
+ end
+
+ hoge = Hoge.new
+ fuga = Fuga.new
+
+ test(hoge) # VM: cc set index=1
+ test(hoge) # JIT: compile with index=1
+ test(fuga) # JIT -> VM: cc set index=2
+ print test(hoge) # JIT: should use index=1, not index=2 in cc
+ 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 appveyor_mswin?
+ skip 'Removing so file is failing on AppVeyor mswin due to Permission Denied.'
+ end
+ Dir.mktmpdir("jit_test_clean_so_") do |dir|
+ code = "x = 0; 10.times {|i|x+=i}"
+ eval_with_jit({"TMPDIR"=>dir}, code)
+ assert_send([Dir, :empty?, dir])
+ eval_with_jit({"TMPDIR"=>dir}, code, save_temps: true)
+ assert_not_send([Dir, :empty?, dir])
+ end
+ end
+
+ def test_clean_objects_on_exec
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ # TODO: check call stack and close handle of code which is not on stack, and remove objects on best-effort basis
+ skip 'Removing so file being used does not work on Windows'
+ end
+ Dir.mktmpdir("jit_test_clean_objects_on_exec_") do |dir|
+ eval_with_jit({"TMPDIR"=>dir}, "#{<<~"begin;"}\n#{<<~"end;"}", min_calls: 1)
+ begin;
+ def a; end; a
+ exec "true"
+ end;
+ error_message = "Undeleted files:\n #{Dir.glob("#{dir}/*").join("\n ")}\n"
+ assert_send([Dir, :empty?, dir], error_message)
+ end
+ end
+
+ def test_lambda_longjmp
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '5', success_count: 1)
+ begin;
+ fib = lambda do |x|
+ return x if x == 0 || x == 1
+ fib.call(x-1) + fib.call(x-2)
+ end
+ print fib.call(5)
+ end;
+ end
+
+ def test_stack_pointer_with_assignment
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "nil\nnil\n", success_count: 1)
+ begin;
+ 2.times do
+ a, _ = nil
+ p a
+ end
+ end;
+ end
+
+ def test_program_counter_with_regexpmatch
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "aa", success_count: 1)
+ begin;
+ 2.times do
+ break if /a/ =~ "ab" && !$~[0]
+ print $~[0]
+ end
+ end;
+ end
+
+ def test_pushed_values_with_opt_aset_with
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "{}{}", success_count: 1)
+ begin;
+ 2.times do
+ print(Thread.current["a"] = {})
+ end
+ end;
+ end
+
+ def test_pushed_values_with_opt_aref_with
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: "nil\nnil\n", success_count: 1)
+ begin;
+ 2.times do
+ p(Thread.current["a"])
+ end
+ end;
+ end
+
+ def test_caller_locations_without_catch_table
+ out, _ = eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", min_calls: 1)
+ begin;
+ def b # 2
+ caller_locations.first # 3
+ end # 4
+ # 5
+ def a # 6
+ print # <-- don't leave PC here # 7
+ b # 8
+ end
+ puts a
+ puts a
+ end;
+ lines = out.lines
+ assert_equal("-e:8:in `a'\n", lines[0])
+ assert_equal("-e:8:in `a'\n", lines[1])
+ end
+
+ def test_fork_with_mjit_worker_thread
+ Dir.mktmpdir("jit_test_fork_with_mjit_worker_thread_") do |dir|
+ # min_calls: 2 to skip fork block
+ out, err = eval_with_jit({ "TMPDIR" => dir }, "#{<<~"begin;"}\n#{<<~"end;"}", min_calls: 2, verbose: 1)
+ begin;
+ def before_fork; end
+ def after_fork; end
+
+ before_fork; before_fork # the child should not delete this .o file
+ pid = Process.fork do # this child should not delete shared .pch file
+ after_fork; after_fork # this child does not share JIT-ed after_fork with parent
+ end
+ after_fork; after_fork # this parent does not share JIT-ed after_fork with child
+
+ Process.waitpid(pid)
+ end;
+ success_count = err.scan(/^#{JIT_SUCCESS_PREFIX}:/).size
+ debug_info = "stdout:\n```\n#{out}\n```\n\nstderr:\n```\n#{err}```\n"
+ assert_equal(3, success_count, debug_info)
+
+ # assert no remove error
+ assert_equal("Successful MJIT finish\n" * 2, err.gsub(/^#{JIT_SUCCESS_PREFIX}:[^\n]+\n/, ''), debug_info)
+
+ # ensure objects are deleted
+ assert_send([Dir, :empty?, dir], debug_info)
+ end
+ end if defined?(fork)
+
+ private
+
+ def appveyor_mswin?
+ ENV['APPVEYOR'] == 'True' && RUBY_PLATFORM.match?(/mswin/)
+ end
+
+ # The shortest way to test one proc
+ def assert_compile_once(script, result_inspect:, insns: [], uplevel: 1)
+ if script.match?(/\A\n.+\n\z/m)
+ script = script.gsub(/^/, ' ')
+ else
+ script = " #{script} "
+ end
+ assert_eval_with_jit("p proc {#{script}}.call", stdout: "#{result_inspect}\n", success_count: 1, insns: insns, uplevel: uplevel + 1)
+ end
+
+ # Shorthand for normal test cases
+ def assert_eval_with_jit(script, stdout: nil, success_count:, 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)
+ 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, max_cache: max_cache)
+ end
+
+ # Make sure that the script has insns expected to be tested
+ used_insns = method_insns(script)
+ insns.each do |insn|
+ unless used_insns.include?(insn)
+ $stderr.puts
+ warn "'#{insn}' insn is not included in the script. Actual insns are: #{used_insns.join(' ')}\n", uplevel: uplevel+2
+ end
+ TestJIT.untested_insns.delete(insn)
+ end
+
+ 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
+ )}",
+ )
+ 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 + ignorable_patterns).any? { |pat| pat.match?(l) }
+ end
+ unless err_lines.empty?
+ warn err_lines.join(''), uplevel: uplevel
+ end
+ end
+
+ # Collect block's insns or defined method's insns, which are expected to be JIT-ed.
+ # Note that this intentionally excludes insns in script's toplevel because they are not JIT-ed.
+ def method_insns(script)
+ insns = []
+ RubyVM::InstructionSequence.compile(script).to_a.last.each do |(insn, *args)|
+ case insn
+ when :putiseq, :send
+ insns += collect_insns(args.last)
+ when :defineclass
+ insns += collect_insns(args[1])
+ end
+ end
+ insns.uniq
+ end
+
+ # Recursively collect insns in iseq_array
+ def collect_insns(iseq_array)
+ return [] if iseq_array.nil?
+
+ insns = iseq_array.last.select { |x| x.is_a?(Array) }.map(&:first)
+ iseq_array.last.each do |(insn, *args)|
+ case insn
+ when :putiseq, :send
+ insns += collect_insns(args.last)
+ end
+ end
+ insns
+ end
+end
diff --git a/test/ruby/test_key_error.rb b/test/ruby/test_key_error.rb
new file mode 100644
index 0000000000..fe1d5bb5ab
--- /dev/null
+++ b/test/ruby/test_key_error.rb
@@ -0,0 +1,42 @@
+require 'test/unit'
+
+class TestKeyError < Test::Unit::TestCase
+ def test_default
+ error = KeyError.new
+ assert_equal("KeyError", error.message)
+ end
+
+ def test_message
+ error = KeyError.new("Message")
+ assert_equal("Message", error.message)
+ end
+
+ def test_receiver
+ receiver = Object.new
+ error = KeyError.new(receiver: receiver)
+ assert_equal(receiver, error.receiver)
+ error = KeyError.new
+ assert_raise(ArgumentError) {error.receiver}
+ end
+
+ def test_key
+ error = KeyError.new(key: :key)
+ assert_equal(:key, error.key)
+ error = KeyError.new
+ assert_raise(ArgumentError) {error.key}
+ end
+
+ def test_receiver_and_key
+ receiver = Object.new
+ error = KeyError.new(receiver: receiver, key: :key)
+ assert_equal([receiver, :key],
+ [error.receiver, error.key])
+ end
+
+ def test_all
+ receiver = Object.new
+ error = KeyError.new("Message", receiver: receiver, key: :key)
+ assert_equal(["Message", receiver, :key],
+ [error.message, error.receiver, error.key])
+ end
+end
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
new file mode 100644
index 0000000000..8e2da53bdf
--- /dev/null
+++ b/test/ruby/test_keyword.rb
@@ -0,0 +1,748 @@
+# frozen_string_literal: false
+require 'test/unit'
+
+class TestKeywordArguments < Test::Unit::TestCase
+ def f1(str: "foo", num: 424242)
+ [str, num]
+ end
+
+ def test_f1
+ assert_equal(["foo", 424242], f1)
+ assert_equal(["bar", 424242], f1(str: "bar"))
+ assert_equal(["foo", 111111], f1(num: 111111))
+ assert_equal(["bar", 111111], f1(str: "bar", num: 111111))
+ assert_raise(ArgumentError) { f1(str: "bar", check: true) }
+ assert_raise(ArgumentError) { f1("string") }
+ end
+
+
+ def f2(x, str: "foo", num: 424242)
+ [x, str, num]
+ end
+
+ def test_f2
+ assert_equal([:xyz, "foo", 424242], f2(:xyz))
+ assert_equal([{"bar"=>42}, "foo", 424242], f2("bar"=>42))
+ end
+
+
+ def f3(str: "foo", num: 424242, **h)
+ [str, num, h]
+ end
+
+ def test_f3
+ assert_equal(["foo", 424242, {}], f3)
+ assert_equal(["bar", 424242, {}], f3(str: "bar"))
+ assert_equal(["foo", 111111, {}], f3(num: 111111))
+ assert_equal(["bar", 111111, {}], f3(str: "bar", num: 111111))
+ assert_equal(["bar", 424242, {:check=>true}], f3(str: "bar", check: true))
+ assert_raise(ArgumentError) { f3("string") }
+ end
+
+
+ define_method(:f4) {|str: "foo", num: 424242| [str, num] }
+
+ def test_f4
+ assert_equal(["foo", 424242], f4)
+ assert_equal(["bar", 424242], f4(str: "bar"))
+ assert_equal(["foo", 111111], f4(num: 111111))
+ assert_equal(["bar", 111111], f4(str: "bar", num: 111111))
+ assert_raise(ArgumentError) { f4(str: "bar", check: true) }
+ assert_raise(ArgumentError) { f4("string") }
+ end
+
+
+ define_method(:f5) {|str: "foo", num: 424242, **h| [str, num, h] }
+
+ def test_f5
+ assert_equal(["foo", 424242, {}], f5)
+ assert_equal(["bar", 424242, {}], f5(str: "bar"))
+ assert_equal(["foo", 111111, {}], f5(num: 111111))
+ assert_equal(["bar", 111111, {}], f5(str: "bar", num: 111111))
+ assert_equal(["bar", 424242, {:check=>true}], f5(str: "bar", check: true))
+ assert_raise(ArgumentError) { f5("string") }
+ end
+
+
+ def f6(str: "foo", num: 424242, **h, &blk)
+ [str, num, h, blk]
+ end
+
+ def test_f6 # [ruby-core:40518]
+ assert_equal(["foo", 424242, {}, nil], f6)
+ assert_equal(["bar", 424242, {}, nil], f6(str: "bar"))
+ assert_equal(["foo", 111111, {}, nil], f6(num: 111111))
+ assert_equal(["bar", 111111, {}, nil], f6(str: "bar", num: 111111))
+ assert_equal(["bar", 424242, {:check=>true}, nil], f6(str: "bar", check: true))
+ a = f6 {|x| x + 42 }
+ assert_equal(["foo", 424242, {}], a[0, 3])
+ assert_equal(43, a.last.call(1))
+ end
+
+ def f7(*r, str: "foo", num: 424242, **h)
+ [r, str, num, h]
+ end
+
+ def test_f7 # [ruby-core:41772]
+ assert_equal([[], "foo", 424242, {}], f7)
+ assert_equal([[], "bar", 424242, {}], f7(str: "bar"))
+ assert_equal([[], "foo", 111111, {}], f7(num: 111111))
+ assert_equal([[], "bar", 111111, {}], f7(str: "bar", num: 111111))
+ assert_equal([[1], "foo", 424242, {}], f7(1))
+ assert_equal([[1, 2], "foo", 424242, {}], f7(1, 2))
+ assert_equal([[1, 2, 3], "foo", 424242, {}], f7(1, 2, 3))
+ assert_equal([[1], "bar", 424242, {}], f7(1, str: "bar"))
+ assert_equal([[1, 2], "bar", 424242, {}], f7(1, 2, str: "bar"))
+ assert_equal([[1, 2, 3], "bar", 424242, {}], f7(1, 2, 3, str: "bar"))
+ end
+
+ define_method(:f8) { |opt = :ion, *rest, key: :word|
+ [opt, rest, key]
+ }
+
+ def test_f8
+ assert_equal([:ion, [], :word], f8)
+ assert_equal([1, [], :word], f8(1))
+ assert_equal([1, [2], :word], f8(1, 2))
+ end
+
+ def f9(r, o=42, *args, p, k: :key, **kw, &b)
+ [r, o, args, p, k, kw, b]
+ end
+
+ def test_f9
+ assert_equal([1, 42, [], 2, :key, {}, nil], f9(1, 2))
+ assert_equal([1, 2, [], 3, :key, {}, nil], f9(1, 2, 3))
+ assert_equal([1, 2, [3], 4, :key, {}, nil], f9(1, 2, 3, 4))
+ assert_equal([1, 2, [3, 4], 5, :key, {str: "bar"}, nil], f9(1, 2, 3, 4, 5, str: "bar"))
+ end
+
+ def f10(a: 1, **)
+ a
+ end
+
+ def test_f10
+ assert_equal(42, f10(a: 42))
+ assert_equal(1, f10(b: 42))
+ end
+
+ def test_method_parameters
+ assert_equal([[:key, :str], [:key, :num]], method(:f1).parameters);
+ assert_equal([[:req, :x], [:key, :str], [:key, :num]], method(:f2).parameters);
+ assert_equal([[:key, :str], [:key, :num], [:keyrest, :h]], method(:f3).parameters);
+ assert_equal([[:key, :str], [:key, :num]], method(:f4).parameters);
+ assert_equal([[:key, :str], [:key, :num], [:keyrest, :h]], method(:f5).parameters);
+ assert_equal([[:key, :str], [:key, :num], [:keyrest, :h], [:block, :blk]], method(:f6).parameters);
+ assert_equal([[:rest, :r], [:key, :str], [:key, :num], [:keyrest, :h]], method(:f7).parameters);
+ assert_equal([[:opt, :opt], [:rest, :rest], [:key, :key]], method(:f8).parameters) # [Bug #7540] [ruby-core:50735]
+ assert_equal([[:req, :r], [:opt, :o], [:rest, :args], [:req, :p], [:key, :k],
+ [:keyrest, :kw], [:block, :b]], method(:f9).parameters)
+ end
+
+ def test_lambda
+ f = ->(str: "foo", num: 424242) { [str, num] }
+ assert_equal(["foo", 424242], f[])
+ assert_equal(["bar", 424242], f[str: "bar"])
+ assert_equal(["foo", 111111], f[num: 111111])
+ assert_equal(["bar", 111111], f[str: "bar", num: 111111])
+ end
+
+
+ def p1
+ Proc.new do |str: "foo", num: 424242|
+ [str, num]
+ end
+ end
+
+ def test_p1
+ assert_equal(["foo", 424242], p1[])
+ assert_equal(["bar", 424242], p1[str: "bar"])
+ assert_equal(["foo", 111111], p1[num: 111111])
+ assert_equal(["bar", 111111], p1[str: "bar", num: 111111])
+ assert_raise(ArgumentError) { p1[str: "bar", check: true] }
+ assert_equal(["foo", 424242], p1["string"] )
+ end
+
+
+ def p2
+ Proc.new do |x, str: "foo", num: 424242|
+ [x, str, num]
+ end
+ end
+
+ def test_p2
+ assert_equal([nil, "foo", 424242], p2[])
+ assert_equal([:xyz, "foo", 424242], p2[:xyz])
+ end
+
+
+ def p3
+ Proc.new do |str: "foo", num: 424242, **h|
+ [str, num, h]
+ end
+ end
+
+ def test_p3
+ assert_equal(["foo", 424242, {}], p3[])
+ assert_equal(["bar", 424242, {}], p3[str: "bar"])
+ assert_equal(["foo", 111111, {}], p3[num: 111111])
+ assert_equal(["bar", 111111, {}], p3[str: "bar", num: 111111])
+ assert_equal(["bar", 424242, {:check=>true}], p3[str: "bar", check: true])
+ assert_equal(["foo", 424242, {}], p3["string"])
+ end
+
+
+ def p4
+ Proc.new do |str: "foo", num: 424242, **h, &blk|
+ [str, num, h, blk]
+ end
+ end
+
+ def test_p4
+ assert_equal(["foo", 424242, {}, nil], p4[])
+ assert_equal(["bar", 424242, {}, nil], p4[str: "bar"])
+ assert_equal(["foo", 111111, {}, nil], p4[num: 111111])
+ assert_equal(["bar", 111111, {}, nil], p4[str: "bar", num: 111111])
+ assert_equal(["bar", 424242, {:check=>true}, nil], p4[str: "bar", check: true])
+ a = p4.call {|x| x + 42 }
+ assert_equal(["foo", 424242, {}], a[0, 3])
+ assert_equal(43, a.last.call(1))
+ end
+
+
+ def p5
+ Proc.new do |*r, str: "foo", num: 424242, **h|
+ [r, str, num, h]
+ end
+ end
+
+ def test_p5
+ assert_equal([[], "foo", 424242, {}], p5[])
+ assert_equal([[], "bar", 424242, {}], p5[str: "bar"])
+ assert_equal([[], "foo", 111111, {}], p5[num: 111111])
+ assert_equal([[], "bar", 111111, {}], p5[str: "bar", num: 111111])
+ assert_equal([[1], "foo", 424242, {}], p5[1])
+ assert_equal([[1, 2], "foo", 424242, {}], p5[1, 2])
+ assert_equal([[1, 2, 3], "foo", 424242, {}], p5[1, 2, 3])
+ assert_equal([[1], "bar", 424242, {}], p5[1, str: "bar"])
+ assert_equal([[1, 2], "bar", 424242, {}], p5[1, 2, str: "bar"])
+ assert_equal([[1, 2, 3], "bar", 424242, {}], p5[1, 2, 3, str: "bar"])
+ end
+
+
+ def p6
+ Proc.new do |o1, o2=42, *args, p, k: :key, **kw, &b|
+ [o1, o2, args, p, k, kw, b]
+ end
+ end
+
+ def test_p6
+ assert_equal([nil, 42, [], nil, :key, {}, nil], p6[])
+ assert_equal([1, 42, [], 2, :key, {}, nil], p6[1, 2])
+ assert_equal([1, 2, [], 3, :key, {}, nil], p6[1, 2, 3])
+ assert_equal([1, 2, [3], 4, :key, {}, nil], p6[1, 2, 3, 4])
+ assert_equal([1, 2, [3, 4], 5, :key, {str: "bar"}, nil], p6[1, 2, 3, 4, 5, str: "bar"])
+ end
+
+ def test_proc_parameters
+ assert_equal([[:key, :str], [:key, :num]], p1.parameters);
+ assert_equal([[:opt, :x], [:key, :str], [:key, :num]], p2.parameters);
+ assert_equal([[:key, :str], [:key, :num], [:keyrest, :h]], p3.parameters);
+ assert_equal([[:key, :str], [:key, :num], [:keyrest, :h], [:block, :blk]], p4.parameters);
+ assert_equal([[:rest, :r], [:key, :str], [:key, :num], [:keyrest, :h]], p5.parameters);
+ assert_equal([[:opt, :o1], [:opt, :o2], [:rest, :args], [:opt, :p], [:key, :k],
+ [:keyrest, :kw], [:block, :b]], p6.parameters)
+ end
+
+ def m1(*args)
+ yield(*args)
+ end
+
+ def test_block
+ blk = Proc.new {|str: "foo", num: 424242| [str, num] }
+ assert_equal(["foo", 424242], m1(&blk))
+ assert_equal(["bar", 424242], m1(str: "bar", &blk))
+ assert_equal(["foo", 111111], m1(num: 111111, &blk))
+ assert_equal(["bar", 111111], m1(str: "bar", num: 111111, &blk))
+ end
+
+ def rest_keyrest(*args, **opt)
+ return *args, opt
+ end
+
+ def test_rest_keyrest
+ bug7665 = '[ruby-core:51278]'
+ bug8463 = '[ruby-core:55203] [Bug #8463]'
+ expect = [*%w[foo bar], {zzz: 42}]
+ assert_equal(expect, rest_keyrest(*expect), bug7665)
+ pr = proc {|*args, **opt| next *args, opt}
+ assert_equal(expect, pr.call(*expect), bug7665)
+ assert_equal(expect, pr.call(expect), bug8463)
+ pr = proc {|a, *b, **opt| next a, *b, opt}
+ assert_equal(expect, pr.call(expect), bug8463)
+ pr = proc {|a, **opt| next a, opt}
+ assert_equal(expect.values_at(0, -1), pr.call(expect), bug8463)
+ end
+
+ def test_bare_kwrest
+ # valid syntax, but its semantics is undefined
+ assert_valid_syntax("def bug7662(**) end")
+ assert_valid_syntax("def bug7662(*, **) end")
+ assert_valid_syntax("def bug7662(a, **) end")
+ end
+
+ def test_without_paren
+ bug7942 = '[ruby-core:52820] [Bug #7942]'
+ assert_valid_syntax("def bug7942 a: 1; end")
+ assert_valid_syntax("def bug7942 a: 1, **; end")
+
+ o = Object.new
+ eval("def o.bug7942 a: 1; a; end", nil, __FILE__, __LINE__)
+ assert_equal(1, o.bug7942(), bug7942)
+ assert_equal(42, o.bug7942(a: 42), bug7942)
+
+ o = Object.new
+ eval("def o.bug7942 a: 1, **; a; end", nil, __FILE__, __LINE__)
+ assert_equal(1, o.bug7942(), bug7942)
+ assert_equal(42, o.bug7942(a: 42), bug7942)
+ end
+
+ def test_required_keyword
+ feature7701 = '[ruby-core:51454] [Feature #7701] required keyword argument'
+ o = Object.new
+ assert_nothing_raised(SyntaxError, feature7701) do
+ eval("def o.foo(a:) a; end", nil, "xyzzy")
+ eval("def o.bar(a:,**b) [a, b]; end")
+ end
+ assert_raise_with_message(ArgumentError, /missing keyword/, feature7701) {o.foo}
+ assert_raise_with_message(ArgumentError, /unknown keyword/, feature7701) {o.foo(a:0, b:1)}
+ begin
+ o.foo(a: 0, b: 1)
+ rescue => e
+ assert_equal('xyzzy', e.backtrace_locations[0].path)
+ end
+ assert_equal(42, o.foo(a: 42), feature7701)
+ assert_equal([[:keyreq, :a]], o.method(:foo).parameters, feature7701)
+
+ bug8139 = '[ruby-core:53608] [Bug #8139] required keyword argument with rest hash'
+ assert_equal([42, {}], o.bar(a: 42), feature7701)
+ assert_equal([42, {c: feature7701}], o.bar(a: 42, c: feature7701), feature7701)
+ assert_equal([[:keyreq, :a], [:keyrest, :b]], o.method(:bar).parameters, feature7701)
+ assert_raise_with_message(ArgumentError, /missing keyword/, bug8139) {o.bar(c: bug8139)}
+ assert_raise_with_message(ArgumentError, /missing keyword/, bug8139) {o.bar}
+ end
+
+ def test_required_keyword_with_newline
+ bug9669 = '[ruby-core:61658] [Bug #9669]'
+ assert_nothing_raised(SyntaxError, bug9669) do
+ eval(<<-'end;', nil, __FILE__, __LINE__)
+ def bug9669.foo a:
+ return a
+ end
+ end;
+ end
+ assert_equal(42, bug9669.foo(a: 42))
+ o = nil
+ assert_nothing_raised(SyntaxError, bug9669) do
+ eval(<<-'end;', nil, __FILE__, __LINE__)
+ o = {
+ a:
+ 1
+ }
+ end;
+ end
+ assert_equal({a: 1}, o, bug9669)
+ end
+
+ def test_required_keyword_with_reserved
+ bug10279 = '[ruby-core:65211] [Bug #10279]'
+ h = nil
+ assert_nothing_raised(SyntaxError, bug10279) do
+ break eval(<<-'end;', nil, __FILE__, __LINE__)
+ h = {a: if true then 42 end}
+ end;
+ end
+ assert_equal({a: 42}, h, bug10279)
+ end
+
+ def test_block_required_keyword
+ feature7701 = '[ruby-core:51454] [Feature #7701] required keyword argument'
+ b = assert_nothing_raised(SyntaxError, feature7701) do
+ break eval("proc {|a:| a}", nil, 'xyzzy', __LINE__)
+ end
+ assert_raise_with_message(ArgumentError, /missing keyword/, feature7701) {b.call}
+ e = assert_raise_with_message(ArgumentError, /unknown keyword/, feature7701) {b.call(a:0, b:1)}
+ assert_equal('xyzzy', e.backtrace_locations[0].path)
+
+ assert_equal(42, b.call(a: 42), feature7701)
+ assert_equal([[:keyreq, :a]], b.parameters, feature7701)
+
+ bug8139 = '[ruby-core:53608] [Bug #8139] required keyword argument with rest hash'
+ b = assert_nothing_raised(SyntaxError, feature7701) do
+ break eval("proc {|a:, **bl| [a, bl]}", nil, __FILE__, __LINE__)
+ end
+ assert_equal([42, {}], b.call(a: 42), feature7701)
+ assert_equal([42, {c: feature7701}], b.call(a: 42, c: feature7701), feature7701)
+ assert_equal([[:keyreq, :a], [:keyrest, :bl]], b.parameters, feature7701)
+ assert_raise_with_message(ArgumentError, /missing keyword/, bug8139) {b.call(c: bug8139)}
+ assert_raise_with_message(ArgumentError, /missing keyword/, bug8139) {b.call}
+
+ b = assert_nothing_raised(SyntaxError, feature7701) do
+ break eval("proc {|m, a:| [m, a]}", nil, 'xyzzy', __LINE__)
+ end
+ assert_raise_with_message(ArgumentError, /missing keyword/) {b.call}
+ assert_equal([:ok, 42], b.call(:ok, a: 42))
+ e = assert_raise_with_message(ArgumentError, /unknown keyword/) {b.call(42, a:0, b:1)}
+ assert_equal('xyzzy', e.backtrace_locations[0].path)
+ assert_equal([[:opt, :m], [:keyreq, :a]], b.parameters)
+ end
+
+ def test_super_with_keyword
+ bug8236 = '[ruby-core:54094] [Bug #8236]'
+ base = Class.new do
+ def foo(*args)
+ args
+ end
+ end
+ a = Class.new(base) do
+ def foo(arg, bar: 'x')
+ super
+ end
+ end
+ b = Class.new(base) do
+ def foo(*args, bar: 'x')
+ super
+ end
+ end
+ assert_equal([42, {:bar=>"x"}], a.new.foo(42), bug8236)
+ assert_equal([42, {:bar=>"x"}], b.new.foo(42), bug8236)
+ end
+
+ def test_zsuper_only_named_kwrest
+ bug8416 = '[ruby-core:55033] [Bug #8416]'
+ base = Class.new do
+ def foo(**h)
+ h
+ end
+ end
+ a = Class.new(base) do
+ def foo(**h)
+ super
+ end
+ end
+ assert_equal({:bar=>"x"}, a.new.foo(bar: "x"), bug8416)
+ end
+
+ def test_zsuper_only_anonymous_kwrest
+ bug8416 = '[ruby-core:55033] [Bug #8416]'
+ base = Class.new do
+ def foo(**h)
+ h
+ end
+ end
+ a = Class.new(base) do
+ def foo(**)
+ super
+ end
+ end
+ assert_equal({:bar=>"x"}, a.new.foo(bar: "x"), bug8416)
+ end
+
+ def test_precedence_of_keyword_arguments
+ bug8040 = '[ruby-core:53199] [Bug #8040]'
+ a = Class.new do
+ def foo(x, **h)
+ [x, h]
+ end
+ end
+ assert_equal([{}, {}], a.new.foo({}))
+ assert_equal([{}, {:bar=>"x"}], a.new.foo({}, bar: "x"), bug8040)
+ 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_equal([1, 2, 1, [], {:f=>5}, 2, {}], a.new.foo(1, 2, f:5), bug8993)
+ end
+
+ def test_splat_keyword_nondestructive
+ bug9776 = '[ruby-core:62161] [Bug #9776]'
+
+ h = {a: 1}
+ assert_equal({a:1, b:2}, {**h, b:2})
+ assert_equal({a:1}, h, bug9776)
+
+ pr = proc {|**opt| next opt}
+ assert_equal({a: 1}, pr.call(**h))
+ assert_equal({a: 1, b: 2}, pr.call(**h, b: 2))
+ assert_equal({a: 1}, h, bug9776)
+ end
+
+ def test_splat_hash_conversion
+ bug9898 = '[ruby-core:62921] [Bug #9898]'
+
+ o = Object.new
+ def o.to_hash() { a: 1 } end
+ assert_equal({a: 1}, m1(**o) {|x| break x}, bug9898)
+ o2 = Object.new
+ def o2.to_hash() { b: 2 } end
+ 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]'
+
+ o = Object.new
+ def o.to_hash() { k: 9 } end
+ assert_equal([1, 42, [], o, :key, {}, nil], f9(1, o))
+ assert_equal([1, 9], m1(1, o) {|a, k: 0| break [a, k]}, bug10016)
+ assert_equal([1, 9], m1(1, o, &->(a, k: 0) {break [a, k]}), bug10016)
+ end
+
+ def test_splat_hash
+ m = Object.new
+ def m.f() :ok; end
+ def m.f1(a) a; end
+ def m.f2(a = nil) a; end
+ def m.f3(**a) a; end
+ def m.f4(*a) a; end
+ o = {a: 1}
+ assert_raise_with_message(ArgumentError, /unknown keyword: a/) {
+ m.f(**o)
+ }
+ o = {}
+ assert_equal(:ok, m.f(**o), '[ruby-core:68124] [Bug #10856]')
+ a = []
+ assert_equal(:ok, m.f(*a, **o), '[ruby-core:83638] [Bug #10856]')
+
+ o = {a: 42}
+ assert_warning('', 'splat to kwrest') do
+ assert_equal({a: 42}, m.f3(**o))
+ end
+ assert_warning('', 'splat to rest') do
+ assert_equal([{a: 42}], m.f4(**o))
+ end
+
+ assert_warning('') do
+ assert_equal({a: 42}, m.f2("a".to_sym => 42), '[ruby-core:82291] [Bug #13793]')
+ end
+
+ o = {}
+ a = [:ok]
+ assert_equal(:ok, m.f2(*a, **o), '[ruby-core:83638] [Bug #10856]')
+ end
+
+ def test_gced_object_in_stack
+ bug8964 = '[ruby-dev:47729] [Bug #8964]'
+ assert_normal_exit %q{
+ def m(a: [])
+ end
+ GC.stress = true
+ tap { m }
+ GC.start
+ tap { m }
+ }, bug8964
+ assert_normal_exit %q{
+ prc = Proc.new {|a: []|}
+ GC.stress = true
+ tap { prc.call }
+ GC.start
+ tap { prc.call }
+ }, bug8964
+ end
+
+ def test_dynamic_symbol_keyword
+ bug10266 = '[ruby-dev:48564] [Bug #10266]'
+ assert_separately(['-', bug10266], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ bug = ARGV.shift
+ "hoge".to_sym
+ assert_nothing_raised(bug) {eval("def a(hoge:); end")}
+ end;
+ end
+
+ def test_unknown_keyword_with_block
+ bug10413 = '[ruby-core:65837] [Bug #10413]'
+ class << (o = Object.new)
+ def bar(k2: 'v2')
+ end
+
+ def foo
+ bar(k1: 1)
+ end
+ end
+ assert_raise_with_message(ArgumentError, /unknown keyword: k1/, bug10413) {
+ o.foo {raise "unreachable"}
+ }
+ end
+
+ def test_unknown_keyword
+ bug13004 = '[ruby-dev:49893] [Bug #13004]'
+ assert_raise_with_message(ArgumentError, /unknown keyword: invalid-argument/, bug13004) {
+ [].sample(random: nil, "invalid-argument": nil)
+ }
+ end
+
+ def test_super_with_anon_restkeywords
+ bug10659 = '[ruby-core:67157] [Bug #10659]'
+
+ foo = Class.new do
+ def foo(**h)
+ h
+ end
+ end
+
+ class << (obj = foo.new)
+ def foo(bar: "bar", **)
+ super
+ end
+ end
+
+ assert_nothing_raised(TypeError, bug10659) {
+ assert_equal({:bar => "bar"}, obj.foo, bug10659)
+ }
+ end
+
+ def m(a) yield a end
+
+ def test_nonsymbol_key
+ result = m(["a" => 10]) { |a = nil, **b| [a, b] }
+ assert_equal([{"a" => 10}, {}], result)
+ end
+
+ def method_for_test_to_hash_call_during_setup_complex_parameters k1:, k2:, **rest_kw
+ [k1, k2, rest_kw]
+ end
+
+ def test_to_hash_call_during_setup_complex_parameters
+ sym = "sym_#{Time.now}".to_sym
+ h = method_for_test_to_hash_call_during_setup_complex_parameters k1: "foo", k2: "bar", sym => "baz"
+ assert_equal ["foo", "bar", {sym => "baz"}], h, '[Bug #11027]'
+ end
+
+ class AttrSetTest
+ attr_accessor :foo
+ alias set_foo :foo=
+ end
+
+ def test_attr_set_method_cache
+ obj = AttrSetTest.new
+ h = {a: 1, b: 2}
+ 2.times{
+ obj.foo = 1
+ assert_equal(1, obj.foo)
+ obj.set_foo 2
+ assert_equal(2, obj.foo)
+ obj.set_foo(x: 1, y: 2)
+ assert_equal({x: 1, y: 2}, obj.foo)
+ obj.set_foo(x: 1, y: 2, **h)
+ assert_equal({x: 1, y: 2, **h}, obj.foo)
+ }
+ end
+
+ def test_kwrest_overwritten
+ bug13015 = '[ruby-core:78536] [Bug #13015]'
+
+ klass = EnvUtil.labeled_class("Parent") do
+ def initialize(d:)
+ end
+ end
+
+ klass = EnvUtil.labeled_class("Child", klass) do
+ def initialize(d:, **h)
+ h = [2, 3]
+ super
+ end
+ end
+
+ assert_raise_with_message(TypeError, /expected Hash/, bug13015) do
+ klass.new(d: 4)
+ end
+ end
+
+ def test_non_keyword_hash_subclass
+ bug12884 = '[ruby-core:77813] [Bug #12884]'
+ klass = EnvUtil.labeled_class("Child", Hash)
+ obj = Object.new
+ def obj.t(params = klass.new, d: nil); params; end
+ x = klass.new
+ x["foo"] = "bar"
+ result = obj.t(x)
+ assert_equal(x, result)
+ assert_kind_of(klass, result, bug12884)
+ end
+
+ def test_arity_error_message
+ obj = Object.new
+ def obj.t(x:) end
+ assert_raise_with_message(ArgumentError, /required keyword: x\)/) do
+ obj.t(42)
+ end
+ obj = Object.new
+ def obj.t(x:, y:, z: nil) end
+ assert_raise_with_message(ArgumentError, /required keywords: x, y\)/) do
+ obj.t(42)
+ end
+ end
+
+ def many_kwargs(a0: '', a1: '', a2: '', a3: '', a4: '', a5: '', a6: '', a7: '',
+ b0: '', b1: '', b2: '', b3: '', b4: '', b5: '', b6: '', b7: '',
+ c0: '', c1: '', c2: '', c3: '', c4: '', c5: '', c6: '', c7: '',
+ d0: '', d1: '', d2: '', d3: '', d4: '', d5: '', d6: '', d7: '',
+ e0: '')
+ [a0, a1, a2, a3, a4, a5, a6, a7,
+ b0, b1, b2, b3, b4, b5, b6, b7,
+ c0, c1, c2, c3, c4, c5, c6, c7,
+ d0, d1, d2, d3, d4, d5, d6, d7,
+ e0]
+ end
+
+ def test_many_kwargs
+ i = 0
+ assert_equal(:ok, many_kwargs(a0: :ok)[i], "#{i}: a0"); i+=1
+ assert_equal(:ok, many_kwargs(a1: :ok)[i], "#{i}: a1"); i+=1
+ assert_equal(:ok, many_kwargs(a2: :ok)[i], "#{i}: a2"); i+=1
+ assert_equal(:ok, many_kwargs(a3: :ok)[i], "#{i}: a3"); i+=1
+ assert_equal(:ok, many_kwargs(a4: :ok)[i], "#{i}: a4"); i+=1
+ assert_equal(:ok, many_kwargs(a5: :ok)[i], "#{i}: a5"); i+=1
+ assert_equal(:ok, many_kwargs(a6: :ok)[i], "#{i}: a6"); i+=1
+ assert_equal(:ok, many_kwargs(a7: :ok)[i], "#{i}: a7"); i+=1
+
+ assert_equal(:ok, many_kwargs(b0: :ok)[i], "#{i}: b0"); i+=1
+ assert_equal(:ok, many_kwargs(b1: :ok)[i], "#{i}: b1"); i+=1
+ assert_equal(:ok, many_kwargs(b2: :ok)[i], "#{i}: b2"); i+=1
+ assert_equal(:ok, many_kwargs(b3: :ok)[i], "#{i}: b3"); i+=1
+ assert_equal(:ok, many_kwargs(b4: :ok)[i], "#{i}: b4"); i+=1
+ assert_equal(:ok, many_kwargs(b5: :ok)[i], "#{i}: b5"); i+=1
+ assert_equal(:ok, many_kwargs(b6: :ok)[i], "#{i}: b6"); i+=1
+ assert_equal(:ok, many_kwargs(b7: :ok)[i], "#{i}: b7"); i+=1
+
+ assert_equal(:ok, many_kwargs(c0: :ok)[i], "#{i}: c0"); i+=1
+ assert_equal(:ok, many_kwargs(c1: :ok)[i], "#{i}: c1"); i+=1
+ assert_equal(:ok, many_kwargs(c2: :ok)[i], "#{i}: c2"); i+=1
+ assert_equal(:ok, many_kwargs(c3: :ok)[i], "#{i}: c3"); i+=1
+ assert_equal(:ok, many_kwargs(c4: :ok)[i], "#{i}: c4"); i+=1
+ assert_equal(:ok, many_kwargs(c5: :ok)[i], "#{i}: c5"); i+=1
+ assert_equal(:ok, many_kwargs(c6: :ok)[i], "#{i}: c6"); i+=1
+ assert_equal(:ok, many_kwargs(c7: :ok)[i], "#{i}: c7"); i+=1
+
+ assert_equal(:ok, many_kwargs(d0: :ok)[i], "#{i}: d0"); i+=1
+ assert_equal(:ok, many_kwargs(d1: :ok)[i], "#{i}: d1"); i+=1
+ assert_equal(:ok, many_kwargs(d2: :ok)[i], "#{i}: d2"); i+=1
+ assert_equal(:ok, many_kwargs(d3: :ok)[i], "#{i}: d3"); i+=1
+ assert_equal(:ok, many_kwargs(d4: :ok)[i], "#{i}: d4"); i+=1
+ assert_equal(:ok, many_kwargs(d5: :ok)[i], "#{i}: d5"); i+=1
+ assert_equal(:ok, many_kwargs(d6: :ok)[i], "#{i}: d6"); i+=1
+ assert_equal(:ok, many_kwargs(d7: :ok)[i], "#{i}: d7"); i+=1
+
+ assert_equal(:ok, many_kwargs(e0: :ok)[i], "#{i}: e0"); i+=1
+ end
+
+ def test_splat_empty_hash_with_block_passing
+ assert_valid_syntax("bug15087(**{}, &nil)")
+ end
+end
diff --git a/test/ruby/test_lambda.rb b/test/ruby/test_lambda.rb
index 241042d2c7..3ac2e4cb98 100644
--- a/test/ruby/test_lambda.rb
+++ b/test/ruby/test_lambda.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestLambdaParameters < Test::Unit::TestCase
@@ -22,13 +23,18 @@ class TestLambdaParameters < Test::Unit::TestCase
assert_raise(ArgumentError) { ->(a,b){ }.call(1,2,3) }
end
-end
-
-__END__
def test_lambda_as_iterator
a = 0
2.times(&->(_){ a += 1 })
- assert_equal(a, 2)
+ assert_equal(2, a)
+ assert_raise(ArgumentError) {1.times(&->(){ a += 1 })}
+ bug9605 = '[ruby-core:61468] [Bug #9605]'
+ assert_nothing_raised(ArgumentError, bug9605) {1.times(&->(n){ a += 1 })}
+ assert_equal(3, a, bug9605)
+ assert_nothing_raised(ArgumentError, bug9605) {
+ a = %w(Hi there how are you).each_with_index.detect(&->(w, i) {w.length == 3})
+ }
+ assert_equal(["how", 2], a, bug9605)
end
def test_call_rest_args
@@ -57,12 +63,121 @@ __END__
assert_equal(nil, ->(&b){ b }.call)
foo { puts "bogus block " }
assert_equal(1, ->(&b){ b.call }.call { 1 })
- b = nil
- assert_equal(1, ->(&b){ b.call }.call { 1 })
- assert_nil(b)
+ _b = nil
+ assert_equal(1, ->(&_b){ _b.call }.call { 1 })
+ assert_nil(_b)
+ end
+
+ def test_call_block_from_lambda
+ bug9605 = '[ruby-core:61470] [Bug #9605]'
+ plus = ->(x,y) {x+y}
+ assert_raise(ArgumentError, bug9605) {proc(&plus).call [1,2]}
+ end
+
+ def test_instance_exec
+ bug12568 = '[ruby-core:76300] [Bug #12568]'
+ assert_nothing_raised(ArgumentError, bug12568) do
+ instance_exec([1,2,3], &->(a=[]){ a })
+ end
+ end
+
+ def test_instance_eval_return
+ bug13090 = '[ruby-core:78917] [Bug #13090] cannot return in lambdas'
+ x = :ng
+ assert_nothing_raised(LocalJumpError) do
+ x = instance_eval(&->(_){return :ok})
+ end
+ ensure
+ assert_equal(:ok, x, bug13090)
+ end
+
+ def test_instance_exec_return
+ bug13090 = '[ruby-core:78917] [Bug #13090] cannot return in lambdas'
+ x = :ng
+ assert_nothing_raised(LocalJumpError) do
+ x = instance_exec(&->(){return :ok})
+ end
+ ensure
+ assert_equal(:ok, x, bug13090)
+ end
+
+ def test_arity_error
+ assert_raise(ArgumentError, '[Bug #12705]') do
+ [1, 2].tap(&lambda {|a, b|})
+ end
end
def foo
assert_equal(nil, ->(&b){ b }.call)
end
+
+ def test_in_basic_object
+ bug5966 = '[ruby-core:42349]'
+ called = false
+ BasicObject.new.instance_eval {->() {called = true}.()}
+ assert_equal(true, called, bug5966)
+ end
+
+ def test_location_on_error
+ bug6151 = '[ruby-core:43314]'
+ called = 0
+ line, f = __LINE__, lambda do
+ called += 1
+ true
+ end
+ e = assert_raise(ArgumentError) do
+ f.call(42)
+ end
+ assert_send([e.backtrace.first, :start_with?, "#{__FILE__}:#{line}:"], bug6151)
+ assert_equal(0, called)
+ e = assert_raise(ArgumentError) do
+ 42.times(&f)
+ end
+ assert_send([e.backtrace.first, :start_with?, "#{__FILE__}:#{line}:"], bug6151)
+ assert_equal(0, called)
+ end
+
+ def return_in_current(val)
+ 1.tap(&->(*) {return 0})
+ val
+ end
+
+ def yield_block
+ yield
+ end
+
+ def return_in_callee(val)
+ yield_block(&->(*) {return 0})
+ val
+ end
+
+ def test_return
+ feature8693 = '[ruby-core:56193] [Feature #8693]'
+ assert_equal(42, return_in_current(42), feature8693)
+ assert_equal(42, return_in_callee(42), feature8693)
+ end
+
+ def test_do_lambda_source_location
+ exp_lineno = __LINE__ + 3
+ lmd = ->(x,
+ y,
+ z) do
+ #
+ end
+ file, lineno = lmd.source_location
+ assert_match(/^#{ Regexp.quote(__FILE__) }$/, file)
+ assert_equal(exp_lineno, lineno, "must be at the beginning of the block")
+ end
+
+ def test_brace_lambda_source_location
+ exp_lineno = __LINE__ + 3
+ lmd = ->(x,
+ y,
+ z) {
+ #
+ }
+ file, lineno = lmd.source_location
+ assert_match(/^#{ Regexp.quote(__FILE__) }$/, file)
+ assert_equal(exp_lineno, lineno, "must be at the beginning of the block")
+ end
end
diff --git a/test/ruby/test_lazy_enumerator.rb b/test/ruby/test_lazy_enumerator.rb
new file mode 100644
index 0000000000..03371c912a
--- /dev/null
+++ b/test/ruby/test_lazy_enumerator.rb
@@ -0,0 +1,581 @@
+# frozen_string_literal: false
+require 'test/unit'
+
+class TestLazyEnumerator < Test::Unit::TestCase
+ class Step
+ include Enumerable
+ attr_reader :current, :args
+
+ def initialize(enum)
+ @enum = enum
+ @current = nil
+ @args = nil
+ end
+
+ def each(*args)
+ @args = args
+ @enum.each do |v|
+ @current = v
+ if v.is_a? Enumerable
+ yield *v
+ else
+ yield v
+ end
+ end
+ end
+ end
+
+ def test_initialize
+ assert_equal([1, 2, 3], [1, 2, 3].lazy.to_a)
+ assert_equal([1, 2, 3], Enumerator::Lazy.new([1, 2, 3]){|y, v| y << v}.to_a)
+ assert_raise(ArgumentError) { Enumerator::Lazy.new([1, 2, 3]) }
+
+ a = [1, 2, 3].lazy
+ a.freeze
+ assert_raise(FrozenError) {
+ a.__send__ :initialize, [4, 5], &->(y, *v) { y << yield(*v) }
+ }
+ end
+
+ def test_each_args
+ a = Step.new(1..3)
+ assert_equal(1, a.lazy.each(4).first)
+ assert_equal([4], a.args)
+ end
+
+ def test_each_line
+ name = lineno = nil
+ File.open(__FILE__) do |f|
+ f.each("").map do |paragraph|
+ paragraph[/\A\s*(.*)/, 1]
+ end.find do |line|
+ if name = line[/^class\s+(\S+)/, 1]
+ lineno = f.lineno
+ true
+ end
+ end
+ end
+ assert_equal(self.class.name, name)
+ assert_operator(lineno, :>, 2)
+
+ name = lineno = nil
+ File.open(__FILE__) do |f|
+ f.lazy.each("").map do |paragraph|
+ paragraph[/\A\s*(.*)/, 1]
+ end.find do |line|
+ if name = line[/^class\s+(\S+)/, 1]
+ lineno = f.lineno
+ true
+ end
+ end
+ end
+ assert_equal(self.class.name, name)
+ assert_equal(2, lineno)
+ end
+
+ def test_select
+ a = Step.new(1..6)
+ assert_equal(4, a.select {|x| x > 3}.first)
+ assert_equal(6, a.current)
+ assert_equal(4, a.lazy.select {|x| x > 3}.first)
+ assert_equal(4, a.current)
+
+ a = Step.new(['word', nil, 1])
+ assert_raise(TypeError) {a.select {|x| "x"+x}.first}
+ assert_equal(nil, a.current)
+ assert_equal("word", a.lazy.select {|x| "x"+x}.first)
+ assert_equal("word", a.current)
+ end
+
+ def test_select_multiple_values
+ e = Enumerator.new { |yielder|
+ for i in 1..5
+ yielder.yield(i, i.to_s)
+ end
+ }
+ assert_equal([[2, "2"], [4, "4"]],
+ e.select {|x| x[0] % 2 == 0})
+ assert_equal([[2, "2"], [4, "4"]],
+ e.lazy.select {|x| x[0] % 2 == 0}.force)
+ end
+
+ def test_map
+ a = Step.new(1..3)
+ assert_equal(2, a.map {|x| x * 2}.first)
+ assert_equal(3, a.current)
+ assert_equal(2, a.lazy.map {|x| x * 2}.first)
+ assert_equal(1, a.current)
+ end
+
+ def test_map_packed_nested
+ bug = '[ruby-core:81638] [Bug#13648]'
+
+ a = Step.new([[1, 2]])
+ expected = [[[1, 2]]]
+ assert_equal(expected, a.map {|*args| args}.map {|*args| args}.to_a)
+ assert_equal(expected, a.lazy.map {|*args| args}.map {|*args| args}.to_a, bug)
+ end
+
+ def test_flat_map
+ a = Step.new(1..3)
+ assert_equal(2, a.flat_map {|x| [x * 2]}.first)
+ assert_equal(3, a.current)
+ assert_equal(2, a.lazy.flat_map {|x| [x * 2]}.first)
+ assert_equal(1, a.current)
+ end
+
+ def test_flat_map_nested
+ a = Step.new(1..3)
+ assert_equal([1, "a"],
+ a.flat_map {|x| ("a".."c").map {|y| [x, y]}}.first)
+ assert_equal(3, a.current)
+ assert_equal([1, "a"],
+ a.lazy.flat_map {|x| ("a".."c").lazy.map {|y| [x, y]}}.first)
+ assert_equal(1, a.current)
+ end
+
+ def test_flat_map_to_ary
+ to_ary = Class.new {
+ def initialize(value)
+ @value = value
+ end
+
+ def to_ary
+ [:to_ary, @value]
+ end
+ }
+ assert_equal([:to_ary, 1, :to_ary, 2, :to_ary, 3],
+ [1, 2, 3].flat_map {|x| to_ary.new(x)})
+ assert_equal([:to_ary, 1, :to_ary, 2, :to_ary, 3],
+ [1, 2, 3].lazy.flat_map {|x| to_ary.new(x)}.force)
+ end
+
+ def test_flat_map_non_array
+ assert_equal(["1", "2", "3"], [1, 2, 3].flat_map {|x| x.to_s})
+ assert_equal(["1", "2", "3"], [1, 2, 3].lazy.flat_map {|x| x.to_s}.force)
+ end
+
+ def test_flat_map_hash
+ assert_equal([{?a=>97}, {?b=>98}, {?c=>99}], [?a, ?b, ?c].flat_map {|x| {x=>x.ord}})
+ assert_equal([{?a=>97}, {?b=>98}, {?c=>99}], [?a, ?b, ?c].lazy.flat_map {|x| {x=>x.ord}}.force)
+ end
+
+ def test_reject
+ a = Step.new(1..6)
+ assert_equal(4, a.reject {|x| x < 4}.first)
+ assert_equal(6, a.current)
+ assert_equal(4, a.lazy.reject {|x| x < 4}.first)
+ assert_equal(4, a.current)
+
+ a = Step.new(['word', nil, 1])
+ assert_equal(nil, a.reject {|x| x}.first)
+ assert_equal(1, a.current)
+ assert_equal(nil, a.lazy.reject {|x| x}.first)
+ assert_equal(nil, a.current)
+ end
+
+ def test_reject_multiple_values
+ e = Enumerator.new { |yielder|
+ for i in 1..5
+ yielder.yield(i, i.to_s)
+ end
+ }
+ assert_equal([[2, "2"], [4, "4"]],
+ e.reject {|x| x[0] % 2 != 0})
+ assert_equal([[2, "2"], [4, "4"]],
+ e.lazy.reject {|x| x[0] % 2 != 0}.force)
+ end
+
+ def test_grep
+ a = Step.new('a'..'f')
+ assert_equal('c', a.grep(/c/).first)
+ assert_equal('f', a.current)
+ assert_equal('c', a.lazy.grep(/c/).first)
+ assert_equal('c', a.current)
+ assert_equal(%w[a e], a.grep(proc {|x| /[aeiou]/ =~ x}))
+ assert_equal(%w[a e], a.lazy.grep(proc {|x| /[aeiou]/ =~ x}).to_a)
+ end
+
+ def test_grep_with_block
+ a = Step.new('a'..'f')
+ assert_equal('C', a.grep(/c/) {|i| i.upcase}.first)
+ assert_equal('C', a.lazy.grep(/c/) {|i| i.upcase}.first)
+ end
+
+ def test_grep_multiple_values
+ e = Enumerator.new { |yielder|
+ 3.times { |i|
+ yielder.yield(i, i.to_s)
+ }
+ }
+ assert_equal([[2, "2"]], e.grep(proc {|x| x == [2, "2"]}))
+ assert_equal([[2, "2"]], e.lazy.grep(proc {|x| x == [2, "2"]}).force)
+ assert_equal(["22"],
+ e.lazy.grep(proc {|x| x == [2, "2"]}, &:join).force)
+ end
+
+ def test_grep_v
+ a = Step.new('a'..'f')
+ assert_equal('b', a.grep_v(/a/).first)
+ assert_equal('f', a.current)
+ assert_equal('a', a.lazy.grep_v(/c/).first)
+ assert_equal('a', a.current)
+ assert_equal(%w[b c d f], a.grep_v(proc {|x| /[aeiou]/ =~ x}))
+ assert_equal(%w[b c d f], a.lazy.grep_v(proc {|x| /[aeiou]/ =~ x}).to_a)
+ end
+
+ def test_grep_v_with_block
+ a = Step.new('a'..'f')
+ assert_equal('B', a.grep_v(/a/) {|i| i.upcase}.first)
+ assert_equal('B', a.lazy.grep_v(/a/) {|i| i.upcase}.first)
+ end
+
+ def test_grep_v_multiple_values
+ e = Enumerator.new { |yielder|
+ 3.times { |i|
+ yielder.yield(i, i.to_s)
+ }
+ }
+ assert_equal([[0, "0"], [1, "1"]], e.grep_v(proc {|x| x == [2, "2"]}))
+ assert_equal([[0, "0"], [1, "1"]], e.lazy.grep_v(proc {|x| x == [2, "2"]}).force)
+ assert_equal(["00", "11"],
+ e.lazy.grep_v(proc {|x| x == [2, "2"]}, &:join).force)
+ end
+
+ def test_zip
+ a = Step.new(1..3)
+ assert_equal([1, "a"], a.zip("a".."c").first)
+ assert_equal(3, a.current)
+ assert_equal([1, "a"], a.lazy.zip("a".."c").first)
+ assert_equal(1, a.current)
+ end
+
+ def test_zip_short_arg
+ a = Step.new(1..5)
+ assert_equal([5, nil], a.zip("a".."c").last)
+ assert_equal([5, nil], a.lazy.zip("a".."c").force.last)
+ end
+
+ def test_zip_without_arg
+ a = Step.new(1..3)
+ assert_equal([1], a.zip.first)
+ assert_equal(3, a.current)
+ assert_equal([1], a.lazy.zip.first)
+ assert_equal(1, a.current)
+ end
+
+ def test_zip_bad_arg
+ a = Step.new(1..3)
+ assert_raise(TypeError){ a.lazy.zip(42) }
+ end
+
+ def test_zip_with_block
+ # zip should be eager when a block is given
+ a = Step.new(1..3)
+ ary = []
+ assert_equal(nil, a.lazy.zip("a".."c") {|x, y| ary << [x, y]})
+ assert_equal(a.zip("a".."c"), ary)
+ assert_equal(3, a.current)
+ end
+
+ def test_take
+ a = Step.new(1..10)
+ assert_equal(1, a.take(5).first)
+ assert_equal(5, a.current)
+ assert_equal(1, a.lazy.take(5).first)
+ assert_equal(1, a.current)
+ assert_equal((1..5).to_a, a.lazy.take(5).force)
+ assert_equal(5, a.current)
+ a = Step.new(1..10)
+ assert_equal([], a.lazy.take(0).force)
+ assert_equal(nil, a.current)
+ end
+
+ def test_take_bad_arg
+ a = Step.new(1..10)
+ assert_raise(ArgumentError) { a.lazy.take(-1) }
+ end
+
+ def test_take_recycle
+ bug6428 = '[ruby-dev:45634]'
+ a = Step.new(1..10)
+ take5 = a.lazy.take(5)
+ assert_equal((1..5).to_a, take5.force, bug6428)
+ assert_equal((1..5).to_a, take5.force, bug6428)
+ end
+
+ def test_take_nested
+ bug7696 = '[ruby-core:51470]'
+ a = Step.new(1..10)
+ take5 = a.lazy.take(5)
+ assert_equal([*(1..5)]*5, take5.flat_map{take5}.force, bug7696)
+ end
+
+ def test_drop_while_nested
+ bug7696 = '[ruby-core:51470]'
+ a = Step.new(1..10)
+ drop5 = a.lazy.drop_while{|x| x < 6}
+ assert_equal([*(6..10)]*5, drop5.flat_map{drop5}.force, bug7696)
+ end
+
+ def test_drop_nested
+ bug7696 = '[ruby-core:51470]'
+ a = Step.new(1..10)
+ drop5 = a.lazy.drop(5)
+ assert_equal([*(6..10)]*5, drop5.flat_map{drop5}.force, bug7696)
+ end
+
+ def test_zip_nested
+ bug7696 = '[ruby-core:51470]'
+ enum = ('a'..'z').each
+ enum.next
+ zip = (1..3).lazy.zip(enum, enum)
+ assert_equal([[1, 'a', 'a'], [2, 'b', 'b'], [3, 'c', 'c']]*3, zip.flat_map{zip}.force, bug7696)
+ end
+
+ def test_zip_lazy_on_args
+ zip = Step.new(1..2).lazy.zip(42..Float::INFINITY)
+ assert_equal [[1, 42], [2, 43]], zip.force
+ end
+
+ def test_zip_efficient_on_array_args
+ ary = [42, :foo]
+ %i[to_enum enum_for lazy each].each do |forbid|
+ ary.define_singleton_method(forbid){ fail "#{forbid} was called"}
+ end
+ zip = Step.new(1..2).lazy.zip(ary)
+ assert_equal [[1, 42], [2, :foo]], zip.force
+ end
+
+ def test_zip_nonsingle
+ bug8735 = '[ruby-core:56383] [Bug #8735]'
+
+ obj = Object.new
+ def obj.each
+ yield
+ yield 1, 2
+ end
+
+ assert_equal(obj.to_enum.zip(obj.to_enum), obj.to_enum.lazy.zip(obj.to_enum).force, bug8735)
+ end
+
+ def test_take_rewound
+ bug7696 = '[ruby-core:51470]'
+ e=(1..42).lazy.take(2)
+ assert_equal 1, e.next, bug7696
+ assert_equal 2, e.next, bug7696
+ e.rewind
+ assert_equal 1, e.next, bug7696
+ assert_equal 2, e.next, bug7696
+ end
+
+ def test_take_while
+ a = Step.new(1..10)
+ assert_equal(1, a.take_while {|i| i < 5}.first)
+ assert_equal(5, a.current)
+ assert_equal(1, a.lazy.take_while {|i| i < 5}.first)
+ assert_equal(1, a.current)
+ assert_equal((1..4).to_a, a.lazy.take_while {|i| i < 5}.to_a)
+ end
+
+ def test_drop
+ a = Step.new(1..10)
+ assert_equal(6, a.drop(5).first)
+ assert_equal(10, a.current)
+ assert_equal(6, a.lazy.drop(5).first)
+ assert_equal(6, a.current)
+ assert_equal((6..10).to_a, a.lazy.drop(5).to_a)
+ end
+
+ def test_drop_while
+ a = Step.new(1..10)
+ assert_equal(5, a.drop_while {|i| i % 5 > 0}.first)
+ assert_equal(10, a.current)
+ assert_equal(5, a.lazy.drop_while {|i| i % 5 > 0}.first)
+ assert_equal(5, a.current)
+ assert_equal((5..10).to_a, a.lazy.drop_while {|i| i % 5 > 0}.to_a)
+ end
+
+ def test_drop_and_take
+ assert_equal([4, 5], (1..Float::INFINITY).lazy.drop(3).take(2).to_a)
+ end
+
+ def test_cycle
+ a = Step.new(1..3)
+ assert_equal("1", a.cycle(2).map(&:to_s).first)
+ assert_equal(3, a.current)
+ assert_equal("1", a.lazy.cycle(2).map(&:to_s).first)
+ assert_equal(1, a.current)
+ end
+
+ def test_cycle_with_block
+ # cycle should be eager when a block is given
+ a = Step.new(1..3)
+ ary = []
+ assert_equal(nil, a.lazy.cycle(2) {|i| ary << i})
+ assert_equal(a.cycle(2).to_a, ary)
+ assert_equal(3, a.current)
+ end
+
+ def test_cycle_chain
+ a = 1..3
+ assert_equal([1,2,3,1,2,3,1,2,3,1], a.lazy.cycle.take(10).force)
+ assert_equal([2,2,2,2,2,2,2,2,2,2], a.lazy.cycle.select {|x| x == 2}.take(10).force)
+ assert_equal([2,2,2,2,2,2,2,2,2,2], a.lazy.select {|x| x == 2}.cycle.take(10).force)
+ end
+
+ def test_force
+ assert_equal([1, 2, 3], (1..Float::INFINITY).lazy.take(3).force)
+ end
+
+ def test_inspect
+ assert_equal("#<Enumerator::Lazy: 1..10>", (1..10).lazy.inspect)
+ assert_equal('#<Enumerator::Lazy: #<Enumerator: "foo":each_char>>',
+ "foo".each_char.lazy.inspect)
+ assert_equal("#<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:map>",
+ (1..10).lazy.map {}.inspect)
+ assert_equal("#<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:take(0)>",
+ (1..10).lazy.take(0).inspect)
+ assert_equal("#<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:take(3)>",
+ (1..10).lazy.take(3).inspect)
+ assert_equal('#<Enumerator::Lazy: #<Enumerator::Lazy: "a".."c">:grep(/b/)>',
+ ("a".."c").lazy.grep(/b/).inspect)
+ assert_equal("#<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:cycle(3)>",
+ (1..10).lazy.cycle(3).inspect)
+ assert_equal("#<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:cycle>",
+ (1..10).lazy.cycle.inspect)
+ assert_equal("#<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:cycle(3)>",
+ (1..10).lazy.cycle(3).inspect)
+ l = (1..10).lazy.map {}.collect {}.flat_map {}.collect_concat {}.select {}.find_all {}.reject {}.grep(1).zip(?a..?c).take(10).take_while {}.drop(3).drop_while {}.cycle(3)
+ assert_equal(<<EOS.chomp, l.inspect)
+#<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: 1..10>:map>:collect>:flat_map>:collect_concat>:select>:find_all>:reject>:grep(1)>:zip("a".."c")>:take(10)>:take_while>:drop(3)>:drop_while>:cycle(3)>
+EOS
+ end
+
+ def test_lazy_to_enum
+ lazy = [1, 2, 3].lazy
+ def lazy.foo(*args)
+ yield args
+ yield args
+ end
+ enum = lazy.to_enum(:foo, :hello, :world)
+ assert_equal Enumerator::Lazy, enum.class
+ assert_equal nil, enum.size
+ assert_equal [[:hello, :world], [:hello, :world]], enum.to_a
+
+ assert_equal [1, 2, 3], lazy.to_enum.to_a
+ end
+
+ def test_size
+ lazy = [1, 2, 3].lazy
+ assert_equal 3, lazy.size
+ assert_equal 42, Enumerator::Lazy.new([],->{42}){}.size
+ assert_equal 42, Enumerator::Lazy.new([],42){}.size
+ assert_equal 42, Enumerator::Lazy.new([],42){}.lazy.size
+ assert_equal 42, lazy.to_enum{ 42 }.size
+
+ %i[map collect].each do |m|
+ assert_equal 3, lazy.send(m){}.size
+ end
+ assert_equal 3, lazy.zip([4]).size
+ %i[flat_map collect_concat select find_all reject take_while drop_while].each do |m|
+ assert_equal nil, lazy.send(m){}.size
+ end
+ assert_equal nil, lazy.grep(//).size
+
+ assert_equal 2, lazy.take(2).size
+ assert_equal 3, lazy.take(4).size
+ assert_equal 4, loop.lazy.take(4).size
+ assert_equal nil, lazy.select{}.take(4).size
+
+ assert_equal 1, lazy.drop(2).size
+ assert_equal 0, lazy.drop(4).size
+ assert_equal Float::INFINITY, loop.lazy.drop(4).size
+ assert_equal nil, lazy.select{}.drop(4).size
+
+ assert_equal 0, lazy.cycle(0).size
+ assert_equal 6, lazy.cycle(2).size
+ assert_equal 3 << 80, 4.times.inject(lazy){|enum| enum.cycle(1 << 20)}.size
+ assert_equal Float::INFINITY, lazy.cycle.size
+ assert_equal Float::INFINITY, loop.lazy.cycle(4).size
+ assert_equal Float::INFINITY, loop.lazy.cycle.size
+ assert_equal nil, lazy.select{}.cycle(4).size
+ assert_equal nil, lazy.select{}.cycle.size
+
+ class << (obj = Object.new)
+ def each; end
+ def size; 0; end
+ include Enumerable
+ end
+ lazy = obj.lazy
+ assert_equal 0, lazy.cycle.size
+ assert_raise(TypeError) {lazy.cycle("").size}
+ end
+
+ def test_map_zip
+ bug7507 = '[ruby-core:50545]'
+ assert_ruby_status(["-e", "GC.stress = true", "-e", "(1..10).lazy.map{}.zip(){}"], "", bug7507)
+ assert_ruby_status(["-e", "GC.stress = true", "-e", "(1..10).lazy.map{}.zip().to_a"], "", bug7507)
+ end
+
+ def test_require_block
+ %i[select reject drop_while take_while map flat_map].each do |method|
+ assert_raise(ArgumentError){ [].lazy.send(method) }
+ end
+ end
+
+ def test_laziness_conservation
+ bug7507 = '[ruby-core:51510]'
+ {
+ slice_before: //,
+ slice_after: //,
+ with_index: nil,
+ cycle: nil,
+ each_with_object: 42,
+ each_slice: 42,
+ each_entry: nil,
+ each_cons: 42,
+ }.each do |method, arg|
+ assert_equal Enumerator::Lazy, [].lazy.send(method, *arg).class, bug7507
+ end
+ assert_equal Enumerator::Lazy, [].lazy.chunk{}.class, bug7507
+ assert_equal Enumerator::Lazy, [].lazy.slice_when{}.class, bug7507
+ end
+
+ def test_each_cons_limit
+ n = 1 << 120
+ assert_equal([1, 2], (1..n).lazy.each_cons(2).first)
+ assert_equal([[1, 2], [2, 3]], (1..n).lazy.each_cons(2).first(2))
+ end
+
+ def test_each_slice_limit
+ n = 1 << 120
+ assert_equal([1, 2], (1..n).lazy.each_slice(2).first)
+ assert_equal([[1, 2], [3, 4]], (1..n).lazy.each_slice(2).first(2))
+ end
+
+ def test_no_warnings
+ le = (1..3).lazy
+ assert_warning("") {le.zip([4,5,6]).force}
+ assert_warning("") {le.zip(4..6).force}
+ assert_warning("") {le.take(1).force}
+ assert_warning("") {le.drop(1).force}
+ assert_warning("") {le.drop_while{false}.force}
+ end
+
+ def test_symbol_chain
+ assert_equal(["1", "3"], [1, 2, 3].lazy.reject(&:even?).map(&:to_s).force)
+ assert_raise(NoMethodError) do
+ [1, 2, 3].lazy.map(&:undefined).map(&:to_s).force
+ end
+ end
+
+ def test_uniq
+ u = (1..Float::INFINITY).lazy.uniq do |x|
+ raise "too big" if x > 10000
+ (x**2) % 10
+ end
+ assert_equal([1, 2, 3, 4, 5, 10], u.first(6))
+ assert_equal([1, 2, 3, 4, 5, 10], u.first(6))
+ end
+end
diff --git a/test/ruby/test_literal.rb b/test/ruby/test_literal.rb
index 71fcf49eff..ff40474bf7 100644
--- a/test/ruby/test_literal.rb
+++ b/test/ruby/test_literal.rb
@@ -1,3 +1,5 @@
+# -*- coding: us-ascii -*-
+# frozen_string_literal: false
require 'test/unit'
class TestRubyLiteral < Test::Unit::TestCase
@@ -12,21 +14,22 @@ class TestRubyLiteral < Test::Unit::TestCase
assert_equal ':sym', :sym.inspect
assert_instance_of Symbol, :sym
assert_equal '1234', 1234.inspect
- assert_instance_of Fixnum, 1234
+ assert_instance_of Integer, 1234
assert_equal '1234', 1_2_3_4.inspect
- assert_instance_of Fixnum, 1_2_3_4
+ assert_instance_of Integer, 1_2_3_4
assert_equal '18', 0x12.inspect
- assert_instance_of Fixnum, 0x12
+ assert_instance_of Integer, 0x12
assert_raise(SyntaxError) { eval("0x") }
assert_equal '15', 0o17.inspect
- assert_instance_of Fixnum, 0o17
+ assert_instance_of Integer, 0o17
assert_raise(SyntaxError) { eval("0o") }
assert_equal '5', 0b101.inspect
- assert_instance_of Fixnum, 0b101
+ assert_instance_of Integer, 0b101
assert_raise(SyntaxError) { eval("0b") }
assert_equal '123456789012345678901234567890', 123456789012345678901234567890.inspect
- assert_instance_of Bignum, 123456789012345678901234567890
+ assert_instance_of Integer, 123456789012345678901234567890
assert_instance_of Float, 1.3
+ assert_equal '2', eval("0x00+2").inspect
end
def test_self
@@ -53,15 +56,43 @@ class TestRubyLiteral < Test::Unit::TestCase
assert_equal "3", "\x33"
assert_equal "\n", "\n"
bug2500 = '[ruby-core:27228]'
+ bug5262 = '[ruby-core:39222]'
%w[c C- M-].each do |pre|
["u", %w[u{ }]].each do |open, close|
- str = "\"\\#{pre}\\#{open}5555#{close}\""
- assert_raise(SyntaxError, "#{bug2500} eval(#{str})") {eval(str)}
+ ["?", ['"', '"']].each do |qopen, qclose|
+ str = "#{qopen}\\#{pre}\\#{open}5555#{close}#{qclose}"
+ assert_raise(SyntaxError, "#{bug2500} eval(#{str})") {eval(str)}
+
+ str = "#{qopen}\\#{pre}\\#{open}\u201c#{close}#{qclose}"
+ assert_raise(SyntaxError, "#{bug5262} eval(#{str})") {eval(str)}
+
+ str = "#{qopen}\\#{pre}\\#{open}\u201c#{close}#{qclose}".encode("euc-jp")
+ assert_raise(SyntaxError, "#{bug5262} eval(#{str})") {eval(str)}
+
+ str = "#{qopen}\\#{pre}\\#{open}\u201c#{close}#{qclose}".encode("iso-8859-13")
+ assert_raise(SyntaxError, "#{bug5262} eval(#{str})") {eval(str)}
+
+ str = "#{qopen}\\#{pre}\\#{open}\xe2\x7f#{close}#{qclose}".force_encoding("utf-8")
+ assert_raise(SyntaxError, "#{bug5262} eval(#{str})") {eval(str)}
+ end
end
end
+ bug6069 = '[ruby-dev:45278]'
assert_equal "\x13", "\c\x33"
assert_equal "\x13", "\C-\x33"
assert_equal "\xB3", "\M-\x33"
+ assert_equal "\u201c", eval(%["\\\u{201c}"]), bug5262
+ assert_equal "\u201c".encode("euc-jp"), eval(%["\\\u{201c}"].encode("euc-jp")), bug5262
+ assert_equal "\u201c".encode("iso-8859-13"), eval(%["\\\u{201c}"].encode("iso-8859-13")), bug5262
+ assert_equal "\\\u201c", eval(%['\\\u{201c}']), bug6069
+ assert_equal "\\\u201c".encode("euc-jp"), eval(%['\\\u{201c}'].encode("euc-jp")), bug6069
+ assert_equal "\\\u201c".encode("iso-8859-13"), eval(%['\\\u{201c}'].encode("iso-8859-13")), bug6069
+ assert_equal "\u201c", eval(%[?\\\u{201c}]), bug6069
+ assert_equal "\u201c".encode("euc-jp"), eval(%[?\\\u{201c}].encode("euc-jp")), bug6069
+ assert_equal "\u201c".encode("iso-8859-13"), eval(%[?\\\u{201c}].encode("iso-8859-13")), bug6069
+
+ assert_equal "ab", eval("?a 'b'")
+ assert_equal "a\nb", eval("<<A 'b'\na\nA")
end
def test_dstring
@@ -75,16 +106,108 @@ class TestRubyLiteral < Test::Unit::TestCase
assert_equal('FooBar', b, 'r3842')
end
+ def test_dstring_encoding
+ bug11519 = '[ruby-core:70703] [Bug #11519]'
+ ['"foo#{}"', '"#{}foo"', '"#{}"'].each do |code|
+ a = eval("#-*- coding: utf-8 -*-\n#{code}")
+ assert_equal(Encoding::UTF_8, a.encoding,
+ proc{"#{bug11519}: #{code}.encoding"})
+ end
+ end
+
def test_dsymbol
assert_equal :a3c, :"a#{1+2}c"
end
+ def test_dsymbol_redefined_intern
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ class String
+ alias _intern intern
+ def intern
+ "<#{upcase}>"
+ end
+ end
+ mesg = "literal symbol should not be affected by method redefinition"
+ str = "foo"
+ assert_equal(:foo, :"#{str}", mesg)
+ end;
+ end
+
def test_xstring
assert_equal "foo\n", `echo foo`
s = 'foo'
assert_equal "foo\n", `echo #{s}`
end
+ def test_frozen_string
+ all_assertions do |a|
+ a.for("false with indicator") do
+ str = eval("# -*- frozen-string-literal: false -*-\n""'foo'")
+ assert_not_predicate(str, :frozen?)
+ end
+ a.for("true with indicator") do
+ str = eval("# -*- frozen-string-literal: true -*-\n""'foo'")
+ assert_predicate(str, :frozen?)
+ end
+ a.for("false without indicator") do
+ str = eval("# frozen-string-literal: false\n""'foo'")
+ assert_not_predicate(str, :frozen?)
+ end
+ a.for("true without indicator") do
+ str = eval("# frozen-string-literal: true\n""'foo'")
+ assert_predicate(str, :frozen?)
+ end
+ a.for("false with preceding garbage") do
+ str = eval("# x frozen-string-literal: false\n""'foo'")
+ assert_not_predicate(str, :frozen?)
+ end
+ a.for("true with preceding garbage") do
+ str = eval("# x frozen-string-literal: true\n""'foo'")
+ assert_not_predicate(str, :frozen?)
+ end
+ a.for("false with succeeding garbage") do
+ str = eval("# frozen-string-literal: false x\n""'foo'")
+ assert_not_predicate(str, :frozen?)
+ end
+ a.for("true with succeeding garbage") do
+ str = eval("# frozen-string-literal: true x\n""'foo'")
+ assert_not_predicate(str, :frozen?)
+ end
+ end
+ end
+
+ def test_frozen_string_in_array_literal
+ list = eval("# frozen-string-literal: true\n""['foo', 'bar']")
+ assert_equal 2, list.length
+ list.each { |str| assert_predicate str, :frozen? }
+ end
+
+ 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
+ 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"
+ }
+ end
+
+ def test_debug_frozen_string_in_array_literal
+ src = '["foo"]'; f = "test.rb"; n = 1
+ opt = {frozen_string_literal: true, debug_frozen_string_literal: true}
+ ary = RubyVM::InstructionSequence.compile(src, f, f, n, opt).eval
+ assert_equal("foo", ary.first)
+ assert_predicate(ary.first, :frozen?)
+ assert_raise_with_message(FrozenError, /created at #{Regexp.quote(f)}:#{n}/) {
+ ary.first << "x"
+ } unless ENV['RUBY_ISEQ_DUMP_DEBUG']
+ end
+ end
+
def test_regexp
assert_instance_of Regexp, //
assert_match(//, 'a')
@@ -110,6 +233,8 @@ class TestRubyLiteral < Test::Unit::TestCase
def test_dregexp
assert_instance_of Regexp, /re#{'ge'}xp/
assert_equal(/regexp/, /re#{'ge'}xp/)
+ bug3903 = '[ruby-core:32682]'
+ assert_raise(SyntaxError, bug3903) {eval('/[#{"\x80"}]/')}
end
def test_array
@@ -158,6 +283,210 @@ class TestRubyLiteral < Test::Unit::TestCase
assert_equal "literal", h["string"]
end
+ def test_hash_literal_frozen
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ def frozen_hash_literal_arg
+ {0=>1,1=>4,2=>17}
+ end
+
+ ObjectSpace.each_object(Hash) do |a|
+ if a.class == Hash and !a.default_proc and a.size == 3 &&
+ a[0] == 1 && a[1] == 4 && a[2] == 17
+ # should not be found.
+ raise
+ end
+ end
+ assert_not_include frozen_hash_literal_arg, 3
+ end;
+ end
+
+ def test_big_array_and_hash_literal
+ assert_normal_exit %q{GC.disable=true; x = nil; raise if eval("[#{(1..1_000_000).map{'x'}.join(", ")}]").size != 1_000_000}, "", timeout: 300, child_env: %[--disable-gems]
+ assert_normal_exit %q{GC.disable=true; x = nil; raise if eval("[#{(1..1_000_000).to_a.join(", ")}]").size != 1_000_000}, "", timeout: 300, child_env: %[--disable-gems]
+ assert_normal_exit %q{GC.disable=true; x = nil; raise if eval("{#{(1..1_000_000).map{|n| "#{n} => x"}.join(', ')}}").size != 1_000_000}, "", timeout: 300, child_env: %[--disable-gems]
+ assert_normal_exit %q{GC.disable=true; x = nil; raise if eval("{#{(1..1_000_000).map{|n| "#{n} => #{n}"}.join(', ')}}").size != 1_000_000}, "", timeout: 300, child_env: %[--disable-gems]
+ end
+
+ def test_big_hash_literal
+ bug7466 = '[ruby-dev:46658]'
+ h = {
+ 0xFE042 => 0xE5CD,
+ 0xFE043 => 0xE5CD,
+ 0xFE045 => 0xEA94,
+ 0xFE046 => 0xE4E3,
+ 0xFE047 => 0xE4E2,
+ 0xFE048 => 0xEA96,
+ 0xFE049 => 0x3013,
+ 0xFE04A => 0xEB36,
+ 0xFE04B => 0xEB37,
+ 0xFE04C => 0xEB38,
+ 0xFE04D => 0xEB49,
+ 0xFE04E => 0xEB82,
+ 0xFE04F => 0xE4D2,
+ 0xFE050 => 0xEB35,
+ 0xFE051 => 0xEAB9,
+ 0xFE052 => 0xEABA,
+ 0xFE053 => 0xE4D4,
+ 0xFE054 => 0xE4CD,
+ 0xFE055 => 0xEABB,
+ 0xFE056 => 0xEABC,
+ 0xFE057 => 0xEB32,
+ 0xFE058 => 0xEB33,
+ 0xFE059 => 0xEB34,
+ 0xFE05A => 0xEB39,
+ 0xFE05B => 0xEB5A,
+ 0xFE190 => 0xE5A4,
+ 0xFE191 => 0xE5A5,
+ 0xFE192 => 0xEAD0,
+ 0xFE193 => 0xEAD1,
+ 0xFE194 => 0xEB47,
+ 0xFE195 => 0xE509,
+ 0xFE196 => 0xEAA0,
+ 0xFE197 => 0xE50B,
+ 0xFE198 => 0xEAA1,
+ 0xFE199 => 0xEAA2,
+ 0xFE19A => 0x3013,
+ 0xFE19B => 0xE4FC,
+ 0xFE19C => 0xE4FA,
+ 0xFE19D => 0xE4FC,
+ 0xFE19E => 0xE4FA,
+ 0xFE19F => 0xE501,
+ 0xFE1A0 => 0x3013,
+ 0xFE1A1 => 0xE5DD,
+ 0xFE1A2 => 0xEADB,
+ 0xFE1A3 => 0xEAE9,
+ 0xFE1A4 => 0xEB13,
+ 0xFE1A5 => 0xEB14,
+ 0xFE1A6 => 0xEB15,
+ 0xFE1A7 => 0xEB16,
+ 0xFE1A8 => 0xEB17,
+ 0xFE1A9 => 0xEB18,
+ 0xFE1AA => 0xEB19,
+ 0xFE1AB => 0xEB1A,
+ 0xFE1AC => 0xEB44,
+ 0xFE1AD => 0xEB45,
+ 0xFE1AE => 0xE4CB,
+ 0xFE1AF => 0xE5BF,
+ 0xFE1B0 => 0xE50E,
+ 0xFE1B1 => 0xE4EC,
+ 0xFE1B2 => 0xE4EF,
+ 0xFE1B3 => 0xE4F8,
+ 0xFE1B4 => 0x3013,
+ 0xFE1B5 => 0x3013,
+ 0xFE1B6 => 0xEB1C,
+ 0xFE1B9 => 0xEB7E,
+ 0xFE1D3 => 0xEB22,
+ 0xFE7DC => 0xE4D8,
+ 0xFE1D4 => 0xEB23,
+ 0xFE1D5 => 0xEB24,
+ 0xFE1D6 => 0xEB25,
+ 0xFE1CC => 0xEB1F,
+ 0xFE1CD => 0xEB20,
+ 0xFE1CE => 0xE4D9,
+ 0xFE1CF => 0xE48F,
+ 0xFE1C5 => 0xE5C7,
+ 0xFE1C6 => 0xEAEC,
+ 0xFE1CB => 0xEB1E,
+ 0xFE1DA => 0xE4DD,
+ 0xFE1E1 => 0xEB57,
+ 0xFE1E2 => 0xEB58,
+ 0xFE1E3 => 0xE492,
+ 0xFE1C9 => 0xEB1D,
+ 0xFE1D9 => 0xE4D3,
+ 0xFE1DC => 0xE5D4,
+ 0xFE1BA => 0xE4E0,
+ 0xFE1BB => 0xEB76,
+ 0xFE1C8 => 0xE4E0,
+ 0xFE1DD => 0xE5DB,
+ 0xFE1BC => 0xE4DC,
+ 0xFE1D8 => 0xE4DF,
+ 0xFE1BD => 0xE49A,
+ 0xFE1C7 => 0xEB1B,
+ 0xFE1C2 => 0xE5C2,
+ 0xFE1C0 => 0xE5C0,
+ 0xFE1B8 => 0xE4DB,
+ 0xFE1C3 => 0xE470,
+ 0xFE1BE => 0xE4D8,
+ 0xFE1C4 => 0xE4D9,
+ 0xFE1B7 => 0xE4E1,
+ 0xFE1BF => 0xE4DE,
+ 0xFE1C1 => 0xE5C1,
+ 0xFE1CA => 0x3013,
+ 0xFE1D0 => 0xE4E1,
+ 0xFE1D1 => 0xEB21,
+ 0xFE1D2 => 0xE4D7,
+ 0xFE1D7 => 0xE4DA,
+ 0xFE1DB => 0xE4EE,
+ 0xFE1DE => 0xEB3F,
+ 0xFE1DF => 0xEB46,
+ 0xFE1E0 => 0xEB48,
+ 0xFE336 => 0xE4FB,
+ 0xFE320 => 0xE472,
+ 0xFE321 => 0xEB67,
+ 0xFE322 => 0xEACA,
+ 0xFE323 => 0xEAC0,
+ 0xFE324 => 0xE5AE,
+ 0xFE325 => 0xEACB,
+ 0xFE326 => 0xEAC9,
+ 0xFE327 => 0xE5C4,
+ 0xFE328 => 0xEAC1,
+ 0xFE329 => 0xE4E7,
+ 0xFE32A => 0xE4E7,
+ 0xFE32B => 0xEACD,
+ 0xFE32C => 0xEACF,
+ 0xFE32D => 0xEACE,
+ 0xFE32E => 0xEAC7,
+ 0xFE32F => 0xEAC8,
+ 0xFE330 => 0xE471,
+ 0xFE331 => "[Bug #7466]",
+ }
+ k = h.keys
+ assert_equal([129, 0xFE331], [k.size, k.last], bug7466)
+
+ code = [
+ "h = {",
+ (1..128).map {|i| "#{i} => 0,"},
+ (129..140).map {|i| "#{i} => [],"},
+ "}",
+ ].join
+ assert_separately([], <<-"end;")
+ GC.stress = true
+ #{code}
+ GC.stress = false
+ assert_equal(140, h.size)
+ end;
+ end
+
+ def test_hash_duplicated_key
+ 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" => 100, "b" => 200, "a" => 300, "a" => 400}
+ 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'))
+ end
+
+ def test_hash_frozen_key_id
+ key = "a".freeze
+ h = {key => 100}
+ assert_equal(100, h['a'])
+ assert_same(key, *h.keys)
+ end
+
+ def test_hash_key_tampering
+ key = "a"
+ h = {key => 100}
+ key.upcase!
+ assert_equal(100, h['a'])
+ end
+
def test_range
assert_instance_of Range, (1..2)
assert_equal(1..2, 1..2)
@@ -194,7 +523,7 @@ class TestRubyLiteral < Test::Unit::TestCase
end
def test__LINE__
- assert_instance_of Fixnum, __LINE__
+ assert_instance_of Integer, __LINE__
assert_equal __LINE__, __LINE__
end
@@ -222,15 +551,12 @@ class TestRubyLiteral < Test::Unit::TestCase
}
}
bug2407 = '[ruby-dev:39798]'
- head.each {|h|
- if /^0/ =~ h
- begin
- eval("#{h}_")
- rescue SyntaxError => e
- assert_match(/numeric literal without digits\Z/, e.message, bug2407)
- end
+ head.grep_v(/^0/) do |s|
+ head.grep(/^0/) do |h|
+ h = "#{s}#{h}_"
+ assert_syntax_error(h, /numeric literal without digits\Z/, "#{bug2407}: #{h.inspect}")
end
- }
+ end
end
def test_float
@@ -260,6 +586,17 @@ class TestRubyLiteral < Test::Unit::TestCase
}
}
}
+ assert_equal(100.0, 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100e100)
end
+ def test_symbol_list
+ assert_equal([:foo, :bar], %i[foo bar])
+ assert_equal([:"\"foo"], %i["foo])
+
+ x = 10
+ assert_equal([:foo, :b10], %I[foo b#{x}])
+ assert_equal([:"\"foo10"], %I["foo#{x}])
+
+ assert_ruby_status(["--disable-gems", "--dump=parsetree"], "%I[foo bar]")
+ end
end
diff --git a/test/ruby/test_m17n.rb b/test/ruby/test_m17n.rb
index 07cda751d1..75daf61376 100644
--- a/test/ruby/test_m17n.rb
+++ b/test/ruby/test_m17n.rb
@@ -1,5 +1,6 @@
+# coding: US-ASCII
+# frozen_string_literal: false
require 'test/unit'
-require 'stringio'
class TestM17N < Test::Unit::TestCase
def assert_encoding(encname, actual, message=nil)
@@ -8,7 +9,7 @@ class TestM17N < Test::Unit::TestCase
module AESU
def ua(str) str.dup.force_encoding("US-ASCII") end
- def a(str) str.dup.force_encoding("ASCII-8BIT") end
+ def a(str) str.b end
def e(str) str.dup.force_encoding("EUC-JP") end
def s(str) str.dup.force_encoding("Windows-31J") end
def u(str) str.dup.force_encoding("UTF-8") end
@@ -23,19 +24,8 @@ class TestM17N < Test::Unit::TestCase
assert_equal(a(bytes), a(actual), message)
end
- def assert_warning(pat, mesg=nil)
- begin
- org_stderr = $stderr
- $stderr = StringIO.new(warn = '')
- yield
- ensure
- $stderr = org_stderr
- end
- assert_match(pat, warn, mesg)
- end
-
def assert_regexp_generic_encoding(r)
- assert(!r.fixed_encoding?)
+ assert_not_predicate(r, :fixed_encoding?)
%w[ASCII-8BIT EUC-JP Windows-31J UTF-8].each {|ename|
# "\xc2\xa1" is a valid sequence for ASCII-8BIT, EUC-JP, Windows-31J and UTF-8.
assert_nothing_raised { r =~ "\xc2\xa1".force_encoding(ename) }
@@ -43,7 +33,7 @@ class TestM17N < Test::Unit::TestCase
end
def assert_regexp_fixed_encoding(r)
- assert(r.fixed_encoding?)
+ assert_predicate(r, :fixed_encoding?)
%w[ASCII-8BIT EUC-JP Windows-31J UTF-8].each {|ename|
enc = Encoding.find(ename)
if enc == r.encoding
@@ -117,7 +107,7 @@ class TestM17N < Test::Unit::TestCase
elsif !s2.ascii_only?
assert_equal(s2.encoding, t.encoding)
else
- assert([s1.encoding, s2.encoding].include?(t.encoding))
+ assert_include([s1.encoding, s2.encoding], t.encoding)
end
end
@@ -205,31 +195,137 @@ class TestM17N < Test::Unit::TestCase
end
def test_string_inspect_encoding
- orig_int = Encoding.default_internal
+ EnvUtil.suppress_warning do
+ begin
+ orig_int = Encoding.default_internal
+ orig_ext = Encoding.default_external
+ Encoding.default_internal = nil
+ [Encoding::UTF_8, Encoding::EUC_JP, Encoding::Windows_31J, Encoding::GB18030].
+ each do |e|
+ Encoding.default_external = e
+ str = "\x81\x30\x81\x30".force_encoding('GB18030')
+ assert_equal(Encoding::GB18030 == e ? %{"#{str}"} : '"\x{81308130}"', str.inspect)
+ str = e("\xa1\x8f\xa1\xa1")
+ expected = "\"\\xA1\x8F\xA1\xA1\"".force_encoding("EUC-JP")
+ assert_equal(Encoding::EUC_JP == e ? expected : "\"\\xA1\\x{8FA1A1}\"", str.inspect)
+ str = s("\x81@")
+ assert_equal(Encoding::Windows_31J == e ? %{"#{str}"} : '"\x{8140}"', str.inspect)
+ str = "\u3042\u{10FFFD}"
+ assert_equal(Encoding::UTF_8 == e ? %{"#{str}"} : '"\u3042\u{10FFFD}"', str.inspect)
+ end
+ Encoding.default_external = Encoding::UTF_8
+ [Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE,
+ Encoding::UTF8_SOFTBANK].each do |e|
+ str = "abc".encode(e)
+ assert_equal('"abc"', str.inspect)
+ end
+ ensure
+ Encoding.default_internal = orig_int
+ Encoding.default_external = orig_ext
+ end
+ end
+ end
+
+ STR_WITHOUT_BOM = "\u3042".freeze
+ STR_WITH_BOM = "\uFEFF\u3042".freeze
+ bug8940 = '[ruby-core:59757] [Bug #8940]'
+ bug9415 = '[ruby-dev:47895] [Bug #9415]'
+ %w/UTF-16 UTF-32/.each do |enc|
+ %w/BE LE/.each do |endian|
+ bom = "\uFEFF".encode("#{enc}#{endian}").force_encoding(enc)
+
+ define_method("test_utf_16_32_inspect(#{enc}#{endian})") do
+ s = STR_WITHOUT_BOM.encode(enc + endian)
+ # When a UTF-16/32 string doesn't have a BOM,
+ # inspect as a dummy encoding string.
+ assert_equal(s.dup.force_encoding("ISO-2022-JP").inspect,
+ s.dup.force_encoding(enc).inspect)
+ assert_normal_exit("#{bom.b.dump}.force_encoding('#{enc}').inspect", bug8940)
+ end
+
+ define_method("test_utf_16_32_codepoints(#{enc}#{endian})") do
+ assert_equal([0xFEFF], bom.codepoints, bug9415)
+ end
+
+ define_method("test_utf_16_32_ord(#{enc}#{endian})") do
+ assert_equal(0xFEFF, bom.ord, bug9415)
+ end
+
+ define_method("test_utf_16_32_inspect(#{enc}#{endian}-BOM)") do
+ s = STR_WITH_BOM.encode(enc + endian)
+ # When a UTF-16/32 string has a BOM,
+ # inspect as a particular encoding string.
+ assert_equal(s.inspect,
+ s.dup.force_encoding(enc).inspect)
+ end
+ end
+ end
+
+ def test_utf_without_bom_asciionly
+ bug10598 = '[ruby-core:66835] [Bug #10598]'
+ encs = [Encoding::UTF_16, Encoding::UTF_32].find_all {|enc|
+ "abcd".force_encoding(enc).ascii_only?
+ }
+ assert_empty(encs, bug10598)
+ end
+
+ def test_utf_without_bom_valid
+ encs = [Encoding::UTF_16, Encoding::UTF_32].find_all {|enc|
+ !(+"abcd").encode!(enc).force_encoding(enc).valid_encoding?
+ }
+ assert_empty(encs)
+ end
+
+ def test_object_utf16_32_inspect
+ EnvUtil.suppress_warning do
+ begin
+ orig_int = Encoding.default_internal
+ orig_ext = Encoding.default_external
+ Encoding.default_internal = nil
+ Encoding.default_external = Encoding::UTF_8
+ o = Object.new
+ [Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE].each do |e|
+ o.instance_eval "undef inspect;def inspect;'abc'.encode('#{e}');end"
+ assert_equal '[abc]', [o].inspect
+ end
+ ensure
+ Encoding.default_internal = orig_int
+ Encoding.default_external = orig_ext
+ end
+ end
+ end
+
+ def test_object_inspect_external
+ orig_v, $VERBOSE = $VERBOSE, false
+ orig_int, Encoding.default_internal = Encoding.default_internal, nil
orig_ext = Encoding.default_external
- Encoding.default_internal = nil
- [Encoding::UTF_8, Encoding::EUC_JP, Encoding::Windows_31J, Encoding::GB18030].
- each do |e|
- Encoding.default_external = e
- str = "\x81\x30\x81\x30".force_encoding('GB18030')
- assert_equal(Encoding::GB18030 == e ? %{"#{str}"} : '"\x{81308130}"', str.inspect)
- str = e("\xa1\x8f\xa1\xa1")
- expected = "\"\\xA1\x8F\xA1\xA1\"".force_encoding("EUC-JP")
- assert_equal(Encoding::EUC_JP == e ? expected : "\"\\xA1\\x{8FA1A1}\"", str.inspect)
- str = s("\x81@")
- assert_equal(Encoding::Windows_31J == e ? %{"#{str}"} : '"\x{8140}"', str.inspect)
- str = "\u3042\u{10FFFD}"
- assert_equal(Encoding::UTF_8 == e ? %{"#{str}"} : '"\u3042\u{10FFFD}"', str.inspect)
+ o = Object.new
+
+ Encoding.default_external = Encoding::UTF_16BE
+ def o.inspect
+ "abc"
end
- Encoding.default_external = Encoding::UTF_8
- [Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE,
- Encoding::UTF8_SOFTBANK].each do |e|
- str = "abc".encode(e)
- assert_equal('"abc"', str.inspect)
+ assert_nothing_raised(Encoding::CompatibilityError) { [o].inspect }
+
+ def o.inspect
+ "abc".encode(Encoding.default_external)
+ end
+ assert_equal '[abc]', [o].inspect
+
+ Encoding.default_external = Encoding::US_ASCII
+ def o.inspect
+ "\u3042"
end
+ assert_equal '[\u3042]', [o].inspect
+
+ def o.inspect
+ "\x82\xa0".force_encoding(Encoding::Windows_31J)
+ end
+ assert_equal '[\x{82A0}]', [o].inspect
ensure
Encoding.default_internal = orig_int
Encoding.default_external = orig_ext
+ $VERBOSE = orig_v
end
def test_str_dump
@@ -269,7 +365,10 @@ class TestM17N < Test::Unit::TestCase
"\u3042".encode("UTF-16LE"),
"\u3042".encode("UTF-16BE"),
].each do |str|
- assert_equal(str, eval(str.dump), "[ruby-dev:33142]")
+ dump = str.dump
+ assert_equal(str, eval(dump), "[ruby-dev:33142]")
+ assert_equal(str, dump.undump)
+ assert_equal(str, eval("# frozen-string-literal: true\n#{dump}"), '[Bug #14687]')
end
end
@@ -295,15 +394,15 @@ class TestM17N < Test::Unit::TestCase
].each {|pat2|
s = [pat2.gsub(/ /, "")].pack("B*").force_encoding("utf-8")
if pat2 <= bits_0x10ffff
- assert(s.valid_encoding?, "#{pat2}")
+ assert_predicate(s, :valid_encoding?, "#{pat2}")
else
- assert(!s.valid_encoding?, "#{pat2}")
+ assert_not_predicate(s, :valid_encoding?, "#{pat2}")
end
}
if / / =~ pat0
pat3 = pat1.gsub(/X/, "0")
s = [pat3.gsub(/ /, "")].pack("B*").force_encoding("utf-8")
- assert(!s.valid_encoding?, "#{pat3}")
+ assert_not_predicate(s, :valid_encoding?, "#{pat3}")
end
}
}
@@ -323,12 +422,12 @@ class TestM17N < Test::Unit::TestCase
pat0.gsub(/x/, '1'),
].each {|pat1|
s = [pat1.gsub(/ /, "")].pack("B*").force_encoding("utf-8")
- assert(!s.valid_encoding?, "#{pat1}")
+ assert_not_predicate(s, :valid_encoding?, "#{pat1}")
}
}
pats.values_at(0,3).each {|pat|
s = [pat.gsub(/ /, "")].pack("B*").force_encoding("utf-8")
- assert(s.valid_encoding?, "#{pat}")
+ assert_predicate(s, :valid_encoding?, "#{pat}")
}
end
@@ -376,7 +475,7 @@ class TestM17N < Test::Unit::TestCase
def test_regexp_ascii_none
r = /a/n
- assert_warning(%r{regexp match /.../n against to}) {
+ assert_warning(%r{historical binary regexp match /\.\.\./n against}) {
assert_regexp_generic_ascii(r)
}
@@ -385,13 +484,13 @@ class TestM17N < Test::Unit::TestCase
assert_equal(0, r =~ s("a"))
assert_equal(0, r =~ u("a"))
assert_equal(nil, r =~ a("\xc2\xa1"))
- assert_warning(%r{regexp match /.../n against to EUC-JP string}) {
+ assert_warning(%r{historical binary regexp match /\.\.\./n against EUC-JP string}) {
assert_equal(nil, r =~ e("\xc2\xa1"))
}
- assert_warning(%r{regexp match /.../n against to Windows-31J string}) {
+ assert_warning(%r{historical binary regexp match /\.\.\./n against Windows-31J string}) {
assert_equal(nil, r =~ s("\xc2\xa1"))
}
- assert_warning(%r{regexp match /.../n against to UTF-8 string}) {
+ assert_warning(%r{historical binary regexp match /\.\.\./n against UTF-8 string}) {
assert_equal(nil, r =~ u("\xc2\xa1"))
}
@@ -404,6 +503,9 @@ class TestM17N < Test::Unit::TestCase
assert_regexp_fixed_ascii8bit(eval(a(%{/\xc2\xa1/n})))
assert_regexp_fixed_ascii8bit(eval(a(%q{/\xc2\xa1/})))
+ s = '\xc2\xa1'
+ assert_regexp_fixed_ascii8bit(/#{s}/)
+
assert_raise(SyntaxError) { eval("/\xa1\xa1/n".force_encoding("euc-jp")) }
[/\xc2\xa1/n, eval(a(%{/\xc2\xa1/})), eval(a(%{/\xc2\xa1/n}))].each {|r|
@@ -527,30 +629,38 @@ class TestM17N < Test::Unit::TestCase
assert_nothing_raised {
r = Regexp.new(s)
}
- assert(r.fixed_encoding?)
+ assert_predicate(r, :fixed_encoding?)
assert_match(r, "\xa4\xa2".force_encoding("euc-jp"))
r = eval('/\p{Hiragana}/'.force_encoding("euc-jp"))
- assert(r.fixed_encoding?)
+ assert_predicate(r, :fixed_encoding?)
assert_match(r, "\xa4\xa2".force_encoding("euc-jp"))
r = /\p{Hiragana}/e
- assert(r.fixed_encoding?)
+ assert_predicate(r, :fixed_encoding?)
+ assert_match(r, "\xa4\xa2".force_encoding("euc-jp"))
+
+ r = /\p{AsciI}/e
+ assert_predicate(r, :fixed_encoding?)
+ assert_match(r, "a".force_encoding("euc-jp"))
+
+ r = /\p{hiraganA}/e
+ assert_predicate(r, :fixed_encoding?)
assert_match(r, "\xa4\xa2".force_encoding("euc-jp"))
r = eval('/\u{3042}\p{Hiragana}/'.force_encoding("euc-jp"))
- assert(r.fixed_encoding?)
+ assert_predicate(r, :fixed_encoding?)
assert_equal(Encoding::UTF_8, r.encoding)
r = eval('/\p{Hiragana}\u{3042}/'.force_encoding("euc-jp"))
- assert(r.fixed_encoding?)
+ assert_predicate(r, :fixed_encoding?)
assert_equal(Encoding::UTF_8, r.encoding)
end
def test_regexp_embed_preprocess
r1 = /\xa4\xa2/e
r2 = /#{r1}/
- assert(r2.source.include?(r1.source))
+ assert_include(r2.source, r1.source)
end
def test_begin_end_offset
@@ -595,10 +705,10 @@ class TestM17N < Test::Unit::TestCase
def test_union_0
r = Regexp.union
assert_regexp_generic_ascii(r)
- assert(r !~ a(""))
- assert(r !~ e(""))
- assert(r !~ s(""))
- assert(r !~ u(""))
+ assert_not_match(r, a(""))
+ assert_not_match(r, e(""))
+ assert_not_match(r, s(""))
+ assert_not_match(r, u(""))
end
def test_union_1_asciionly_string
@@ -625,7 +735,7 @@ class TestM17N < Test::Unit::TestCase
def test_union_1_regexp
assert_regexp_generic_ascii(Regexp.union(//))
- assert_warning(%r{regexp match /.../n against to}) {
+ assert_warning(%r{historical binary regexp match /.../n against}) {
assert_regexp_generic_ascii(Regexp.union(//n))
}
assert_regexp_fixed_eucjp(Regexp.union(//e))
@@ -668,7 +778,7 @@ class TestM17N < Test::Unit::TestCase
end
def test_dynamic_ascii_regexp
- assert_warning(%r{regexp match /.../n against to}) {
+ assert_warning(%r{historical binary regexp match /.../n against}) {
assert_regexp_generic_ascii(/#{ }/n)
}
assert_regexp_fixed_ascii8bit(/#{ }\xc2\xa1/n)
@@ -779,15 +889,15 @@ class TestM17N < Test::Unit::TestCase
end
def test_sprintf_p
- enc = "".inspect.encoding
- asc = Encoding::US_ASCII
Encoding.list.each do |e|
format = "%p".force_encoding(e)
['', 'a', "\xC2\xA1", "\x00"].each do |s|
s.force_encoding(e)
- assert_strenc(s.inspect, e.ascii_compatible? && enc == asc ? e : enc, format % s)
+ enc = (''.force_encoding(e) + s.inspect).encoding
+ assert_strenc(s.inspect, enc, format % s)
end
s = "\xC2\xA1".force_encoding(e)
+ enc = ('' + s.inspect).encoding
assert_strenc('%10s' % s.inspect, enc, "%10p" % s)
end
end
@@ -821,9 +931,9 @@ class TestM17N < Test::Unit::TestCase
end
def test_str_lt
- assert(a("a") < a("\xa1"))
- assert(a("a") < s("\xa1"))
- assert(s("a") < a("\xa1"))
+ assert_operator(a("a"), :<, a("\xa1"))
+ assert_operator(a("a"), :<, s("\xa1"))
+ assert_operator(s("a"), :<, a("\xa1"))
end
def test_str_multiply
@@ -898,9 +1008,22 @@ class TestM17N < Test::Unit::TestCase
assert_equal("\u{439}", "a\u{439}bcdefghijklmnop"[1, 1][0, 1], bug2379)
end
+ def test_str_aref_force_encoding
+ bug5836 = '[ruby-core:41896]'
+ Encoding.list.each do |enc|
+ next unless enc.ascii_compatible?
+ s = "abc".force_encoding(enc)
+ assert_equal("", s[3, 1], bug5836)
+ end
+ end
+
def test_aset
s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
assert_raise(Encoding::CompatibilityError){s["\xb0\xa3"] = "foo"}
+
+ a = ua("a")
+ a[/a/] = u("")
+ assert_equal Encoding::US_ASCII, a.encoding
end
def test_str_center
@@ -926,6 +1049,7 @@ class TestM17N < Test::Unit::TestCase
assert_equal("X\u3042\u3044X", "A\u3042\u3044\u3046".tr("^\u3042\u3044", "X"))
assert_equal("\u3042\u3046" * 100, ("\u3042\u3044" * 100).tr("\u3044", "\u3046"))
+ assert_equal("Y", "\u3042".tr("^X", "Y"))
end
def test_tr_s
@@ -939,6 +1063,11 @@ class TestM17N < Test::Unit::TestCase
assert_raise(Encoding::CompatibilityError){s.count(a("\xa3\xb0"))}
end
+ def test_count_sjis_trailing_byte
+ bug10078 = '[ruby-dev:48442] [Bug #10078]'
+ assert_equal(0, s("\x98\x61").count("a"), bug10078)
+ end
+
def test_delete
assert_equal(1, e("\xa1\xa2").delete("z").length)
s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
@@ -953,6 +1082,10 @@ class TestM17N < Test::Unit::TestCase
assert_equal(false, e("\xa1\xa2\xa3\xa4").include?(e("\xa3")))
s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
assert_equal(false, s.include?(e("\xb0\xa3")))
+ bug11488 = '[ruby-core:70592] [Bug #11488]'
+ each_encoding("abcdef", "def") do |str, substr|
+ assert_equal(true, str.include?(substr), bug11488)
+ end
end
def test_index
@@ -962,6 +1095,10 @@ class TestM17N < Test::Unit::TestCase
assert_nil(e("\xa1\xa2\xa3\xa4").rindex(e("\xa3")))
s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
assert_raise(Encoding::CompatibilityError){s.rindex(a("\xb1\xa3"))}
+ bug11488 = '[ruby-core:70592] [Bug #11488]'
+ each_encoding("abcdef", "def") do |str, substr|
+ assert_equal(3, str.index(substr), bug11488)
+ end
end
def test_next
@@ -982,15 +1119,15 @@ class TestM17N < Test::Unit::TestCase
s = "\x80".force_encoding("ASCII-8BIT")
r = Regexp.new("\x80".force_encoding("ASCII-8BIT"))
s2 = s.sub(r, "")
- assert(s2.empty?)
- assert(s2.ascii_only?)
+ assert_empty(s2)
+ assert_predicate(s2, :ascii_only?)
end
def test_sub3
repl = "\x81".force_encoding("sjis")
assert_equal(false, repl.valid_encoding?)
s = "a@".sub(/a/, repl)
- assert(s.valid_encoding?)
+ assert_predicate(s, :valid_encoding?)
end
def test_insert
@@ -1004,7 +1141,7 @@ class TestM17N < Test::Unit::TestCase
def test_dup_scan
s1 = e("\xa4\xa2")*100
- s2 = s1.dup.force_encoding("ascii-8bit")
+ s2 = s1.b
s2.scan(/\A./n) {|f|
assert_equal(Encoding::ASCII_8BIT, f.encoding)
}
@@ -1012,7 +1149,7 @@ class TestM17N < Test::Unit::TestCase
def test_dup_aref
s1 = e("\xa4\xa2")*100
- s2 = s1.dup.force_encoding("ascii-8bit")
+ s2 = s1.b
assert_equal(Encoding::ASCII_8BIT, s2[10..-1].encoding)
end
@@ -1029,7 +1166,12 @@ class TestM17N < Test::Unit::TestCase
end
def test_reverse
- assert_equal(u("\xf0jihgfedcba"), u("abcdefghij\xf0").reverse)
+ bug11387 = '[ruby-dev:49189] [Bug #11387]'
+ s1 = u("abcdefghij\xf0")
+ s2 = s1.reverse
+ assert_not_predicate(s1, :valid_encoding?, bug11387)
+ assert_equal(u("\xf0jihgfedcba"), s2)
+ assert_not_predicate(s2, :valid_encoding?, bug11387)
end
def test_reverse_bang
@@ -1054,7 +1196,6 @@ class TestM17N < Test::Unit::TestCase
assert_equal(false, s.ascii_only?, "[ruby-core:14566] reported by Sam Ruby")
s = "abc".force_encoding(Encoding::ASCII_8BIT)
- t = s.gsub(/b/, "\xa1\xa1".force_encoding("euc-jp"))
assert_equal(Encoding::ASCII_8BIT, s.encoding)
assert_raise(Encoding::CompatibilityError) {
@@ -1066,14 +1207,20 @@ class TestM17N < Test::Unit::TestCase
s = e("\xa3\xb0\xa3\xb1\xa3\xb2\xa3\xb3\xa3\xb4")
assert_equal(e("\xa3\xb0z\xa3\xb2\xa3\xb3\xa3\xb4"), s.gsub(/\xa3\xb1/e, "z"))
- assert_equal(Encoding::EUC_JP, (a("").gsub(//) { e("") }.encoding))
- assert_equal(Encoding::EUC_JP, (a("a").gsub(/a/) { e("") }.encoding))
+ assert_equal(Encoding::ASCII_8BIT, (a("").gsub(//) { e("") }.encoding))
+ assert_equal(Encoding::ASCII_8BIT, (a("a").gsub(/a/) { e("") }.encoding))
end
def test_end_with
s1 = s("\x81\x40")
s2 = "@"
assert_equal(false, s1.end_with?(s2), "#{encdump s1}.end_with?(#{encdump s2})")
+ each_encoding("\u3042\u3044", "\u3044") do |_s1, _s2|
+ assert_equal(true, _s1.end_with?(_s2), "#{encdump _s1}.end_with?(#{encdump _s2})")
+ end
+ each_encoding("\u3042a\u3044", "a\u3044") do |_s1, _s2|
+ assert_equal(true, _s1.end_with?(_s2), "#{encdump _s1}.end_with?(#{encdump _s2})")
+ end
end
def test_each_line
@@ -1093,6 +1240,13 @@ class TestM17N < Test::Unit::TestCase
assert_equal(a, s.each_char.to_a, "[ruby-dev:33211] #{encdump s}.each_char.to_a")
end
+ def test_str_concat
+ assert_equal(1, "".concat(0xA2).size)
+ assert_equal(Encoding::ASCII_8BIT, "".force_encoding("US-ASCII").concat(0xA2).encoding)
+ assert_equal("A\x84\x31\xA4\x39".force_encoding("GB18030"),
+ "A".force_encoding("GB18030") << 0x8431A439)
+ end
+
def test_regexp_match
assert_equal([0,0], //.match("\xa1\xa1".force_encoding("euc-jp"),-1).offset(0))
assert_equal(0, // =~ :a)
@@ -1102,6 +1256,13 @@ class TestM17N < Test::Unit::TestCase
assert_equal(e("\xa1\xa2\xa1\xa3").split(//),
[e("\xa1\xa2"), e("\xa1\xa3")],
'[ruby-dev:32452]')
+
+ each_encoding("abc,def", ",", "abc", "def") do |str, sep, *expected|
+ assert_equal(expected, str.split(sep, -1))
+ end
+ each_encoding("abc\0def", "\0", "abc", "def") do |str, sep, *expected|
+ assert_equal(expected, str.split(sep, -1))
+ end
end
def test_nonascii_method_name
@@ -1127,7 +1288,7 @@ class TestM17N < Test::Unit::TestCase
def test_symbol_op
ops = %w"
- .. ... + - +(binary) -(binary) * / % ** +@ -@ | ^ & ! <=> > >= < <= ==
+ .. ... + - * / % ** +@ -@ | ^ & ! <=> > >= < <= ==
=== != =~ !~ ~ ! [] []= << >> :: `
"
ops.each do |op|
@@ -1139,6 +1300,19 @@ class TestM17N < Test::Unit::TestCase
0.upto(255) {|b|
assert_equal([b].pack("C"), b.chr)
}
+ assert_equal("\x84\x31\xA4\x39".force_encoding("GB18030"), 0x8431A439.chr("GB18030"))
+ e = assert_raise(RangeError) {
+ 2206368128.chr(Encoding::UTF_8)
+ }
+ assert_not_match(/-\d+ out of char range/, e.message)
+
+ assert_raise(RangeError){ 0x80.chr("US-ASCII") }
+ assert_raise(RangeError){ 0x80.chr("SHIFT_JIS") }
+ assert_raise(RangeError){ 0xE0.chr("SHIFT_JIS") }
+ assert_raise(RangeError){ 0x100.chr("SHIFT_JIS") }
+ assert_raise(RangeError){ 0xA0.chr("EUC-JP") }
+ assert_raise(RangeError){ 0x100.chr("EUC-JP") }
+ assert_raise(RangeError){ 0xA1A0.chr("EUC-JP") }
end
def test_marshal
@@ -1150,8 +1324,8 @@ class TestM17N < Test::Unit::TestCase
def test_env
locale_encoding = Encoding.find("locale")
ENV.each {|k, v|
- assert_equal(locale_encoding, k.encoding)
- assert_equal(locale_encoding, v.encoding)
+ assert_equal(locale_encoding, k.encoding, k)
+ assert_equal(locale_encoding, v.encoding, v)
}
end
@@ -1293,6 +1467,24 @@ class TestM17N < Test::Unit::TestCase
s = "\xa1\xa1\x8f".force_encoding("euc-jp")
assert_equal(false, s.valid_encoding?)
assert_equal(true, s.reverse.valid_encoding?)
+
+ bug4018 = '[ruby-core:33027]'
+ s = "\xa1\xa1".force_encoding("euc-jp")
+ assert_equal(true, s.valid_encoding?)
+ s << "\x8f".force_encoding("euc-jp")
+ assert_equal(false, s.valid_encoding?, bug4018)
+ s = "aa".force_encoding("utf-16be")
+ assert_equal(true, s.valid_encoding?)
+ s << "\xff".force_encoding("utf-16be")
+ assert_equal(false, s.valid_encoding?, bug4018)
+
+ bug6190 = '[ruby-core:43557]'
+ s = "\xe9"
+ s = s.encode("utf-8", "utf-8")
+ assert_equal(false, s.valid_encoding?, bug6190)
+ s = "\xe9"
+ s.encode!("utf-8", "utf-8")
+ assert_equal(false, s.valid_encoding?, bug6190)
end
def test_getbyte
@@ -1312,6 +1504,42 @@ class TestM17N < Test::Unit::TestCase
s = u("\xE3\x81\x82\xE3\x81\x84")
s.setbyte(-4, 0x84)
assert_equal(u("\xE3\x81\x84\xE3\x81\x84"), s)
+
+ x = "x" * 100
+ t = nil
+ failure = proc {"#{i}: #{encdump(t)}"}
+
+ s = "\u{3042 3044}"
+ s.bytesize.times {|i|
+ t = s + x
+ t.setbyte(i, t.getbyte(i)+1)
+ assert_predicate(t, :valid_encoding?, failure)
+ assert_not_predicate(t, :ascii_only?, failure)
+ t = s + x
+ t.setbyte(i, 0x20)
+ assert_not_predicate(t, :valid_encoding?, failure)
+ }
+
+ s = "\u{41 42 43}"
+ s.bytesize.times {|i|
+ t = s + x
+ t.setbyte(i, 0x20)
+ assert_predicate(t, :valid_encoding?, failure)
+ assert_predicate(t, :ascii_only?, failure)
+ t.setbyte(i, 0xe3)
+ assert_not_predicate(t, :valid_encoding?, failure)
+ }
+ end
+
+ def test_setbyte_range
+ s = u("\xE3\x81\x82\xE3\x81\x84")
+ assert_nothing_raised { s.setbyte(0, -1) }
+ assert_nothing_raised { s.setbyte(0, 0x00) }
+ assert_nothing_raised { s.setbyte(0, 0x7F) }
+ assert_nothing_raised { s.setbyte(0, 0x80) }
+ assert_nothing_raised { s.setbyte(0, 0xff) }
+ assert_nothing_raised { s.setbyte(0, 0x100) }
+ assert_nothing_raised { s.setbyte(0, 0x4f7574206f6620636861722072616e6765) }
end
def test_compatible
@@ -1325,15 +1553,162 @@ class TestM17N < Test::Unit::TestCase
end
def test_force_encoding
- assert(("".center(1, "\x80".force_encoding("utf-8")); true),
- "moved from btest/knownbug, [ruby-dev:33807]")
+ assert_equal(u("\x80"), "".center(1, u("\x80")),
+ "moved from btest/knownbug, [ruby-dev:33807]")
a = "".force_encoding("ascii-8bit") << 0xC3 << 0xB6
assert_equal(1, a.force_encoding("utf-8").size, '[ruby-core:22437]')
b = "".force_encoding("ascii-8bit") << 0xC3.chr << 0xB6.chr
assert_equal(1, b.force_encoding("utf-8").size, '[ruby-core:22437]')
+
+ assert_raise(TypeError){ ''.force_encoding(nil) }
end
def test_combchar_codepoint
assert_equal([0x30BB, 0x309A], "\u30BB\u309A".codepoints.to_a)
+ assert_equal([0x30BB, 0x309A], "\u30BB\u309A".codepoints.to_a)
+ end
+
+ def each_encoding(*strings)
+ Encoding.list.each do |enc|
+ next if enc.dummy?
+ strs = strings.map {|s| s.encode(enc)} rescue next
+ yield(*strs)
+ end
+ end
+
+ def test_str_b
+ s = "\u3042"
+ assert_equal(a("\xE3\x81\x82"), s.b)
+ assert_equal(Encoding::ASCII_8BIT, s.b.encoding)
+ s.taint
+ assert_predicate(s.b, :tainted?)
+ s = "abc".b
+ assert_predicate(s.b, :ascii_only?)
+ end
+
+ def test_scrub_valid_string
+ str = "foo"
+ assert_equal(str, str.scrub)
+ assert_not_same(str, str.scrub)
+ assert_predicate(str.dup.taint.scrub, :tainted?)
+ str = "\u3042\u3044"
+ assert_equal(str, str.scrub)
+ assert_not_same(str, str.scrub)
+ assert_predicate(str.dup.taint.scrub, :tainted?)
+ str.force_encoding(Encoding::ISO_2022_JP) # dummy encoding
+ assert_equal(str, str.scrub)
+ assert_not_same(str, str.scrub)
+ assert_nothing_raised(ArgumentError) {str.scrub(nil)}
+ assert_predicate(str.dup.taint.scrub, :tainted?)
+ end
+
+ def test_scrub_replace_default
+ assert_equal("\uFFFD\uFFFD\uFFFD", u("\x80\x80\x80").scrub)
+ assert_equal("\uFFFDA", u("\xF4\x80\x80A").scrub)
+ assert_predicate(u("\x80\x80\x80").taint.scrub, :tainted?)
+ assert_predicate(u("\xF4\x80\x80A").taint.scrub, :tainted?)
+
+ # examples in Unicode 6.1.0 D93b
+ assert_equal("\x41\uFFFD\uFFFD\x41\uFFFD\x41",
+ u("\x41\xC0\xAF\x41\xF4\x80\x80\x41").scrub)
+ assert_equal("\x41\uFFFD\uFFFD\uFFFD\x41",
+ u("\x41\xE0\x9F\x80\x41").scrub)
+ assert_equal("\u0061\uFFFD\uFFFD\uFFFD\u0062\uFFFD\u0063\uFFFD\uFFFD\u0064",
+ u("\x61\xF1\x80\x80\xE1\x80\xC2\x62\x80\x63\x80\xBF\x64").scrub)
+ assert_equal("abcdefghijklmnopqrstuvwxyz\u0061\uFFFD\uFFFD\uFFFD\u0062\uFFFD\u0063\uFFFD\uFFFD\u0064",
+ u("abcdefghijklmnopqrstuvwxyz\x61\xF1\x80\x80\xE1\x80\xC2\x62\x80\x63\x80\xBF\x64").scrub)
+ end
+
+ def test_scrub_replace_argument
+ assert_equal("foo", u("foo").scrub("\u3013"))
+ assert_predicate(u("foo").taint.scrub("\u3013"), :tainted?)
+ assert_not_predicate(u("foo").scrub("\u3013".taint), :tainted?)
+ assert_equal("\u3042\u3044", u("\xE3\x81\x82\xE3\x81\x84").scrub("\u3013"))
+ assert_predicate(u("\xE3\x81\x82\xE3\x81\x84").taint.scrub("\u3013"), :tainted?)
+ assert_not_predicate(u("\xE3\x81\x82\xE3\x81\x84").scrub("\u3013".taint), :tainted?)
+ assert_equal("\u3042\u3013", u("\xE3\x81\x82\xE3\x81").scrub("\u3013"))
+ assert_predicate(u("\xE3\x81\x82\xE3\x81").taint.scrub("\u3013"), :tainted?)
+ assert_predicate(u("\xE3\x81\x82\xE3\x81").scrub("\u3013".taint), :tainted?)
+ assert_raise(Encoding::CompatibilityError){ u("\xE3\x81\x82\xE3\x81").scrub(e("\xA4\xA2")) }
+ assert_raise(TypeError){ u("\xE3\x81\x82\xE3\x81").scrub(1) }
+ assert_raise(ArgumentError){ u("\xE3\x81\x82\xE3\x81\x82\xE3\x81").scrub(u("\x81")) }
+ assert_equal(e("\xA4\xA2\xA2\xAE"), e("\xA4\xA2\xA4").scrub(e("\xA2\xAE")))
+ end
+
+ def test_scrub_replace_block
+ assert_equal("\u3042<e381>", u("\xE3\x81\x82\xE3\x81").scrub{|x|'<'+x.unpack('H*')[0]+'>'})
+ assert_predicate(u("\xE3\x81\x82\xE3\x81").taint.scrub{|x|'<'+x.unpack('H*')[0]+'>'}, :tainted?)
+ assert_predicate(u("\xE3\x81\x82\xE3\x81").scrub{|x|('<'+x.unpack('H*')[0]+'>').taint}, :tainted?)
+ assert_raise(Encoding::CompatibilityError){ u("\xE3\x81\x82\xE3\x81").scrub{e("\xA4\xA2")} }
+ assert_raise(TypeError){ u("\xE3\x81\x82\xE3\x81").scrub{1} }
+ assert_raise(ArgumentError){ u("\xE3\x81\x82\xE3\x81\x82\xE3\x81").scrub{u("\x81")} }
+ assert_equal(e("\xA4\xA2\xA2\xAE"), e("\xA4\xA2\xA4").scrub{e("\xA2\xAE")})
+
+ assert_equal(u("\x81"), u("a\x81c").scrub {|c| break c})
+ assert_raise(ArgumentError) {u("a\x81").scrub {|c| c}}
+ assert_raise(ArgumentError) {u("a").scrub("?") {|c| c}}
+ end
+
+ def test_scrub_widechar
+ assert_equal("\uFFFD\u3042".encode("UTF-16BE"),
+ "\xD8\x00\x30\x42".force_encoding(Encoding::UTF_16BE).
+ scrub)
+ assert_equal("\uFFFD\u3042".encode("UTF-16LE"),
+ "\x00\xD8\x42\x30".force_encoding(Encoding::UTF_16LE).
+ scrub)
+ assert_equal("\uFFFD".encode("UTF-32BE"),
+ "\xff".force_encoding(Encoding::UTF_32BE).
+ scrub)
+ assert_equal("\uFFFD".encode("UTF-32LE"),
+ "\xff".force_encoding(Encoding::UTF_32LE).
+ scrub)
+ c = nil
+ assert_equal("?\u3042".encode(Encoding::UTF_16LE),
+ "\x00\xD8\x42\x30".force_encoding(Encoding::UTF_16LE).
+ scrub {|e| c = e; "?".encode(Encoding::UTF_16LE)})
+ assert_equal("\x00\xD8".force_encoding(Encoding::UTF_16LE), c)
+ assert_raise(ArgumentError) {"\uFFFD\u3042".encode("UTF-16BE").scrub("") {}}
+ end
+
+ def test_scrub_dummy_encoding
+ s = "\u{3042}".encode("iso-2022-jp")
+ assert_equal(s, s.scrub)
+ assert_equal(s, s.force_encoding("iso-2022-jp").scrub("?"))
+ end
+
+ def test_scrub_bang
+ str = "\u3042\u3044"
+ assert_same(str, str.scrub!)
+ str.force_encoding(Encoding::ISO_2022_JP) # dummy encoding
+ assert_same(str, str.scrub!)
+ assert_nothing_raised(ArgumentError) {str.scrub!(nil)}
+
+ str = u("\x80\x80\x80")
+ str.scrub!
+ assert_same(str, str.scrub!)
+ assert_equal("\uFFFD\uFFFD\uFFFD", str)
+ end
+
+ def test_escaped_metachar
+ bug10670 = '[ruby-core:67193] [Bug #10670]'
+
+ escape_plain = /\A[\x5B]*\z/.freeze
+
+ assert_match(escape_plain, 0x5b.chr(::Encoding::UTF_8), bug10670)
+ assert_match(escape_plain, 0x5b.chr, bug10670)
+ end
+
+ def test_inspect_with_default_internal
+ bug11787 = '[ruby-dev:49415] [Bug #11787]'
+
+ s = EnvUtil.with_default_internal(::Encoding::EUC_JP) do
+ [e("\xB4\xC1\xBB\xFA")].inspect
+ end
+ assert_equal(e("[\"\xB4\xC1\xBB\xFA\"]"), s, bug11787)
+ end
+
+ def test_greek_capital_gap
+ bug12204 = '[ruby-core:74478] [Bug #12204] GREEK CAPITAL RHO and SIGMA'
+ assert_equal("\u03A3", "\u03A1".succ, bug12204)
end
end
diff --git a/test/ruby/test_m17n_comb.rb b/test/ruby/test_m17n_comb.rb
index cf80377172..99c162a92f 100644
--- a/test/ruby/test_m17n_comb.rb
+++ b/test/ruby/test_m17n_comb.rb
@@ -1,5 +1,6 @@
+# frozen_string_literal: false
require 'test/unit'
-require 'stringio'
+require 'etc'
require_relative 'allpairs'
class TestM17NComb < Test::Unit::TestCase
@@ -8,10 +9,11 @@ class TestM17NComb < Test::Unit::TestCase
end
module AESU
- def a(str) str.dup.force_encoding("ASCII-8BIT") end
- def e(str) str.dup.force_encoding("EUC-JP") end
- def s(str) str.dup.force_encoding("Shift_JIS") end
- def u(str) str.dup.force_encoding("UTF-8") end
+ def a(str) str.dup.force_encoding(Encoding::US_ASCII) end
+ def b(str) str.b end
+ def e(str) str.dup.force_encoding(Encoding::EUC_JP) end
+ def s(str) str.dup.force_encoding(Encoding::SJIS) end
+ def u(str) str.dup.force_encoding(Encoding::UTF_8) end
end
include AESU
extend AESU
@@ -20,72 +22,16 @@ class TestM17NComb < Test::Unit::TestCase
assert_instance_of(String, actual, message)
enc = Encoding.find(enc) if String === enc
assert_equal(enc, actual.encoding, message)
- assert_equal(a(bytes), a(actual), message)
- end
-
- def assert_warning(pat, mesg=nil)
- begin
- org_stderr = $stderr
- $stderr = StringIO.new(warn = '')
- yield
- ensure
- $stderr = org_stderr
- end
- assert_match(pat, warn, mesg)
- end
-
- def assert_regexp_generic_encoding(r)
- assert(!r.fixed_encoding?)
- %w[ASCII-8BIT EUC-JP Shift_JIS UTF-8].each {|ename|
- # "\xc2\xa1" is a valid sequence for ASCII-8BIT, EUC-JP, Shift_JIS and UTF-8.
- assert_nothing_raised { r =~ "\xc2\xa1".force_encoding(ename) }
- }
- end
-
- def assert_regexp_fixed_encoding(r)
- assert(r.fixed_encoding?)
- %w[ASCII-8BIT EUC-JP Shift_JIS UTF-8].each {|ename|
- enc = Encoding.find(ename)
- if enc == r.encoding
- assert_nothing_raised { r =~ "\xc2\xa1".force_encoding(enc) }
- else
- assert_raise(ArgumentError) { r =~ "\xc2\xa1".force_encoding(enc) }
- end
- }
- end
-
- def assert_regexp_generic_ascii(r)
- assert_encoding("ASCII-8BIT", r.encoding)
- assert_regexp_generic_encoding(r)
- end
-
- def assert_regexp_fixed_ascii8bit(r)
- assert_encoding("ASCII-8BIT", r.encoding)
- assert_regexp_fixed_encoding(r)
- end
-
- def assert_regexp_fixed_eucjp(r)
- assert_encoding("EUC-JP", r.encoding)
- assert_regexp_fixed_encoding(r)
- end
-
- def assert_regexp_fixed_sjis(r)
- assert_encoding("Shift_JIS", r.encoding)
- assert_regexp_fixed_encoding(r)
- end
-
- def assert_regexp_fixed_utf8(r)
- assert_encoding("UTF-8", r.encoding)
- assert_regexp_fixed_encoding(r)
+ assert_equal(b(bytes), b(actual), message)
end
STRINGS = [
- a(""), e(""), s(""), u(""),
- a("a"), e("a"), s("a"), u("a"),
- a("."), e("."), s("."), u("."),
+ b(""), e(""), s(""), u(""),
+ b("a"), e("a"), s("a"), u("a"),
+ b("."), e("."), s("."), u("."),
# single character
- a("\x80"), a("\xff"),
+ b("\x80"), b("\xff"),
e("\xa1\xa1"), e("\xfe\xfe"),
e("\x8e\xa1"), e("\x8e\xfe"),
e("\x8f\xa1\xa1"), e("\x8f\xfe\xfe"),
@@ -94,7 +40,7 @@ class TestM17NComb < Test::Unit::TestCase
u("\xc2\x80"), u("\xf4\x8f\xbf\xbf"),
# same byte sequence
- a("\xc2\xa1"), e("\xc2\xa1"), s("\xc2\xa1"), u("\xc2\xa1"),
+ b("\xc2\xa1"), e("\xc2\xa1"), s("\xc2\xa1"), u("\xc2\xa1"),
s("\x81A"), # mutibyte character which contains "A"
s("\x81a"), # mutibyte character which contains "a"
@@ -106,11 +52,13 @@ class TestM17NComb < Test::Unit::TestCase
# for transitivity test
u("\xe0\xa0\xa1"), e("\xe0\xa0\xa1"), s("\xe0\xa0\xa1"), # [ruby-dev:32693]
- e("\xa1\xa1"), a("\xa1\xa1"), s("\xa1\xa1"), # [ruby-dev:36484]
+ e("\xa1\xa1"), b("\xa1\xa1"), s("\xa1\xa1"), # [ruby-dev:36484]
+ ]
- #"aa".force_encoding("utf-16be"),
- #"aaaa".force_encoding("utf-32be"),
- #"aaa".force_encoding("utf-32be"),
+ WSTRINGS = [
+ "aa".force_encoding("utf-16be"),
+ "aaaa".force_encoding("utf-32be"),
+ "aaa".force_encoding("utf-32be"),
]
def combination(*args, &b)
@@ -141,7 +89,7 @@ class TestM17NComb < Test::Unit::TestCase
r
end
- def enccall(recv, meth, *args, &block)
+ def encdumpcall(recv, meth, *args, &block)
desc = ''
if String === recv
desc << encdump(recv)
@@ -164,12 +112,18 @@ class TestM17NComb < Test::Unit::TestCase
if block
desc << ' {}'
end
+ desc
+ end
+
+ def assert_enccall(recv, meth, *args, &block)
+ desc = encdumpcall(recv, meth, *args, &block)
result = nil
assert_nothing_raised(desc) {
result = recv.send(meth, *args, &block)
}
result
end
+ alias enccall assert_enccall
def assert_str_enc_propagation(t, s1, s2)
if !s1.ascii_only?
@@ -177,7 +131,7 @@ class TestM17NComb < Test::Unit::TestCase
elsif !s2.ascii_only?
assert_equal(s2.encoding, t.encoding)
else
- assert([s1.encoding, s2.encoding].include?(t.encoding))
+ assert_include([s1.encoding, s2.encoding], t.encoding)
end
end
@@ -254,7 +208,7 @@ class TestM17NComb < Test::Unit::TestCase
def test_str_new
STRINGS.each {|s|
t = String.new(s)
- assert_strenc(a(s), s.encoding, t)
+ assert_strenc(b(s), s.encoding, t)
}
end
@@ -264,8 +218,8 @@ class TestM17NComb < Test::Unit::TestCase
assert_raise(Encoding::CompatibilityError) { s1 + s2 }
else
t = enccall(s1, :+, s2)
- assert(t.valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
- assert_equal(a(s1) + a(s2), a(t))
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_equal(b(s1) + b(s2), b(t))
assert_str_enc_propagation(t, s1, s2)
end
}
@@ -275,34 +229,34 @@ class TestM17NComb < Test::Unit::TestCase
STRINGS.each {|s|
[0,1,2].each {|n|
t = s * n
- assert(t.valid_encoding?) if s.valid_encoding?
- assert_strenc(a(s) * n, s.encoding, t)
+ assert_predicate(t, :valid_encoding?) if s.valid_encoding?
+ assert_strenc(b(s) * n, s.encoding, t)
}
}
end
def test_sprintf_s
STRINGS.each {|s|
- assert_strenc(a(s), s.encoding, "%s".force_encoding(s.encoding) % s)
+ assert_strenc(b(s), s.encoding, "%s".force_encoding(s.encoding) % s)
if !s.empty? # xxx
- t = enccall(a("%s"), :%, s)
- assert_strenc(a(s), s.encoding, t)
+ t = enccall(b("%s"), :%, s)
+ assert_strenc(b(s), (b('')+s).encoding, t)
end
}
end
def test_str_eq_reflexive
STRINGS.each {|s|
- assert(s == s, "#{encdump s} == #{encdump s}")
+ assert_equal(s, s, "#{encdump s} == #{encdump s}")
}
end
def test_str_eq_symmetric
combination(STRINGS, STRINGS) {|s1, s2|
if s1 == s2
- assert(s2 == s1, "#{encdump s2} == #{encdump s1}")
+ assert_equal(s2, s1, "#{encdump s2} == #{encdump s1}")
else
- assert(!(s2 == s1), "!(#{encdump s2} == #{encdump s1})")
+ assert_not_equal(s2, s1, "!(#{encdump s2} == #{encdump s1})")
end
}
end
@@ -310,7 +264,7 @@ class TestM17NComb < Test::Unit::TestCase
def test_str_eq_transitive
combination(STRINGS, STRINGS, STRINGS) {|s1, s2, s3|
if s1 == s2 && s2 == s3
- assert(s1 == s3, "transitive: #{encdump s1} == #{encdump s2} == #{encdump s3}")
+ assert_equal(s1, s3, "transitive: #{encdump s1} == #{encdump s2} == #{encdump s3}")
end
}
end
@@ -318,18 +272,18 @@ class TestM17NComb < Test::Unit::TestCase
def test_str_eq
combination(STRINGS, STRINGS) {|s1, s2|
desc_eq = "#{encdump s1} == #{encdump s2}"
- if a(s1) == a(s2) and
+ if b(s1) == b(s2) and
(s1.ascii_only? && s2.ascii_only? or
s1.encoding == s2.encoding) then
- assert(s1 == s2, desc_eq)
- assert(!(s1 != s2))
+ assert_operator(s1, :==, s2, desc_eq)
+ assert_not_operator(s1, :!=, s2)
assert_equal(0, s1 <=> s2)
- assert(s1.eql?(s2), desc_eq)
+ assert_operator(s1, :eql?, s2, desc_eq)
else
- assert(!(s1 == s2), "!(#{desc_eq})")
- assert(s1 != s2)
+ assert_not_operator(s1, :==, s2, "!(#{desc_eq})")
+ assert_operator(s1, :!=, s2)
assert_not_equal(0, s1 <=> s2)
- assert(!s1.eql?(s2))
+ assert_not_operator(s1, :eql?, s2)
end
}
end
@@ -339,8 +293,8 @@ class TestM17NComb < Test::Unit::TestCase
s = s1.dup
if s1.ascii_only? || s2.ascii_only? || s1.encoding == s2.encoding
s << s2
- assert(s.valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
- assert_equal(a(s), a(s1) + a(s2))
+ assert_predicate(s, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_equal(b(s), b(s1) + b(s2))
assert_str_enc_propagation(s, s1, s2)
else
assert_raise(Encoding::CompatibilityError) { s << s2 }
@@ -353,7 +307,7 @@ class TestM17NComb < Test::Unit::TestCase
t = ''.force_encoding(s.encoding)
0.upto(s.length-1) {|i|
u = s[i]
- assert(u.valid_encoding?) if s.valid_encoding?
+ assert_predicate(u, :valid_encoding?) if s.valid_encoding?
t << u
}
assert_equal(t, s)
@@ -365,7 +319,7 @@ class TestM17NComb < Test::Unit::TestCase
t = ''.force_encoding(s.encoding)
0.upto(s.length-1) {|i|
u = s[i,1]
- assert(u.valid_encoding?) if s.valid_encoding?
+ assert_predicate(u, :valid_encoding?) if s.valid_encoding?
t << u
}
assert_equal(t, s)
@@ -375,7 +329,7 @@ class TestM17NComb < Test::Unit::TestCase
t = ''.force_encoding(s.encoding)
0.step(s.length-1, 2) {|i|
u = s[i,2]
- assert(u.valid_encoding?) if s.valid_encoding?
+ assert_predicate(u, :valid_encoding?) if s.valid_encoding?
t << u
}
assert_equal(t, s)
@@ -387,9 +341,9 @@ class TestM17NComb < Test::Unit::TestCase
if s1.ascii_only? || s2.ascii_only? || s1.encoding == s2.encoding
t = enccall(s1, :[], s2)
if t != nil
- assert(t.valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
assert_equal(s2, t)
- assert_match(/#{Regexp.escape(a(s2))}/, a(s1))
+ assert_match(/#{Regexp.escape(b(s2))}/, b(s1))
if s1.valid_encoding?
assert_match(/#{Regexp.escape(s2)}/, s1)
end
@@ -415,7 +369,7 @@ class TestM17NComb < Test::Unit::TestCase
assert_nil(t, desc)
next
end
- assert(t.valid_encoding?) if s.valid_encoding?
+ assert_predicate(t, :valid_encoding?) if s.valid_encoding?
if last < 0
last += s.length
end
@@ -446,7 +400,7 @@ class TestM17NComb < Test::Unit::TestCase
if last < 0
last += s.length
end
- assert(t.valid_encoding?) if s.valid_encoding?
+ assert_predicate(t, :valid_encoding?) if s.valid_encoding?
t2 = ''
first.upto(last-1) {|i|
c = s[i]
@@ -465,8 +419,8 @@ class TestM17NComb < Test::Unit::TestCase
assert_raise(IndexError) { t[i] = s2 }
else
t[i] = s2
- assert(t.valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
- assert(a(t).index(a(s2)))
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_send([b(t), :index, b(s2)])
if s1.valid_encoding? && s2.valid_encoding?
if i == s1.length && s2.empty?
assert_nil(t[i])
@@ -493,9 +447,9 @@ class TestM17NComb < Test::Unit::TestCase
if i < -s1.length || s1.length < i
assert_raise(IndexError) { t[i,len] = s2 }
else
- assert(t.valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
t[i,len] = s2
- assert(a(t).index(a(s2)))
+ assert_send([b(t), :index, b(s2)])
if s1.valid_encoding? && s2.valid_encoding?
if i == s1.length && s2.empty?
assert_nil(t[i])
@@ -539,7 +493,7 @@ class TestM17NComb < Test::Unit::TestCase
if !t[s2]
else
enccall(t, :[]=, s2, s3)
- assert(t.valid_encoding?) if s1.valid_encoding? && s2.valid_encoding? && s3.valid_encoding?
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding? && s3.valid_encoding?
end
end
}
@@ -553,8 +507,8 @@ class TestM17NComb < Test::Unit::TestCase
assert_raise(RangeError) { t[first..last] = s2 }
else
enccall(t, :[]=, first..last, s2)
- assert(t.valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
- assert(a(t).index(a(s2)))
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_send([b(t), :index, b(s2)])
if s1.valid_encoding? && s2.valid_encoding?
if first < 0
assert_equal(s2, t[s1.length+first, s2.length])
@@ -580,8 +534,8 @@ class TestM17NComb < Test::Unit::TestCase
assert_raise(RangeError) { t[first...last] = s2 }
else
enccall(t, :[]=, first...last, s2)
- assert(t.valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
- assert(a(t).index(a(s2)))
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_send([b(t), :index, b(s2)])
if s1.valid_encoding? && s2.valid_encoding?
if first < 0
assert_equal(s2, t[s1.length+first, s2.length])
@@ -616,16 +570,16 @@ class TestM17NComb < Test::Unit::TestCase
begin
t1 = s.capitalize
rescue ArgumentError
- assert(!s.valid_encoding?)
+ assert_not_predicate(s, :valid_encoding?)
next
end
- assert(t1.valid_encoding?) if s.valid_encoding?
- assert(t1.casecmp(s))
+ assert_predicate(t1, :valid_encoding?) if s.valid_encoding?
+ assert_operator(t1, :casecmp, s)
t2 = s.dup
t2.capitalize!
assert_equal(t1, t2)
r = s.downcase
- r = enccall(r, :sub, /\A[a-z]/) {|ch| a(ch).upcase }
+ r = enccall(r, :sub, /\A[a-z]/) {|ch| b(ch).upcase }
assert_equal(r, t1)
}
end
@@ -633,20 +587,16 @@ class TestM17NComb < Test::Unit::TestCase
def test_str_casecmp
combination(STRINGS, STRINGS) {|s1, s2|
#puts "#{encdump(s1)}.casecmp(#{encdump(s2)})"
- begin
- r = s1.casecmp(s2)
- rescue ArgumentError
- assert(!s1.valid_encoding? || !s2.valid_encoding?)
- next
- end
- #assert_equal(s1.upcase <=> s2.upcase, r)
+ next unless s1.valid_encoding? && s2.valid_encoding? && Encoding.compatible?(s1, s2)
+ r = s1.casecmp(s2)
+ assert_equal(s1.upcase <=> s2.upcase, r)
}
end
def test_str_center
combination(STRINGS, [0,1,2,3,10]) {|s1, width|
t = s1.center(width)
- assert(a(t).index(a(s1)))
+ assert_send([b(t), :index, b(s1)])
}
combination(STRINGS, [0,1,2,3,10], STRINGS) {|s1, width, s2|
if s2.empty?
@@ -658,8 +608,8 @@ class TestM17NComb < Test::Unit::TestCase
next
end
t = enccall(s1, :center, width, s2)
- assert(t.valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
- assert(a(t).index(a(s1)))
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_send([b(t), :index, b(s1)])
assert_str_enc_propagation(t, s1, s2) if (t != s1)
}
end
@@ -667,7 +617,7 @@ class TestM17NComb < Test::Unit::TestCase
def test_str_ljust
combination(STRINGS, [0,1,2,3,10]) {|s1, width|
t = s1.ljust(width)
- assert(a(t).index(a(s1)))
+ assert_send([b(t), :index, b(s1)])
}
combination(STRINGS, [0,1,2,3,10], STRINGS) {|s1, width, s2|
if s2.empty?
@@ -679,8 +629,8 @@ class TestM17NComb < Test::Unit::TestCase
next
end
t = enccall(s1, :ljust, width, s2)
- assert(t.valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
- assert(a(t).index(a(s1)))
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_send([b(t), :index, b(s1)])
assert_str_enc_propagation(t, s1, s2) if (t != s1)
}
end
@@ -688,7 +638,7 @@ class TestM17NComb < Test::Unit::TestCase
def test_str_rjust
combination(STRINGS, [0,1,2,3,10]) {|s1, width|
t = s1.rjust(width)
- assert(a(t).index(a(s1)))
+ assert_send([b(t), :index, b(s1)])
}
combination(STRINGS, [0,1,2,3,10], STRINGS) {|s1, width, s2|
if s2.empty?
@@ -700,8 +650,8 @@ class TestM17NComb < Test::Unit::TestCase
next
end
t = enccall(s1, :rjust, width, s2)
- assert(t.valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
- assert(a(t).index(a(s1)))
+ assert_predicate(t, :valid_encoding?) if s1.valid_encoding? && s2.valid_encoding?
+ assert_send([b(t), :index, b(s1)])
assert_str_enc_propagation(t, s1, s2) if (t != s1)
}
end
@@ -710,12 +660,14 @@ class TestM17NComb < Test::Unit::TestCase
combination(STRINGS, STRINGS) {|s1, s2|
if !s1.ascii_only? && !s2.ascii_only? && !Encoding.compatible?(s1,s2)
if s1.bytesize > s2.bytesize
- assert_raise(Encoding::CompatibilityError) { s1.chomp(s2) }
+ assert_raise(Encoding::CompatibilityError, "#{encdump(s1)}.chomp(#{encdump(s2)})") do
+ s1.chomp(s2)
+ end
end
next
end
t = enccall(s1, :chomp, s2)
- assert(t.valid_encoding?, "#{encdump(s1)}.chomp(#{encdump(s2)})") if s1.valid_encoding? && s2.valid_encoding?
+ assert_predicate(t, :valid_encoding?, "#{encdump(s1)}.chomp(#{encdump(s2)})") if s1.valid_encoding? && s2.valid_encoding?
assert_equal(s1.encoding, t.encoding)
t2 = s1.dup
t2.chomp!(s2)
@@ -723,14 +675,25 @@ class TestM17NComb < Test::Unit::TestCase
}
end
+ def test_str_smart_chomp
+ bug10893 = '[ruby-core:68258] [Bug #10893]'
+ encodings = Encoding.list.select {|enc| !enc.dummy?}
+ combination(encodings, encodings) do |e1, e2|
+ expected = "abc".encode(e1)
+ combination(["abc\n", "abc\r\n"], ["", "\n"]) do |str, rs|
+ assert_equal(expected, str.encode(e1).chomp(rs.encode(e2)), bug10893)
+ end
+ end
+ end
+
def test_str_chop
STRINGS.each {|s|
s = s.dup
desc = "#{encdump s}.chop"
t = nil
assert_nothing_raised(desc) { t = s.chop }
- assert(t.valid_encoding?) if s.valid_encoding?
- assert(a(s).index(a(t)))
+ assert_predicate(t, :valid_encoding?) if s.valid_encoding?
+ assert_send([b(s), :index, b(t)])
t2 = s.dup
t2.chop!
assert_equal(t, t2)
@@ -741,8 +704,8 @@ class TestM17NComb < Test::Unit::TestCase
STRINGS.each {|s|
t = s.dup
t.clear
- assert(t.valid_encoding?)
- assert(t.empty?)
+ assert_predicate(t, :valid_encoding?)
+ assert_empty(t)
}
end
@@ -751,7 +714,7 @@ class TestM17NComb < Test::Unit::TestCase
t = s.clone
assert_equal(s, t)
assert_equal(s.encoding, t.encoding)
- assert_equal(a(s), a(t))
+ assert_equal(b(s), b(t))
}
end
@@ -760,38 +723,64 @@ class TestM17NComb < Test::Unit::TestCase
t = s.dup
assert_equal(s, t)
assert_equal(s.encoding, t.encoding)
- assert_equal(a(s), a(t))
+ assert_equal(b(s), b(t))
}
end
def test_str_count
combination(STRINGS, STRINGS) {|s1, s2|
+ desc = proc {encdumpcall(s1, :count, s2)}
if !s1.valid_encoding? || !s2.valid_encoding?
- assert_raise(ArgumentError, Encoding::CompatibilityError) { s1.count(s2) }
+ assert_raise(ArgumentError, Encoding::CompatibilityError, desc) { s1.count(s2) }
next
end
if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
- assert_raise(Encoding::CompatibilityError) { s1.count(s2) }
+ assert_raise(Encoding::CompatibilityError, desc) { s1.count(s2) }
next
end
n = enccall(s1, :count, s2)
- n0 = a(s1).count(a(s2))
+ n0 = b(s1).count(b(s2))
assert_operator(n, :<=, n0)
}
end
+ # 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
+ end
+
def test_str_crypt
combination(STRINGS, STRINGS) {|str, salt|
- if a(salt).length < 2
- assert_raise(ArgumentError) { str.crypt(salt) }
- next
- end
- t = str.crypt(salt)
- assert_equal(a(str).crypt(a(salt)), t, "#{encdump(str)}.crypt(#{encdump(salt)})")
- assert_encoding('ASCII-8BIT', t.encoding)
+ # skip input other than [0-9A-Za-z./] to confirm strict behavior
+ next unless salt.ascii_only? && /\A[0-9a-zA-Z.\/]+\z/ =~ salt
+
+ confirm_crypt_result(str, salt)
}
end
+ if !strict_crypt
+ def test_str_crypt_nonstrict
+ combination(STRINGS, STRINGS) {|str, salt|
+ # only test input other than [0-9A-Za-z./] to confirm non-strict behavior
+ next if salt.ascii_only? && /\A[0-9a-zA-Z.\/]+\z/ =~ salt
+
+ confirm_crypt_result(str, salt)
+ }
+ end
+ end
+
+ private def confirm_crypt_result(str, salt)
+ if b(salt).length < 2
+ assert_raise(ArgumentError) { str.crypt(salt) }
+ return
+ end
+ t = str.crypt(salt)
+ assert_equal(b(str).crypt(b(salt)), t, "#{encdump(str)}.crypt(#{encdump(salt)})")
+ assert_encoding('ASCII-8BIT', t.encoding)
+ end
+
def test_str_delete
combination(STRINGS, STRINGS) {|s1, s2|
if s1.empty?
@@ -807,7 +796,7 @@ class TestM17NComb < Test::Unit::TestCase
next
end
t = enccall(s1, :delete, s2)
- assert(t.valid_encoding?)
+ assert_predicate(t, :valid_encoding?)
assert_equal(t.encoding, s1.encoding)
assert_operator(t.length, :<=, s1.length)
t2 = s1.dup
@@ -819,13 +808,13 @@ class TestM17NComb < Test::Unit::TestCase
def test_str_downcase
STRINGS.each {|s|
if !s.valid_encoding?
- assert_raise(ArgumentError) { s.downcase }
+ assert_raise(ArgumentError, "Offending string: #{s.inspect}, encoding: #{s.encoding}") { s.downcase }
next
end
t = s.downcase
- assert(t.valid_encoding?)
+ assert_predicate(t, :valid_encoding?)
assert_equal(t.encoding, s.encoding)
- assert(t.casecmp(s))
+ assert_operator(t, :casecmp, s)
t2 = s.dup
t2.downcase!
assert_equal(t, t2)
@@ -835,26 +824,21 @@ class TestM17NComb < Test::Unit::TestCase
def test_str_dump
STRINGS.each {|s|
t = s.dump
- assert(t.valid_encoding?)
- assert(t.ascii_only?)
+ assert_predicate(t, :valid_encoding?)
+ assert_predicate(t, :ascii_only?)
u = eval(t)
- assert_equal(a(s), a(u))
+ assert_equal(b(s), b(u))
}
end
def test_str_each_line
combination(STRINGS, STRINGS) {|s1, s2|
- if !s1.valid_encoding? || !s2.valid_encoding?
- assert_raise(ArgumentError, Encoding::CompatibilityError) { s1.each_line(s2) {} }
- next
- end
if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
assert_raise(Encoding::CompatibilityError) { s1.each_line(s2) {} }
next
end
lines = []
enccall(s1, :each_line, s2) {|line|
- assert(line.valid_encoding?)
assert_equal(s1.encoding, line.encoding)
lines << line
}
@@ -871,7 +855,7 @@ class TestM17NComb < Test::Unit::TestCase
s.each_byte {|b|
bytes << b
}
- a(s).split(//).each_with_index {|ch, i|
+ b(s).split(//).each_with_index {|ch, i|
assert_equal(ch.ord, bytes[i])
}
}
@@ -880,9 +864,9 @@ class TestM17NComb < Test::Unit::TestCase
def test_str_empty?
STRINGS.each {|s|
if s.length == 0
- assert(s.empty?)
+ assert_empty(s)
else
- assert(!s.empty?)
+ assert_not_empty(s)
end
}
end
@@ -890,7 +874,7 @@ class TestM17NComb < Test::Unit::TestCase
def test_str_hex
STRINGS.each {|s|
t = s.hex
- t2 = a(s)[/\A[0-9a-fA-Fx]*/].hex
+ t2 = b(s)[/\A[0-9a-fA-Fx]*/].hex
assert_equal(t2, t)
}
end
@@ -905,12 +889,12 @@ class TestM17NComb < Test::Unit::TestCase
end
t = enccall(s1, :include?, s2)
if t
- assert(a(s1).include?(a(s2)))
- assert(s1.index(s2))
- assert(s1.rindex(s2))
+ assert_include(b(s1), b(s2))
+ assert_send([s1, :index, s2])
+ assert_send([s1, :rindex, s2])
else
- assert(!s1.index(s2))
- assert(!s1.rindex(s2), "!#{encdump(s1)}.rindex(#{encdump(s2)})")
+ assert_not_send([s1, :index, s2])
+ assert_not_send([s1, :rindex, s2], "!#{encdump(s1)}.rindex(#{encdump(s2)})")
end
if s2.empty?
assert_equal(true, t)
@@ -986,7 +970,7 @@ class TestM17NComb < Test::Unit::TestCase
end
if t
#puts "#{encdump s1}.rindex(#{encdump s2}, #{pos}) => #{t}"
- assert(a(s1).index(a(s2)))
+ assert_send([b(s1), :index, b(s2)])
pos2 = pos
pos2 += s1.length if pos < 0
re = /\A(.{0,#{pos2}})#{Regexp.escape(s2)}/m
@@ -1031,14 +1015,14 @@ class TestM17NComb < Test::Unit::TestCase
t1.insert(nth, s2)
slen = s2.length
assert_equal(t1[nth-slen+1,slen], s2, "t=#{encdump s1}; t.insert(#{nth},#{encdump s2}); t")
- rescue Encoding::CompatibilityError, IndexError => e
+ rescue Encoding::CompatibilityError, IndexError
end
}
end
def test_str_intern
STRINGS.each {|s|
- if /\0/ =~ a(s)
+ if /\0/ =~ b(s)
assert_raise(ArgumentError) { s.intern }
elsif s.valid_encoding?
sym = s.intern
@@ -1059,7 +1043,7 @@ class TestM17NComb < Test::Unit::TestCase
def test_str_oct
STRINGS.each {|s|
t = s.oct
- t2 = a(s)[/\A[0-9a-fA-FxX]*/].oct
+ t2 = b(s)[/\A[0-9a-fA-FxX]*/].oct
assert_equal(t2, t)
}
end
@@ -1087,25 +1071,26 @@ class TestM17NComb < Test::Unit::TestCase
def test_str_scan
combination(STRINGS, STRINGS) {|s1, s2|
+ desc = proc {"#{s1.dump}.scan(#{s2.dump})"}
if !s2.valid_encoding?
- assert_raise(RegexpError) { s1.scan(s2) }
+ assert_raise(RegexpError, desc) { s1.scan(s2) }
next
end
if !s1.ascii_only? && !s2.ascii_only? && s1.encoding != s2.encoding
if s1.valid_encoding?
- assert_raise(Encoding::CompatibilityError) { s1.scan(s2) }
+ assert_raise(Encoding::CompatibilityError, desc) { s1.scan(s2) }
else
- assert_raise(ArgumentError, /invalid byte sequence/) { s1.scan(s2) }
+ assert_raise_with_message(ArgumentError, /invalid byte sequence/, desc) { s1.scan(s2) }
end
next
end
if !s1.valid_encoding?
- assert_raise(ArgumentError) { s1.scan(s2) }
+ assert_raise(ArgumentError, desc) { s1.scan(s2) }
next
end
r = enccall(s1, :scan, s2)
r.each {|t|
- assert_equal(s2, t)
+ assert_equal(s2, t, desc)
}
}
end
@@ -1144,8 +1129,8 @@ class TestM17NComb < Test::Unit::TestCase
"#{encdump s}.slice!#{encdumpargs args}.encoding")
end
if [s, *args].all? {|o| !(String === o) || o.valid_encoding? }
- assert(r.valid_encoding?)
- assert(t.valid_encoding?)
+ assert_predicate(r, :valid_encoding?)
+ assert_predicate(t, :valid_encoding?)
assert_equal(s.length, r.length + t.length)
end
}
@@ -1167,13 +1152,13 @@ class TestM17NComb < Test::Unit::TestCase
end
t = enccall(s1, :split, s2)
t.each {|r|
- assert(a(s1).include?(a(r)))
+ assert_include(b(s1), b(r))
assert_equal(s1.encoding, r.encoding)
}
- assert(a(s1).include?(t.map {|u| a(u) }.join(a(s2))))
+ assert_include(b(s1), t.map {|u| b(u) }.join(b(s2)))
if s1.valid_encoding? && s2.valid_encoding?
t.each {|r|
- assert(r.valid_encoding?)
+ assert_predicate(r, :valid_encoding?)
}
end
}
@@ -1224,7 +1209,7 @@ class TestM17NComb < Test::Unit::TestCase
def test_str_sum
STRINGS.each {|s|
- assert_equal(a(s).sum, s.sum)
+ assert_equal(b(s).sum, s.sum)
}
end
@@ -1235,8 +1220,8 @@ class TestM17NComb < Test::Unit::TestCase
next
end
t1 = s.swapcase
- assert(t1.valid_encoding?) if s.valid_encoding?
- assert(t1.casecmp(s))
+ assert_predicate(t1, :valid_encoding?) if s.valid_encoding?
+ assert_operator(t1, :casecmp, s)
t2 = s.dup
t2.swapcase!
assert_equal(t1, t2)
@@ -1297,6 +1282,20 @@ class TestM17NComb < Test::Unit::TestCase
}
end
+ def test_tr_sjis
+ expected = "\x83}\x83~\x83\x80\x83\x81\x83\x82".force_encoding(Encoding::SJIS)
+ source = "\xCF\xD0\xD1\xD2\xD3".force_encoding(Encoding::SJIS)
+ from = "\xCF-\xD3".force_encoding(Encoding::SJIS)
+ to = "\x83}-\x83\x82".force_encoding(Encoding::SJIS)
+ assert_equal(expected, source.tr(from, to))
+
+ expected = "\x84}\x84~\x84\x80\x84\x81\x84\x82".force_encoding(Encoding::SJIS)
+ source = "\x84M\x84N\x84O\x84P\x84Q".force_encoding(Encoding::SJIS)
+ from = "\x84@-\x84`".force_encoding(Encoding::SJIS)
+ to = "\x84p-\x84\x91".force_encoding(Encoding::SJIS)
+ assert_equal(expected, source.tr(from, to))
+ end
+
def test_tr_s
combination(STRINGS, STRINGS, STRINGS) {|s1, s2, s3|
desc = "#{encdump s1}.tr_s(#{encdump s2}, #{encdump s3})"
@@ -1335,8 +1334,8 @@ class TestM17NComb < Test::Unit::TestCase
next
end
t1 = s.upcase
- assert(t1.valid_encoding?)
- assert(t1.casecmp(s))
+ assert_predicate(t1, :valid_encoding?)
+ assert_operator(t1, :casecmp, s)
t2 = s.dup
t2.upcase!
assert_equal(t1, t2)
@@ -1358,11 +1357,24 @@ class TestM17NComb < Test::Unit::TestCase
#puts encdump(s)
t = s.succ
if s.valid_encoding?
- assert(t.valid_encoding?, "#{encdump s}.succ.valid_encoding?")
+ assert_predicate(t, :valid_encoding?, "#{encdump s}.succ.valid_encoding?")
end
s = t
}
}
+
+ Encoding.list.each do |enc|
+ next if enc.dummy?
+ {"A"=>"B", "A1"=>"A2", "A9"=>"B0", "9"=>"10", "Z"=>"AA"}.each do |orig, expected|
+ s = orig.encode(enc)
+ assert_strenc(expected.encode(enc), enc, s.succ, proc {"#{orig.dump}.encode(#{enc}).succ"})
+ end
+ end
+ end
+
+ def test_str_succ2
+ assert_equal(a("\x01\x00"), a("\x7f").succ)
+ assert_equal(b("\x01\x00"), b("\xff").succ)
end
def test_str_hash
@@ -1564,8 +1576,8 @@ class TestM17NComb < Test::Unit::TestCase
assert_raise(Encoding::CompatibilityError, desc) { s1.start_with?(s2) }
next
end
- s1 = s1.dup.force_encoding("ASCII-8BIT")
- s2 = s2.dup.force_encoding("ASCII-8BIT")
+ s1 = s1.b
+ s2 = s2.b
if s1.length < s2.length
assert_equal(false, enccall(s1, :start_with?, s2), desc)
next
@@ -1624,4 +1636,9 @@ class TestM17NComb < Test::Unit::TestCase
}
end
+ def test_bug11486
+ bug11486 = '[Bug #11486]'
+ assert_nil ("\u3042"*19+"\r"*19+"\u3042"*20+"\r"*20).encode(Encoding::EUC_JP).gsub!(/xxx/i, ""), bug11486
+ assert_match Regexp.new("ABC\uff41".encode(Encoding::EUC_JP), Regexp::IGNORECASE), "abc\uFF21".encode(Encoding::EUC_JP), bug11486
+ end
end
diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb
index d78183192c..f6d84d181a 100644
--- a/test/ruby/test_marshal.rb
+++ b/test/ruby/test_marshal.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
require 'tempfile'
require_relative 'marshaltestlib'
@@ -40,6 +41,11 @@ class TestMarshal < Test::Unit::TestCase
obj = (x.to_f + y.to_f / z.to_f) * Math.exp(w.to_f / (x.to_f + y.to_f / z.to_f))
assert_equal obj, Marshal.load(Marshal.dump(obj))
}
+
+ bug3659 = '[ruby-dev:41936]'
+ [1.0, 10.0, 100.0, 110.0].each {|x|
+ assert_equal(x, Marshal.load(Marshal.dump(x)), bug3659)
+ }
end
StrClone = String.clone
@@ -57,8 +63,8 @@ class TestMarshal < Test::Unit::TestCase
def test_struct_invalid_members
TestMarshal.const_set :StructInvalidMembers, Struct.new(:a)
- Marshal.load("\004\bIc&TestMarshal::StructInvalidMembers\006:\020__members__\"\bfoo")
assert_raise(TypeError, "[ruby-dev:31759]") {
+ Marshal.load("\004\bIc&TestMarshal::StructInvalidMembers\006:\020__members__\"\bfoo")
TestMarshal::StructInvalidMembers.members
}
end
@@ -79,10 +85,9 @@ class TestMarshal < Test::Unit::TestCase
def test_too_long_string
data = Marshal.dump(C.new("a".force_encoding("ascii-8bit")))
data[-2, 1] = "\003\377\377\377"
- e = assert_raise(ArgumentError, "[ruby-dev:32054]") {
+ assert_raise_with_message(ArgumentError, "marshal data too short", "[ruby-dev:32054]") {
Marshal.load(data)
}
- assert_equal("marshal data too short", e.message)
end
@@ -98,17 +103,19 @@ class TestMarshal < Test::Unit::TestCase
def test_pipe
o1 = C.new("a" * 10000)
- r, w = IO.pipe
- t = Thread.new { Marshal.load(r) }
- Marshal.dump(o1, w)
- o2 = t.value
- assert_equal(o1.str, o2.str)
+ IO.pipe do |r, w|
+ th = Thread.new {Marshal.dump(o1, w)}
+ o2 = Marshal.load(r)
+ th.join
+ assert_equal(o1.str, o2.str)
+ end
- r, w = IO.pipe
- t = Thread.new { Marshal.load(r) }
- Marshal.dump(o1, w, 2)
- o2 = t.value
- assert_equal(o1.str, o2.str)
+ IO.pipe do |r, w|
+ th = Thread.new {Marshal.dump(o1, w, 2)}
+ o2 = Marshal.load(r)
+ th.join
+ assert_equal(o1.str, o2.str)
+ end
assert_raise(TypeError) { Marshal.dump("foo", Object.new) }
assert_raise(TypeError) { Marshal.load(Object.new) }
@@ -182,83 +189,58 @@ class TestMarshal < Test::Unit::TestCase
end
end
- def test_taint_and_untrust
+ def test_taint
x = Object.new
x.taint
- x.untrust
s = Marshal.dump(x)
assert_equal(true, s.tainted?)
- assert_equal(true, s.untrusted?)
y = Marshal.load(s)
assert_equal(true, y.tainted?)
- assert_equal(true, y.untrusted?)
end
- def test_taint_and_untrust_each_object
+ def test_taint_each_object
x = Object.new
obj = [[x]]
# clean object causes crean stream
assert_equal(false, obj.tainted?)
- assert_equal(false, obj.untrusted?)
assert_equal(false, obj.first.tainted?)
- assert_equal(false, obj.first.untrusted?)
assert_equal(false, obj.first.first.tainted?)
- assert_equal(false, obj.first.first.untrusted?)
s = Marshal.dump(obj)
assert_equal(false, s.tainted?)
- assert_equal(false, s.untrusted?)
- # tainted/untrusted object causes tainted/untrusted stream
+ # tainted object causes tainted stream
x.taint
- x.untrust
assert_equal(false, obj.tainted?)
- assert_equal(false, obj.untrusted?)
assert_equal(false, obj.first.tainted?)
- assert_equal(false, obj.first.untrusted?)
assert_equal(true, obj.first.first.tainted?)
- assert_equal(true, obj.first.first.untrusted?)
t = Marshal.dump(obj)
assert_equal(true, t.tainted?)
- assert_equal(true, t.untrusted?)
# clean stream causes clean objects
assert_equal(false, s.tainted?)
- assert_equal(false, s.untrusted?)
y = Marshal.load(s)
assert_equal(false, y.tainted?)
- assert_equal(false, y.untrusted?)
assert_equal(false, y.first.tainted?)
- assert_equal(false, y.first.untrusted?)
assert_equal(false, y.first.first.tainted?)
- assert_equal(false, y.first.first.untrusted?)
- # tainted/untrusted stream causes tainted/untrusted objects
+ # tainted stream causes tainted objects
assert_equal(true, t.tainted?)
- assert_equal(true, t.untrusted?)
y = Marshal.load(t)
assert_equal(true, y.tainted?)
- assert_equal(true, y.untrusted?)
assert_equal(true, y.first.tainted?)
- assert_equal(true, y.first.untrusted?)
assert_equal(true, y.first.first.tainted?)
- assert_equal(true, y.first.first.untrusted?)
# same tests by different senario
s.taint
- s.untrust
assert_equal(true, s.tainted?)
- assert_equal(true, s.untrusted?)
y = Marshal.load(s)
assert_equal(true, y.tainted?)
- assert_equal(true, y.untrusted?)
assert_equal(true, y.first.tainted?)
- assert_equal(true, y.first.untrusted?)
assert_equal(true, y.first.first.tainted?)
- assert_equal(true, y.first.first.untrusted?)
end
- def test_symbol
+ def test_symbol2
[:ruby, :"\u{7d05}\u{7389}"].each do |sym|
assert_equal(sym, Marshal.load(Marshal.dump(sym)), '[ruby-core:24788]')
end
@@ -267,6 +249,21 @@ class TestMarshal < Test::Unit::TestCase
assert_equal(ary, Marshal.load(Marshal.dump(ary)), bug2548)
end
+ def test_symlink
+ assert_include(Marshal.dump([:a, :a]), ';')
+ end
+
+ def test_symlink_in_ivar
+ bug10991 = '[ruby-core:68587] [Bug #10991]'
+ sym = Marshal.load("\x04\x08" +
+ "I" ":\x0bKernel" +
+ ("\x06" +
+ ("I" ":\x07@a" +
+ ("\x06" ":\x07@b" "e;\x0""o:\x0bObject""\x0")) +
+ "0"))
+ assert_equal(:Kernel, sym, bug10991)
+ end
+
ClassUTF8 = eval("class R\u{e9}sum\u{e9}; self; end")
iso_8859_1 = Encoding::ISO_8859_1
@@ -316,7 +313,7 @@ class TestMarshal < Test::Unit::TestCase
end
end
- def test_regexp
+ def test_regexp2
assert_equal(/\\u/, Marshal.load("\004\b/\b\\\\u\000"))
assert_equal(/u/, Marshal.load("\004\b/\a\\u\000"))
assert_equal(/u/, Marshal.load("\004\bI/\a\\u\000\006:\016@encoding\"\vEUC-JP"))
@@ -328,10 +325,11 @@ class TestMarshal < Test::Unit::TestCase
assert_equal(c, Marshal.load(Marshal.dump(c)), bug2109)
assert_nothing_raised(ArgumentError, '[ruby-dev:40386]') do
- re = Tempfile.open("marshal_regexp") do |f|
+ re = Tempfile.create("marshal_regexp") do |f|
f.binmode.write("\x04\bI/\x00\x00\x06:\rencoding\"\rUS-ASCII")
- f.close
- Marshal.load(f.open.binmode)
+ f.rewind
+ re2 = Marshal.load(f)
+ re2
end
assert_equal(//, re)
end
@@ -426,7 +424,7 @@ class TestMarshal < Test::Unit::TestCase
m = Marshal.dump(o)
}
o2 = Marshal.load(m)
- assert_equal(STDIN, o.stdin)
+ assert_equal(STDIN, o2.stdin)
end
def test_marshal_string_encoding
@@ -449,5 +447,380 @@ class TestMarshal < Test::Unit::TestCase
o2 = Marshal.load(m)
assert_equal(o1, o2)
end
-
+
+ def test_marshal_symbol_ascii8bit
+ bug6209 = '[ruby-core:43762]'
+ o1 = "\xff".force_encoding("ASCII-8BIT").intern
+ m = Marshal.dump(o1)
+ o2 = nil
+ assert_nothing_raised(EncodingError, bug6209) {o2 = Marshal.load(m)}
+ assert_equal(o1, o2, bug6209)
+ end
+
+ class PrivateClass
+ def initialize(foo)
+ @foo = foo
+ end
+ attr_reader :foo
+ end
+ private_constant :PrivateClass
+
+ def test_marshal_private_class
+ o1 = PrivateClass.new("test")
+ o2 = Marshal.load(Marshal.dump(o1))
+ assert_equal(o1.class, o2.class)
+ assert_equal(o1.foo, o2.foo)
+ end
+
+ def test_marshal_complex
+ assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\x05")}
+ assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\x06i\x00")}
+ assert_equal(Complex(1, 2), Marshal.load("\x04\bU:\fComplex[\ai\x06i\a"))
+ assert_raise(ArgumentError){Marshal.load("\x04\bU:\fComplex[\bi\x00i\x00i\x00")}
+ end
+
+ def test_marshal_rational
+ assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\x05")}
+ assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\x06i\x00")}
+ assert_equal(Rational(1, 2), Marshal.load("\x04\bU:\rRational[\ai\x06i\a"))
+ assert_raise(ArgumentError){Marshal.load("\x04\bU:\rRational[\bi\x00i\x00i\x00")}
+ end
+
+ def test_marshal_flonum_reference
+ bug7348 = '[ruby-core:49323]'
+ e = []
+ ary = [ [2.0, e], [e] ]
+ assert_equal(ary, Marshal.load(Marshal.dump(ary)), bug7348)
+ end
+
+ class TestClass
+ end
+
+ module TestModule
+ end
+
+ def test_marshal_load_should_not_taint_classes
+ bug7325 = '[ruby-core:49198]'
+ for c in [TestClass, TestModule]
+ assert_not_predicate(c, :tainted?)
+ c2 = Marshal.load(Marshal.dump(c).taint)
+ assert_same(c, c2)
+ assert_not_predicate(c, :tainted?, bug7325)
+ end
+ end
+
+ class Bug7627 < Struct.new(:bar)
+ attr_accessor :foo
+
+ def marshal_dump; 'dump'; end # fake dump data
+ def marshal_load(*); end # do nothing
+ end
+
+ def test_marshal_dump_struct_ivar
+ bug7627 = '[ruby-core:51163]'
+ obj = Bug7627.new
+ obj.foo = '[Bug #7627]'
+
+ dump = Marshal.dump(obj)
+ loaded = Marshal.load(dump)
+
+ assert_equal(obj, loaded, bug7627)
+ assert_nil(loaded.foo, bug7627)
+ end
+
+ class LoadData
+ attr_reader :data
+ def initialize(data)
+ @data = data
+ end
+ alias marshal_dump data
+ alias marshal_load initialize
+ end
+
+ class Bug8276 < LoadData
+ def initialize(*)
+ super
+ freeze
+ end
+ alias marshal_load initialize
+ end
+
+ class FrozenData < LoadData
+ def marshal_load(data)
+ super
+ data.instance_variables.each do |iv|
+ instance_variable_set(iv, data.instance_variable_get(iv))
+ end
+ freeze
+ end
+ end
+
+ def test_marshal_dump_excess_encoding
+ bug8276 = '[ruby-core:54334] [Bug #8276]'
+ t = Bug8276.new(bug8276)
+ s = Marshal.dump(t)
+ assert_nothing_raised(RuntimeError, bug8276) {s = Marshal.load(s)}
+ assert_equal(t.data, s.data, bug8276)
+ end
+
+ def test_marshal_dump_ivar
+ s = "data with ivar"
+ s.instance_variable_set(:@t, 42)
+ t = Bug8276.new(s)
+ s = Marshal.dump(t)
+ assert_raise(FrozenError) {Marshal.load(s)}
+ end
+
+ def test_marshal_load_ivar
+ s = "data with ivar"
+ s.instance_variable_set(:@t, 42)
+ hook = ->(v) {
+ if LoadData === v
+ assert_send([v, :instance_variable_defined?, :@t], v.class.name)
+ assert_equal(42, v.instance_variable_get(:@t), v.class.name)
+ end
+ v
+ }
+ [LoadData, FrozenData].each do |klass|
+ t = klass.new(s)
+ d = Marshal.dump(t)
+ v = assert_nothing_raised(RuntimeError) {break Marshal.load(d, hook)}
+ assert_send([v, :instance_variable_defined?, :@t], klass.name)
+ assert_equal(42, v.instance_variable_get(:@t), klass.name)
+ end
+ end
+
+ def test_class_ivar
+ assert_raise(TypeError) {Marshal.load("\x04\x08Ic\x1bTestMarshal::TestClass\x06:\x0e@ivar_bug\"\x08bug")}
+ assert_raise(TypeError) {Marshal.load("\x04\x08IM\x1bTestMarshal::TestClass\x06:\x0e@ivar_bug\"\x08bug")}
+ assert_not_operator(TestClass, :instance_variable_defined?, :@bug)
+ end
+
+ def test_module_ivar
+ assert_raise(TypeError) {Marshal.load("\x04\x08Im\x1cTestMarshal::TestModule\x06:\x0e@ivar_bug\"\x08bug")}
+ assert_raise(TypeError) {Marshal.load("\x04\x08IM\x1cTestMarshal::TestModule\x06:\x0e@ivar_bug\"\x08bug")}
+ assert_not_operator(TestModule, :instance_variable_defined?, :@bug)
+ end
+
+ class TestForRespondToFalse
+ def respond_to?(a)
+ false
+ end
+ end
+
+ def test_marshal_respond_to_arity
+ assert_nothing_raised(ArgumentError, '[Bug #7722]') do
+ Marshal.dump(TestForRespondToFalse.new)
+ end
+ end
+
+ def test_packed_string
+ packed = ["foo"].pack("p")
+ bare = "".force_encoding(Encoding::ASCII_8BIT) << packed
+ assert_equal(Marshal.dump(bare), Marshal.dump(packed))
+ end
+
+ def test_untainted_numeric
+ bug8945 = '[ruby-core:57346] [Bug #8945] Numerics never be tainted'
+ b = RbConfig::LIMITS['FIXNUM_MAX'] + 1
+ tainted = [0, 1.0, 1.72723e-77, b].select do |x|
+ Marshal.load(Marshal.dump(x).taint).tainted?
+ end
+ assert_empty(tainted.map {|x| [x, x.class]}, bug8945)
+ end
+
+ class Bug9523
+ attr_reader :cc
+ def marshal_dump
+ callcc {|c| @cc = c }
+ nil
+ end
+ def marshal_load(v)
+ end
+ end
+
+ def test_continuation
+ require "continuation"
+ c = Bug9523.new
+ assert_raise_with_message(RuntimeError, /Marshal\.dump reentered at marshal_dump/) do
+ Marshal.dump(c)
+ GC.start
+ 1000.times {"x"*1000}
+ GC.start
+ c.cc.call
+ end
+ end
+
+ def test_undumpable_message
+ c = Module.new {break module_eval("class IO\u{26a1} < IO;self;end")}
+ assert_raise_with_message(TypeError, /IO\u{26a1}/) {
+ Marshal.dump(c.new(0, autoclose: false))
+ }
+ end
+
+ def test_undumpable_data
+ c = Module.new {break module_eval("class T\u{23F0 23F3}<Time;undef _dump;self;end")}
+ assert_raise_with_message(TypeError, /T\u{23F0 23F3}/) {
+ Marshal.dump(c.new)
+ }
+ end
+
+ def test_unloadable_data
+ c = eval("class Unloadable\u{23F0 23F3}<Time;;self;end")
+ c.class_eval {
+ alias _dump_data _dump
+ undef _dump
+ }
+ d = Marshal.dump(c.new)
+ assert_raise_with_message(TypeError, /Unloadable\u{23F0 23F3}/) {
+ Marshal.load(d)
+ }
+ end
+
+ def test_unloadable_userdef
+ c = eval("class Userdef\u{23F0 23F3}<Time;self;end")
+ class << c
+ undef _load
+ end
+ d = Marshal.dump(c.new)
+ assert_raise_with_message(TypeError, /Userdef\u{23F0 23F3}/) {
+ Marshal.load(d)
+ }
+ end
+
+ def test_unloadable_usrmarshal
+ c = eval("class UsrMarshal\u{23F0 23F3}<Time;self;end")
+ c.class_eval {
+ alias marshal_dump _dump
+ }
+ d = Marshal.dump(c.new)
+ assert_raise_with_message(TypeError, /UsrMarshal\u{23F0 23F3}/) {
+ Marshal.load(d)
+ }
+ end
+
+ 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)
+ 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)
+ assert_empty(err)
+ assert_predicate(status, :success?)
+ assert_equal(expected, out)
+ end
+
+ def test_marshal_honor_post_proc_value_for_link
+ str = 'x' # for link
+ obj = [str, str]
+ assert_equal(['X', 'X'], Marshal.load(Marshal.dump(obj), ->(v) { v == str ? v.upcase : v }))
+ end
+
+ def test_marshal_load_extended_class_crash
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ assert_raise_with_message(ArgumentError, /undefined/) do
+ Marshal.load("\x04\be:\x0F\x00omparableo:\vObject\x00")
+ end
+ end;
+ end
+
+ def test_marshal_load_r_prepare_reference_crash
+ crash = "\x04\bI/\x05\x00\x06:\x06E{\x06@\x05T"
+
+ opt = %w[--disable=gems]
+ assert_separately(opt, <<-RUBY)
+ assert_raise_with_message(ArgumentError, /bad link/) do
+ Marshal.load(#{crash.dump})
+ end
+ RUBY
+ end
+
+ MethodMissingWithoutRespondTo = Struct.new(:wrapped_object) do
+ undef respond_to?
+ def method_missing(*args, &block)
+ wrapped_object.public_send(*args, &block)
+ end
+ def respond_to_missing?(name, private = false)
+ wrapped_object.respond_to?(name, false)
+ end
+ end
+
+ def test_method_missing_without_respond_to
+ bug12353 = "[ruby-core:75377] [Bug #12353]: try method_missing if" \
+ " respond_to? is undefined"
+ obj = MethodMissingWithoutRespondTo.new("foo")
+ dump = assert_nothing_raised(NoMethodError, bug12353) do
+ Marshal.dump(obj)
+ end
+ assert_equal(obj, Marshal.load(dump))
+ end
+
+ class Bug12974
+ def marshal_dump
+ dup
+ end
+ end
+
+ def test_marshal_dump_recursion
+ assert_raise_with_message(RuntimeError, /same class instance/) do
+ Marshal.dump(Bug12974.new)
+ end
+ end
+
+ Bug14314 = Struct.new(:foo, keyword_init: true)
+
+ def test_marshal_keyword_init_struct
+ obj = Bug14314.new(foo: 42)
+ assert_equal obj, Marshal.load(Marshal.dump(obj))
+ end
+
+ class Bug15968
+ attr_accessor :bar, :baz
+
+ def initialize
+ self.bar = Bar.new(self)
+ end
+
+ class Bar
+ attr_accessor :foo
+
+ def initialize(foo)
+ self.foo = foo
+ end
+
+ def marshal_dump
+ if self.foo.baz
+ self.foo.remove_instance_variable(:@baz)
+ else
+ self.foo.baz = :problem
+ end
+ {foo: self.foo}
+ end
+
+ def marshal_load(data)
+ self.foo = data[:foo]
+ end
+ end
+ end
+
+ def test_marshal_dump_adding_instance_variable
+ obj = Bug15968.new
+ assert_raise_with_message(RuntimeError, /instance variable added/) do
+ Marshal.dump(obj)
+ end
+ end
+
+ def test_marshal_dump_removing_instance_variable
+ obj = Bug15968.new
+ obj.baz = :Bug15968
+ assert_raise_with_message(RuntimeError, /instance variable removed/) do
+ Marshal.dump(obj)
+ end
+ end
end
diff --git a/test/ruby/test_math.rb b/test/ruby/test_math.rb
index 64b72ce5c0..5cc12bcfeb 100644
--- a/test/ruby/test_math.rb
+++ b/test/ruby/test_math.rb
@@ -1,27 +1,43 @@
+# frozen_string_literal: false
require 'test/unit'
class TestMath < Test::Unit::TestCase
def assert_infinity(a, *rest)
rest = ["not infinity: #{a.inspect}"] if rest.empty?
- assert(!a.finite?, *rest)
+ assert_predicate(a, :infinite?, *rest)
end
def assert_nan(a, *rest)
rest = ["not nan: #{a.inspect}"] if rest.empty?
- assert(a.nan?, *rest)
+ assert_predicate(a, :nan?, *rest)
end
- def check(a, b)
+ def assert_float(a, b)
err = [Float::EPSILON * 4, [a.abs, b.abs].max * Float::EPSILON * 256].max
assert_in_delta(a, b, err)
end
+ alias check assert_float
+
+ def assert_float_and_int(exp_ary, act_ary)
+ flo_exp, int_exp, flo_act, int_act = *exp_ary, *act_ary
+ assert_float(flo_exp, flo_act)
+ assert_equal(int_exp, int_act)
+ end
def test_atan2
- assert_raise(Math::DomainError) { Math.atan2(0, 0) }
- assert_raise(Math::DomainError) { Math.atan2(Float::INFINITY, Float::INFINITY) }
- assert_raise(Math::DomainError) { Math.atan2(Float::INFINITY, -Float::INFINITY) }
- assert_raise(Math::DomainError) { Math.atan2(-Float::INFINITY, Float::INFINITY) }
- assert_raise(Math::DomainError) { Math.atan2(-Float::INFINITY, -Float::INFINITY) }
+ check(+0.0, Math.atan2(+0.0, +0.0))
+ check(-0.0, Math.atan2(-0.0, +0.0))
+ check(+Math::PI, Math.atan2(+0.0, -0.0))
+ check(-Math::PI, Math.atan2(-0.0, -0.0))
+
+ inf = Float::INFINITY
+ expected = 3.0 * Math::PI / 4.0
+ assert_nothing_raised { check(+expected, Math.atan2(+inf, -inf)) }
+ assert_nothing_raised { check(-expected, Math.atan2(-inf, -inf)) }
+ expected = Math::PI / 4.0
+ assert_nothing_raised { check(+expected, Math.atan2(+inf, +inf)) }
+ assert_nothing_raised { check(-expected, Math.atan2(-inf, +inf)) }
+
check(0, Math.atan2(0, 1))
check(Math::PI / 4, Math.atan2(1, 1))
check(Math::PI / 2, Math.atan2(1, 0))
@@ -33,6 +49,7 @@ class TestMath < Test::Unit::TestCase
check(0.0, Math.cos(2 * Math::PI / 4))
check(-1.0, Math.cos(4 * Math::PI / 4))
check(0.0, Math.cos(6 * Math::PI / 4))
+ check(0.5403023058681398, Math.cos(1))
end
def test_sin
@@ -46,9 +63,9 @@ class TestMath < Test::Unit::TestCase
def test_tan
check(0.0, Math.tan(0 * Math::PI / 4))
check(1.0, Math.tan(1 * Math::PI / 4))
- assert(Math.tan(2 * Math::PI / 4).abs > 1024)
+ assert_operator(Math.tan(2 * Math::PI / 4).abs, :>, 1024)
check(0.0, Math.tan(4 * Math::PI / 4))
- assert(Math.tan(6 * Math::PI / 4).abs > 1024)
+ assert_operator(Math.tan(6 * Math::PI / 4).abs, :>, 1024)
end
def test_acos
@@ -94,6 +111,8 @@ class TestMath < Test::Unit::TestCase
check(Math.sinh(0) / Math.cosh(0), Math.tanh(0))
check(Math.sinh(1) / Math.cosh(1), Math.tanh(1))
check(Math.sinh(2) / Math.cosh(2), Math.tanh(2))
+ check(+1.0, Math.tanh(+1000.0))
+ check(-1.0, Math.tanh(-1000.0))
end
def test_acosh
@@ -133,18 +152,24 @@ class TestMath < Test::Unit::TestCase
check(0, Math.log(1, 10))
check(1, Math.log(10, 10))
check(2, Math.log(100, 10))
- assert_equal(1.0/0, Math.log(1.0/0))
+ check(Math.log(2.0 ** 64), Math.log(1 << 64))
+ check(Math.log(2) * 1024.0, Math.log(2 ** 1024))
+ 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(TypeError) { Math.log(1,nil) }
+ assert_raise(Math::DomainError, '[ruby-core:62309] [ruby-Bug #9797]') { Math.log(1.0, -1.0) }
+ assert_nothing_raised { assert_nan(Math.log(0.0, 0.0)) }
end
def test_log2
check(0, Math.log2(1))
check(1, Math.log2(2))
check(2, Math.log2(4))
- assert_equal(1.0/0, Math.log2(1.0/0))
+ check(Math.log2(2.0 ** 64), Math.log2(1 << 64))
+ check(1024.0, Math.log2(2 ** 1024))
+ 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) }
@@ -154,7 +179,9 @@ class TestMath < Test::Unit::TestCase
check(0, Math.log10(1))
check(1, Math.log10(10))
check(2, Math.log10(100))
- assert_equal(1.0/0, Math.log10(1.0/0))
+ check(Math.log10(2.0 ** 64), Math.log10(1 << 64))
+ check(Math.log10(2) * 1024, Math.log10(2 ** 1024))
+ 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) }
@@ -164,22 +191,26 @@ class TestMath < Test::Unit::TestCase
check(0, Math.sqrt(0))
check(1, Math.sqrt(1))
check(2, Math.sqrt(4))
- assert_equal(1.0/0, Math.sqrt(1.0/0))
+ 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) }
end
+ def test_cbrt
+ check(1, Math.cbrt(1))
+ check(-2, Math.cbrt(-8))
+ check(3, Math.cbrt(27))
+ check(-0.1, Math.cbrt(-0.001))
+ assert_nothing_raised { assert_infinity(Math.cbrt(1.0/0)) }
+ assert_operator(Math.cbrt(1.0 - Float::EPSILON), :<=, 1.0)
+ end
+
def test_frexp
- check(0.0, Math.frexp(0.0).first)
- assert_equal(0, Math.frexp(0).last)
- check(0.5, Math.frexp(0.5).first)
- assert_equal(0, Math.frexp(0.5).last)
- check(0.5, Math.frexp(1.0).first)
- assert_equal(1, Math.frexp(1.0).last)
- check(0.5, Math.frexp(2.0).first)
- assert_equal(2, Math.frexp(2.0).last)
- check(0.75, Math.frexp(3.0).first)
- assert_equal(2, Math.frexp(3.0).last)
+ assert_float_and_int([0.0, 0], Math.frexp(0.0))
+ assert_float_and_int([0.5, 0], Math.frexp(0.5))
+ 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))
end
def test_ldexp
@@ -216,6 +247,8 @@ class TestMath < Test::Unit::TestCase
check(2, Math.gamma(3))
check(15 * sqrt_pi / 8, Math.gamma(3.5))
check(6, Math.gamma(4))
+ check(1.1240007277776077e+21, Math.gamma(23))
+ check(2.5852016738885062e+22, Math.gamma(24))
# no SEGV [ruby-core:25257]
31.upto(65) do |i|
@@ -225,54 +258,86 @@ class TestMath < Test::Unit::TestCase
end
assert_raise(Math::DomainError) { Math.gamma(-Float::INFINITY) }
+ x = Math.gamma(-0.0)
+ mesg = "Math.gamma(-0.0) should be -INF"
+ assert_infinity(x, mesg)
+ assert_predicate(x, :negative?, mesg)
end
def test_lgamma
sqrt_pi = Math.sqrt(Math::PI)
+ assert_float_and_int([Math.log(4 * sqrt_pi / 3), 1], Math.lgamma(-1.5))
+ assert_float_and_int([Math.log(2 * sqrt_pi), -1], Math.lgamma(-0.5))
+ assert_float_and_int([Math.log(sqrt_pi), 1], Math.lgamma(0.5))
+ assert_float_and_int([0, 1], Math.lgamma(1))
+ assert_float_and_int([Math.log(sqrt_pi / 2), 1], Math.lgamma(1.5))
+ assert_float_and_int([0, 1], Math.lgamma(2))
+ assert_float_and_int([Math.log(3 * sqrt_pi / 4), 1], Math.lgamma(2.5))
+ assert_float_and_int([Math.log(2), 1], Math.lgamma(3))
+ 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))
- g, s = Math.lgamma(-1.5)
- check(Math.log(4 * sqrt_pi / 3), g)
- assert_equal(s, 1)
-
- g, s = Math.lgamma(-0.5)
- check(Math.log(2 * sqrt_pi), g)
- assert_equal(s, -1)
-
- g, s = Math.lgamma(0.5)
- check(Math.log(sqrt_pi), g)
- assert_equal(s, 1)
-
- assert_equal([0, 1], Math.lgamma(1))
+ assert_raise(Math::DomainError) { 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)
+ end
- g, s = Math.lgamma(1.5)
- check(Math.log(sqrt_pi / 2), g)
- assert_equal(s, 1)
+ def test_fixnum_to_f
+ check(12.0, Math.sqrt(144))
+ end
- assert_equal([0, 1], Math.lgamma(2))
+ def test_override_integer_to_f
+ Integer.class_eval do
+ alias _to_f to_f
+ def to_f
+ (self + 1)._to_f
+ end
+ end
- g, s = Math.lgamma(2.5)
- check(Math.log(3 * sqrt_pi / 4), g)
- assert_equal(s, 1)
+ check(Math.cos((0 + 1)._to_f), Math.cos(0))
+ check(Math.exp((0 + 1)._to_f), Math.exp(0))
+ check(Math.log((0 + 1)._to_f), Math.log(0))
+ ensure
+ Integer.class_eval { undef to_f; alias to_f _to_f; undef _to_f }
+ end
- g, s = Math.lgamma(3)
- check(Math.log(2), g)
- assert_equal(s, 1)
+ def test_bignum_to_f
+ check((1 << 65).to_f, Math.sqrt(1 << 130))
+ end
- g, s = Math.lgamma(3.5)
- check(Math.log(15 * sqrt_pi / 8), g)
- assert_equal(s, 1)
+ def test_override_bignum_to_f
+ Integer.class_eval do
+ alias _to_f to_f
+ def to_f
+ (self << 1)._to_f
+ end
+ end
- g, s = Math.lgamma(4)
- check(Math.log(6), g)
- assert_equal(s, 1)
+ check(Math.cos((1 << 64 << 1)._to_f), Math.cos(1 << 64))
+ check(Math.log((1 << 64 << 1)._to_f), Math.log(1 << 64))
+ ensure
+ Integer.class_eval { undef to_f; alias to_f _to_f; undef _to_f }
+ end
- assert_raise(Math::DomainError) { Math.lgamma(-Float::INFINITY) }
+ def test_rational_to_f
+ check((2 ** 31).fdiv(3 ** 20), Math.sqrt((2 ** 62)/(3 ** 40).to_r))
end
- def test_cbrt
- check(1, Math.cbrt(1))
- check(-2, Math.cbrt(-8))
- check(3, Math.cbrt(27))
- check(-0.1, Math.cbrt(-0.001))
+ def test_override_rational_to_f
+ Rational.class_eval do
+ alias _to_f to_f
+ def to_f
+ (self + 1)._to_f
+ end
+ end
+
+ check(Math.cos((0r + 1)._to_f), Math.cos(0r))
+ check(Math.exp((0r + 1)._to_f), Math.exp(0r))
+ check(Math.log((0r + 1)._to_f), Math.log(0r))
+ ensure
+ Rational.class_eval { undef to_f; alias to_f _to_f; undef _to_f }
end
end
diff --git a/test/ruby/test_metaclass.rb b/test/ruby/test_metaclass.rb
index 6386a02dfa..8c1990a78c 100644
--- a/test/ruby/test_metaclass.rb
+++ b/test/ruby/test_metaclass.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestMetaclass < Test::Unit::TestCase
diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb
index d135577208..3bf25928c9 100644
--- a/test/ruby/test_method.rb
+++ b/test/ruby/test_method.rb
@@ -1,5 +1,6 @@
+# -*- coding: us-ascii -*-
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestMethod < Test::Unit::TestCase
def setup
@@ -21,7 +22,14 @@ class TestMethod < Test::Unit::TestCase
def mo5(a, *b, c) end
def mo6(a, *b, c, &d) end
def mo7(a, b = nil, *c, d, &e) end
- def ma1((a), &b) end
+ def ma1((a), &b) nil && a end
+ def mk1(**) end
+ def mk2(**o) nil && o end
+ def mk3(a, **o) nil && o end
+ def mk4(a = nil, **o) nil && o end
+ def mk5(a, b = nil, **o) nil && o end
+ def mk6(a, b = nil, c, **o) nil && o end
+ def mk7(a, b = nil, *c, d, **o) nil && o end
class Base
def foo() :base end
@@ -31,12 +39,16 @@ class TestMethod < Test::Unit::TestCase
end
class T
def initialize; end
+ def initialize_copy(*) super end
+ def initialize_clone(*) super end
+ def initialize_dup(*) super end
+ def respond_to_missing?(*) super end
def normal_method; end
end
module M
def func; end
module_function :func
- def meth; end
+ def meth; :meth end
end
def mv1() end
@@ -63,6 +75,13 @@ class TestMethod < Test::Unit::TestCase
assert_equal(-2, method(:mo4).arity)
assert_equal(-3, method(:mo5).arity)
assert_equal(-3, method(:mo6).arity)
+ assert_equal(-1, method(:mk1).arity)
+ assert_equal(-1, method(:mk2).arity)
+ assert_equal(-2, method(:mk3).arity)
+ assert_equal(-1, method(:mk4).arity)
+ assert_equal(-2, method(:mk5).arity)
+ assert_equal(-3, method(:mk6).arity)
+ assert_equal(-3, method(:mk7).arity)
end
def test_arity_special
@@ -90,6 +109,51 @@ class TestMethod < Test::Unit::TestCase
assert_equal(:m, Class.new {define_method(:m) {__method__}}.new.m)
assert_equal(:m, Class.new {define_method(:m) {tap{return __method__}}}.new.m)
assert_nil(eval("class TestCallee; __method__; end"))
+
+ assert_equal(:test_callee, __callee__)
+ [
+ ["method", Class.new {def m; __callee__; end},],
+ ["block", Class.new {def m; tap{return __callee__}; end},],
+ ["define_method", Class.new {define_method(:m) {__callee__}}],
+ ["define_method block", Class.new {define_method(:m) {tap{return __callee__}}}],
+ ].each do |mesg, c|
+ c.class_eval {alias m2 m}
+ o = c.new
+ assert_equal(:m, o.m, mesg)
+ assert_equal(:m2, o.m2, mesg)
+ end
+ assert_nil(eval("class TestCallee; __callee__; end"))
+ end
+
+ def test_orphan_callee
+ c = Class.new{def foo; proc{__callee__}; end; alias alias_foo foo}
+ assert_equal(:alias_foo, c.new.alias_foo.call, '[Bug #11046]')
+ end
+
+ def test_method_in_define_method_block
+ bug4606 = '[ruby-core:35386]'
+ c = Class.new do
+ [:m1, :m2].each do |m|
+ define_method(m) do
+ __method__
+ end
+ end
+ end
+ assert_equal(:m1, c.new.m1, bug4606)
+ assert_equal(:m2, c.new.m2, bug4606)
+ end
+
+ def test_method_in_block_in_define_method_block
+ bug4606 = '[ruby-core:35386]'
+ c = Class.new do
+ [:m1, :m2].each do |m|
+ define_method(m) do
+ tap { return __method__ }
+ end
+ end
+ end
+ assert_equal(:m1, c.new.m1, bug4606)
+ assert_equal(:m2, c.new.m2, bug4606)
end
def test_body
@@ -97,6 +161,7 @@ class TestMethod < Test::Unit::TestCase
def o.foo; end
assert_nothing_raised { RubyVM::InstructionSequence.disasm(o.method(:foo)) }
assert_nothing_raised { RubyVM::InstructionSequence.disasm("x".method(:upcase)) }
+ assert_nothing_raised { RubyVM::InstructionSequence.disasm(method(:to_s).to_proc) }
end
def test_new
@@ -131,6 +196,28 @@ class TestMethod < Test::Unit::TestCase
o = Object.new
def o.foo; end
assert_kind_of(Integer, o.method(:foo).hash)
+ assert_equal(Array.instance_method(:map).hash, Array.instance_method(:collect).hash)
+ assert_kind_of(String, o.method(:foo).hash.to_s)
+ end
+
+ def test_owner
+ c = Class.new do
+ def foo; end
+ end
+ assert_equal(c, c.instance_method(:foo).owner)
+ c2 = Class.new(c)
+ assert_equal(c, c2.instance_method(:foo).owner)
+ end
+
+ def test_owner_missing
+ c = Class.new do
+ def respond_to_missing?(name, bool)
+ name == :foo
+ end
+ end
+ c2 = Class.new(c)
+ assert_equal(c, c.new.method(:foo).owner)
+ assert_equal(c2, c2.new.method(:foo).owner)
end
def test_receiver_name_owner
@@ -142,6 +229,12 @@ class TestMethod < Test::Unit::TestCase
assert_equal(class << o; self; end, m.owner)
assert_equal(:foo, m.unbind.name)
assert_equal(class << o; self; end, m.unbind.owner)
+ class << o
+ alias bar foo
+ end
+ m = o.method(:bar)
+ assert_equal(:bar, m.name)
+ assert_equal(:foo, m.original_name)
end
def test_instance_method
@@ -159,6 +252,17 @@ class TestMethod < Test::Unit::TestCase
def o.bar; end
m = o.method(:bar).unbind
assert_raise(TypeError) { m.bind(Object.new) }
+
+ cx = EnvUtil.labeled_class("X\u{1f431}")
+ assert_raise_with_message(TypeError, /X\u{1f431}/) do
+ o.method(cx)
+ end
+ end
+
+ def test_bind_module_instance_method
+ feature4254 = '[ruby-core:34267]'
+ m = M.instance_method(:meth)
+ assert_equal(:meth, m.bind(Object.new).call, feature4254)
end
def test_define_method
@@ -181,22 +285,140 @@ class TestMethod < Test::Unit::TestCase
assert_raise(TypeError) do
Class.new.class_eval { define_method(:bar, o.method(:bar)) }
end
+ cx = EnvUtil.labeled_class("X\u{1f431}")
+ assert_raise_with_message(TypeError, /X\u{1F431}/) do
+ Class.new {define_method(cx) {}}
+ end
+ end
+ def test_define_method_no_proc
o = Object.new
def o.foo(c)
c.class_eval { define_method(:foo) }
end
c = Class.new
- o.foo(c) { :foo }
- assert_equal(:foo, c.new.foo)
+ assert_raise(ArgumentError) {o.foo(c)}
+ bug11283 = '[ruby-core:69655] [Bug #11283]'
+ assert_raise(ArgumentError, bug11283) {o.foo(c) {:foo}}
+ end
+
+ def test_define_singleton_method
o = Object.new
o.instance_eval { define_singleton_method(:foo) { :foo } }
assert_equal(:foo, o.foo)
+ end
+
+ def test_define_singleton_method_no_proc
+ o = Object.new
+ assert_raise(ArgumentError) {
+ o.instance_eval { define_singleton_method(:bar) }
+ }
+
+ bug11283 = '[ruby-core:69655] [Bug #11283]'
+ def o.define(n)
+ define_singleton_method(n)
+ end
+ assert_raise(ArgumentError, bug11283) {o.define(:bar) {:bar}}
+ end
+ def test_define_method_invalid_arg
assert_raise(TypeError) do
Class.new.class_eval { define_method(:foo, Object.new) }
end
+
+ assert_raise(TypeError) do
+ Module.new.module_eval {define_method(:foo, Base.instance_method(:foo))}
+ end
+ end
+
+ def test_define_singleton_method_with_extended_method
+ bug8686 = "[ruby-core:56174]"
+
+ m = Module.new do
+ extend self
+
+ def a
+ "a"
+ end
+ end
+
+ assert_nothing_raised(bug8686) do
+ m.define_singleton_method(:a, m.method(:a))
+ end
+ end
+
+ def test_define_method_transplating
+ feature4254 = '[ruby-core:34267]'
+ m = Module.new {define_method(:meth, M.instance_method(:meth))}
+ assert_equal(:meth, Object.new.extend(m).meth, feature4254)
+ c = Class.new {define_method(:meth, M.instance_method(:meth))}
+ assert_equal(:meth, c.new.meth, feature4254)
+ end
+
+ def test_define_method_visibility
+ c = Class.new do
+ public
+ define_method(:foo) {:foo}
+ protected
+ define_method(:bar) {:bar}
+ private
+ define_method(:baz) {:baz}
+ end
+
+ assert_equal(true, c.public_method_defined?(:foo))
+ assert_equal(false, c.public_method_defined?(:bar))
+ assert_equal(false, c.public_method_defined?(:baz))
+
+ assert_equal(false, c.protected_method_defined?(:foo))
+ assert_equal(true, c.protected_method_defined?(:bar))
+ assert_equal(false, c.protected_method_defined?(:baz))
+
+ assert_equal(false, c.private_method_defined?(:foo))
+ assert_equal(false, c.private_method_defined?(:bar))
+ assert_equal(true, c.private_method_defined?(:baz))
+
+ m = Module.new do
+ module_function
+ define_method(:foo) {:foo}
+ end
+ assert_equal(true, m.respond_to?(:foo))
+ assert_equal(false, m.public_method_defined?(:foo))
+ assert_equal(false, m.protected_method_defined?(:foo))
+ assert_equal(true, m.private_method_defined?(:foo))
+ end
+
+ def test_define_method_in_private_scope
+ bug9005 = '[ruby-core:57747] [Bug #9005]'
+ c = Class.new
+ class << c
+ public :define_method
+ end
+ TOPLEVEL_BINDING.eval("proc{|c|c.define_method(:x) {|x|throw x}}").call(c)
+ o = c.new
+ assert_throw(bug9005) {o.x(bug9005)}
+ end
+
+ def test_singleton_define_method_in_private_scope
+ bug9141 = '[ruby-core:58497] [Bug #9141]'
+ o = Object.new
+ class << o
+ public :define_singleton_method
+ end
+ TOPLEVEL_BINDING.eval("proc{|o|o.define_singleton_method(:x) {|x|throw x}}").call(o)
+ assert_throw(bug9141) do
+ o.x(bug9141)
+ end
+ end
+
+ def test_super_in_proc_from_define_method
+ c1 = Class.new {
+ def m
+ :m1
+ end
+ }
+ c2 = Class.new(c1) { define_method(:m) { Proc.new { super() } } }
+ assert_equal(:m1, c2.new.m.call, 'see [Bug #4881] and [Bug #3136]')
end
def test_clone
@@ -208,15 +430,6 @@ class TestMethod < Test::Unit::TestCase
assert_equal(:bar, m.clone.bar)
end
- def test_call
- o = Object.new
- def o.foo; p 1; end
- def o.bar(x); x; end
- m = o.method(:foo)
- m.taint
- assert_raise(SecurityError) { m.call }
- end
-
def test_inspect
o = Object.new
def o.foo; end
@@ -236,6 +449,23 @@ class TestMethod < Test::Unit::TestCase
c2.class_eval { private :foo }
m2 = c2.new.method(:foo)
assert_equal("#<Method: #{ c2.inspect }(#{ c.inspect })#foo>", m2.inspect)
+
+ bug7806 = '[ruby-core:52048] [Bug #7806]'
+ c3 = Class.new(c)
+ c3.class_eval { alias bar foo }
+ m3 = c3.new.method(:bar)
+ assert_equal("#<Method: #{c3.inspect}(#{c.inspect})#bar(foo)>", m3.inspect, bug7806)
+
+ m.taint
+ assert_predicate(m.inspect, :tainted?, "inspect result should be infected")
+
+ 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)>", m4.inspect, bug15608)
end
def test_callee_top_level
@@ -261,10 +491,16 @@ class TestMethod < Test::Unit::TestCase
end
def test_default_accessibility
- assert T.public_instance_methods.include?(:normal_method), 'normal methods are public by default'
- assert !T.public_instance_methods.include?(:initialize), '#initialize is private'
- assert !M.public_instance_methods.include?(:func), 'module methods are private by default'
- assert M.public_instance_methods.include?(:meth), 'normal methods are public by default'
+ tmethods = T.public_instance_methods
+ assert_include tmethods, :normal_method, 'normal methods are public by default'
+ assert_not_include tmethods, :initialize, '#initialize is private'
+ assert_not_include tmethods, :initialize_copy, '#initialize_copy is private'
+ assert_not_include tmethods, :initialize_clone, '#initialize_clone is private'
+ assert_not_include tmethods, :initialize_dup, '#initialize_dup is private'
+ assert_not_include tmethods, :respond_to_missing?, '#respond_to_missing? is private'
+ mmethods = M.public_instance_methods
+ assert_not_include mmethods, :func, 'module methods are private by default'
+ assert_include mmethods, :meth, 'normal methods are public by default'
end
define_method(:pm0) {||}
@@ -277,7 +513,14 @@ class TestMethod < Test::Unit::TestCase
define_method(:pmo5) {|a, *b, c|}
define_method(:pmo6) {|a, *b, c, &d|}
define_method(:pmo7) {|a, b = nil, *c, d, &e|}
- define_method(:pma1) {|(a), &b|}
+ define_method(:pma1) {|(a), &b| nil && a}
+ define_method(:pmk1) {|**|}
+ define_method(:pmk2) {|**o|}
+ define_method(:pmk3) {|a, **o|}
+ define_method(:pmk4) {|a = nil, **o|}
+ define_method(:pmk5) {|a, b = nil, **o|}
+ define_method(:pmk6) {|a, b = nil, c, **o|}
+ define_method(:pmk7) {|a, b = nil, *c, d, **o|}
def test_bound_parameters
assert_equal([], method(:m0).parameters)
@@ -291,6 +534,13 @@ class TestMethod < Test::Unit::TestCase
assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:mo6).parameters)
assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], method(:mo7).parameters)
assert_equal([[:req], [:block, :b]], method(:ma1).parameters)
+ assert_equal([[:keyrest]], method(:mk1).parameters)
+ assert_equal([[:keyrest, :o]], method(:mk2).parameters)
+ assert_equal([[:req, :a], [:keyrest, :o]], method(:mk3).parameters)
+ assert_equal([[:opt, :a], [:keyrest, :o]], method(:mk4).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], method(:mk5).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], method(:mk6).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], method(:mk7).parameters)
end
def test_unbound_parameters
@@ -305,6 +555,13 @@ class TestMethod < Test::Unit::TestCase
assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], self.class.instance_method(:mo6).parameters)
assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], self.class.instance_method(:mo7).parameters)
assert_equal([[:req], [:block, :b]], self.class.instance_method(:ma1).parameters)
+ assert_equal([[:keyrest]], self.class.instance_method(:mk1).parameters)
+ assert_equal([[:keyrest, :o]], self.class.instance_method(:mk2).parameters)
+ assert_equal([[:req, :a], [:keyrest, :o]], self.class.instance_method(:mk3).parameters)
+ assert_equal([[:opt, :a], [:keyrest, :o]], self.class.instance_method(:mk4).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], self.class.instance_method(:mk5).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], self.class.instance_method(:mk6).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], self.class.instance_method(:mk7).parameters)
end
def test_bmethod_bound_parameters
@@ -319,6 +576,13 @@ class TestMethod < Test::Unit::TestCase
assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:pmo6).parameters)
assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], method(:pmo7).parameters)
assert_equal([[:req], [:block, :b]], method(:pma1).parameters)
+ assert_equal([[:keyrest]], method(:pmk1).parameters)
+ assert_equal([[:keyrest, :o]], method(:pmk2).parameters)
+ assert_equal([[:req, :a], [:keyrest, :o]], method(:pmk3).parameters)
+ assert_equal([[:opt, :a], [:keyrest, :o]], method(:pmk4).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], method(:pmk5).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], method(:pmk6).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], method(:pmk7).parameters)
end
def test_bmethod_unbound_parameters
@@ -333,6 +597,19 @@ class TestMethod < Test::Unit::TestCase
assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], self.class.instance_method(:pmo6).parameters)
assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], self.class.instance_method(:pmo7).parameters)
assert_equal([[:req], [:block, :b]], self.class.instance_method(:pma1).parameters)
+ assert_equal([[:req], [:block, :b]], self.class.instance_method(:pma1).parameters)
+ assert_equal([[:keyrest]], self.class.instance_method(:pmk1).parameters)
+ assert_equal([[:keyrest, :o]], self.class.instance_method(:pmk2).parameters)
+ assert_equal([[:req, :a], [:keyrest, :o]], self.class.instance_method(:pmk3).parameters)
+ assert_equal([[:opt, :a], [:keyrest, :o]], self.class.instance_method(:pmk4).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], self.class.instance_method(:pmk5).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], self.class.instance_method(:pmk6).parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], self.class.instance_method(:pmk7).parameters)
+ end
+
+ def test_hidden_parameters
+ instance_eval("def m((_)"+",(_)"*256+");end")
+ assert_empty(method(:m).parameters.map{|_,n|n}.compact)
end
def test_public_method_with_zsuper_method
@@ -411,4 +688,514 @@ class TestMethod < Test::Unit::TestCase
assert_nothing_raised { v.instance_eval { mv2 } }
assert_nothing_raised { v.instance_eval { mv3 } }
end
+
+ def test_bound_method_entry
+ bug6171 = '[ruby-core:43383]'
+ assert_ruby_status([], <<-EOC, bug6171)
+ class Bug6171
+ def initialize(target)
+ define_singleton_method(:reverse, target.method(:reverse).to_proc)
+ end
+ end
+ 100.times {p = Bug6171.new('test'); 1000.times {p.reverse}}
+ EOC
+ end
+
+ def test_unbound_method_proc_coerce
+ # '&' coercion of an UnboundMethod raises TypeError
+ assert_raise(TypeError) do
+ Class.new do
+ define_method('foo', &Object.instance_method(:to_s))
+ end
+ end
+ end
+
+ def test___dir__
+ 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)
+ 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)
+ end
+
+ def test_alias_owner
+ bug7613 = '[ruby-core:51105]'
+ bug7993 = '[Bug #7993]'
+ c = Class.new {
+ def foo
+ end
+ prepend Module.new
+ attr_reader :zot
+ }
+ x = c.new
+ class << x
+ alias bar foo
+ end
+ 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(c, x.method(:zot).owner, bug7993)
+ assert_equal(c, c.instance_method(:zot).owner, bug7993)
+ end
+
+ def test_included
+ m = Module.new {
+ def foo
+ end
+ }
+ c = Class.new {
+ def foo
+ end
+ include m
+ }
+ assert_equal(c, c.instance_method(:foo).owner)
+ end
+
+ def test_prepended
+ bug7836 = '[ruby-core:52160] [Bug #7836]'
+ bug7988 = '[ruby-core:53038] [Bug #7988]'
+ m = Module.new {
+ def foo
+ end
+ }
+ c = Class.new {
+ def foo
+ end
+ prepend m
+ }
+ assert_raise(NameError, bug7988) {Module.new{prepend m}.instance_method(:bar)}
+ true || c || bug7836
+ end
+
+ def test_gced_bmethod
+ assert_normal_exit %q{
+ require 'irb'
+ IRB::Irb.module_eval do
+ define_method(:eval_input) do
+ IRB::Irb.module_eval { alias_method :eval_input, :to_s }
+ GC.start
+ Kernel
+ end
+ end
+ IRB.start
+ }, '[Bug #7825]'
+ end
+
+ def test_singleton_method
+ feature8391 = '[ruby-core:54914] [Feature #8391]'
+ c1 = Class.new
+ c1.class_eval { def foo; :foo; end }
+ o = c1.new
+ def o.bar; :bar; end
+ assert_nothing_raised(NameError) {o.method(:foo)}
+ assert_raise(NameError, feature8391) {o.singleton_method(:foo)}
+ m = assert_nothing_raised(NameError, feature8391) {break o.singleton_method(:bar)}
+ assert_equal(:bar, m.call, feature8391)
+ end
+
+ def test_singleton_method_prepend
+ bug14658 = '[Bug #14658]'
+ c1 = Class.new
+ o = c1.new
+ def o.bar; :bar; end
+ class << o; prepend Module.new; end
+ m = assert_nothing_raised(NameError, bug14658) {o.singleton_method(:bar)}
+ assert_equal(:bar, m.call, bug14658)
+
+ o = Object.new
+ assert_raise(NameError, bug14658) {o.singleton_method(:bar)}
+ end
+
+ Feature9783 = '[ruby-core:62212] [Feature #9783]'
+
+ def assert_curry_three_args(m)
+ curried = m.curry
+ assert_equal(6, curried.(1).(2).(3), Feature9783)
+
+ curried = m.curry(3)
+ assert_equal(6, curried.(1).(2).(3), Feature9783)
+
+ assert_raise_with_message(ArgumentError, /wrong number/) {m.curry(2)}
+ end
+
+ def test_curry_method
+ c = Class.new {
+ def three_args(a,b,c) a + b + c end
+ }
+ assert_curry_three_args(c.new.method(:three_args))
+ end
+
+ def test_curry_from_proc
+ c = Class.new {
+ define_method(:three_args) {|x,y,z| x + y + z}
+ }
+ assert_curry_three_args(c.new.method(:three_args))
+ end
+
+ def assert_curry_var_args(m)
+ curried = m.curry(3)
+ assert_equal([1, 2, 3], curried.(1).(2).(3), Feature9783)
+
+ curried = m.curry(2)
+ assert_equal([1, 2], curried.(1).(2), Feature9783)
+
+ curried = m.curry(0)
+ assert_equal([1], curried.(1), Feature9783)
+ end
+
+ def test_curry_var_args
+ c = Class.new {
+ def var_args(*args) args end
+ }
+ assert_curry_var_args(c.new.method(:var_args))
+ end
+
+ def test_curry_from_proc_var_args
+ c = Class.new {
+ define_method(:var_args) {|*args| args}
+ }
+ assert_curry_var_args(c.new.method(:var_args))
+ end
+
+ Feature9781 = '[ruby-core:62202] [Feature #9781]'
+
+ def test_super_method
+ o = Derived.new
+ m = o.method(:foo).super_method
+ assert_equal(Base, m.owner, Feature9781)
+ assert_same(o, m.receiver, Feature9781)
+ assert_equal(:foo, m.name, Feature9781)
+ m = assert_nothing_raised(NameError, Feature9781) {break m.super_method}
+ assert_nil(m, Feature9781)
+ end
+
+ def test_super_method_unbound
+ m = Derived.instance_method(:foo)
+ m = m.super_method
+ assert_equal(Base.instance_method(:foo), m, Feature9781)
+ m = assert_nothing_raised(NameError, Feature9781) {break m.super_method}
+ assert_nil(m, Feature9781)
+
+ bug11419 = '[ruby-core:70254]'
+ m = Object.instance_method(:tap)
+ m = assert_nothing_raised(NameError, bug11419) {break m.super_method}
+ assert_nil(m, bug11419)
+ end
+
+ def test_super_method_module
+ m1 = Module.new {def foo; end}
+ c1 = Class.new(Derived) {include m1; def foo; end}
+ m = c1.instance_method(:foo)
+ assert_equal(c1, m.owner, Feature9781)
+ m = m.super_method
+ assert_equal(m1, m.owner, Feature9781)
+ m = m.super_method
+ assert_equal(Derived, m.owner, Feature9781)
+ m = m.super_method
+ assert_equal(Base, m.owner, Feature9781)
+ m2 = Module.new {def foo; end}
+ o = c1.new.extend(m2)
+ m = o.method(:foo)
+ assert_equal(m2, m.owner, Feature9781)
+ m = m.super_method
+ assert_equal(c1, m.owner, Feature9781)
+ assert_same(o, m.receiver, Feature9781)
+
+ c1 = Class.new {def foo; end}
+ c2 = Class.new(c1) {include m1; include m2}
+ m = c2.instance_method(:foo)
+ assert_equal(m2, m.owner)
+ m = m.super_method
+ assert_equal(m1, m.owner)
+ m = m.super_method
+ assert_equal(c1, m.owner)
+ assert_nil(m.super_method)
+ end
+
+ def test_super_method_removed
+ c1 = Class.new {private def foo; end}
+ c2 = Class.new(c1) {public :foo}
+ c3 = Class.new(c2) {def foo; end}
+ c1.class_eval {undef foo}
+ m = c3.instance_method(:foo)
+ m = assert_nothing_raised(NameError, Feature9781) {break m.super_method}
+ assert_nil(m, Feature9781)
+ end
+
+ def test_prepended_public_zsuper
+ mod = EnvUtil.labeled_module("Mod") {private def foo; :ok end}
+ mods = [mod]
+ 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
+ m = obj.method(:foo)
+ assert_equal(mods, mods.map {m.owner.tap {m = m.super_method}})
+ assert_nil(m)
+ end
+
+ def test_super_method_with_prepended_module
+ bug = '[ruby-core:81666] [Bug #13656] should be the method of the parent'
+ c1 = EnvUtil.labeled_class("C1") {def m; end}
+ c2 = EnvUtil.labeled_class("C2", c1) {def m; end}
+ c2.prepend(EnvUtil.labeled_module("M"))
+ m1 = c1.instance_method(:m)
+ m2 = c2.instance_method(:m).super_method
+ assert_equal(m1, m2, bug)
+ assert_equal(c1, m2.owner, bug)
+ assert_equal(m1.source_location, m2.source_location, bug)
+ end
+
+ def test_super_method_after_bind
+ assert_nil String.instance_method(:length).bind(String.new).super_method,
+ '[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 rest_parameter(*rest)
+ rest
+ end
+
+ def test_splat_long_array
+ n = 10_000_000
+ assert_equal n , rest_parameter(*(1..n)).size, '[Feature #10440]'
+ end
+
+ class C
+ D = "Const_D"
+ def foo
+ a = b = c = a = b = c = 12345
+ end
+ end
+
+ def test_to_proc_binding
+ bug11012 = '[ruby-core:68673] [Bug #11012]'
+
+ b = C.new.method(:foo).to_proc.binding
+ assert_equal([], b.local_variables, bug11012)
+ assert_equal("Const_D", b.eval("D"), bug11012) # Check CREF
+
+ assert_raise(NameError, bug11012){ b.local_variable_get(:foo) }
+ assert_equal(123, b.local_variable_set(:foo, 123), bug11012)
+ assert_equal(123, b.local_variable_get(:foo), bug11012)
+ assert_equal(456, b.local_variable_set(:bar, 456), bug11012)
+ assert_equal(123, b.local_variable_get(:foo), bug11012)
+ assert_equal(456, b.local_variable_get(:bar), bug11012)
+ assert_equal([:bar, :foo], b.local_variables.sort, bug11012)
+ end
+
+ class MethodInMethodClass
+ def m1
+ def m2
+ end
+
+ self.class.send(:define_method, :m3){} # [Bug #11754]
+ end
+ private
+ end
+
+ def test_method_in_method_visibility_should_be_public
+ assert_equal([:m1].sort, MethodInMethodClass.public_instance_methods(false).sort)
+ assert_equal([].sort, MethodInMethodClass.private_instance_methods(false).sort)
+
+ MethodInMethodClass.new.m1
+ assert_equal([:m1, :m2, :m3].sort, MethodInMethodClass.public_instance_methods(false).sort)
+ assert_equal([].sort, MethodInMethodClass.private_instance_methods(false).sort)
+ end
+
+ def test_define_method_with_symbol
+ assert_normal_exit %q{
+ define_method(:foo, &:to_s)
+ define_method(:bar, :to_s.to_proc)
+ }, '[Bug #11850]'
+ c = Class.new{
+ define_method(:foo, &:to_s)
+ define_method(:bar, :to_s.to_proc)
+ }
+ obj = c.new
+ assert_equal('1', obj.foo(1))
+ assert_equal('1', obj.bar(1))
+ end
+
+ def test_argument_error_location
+ body = <<-'END_OF_BODY'
+ eval <<-'EOS'
+ $line_lambda = __LINE__; $f = lambda do
+ _x = 1
+ end
+ $line_method = __LINE__; def foo
+ _x = 1
+ end
+ begin
+ $f.call(1)
+ rescue ArgumentError => e
+ assert_equal "(eval):#{$line_lambda.to_s}:in `block in <main>'", e.backtrace.first
+ end
+ begin
+ foo(1)
+ rescue ArgumentError => e
+ assert_equal "(eval):#{$line_method}:in `foo'", e.backtrace.first
+ end
+ EOS
+ END_OF_BODY
+
+ assert_separately [], body
+ # without trace insn
+ assert_separately [], "RubyVM::InstructionSequence.compile_option = {trace_instruction: false}\n" + body
+ end
+
+ def test_eqq
+ assert_operator(0.method(:<), :===, 5)
+ assert_not_operator(0.method(:<), :===, -5)
+ end
+
+ def test_compose_with_method
+ c = Class.new {
+ def f(x) x * 2 end
+ def g(x) x + 1 end
+ }
+ f = c.new.method(:f)
+ g = c.new.method(:g)
+
+ assert_equal(6, (f << g).call(2))
+ assert_equal(6, (g >> f).call(2))
+ end
+
+ def test_compose_with_proc
+ c = Class.new {
+ def f(x) x * 2 end
+ }
+ f = c.new.method(:f)
+ g = proc {|x| x + 1}
+
+ assert_equal(6, (f << g).call(2))
+ assert_equal(6, (g >> f).call(2))
+ end
+
+ def test_compose_with_callable
+ c = Class.new {
+ def f(x) x * 2 end
+ }
+ c2 = Class.new {
+ def call(x) x + 1 end
+ }
+ f = c.new.method(:f)
+ g = c2.new
+
+ assert_equal(6, (f << g).call(2))
+ assert_equal(5, (f >> g).call(2))
+ end
+
+ def test_compose_with_noncallable
+ c = Class.new {
+ def f(x) x * 2 end
+ }
+ f = c.new.method(:f)
+
+ assert_raise(NoMethodError) {
+ (f << 5).call(2)
+ }
+ assert_raise(NoMethodError) {
+ (f >> 5).call(2)
+ }
+ end
end
diff --git a/test/ruby/test_mixed_unicode_escapes.rb b/test/ruby/test_mixed_unicode_escapes.rb
index 982b57e286..f0b4cc691f 100644
--- a/test/ruby/test_mixed_unicode_escapes.rb
+++ b/test/ruby/test_mixed_unicode_escapes.rb
@@ -1,5 +1,6 @@
# -*- coding: cp932 -*-
-# This test is in a differnt file than TestUnicodeEscapes
+# frozen_string_literal: false
+# This test is in a different file than TestUnicodeEscapes
# So that we can have a different coding comment above
require 'test/unit'
@@ -12,14 +13,18 @@ class TestMixedUnicodeEscape < Test::Unit::TestCase
# 8-bit character escapes are okay.
assert_equal("B\xFF", "\u0042\xFF")
- # sjis mb chars mixed with Unicode shound not work
+ # sjis mb chars mixed with Unicode should not work
assert_raise(SyntaxError) { eval %q("\u1234")}
assert_raise(SyntaxError) { eval %q("\u{1234}")}
+ # also should not work for Regexp
+ assert_raise(SyntaxError) { eval %q(/#{"\u1234"}#{""}/)}
+ assert_raise(RegexpError) { eval %q(/\u{1234}#{nil}/)}
+ assert_raise(RegexpError) { eval %q(/#{nil}\u1234/)}
+
# String interpolation turns into an expression and we get
# a different kind of error, but we still can't mix these
assert_raise(Encoding::CompatibilityError) { eval %q("\u{1234}#{nil}")}
assert_raise(Encoding::CompatibilityError) { eval %q("#{nil}\u1234")}
-
end
end
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index 0e4737dea6..ef78475424 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -1,6 +1,6 @@
+# frozen_string_literal: false
require 'test/unit'
require 'pp'
-require_relative 'envutil'
$m0 = Module.nesting
@@ -64,20 +64,6 @@ class TestModule < Test::Unit::TestCase
# Support stuff
- def remove_pp_mixins(list)
- list.reject {|c| c == PP::ObjectMixin }
- end
-
- def remove_json_mixins(list)
- list.reject {|c| c.to_s.start_with?("JSON") }
- end
-
- def remove_rake_mixins(list)
- list.
- reject {|c| c.to_s == "RakeFileUtils" }.
- reject {|c| c.to_s.start_with?("FileUtils") }
- end
-
module Mixin
MIXIN = 1
def mixin
@@ -89,6 +75,14 @@ class TestModule < Test::Unit::TestCase
include Mixin
def user
end
+
+ def user2
+ end
+ protected :user2
+
+ def user3
+ end
+ private :user3
end
module Other
@@ -141,7 +135,7 @@ class TestModule < Test::Unit::TestCase
:bClass3
end
end
-
+
class CClass < BClass
def self.cClass
end
@@ -165,60 +159,61 @@ class TestModule < Test::Unit::TestCase
end
def test_GE # '>='
- assert(Mixin >= User)
- assert(Mixin >= Mixin)
- assert(!(User >= Mixin))
+ assert_operator(Mixin, :>=, User)
+ assert_operator(Mixin, :>=, Mixin)
+ assert_not_operator(User, :>=, Mixin)
- assert(Object >= String)
- assert(String >= String)
- assert(!(String >= Object))
+ assert_operator(Object, :>=, String)
+ assert_operator(String, :>=, String)
+ assert_not_operator(String, :>=, Object)
end
def test_GT # '>'
- assert(Mixin > User)
- assert(!(Mixin > Mixin))
- assert(!(User > Mixin))
+ assert_operator(Mixin, :>, User)
+ assert_not_operator(Mixin, :>, Mixin)
+ assert_not_operator(User, :>, Mixin)
- assert(Object > String)
- assert(!(String > String))
- assert(!(String > Object))
+ assert_operator(Object, :>, String)
+ assert_not_operator(String, :>, String)
+ assert_not_operator(String, :>, Object)
end
def test_LE # '<='
- assert(User <= Mixin)
- assert(Mixin <= Mixin)
- assert(!(Mixin <= User))
+ assert_operator(User, :<=, Mixin)
+ assert_operator(Mixin, :<=, Mixin)
+ assert_not_operator(Mixin, :<=, User)
- assert(String <= Object)
- assert(String <= String)
- assert(!(Object <= String))
+ assert_operator(String, :<=, Object)
+ assert_operator(String, :<=, String)
+ assert_not_operator(Object, :<=, String)
end
def test_LT # '<'
- assert(User < Mixin)
- assert(!(Mixin < Mixin))
- assert(!(Mixin < User))
+ assert_operator(User, :<, Mixin)
+ assert_not_operator(Mixin, :<, Mixin)
+ assert_not_operator(Mixin, :<, User)
- assert(String < Object)
- assert(!(String < String))
- assert(!(Object < String))
+ assert_operator(String, :<, Object)
+ assert_not_operator(String, :<, String)
+ assert_not_operator(Object, :<, String)
end
def test_VERY_EQUAL # '==='
- assert(Object === self)
- assert(Test::Unit::TestCase === self)
- assert(TestModule === self)
- assert(!(String === self))
+ assert_operator(Object, :===, self)
+ assert_operator(Test::Unit::TestCase, :===, self)
+ assert_operator(TestModule, :===, self)
+ assert_not_operator(String, :===, self)
end
def test_ancestors
assert_equal([User, Mixin], User.ancestors)
assert_equal([Mixin], Mixin.ancestors)
- assert_equal([Object, Kernel, BasicObject],
- remove_rake_mixins(remove_json_mixins(remove_pp_mixins(Object.ancestors))))
- assert_equal([String, Comparable, Object, Kernel, BasicObject],
- remove_rake_mixins(remove_json_mixins(remove_pp_mixins(String.ancestors))))
+ ancestors = Object.ancestors
+ mixins = ancestors - [Object, Kernel, BasicObject]
+ mixins << JSON::Ext::Generator::GeneratorMethods::String if defined?(JSON::Ext::Generator::GeneratorMethods::String)
+ assert_equal([Object, Kernel, BasicObject], ancestors - mixins)
+ assert_equal([String, Comparable, Object, Kernel, BasicObject], String.ancestors - mixins)
end
CLASS_EVAL = 2
@@ -227,7 +222,7 @@ class TestModule < Test::Unit::TestCase
def test_class_eval
Other.class_eval("CLASS_EVAL = 1")
assert_equal(1, Other::CLASS_EVAL)
- assert(Other.constants.include?(:CLASS_EVAL))
+ assert_include(Other.constants, :CLASS_EVAL)
assert_equal(2, Other.class_eval { CLASS_EVAL })
Other.class_eval("@@class_eval = 'a'")
@@ -247,24 +242,144 @@ class TestModule < Test::Unit::TestCase
end
def test_const_defined?
- assert(Math.const_defined?(:PI))
- assert(Math.const_defined?("PI"))
- assert(!Math.const_defined?(:IP))
- assert(!Math.const_defined?("IP"))
+ assert_operator(Math, :const_defined?, :PI)
+ assert_operator(Math, :const_defined?, "PI")
+ assert_not_operator(Math, :const_defined?, :IP)
+ assert_not_operator(Math, :const_defined?, "IP")
+ end
+
+ def each_bad_constants(m, &b)
+ [
+ "#<Class:0x7b8b718b>",
+ ":Object",
+ "",
+ ":",
+ ["String::", "[Bug #7573]"],
+ "\u3042",
+ "Name?",
+ ].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
+ yield name
+ end
+ end
+ end
+
+ def test_bad_constants_get
+ each_bad_constants("get") {|name|
+ Object.const_get name
+ }
+ end
+
+ def test_bad_constants_defined
+ each_bad_constants("defined?") {|name|
+ Object.const_defined? name
+ }
+ end
+
+ def test_leading_colons
+ assert_equal Object, AClass.const_get('::Object')
end
def test_const_get
assert_equal(Math::PI, Math.const_get("PI"))
assert_equal(Math::PI, Math.const_get(:PI))
+
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "PI"; end
+ def n.count; @count; end
+ assert_equal(Math::PI, Math.const_get(n))
+ assert_equal(1, n.count)
+ end
+
+ def test_nested_get
+ assert_equal Other, Object.const_get([self.class, 'Other'].join('::'))
+ assert_equal User::USER, self.class.const_get([User, 'USER'].join('::'))
+ assert_raise(NameError) {
+ Object.const_get([self.class.name, 'String'].join('::'))
+ }
+ end
+
+ def test_nested_get_symbol
+ const = [self.class, Other].join('::').to_sym
+ assert_raise(NameError) {Object.const_get(const)}
+
+ const = [User, 'USER'].join('::').to_sym
+ assert_raise(NameError) {self.class.const_get(const)}
+ end
+
+ def test_nested_get_const_missing
+ classes = []
+ klass = Class.new {
+ define_singleton_method(:const_missing) { |name|
+ classes << name
+ klass
+ }
+ }
+ klass.const_get("Foo::Bar::Baz")
+ assert_equal [:Foo, :Bar, :Baz], classes
+ end
+
+ def test_nested_get_bad_class
+ assert_raise(TypeError) do
+ self.class.const_get([User, 'USER', 'Foo'].join('::'))
+ end
+ end
+
+ def test_nested_defined
+ 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'])
+ assert_not_send([Object, :const_defined?, [self.class.name, 'String'].join('::')])
+ end
+
+ def test_nested_defined_symbol
+ const = [self.class, Other].join('::').to_sym
+ assert_raise(NameError) {Object.const_defined?(const)}
+
+ const = [User, 'USER'].join('::').to_sym
+ assert_raise(NameError) {self.class.const_defined?(const)}
+ end
+
+ def test_nested_defined_inheritance
+ assert_send([Object, :const_defined?, [self.class.name, 'User', 'MIXIN'].join('::')])
+ assert_send([self.class, :const_defined?, 'User::MIXIN'])
+ assert_send([Object, :const_defined?, 'File::SEEK_SET'])
+
+ # const_defined? with `false`
+ assert_not_send([Object, :const_defined?, [self.class.name, 'User', 'MIXIN'].join('::'), false])
+ assert_not_send([self.class, :const_defined?, 'User::MIXIN', false])
+ assert_not_send([Object, :const_defined?, 'File::SEEK_SET', false])
+ end
+
+ def test_nested_defined_bad_class
+ assert_raise(TypeError) do
+ self.class.const_defined?('User::USER::Foo')
+ end
end
def test_const_set
- assert(!Other.const_defined?(:KOALA))
+ assert_not_operator(Other, :const_defined?, :KOALA)
Other.const_set(:KOALA, 99)
- assert(Other.const_defined?(:KOALA))
+ assert_operator(Other, :const_defined?, :KOALA)
assert_equal(99, Other::KOALA)
Other.const_set("WOMBAT", "Hi")
assert_equal("Hi", Other::WOMBAT)
+
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "HOGE"; end
+ def n.count; @count; end
+ def n.count=(v); @count=v; end
+ assert_not_operator(Other, :const_defined?, :HOGE)
+ Other.const_set(n, 999)
+ assert_equal(1, n.count)
+ n.count = 0
+ assert_equal(999, Other.const_get(n))
+ assert_equal(1, n.count)
+ n.count = 0
+ assert_equal(true, Other.const_defined?(n))
+ assert_equal(1, n.count)
end
def test_constants
@@ -272,38 +387,138 @@ 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)
+ end
+ assert_equal(:ok, Object.new.extend(m).foo, bug9535)
+ end
+
+ def test_initialize_copy_empty
+ bug9813 = '[ruby-dev:48182] [Bug #9813]'
+ m = Module.new do
+ def x
+ end
+ const_set(:X, 1)
+ @x = 2
+ end
+ 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)
+ end
+ assert_empty(m.instance_methods, bug9813)
+ assert_empty(m.instance_variables, bug9813)
+ assert_empty(m.constants, bug9813)
+ end
+
+ def test_dup
+ bug6454 = '[ruby-core:45132]'
+
+ a = Module.new
+ Other.const_set :BUG6454, a
+ b = a.dup
+ Other.const_set :BUG6454_dup, b
+
+ assert_equal "TestModule::Other::BUG6454_dup", b.inspect, bug6454
+ end
+
+ def test_dup_anonymous
+ bug6454 = '[ruby-core:45132]'
+
+ a = Module.new
+ original = a.inspect
+
+ b = a.dup
+
+ assert_not_equal original, b.inspect, bug6454
+ end
+
+ def test_public_include
+ assert_nothing_raised('#8846') do
+ Module.new.include(Module.new { def foo; end }).instance_methods == [:foo]
+ end
+ end
+
+ def test_include_toplevel
+ assert_separately([], <<-EOS)
+ Mod = Module.new {def foo; :include_foo end}
+ TOPLEVEL_BINDING.eval('include Mod')
+
+ assert_equal(:include_foo, TOPLEVEL_BINDING.eval('foo'))
+ assert_equal([Object, Mod], Object.ancestors.slice(0, 2))
+ EOS
+ end
+
+ def test_include_with_no_args
+ assert_raise(ArgumentError) { Module.new { include } }
+ end
+
def test_included_modules
assert_equal([], Mixin.included_modules)
assert_equal([Mixin], User.included_modules)
- assert_equal([Kernel],
- remove_rake_mixins(remove_json_mixins(remove_pp_mixins(Object.included_modules))))
- assert_equal([Comparable, Kernel],
- remove_rake_mixins(remove_json_mixins(remove_pp_mixins(String.included_modules))))
+
+ mixins = Object.included_modules - [Kernel]
+ mixins << JSON::Ext::Generator::GeneratorMethods::String if defined?(JSON::Ext::Generator::GeneratorMethods::String)
+ assert_equal([Kernel], Object.included_modules - mixins)
+ assert_equal([Comparable, Kernel], String.included_modules - mixins)
end
def test_instance_methods
- assert_equal([:user], User.instance_methods(false))
- assert_equal([:user, :mixin].sort, User.instance_methods(true).sort)
+ assert_equal([:user, :user2], User.instance_methods(false).sort)
+ assert_equal([:user, :user2, :mixin].sort, User.instance_methods(true).sort)
assert_equal([:mixin], Mixin.instance_methods)
assert_equal([:mixin], Mixin.instance_methods(true))
assert_equal([:cClass], (class << CClass; self; end).instance_methods(false))
assert_equal([], (class << BClass; self; end).instance_methods(false))
assert_equal([:cm2], (class << AClass; self; end).instance_methods(false))
- # Ruby 1.8 feature change:
- # #instance_methods includes protected methods.
- #assert_equal([:aClass], AClass.instance_methods(false))
assert_equal([:aClass, :aClass2], AClass.instance_methods(false).sort)
assert_equal([:aClass, :aClass2],
(AClass.instance_methods(true) - Object.instance_methods(true)).sort)
end
def test_method_defined?
- assert_method_not_defined?(User, :wombat)
- assert_method_defined?(User, :user)
- assert_method_defined?(User, :mixin)
- assert_method_not_defined?(User, :wombat)
- assert_method_defined?(User, :user)
- assert_method_defined?(User, :mixin)
+ [User, Class.new{include User}, Class.new{prepend User}].each do |klass|
+ [[], [true]].each do |args|
+ assert !klass.method_defined?(:wombat, *args)
+ assert klass.method_defined?(:mixin, *args)
+ assert klass.method_defined?(:user, *args)
+ assert klass.method_defined?(:user2, *args)
+ assert !klass.method_defined?(:user3, *args)
+
+ assert !klass.method_defined?("wombat", *args)
+ assert klass.method_defined?("mixin", *args)
+ assert klass.method_defined?("user", *args)
+ assert klass.method_defined?("user2", *args)
+ assert !klass.method_defined?("user3", *args)
+ end
+ end
+ end
+
+ def test_method_defined_without_include_super
+ assert User.method_defined?(:user, false)
+ assert !User.method_defined?(:mixin, false)
+ assert Mixin.method_defined?(:mixin, false)
+
+ User.const_set(:FOO, c = Class.new)
+
+ c.prepend(User)
+ assert !c.method_defined?(:user, false)
+ c.define_method(:user){}
+ assert c.method_defined?(:user, false)
+
+ assert !c.method_defined?(:mixin, false)
+ c.define_method(:mixin){}
+ assert c.method_defined?(:mixin, false)
+
+ assert !c.method_defined?(:userx, false)
+ c.define_method(:userx){}
+ assert c.method_defined?(:userx, false)
end
def module_exec_aux
@@ -339,17 +554,38 @@ class TestModule < Test::Unit::TestCase
def test_module_eval
User.module_eval("MODULE_EVAL = 1")
assert_equal(1, User::MODULE_EVAL)
- assert(User.constants.include?(:MODULE_EVAL))
+ assert_include(User.constants, :MODULE_EVAL)
User.instance_eval("remove_const(:MODULE_EVAL)")
- assert(!User.constants.include?(:MODULE_EVAL))
+ assert_not_include(User.constants, :MODULE_EVAL)
end
def test_name
- assert_equal("Fixnum", Fixnum.name)
+ assert_equal("Integer", Integer.name)
assert_equal("TestModule::Mixin", Mixin.name)
assert_equal("TestModule::User", User.name)
end
+ def test_classpath
+ m = Module.new
+ n = Module.new
+ m.const_set(:N, n)
+ assert_nil(m.name)
+ assert_nil(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(/\A#<Module:.*>::O\z/, m::O.name)
+ assert_match(/\A#<Module:.*>::C\z/, m::C.name)
+ self.class.const_set(:M, m)
+ prefix = self.class.name + "::M::"
+ assert_equal(prefix+"N", m.const_get(:N).name)
+ assert_equal(prefix+"O", m.const_get(:O).name)
+ assert_equal(prefix+"C", m.const_get(:C).name)
+ end
+
def test_private_class_method
assert_raise(ExpectedException) { AClass.cm1 }
assert_raise(ExpectedException) { AClass.cm3 }
@@ -403,6 +639,13 @@ class TestModule < Test::Unit::TestCase
p Module.constants - ary, Module.constants(true), Module.constants(false)
INPUT
assert_in_out_err([], src, %w([:M] [:WALTER] []), [])
+
+ klass = Class.new do
+ const_set(:X, 123)
+ end
+ assert_equal(false, klass.class_eval { Module.constants }.include?(:X))
+
+ assert_equal(false, Complex.constants(false).include?(:compatible))
end
module M1
@@ -425,13 +668,22 @@ class TestModule < Test::Unit::TestCase
end
def test_freeze
- m = Module.new
+ m = Module.new do
+ def self.baz; end
+ def bar; end
+ end
m.freeze
- assert_raise(RuntimeError) do
+ assert_raise(FrozenError) do
m.module_eval do
def foo; end
end
end
+ assert_raise(FrozenError) do
+ m.__send__ :private, :bar
+ end
+ assert_raise(FrozenError) do
+ m.private_class_method :baz
+ end
end
def test_attr_obsoleted_flag
@@ -451,7 +703,7 @@ class TestModule < Test::Unit::TestCase
assert_equal(false, o.respond_to?(:bar=))
end
- def test_const_get2
+ def test_const_get_evaled
c1 = Class.new
c2 = Class.new(c1)
@@ -466,6 +718,9 @@ class TestModule < Test::Unit::TestCase
assert_raise(NameError) { c2::Bar }
assert_raise(NameError) { c2.const_get(:Bar) }
assert_raise(NameError) { c2.const_get(:Bar, false) }
+ assert_raise(NameError) { c2.const_get("Bar", false) }
+ assert_raise(NameError) { c2.const_get("BaR11", false) }
+ assert_raise(NameError) { Object.const_get("BaR11", false) }
c1.instance_eval do
def const_missing(x)
@@ -477,18 +732,104 @@ class TestModule < Test::Unit::TestCase
assert_equal(:Bar, c2::Bar)
assert_equal(:Bar, c2.const_get(:Bar))
assert_equal(:Bar, c2.const_get(:Bar, false))
+ assert_equal(:Bar, c2.const_get("Bar"))
+ assert_equal(:Bar, c2.const_get("Bar", false))
+
+ v = c2.const_get("Bar11", false)
+ assert_equal("Bar11".to_sym, v)
assert_raise(NameError) { c1.const_get(:foo) }
end
- def test_const_set2
+ def test_const_set_invalid_name
+ c1 = Class.new
+ assert_raise_with_message(NameError, /foo/) { c1.const_set(:foo, :foo) }
+ assert_raise_with_message(NameError, /bar/) { c1.const_set("bar", :foo) }
+ assert_raise_with_message(TypeError, /1/) { c1.const_set(1, :foo) }
+ assert_nothing_raised(NameError) { c1.const_set("X\u{3042}", :foo) }
+ assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-16be"), :foo) }
+ assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-16le"), :foo) }
+ assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-32be"), :foo) }
+ assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-32le"), :foo) }
+ cx = EnvUtil.labeled_class("X\u{3042}")
+ assert_raise_with_message(TypeError, /X\u{3042}/) { c1.const_set(cx, :foo) }
+ end
+
+ def test_const_get_invalid_name
c1 = Class.new
- assert_raise(NameError) { c1.const_set(:foo, :foo) }
+ assert_raise(NameError) { c1.const_get(:foo) }
+ bug5084 = '[ruby-dev:44200]'
+ assert_raise(TypeError, bug5084) { c1.const_get(1) }
+ bug7574 = '[ruby-dev:46749]'
+ assert_raise_with_message(NameError, "wrong constant name \"String\\u0000\"", bug7574) {
+ Object.const_get("String\0")
+ }
end
- def test_const_get3
+ def test_const_defined_invalid_name
c1 = Class.new
assert_raise(NameError) { c1.const_defined?(:foo) }
+ bug5084 = '[ruby-dev:44200]'
+ assert_raise(TypeError, bug5084) { c1.const_defined?(1) }
+ bug7574 = '[ruby-dev:46749]'
+ assert_raise_with_message(NameError, "wrong constant name \"String\\u0000\"", bug7574) {
+ Object.const_defined?("String\0")
+ }
+ end
+
+ def test_const_get_no_inherited
+ bug3422 = '[ruby-core:30719]'
+ assert_in_out_err([], <<-INPUT, %w[1 NameError A], [], bug3422)
+ BasicObject::A = 1
+ puts [true, false].map {|inh|
+ begin
+ Object.const_get(:A, inh)
+ rescue NameError => e
+ [e.class, e.name]
+ end
+ }
+ INPUT
+ end
+
+ def test_const_get_inherited
+ bug3423 = '[ruby-core:30720]'
+ assert_in_out_err([], <<-INPUT, %w[NameError A NameError A], [], bug3423)
+ module Foo; A = 1; end
+ class Object; include Foo; end
+ class Bar; include Foo; end
+
+ puts [Object, Bar].map {|klass|
+ begin
+ klass.const_get(:A, false)
+ rescue NameError => e
+ [e.class, e.name]
+ end
+ }
+ INPUT
+ end
+
+ def test_const_in_module
+ bug3423 = '[ruby-core:37698]'
+ assert_in_out_err([], <<-INPUT, %w[ok], [], bug3423)
+ module LangModuleSpecInObject
+ module LangModuleTop
+ end
+ end
+ include LangModuleSpecInObject
+ module LangModuleTop
+ end
+ puts "ok" if LangModuleSpecInObject::LangModuleTop == LangModuleTop
+ INPUT
+
+ bug5264 = '[ruby-core:39227]'
+ assert_in_out_err([], <<-'INPUT', [], [], bug5264)
+ class A
+ class X; end
+ end
+ class B < A
+ module X; end
+ end
+ INPUT
end
def test_class_variable_get
@@ -496,14 +837,35 @@ class TestModule < Test::Unit::TestCase
c.class_eval('@@foo = :foo')
assert_equal(:foo, c.class_variable_get(:@@foo))
assert_raise(NameError) { c.class_variable_get(:@@bar) } # c.f. instance_variable_get
+ assert_raise(NameError) { c.class_variable_get(:'@@') }
+ assert_raise(NameError) { c.class_variable_get('@@') }
assert_raise(NameError) { c.class_variable_get(:foo) }
+ assert_raise(NameError) { c.class_variable_get("bar") }
+ assert_raise(TypeError) { c.class_variable_get(1) }
+
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@@foo"; end
+ def n.count; @count; end
+ assert_equal(:foo, c.class_variable_get(n))
+ assert_equal(1, n.count)
end
def test_class_variable_set
c = Class.new
c.class_variable_set(:@@foo, :foo)
assert_equal(:foo, c.class_eval('@@foo'))
+ assert_raise(NameError) { c.class_variable_set(:'@@', 1) }
+ assert_raise(NameError) { c.class_variable_set('@@', 1) }
assert_raise(NameError) { c.class_variable_set(:foo, 1) }
+ assert_raise(NameError) { c.class_variable_set("bar", 1) }
+ assert_raise(TypeError) { c.class_variable_set(1, 1) }
+
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@@foo"; end
+ def n.count; @count; end
+ c.class_variable_set(n, :bar)
+ assert_equal(:bar, c.class_eval('@@foo'))
+ assert_equal(1, n.count)
end
def test_class_variable_defined
@@ -511,7 +873,16 @@ class TestModule < Test::Unit::TestCase
c.class_eval('@@foo = :foo')
assert_equal(true, c.class_variable_defined?(:@@foo))
assert_equal(false, c.class_variable_defined?(:@@bar))
+ assert_raise(NameError) { c.class_variable_defined?(:'@@') }
+ assert_raise(NameError) { c.class_variable_defined?('@@') }
assert_raise(NameError) { c.class_variable_defined?(:foo) }
+ assert_raise(NameError) { c.class_variable_defined?("bar") }
+ assert_raise(TypeError) { c.class_variable_defined?(1) }
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@@foo"; end
+ def n.count; @count; end
+ assert_equal(true, c.class_variable_defined?(n))
+ assert_equal(1, n.count)
end
def test_remove_class_variable
@@ -519,6 +890,9 @@ class TestModule < Test::Unit::TestCase
c.class_eval('@@foo = :foo')
c.class_eval { remove_class_variable(:@@foo) }
assert_equal(false, c.class_variable_defined?(:@@foo))
+ assert_raise(NameError) do
+ c.class_eval { remove_class_variable(:@var) }
+ end
end
def test_export_method
@@ -529,7 +903,7 @@ class TestModule < Test::Unit::TestCase
end
def test_attr
- assert_in_out_err([], <<-INPUT, %w(:ok nil), /warning: private attribute\?$/)
+ assert_in_out_err([], <<-INPUT, %w(nil))
$VERBOSE = true
c = Class.new
c.instance_eval do
@@ -537,7 +911,6 @@ class TestModule < Test::Unit::TestCase
attr_reader :foo
end
o = c.new
- o.foo rescue p(:ok)
p(o.instance_eval { foo })
INPUT
@@ -548,13 +921,6 @@ class TestModule < Test::Unit::TestCase
end
def test_undef
- assert_raise(SecurityError) do
- Thread.new do
- $SAFE = 4
- Class.instance_eval { undef_method(:foo) }
- end.join
- end
-
c = Class.new
assert_raise(NameError) do
c.instance_eval { undef_method(:foo) }
@@ -571,7 +937,7 @@ class TestModule < Test::Unit::TestCase
end
%w(object_id __send__ initialize).each do |n|
- assert_in_out_err([], <<-INPUT, [], /warning: undefining `#{n}' may cause serious problems$/)
+ assert_in_out_err([], <<-INPUT, [], %r"warning: undefining `#{n}' may cause serious problems$")
$VERBOSE = false
Class.new.instance_eval { undef_method(:#{n}) }
INPUT
@@ -604,30 +970,52 @@ class TestModule < Test::Unit::TestCase
m.instance_eval { remove_const(:Foo) }
end
- def test_frozen_class
+ class Bug9413
+ class << self
+ Foo = :foo
+ end
+ end
+
+ def test_singleton_constants
+ bug9413 = '[ruby-core:59763] [Bug #9413]'
+ c = Bug9413.singleton_class
+ assert_include(c.constants(true), :Foo, bug9413)
+ assert_include(c.constants(false), :Foo, bug9413)
+ end
+
+ def test_frozen_module
m = Module.new
m.freeze
- assert_raise(RuntimeError) do
+ assert_raise(FrozenError) do
m.instance_eval { undef_method(:foo) }
end
+ end
+ def test_frozen_class
c = Class.new
c.freeze
- assert_raise(RuntimeError) do
+ assert_raise(FrozenError) do
c.instance_eval { undef_method(:foo) }
end
+ end
- o = Object.new
+ def test_frozen_singleton_class
+ klass = Class.new
+ o = klass.new
c = class << o; self; end
c.freeze
- assert_raise(RuntimeError) do
+ assert_raise_with_message(FrozenError, /frozen/) do
c.instance_eval { undef_method(:foo) }
end
+ klass.class_eval do
+ def self.foo
+ end
+ end
end
def test_method_defined
- c = Class.new
- c.class_eval do
+ cl = Class.new
+ def_methods = proc do
def foo; end
def bar; end
def baz; end
@@ -635,30 +1023,46 @@ class TestModule < Test::Unit::TestCase
protected :bar
private :baz
end
-
- assert_equal(true, c.public_method_defined?(:foo))
- assert_equal(false, c.public_method_defined?(:bar))
- assert_equal(false, c.public_method_defined?(:baz))
-
- assert_equal(false, c.protected_method_defined?(:foo))
- assert_equal(true, c.protected_method_defined?(:bar))
- assert_equal(false, c.protected_method_defined?(:baz))
-
- assert_equal(false, c.private_method_defined?(:foo))
- assert_equal(false, c.private_method_defined?(:bar))
- assert_equal(true, c.private_method_defined?(:baz))
- end
-
- def test_change_visibility_under_safe4
- c = Class.new
- c.class_eval do
- def foo; end
- end
- assert_raise(SecurityError) do
- Thread.new do
- $SAFE = 4
- c.class_eval { private :foo }
- end.join
+ cl.class_eval(&def_methods)
+ sc = Class.new(cl)
+ mod = Module.new(&def_methods)
+ only_prepend = Class.new{prepend(mod)}
+ empty_prepend = cl.clone
+ empty_prepend.prepend(Module.new)
+ overlap_prepend = cl.clone
+ overlap_prepend.prepend(mod)
+
+ [[], [true], [false]].each do |args|
+ [cl, sc, only_prepend, empty_prepend, overlap_prepend].each do |c|
+ always_false = [sc, only_prepend].include?(c) && args == [false]
+
+ assert_equal(always_false ? false : true, c.public_method_defined?(:foo, *args))
+ assert_equal(always_false ? false : false, c.public_method_defined?(:bar, *args))
+ assert_equal(always_false ? false : false, c.public_method_defined?(:baz, *args))
+
+ # Test if string arguments are converted to symbols
+ assert_equal(always_false ? false : true, c.public_method_defined?("foo", *args))
+ assert_equal(always_false ? false : false, c.public_method_defined?("bar", *args))
+ assert_equal(always_false ? false : false, c.public_method_defined?("baz", *args))
+
+ assert_equal(always_false ? false : false, c.protected_method_defined?(:foo, *args))
+ assert_equal(always_false ? false : true, c.protected_method_defined?(:bar, *args))
+ assert_equal(always_false ? false : false, c.protected_method_defined?(:baz, *args))
+
+ # Test if string arguments are converted to symbols
+ assert_equal(always_false ? false : false, c.protected_method_defined?("foo", *args))
+ assert_equal(always_false ? false : true, c.protected_method_defined?("bar", *args))
+ assert_equal(always_false ? false : false, c.protected_method_defined?("baz", *args))
+
+ assert_equal(always_false ? false : false, c.private_method_defined?(:foo, *args))
+ assert_equal(always_false ? false : false, c.private_method_defined?(:bar, *args))
+ assert_equal(always_false ? false : true, c.private_method_defined?(:baz, *args))
+
+ # Test if string arguments are converted to symbols
+ assert_equal(always_false ? false : false, c.private_method_defined?("foo", *args))
+ assert_equal(always_false ? false : false, c.private_method_defined?("bar", *args))
+ assert_equal(always_false ? false : true, c.private_method_defined?("baz", *args))
+ end
end
end
@@ -758,24 +1162,6 @@ class TestModule < Test::Unit::TestCase
assert_equal(false, m.include?(m))
end
- def test_include_under_safe4
- m = Module.new
- c1 = Class.new
- assert_raise(SecurityError) do
- lambda {
- $SAFE = 4
- c1.instance_eval { include(m) }
- }.call
- end
- assert_nothing_raised do
- lambda {
- $SAFE = 4
- c2 = Class.new
- c2.instance_eval { include(m) }
- }.call
- end
- end
-
def test_send
a = AClass.new
assert_equal(:aClass, a.__send__(:aClass))
@@ -796,6 +1182,8 @@ class TestModule < Test::Unit::TestCase
assert_equal("C\u{df}", c.name, '[ruby-core:24600]')
c = eval("class C\u{df}; self; end")
assert_equal("TestModule::C\u{df}", c.name, '[ruby-core:24600]')
+ c = Module.new.module_eval("class X\u{df} < Module; self; end")
+ assert_match(/::X\u{df}:/, c.new.to_s)
end
def test_method_added
@@ -819,16 +1207,74 @@ class TestModule < Test::Unit::TestCase
assert_equal [:f], memo.shift, '[ruby-core:25536]'
assert_equal mod.instance_method(:f), memo.shift
assert_equal :g, memo.shift
- assert_equal [:f, :g], memo.shift
+ assert_equal [:f, :g].sort, memo.shift.sort
assert_equal mod.instance_method(:f), memo.shift
assert_equal :a, memo.shift
- assert_equal [:f, :g, :a], memo.shift
+ assert_equal [:f, :g, :a].sort, memo.shift.sort
assert_equal mod.instance_method(:a), memo.shift
assert_equal :a=, memo.shift
- assert_equal [:f, :g, :a, :a=], memo.shift
+ assert_equal [:f, :g, :a, :a=].sort, memo.shift.sort
assert_equal mod.instance_method(:a=), memo.shift
end
+ def test_method_undefined
+ added = []
+ undefed = []
+ removed = []
+ mod = Module.new do
+ mod = self
+ def f
+ end
+ (class << self ; self ; end).class_eval do
+ define_method :method_added do |sym|
+ added << sym
+ end
+ define_method :method_undefined do |sym|
+ undefed << sym
+ end
+ define_method :method_removed do |sym|
+ removed << sym
+ end
+ end
+ end
+ assert_method_defined?(mod, :f)
+ mod.module_eval do
+ undef :f
+ end
+ assert_equal [], added
+ assert_equal [:f], undefed
+ assert_equal [], removed
+ end
+
+ def test_method_removed
+ added = []
+ undefed = []
+ removed = []
+ mod = Module.new do
+ mod = self
+ def f
+ end
+ (class << self ; self ; end).class_eval do
+ define_method :method_added do |sym|
+ added << sym
+ end
+ define_method :method_undefined do |sym|
+ undefed << sym
+ end
+ define_method :method_removed do |sym|
+ removed << sym
+ end
+ end
+ end
+ assert_method_defined?(mod, :f)
+ mod.module_eval do
+ remove_method :f
+ end
+ assert_equal [], added
+ assert_equal [], undefed
+ assert_equal [:f], removed
+ end
+
def test_method_redefinition
feature2155 = '[ruby-dev:39400]'
@@ -842,23 +1288,21 @@ class TestModule < Test::Unit::TestCase
assert_match(/:#{line}: warning: method redefined; discarding old foo/, stderr)
assert_match(/:#{line-1}: warning: previous definition of foo/, stderr, feature2155)
- stderr = EnvUtil.verbose_warning do
+ assert_warning '' do
Module.new do
def foo; end
alias bar foo
def foo; end
end
end
- assert_equal("", stderr)
- stderr = EnvUtil.verbose_warning do
+ assert_warning '' do
Module.new do
def foo; end
alias bar foo
alias bar foo
end
end
- assert_equal("", stderr)
line = __LINE__+4
stderr = EnvUtil.verbose_warning do
@@ -870,31 +1314,42 @@ class TestModule < Test::Unit::TestCase
assert_match(/:#{line}: warning: method redefined; discarding old foo/, stderr)
assert_match(/:#{line-1}: warning: previous definition of foo/, stderr, feature2155)
- stderr = EnvUtil.verbose_warning do
+ assert_warning '' do
Module.new do
define_method(:foo) do end
alias bar foo
- alias barf oo
+ alias bar foo
end
end
- assert_equal("", stderr)
- stderr = EnvUtil.verbose_warning do
+ assert_warning('', '[ruby-dev:39397]') do
Module.new do
module_function
def foo; end
module_function :foo
end
end
- assert_equal("", stderr, '[ruby-dev:39397]')
- stderr = EnvUtil.verbose_warning do
+ assert_warning '' do
Module.new do
def foo; end
undef foo
end
end
- assert_equal("", stderr)
+
+ stderr = EnvUtil.verbose_warning do
+ Module.new do
+ def foo; end
+ mod = self
+ c = Class.new do
+ include mod
+ end
+ c.new.foo
+ def foo; end
+ end
+ end
+ assert_match(/: warning: method redefined; discarding old foo/, stderr)
+ assert_match(/: warning: previous definition of foo/, stderr)
end
def test_protected_singleton_method
@@ -929,7 +1384,6 @@ class TestModule < Test::Unit::TestCase
def test_attr_inherited_visibility
bug3406 = '[ruby-core:30638]'
- skip(bug3406)
c = Class.new do
class << self
private
@@ -940,4 +1394,1007 @@ class TestModule < Test::Unit::TestCase
assert_nothing_raised(bug3406) {c.x = 1}
assert_equal(1, c.x, bug3406)
end
+
+ def test_attr_writer_with_no_arguments
+ bug8540 = "[ruby-core:55543]"
+ c = Class.new do
+ attr_writer :foo
+ end
+ assert_raise(ArgumentError, bug8540) { c.new.send :foo= }
+ end
+
+ def test_private_constant_in_class
+ c = Class.new
+ c.const_set(:FOO, "foo")
+ assert_equal("foo", c::FOO)
+ c.private_constant(:FOO)
+ e = assert_raise(NameError) {c::FOO}
+ assert_equal(c, e.receiver)
+ assert_equal(:FOO, e.name)
+ assert_equal("foo", c.class_eval("FOO"))
+ assert_equal("foo", c.const_get("FOO"))
+ $VERBOSE, verbose = nil, $VERBOSE
+ c.const_set(:FOO, "foo")
+ $VERBOSE = verbose
+ e = assert_raise(NameError) {c::FOO}
+ assert_equal(c, e.receiver)
+ assert_equal(:FOO, e.name)
+ e = assert_raise_with_message(NameError, /#{c}::FOO/) do
+ Class.new(c)::FOO
+ end
+ assert_equal(c, e.receiver)
+ assert_equal(:FOO, e.name)
+ end
+
+ def test_private_constant_in_module
+ m = Module.new
+ m.const_set(:FOO, "foo")
+ assert_equal("foo", m::FOO)
+ m.private_constant(:FOO)
+ e = assert_raise(NameError) {m::FOO}
+ assert_equal(m, e.receiver)
+ assert_equal(:FOO, e.name)
+ assert_equal("foo", m.class_eval("FOO"))
+ assert_equal("foo", m.const_get("FOO"))
+ $VERBOSE, verbose = nil, $VERBOSE
+ m.const_set(:FOO, "foo")
+ $VERBOSE = verbose
+ e = assert_raise(NameError) {m::FOO}
+ assert_equal(m, e.receiver)
+ assert_equal(:FOO, e.name)
+ e = assert_raise(NameError, /#{m}::FOO/) do
+ Module.new {include m}::FOO
+ end
+ assert_equal(m, e.receiver)
+ assert_equal(:FOO, e.name)
+ e = assert_raise(NameError, /#{m}::FOO/) do
+ Class.new {include m}::FOO
+ end
+ assert_equal(m, e.receiver)
+ assert_equal(:FOO, e.name)
+ end
+
+ def test_private_constant2
+ c = Class.new
+ c.const_set(:FOO, "foo")
+ c.const_set(:BAR, "bar")
+ assert_equal("foo", c::FOO)
+ assert_equal("bar", c::BAR)
+ c.private_constant(:FOO, :BAR)
+ assert_raise(NameError) { c::FOO }
+ assert_raise(NameError) { c::BAR }
+ assert_equal("foo", c.class_eval("FOO"))
+ assert_equal("bar", c.class_eval("BAR"))
+ end
+
+ def test_private_constant_with_no_args
+ assert_in_out_err([], <<-RUBY, [], ["-:3: warning: private_constant with no argument is just ignored"])
+ $-w = true
+ class X
+ private_constant
+ end
+ RUBY
+ end
+
+ def test_private_constant_const_missing
+ c = Class.new
+ c.const_set(:FOO, "foo")
+ c.private_constant(:FOO)
+ class << c
+ attr_reader :const_missing_arg
+ def const_missing(name)
+ @const_missing_arg = name
+ name == :FOO ? const_get(:FOO) : super
+ end
+ end
+ assert_equal("foo", c::FOO)
+ assert_equal(:FOO, c.const_missing_arg)
+ end
+
+ class PrivateClass
+ end
+ private_constant :PrivateClass
+
+ def test_define_module_under_private_constant
+ assert_raise(NameError) do
+ eval %q{class TestModule::PrivateClass; end}
+ end
+ assert_raise(NameError) do
+ eval %q{module TestModule::PrivateClass::TestModule; end}
+ end
+ eval %q{class PrivateClass; end}
+ eval %q{module PrivateClass::TestModule; end}
+ assert_instance_of(Module, PrivateClass::TestModule)
+ PrivateClass.class_eval { remove_const(:TestModule) }
+ end
+
+ def test_public_constant
+ c = Class.new
+ c.const_set(:FOO, "foo")
+ assert_equal("foo", c::FOO)
+ c.private_constant(:FOO)
+ assert_raise(NameError) { c::FOO }
+ assert_equal("foo", c.class_eval("FOO"))
+ c.public_constant(:FOO)
+ assert_equal("foo", c::FOO)
+ end
+
+ def test_deprecate_constant
+ 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}
+ 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)
+ end
+
+ def test_constants_with_private_constant
+ assert_not_include(::TestModule.constants, :PrivateClass)
+ assert_not_include(::TestModule.constants(true), :PrivateClass)
+ assert_not_include(::TestModule.constants(false), :PrivateClass)
+ end
+
+ def test_toplevel_private_constant
+ src = <<-INPUT
+ class Object
+ private_constant :Object
+ end
+ p Object
+ begin
+ p ::Object
+ rescue
+ p :ok
+ end
+ INPUT
+ assert_in_out_err([], src, %w(Object :ok), [])
+ end
+
+ def test_private_constants_clear_inlinecache
+ bug5702 = '[ruby-dev:44929]'
+ src = <<-INPUT
+ class A
+ C = :Const
+ def self.get_C
+ A::C
+ end
+ # fill cache
+ A.get_C
+ private_constant :C, :D rescue nil
+ begin
+ A.get_C
+ rescue NameError
+ puts "A.get_C"
+ end
+ end
+ INPUT
+ assert_in_out_err([], src, %w(A.get_C), [], bug5702)
+ end
+
+ def test_constant_lookup_in_method_defined_by_class_eval
+ src = <<-INPUT
+ class A
+ B = 42
+ end
+
+ A.class_eval do
+ def self.f
+ B
+ end
+
+ def f
+ B
+ end
+ end
+
+ begin
+ A.f
+ rescue NameError
+ puts "A.f"
+ end
+ begin
+ A.new.f
+ rescue NameError
+ puts "A.new.f"
+ end
+ INPUT
+ assert_in_out_err([], src, %w(A.f A.new.f), [])
+ end
+
+ def test_constant_lookup_in_toplevel_class_eval
+ src = <<-INPUT
+ module X
+ A = 123
+ end
+ begin
+ X.class_eval { A }
+ rescue NameError => e
+ puts e
+ end
+ INPUT
+ assert_in_out_err([], src, ["uninitialized constant A"], [])
+ end
+
+ def test_constant_lookup_in_module_in_class_eval
+ src = <<-INPUT
+ class A
+ B = 42
+ end
+
+ A.class_eval do
+ module C
+ begin
+ B
+ rescue NameError
+ puts "NameError"
+ end
+ end
+ end
+ INPUT
+ assert_in_out_err([], src, ["NameError"], [])
+ end
+
+ module M0
+ def m1; [:M0] end
+ end
+ module M1
+ def m1; [:M1, *super] end
+ end
+ module M2
+ def m1; [:M2, *super] end
+ end
+ M3 = Module.new do
+ def m1; [:M3, *super] end
+ end
+ module M4
+ def m1; [:M4, *super] end
+ end
+ class C
+ def m1; end
+ end
+ class C0 < C
+ include M0
+ prepend M1
+ def m1; [:C0, *super] end
+ end
+ class C1 < C0
+ prepend M2, M3
+ include M4
+ def m1; [:C1, *super] end
+ end
+
+ def test_prepend
+ obj = C0.new
+ expected = [:M1,:C0,:M0]
+ assert_equal(expected, obj.m1)
+ obj = C1.new
+ expected = [:M2,:M3,:C1,:M4,:M1,:C0,:M0]
+ assert_equal(expected, obj.m1)
+ end
+
+ def test_public_prepend
+ assert_nothing_raised('#8846') do
+ Class.new.prepend(Module.new)
+ end
+ end
+
+ def test_prepend_CMP
+ bug11878 = '[ruby-core:72493] [Bug #11878]'
+ assert_equal(-1, C1 <=> M2)
+ assert_equal(+1, M2 <=> C1, bug11878)
+ end
+
+ def test_prepend_inheritance
+ bug6654 = '[ruby-core:45914]'
+ a = labeled_module("a")
+ b = labeled_module("b") {include a}
+ c = labeled_class("c") {prepend b}
+ assert_operator(c, :<, b, bug6654)
+ assert_operator(c, :<, a, bug6654)
+ bug8357 = '[ruby-core:54736] [Bug #8357]'
+ b = labeled_module("b") {prepend a}
+ c = labeled_class("c") {include b}
+ assert_operator(c, :<, b, bug8357)
+ assert_operator(c, :<, a, bug8357)
+ bug8357 = '[ruby-core:54742] [Bug #8357]'
+ assert_kind_of(b, c.new, bug8357)
+ end
+
+ def test_prepend_instance_methods
+ bug6655 = '[ruby-core:45915]'
+ assert_equal(Object.instance_methods, Class.new {prepend Module.new}.instance_methods, bug6655)
+ end
+
+ def test_prepend_singleton_methods
+ o = Object.new
+ o.singleton_class.class_eval {prepend Module.new}
+ assert_equal([], o.singleton_methods)
+ end
+
+ def test_prepend_remove_method
+ c = Class.new do
+ prepend Module.new {def foo; end}
+ end
+ assert_raise(NameError) do
+ c.class_eval do
+ remove_method(:foo)
+ end
+ end
+ c.class_eval do
+ def foo; end
+ end
+ removed = nil
+ c.singleton_class.class_eval do
+ define_method(:method_removed) {|id| removed = id}
+ end
+ assert_nothing_raised(NoMethodError, NameError, '[Bug #7843]') do
+ c.class_eval do
+ remove_method(:foo)
+ end
+ end
+ assert_equal(:foo, removed)
+ end
+
+ def test_prepend_class_ancestors
+ bug6658 = '[ruby-core:45919]'
+ m = labeled_module("m")
+ c = labeled_class("c") {prepend m}
+ assert_equal([m, c], c.ancestors[0, 2], bug6658)
+
+ bug6662 = '[ruby-dev:45868]'
+ c2 = labeled_class("c2", c)
+ anc = c2.ancestors
+ assert_equal([c2, m, c, Object], anc[0..anc.index(Object)], bug6662)
+ end
+
+ def test_prepend_module_ancestors
+ bug6659 = '[ruby-dev:45861]'
+ m0 = labeled_module("m0") {def x; [:m0, *super] end}
+ m1 = labeled_module("m1") {def x; [:m1, *super] end; prepend m0}
+ m2 = labeled_module("m2") {def x; [:m2, *super] end; prepend m1}
+ c0 = labeled_class("c0") {def x; [:c0] end}
+ c1 = labeled_class("c1") {def x; [:c1] end; prepend m2}
+ c2 = labeled_class("c2", c0) {def x; [:c2, *super] end; include m2}
+
+ assert_equal([m0, m1], m1.ancestors, bug6659)
+
+ bug6662 = '[ruby-dev:45868]'
+ assert_equal([m0, m1, m2], m2.ancestors, bug6662)
+ assert_equal([m0, m1, m2, c1], c1.ancestors[0, 4], bug6662)
+ assert_equal([:m0, :m1, :m2, :c1], c1.new.x)
+ assert_equal([c2, m0, m1, m2, c0], c2.ancestors[0, 5], bug6662)
+ 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)
+ m3 = labeled_module("m3") {prepend m1; include m1}
+ assert_equal([m0, m1, m3], m3.ancestors)
+ m3 = labeled_module("m3") {prepend m1; prepend m1}
+ assert_equal([m0, m1, m3], m3.ancestors)
+ m3 = labeled_module("m3") {include m1; include m1}
+ assert_equal([m3, m0, m1], m3.ancestors)
+ end
+
+ def labeled_module(name, &block)
+ EnvUtil.labeled_module(name, &block)
+ end
+
+ def labeled_class(name, superclass = Object, &block)
+ EnvUtil.labeled_class(name, superclass, &block)
+ end
+
+ def test_prepend_instance_methods_false
+ bug6660 = '[ruby-dev:45863]'
+ assert_equal([:m1], Class.new{ prepend Module.new; def m1; end }.instance_methods(false), bug6660)
+ assert_equal([:m1], Class.new(Class.new{def m2;end}){ prepend Module.new; def m1; end }.instance_methods(false), bug6660)
+ end
+
+ def test_cyclic_prepend
+ bug7841 = '[ruby-core:52205] [Bug #7841]'
+ m1 = Module.new
+ m2 = Module.new
+ m1.instance_eval { prepend(m2) }
+ assert_raise(ArgumentError, bug7841) do
+ m2.instance_eval { prepend(m1) }
+ end
+ end
+
+ def test_prepend_optmethod
+ bug7983 = '[ruby-dev:47124] [Bug #7983]'
+ assert_separately [], %{
+ module M
+ def /(other)
+ to_f / other
+ end
+ end
+ Integer.send(:prepend, M)
+ assert_equal(0.5, 1 / 2, "#{bug7983}")
+ }
+ assert_equal(0, 1 / 2)
+ end
+
+ def test_redefine_optmethod_after_prepend
+ bug11826 = '[ruby-core:72188] [Bug #11826]'
+ assert_separately [], %{
+ module M
+ end
+ class Integer
+ prepend M
+ def /(other)
+ quo(other)
+ end
+ end
+ assert_equal(1 / 2r, 1 / 2, "#{bug11826}")
+ }, ignore_stderr: true
+ assert_equal(0, 1 / 2)
+ end
+
+ def test_override_optmethod_after_prepend
+ bug11836 = '[ruby-core:72226] [Bug #11836]'
+ assert_separately [], %{
+ module M
+ end
+ class Integer
+ prepend M
+ end
+ module M
+ def /(other)
+ quo(other)
+ end
+ end
+ assert_equal(1 / 2r, 1 / 2, "#{bug11836}")
+ }, ignore_stderr: true
+ assert_equal(0, 1 / 2)
+ end
+
+ def test_prepend_visibility
+ bug8005 = '[ruby-core:53106] [Bug #8005]'
+ c = Class.new do
+ prepend Module.new {}
+ def foo() end
+ protected :foo
+ end
+ a = c.new
+ assert_respond_to a, [:foo, true], bug8005
+ assert_nothing_raised(NoMethodError, bug8005) {a.send :foo}
+ end
+
+ def test_prepend_visibility_inherited
+ bug8238 = '[ruby-core:54105] [Bug #8238]'
+ assert_separately [], <<-"end;", timeout: 20
+ class A
+ def foo() A; end
+ private :foo
+ end
+ class B < A
+ public :foo
+ prepend Module.new
+ end
+ assert_equal(A, B.new.foo, "#{bug8238}")
+ end;
+ end
+
+ def test_prepend_included_modules
+ bug8025 = '[ruby-core:53158] [Bug #8025]'
+ mixin = labeled_module("mixin")
+ c = labeled_module("c") {prepend mixin}
+ im = c.included_modules
+ assert_not_include(im, c, bug8025)
+ assert_include(im, mixin, bug8025)
+ c1 = labeled_class("c1") {prepend mixin}
+ c2 = labeled_class("c2", c1)
+ im = c2.included_modules
+ assert_not_include(im, c1, bug8025)
+ assert_not_include(im, c2, bug8025)
+ assert_include(im, mixin, bug8025)
+ end
+
+ def test_prepend_super_in_alias
+ bug7842 = '[Bug #7842]'
+
+ p = labeled_module("P") do
+ def m; "P"+super; end
+ end
+ a = labeled_class("A") do
+ def m; "A"; end
+ end
+ b = labeled_class("B", a) do
+ def m; "B"+super; end
+ alias m2 m
+ prepend p
+ alias m3 m
+ end
+ assert_equal("BA", b.new.m2, bug7842)
+ assert_equal("PBA", b.new.m3, bug7842)
+ end
+
+ def test_include_super_in_alias
+ bug9236 = '[Bug #9236]'
+
+ fun = labeled_module("Fun") do
+ def hello
+ orig_hello
+ end
+ end
+
+ m1 = labeled_module("M1") do
+ def hello
+ 'hello!'
+ end
+ end
+
+ m2 = labeled_module("M2") do
+ def hello
+ super
+ end
+ end
+
+ foo = labeled_class("Foo") do
+ include m1
+ include m2
+
+ alias orig_hello hello
+ include fun
+ end
+
+ assert_equal('hello!', foo.new.hello, bug9236)
+ end
+
+ def test_prepend_each_classes
+ m = labeled_module("M")
+ c1 = labeled_class("C1") {prepend m}
+ c2 = labeled_class("C2", c1) {prepend m}
+ assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should be able to prepend each classes")
+ end
+
+ def test_prepend_no_duplication
+ m = labeled_module("M")
+ c = labeled_class("C") {prepend m; prepend m}
+ assert_equal([m, c], c.ancestors[0, 2], "should never duplicate")
+ end
+
+ def test_prepend_in_superclass
+ m = labeled_module("M")
+ c1 = labeled_class("C1")
+ c2 = labeled_class("C2", c1) {prepend m}
+ c1.class_eval {prepend m}
+ assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should accesisble prepended module in superclass")
+ end
+
+ def test_prepend_call_super
+ assert_separately([], <<-'end;') #do
+ bug10847 = '[ruby-core:68093] [Bug #10847]'
+ module M; end
+ Float.prepend M
+ assert_nothing_raised(SystemStackError, bug10847) do
+ 0.3.numerator
+ end
+ end;
+ end
+
+ def test_prepend_module_with_no_args
+ assert_raise(ArgumentError) { Module.new { prepend } }
+ end
+
+ def test_prepend_private_super
+ wrapper = Module.new do
+ def wrapped
+ super + 1
+ end
+ end
+
+ klass = Class.new do
+ prepend wrapper
+
+ def wrapped
+ 1
+ end
+ private :wrapped
+ end
+
+ assert_equal(2, klass.new.wrapped)
+ end
+
+ def test_class_variables
+ m = Module.new
+ m.class_variable_set(:@@foo, 1)
+ m2 = Module.new
+ m2.send(:include, m)
+ m2.class_variable_set(:@@bar, 2)
+ assert_equal([:@@foo], m.class_variables)
+ assert_equal([:@@bar, :@@foo], m2.class_variables.sort)
+ assert_equal([:@@bar, :@@foo], m2.class_variables(true).sort)
+ assert_equal([:@@bar], m2.class_variables(false))
+ end
+
+ Bug6891 = '[ruby-core:47241]'
+
+ def test_extend_module_with_protected_method
+ list = []
+
+ x = Class.new {
+ @list = list
+
+ extend Module.new {
+ protected
+
+ def inherited(klass)
+ @list << "protected"
+ super(klass)
+ end
+ }
+
+ extend Module.new {
+ def inherited(klass)
+ @list << "public"
+ super(klass)
+ end
+ }
+ }
+
+ assert_nothing_raised(NoMethodError, Bug6891) {Class.new(x)}
+ assert_equal(['public', 'protected'], list)
+ end
+
+ def test_extend_module_with_protected_bmethod
+ list = []
+
+ x = Class.new {
+ extend Module.new {
+ protected
+
+ define_method(:inherited) do |klass|
+ list << "protected"
+ super(klass)
+ end
+ }
+
+ extend Module.new {
+ define_method(:inherited) do |klass|
+ list << "public"
+ super(klass)
+ end
+ }
+ }
+
+ assert_nothing_raised(NoMethodError, Bug6891) {Class.new(x)}
+ assert_equal(['public', 'protected'], list)
+ end
+
+ def test_extend_module_with_no_args
+ assert_raise(ArgumentError) { Module.new { extend } }
+ end
+
+ def test_invalid_attr
+ %W[
+ foo?
+ @foo
+ @@foo
+ $foo
+ \u3042$
+ ].each do |name|
+ assert_raise_with_message(NameError, /#{Regexp.quote(quote(name))}/) do
+ Module.new { attr_accessor name.to_sym }
+ end
+ end
+ end
+
+ private def quote(name)
+ encoding = Encoding.default_internal || Encoding.default_external
+ (name.encoding == encoding || name.ascii_only?) ? name : name.inspect
+ end
+
+ class AttrTest
+ class << self
+ attr_accessor :cattr
+ end
+ attr_accessor :iattr
+ def ivar
+ @ivar
+ end
+ end
+
+ def test_uninitialized_instance_variable
+ a = AttrTest.new
+ assert_warning(/instance variable @ivar not initialized/) do
+ assert_nil(a.ivar)
+ end
+ a.instance_variable_set(:@ivar, 42)
+ assert_warning '' do
+ assert_equal(42, a.ivar)
+ end
+
+ name = "@\u{5909 6570}"
+ assert_warning(/instance variable #{name} not initialized/) do
+ assert_nil(a.instance_eval(name))
+ end
+ end
+
+ def test_uninitialized_attr
+ a = AttrTest.new
+ assert_warning '' do
+ assert_nil(a.iattr)
+ end
+ a.iattr = 42
+ assert_warning '' do
+ assert_equal(42, a.iattr)
+ end
+ end
+
+ def test_uninitialized_attr_class
+ assert_warning '' do
+ assert_nil(AttrTest.cattr)
+ end
+ AttrTest.cattr = 42
+ assert_warning '' do
+ assert_equal(42, AttrTest.cattr)
+ end
+ end
+
+ def test_uninitialized_attr_non_object
+ a = Class.new(Array) do
+ attr_accessor :iattr
+ end.new
+ assert_warning '' do
+ assert_nil(a.iattr)
+ end
+ a.iattr = 42
+ assert_warning '' do
+ assert_equal(42, a.iattr)
+ end
+ end
+
+ def test_remove_const
+ m = Module.new
+ assert_raise(NameError){ m.instance_eval { remove_const(:__FOO__) } }
+ end
+
+ def test_public_methods
+ public_methods = %i[
+ include
+ prepend
+ attr
+ attr_accessor
+ attr_reader
+ attr_writer
+ define_method
+ alias_method
+ undef_method
+ remove_method
+ ]
+ assert_equal public_methods.sort, (Module.public_methods & public_methods).sort
+ end
+
+ def test_private_top_methods
+ assert_top_method_is_private(:include)
+ assert_top_method_is_private(:public)
+ assert_top_method_is_private(:private)
+ assert_top_method_is_private(:define_method)
+ end
+
+ module PrivateConstantReopen
+ PRIVATE_CONSTANT = true
+ private_constant :PRIVATE_CONSTANT
+ end
+
+ def test_private_constant_reopen
+ assert_raise(NameError) do
+ eval <<-EOS, TOPLEVEL_BINDING
+ module TestModule::PrivateConstantReopen::PRIVATE_CONSTANT
+ end
+ EOS
+ end
+ assert_raise(NameError) do
+ eval <<-EOS, TOPLEVEL_BINDING
+ class TestModule::PrivateConstantReopen::PRIVATE_CONSTANT
+ end
+ EOS
+ end
+ end
+
+ def test_frozen_visibility
+ bug11532 = '[ruby-core:70828] [Bug #11532]'
+
+ c = Class.new {const_set(:A, 1)}.freeze
+ assert_raise_with_message(FrozenError, /frozen class/, bug11532) {
+ c.class_eval {private_constant :A}
+ }
+
+ c = Class.new {const_set(:A, 1); private_constant :A}.freeze
+ assert_raise_with_message(FrozenError, /frozen class/, bug11532) {
+ c.class_eval {public_constant :A}
+ }
+
+ c = Class.new {const_set(:A, 1)}.freeze
+ assert_raise_with_message(FrozenError, /frozen class/, bug11532) {
+ c.class_eval {deprecate_constant :A}
+ }
+ end
+
+ def test_singleton_class_ancestors
+ feature8035 = '[ruby-core:53171]'
+ obj = Object.new
+ assert_equal [obj.singleton_class, Object], obj.singleton_class.ancestors.first(2), feature8035
+
+ mod = Module.new
+ obj.extend mod
+ assert_equal [obj.singleton_class, mod, Object], obj.singleton_class.ancestors.first(3)
+
+ obj = Object.new
+ obj.singleton_class.send :prepend, mod
+ assert_equal [mod, obj.singleton_class, Object], obj.singleton_class.ancestors.first(3)
+ end
+
+ def test_visibility_by_public_class_method
+ bug8284 = '[ruby-core:54404] [Bug #8284]'
+ assert_raise(NoMethodError) {Object.remove_const}
+ Module.new.public_class_method(:remove_const)
+ 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
+ retvals << define_method(:foo){}
+ retvals << define_method(:bar, instance_method(:foo))
+ end
+ assert_equal :foo, retvals[0]
+ assert_equal :bar, retvals[1]
+ end
+
+ def test_return_value_of_define_singleton_method
+ retvals = []
+ Class.new do
+ retvals << define_singleton_method(:foo){}
+ retvals << define_singleton_method(:bar, method(:foo))
+ end
+ assert_equal :foo, retvals[0]
+ assert_equal :bar, retvals[1]
+ end
+
+ def test_prepend_gc
+ assert_separately [], %{
+ module Foo
+ end
+ class Object
+ prepend Foo
+ end
+ GC.start # make created T_ICLASS old (or remembered shady)
+ class Object # add methods into T_ICLASS (need WB if it is old)
+ def foo; end
+ attr_reader :bar
+ end
+ 1_000_000.times{''} # cause GC
+ }
+ end
+
+ def test_inspect_segfault
+ bug_10282 = '[ruby-core:65214] [Bug #10282]'
+ assert_separately [], <<-RUBY
+ module ShallowInspect
+ def shallow_inspect
+ "foo"
+ end
+ end
+
+ module InspectIsShallow
+ include ShallowInspect
+ alias_method :inspect, :shallow_inspect
+ end
+
+ class A
+ end
+
+ A.prepend InspectIsShallow
+
+ expect = "#<Method: A(ShallowInspect)#inspect(shallow_inspect)>"
+ assert_equal expect, A.new.method(:inspect).inspect, "#{bug_10282}"
+ RUBY
+ end
+
+ def test_define_method_with_unbound_method
+ # Passing an UnboundMethod to define_method succeeds if it is from an ancestor
+ assert_nothing_raised do
+ cls = Class.new(String) do
+ define_method('foo', String.instance_method(:to_s))
+ end
+
+ obj = cls.new('bar')
+ assert_equal('bar', obj.foo)
+ end
+
+ # Passing an UnboundMethod to define_method fails if it is not from an ancestor
+ assert_raise(TypeError) do
+ Class.new do
+ define_method('foo', String.instance_method(:to_s))
+ end
+ end
+ end
+
+ def test_redefinition_mismatch
+ m = Module.new
+ m.module_eval "A = 1"
+ assert_raise_with_message(TypeError, /is not a module/) {
+ m.module_eval "module A; end"
+ }
+ n = "M\u{1f5ff}"
+ m.module_eval "#{n} = 42"
+ assert_raise_with_message(TypeError, "#{n} is not a module") {
+ m.module_eval "module #{n}; end"
+ }
+
+ assert_separately([], <<-"end;")
+ Etc = (class C\u{1f5ff}; self; end).new
+ assert_raise_with_message(TypeError, /C\u{1f5ff}/) {
+ require 'etc'
+ }
+ end;
+ end
+
+ def test_private_extended_module
+ assert_separately [], %q{
+ class Object
+ def bar; "Object#bar"; end
+ end
+ module M1
+ def bar; super; end
+ end
+ module M2
+ include M1
+ private(:bar)
+ def foo; bar; end
+ end
+ extend M2
+ assert_equal 'Object#bar', foo
+ }
+ end
+
+ private
+
+ def assert_top_method_is_private(method)
+ assert_separately [], %{
+ methods = singleton_class.private_instance_methods(false)
+ assert_include(methods, :#{method}, ":#{method} should be private")
+
+ assert_raise_with_message(NoMethodError, "private method `#{method}' called for main:Object") {
+ self.#{method}
+ }
+ }
+ end
end
diff --git a/test/ruby/test_not.rb b/test/ruby/test_not.rb
new file mode 100644
index 0000000000..12e4c4b696
--- /dev/null
+++ b/test/ruby/test_not.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: false
+require 'test/unit'
+
+class TestNot < Test::Unit::TestCase
+ def test_not_with_grouped_expression
+ assert_equal(false, (not (true)))
+ assert_equal(true, (not (false)))
+ end
+
+ def test_not_with_empty_grouped_expression
+ assert_equal(true, (not ()))
+ end
+end
diff --git a/test/ruby/test_notimp.rb b/test/ruby/test_notimp.rb
index dfe51683c9..daa5a82d7b 100644
--- a/test/ruby/test_notimp.rb
+++ b/test/ruby/test_notimp.rb
@@ -1,32 +1,53 @@
+# frozen_string_literal: false
require 'test/unit'
+require 'timeout'
require 'tmpdir'
class TestNotImplement < Test::Unit::TestCase
def test_respond_to_fork
- assert_includes(Process.methods, :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_includes(File.methods, :lchmod)
- if /linux/ =~ RUBY_PLATFORM
- assert_equal(false, File.respond_to?(:lchmod))
- end
- if /freebsd/ =~ RUBY_PLATFORM
+ assert_include(File.methods, :lchmod)
+ case RUBY_PLATFORM
+ when /freebsd/, /linux-musl/
assert_equal(true, File.respond_to?(:lchmod))
+ when /linux/
+ assert_equal(false, File.respond_to?(:lchmod))
end
end
def test_call_fork
- if Process.respond_to?(:fork)
- assert_nothing_raised {
+ 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
+ Timeout.timeout(EnvUtil.apply_timeout_scale(5)) {
pid = fork {}
Process.wait pid
+ pid = nil
}
end
- 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)
@@ -36,9 +57,14 @@ class TestNotImplement < Test::Unit::TestCase
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)
+ begin
+ File.lchmod newmode, "#{d}/g"
+ rescue Errno::EOPNOTSUPP
+ skip $!
+ else
+ snew = File.lstat(g)
+ assert_equal(newmode, snew.mode & 0777)
+ end
}
end
end
diff --git a/test/ruby/test_numeric.rb b/test/ruby/test_numeric.rb
index f4fbea4ce9..e48c3448e8 100644
--- a/test/ruby/test_numeric.rb
+++ b/test/ruby/test_numeric.rb
@@ -1,135 +1,194 @@
+# frozen_string_literal: false
require 'test/unit'
class TestNumeric < Test::Unit::TestCase
- class DummyNumeric < Numeric
- end
-
def test_coerce
a, b = 1.coerce(2)
- assert_equal(Fixnum, a.class)
- assert_equal(Fixnum, b.class)
+ assert_kind_of(Integer, a)
+ assert_kind_of(Integer, b)
a, b = 1.coerce(2.0)
assert_equal(Float, a.class)
assert_equal(Float, b.class)
assert_raise(TypeError) { -Numeric.new }
+
+ assert_raise_with_message(TypeError, /can't be coerced into /) {1+:foo}
+ assert_raise_with_message(TypeError, /can't be coerced into /) {1&:foo}
+ assert_raise_with_message(TypeError, /can't be coerced into /) {1|:foo}
+ assert_raise_with_message(TypeError, /can't be coerced into /) {1^:foo}
+
+ assert_raise_with_message(TypeError, /:\u{3042}/) {1+:"\u{3042}"}
+ assert_raise_with_message(TypeError, /:\u{3042}/) {1&:"\u{3042}"}
+ assert_raise_with_message(TypeError, /:\u{3042}/) {1|:"\u{3042}"}
+ assert_raise_with_message(TypeError, /:\u{3042}/) {1^:"\u{3042}"}
+ assert_raise_with_message(TypeError, /:"\\u3042"/) {1+:"\u{3042}"}
+ assert_raise_with_message(TypeError, /:"\\u3042"/) {1&:"\u{3042}"}
+ assert_raise_with_message(TypeError, /:"\\u3042"/) {1|:"\u{3042}"}
+ assert_raise_with_message(TypeError, /:"\\u3042"/) {1^:"\u{3042}"}
+
+ bug10711 = '[ruby-core:67405] [Bug #10711]'
+ exp = "1.2 can't be coerced into Integer"
+ assert_raise_with_message(TypeError, exp, bug10711) { 1 & 1.2 }
end
def test_dummynumeric
- a = DummyNumeric.new
-
- DummyNumeric.class_eval do
+ a = Class.new(Numeric) do
def coerce(x); nil; end
- end
+ end.new
assert_raise(TypeError) { -a }
assert_nil(1 <=> a)
assert_raise(ArgumentError) { 1 <= a }
- DummyNumeric.class_eval do
+ a = Class.new(Numeric) do
def coerce(x); 1.coerce(x); end
- end
+ end.new
assert_equal(2, 1 + a)
assert_equal(0, 1 <=> a)
- assert(1 <= a)
+ assert_operator(1, :<=, a)
- DummyNumeric.class_eval do
+ a = Class.new(Numeric) do
def coerce(x); [x, 1]; end
- end
+ end.new
assert_equal(-1, -a)
- ensure
- DummyNumeric.class_eval do
- remove_method :coerce
- end
+ bug7688 = '[ruby-core:51389] [Bug #7688]'
+ a = Class.new(Numeric) do
+ def coerce(x); raise StandardError, "my error"; end
+ end.new
+ assert_raise_with_message(StandardError, "my error") { 1 + a }
+ assert_raise_with_message(StandardError, "my error") { 1 < a }
+
+ a = Class.new(Numeric) do
+ def coerce(x); :bad_return_value; end
+ end.new
+ assert_raise_with_message(TypeError, "coerce must return [x, y]") { 1 + a }
+ assert_raise_with_message(TypeError, "coerce must return [x, y]") { 1 < a }
+ end
+
+ def test_singleton_method
+ a = Numeric.new
+ assert_raise_with_message(TypeError, /foo/) { def a.foo; end }
+ assert_raise_with_message(TypeError, /\u3042/) { eval("def a.\u3042; end") }
end
- def test_numeric
+ def test_dup
a = Numeric.new
- assert_raise(TypeError) { def a.foo; end }
- assert_raise(TypeError) { eval("def a.\u3042; end") }
- assert_raise(TypeError) { a.dup }
+ assert_same a, a.dup
+ end
+
+ def test_clone
+ a = Numeric.new
+ assert_same a, a.clone
+ assert_raise(ArgumentError) {a.clone(freeze: false)}
+
+ c = EnvUtil.labeled_class("\u{1f4a9}", Numeric)
+ assert_raise_with_message(ArgumentError, /\u{1f4a9}/) do
+ c.new.clone(freeze: false)
+ end
end
def test_quo
- assert_raise(ArgumentError) {DummyNumeric.new.quo(1)}
+ a = Numeric.new
+ assert_raise(TypeError) {a.quo(1)}
+ end
+
+ def test_quo_ruby_core_41575
+ rat = 84.quo(1)
+ x = Class.new(Numeric) do
+ define_method(:to_r) { rat }
+ end.new
+ assert_equal(2.quo(1), x.quo(42), '[ruby-core:41575]')
end
def test_divmod
=begin
- DummyNumeric.class_eval do
+ x = Class.new(Numeric) do
def /(x); 42.0; end
def %(x); :mod; end
- end
+ end.new
- assert_equal(42, DummyNumeric.new.div(1))
- assert_equal(:mod, DummyNumeric.new.modulo(1))
- assert_equal([42, :mod], DummyNumeric.new.divmod(1))
+ assert_equal(42, x.div(1))
+ assert_equal(:mod, x.modulo(1))
+ assert_equal([42, :mod], x.divmod(1))
=end
assert_kind_of(Integer, 11.divmod(3.5).first, '[ruby-dev:34006]')
-
-=begin
- ensure
- DummyNumeric.class_eval do
- remove_method :/, :%
- end
-=end
end
def test_real_p
- assert(Numeric.new.real?)
+ assert_predicate(Numeric.new, :real?)
end
def test_integer_p
- assert(!Numeric.new.integer?)
+ assert_not_predicate(Numeric.new, :integer?)
end
def test_abs
- a = DummyNumeric.new
- DummyNumeric.class_eval do
+ a = Class.new(Numeric) do
def -@; :ok; end
def <(x); true; end
- end
+ end.new
assert_equal(:ok, a.abs)
- DummyNumeric.class_eval do
+ a = Class.new(Numeric) do
def <(x); false; end
- end
+ end.new
assert_equal(a, a.abs)
-
- ensure
- DummyNumeric.class_eval do
- remove_method :-@, :<
- end
end
def test_zero_p
- DummyNumeric.class_eval do
+ a = Class.new(Numeric) do
def ==(x); true; end
- end
+ end.new
- assert(DummyNumeric.new.zero?)
+ assert_predicate(a, :zero?)
+ end
- ensure
- DummyNumeric.class_eval do
- remove_method :==
- end
+ def test_nonzero_p
+ a = Class.new(Numeric) do
+ def zero?; true; end
+ end.new
+ assert_nil(a.nonzero?)
+
+ a = Class.new(Numeric) do
+ def zero?; false; end
+ end.new
+ assert_equal(a, a.nonzero?)
+ end
+
+ def test_positive_p
+ a = Class.new(Numeric) do
+ def >(x); true; end
+ end.new
+ assert_predicate(a, :positive?)
+
+ a = Class.new(Numeric) do
+ def >(x); false; end
+ end.new
+ assert_not_predicate(a, :positive?)
+ end
+
+ def test_negative_p
+ a = Class.new(Numeric) do
+ def <(x); true; end
+ end.new
+ assert_predicate(a, :negative?)
+
+ a = Class.new(Numeric) do
+ def <(x); false; end
+ end.new
+ assert_not_predicate(a, :negative?)
end
def test_to_int
- DummyNumeric.class_eval do
+ a = Class.new(Numeric) do
def to_i; :ok; end
- end
-
- assert_equal(:ok, DummyNumeric.new.to_int)
+ end.new
- ensure
- DummyNumeric.class_eval do
- remove_method :to_i
- end
+ assert_equal(:ok, a.to_int)
end
def test_cmp
@@ -139,69 +198,145 @@ class TestNumeric < Test::Unit::TestCase
end
def test_floor_ceil_round_truncate
- DummyNumeric.class_eval do
+ a = Class.new(Numeric) do
def to_f; 1.5; end
- end
+ end.new
- a = DummyNumeric.new
assert_equal(1, a.floor)
assert_equal(2, a.ceil)
assert_equal(2, a.round)
assert_equal(1, a.truncate)
- DummyNumeric.class_eval do
+ a = Class.new(Numeric) do
def to_f; 1.4; end
- end
+ end.new
- a = DummyNumeric.new
assert_equal(1, a.floor)
assert_equal(2, a.ceil)
assert_equal(1, a.round)
assert_equal(1, a.truncate)
- DummyNumeric.class_eval do
+ a = Class.new(Numeric) do
def to_f; -1.5; end
- end
+ end.new
- a = DummyNumeric.new
assert_equal(-2, a.floor)
assert_equal(-1, a.ceil)
assert_equal(-2, a.round)
assert_equal(-1, a.truncate)
-
- ensure
- DummyNumeric.class_eval do
- remove_method :to_f
- end
end
- def test_step
- a = []
- 1.step(10) {|x| a << x }
- assert_equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], a)
+ def assert_step(expected, (from, *args), inf: false)
+ enum = from.step(*args)
+ size = enum.size
+ xsize = expected.size
- a = []
- 1.step(10, 2) {|x| a << x }
- assert_equal([1, 3, 5, 7, 9], a)
+ if inf
+ assert_send [size, :infinite?], "step size: +infinity"
+ assert_send [size, :>, 0], "step size: +infinity"
- assert_raise(ArgumentError) { 1.step(10, 1, 0) { } }
- assert_raise(ArgumentError) { 1.step(10, 0) { } }
+ a = []
+ from.step(*args) { |x| a << x; break if a.size == xsize }
+ assert_equal expected, a, "step"
- a = []
- 10.step(1, -2) {|x| a << x }
- assert_equal([10, 8, 6, 4, 2], a)
+ a = []
+ enum.each { |x| a << x; break if a.size == xsize }
+ assert_equal expected, a, "step enumerator"
+ else
+ assert_equal expected.size, size, "step size"
- a = []
- 1.0.step(10.0, 2.0) {|x| a << x }
- assert_equal([1.0, 3.0, 5.0, 7.0, 9.0], a)
+ a = []
+ from.step(*args) { |x| a << x }
+ assert_equal expected, a, "step"
- a = []
- 1.step(10, 2**32) {|x| a << x }
- assert_equal([1], a)
+ a = []
+ enum.each { |x| a << x }
+ assert_equal expected, a, "step enumerator"
+ end
+ end
- a = []
- 10.step(1, -(2**32)) {|x| a << x }
- assert_equal([10], a)
+ def test_step
+ bignum = RbConfig::LIMITS['FIXNUM_MAX'] + 1
+ assert_raise(ArgumentError) { 1.step(10, 1, 0) { } }
+ assert_raise(ArgumentError) { 1.step(10, 1, 0).size }
+ assert_raise(ArgumentError) { 1.step(10, 0) { } }
+ 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 }
+
+ assert_kind_of(Enumerator::ArithmeticSequence, 1.step(10))
+ assert_kind_of(Enumerator::ArithmeticSequence, 1.step(10, 2))
+ assert_kind_of(Enumerator::ArithmeticSequence, 1.step(10, by: 2))
+ assert_kind_of(Enumerator::ArithmeticSequence, 1.step(by: 2))
+ assert_kind_of(Enumerator::ArithmeticSequence, 1.step(by: 2, to: nil))
+ assert_kind_of(Enumerator::ArithmeticSequence, 1.step(by: 2, to: 10))
+ assert_kind_of(Enumerator::ArithmeticSequence, 1.step(by: -1))
+
+ bug9811 = '[ruby-dev:48177] [Bug #9811]'
+ assert_raise(ArgumentError, bug9811) { 1.step(10, foo: nil) {} }
+ assert_raise(ArgumentError, bug9811) { 1.step(10, foo: nil).size }
+ assert_raise(ArgumentError, bug9811) { 1.step(10, to: 11) {} }
+ assert_raise(ArgumentError, bug9811) { 1.step(10, to: 11).size }
+ assert_raise(ArgumentError, bug9811) { 1.step(10, 1, by: 11) {} }
+ assert_raise(ArgumentError, bug9811) { 1.step(10, 1, by: 11).size }
+
+ assert_equal(bignum*2+1, (-bignum).step(bignum, 1).size)
+ assert_equal(bignum*2, (-bignum).step(bignum-1, 1).size)
+
+ assert_equal(10+1, (0.0).step(10.0, 1.0).size)
+
+ i, bigflo = 1, bignum.to_f
+ i <<= 1 until (bigflo - i).to_i < bignum
+ bigflo -= i >> 1
+ assert_equal(bigflo.to_i, (0.0).step(bigflo-1.0, 1.0).size)
+
+ assert_step [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 10]
+ assert_step [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, to: 10]
+ assert_step [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, to: 10, by: nil]
+ assert_step [1, 3, 5, 7, 9], [1, 10, 2]
+ assert_step [1, 3, 5, 7, 9], [1, to: 10, by: 2]
+
+ assert_step [10, 8, 6, 4, 2], [10, 1, -2]
+ assert_step [10, 8, 6, 4, 2], [10, to: 1, by: -2]
+ assert_step [1.0, 3.0, 5.0, 7.0, 9.0], [1.0, 10.0, 2.0]
+ assert_step [1.0, 3.0, 5.0, 7.0, 9.0], [1.0, to: 10.0, by: 2.0]
+ assert_step [1], [1, 10, bignum]
+ assert_step [1], [1, to: 10, by: bignum]
+
+ 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]
+ assert_step [], [0, 1, -Float::INFINITY]
+ assert_step [10], [10, to: 1, by: -bignum]
+
+ 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_num2long
@@ -211,12 +346,72 @@ class TestNumeric < Test::Unit::TestCase
assert_raise(TypeError) { 1 & 9223372036854777856.0 }
o = Object.new
def o.to_int; 1; end
- assert_equal(1, 1 & o)
+ assert_raise(TypeError) { assert_equal(1, 1 & o) }
end
def test_eql
- assert(1 == 1.0)
- assert(!(1.eql?(1.0)))
- assert(!(1.eql?(2)))
+ assert_equal(1, 1.0)
+ assert_not_operator(1, :eql?, 1.0)
+ assert_not_operator(1, :eql?, 2)
end
+
+ def test_coerced_remainder
+ assert_separately([], <<-'end;')
+ x = Class.new do
+ def coerce(a) [self, a]; end
+ def %(a) self; end
+ end.new
+ assert_raise(ArgumentError) {1.remainder(x)}
+ end;
+ end
+
+ def test_comparison_comparable
+ bug12864 = '[ruby-core:77713] [Bug #12864]'
+
+ myinteger = Class.new do
+ include Comparable
+
+ def initialize(i)
+ @i = i.to_i
+ end
+ attr_reader :i
+
+ def <=>(other)
+ @i <=> (other.is_a?(self.class) ? other.i : other)
+ end
+ end
+
+ all_assertions(bug12864) do |a|
+ [5, 2**62, 2**61].each do |i|
+ a.for("%#x"%i) do
+ m = myinteger.new(i)
+ assert_equal(i, m)
+ assert_equal(m, i)
+ end
+ end
+ end
+ end
+
+ def test_pow
+ assert_equal(2**3, 2.pow(3))
+ assert_equal(2**-1, 2.pow(-1))
+ assert_equal(2**0.5, 2.pow(0.5))
+ assert_equal((-1)**0.5, -1.pow(0.5))
+ assert_equal(3**3 % 8, 3.pow(3, 8))
+ assert_equal(3**3 % -8, 3.pow(3,-8))
+ assert_equal(3**2 % -2, 3.pow(2,-2))
+ assert_equal((-3)**3 % 8, -3.pow(3,8))
+ assert_equal((-3)**3 % -8, -3.pow(3,-8))
+ assert_equal(5**2 % -8, 5.pow(2,-8))
+ assert_equal(4481650795473624846969600733813414725093,
+ 2120078484650058507891187874713297895455.
+ pow(5478118174010360425845660566650432540723,
+ 5263488859030795548286226023720904036518))
+
+ assert_equal(12, 12.pow(1, 10000000000), '[Bug #14259]')
+ 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]')
+ end
+
end
diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb
index 63c1d32bef..28e07162d7 100644
--- a/test/ruby/test_object.rb
+++ b/test/ruby/test_object.rb
@@ -1,5 +1,6 @@
+# -*- coding: us-ascii -*-
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestObject < Test::Unit::TestCase
def setup
@@ -11,16 +12,82 @@ class TestObject < Test::Unit::TestCase
$VERBOSE = @verbose
end
+ def test_itself
+ feature6373 = '[ruby-core:44704] [Feature #6373]'
+ object = Object.new
+ assert_same(object, object.itself, feature6373)
+ end
+
+ def test_yield_self
+ feature = '[ruby-core:46320] [Feature #6721]'
+ object = Object.new
+ assert_same(self, object.yield_self {self}, feature)
+ assert_same(object, object.yield_self {|x| break x}, feature)
+ enum = object.yield_self
+ assert_instance_of(Enumerator, enum)
+ assert_equal(1, enum.size)
+ end
+
def test_dup
- assert_raise(TypeError) { 1.dup }
- assert_raise(TypeError) { true.dup }
- assert_raise(TypeError) { nil.dup }
+ assert_equal 1, 1.dup
+ assert_equal true, true.dup
+ assert_equal nil, nil.dup
+ assert_equal false, false.dup
+ x = :x; assert_equal x, x.dup
+ x = "bug13145".intern; assert_equal x, x.dup
+ x = 1 << 64; assert_equal x, x.dup
+ x = 1.72723e-77; assert_equal x, x.dup
assert_raise(TypeError) do
Object.new.instance_eval { initialize_copy(1) }
end
end
+ def test_clone
+ a = Object.new
+ def a.b; 2 end
+
+ a.freeze
+ c = a.clone
+ assert_equal(true, c.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(2, d.b)
+ assert_equal(3, d.e)
+
+ assert_equal 1, 1.clone
+ assert_equal true, true.clone
+ assert_equal nil, nil.clone
+ assert_equal false, false.clone
+ x = :x; assert_equal x, x.dup
+ x = "bug13145".intern; assert_equal x, x.dup
+ x = 1 << 64; assert_equal x, x.clone
+ x = 1.72723e-77; assert_equal x, x.clone
+ assert_raise(ArgumentError) {1.clone(freeze: false)}
+ assert_raise(ArgumentError) {true.clone(freeze: false)}
+ assert_raise(ArgumentError) {nil.clone(freeze: false)}
+ assert_raise(ArgumentError) {false.clone(freeze: false)}
+ x = EnvUtil.labeled_class("\u{1f4a9}").new
+ assert_raise_with_message(ArgumentError, /\u{1f4a9}/) do
+ Object.new.clone(freeze: x)
+ end
+ end
+
+ def test_init_dupclone
+ cls = Class.new do
+ def initialize_clone(orig); throw :initialize_clone; end
+ def initialize_dup(orig); throw :initialize_dup; end
+ end
+
+ obj = cls.new
+ assert_throw(:initialize_clone) {obj.clone}
+ assert_throw(:initialize_dup) {obj.dup}
+ end
+
def test_instance_of
assert_raise(TypeError) { 1.instance_of?(1) }
end
@@ -32,29 +99,33 @@ class TestObject < Test::Unit::TestCase
def test_taint_frozen_obj
o = Object.new
o.freeze
- assert_raise(RuntimeError) { o.taint }
+ assert_raise(FrozenError) { o.taint }
o = Object.new
o.taint
o.freeze
- assert_raise(RuntimeError) { o.untaint }
- end
-
- def test_freeze_under_safe_4
- o = Object.new
- assert_raise(SecurityError) do
- Thread.new do
- $SAFE = 4
- o.freeze
- end.join
- end
+ assert_raise(FrozenError) { o.untaint }
end
def test_freeze_immediate
- assert_equal(false, 1.frozen?)
+ assert_equal(true, 1.frozen?)
1.freeze
assert_equal(true, 1.frozen?)
- assert_equal(false, 2.frozen?)
+ assert_equal(true, 2.frozen?)
+ assert_equal(true, true.frozen?)
+ assert_equal(true, false.frozen?)
+ assert_equal(true, nil.frozen?)
+ end
+
+ def test_frozen_error_message
+ name = "C\u{30c6 30b9 30c8}"
+ klass = EnvUtil.labeled_class(name) {
+ attr_accessor :foo
+ }
+ obj = klass.new.freeze
+ assert_raise_with_message(FrozenError, /#{name}/) {
+ obj.foo = 1
+ }
end
def test_nil_to_f
@@ -147,19 +218,49 @@ class TestObject < Test::Unit::TestCase
assert_equal([:foo2], (o2.public_methods(false) - o0.public_methods(false)).sort)
end
+ def test_methods_prepend
+ bug8044 = '[ruby-core:53207] [Bug #8044]'
+ o = Object.new
+ def o.foo; end
+ assert_equal([:foo], o.methods(false))
+ class << o; prepend Module.new; end
+ assert_equal([:foo], o.methods(false), bug8044)
+ end
+
def test_instance_variable_get
o = Object.new
o.instance_eval { @foo = :foo }
assert_equal(:foo, o.instance_variable_get(:@foo))
assert_equal(nil, o.instance_variable_get(:@bar))
+ assert_raise(NameError) { o.instance_variable_get('@') }
+ assert_raise(NameError) { o.instance_variable_get(:'@') }
assert_raise(NameError) { o.instance_variable_get(:foo) }
+ assert_raise(NameError) { o.instance_variable_get("bar") }
+ assert_raise(TypeError) { o.instance_variable_get(1) }
+
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@foo"; end
+ def n.count; @count; end
+ assert_equal(:foo, o.instance_variable_get(n))
+ assert_equal(1, n.count)
end
def test_instance_variable_set
o = Object.new
o.instance_variable_set(:@foo, :foo)
assert_equal(:foo, o.instance_eval { @foo })
+ assert_raise(NameError) { o.instance_variable_set(:'@', 1) }
+ assert_raise(NameError) { o.instance_variable_set('@', 1) }
assert_raise(NameError) { o.instance_variable_set(:foo, 1) }
+ assert_raise(NameError) { o.instance_variable_set("bar", 1) }
+ assert_raise(TypeError) { o.instance_variable_set(1, 1) }
+
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@foo"; end
+ def n.count; @count; end
+ o.instance_variable_set(n, :bar)
+ assert_equal(:bar, o.instance_eval { @foo })
+ assert_equal(1, n.count)
end
def test_instance_variable_defined
@@ -167,32 +268,85 @@ class TestObject < Test::Unit::TestCase
o.instance_eval { @foo = :foo }
assert_equal(true, o.instance_variable_defined?(:@foo))
assert_equal(false, o.instance_variable_defined?(:@bar))
+ assert_raise(NameError) { o.instance_variable_defined?(:'@') }
+ assert_raise(NameError) { o.instance_variable_defined?('@') }
assert_raise(NameError) { o.instance_variable_defined?(:foo) }
+ assert_raise(NameError) { o.instance_variable_defined?("bar") }
+ assert_raise(TypeError) { o.instance_variable_defined?(1) }
+
+ n = Object.new
+ def n.to_str; @count = defined?(@count) ? @count + 1 : 1; "@foo"; end
+ def n.count; @count; end
+ assert_equal(true, o.instance_variable_defined?(n))
+ assert_equal(1, n.count)
end
def test_remove_instance_variable
- o = Object.new
- o.instance_eval { @foo = :foo }
- o.instance_eval { remove_instance_variable(:@foo) }
- assert_equal(false, o.instance_variable_defined?(:@foo))
+ { 'T_OBJECT' => Object.new,
+ 'T_CLASS,T_MODULE' => Class.new(Object),
+ 'generic ivar' => '',
+ }.each do |desc, o|
+ e = assert_raise(NameError, "#{desc} iv removal raises before set") do
+ o.remove_instance_variable(:@foo)
+ end
+ assert_equal([o, :@foo], [e.receiver, e.name])
+ o.instance_eval { @foo = :foo }
+ assert_equal(:foo, o.remove_instance_variable(:@foo),
+ "#{desc} iv removal returns original value")
+ assert_not_send([o, :instance_variable_defined?, :@foo],
+ "#{desc} iv removed successfully")
+ e = assert_raise(NameError, "#{desc} iv removal raises after removal") do
+ o.remove_instance_variable(:@foo)
+ end
+ assert_equal([o, :@foo], [e.receiver, e.name])
+ end
end
- def test_convert_type
+ def test_convert_string
o = Object.new
def o.to_s; 1; end
assert_raise(TypeError) { String(o) }
+ def o.to_s; "o"; end
+ assert_equal("o", String(o))
+ def o.to_str; "O"; end
+ assert_equal("O", String(o))
+ def o.respond_to?(*) false; end
+ assert_raise(TypeError) { String(o) }
end
- def test_check_convert_type
+ def test_convert_array
o = Object.new
def o.to_a; 1; end
assert_raise(TypeError) { Array(o) }
+ def o.to_a; [1]; end
+ assert_equal([1], Array(o))
+ def o.to_ary; [2]; end
+ assert_equal([2], Array(o))
+ def o.respond_to?(*) false; end
+ assert_equal([o], Array(o))
+ end
+
+ def test_convert_hash
+ assert_equal({}, Hash(nil))
+ assert_equal({}, Hash([]))
+ assert_equal({key: :value}, Hash(key: :value))
+ assert_raise(TypeError) { Hash([1,2]) }
+ assert_raise(TypeError) { Hash(Object.new) }
+ o = Object.new
+ def o.to_hash; {a: 1, b: 2}; end
+ assert_equal({a: 1, b: 2}, Hash(o))
+ def o.to_hash; 9; end
+ assert_raise(TypeError) { Hash(o) }
end
def test_to_integer
o = Object.new
def o.to_i; nil; end
assert_raise(TypeError) { Integer(o) }
+ def o.to_i; 42; end
+ assert_equal(42, Integer(o))
+ def o.respond_to?(*) false; end
+ assert_raise(TypeError) { Integer(o) }
end
class MyInteger
@@ -211,17 +365,6 @@ class TestObject < Test::Unit::TestCase
assert_equal(1+3+5+7+9, n)
end
- def test_add_method_under_safe4
- o = Object.new
- assert_raise(SecurityError) do
- Thread.new do
- $SAFE = 4
- def o.foo
- end
- end.join
- end
- end
-
def test_redefine_method_under_verbose
assert_in_out_err([], <<-INPUT, %w(2), /warning: method redefined; discarding old foo$/)
$VERBOSE = true
@@ -233,35 +376,30 @@ class TestObject < Test::Unit::TestCase
end
def test_redefine_method_which_may_case_serious_problem
- assert_in_out_err([], <<-INPUT, [], /warning: redefining `object_id' may cause serious problems$/)
+ assert_in_out_err([], <<-INPUT, [], %r"warning: redefining `object_id' may cause serious problems$")
$VERBOSE = false
def (Object.new).object_id; end
INPUT
- assert_in_out_err([], <<-INPUT, [], /warning: redefining `__send__' may cause serious problems$/)
+ assert_in_out_err([], <<-INPUT, [], %r"warning: redefining `__send__' may cause serious problems$")
$VERBOSE = false
def (Object.new).__send__; end
INPUT
+
+ bug10421 = '[ruby-dev:48691] [Bug #10421]'
+ assert_in_out_err([], <<-INPUT, ["1"], [], bug10421)
+ $VERBOSE = false
+ class C < BasicObject
+ def object_id; 1; end
+ end
+ puts C.new.object_id
+ INPUT
end
def test_remove_method
- assert_raise(SecurityError) do
- Thread.new do
- $SAFE = 4
- Object.instance_eval { remove_method(:foo) }
- end.join
- end
-
- assert_raise(SecurityError) do
- Thread.new do
- $SAFE = 4
- Class.instance_eval { remove_method(:foo) }
- end.join
- end
-
c = Class.new
c.freeze
- assert_raise(RuntimeError) do
+ assert_raise(FrozenError) do
c.instance_eval { remove_method(:foo) }
end
@@ -286,7 +424,7 @@ class TestObject < Test::Unit::TestCase
assert_raise(NoMethodError, bug2202) {o2.meth2}
%w(object_id __send__ initialize).each do |m|
- assert_in_out_err([], <<-INPUT, %w(:ok), /warning: removing `#{m}' may cause serious problems$/)
+ assert_in_out_err([], <<-INPUT, %w(:ok), %r"warning: removing `#{m}' may cause serious problems$")
$VERBOSE = false
begin
Class.new.instance_eval { remove_method(:#{m}) }
@@ -295,6 +433,19 @@ class TestObject < Test::Unit::TestCase
end
INPUT
end
+
+ m = "\u{30e1 30bd 30c3 30c9}"
+ c = Class.new
+ assert_raise_with_message(NameError, /#{m}/) do
+ c.class_eval {remove_method m}
+ end
+ c = Class.new {
+ define_method(m) {}
+ remove_method(m)
+ }
+ assert_raise_with_message(NameError, /#{m}/) do
+ c.class_eval {remove_method m}
+ end
end
def test_method_missing
@@ -334,16 +485,29 @@ class TestObject < Test::Unit::TestCase
assert_nothing_raised(bug2494) {[b].flatten}
end
+ def test_respond_to_missing_string
+ c = Class.new do
+ def respond_to_missing?(id, priv)
+ !(id !~ /\Agadzoks\d+\z/) ^ priv
+ end
+ end
+ foo = c.new
+ assert_equal(false, foo.respond_to?("gadzooks16"))
+ assert_equal(true, foo.respond_to?("gadzooks17", true))
+ assert_equal(true, foo.respond_to?("gadzoks16"))
+ assert_equal(false, foo.respond_to?("gadzoks17", true))
+ end
+
def test_respond_to_missing
c = Class.new do
- def respond_to_missing?(id, priv=false)
+ def respond_to_missing?(id, priv)
if id == :foobar
true
else
false
end
end
- def method_missing(id,*args)
+ def method_missing(id, *args)
if id == :foobar
return [:foo, *args]
else
@@ -356,8 +520,8 @@ class TestObject < Test::Unit::TestCase
assert_equal([:foo], foo.foobar);
assert_equal([:foo, 1], foo.foobar(1));
assert_equal([:foo, 1, 2, 3, 4, 5], foo.foobar(1, 2, 3, 4, 5));
- assert(foo.respond_to?(:foobar))
- assert_equal(false, foo.respond_to?(:foobarbaz))
+ assert_respond_to(foo, :foobar)
+ assert_not_respond_to(foo, :foobarbaz)
assert_raise(NoMethodError) do
foo.foobarbaz
end
@@ -383,10 +547,163 @@ class TestObject < Test::Unit::TestCase
end
end
+ def test_implicit_respond_to
+ bug5158 = '[ruby-core:38799]'
+
+ p = Object.new
+
+ called = []
+ p.singleton_class.class_eval do
+ define_method(:to_ary) do
+ called << [:to_ary, bug5158]
+ end
+ end
+ [[p]].flatten
+ assert_equal([[:to_ary, bug5158]], called, bug5158)
+
+ called = []
+ p.singleton_class.class_eval do
+ define_method(:respond_to?) do |*a|
+ called << [:respond_to?, *a]
+ false
+ end
+ end
+ [[p]].flatten
+ assert_equal([[:respond_to?, :to_ary, true]], called, bug5158)
+ end
+
+ def test_implicit_respond_to_arity_1
+ p = Object.new
+
+ called = []
+ p.singleton_class.class_eval do
+ define_method(:respond_to?) do |a|
+ called << [:respond_to?, a]
+ false
+ end
+ end
+ [[p]].flatten
+ assert_equal([[:respond_to?, :to_ary]], called, '[bug:6000]')
+ end
+
+ def test_implicit_respond_to_arity_3
+ p = Object.new
+
+ called = []
+ p.singleton_class.class_eval do
+ define_method(:respond_to?) do |a, b, c|
+ called << [:respond_to?, a, b, c]
+ false
+ end
+ end
+
+ msg = 'respond_to? must accept 1 or 2 arguments (requires 3)'
+ assert_raise_with_message(ArgumentError, msg, '[bug:6000]') do
+ [[p]].flatten
+ end
+ end
+
+ def test_method_missing_passed_block
+ bug5731 = '[ruby-dev:44961]'
+
+ c = Class.new do
+ def method_missing(meth, *args) yield(meth, *args) end
+ end
+ a = c.new
+ result = nil
+ assert_nothing_raised(LocalJumpError, bug5731) do
+ a.foo {|x| result = x}
+ end
+ assert_equal(:foo, result, bug5731)
+ result = nil
+ e = a.enum_for(:foo)
+ assert_nothing_raised(LocalJumpError, bug5731) do
+ e.each {|x| result = x}
+ end
+ assert_equal(:foo, result, bug5731)
+
+ c = Class.new do
+ def respond_to_missing?(id, priv)
+ true
+ end
+ def method_missing(id, *args, &block)
+ return block.call(:foo, *args)
+ end
+ end
+ foo = c.new
+
+ result = nil
+ assert_nothing_raised(LocalJumpError, bug5731) do
+ foo.foobar {|x| result = x}
+ end
+ assert_equal(:foo, result, bug5731)
+ result = nil
+ assert_nothing_raised(LocalJumpError, bug5731) do
+ foo.enum_for(:foobar).each {|x| result = x}
+ end
+ assert_equal(:foo, result, bug5731)
+
+ result = nil
+ foobar = foo.method(:foobar)
+ foobar.call {|x| result = x}
+ assert_equal(:foo, result, bug5731)
+
+ result = nil
+ foobar = foo.method(:foobar)
+ foobar.enum_for(:call).each {|x| result = x}
+ assert_equal(:foo, result, bug5731)
+ end
+
def test_send_with_no_arguments
assert_raise(ArgumentError) { 1.send }
end
+ def test_send_with_block
+ x = :ng
+ 1.send(:times) { x = :ok }
+ assert_equal(:ok, x)
+
+ x = :ok
+ o = Object.new
+ def o.inspect
+ yield if block_given?
+ super
+ end
+ begin
+ nil.public_send(o) { x = :ng }
+ rescue TypeError
+ end
+ assert_equal(:ok, x)
+ end
+
+ def test_public_send
+ c = Class.new do
+ def pub
+ :ok
+ end
+
+ def invoke(m)
+ public_send(m)
+ end
+
+ protected
+ def prot
+ :ng
+ end
+
+ private
+ def priv
+ :ng
+ end
+ end.new
+ assert_equal(:ok, c.public_send(:pub))
+ assert_raise(NoMethodError) {c.public_send(:priv)}
+ assert_raise(NoMethodError) {c.public_send(:prot)}
+ assert_raise(NoMethodError) {c.invoke(:priv)}
+ bug7499 = '[ruby-core:50489]'
+ assert_raise(NoMethodError, bug7499) {c.invoke(:prot)}
+ end
+
def test_no_superclass_method
bug2312 = '[ruby-dev:39581]'
@@ -449,117 +766,95 @@ class TestObject < Test::Unit::TestCase
end
def test_untrusted
- obj = lambda {
- $SAFE = 4
- x = Object.new
- x.instance_eval { @foo = 1 }
- x
- }.call
- assert_equal(true, obj.untrusted?)
- assert_equal(true, obj.tainted?)
-
- x = Object.new
- assert_equal(false, x.untrusted?)
- assert_raise(SecurityError) do
- lambda {
- $SAFE = 4
- x.instance_eval { @foo = 1 }
- }.call
+ verbose = $VERBOSE
+ $VERBOSE = false
+ begin
+ obj = Object.new
+ assert_equal(false, obj.untrusted?)
+ assert_equal(false, obj.tainted?)
+ obj.untrust
+ assert_equal(true, obj.untrusted?)
+ assert_equal(true, obj.tainted?)
+ obj.trust
+ assert_equal(false, obj.untrusted?)
+ assert_equal(false, obj.tainted?)
+ obj.taint
+ assert_equal(true, obj.untrusted?)
+ assert_equal(true, obj.tainted?)
+ obj.untaint
+ assert_equal(false, obj.untrusted?)
+ assert_equal(false, obj.tainted?)
+ ensure
+ $VERBOSE = verbose
end
+ end
+ def test_to_s
x = Object.new
x.taint
- assert_raise(SecurityError) do
- lambda {
- $SAFE = 4
- x.instance_eval { @foo = 1 }
- }.call
- end
+ s = x.to_s
+ assert_equal(true, s.tainted?)
- x.untrust
- assert_equal(true, x.untrusted?)
- assert_nothing_raised do
- lambda {
- $SAFE = 4
- x.instance_eval { @foo = 1 }
- }.call
- end
+ x = eval(<<-EOS)
+ class ToS\u{3042}
+ new.to_s
+ end
+ EOS
+ assert_match(/\bToS\u{3042}:/, x)
- x.trust
- assert_equal(false, x.untrusted?)
- assert_raise(SecurityError) do
- lambda {
- $SAFE = 4
- x.instance_eval { @foo = 1 }
- }.call
- end
+ name = "X".freeze
+ x = Object.new.taint
+ class<<x;self;end.class_eval {define_method(:to_s) {name}}
+ assert_same(name, x.to_s)
+ assert_not_predicate(name, :tainted?)
+ assert_raise(FrozenError) {name.taint}
+ assert_equal("X", [x].join(""))
+ assert_not_predicate(name, :tainted?)
+ assert_not_predicate(eval('"X".freeze'), :tainted?)
+ end
- a = Object.new
- a.untrust
- assert_equal(true, a.untrusted?)
- b = a.dup
- assert_equal(true, b.untrusted?)
- c = a.clone
- assert_equal(true, c.untrusted?)
+ def test_inspect
+ x = Object.new
+ assert_match(/\A#<Object:0x\h+>\z/, x.inspect)
- a = Object.new
- b = lambda {
- $SAFE = 4
- a.dup
- }.call
- assert_equal(true, b.untrusted?)
+ x.instance_variable_set(:@ivar, :value)
+ assert_match(/\A#<Object:0x\h+ @ivar=:value>\z/, x.inspect)
- a = Object.new
- b = lambda {
- $SAFE = 4
- a.clone
- }.call
- assert_equal(true, b.untrusted?)
- end
-
- def test_to_s
x = Object.new
- x.taint
- x.untrust
- s = x.to_s
- assert_equal(true, s.untrusted?)
- assert_equal(true, s.tainted?)
- end
+ x.instance_variable_set(:@recur, x)
+ assert_match(/\A#<Object:0x\h+ @recur=#<Object:0x\h+ \.\.\.>>\z/, x.inspect)
- def test_exec_recursive
- Thread.current[:__recursive_key__] = nil
- a = [[]]
- a.inspect
+ x = Object.new
+ x.instance_variable_set(:@foo, "value")
+ x.instance_variable_set(:@bar, 42)
+ assert_match(/\A#<Object:0x\h+ (?:@foo="value", @bar=42|@bar=42, @foo="value")>\z/, x.inspect)
- assert_nothing_raised do
- -> do
- $SAFE = 4
- begin
- a.hash
- rescue ArgumentError
- end
- end.call
+ # #inspect does not call #to_s anymore
+ feature6130 = '[ruby-core:43238]'
+ x = Object.new
+ def x.to_s
+ "to_s"
end
+ assert_match(/\A#<Object:0x\h+>\z/, x.inspect, feature6130)
- -> do
- assert_nothing_raised do
- $SAFE = 4
- a.inspect
+ x = eval(<<-EOS)
+ class Inspect\u{3042}
+ new.inspect
end
- end.call
+ EOS
+ assert_match(/\bInspect\u{3042}:/, x)
- -> do
- o = Object.new
- def o.to_ary(x); end
- def o.==(x); $SAFE = 4; false; end
- a = [[o]]
- b = []
- b << b
-
- assert_nothing_raised do
- b == a
+ x = eval(<<-EOS)
+ class Inspect\u{3042}
+ def initialize
+ @\u{3044} = 42
+ end
+ new
end
- end.call
+ EOS
+ assert_match(/\bInspect\u{3042}:.* @\u{3044}=42\b/, x.inspect)
+ x.instance_variable_set("@\u{3046}".encode(Encoding::EUC_JP), 6)
+ assert_match(/@\u{3046}=6\b/, x.inspect)
end
def test_singleton_class
@@ -582,4 +877,73 @@ class TestObject < Test::Unit::TestCase
:foo.singleton_class
end
end
+
+ def test_redef_method_missing
+ bug5473 = '[ruby-core:40287]'
+ ['ArgumentError.new("bug5473")', 'ArgumentError, "bug5473"', '"bug5473"'].each do |code|
+ exc = code[/\A[A-Z]\w+/] || 'RuntimeError'
+ assert_separately([], <<-SRC)
+ $VERBOSE = nil
+ class ::Object
+ def method_missing(m, *a, &b)
+ raise #{code}
+ end
+ end
+
+ assert_raise_with_message(#{exc}, "bug5473", #{bug5473.dump}) {1.foo}
+ SRC
+ end
+ end
+
+ def assert_not_initialize_copy
+ a = yield
+ b = yield
+ assert_nothing_raised("copy") {a.instance_eval {initialize_copy(b)}}
+ c = a.dup.freeze
+ assert_raise(FrozenError, "frozen") {c.instance_eval {initialize_copy(b)}}
+ d = a.dup.trust
+ [a, b, c, d]
+ end
+
+ def test_bad_initialize_copy
+ assert_not_initialize_copy {Object.new}
+ assert_not_initialize_copy {[].to_enum}
+ assert_not_initialize_copy {Enumerator::Generator.new {}}
+ assert_not_initialize_copy {Enumerator::Yielder.new {}}
+ assert_not_initialize_copy {File.stat(__FILE__)}
+ assert_not_initialize_copy {open(__FILE__)}.each(&:close)
+ assert_not_initialize_copy {ARGF.class.new}
+ assert_not_initialize_copy {Random.new}
+ assert_not_initialize_copy {//}
+ assert_not_initialize_copy {/.*/.match("foo")}
+ st = Struct.new(:foo)
+ assert_not_initialize_copy {st.new}
+ end
+
+ def test_type_error_message
+ _issue = "Bug #7539"
+ assert_raise_with_message(TypeError, "can't convert Array into Integer") {Integer([42])}
+ assert_raise_with_message(TypeError, 'no implicit conversion of Array into Integer') {[].first([42])}
+ assert_raise_with_message(TypeError, "can't convert Array into Rational") {Rational([42])}
+ end
+
+ def test_copied_ivar_memory_leak
+ bug10191 = '[ruby-core:64700] [Bug #10191]'
+ assert_no_memory_leak([], <<-"end;", <<-"end;", bug10191, timeout: 60, limit: 1.8)
+ def (a = Object.new).set; @v = nil; end
+ num = 500_000
+ end;
+ num.times {a.clone.set}
+ end;
+ end
+
+ def test_clone_object_should_not_be_old
+ assert_normal_exit <<-EOS, '[Bug #13775]'
+ b = proc { }
+ 10.times do |i|
+ b.clone
+ GC.start
+ end
+ EOS
+ end
end
diff --git a/test/ruby/test_objectspace.rb b/test/ruby/test_objectspace.rb
index 24731a7a50..c352b75b70 100644
--- a/test/ruby/test_objectspace.rb
+++ b/test/ruby/test_objectspace.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestObjectSpace < Test::Unit::TestCase
def self.deftest_id2ref(obj)
@@ -39,13 +39,13 @@ End
h = {}
ObjectSpace.count_objects(h)
assert_kind_of(Hash, h)
- assert(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) })
- assert(h.values.all? {|x| x.is_a?(Integer) })
+ assert_empty(h.keys.delete_if {|x| x.is_a?(Symbol) || x.is_a?(Integer) })
+ assert_empty(h.values.delete_if {|x| x.is_a?(Integer) })
h = ObjectSpace.count_objects
assert_kind_of(Hash, h)
- assert(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) })
- assert(h.values.all? {|x| x.is_a?(Integer) })
+ assert_empty(h.keys.delete_if {|x| x.is_a?(Symbol) || x.is_a?(Integer) })
+ assert_empty(h.values.delete_if {|x| x.is_a?(Integer) })
assert_raise(TypeError) { ObjectSpace.count_objects(1) }
@@ -64,5 +64,143 @@ End
!b
END
assert_raise(ArgumentError) { ObjectSpace.define_finalizer([], Object.new) }
+
+ code = proc do |priv|
+ <<-"CODE"
+ fin = Object.new
+ class << fin
+ #{priv}def call(id)
+ puts "finalized"
+ end
+ end
+ ObjectSpace.define_finalizer([], fin)
+ CODE
+ end
+ assert_in_out_err([], code[""], ["finalized"])
+ assert_in_out_err([], code["private "], ["finalized"])
+ c = EnvUtil.labeled_class("C\u{3042}").new
+ o = Object.new
+ assert_raise_with_message(ArgumentError, /C\u{3042}/) {
+ ObjectSpace.define_finalizer(o, c)
+ }
+ end
+
+ def test_finalizer_with_super
+ assert_in_out_err(["-e", <<-END], "", %w(:ok), [])
+ class A
+ def foo
+ end
+ end
+
+ class B < A
+ def foo
+ 1.times { super }
+ end
+ end
+
+ class C
+ module M
+ end
+
+ FINALIZER = proc do
+ M.module_eval(__FILE__, "", __LINE__) do
+ end
+ end
+
+ def define_finalizer
+ ObjectSpace.define_finalizer(self, FINALIZER)
+ end
+ end
+
+ class D
+ def foo
+ B.new.foo
+ end
+ end
+
+ C::M.singleton_class.send :define_method, :module_eval do |src, id, line|
+ end
+
+ GC.stress = true
+ 10.times do
+ C.new.define_finalizer
+ D.new.foo
+ end
+
+ p :ok
+ END
+ end
+
+ def test_each_object
+ klass = Class.new
+ new_obj = klass.new
+
+ found = []
+ count = ObjectSpace.each_object(klass) do |obj|
+ found << obj
+ end
+ assert_equal(1, count)
+ assert_equal(1, found.size)
+ assert_same(new_obj, found[0])
+ end
+
+ def test_each_object_enumerator
+ klass = Class.new
+ new_obj = klass.new
+
+ found = []
+ counter = ObjectSpace.each_object(klass)
+ assert_equal(1, counter.each {|obj| found << obj})
+ assert_equal(1, found.size)
+ assert_same(new_obj, found[0])
+ end
+
+ def test_each_object_no_gabage
+ assert_separately([], <<-End)
+ GC.disable
+ eval('begin; 1.times{}; rescue; ensure; end')
+ arys = []
+ ObjectSpace.each_object(Array){|ary|
+ arys << ary
+ }
+ GC.enable
+ arys.each{|ary|
+ begin
+ assert_equal(String, ary.inspect.class) # should not cause SEGV
+ rescue RuntimeError
+ # rescue "can't modify frozen File" error.
+ end
+ }
+ End
+ end
+
+ def test_each_object_recursive_key
+ assert_normal_exit(<<-'end;', '[ruby-core:66742] [Bug #10579]')
+ h = {["foo"]=>nil}
+ p Thread.current[:__recursive_key__]
+ end;
+ end
+
+ def test_each_object_singleton_class
+ assert_separately([], <<-End)
+ class C
+ class << self
+ $c = self
+ end
+ end
+
+ exist = false
+ ObjectSpace.each_object(Class){|o|
+ exist = true if $c == o
+ }
+ assert(exist, 'Bug #11360')
+ End
+
+ klass = Class.new
+ instance = klass.new
+ sclass = instance.singleton_class
+ meta = klass.singleton_class
+ assert_kind_of(meta, sclass)
+ assert_include(ObjectSpace.each_object(meta).to_a, sclass)
end
end
diff --git a/test/ruby/test_optimization.rb b/test/ruby/test_optimization.rb
index 8e8311e6ef..bc3eacce52 100644
--- a/test/ruby/test_optimization.rb
+++ b/test/ruby/test_optimization.rb
@@ -1,88 +1,124 @@
+# frozen_string_literal: false
require 'test/unit'
+require 'objspace'
class TestRubyOptimization < Test::Unit::TestCase
-
- BIGNUM_POS_MIN_32 = 1073741824 # 2 ** 30
- if BIGNUM_POS_MIN_32.kind_of?(Fixnum)
- FIXNUM_MAX = 4611686018427387903 # 2 ** 62 - 1
- else
- FIXNUM_MAX = 1073741823 # 2 ** 30 - 1
- end
-
- BIGNUM_NEG_MAX_32 = -1073741825 # -2 ** 30 - 1
- if BIGNUM_NEG_MAX_32.kind_of?(Fixnum)
- FIXNUM_MIN = -4611686018427387904 # -2 ** 62
- else
- FIXNUM_MIN = -1073741824 # -2 ** 30
- end
-
- def redefine_method(klass, method)
- (@redefine_method_seq ||= 0)
- seq = (@redefine_method_seq += 1)
- eval(<<-End, ::TOPLEVEL_BINDING)
+ def assert_redefine_method(klass, method, code, msg = nil)
+ assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
class #{klass}
- alias redefine_method_orig_#{seq} #{method}
undef #{method}
def #{method}(*args)
args[0]
end
end
- End
- begin
- return yield
- ensure
- eval(<<-End, ::TOPLEVEL_BINDING)
- class #{klass}
- undef #{method}
- alias #{method} redefine_method_orig_#{seq}
- end
- End
- end
+ #{code}
+ end;
end
- def test_fixnum_plus
- a, b = 1, 2
- assert_equal 3, a + b
- assert_instance_of Fixnum, FIXNUM_MAX
- assert_instance_of Bignum, FIXNUM_MAX + 1
+ def disasm(name)
+ RubyVM::InstructionSequence.of(method(name)).disasm
+ end
+ def test_fixnum_plus
assert_equal 21, 10 + 11
- assert_equal 11, redefine_method('Fixnum', '+') { 10 + 11 }
- assert_equal 21, 10 + 11
+ assert_redefine_method('Integer', '+', 'assert_equal 11, 10 + 11')
end
def test_fixnum_minus
assert_equal 5, 8 - 3
- assert_instance_of Fixnum, FIXNUM_MIN
- assert_instance_of Bignum, FIXNUM_MIN - 1
-
- assert_equal 5, 8 - 3
- assert_equal 3, redefine_method('Fixnum', '-') { 8 - 3 }
- assert_equal 5, 8 - 3
+ assert_redefine_method('Integer', '-', 'assert_equal 3, 8 - 3')
end
def test_fixnum_mul
assert_equal 15, 3 * 5
+ assert_redefine_method('Integer', '*', 'assert_equal 5, 3 * 5')
end
def test_fixnum_div
assert_equal 3, 15 / 5
+ assert_redefine_method('Integer', '/', 'assert_equal 5, 15 / 5')
end
def test_fixnum_mod
assert_equal 1, 8 % 7
+ assert_redefine_method('Integer', '%', 'assert_equal 7, 8 % 7')
+ end
+
+ def test_fixnum_lt
+ assert_equal true, 1 < 2
+ assert_redefine_method('Integer', '<', 'assert_equal 2, 1 < 2')
+ end
+
+ def test_fixnum_le
+ assert_equal true, 1 <= 2
+ assert_redefine_method('Integer', '<=', 'assert_equal 2, 1 <= 2')
+ end
+
+ def test_fixnum_gt
+ assert_equal false, 1 > 2
+ assert_redefine_method('Integer', '>', 'assert_equal 2, 1 > 2')
+ end
+
+ def test_fixnum_ge
+ assert_equal false, 1 >= 2
+ assert_redefine_method('Integer', '>=', 'assert_equal 2, 1 >= 2')
end
def test_float_plus
assert_equal 4.0, 2.0 + 2.0
- assert_equal 2.0, redefine_method('Float', '+') { 2.0 + 2.0 }
+ assert_redefine_method('Float', '+', 'assert_equal 2.0, 2.0 + 2.0')
+ end
+
+ def test_float_minus
assert_equal 4.0, 2.0 + 2.0
+ assert_redefine_method('Float', '+', 'assert_equal 2.0, 2.0 + 2.0')
+ end
+
+ def test_float_mul
+ assert_equal 29.25, 4.5 * 6.5
+ assert_redefine_method('Float', '*', 'assert_equal 6.5, 4.5 * 6.5')
+ end
+
+ def test_float_div
+ assert_in_delta 0.63063063063063063, 4.2 / 6.66
+ assert_redefine_method('Float', '/', 'assert_equal 6.66, 4.2 / 6.66', "[Bug #9238]")
+ end
+
+ def test_float_lt
+ assert_equal true, 1.1 < 2.2
+ assert_redefine_method('Float', '<', 'assert_equal 2.2, 1.1 < 2.2')
+ end
+
+ def test_float_le
+ assert_equal true, 1.1 <= 2.2
+ assert_redefine_method('Float', '<=', 'assert_equal 2.2, 1.1 <= 2.2')
+ end
+
+ def test_float_gt
+ assert_equal false, 1.1 > 2.2
+ assert_redefine_method('Float', '>', 'assert_equal 2.2, 1.1 > 2.2')
+ end
+
+ def test_float_ge
+ assert_equal false, 1.1 >= 2.2
+ assert_redefine_method('Float', '>=', 'assert_equal 2.2, 1.1 >= 2.2')
end
def test_string_length
assert_equal 6, "string".length
- assert_nil redefine_method('String', 'length') { "string".length }
- assert_equal 6, "string".length
+ assert_redefine_method('String', 'length', 'assert_nil "string".length')
+ end
+
+ def test_string_size
+ assert_equal 6, "string".size
+ assert_redefine_method('String', 'size', 'assert_nil "string".size')
+ end
+
+ def test_string_empty?
+ assert_equal true, "".empty?
+ assert_equal false, "string".empty?
+ assert_redefine_method('String', 'empty?', 'assert_nil "string".empty?')
end
def test_string_plus
@@ -90,8 +126,7 @@ class TestRubyOptimization < Test::Unit::TestCase
assert_equal "x", "x" + ""
assert_equal "x", "" + "x"
assert_equal "ab", "a" + "b"
- assert_equal 'b', redefine_method('String', '+') { "a" + "b" }
- assert_equal "ab", "a" + "b"
+ assert_redefine_method('String', '+', 'assert_equal "b", "a" + "b"')
end
def test_string_succ
@@ -101,24 +136,120 @@ class TestRubyOptimization < Test::Unit::TestCase
def test_string_format
assert_equal '2', '%d' % 2
+ assert_redefine_method('String', '%', 'assert_equal 2, "%d" % 2')
+ end
+
+ def test_string_freeze
+ assert_equal "foo", "foo".freeze
+ assert_equal "foo".freeze.object_id, "foo".freeze.object_id
+ assert_redefine_method('String', 'freeze', 'assert_nil "foo".freeze')
+ end
+
+ def test_string_uminus
+ assert_same "foo".freeze, -"foo"
+ assert_redefine_method('String', '-@', 'assert_nil(-"foo")')
+ end
+
+ def test_string_freeze_saves_memory
+ n = 16384
+ data = '.'.freeze
+ r, w = IO.pipe
+ w.write data
+
+ s = r.readpartial(n, '')
+ assert_operator ObjectSpace.memsize_of(s), :>=, n,
+ 'IO buffer NOT resized prematurely because will likely be reused'
+
+ s.freeze
+ assert_equal ObjectSpace.memsize_of(data), ObjectSpace.memsize_of(s),
+ 'buffer resized on freeze since it cannot be written to again'
+ ensure
+ r.close if r
+ w.close if w
+ end
+
+ def test_string_eq_neq
+ %w(== !=).each do |m|
+ assert_redefine_method('String', m, <<-end)
+ assert_equal :b, ("a" #{m} "b").to_sym
+ b = 'b'
+ assert_equal :b, ("a" #{m} b).to_sym
+ assert_equal :b, (b #{m} "b").to_sym
+ end
+ end
+ end
+
+ def test_string_ltlt
+ assert_equal "", "" << ""
+ assert_equal "x", "x" << ""
+ assert_equal "x", "" << "x"
+ assert_equal "ab", "a" << "b"
+ assert_redefine_method('String', '<<', 'assert_equal "b", "a" << "b"')
+ end
+
+ def test_fixnum_and
+ assert_equal 1, 1&3
+ assert_redefine_method('Integer', '&', 'assert_equal 3, 1&3')
+ end
+
+ def test_fixnum_or
+ assert_equal 3, 1|3
+ assert_redefine_method('Integer', '|', 'assert_equal 1, 3|1')
end
def test_array_plus
assert_equal [1,2], [1]+[2]
+ assert_redefine_method('Array', '+', 'assert_equal [2], [1]+[2]')
end
def test_array_minus
assert_equal [2], [1,2] - [1]
+ assert_redefine_method('Array', '-', 'assert_equal [1], [1,2]-[1]')
end
def test_array_length
assert_equal 0, [].length
assert_equal 3, [1,2,3].length
+ assert_redefine_method('Array', 'length', 'assert_nil([].length); assert_nil([1,2,3].length)')
+ end
+
+ def test_array_empty?
+ assert_equal true, [].empty?
+ assert_equal false, [1,2,3].empty?
+ assert_redefine_method('Array', 'empty?', 'assert_nil([].empty?); assert_nil([1,2,3].empty?)')
end
def test_hash_length
assert_equal 0, {}.length
assert_equal 1, {1=>1}.length
+ assert_redefine_method('Hash', 'length', 'assert_nil({}.length); assert_nil({1=>1}.length)')
+ end
+
+ def test_hash_empty?
+ assert_equal true, {}.empty?
+ assert_equal false, {1=>1}.empty?
+ assert_redefine_method('Hash', 'empty?', 'assert_nil({}.empty?); assert_nil({1=>1}.empty?)')
+ end
+
+ def test_hash_aref_with
+ h = { "foo" => 1 }
+ assert_equal 1, h["foo"]
+ assert_redefine_method('Hash', '[]', "#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ h = { "foo" => 1 }
+ assert_equal "foo", h["foo"]
+ end;
+ end
+
+ def test_hash_aset_with
+ h = {}
+ assert_equal 1, h["foo"] = 1
+ assert_redefine_method('Hash', '[]=', "#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ h = {}
+ assert_equal 1, h["foo"] = 1, "assignment always returns value set"
+ assert_nil h["foo"]
+ end;
end
class MyObj
@@ -137,4 +268,560 @@ class TestRubyOptimization < Test::Unit::TestCase
assert_equal true, MyObj.new == nil
end
+ def self.tailcall(klass, src, file = nil, path = nil, line = nil, tailcall: true)
+ unless file
+ loc, = caller_locations(1, 1)
+ file = loc.path
+ line ||= loc.lineno
+ end
+ RubyVM::InstructionSequence.new("proc {|_|_.class_eval {#{src}}}",
+ file, (path || file), line,
+ tailcall_optimization: tailcall,
+ trace_instruction: false)
+ .eval[klass]
+ end
+
+ def tailcall(*args)
+ self.class.tailcall(singleton_class, *args)
+ end
+
+ def test_tailcall
+ bug4082 = '[ruby-core:33289]'
+
+ tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ def fact_helper(n, res)
+ if n == 1
+ res
+ else
+ fact_helper(n - 1, n * res)
+ end
+ end
+ def fact(n)
+ fact_helper(n, 1)
+ end
+ end;
+ assert_equal(9131, fact(3000).to_s.size, message(bug4082) {disasm(:fact_helper)})
+ end
+
+ def test_tailcall_with_block
+ bug6901 = '[ruby-dev:46065]'
+
+ tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ def identity(val)
+ val
+ end
+
+ def delay
+ -> {
+ identity(yield)
+ }
+ end
+ end;
+ assert_equal(123, delay { 123 }.call, message(bug6901) {disasm(:delay)})
+ end
+
+ def just_yield
+ yield
+ end
+
+ def test_tailcall_inhibited_by_block
+ tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ def yield_result
+ just_yield {:ok}
+ end
+ end;
+ assert_equal(:ok, yield_result, message {disasm(:yield_result)})
+ end
+
+ def do_raise
+ raise "should be rescued"
+ end
+
+ def errinfo
+ $!
+ end
+
+ def test_tailcall_inhibited_by_rescue
+ bug12082 = '[ruby-core:73871] [Bug #12082]'
+
+ tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ def to_be_rescued
+ return do_raise
+ 1 + 2
+ rescue
+ errinfo
+ end
+ end;
+ result = assert_nothing_raised(RuntimeError, message(bug12082) {disasm(:to_be_rescued)}) {
+ to_be_rescued
+ }
+ assert_instance_of(RuntimeError, result, bug12082)
+ assert_equal("should be rescued", result.message, bug12082)
+ end
+
+ def test_tailcall_symbol_block_arg
+ bug12565 = '[ruby-core:46065]'
+ tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ def apply_one_and_two(&block)
+ yield(1, 2)
+ end
+
+ def add_one_and_two
+ apply_one_and_two(&:+)
+ end
+ end;
+ assert_equal(3, add_one_and_two,
+ message(bug12565) {disasm(:add_one_and_two)})
+ end
+
+ def test_tailcall_interrupted_by_sigint
+ bug12576 = 'ruby-core:76327'
+ script = "#{<<-"begin;"}\n#{<<~'end;'}"
+ begin;
+ RubyVM::InstructionSequence.compile_option = {
+ :tailcall_optimization => true,
+ :trace_instruction => false
+ }
+
+ eval "#{<<~"begin;"}\n#{<<~'end;1'}"
+ begin;
+ def foo
+ foo
+ end
+ puts("start")
+ STDOUT.flush
+ foo
+ end;1
+ end;
+ status, _err = EnvUtil.invoke_ruby([], "", true, true, {}) {
+ |in_p, out_p, err_p, pid|
+ in_p.write(script)
+ in_p.close
+ out_p.gets
+ sig = :INT
+ begin
+ Process.kill(sig, pid)
+ Timeout.timeout(1) do
+ *, stat = Process.wait2(pid)
+ [stat, err_p.read]
+ end
+ rescue Timeout::Error
+ if sig == :INT
+ sig = :KILL
+ retry
+ else
+ raise
+ end
+ end
+ }
+ assert_not_equal("SEGV", Signal.signame(status.termsig || 0), bug12576)
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_tailcall_condition_block
+ bug = '[ruby-core:78015] [Bug #12905]'
+
+ src = "#{<<-"begin;"}\n#{<<~"end;"}"
+ begin;
+ def run(current, final)
+ if current < final
+ run(current+1, final)
+ else
+ nil
+ end
+ end
+ end;
+
+ obj = Object.new
+ self.class.tailcall(obj.singleton_class, src, tailcall: false)
+ e = assert_raise(SystemStackError) {
+ obj.run(1, Float::INFINITY)
+ }
+ level = e.backtrace_locations.size
+ obj = Object.new
+ self.class.tailcall(obj.singleton_class, src, tailcall: true)
+ level *= 2
+ mesg = message {"#{bug}: #{$!.backtrace_locations.size} / #{level} stack levels"}
+ assert_nothing_raised(SystemStackError, mesg) {
+ obj.run(1, level)
+ }
+ end
+
+ class Bug10557
+ def [](_)
+ block_given?
+ end
+
+ def []=(_, _)
+ block_given?
+ end
+ end
+
+ def test_block_given_aset_aref
+ bug10557 = '[ruby-core:66595]'
+ assert_equal(true, Bug10557.new.[](nil){}, bug10557)
+ assert_equal(true, Bug10557.new.[](0){}, bug10557)
+ assert_equal(true, Bug10557.new.[](false){}, bug10557)
+ assert_equal(true, Bug10557.new.[](''){}, bug10557)
+ assert_equal(true, Bug10557.new.[]=(nil, 1){}, bug10557)
+ assert_equal(true, Bug10557.new.[]=(0, 1){}, bug10557)
+ assert_equal(true, Bug10557.new.[]=(false, 1){}, bug10557)
+ assert_equal(true, Bug10557.new.[]=('', 1){}, bug10557)
+ end
+
+ def test_string_freeze_block
+ assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ class String
+ undef freeze
+ def freeze
+ block_given?
+ end
+ end
+ assert_equal(true, "block".freeze {})
+ assert_equal(false, "block".freeze)
+ end;
+ end
+
+ def test_opt_case_dispatch
+ code = "#{<<-"begin;"}\n#{<<~"end;"}"
+ begin;
+ case foo
+ when "foo" then :foo
+ when true then true
+ when false then false
+ when :sym then :sym
+ when 6 then :fix
+ when nil then nil
+ when 0.1 then :float
+ when 0xffffffffffffffff then :big
+ else
+ :nomatch
+ end
+ end;
+ check = {
+ 'foo' => :foo,
+ true => true,
+ false => false,
+ :sym => :sym,
+ 6 => :fix,
+ nil => nil,
+ 0.1 => :float,
+ 0xffffffffffffffff => :big,
+ }
+ iseq = RubyVM::InstructionSequence.compile(code)
+ assert_match %r{\bopt_case_dispatch\b}, iseq.disasm
+ check.each do |foo, expect|
+ assert_equal expect, eval("foo = #{foo.inspect}\n#{code}")
+ end
+ assert_equal :nomatch, eval("foo = :blah\n#{code}")
+ check.each do |foo, _|
+ klass = foo.class.to_s
+ assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}")
+ begin;
+ class #{klass}
+ undef ===
+ def ===(*args)
+ false
+ end
+ end
+ foo = #{foo.inspect}
+ ret = #{code}
+ assert_equal :nomatch, ret, foo.inspect
+ end;
+ end
+ end
+
+ def test_eqq
+ [ nil, true, false, 0.1, :sym, 'str', 0xffffffffffffffff ].each do |v|
+ k = v.class.to_s
+ assert_redefine_method(k, '===', "assert_equal(#{v.inspect} === 0, 0)")
+ end
+ end
+
+ def test_opt_case_dispatch_inf
+ inf = 1.0/0.0
+ result = case inf
+ when 1 then 1
+ when 0 then 0
+ else
+ inf.to_i rescue nil
+ end
+ assert_nil result, '[ruby-dev:49423] [Bug #11804]'
+ end
+
+ def test_nil_safe_conditional_assign
+ bug11816 = '[ruby-core:74993] [Bug #11816]'
+ assert_ruby_status([], 'nil&.foo &&= false', bug11816)
+ end
+
+ def test_peephole_string_literal_range
+ code = "#{<<~"begin;"}\n#{<<~"end;"}"
+ begin;
+ case ver
+ when "2.0.0".."2.3.2" then :foo
+ when "1.8.0"..."1.8.8" then :bar
+ end
+ end;
+ [ true, false ].each do |opt|
+ iseq = RubyVM::InstructionSequence.compile(code,
+ frozen_string_literal: opt)
+ insn = iseq.disasm
+ assert_match %r{putobject\s+#{Regexp.quote('"1.8.0"..."1.8.8"')}}, insn
+ assert_match %r{putobject\s+#{Regexp.quote('"2.0.0".."2.3.2"')}}, insn
+ assert_no_match(/putstring/, insn)
+ assert_no_match(/newrange/, insn)
+ end
+ end
+
+ def test_peephole_dstr
+ code = "#{<<~'begin;'}\n#{<<~'end;'}"
+ begin;
+ exp = -'a'
+ z = 'a'
+ [exp, -"#{z}"]
+ end;
+ [ false, true ].each do |fsl|
+ iseq = RubyVM::InstructionSequence.compile(code,
+ frozen_string_literal: fsl)
+ assert_same(*iseq.eval,
+ "[ruby-core:85542] [Bug #14475] fsl: #{fsl}")
+ end
+ end
+
+ def test_branch_condition_backquote
+ bug = '[ruby-core:80740] [Bug #13444] redefined backquote should be called'
+ class << self
+ def `(s)
+ @q = s
+ @r
+ end
+ end
+
+ @q = nil
+ @r = nil
+ assert_equal("bar", ("bar" unless `foo`), bug)
+ assert_equal("foo", @q, bug)
+
+ @q = nil
+ @r = true
+ assert_equal("bar", ("bar" if `foo`), bug)
+ assert_equal("foo", @q, bug)
+
+ @q = nil
+ @r = "z"
+ assert_equal("bar", ("bar" if `foo#{@r}`))
+ assert_equal("fooz", @q, bug)
+ end
+
+ def test_branch_condition_def
+ bug = '[ruby-core:80740] [Bug #13444] method should be defined'
+ c = Class.new do
+ raise "bug" unless def t;:ok;end
+ end
+ assert_nothing_raised(NoMethodError, bug) do
+ assert_equal(:ok, c.new.t)
+ end
+ end
+
+ def test_branch_condition_defs
+ bug = '[ruby-core:80740] [Bug #13444] singleton method should be defined'
+ raise "bug" unless def self.t;:ok;end
+ assert_nothing_raised(NameError, bug) do
+ assert_equal(:ok, t)
+ end
+ end
+
+ def test_retry_label_in_unreachable_chunk
+ bug = '[ruby-core:81272] [Bug #13578]'
+ assert_valid_syntax("#{<<-"begin;"}\n#{<<-"end;"}", bug)
+ begin;
+ def t; if false; case 42; when s {}; end; end; end
+ end;
+ end
+
+ def bptest_yield &b
+ yield
+ end
+
+ def bptest_yield_pass &b
+ bptest_yield(&b)
+ end
+
+ def bptest_bp_value &b
+ b
+ end
+
+ def bptest_bp_pass_bp_value &b
+ bptest_bp_value(&b)
+ end
+
+ def bptest_binding &b
+ binding
+ end
+
+ def bptest_set &b
+ b = Proc.new{2}
+ end
+
+ def test_block_parameter
+ assert_equal(1, bptest_yield{1})
+ assert_equal(1, bptest_yield_pass{1})
+ assert_equal(1, send(:bptest_yield){1})
+
+ assert_equal(Proc, bptest_bp_value{}.class)
+ assert_equal nil, bptest_bp_value
+ assert_equal(Proc, bptest_bp_pass_bp_value{}.class)
+ assert_equal nil, bptest_bp_pass_bp_value
+
+ assert_equal Proc, bptest_binding{}.local_variable_get(:b).class
+
+ assert_equal 2, bptest_set{1}.call
+ end
+
+ 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)
+ foo{}
+ ObjectSpace.count_objects(h2)
+
+ assert_equal 0, h2[:TOTAL] - h1[:TOTAL]
+ END
+ end
+
+ def test_block_parameter_should_restore_safe_level
+ assert_separately [], <<-END
+ #
+ def foo &b
+ $SAFE = 1
+ b.call
+ end
+ assert_equal 1, foo{$SAFE}
+ END
+ end
+
+ def test_peephole_optimization_without_trace
+ assert_separately [], <<-END
+ RubyVM::InstructionSequence.compile_option = {trace_instruction: false}
+ eval "def foo; 1.times{|(a), &b| nil && a}; end"
+ END
+ end
+
+ def test_clear_unreachable_keyword_args
+ assert_separately [], <<-END, timeout: 30
+ script = <<-EOS
+ if true
+ else
+ foo(k1:1)
+ end
+ EOS
+ GC.stress = true
+ 30.times{
+ RubyVM::InstructionSequence.compile(script)
+ }
+ END
+ end
+
+ def test_callinfo_unreachable_path
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ iseq = RubyVM::InstructionSequence.compile("if false; foo(bar: :baz); else :ok end")
+ bin = iseq.to_binary
+ iseq = RubyVM::InstructionSequence.load_from_binary(bin)
+ assert_instance_of(RubyVM::InstructionSequence, iseq)
+ assert_equal(:ok, iseq.eval)
+ end;
+ end
+
+ def test_side_effect_in_popped_splat
+ bug = '[ruby-core:84340] [Bug #14201]'
+ eval("{**(bug = nil; {})};42")
+ assert_nil(bug)
+
+ bug = '[ruby-core:85486] [Bug #14459]'
+ h = {}
+ assert_equal(bug, eval('{ok: 42, **h}; bug'))
+ assert_equal(:ok, eval('{ok: bug = :ok, **h}; bug'))
+ end
+
+ def test_overwritten_blockparam
+ obj = Object.new
+ def obj.a(&block)
+ block = 1
+ return :ok if block
+ :ng
+ end
+ assert_equal(:ok, obj.a())
+ end
+
+ def test_blockparam_in_rescue
+ obj = Object.new
+ def obj.foo(&b)
+ raise
+ rescue
+ b.call
+ end
+ result = nil
+ assert_equal(42, obj.foo {result = 42})
+ assert_equal(42, result)
+ end
+
+ def test_unconditional_branch_to_leave_block
+ assert_valid_syntax("#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ tap {true || tap {}}
+ end;
+ end
+
+ def test_jump_elimination_with_optimized_out_block
+ x = Object.new
+ def x.bug(obj)
+ if obj || obj
+ obj = obj
+ else
+ raise "[ruby-core:87830] [Bug #14897]"
+ end
+ obj
+ end
+ assert_equal(:ok, x.bug(:ok))
+ end
+
+ def test_jump_elimination_with_optimized_out_block_2
+ x = Object.new
+ def x.bug
+ a = "aaa"
+ ok = :NG
+ if a == "bbb" || a == "ccc" then
+ a = a
+ else
+ ok = :ok
+ end
+ ok
+ end
+ assert_equal(:ok, x.bug)
+ end
+
+ def test_peephole_jump_after_newarray
+ i = 0
+ %w(1) || 2 while (i += 1) < 100
+ assert_equal(100, i)
+ end
+
+ def test_optimized_empty_ensure
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}", timeout: 1)
+ begin;
+ assert_raise(RuntimeError) {
+ begin raise ensure nil if nil end
+ }
+ end;
+ end
end
diff --git a/test/ruby/test_pack.rb b/test/ruby/test_pack.rb
index a4a6308299..658208d9df 100644
--- a/test/ruby/test_pack.rb
+++ b/test/ruby/test_pack.rb
@@ -1,3 +1,5 @@
+# coding: US-ASCII
+# frozen_string_literal: false
require 'test/unit'
class TestPack < Test::Unit::TestCase
@@ -70,73 +72,118 @@ class TestPack < Test::Unit::TestCase
assert_equal [1,1,1], "\000\000\000\001\000\000\000\001\000\000\000\001".unpack('N*')
end
+ def _integer_big_endian(mod='')
+ assert_equal("\x01\x02", [0x0102].pack("s"+mod))
+ assert_equal("\x01\x02", [0x0102].pack("S"+mod))
+ assert_equal("\x01\x02\x03\x04", [0x01020304].pack("l"+mod))
+ assert_equal("\x01\x02\x03\x04", [0x01020304].pack("L"+mod))
+ assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("q"+mod))
+ assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("Q"+mod))
+ psize = [nil].pack('p').bytesize
+ if psize == 4
+ assert_equal("\x01\x02\x03\x04", [0x01020304].pack("j"+mod))
+ assert_equal("\x01\x02\x03\x04", [0x01020304].pack("J"+mod))
+ elsif psize == 8
+ assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("j"+mod))
+ assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("J"+mod))
+ end
+ assert_match(/\A\x00*\x01\x02\z/, [0x0102].pack("s!"+mod))
+ assert_match(/\A\x00*\x01\x02\z/, [0x0102].pack("S!"+mod))
+ assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("i"+mod))
+ assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("I"+mod))
+ assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("i!"+mod))
+ assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("I!"+mod))
+ assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("l!"+mod))
+ assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("L!"+mod))
+ if psize == 4
+ assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("j!"+mod))
+ assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("J!"+mod))
+ elsif psize == 8
+ assert_match(/\A\x00*\x01\x02\x03\x04\x05\x06\x07\x08\z/, [0x0102030405060708].pack("j!"+mod))
+ assert_match(/\A\x00*\x01\x02\x03\x04\x05\x06\x07\x08\z/, [0x0102030405060708].pack("J!"+mod))
+ end
+ %w[s S l L q Q j J s! S! i I i! I! l! L! j! J!].each {|fmt|
+ fmt += mod
+ nuls = [0].pack(fmt)
+ v = 0
+ s = "".force_encoding("ascii-8bit")
+ nuls.bytesize.times {|i|
+ j = i + 40
+ v = v * 256 + j
+ s << [j].pack("C")
+ }
+ assert_equal(s, [v].pack(fmt), "[#{v}].pack(#{fmt.dump})")
+ assert_equal([v], s.unpack(fmt), "#{s.dump}.unpack(#{fmt.dump})")
+ s2 = s+s
+ fmt2 = fmt+"*"
+ assert_equal([v,v], s2.unpack(fmt2), "#{s2.dump}.unpack(#{fmt2.dump})")
+ }
+ end
+
+ def _integer_little_endian(mod='')
+ assert_equal("\x02\x01", [0x0102].pack("s"+mod))
+ assert_equal("\x02\x01", [0x0102].pack("S"+mod))
+ assert_equal("\x04\x03\x02\x01", [0x01020304].pack("l"+mod))
+ assert_equal("\x04\x03\x02\x01", [0x01020304].pack("L"+mod))
+ assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("q"+mod))
+ assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("Q"+mod))
+ psize = [nil].pack('p').bytesize
+ if psize == 4
+ assert_equal("\x04\x03\x02\x01", [0x01020304].pack("j"+mod))
+ assert_equal("\x04\x03\x02\x01", [0x01020304].pack("J"+mod))
+ elsif psize == 8
+ assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("j"+mod))
+ assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("J"+mod))
+ end
+ assert_match(/\A\x02\x01\x00*\z/, [0x0102].pack("s!"+mod))
+ assert_match(/\A\x02\x01\x00*\z/, [0x0102].pack("S!"+mod))
+ assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("i"+mod))
+ assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("I"+mod))
+ assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("i!"+mod))
+ assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("I!"+mod))
+ assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("l!"+mod))
+ assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("L!"+mod))
+ if psize == 4
+ assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("j!"+mod))
+ assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("J!"+mod))
+ elsif psize == 8
+ assert_match(/\A\x08\x07\x06\x05\x04\x03\x02\x01\x00*\z/, [0x0102030405060708].pack("j!"+mod))
+ assert_match(/\A\x08\x07\x06\x05\x04\x03\x02\x01\x00*\z/, [0x0102030405060708].pack("J!"+mod))
+ end
+ %w[s S l L q Q j J s! S! i I i! I! l! L! j! J!].each {|fmt|
+ fmt += mod
+ nuls = [0].pack(fmt)
+ v = 0
+ s = "".force_encoding("ascii-8bit")
+ nuls.bytesize.times {|i|
+ j = i+40
+ v = v * 256 + j
+ s << [j].pack("C")
+ }
+ s.reverse!
+ assert_equal(s, [v].pack(fmt), "[#{v}].pack(#{fmt.dump})")
+ assert_equal([v], s.unpack(fmt), "#{s.dump}.unpack(#{fmt.dump})")
+ s2 = s+s
+ fmt2 = fmt+"*"
+ assert_equal([v,v], s2.unpack(fmt2), "#{s2.dump}.unpack(#{fmt2.dump})")
+ }
+ end
+
def test_integer_endian
s = [1].pack("s")
- assert_includes(["\0\1", "\1\0"], s)
+ assert_include(["\0\1", "\1\0"], s)
if s == "\0\1"
- # big endian
- assert_equal("\x01\x02", [0x0102].pack("s"))
- assert_equal("\x01\x02", [0x0102].pack("S"))
- assert_equal("\x01\x02\x03\x04", [0x01020304].pack("l"))
- assert_equal("\x01\x02\x03\x04", [0x01020304].pack("L"))
- assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("q"))
- assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("Q"))
- assert_match(/\A\x00*\x01\x02\z/, [0x0102].pack("s!"))
- assert_match(/\A\x00*\x01\x02\z/, [0x0102].pack("S!"))
- assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("i"))
- assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("I"))
- assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("i!"))
- assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("I!"))
- assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("l!"))
- assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("L!"))
- %w[s S l L q Q s! S! i I i! I! l! L!].each {|fmt|
- nuls = [0].pack(fmt)
- v = 0
- s = "".force_encoding("ascii-8bit")
- nuls.bytesize.times {|i|
- j = i + 40
- v = v * 256 + j
- s << [j].pack("C")
- }
- assert_equal(s, [v].pack(fmt), "[#{v}].pack(#{fmt.dump})")
- assert_equal([v], s.unpack(fmt), "#{s.dump}.unpack(#{fmt.dump})")
- s2 = s+s
- fmt2 = fmt+"*"
- assert_equal([v,v], s2.unpack(fmt2), "#{s2.dump}.unpack(#{fmt2.dump})")
- }
+ _integer_big_endian()
else
- # little endian
- assert_equal("\x02\x01", [0x0102].pack("s"))
- assert_equal("\x02\x01", [0x0102].pack("S"))
- assert_equal("\x04\x03\x02\x01", [0x01020304].pack("l"))
- assert_equal("\x04\x03\x02\x01", [0x01020304].pack("L"))
- assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("q"))
- assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("Q"))
- assert_match(/\A\x02\x01\x00*\z/, [0x0102].pack("s!"))
- assert_match(/\A\x02\x01\x00*\z/, [0x0102].pack("S!"))
- assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("i"))
- assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("I"))
- assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("i!"))
- assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("I!"))
- assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("l!"))
- assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("L!"))
- %w[s S l L q Q s! S! i I i! I! l! L!].each {|fmt|
- nuls = [0].pack(fmt)
- v = 0
- s = "".force_encoding("ascii-8bit")
- nuls.bytesize.times {|i|
- j = i+40
- v = v * 256 + j
- s << [j].pack("C")
- }
- s.reverse!
- assert_equal(s, [v].pack(fmt), "[#{v}].pack(#{fmt.dump})")
- assert_equal([v], s.unpack(fmt), "#{s.dump}.unpack(#{fmt.dump})")
- s2 = s+s
- fmt2 = fmt+"*"
- assert_equal([v,v], s2.unpack(fmt2), "#{s2.dump}.unpack(#{fmt2.dump})")
- }
+ _integer_little_endian()
end
+ assert_equal("\x01\x02\x02\x01", [0x0102,0x0102].pack("s>s<"))
+ assert_equal([0x0102,0x0102], "\x01\x02\x02\x01".unpack("s>s<"))
+ end
+
+ def test_integer_endian_explicit
+ _integer_big_endian('>')
+ _integer_little_endian('<')
end
def test_pack_U
@@ -165,6 +212,7 @@ class TestPack < Test::Unit::TestCase
assert_equal a[0], a.pack("p").unpack("p")[0]
assert_equal a, a.pack("p").freeze.unpack("p*")
assert_raise(ArgumentError) { (a.pack("p") + "").unpack("p*") }
+ assert_equal a, (a.pack("p") << "d").unpack("p*")
end
def test_format_string_modified
@@ -265,6 +313,9 @@ class TestPack < Test::Unit::TestCase
assert_equal(["1"], "\x80".unpack("B1"))
assert_equal(["10"], "\x80".unpack("B2"))
assert_equal(["100"], "\x80".unpack("B3"))
+
+ assert_equal(Encoding::US_ASCII, "\xff\x00".unpack("b*")[0].encoding)
+ assert_equal(Encoding::US_ASCII, "\xff\x00".unpack("B*")[0].encoding)
end
def test_pack_unpack_hH
@@ -305,6 +356,9 @@ class TestPack < Test::Unit::TestCase
assert_equal(["10e"], "\x10\xef".unpack("H3"))
assert_equal(["10ef"], "\x10\xef".unpack("H4"))
assert_equal(["10ef"], "\x10\xef".unpack("H5"))
+
+ assert_equal(Encoding::US_ASCII, "\x10\xef".unpack("h*")[0].encoding)
+ assert_equal(Encoding::US_ASCII, "\x10\xef".unpack("H*")[0].encoding)
end
def test_pack_unpack_cC
@@ -374,6 +428,7 @@ class TestPack < Test::Unit::TestCase
assert_operator(4, :<=, [1].pack("L!").bytesize)
end
+ require 'rbconfig'
def test_pack_unpack_qQ
s1 = [578437695752307201, -506097522914230529].pack("q*")
s2 = [578437695752307201, 17940646550795321087].pack("Q*")
@@ -381,8 +436,55 @@ class TestPack < Test::Unit::TestCase
assert_equal([578437695752307201, -506097522914230529], s2.unpack("q*"))
assert_equal([578437695752307201, 17940646550795321087], s1.unpack("Q*"))
+ # Note: q! and Q! should not work on platform which has no long long type.
+ # Is there a such platform now?
+ # @shyouhei: Yes. gcc -ansi is one of such platform.
+ s1 = [578437695752307201, -506097522914230529].pack("q!*")
+ s2 = [578437695752307201, 17940646550795321087].pack("Q!*")
+ assert_equal([578437695752307201, -506097522914230529], s2.unpack("q!*"))
+ assert_equal([578437695752307201, 17940646550795321087], s1.unpack("Q!*"))
+
assert_equal(8, [1].pack("q").bytesize)
assert_equal(8, [1].pack("Q").bytesize)
+ assert_operator(8, :<=, [1].pack("q!").bytesize)
+ assert_operator(8, :<=, [1].pack("Q!").bytesize)
+ end if RbConfig::CONFIG['HAVE_LONG_LONG']
+
+ def test_pack_unpack_jJ
+ # Note: we assume that the size of intptr_t and uintptr_t equals to the size
+ # of real pointer.
+ psize = [nil].pack("p").bytesize
+ if psize == 4
+ s1 = [67305985, -50462977].pack("j*")
+ s2 = [67305985, 4244504319].pack("J*")
+ assert_equal(s1, s2)
+ assert_equal([67305985, -50462977], s2.unpack("j*"))
+ assert_equal([67305985, 4244504319], s1.unpack("J*"))
+
+ s1 = [67305985, -50462977].pack("j!*")
+ s2 = [67305985, 4244504319].pack("J!*")
+ assert_equal([67305985, -50462977], s1.unpack("j!*"))
+ assert_equal([67305985, 4244504319], s2.unpack("J!*"))
+
+ assert_equal(4, [1].pack("j").bytesize)
+ assert_equal(4, [1].pack("J").bytesize)
+ elsif psize == 8
+ s1 = [578437695752307201, -506097522914230529].pack("j*")
+ s2 = [578437695752307201, 17940646550795321087].pack("J*")
+ assert_equal(s1, s2)
+ assert_equal([578437695752307201, -506097522914230529], s2.unpack("j*"))
+ assert_equal([578437695752307201, 17940646550795321087], s1.unpack("J*"))
+
+ s1 = [578437695752307201, -506097522914230529].pack("j!*")
+ s2 = [578437695752307201, 17940646550795321087].pack("J!*")
+ assert_equal([578437695752307201, -506097522914230529], s2.unpack("j!*"))
+ assert_equal([578437695752307201, 17940646550795321087], s1.unpack("J!*"))
+
+ assert_equal(8, [1].pack("j").bytesize)
+ assert_equal(8, [1].pack("J").bytesize)
+ else
+ assert false, "we don't know such platform now."
+ end
end
def test_pack_unpack_nN
@@ -414,7 +516,7 @@ class TestPack < Test::Unit::TestCase
%w(f d e E g G).each do |f|
v = [x].pack(f).unpack(f)
if x.nan?
- assert(v.first.nan?)
+ assert_predicate(v.first, :nan?)
else
assert_equal([x], v)
end
@@ -448,6 +550,9 @@ class TestPack < Test::Unit::TestCase
assert_equal([1, 2], "\x01\x00\x00\x02".unpack("C@3C"))
assert_equal([nil], "\x00".unpack("@1C")) # is it OK?
assert_raise(ArgumentError) { "\x00".unpack("@2C") }
+
+ pos = RbConfig::LIMITS["UINTPTR_MAX"] - 99 # -100
+ assert_raise(RangeError) {"0123456789".unpack("@#{pos}C10")}
end
def test_pack_unpack_percent
@@ -481,6 +586,10 @@ class TestPack < Test::Unit::TestCase
assert_equal("M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n!80``\n", ["a"*46].pack("u0"))
assert_equal("M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n!80``\n", ["a"*46].pack("u1"))
assert_equal("M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n!80``\n", ["a"*46].pack("u2"))
+ assert_equal(<<EXPECTED, ["a"*80].pack("u68"))
+_86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A
+186%A86%A86%A86%A86%A86$`
+EXPECTED
assert_equal([""], "".unpack("u"))
assert_equal(["a"], "!80``\n".unpack("u"))
@@ -490,6 +599,11 @@ class TestPack < Test::Unit::TestCase
assert_equal(["a"*46], "M86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A86%A\n!80``\n".unpack("u"))
assert_equal(["abcdefghi"], "&86)C9&5F\n#9VAI\n".unpack("u"))
+ assert_equal(["abcdef"], "#86)C\n#9&5F\n".unpack("u"))
+ assert_equal(["abcdef"], "#86)CX\n#9&5FX\n".unpack("u")) # X is a (dummy) checksum.
+ assert_equal(["abcdef"], "#86)C\r\n#9&5F\r\n".unpack("u"))
+ assert_equal(["abcdef"], "#86)CX\r\n#9&5FX\r\n".unpack("u")) # X is a (dummy) checksum.
+
assert_equal(["\x00"], "\"\n".unpack("u"))
assert_equal(["\x00"], "! \r \n".unpack("u"))
end
@@ -510,6 +624,18 @@ class TestPack < Test::Unit::TestCase
assert_equal(["\377"], "/w==\n".unpack("m"))
assert_equal(["\377\377"], "//8=\n".unpack("m"))
assert_equal(["\377\377\377"], "////\n".unpack("m"))
+ assert_equal([""], "A\n".unpack("m"))
+ assert_equal(["\0"], "AA\n".unpack("m"))
+ assert_equal(["\0"], "AA=\n".unpack("m"))
+ assert_equal(["\0\0"], "AAA\n".unpack("m"))
+
+ bug10019 = '[ruby-core:63604] [Bug #10019]'
+ size = ((4096-4)/4*3+1)
+ assert_separately(%W[- #{size} #{bug10019}], <<-'end;')
+ size = ARGV.shift.to_i
+ bug = ARGV.shift
+ assert_equal(size, ["a"*size].pack("m#{size+2}").unpack("m")[0].size, bug)
+ end;
end
def test_pack_unpack_m0
@@ -553,8 +679,23 @@ class TestPack < Test::Unit::TestCase
assert_equal(["a"*1023], (("a"*73+"=\n")*14+"a=\n").unpack("M"))
assert_equal(["\x0a"], "=0a=\n".unpack("M"))
assert_equal(["\x0a"], "=0A=\n".unpack("M"))
- assert_equal([""], "=0Z=\n".unpack("M"))
+ assert_equal(["=0Z=\n"], "=0Z=\n".unpack("M"))
assert_equal([""], "=\r\n".unpack("M"))
+ assert_equal(["\xC6\xF7"], "=C6=F7".unpack('M*'))
+
+ assert_equal(["pre123after"], "pre=31=32=33after".unpack("M"))
+ assert_equal(["preafter"], "pre=\nafter".unpack("M"))
+ assert_equal(["preafter"], "pre=\r\nafter".unpack("M"))
+ assert_equal(["pre="], "pre=".unpack("M"))
+ assert_equal(["pre=\r"], "pre=\r".unpack("M"))
+ assert_equal(["pre=hoge"], "pre=hoge".unpack("M"))
+ assert_equal(["pre==31after"], "pre==31after".unpack("M"))
+ assert_equal(["pre===31after"], "pre===31after".unpack("M"))
+
+ bug = '[ruby-core:83055] [Bug #13949]'
+ s = "abcdef".unpack1("M")
+ assert_equal(Encoding::ASCII_8BIT, s.encoding)
+ assert_predicate(s, :ascii_only?, bug)
end
def test_pack_unpack_P2
@@ -595,16 +736,6 @@ class TestPack < Test::Unit::TestCase
assert_equal([0x100000000], "\220\200\200\200\000".unpack("w"), [0x100000000])
end
- def test_modify_under_safe4
- s = "foo"
- assert_raise(SecurityError) do
- Thread.new do
- $SAFE = 4
- s.clear
- end.join
- end
- end
-
def test_length_too_big
assert_raise(RangeError) { [].pack("C100000000000000000000") }
end
@@ -615,4 +746,143 @@ class TestPack < Test::Unit::TestCase
assert_equal([1,nil], str.unpack("#{fmt}2"))
}
end
+
+ def test_short_with_block
+ bug4059 = '[ruby-core:33193]'
+ result = :ok
+ assert_nil("".unpack("i") {|x| result = x}, bug4059)
+ assert_equal(:ok, result)
+ end
+
+ def test_pack_garbage
+ verbose = $VERBOSE
+ $VERBOSE = false
+
+ assert_silent do
+ assert_equal "\000", [0].pack("*U")
+ end
+
+ $VERBOSE = true
+
+ _, err = capture_io do
+ assert_equal "\000", [0].pack("*U")
+ end
+
+ assert_match %r%unknown pack directive '\*' in '\*U'$%, err
+ ensure
+ $VERBOSE = verbose
+ end
+
+ def test_unpack_garbage
+ verbose = $VERBOSE
+ $VERBOSE = false
+
+ assert_silent do
+ assert_equal [0], "\000".unpack("*U")
+ end
+
+ $VERBOSE = true
+
+ _, err = capture_io do
+ assert_equal [0], "\000".unpack("*U")
+ end
+
+ assert_match %r%unknown unpack directive '\*' in '\*U'$%, err
+ ensure
+ $VERBOSE = verbose
+ end
+
+ def test_invalid_warning
+ assert_warning(/unknown pack directive ',' in ','/) {
+ [].pack(",")
+ }
+ assert_warning(/\A[ -~]+\Z/) {
+ [].pack("\x7f")
+ }
+ assert_warning(/\A(.* in '\u{3042}'\n)+\z/) {
+ [].pack("\u{3042}")
+ }
+
+ assert_warning(/\A.* in '.*U'\Z/) {
+ assert_equal "\000", [0].pack("\0U")
+ }
+ assert_warning(/\A.* in '.*U'\Z/) {
+ "\000".unpack("\0U")
+ }
+ end
+
+ def test_pack_resize
+ assert_separately([], <<-'end;')
+ ary = []
+ obj = Class.new {
+ define_method(:to_str) {
+ ary.clear()
+ ary = nil
+ GC.start
+ "TALOS"
+ }
+ }.new
+
+ ary.push(obj)
+ ary.push(".")
+
+ assert_raise_with_message(ArgumentError, /too few/) {ary.pack("AA")}
+ end;
+ end
+
+ def test_pack_with_buffer
+ buf = String.new(capacity: 100)
+
+ assert_raise_with_message(FrozenError, /frozen/) {
+ [0xDEAD_BEEF].pack('N', buffer: 'foo'.freeze)
+ }
+ assert_raise_with_message(TypeError, /must be String/) {
+ [0xDEAD_BEEF].pack('N', buffer: Object.new)
+ }
+
+ addr = [buf].pack('p')
+
+ [0xDEAD_BEEF].pack('N', buffer: buf)
+ assert_equal "\xDE\xAD\xBE\xEF", buf
+
+ [0xBABE_F00D].pack('@4N', buffer: buf)
+ assert_equal "\xDE\xAD\xBE\xEF\xBA\xBE\xF0\x0D", buf
+ assert_equal addr, [buf].pack('p')
+
+ [0xBAAD_FACE].pack('@10N', buffer: buf)
+ assert_equal "\xDE\xAD\xBE\xEF\xBA\xBE\xF0\x0D\0\0\xBA\xAD\xFA\xCE", buf
+
+ assert_equal addr, [buf].pack('p')
+ end
+
+ def test_unpack_with_block
+ ret = []; "ABCD".unpack("CCCC") {|v| ret << v }
+ assert_equal [65, 66, 67, 68], ret
+ ret = []; "A".unpack("B*") {|v| ret << v.dup }
+ assert_equal ["01000001"], ret
+ end
+
+ def test_unpack1
+ assert_equal 65, "A".unpack1("C")
+ assert_equal 68, "ABCD".unpack1("x3C")
+ assert_equal 0x3042, "\u{3042 3044 3046}".unpack1("U*")
+ assert_equal "hogefuga", "aG9nZWZ1Z2E=".unpack1("m")
+ assert_equal "01000001", "A".unpack1("B*")
+ end
+
+ def test_pack_infection
+ tainted_array_string = ["123456"]
+ tainted_array_string.first.taint
+ ['a', 'A', 'Z', 'B', 'b', 'H', 'h', 'u', 'M', 'm', 'P', 'p'].each do |f|
+ assert_predicate(tainted_array_string.pack(f), :tainted?)
+ end
+ end
+
+ def test_unpack_infection
+ tainted_string = "123456"
+ tainted_string.taint
+ ['a', 'A', 'Z', 'B', 'b', 'H', 'h', 'u', 'M', 'm'].each do |f|
+ assert_predicate(tainted_string.unpack(f).first, :tainted?)
+ end
+ end
end
diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb
index 14990be12c..24ca62a3f6 100644
--- a/test/ruby/test_parse.rb
+++ b/test/ruby/test_parse.rb
@@ -1,3 +1,5 @@
+# coding: US-ASCII
+# frozen_string_literal: false
require 'test/unit'
require 'stringio'
@@ -12,18 +14,17 @@ class TestParse < Test::Unit::TestCase
end
def test_else_without_rescue
- x = eval <<-END
+ assert_syntax_error(<<-END, %r":#{__LINE__+2}: else without rescue"o, [__FILE__, __LINE__+1])
begin
else
42
end
END
- assert_equal(42, x)
end
def test_alias_backref
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
alias $foo $1
END
end
@@ -36,7 +37,7 @@ class TestParse < Test::Unit::TestCase
a = false
b = c = d = true
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
a &&= t.foo 42
b &&= t.foo 42
c &&= t.foo nil
@@ -51,7 +52,7 @@ class TestParse < Test::Unit::TestCase
a = [nil, nil, true, true]
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
a[0] ||= t.foo 42
a[1] &&= t.foo 42
a[2] ||= t.foo 42
@@ -67,7 +68,7 @@ class TestParse < Test::Unit::TestCase
o.foo = o.Foo = o::baz = nil
o.bar = o.Bar = o::qux = 1
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
o.foo ||= t.foo 42
o.bar &&= t.foo 42
o.Foo ||= t.foo 42
@@ -81,7 +82,7 @@ class TestParse < Test::Unit::TestCase
assert_equal([42, 42], [o::baz, o::qux])
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
$1 ||= t.foo 42
END
end
@@ -90,7 +91,7 @@ class TestParse < Test::Unit::TestCase
a = b = nil
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
a = t.bar "foo" do
"bar"
end.gsub "ob", "OB"
@@ -104,7 +105,7 @@ class TestParse < Test::Unit::TestCase
a = nil
assert_nothing_raised do
- t.instance_eval <<-END
+ t.instance_eval <<-END, __FILE__, __LINE__+1
a = bar "foo" do "bar" end
END
end
@@ -112,7 +113,7 @@ class TestParse < Test::Unit::TestCase
a = nil
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
a = t::bar "foo" do "bar" end
END
end
@@ -136,7 +137,7 @@ class TestParse < Test::Unit::TestCase
end
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
c::foo, c::bar = 1, 2
c.Foo, c.Bar = 1, 2
c::FOO, c::BAR = 1, 2
@@ -149,7 +150,7 @@ class TestParse < Test::Unit::TestCase
def test_dynamic_constant_assignment
assert_raise(SyntaxError) do
- Object.new.instance_eval <<-END
+ Object.new.instance_eval <<-END, __FILE__, __LINE__+1
def foo
self::FOO, self::BAR = 1, 2
::FOO, ::BAR = 1, 2
@@ -158,13 +159,13 @@ class TestParse < Test::Unit::TestCase
end
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
$1, $2 = 1, 2
END
end
assert_raise(SyntaxError) do
- Object.new.instance_eval <<-END
+ Object.new.instance_eval <<-END, __FILE__, __LINE__+1
def foo
::FOO = 1
end
@@ -172,16 +173,18 @@ class TestParse < Test::Unit::TestCase
end
c = Class.new
- assert_raise(SyntaxError) do
- eval <<-END
+ c.freeze
+ assert_nothing_raised(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ if false
c::FOO &= 1
::FOO &= 1
+ end
END
end
- c = Class.new
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
$1 &= 1
END
end
@@ -189,13 +192,13 @@ class TestParse < Test::Unit::TestCase
def test_class_module
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
class foo; end
END
end
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
def foo
class Foo; end
module Bar; end
@@ -203,9 +206,9 @@ class TestParse < Test::Unit::TestCase
END
end
- assert_raise(SyntaxError) do
- eval <<-END
- class Foo Bar; end
+ assert_nothing_raised(SyntaxError) do
+ eval <<-END, nil, __FILE__, __LINE__+1
+ class Foo 1; end
END
end
end
@@ -215,9 +218,8 @@ class TestParse < Test::Unit::TestCase
def o.>(x); x; end
def o./(x); x; end
- a = nil
assert_nothing_raised do
- o.instance_eval <<-END
+ o.instance_eval <<-END, __FILE__, __LINE__+1
undef >, /
END
end
@@ -231,7 +233,7 @@ class TestParse < Test::Unit::TestCase
o.foo = o.Foo = o::baz = nil
o.bar = o.Bar = o::qux = 1
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
o.foo ||= 42
o.bar &&= 42
o.Foo ||= 42
@@ -246,7 +248,7 @@ class TestParse < Test::Unit::TestCase
a = nil
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
a = -2.0 ** 2
END
end
@@ -259,7 +261,7 @@ class TestParse < Test::Unit::TestCase
a = nil
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
o.foo 1 do|; a| a = 42 end
END
end
@@ -268,25 +270,25 @@ class TestParse < Test::Unit::TestCase
def test_bad_arg
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
def foo(FOO); end
END
end
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
def foo(@foo); end
END
end
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
def foo($foo); end
END
end
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
def foo(@@foo); end
END
end
@@ -295,7 +297,7 @@ class TestParse < Test::Unit::TestCase
def o.foo(*r); yield(*r); end
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
o.foo 1 {|; @a| @a = 42 }
END
end
@@ -304,7 +306,7 @@ class TestParse < Test::Unit::TestCase
def test_do_lambda
a = b = nil
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
a = -> do
b = 42
end
@@ -320,7 +322,7 @@ class TestParse < Test::Unit::TestCase
a = b = nil
assert_nothing_raised do
- o.instance_eval <<-END
+ o.instance_eval <<-END, __FILE__, __LINE__+1
a = foo 1 do 42 end.to_s
b = foo 1 do 42 end::to_s
END
@@ -332,7 +334,7 @@ class TestParse < Test::Unit::TestCase
def test_call_method
a = b = nil
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
a = proc {|x| x + "bar" }.("foo")
b = proc {|x| x + "bar" }::("foo")
END
@@ -358,15 +360,45 @@ class TestParse < Test::Unit::TestCase
assert_equal("foo 1 bar", "foo #$1 bar")
end
+ def test_dstr_disallowed_variable
+ bug8375 = '[ruby-core:54885] [Bug #8375]'
+ %w[@ @1 @. @@ @@1 @@. $ $%].each do |src|
+ src = '#'+src+' '
+ str = assert_nothing_raised(SyntaxError, "#{bug8375} #{src.dump}") do
+ break eval('"'+src+'"')
+ end
+ assert_equal(src, str, bug8375)
+ end
+ end
+
def test_dsym
assert_nothing_raised { eval(':""') }
end
+ def assert_disallowed_variable(type, noname, *invalid)
+ assert_syntax_error(noname, "`#{noname}' without identifiers is not allowed as #{type} variable name")
+ invalid.each do |name|
+ assert_syntax_error(name, "`#{name}' is not allowed as #{type} variable name")
+ end
+ end
+
+ def test_disallowed_instance_variable
+ assert_disallowed_variable("an instance", *%w[@ @1 @.])
+ end
+
+ def test_disallowed_class_variable
+ assert_disallowed_variable("a class", *%w[@@ @@1 @@.])
+ end
+
+ def test_disallowed_gloal_variable
+ assert_disallowed_variable("a global", *%w[$ $%])
+ end
+
def test_arg2
o = Object.new
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
def o.foo(a=42,*r,z,&b); b.call(r.inject(a*1000+z*100, :+)); end
END
end
@@ -377,7 +409,7 @@ class TestParse < Test::Unit::TestCase
assert_raise(ArgumentError) { o.foo() }
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
def o.foo(a=42,z,&b); b.call(a*1000+z*100); end
END
end
@@ -386,7 +418,7 @@ class TestParse < Test::Unit::TestCase
assert_raise(ArgumentError) { o.foo() }
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
def o.foo(*r,z,&b); b.call(r.inject(z*100, :+)); end
END
end
@@ -398,19 +430,19 @@ class TestParse < Test::Unit::TestCase
def test_duplicate_argument
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
1.times {|&b?| }
END
end
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
1.times {|a, a|}
END
end
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
def foo(a, a); end
END
end
@@ -418,27 +450,51 @@ class TestParse < Test::Unit::TestCase
def test_define_singleton_error
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
def ("foo").foo; end
END
end
end
+ def test_op_asgn1_with_block
+ t = Object.new
+ a = []
+ blk = proc {|x| a << x }
+ def t.[](_)
+ yield(:aref)
+ nil
+ end
+ def t.[]=(_, _)
+ yield(:aset)
+ end
+ def t.dummy(_)
+ end
+ eval <<-END, nil, __FILE__, __LINE__+1
+ t[42, &blk] ||= 42
+ END
+ assert_equal([:aref, :aset], a)
+ a.clear
+ eval <<-END, nil, __FILE__, __LINE__+1
+ t[42, &blk] ||= t.dummy 42 # command_asgn test
+ END
+ assert_equal([:aref, :aset], a)
+ end
+
def test_backquote
t = Object.new
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
def t.`(x); "foo" + x + "bar"; end
END
end
a = b = nil
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
a = t.` "zzz"
1.times {|;z| t.` ("zzz") }
END
- t.instance_eval <<-END
+ t.instance_eval <<-END, __FILE__, __LINE__+1
b = `zzz`
END
end
@@ -451,21 +507,41 @@ class TestParse < Test::Unit::TestCase
end
def test_string
- assert_raise(SyntaxError) do
- eval '"\xg1"'
- end
+ mesg = 'from the backslash through the invalid char'
- assert_raise(SyntaxError) do
- eval '"\u{1234"'
- end
+ e = assert_syntax_error('"\xg1"', /hex escape/)
+ assert_equal(' ^', e.message.lines.last, mesg)
- assert_raise(SyntaxError) do
- eval '"\M1"'
- end
+ e = assert_syntax_error('"\u{1234"', 'unterminated Unicode escape')
+ assert_equal(' ^', e.message.lines.last, mesg)
- assert_raise(SyntaxError) do
- eval '"\C1"'
- end
+ e = assert_syntax_error('"\u{xxxx}"', 'invalid Unicode escape')
+ assert_equal(' ^', e.message.lines.last, mesg)
+
+ e = assert_syntax_error('"\u{xxxx', 'Unicode escape')
+ assert_pattern_list([
+ /.*: invalid Unicode escape\n.*\n/,
+ / \^/,
+ /\n/,
+ /.*: unterminated Unicode escape\n.*\n/,
+ / \^/,
+ /\n/,
+ /.*: unterminated string.*\n.*\n/,
+ / \^/,
+ ], e.message)
+
+ e = assert_syntax_error('"\M1"', /escape character syntax/)
+ assert_equal(' ^~~', e.message.lines.last, mesg)
+
+ e = assert_syntax_error('"\C1"', /escape character syntax/)
+ assert_equal(' ^~~', e.message.lines.last, mesg)
+
+ src = '"\xD0\u{90'"\n""000000000000000000000000"
+ assert_syntax_error(src, /:#{__LINE__}: unterminated/o)
+
+ assert_syntax_error('"\u{100000000}"', /invalid Unicode escape/)
+ assert_equal("", eval('"\u{}"'))
+ assert_equal("", eval('"\u{ }"'))
assert_equal("\x81", eval('"\C-\M-a"'))
assert_equal("\177", eval('"\c?"'))
@@ -479,6 +555,8 @@ class TestParse < Test::Unit::TestCase
assert_raise(SyntaxError) { eval("?\v") }
assert_raise(SyntaxError) { eval("?\r") }
assert_raise(SyntaxError) { eval("?\f") }
+ assert_raise(SyntaxError) { eval("?\f") }
+ assert_raise(SyntaxError) { eval(" ?a\x8a".force_encoding("utf-8")) }
assert_equal("\u{1234}", eval("?\u{1234}"))
assert_equal("\u{1234}", eval('?\u{1234}'))
end
@@ -502,14 +580,15 @@ class TestParse < Test::Unit::TestCase
assert_nothing_raised(SyntaxError, bug) do
assert_equal(sym, eval(':"foo\u{0}bar"'))
end
- assert_raise(SyntaxError) do
- eval ':"foo\u{}bar"'
+ assert_nothing_raised(SyntaxError) do
+ assert_equal(:foobar, eval(':"foo\u{}bar"'))
+ assert_equal(:foobar, eval(':"foo\u{ }bar"'))
end
end
def test_parse_string
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
/
END
end
@@ -524,13 +603,14 @@ class TestParse < Test::Unit::TestCase
)
end
- assert_raise(SyntaxError) do
- eval %q(
+ assert_nothing_raised(SyntaxError) do
+ x = eval %q(
<<FOO
#$
FOO
)
end
+ assert_equal "\#$\n", x
assert_raise(SyntaxError) do
eval %Q(
@@ -550,14 +630,15 @@ FOO
)
end
- assert_raise(SyntaxError) do
- eval %q(
+ assert_nothing_raised(SyntaxError) do
+ x = eval %q(
<<FOO
#$
foo
FOO
)
end
+ assert_equal "\#$\nfoo\n", x
assert_nothing_raised do
eval "x = <<""FOO\r\n1\r\nFOO"
@@ -568,7 +649,7 @@ FOO
def test_magic_comment
x = nil
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
# coding = utf-8
x = __ENCODING__
END
@@ -576,7 +657,7 @@ x = __ENCODING__
assert_equal(Encoding.find("UTF-8"), x)
assert_raise(ArgumentError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
# coding = foobarbazquxquux_dummy_enconding
x = __ENCODING__
END
@@ -595,7 +676,7 @@ x = __ENCODING__
def test_dot_in_next_line
x = nil
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
x = 1
.to_s
END
@@ -611,7 +692,7 @@ x = __ENCODING__
def test_embedded_rd
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
=begin
END
end
@@ -635,21 +716,28 @@ x = __ENCODING__
def test_invalid_instance_variable
assert_raise(SyntaxError) { eval('@#') }
+ assert_raise(SyntaxError) { eval('@') }
end
def test_invalid_class_variable
assert_raise(SyntaxError) { eval('@@1') }
+ assert_raise(SyntaxError) { eval('@@') }
end
def test_invalid_char
+ bug10117 = '[ruby-core:64243] [Bug #10117]'
+ invalid_char = /Invalid char `\\x01'/
x = 1
- assert_equal(1, eval("\x01x"))
+ assert_in_out_err(%W"-e \x01x", "", [], invalid_char, bug10117)
+ assert_syntax_error("\x01x", invalid_char, bug10117)
assert_equal(nil, eval("\x04x"))
+ assert_equal 1, x
end
def test_literal_concat
x = "baz"
assert_equal("foobarbaz", eval('"foo" "bar#{x}"'))
+ assert_equal("baz", x)
end
def test_unassignable
@@ -675,17 +763,23 @@ x = __ENCODING__
eval %q(__ENCODING__ = 1)
end
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
def foo
FOO = 1
end
END
end
+ assert_raise(SyntaxError) do
+ eval "#{<<~"begin;"}\n#{<<~'end;'}", nil, __FILE__, __LINE__+1
+ begin;
+ x, true
+ end;
+ end
end
def test_block_dup
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
foo(&proc{}) {}
END
end
@@ -693,7 +787,7 @@ x = __ENCODING__
def test_set_backref
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
$& = 1
END
end
@@ -706,7 +800,7 @@ x = __ENCODING__
end
r = nil
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
o[&proc{|x| r = x }] = 1
END
end
@@ -722,6 +816,7 @@ x = __ENCODING__
x = 1
assert_nil eval("x; nil")
assert_nil eval("1+1; nil")
+ assert_nil eval("1.+(1); nil")
assert_nil eval("TestParse; nil")
assert_nil eval("::TestParse; nil")
assert_nil eval("x..x; nil")
@@ -731,30 +826,25 @@ x = __ENCODING__
assert_nil eval("true; nil")
assert_nil eval("false; nil")
assert_nil eval("defined?(1); nil")
+ assert_equal 1, x
assert_raise(SyntaxError) do
eval %q(1; next; 2)
end
- o = Object.new
- assert_nothing_raised do
- eval <<-END
- x = def o.foo; end
- END
- end
- assert_equal($stderr.string.lines.to_a.size, 14)
+ assert_equal(13, $stderr.string.lines.to_a.size)
$stderr = stderr
end
def test_assign_in_conditional
- assert_raise(SyntaxError) do
- eval <<-END
+ assert_nothing_raised do
+ eval <<-END, nil, __FILE__, __LINE__+1
(x, y = 1, 2) ? 1 : 2
END
end
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
if @x = true
1
else
@@ -766,63 +856,357 @@ x = __ENCODING__
def test_literal_in_conditional
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
"foo" ? 1 : 2
END
end
assert_nothing_raised do
x = "bar"
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
/foo#{x}baz/ ? 1 : 2
END
end
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
(true..false) ? 1 : 2
END
end
assert_nothing_raised do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
("foo".."bar") ? 1 : 2
END
end
assert_nothing_raised do
x = "bar"
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
:"foo#{"x"}baz" ? 1 : 2
END
+ assert_equal "bar", x
end
end
def test_no_blockarg
assert_raise(SyntaxError) do
- eval <<-END
+ eval <<-END, nil, __FILE__, __LINE__+1
yield(&:+)
END
end
end
- def test_intern
- assert_equal(':""', ''.intern.inspect)
- assert_equal(':$foo', '$foo'.intern.inspect)
- assert_equal(':"!foo"', '!foo'.intern.inspect)
- assert_equal(':"foo=="', "foo==".intern.inspect)
+ def test_method_block_location
+ bug5614 = '[ruby-core:40936]'
+ expected = nil
+ e = assert_raise(NoMethodError) do
+ 1.times do
+ expected = __LINE__+1
+ end.print do
+ #
+ end
+ end
+ actual = e.backtrace.first[/\A#{Regexp.quote(__FILE__)}:(\d+):/o, 1].to_i
+ assert_equal(expected, actual, bug5614)
+ end
+
+ def test_no_shadowing_variable_warning
+ assert_no_warning(/shadowing outer local variable/) {eval("a=1; tap {|a|}")}
end
- def test_all_symbols
- x = Symbol.all_symbols
- assert_kind_of(Array, x)
- assert(x.all? {|s| s.is_a?(Symbol) })
+ def test_unused_variable
+ o = Object.new
+ assert_warning(/assigned but unused variable/) {o.instance_eval("def foo; a=1; nil; end")}
+ assert_warning(/assigned but unused variable/) {o.instance_eval("def bar; a=1; a(); end")}
+ a = "\u{3042}"
+ assert_warning(/#{a}/) {o.instance_eval("def foo; #{a}=1; nil; end")}
+ o = Object.new
+ assert_warning(/assigned but unused variable/) {o.instance_eval("def foo; tap {a=1; a()}; end")}
+ assert_warning('') {o.instance_eval("def bar; a=a=1; nil; end")}
end
- def test_is_class_id
- c = Class.new
- assert_raise(NameError) do
- c.instance_eval { remove_class_variable(:@var) }
+ def test_named_capture_conflict
+ a = 1
+ assert_warning('') {eval("a = 1; /(?<a>)/ =~ ''")}
+ a = "\u{3042}"
+ assert_warning('') {eval("#{a} = 1; /(?<#{a}>)/ =~ ''")}
+ end
+
+ def test_rescue_in_command_assignment
+ bug = '[ruby-core:75621] [Bug #12402]'
+ all_assertions(bug) do |a|
+ a.for("lhs = arg") do
+ v = bug
+ v = raise(bug) rescue "ok"
+ assert_equal("ok", v)
+ end
+ a.for("lhs op_asgn arg") do
+ v = 0
+ v += raise(bug) rescue 1
+ assert_equal(1, v)
+ end
+ a.for("lhs[] op_asgn arg") do
+ v = [0]
+ v[0] += raise(bug) rescue 1
+ assert_equal([1], v)
+ end
+ a.for("lhs.m op_asgn arg") do
+ k = Struct.new(:m)
+ v = k.new(0)
+ v.m += raise(bug) rescue 1
+ assert_equal(k.new(1), v)
+ end
+ a.for("lhs::m op_asgn arg") do
+ k = Struct.new(:m)
+ v = k.new(0)
+ v::m += raise(bug) rescue 1
+ assert_equal(k.new(1), v)
+ end
+ a.for("lhs.C op_asgn arg") do
+ k = Struct.new(:C)
+ v = k.new(0)
+ v.C += raise(bug) rescue 1
+ assert_equal(k.new(1), v)
+ end
+ a.for("lhs::C op_asgn arg") do
+ v = Class.new
+ v::C ||= raise(bug) rescue 1
+ assert_equal(1, v::C)
+ end
+ a.for("lhs = command") do
+ v = bug
+ v = raise bug rescue "ok"
+ assert_equal("ok", v)
+ end
+ a.for("lhs op_asgn command") do
+ v = 0
+ v += raise bug rescue 1
+ assert_equal(1, v)
+ end
+ a.for("lhs[] op_asgn command") do
+ v = [0]
+ v[0] += raise bug rescue 1
+ assert_equal([1], v)
+ end
+ a.for("lhs.m op_asgn command") do
+ k = Struct.new(:m)
+ v = k.new(0)
+ v.m += raise bug rescue 1
+ assert_equal(k.new(1), v)
+ end
+ a.for("lhs::m op_asgn command") do
+ k = Struct.new(:m)
+ v = k.new(0)
+ v::m += raise bug rescue 1
+ assert_equal(k.new(1), v)
+ end
+ a.for("lhs.C op_asgn command") do
+ k = Struct.new(:C)
+ v = k.new(0)
+ v.C += raise bug rescue 1
+ assert_equal(k.new(1), v)
+ end
+ a.for("lhs::C op_asgn command") do
+ v = Class.new
+ v::C ||= raise bug rescue 1
+ assert_equal(1, v::C)
+ end
+ end
+ end
+
+ def test_yyerror_at_eol
+ assert_syntax_error(" 0b", /\^/)
+ assert_syntax_error(" 0b\n", /\^/)
+ end
+
+ def test_error_def_in_argument
+ assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ assert_syntax_error("def f r:def d; def f 0end", /unexpected/)
+ end;
+
+ assert_syntax_error("def\nf(000)end", /^ \^~~/)
+ assert_syntax_error("def\nf(&)end", /^ \^/)
+ end
+
+ def test_method_location_in_rescue
+ bug = '[ruby-core:79388] [Bug #13181]'
+ obj, line = Object.new, __LINE__+1
+ def obj.location
+ #
+ raise
+ rescue
+ caller_locations(1, 1)[0]
+ end
+
+ assert_equal(line, obj.location.lineno, bug)
+ end
+
+ def test_negative_line_number
+ bug = '[ruby-core:80920] [Bug #13523]'
+ obj = Object.new
+ obj.instance_eval("def t(e = false);raise if e; __LINE__;end", "test", -100)
+ assert_equal(-100, obj.t, bug)
+ assert_equal(-100, obj.method(:t).source_location[1], bug)
+ e = assert_raise(RuntimeError) {obj.t(true)}
+ assert_equal(-100, e.backtrace_locations.first.lineno, bug)
+ end
+
+ def test_file_in_indented_heredoc
+ name = '[ruby-core:80987] [Bug #13540]' # long enough to be shared
+ assert_equal(name+"\n", eval("#{<<-"begin;"}\n#{<<-'end;'}", nil, name))
+ begin;
+ <<~HEREDOC
+ #{__FILE__}
+ HEREDOC
+ end;
+ end
+
+ def test_unexpected_token_error
+ assert_raise(SyntaxError) do
+ eval('"x"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
+ end
+ end
+
+ def test_unexpected_token_after_numeric
+ assert_raise_with_message(SyntaxError, /^ \^~~\z/) do
+ eval('0000xyz')
+ end
+ assert_raise_with_message(SyntaxError, /^ \^~~\z/) do
+ eval('1.2i1.1')
+ end
+ end
+
+ def test_truncated_source_line
+ e = assert_raise_with_message(SyntaxError, /unexpected tIDENTIFIER/) do
+ eval("'0123456789012345678901234567890123456789' abcdefghijklmnopqrstuvwxyz0123456789 0123456789012345678901234567890123456789")
+ end
+ line = e.message.lines[1]
+ assert_operator(line, :start_with?, "...")
+ assert_operator(line, :end_with?, "...\n")
+ end
+
+ def test_unterminated_regexp_error
+ e = assert_raise(SyntaxError) do
+ eval("/x")
+ end.message
+ assert_match(/unterminated regexp meets end of file/, e)
+ assert_not_match(/unexpected tSTRING_END/, e)
+ end
+
+ def test_lparenarg
+ o = Struct.new(:x).new
+ def o.i(x)
+ self.x = x
+ end
+ o.instance_eval {i (-1.3).abs}
+ assert_equal(1.3, o.x)
+ o.instance_eval {i = 0; i (-1.3).abs}
+ assert_equal(1.3, o.x)
+ end
+
+ def test_serial_comparison
+ assert_warning(/comparison '<' after/) do
+ $VERBOSE = true
+ x = 1
+ eval("if false; 0 < x < 2; end")
end
end
+
+ def test_eof_in_def
+ assert_raise(SyntaxError) { eval("def m\n\0""end") }
+ assert_raise(SyntaxError) { eval("def m\n\C-d""end") }
+ assert_raise(SyntaxError) { eval("def m\n\C-z""end") }
+ end
+
+ def test_location_of_invalid_token
+ assert_raise_with_message(SyntaxError, /^ \^~~\z/) do
+ eval('class xxx end')
+ end
+ end
+
+ def test_whitespace_warning
+ assert_raise_with_message(SyntaxError, /backslash/) do
+ eval("\\foo")
+ end
+ assert_raise_with_message(SyntaxError, /escaped space/) do
+ eval("\\ ")
+ end
+ assert_raise_with_message(SyntaxError, /escaped horizontal tab/) do
+ eval("\\\t")
+ end
+ assert_raise_with_message(SyntaxError, /escaped form feed/) do
+ eval("\\\f")
+ end
+ assert_raise_with_message(SyntaxError, /escaped carriage return/) do
+ assert_warn(/middle of line/) {eval("\\\r")}
+ end
+ assert_raise_with_message(SyntaxError, /escaped vertical tab/) do
+ eval("\\\v")
+ end
+ end
+
+ def test_command_def_cmdarg
+ assert_valid_syntax("\n#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ m def x(); end
+ 1.tap do end
+ end;
+ end
+
+ NONASCII_CONSTANTS = [
+ *%W"\u{00de} \u{00C0}".flat_map {|c| [c, c.encode("iso-8859-15")]},
+ "\u{1c4}", "\u{1f2}", "\u{1f88}", "\u{370}",
+ *%W"\u{391} \u{ff21}".flat_map {|c| [c, c.encode("cp932"), c.encode("euc-jp")]},
+ ]
+
+ def assert_nonascii_const
+ assert_all_assertions_foreach("NONASCII_CONSTANTS", *NONASCII_CONSTANTS) do |n|
+ m = Module.new
+ assert_not_operator(m, :const_defined?, n)
+ assert_raise_with_message(NameError, /uninitialized/) do
+ m.const_get(n)
+ end
+ assert_nil(eval("defined?(m::#{n})"))
+
+ v = yield m, n
+
+ assert_operator(m, :const_defined?, n)
+ assert_equal("constant", eval("defined?(m::#{n})"))
+ assert_same(v, m.const_get(n))
+
+ m.__send__(:remove_const, n)
+ assert_not_operator(m, :const_defined?, n)
+ assert_nil(eval("defined?(m::#{n})"))
+ end
+ end
+
+ def test_nonascii_const_set
+ assert_nonascii_const do |m, n|
+ m.const_set(n, 42)
+ end
+ end
+
+ def test_nonascii_constant
+ assert_nonascii_const do |m, n|
+ m.module_eval("class #{n}; self; end")
+ end
+ end
+
+ def test_cdmarg_after_command_args_and_tlbrace_arg
+ assert_valid_syntax('let () { m(a) do; end }')
+ end
+
+ def test_void_value_in_command_rhs
+ w = "void value expression"
+ ex = assert_syntax_error("x = return 1", w)
+ assert_equal(1, ex.message.scan(w).size, "same #{w.inspect} warning should be just once")
+ end
+
+=begin
+ def test_past_scope_variable
+ assert_warning(/past scope/) {catch {|tag| eval("BEGIN{throw tag}; tap {a = 1}; a")}}
+ end
+=end
end
diff --git a/test/ruby/test_path.rb b/test/ruby/test_path.rb
index 31c1885371..6af4fb6ac0 100644
--- a/test/ruby/test_path.rb
+++ b/test/ruby/test_path.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestPath < Test::Unit::TestCase
@@ -230,6 +231,9 @@ class TestPath < Test::Unit::TestCase
assert_equal('', File.extname('a'))
ext = '.rb'
assert_equal(ext, File.extname('a.rb'))
+ assert_equal(ext, File.extname('.a.rb'))
+ assert_equal(ext, File.extname('a/b/d/test.rb'))
+ assert_equal(ext, File.extname('.a/b/d/test.rb'))
unless /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
# trailing spaces and dots are ignored on NTFS.
ext = ''
@@ -239,4 +243,21 @@ class TestPath < Test::Unit::TestCase
assert_equal('', File.extname('.x'))
assert_equal('', File.extname('..x'))
end
+
+ def test_ascii_incompatible_path
+ s = "\u{221e}\u{2603}"
+ assert_raise(Encoding::CompatibilityError) {open(s.encode("utf-16be"))}
+ assert_raise(Encoding::CompatibilityError) {open(s.encode("utf-16le"))}
+ assert_raise(Encoding::CompatibilityError) {open(s.encode("utf-32be"))}
+ assert_raise(Encoding::CompatibilityError) {open(s.encode("utf-32le"))}
+ end
+
+ def test_join
+ bug5483 = '[ruby-core:40338]'
+ path = %w[a b]
+ Encoding.list.each do |e|
+ next unless e.ascii_compatible?
+ assert_equal(e, File.join(*path.map {|s| s.force_encoding(e)}).encoding, bug5483)
+ end
+ end
end
diff --git a/test/ruby/test_pipe.rb b/test/ruby/test_pipe.rb
index 34f231ad8c..9fa42fd375 100644
--- a/test/ruby/test_pipe.rb
+++ b/test/ruby/test_pipe.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
require_relative 'ut_eof'
@@ -13,4 +14,36 @@ class TestPipe < Test::Unit::TestCase
r.close
end
end
+ class WithConversion < self
+ def open_file(content)
+ r, w = IO.pipe
+ w << content
+ w.close
+ r.set_encoding("us-ascii:utf-8")
+ begin
+ yield r
+ ensure
+ r.close
+ end
+ end
+ end
+
+ def test_stdout_epipe
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ io = STDOUT
+ begin
+ save = io.dup
+ IO.popen("echo", "w", out: IO::NULL) do |f|
+ io.reopen(f)
+ Process.wait(f.pid)
+ assert_raise(Errno::EPIPE) do
+ io.print "foo\n"
+ end
+ end
+ ensure
+ io.reopen(save)
+ end
+ end;
+ end
end
diff --git a/test/ruby/test_primitive.rb b/test/ruby/test_primitive.rb
index d701348f26..19af44ad32 100644
--- a/test/ruby/test_primitive.rb
+++ b/test/ruby/test_primitive.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestRubyPrimitive < Test::Unit::TestCase
@@ -81,7 +82,7 @@ class TestRubyPrimitive < Test::Unit::TestCase
end
i = 0
while i < 3
- r = A3::B3::C # cache
+ r = r = A3::B3::C # cache
class A3::B3
remove_const :C
end
@@ -400,4 +401,24 @@ class TestRubyPrimitive < Test::Unit::TestCase
#assert_equal [0,1,2,3,4], [0, *a, 4]
end
+ def test_concatarray_ruby_dev_41933
+ bug3658 = '[ruby-dev:41933]'
+ [0, *x=1]
+ assert_equal(1, x, bug3658)
+ [0, *x=1, 2]
+ assert_equal(1, x, bug3658)
+ class << (x = Object.new)
+ attr_accessor :to_a_called
+ def to_a
+ @to_a_called = true
+ [self]
+ end
+ end
+ x.to_a_called = false
+ [0, *x]
+ assert_predicate(x, :to_a_called, bug3658)
+ x.to_a_called = false
+ [0, *x, 2]
+ assert_predicate(x, :to_a_called, bug3658)
+ end
end
diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb
index 5a108d3a0f..9ae1de62ff 100644
--- a/test/ruby/test_proc.rb
+++ b/test/ruby/test_proc.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestProc < Test::Unit::TestCase
@@ -35,9 +36,9 @@ class TestProc < Test::Unit::TestCase
}.call
assert(!defined?(iii)) # out of scope
- loop{iii=5; assert(eval("defined? iii")); break}
+ loop{iii=iii=5; assert(eval("defined? iii")); break}
loop {
- iii = 10
+ iii=iii = 10
def self.dyna_var_check
loop {
assert(!defined?(iii))
@@ -62,12 +63,57 @@ class TestProc < Test::Unit::TestCase
assert_equal(0, proc{}.arity)
assert_equal(0, proc{||}.arity)
assert_equal(1, proc{|x|}.arity)
+ assert_equal(0, proc{|x=1|}.arity)
assert_equal(2, proc{|x, y|}.arity)
+ assert_equal(1, proc{|x=0, y|}.arity)
+ assert_equal(0, proc{|x=0, y=0|}.arity)
+ assert_equal(1, proc{|x, y=0|}.arity)
assert_equal(-2, proc{|x, *y|}.arity)
+ assert_equal(-1, proc{|x=0, *y|}.arity)
assert_equal(-1, proc{|*x|}.arity)
assert_equal(-1, proc{|*|}.arity)
assert_equal(-3, proc{|x, *y, z|}.arity)
+ assert_equal(-2, proc{|x=0, *y, z|}.arity)
+ assert_equal(2, proc{|(x, y), z|[x,y]}.arity)
+ assert_equal(1, proc{|(x, y), z=0|[x,y]}.arity)
assert_equal(-4, proc{|x, *y, z, a|}.arity)
+ assert_equal(0, proc{|**|}.arity)
+ assert_equal(0, proc{|**o|}.arity)
+ assert_equal(1, proc{|x, **o|}.arity)
+ assert_equal(0, proc{|x=0, **o|}.arity)
+ assert_equal(1, proc{|x, y=0, **o|}.arity)
+ assert_equal(2, proc{|x, y=0, z, **o|}.arity)
+ assert_equal(-3, proc{|x, y=0, *z, w, **o|}.arity)
+
+ assert_equal(2, proc{|x, y=0, z, a:1|}.arity)
+ assert_equal(3, proc{|x, y=0, z, a:|}.arity)
+ assert_equal(-4, proc{|x, y, *rest, a:, b:, c:|}.arity)
+ assert_equal(3, proc{|x, y=0, z, a:, **o|}.arity)
+
+ assert_equal(0, lambda{}.arity)
+ assert_equal(0, lambda{||}.arity)
+ assert_equal(1, lambda{|x|}.arity)
+ assert_equal(-1, lambda{|x=1|}.arity) # different from proc
+ assert_equal(2, lambda{|x, y|}.arity)
+ assert_equal(-2, lambda{|x=0, y|}.arity) # different from proc
+ assert_equal(-1, lambda{|x=0, y=0|}.arity) # different from proc
+ assert_equal(-2, lambda{|x, y=0|}.arity) # different from proc
+ assert_equal(-2, lambda{|x, *y|}.arity)
+ assert_equal(-1, lambda{|x=0, *y|}.arity)
+ assert_equal(-1, lambda{|*x|}.arity)
+ assert_equal(-1, lambda{|*|}.arity)
+ assert_equal(-3, lambda{|x, *y, z|}.arity)
+ assert_equal(-2, lambda{|x=0, *y, z|}.arity)
+ assert_equal(2, lambda{|(x, y), z|[x,y]}.arity)
+ assert_equal(-2, lambda{|(x, y), z=0|[x,y]}.arity)
+ assert_equal(-4, lambda{|x, *y, z, a|}.arity)
+ assert_equal(-1, lambda{|**|}.arity)
+ assert_equal(-1, lambda{|**o|}.arity)
+ assert_equal(-2, lambda{|x, **o|}.arity)
+ assert_equal(-1, lambda{|x=0, **o|}.arity)
+ assert_equal(-2, lambda{|x, y=0, **o|}.arity)
+ assert_equal(-3, lambda{|x, y=0, z, **o|}.arity)
+ assert_equal(-3, lambda{|x, y=0, *z, w, **o|}.arity)
assert_arity(0) {}
assert_arity(0) {||}
@@ -77,6 +123,10 @@ class TestProc < Test::Unit::TestCase
assert_arity(-3) {|x, *y, z|}
assert_arity(-1) {|*x|}
assert_arity(-1) {|*|}
+ assert_arity(-1) {|**o|}
+ assert_arity(-1) {|**|}
+ assert_arity(-2) {|x, *y, **|}
+ assert_arity(-3) {|x, *y, z, **|}
end
def m(x)
@@ -110,26 +160,34 @@ class TestProc < Test::Unit::TestCase
$SAFE += 1
proc {$SAFE}
}.call
- assert_equal(safe, $SAFE)
+
+ assert_equal(safe + 1, $SAFE)
assert_equal(safe + 1, p.call)
- assert_equal(safe, $SAFE)
+ assert_equal(safe + 1, $SAFE)
+ $SAFE = 0
c.class_eval {define_method(:safe, p)}
assert_equal(safe, x.safe)
- assert_equal(safe, x.method(:safe).call)
- assert_equal(safe, x.method(:safe).to_proc.call)
+ $SAFE = 0
p = proc {$SAFE += 1}
assert_equal(safe + 1, p.call)
- assert_equal(safe, $SAFE)
+ assert_equal(safe + 1, $SAFE)
+ $SAFE = 0
c.class_eval {define_method(:inc, p)}
assert_equal(safe + 1, proc {x.inc; $SAFE}.call)
- assert_equal(safe, $SAFE)
+ assert_equal(safe + 1, $SAFE)
+
+ $SAFE = 0
assert_equal(safe + 1, proc {x.method(:inc).call; $SAFE}.call)
- assert_equal(safe, $SAFE)
+ assert_equal(safe + 1, $SAFE)
+
+ $SAFE = 0
assert_equal(safe + 1, proc {x.method(:inc).to_proc.call; $SAFE}.call)
- assert_equal(safe, $SAFE)
+ assert_equal(safe + 1, $SAFE)
+ ensure
+ $SAFE = 0
end
def m2
@@ -140,55 +198,122 @@ class TestProc < Test::Unit::TestCase
method(:m2).to_proc
end
+ def m1(var)
+ var
+ end
+
+ def m_block_given?
+ m1(block_given?)
+ end
+
# [yarv-dev:777] block made by Method#to_proc
def test_method_to_proc
b = block()
assert_equal "OK", b.call
- assert_instance_of(Binding, b.binding, '[ruby-core:25589]')
+ b = b.binding
+ assert_instance_of(Binding, b, '[ruby-core:25589]')
+ bug10432 = '[ruby-core:65919] [Bug #10432]'
+ assert_same(self, b.receiver, bug10432)
+ assert_not_send [b, :local_variable_defined?, :value]
+ assert_raise(NameError) {
+ b.local_variable_get(:value)
+ }
+ assert_equal 42, b.local_variable_set(:value, 42)
+ assert_send [b, :local_variable_defined?, :value]
+ assert_equal 42, b.local_variable_get(:value)
+ end
+
+ def test_block_given_method
+ m = method(:m_block_given?)
+ assert(!m.call, "without block")
+ assert(m.call {}, "with block")
+ assert(!m.call, "without block second")
+ end
+
+ def test_block_given_method_to_proc
+ bug8341 = '[Bug #8341]'
+ m = method(:m_block_given?).to_proc
+ assert(!m.call, "#{bug8341} without block")
+ assert(m.call {}, "#{bug8341} with block")
+ assert(!m.call, "#{bug8341} without block second")
+ end
+
+ def test_block_persist_between_calls
+ bug8341 = '[Bug #8341]'
+ o = Object.new
+ def o.m1(top=true)
+ if top
+ [block_given?, @m.call(false)]
+ else
+ block_given?
+ end
+ end
+ m = o.method(:m1).to_proc
+ o.instance_variable_set(:@m, m)
+ assert_equal([true, false], m.call {}, "#{bug8341} nested with block")
+ assert_equal([false, false], m.call, "#{bug8341} nested without block")
end
- def test_curry
+ def test_curry_proc
b = proc {|x, y, z| (x||0) + (y||0) + (z||0) }
assert_equal(6, b.curry[1][2][3])
assert_equal(6, b.curry[1, 2][3, 4])
assert_equal(6, b.curry(5)[1][2][3][4][5])
assert_equal(6, b.curry(5)[1, 2][3, 4][5])
assert_equal(1, b.curry(1)[1])
+ end
+ def test_curry_proc_splat
b = proc {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) }
assert_equal(6, b.curry[1][2][3])
assert_equal(10, b.curry[1, 2][3, 4])
assert_equal(15, b.curry(5)[1][2][3][4][5])
assert_equal(15, b.curry(5)[1, 2][3, 4][5])
assert_equal(1, b.curry(1)[1])
+ end
+ def test_curry_lambda
b = lambda {|x, y, z| (x||0) + (y||0) + (z||0) }
assert_equal(6, b.curry[1][2][3])
assert_raise(ArgumentError) { b.curry[1, 2][3, 4] }
assert_raise(ArgumentError) { b.curry(5) }
assert_raise(ArgumentError) { b.curry(1) }
+ end
+ def test_curry_lambda_splat
b = lambda {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) }
assert_equal(6, b.curry[1][2][3])
assert_equal(10, b.curry[1, 2][3, 4])
assert_equal(15, b.curry(5)[1][2][3][4][5])
assert_equal(15, b.curry(5)[1, 2][3, 4][5])
assert_raise(ArgumentError) { b.curry(1) }
+ end
+ def test_curry_no_arguments
b = proc { :foo }
assert_equal(:foo, b.curry[])
+ end
+ def test_curry_given_blocks
b = lambda {|x, y, &blk| blk.call(x + y) }.curry
b = b.call(2) { raise }
b = b.call(3) {|x| x + 4 }
assert_equal(9, b)
+ end
+ def test_lambda?
l = proc {}
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, Proc.new(&l).lambda?)
l = lambda {}
assert_equal(true, l.lambda?)
assert_equal(true, l.curry.lambda?, '[ruby-core:24127]')
+ assert_equal(true, proc(&l).lambda?)
+ assert_equal(true, lambda(&l).lambda?)
+ assert_equal(true, Proc.new(&l).lambda?)
end
def test_curry_ski_fib
@@ -223,16 +348,27 @@ class TestProc < Test::Unit::TestCase
assert_equal(fib, [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89])
end
- def test_curry_from_knownbug
+ def test_curry_passed_block
a = lambda {|x, y, &b| b }
b = a.curry[1]
- assert_equal(:ok,
- if b.call(2){} == nil
- :ng
- else
- :ok
- end, 'moved from btest/knownbug, [ruby-core:15551]')
+ assert_not_nil(b.call(2){}, '[ruby-core:15551]: passed block to curried block')
+ end
+
+ def test_curry_instance_exec
+ a = lambda { |x, y| [x + y, self] }
+ b = a.curry.call(1)
+ result = instance_exec 2, &b
+
+ assert_equal(3, result[0])
+ assert_equal(self, result[1])
+ end
+
+ def test_curry_optional_params
+ obj = Object.new
+ def obj.foo(a, b=42); end
+ assert_raise(ArgumentError) { obj.method(:foo).to_proc.curry(3) }
+ assert_raise(ArgumentError) { ->(a, b=42){}.curry(3) }
end
def test_dup_clone
@@ -265,10 +401,19 @@ class TestProc < Test::Unit::TestCase
assert_equal(:foo, bc.foo)
b = nil
- 1.times { x, y, z = 1, 2, 3; b = binding }
+ 1.times { x, y, z = 1, 2, 3; [x,y,z]; b = binding }
assert_equal([1, 2, 3], b.eval("[x, y, z]"))
end
+ def test_binding_source_location
+ b, expected_location = binding, [__FILE__, __LINE__]
+ assert_equal(expected_location, b.source_location)
+
+ file, lineno = method(:source_location_test).to_proc.binding.source_location
+ assert_match(/^#{ Regexp.quote(__FILE__) }$/, file)
+ assert_equal(@@line_of_source_location_test, lineno, 'Bug #2427')
+ end
+
def test_proc_lambda
assert_raise(ArgumentError) { proc }
assert_raise(ArgumentError) { lambda }
@@ -302,12 +447,7 @@ class TestProc < Test::Unit::TestCase
t = Thread.new { sleep }
assert_raise(ThreadError) { t.instance_eval { initialize { } } }
t.kill
- end
-
- def test_eq2
- b1 = proc { }
- b2 = b1.dup
- assert(b1 == b2)
+ t.join
end
def test_to_proc
@@ -316,14 +456,14 @@ class TestProc < Test::Unit::TestCase
end
def test_localjump_error
- o = Object.new
+ o = o = Object.new
def foo; yield; end
exc = foo rescue $!
assert_nil(exc.exit_value)
assert_equal(:noreason, exc.reason)
end
- def test_binding2
+ def test_curry_binding
assert_raise(ArgumentError) { proc {}.curry.binding }
end
@@ -382,7 +522,7 @@ class TestProc < Test::Unit::TestCase
assert_equal [[1,2,3]], r
end
- def test_proc_args_rest_and_post
+ def test_proc_args_pos_rest_post
pr = proc {|a,b,*c,d,e|
[a,b,c,d,e]
}
@@ -405,7 +545,30 @@ class TestProc < Test::Unit::TestCase
assert_equal [1, 2, [3, 4, 5], 6,7], pr.call([1,2,3,4,5,6,7])
end
- def test_proc_args_opt
+ def test_proc_args_rest_post
+ pr = proc {|*a,b,c|
+ [a,b,c]
+ }
+ assert_equal [[], nil, nil], pr.call()
+ assert_equal [[], 1, nil], pr.call(1)
+ assert_equal [[], 1, 2], pr.call(1,2)
+ assert_equal [[1], 2, 3], pr.call(1,2,3)
+ assert_equal [[1, 2], 3, 4], pr.call(1,2,3,4)
+ assert_equal [[1, 2, 3], 4, 5], pr.call(1,2,3,4,5)
+ assert_equal [[1, 2, 3, 4], 5, 6], pr.call(1,2,3,4,5,6)
+ assert_equal [[1, 2, 3, 4, 5], 6,7], pr.call(1,2,3,4,5,6,7)
+
+ assert_equal [[], nil, nil], pr.call([])
+ assert_equal [[], 1, nil], pr.call([1])
+ assert_equal [[], 1, 2], pr.call([1,2])
+ assert_equal [[1], 2, 3], pr.call([1,2,3])
+ assert_equal [[1, 2], 3, 4], pr.call([1,2,3,4])
+ assert_equal [[1, 2, 3], 4, 5], pr.call([1,2,3,4,5])
+ assert_equal [[1, 2, 3, 4], 5, 6], pr.call([1,2,3,4,5,6])
+ assert_equal [[1, 2, 3, 4, 5], 6,7], pr.call([1,2,3,4,5,6,7])
+ end
+
+ def test_proc_args_pos_opt
pr = proc {|a,b,c=:c|
[a,b,c]
}
@@ -426,7 +589,44 @@ class TestProc < Test::Unit::TestCase
assert_equal [1, 2, 3], pr.call([1,2,3,4,5,6])
end
- def test_proc_args_opt_and_post
+ def test_proc_args_opt
+ pr = proc {|a=:a,b=:b,c=:c|
+ [a,b,c]
+ }
+ assert_equal [:a, :b, :c], pr.call()
+ assert_equal [1, :b, :c], pr.call(1)
+ assert_equal [1, 2, :c], pr.call(1,2)
+ assert_equal [1, 2, 3], pr.call(1,2,3)
+ assert_equal [1, 2, 3], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3], pr.call(1,2,3,4,5,6)
+
+ assert_equal [:a, :b, :c], pr.call([])
+ assert_equal [1, :b, :c], pr.call([1])
+ assert_equal [1, 2, :c], pr.call([1,2])
+ assert_equal [1, 2, 3], pr.call([1,2,3])
+ assert_equal [1, 2, 3], pr.call([1,2,3,4])
+ assert_equal [1, 2, 3], pr.call([1,2,3,4,5])
+ assert_equal [1, 2, 3], pr.call([1,2,3,4,5,6])
+ end
+
+ def test_proc_args_opt_single
+ bug7621 = '[ruby-dev:46801]'
+ pr = proc {|a=:a|
+ a
+ }
+ assert_equal :a, pr.call()
+ assert_equal 1, pr.call(1)
+ assert_equal 1, pr.call(1,2)
+
+ assert_equal [], pr.call([]), bug7621
+ assert_equal [1], pr.call([1]), bug7621
+ assert_equal [1, 2], pr.call([1,2]), bug7621
+ assert_equal [1, 2, 3], pr.call([1,2,3]), bug7621
+ assert_equal [1, 2, 3, 4], pr.call([1,2,3,4]), bug7621
+ end
+
+ def test_proc_args_pos_opt_post
pr = proc {|a,b,c=:c,d,e|
[a,b,c,d,e]
}
@@ -447,7 +647,28 @@ class TestProc < Test::Unit::TestCase
assert_equal [1, 2, 3, 4, 5], pr.call([1,2,3,4,5,6])
end
- def test_proc_args_opt_and_rest
+ def test_proc_args_opt_post
+ pr = proc {|a=:a,b=:b,c=:c,d,e|
+ [a,b,c,d,e]
+ }
+ assert_equal [:a, :b, :c, nil, nil], pr.call()
+ assert_equal [:a, :b, :c, 1, nil], pr.call(1)
+ assert_equal [:a, :b, :c, 1, 2], pr.call(1,2)
+ assert_equal [1, :b, :c, 2, 3], pr.call(1,2,3)
+ assert_equal [1, 2, :c, 3, 4], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, 4, 5], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, 4, 5], pr.call(1,2,3,4,5,6)
+
+ assert_equal [:a, :b, :c, nil, nil], pr.call([])
+ assert_equal [:a, :b, :c, 1, nil], pr.call([1])
+ assert_equal [:a, :b, :c, 1, 2], pr.call([1,2])
+ assert_equal [1, :b, :c, 2, 3], pr.call([1,2,3])
+ assert_equal [1, 2, :c, 3, 4], pr.call([1,2,3,4])
+ assert_equal [1, 2, 3, 4, 5], pr.call([1,2,3,4,5])
+ assert_equal [1, 2, 3, 4, 5], pr.call([1,2,3,4,5,6])
+ end
+
+ def test_proc_args_pos_opt_rest
pr = proc {|a,b,c=:c,*d|
[a,b,c,d]
}
@@ -466,7 +687,26 @@ class TestProc < Test::Unit::TestCase
assert_equal [1, 2, 3, [4, 5]], pr.call([1,2,3,4,5])
end
- def test_proc_args_opt_and_rest_and_post
+ def test_proc_args_opt_rest
+ pr = proc {|a=:a,b=:b,c=:c,*d|
+ [a,b,c,d]
+ }
+ assert_equal [:a, :b, :c, []], pr.call()
+ assert_equal [1, :b, :c, []], pr.call(1)
+ assert_equal [1, 2, :c, []], pr.call(1,2)
+ assert_equal [1, 2, 3, []], pr.call(1,2,3)
+ assert_equal [1, 2, 3, [4]], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, [4, 5]], pr.call(1,2,3,4,5)
+
+ assert_equal [:a, :b, :c, []], pr.call([])
+ assert_equal [1, :b, :c, []], pr.call([1])
+ assert_equal [1, 2, :c, []], pr.call([1,2])
+ assert_equal [1, 2, 3, []], pr.call([1,2,3])
+ assert_equal [1, 2, 3, [4]], pr.call([1,2,3,4])
+ assert_equal [1, 2, 3, [4, 5]], pr.call([1,2,3,4,5])
+ end
+
+ def test_proc_args_pos_opt_rest_post
pr = proc {|a,b,c=:c,*d,e|
[a,b,c,d,e]
}
@@ -487,7 +727,28 @@ class TestProc < Test::Unit::TestCase
assert_equal [1, 2, 3, [4,5], 6], pr.call([1,2,3,4,5,6])
end
- def test_proc_args_block
+ def test_proc_args_opt_rest_post
+ pr = proc {|a=:a,b=:b,c=:c,*d,e|
+ [a,b,c,d,e]
+ }
+ assert_equal [:a, :b, :c, [], nil], pr.call()
+ assert_equal [:a, :b, :c, [], 1], pr.call(1)
+ assert_equal [1, :b, :c, [], 2], pr.call(1,2)
+ assert_equal [1, 2, :c, [], 3], pr.call(1,2,3)
+ assert_equal [1, 2, 3, [], 4], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, [4], 5], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, [4,5], 6], pr.call(1,2,3,4,5,6)
+
+ assert_equal [:a, :b, :c, [], nil], pr.call([])
+ assert_equal [:a, :b, :c, [], 1], pr.call([1])
+ assert_equal [1, :b, :c, [], 2], pr.call([1,2])
+ assert_equal [1, 2, :c, [], 3], pr.call([1,2,3])
+ assert_equal [1, 2, 3, [], 4], pr.call([1,2,3,4])
+ assert_equal [1, 2, 3, [4], 5], pr.call([1,2,3,4,5])
+ assert_equal [1, 2, 3, [4,5], 6], pr.call([1,2,3,4,5,6])
+ end
+
+ def test_proc_args_pos_block
pr = proc {|a,b,&c|
[a, b, c.class, c&&c.call(:x)]
}
@@ -497,6 +758,12 @@ class TestProc < Test::Unit::TestCase
assert_equal [1, 2, NilClass, nil], pr.call(1,2,3)
assert_equal [1, 2, NilClass, nil], pr.call(1,2,3,4)
+ assert_equal [nil, nil, NilClass, nil], pr.call([])
+ assert_equal [1, nil, NilClass, nil], pr.call([1])
+ assert_equal [1, 2, NilClass, nil], pr.call([1,2])
+ assert_equal [1, 2, NilClass, nil], pr.call([1,2,3])
+ assert_equal [1, 2, NilClass, nil], pr.call([1,2,3,4])
+
assert_equal [nil, nil, Proc, :proc], (pr.call(){ :proc })
assert_equal [1, nil, Proc, :proc], (pr.call(1){ :proc })
assert_equal [1, 2, Proc, :proc], (pr.call(1, 2){ :proc })
@@ -510,7 +777,7 @@ class TestProc < Test::Unit::TestCase
assert_equal [1, 2, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
end
- def test_proc_args_rest_and_block
+ def test_proc_args_pos_rest_block
pr = proc {|a,b,*c,&d|
[a, b, c, d.class, d&&d.call(:x)]
}
@@ -533,7 +800,24 @@ class TestProc < Test::Unit::TestCase
assert_equal [1, 2, [3,4], Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
end
- def test_proc_args_rest_and_post_and_block
+ def test_proc_args_rest_block
+ pr = proc {|*c,&d|
+ [c, d.class, d&&d.call(:x)]
+ }
+ assert_equal [[], NilClass, nil], pr.call()
+ assert_equal [[1], NilClass, nil], pr.call(1)
+ assert_equal [[1, 2], NilClass, nil], pr.call(1,2)
+
+ assert_equal [[], Proc, :proc], (pr.call(){ :proc })
+ assert_equal [[1], Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [[1, 2], Proc, :proc], (pr.call(1, 2){ :proc })
+
+ assert_equal [[], Proc, :x], (pr.call(){|x| x})
+ assert_equal [[1], Proc, :x], (pr.call(1){|x| x})
+ assert_equal [[1, 2], Proc, :x], (pr.call(1, 2){|x| x})
+ 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)]
}
@@ -562,7 +846,30 @@ class TestProc < Test::Unit::TestCase
assert_equal [1, 2, [3,4], 5, 6, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6){|x| x})
end
- def test_proc_args_opt_and_block
+ def test_proc_args_rest_post_block
+ pr = proc {|*c,d,e,&f|
+ [c, d, e, f.class, f&&f.call(:x)]
+ }
+ assert_equal [[], nil, nil, NilClass, nil], pr.call()
+ assert_equal [[], 1, nil, NilClass, nil], pr.call(1)
+ assert_equal [[], 1, 2, NilClass, nil], pr.call(1,2)
+ assert_equal [[1], 2, 3, NilClass, nil], pr.call(1,2,3)
+ assert_equal [[1, 2], 3, 4, NilClass, nil], pr.call(1,2,3,4)
+
+ assert_equal [[], nil, nil, Proc, :proc], (pr.call(){ :proc })
+ assert_equal [[], 1, nil, Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [[], 1, 2, Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [[1], 2, 3, Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [[1, 2], 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+
+ assert_equal [[], nil, nil, Proc, :x], (pr.call(){|x| x})
+ assert_equal [[], 1, nil, Proc, :x], (pr.call(1){|x| x})
+ assert_equal [[], 1, 2, Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [[1], 2, 3, Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [[1, 2], 3, 4, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ end
+
+ def test_proc_args_pos_opt_block
pr = proc {|a,b,c=:c,d=:d,&e|
[a, b, c, d, e.class, e&&e.call(:x)]
}
@@ -588,7 +895,33 @@ class TestProc < Test::Unit::TestCase
assert_equal [1, 2, 3, 4, Proc, :x], (pr.call(1, 2, 3, 4, 5){|x| x})
end
- def test_proc_args_opt_and_post_and_block
+ def test_proc_args_opt_block
+ pr = proc {|a=:a,b=:b,c=:c,d=:d,&e|
+ [a, b, c, d, e.class, e&&e.call(:x)]
+ }
+ assert_equal [:a, :b, :c, :d, NilClass, nil], pr.call()
+ assert_equal [1, :b, :c, :d, NilClass, nil], pr.call(1)
+ assert_equal [1, 2, :c, :d, NilClass, nil], pr.call(1,2)
+ assert_equal [1, 2, 3, :d, NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, 3, 4, NilClass, nil], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, 4, NilClass, nil], pr.call(1,2,3,4,5)
+
+ assert_equal [:a, :b, :c, :d, Proc, :proc], (pr.call(){ :proc })
+ assert_equal [1, :b, :c, :d, Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [1, 2, :c, :d, Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, 2, 3, :d, Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+ assert_equal [1, 2, 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4, 5){ :proc })
+
+ assert_equal [:a, :b, :c, :d, Proc, :x], (pr.call(){|x| x})
+ assert_equal [1, :b, :c, :d, Proc, :x], (pr.call(1){|x| x})
+ assert_equal [1, 2, :c, :d, Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, 2, 3, :d, Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, 3, 4, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ assert_equal [1, 2, 3, 4, Proc, :x], (pr.call(1, 2, 3, 4, 5){|x| x})
+ end
+
+ def test_proc_args_pos_opt_post_block
pr = proc {|a,b,c=:c,d=:d,e,f,&g|
[a, b, c, d, e, f, g.class, g&&g.call(:x)]
}
@@ -620,7 +953,39 @@ class TestProc < Test::Unit::TestCase
assert_equal [1, 2, 3, 4, 5, 6, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6, 7){|x| x})
end
- def test_proc_args_opt_and_block2
+ def test_proc_args_opt_post_block
+ pr = proc {|a=:a,b=:b,c=:c,d=:d,e,f,&g|
+ [a, b, c, d, e, f, g.class, g&&g.call(:x)]
+ }
+ assert_equal [:a, :b, :c, :d, nil, nil, NilClass, nil], pr.call()
+ assert_equal [:a, :b, :c, :d, 1, nil, NilClass, nil], pr.call(1)
+ assert_equal [:a, :b, :c, :d, 1, 2, NilClass, nil], pr.call(1,2)
+ assert_equal [1, :b, :c, :d, 2, 3, NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, :c, :d, 3, 4, NilClass, nil], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, :d, 4, 5, NilClass, nil], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, 4, 5, 6, NilClass, nil], pr.call(1,2,3,4,5,6)
+ assert_equal [1, 2, 3, 4, 5, 6, NilClass, nil], pr.call(1,2,3,4,5,6,7)
+
+ assert_equal [:a, :b, :c, :d, nil, nil, Proc, :proc], (pr.call(){ :proc })
+ assert_equal [:a, :b, :c, :d, 1, nil, Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [:a, :b, :c, :d, 1, 2, Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, :b, :c, :d, 2, 3, Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, :c, :d, 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+ assert_equal [1, 2, 3, :d, 4, 5, Proc, :proc], (pr.call(1, 2, 3, 4, 5){ :proc })
+ assert_equal [1, 2, 3, 4, 5, 6, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6){ :proc })
+ assert_equal [1, 2, 3, 4, 5, 6, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6, 7){ :proc })
+
+ assert_equal [:a, :b, :c, :d, nil, nil, Proc, :x], (pr.call(){|x| x})
+ assert_equal [:a, :b, :c, :d, 1, nil, Proc, :x], (pr.call(1){|x| x})
+ assert_equal [:a, :b, :c, :d, 1, 2, Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, :b, :c, :d, 2, 3, Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, :c, :d, 3, 4, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ assert_equal [1, 2, 3, :d, 4, 5, Proc, :x], (pr.call(1, 2, 3, 4, 5){|x| x})
+ assert_equal [1, 2, 3, 4, 5, 6, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6){|x| x})
+ assert_equal [1, 2, 3, 4, 5, 6, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6, 7){|x| x})
+ end
+
+ def test_proc_args_pos_opt_rest_block
pr = proc {|a,b,c=:c,d=:d,*e,&f|
[a, b, c, d, e, f.class, f&&f.call(:x)]
}
@@ -649,7 +1014,36 @@ class TestProc < Test::Unit::TestCase
assert_equal [1, 2, 3, 4, [5,6], Proc, :x], (pr.call(1, 2, 3, 4, 5, 6){|x| x})
end
- def test_proc_args_opt_and_rest_and_post_and_block
+ def test_proc_args_opt_rest_block
+ pr = proc {|a=:a,b=:b,c=:c,d=:d,*e,&f|
+ [a, b, c, d, e, f.class, f&&f.call(:x)]
+ }
+ assert_equal [:a, :b, :c, :d, [], NilClass, nil], pr.call()
+ assert_equal [1, :b, :c, :d, [], NilClass, nil], pr.call(1)
+ assert_equal [1, 2, :c, :d, [], NilClass, nil], pr.call(1,2)
+ assert_equal [1, 2, 3, :d, [], NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, 3, 4, [], NilClass, nil], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, 4, [5], NilClass, nil], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, 4, [5,6], NilClass, nil], pr.call(1,2,3,4,5,6)
+
+ assert_equal [:a, :b, :c, :d, [], Proc, :proc], (pr.call(){ :proc })
+ assert_equal [1, :b, :c, :d, [], Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [1, 2, :c, :d, [], Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, 2, 3, :d, [], Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, 3, 4, [], Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+ assert_equal [1, 2, 3, 4, [5], Proc, :proc], (pr.call(1, 2, 3, 4, 5){ :proc })
+ assert_equal [1, 2, 3, 4, [5,6], Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6){ :proc })
+
+ assert_equal [:a, :b, :c, :d, [], Proc, :x], (pr.call(){|x| x})
+ assert_equal [1, :b, :c, :d, [], Proc, :x], (pr.call(1){|x| x})
+ assert_equal [1, 2, :c, :d, [], Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, 2, 3, :d, [], Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, 3, 4, [], Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ assert_equal [1, 2, 3, 4, [5], Proc, :x], (pr.call(1, 2, 3, 4, 5){|x| x})
+ assert_equal [1, 2, 3, 4, [5,6], Proc, :x], (pr.call(1, 2, 3, 4, 5, 6){|x| x})
+ end
+
+ def test_proc_args_pos_opt_rest_post_block
pr = proc {|a,b,c=:c,d=:d,*e,f,g,&h|
[a, b, c, d, e, f, g, h.class, h&&h.call(:x)]
}
@@ -684,7 +1078,42 @@ class TestProc < Test::Unit::TestCase
assert_equal [1, 2, 3, 4, [5,6], 7, 8, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6, 7, 8){|x| x})
end
- def test_proc_args_unleashed
+ def test_proc_args_opt_rest_post_block
+ pr = proc {|a=:a,b=:b,c=:c,d=:d,*e,f,g,&h|
+ [a, b, c, d, e, f, g, h.class, h&&h.call(:x)]
+ }
+ assert_equal [:a, :b, :c, :d, [], nil, nil, NilClass, nil], pr.call()
+ assert_equal [:a, :b, :c, :d, [], 1, nil, NilClass, nil], pr.call(1)
+ assert_equal [:a, :b, :c, :d, [], 1, 2, NilClass, nil], pr.call(1,2)
+ assert_equal [1, :b, :c, :d, [], 2, 3, NilClass, nil], pr.call(1,2,3)
+ assert_equal [1, 2, :c, :d, [], 3, 4, NilClass, nil], pr.call(1,2,3,4)
+ assert_equal [1, 2, 3, :d, [], 4, 5, NilClass, nil], pr.call(1,2,3,4,5)
+ assert_equal [1, 2, 3, 4, [], 5, 6, NilClass, nil], pr.call(1,2,3,4,5,6)
+ assert_equal [1, 2, 3, 4, [5], 6, 7, NilClass, nil], pr.call(1,2,3,4,5,6,7)
+ assert_equal [1, 2, 3, 4, [5,6], 7, 8, NilClass, nil], pr.call(1,2,3,4,5,6,7,8)
+
+ assert_equal [:a, :b, :c, :d, [], nil, nil, Proc, :proc], (pr.call(){ :proc })
+ assert_equal [:a, :b, :c, :d, [], 1, nil, Proc, :proc], (pr.call(1){ :proc })
+ assert_equal [:a, :b, :c, :d, [], 1, 2, Proc, :proc], (pr.call(1, 2){ :proc })
+ assert_equal [1, :b, :c, :d, [], 2, 3, Proc, :proc], (pr.call(1, 2, 3){ :proc })
+ assert_equal [1, 2, :c, :d, [], 3, 4, Proc, :proc], (pr.call(1, 2, 3, 4){ :proc })
+ assert_equal [1, 2, 3, :d, [], 4, 5, Proc, :proc], (pr.call(1, 2, 3, 4, 5){ :proc })
+ assert_equal [1, 2, 3, 4, [], 5, 6, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6){ :proc })
+ assert_equal [1, 2, 3, 4, [5], 6, 7, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6, 7){ :proc })
+ assert_equal [1, 2, 3, 4, [5,6], 7, 8, Proc, :proc], (pr.call(1, 2, 3, 4, 5, 6, 7, 8){ :proc })
+
+ assert_equal [:a, :b, :c, :d, [], nil, nil, Proc, :x], (pr.call(){|x| x})
+ assert_equal [:a, :b, :c, :d, [], 1, nil, Proc, :x], (pr.call(1){|x| x})
+ assert_equal [:a, :b, :c, :d, [], 1, 2, Proc, :x], (pr.call(1, 2){|x| x})
+ assert_equal [1, :b, :c, :d, [], 2, 3, Proc, :x], (pr.call(1, 2, 3){|x| x})
+ assert_equal [1, 2, :c, :d, [], 3, 4, Proc, :x], (pr.call(1, 2, 3, 4){|x| x})
+ assert_equal [1, 2, 3, :d, [], 4, 5, Proc, :x], (pr.call(1, 2, 3, 4, 5){|x| x})
+ assert_equal [1, 2, 3, 4, [], 5, 6, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6){|x| x})
+ assert_equal [1, 2, 3, 4, [5], 6, 7, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6, 7){|x| x})
+ assert_equal [1, 2, 3, 4, [5,6], 7, 8, Proc, :x], (pr.call(1, 2, 3, 4, 5, 6, 7, 8){|x| x})
+ end
+
+ def test_proc_args_pos_unleashed
r = proc {|a,b=1,*c,d,e|
[a,b,c,d,e]
}.call(1,2,3,4,5)
@@ -703,11 +1132,14 @@ class TestProc < Test::Unit::TestCase
assert_equal([[:opt, :a], [:rest, :b], [:opt, :c]], proc {|a, *b, c|}.parameters)
assert_equal([[:opt, :a], [:rest, :b], [:opt, :c], [:block, :d]], proc {|a, *b, c, &d|}.parameters)
assert_equal([[:opt, :a], [:opt, :b], [:rest, :c], [:opt, :d], [:block, :e]], proc {|a, b=:b, *c, d, &e|}.parameters)
- assert_equal([[:opt, nil], [:block, :b]], proc {|(a), &b|}.parameters)
+ assert_equal([[:opt, nil], [:block, :b]], proc {|(a), &b|a}.parameters)
assert_equal([[:opt, :a], [:opt, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:opt, :f], [:opt, :g], [:block, :h]], proc {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters)
assert_equal([[:req]], method(:putc).parameters)
assert_equal([[:rest]], method(:p).parameters)
+
+ pr = eval("proc{|"+"(_),"*30+"|}")
+ assert_empty(pr.parameters.map{|_,n|n}.compact)
end
def pm0() end
@@ -720,7 +1152,14 @@ class TestProc < Test::Unit::TestCase
def pmo5(a, *b, c) end
def pmo6(a, *b, c, &d) end
def pmo7(a, b = :b, *c, d, &e) end
- def pma1((a), &b) end
+ def pma1((a), &b) a; end
+ def pmk1(**) end
+ def pmk2(**o) nil && o end
+ def pmk3(a, **o) nil && o end
+ def pmk4(a = nil, **o) nil && o end
+ def pmk5(a, b = nil, **o) nil && o end
+ def pmk6(a, b = nil, c, **o) nil && o end
+ def pmk7(a, b = nil, *c, d, **o) nil && o end
def test_bound_parameters
@@ -735,8 +1174,15 @@ class TestProc < Test::Unit::TestCase
assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:pmo6).to_proc.parameters)
assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], method(:pmo7).to_proc.parameters)
assert_equal([[:req], [:block, :b]], method(:pma1).to_proc.parameters)
-
- assert_equal([], "".method(:upcase).to_proc.parameters)
+ assert_equal([[:keyrest]], method(:pmk1).to_proc.parameters)
+ assert_equal([[:keyrest, :o]], method(:pmk2).to_proc.parameters)
+ assert_equal([[:req, :a], [:keyrest, :o]], method(:pmk3).to_proc.parameters)
+ assert_equal([[:opt, :a], [:keyrest, :o]], method(:pmk4).to_proc.parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], method(:pmk5).to_proc.parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], method(:pmk6).to_proc.parameters)
+ assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], method(:pmk7).to_proc.parameters)
+
+ assert_equal([], "".method(:empty?).to_proc.parameters)
assert_equal([[:rest]], "".method(:gsub).to_proc.parameters)
assert_equal([[:rest]], proc {}.curry.parameters)
end
@@ -747,7 +1193,9 @@ class TestProc < Test::Unit::TestCase
assert_match(/^#<Proc:0x\h+ \(lambda\)>$/, method(:p).to_proc.to_s)
x = proc {}
x.taint
- assert(x.to_s.tainted?)
+ assert_predicate(x.to_s, :tainted?)
+ name = "Proc\u{1f37b}"
+ assert_include(EnvUtil.labeled_class(name, Proc).new {}.to_s, name)
end
@@line_of_source_location_test = __LINE__ + 1
@@ -786,10 +1234,254 @@ class TestProc < Test::Unit::TestCase
assert_equal(@@line_of_attr_accessor_source_location_test, lineno)
end
+ def block_source_location_test(*args, &block)
+ block.source_location
+ end
+
+ def test_block_source_location
+ exp_lineno = __LINE__ + 3
+ file, lineno = block_source_location_test(1,
+ 2,
+ 3) do
+ end
+ assert_match(/^#{ Regexp.quote(__FILE__) }$/, file)
+ assert_equal(exp_lineno, lineno)
+ end
+
def test_splat_without_respond_to
- def (obj = Object.new).respond_to?(m); false end
+ def (obj = Object.new).respond_to?(m,*); false end
[obj].each do |a, b|
assert_equal([obj, nil], [a, b], '[ruby-core:24139]')
end
end
+
+ def test_curry_with_trace
+ # bug3751 = '[ruby-core:31871]'
+ set_trace_func(proc {})
+ methods.grep(/\Atest_curry/) do |test|
+ next if test == __method__
+ __send__(test)
+ end
+ ensure
+ set_trace_func(nil)
+ end
+
+ def test_block_propagation
+ bug3792 = '[ruby-core:32075]'
+ c = Class.new do
+ def foo
+ yield
+ end
+ end
+
+ o = c.new
+ f = :foo.to_proc
+ assert_nothing_raised(LocalJumpError, bug3792) {
+ assert_equal('bar', f.(o) {'bar'}, bug3792)
+ }
+ assert_nothing_raised(LocalJumpError, bug3792) {
+ assert_equal('zot', o.method(:foo).to_proc.() {'zot'}, bug3792)
+ }
+ end
+
+ def test_overridden_lambda
+ bug8345 = '[ruby-core:54687] [Bug #8345]'
+ assert_normal_exit('def lambda; end; method(:puts).to_proc', bug8345)
+ end
+
+ def test_overridden_proc
+ bug8345 = '[ruby-core:54688] [Bug #8345]'
+ assert_normal_exit('def proc; end; ->{}.curry', bug8345)
+ end
+
+ def get_binding if: 1, case: 2, when: 3, begin: 4, end: 5
+ a ||= 0
+ binding
+ end
+
+ def test_local_variables
+ b = get_binding
+ assert_equal(%i'if case when begin end a', b.local_variables)
+ a = tap {|;x, y| x = y; break binding.local_variables}
+ assert_equal(%i[a b x y], a.sort)
+ end
+
+ def test_local_variables_nested
+ b = tap {break binding}
+ assert_equal(%i[b], b.local_variables, '[ruby-dev:48351] [Bug #10001]')
+ end
+
+ def local_variables_of(bind)
+ this_should_not_be_in_bind = this_should_not_be_in_bind = 2
+ bind.local_variables
+ end
+
+ def test_local_variables_in_other_context
+ feature8773 = '[Feature #8773]'
+ assert_equal([:feature8773], local_variables_of(binding), feature8773)
+ end
+
+ def test_local_variable_get
+ b = get_binding
+ assert_equal(0, b.local_variable_get(:a))
+ assert_raise(NameError){ b.local_variable_get(:b) }
+
+ # access keyword named local variables
+ assert_equal(1, b.local_variable_get(:if))
+ assert_equal(2, b.local_variable_get(:case))
+ assert_equal(3, b.local_variable_get(:when))
+ assert_equal(4, b.local_variable_get(:begin))
+ assert_equal(5, b.local_variable_get(:end))
+ end
+
+ def test_local_variable_set
+ b = get_binding
+ b.local_variable_set(:a, 10)
+ b.local_variable_set(:b, 20)
+ assert_equal(10, b.local_variable_get(:a))
+ assert_equal(20, b.local_variable_get(:b))
+ assert_equal(10, b.eval("a"))
+ assert_equal(20, b.eval("b"))
+ end
+
+ def test_local_variable_set_wb
+ assert_ruby_status([], <<-'end;', '[Bug #13605]', timeout: 30)
+ b = binding
+ n = 20_000
+
+ n.times do |i|
+ v = rand(2_000)
+ name = "n#{v}"
+ value = Object.new
+ b.local_variable_set name, value
+ end
+ end;
+ end
+
+ def test_local_variable_defined?
+ b = get_binding
+ assert_equal(true, b.local_variable_defined?(:a))
+ assert_equal(false, b.local_variable_defined?(:b))
+ end
+
+ def test_binding_receiver
+ feature8779 = '[ruby-dev:47613] [Feature #8779]'
+
+ assert_same(self, binding.receiver, feature8779)
+
+ obj = Object.new
+ def obj.b; binding; end
+ assert_same(obj, obj.b.receiver, feature8779)
+ end
+
+ def test_proc_mark
+ assert_normal_exit(<<-'EOS')
+ def f
+ Enumerator.new{
+ 100000.times {|i|
+ yield
+ s = "#{i}"
+ }
+ }
+ end
+
+ def g
+ x = proc{}
+ f(&x)
+ end
+ e = g
+ e.each {}
+ EOS
+ end
+
+ def test_prepended_call
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", ["call"])
+ begin;
+ Proc.prepend Module.new {def call() puts "call"; super; end}
+ def m(&blk) blk.call; end
+ m {}
+ end;
+ end
+
+ def test_refined_call
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", ["call"])
+ begin;
+ using Module.new {refine(Proc) {def call() puts "call"; super; end}}
+ def m(&blk) blk.call; end
+ m {}
+ end;
+ end
+
+ def method_for_test_proc_without_block_for_symbol
+ binding.eval('proc')
+ 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}
+
+ assert_equal(6, (f << g).call(2))
+ assert_equal(6, (g >> f).call(2))
+ end
+
+ def test_compose_with_multiple_args
+ f = proc {|x| x * 2}
+ g = proc {|x, y| x + y}
+
+ assert_equal(6, (f << g).call(1, 2))
+ assert_equal(6, (g >> f).call(1, 2))
+ end
+
+ def test_compose_with_block
+ f = proc {|x| x * 2}
+ g = proc {|&blk| blk.call(1) }
+
+ assert_equal(8, (f << g).call { |x| x + 3 })
+ assert_equal(8, (g >> f).call { |x| x + 3 })
+ end
+
+ def test_compose_with_lambda
+ f = lambda {|x| x * 2}
+ g = lambda {|x| x}
+
+ assert_predicate((f << g), :lambda?)
+ assert_predicate((g >> f), :lambda?)
+ end
+
+ def test_compose_with_method
+ f = proc {|x| x * 2}
+ c = Class.new {
+ def g(x) x + 1 end
+ }
+ g = c.new.method(:g)
+
+ assert_equal(6, (f << g).call(2))
+ assert_equal(5, (f >> g).call(2))
+ end
+
+ def test_compose_with_callable
+ f = proc {|x| x * 2}
+ c = Class.new {
+ def call(x) x + 1 end
+ }
+ g = c.new
+
+ assert_equal(6, (f << g).call(2))
+ assert_equal(5, (f >> g).call(2))
+ end
+
+ def test_compose_with_noncallable
+ f = proc {|x| x * 2}
+
+ assert_raise(NoMethodError) {
+ (f << 5).call(2)
+ }
+ assert_raise(NoMethodError) {
+ (f >> 5).call(2)
+ }
+ end
end
diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb
index 03cf36e44b..0b43c6bc48 100644
--- a/test/ruby/test_process.rb
+++ b/test/ruby/test_process.rb
@@ -1,7 +1,9 @@
+# coding: utf-8
+# frozen_string_literal: false
require 'test/unit'
-require 'tmpdir'
-require 'pathname'
-require_relative 'envutil'
+require 'tempfile'
+require 'timeout'
+require 'io/wait'
require 'rbconfig'
class TestProcess < Test::Unit::TestCase
@@ -15,6 +17,13 @@ class TestProcess < Test::Unit::TestCase
Process.waitall
end
+ def windows?
+ self.class.windows?
+ end
+ def self.windows?
+ return /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
+ end
+
def write_file(filename, content)
File.open(filename, "w") {|f|
f << content
@@ -23,7 +32,7 @@ class TestProcess < Test::Unit::TestCase
def with_tmpchdir
Dir.mktmpdir {|d|
- d = Pathname.new(d).realpath.to_s
+ d = File.realpath(d)
Dir.chdir(d) {
yield d
}
@@ -58,9 +67,14 @@ class TestProcess < Test::Unit::TestCase
return unless rlimit_exist?
with_tmpchdir {
write_file 's', <<-"End"
+ # Too small RLIMIT_NOFILE, such as zero, causes problems.
+ # [OpenBSD] Setting to zero freezes this test.
+ # [GNU/Linux] EINVAL on poll(). EINVAL on ruby's internal poll() ruby with "[ASYNC BUG] thread_timer: select".
+ pipes = IO.pipe
+ limit = pipes.map {|io| io.fileno }.min
result = 1
begin
- Process.setrlimit(Process::RLIMIT_NOFILE, 0)
+ Process.setrlimit(Process::RLIMIT_NOFILE, limit)
rescue Errno::EINVAL
result = 0
end
@@ -88,11 +102,16 @@ class TestProcess < Test::Unit::TestCase
:DATA, "DATA",
:FSIZE, "FSIZE",
:MEMLOCK, "MEMLOCK",
+ :MSGQUEUE, "MSGQUEUE",
+ :NICE, "NICE",
:NOFILE, "NOFILE",
:NPROC, "NPROC",
:RSS, "RSS",
- :STACK, "STACK",
+ :RTPRIO, "RTPRIO",
+ :RTTIME, "RTTIME",
:SBSIZE, "SBSIZE",
+ :SIGPENDING, "SIGPENDING",
+ :STACK, "STACK",
].each {|name|
if Process.const_defined? "RLIMIT_#{name}"
assert_nothing_raised { Process.getrlimit(name) }
@@ -102,11 +121,15 @@ class TestProcess < Test::Unit::TestCase
}
assert_raise(ArgumentError) { Process.getrlimit(:FOO) }
assert_raise(ArgumentError) { Process.getrlimit("FOO") }
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Process.getrlimit("\u{30eb 30d3 30fc}") }
end
def test_rlimit_value
return unless rlimit_exist?
+ assert_raise(ArgumentError) { Process.setrlimit(:FOO, 0) }
assert_raise(ArgumentError) { Process.setrlimit(:CORE, :FOO) }
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Process.setrlimit("\u{30eb 30d3 30fc}", 0) }
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Process.setrlimit(:CORE, "\u{30eb 30d3 30fc}") }
with_tmpchdir do
s = run_in_child(<<-'End')
cur, max = Process.getrlimit(:NOFILE)
@@ -117,7 +140,7 @@ class TestProcess < Test::Unit::TestCase
exit false
end
End
- assert_not_equal(0, s.exitstatus)
+ assert_not_predicate(s, :success?)
s = run_in_child(<<-'End')
cur, max = Process.getrlimit(:NOFILE)
Process.setrlimit(:NOFILE, [max-10, cur].min)
@@ -127,7 +150,7 @@ class TestProcess < Test::Unit::TestCase
exit false
end
End
- assert_not_equal(0, s.exitstatus)
+ assert_not_predicate(s, :success?)
end
end
@@ -146,7 +169,7 @@ class TestProcess < Test::Unit::TestCase
end
def test_execopts_pgroup
- skip "system(:pgroup) is not supported" if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
+ skip "system(:pgroup) is not supported" if windows?
assert_nothing_raised { system(*TRUECOMMAND, :pgroup=>false) }
io = IO.popen([RUBY, "-e", "print Process.getpgrp"])
@@ -158,7 +181,11 @@ class TestProcess < Test::Unit::TestCase
io.close
assert_raise(ArgumentError) { system(*TRUECOMMAND, :pgroup=>-1) }
- assert_raise(Errno::EPERM) { Process.wait spawn(*TRUECOMMAND, :pgroup=>2) }
+ IO.popen([RUBY, '-egets'], 'w') do |f|
+ assert_raise(Errno::EPERM) {
+ Process.wait spawn(*TRUECOMMAND, :pgroup=>f.pid)
+ }
+ end
io1 = IO.popen([RUBY, "-e", "print Process.getpgrp", :pgroup=>true])
io2 = IO.popen([RUBY, "-e", "print Process.getpgrp", :pgroup=>io1.pid])
@@ -215,18 +242,42 @@ class TestProcess < Test::Unit::TestCase
:rlimit_core=>n, :rlimit_cpu=>3600]) {|io|
assert_equal("[#{n}, #{n}]\n[3600, 3600]", io.read.chomp)
}
+
+ assert_raise(ArgumentError) do
+ system(RUBY, '-e', 'exit', 'rlimit_bogus'.to_sym => 123)
+ end
+ assert_separately([],"#{<<-"begin;"}\n#{<<~'end;'}")
+ BUG = "[ruby-core:82033] [Bug #13744]"
+ RUBY = "#{RUBY}"
+ begin;
+ assert(system("#{RUBY}", "-e",
+ "exit([3600,3600] == Process.getrlimit(:CPU))",
+ 'rlimit_cpu'.to_sym => 3600), BUG)
+ assert_raise(ArgumentError, BUG) do
+ system("#{RUBY}", '-e', 'exit', :rlimit_bogus => 123)
+ end
+ end;
+
+ assert_raise(ArgumentError, /rlimit_cpu/) {
+ system(RUBY, '-e', 'exit', "rlimit_cpu\0".to_sym => 3600)
+ }
end
- MANDATORY_ENVS = %w[RUBYLIB]
+ MANDATORY_ENVS = %w[RUBYLIB MJIT_SEARCH_BUILD_DIR]
case RbConfig::CONFIG['target_os']
when /linux/
MANDATORY_ENVS << 'LD_PRELOAD'
when /mswin|mingw/
MANDATORY_ENVS.concat(%w[HOME USER TMPDIR])
+ when /darwin/
+ MANDATORY_ENVS.concat(ENV.keys.grep(/\A__CF_/))
end
if e = RbConfig::CONFIG['LIBPATHENV']
MANDATORY_ENVS << e
end
+ if e = RbConfig::CONFIG['PRELOADENV'] and !e.empty?
+ MANDATORY_ENVS << e
+ end
PREENVARG = ['-e', "%w[#{MANDATORY_ENVS.join(' ')}].each{|e|ENV.delete(e)}"]
ENVARG = ['-e', 'ENV.each {|k,v| puts "#{k}=#{v}" }']
ENVCOMMAND = [RUBY].concat(PREENVARG).concat(ENVARG)
@@ -291,6 +342,88 @@ class TestProcess < Test::Unit::TestCase
end
end
+ def test_execopt_env_path
+ bug8004 = '[ruby-core:53103] [Bug #8004]'
+ Dir.mktmpdir do |d|
+ open("#{d}/tmp_script.cmd", "w") {|f| f.puts ": ;"; f.chmod(0755)}
+ assert_not_nil(pid = Process.spawn({"PATH" => d}, "tmp_script.cmd"), bug8004)
+ wpid, st = Process.waitpid2(pid)
+ assert_equal([pid, true], [wpid, st.success?], bug8004)
+ end
+ end
+
+ def _test_execopts_env_popen(cmd)
+ message = cmd.inspect
+ IO.popen({"FOO"=>"BAR"}, cmd) {|io|
+ assert_equal('FOO=BAR', io.read[/^FOO=.*/], message)
+ }
+
+ old = ENV["hmm"]
+ begin
+ ENV["hmm"] = "fufu"
+ IO.popen(cmd) {|io| assert_match(/^hmm=fufu$/, io.read, message)}
+ IO.popen({"hmm"=>""}, cmd) {|io| assert_match(/^hmm=$/, io.read, message)}
+ IO.popen({"hmm"=>nil}, cmd) {|io| assert_not_match(/^hmm=/, io.read, message)}
+ ENV["hmm"] = ""
+ IO.popen(cmd) {|io| assert_match(/^hmm=$/, io.read, message)}
+ IO.popen({"hmm"=>""}, cmd) {|io| assert_match(/^hmm=$/, io.read, message)}
+ IO.popen({"hmm"=>nil}, cmd) {|io| assert_not_match(/^hmm=/, io.read, message)}
+ ENV["hmm"] = nil
+ IO.popen(cmd) {|io| assert_not_match(/^hmm=/, io.read, message)}
+ IO.popen({"hmm"=>""}, cmd) {|io| assert_match(/^hmm=$/, io.read, message)}
+ IO.popen({"hmm"=>nil}, cmd) {|io| assert_not_match(/^hmm=/, io.read, message)}
+ ensure
+ ENV["hmm"] = old
+ end
+ end
+
+ def test_execopts_env_popen_vector
+ _test_execopts_env_popen(ENVCOMMAND)
+ end
+
+ def test_execopts_env_popen_string
+ with_tmpchdir do |d|
+ open('test-script', 'w') do |f|
+ ENVCOMMAND.each_with_index do |cmd, i|
+ next if i.zero? or cmd == "-e"
+ f.puts cmd
+ end
+ end
+ _test_execopts_env_popen("#{RUBY} test-script")
+ end
+ end
+
+ def test_execopts_preserve_env_on_exec_failure
+ with_tmpchdir {|d|
+ write_file 's', <<-"End"
+ ENV["mgg"] = nil
+ prog = "./nonexistent"
+ begin
+ Process.exec({"mgg" => "mggoo"}, [prog, prog])
+ rescue Errno::ENOENT
+ end
+ open('out', 'w') {|f|
+ f.print ENV["mgg"].inspect
+ }
+ End
+ system(RUBY, 's')
+ assert_equal(nil.inspect, File.read('out'),
+ "[ruby-core:44093] [ruby-trunk - Bug #6249]")
+ }
+ end
+
+ def test_execopts_env_single_word
+ with_tmpchdir {|d|
+ open("test_execopts_env_single_word.rb", "w") {|f|
+ f.puts "print ENV['hgga']"
+ }
+ system({"hgga"=>"ugu"}, RUBY,
+ :in => 'test_execopts_env_single_word.rb',
+ :out => 'test_execopts_env_single_word.out')
+ assert_equal('ugu', File.read('test_execopts_env_single_word.out'))
+ }
+ end
+
def test_execopts_unsetenv_others
h = {}
MANDATORY_ENVS.each {|k| e = ENV[k] and h[k] = e}
@@ -309,16 +442,59 @@ class TestProcess < Test::Unit::TestCase
IO.popen([*PWD, :chdir => d]) {|io|
assert_equal(d, io.read.chomp)
}
- assert_raise(Errno::ENOENT) {
+ assert_raise_with_message(Errno::ENOENT, %r"d/notexist") {
Process.wait Process.spawn(*PWD, :chdir => "d/notexist")
}
+ n = "d/\u{1F37A}"
+ assert_raise_with_message(Errno::ENOENT, /#{n}/) {
+ Process.wait Process.spawn(*PWD, :chdir => n)
+ }
+ }
+ end
+
+ def test_execopts_open_chdir
+ with_tmpchdir {|d|
+ Dir.mkdir "foo"
+ system(*PWD, :chdir => "foo", :out => "open_chdir_test")
+ assert_file.exist?("open_chdir_test")
+ assert_file.not_exist?("foo/open_chdir_test")
+ assert_equal("#{d}/foo", File.read("open_chdir_test").chomp)
+ }
+ end
+
+ def test_execopts_open_chdir_m17n_path
+ with_tmpchdir {|d|
+ Dir.mkdir "テスト"
+ (pwd = PWD.dup).insert(1, '-EUTF-8:UTF-8')
+ system(*pwd, :chdir => "テスト", :out => "open_chdir_テスト")
+ assert_file.exist?("open_chdir_テスト")
+ assert_file.not_exist?("テスト/open_chdir_テスト")
+ assert_equal("#{d}/テスト", File.read("open_chdir_テスト", encoding: "UTF-8").chomp)
+ }
+ end if windows? || Encoding.find('locale') == Encoding::UTF_8
+
+ def test_execopts_open_failure
+ with_tmpchdir {|d|
+ assert_raise_with_message(Errno::ENOENT, %r"d/notexist") {
+ Process.wait Process.spawn(*PWD, :in => "d/notexist")
+ }
+ assert_raise_with_message(Errno::ENOENT, %r"d/notexist") {
+ Process.wait Process.spawn(*PWD, :out => "d/notexist")
+ }
+ n = "d/\u{1F37A}"
+ assert_raise_with_message(Errno::ENOENT, /#{n}/) {
+ Process.wait Process.spawn(*PWD, :in => n)
+ }
+ assert_raise_with_message(Errno::ENOENT, /#{n}/) {
+ Process.wait Process.spawn(*PWD, :out => n)
+ }
}
end
UMASK = [RUBY, '-e', 'printf "%04o\n", File.umask']
def test_execopts_umask
- skip "umask is not supported" if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
+ skip "umask is not supported" if windows?
IO.popen([*UMASK, :umask => 0]) {|io|
assert_equal("0000", io.read.chomp)
}
@@ -356,11 +532,11 @@ class TestProcess < Test::Unit::TestCase
SORT = [RUBY, '-e', "puts ARGF.readlines.sort"]
CAT = [RUBY, '-e', "IO.copy_stream STDIN, STDOUT"]
- def test_execopts_redirect
+ def test_execopts_redirect_fd
with_tmpchdir {|d|
Process.wait Process.spawn(*ECHO["a"], STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
assert_equal("a", File.read("out").chomp)
- if /mswin|mingw/ =~ RUBY_PLATFORM
+ if windows?
# currently telling to child the file modes is not supported.
open("out", "a") {|f| f.write "0\n"}
else
@@ -375,22 +551,22 @@ class TestProcess < Test::Unit::TestCase
# problem occur with valgrind
#Process.wait Process.spawn(*ECHO["a"], STDOUT=>:close, STDERR=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
#p File.read("out")
- #assert(!File.read("out").empty?) # error message such as "-e:1:in `flush': Bad file descriptor (Errno::EBADF)"
+ #assert_not_empty(File.read("out")) # error message such as "-e:1:in `flush': Bad file descriptor (Errno::EBADF)"
Process.wait Process.spawn(*ECHO["c"], STDERR=>STDOUT, STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644])
assert_equal("c", File.read("out").chomp)
File.open("out", "w") {|f|
- Process.wait Process.spawn(*ECHO["d"], f=>STDOUT, STDOUT=>f)
+ Process.wait Process.spawn(*ECHO["d"], STDOUT=>f)
assert_equal("d", File.read("out").chomp)
}
- Process.wait Process.spawn(*ECHO["e"], STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644],
- 3=>STDOUT, 4=>STDOUT, 5=>STDOUT, 6=>STDOUT, 7=>STDOUT)
+ opts = {STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]}
+ opts.merge(3=>STDOUT, 4=>STDOUT, 5=>STDOUT, 6=>STDOUT, 7=>STDOUT) unless windows?
+ Process.wait Process.spawn(*ECHO["e"], opts)
assert_equal("e", File.read("out").chomp)
- Process.wait Process.spawn(*ECHO["ee"], STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644],
- 3=>0, 4=>:in, 5=>STDIN,
- 6=>1, 7=>:out, 8=>STDOUT,
- 9=>2, 10=>:err, 11=>STDERR)
+ opts = {STDOUT=>["out", File::WRONLY|File::CREAT|File::TRUNC, 0644]}
+ opts.merge(3=>0, 4=>:in, 5=>STDIN, 6=>1, 7=>:out, 8=>STDOUT, 9=>2, 10=>:err, 11=>STDERR) unless windows?
+ Process.wait Process.spawn(*ECHO["ee"], opts)
assert_equal("ee", File.read("out").chomp)
- if /mswin|mingw/ !~ RUBY_PLATFORM
+ unless windows?
# passing non-stdio fds is not supported on Windows
File.open("out", "w") {|f|
h = {STDOUT=>f, f=>STDOUT}
@@ -416,7 +592,7 @@ class TestProcess < Test::Unit::TestCase
Process.wait Process.spawn(*SORT, STDIN=>"out", STDOUT=>"out2")
assert_equal("ggg\nhhh\n", File.read("out2"))
- if /mswin|mingw/ !~ RUBY_PLATFORM
+ unless windows?
# passing non-stdio fds is not supported on Windows
assert_raise(Errno::ENOENT) {
Process.wait Process.spawn("non-existing-command", (3..60).to_a=>["err", File::WRONLY|File::CREAT])
@@ -428,74 +604,181 @@ class TestProcess < Test::Unit::TestCase
assert_equal("bb\naa\n", File.read("out"))
system(*SORT, STDIN=>["out"], STDOUT=>"out2")
assert_equal("aa\nbb\n", File.read("out2"))
+ }
+ end
+
+ def test_execopts_redirect_open_order_normal
+ minfd = 3
+ maxfd = 20
+ with_tmpchdir {|d|
+ opts = {}
+ minfd.upto(maxfd) {|fd| opts[fd] = ["out#{fd}", "w"] }
+ system RUBY, "-e", "#{minfd}.upto(#{maxfd}) {|fd| IO.new(fd).print fd.to_s }", opts
+ minfd.upto(maxfd) {|fd| assert_equal(fd.to_s, File.read("out#{fd}")) }
+ }
+ end unless windows? # passing non-stdio fds is not supported on Windows
+
+ def test_execopts_redirect_open_order_reverse
+ minfd = 3
+ maxfd = 20
+ with_tmpchdir {|d|
+ opts = {}
+ maxfd.downto(minfd) {|fd| opts[fd] = ["out#{fd}", "w"] }
+ system RUBY, "-e", "#{minfd}.upto(#{maxfd}) {|fd| IO.new(fd).print fd.to_s }", opts
+ minfd.upto(maxfd) {|fd| assert_equal(fd.to_s, File.read("out#{fd}")) }
+ }
+ end unless windows? # passing non-stdio fds is not supported on Windows
+
+ def test_execopts_redirect_open_fifo
+ with_tmpchdir {|d|
+ begin
+ File.mkfifo("fifo")
+ rescue NotImplementedError
+ return
+ end
+ assert(FileTest.pipe?("fifo"), "should be pipe")
+ t1 = Thread.new {
+ system(*ECHO["output to fifo"], :out=>"fifo")
+ }
+ t2 = Thread.new {
+ IO.popen([*CAT, :in=>"fifo"]) {|f| f.read }
+ }
+ _, v2 = assert_join_threads([t1, t2])
+ assert_equal("output to fifo\n", v2)
+ }
+ end unless windows? # does not support fifo
+
+ def test_execopts_redirect_open_fifo_interrupt_raise
+ with_tmpchdir {|d|
+ begin
+ File.mkfifo("fifo")
+ rescue NotImplementedError
+ return
+ end
+ IO.popen([RUBY, '-e', <<-'EOS']) {|io|
+ class E < StandardError; end
+ trap(:USR1) { raise E }
+ begin
+ puts "start"
+ STDOUT.flush
+ system("cat", :in => "fifo")
+ rescue E
+ puts "ok"
+ end
+ EOS
+ assert_equal("start\n", io.gets)
+ sleep 0.5
+ Process.kill(:USR1, io.pid)
+ assert_equal("ok\n", io.read)
+ }
+ }
+ end unless windows? # does not support fifo
+
+ def test_execopts_redirect_open_fifo_interrupt_print
+ with_tmpchdir {|d|
+ begin
+ File.mkfifo("fifo")
+ rescue NotImplementedError
+ return
+ end
+ IO.popen([RUBY, '-e', <<-'EOS']) {|io|
+ STDOUT.sync = true
+ trap(:USR1) { print "trap\n" }
+ puts "start"
+ system("cat", :in => "fifo")
+ EOS
+ assert_equal("start\n", io.gets)
+ sleep 0.2 # wait for the child to stop at opening "fifo"
+ Process.kill(:USR1, io.pid)
+ assert_equal("trap\n", io.readpartial(8))
+ File.write("fifo", "ok\n")
+ assert_equal("ok\n", io.read)
+ }
+ }
+ end unless windows? # does not support fifo
+
+ def test_execopts_redirect_pipe
+ with_pipe {|r1, w1|
+ with_pipe {|r2, w2|
+ opts = {STDIN=>r1, STDOUT=>w2}
+ opts.merge(w1=>:close, r2=>:close) unless windows?
+ pid = spawn(*SORT, opts)
+ r1.close
+ w2.close
+ w1.puts "c"
+ w1.puts "a"
+ w1.puts "b"
+ w1.close
+ assert_equal("a\nb\nc\n", r2.read)
+ r2.close
+ Process.wait(pid)
+ }
+ }
- with_pipe {|r1, w1|
- with_pipe {|r2, w2|
- pid = spawn(*SORT, STDIN=>r1, STDOUT=>w2, w1=>:close, r2=>:close)
- r1.close
- w2.close
- w1.puts "c"
- w1.puts "a"
- w1.puts "b"
- w1.close
- assert_equal("a\nb\nc\n", r2.read)
- r2.close
- Process.wait(pid)
+ unless windows?
+ # passing non-stdio fds is not supported on Windows
+ with_pipes(5) {|pipes|
+ ios = pipes.flatten
+ h = {}
+ ios.length.times {|i| h[ios[i]] = ios[(i-1)%ios.length] }
+ h2 = h.invert
+ _rios = pipes.map {|r, w| r }
+ wios = pipes.map {|r, w| w }
+ child_wfds = wios.map {|w| h2[w].fileno }
+ pid = spawn(RUBY, "-e",
+ "[#{child_wfds.join(',')}].each {|fd| IO.new(fd, 'w').puts fd }", h)
+ pipes.each {|r, w|
+ assert_equal("#{h2[w].fileno}\n", r.gets)
}
+ Process.wait pid;
}
- if /mswin|mingw/ !~ RUBY_PLATFORM
- # passing non-stdio fds is not supported on Windows
- with_pipes(5) {|pipes|
- ios = pipes.flatten
- h = {}
- ios.length.times {|i| h[ios[i]] = ios[(i-1)%ios.length] }
- h2 = h.invert
- rios = pipes.map {|r, w| r }
- wios = pipes.map {|r, w| w }
- child_wfds = wios.map {|w| h2[w].fileno }
- pid = spawn(RUBY, "-e",
- "[#{child_wfds.join(',')}].each {|fd| IO.new(fd, 'w').puts fd }", h)
- pipes.each {|r, w|
- assert_equal("#{h2[w].fileno}\n", r.gets)
- }
- Process.wait pid;
+ with_pipes(5) {|pipes|
+ ios = pipes.flatten
+ h = {}
+ ios.length.times {|i| h[ios[i]] = ios[(i+1)%ios.length] }
+ h2 = h.invert
+ _rios = pipes.map {|r, w| r }
+ wios = pipes.map {|r, w| w }
+ child_wfds = wios.map {|w| h2[w].fileno }
+ pid = spawn(RUBY, "-e",
+ "[#{child_wfds.join(',')}].each {|fd| IO.new(fd, 'w').puts fd }", h)
+ pipes.each {|r, w|
+ assert_equal("#{h2[w].fileno}\n", r.gets)
}
+ Process.wait pid
+ }
- with_pipes(5) {|pipes|
- ios = pipes.flatten
- h = {}
- ios.length.times {|i| h[ios[i]] = ios[(i+1)%ios.length] }
- h2 = h.invert
- rios = pipes.map {|r, w| r }
- wios = pipes.map {|r, w| w }
- child_wfds = wios.map {|w| h2[w].fileno }
- pid = spawn(RUBY, "-e",
- "[#{child_wfds.join(',')}].each {|fd| IO.new(fd, 'w').puts fd }", h)
- pipes.each {|r, w|
- assert_equal("#{h2[w].fileno}\n", r.gets)
- }
+ closed_fd = nil
+ with_pipes(5) {|pipes|
+ io = pipes.last.last
+ closed_fd = io.fileno
+ }
+ assert_raise(Errno::EBADF) { Process.wait spawn(*TRUECOMMAND, closed_fd=>closed_fd) }
+
+ with_pipe {|r, w|
+ if w.respond_to?(:"close_on_exec=")
+ w.close_on_exec = true
+ pid = spawn(RUBY, "-e", "IO.new(#{w.fileno}, 'w').print 'a'", w=>w)
+ w.close
+ assert_equal("a", r.read)
Process.wait pid
- }
+ end
+ }
- closed_fd = nil
- with_pipes(5) {|pipes|
- io = pipes.last.last
- closed_fd = io.fileno
- }
- assert_raise(Errno::EBADF) { Process.wait spawn(*TRUECOMMAND, closed_fd=>closed_fd) }
-
- with_pipe {|r, w|
- if w.respond_to?(:"close_on_exec=")
- w.close_on_exec = true
- pid = spawn(RUBY, "-e", "IO.new(#{w.fileno}, 'w').print 'a'", w=>w)
- w.close
- assert_equal("a", r.read)
- Process.wait pid
- end
- }
+ # ensure standard FDs we redirect to are blocking for compatibility
+ with_pipes(3) do |pipes|
+ src = 'p [STDIN,STDOUT,STDERR].map(&:nonblock?)'
+ rdr = { 0 => pipes[0][0], 1 => pipes[1][1], 2 => pipes[2][1] }
+ pid = spawn(RUBY, '-rio/nonblock', '-e', src, rdr)
+ assert_equal("[false, false, false]\n", pipes[1][0].gets)
+ Process.wait pid
end
+ end
+ end
+ def test_execopts_redirect_symbol
+ with_tmpchdir {|d|
system(*ECHO["funya"], :out=>"out")
assert_equal("funya\n", File.read("out"))
system(RUBY, '-e', 'STDOUT.reopen(STDERR); puts "henya"', :err=>"out")
@@ -506,6 +789,27 @@ class TestProcess < Test::Unit::TestCase
}
end
+ def test_execopts_redirect_nonascii_path
+ bug9946 = '[ruby-core:63185] [Bug #9946]'
+ with_tmpchdir {|d|
+ path = "t-\u{30c6 30b9 30c8 f6}.txt"
+ system(*ECHO["a"], out: path)
+ assert_file.for(bug9946).exist?(path)
+ assert_equal("a\n", File.read(path), bug9946)
+ }
+ end
+
+ def test_execopts_redirect_to_out_and_err
+ with_tmpchdir {|d|
+ ret = system(RUBY, "-e", 'STDERR.print "e"; STDOUT.print "o"', [:out, :err] => "foo")
+ assert_equal(true, ret)
+ assert_equal("eo", File.read("foo"))
+ ret = system(RUBY, "-e", 'STDERR.print "E"; STDOUT.print "O"', [:err, :out] => "bar")
+ assert_equal(true, ret)
+ assert_equal("EO", File.read("bar"))
+ }
+ end
+
def test_execopts_redirect_dup2_child
with_tmpchdir {|d|
Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'",
@@ -516,9 +820,7 @@ class TestProcess < Test::Unit::TestCase
STDERR=>"out", STDOUT=>[:child, STDERR])
assert_equal("errout", File.read("out"))
- if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
- skip "inheritance of fd other than stdin,stdout and stderr is not supported"
- end
+ skip "inheritance of fd other than stdin,stdout and stderr is not supported" if windows?
Process.wait spawn(RUBY, "-e", "STDERR.print 'err'; STDOUT.print 'out'",
STDOUT=>"out",
STDERR=>[:child, 3],
@@ -552,6 +854,11 @@ class TestProcess < Test::Unit::TestCase
IO.popen("#{RUBY} -e 'puts :foo'") {|io| assert_equal("foo\n", io.read) }
assert_raise(Errno::ENOENT) { IO.popen(["echo bar"]) {} } # assuming "echo bar" command not exist.
IO.popen(ECHO["baz"]) {|io| assert_equal("baz\n", io.read) }
+ }
+ end
+
+ def test_execopts_popen_stdio
+ with_tmpchdir {|d|
assert_raise(ArgumentError) {
IO.popen([*ECHO["qux"], STDOUT=>STDOUT]) {|io| }
}
@@ -561,9 +868,12 @@ class TestProcess < Test::Unit::TestCase
assert_raise(ArgumentError) {
IO.popen([*ECHO["fuga"], STDOUT=>"out"]) {|io| }
}
- if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
- skip "inheritance of fd other than stdin,stdout and stderr is not supported"
- end
+ }
+ end
+
+ def test_execopts_popen_extra_fd
+ skip "inheritance of fd other than stdin,stdout and stderr is not supported" if windows?
+ with_tmpchdir {|d|
with_pipe {|r, w|
IO.popen([RUBY, '-e', 'IO.new(3, "w").puts("a"); puts "b"', 3=>w]) {|io|
assert_equal("b\n", io.read)
@@ -580,7 +890,6 @@ class TestProcess < Test::Unit::TestCase
end
def test_popen_fork
- return if /freebsd/ =~ RUBY_PLATFORM # this test freeze in FreeBSD
IO.popen("-") {|io|
if !io
puts "fooo"
@@ -592,11 +901,9 @@ class TestProcess < Test::Unit::TestCase
end
def test_fd_inheritance
- if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
- skip "inheritance of fd other than stdin,stdout and stderr is not supported"
- end
+ skip "inheritance of fd other than stdin,stdout and stderr is not supported" if windows?
with_pipe {|r, w|
- system(RUBY, '-e', 'IO.new(ARGV[0].to_i, "w").puts(:ba)', w.fileno.to_s)
+ system(RUBY, '-e', 'IO.new(ARGV[0].to_i, "w").puts(:ba)', w.fileno.to_s, w=>w)
w.close
assert_equal("ba\n", r.read)
}
@@ -612,8 +919,9 @@ class TestProcess < Test::Unit::TestCase
write_file("s", <<-"End")
exec(#{RUBY.dump}, '-e',
'IO.new(ARGV[0].to_i, "w").puts("bu") rescue nil',
- #{w.fileno.to_s.dump})
+ #{w.fileno.to_s.dump}, :close_others=>false)
End
+ w.close_on_exec = false
Process.wait spawn(RUBY, "s", :close_others=>false)
w.close
assert_equal("bu\n", r.read)
@@ -621,11 +929,14 @@ class TestProcess < Test::Unit::TestCase
}
with_pipe {|r, w|
io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts('me')"])
- w.close
- errmsg = io.read
- assert_equal("", r.read)
- assert_not_equal("", errmsg)
- Process.wait
+ begin
+ w.close
+ errmsg = io.read
+ assert_equal("", r.read)
+ assert_not_equal("", errmsg)
+ ensure
+ io.close
+ end
}
with_pipe {|r, w|
errmsg = `#{RUBY} -e "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts(123)"`
@@ -636,9 +947,7 @@ class TestProcess < Test::Unit::TestCase
end
def test_execopts_close_others
- if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
- skip "inheritance of fd other than stdin,stdout and stderr is not supported"
- end
+ skip "inheritance of fd other than stdin,stdout and stderr is not supported" if windows?
with_tmpchdir {|d|
with_pipe {|r, w|
system(RUBY, '-e', 'STDERR.reopen("err", "w"); IO.new(ARGV[0].to_i, "w").puts("ma")', w.fileno.to_s, :close_others=>true)
@@ -655,6 +964,7 @@ class TestProcess < Test::Unit::TestCase
File.unlink("err")
}
with_pipe {|r, w|
+ w.close_on_exec = false
Process.wait spawn(RUBY, '-e', 'IO.new(ARGV[0].to_i, "w").puts("bi")', w.fileno.to_s, :close_others=>false)
w.close
assert_equal("bi\n", r.read)
@@ -674,32 +984,52 @@ class TestProcess < Test::Unit::TestCase
}
with_pipe {|r, w|
io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts('me')", :close_others=>true])
- w.close
- errmsg = io.read
- assert_equal("", r.read)
- assert_not_equal("", errmsg)
- Process.wait
+ begin
+ w.close
+ errmsg = io.read
+ assert_equal("", r.read)
+ assert_not_equal("", errmsg)
+ ensure
+ io.close
+ end
}
with_pipe {|r, w|
+ w.close_on_exec = false
io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts('mo')", :close_others=>false])
- w.close
- errmsg = io.read
- assert_equal("mo\n", r.read)
- assert_equal("", errmsg)
- Process.wait
+ begin
+ w.close
+ errmsg = io.read
+ assert_equal("mo\n", r.read)
+ assert_equal("", errmsg)
+ ensure
+ io.close
+ end
}
with_pipe {|r, w|
+ w.close_on_exec = false
io = IO.popen([RUBY, "-e", "STDERR.reopen(STDOUT); IO.new(#{w.fileno}, 'w').puts('mo')", :close_others=>nil])
- w.close
- errmsg = io.read
- assert_equal("mo\n", r.read)
- assert_equal("", errmsg)
- Process.wait
+ begin
+ w.close
+ errmsg = io.read
+ assert_equal("mo\n", r.read)
+ assert_equal("", errmsg)
+ ensure
+ io.close
+ end
}
}
end
+ def test_close_others_default_false
+ IO.pipe do |r,w|
+ w.close_on_exec = false
+ src = "IO.new(#{w.fileno}).puts(:hi)"
+ assert_equal true, system(*%W(#{RUBY} --disable=gems -e #{src}))
+ assert_equal "hi\n", r.gets
+ end
+ end unless windows? # passing non-stdio fds is not supported on Windows
+
def test_execopts_redirect_self
begin
with_pipe {|r, w|
@@ -713,6 +1043,18 @@ class TestProcess < Test::Unit::TestCase
rescue NotImplementedError
skip "IO#close_on_exec= is not supported"
end
+ end unless windows? # passing non-stdio fds is not supported on Windows
+
+ def test_execopts_redirect_tempfile
+ bug6269 = '[ruby-core:44181]'
+ Tempfile.create("execopts") do |tmp|
+ pid = assert_nothing_raised(ArgumentError, bug6269) do
+ break spawn(RUBY, "-e", "print $$", out: tmp)
+ end
+ Process.wait(pid)
+ tmp.rewind
+ assert_equal(pid.to_s, tmp.read)
+ end
end
def test_execopts_duplex_io
@@ -778,7 +1120,7 @@ class TestProcess < Test::Unit::TestCase
ret = system(str)
status = $?
assert_equal(false, ret)
- assert(status.exited?)
+ assert_predicate(status, :exited?)
assert_equal(5, status.exitstatus)
assert_equal("haha pid=#{status.pid} ppid=#{$$}", File.read("result"))
}
@@ -795,7 +1137,7 @@ class TestProcess < Test::Unit::TestCase
Process.wait pid
status = $?
assert_equal(pid, status.pid)
- assert(status.exited?)
+ assert_predicate(status, :exited?)
assert_equal(6, status.exitstatus)
assert_equal("hihi pid=#{status.pid} ppid=#{$$}", File.read("result"))
}
@@ -814,12 +1156,31 @@ class TestProcess < Test::Unit::TestCase
io.close
status = $?
assert_equal(pid, status.pid)
- assert(status.exited?)
+ assert_predicate(status, :exited?)
assert_equal(7, status.exitstatus)
assert_equal("fufu pid=#{status.pid} ppid=#{$$}", result)
}
end
+ def test_popen_wordsplit_beginning_and_trailing_spaces
+ with_tmpchdir {|d|
+ write_file("script", <<-'End')
+ print "fufumm pid=#{$$} ppid=#{Process.ppid}"
+ exit 7
+ End
+ str = " #{RUBY} script "
+ io = IO.popen(str)
+ pid = io.pid
+ result = io.read
+ io.close
+ status = $?
+ assert_equal(pid, status.pid)
+ assert_predicate(status, :exited?)
+ assert_equal(7, status.exitstatus)
+ assert_equal("fufumm pid=#{status.pid} ppid=#{$$}", result)
+ }
+ end
+
def test_exec_wordsplit
with_tmpchdir {|d|
write_file("script", <<-'End')
@@ -840,9 +1201,9 @@ class TestProcess < Test::Unit::TestCase
Process.wait pid
status = $?
assert_equal(pid, status.pid)
- assert(status.exited?)
+ assert_predicate(status, :exited?)
assert_equal(6, status.exitstatus)
- if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
+ if windows?
expected = "hehe ppid=#{status.pid}"
else
expected = "hehe pid=#{status.pid} ppid=#{$$}"
@@ -864,14 +1225,14 @@ class TestProcess < Test::Unit::TestCase
ret = system("#{RUBY} script1 || #{RUBY} script2")
status = $?
assert_equal(false, ret)
- assert(status.exited?)
+ assert_predicate(status, :exited?)
result1 = File.read("result1")
result2 = File.read("result2")
assert_match(/\Ataka pid=\d+ ppid=\d+\z/, result1)
assert_match(/\Ataki pid=\d+ ppid=\d+\z/, result2)
assert_not_equal(result1[/\d+/].to_i, status.pid)
- if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
+ if windows?
Dir.mkdir(path = "path with space")
write_file(bat = path + "/bat test.bat", "@echo %1>out")
system(bat, "foo 'bar'")
@@ -895,28 +1256,28 @@ class TestProcess < Test::Unit::TestCase
pid = spawn("#{RUBY} script1 || #{RUBY} script2")
Process.wait pid
status = $?
- assert(status.exited?)
- assert(!status.success?)
+ assert_predicate(status, :exited?)
+ assert_not_predicate(status, :success?)
result1 = File.read("result1")
result2 = File.read("result2")
assert_match(/\Ataku pid=\d+ ppid=\d+\z/, result1)
assert_match(/\Atake pid=\d+ ppid=\d+\z/, result2)
assert_not_equal(result1[/\d+/].to_i, status.pid)
- if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
+ if windows?
Dir.mkdir(path = "path with space")
write_file(bat = path + "/bat test.bat", "@echo %1>out")
pid = spawn(bat, "foo 'bar'")
Process.wait pid
status = $?
- assert(status.exited?)
- assert(status.success?)
+ assert_predicate(status, :exited?)
+ assert_predicate(status, :success?)
assert_equal(%["foo 'bar'"\n], File.read("out"), '[ruby-core:22960]')
pid = spawn(%[#{bat.dump} "foo 'bar'"])
Process.wait pid
status = $?
- assert(status.exited?)
- assert(status.success?)
+ assert_predicate(status, :exited?)
+ assert_predicate(status, :success?)
assert_equal(%["foo 'bar'"\n], File.read("out"), '[ruby-core:22960]')
end
}
@@ -936,12 +1297,12 @@ class TestProcess < Test::Unit::TestCase
result = io.read
io.close
status = $?
- assert(status.exited?)
- assert(!status.success?)
+ assert_predicate(status, :exited?)
+ assert_not_predicate(status, :success?)
assert_match(/\Atako pid=\d+ ppid=\d+\ntika pid=\d+ ppid=\d+\n\z/, result)
assert_not_equal(result[/\d+/].to_i, status.pid)
- if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
+ if windows?
Dir.mkdir(path = "path with space")
write_file(bat = path + "/bat test.bat", "@echo %1")
r = IO.popen([bat, "foo 'bar'"]) {|f| f.read}
@@ -969,8 +1330,8 @@ class TestProcess < Test::Unit::TestCase
pid = spawn RUBY, "s"
Process.wait pid
status = $?
- assert(status.exited?)
- assert(!status.success?)
+ assert_predicate(status, :exited?)
+ assert_not_predicate(status, :success?)
result1 = File.read("result1")
result2 = File.read("result2")
assert_match(/\Atiki pid=\d+ ppid=\d+\z/, result1)
@@ -987,8 +1348,7 @@ class TestProcess < Test::Unit::TestCase
Process.wait spawn([RUBY, "poiu"], "-e", "exit 4")
assert_equal(4, $?.exitstatus)
- assert_equal("1", IO.popen([[RUBY, "qwerty"], "-e", "print 1"]).read)
- Process.wait
+ assert_equal("1", IO.popen([[RUBY, "qwerty"], "-e", "print 1"]) {|f| f.read })
write_file("s", <<-"End")
exec([#{RUBY.dump}, "lkjh"], "-e", "exit 5")
@@ -1024,22 +1384,30 @@ class TestProcess < Test::Unit::TestCase
with_stdin("f") { assert_equal(false, system([RUBY, "wsx"])) }
with_stdin("t") { Process.wait spawn([RUBY, "edc"]) }
- assert($?.success?)
+ assert_predicate($?, :success?)
with_stdin("f") { Process.wait spawn([RUBY, "rfv"]) }
- assert(!$?.success?)
+ assert_not_predicate($?, :success?)
with_stdin("t") { IO.popen([[RUBY, "tgb"]]) {|io| assert_equal("", io.read) } }
- assert($?.success?)
+ assert_predicate($?, :success?)
with_stdin("f") { IO.popen([[RUBY, "yhn"]]) {|io| assert_equal("", io.read) } }
- assert(!$?.success?)
+ assert_not_predicate($?, :success?)
status = run_in_child "STDIN.reopen('t'); exec([#{RUBY.dump}, 'ujm'])"
- assert(status.success?)
+ assert_predicate(status, :success?)
status = run_in_child "STDIN.reopen('f'); exec([#{RUBY.dump}, 'ik,'])"
- assert(!status.success?)
+ assert_not_predicate(status, :success?)
}
end
+ def test_argv0_keep_alive
+ assert_in_out_err([], <<~REPRO, ['-'], [], "[Bug #15887]")
+ $0 = "diverge"
+ 4.times { GC.start }
+ puts Process.argv0
+ REPRO
+ end
+
def test_status
with_tmpchdir do
s = run_in_child("exit 1")
@@ -1057,24 +1425,32 @@ class TestProcess < Test::Unit::TestCase
def test_status_kill
return unless Process.respond_to?(:kill)
- return unless Signal.list.include?("QUIT")
+ return unless Signal.list.include?("KILL")
+
+ # assume the system supports signal if SIGQUIT is available
+ expected = Signal.list.include?("QUIT") ? [false, true, false, nil] : [true, false, false, true]
with_tmpchdir do
- write_file("foo", "sleep 30")
- pid = spawn(RUBY, "foo")
- Thread.new { sleep 1; Process.kill(:SIGQUIT, pid) }
- Process.wait(pid)
+ write_file("foo", "Process.kill(:KILL, $$); exit(42)")
+ system(RUBY, "foo")
s = $?
- assert_equal([false, true, false],
- [s.exited?, s.signaled?, s.stopped?],
- "[s.exited?, s.signaled?, s.stopped?]")
- assert_send(
- [["#<Process::Status: pid #{ s.pid } SIGQUIT (signal #{ s.termsig })>",
- "#<Process::Status: pid #{ s.pid } SIGQUIT (signal #{ s.termsig }) (core dumped)>"],
- :include?,
- s.inspect])
- assert_equal(false, s.exited?)
- assert_equal(nil, s.success?)
+ assert_equal(expected,
+ [s.exited?, s.signaled?, s.stopped?, s.success?],
+ "[s.exited?, s.signaled?, s.stopped?, s.success?]")
+ end
+ end
+
+ def test_status_quit
+ return unless Process.respond_to?(:kill)
+ return unless Signal.list.include?("QUIT")
+
+ with_tmpchdir do
+ s = assert_in_out_err([], "Signal.trap(:QUIT,'DEFAULT'); Process.kill(:SIGQUIT, $$);sleep 30", //, //, rlimit_core: 0)
+ assert_equal([false, true, false, nil],
+ [s.exited?, s.signaled?, s.stopped?, s.success?],
+ "[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)/, ''))
end
end
@@ -1106,6 +1482,32 @@ class TestProcess < Test::Unit::TestCase
end
end
+ def test_wait_exception
+ bug11340 = '[ruby-dev:49176] [Bug #11340]'
+ t0 = t1 = nil
+ sec = 3
+ code = "puts;STDOUT.flush;Thread.start{gets;exit};sleep(#{sec})"
+ IO.popen([RUBY, '-e', code], 'r+') do |f|
+ pid = f.pid
+ f.gets
+ t0 = Time.now
+ th = Thread.start(Thread.current) do |main|
+ Thread.pass until main.stop?
+ main.raise Interrupt
+ end
+ begin
+ assert_raise(Interrupt) {Process.wait(pid)}
+ ensure
+ th.kill.join
+ end
+ t1 = Time.now
+ diff = t1 - t0
+ assert_operator(diff, :<, sec,
+ ->{"#{bug11340}: #{diff} seconds to interrupt Process.wait"})
+ f.puts
+ end
+ end
+
def test_abort
with_tmpchdir do
s = run_in_child("abort")
@@ -1115,6 +1517,9 @@ class TestProcess < Test::Unit::TestCase
def test_sleep
assert_raise(ArgumentError) { sleep(1, 1) }
+ [-1, -1.0, -1r].each do |sec|
+ assert_raise_with_message(ArgumentError, /not.*negative/) { sleep(sec) }
+ end
end
def test_getpgid
@@ -1148,31 +1553,60 @@ class TestProcess < Test::Unit::TestCase
end
def test_maxgroups
- assert_kind_of(Integer, Process.maxgroups)
+ max = Process.maxgroups
+ rescue NotImplementedError
+ else
+ assert_kind_of(Integer, max)
+ assert_predicate(max, :positive?)
+ skip "not limited to NGROUPS_MAX" if /darwin/ =~ RUBY_PLATFORM
+ gs = Process.groups
+ assert_operator(gs.size, :<=, max)
+ gs[0] ||= 0
+ assert_raise(ArgumentError) {Process.groups = gs * (max / gs.size + 1)}
end
def test_geteuid
+ assert_kind_of(Integer, Process.euid)
+ end
+
+ def test_seteuid
+ assert_nothing_raised(TypeError) {Process.euid += 0}
+ rescue NotImplementedError
+ end
+
+ def test_seteuid_name
+ user = (Etc.getpwuid(Process.euid).name rescue ENV["USER"]) or return
+ assert_nothing_raised(TypeError) {Process.euid = user}
+ rescue NotImplementedError
+ end
+
+ def test_getegid
assert_kind_of(Integer, Process.egid)
end
+ def test_setegid
+ assert_nothing_raised(TypeError) {Process.egid += 0}
+ rescue NotImplementedError
+ end
+
def test_uid_re_exchangeable_p
r = Process::UID.re_exchangeable?
- assert(true == r || false == r)
+ assert_include([true, false], r)
end
def test_gid_re_exchangeable_p
r = Process::GID.re_exchangeable?
- assert(true == r || false == r)
+ assert_include([true, false], r)
end
def test_uid_sid_available?
r = Process::UID.sid_available?
- assert(true == r || false == r)
+ assert_include([true, false], r)
end
def test_gid_sid_available?
r = Process::GID.sid_available?
- assert(true == r || false == r)
+ assert_include([true, false], r)
end
def test_pst_inspect
@@ -1180,16 +1614,34 @@ class TestProcess < Test::Unit::TestCase
end
def test_wait_and_sigchild
+ if /freebsd|openbsd/ =~ RUBY_PLATFORM
+ # this relates #4173
+ # When ruby can use 2 cores, signal and wait4 may miss the signal.
+ skip "this fails on FreeBSD and OpenBSD on multithreaded environment"
+ end
signal_received = []
- Signal.trap(:CHLD) { signal_received << true }
- pid = fork { sleep 1; exit }
- Thread.start { raise }
- Process.wait pid
- 5.times do
- sleep 1
- break unless signal_received.empty?
- end
- assert_equal [true], signal_received, " [ruby-core:19744]"
+ IO.pipe do |sig_r, sig_w|
+ Signal.trap(:CHLD) do
+ signal_received << true
+ sig_w.write('?')
+ end
+ pid = nil
+ IO.pipe do |r, w|
+ pid = fork { r.read(1); exit }
+ Thread.start {
+ Thread.current.report_on_exception = false
+ raise
+ }
+ w.puts
+ end
+ Process.wait pid
+ assert sig_r.wait_readable(5), 'self-pipe not readable'
+ end
+ if 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]"
+ end
rescue NotImplementedError, ArgumentError
ensure
begin
@@ -1199,20 +1651,799 @@ class TestProcess < Test::Unit::TestCase
end
def test_no_curdir
- if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
- skip "removing current directory is not supported"
- end
with_tmpchdir {|d|
Dir.mkdir("vd")
status = nil
Dir.chdir("vd") {
dir = "#{d}/vd"
# OpenSolaris cannot remove the current directory.
- system(RUBY, "-e", "Dir.chdir '..'; Dir.rmdir #{dir.dump}")
- system({"RUBYLIB"=>nil}, RUBY, "-e", "exit true")
+ system(RUBY, "--disable-gems", "-e", "Dir.chdir '..'; Dir.rmdir #{dir.dump}", err: File::NULL)
+ system({"RUBYLIB"=>nil}, RUBY, "--disable-gems", "-e", "exit true")
status = $?
}
- assert(status.success?, "[ruby-dev:38105]")
+ assert_predicate(status, :success?, "[ruby-dev:38105]")
+ }
+ end
+
+ def test_fallback_to_sh
+ feature = '[ruby-core:32745]'
+ with_tmpchdir do |d|
+ open("tmp_script.#{$$}", "w") {|f| f.puts ": ;"; f.chmod(0755)}
+ assert_not_nil(pid = Process.spawn("./tmp_script.#{$$}"), feature)
+ wpid, st = Process.waitpid2(pid)
+ assert_equal([pid, true], [wpid, st.success?], feature)
+
+ open("tmp_script.#{$$}", "w") {|f| f.puts "echo $#: $@"; f.chmod(0755)}
+ result = IO.popen(["./tmp_script.#{$$}", "a b", "c"]) {|f| f.read}
+ assert_equal("2: a b c\n", result, feature)
+
+ open("tmp_script.#{$$}", "w") {|f| f.puts "echo $hghg"; f.chmod(0755)}
+ result = IO.popen([{"hghg" => "mogomogo"}, "./tmp_script.#{$$}", "a b", "c"]) {|f| f.read}
+ assert_equal("mogomogo\n", result, feature)
+
+ end
+ end if File.executable?("/bin/sh")
+
+ def test_spawn_too_long_path
+ bug4314 = '[ruby-core:34842]'
+ assert_fail_too_long_path(%w"echo", bug4314)
+ end
+
+ def test_aspawn_too_long_path
+ if /solaris/i =~ RUBY_PLATFORM && !defined?(Process::RLIMIT_NPROC)
+ skip "Too exhaustive test on platforms without Process::RLIMIT_NPROC such as Solaris 10"
+ end
+ bug4315 = '[ruby-core:34833] #7904 [ruby-core:52628] #11613'
+ assert_fail_too_long_path(%w"echo |", bug4315)
+ end
+
+ def assert_fail_too_long_path((cmd, sep), mesg)
+ sep ||= ""
+ min = 1_000 / (cmd.size + sep.size)
+ cmds = Array.new(min, cmd)
+ exs = [Errno::ENOENT]
+ exs << Errno::E2BIG if defined?(Errno::E2BIG)
+ opts = {[STDOUT, STDERR]=>File::NULL}
+ opts[:rlimit_nproc] = 128 if defined?(Process::RLIMIT_NPROC)
+ EnvUtil.suppress_warning do
+ assert_raise(*exs, mesg) do
+ begin
+ loop do
+ Process.spawn(cmds.join(sep), opts)
+ min = [cmds.size, min].max
+ cmds *= 100
+ end
+ rescue NoMemoryError
+ size = cmds.size
+ raise if min >= size - 1
+ min = [min, size /= 2].max
+ cmds[size..-1] = []
+ raise if size < 250
+ retry
+ end
+ end
+ end
+ end
+
+ def test_system_sigpipe
+ return if windows?
+
+ pid = 0
+
+ with_tmpchdir do
+ assert_nothing_raised('[ruby-dev:12261]') do
+ Timeout.timeout(3) do
+ pid = spawn('yes | ls')
+ Process.waitpid pid
+ end
+ end
+ end
+ ensure
+ Process.kill(:KILL, pid) if (pid != 0) rescue false
+ end
+
+ if Process.respond_to?(:daemon)
+ def test_daemon_default
+ data = IO.popen("-", "r+") do |f|
+ break f.read if f
+ Process.daemon
+ puts "ng"
+ end
+ assert_equal("", data)
+ end
+
+ def test_daemon_noclose
+ data = IO.popen("-", "r+") do |f|
+ break f.read if f
+ Process.daemon(false, true)
+ puts "ok", Dir.pwd
+ end
+ assert_equal("ok\n/\n", data)
+ end
+
+ def test_daemon_nochdir_noclose
+ data = IO.popen("-", "r+") do |f|
+ break f.read if f
+ Process.daemon(true, true)
+ puts "ok", Dir.pwd
+ end
+ assert_equal("ok\n#{Dir.pwd}\n", data)
+ end
+
+ def test_daemon_readwrite
+ data = IO.popen("-", "r+") do |f|
+ if f
+ f.puts "ok?"
+ break f.read
+ end
+ Process.daemon(true, true)
+ puts STDIN.gets
+ end
+ assert_equal("ok?\n", data)
+ end
+
+ def test_daemon_pid
+ cpid, dpid = IO.popen("-", "r+") do |f|
+ break f.pid, Integer(f.read) if f
+ Process.daemon(false, true)
+ puts $$
+ end
+ assert_not_equal(cpid, dpid)
+ end
+
+ if File.directory?("/proc/self/task") && /netbsd[a-z]*[1-6]/ !~ RUBY_PLATFORM
+ def test_daemon_no_threads
+ pid, data = IO.popen("-", "r+") do |f|
+ break f.pid, f.readlines if f
+ Process.daemon(true, true)
+ puts Dir.entries("/proc/self/task") - %W[. ..]
+ end
+ bug4920 = '[ruby-dev:43873]'
+ assert_include(1..2, data.size, bug4920)
+ assert_not_include(data.map(&:to_i), pid)
+ end
+ else # darwin
+ def test_daemon_no_threads
+ data = Timeout.timeout(3) do
+ IO.popen("-") do |f|
+ break f.readlines.map(&:chomp) if f
+ th = Thread.start {sleep 3}
+ Process.daemon(true, true)
+ puts Thread.list.size, th.status.inspect
+ end
+ end
+ assert_equal(["1", "false"], data)
+ end
+ end
+ end
+
+ def test_popen_cloexec
+ return unless defined? Fcntl::FD_CLOEXEC
+ IO.popen([RUBY, "-e", ""]) {|io|
+ assert_predicate(io, :close_on_exec?)
+ }
+ end
+
+ def test_popen_exit
+ bug11510 = '[ruby-core:70671] [Bug #11510]'
+ pid = nil
+ opt = {timeout: 10, stdout_filter: ->(s) {pid = s}}
+ if windows?
+ opt[:new_pgroup] = true
+ else
+ opt[:pgroup] = true
+ end
+ assert_ruby_status(["-", RUBY], <<-'end;', bug11510, **opt)
+ RUBY = ARGV[0]
+ th = Thread.start {
+ Thread.current.abort_on_exception = true
+ IO.popen([RUBY, "-esleep 15", err: [:child, :out]]) {|f|
+ STDOUT.puts f.pid
+ STDOUT.flush
+ sleep(2)
+ }
+ }
+ sleep(0.001) until th.stop?
+ end;
+ assert_match(/\A\d+\Z/, pid)
+ ensure
+ if pid
+ pid = pid.to_i
+ [:TERM, :KILL].each {|sig| Process.kill(sig, pid) rescue break}
+ end
+ end
+
+ def test_popen_reopen
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ io = File.open(IO::NULL)
+ io2 = io.dup
+ IO.popen("echo") {|f| io.reopen(f)}
+ io.reopen(io2)
+ end;
+ end
+
+ def test_execopts_new_pgroup
+ return unless windows?
+
+ assert_nothing_raised { system(*TRUECOMMAND, :new_pgroup=>true) }
+ assert_nothing_raised { system(*TRUECOMMAND, :new_pgroup=>false) }
+ assert_nothing_raised { spawn(*TRUECOMMAND, :new_pgroup=>true) }
+ assert_nothing_raised { IO.popen([*TRUECOMMAND, :new_pgroup=>true]) {} }
+ end
+
+ def test_execopts_uid
+ feature6975 = '[ruby-core:47414]'
+
+ [30000, [Process.uid, ENV["USER"]]].each do |uid, user|
+ if user
+ assert_nothing_raised(feature6975) do
+ begin
+ system(*TRUECOMMAND, uid: user)
+ rescue Errno::EPERM, NotImplementedError
+ end
+ end
+ end
+
+ assert_nothing_raised(feature6975) do
+ begin
+ system(*TRUECOMMAND, uid: uid)
+ rescue Errno::EPERM, NotImplementedError
+ end
+ end
+
+ assert_nothing_raised(feature6975) do
+ begin
+ u = IO.popen([RUBY, "-e", "print Process.uid", uid: user||uid], &:read)
+ assert_equal(uid.to_s, u, feature6975)
+ rescue Errno::EPERM, NotImplementedError
+ end
+ end
+ end
+ end
+
+ def test_execopts_gid
+ skip "Process.groups not implemented on Windows platform" if windows?
+ feature6975 = '[ruby-core:47414]'
+
+ groups = Process.groups.map do |g|
+ g = Etc.getgrgid(g) rescue next
+ [g.name, g.gid]
+ end
+ groups.compact!
+ [30000, *groups].each do |group, gid|
+ assert_nothing_raised(feature6975) do
+ begin
+ system(*TRUECOMMAND, gid: group)
+ rescue Errno::EPERM, NotImplementedError
+ end
+ end
+
+ gid = "#{gid || group}"
+ assert_nothing_raised(feature6975) do
+ begin
+ g = IO.popen([RUBY, "-e", "print Process.gid", gid: group], &:read)
+ # AIX allows a non-root process to setgid to its supplementary group,
+ # while other UNIXes do not. (This might be AIX's violation of the POSIX standard.)
+ # However, Ruby does not allow a setgid'ed Ruby process to use the -e option.
+ # As a result, the Ruby process invoked by "IO.popen([RUBY, "-e", ..." above fails
+ # with a message like "no -e allowed while running setgid (SecurityError)" to stderr,
+ # the exis status is set to 1, and the variable "g" is set to an empty string.
+ # To conclude, on AIX, if the "gid" variable is a supplementary group,
+ # the assert_equal next can fail, so skip it.
+ assert_equal(gid, g, feature6975) unless $?.exitstatus == 1 && /aix/ =~ RUBY_PLATFORM && gid != Process.gid
+ rescue Errno::EPERM, NotImplementedError
+ end
+ end
+ end
+ end
+
+ def test_sigpipe
+ system(RUBY, "-e", "")
+ with_pipe {|r, w|
+ r.close
+ assert_raise(Errno::EPIPE) { w.print "a" }
+ }
+ end
+
+ def test_sh_comment
+ IO.popen("echo a # fofoof") {|f|
+ assert_equal("a\n", f.read)
+ }
+ end if File.executable?("/bin/sh")
+
+ def test_sh_env
+ IO.popen("foofoo=barbar env") {|f|
+ lines = f.readlines
+ assert_operator(lines, :include?, "foofoo=barbar\n")
+ }
+ end if File.executable?("/bin/sh")
+
+ def test_sh_exec
+ IO.popen("exec echo exexexec") {|f|
+ assert_equal("exexexec\n", f.read)
+ }
+ end if File.executable?("/bin/sh")
+
+ def test_setsid
+ return unless Process.respond_to?(:setsid)
+ return unless Process.respond_to?(:getsid)
+ # OpenBSD and AIX don't allow Process::getsid(pid) when pid is in
+ # different session.
+ return if /openbsd|aix/ =~ RUBY_PLATFORM
+
+ IO.popen([RUBY, "-e", <<EOS]) do|io|
+ Marshal.dump(Process.getsid, STDOUT)
+ newsid = Process.setsid
+ Marshal.dump(newsid, STDOUT)
+ STDOUT.flush
+ # getsid() on MacOS X return ESRCH when target process is zombie
+ # even if it is valid process id.
+ sleep
+EOS
+ begin
+ # test Process.getsid() w/o arg
+ assert_equal(Marshal.load(io), Process.getsid)
+
+ # test Process.setsid return value and Process::getsid(pid)
+ assert_equal(Marshal.load(io), Process.getsid(io.pid))
+ ensure
+ Process.kill(:KILL, io.pid) rescue nil
+ Process.wait(io.pid)
+ end
+ end
+ end
+
+ def test_spawn_nonascii
+ bug1771 = '[ruby-core:24309] [Bug #1771]'
+
+ with_tmpchdir do
+ [
+ "\u{7d05 7389}",
+ "zuf\u{00E4}llige_\u{017E}lu\u{0165}ou\u{010D}k\u{00FD}_\u{10D2 10D0 10DB 10D4 10DD 10E0 10D4 10D1}_\u{0440 0430 0437 043B 043E 0433 0430}_\u{548C 65B0 52A0 5761 4EE5 53CA 4E1C}",
+ "c\u{1EE7}a",
+ ].each do |name|
+ msg = "#{bug1771} #{name}"
+ exename = "./#{name}.exe"
+ FileUtils.cp(ENV["COMSPEC"], exename)
+ assert_equal(true, system("#{exename} /c exit"), msg)
+ system("#{exename} /c exit 12")
+ assert_equal(12, $?.exitstatus, msg)
+ _, status = Process.wait2(Process.spawn("#{exename} /c exit 42"))
+ assert_equal(42, status.exitstatus, msg)
+ assert_equal("ok\n", `#{exename} /c echo ok`, msg)
+ assert_equal("ok\n", IO.popen("#{exename} /c echo ok", &:read), msg)
+ assert_equal("ok\n", IO.popen(%W"#{exename} /c echo ok", &:read), msg)
+ File.binwrite("#{name}.txt", "ok")
+ assert_equal("ok", `type #{name}.txt`)
+ end
+ end
+ end if windows?
+
+ def test_exec_nonascii
+ bug12841 = '[ruby-dev:49838] [Bug #12841]'
+
+ [
+ "\u{7d05 7389}",
+ "zuf\u{00E4}llige_\u{017E}lu\u{0165}ou\u{010D}k\u{00FD}_\u{10D2 10D0 10DB 10D4 10DD 10E0 10D4 10D1}_\u{0440 0430 0437 043B 043E 0433 0430}_\u{548C 65B0 52A0 5761 4EE5 53CA 4E1C}",
+ "c\u{1EE7}a",
+ ].each do |arg|
+ begin
+ arg = arg.encode(Encoding.find("locale"))
+ rescue
+ else
+ assert_in_out_err([], "#{<<-"begin;"}\n#{<<-"end;"}", [arg], [], bug12841)
+ begin;
+ arg = "#{arg.b}".force_encoding("#{arg.encoding.name}")
+ exec(ENV["COMSPEC"]||"cmd.exe", "/c", "echo", arg)
+ end;
+ end
+ end
+ end if windows?
+
+ def test_clock_gettime
+ t1 = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
+ t2 = Time.now; t2 = t2.tv_sec * 1000000000 + t2.tv_nsec
+ t3 = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
+ assert_operator(t1, :<=, t2)
+ assert_operator(t2, :<=, t3)
+ assert_raise(Errno::EINVAL) { Process.clock_gettime(:foo) }
+ end
+
+ def test_clock_gettime_unit
+ t0 = Time.now.to_f
+ [
+ [:nanosecond, 1_000_000_000],
+ [:microsecond, 1_000_000],
+ [:millisecond, 1_000],
+ [:second, 1],
+ [:float_microsecond, 1_000_000.0],
+ [:float_millisecond, 1_000.0],
+ [:float_second, 1.0],
+ [nil, 1.0],
+ [:foo],
+ ].each do |unit, num|
+ unless num
+ assert_raise(ArgumentError){ Process.clock_gettime(Process::CLOCK_REALTIME, unit) }
+ next
+ end
+ t1 = Process.clock_gettime(Process::CLOCK_REALTIME, unit)
+ assert_kind_of num.integer? ? Integer : num.class, t1, [unit, num].inspect
+ assert_in_delta t0, t1/num, 1, [unit, num].inspect
+ end
+ end
+
+ def test_clock_gettime_constants
+ Process.constants.grep(/\ACLOCK_/).each {|n|
+ c = Process.const_get(n)
+ begin
+ t = Process.clock_gettime(c)
+ rescue Errno::EINVAL
+ next
+ end
+ assert_kind_of(Float, t, "Process.clock_gettime(Process::#{n})")
}
end
+
+ def test_clock_gettime_GETTIMEOFDAY_BASED_CLOCK_REALTIME
+ n = :GETTIMEOFDAY_BASED_CLOCK_REALTIME
+ begin
+ t = Process.clock_gettime(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
+ end
+
+ def test_clock_gettime_TIME_BASED_CLOCK_REALTIME
+ n = :TIME_BASED_CLOCK_REALTIME
+ t = Process.clock_gettime(n)
+ assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
+ end
+
+ def test_clock_gettime_TIMES_BASED_CLOCK_MONOTONIC
+ n = :TIMES_BASED_CLOCK_MONOTONIC
+ begin
+ t = Process.clock_gettime(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
+ end
+
+ def test_clock_gettime_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
+ n = :GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
+ begin
+ t = Process.clock_gettime(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
+ end
+
+ def test_clock_gettime_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
+ n = :TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
+ begin
+ t = Process.clock_gettime(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
+ end
+
+ def test_clock_gettime_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
+ n = :CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
+ t = Process.clock_gettime(n)
+ assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
+ end
+
+ def test_clock_gettime_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
+ n = :MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
+ begin
+ t = Process.clock_gettime(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_gettime(:#{n})")
+ end
+
+ def test_clock_getres
+ r = Process.clock_getres(Process::CLOCK_REALTIME, :nanosecond)
+ rescue Errno::EINVAL
+ else
+ assert_kind_of(Integer, r)
+ assert_raise(Errno::EINVAL) { Process.clock_getres(:foo) }
+ end
+
+ def test_clock_getres_constants
+ Process.constants.grep(/\ACLOCK_/).each {|n|
+ c = Process.const_get(n)
+ begin
+ t = Process.clock_getres(c)
+ rescue Errno::EINVAL
+ next
+ end
+ assert_kind_of(Float, t, "Process.clock_getres(Process::#{n})")
+ }
+ end
+
+ def test_clock_getres_GETTIMEOFDAY_BASED_CLOCK_REALTIME
+ n = :GETTIMEOFDAY_BASED_CLOCK_REALTIME
+ begin
+ t = Process.clock_getres(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
+ assert_equal(1000, Process.clock_getres(n, :nanosecond))
+ end
+
+ def test_clock_getres_TIME_BASED_CLOCK_REALTIME
+ n = :TIME_BASED_CLOCK_REALTIME
+ t = Process.clock_getres(n)
+ assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
+ assert_equal(1000000000, Process.clock_getres(n, :nanosecond))
+ end
+
+ def test_clock_getres_TIMES_BASED_CLOCK_MONOTONIC
+ n = :TIMES_BASED_CLOCK_MONOTONIC
+ begin
+ t = Process.clock_getres(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
+ f = Process.clock_getres(n, :hertz)
+ assert_equal(0, f - f.floor)
+ end
+
+ def test_clock_getres_GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
+ n = :GETRUSAGE_BASED_CLOCK_PROCESS_CPUTIME_ID
+ begin
+ t = Process.clock_getres(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
+ assert_equal(1000, Process.clock_getres(n, :nanosecond))
+ end
+
+ def test_clock_getres_TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
+ n = :TIMES_BASED_CLOCK_PROCESS_CPUTIME_ID
+ begin
+ t = Process.clock_getres(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
+ f = Process.clock_getres(n, :hertz)
+ assert_equal(0, f - f.floor)
+ end
+
+ def test_clock_getres_CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
+ n = :CLOCK_BASED_CLOCK_PROCESS_CPUTIME_ID
+ t = Process.clock_getres(n)
+ assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
+ f = Process.clock_getres(n, :hertz)
+ assert_equal(0, f - f.floor)
+ end
+
+ def test_clock_getres_MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
+ n = :MACH_ABSOLUTE_TIME_BASED_CLOCK_MONOTONIC
+ begin
+ t = Process.clock_getres(n)
+ rescue Errno::EINVAL
+ return
+ end
+ assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
+ end
+
+ def test_deadlock_by_signal_at_forking
+ assert_separately(%W(--disable=gems - #{RUBY}), <<-INPUT, timeout: 100)
+ ruby = ARGV.shift
+ GC.start # reduce garbage
+ GC.disable # avoid triggering CoW after forks
+ trap(:QUIT) {}
+ parent = $$
+ 100.times do |i|
+ 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)
+
+ def test_process_detach
+ pid = fork {}
+ th = Process.detach(pid)
+ assert_equal pid, th.pid
+ status = th.value
+ assert status.success?, status.inspect
+ end if defined?(fork)
+
+ def test_kill_at_spawn_failure
+ bug11166 = '[ruby-core:69304] [Bug #11166]'
+ th = nil
+ x = with_tmpchdir {|d|
+ prog = "#{d}/notexist"
+ th = Thread.start {system(prog);sleep}
+ th.kill
+ th.join(0.1)
+ }
+ assert_equal(th, x, bug11166)
+ end if defined?(fork)
+
+ def test_exec_fd_3_redirect
+ # ensure we can redirect anything to fd=3 in a child process.
+ # fd=3 is a commonly reserved FD for the timer thread pipe in the
+ # parent, but fd=3 is the first FD used by the sd_listen_fds function
+ # for systemd
+ assert_separately(['-', RUBY], <<-INPUT, timeout: 60)
+ ruby = ARGV.shift
+ begin
+ a = IO.pipe
+ b = IO.pipe
+ pid = fork do
+ exec ruby, '-e', 'print IO.for_fd(3).read(1)', 3 => a[0], 1 => b[1]
+ end
+ b[1].close
+ a[0].close
+ a[1].write('.')
+ assert_equal ".", b[0].read(1)
+ ensure
+ Process.wait(pid) if pid
+ a.each(&:close) if a
+ b.each(&:close) if b
+ end
+ INPUT
+ end if defined?(fork)
+
+ def test_exec_close_reserved_fd
+ cmd = ".#{File::ALT_SEPARATOR || File::SEPARATOR}bug11353"
+ with_tmpchdir {
+ (3..6).each do |i|
+ ret = run_in_child(<<-INPUT)
+ begin
+ $VERBOSE = nil
+ Process.exec('#{cmd}', 'dummy', #{i} => :close)
+ rescue SystemCallError
+ end
+ INPUT
+ assert_equal(0, ret)
+ end
+ }
+ end
+
+ def test_signals_work_after_exec_fail
+ r, w = IO.pipe
+ pid = status = nil
+ Timeout.timeout(30) do
+ pid = fork do
+ r.close
+ begin
+ trap(:USR1) { w.syswrite("USR1\n"); exit 0 }
+ exec "/path/to/non/existent/#$$/#{rand}.ex"
+ rescue SystemCallError
+ w.syswrite("exec failed\n")
+ end
+ sleep
+ exit 1
+ end
+ w.close
+ assert_equal "exec failed\n", r.gets
+ Process.kill(:USR1, pid)
+ assert_equal "USR1\n", r.gets
+ assert_nil r.gets
+ _, status = Process.waitpid2(pid)
+ end
+ assert_predicate status, :success?
+ rescue Timeout::Error
+ begin
+ Process.kill(:KILL, pid)
+ rescue Errno::ESRCH
+ end
+ raise
+ ensure
+ w.close if w
+ r.close if r
+ end if defined?(fork)
+
+ def test_threading_works_after_exec_fail
+ r, w = IO.pipe
+ pid = status = nil
+ Timeout.timeout(90) do
+ pid = fork do
+ r.close
+ begin
+ exec "/path/to/non/existent/#$$/#{rand}.ex"
+ rescue SystemCallError
+ w.syswrite("exec failed\n")
+ end
+ q = Queue.new
+ run = true
+ 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
+ q << true
+ w.syswrite "#{th1.value} #{th2.value}\n"
+ end
+ w.close
+ assert_equal "exec failed\n", r.gets
+ vals = r.gets.chomp.split.map!(&:to_i)
+ assert_operator vals[0], :>, vals[1], vals.inspect
+ _, status = Process.waitpid2(pid)
+ end
+ assert_predicate status, :success?
+ rescue Timeout::Error
+ begin
+ Process.kill(:KILL, pid)
+ rescue Errno::ESRCH
+ end
+ raise
+ ensure
+ w.close if w
+ 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)
+ bin = "#{EnvUtil.rubybin}"
+ args = Array.new(256) {"x"}
+ GC.stress = true
+ system(bin, "--disable=gems", "-w", "-e", "puts ARGV", *args)
+ end;
+ end
+
+ def test_to_hash_on_arguments
+ all_assertions do |a|
+ %w[Array String].each do |type|
+ a.for(type) do
+ assert_separately(['-', EnvUtil.rubybin], <<~"END;")
+ class #{type}
+ def to_hash
+ raise "[Bug-12355]: #{type}#to_hash is called"
+ end
+ end
+ ex = ARGV[0]
+ assert_equal(true, system([ex, ex], "-e", ""))
+ END;
+ end
+ end
+ end
+ end
+
+ def test_forked_child_handles_signal
+ skip "fork not supported" unless Process.respond_to?(:fork)
+ assert_normal_exit(<<-"end;", '[ruby-core:82883] [Bug #13916]')
+ require 'timeout'
+ pid = fork { sleep }
+ Process.kill(:TERM, pid)
+ assert_equal pid, Timeout.timeout(30) { Process.wait(pid) }
+ end;
+ end
+
+ if Process.respond_to?(:initgroups)
+ def test_initgroups
+ assert_raise(ArgumentError) do
+ Process.initgroups("\0", 0)
+ end
+ end
+ end
+
+ def test_last_status
+ Process.wait spawn(RUBY, "-e", "exit 13")
+ assert_same(Process.last_status, $?)
+ end
end
diff --git a/test/ruby/test_rand.rb b/test/ruby/test_rand.rb
index e22e532c53..ab9a1837f6 100644
--- a/test/ruby/test_rand.rb
+++ b/test/ruby/test_rand.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestRand < Test::Unit::TestCase
@@ -170,11 +171,13 @@ class TestRand < Test::Unit::TestCase
def test_shuffle
srand(0)
- assert_equal([1,4,2,5,3], [1,2,3,4,5].shuffle)
+ result = [*1..5].shuffle
+ assert_equal([*1..5], result.sort)
+ assert_equal(result, [*1..5].shuffle(random: Random.new(0)))
end
def test_big_seed
- assert_random_int(%w(1143843490), 0x100000000, 2**1000000-1)
+ assert_random_int(%w(2757555016), 0x100000000, 2**1000000-1)
end
def test_random_gc
@@ -207,7 +210,8 @@ class TestRand < Test::Unit::TestCase
assert_raise(ArgumentError, '[ruby-dev:39166]') { r.rand(0..-1) }
assert_raise(ArgumentError, '[ruby-dev:39166]') { r.rand(0.0...0.0) }
assert_raise(ArgumentError, '[ruby-dev:39166]') { r.rand(0.0...-0.1) }
- assert_raise(ArgumentError, bug3027 = '[ruby-core:29075]') { r.rand(nil) }
+ bug3027 = '[ruby-core:29075]'
+ assert_raise(ArgumentError, bug3027) { r.rand(nil) }
end
def test_random_seed
@@ -339,26 +343,49 @@ END
end
def test_random_bytes
- r = Random.new(0)
+ assert_random_bytes(Random.new(0))
+ end
+
+ def assert_random_bytes(r)
+ srand(0)
assert_equal("", r.bytes(0))
- assert_equal("\xAC".force_encoding("ASCII-8BIT"), r.bytes(1))
- assert_equal("/\xAA\xC4\x97u\xA6\x16\xB7\xC0\xCC".force_encoding("ASCII-8BIT"), r.bytes(10))
+ assert_equal("", Random.bytes(0))
+ x = "\xAC".force_encoding("ASCII-8BIT")
+ assert_equal(x, r.bytes(1))
+ 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))
+ 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, r.rand(5..9)) }
- %w(-237 731 383).each {|w| assert_equal(w.to_i, r.rand(-1000..1000)) }
+ %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_includes(3.1..4, v)
+ assert_include(3.1..4, v)
now = Time.now
+ assert_equal(now, rand(now..now))
assert_equal(now, r.rand(now..now))
end
@@ -372,36 +399,84 @@ 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) }
+ 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]')
+
+ assert_nothing_raised {r.rand(-Float::MAX..Float::MAX)}
end
def test_random_equal
r = Random.new(0)
- assert(r == r)
- assert(r == r.dup)
+ assert_equal(r, r)
+ assert_equal(r, r.dup)
r1 = r.dup
r2 = r.dup
r1.rand(0x100)
- assert(r1 != r2)
+ assert_not_equal(r1, r2)
r2.rand(0x100)
- assert(r1 == r2)
+ assert_equal(r1, r2)
end
def test_fork_shuffle
pid = fork do
- (1..10).to_a.shuffle
- raise 'default seed is not set' if srand == 0
+ (1..10).to_a.shuffle
+ raise 'default seed is not set' if srand == 0
end
- p2, st = Process.waitpid2(pid)
- assert(st.success?, "#{st.inspect}")
+ _, st = Process.waitpid2(pid)
+ assert_predicate(st, :success?, "#{st.inspect}")
rescue NotImplementedError, ArgumentError
end
+ def assert_fork_status(n, mesg, &block)
+ IO.pipe do |r, w|
+ (1..n).map do
+ st = desc = nil
+ IO.pipe do |re, we|
+ p1 = fork {
+ re.close
+ STDERR.reopen(we)
+ w.puts(block.call.to_s)
+ }
+ we.close
+ err = Thread.start {re.read}
+ _, st = Process.waitpid2(p1)
+ desc = FailDesc[st, mesg, err.value]
+ end
+ assert(!st.signaled?, desc)
+ assert(st.success?, mesg)
+ r.gets.strip
+ end
+ end
+ end
+
+ def test_rand_reseed_on_fork
+ GC.start
+ bug5661 = '[ruby-core:41209]'
+
+ assert_fork_status(1, bug5661) {Random.rand(4)}
+ r1, r2 = *assert_fork_status(2, bug5661) {Random.rand}
+ assert_not_equal(r1, r2, bug5661)
+
+ assert_fork_status(1, bug5661) {rand(4)}
+ r1, r2 = *assert_fork_status(2, bug5661) {rand}
+ assert_not_equal(r1, r2, bug5661)
+
+ stable = Random.new
+ assert_fork_status(1, bug5661) {stable.rand(4)}
+ r1, r2 = *assert_fork_status(2, bug5661) {stable.rand}
+ assert_equal(r1, r2, bug5661)
+
+ assert_fork_status(1, '[ruby-core:82100] [Bug #13753]') do
+ Random::DEFAULT.rand(4)
+ end
+ rescue NotImplementedError
+ end
+
def test_seed
bug3104 = '[ruby-core:29292]'
rand_1 = Random.new(-1).rand
@@ -415,4 +490,104 @@ END
assert_equal(r1, r2)
}
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) {
+ Random.new.__send__(:marshal_load, 0)
+ }
+ end
+
+ def test_initialize_frozen
+ r = Random.new(0)
+ r.freeze
+ assert_raise(FrozenError, '[Bug #6540]') do
+ r.__send__(:initialize, r)
+ end
+ end
+
+ def test_marshal_load_frozen
+ r = Random.new(0)
+ d = r.__send__(:marshal_dump)
+ r.freeze
+ assert_raise(FrozenError, '[Bug #6540]') do
+ r.__send__(:marshal_load, d)
+ end
+ end
+
+ def test_random_ulong_limited
+ def (gen = Object.new).rand(*) 1 end
+ assert_equal([2], (1..100).map {[1,2,3].sample(random: gen)}.uniq)
+
+ def (gen = Object.new).rand(*) 100 end
+ assert_raise_with_message(RangeError, /big 100\z/) {[1,2,3].sample(random: gen)}
+
+ bug7903 = '[ruby-dev:47061] [Bug #7903]'
+ def (gen = Object.new).rand(*) -1 end
+ assert_raise_with_message(RangeError, /small -1\z/, bug7903) {[1,2,3].sample(random: gen)}
+
+ bug7935 = '[ruby-core:52779] [Bug #7935]'
+ class << (gen = Object.new)
+ def rand(limit) @limit = limit; 0 end
+ attr_reader :limit
+ end
+ [1, 2].sample(1, random: gen)
+ assert_equal(2, gen.limit, bug7935)
+ end
+
+ def test_random_ulong_limited_no_rand
+ c = Class.new do
+ undef rand
+ def bytes(n)
+ "\0"*n
+ end
+ end
+ gen = c.new.extend(Random::Formatter)
+ assert_equal(1, [1, 2].sample(random: gen))
+ end
+
+ def test_default_seed
+ assert_separately([], <<-End)
+ seed = Random::DEFAULT::seed
+ rand1 = Random::DEFAULT::rand
+ rand2 = Random.new(seed).rand
+ assert_equal(rand1, rand2)
+
+ srand seed
+ rand3 = rand
+ assert_equal(rand1, rand3)
+ End
+ end
+
+ def test_urandom
+ [0, 1, 100].each do |size|
+ v = Random.urandom(size)
+ assert_kind_of(String, v)
+ assert_equal(size, v.bytesize)
+ end
+ end
+
+ def test_new_seed
+ size = 0
+ n = 8
+ n.times do
+ v = Random.new_seed
+ assert_kind_of(Integer, v)
+ size += v.size
+ end
+ # probability of failure <= 1/256**8
+ assert_operator(size.fdiv(n), :>, 15)
+ end
end
diff --git a/test/ruby/test_range.rb b/test/ruby/test_range.rb
index a2935955de..a7d34655b3 100644
--- a/test/ruby/test_range.rb
+++ b/test/ruby/test_range.rb
@@ -1,22 +1,51 @@
+# frozen_string_literal: false
require 'test/unit'
require 'delegate'
require 'timeout'
+require 'bigdecimal'
+require 'rbconfig/sizeof'
class TestRange < Test::Unit::TestCase
+ def test_new
+ assert_equal((0..2), Range.new(0, 2))
+ assert_equal((0..2), Range.new(0, 2, false))
+ assert_equal((0...2), Range.new(0, 2, true))
+
+ assert_raise(ArgumentError) { (1.."3") }
+
+ assert_equal((0..nil), Range.new(0, nil, false))
+ assert_equal((0...nil), Range.new(0, nil, true))
+
+ obj = Object.new
+ def obj.<=>(other)
+ raise RuntimeError, "cmp"
+ end
+ assert_raise_with_message(RuntimeError, "cmp") { (obj..3) }
+ end
+
+ def test_frozen_initialize
+ r = Range.allocate
+ r.freeze
+ assert_raise(FrozenError){r.__send__(:initialize, 1, 2)}
+ end
+
def test_range_string
# XXX: Is this really the test of Range?
assert_equal([], ("a" ... "a").to_a)
assert_equal(["a"], ("a" .. "a").to_a)
assert_equal(["a"], ("a" ... "b").to_a)
assert_equal(["a", "b"], ("a" .. "b").to_a)
+ assert_equal([*"a".."z", "aa"], ("a"..).take(27))
end
def test_range_numeric_string
assert_equal(["6", "7", "8"], ("6".."8").to_a, "[ruby-talk:343187]")
assert_equal(["6", "7"], ("6"..."8").to_a)
assert_equal(["9", "10"], ("9".."10").to_a)
+ assert_equal(["9", "10"], ("9"..).take(2))
assert_equal(["09", "10"], ("09".."10").to_a, "[ruby-dev:39361]")
assert_equal(["9", "10"], (SimpleDelegator.new("9").."10").to_a)
+ assert_equal(["9", "10"], (SimpleDelegator.new("9")..).take(2))
assert_equal(["9", "10"], ("9"..SimpleDelegator.new("10")).to_a)
end
@@ -51,33 +80,51 @@ class TestRange < Test::Unit::TestCase
assert_equal(1, (1..2).min)
assert_equal(nil, (2..1).min)
assert_equal(1, (1...2).min)
+ assert_equal(1, (1..).min)
assert_equal(1.0, (1.0..2.0).min)
assert_equal(nil, (2.0..1.0).min)
assert_equal(1, (1.0...2.0).min)
+ assert_equal(1, (1.0..).min)
assert_equal(0, (0..0).min)
assert_equal(nil, (0...0).min)
+
+ 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) { (0..).min {|a, b| a <=> b } }
end
def test_max
assert_equal(2, (1..2).max)
assert_equal(nil, (2..1).max)
assert_equal(1, (1...2).max)
+ assert_raise(RangeError) { (1..).max }
+ assert_raise(RangeError) { (1...).max }
assert_equal(2.0, (1.0..2.0).max)
assert_equal(nil, (2.0..1.0).max)
assert_raise(TypeError) { (1.0...2.0).max }
+ assert_raise(TypeError) { (1...1.5).max }
+ assert_raise(TypeError) { (1.5...2).max }
assert_equal(-0x80000002, ((-0x80000002)...(-0x80000001)).max)
assert_equal(0, (0..0).max)
assert_equal(nil, (0...0).max)
+
+ assert_equal([10,9,8], (0..10).max(3))
+ assert_equal([9,8,7], (0...10).max(3))
+ assert_raise(RangeError) { (1..).max(3) }
+ assert_raise(RangeError) { (1...).max(3) }
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 } }
end
def test_uninitialized_range
@@ -87,41 +134,76 @@ class TestRange < Test::Unit::TestCase
assert_nothing_raised { r.instance_eval { initialize 5, 6} }
end
+ def test_marshal
+ r = 1..2
+ assert_equal(r, Marshal.load(Marshal.dump(r)))
+ r = 1...2
+ assert_equal(r, Marshal.load(Marshal.dump(r)))
+ r = (1..)
+ assert_equal(r, Marshal.load(Marshal.dump(r)))
+ r = (1...)
+ assert_equal(r, Marshal.load(Marshal.dump(r)))
+ end
+
def test_bad_value
assert_raise(ArgumentError) { (1 .. :a) }
end
def test_exclude_end
- assert(!((0..1).exclude_end?))
- assert((0...1).exclude_end?)
+ assert_not_predicate(0..1, :exclude_end?)
+ assert_predicate(0...1, :exclude_end?)
+ assert_not_predicate(0.., :exclude_end?)
+ assert_predicate(0..., :exclude_end?)
end
def test_eq
r = (0..1)
- assert(r == r)
- assert(r == (0..1))
- assert(r != 0)
- assert(r != (1..2))
- assert(r != (0..2))
- assert(r != (0...1))
+ assert_equal(r, r)
+ assert_equal(r, (0..1))
+ assert_not_equal(r, 0)
+ assert_not_equal(r, (1..2))
+ assert_not_equal(r, (0..2))
+ assert_not_equal(r, (0...1))
+ assert_not_equal(r, (0..nil))
+ subclass = Class.new(Range)
+ assert_equal(r, subclass.new(0,1))
+
+ r = (0..nil)
+ assert_equal(r, r)
+ assert_equal(r, (0..nil))
+ assert_not_equal(r, 0)
+ assert_not_equal(r, (0...nil))
subclass = Class.new(Range)
- assert(r == subclass.new(0,1))
+ assert_equal(r, subclass.new(0,nil))
end
def test_eql
r = (0..1)
- assert(r.eql?(r))
- assert(r.eql?(0..1))
- assert(!r.eql?(0))
- assert(!r.eql?(1..2))
- assert(!r.eql?(0..2))
- assert(!r.eql?(0...1))
+ assert_operator(r, :eql?, r)
+ assert_operator(r, :eql?, 0..1)
+ assert_not_operator(r, :eql?, 0)
+ assert_not_operator(r, :eql?, 1..2)
+ assert_not_operator(r, :eql?, 0..2)
+ assert_not_operator(r, :eql?, 0...1)
subclass = Class.new(Range)
- assert(r.eql?(subclass.new(0,1)))
+ assert_operator(r, :eql?, subclass.new(0,1))
+
+ r = (0..nil)
+ assert_operator(r, :eql?, r)
+ assert_operator(r, :eql?, 0..nil)
+ assert_not_operator(r, :eql?, 0)
+ assert_not_operator(r, :eql?, 0...nil)
+ subclass = Class.new(Range)
+ assert_operator(r, :eql?, subclass.new(0,nil))
end
def test_hash
- assert((0..1).hash.is_a?(Fixnum))
+ assert_kind_of(Integer, (0..1).hash)
+ assert_equal((0..1).hash, (0..1).hash)
+ assert_not_equal((0..1).hash, (0...1).hash)
+ assert_equal((0..nil).hash, (0..nil).hash)
+ assert_not_equal((0..nil).hash, (0...nil).hash)
+ assert_kind_of(String, (0..1).hash.to_s)
end
def test_step
@@ -130,31 +212,73 @@ class TestRange < Test::Unit::TestCase
assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], a)
a = []
+ (0..).step {|x| a << x; break if a.size == 10 }
+ assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], a)
+
+ a = []
(0..10).step(2) {|x| a << x }
assert_equal([0, 2, 4, 6, 8, 10], a)
- assert_raise(ArgumentError) { (0..10).step(-1) { } }
+ a = []
+ (0..).step(2) {|x| a << x; break if a.size == 10 }
+ assert_equal([0, 2, 4, 6, 8, 10, 12, 14, 16, 18], a)
+
+ assert_kind_of(Enumerator::ArithmeticSequence, (0..10).step)
+ assert_kind_of(Enumerator::ArithmeticSequence, (0..10).step(2))
+ assert_kind_of(Enumerator::ArithmeticSequence, (0..10).step(0.5))
+ assert_kind_of(Enumerator::ArithmeticSequence, (10..0).step(-1))
+
assert_raise(ArgumentError) { (0..10).step(0) { } }
+ assert_raise(ArgumentError) { (0..).step(-1) { } }
+ assert_raise(ArgumentError) { (0..).step(0) { } }
a = []
("a" .. "z").step(2) {|x| a << x }
assert_equal(%w(a c e g i k m o q s u w y), a)
a = []
+ ("a" .. ).step(2) {|x| a << x; break if a.size == 13 }
+ assert_equal(%w(a c e g i k m o q s u w y), a)
+
+ a = []
("a" .. "z").step(2**32) {|x| a << x }
assert_equal(["a"], a)
a = []
+ (:a .. :z).step(2) {|x| a << x }
+ assert_equal(%i(a c e g i k m o q s u w y), a)
+
+ a = []
+ (:a .. ).step(2) {|x| a << x; break if a.size == 13 }
+ assert_equal(%i(a c e g i k m o q s u w y), a)
+
+ a = []
+ (:a .. :z).step(2**32) {|x| a << x }
+ assert_equal([:a], a)
+
+ a = []
(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) { } }
+ a = []
+ (2**32-1 .. ).step(2) {|x| a << x; break if a.size == 2 }
+ assert_equal([4294967295, 4294967297], a)
+
+ max = RbConfig::LIMITS["FIXNUM_MAX"]
+ a = []
+ (max..).step {|x| a << x; break if a.size == 2 }
+ assert_equal([max, max+1], a)
+ a = []
+ (max..).step(max) {|x| a << x; break if a.size == 4 }
+ assert_equal([max, 2*max, 3*max, 4*max], a)
o1 = Object.new
o2 = Object.new
def o1.<=>(x); -1; end
def o2.<=>(x); 0; end
assert_raise(TypeError) { (o1..o2).step(1) { } }
+ assert_raise(TypeError) { (o1..).step(1) { } }
class << o1; self; end.class_eval do
define_method(:succ) { o2 }
@@ -174,12 +298,47 @@ class TestRange < Test::Unit::TestCase
assert_equal([0, 0.5, 1.0, 1.5, 2.0], a)
a = []
+ (0..).step(0.5) {|x| a << x; break if a.size == 5 }
+ assert_equal([0, 0.5, 1.0, 1.5, 2.0], a)
+
+ a = []
(0x40000000..0x40000002).step(0.5) {|x| a << x }
assert_equal([1073741824, 1073741824.5, 1073741825.0, 1073741825.5, 1073741826], a)
o = Object.new
def o.to_int() 1 end
assert_nothing_raised("[ruby-dev:34558]") { (0..2).step(o) {|x| } }
+
+ o = Object.new
+ class << o
+ def to_str() "a" end
+ def <=>(other) to_str <=> other end
+ end
+
+ a = []
+ (o.."c").step(1) {|x| a << x}
+ assert_equal(["a", "b", "c"], a)
+ a = []
+ (o..).step(1) {|x| a << x; break if a.size >= 3}
+ assert_equal(["a", "b", "c"], a)
+ end
+
+ def test_percent_step
+ aseq = (1..10) % 2
+ assert_equal(Enumerator::ArithmeticSequence, aseq.class)
+ assert_equal(1, aseq.begin)
+ assert_equal(10, aseq.end)
+ assert_equal(2, aseq.step)
+ assert_equal([1, 3, 5, 7, 9], aseq.to_a)
+ end
+
+ def test_step_ruby_core_35753
+ assert_equal(6, (1...6.3).step.to_a.size)
+ assert_equal(5, (1.1...6).step.to_a.size)
+ assert_equal(5, (1...6).step(1.1).to_a.size)
+ assert_equal(3, (1.0...5.4).step(1.5).to_a.size)
+ assert_equal(3, (1.0...5.5).step(1.5).to_a.size)
+ assert_equal(4, (1.0...5.6).step(1.5).to_a.size)
end
def test_each
@@ -187,6 +346,10 @@ class TestRange < Test::Unit::TestCase
(0..10).each {|x| a << x }
assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], a)
+ a = []
+ (0..).each {|x| a << x; break if a.size == 10 }
+ assert_equal([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], a)
+
o1 = Object.new
o2 = Object.new
def o1.setcmp(v) @cmpresult = v end
@@ -227,48 +390,197 @@ class TestRange < Test::Unit::TestCase
a = []
r2.each {|x| a << x }
assert_equal([], a)
+
+ o = Object.new
+ class << o
+ def to_str() "a" end
+ def <=>(other) to_str <=> other end
+ end
+
+ a = []
+ (o.."c").each {|x| a << x}
+ assert_equal(["a", "b", "c"], a)
+ a = []
+ (o..).each {|x| a << x; break if a.size >= 3}
+ assert_equal(["a", "b", "c"], a)
end
def test_begin_end
assert_equal(0, (0..1).begin)
assert_equal(1, (0..1).end)
+ assert_equal(1, (0...1).end)
+ assert_equal(0, (0..nil).begin)
+ assert_equal(nil, (0..nil).end)
+ assert_equal(nil, (0...nil).end)
end
def test_first_last
assert_equal([0, 1, 2], (0..10).first(3))
assert_equal([8, 9, 10], (0..10).last(3))
+ assert_equal(0, (0..10).first)
+ assert_equal(10, (0..10).last)
+ assert_equal("a", ("a".."c").first)
+ assert_equal("c", ("a".."c").last)
+ assert_equal(0, (2..0).last)
+
+ assert_equal([0, 1, 2], (0...10).first(3))
+ assert_equal([7, 8, 9], (0...10).last(3))
+ assert_equal(0, (0...10).first)
+ assert_equal(10, (0...10).last)
+ assert_equal("a", ("a"..."c").first)
+ assert_equal("c", ("a"..."c").last)
+ assert_equal(0, (2...0).last)
+
+ assert_equal([0, 1, 2], (0..nil).first(3))
+ assert_equal(0, (0..nil).first)
+ assert_equal("a", ("a"..nil).first)
+ assert_raise(RangeError) { (0..nil).last }
+ assert_raise(RangeError) { (0..nil).last(3) }
end
def test_to_s
assert_equal("0..1", (0..1).to_s)
assert_equal("0...1", (0...1).to_s)
+ assert_equal("0..", (0..nil).to_s)
+ assert_equal("0...", (0...nil).to_s)
+
+ bug11767 = '[ruby-core:71811] [Bug #11767]'
+ assert_predicate(("0".taint.."1").to_s, :tainted?, bug11767)
+ assert_predicate(("0".."1".taint).to_s, :tainted?, bug11767)
+ assert_predicate(("0".."1").taint.to_s, :tainted?, bug11767)
end
def test_inspect
assert_equal("0..1", (0..1).inspect)
assert_equal("0...1", (0...1).inspect)
+ assert_equal("0..", (0..nil).inspect)
+ assert_equal("0...", (0...nil).inspect)
+
+ bug11767 = '[ruby-core:71811] [Bug #11767]'
+ assert_predicate(("0".taint.."1").inspect, :tainted?, bug11767)
+ assert_predicate(("0".."1".taint).inspect, :tainted?, bug11767)
+ assert_predicate(("0".."1").taint.inspect, :tainted?, bug11767)
end
def test_eqq
- assert((0..10) === 5)
- assert(!((0..10) === 11))
+ assert_operator(0..10, :===, 5)
+ assert_not_operator(0..10, :===, 11)
+ assert_operator(5..nil, :===, 11)
+ assert_not_operator(5..nil, :===, 0)
+ end
+
+ def test_eqq_time
+ bug11113 = '[ruby-core:69052] [Bug #11113]'
+ t = Time.now
+ assert_nothing_raised(TypeError, bug11113) {
+ assert_operator(t..(t+10), :===, t+5)
+ assert_operator(t.., :===, t+5)
+ assert_not_operator(t.., :===, t-5)
+ }
+ end
+
+ def test_eqq_non_linear
+ bug12003 = '[ruby-core:72908] [Bug #12003]'
+ c = Class.new {
+ attr_reader :value
+
+ def initialize(value)
+ @value = value
+ end
+
+ def succ
+ self.class.new(@value.succ)
+ end
+
+ def ==(other)
+ @value == other.value
+ end
+
+ def <=>(other)
+ @value <=> other.value
+ end
+ }
+ assert_operator(c.new(0)..c.new(10), :===, c.new(5), bug12003)
+ end
+
+ def test_eqq_non_iteratable
+ k = Class.new do
+ include Comparable
+ attr_reader :i
+ def initialize(i) @i = i; end
+ def <=>(o); i <=> o.i; end
+ end
+ assert_operator(k.new(0)..k.new(2), :===, k.new(1))
end
def test_include
- assert(("a".."z").include?("c"))
- assert(!(("a".."z").include?("5")))
- assert(("a"..."z").include?("y"))
- assert(!(("a"..."z").include?("z")))
- assert(!(("a".."z").include?("cc")))
- assert((0...10).include?(5))
+ assert_include("a".."z", "c")
+ assert_not_include("a".."z", "5")
+ assert_include("a"..."z", "y")
+ assert_not_include("a"..."z", "z")
+ assert_not_include("a".."z", "cc")
+ assert_include("a".., "c")
+ assert_not_include("a".., "5")
+ assert_include(0...10, 5)
+ assert_include(5..., 10)
+ assert_not_include(5..., 0)
end
def test_cover
- assert(("a".."z").cover?("c"))
- assert(!(("a".."z").cover?("5")))
- assert(("a"..."z").cover?("y"))
- assert(!(("a"..."z").cover?("z")))
- assert(("a".."z").cover?("cc"))
+ assert_operator("a".."z", :cover?, "c")
+ assert_not_operator("a".."z", :cover?, "5")
+ assert_operator("a"..."z", :cover?, "y")
+ assert_not_operator("a"..."z", :cover?, "z")
+ assert_operator("a".."z", :cover?, "cc")
+ assert_not_operator(5..., :cover?, 0)
+ assert_not_operator(5..., :cover?, "a")
+ assert_operator(5.., :cover?, 10)
+
+ assert_operator(2..5, :cover?, 2..5)
+ assert_operator(2...6, :cover?, 2...6)
+ assert_operator(2...6, :cover?, 2..5)
+ assert_operator(2..5, :cover?, 2...6)
+ assert_operator(2..5, :cover?, 2..4)
+ assert_operator(2..5, :cover?, 2...4)
+ assert_operator(2..5, :cover?, 2...5)
+ assert_operator(2..5, :cover?, 3..5)
+ assert_operator(2..5, :cover?, 3..4)
+ assert_operator(2..5, :cover?, 3...6)
+ assert_operator(2...6, :cover?, 2...5)
+ assert_operator(2...6, :cover?, 2..5)
+ assert_operator(2..6, :cover?, 2...6)
+ assert_operator(2.., :cover?, 2..)
+ assert_operator(2.., :cover?, 3..)
+ assert_operator(1.., :cover?, 1..10)
+ assert_operator(2.0..5.0, :cover?, 2..3)
+ assert_operator(2..5, :cover?, 2.0..3.0)
+ assert_operator(2..5, :cover?, 2.0...3.0)
+ assert_operator(2..5, :cover?, 2.0...5.0)
+ assert_operator(2.0..5.0, :cover?, 2.0...3.0)
+ assert_operator(2.0..5.0, :cover?, 2.0...5.0)
+ assert_operator('aa'..'zz', :cover?, 'aa'...'bb')
+
+ assert_not_operator(2..5, :cover?, 1..5)
+ assert_not_operator(2...6, :cover?, 1..5)
+ assert_not_operator(2..5, :cover?, 1...6)
+ assert_not_operator(1..3, :cover?, 1...6)
+ assert_not_operator(2..5, :cover?, 2..6)
+ assert_not_operator(2...6, :cover?, 2..6)
+ assert_not_operator(2...6, :cover?, 2...7)
+ assert_not_operator(2..3, :cover?, 1..4)
+ assert_not_operator(1..2, :cover?, 1.0..3.0)
+ assert_not_operator(1.0..2.9, :cover?, 1.0..3.0)
+ assert_not_operator(1..2, :cover?, 4..3)
+ assert_not_operator(2..1, :cover?, 1..2)
+ assert_not_operator(1...2, :cover?, 1...3)
+ assert_not_operator(2.., :cover?, 1..)
+ assert_not_operator(2.., :cover?, 1..10)
+ assert_not_operator(1..10, :cover?, 1..)
+ assert_not_operator(1..5, :cover?, 3..2)
+ assert_not_operator(1..10, :cover?, 3...2)
+ 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)
end
def test_beg_len
@@ -279,12 +591,15 @@ class TestRange < Test::Unit::TestCase
assert_raise(TypeError) { [][o] }
class << o; attr_accessor :end end
o.end = 0
- assert_raise(NoMethodError) { [][o] }
+ assert_raise(TypeError) { [][o] }
def o.exclude_end=(v) @exclude_end = v end
def o.exclude_end?() @exclude_end end
o.exclude_end = false
assert_nil([0][o])
assert_raise(RangeError) { [0][o] = 1 }
+ class << o
+ private :begin, :end
+ end
o.begin = 10
o.end = 10
assert_nil([0][o])
@@ -308,14 +623,14 @@ class TestRange < Test::Unit::TestCase
x = CyclicRange.allocate; x.send(:initialize, x, 1)
y = CyclicRange.allocate; y.send(:initialize, y, 1)
Timeout.timeout(1) {
- assert x == y
- assert x.eql? y
+ assert_equal x, y
+ assert_operator x, :eql?, y
}
z = CyclicRange.allocate; z.send(:initialize, z, :another)
Timeout.timeout(1) {
- assert x != z
- assert !x.eql?(z)
+ assert_not_equal x, z
+ assert_not_operator x, :eql?, z
}
x = CyclicRange.allocate
@@ -323,8 +638,8 @@ class TestRange < Test::Unit::TestCase
x.send(:initialize, y, 1)
y.send(:initialize, x, 1)
Timeout.timeout(1) {
- assert x == y
- assert x.eql?(y)
+ assert_equal x, y
+ assert_operator x, :eql?, y
}
x = CyclicRange.allocate
@@ -332,8 +647,235 @@ class TestRange < Test::Unit::TestCase
x.send(:initialize, z, 1)
z.send(:initialize, x, :other)
Timeout.timeout(1) {
- assert x != z
- assert !x.eql?(z)
+ assert_not_equal x, z
+ assert_not_operator x, :eql?, z
}
end
+
+ def test_size
+ assert_equal 42, (1..42).size
+ assert_equal 41, (1...42).size
+ assert_equal 6, (1...6.3).size
+ assert_equal 5, (1.1...6).size
+ assert_equal 42, (1..42).each.size
+ assert_nil ("a"..."z").size
+
+ assert_equal Float::INFINITY, (1...).size
+ assert_equal Float::INFINITY, (1.0...).size
+ assert_nil ("a"...).size
+ end
+
+ def test_bsearch_typechecks_return_values
+ assert_raise(TypeError) do
+ (1..42).bsearch{ "not ok" }
+ end
+ c = eval("class C\u{309a 26a1 26c4 1f300};self;end")
+ assert_raise_with_message(TypeError, /C\u{309a 26a1 26c4 1f300}/) do
+ (1..42).bsearch {c.new}
+ end
+ assert_equal (1..42).bsearch{}, (1..42).bsearch{false}
+ end
+
+ def test_bsearch_with_no_block
+ enum = (42...666).bsearch
+ assert_nil enum.size
+ assert_equal 200, enum.each{|x| x >= 200 }
+ end
+
+ def test_bsearch_for_other_numerics
+ assert_raise(TypeError) {
+ (Rational(-1,2)..Rational(9,4)).bsearch
+ }
+ assert_raise(TypeError) {
+ (BigDecimal('0.5')..BigDecimal('2.25')).bsearch
+ }
+ end
+
+ def test_bsearch_for_fixnum
+ ary = [3, 4, 7, 9, 12]
+ assert_equal(0, (0...ary.size).bsearch {|i| ary[i] >= 2 })
+ assert_equal(1, (0...ary.size).bsearch {|i| ary[i] >= 4 })
+ assert_equal(2, (0...ary.size).bsearch {|i| ary[i] >= 6 })
+ assert_equal(3, (0...ary.size).bsearch {|i| ary[i] >= 8 })
+ assert_equal(4, (0...ary.size).bsearch {|i| ary[i] >= 10 })
+ assert_equal(nil, (0...ary.size).bsearch {|i| ary[i] >= 100 })
+ assert_equal(0, (0...ary.size).bsearch {|i| true })
+ assert_equal(nil, (0...ary.size).bsearch {|i| false })
+
+ ary = [0, 100, 100, 100, 200]
+ assert_equal(1, (0...ary.size).bsearch {|i| ary[i] >= 100 })
+
+ assert_equal(1_000_001, (0...).bsearch {|i| i > 1_000_000 })
+ end
+
+ def test_bsearch_for_float
+ inf = Float::INFINITY
+ assert_in_delta(10.0, (0.0...100.0).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+ assert_in_delta(10.0, (0.0...inf).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+ assert_in_delta(-10.0, (-inf..100.0).bsearch {|x| x >= 0 || Math.log(-x / 10) < 0 }, 0.0001)
+ assert_in_delta(10.0, (-inf..inf).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+ assert_equal(nil, (-inf..5).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+
+ assert_in_delta(10.0, (-inf.. 10).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+ assert_equal(nil, (-inf...10).bsearch {|x| x > 0 && Math.log(x / 10) >= 0 }, 0.0001)
+
+ assert_equal(nil, (-inf..inf).bsearch { false })
+ assert_equal(-inf, (-inf..inf).bsearch { true })
+
+ assert_equal(inf, (0..inf).bsearch {|x| x == inf })
+ assert_equal(nil, (0...inf).bsearch {|x| x == inf })
+
+ v = (-inf..0).bsearch {|x| x != -inf }
+ assert_operator(-Float::MAX, :>=, v)
+ assert_operator(-inf, :<, v)
+
+ v = (0.0..1.0).bsearch {|x| x > 0 } # the nearest positive value to 0.0
+ assert_in_delta(0, v, 0.0001)
+ assert_operator(0, :<, v)
+ assert_equal(0.0, (-1.0..0.0).bsearch {|x| x >= 0 })
+ assert_equal(nil, (-1.0...0.0).bsearch {|x| x >= 0 })
+
+ v = (0..Float::MAX).bsearch {|x| x >= Float::MAX }
+ assert_in_delta(Float::MAX, v)
+ assert_equal(nil, v.infinite?)
+
+ v = (0..inf).bsearch {|x| x >= Float::MAX }
+ assert_in_delta(Float::MAX, v)
+ assert_equal(nil, v.infinite?)
+
+ v = (-Float::MAX..0).bsearch {|x| x > -Float::MAX }
+ assert_operator(-Float::MAX, :<, v)
+ assert_equal(nil, v.infinite?)
+
+ v = (-inf..0).bsearch {|x| x >= -Float::MAX }
+ assert_in_delta(-Float::MAX, v)
+ assert_equal(nil, v.infinite?)
+
+ v = (-inf..0).bsearch {|x| x > -Float::MAX }
+ assert_operator(-Float::MAX, :<, v)
+ assert_equal(nil, v.infinite?)
+
+ assert_in_delta(1.0, (0.0..inf).bsearch {|x| Math.log(x) >= 0 })
+ assert_in_delta(7.0, (0.0..10).bsearch {|x| 7.0 - x })
+
+ assert_equal(1_000_000.0.next_float, (0.0..).bsearch {|x| x > 1_000_000 })
+ end
+
+ def check_bsearch_values(range, search, a)
+ from, to = range.begin, range.end
+ cmp = range.exclude_end? ? :< : :<=
+ r = nil
+
+ a.for "(0) trivial test" do
+ r = Range.new(to, from, range.exclude_end?).bsearch do |x|
+ fail "#{to}, #{from}, #{range.exclude_end?}, #{x}"
+ end
+ assert_nil r
+
+ r = (to...to).bsearch do
+ fail
+ end
+ assert_nil r
+ end
+
+ # prepare for others
+ yielded = []
+ r = range.bsearch do |val|
+ yielded << val
+ val >= search
+ end
+
+ a.for "(1) log test" do
+ max = case from
+ when Float then 65
+ when Integer then Math.log(to-from+(range.exclude_end? ? 0 : 1), 2).to_i + 1
+ end
+ assert_operator yielded.size, :<=, max
+ end
+
+ a.for "(2) coverage test" do
+ expect = case
+ when search < from
+ from
+ when search.send(cmp, to)
+ search
+ else
+ nil
+ end
+ assert_equal expect, r
+ end
+
+ a.for "(3) uniqueness test" do
+ assert_nil yielded.uniq!
+ end
+
+ a.for "(4) end of range test" do
+ case
+ when range.exclude_end?
+ assert_not_include yielded, to
+ assert_not_equal r, to
+ when search >= to
+ assert_include yielded, to
+ assert_equal search == to ? to : nil, r
+ end
+ end
+
+ a.for "(5) start of range test" do
+ if search <= from
+ assert_include yielded, from
+ assert_equal from, r
+ end
+ end
+
+ a.for "(6) out of range test" do
+ yielded.each do |val|
+ assert_operator from, :<=, val
+ assert_send [val, cmp, to]
+ end
+ end
+ end
+
+ def test_range_bsearch_for_floats
+ ints = [-1 << 100, -123456789, -42, -1, 0, 1, 42, 123456789, 1 << 100]
+ floats = [-Float::INFINITY, -Float::MAX, -42.0, -4.2, -Float::EPSILON, -Float::MIN, 0.0, Float::MIN, Float::EPSILON, Math::PI, 4.2, 42.0, Float::MAX, Float::INFINITY]
+
+ all_assertions do |a|
+ [ints, floats].each do |values|
+ values.combination(2).to_a.product(values).each do |(from, to), search|
+ check_bsearch_values(from..to, search, a)
+ check_bsearch_values(from...to, search, a)
+ end
+ end
+ end
+ end
+
+ def test_bsearch_for_bignum
+ bignum = 2**100
+ ary = [3, 4, 7, 9, 12]
+ assert_equal(bignum + 0, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 2 })
+ assert_equal(bignum + 1, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 4 })
+ assert_equal(bignum + 2, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 6 })
+ assert_equal(bignum + 3, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 8 })
+ assert_equal(bignum + 4, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 10 })
+ assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| ary[i - bignum] >= 100 })
+ assert_equal(bignum + 0, (bignum...bignum+ary.size).bsearch {|i| true })
+ assert_equal(nil, (bignum...bignum+ary.size).bsearch {|i| false })
+ assert_equal(bignum * 2 + 1, (bignum...).bsearch {|i| i > bignum * 2 })
+
+ assert_raise(TypeError) { ("a".."z").bsearch {} }
+ end
+
+ def test_each_no_blockarg
+ a = "a"
+ def a.upto(x, e, &b)
+ super {|y| b.call(y) {|z| assert(false)}}
+ end
+ (a.."c").each {|x, &b| assert_nil(b)}
+ end
+
+ def test_to_a
+ assert_equal([1,2,3,4,5], (1..5).to_a)
+ assert_equal([1,2,3,4], (1...5).to_a)
+ assert_raise(RangeError) { (1..).to_a }
+ end
end
diff --git a/test/ruby/test_rational.rb b/test/ruby/test_rational.rb
index 02d8bd61ed..2957e1bfc8 100644
--- a/test/ruby/test_rational.rb
+++ b/test/ruby/test_rational.rb
@@ -1,41 +1,29 @@
+# frozen_string_literal: false
require 'test/unit'
class RationalSub < Rational; end
class Rational_Test < Test::Unit::TestCase
- def setup
- @complex = defined?(Complex)
- if @complex
- @keiju = Complex.instance_variables.include?(:@RCS_ID)
- end
- seps = [File::SEPARATOR, File::ALT_SEPARATOR].compact.map{|x| Regexp.escape(x)}.join("|")
- @unify = $".grep(/(?:^|#{seps})mathn(?:\.(?:rb|so))?/).size != 0
- end
-
def test_ratsub
c = RationalSub.__send__(:convert, 1)
assert_kind_of(Numeric, c)
- if @unify
- assert_instance_of(Fixnum, c)
- else
- assert_instance_of(RationalSub, c)
+ assert_instance_of(RationalSub, c)
- c2 = c + 1
- assert_instance_of(RationalSub, c2)
- c2 = c - 1
- assert_instance_of(RationalSub, c2)
+ c2 = c + 1
+ assert_instance_of(RationalSub, c2)
+ c2 = c - 1
+ assert_instance_of(RationalSub, c2)
- c3 = c - c2
- assert_instance_of(RationalSub, c3)
+ c3 = c - c2
+ assert_instance_of(RationalSub, c3)
- s = Marshal.dump(c)
- c5 = Marshal.load(s)
- assert_equal(c, c5)
- assert_instance_of(RationalSub, c5)
- end
+ s = Marshal.dump(c)
+ c5 = Marshal.load(s)
+ assert_equal(c, c5)
+ assert_instance_of(RationalSub, c5)
c1 = Rational(1)
assert_equal(c1.hash, c.hash, '[ruby-dev:38850]')
@@ -47,18 +35,16 @@ class Rational_Test < Test::Unit::TestCase
c2 = Rational(0)
c3 = Rational(1)
- assert_equal(true, c.eql?(c2))
- assert_equal(false, c.eql?(c3))
+ assert_operator(c, :eql?, c2)
+ assert_not_operator(c, :eql?, c3)
- if @unify
- assert_equal(true, c.eql?(0))
- else
- assert_equal(false, c.eql?(0))
- end
+ assert_not_operator(c, :eql?, 0)
end
def test_hash
- assert_instance_of(Fixnum, Rational(1,2).hash)
+ h = Rational(1,2).hash
+ assert_kind_of(Integer, h)
+ assert_nothing_raised {h.to_s}
h = {}
h[Rational(0)] = 0
@@ -75,10 +61,7 @@ class Rational_Test < Test::Unit::TestCase
def test_freeze
c = Rational(1)
- c.freeze
- unless @unify
- assert_equal(true, c.frozen?)
- end
+ assert_predicate(c, :frozen?)
assert_instance_of(String, c.to_s)
end
@@ -111,16 +94,14 @@ class Rational_Test < Test::Unit::TestCase
c = Rational(Rational(1,2),Rational(1,2))
assert_equal(Rational(1), c)
- if @complex && !@keiju
- c = Rational(Complex(1,2),2)
- assert_equal(Complex(Rational(1,2),1), c)
+ c = Rational(Complex(1,2),2)
+ assert_equal(Complex(Rational(1,2),1), c)
- c = Rational(2,Complex(1,2))
- assert_equal(Complex(Rational(2,5),Rational(-4,5)), c)
+ c = Rational(2,Complex(1,2))
+ assert_equal(Complex(Rational(2,5),Rational(-4,5)), c)
- c = Rational(Complex(1,2),Complex(1,2))
- assert_equal(Rational(1), c)
- end
+ c = Rational(Complex(1,2),Complex(1,2))
+ assert_equal(Rational(1), c)
assert_equal(Rational(3),Rational(3))
assert_equal(Rational(1),Rational(3,3))
@@ -129,9 +110,45 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(Rational(3),Rational('3'))
assert_equal(Rational(1),Rational('3.0','3.0'))
assert_equal(Rational(1),Rational('3/3','3/3'))
+ assert_equal(Rational(111, 1), Rational('1.11e+2'))
+ assert_equal(Rational(111, 10), Rational('1.11e+1'))
+ assert_equal(Rational(111, 10), Rational('1.11e1'))
+ assert_equal(Rational(111, 100), Rational('1.11e0'))
+ assert_equal(Rational(111, 1000), Rational('1.11e-1'))
assert_raise(TypeError){Rational(nil)}
assert_raise(ArgumentError){Rational('')}
+ assert_raise_with_message(ArgumentError, /\u{221a 2668}/) {
+ Rational("\u{221a 2668}")
+ }
+ assert_warning('') {
+ assert_predicate(Rational('1e-99999999999999999999'), :zero?)
+ }
+
assert_raise(TypeError){Rational(Object.new)}
+ assert_raise(TypeError){Rational(Object.new, Object.new)}
+ assert_raise(TypeError){Rational(1, Object.new)}
+
+ o = Object.new
+ def o.to_r; 1/42r; end
+ assert_equal(1/42r, Rational(o))
+ assert_equal(1/84r, Rational(o, 2))
+ assert_equal(42, Rational(1, o))
+ assert_equal(1, Rational(o, o))
+
+ o = Object.new
+ def o.to_r; nil; end
+ assert_raise(TypeError) { Rational(o) }
+ assert_raise(TypeError) { Rational(o, 2) }
+ assert_raise(TypeError) { Rational(1, o) }
+ assert_raise(TypeError) { Rational(o, o) }
+
+ o = Object.new
+ def o.to_r; raise; end
+ assert_raise(RuntimeError) { Rational(o) }
+ assert_raise(RuntimeError) { Rational(o, 2) }
+ assert_raise(RuntimeError) { Rational(1, o) }
+ assert_raise(RuntimeError) { Rational(o, o) }
+
assert_raise(ArgumentError){Rational()}
assert_raise(ArgumentError){Rational(1,2,3)}
@@ -178,57 +195,15 @@ class Rational_Test < Test::Unit::TestCase
def test_attr2
c = Rational(1)
- if @unify
-=begin
- assert_equal(true, c.finite?)
- assert_equal(false, c.infinite?)
- assert_equal(false, c.nan?)
- assert_equal(true, c.integer?)
- assert_equal(false, c.float?)
- assert_equal(true, c.rational?)
-=end
- assert_equal(true, c.real?)
-=begin
- assert_equal(false, c.complex?)
- assert_equal(true, c.exact?)
- assert_equal(false, c.inexact?)
-=end
- else
-=begin
- assert_equal(true, c.finite?)
- assert_equal(false, c.infinite?)
- assert_equal(false, c.nan?)
- assert_equal(false, c.integer?)
- assert_equal(false, c.float?)
- assert_equal(true, c.rational?)
-=end
- assert_equal(true, c.real?)
-=begin
- assert_equal(false, c.complex?)
- assert_equal(true, c.exact?)
- assert_equal(false, c.inexact?)
-=end
- end
+ assert_not_predicate(c, :integer?)
+ assert_predicate(c, :real?)
+
+ assert_predicate(Rational(0), :zero?)
+ assert_predicate(Rational(0,1), :zero?)
+ assert_not_predicate(Rational(1,1), :zero?)
-=begin
- assert_equal(true, Rational(0).positive?)
- assert_equal(true, Rational(1).positive?)
- assert_equal(false, Rational(-1).positive?)
- assert_equal(false, Rational(0).negative?)
- assert_equal(false, Rational(1).negative?)
- assert_equal(true, Rational(-1).negative?)
-
- assert_equal(0, Rational(0).sign)
- assert_equal(1, Rational(2).sign)
- assert_equal(-1, Rational(-2).sign)
-=end
-
- assert_equal(true, Rational(0).zero?)
- assert_equal(true, Rational(0,1).zero?)
- assert_equal(false, Rational(1,1).zero?)
-
- assert_equal(nil, Rational(0).nonzero?)
- assert_equal(nil, Rational(0,1).nonzero?)
+ assert_nil(Rational(0).nonzero?)
+ assert_nil(Rational(0,1).nonzero?)
assert_equal(Rational(1,1), Rational(1,1).nonzero?)
end
@@ -248,12 +223,6 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(Rational(1,1), -Rational(-1,1))
assert_equal(Rational(1,1), -Rational(1,-1))
assert_equal(Rational(-1,1), -Rational(-1,-1))
-
-=begin
- assert_equal(0, Rational(0).negate)
- assert_equal(-2, Rational(2).negate)
- assert_equal(2, Rational(-2).negate)
-=end
end
def test_add
@@ -297,6 +266,9 @@ class Rational_Test < Test::Unit::TestCase
assert_raise(ZeroDivisionError){Rational(1, 3) / 0}
assert_raise(ZeroDivisionError){Rational(1, 3) / Rational(0)}
+
+ assert_equal(0, Rational(1, 3) / Float::INFINITY)
+ assert_predicate(Rational(1, 3) / 0.0, :infinite?, '[ruby-core:31626]')
end
def assert_eql(exp, act, *args)
@@ -338,15 +310,13 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(-2, (-c).div(c2))
assert_equal(1, (-c).div(-c2))
- unless @unify
- c = Rational(11)
- c2 = Rational(3)
+ c = Rational(11)
+ c2 = Rational(3)
- assert_equal(3, c.div(c2))
- assert_equal(-4, c.div(-c2))
- assert_equal(-4, (-c).div(c2))
- assert_equal(3, (-c).div(-c2))
- end
+ assert_equal(3, c.div(c2))
+ assert_equal(-4, c.div(-c2))
+ assert_equal(-4, (-c).div(c2))
+ assert_equal(3, (-c).div(-c2))
end
def test_modulo
@@ -373,15 +343,13 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(Rational(99,100), (-c).modulo(c2))
assert_equal(Rational(-101,100), (-c).modulo(-c2))
- unless @unify
- c = Rational(11)
- c2 = Rational(3)
+ c = Rational(11)
+ c2 = Rational(3)
- assert_equal(2, c.modulo(c2))
- assert_equal(-1, c.modulo(-c2))
- assert_equal(1, (-c).modulo(c2))
- assert_equal(-2, (-c).modulo(-c2))
- end
+ assert_equal(2, c.modulo(c2))
+ assert_equal(-1, c.modulo(-c2))
+ assert_equal(1, (-c).modulo(c2))
+ assert_equal(-2, (-c).modulo(-c2))
end
def test_divmod
@@ -408,53 +376,14 @@ class Rational_Test < Test::Unit::TestCase
assert_equal([-2, Rational(99,100)], (-c).divmod(c2))
assert_equal([1, Rational(-101,100)], (-c).divmod(-c2))
- unless @unify
- c = Rational(11)
- c2 = Rational(3)
-
- assert_equal([3,2], c.divmod(c2))
- assert_equal([-4,-1], c.divmod(-c2))
- assert_equal([-4,1], (-c).divmod(c2))
- assert_equal([3,-2], (-c).divmod(-c2))
- end
- end
-
-=begin
- def test_quot
- c = Rational(1,2)
- c2 = Rational(2,3)
-
- assert_eql(0, c.quot(c2))
- assert_eql(0, c.quot(2))
- assert_eql(0, c.quot(2.0))
-
- c = Rational(301,100)
- c2 = Rational(7,5)
-
- assert_equal(2, c.quot(c2))
- assert_equal(-2, c.quot(-c2))
- assert_equal(-2, (-c).quot(c2))
- assert_equal(2, (-c).quot(-c2))
+ c = Rational(11)
+ c2 = Rational(3)
- c = Rational(301,100)
- c2 = Rational(2)
-
- assert_equal(1, c.quot(c2))
- assert_equal(-1, c.quot(-c2))
- assert_equal(-1, (-c).quot(c2))
- assert_equal(1, (-c).quot(-c2))
-
- unless @unify
- c = Rational(11)
- c2 = Rational(3)
-
- assert_equal(3, c.quot(c2))
- assert_equal(-3, c.quot(-c2))
- assert_equal(-3, (-c).quot(c2))
- assert_equal(3, (-c).quot(-c2))
- end
+ assert_equal([3,2], c.divmod(c2))
+ assert_equal([-4,-1], c.divmod(-c2))
+ assert_equal([-4,1], (-c).divmod(c2))
+ assert_equal([3,-2], (-c).divmod(-c2))
end
-=end
def test_remainder
c = Rational(1,2)
@@ -480,53 +409,14 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(Rational(-101,100), (-c).remainder(c2))
assert_equal(Rational(-101,100), (-c).remainder(-c2))
- unless @unify
- c = Rational(11)
- c2 = Rational(3)
-
- assert_equal(2, c.remainder(c2))
- assert_equal(2, c.remainder(-c2))
- assert_equal(-2, (-c).remainder(c2))
- assert_equal(-2, (-c).remainder(-c2))
- end
- end
-
-=begin
- def test_quotrem
- c = Rational(1,2)
- c2 = Rational(2,3)
-
- assert_eql([0, Rational(1,2)], c.quotrem(c2))
- assert_eql([0, Rational(1,2)], c.quotrem(2))
- assert_eql([0, 0.5], c.quotrem(2.0))
-
- c = Rational(301,100)
- c2 = Rational(7,5)
-
- assert_equal([2, Rational(21,100)], c.quotrem(c2))
- assert_equal([-2, Rational(21,100)], c.quotrem(-c2))
- assert_equal([-2, Rational(-21,100)], (-c).quotrem(c2))
- assert_equal([2, Rational(-21,100)], (-c).quotrem(-c2))
-
- c = Rational(301,100)
- c2 = Rational(2)
-
- assert_equal([1, Rational(101,100)], c.quotrem(c2))
- assert_equal([-1, Rational(101,100)], c.quotrem(-c2))
- assert_equal([-1, Rational(-101,100)], (-c).quotrem(c2))
- assert_equal([1, Rational(-101,100)], (-c).quotrem(-c2))
+ c = Rational(11)
+ c2 = Rational(3)
- unless @unify
- c = Rational(11)
- c2 = Rational(3)
-
- assert_equal([3,2], c.quotrem(c2))
- assert_equal([-3,2], c.quotrem(-c2))
- assert_equal([-3,-2], (-c).quotrem(c2))
- assert_equal([3,-2], (-c).quotrem(-c2))
- end
+ assert_equal(2, c.remainder(c2))
+ assert_equal(2, c.remainder(-c2))
+ assert_equal(-2, (-c).remainder(c2))
+ assert_equal(-2, (-c).remainder(-c2))
end
-=end
def test_quo
c = Rational(1,2)
@@ -546,6 +436,8 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(0.25, c.fdiv(2))
assert_equal(0.25, c.fdiv(2.0))
+ assert_equal(0, c.fdiv(Float::INFINITY))
+ assert_predicate(c.fdiv(0), :infinite?, '[ruby-core:31626]')
end
def test_expt
@@ -573,50 +465,38 @@ class Rational_Test < Test::Unit::TestCase
# p ** p
x = 2 ** Rational(2)
assert_equal(Rational(4), x)
- unless @unify
- assert_instance_of(Rational, x)
- end
+ assert_instance_of(Rational, x)
assert_equal(4, x.numerator)
assert_equal(1, x.denominator)
x = Rational(2) ** 2
assert_equal(Rational(4), x)
- unless @unify
- assert_instance_of(Rational, x)
- end
+ assert_instance_of(Rational, x)
assert_equal(4, x.numerator)
assert_equal(1, x.denominator)
x = Rational(2) ** Rational(2)
assert_equal(Rational(4), x)
- unless @unify
- assert_instance_of(Rational, x)
- end
+ assert_instance_of(Rational, x)
assert_equal(4, x.numerator)
assert_equal(1, x.denominator)
# -p ** p
x = (-2) ** Rational(2)
assert_equal(Rational(4), x)
- unless @unify
- assert_instance_of(Rational, x)
- end
+ assert_instance_of(Rational, x)
assert_equal(4, x.numerator)
assert_equal(1, x.denominator)
x = Rational(-2) ** 2
assert_equal(Rational(4), x)
- unless @unify
- assert_instance_of(Rational, x)
- end
+ assert_instance_of(Rational, x)
assert_equal(4, x.numerator)
assert_equal(1, x.denominator)
x = Rational(-2) ** Rational(2)
assert_equal(Rational(4), x)
- unless @unify
- assert_instance_of(Rational, x)
- end
+ assert_instance_of(Rational, x)
assert_equal(4, x.numerator)
assert_equal(1, x.denominator)
@@ -658,9 +538,7 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(1, x.numerator)
assert_equal(4, x.denominator)
- unless @unify # maybe bug mathn
- assert_raise(ZeroDivisionError){0 ** -1}
- end
+ assert_raise(ZeroDivisionError){0 ** -1}
end
def test_cmp
@@ -693,67 +571,88 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(-1, Rational(b-1) <=> Rational(b))
assert_equal(+1, Rational(b) <=> Rational(b-1))
- assert_equal(false, Rational(0) < Rational(0))
- assert_equal(true, Rational(0) <= Rational(0))
- assert_equal(true, Rational(0) >= Rational(0))
- assert_equal(false, Rational(0) > Rational(0))
+ assert_not_operator(Rational(0), :<, Rational(0))
+ assert_operator(Rational(0), :<=, Rational(0))
+ assert_operator(Rational(0), :>=, Rational(0))
+ assert_not_operator(Rational(0), :>, Rational(0))
- assert_equal(nil, Rational(0) <=> nil)
- assert_equal(nil, Rational(0) <=> 'foo')
+ assert_nil(Rational(0) <=> nil)
+ assert_nil(Rational(0) <=> 'foo')
end
def test_eqeq
- assert(Rational(1,1) == Rational(1))
- assert(Rational(-1,1) == Rational(-1))
+ assert_equal(Rational(1,1), Rational(1))
+ assert_equal(Rational(-1,1), Rational(-1))
- assert_equal(false, Rational(2,1) == Rational(1))
- assert_equal(true, Rational(2,1) != Rational(1))
- assert_equal(false, Rational(1) == nil)
- assert_equal(false, Rational(1) == '')
+ assert_not_operator(Rational(2,1), :==, Rational(1))
+ assert_operator(Rational(2,1), :!=, Rational(1))
+ assert_not_operator(Rational(1), :==, nil)
+ assert_not_operator(Rational(1), :==, '')
end
def test_coerce
assert_equal([Rational(2),Rational(1)], Rational(1).coerce(2))
assert_equal([Rational(2.2),Rational(1)], Rational(1).coerce(2.2))
assert_equal([Rational(2),Rational(1)], Rational(1).coerce(Rational(2)))
+
+ 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
- def test_unify
- if @unify
- assert_instance_of(Fixnum, Rational(1,2) + Rational(1,2))
- assert_instance_of(Fixnum, Rational(1,2) - Rational(1,2))
- assert_instance_of(Fixnum, Rational(1,2) * 2)
- assert_instance_of(Fixnum, Rational(1,2) / Rational(1,2))
- assert_instance_of(Fixnum, Rational(1,2).div(Rational(1,2)))
- assert_instance_of(Fixnum, Rational(1,2).quo(Rational(1,2)))
- assert_instance_of(Fixnum, Rational(1,2) ** -2)
+ class ObjectX
+ def +(x) Rational(1) end
+ alias - +
+ alias * +
+ alias / +
+ alias quo +
+ alias div +
+ alias % +
+ alias remainder +
+ alias ** +
+ def coerce(x) [x, Rational(1)] end
+ end
+
+ def test_coerce2
+ x = ObjectX.new
+ %w(+ - * / quo div % remainder **).each do |op|
+ assert_kind_of(Numeric, Rational(1).__send__(op, x))
end
end
def test_math
assert_equal(Rational(1,2), Rational(1,2).abs)
assert_equal(Rational(1,2), Rational(-1,2).abs)
- if @complex && !@keiju
- assert_equal(Rational(1,2), Rational(1,2).magnitude)
- assert_equal(Rational(1,2), Rational(-1,2).magnitude)
- end
+ assert_equal(Rational(1,2), Rational(1,2).magnitude)
+ assert_equal(Rational(1,2), Rational(-1,2).magnitude)
assert_equal(1, Rational(1,2).numerator)
assert_equal(2, Rational(1,2).denominator)
end
def test_trunc
- [[Rational(13, 5), [ 2, 3, 2, 3]], # 2.6
- [Rational(5, 2), [ 2, 3, 2, 3]], # 2.5
- [Rational(12, 5), [ 2, 3, 2, 2]], # 2.4
- [Rational(-12,5), [-3, -2, -2, -2]], # -2.4
- [Rational(-5, 2), [-3, -2, -2, -3]], # -2.5
- [Rational(-13, 5), [-3, -2, -2, -3]], # -2.6
+ [[Rational(13, 5), [ 2, 3, 2, 3, 3, 3, 3]], # 2.6
+ [Rational(5, 2), [ 2, 3, 2, 3, 2, 3, 2]], # 2.5
+ [Rational(12, 5), [ 2, 3, 2, 2, 2, 2, 2]], # 2.4
+ [Rational(-12,5), [-3, -2, -2, -2, -2, -2, -2]], # -2.4
+ [Rational(-5, 2), [-3, -2, -2, -3, -2, -3, -2]], # -2.5
+ [Rational(-13, 5), [-3, -2, -2, -3, -3, -3, -3]], # -2.6
].each do |i, a|
- assert_equal(a[0], i.floor)
- assert_equal(a[1], i.ceil)
- assert_equal(a[2], i.truncate)
- assert_equal(a[3], i.round)
+ s = proc {i.inspect}
+ assert_equal(a[0], i.floor, s)
+ assert_equal(a[1], i.ceil, s)
+ assert_equal(a[2], i.truncate, s)
+ assert_equal(a[3], i.round, s)
+ assert_equal(a[4], i.round(half: :even), s)
+ assert_equal(a[5], i.round(half: :up), s)
+ assert_equal(a[6], i.round(half: :down), s)
end
end
@@ -763,13 +662,8 @@ class Rational_Test < Test::Unit::TestCase
assert_instance_of(String, c.to_s)
assert_equal('1/2', c.to_s)
- if @unify
- assert_equal('0', Rational(0,2).to_s)
- assert_equal('0', Rational(0,-2).to_s)
- else
- assert_equal('0/1', Rational(0,2).to_s)
- assert_equal('0/1', Rational(0,-2).to_s)
- end
+ assert_equal('0/1', Rational(0,2).to_s)
+ assert_equal('0/1', Rational(0,-2).to_s)
assert_equal('1/2', Rational(1,2).to_s)
assert_equal('-1/2', Rational(-1,2).to_s)
assert_equal('1/2', Rational(-1,-2).to_s)
@@ -786,129 +680,170 @@ class Rational_Test < Test::Unit::TestCase
def test_marshal
c = Rational(1,2)
- c.instance_eval{@ivar = 9}
s = Marshal.dump(c)
c2 = Marshal.load(s)
assert_equal(c, c2)
- assert_equal(9, c2.instance_variable_get(:@ivar))
assert_instance_of(Rational, c2)
+ assert_raise(TypeError){
+ Marshal.load("\x04\bU:\rRational[\ai\x060")
+ }
+
assert_raise(ZeroDivisionError){
Marshal.load("\x04\bU:\rRational[\ai\x06i\x05")
}
+
+ bug3656 = '[ruby-core:31622]'
+ c = Rational(1,2)
+ assert_predicate(c, :frozen?)
+ result = c.marshal_load([2,3]) rescue :fail
+ assert_equal(:fail, result, bug3656)
+ end
+
+ def test_marshal_compatibility
+ bug6625 = '[ruby-core:45775]'
+ dump = "\x04\x08o:\x0dRational\x07:\x11@denominatori\x07:\x0f@numeratori\x06"
+ assert_nothing_raised(bug6625) do
+ assert_equal(Rational(1, 2), Marshal.load(dump), bug6625)
+ end
+ dump = "\x04\x08o:\x0dRational\x07:\x11@denominatori\x07:\x0f@numerator0"
+ assert_raise(TypeError) do
+ Marshal.load(dump)
+ end
+ end
+
+ def assert_valid_rational(n, d, r)
+ x = Rational(n, d)
+ assert_equal(x, r.to_r, "#{r.dump}.to_r")
+ assert_equal(x, Rational(r), "Rational(#{r.dump})")
+ end
+
+ def assert_invalid_rational(n, d, r)
+ x = Rational(n, d)
+ assert_equal(x, r.to_r, "#{r.dump}.to_r")
+ assert_raise(ArgumentError, "Rational(#{r.dump})") {Rational(r)}
end
def test_parse
- assert_equal(Rational(5), '5'.to_r)
- assert_equal(Rational(-5), '-5'.to_r)
- assert_equal(Rational(5,3), '5/3'.to_r)
- assert_equal(Rational(-5,3), '-5/3'.to_r)
-# assert_equal(Rational(5,-3), '5/-3'.to_r)
-# assert_equal(Rational(-5,-3), '-5/-3'.to_r)
-
- assert_equal(Rational(5), '5.0'.to_r)
- assert_equal(Rational(-5), '-5.0'.to_r)
- assert_equal(Rational(5,3), '5.0/3'.to_r)
- assert_equal(Rational(-5,3), '-5.0/3'.to_r)
-# assert_equal(Rational(5,-3), '5.0/-3'.to_r)
-# assert_equal(Rational(-5,-3), '-5.0/-3'.to_r)
-
- assert_equal(Rational(5), '5e0'.to_r)
- assert_equal(Rational(-5), '-5e0'.to_r)
- assert_equal(Rational(5,3), '5e0/3'.to_r)
- assert_equal(Rational(-5,3), '-5e0/3'.to_r)
-# assert_equal(Rational(5,-3), '5e0/-3'.to_r)
-# assert_equal(Rational(-5,-3), '-5e0/-3'.to_r)
-
- assert_equal(Rational(33,100), '.33'.to_r)
- assert_equal(Rational(33,100), '0.33'.to_r)
- assert_equal(Rational(-33,100), '-.33'.to_r)
- assert_equal(Rational(-33,100), '-0.33'.to_r)
- assert_equal(Rational(-33,100), '-0.3_3'.to_r)
-
- assert_equal(Rational(1,2), '5e-1'.to_r)
- assert_equal(Rational(50), '5e+1'.to_r)
- assert_equal(Rational(1,2), '5.0e-1'.to_r)
- assert_equal(Rational(50), '5.0e+1'.to_r)
- assert_equal(Rational(50), '5e1'.to_r)
- assert_equal(Rational(50), '5E1'.to_r)
- assert_equal(Rational(500), '5e2'.to_r)
- assert_equal(Rational(5000), '5e3'.to_r)
- assert_equal(Rational(500000000000), '5e1_1'.to_r)
-
- assert_equal(Rational(5), Rational('5'))
- assert_equal(Rational(-5), Rational('-5'))
- assert_equal(Rational(5,3), Rational('5/3'))
- assert_equal(Rational(-5,3), Rational('-5/3'))
-# assert_equal(Rational(5,-3), Rational('5/-3'))
-# assert_equal(Rational(-5,-3), Rational('-5/-3'))
-
- assert_equal(Rational(5), Rational('5.0'))
- assert_equal(Rational(-5), Rational('-5.0'))
- assert_equal(Rational(5,3), Rational('5.0/3'))
- assert_equal(Rational(-5,3), Rational('-5.0/3'))
-# assert_equal(Rational(5,-3), Rational('5.0/-3'))
-# assert_equal(Rational(-5,-3), Rational('-5.0/-3'))
-
- assert_equal(Rational(5), Rational('5e0'))
- assert_equal(Rational(-5), Rational('-5e0'))
- assert_equal(Rational(5,3), Rational('5e0/3'))
- assert_equal(Rational(-5,3), Rational('-5e0/3'))
-# assert_equal(Rational(5,-3), Rational('5e0/-3'))
-# assert_equal(Rational(-5,-3), Rational('-5e0/-3'))
-
- assert_equal(Rational(33,100), Rational('.33'))
- assert_equal(Rational(33,100), Rational('0.33'))
- assert_equal(Rational(-33,100), Rational('-.33'))
- assert_equal(Rational(-33,100), Rational('-0.33'))
- assert_equal(Rational(-33,100), Rational('-0.3_3'))
-
- assert_equal(Rational(1,2), Rational('5e-1'))
- assert_equal(Rational(50), Rational('5e+1'))
- assert_equal(Rational(1,2), Rational('5.0e-1'))
- assert_equal(Rational(50), Rational('5.0e+1'))
- assert_equal(Rational(50), Rational('5e1'))
- assert_equal(Rational(50), Rational('5E1'))
- assert_equal(Rational(500), Rational('5e2'))
- assert_equal(Rational(5000), Rational('5e3'))
- assert_equal(Rational(500000000000), Rational('5e1_1'))
-
- assert_equal(Rational(0), ''.to_r)
- assert_equal(Rational(0), ' '.to_r)
- assert_equal(Rational(5), "\f\n\r\t\v5\0".to_r)
- assert_equal(Rational(0), '_'.to_r)
- assert_equal(Rational(0), '_5'.to_r)
- assert_equal(Rational(5), '5_'.to_r)
- assert_equal(Rational(5), '5x'.to_r)
- assert_equal(Rational(5), '5/_3'.to_r)
- assert_equal(Rational(5,3), '5/3_'.to_r)
- assert_equal(Rational(5,3), '5/3.3'.to_r)
- assert_equal(Rational(5,3), '5/3x'.to_r)
- assert_raise(ArgumentError){ Rational('')}
- assert_raise(ArgumentError){ Rational('_')}
- assert_raise(ArgumentError){ Rational("\f\n\r\t\v5\0")}
- assert_raise(ArgumentError){ Rational('_5')}
- assert_raise(ArgumentError){ Rational('5_')}
- assert_raise(ArgumentError){ Rational('5x')}
- assert_raise(ArgumentError){ Rational('5/_3')}
- assert_raise(ArgumentError){ Rational('5/3_')}
- assert_raise(ArgumentError){ Rational('5/3.3')}
- assert_raise(ArgumentError){ Rational('5/3x')}
+ ok = method(:assert_valid_rational)
+ ng = method(:assert_invalid_rational)
+
+ ok[ 5, 1, '5']
+ ok[-5, 1, '-5']
+ ok[ 5, 3, '5/3']
+ ok[-5, 3, '-5/3']
+ ok[ 5, 3, '5_5/33']
+ ok[ 5,33, '5/3_3']
+ ng[ 5, 1, '5__5/33']
+ ng[ 5, 3, '5/3__3']
+
+ ok[ 5, 1, '5.0']
+ ok[-5, 1, '-5.0']
+ ok[ 5, 3, '5.0/3']
+ ok[-5, 3, '-5.0/3']
+ ok[ 501,100, '5.0_1']
+ ok[ 501,300, '5.0_1/3']
+ ok[ 5,33, '5.0/3_3']
+ ng[ 5, 1, '5.0__1/3']
+ ng[ 5, 3, '5.0/3__3']
+
+ ok[ 5, 1, '5e0']
+ ok[-5, 1, '-5e0']
+ ok[ 5, 3, '5e0/3']
+ ok[-5, 3, '-5e0/3']
+ ok[550, 1, '5_5e1']
+ ng[ 5, 1, '5_e1']
+
+ ok[ 5e1, 1, '5e1']
+ ok[-5e2, 1, '-5e2']
+ ok[ 5e3, 3, '5e003/3']
+ ok[-5e4, 3, '-5e004/3']
+ ok[ 5e3, 1, '5e0_3']
+ ok[ 5e1,33, '5e1/3_3']
+ ng[ 5e0, 1, '5e0__3/3']
+ ng[ 5e1, 3, '5e1/3__3']
+
+ ok[ 33, 100, '.33']
+ ok[ 33, 100, '0.33']
+ ok[-33, 100, '-.33']
+ ok[-33, 100, '-0.33']
+ ok[-33, 100, '-0.3_3']
+ ng[ -3, 10, '-0.3__3']
+
+ ok[ 1, 2, '5e-1']
+ ok[50, 1, '5e+1']
+ ok[ 1, 2, '5.0e-1']
+ ok[50, 1, '5.0e+1']
+ ok[50, 1, '5e1']
+ ok[50, 1, '5E1']
+ ok[500, 1, '5e2']
+ ok[5000, 1, '5e3']
+ ok[500000000000, 1, '5e1_1']
+ ng[ 5, 1, '5e']
+ ng[ 5, 1, '5e_']
+ ng[ 5, 1, '5e_1']
+ ng[50, 1, '5e1_']
+
+ ok[ 50, 33, '5/3.3']
+ ok[ 5, 3, '5/3e0']
+ ok[ 5, 30, '5/3e1']
+ ng[ 5, 3, '5/3._3']
+ ng[ 50, 33, '5/3.3_']
+ ok[500,333, '5/3.3_3']
+ ng[ 5, 3, '5/3e']
+ ng[ 5, 3, '5/3_e']
+ ng[ 5, 3, '5/3e_']
+ ng[ 5, 3, '5/3e_1']
+ ng[ 5, 30, '5/3e1_']
+ ok[ 5, 300000000000, '5/3e1_1']
+
+ ng[0, 1, '']
+ ng[0, 1, ' ']
+ ng[5, 1, "\f\n\r\t\v5\0"]
+ ng[0, 1, '_']
+ ng[0, 1, '_5']
+ ng[5, 1, '5_']
+ ng[5, 1, '5x']
+ ng[5, 1, '5/_3']
+ ng[5, 3, '5/3_']
+ ng[5, 3, '5/3x']
end
-=begin
- def test_reciprocal
- assert_equal(Rational(1,9), Rational(9,1).reciprocal)
- assert_equal(Rational(9,1), Rational(1,9).reciprocal)
- assert_equal(Rational(-1,9), Rational(-9,1).reciprocal)
- assert_equal(Rational(-9,1), Rational(-1,9).reciprocal)
- assert_equal(Rational(1,9), Rational(9,1).inverse)
- assert_equal(Rational(9,1), Rational(1,9).inverse)
- assert_equal(Rational(-1,9), Rational(-9,1).inverse)
- assert_equal(Rational(-9,1), Rational(-1,9).inverse)
+ def test_parse_zero_denominator
+ assert_raise(ZeroDivisionError) {"1/0".to_r}
+ assert_raise(ZeroDivisionError) {Rational("1/0")}
+ end
+
+ def test_Rational_without_exception
+ assert_nothing_raised(ArgumentError) {
+ assert_equal(nil, Rational("5/3x", exception: false))
+ }
+ assert_nothing_raised(ZeroDivisionError) {
+ assert_equal(nil, Rational("1/0", exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Rational(nil, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Rational(Object.new, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Rational(1, nil, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Rational(1, Object.new, exception: false))
+ }
+
+ o = Object.new;
+ def o.to_r; raise; end
+ assert_nothing_raised(RuntimeError) {
+ assert_equal(nil, Rational(o, exception: false))
+ }
+ assert_nothing_raised(TypeError) {
+ assert_equal(nil, Rational(1, o, exception: false))
+ }
end
-=end
def test_to_i
assert_equal(1, Rational(3,2).to_i)
@@ -918,18 +853,12 @@ class Rational_Test < Test::Unit::TestCase
def test_to_f
assert_equal(1.5, Rational(3,2).to_f)
assert_equal(1.5, Float(Rational(3,2)))
+ assert_equal(1e-23, Rational(1, 10**23).to_f, "Bug #14637")
end
def test_to_c
- if @complex && !@keiju
- if @unify
- assert_equal(Rational(3,2), Rational(3,2).to_c)
- assert_equal(Rational(3,2), Complex(Rational(3,2)))
- else
- assert_equal(Complex(Rational(3,2)), Rational(3,2).to_c)
- assert_equal(Complex(Rational(3,2)), Complex(Rational(3,2)))
- end
- end
+ assert_equal(Complex(Rational(3,2)), Rational(3,2).to_c)
+ assert_equal(Complex(Rational(3,2)), Complex(Rational(3,2)))
end
def test_to_r
@@ -949,13 +878,7 @@ class Rational_Test < Test::Unit::TestCase
c = Rational(1,2).to_r
assert_equal([1,2], [c.numerator, c.denominator])
- if @complex
- if @keiju
- assert_raise(NoMethodError){Complex(1,2).to_r}
- else
- assert_raise(RangeError){Complex(1,2).to_r}
- end
- end
+ assert_raise(RangeError){Complex(1,2).to_r}
if (0.0/0).nan?
assert_raise(FloatDomainError){(0.0/0).to_r}
@@ -1005,12 +928,7 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(r.rationalize(Rational(1,10)), Rational(-1,3))
assert_equal(r.rationalize(Rational(-1,10)), Rational(-1,3))
- if @complex
- if @keiju
- else
- assert_raise(RangeError){Complex(1,2).rationalize}
- end
- end
+ assert_raise(RangeError){Complex(1,2).rationalize}
if (0.0/0).nan?
assert_raise(FloatDomainError){(0.0/0).rationalize}
@@ -1037,9 +955,19 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(1152921470247108503, 1073741789.lcm(1073741827))
end
+ def test_gcd_no_memory_leak
+ assert_no_memory_leak([], "#{<<-"begin;"}", "#{<<-"end;"}", limit: 1.2, rss: true)
+ x = (1<<121) + 1
+ y = (1<<99) + 1
+ 1000.times{x.gcd(y)}
+ begin;
+ 100.times {1000.times{x.gcd(y)}}
+ end;
+ end
+
def test_supp
- assert_equal(true, 1.real?)
- assert_equal(true, 1.1.real?)
+ assert_predicate(1, :real?)
+ assert_predicate(1.1, :real?)
assert_equal(1, 1.numerator)
assert_equal(9, 9.numerator)
@@ -1051,22 +979,24 @@ class Rational_Test < Test::Unit::TestCase
assert_equal(1.0, 1.0.denominator)
assert_equal(1.0, 9.0.denominator)
-=begin
- assert_equal(Rational(1,9), 9.reciprocal)
- assert_in_delta(0.1111, 9.0.reciprocal, 0.001)
- assert_equal(Rational(1,9), 9.inverse)
- assert_in_delta(0.1111, 9.0.inverse, 0.001)
-=end
-
assert_equal(Rational(1,2), 1.quo(2))
assert_equal(Rational(5000000000), 10000000000.quo(2))
assert_equal(0.5, 1.0.quo(2))
assert_equal(Rational(1,4), Rational(1,2).quo(2))
+ assert_equal(0, Rational(1,2).quo(Float::INFINITY))
+ assert_predicate(Rational(1,2).quo(0.0), :infinite?, '[ruby-core:31626]')
assert_equal(0.5, 1.fdiv(2))
assert_equal(5000000000.0, 10000000000.fdiv(2))
assert_equal(0.5, 1.0.fdiv(2))
assert_equal(0.25, Rational(1,2).fdiv(2))
+
+ a = 0xa42fcabf_c51ce400_00001000_00000000_00000000_00000000_00000000_00000000
+ b = 1<<1074
+ assert_equal(Rational(a, b).to_f, a.fdiv(b))
+ a = 3
+ b = 0x20_0000_0000_0001
+ assert_equal(Rational(a, b).to_f, a.fdiv(b))
end
def test_ruby19
@@ -1075,15 +1005,59 @@ class Rational_Test < Test::Unit::TestCase
end
def test_fixed_bug
- if @unify
- assert_instance_of(Fixnum, Rational(1,2) ** 0) # mathn's bug
- end
-
n = Float::MAX.to_i * 2
- assert_equal(1.0, Rational(n + 2, n + 1).to_f, '[ruby-dev:33852]')
+ x = EnvUtil.suppress_warning {Rational(n + 2, n + 1).to_f}
+ assert_equal(1.0, x, '[ruby-dev:33852]')
+ end
+
+ def test_power_of_1_and_minus_1
+ bug5715 = '[ruby-core:41498]'
+ big = 1 << 66
+ one = Rational( 1, 1)
+ assert_eql one, one ** -big , bug5715
+ assert_eql one, (-one) ** -big , bug5715
+ assert_eql (-one), (-one) ** -(big+1) , bug5715
+ assert_equal Complex, ((-one) ** Rational(1,3)).class
+ end
+
+ def test_power_of_0
+ bug5713 = '[ruby-core:41494]'
+ big = 1 << 66
+ zero = Rational(0, 1)
+ assert_eql zero, zero ** big
+ assert_eql zero, zero ** Rational(2, 3)
+ assert_raise(ZeroDivisionError, bug5713) { Rational(0, 1) ** -big }
+ assert_raise(ZeroDivisionError, bug5713) { Rational(0, 1) ** Rational(-2,3) }
+ end
+
+ def test_power_overflow
+ bug = '[ruby-core:79686] [Bug #13242]: Infinity due to overflow'
+ x = EnvUtil.suppress_warning {4r**40000000}
+ assert_predicate x, :infinite?, bug
+ x = EnvUtil.suppress_warning {(1/4r)**40000000}
+ assert_equal 0, x, bug
+ end
+
+ def test_positive_p
+ assert_predicate(1/2r, :positive?)
+ assert_not_predicate(-1/2r, :positive?)
+ end
+
+ def test_negative_p
+ assert_predicate(-1/2r, :negative?)
+ assert_not_predicate(1/2r, :negative?)
end
def test_known_bug
end
+ def test_finite_p
+ assert_predicate(1/2r, :finite?)
+ assert_predicate(-1/2r, :finite?)
+ end
+
+ def test_infinite_p
+ assert_nil((1/2r).infinite?)
+ assert_nil((-1/2r).infinite?)
+ end
end
diff --git a/test/ruby/test_rational2.rb b/test/ruby/test_rational2.rb
index 3b6a985bc6..4e96bf621c 100644
--- a/test/ruby/test_rational2.rb
+++ b/test/ruby/test_rational2.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class Rational_Test2 < Test::Unit::TestCase
diff --git a/test/ruby/test_readpartial.rb b/test/ruby/test_readpartial.rb
index b969c38692..bc22556cd4 100644
--- a/test/ruby/test_readpartial.rb
+++ b/test/ruby/test_readpartial.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
require 'timeout'
require 'fcntl'
@@ -50,8 +51,8 @@ class TestReadPartial < Test::Unit::TestCase
w << 'abc'
assert_equal('ab', r.readpartial(2))
assert_equal('c', r.readpartial(2))
- assert_raise(TimeoutError) {
- timeout(0.1) { r.readpartial(2) }
+ assert_raise(Timeout::Error) {
+ Timeout.timeout(0.1) { r.readpartial(2) }
}
}
end
@@ -64,8 +65,8 @@ class TestReadPartial < Test::Unit::TestCase
assert_equal("de", r.readpartial(2))
assert_equal("f\n", r.readpartial(4096))
assert_equal("ghi\n", r.readpartial(4096))
- assert_raise(TimeoutError) {
- timeout(0.1) { r.readpartial(2) }
+ assert_raise(Timeout::Error) {
+ Timeout.timeout(0.1) { r.readpartial(2) }
}
}
end
diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb
new file mode 100644
index 0000000000..38d0adbd36
--- /dev/null
+++ b/test/ruby/test_refinement.rb
@@ -0,0 +1,2263 @@
+# frozen_string_literal: false
+require 'test/unit'
+
+class TestRefinement < Test::Unit::TestCase
+ module Sandbox
+ BINDING = binding
+ end
+
+ class Foo
+ def x
+ return "Foo#x"
+ end
+
+ def y
+ return "Foo#y"
+ end
+
+ def a
+ return "Foo#a"
+ end
+
+ def b
+ return "Foo#b"
+ end
+
+ def call_x
+ return x
+ end
+ end
+
+ module FooExt
+ refine Foo do
+ def x
+ return "FooExt#x"
+ end
+
+ def y
+ return "FooExt#y " + super
+ end
+
+ def z
+ return "FooExt#z"
+ end
+
+ def a
+ return "FooExt#a"
+ end
+
+ private def b
+ return "FooExt#b"
+ end
+ end
+ end
+
+ module FooExt2
+ refine Foo do
+ def x
+ return "FooExt2#x"
+ end
+
+ def y
+ return "FooExt2#y " + super
+ end
+
+ def z
+ return "FooExt2#z"
+ end
+ end
+ end
+
+ class FooSub < Foo
+ def x
+ return "FooSub#x"
+ end
+
+ def y
+ return "FooSub#y " + super
+ end
+ end
+
+ class FooExtClient
+ using TestRefinement::FooExt
+
+ begin
+ def self.map_x_on(foo)
+ [foo].map(&:x)[0]
+ end
+
+ def self.invoke_x_on(foo)
+ return foo.x
+ end
+
+ def self.invoke_y_on(foo)
+ return foo.y
+ end
+
+ def self.invoke_z_on(foo)
+ return foo.z
+ end
+
+ def self.send_z_on(foo)
+ return foo.send(:z)
+ end
+
+ def self.send_b_on(foo)
+ return foo.send(:b)
+ end
+
+ def self.public_send_z_on(foo)
+ return foo.public_send(:z)
+ end
+
+ def self.public_send_b_on(foo)
+ return foo.public_send(:b)
+ end
+
+ def self.method_z(foo)
+ return foo.method(:z)
+ end
+
+ def self.invoke_call_x_on(foo)
+ return foo.call_x
+ end
+
+ def self.return_proc(&block)
+ block
+ end
+ end
+ end
+
+ class TestRefinement::FooExtClient2
+ using TestRefinement::FooExt
+ using TestRefinement::FooExt2
+
+ begin
+ def self.invoke_y_on(foo)
+ return foo.y
+ end
+
+ def self.invoke_a_on(foo)
+ return foo.a
+ end
+ end
+ end
+
+ def test_override
+ foo = Foo.new
+ assert_equal("Foo#x", foo.x)
+ assert_equal("FooExt#x", FooExtClient.invoke_x_on(foo))
+ assert_equal("Foo#x", foo.x)
+ end
+
+ def test_super
+ foo = Foo.new
+ assert_equal("Foo#y", foo.y)
+ assert_equal("FooExt#y Foo#y", FooExtClient.invoke_y_on(foo))
+ assert_equal("Foo#y", foo.y)
+ end
+
+ def test_super_not_chained
+ foo = Foo.new
+ assert_equal("Foo#y", foo.y)
+ assert_equal("FooExt2#y Foo#y", FooExtClient2.invoke_y_on(foo))
+ assert_equal("Foo#y", foo.y)
+ end
+
+ def test_using_same_class_refinements
+ foo = Foo.new
+ assert_equal("Foo#a", foo.a)
+ assert_equal("FooExt#a", FooExtClient2.invoke_a_on(foo))
+ assert_equal("Foo#a", foo.a)
+ end
+
+ def test_new_method
+ foo = Foo.new
+ assert_raise(NoMethodError) { foo.z }
+ assert_equal("FooExt#z", FooExtClient.invoke_z_on(foo))
+ assert_raise(NoMethodError) { foo.z }
+ end
+
+ module RespondTo
+ class Super
+ def foo
+ end
+ end
+
+ class Sub < Super
+ end
+
+ module M
+ refine Sub do
+ def foo
+ end
+ end
+ end
+ end
+
+ def test_send_should_use_refinements
+ foo = Foo.new
+ assert_raise(NoMethodError) { foo.send(:z) }
+ assert_equal("FooExt#z", FooExtClient.send_z_on(foo))
+ assert_equal("FooExt#b", FooExtClient.send_b_on(foo))
+ assert_raise(NoMethodError) { foo.send(:z) }
+
+ assert_equal(true, RespondTo::Sub.new.respond_to?(:foo))
+ end
+
+ def test_public_send_should_use_refinements
+ foo = Foo.new
+ assert_raise(NoMethodError) { foo.public_send(:z) }
+ assert_equal("FooExt#z", FooExtClient.public_send_z_on(foo))
+ assert_equal("Foo#b", foo.public_send(:b))
+ assert_raise(NoMethodError) { FooExtClient.public_send_b_on(foo) }
+ end
+
+ def test_method_should_not_use_refinements
+ foo = Foo.new
+ assert_raise(NameError) { foo.method(:z) }
+ assert_raise(NameError) { FooExtClient.method_z(foo) }
+ assert_raise(NameError) { foo.method(:z) }
+ end
+
+ def test_no_local_rebinding
+ foo = Foo.new
+ assert_equal("Foo#x", foo.call_x)
+ assert_equal("Foo#x", FooExtClient.invoke_call_x_on(foo))
+ assert_equal("Foo#x", foo.call_x)
+ end
+
+ def test_subclass_is_prior
+ sub = FooSub.new
+ assert_equal("FooSub#x", sub.x)
+ assert_equal("FooSub#x", FooExtClient.invoke_x_on(sub))
+ assert_equal("FooSub#x", sub.x)
+ end
+
+ def test_super_in_subclass
+ sub = FooSub.new
+ assert_equal("FooSub#y Foo#y", sub.y)
+ # not "FooSub#y FooExt#y Foo#y"
+ assert_equal("FooSub#y Foo#y", FooExtClient.invoke_y_on(sub))
+ assert_equal("FooSub#y Foo#y", sub.y)
+ end
+
+ def test_new_method_on_subclass
+ sub = FooSub.new
+ assert_raise(NoMethodError) { sub.z }
+ assert_equal("FooExt#z", FooExtClient.invoke_z_on(sub))
+ assert_raise(NoMethodError) { sub.z }
+ end
+
+ def test_module_eval
+ foo = Foo.new
+ assert_equal("Foo#x", foo.x)
+ assert_equal("Foo#x", FooExt.module_eval { foo.x })
+ assert_equal("Foo#x", FooExt.module_eval("foo.x"))
+ assert_equal("Foo#x", foo.x)
+ end
+
+ def test_instance_eval_without_refinement
+ foo = Foo.new
+ ext_client = FooExtClient.new
+ assert_equal("Foo#x", foo.x)
+ assert_equal("Foo#x", ext_client.instance_eval { foo.x })
+ assert_equal("Foo#x", foo.x)
+ end
+
+ module IntegerSlashExt
+ refine Integer do
+ def /(other) quo(other) end
+ end
+ end
+
+ def test_override_builtin_method
+ assert_equal(0, 1 / 2)
+ assert_equal(Rational(1, 2), eval_using(IntegerSlashExt, "1 / 2"))
+ assert_equal(0, 1 / 2)
+ end
+
+ module IntegerPlusExt
+ refine Integer do
+ def self.method_added(*args); end
+ def +(other) "overridden" end
+ end
+ end
+
+ def test_override_builtin_method_with_method_added
+ assert_equal(3, 1 + 2)
+ assert_equal("overridden", eval_using(IntegerPlusExt, "1 + 2"))
+ assert_equal(3, 1 + 2)
+ end
+
+ def test_return_value_of_refine
+ mod = nil
+ result = nil
+ Module.new {
+ result = refine(Object) {
+ mod = self
+ }
+ }
+ assert_equal mod, result
+ end
+
+ module RefineSameClass
+ REFINEMENT1 = refine(Integer) {
+ def foo; return "foo" end
+ }
+ REFINEMENT2 = refine(Integer) {
+ def bar; return "bar" end
+ }
+ REFINEMENT3 = refine(String) {
+ def baz; return "baz" end
+ }
+ end
+
+ def test_refine_same_class_twice
+ assert_equal("foo", eval_using(RefineSameClass, "1.foo"))
+ assert_equal("bar", eval_using(RefineSameClass, "1.bar"))
+ assert_equal(RefineSameClass::REFINEMENT1, RefineSameClass::REFINEMENT2)
+ assert_not_equal(RefineSameClass::REFINEMENT1, RefineSameClass::REFINEMENT3)
+ end
+
+ module IntegerFooExt
+ refine Integer do
+ def foo; "foo"; end
+ end
+ end
+
+ def test_respond_to_should_use_refinements
+ assert_equal(false, 1.respond_to?(:foo))
+ assert_equal(true, eval_using(IntegerFooExt, "1.respond_to?(:foo)"))
+ end
+
+ module StringCmpExt
+ refine String do
+ def <=>(other) return 0 end
+ end
+ end
+
+ module ArrayEachExt
+ refine Array do
+ def each
+ super do |i|
+ yield 2 * i
+ end
+ end
+ end
+ end
+
+ def test_builtin_method_no_local_rebinding
+ assert_equal(false, eval_using(StringCmpExt, '"1" >= "2"'))
+ assert_equal(1, eval_using(ArrayEachExt, "[1, 2, 3].min"))
+ end
+
+ module RefinePrependedClass
+ module M1
+ def foo
+ super << :m1
+ end
+ end
+
+ class C
+ prepend M1
+
+ def foo
+ [:c]
+ end
+ end
+
+ module M2
+ refine C do
+ def foo
+ super << :m2
+ end
+ end
+ end
+ end
+
+ def test_refine_prepended_class
+ x = eval_using(RefinePrependedClass::M2,
+ "TestRefinement::RefinePrependedClass::C.new.foo")
+ assert_equal([:c, :m1, :m2], x)
+ end
+
+ module RefineModule
+ module M
+ def foo
+ "M#foo"
+ end
+
+ def bar
+ "M#bar"
+ end
+
+ def baz
+ "M#baz"
+ end
+ end
+
+ class C
+ include M
+
+ def baz
+ "#{super} C#baz"
+ end
+ end
+
+ module M2
+ refine M do
+ def foo
+ "M@M2#foo"
+ end
+
+ def bar
+ "#{super} M@M2#bar"
+ end
+
+ def baz
+ "#{super} M@M2#baz"
+ end
+ end
+ end
+
+ using M2
+
+ def self.call_foo
+ C.new.foo
+ end
+
+ def self.call_bar
+ C.new.bar
+ end
+
+ def self.call_baz
+ C.new.baz
+ end
+ end
+
+ def test_refine_module
+ assert_equal("M@M2#foo", RefineModule.call_foo)
+ assert_equal("M#bar M@M2#bar", RefineModule.call_bar)
+ assert_equal("M#baz C#baz", RefineModule.call_baz)
+ end
+
+ def test_refine_neither_class_nor_module
+ assert_raise(TypeError) do
+ Module.new {
+ refine Object.new do
+ end
+ }
+ end
+ assert_raise(TypeError) do
+ Module.new {
+ refine 123 do
+ end
+ }
+ end
+ assert_raise(TypeError) do
+ Module.new {
+ refine "foo" do
+ end
+ }
+ end
+ end
+
+ def test_refine_in_class
+ assert_raise(NoMethodError) do
+ Class.new {
+ refine Integer do
+ def foo
+ "c"
+ end
+ end
+ }
+ end
+ end
+
+ def test_main_using
+ assert_in_out_err([], <<-INPUT, %w(:C :M), [])
+ class C
+ def foo
+ :C
+ end
+ end
+
+ module M
+ refine C do
+ def foo
+ :M
+ end
+ end
+ end
+
+ c = C.new
+ p c.foo
+ using M
+ p c.foo
+ INPUT
+ end
+
+ def test_main_using_is_private
+ assert_raise(NoMethodError) do
+ eval("self.using Module.new", Sandbox::BINDING)
+ end
+ end
+
+ def test_no_kernel_using
+ assert_raise(NoMethodError) do
+ using Module.new
+ end
+ end
+
+ class UsingClass
+ end
+
+ def test_module_using_class
+ assert_raise(TypeError) do
+ eval("using TestRefinement::UsingClass", Sandbox::BINDING)
+ end
+ end
+
+ def test_refine_without_block
+ c1 = Class.new
+ assert_raise_with_message(ArgumentError, "no block given") {
+ Module.new do
+ refine c1
+ end
+ }
+ end
+
+ module Inspect
+ module M
+ Integer = refine(Integer) {}
+ end
+ end
+
+ def test_inspect
+ assert_equal("#<refinement:Integer@TestRefinement::Inspect::M>",
+ Inspect::M::Integer.inspect)
+ end
+
+ def test_using_method_cache
+ assert_in_out_err([], <<-INPUT, %w(:M1 :M2), [])
+ class C
+ def foo
+ "original"
+ end
+ end
+
+ module M1
+ refine C do
+ def foo
+ :M1
+ end
+ end
+ end
+
+ module M2
+ refine C do
+ def foo
+ :M2
+ end
+ end
+ end
+
+ c = C.new
+ using M1
+ p c.foo
+ using M2
+ p c.foo
+ INPUT
+ end
+
+ module RedefineRefinedMethod
+ class C
+ def foo
+ "original"
+ end
+ end
+
+ module M
+ refine C do
+ def foo
+ "refined"
+ end
+ end
+ end
+
+ class C
+ EnvUtil.suppress_warning do
+ def foo
+ "redefined"
+ end
+ end
+ end
+ end
+
+ def test_redefine_refined_method
+ x = eval_using(RedefineRefinedMethod::M,
+ "TestRefinement::RedefineRefinedMethod::C.new.foo")
+ assert_equal("refined", x)
+ end
+
+ module StringExt
+ refine String do
+ def foo
+ "foo"
+ end
+ end
+ end
+
+ module RefineScoping
+ refine String do
+ def foo
+ "foo"
+ end
+
+ def RefineScoping.call_in_refine_block
+ "".foo
+ end
+ end
+
+ def self.call_outside_refine_block
+ "".foo
+ end
+ end
+
+ def test_refine_scoping
+ assert_equal("foo", RefineScoping.call_in_refine_block)
+ assert_raise(NoMethodError) do
+ RefineScoping.call_outside_refine_block
+ end
+ end
+
+ module StringRecursiveLength
+ refine String do
+ def recursive_length
+ if empty?
+ 0
+ else
+ self[1..-1].recursive_length + 1
+ end
+ end
+ end
+ end
+
+ def test_refine_recursion
+ x = eval_using(StringRecursiveLength, "'foo'.recursive_length")
+ assert_equal(3, x)
+ end
+
+ module ToJSON
+ refine Integer do
+ def to_json; to_s; end
+ end
+
+ refine Array do
+ def to_json; "[" + map { |i| i.to_json }.join(",") + "]" end
+ end
+
+ refine Hash do
+ def to_json; "{" + map { |k, v| k.to_s.dump + ":" + v.to_json }.join(",") + "}" end
+ end
+ end
+
+ def test_refine_mutual_recursion
+ x = eval_using(ToJSON, "[{1=>2}, {3=>4}].to_json")
+ assert_equal('[{"1":2},{"3":4}]', x)
+ end
+
+ def test_refine_with_proc
+ assert_raise(ArgumentError) do
+ Module.new {
+ refine(String, &Proc.new {})
+ }
+ end
+ end
+
+ def test_using_in_module
+ assert_raise(RuntimeError) do
+ eval(<<-EOF, Sandbox::BINDING)
+ $main = self
+ module M
+ end
+ module M2
+ $main.send(:using, M)
+ end
+ EOF
+ end
+ end
+
+ def test_using_in_method
+ assert_raise(RuntimeError) do
+ eval(<<-EOF, Sandbox::BINDING)
+ $main = self
+ module M
+ end
+ class C
+ def call_using_in_method
+ $main.send(:using, M)
+ end
+ end
+ C.new.call_using_in_method
+ EOF
+ end
+ end
+
+ module IncludeIntoRefinement
+ class C
+ def bar
+ return "C#bar"
+ end
+
+ def baz
+ return "C#baz"
+ end
+ end
+
+ module Mixin
+ def foo
+ return "Mixin#foo"
+ end
+
+ def bar
+ return super << " Mixin#bar"
+ end
+
+ def baz
+ return super << " Mixin#baz"
+ end
+ end
+
+ module M
+ refine C do
+ include Mixin
+
+ def baz
+ return super << " M#baz"
+ end
+ end
+ end
+ end
+
+ eval <<-EOF, Sandbox::BINDING
+ using TestRefinement::IncludeIntoRefinement::M
+
+ module TestRefinement::IncludeIntoRefinement::User
+ def self.invoke_foo_on(x)
+ x.foo
+ end
+
+ def self.invoke_bar_on(x)
+ x.bar
+ end
+
+ def self.invoke_baz_on(x)
+ x.baz
+ end
+ end
+ EOF
+
+ def test_include_into_refinement
+ x = IncludeIntoRefinement::C.new
+ assert_equal("Mixin#foo", IncludeIntoRefinement::User.invoke_foo_on(x))
+ assert_equal("C#bar Mixin#bar",
+ IncludeIntoRefinement::User.invoke_bar_on(x))
+ assert_equal("C#baz Mixin#baz M#baz",
+ IncludeIntoRefinement::User.invoke_baz_on(x))
+ end
+
+ module PrependIntoRefinement
+ class C
+ def bar
+ return "C#bar"
+ end
+
+ def baz
+ return "C#baz"
+ end
+ end
+
+ module Mixin
+ def foo
+ return "Mixin#foo"
+ end
+
+ def bar
+ return super << " Mixin#bar"
+ end
+
+ def baz
+ return super << " Mixin#baz"
+ end
+ end
+
+ module M
+ refine C do
+ prepend Mixin
+
+ def baz
+ return super << " M#baz"
+ end
+ end
+ end
+ end
+
+ eval <<-EOF, Sandbox::BINDING
+ using TestRefinement::PrependIntoRefinement::M
+
+ module TestRefinement::PrependIntoRefinement::User
+ def self.invoke_foo_on(x)
+ x.foo
+ end
+
+ def self.invoke_bar_on(x)
+ x.bar
+ end
+
+ def self.invoke_baz_on(x)
+ x.baz
+ end
+ end
+ EOF
+
+ def test_prepend_into_refinement
+ x = PrependIntoRefinement::C.new
+ assert_equal("Mixin#foo", PrependIntoRefinement::User.invoke_foo_on(x))
+ assert_equal("C#bar Mixin#bar",
+ PrependIntoRefinement::User.invoke_bar_on(x))
+ assert_equal("C#baz M#baz Mixin#baz",
+ PrependIntoRefinement::User.invoke_baz_on(x))
+ end
+
+ PrependAfterRefine_CODE = <<-EOC
+ module PrependAfterRefine
+ class C
+ def foo
+ "original"
+ end
+ end
+
+ module M
+ refine C do
+ def foo
+ "refined"
+ end
+
+ def bar
+ "refined"
+ end
+ end
+ end
+
+ module Mixin
+ def foo
+ "mixin"
+ end
+
+ def bar
+ "mixin"
+ end
+ end
+
+ class C
+ prepend Mixin
+ end
+ end
+ EOC
+ eval PrependAfterRefine_CODE
+
+ def test_prepend_after_refine_wb_miss
+ if /\A(arm|mips)/ =~ RUBY_PLATFORM
+ skip "too slow cpu"
+ end
+ assert_normal_exit %Q{
+ GC.stress = true
+ 10.times{
+ #{PrependAfterRefine_CODE}
+ undef PrependAfterRefine
+ }
+ }
+ end
+
+ def test_prepend_after_refine
+ x = eval_using(PrependAfterRefine::M,
+ "TestRefinement::PrependAfterRefine::C.new.foo")
+ assert_equal("refined", x)
+ assert_equal("mixin", TestRefinement::PrependAfterRefine::C.new.foo)
+ y = eval_using(PrependAfterRefine::M,
+ "TestRefinement::PrependAfterRefine::C.new.bar")
+ assert_equal("refined", y)
+ assert_equal("mixin", TestRefinement::PrependAfterRefine::C.new.bar)
+ end
+
+ module SuperInBlock
+ class C
+ def foo(*args)
+ [:foo, *args]
+ end
+ end
+
+ module R
+ refine C do
+ def foo(*args)
+ tap do
+ return super(:ref, *args)
+ end
+ end
+ end
+ end
+ end
+
+ def test_super_in_block
+ bug7925 = '[ruby-core:52750] [Bug #7925]'
+ x = eval_using(SuperInBlock::R,
+ "TestRefinement:: SuperInBlock::C.new.foo(#{bug7925.dump})")
+ assert_equal([:foo, :ref, bug7925], x, bug7925)
+ end
+
+ module ModuleUsing
+ using FooExt
+
+ def self.invoke_x_on(foo)
+ return foo.x
+ end
+
+ def self.invoke_y_on(foo)
+ return foo.y
+ end
+
+ def self.invoke_z_on(foo)
+ return foo.z
+ end
+
+ def self.send_z_on(foo)
+ return foo.send(:z)
+ end
+
+ def self.public_send_z_on(foo)
+ return foo.public_send(:z)
+ end
+
+ def self.method_z(foo)
+ return foo.method(:z)
+ end
+
+ def self.invoke_call_x_on(foo)
+ return foo.call_x
+ end
+ end
+
+ def test_module_using
+ foo = Foo.new
+ assert_equal("Foo#x", foo.x)
+ assert_equal("Foo#y", foo.y)
+ assert_raise(NoMethodError) { foo.z }
+ assert_equal("FooExt#x", ModuleUsing.invoke_x_on(foo))
+ assert_equal("FooExt#y Foo#y", ModuleUsing.invoke_y_on(foo))
+ assert_equal("FooExt#z", ModuleUsing.invoke_z_on(foo))
+ assert_equal("Foo#x", foo.x)
+ assert_equal("Foo#y", foo.y)
+ assert_raise(NoMethodError) { foo.z }
+ end
+
+ def test_module_using_in_method
+ assert_raise(RuntimeError) do
+ Module.new.send(:using, FooExt)
+ end
+ end
+
+ def test_module_using_invalid_self
+ assert_raise(RuntimeError) do
+ eval <<-EOF, Sandbox::BINDING
+ module TestRefinement::TestModuleUsingInvalidSelf
+ Module.new.send(:using, TestRefinement::FooExt)
+ end
+ EOF
+ end
+ end
+
+ class Bar
+ end
+
+ module BarExt
+ refine Bar do
+ def x
+ return "BarExt#x"
+ end
+ end
+ end
+
+ module FooBarExt
+ include FooExt
+ include BarExt
+ end
+
+ module FooBarExtClient
+ using FooBarExt
+
+ def self.invoke_x_on(foo)
+ return foo.x
+ end
+ end
+
+ def test_module_inclusion
+ foo = Foo.new
+ assert_equal("FooExt#x", FooBarExtClient.invoke_x_on(foo))
+ bar = Bar.new
+ assert_equal("BarExt#x", FooBarExtClient.invoke_x_on(bar))
+ end
+
+ module FooFoo2Ext
+ include FooExt
+ include FooExt2
+ end
+
+ module FooFoo2ExtClient
+ using FooFoo2Ext
+
+ def self.invoke_x_on(foo)
+ return foo.x
+ end
+
+ def self.invoke_y_on(foo)
+ return foo.y
+ end
+ end
+
+ def test_module_inclusion2
+ foo = Foo.new
+ assert_equal("FooExt2#x", FooFoo2ExtClient.invoke_x_on(foo))
+ assert_equal("FooExt2#y Foo#y", FooFoo2ExtClient.invoke_y_on(foo))
+ end
+
+ def test_eval_scoping
+ assert_in_out_err([], <<-INPUT, ["HELLO WORLD", "dlrow olleh", "HELLO WORLD"], [])
+ module M
+ refine String do
+ def upcase
+ reverse
+ end
+ end
+ end
+
+ puts "hello world".upcase
+ puts eval(%{using M; "hello world".upcase})
+ puts "hello world".upcase
+ INPUT
+ end
+
+ def test_eval_with_binding_scoping
+ assert_in_out_err([], <<-INPUT, ["HELLO WORLD", "dlrow olleh", "dlrow olleh"], [])
+ module M
+ refine String do
+ def upcase
+ reverse
+ end
+ end
+ end
+
+ puts "hello world".upcase
+ b = binding
+ puts eval(%{using M; "hello world".upcase}, b)
+ puts eval(%{"hello world".upcase}, b)
+ INPUT
+ end
+
+ def test_case_dispatch_is_aware_of_refinements
+ assert_in_out_err([], <<-RUBY, ["refinement used"], [])
+ module RefineSymbol
+ refine Symbol do
+ def ===(other)
+ true
+ end
+ end
+ end
+
+ using RefineSymbol
+
+ case :a
+ when :b
+ puts "refinement used"
+ else
+ puts "refinement not used"
+ end
+ RUBY
+ end
+
+ def test_refine_after_using
+ assert_separately([], <<-"end;")
+ bug8880 = '[ruby-core:57079] [Bug #8880]'
+ module Test
+ refine(String) do
+ end
+ end
+ using Test
+ def t
+ 'Refinements are broken!'.chop!
+ end
+ t
+ module Test
+ refine(String) do
+ def chop!
+ self.sub!(/broken/, 'fine')
+ end
+ end
+ end
+ assert_equal('Refinements are fine!', t, bug8880)
+ end;
+ end
+
+ def test_instance_methods
+ bug8881 = '[ruby-core:57080] [Bug #8881]'
+ assert_not_include(Foo.instance_methods(false), :z, bug8881)
+ assert_not_include(FooSub.instance_methods(true), :z, bug8881)
+ end
+
+ def test_method_defined
+ assert_not_send([Foo, :method_defined?, :z])
+ assert_not_send([FooSub, :method_defined?, :z])
+ end
+
+ def test_undef_refined_method
+ bug8966 = '[ruby-core:57466] [Bug #8966]'
+
+ assert_in_out_err([], <<-INPUT, ["NameError"], [], bug8966)
+ module Foo
+ refine Object do
+ def foo
+ puts "foo"
+ end
+ end
+ end
+
+ using Foo
+
+ class Object
+ begin
+ undef foo
+ rescue Exception => e
+ p e.class
+ end
+ end
+ INPUT
+
+ assert_in_out_err([], <<-INPUT, ["NameError"], [], bug8966)
+ module Foo
+ refine Object do
+ def foo
+ puts "foo"
+ end
+ end
+ end
+
+ # without `using Foo'
+
+ class Object
+ begin
+ undef foo
+ rescue Exception => e
+ p e.class
+ end
+ end
+ INPUT
+ end
+
+ def test_refine_undefed_method_and_call
+ assert_in_out_err([], <<-INPUT, ["NoMethodError"], [])
+ class Foo
+ def foo
+ end
+
+ undef foo
+ end
+
+ module FooExt
+ refine Foo do
+ def foo
+ end
+ end
+ end
+
+ begin
+ Foo.new.foo
+ rescue => e
+ p e.class
+ end
+ INPUT
+ end
+
+ def test_refine_undefed_method_and_send
+ assert_in_out_err([], <<-INPUT, ["NoMethodError"], [])
+ class Foo
+ def foo
+ end
+
+ undef foo
+ end
+
+ module FooExt
+ refine Foo do
+ def foo
+ end
+ end
+ end
+
+ begin
+ Foo.new.send(:foo)
+ rescue => e
+ p e.class
+ end
+ INPUT
+ end
+
+ def test_adding_private_method
+ bug9452 = '[ruby-core:60111] [Bug #9452]'
+
+ assert_in_out_err([], <<-INPUT, ["Success!", "NoMethodError"], [], bug9452)
+ module R
+ refine Object do
+ def m
+ puts "Success!"
+ end
+
+ private(:m)
+ end
+ end
+
+ using R
+
+ m
+ 42.m rescue p($!.class)
+ INPUT
+ end
+
+ def test_making_private_method_public
+ bug9452 = '[ruby-core:60111] [Bug #9452]'
+
+ assert_in_out_err([], <<-INPUT, ["Success!", "Success!"], [], bug9452)
+ class Object
+ private
+ def m
+ end
+ end
+
+ module R
+ refine Object do
+ def m
+ puts "Success!"
+ end
+ end
+ end
+
+ using R
+ m
+ 42.m
+ INPUT
+ end
+
+ def test_refine_basic_object
+ assert_separately([], <<-"end;")
+ bug10106 = '[ruby-core:64166] [Bug #10106]'
+ module RefinementBug
+ refine BasicObject do
+ def foo
+ 1
+ end
+ end
+ end
+
+ assert_raise(NoMethodError, bug10106) {Object.new.foo}
+ end;
+
+ assert_separately([], <<-"end;")
+ bug10707 = '[ruby-core:67389] [Bug #10707]'
+ module RefinementBug
+ refine BasicObject do
+ def foo
+ end
+ end
+ end
+
+ assert(methods, bug10707)
+ assert_raise(NameError, bug10707) {method(:foo)}
+ end;
+ end
+
+ def test_change_refined_new_method_visibility
+ assert_separately([], <<-"end;")
+ bug10706 = '[ruby-core:67387] [Bug #10706]'
+ module RefinementBug
+ refine Object do
+ def foo
+ end
+ end
+ end
+
+ assert_raise(NameError, bug10706) {private(:foo)}
+ end;
+ end
+
+ def test_alias_refined_method
+ assert_separately([], <<-"end;")
+ bug10731 = '[ruby-core:67523] [Bug #10731]'
+
+ class C
+ end
+
+ module RefinementBug
+ refine C do
+ def foo
+ end
+
+ def bar
+ end
+ end
+ end
+
+ assert_raise(NameError, bug10731) do
+ class C
+ alias foo bar
+ end
+ end
+ end;
+ end
+
+ def test_singleton_method_should_not_use_refinements
+ assert_separately([], <<-"end;")
+ bug10744 = '[ruby-core:67603] [Bug #10744]'
+
+ class C
+ end
+
+ module RefinementBug
+ refine C.singleton_class do
+ def foo
+ end
+ end
+ end
+
+ assert_raise(NameError, bug10744) { C.singleton_method(:foo) }
+ end;
+ end
+
+ def test_refined_method_defined
+ assert_separately([], <<-"end;")
+ bug10753 = '[ruby-core:67656] [Bug #10753]'
+
+ c = Class.new do
+ def refined_public; end
+ def refined_protected; end
+ def refined_private; end
+
+ public :refined_public
+ protected :refined_protected
+ private :refined_private
+ end
+
+ m = Module.new do
+ refine(c) do
+ def refined_public; end
+ def refined_protected; end
+ def refined_private; end
+
+ public :refined_public
+ protected :refined_protected
+ private :refined_private
+ end
+ end
+
+ using m
+
+ assert_equal(true, c.public_method_defined?(:refined_public), bug10753)
+ assert_equal(false, c.public_method_defined?(:refined_protected), bug10753)
+ assert_equal(false, c.public_method_defined?(:refined_private), bug10753)
+
+ assert_equal(false, c.protected_method_defined?(:refined_public), bug10753)
+ assert_equal(true, c.protected_method_defined?(:refined_protected), bug10753)
+ assert_equal(false, c.protected_method_defined?(:refined_private), bug10753)
+
+ assert_equal(false, c.private_method_defined?(:refined_public), bug10753)
+ assert_equal(false, c.private_method_defined?(:refined_protected), bug10753)
+ assert_equal(true, c.private_method_defined?(:refined_private), bug10753)
+ end;
+ end
+
+ def test_undefined_refined_method_defined
+ assert_separately([], <<-"end;")
+ bug10753 = '[ruby-core:67656] [Bug #10753]'
+
+ c = Class.new
+
+ m = Module.new do
+ refine(c) do
+ def undefined_refined_public; end
+ def undefined_refined_protected; end
+ def undefined_refined_private; end
+ public :undefined_refined_public
+ protected :undefined_refined_protected
+ private :undefined_refined_private
+ end
+ end
+
+ using m
+
+ assert_equal(false, c.public_method_defined?(:undefined_refined_public), bug10753)
+ assert_equal(false, c.public_method_defined?(:undefined_refined_protected), bug10753)
+ assert_equal(false, c.public_method_defined?(:undefined_refined_private), bug10753)
+
+ assert_equal(false, c.protected_method_defined?(:undefined_refined_public), bug10753)
+ assert_equal(false, c.protected_method_defined?(:undefined_refined_protected), bug10753)
+ assert_equal(false, c.protected_method_defined?(:undefined_refined_private), bug10753)
+
+ assert_equal(false, c.private_method_defined?(:undefined_refined_public), bug10753)
+ assert_equal(false, c.private_method_defined?(:undefined_refined_protected), bug10753)
+ assert_equal(false, c.private_method_defined?(:undefined_refined_private), bug10753)
+ end;
+ end
+
+ def test_remove_refined_method
+ assert_separately([], <<-"end;")
+ bug10765 = '[ruby-core:67722] [Bug #10765]'
+
+ class C
+ def foo
+ "C#foo"
+ end
+ end
+
+ module RefinementBug
+ refine C do
+ def foo
+ "RefinementBug#foo"
+ end
+ end
+ end
+
+ using RefinementBug
+
+ class C
+ remove_method :foo
+ end
+
+ assert_equal("RefinementBug#foo", C.new.foo, bug10765)
+ end;
+ end
+
+ def test_remove_undefined_refined_method
+ assert_separately([], <<-"end;")
+ bug10765 = '[ruby-core:67722] [Bug #10765]'
+
+ class C
+ end
+
+ module RefinementBug
+ refine C do
+ def foo
+ end
+ end
+ end
+
+ using RefinementBug
+
+ assert_raise(NameError, bug10765) {
+ class C
+ remove_method :foo
+ end
+ }
+ end;
+ end
+
+ module NotIncludeSuperclassMethod
+ class X
+ def foo
+ end
+ end
+
+ class Y < X
+ end
+
+ module Bar
+ refine Y do
+ def foo
+ end
+ end
+ end
+ end
+
+ def test_instance_methods_not_include_superclass_method
+ bug10826 = '[ruby-dev:48854] [Bug #10826]'
+ assert_not_include(NotIncludeSuperclassMethod::Y.instance_methods(false),
+ :foo, bug10826)
+ assert_include(NotIncludeSuperclassMethod::Y.instance_methods(true),
+ :foo, bug10826)
+ end
+
+ def test_undef_original_method
+ assert_in_out_err([], <<-INPUT, ["NoMethodError"], [])
+ module NoPlus
+ refine String do
+ undef +
+ end
+ end
+
+ using NoPlus
+ "a" + "b" rescue p($!.class)
+ INPUT
+ end
+
+ def test_undef_prepended_method
+ bug13096 = '[ruby-core:78944] [Bug #13096]'
+ klass = EnvUtil.labeled_class("X") do
+ def foo; end
+ end
+ klass.prepend(Module.new)
+ ext = EnvUtil.labeled_module("Ext") do
+ refine klass do
+ def foo
+ end
+ end
+ end
+ assert_nothing_raised(NameError, bug13096) do
+ klass.class_eval do
+ undef :foo
+ end
+ end
+ end
+
+ def test_call_refined_method_in_duplicate_module
+ bug10885 = '[ruby-dev:48878]'
+ assert_in_out_err([], <<-INPUT, [], [], bug10885)
+ module M
+ refine Object do
+ def raise
+ # do nothing
+ end
+ end
+
+ class << self
+ using M
+ def m0
+ raise
+ end
+ end
+
+ using M
+ def M.m1
+ raise
+ end
+ end
+
+ M.dup.m0
+ M.dup.m1
+ INPUT
+ end
+
+ def test_check_funcall_undefined
+ bug11117 = '[ruby-core:69064] [Bug #11117]'
+
+ x = Class.new
+ Module.new do
+ refine x do
+ def to_regexp
+ //
+ end
+ end
+ end
+
+ assert_nothing_raised(NoMethodError, bug11117) {
+ assert_nil(Regexp.try_convert(x.new))
+ }
+ end
+
+ def test_funcall_inherited
+ bug11117 = '[ruby-core:69064] [Bug #11117]'
+
+ Module.new {refine(Dir) {def to_s; end}}
+ x = Class.new(Dir).allocate
+ assert_nothing_raised(NoMethodError, bug11117) {
+ x.inspect
+ }
+ end
+
+ def test_alias_refined_method2
+ bug11182 = '[ruby-core:69360]'
+ assert_in_out_err([], <<-INPUT, ["C"], [], bug11182)
+ class C
+ def foo
+ puts "C"
+ end
+ end
+
+ module M
+ refine C do
+ def foo
+ puts "Refined C"
+ end
+ end
+ end
+
+ class D < C
+ alias bar foo
+ end
+
+ using M
+ D.new.bar
+ INPUT
+ end
+
+ def test_reopen_refinement_module
+ assert_separately([], <<-"end;")
+ $VERBOSE = nil
+ class C
+ end
+
+ module R
+ refine C do
+ def m
+ :foo
+ end
+ end
+ end
+
+ using R
+ assert_equal(:foo, C.new.m)
+
+ module R
+ refine C do
+ def m
+ :bar
+ end
+ end
+ end
+
+ assert_equal(:bar, C.new.m, "[ruby-core:71423] [Bug #11672]")
+ end;
+ end
+
+ module MixedUsing1
+ class C
+ def foo
+ :orig_foo
+ end
+ end
+
+ module R1
+ refine C do
+ def foo
+ [:R1, super]
+ end
+ end
+ end
+
+ module_function
+
+ def foo
+ [:foo, C.new.foo]
+ end
+
+ using R1
+
+ def bar
+ [:bar, C.new.foo]
+ end
+ end
+
+ module MixedUsing2
+ class C
+ def foo
+ :orig_foo
+ end
+ end
+
+ module R1
+ refine C do
+ def foo
+ [:R1_foo, super]
+ end
+ end
+ end
+
+ module R2
+ refine C do
+ def bar
+ [:R2_bar, C.new.foo]
+ end
+
+ using R1
+
+ def baz
+ [:R2_baz, C.new.foo]
+ end
+ end
+ end
+
+ using R2
+ module_function
+ def f1; C.new.bar; end
+ def f2; C.new.baz; end
+ end
+
+ def test_mixed_using
+ assert_equal([:foo, :orig_foo], MixedUsing1.foo)
+ assert_equal([:bar, [:R1, :orig_foo]], MixedUsing1.bar)
+
+ assert_equal([:R2_bar, :orig_foo], MixedUsing2.f1)
+ assert_equal([:R2_baz, [:R1_foo, :orig_foo]], MixedUsing2.f2)
+ end
+
+ module MethodMissing
+ class Foo
+ end
+
+ module Bar
+ refine Foo do
+ def method_missing(mid, *args)
+ "method_missing refined"
+ end
+ end
+ end
+
+ using Bar
+
+ def self.call_undefined_method
+ Foo.new.foo
+ end
+ end
+
+ def test_method_missing
+ assert_raise(NoMethodError) do
+ MethodMissing.call_undefined_method
+ end
+ end
+
+ module VisibleRefinements
+ module RefA
+ refine Object do
+ def in_ref_a
+ end
+ end
+ end
+
+ module RefB
+ refine Object do
+ def in_ref_b
+ end
+ end
+ end
+
+ module RefC
+ using RefA
+
+ refine Object do
+ def in_ref_c
+ end
+ end
+ end
+
+ module Foo
+ using RefB
+ USED_MODS = Module.used_modules
+ end
+
+ module Bar
+ using RefC
+ USED_MODS = Module.used_modules
+ end
+
+ module Combined
+ using RefA
+ using RefB
+ USED_MODS = Module.used_modules
+ end
+ end
+
+ def test_used_modules
+ ref = VisibleRefinements
+ assert_equal [], Module.used_modules
+ assert_equal [ref::RefB], ref::Foo::USED_MODS
+ assert_equal [ref::RefC], ref::Bar::USED_MODS
+ assert_equal [ref::RefB, ref::RefA], ref::Combined::USED_MODS
+ end
+
+ def test_warn_setconst_in_refinmenet
+ bug10103 = '[ruby-core:64143] [Bug #10103]'
+ warnings = [
+ "-:3: warning: not defined at the refinement, but at the outer class/module",
+ "-:4: warning: not defined at the refinement, but at the outer class/module"
+ ]
+ assert_in_out_err([], <<-INPUT, [], warnings, bug10103)
+ module M
+ refine String do
+ FOO = 123
+ @@foo = 456
+ end
+ end
+ INPUT
+ end
+
+ def test_symbol_proc
+ assert_equal("FooExt#x", FooExtClient.map_x_on(Foo.new))
+ assert_equal("Foo#x", FooExtClient.return_proc(&:x).(Foo.new))
+ end
+
+ def test_symbol_proc_with_block
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ bug = '[ruby-core:80219] [Bug #13325]'
+ begin;
+ module M
+ refine Class.new do
+ end
+ end
+ class C
+ def call(a, x, &b)
+ b.call(a, &x)
+ end
+ end
+ o = C.new
+ r = nil
+ x = ->(z){r = z}
+ assert_equal(42, o.call(42, x, &:tap))
+ assert_equal(42, r)
+ using M
+ r = nil
+ assert_equal(42, o.call(42, x, &:tap), bug)
+ assert_equal(42, r, bug)
+ end;
+ end
+
+ module AliasInSubclass
+ class C
+ def foo
+ :original
+ end
+ end
+
+ class D < C
+ alias bar foo
+ end
+
+ module M
+ refine D do
+ def bar
+ :refined
+ end
+ end
+ end
+ end
+
+ def test_refine_alias_in_subclass
+ assert_equal(:refined,
+ eval_using(AliasInSubclass::M, "AliasInSubclass::D.new.bar"))
+ end
+
+ def test_refine_with_prepend
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ bug = '[ruby-core:78073] [Bug #12920]'
+ Integer.prepend(Module.new)
+ Module.new do
+ refine Integer do
+ define_method(:+) {}
+ end
+ end
+ assert_kind_of(Time, Time.now, bug)
+ end;
+ end
+
+ def test_public_in_refine
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ bug12729 = '[ruby-core:77161] [Bug #12729]'
+
+ class Cow
+ private
+ def moo() "Moo"; end
+ end
+
+ module PublicCows
+ refine(Cow) {
+ public :moo
+ }
+ end
+
+ using PublicCows
+ assert_equal("Moo", Cow.new.moo, bug12729)
+ end;
+ end
+
+ module SuperToModule
+ class Parent
+ end
+
+ class Child < Parent
+ end
+
+ module FooBar
+ refine Parent do
+ def to_s
+ "Parent"
+ end
+ end
+
+ refine Child do
+ def to_s
+ super + " -> Child"
+ end
+ end
+ end
+
+ using FooBar
+ def Child.test
+ new.to_s
+ end
+ end
+
+ def test_super_to_module
+ bug = '[ruby-core:79588] [Bug #13227]'
+ assert_equal("Parent -> Child", SuperToModule::Child.test, bug)
+ end
+
+ def test_include_refinement
+ bug = '[ruby-core:79632] [Bug #13236] cannot include refinement module'
+ r = nil
+ m = Module.new do
+ r = refine(String) {def test;:ok end}
+ end
+ assert_raise_with_message(ArgumentError, /refinement/, bug) do
+ m.module_eval {include r}
+ end
+ assert_raise_with_message(ArgumentError, /refinement/, bug) do
+ m.module_eval {prepend r}
+ end
+ end
+
+ class ParentDefiningPrivateMethod
+ private
+ def some_inherited_method
+ end
+ end
+
+ module MixinDefiningPrivateMethod
+ private
+ def some_included_method
+ end
+ end
+
+ class SomeChildClassToRefine < ParentDefiningPrivateMethod
+ include MixinDefiningPrivateMethod
+
+ private
+ def some_method
+ end
+ end
+
+ def test_refine_inherited_method_with_visibility_changes
+ Module.new do
+ refine(SomeChildClassToRefine) do
+ def some_inherited_method; end
+ def some_included_method; end
+ def some_method; end
+ end
+ end
+
+ obj = SomeChildClassToRefine.new
+
+ assert_raise_with_message(NoMethodError, /private/) do
+ obj.some_inherited_method
+ end
+
+ assert_raise_with_message(NoMethodError, /private/) do
+ obj.some_included_method
+ end
+
+ assert_raise_with_message(NoMethodError, /private/) do
+ obj.some_method
+ end
+ end
+
+ def test_refined_method_alias_warning
+ c = Class.new do
+ def t; :t end
+ def f; :f end
+ end
+ Module.new do
+ refine(c) do
+ alias foo t
+ end
+ end
+ assert_warning('', '[ruby-core:82385] [Bug #13817] refined method is not redefined') do
+ c.class_eval do
+ alias foo f
+ end
+ end
+ end
+
+ def test_using_wrong_argument
+ bug = '[ruby-dev:50270] [Bug #13956]'
+ pattern = /expected Module/
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ bug = ""#{bug.dump}
+ pattern = /#{pattern}/
+ begin;
+ assert_raise_with_message(TypeError, pattern, bug) {
+ using(1) do end
+ }
+ end;
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ bug = ""#{bug.dump}
+ pattern = /#{pattern}/
+ begin;
+ assert_raise_with_message(TypeError, pattern, bug) {
+ Module.new {using(1) {}}
+ }
+ end;
+ end
+
+ class ToString
+ c = self
+ using Module.new {refine(c) {def to_s; "ok"; end}}
+ def string
+ "#{self}"
+ end
+ end
+
+ def test_tostring
+ assert_equal("ok", ToString.new.string)
+ assert_predicate(ToString.new.taint.string, :tainted?)
+ end
+
+ class ToSymbol
+ c = self
+ using Module.new {refine(c) {def intern; "<#{upcase}>"; end}}
+ def symbol
+ :"#{@string}"
+ end
+ def initialize(string)
+ @string = string
+ end
+ end
+
+ def test_dsym_literal
+ assert_equal(:foo, ToSymbol.new("foo").symbol)
+ end
+
+ module ToProc
+ def self.call &block
+ block.call
+ end
+
+ class ReturnProc
+ c = self
+ using Module.new {
+ refine c do
+ def to_proc
+ proc { "to_proc" }
+ end
+ end
+ }
+ def call
+ ToProc.call &self
+ end
+ end
+
+ class ReturnNoProc
+ c = self
+ using Module.new {
+ refine c do
+ def to_proc
+ true
+ end
+ end
+ }
+
+ def call
+ ToProc.call &self
+ end
+ end
+
+ class PrivateToProc
+ c = self
+ using Module.new {
+ refine c do
+ private
+ def to_proc
+ proc { "private_to_proc" }
+ end
+ end
+ }
+
+ def call
+ ToProc.call &self
+ end
+ end
+
+
+ class NonProc
+ def call
+ ToProc.call &self
+ end
+ end
+
+ class MethodMissing
+ def method_missing *args
+ proc { "method_missing" }
+ end
+
+ def call
+ ToProc.call &self
+ end
+ end
+
+ class ToProcAndMethodMissing
+ def method_missing *args
+ proc { "method_missing" }
+ end
+
+ c = self
+ using Module.new {
+ refine c do
+ def to_proc
+ proc { "to_proc" }
+ end
+ end
+ }
+
+ def call
+ ToProc.call &self
+ end
+ end
+
+ class ToProcAndRefinements
+ def to_proc
+ proc { "to_proc" }
+ end
+
+ c = self
+ using Module.new {
+ refine c do
+ def to_proc
+ proc { "refinements_to_proc" }
+ end
+ end
+ }
+
+ def call
+ ToProc.call &self
+ end
+ end
+ end
+
+ def test_to_proc
+ assert_equal("to_proc", ToProc::ReturnProc.new.call)
+ assert_equal("private_to_proc", ToProc::PrivateToProc.new.call)
+ assert_raise(TypeError){ ToProc::ReturnNoProc.new.call }
+ assert_raise(TypeError){ ToProc::NonProc.new.call }
+ assert_equal("method_missing", ToProc::MethodMissing.new.call)
+ assert_equal("to_proc", ToProc::ToProcAndMethodMissing.new.call)
+ assert_equal("refinements_to_proc", ToProc::ToProcAndRefinements.new.call)
+ end
+
+ def test_unused_refinement_for_module
+ bug14068 = '[ruby-core:83613] [Bug #14068]'
+ assert_in_out_err([], <<-INPUT, ["M1#foo"], [], bug14068)
+ module M1
+ def foo
+ puts "M1#foo"
+ end
+ end
+
+ module M2
+ end
+
+ module UnusedRefinement
+ refine(M2) do
+ def foo
+ puts "M2#foo"
+ end
+ end
+ end
+
+ include M1
+ include M2
+ foo()
+ INPUT
+ end
+
+ def test_refining_module_repeatedly
+ bug14070 = '[ruby-core:83617] [Bug #14070]'
+ assert_in_out_err([], <<-INPUT, ["ok"], [], bug14070)
+ 1000.times do
+ Class.new do
+ include Enumerable
+ end
+
+ Module.new do
+ refine Enumerable do
+ def foo
+ end
+ end
+ end
+ end
+ puts "ok"
+ INPUT
+ end
+
+ def test_call_method_in_unused_refinement
+ bug15720 = '[ruby-core:91916] [Bug #15720]'
+ assert_in_out_err([], <<-INPUT, ["ok"], [], bug15720)
+ module M1
+ refine Kernel do
+ def foo
+ 'foo called!'
+ end
+ end
+ end
+
+ module M2
+ refine Kernel do
+ def bar
+ 'bar called!'
+ end
+ end
+ end
+
+ using M1
+
+ foo
+
+ begin
+ bar
+ rescue NameError
+ end
+
+ puts "ok"
+ INPUT
+ end
+
+ def test_super_from_refined_module
+ a = EnvUtil.labeled_module("A") do
+ def foo;"[A#{super}]";end
+ end
+ b = EnvUtil.labeled_class("B") do
+ def foo;"[B]";end
+ end
+ c = EnvUtil.labeled_class("C", b) do
+ include a
+ def foo;"[C#{super}]";end
+ end
+ d = EnvUtil.labeled_module("D") do
+ refine(a) do
+ def foo;end
+ end
+ end
+ assert_equal("[C[A[B]]]", c.new.foo, '[ruby-dev:50390] [Bug #14232]')
+ end
+
+ private
+
+ def eval_using(mod, s)
+ eval("using #{mod}; #{s}", Sandbox::BINDING)
+ end
+end
diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb
index 9e850b902e..038c1c830f 100644
--- a/test/ruby/test_regexp.rb
+++ b/test/ruby/test_regexp.rb
@@ -1,5 +1,6 @@
+# coding: US-ASCII
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestRegexp < Test::Unit::TestCase
def setup
@@ -11,6 +12,12 @@ class TestRegexp < Test::Unit::TestCase
$VERBOSE = @verbose
end
+ def test_has_NOENCODING
+ assert Regexp::NOENCODING
+ re = //n
+ assert_equal Regexp::NOENCODING, re.options
+ end
+
def test_ruby_dev_999
assert_match(/(?<=a).*b/, "aab")
assert_match(/(?<=\u3042).*b/, "\u3042ab")
@@ -57,6 +64,20 @@ class TestRegexp < Test::Unit::TestCase
def test_to_s
assert_equal '(?-mix:\x00)', Regexp.new("\0").to_s
+
+ str = "abcd\u3042"
+ [:UTF_16BE, :UTF_16LE, :UTF_32BE, :UTF_32LE].each do |es|
+ enc = Encoding.const_get(es)
+ rs = Regexp.new(str.encode(enc)).to_s
+ assert_equal("(?-mix:abcd\u3042)".encode(enc), rs)
+ assert_equal(enc, rs.encoding)
+ end
+ end
+
+ def test_to_s_extended_subexp
+ re = /#\g#{"\n"}/x
+ re = /#{re}/
+ assert_warn('', '[ruby-core:82328] [Bug #13798]') {re.to_s}
end
def test_union
@@ -69,6 +90,16 @@ class TestRegexp < Test::Unit::TestCase
rescue ArgumentError
:ok
end
+ re = Regexp.union(/\//, "")
+ re2 = eval(re.inspect)
+ assert_equal(re.to_s, re2.to_s)
+ assert_equal(re.source, re2.source)
+ assert_equal(re, re2)
+ end
+
+ def test_word_boundary
+ assert_match(/\u3042\b /, "\u3042 ")
+ assert_not_match(/\u3042\ba/, "\u3042a")
end
def test_named_capture
@@ -91,21 +122,29 @@ class TestRegexp < Test::Unit::TestCase
assert_equal('#<MatchData "&amp; y" foo:"amp" foo:"y">',
/&(?<foo>.*?); (?<foo>y)/.match("aaa &amp; yyy").inspect)
- /(?<id>[A-Za-z_]+)/ =~ "!abc"
- assert_equal("abc", Regexp.last_match(:id))
+ /(?<_id>[A-Za-z_]+)/ =~ "!abc"
+ assert_not_nil(Regexp.last_match)
+ assert_equal("abc", Regexp.last_match(1))
+ assert_equal("abc", Regexp.last_match(:_id))
/a/ =~ "b" # doesn't match.
assert_equal(nil, Regexp.last_match)
assert_equal(nil, Regexp.last_match(1))
assert_equal(nil, Regexp.last_match(:foo))
+ bug11825_name = "\u{5b9d 77f3}"
+ bug11825_str = "\u{30eb 30d3 30fc}"
+ bug11825_re = /(?<#{bug11825_name}>)#{bug11825_str}/
+
assert_equal(["foo", "bar"], /(?<foo>.)(?<bar>.)/.names)
assert_equal(["foo"], /(?<foo>.)(?<foo>.)/.names)
assert_equal([], /(.)(.)/.names)
+ assert_equal([bug11825_name], bug11825_re.names)
assert_equal(["foo", "bar"], /(?<foo>.)(?<bar>.)/.match("ab").names)
assert_equal(["foo"], /(?<foo>.)(?<foo>.)/.match("ab").names)
assert_equal([], /(.)(.)/.match("ab").names)
+ assert_equal([bug11825_name], bug11825_re.match(bug11825_str).names)
assert_equal({"foo"=>[1], "bar"=>[2]},
/(?<foo>.)(?<bar>.)/.named_captures)
@@ -116,14 +155,60 @@ class TestRegexp < Test::Unit::TestCase
assert_equal("a[b]c", "abc".sub(/(?<x>[bc])/, "[\\k<x>]"))
assert_equal("o", "foo"[/(?<bar>o)/, "bar"])
+ assert_equal("o", "foo"[/(?<@bar>o)/, "@bar"])
+ assert_equal("o", "foo"[/(?<@bar>.)\g<@bar>\k<@bar>/, "@bar"])
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
+ bug9902 = '[ruby-dev:48275] [Bug #9902]'
+
+ m = /(?<a>.*)/.match("foo")
+ assert_raise(IndexError, bug9902) {m["a\0foo"]}
+ assert_raise(IndexError, bug9902) {m["a\0foo".to_sym]}
+
+ m = Regexp.new("(?<foo\0bar>.*)").match("xxx")
+ assert_raise(IndexError, bug9902) {m["foo"]}
+ assert_raise(IndexError, bug9902) {m["foo".to_sym]}
+ assert_nothing_raised(IndexError, bug9902) {
+ assert_equal("xxx", m["foo\0bar"], bug9902)
+ assert_equal("xxx", m["foo\0bar".to_sym], bug9902)
+ }
+ end
+
+ def test_named_capture_nonascii
+ bug9903 = '[ruby-dev:48278] [Bug #9903]'
+
+ key = "\xb1\xb2".force_encoding(Encoding::EUC_JP)
+ m = /(?<#{key}>.*)/.match("xxx")
+ assert_equal("xxx", m[key])
+ assert_raise(IndexError, bug9903) {m[key.dup.force_encoding(Encoding::Shift_JIS)]}
+ end
+
+ def test_match_data_named_captures
+ assert_equal({'a' => '1', 'b' => '2', 'c' => nil}, /^(?<a>.)(?<b>.)(?<c>.)?/.match('12').named_captures)
+ assert_equal({'a' => '1', 'b' => '2', 'c' => '3'}, /^(?<a>.)(?<b>.)(?<c>.)?/.match('123').named_captures)
+ assert_equal({'a' => '1', 'b' => '2', 'c' => ''}, /^(?<a>.)(?<b>.)(?<c>.?)/.match('12').named_captures)
+
+ assert_equal({'a' => 'x'}, /(?<a>x)|(?<a>y)/.match('x').named_captures)
+ assert_equal({'a' => 'y'}, /(?<a>x)|(?<a>y)/.match('y').named_captures)
+
+ assert_equal({'a' => '1', 'b' => '2'}, /^(.)(?<a>.)(?<b>.)/.match('012').named_captures)
+ assert_equal({'a' => '2'}, /^(?<a>.)(?<a>.)/.match('12').named_captures)
+
+ assert_equal({}, /^(.)/.match('123').named_captures)
end
def test_assign_named_capture
assert_equal("a", eval('/(?<foo>.)/ =~ "a"; foo'))
+ assert_equal(nil, eval('/(?<@foo>.)/ =~ "a"; defined?(@foo)'))
assert_equal("a", eval('foo = 1; /(?<foo>.)/ =~ "a"; foo'))
assert_equal("a", eval('1.times {|foo| /(?<foo>.)/ =~ "a"; break foo }'))
assert_nothing_raised { eval('/(?<Foo>.)/ =~ "a"') }
@@ -132,7 +217,33 @@ class TestRegexp < Test::Unit::TestCase
def test_assign_named_capture_to_reserved_word
/(?<nil>.)/ =~ "a"
- assert(!local_variables.include?(:nil), "[ruby-dev:32675]")
+ 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
+ %W[C \u{1d402}].each do |name|
+ assert_equal(:ok, Class.new.class_eval("#{name} = :ok; /(?<#{name}>.*)/ =~ 'ng'; #{name}"))
+ end
+ end
+
+ def test_assign_named_capture_trace
+ bug = '[ruby-core:79940] [Bug #13287]'
+ assert_normal_exit("#{<<-"begin;"}\n#{<<-"end;"}", bug)
+ begin;
+ / (?<foo>.*)/ =~ "bar" &&
+ true
+ end;
end
def test_match_regexp
@@ -144,7 +255,56 @@ class TestRegexp < Test::Unit::TestCase
end
def test_source
+ bug5484 = '[ruby-core:40364]'
assert_equal('', //.source)
+ assert_equal('\:', /\:/.source, bug5484)
+ assert_equal(':', %r:\::.source, bug5484)
+ end
+
+ def test_source_escaped
+ expected, result = "$*+.?^|".each_char.map {|c|
+ [
+ ["\\#{c}", "\\#{c}", 1],
+ begin
+ re = eval("%r#{c}\\#{c}#{c}", nil, __FILE__, __LINE__)
+ t = eval("/\\#{c}/", nil, __FILE__, __LINE__).source
+ rescue SyntaxError => e
+ [e, t, nil]
+ else
+ [re.source, t, re =~ "a#{c}a"]
+ end
+ ]
+ }.transpose
+ assert_equal(expected, result)
+ end
+
+ def test_source_escaped_paren
+ bug7610 = '[ruby-core:51088] [Bug #7610]'
+ bug8133 = '[ruby-core:53578] [Bug #8133]'
+ [
+ ["(", ")", bug7610], ["[", "]", bug8133],
+ ["{", "}", bug8133], ["<", ">", bug8133],
+ ].each do |lparen, rparen, bug|
+ s = "\\#{lparen}a\\#{rparen}"
+ assert_equal(/#{s}/, eval("%r#{lparen}#{s}#{rparen}"), bug)
+ end
+ end
+
+ def test_source_unescaped
+ expected, result = "!\"#%&',-/:;=@_`~".each_char.map {|c|
+ [
+ ["#{c}", "\\#{c}", 1],
+ begin
+ re = eval("%r#{c}\\#{c}#{c}", nil, __FILE__, __LINE__)
+ t = eval("%r{\\#{c}}", nil, __FILE__, __LINE__).source
+ rescue SyntaxError => e
+ [e, t, nil]
+ else
+ [re.source, t, re =~ "a#{c}a"]
+ end
+ ]
+ }.transpose
+ assert_equal(expected, result)
end
def test_inspect
@@ -155,8 +315,8 @@ class TestRegexp < Test::Unit::TestCase
assert_equal('/\/x/i', /\/x/i.inspect)
assert_equal('/\x00/i', /#{"\0"}/i.inspect)
assert_equal("/\n/i", /#{"\n"}/i.inspect)
- s = [0xff].pack("C")
- assert_equal('/\/\xFF/i', /\/#{s}/i.inspect)
+ s = [0xf1, 0xf2, 0xf3].pack("C*")
+ assert_equal('/\/\xF1\xF2\xF3/i', /\/#{s}/i.inspect)
end
def test_char_to_option
@@ -255,15 +415,46 @@ class TestRegexp < Test::Unit::TestCase
def test_match_aref
m = /(...)(...)(...)(...)?/.match("foobarbaz")
+ assert_equal("foobarbaz", m[0])
assert_equal("foo", m[1])
+ assert_equal("foo", m[-4])
+ assert_nil(m[-1])
+ assert_nil(m[-11])
+ assert_nil(m[-11, 1])
+ assert_nil(m[-11..1])
+ assert_nil(m[5])
+ assert_nil(m[9])
assert_equal(["foo", "bar", "baz"], m[1..3])
+ assert_equal(["foo", "bar", "baz"], m[1, 3])
+ assert_equal([], m[3..1])
+ assert_equal([], m[3, 0])
+ assert_equal(nil, m[3, -1])
+ assert_equal(nil, m[9, 1])
+ assert_equal(["baz"], m[3, 1])
+ assert_equal(["baz", nil], m[3, 5])
assert_nil(m[5])
assert_raise(IndexError) { m[:foo] }
+ assert_raise(TypeError) { m[nil] }
end
def test_match_values_at
+ idx = Object.new
+ def idx.to_int; 2; end
m = /(...)(...)(...)(...)?/.match("foobarbaz")
assert_equal(["foo", "bar", "baz"], m.values_at(1, 2, 3))
+ assert_equal(["foo", "bar", "baz"], m.values_at(1..3))
+ assert_equal(["foo", "bar", "baz", nil, nil], m.values_at(1..5))
+ assert_equal([], m.values_at(3..1))
+ assert_equal([nil, nil, nil, nil, nil], m.values_at(5..9))
+ assert_equal(["bar"], m.values_at(idx))
+ assert_raise(RangeError){ m.values_at(-11..1) }
+ assert_raise(TypeError){ m.values_at(nil) }
+
+ m = /(?<a>\d+) *(?<op>[+\-*\/]) *(?<b>\d+)/.match("1 + 2")
+ assert_equal(["1", "2", "+"], m.values_at(:a, 'b', :op))
+ assert_equal(["+"], m.values_at(idx))
+ assert_raise(TypeError){ m.values_at(nil) }
+ assert_raise(IndexError){ m.values_at(:foo) }
end
def test_match_string
@@ -279,14 +470,6 @@ class TestRegexp < Test::Unit::TestCase
def test_initialize
assert_raise(ArgumentError) { Regexp.new }
assert_equal(/foo/, Regexp.new(/foo/, Regexp::IGNORECASE))
- re = /foo/
- assert_raise(SecurityError) do
- Thread.new { $SAFE = 4; re.instance_eval { initialize(re) } }.join
- end
- re.taint
- assert_raise(SecurityError) do
- Thread.new { $SAFE = 4; re.instance_eval { initialize(re) } }.join
- end
assert_equal(Encoding.find("US-ASCII"), Regexp.new("b..", nil, "n").encoding)
assert_equal("bar", "foobarbaz"[Regexp.new("b..", nil, "n")])
@@ -297,6 +480,9 @@ class TestRegexp < Test::Unit::TestCase
assert_equal(arg_encoding_none, Regexp.new("", nil, "N").options)
assert_raise(RegexpError) { Regexp.new(")(") }
+ assert_raise(RegexpError) { Regexp.new('[\\40000000000') }
+ assert_raise(RegexpError) { Regexp.new('[\\600000000000.') }
+ assert_raise(RegexpError) { Regexp.new("((?<v>))\\g<0>") }
end
def test_unescape
@@ -319,7 +505,7 @@ class TestRegexp < Test::Unit::TestCase
assert_equal(/a/, eval(%q(s="\u0061";/#{s}/n)))
assert_raise(RegexpError) { s = "\u3042"; eval(%q(/#{s}/n)) }
assert_raise(RegexpError) { s = "\u0061"; eval(%q(/\u3042#{s}/n)) }
- assert_raise(RegexpError) { s1=[0xff].pack("C"); s2="\u3042"; eval(%q(/#{s1}#{s2}/)) }
+ assert_raise(RegexpError) { s1=[0xff].pack("C"); s2="\u3042"; eval(%q(/#{s1}#{s2}/)); [s1, s2] }
assert_raise(ArgumentError) { s = '\x'; /#{ s }/ }
@@ -355,12 +541,16 @@ class TestRegexp < Test::Unit::TestCase
s = ".........."
5.times { s.sub!(".", "") }
assert_equal(".....", s)
+
+ assert_equal("\\\u{3042}", Regexp.new("\\\u{3042}").source)
end
def test_equal
- assert_equal(true, /abc/ == /abc/)
- assert_equal(false, /abc/ == /abc/m)
- assert_equal(false, /abc/ == /abd/)
+ bug5484 = '[ruby-core:40364]'
+ assert_equal(/abc/, /abc/)
+ assert_not_equal(/abc/, /abc/m)
+ assert_not_equal(/abc/, /abd/)
+ assert_equal(/\/foo/, Regexp.new('/foo'), bug5484)
end
def test_match
@@ -382,6 +572,30 @@ class TestRegexp < Test::Unit::TestCase
$_ = nil; assert_nil(~/./)
end
+ def test_match_p
+ /backref/ =~ 'backref'
+ # must match here, but not in a separate method, e.g., assert_send,
+ # to check if $~ is affected or not.
+ assert_equal(false, //.match?(nil))
+ assert_equal(true, //.match?(""))
+ assert_equal(true, /.../.match?(:abc))
+ assert_raise(TypeError) { /.../.match?(Object.new) }
+ assert_equal(true, /b/.match?('abc'))
+ assert_equal(true, /b/.match?('abc', 1))
+ assert_equal(true, /../.match?('abc', 1))
+ assert_equal(true, /../.match?('abc', -2))
+ assert_equal(false, /../.match?("abc", -4))
+ assert_equal(false, /../.match?("abc", 4))
+ assert_equal(true, /../.match?("\u3042xx", 1))
+ assert_equal(false, /../.match?("\u3042x", 1))
+ assert_equal(true, /\z/.match?(""))
+ assert_equal(true, /\z/.match?("abc"))
+ assert_equal(true, /R.../.match?("Ruby"))
+ assert_equal(false, /R.../.match?("Ruby", 1))
+ assert_equal(false, /P.../.match?("Ruby"))
+ assert_equal('backref', $&)
+ end
+
def test_eqq
assert_equal(false, /../ === nil)
end
@@ -396,6 +610,10 @@ class TestRegexp < Test::Unit::TestCase
assert_equal("\\v", Regexp.quote("\v"))
assert_equal("\u3042\\t", Regexp.quote("\u3042\t"))
assert_equal("\\t\xff", Regexp.quote("\t" + [0xff].pack("C")))
+
+ bug13034 = '[ruby-core:78646] [Bug #13034]'
+ str = "\x00".force_encoding("UTF-16BE")
+ assert_equal(str, Regexp.quote(str), bug13034)
end
def test_try_convert
@@ -437,6 +655,12 @@ class TestRegexp < Test::Unit::TestCase
assert_equal('foo[\z]baz', "foobarbaz".sub!(/(b..)/, '[\z]'))
end
+ def test_regsub_K
+ bug8856 = '[ruby-dev:47694] [Bug #8856]'
+ result = "foobarbazquux/foobarbazquux".gsub(/foo\Kbar/, "")
+ assert_equal('foobazquux/foobazquux', result, bug8856)
+ end
+
def test_KCODE
assert_nil($KCODE)
assert_nothing_raised { $KCODE = nil }
@@ -444,6 +668,16 @@ class TestRegexp < Test::Unit::TestCase
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/) { $= }
+ end
+
def test_match_setter
/foo/ =~ "foo"
m = $~
@@ -452,6 +686,33 @@ class TestRegexp < Test::Unit::TestCase
assert_equal("foo", $&)
end
+ def test_match_without_regexp
+ # create a MatchData for each assertion because the internal state may change
+ test = proc {|&blk| "abc".sub("a", ""); blk.call($~) }
+
+ bug10877 = '[ruby-core:68209] [Bug #10877]'
+ 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]} }
+ end
+ test.call {|m| assert_equal(/a/, m.regexp) }
+ test.call {|m| assert_equal("abc", m.string) }
+ test.call {|m| assert_equal(1, m.size) }
+ test.call {|m| assert_equal(0, m.begin(0)) }
+ test.call {|m| assert_equal(1, m.end(0)) }
+ test.call {|m| assert_equal([0, 1], m.offset(0)) }
+ test.call {|m| assert_equal([], m.captures) }
+ test.call {|m| assert_equal([], m.names) }
+ test.call {|m| assert_equal({}, m.named_captures) }
+ test.call {|m| assert_equal(/a/.match("abc"), m) }
+ test.call {|m| assert_equal(/a/.match("abc").hash, m.hash) }
+ test.call {|m| assert_equal("bc", m.post_match) }
+ test.call {|m| assert_equal("", m.pre_match) }
+ test.call {|m| assert_equal(["a", nil], m.values_at(0, 1)) }
+ end
+
def test_last_match
/(...)(...)(...)(...)?/.match("foobarbaz")
assert_equal("foobarbaz", Regexp.last_match(0))
@@ -479,35 +740,24 @@ class TestRegexp < Test::Unit::TestCase
assert_equal(3, "foobarbaz\u3042".rindex(/b../n, 5))
end
- def test_taint
- m = Thread.new do
- "foo"[/foo/]
- $SAFE = 4
- /foo/.match("foo")
- end.value
- assert(m.tainted?)
- assert_nothing_raised('[ruby-core:26137]') {
- m = proc {$SAFE = 4; %r"#{ }"o}.call
- }
- assert(m.tainted?)
- end
-
- def check(re, ss, fs = [])
+ def assert_regexp(re, ss, fs = [], msg = nil)
re = Regexp.new(re) unless re.is_a?(Regexp)
ss = [ss] unless ss.is_a?(Array)
ss.each do |e, s|
s ||= e
- assert_match(re, s)
+ assert_match(re, s, msg)
m = re.match(s)
- assert_equal(e, m[0])
+ assert_equal(e, m[0], msg)
end
fs = [fs] unless fs.is_a?(Array)
- fs.each {|s| assert_no_match(re, s) }
+ fs.each {|s| assert_no_match(re, s, msg) }
end
+ alias check assert_regexp
- def failcheck(re)
+ def assert_fail(re)
assert_raise(RegexpError) { %r"#{ re }" }
end
+ alias failcheck assert_fail
def test_parse
check(/\*\+\?\{\}\|\(\)\<\>\`\'/, "*+?{}|()<>`'")
@@ -524,7 +774,7 @@ class TestRegexp < Test::Unit::TestCase
check(/\A\80\z/, "80", ["\100", ""])
check(/\A\77\z/, "?")
check(/\A\78\z/, "\7" + '8', ["\100", ""])
- check(/\A\Qfoo\E\z/, "QfooE")
+ check(eval('/\A\Qfoo\E\z/'), "QfooE")
check(/\Aa++\z/, "aaa")
check('\Ax]\z', "x]")
check(/x#foo/x, "x", "#foo")
@@ -583,6 +833,9 @@ class TestRegexp < Test::Unit::TestCase
failcheck('(?<!.*)')
check(/(?<=A|B.)C/, [%w(C AC), %w(C BXC)], %w(C BC))
check(/(?<!A|B.)C/, [%w(C C), %w(C BC)], %w(AC BXC))
+
+ assert_not_match(/(?<!aa|b)c/i, "Aac")
+ assert_not_match(/(?<!b|aa)c/i, "Aac")
end
def test_parse_kg
@@ -679,7 +932,7 @@ class TestRegexp < Test::Unit::TestCase
check(/\A[a-b-]\z/, %w(a b -), ["", "c"])
check('\A[a-b-&&\w]\z', %w(a b), ["", "-"])
check('\A[a-b-&&\W]\z', "-", ["", "a", "b"])
- check('\A[a-c-e]\z', %w(a b c e), %w(- d)) # is it OK?
+ check('\A[a-c-e]\z', %w(a b c e -), %w(d))
check(/\A[a-f&&[^b-c]&&[^e]]\z/, %w(a d f), %w(b c e g 0))
check(/\A[[^b-c]&&[^e]&&a-f]\z/, %w(a d f), %w(b c e g 0))
check(/\A[\n\r\t]\z/, ["\n", "\r", "\t"])
@@ -695,9 +948,9 @@ class TestRegexp < Test::Unit::TestCase
def test_posix_bracket
check(/\A[[:alpha:]0]\z/, %w(0 a), %w(1 .))
- check(/\A[[:^alpha:]0]\z/, %w(0 1 .), "a")
- check(/\A[[:alpha\:]]\z/, %w(a l p h a :), %w(b 0 1 .))
- check(/\A[[:alpha:foo]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(/\A[[:xdigit:]&&[:alpha:]]\z/, "a", %w(g 0))
check('\A[[:abcdefghijklmnopqrstu:]]+\z', "[]")
failcheck('[[:alpha')
@@ -711,6 +964,29 @@ class TestRegexp < Test::Unit::TestCase
assert_no_match(/[[:ascii:]]/, "\x80\xFF")
end
+ def test_cclass_R
+ assert_match /\A\R\z/, "\r"
+ assert_match /\A\R\z/, "\n"
+ assert_match /\A\R\z/, "\r\n"
+ end
+
+ def test_cclass_X
+ assert_match /\A\X\z/, "\u{20 200d}"
+ assert_match /\A\X\z/, "\u{600 600}"
+ assert_match /\A\X\z/, "\u{600 20}"
+ assert_match /\A\X\z/, "\u{261d 1F3FB}"
+ assert_match /\A\X\z/, "\u{1f600}"
+ assert_match /\A\X\z/, "\u{20 324}"
+ assert_match /\A\X\X\z/, "\u{a 324}"
+ assert_match /\A\X\X\z/, "\u{d 324}"
+ assert_match /\A\X\z/, "\u{1F477 1F3FF 200D 2640 FE0F}"
+ assert_match /\A\X\z/, "\u{1F468 200D 1F393}"
+ assert_match /\A\X\z/, "\u{1F46F 200D 2642 FE0F}"
+ assert_match /\A\X\z/, "\u{1f469 200d 2764 fe0f 200d 1f469}"
+
+ assert_warning('') {/\X/ =~ "\u{a0}"}
+ end
+
def test_backward
assert_equal(3, "foobar".rindex(/b.r/i))
assert_equal(nil, "foovar".rindex(/b.r/i))
@@ -736,6 +1012,7 @@ class TestRegexp < Test::Unit::TestCase
assert_raise(TypeError) { Regexp.allocate.names }
assert_raise(TypeError) { Regexp.allocate.named_captures }
+ assert_raise(TypeError) { MatchData.allocate.hash }
assert_raise(TypeError) { MatchData.allocate.regexp }
assert_raise(TypeError) { MatchData.allocate.names }
assert_raise(TypeError) { MatchData.allocate.size }
@@ -794,44 +1071,238 @@ class TestRegexp < Test::Unit::TestCase
#assert_match(/^(\ufb05)\1\1$/i, "\ufb05\ufb06st") # this must be bug...
assert_match(/^\ufb05{3}$/i, "\ufb05\ufb06st")
assert_match(/^\u03b9\u0308\u0301$/i, "\u0390")
- assert_nothing_raised { 0x03ffffff.chr("utf-8").size }
- assert_nothing_raised { 0x7fffffff.chr("utf-8").size }
end
+ def test_unicode_age
+ assert_match(/^\p{Age=6.0}$/u, "\u261c")
+ assert_match(/^\p{Age=1.1}$/u, "\u261c")
+ assert_no_match(/^\P{age=6.0}$/u, "\u261c")
+
+ assert_match(/^\p{age=6.0}$/u, "\u31f6")
+ assert_match(/^\p{age=3.2}$/u, "\u31f6")
+ assert_no_match(/^\p{age=3.1}$/u, "\u31f6")
+ assert_no_match(/^\p{age=3.0}$/u, "\u31f6")
+ assert_no_match(/^\p{age=1.1}$/u, "\u31f6")
+
+ assert_match(/^\p{age=6.0}$/u, "\u2754")
+ assert_no_match(/^\p{age=5.0}$/u, "\u2754")
+ assert_no_match(/^\p{age=4.0}$/u, "\u2754")
+ assert_no_match(/^\p{age=3.0}$/u, "\u2754")
+ assert_no_match(/^\p{age=2.0}$/u, "\u2754")
+ assert_no_match(/^\p{age=1.1}$/u, "\u2754")
+
+ assert_no_match(/^\p{age=12.0}$/u, "\u32FF")
+ assert_match(/^\p{age=12.1}$/u, "\u32FF")
+ end
+
+ MatchData_A = eval("class MatchData_\u{3042} < MatchData; self; end")
+
def test_matchdata
a = "haystack".match(/hay/)
b = "haystack".match(/hay/)
assert_equal(a, b, '[ruby-core:24748]')
h = {a => 42}
assert_equal(42, h[b], '[ruby-core:24748]')
+ assert_match(/#<TestRegexp::MatchData_\u{3042}:/, MatchData_A.allocate.inspect)
+
+ h = /^(?<@time>\d+): (?<body>.*)/.match("123456: hoge fuga")
+ assert_equal("123456", h["@time"])
+ assert_equal("hoge fuga", h["body"])
end
- def test_regexp_poped
+ def test_regexp_popped
assert_nothing_raised { eval("a = 1; /\#{ a }/; a") }
assert_nothing_raised { eval("a = 1; /\#{ a }/o; a") }
end
- def test_optimize_last_anycharstar
- s = "1" + " " * 5000000
- assert_nothing_raised { s.match(/(\d) (.*)/) }
- assert_equal("1", $1)
- assert_equal(" " * 4999999, $2)
- end
-
def test_invalid_fragment
bug2547 = '[ruby-core:27374]'
assert_raise(SyntaxError, bug2547) {eval('/#{"\\\\"}y/')}
end
def test_dup_warn
- assert_in_out_err(%w/-w -U/, "#coding:utf-8\nx=/[\u3042\u3041]/\n!x", [], [])
- assert_in_out_err(%w/-w -U/, "#coding:utf-8\nx=/[\u3042\u3042]/\n!x", [], /duplicated/u, nil,
- encoding: Encoding::UTF_8)
- assert_in_out_err(%w/-w -U/, "#coding:utf-8\nx=/[\u3042\u3041-\u3043]/\n!x", [], /duplicated/u, nil,
- encoding: Encoding::UTF_8)
+ assert_warning(/duplicated/) { Regexp.new('[\u3042\u3043\u3042]') }
+ assert_warning(/duplicated/) { Regexp.new('[\u3042\u3043\u3043]') }
+ assert_warning(/\A\z/) { Regexp.new('[\u3042\u3044\u3043]') }
+ assert_warning(/\A\z/) { Regexp.new('[\u3042\u3045\u3043]') }
+ assert_warning(/\A\z/) { Regexp.new('[\u3042\u3045\u3044]') }
+ assert_warning(/\A\z/) { Regexp.new('[\u3042\u3045\u3043-\u3044]') }
+ assert_warning(/duplicated/) { Regexp.new('[\u3042\u3045\u3042-\u3043]') }
+ assert_warning(/duplicated/) { Regexp.new('[\u3042\u3045\u3044-\u3045]') }
+ assert_warning(/\A\z/) { Regexp.new('[\u3042\u3046\u3044]') }
+ assert_warning(/duplicated/) { Regexp.new('[\u1000-\u2000\u3042-\u3046\u3044]') }
+ assert_warning(/duplicated/) { Regexp.new('[\u3044\u3041-\u3047]') }
+ assert_warning(/duplicated/) { Regexp.new('[\u3042\u3044\u3046\u3041-\u3047]') }
+
+ bug7471 = '[ruby-core:50344]'
+ assert_warning('', bug7471) { Regexp.new('[\D]') =~ "\u3042" }
+
+ bug8151 = '[ruby-core:53649]'
+ assert_warning(/\A\z/, bug8151) { Regexp.new('(?:[\u{33}])').to_s }
end
def test_property_warn
assert_in_out_err('-w', 'x=/\p%s/', [], %r"warning: invalid Unicode Property \\p: /\\p%s/")
end
+
+ def test_invalid_escape_error
+ bug3539 = '[ruby-core:31048]'
+ error = assert_raise(SyntaxError) {eval('/\x/', nil, bug3539)}
+ assert_match(/invalid hex escape/, error.message)
+ assert_equal(1, error.message.scan(/.*invalid .*escape.*/i).size, bug3539)
+ end
+
+ 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)
+ end
+
+ def test_error_message_on_failed_conversion
+ bug7539 = '[ruby-core:50733]'
+ assert_equal false, /x/=== 42
+ assert_raise_with_message(TypeError, 'no implicit conversion of Integer into String', bug7539) {
+ Regexp.quote(42)
+ }
+ end
+
+ def test_conditional_expression
+ bug8583 = '[ruby-dev:47480] [Bug #8583]'
+
+ conds = {"xy"=>true, "yx"=>true, "xx"=>false, "yy"=>false}
+ assert_match_each(/\A((x)|(y))(?(2)y|x)\z/, conds, bug8583)
+ assert_match_each(/\A((?<x>x)|(?<y>y))(?(<x>)y|x)\z/, conds, bug8583)
+
+ bug12418 = '[ruby-core:75694] [Bug #12418]'
+ assert_raise(RegexpError, bug12418){ Regexp.new('(0?0|(?(5)||)|(?(5)||))?') }
+ end
+
+ def test_options_in_look_behind
+ assert_nothing_raised {
+ assert_match_at("(?<=(?i)ab)cd", "ABcd", [[2,4]])
+ assert_match_at("(?<=(?i:ab))cd", "ABcd", [[2,4]])
+ assert_match_at("(?<!(?i)ab)cd", "aacd", [[2,4]])
+ assert_match_at("(?<!(?i:ab))cd", "aacd", [[2,4]])
+
+ assert_not_match("(?<=(?i)ab)cd", "ABCD")
+ assert_not_match("(?<=(?i:ab))cd", "ABCD")
+ assert_not_match("(?<!(?i)ab)cd", "ABcd")
+ assert_not_match("(?<!(?i:ab))cd", "ABcd")
+ }
+ end
+
+ def test_once
+ pr1 = proc{|i| /#{i}/o}
+ assert_equal(/0/, pr1.call(0))
+ assert_equal(/0/, pr1.call(1))
+ assert_equal(/0/, pr1.call(2))
+ end
+
+ def test_once_recursive
+ pr2 = proc{|i|
+ if i > 0
+ /#{pr2.call(i-1).to_s}#{i}/
+ else
+ //
+ end
+ }
+ assert_equal(/(?-mix:(?-mix:(?-mix:)1)2)3/, pr2.call(3))
+ end
+
+ def test_once_multithread
+ m = Thread::Mutex.new
+ pr3 = proc{|i|
+ /#{m.unlock; sleep 0.5; i}/o
+ }
+ ary = []
+ n = 0
+ th1 = Thread.new{m.lock; ary << pr3.call(n+=1)}
+ th2 = Thread.new{m.lock; ary << pr3.call(n+=1)}
+ th1.join; th2.join
+ assert_equal([/1/, /1/], ary)
+ end
+
+ def test_once_escape
+ pr4 = proc{|i|
+ catch(:xyzzy){
+ /#{throw :xyzzy, i}/o =~ ""
+ :ng
+ }
+ }
+ assert_equal(0, pr4.call(0))
+ assert_equal(1, pr4.call(1))
+ end
+
+ def test_eq_tilde_can_be_overridden
+ assert_separately([], <<-RUBY)
+ class Regexp
+ undef =~
+ def =~(str)
+ "foo"
+ end
+ end
+
+ assert_equal("foo", // =~ "")
+ RUBY
+ end
+
+ def test_invalid_free_at_parse_depth_limit_over
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ begin
+ require '-test-/regexp'
+ rescue LoadError
+ else
+ bug = '[ruby-core:79624] [Bug #13234]'
+ Bug::Regexp.parse_depth_limit = 10
+ src = "[" * 100
+ 3.times do
+ assert_raise_with_message(RegexpError, /parse depth limit over/, bug) do
+ Regexp.new(src)
+ end
+ end
+ end
+ end;
+ end
+
+ def test_absent
+ assert_equal(0, /(?~(a|c)c)/ =~ "abb")
+ assert_equal("abb", $&)
+
+ assert_equal(0, /\/\*((?~\*\/))\*\// =~ "/*abc*def/xyz*/ /* */")
+ assert_equal("abc*def/xyz", $1)
+
+ assert_equal(0, /(?~(a)c)/ =~ "abb")
+ assert_nil($1)
+ 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)
+
+ match = re.match(str)
+
+ assert_not_nil match, message(msg) {
+ "Expected #{re.inspect} to match #{str.inspect}"
+ }
+
+ if match
+ actual_positions = (0...match.size).map { |i|
+ [match.begin(i), match.end(i)]
+ }
+
+ assert_equal positions, actual_positions, message(msg) {
+ "Expected #{re.inspect} to match #{str.inspect} at: #{positions.inspect}"
+ }
+ end
+ end
+
+ def assert_match_each(re, conds, msg = nil)
+ errs = conds.select {|str, match| match ^ (re =~ str)}
+ msg = message(msg) {
+ "Expected #{re.inspect} to\n" +
+ errs.map {|str, match| "\t#{'not ' unless match}match #{str.inspect}"}.join(",\n")
+ }
+ assert_empty(errs, msg)
+ end
end
diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb
index acd9551a55..af8e6e30fa 100644
--- a/test/ruby/test_require.rb
+++ b/test/ruby/test_require.rb
@@ -1,38 +1,47 @@
+# frozen_string_literal: false
require 'test/unit'
require 'tempfile'
-require_relative 'envutil'
require 'tmpdir'
class TestRequire < Test::Unit::TestCase
- def test_require_invalid_shared_object
- t = Tempfile.new(["test_ruby_test_require", ".so"])
- t.puts "dummy"
- t.close
+ def test_load_error_path
+ filename = "should_not_exist"
+ error = assert_raise(LoadError) do
+ require filename
+ end
+ assert_equal filename, error.path
+ end
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
- begin
- require \"#{ t.path }\"
- rescue LoadError
- p :ok
- end
- INPUT
+ def test_require_invalid_shared_object
+ Tempfile.create(["test_ruby_test_require", ".so"]) {|t|
+ t.puts "dummy"
+ t.close
+
+ assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}")
+ begin;
+ $:.replace([IO::NULL])
+ assert_raise(LoadError) do
+ require \"#{ t.path }\"
+ end
+ end;
+ }
end
def test_require_too_long_filename
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
- begin
+ assert_separately(["RUBYOPT"=>nil], "#{<<~"begin;"}\n#{<<~"end;"}")
+ begin;
+ $:.replace([IO::NULL])
+ assert_raise(LoadError) do
require '#{ "foo/" * 10000 }foo'
- rescue LoadError
- p :ok
end
- INPUT
+ end;
begin
- assert_in_out_err(["-S", "foo/" * 10000 + "foo"], "") do |r, e|
+ assert_in_out_err(["-S", "-w", "foo/" * 1024 + "foo"], "") do |r, e|
assert_equal([], r)
assert_operator(2, :<=, e.size)
- assert_equal("openpath: pathname too long (ignored)", e.first)
+ assert_match(/warning: openpath: pathname too long \(ignored\)/, e.first)
assert_match(/\(LoadError\)/, e.last)
end
rescue Errno::EINVAL
@@ -40,33 +49,230 @@ class TestRequire < Test::Unit::TestCase
end
end
- def test_require_path_home
+ 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}
+ end
+ end
+
+ def test_require_nonascii_path
+ bug8165 = '[ruby-core:53733] [Bug #8165]'
+ encoding = 'filesystem'
+ assert_require_nonascii_path(encoding, bug8165)
+ end
+
+ def test_require_insecure_path
+ assert_require_insecure_path("foo")
+ encoding = 'filesystem'
+ assert_require_insecure_path(nil, encoding)
+ end
+
+ def test_require_nonascii_path_utf8
+ bug8676 = '[ruby-core:56136] [Bug #8676]'
+ encoding = Encoding::UTF_8
+ return if Encoding.find('filesystem') == encoding
+ assert_require_nonascii_path(encoding, bug8676)
+ end
+
+ def test_require_insecure_path_utf8
+ encoding = Encoding::UTF_8
+ return if Encoding.find('filesystem') == encoding
+ assert_require_insecure_path(nil, encoding)
+ end
+
+ def test_require_nonascii_path_shift_jis
+ bug8676 = '[ruby-core:56136] [Bug #8676]'
+ encoding = Encoding::Shift_JIS
+ return if Encoding.find('filesystem') == encoding
+ assert_require_nonascii_path(encoding, bug8676)
+ end
+
+ def test_require_insecure_path_shift_jis
+ encoding = Encoding::Shift_JIS
+ return if Encoding.find('filesystem') == encoding
+ assert_require_insecure_path(nil, encoding)
+ end
+
+ case RUBY_PLATFORM
+ when /cygwin/, /mswin/, /mingw/, /darwin/
+ def self.ospath_encoding(path)
+ Encoding::UTF_8
+ end
+ else
+ def self.ospath_encoding(path)
+ path.encoding
+ end
+ end
+
+ SECURITY_WARNING =
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ nil
+ else
+ proc do |require_path|
+ $SAFE = 1
+ require(require_path)
+ ensure
+ $SAFE = 0
+ end
+ end
+
+ def prepare_require_path(dir, encoding)
+ Dir.mktmpdir {|tmp|
+ begin
+ require_path = File.join(tmp, dir, 'foo.rb').encode(encoding)
+ rescue
+ skip "cannot convert path encoding to #{encoding}"
+ end
+ Dir.mkdir(File.dirname(require_path))
+ open(require_path, "wb") {|f| f.puts '$:.push __FILE__'}
+ begin
+ load_path = $:.dup
+ features = $".dup
+ yield require_path
+ ensure
+ $:.replace(load_path)
+ $".replace(features)
+ end
+ }
+ end
+
+ def assert_require_nonascii_path(encoding, bug)
+ prepare_require_path("\u3042" * 5, encoding) {|require_path|
+ begin
+ # leave paths for require encoding objects
+ bug = "#{bug} require #{encoding} path"
+ require_path = "#{require_path}"
+ $:.clear
+ assert_nothing_raised(LoadError, bug) {
+ assert(require(require_path), bug)
+ assert_equal(self.class.ospath_encoding(require_path), $:.last.encoding, '[Bug #8753]')
+ assert(!require(require_path), bug)
+ }
+ end
+ }
+ end
+
+ def assert_require_insecure_path(dirname, encoding = nil)
+ return unless SECURITY_WARNING
+ dirname ||= "\u3042" * 5
+ encoding ||= dirname.encoding
+ prepare_require_path(dirname, encoding) {|require_path|
+ require_path.untaint
+ require(require_path)
+ $".pop
+ File.chmod(0777, File.dirname(require_path))
+ ospath = (require_path.encode('filesystem') rescue
+ require_path.encode(self.class.ospath_encoding(require_path)))
+ e = nil
+ stderr = EnvUtil.verbose_warning do
+ e = assert_raise(SecurityError) do
+ SECURITY_WARNING.call(require_path)
+ end
+ end
+ assert_include(e.message, "loading from unsafe path")
+ assert_include(stderr, "Insecure world writable dir")
+ require_path = require_path.encode(self.class.ospath_encoding(require_path))
+ assert_include(e.message, require_path)
+ assert_include(stderr, File.dirname(require_path))
+ }
+ end
+
+ def test_require_path_home_1
env_rubypath, env_home = ENV["RUBYPATH"], ENV["HOME"]
+ pathname_too_long = /pathname too long \(ignored\).*\(LoadError\)/m
ENV["RUBYPATH"] = "~"
- ENV["HOME"] = "/foo" * 10000
- assert_in_out_err(%w(-S test_ruby_test_require), "", [], /^.+$/)
+ ENV["HOME"] = "/foo" * 1024
+ assert_in_out_err(%w(-S -w test_ruby_test_require), "", [], pathname_too_long)
+
+ ensure
+ env_rubypath ? ENV["RUBYPATH"] = env_rubypath : ENV.delete("RUBYPATH")
+ env_home ? ENV["HOME"] = env_home : ENV.delete("HOME")
+ end
- ENV["RUBYPATH"] = "~" + "/foo" * 10000
+ def test_require_path_home_2
+ env_rubypath, env_home = ENV["RUBYPATH"], ENV["HOME"]
+ pathname_too_long = /pathname too long \(ignored\).*\(LoadError\)/m
+
+ ENV["RUBYPATH"] = "~" + "/foo" * 1024
ENV["HOME"] = "/foo"
- assert_in_out_err(%w(-S test_ruby_test_require), "", [], /^.+$/)
+ assert_in_out_err(%w(-S -w test_ruby_test_require), "", [], pathname_too_long)
- t = Tempfile.new(["test_ruby_test_require", ".rb"])
- t.puts "p :ok"
- t.close
- ENV["RUBYPATH"] = "~"
- ENV["HOME"], name = File.split(t.path)
- assert_in_out_err(["-S", name], "", %w(:ok), [])
+ ensure
+ env_rubypath ? ENV["RUBYPATH"] = env_rubypath : ENV.delete("RUBYPATH")
+ env_home ? ENV["HOME"] = env_home : ENV.delete("HOME")
+ end
+
+ def test_require_path_home_3
+ env_rubypath, env_home = ENV["RUBYPATH"], ENV["HOME"]
+
+ Tempfile.create(["test_ruby_test_require", ".rb"]) {|t|
+ t.puts "p :ok"
+ t.close
+
+ ENV["RUBYPATH"] = "~"
+ ENV["HOME"] = t.path
+ assert_in_out_err(%w(-S test_ruby_test_require), "", [], /\(LoadError\)/)
+ ENV["HOME"], name = File.split(t.path)
+ assert_in_out_err(["-S", name], "", %w(:ok), [])
+ }
ensure
env_rubypath ? ENV["RUBYPATH"] = env_rubypath : ENV.delete("RUBYPATH")
env_home ? ENV["HOME"] = env_home : ENV.delete("HOME")
end
def test_require_with_unc
- assert(system(File.expand_path(EnvUtil.rubybin).sub(/\A(\w):/, '//localhost/\1$/'), "-rabbrev", "-e0"))
+ Tempfile.create(["test_ruby_test_require", ".rb"]) {|t|
+ t.puts "puts __FILE__"
+ t.close
+
+ path = File.expand_path(t.path).sub(/\A(\w):/, '//127.0.0.1/\1$')
+ skip "local drive #$1: is not shared" unless File.exist?(path)
+ args = ['--disable-gems', "-I#{File.dirname(path)}"]
+ assert_in_out_err(args, "#{<<~"END;"}", [path], [])
+ begin
+ require '#{File.basename(path)}'
+ rescue Errno::EPERM
+ end
+ END;
+ }
end if /mswin|mingw/ =~ RUBY_PLATFORM
+ def test_require_twice
+ Dir.mktmpdir do |tmp|
+ req = File.join(tmp, "very_long_file_name.rb")
+ File.write(req, "p :ok\n")
+ assert_file.exist?(req)
+ req[/.rb$/i] = ""
+ assert_in_out_err(['--disable-gems'], <<-INPUT, %w(:ok), [])
+ require "#{req}"
+ require "#{req}"
+ INPUT
+ end
+ end
+
+ def assert_syntax_error_backtrace
+ Dir.mktmpdir do |tmp|
+ req = File.join(tmp, "test.rb")
+ File.write(req, "'\n")
+ e = assert_raise_with_message(SyntaxError, /unterminated/) {
+ yield req
+ }
+ assert_not_nil(bt = e.backtrace)
+ assert_not_empty(bt.find_all {|b| b.start_with? __FILE__})
+ end
+ end
+
+ def test_require_syntax_error
+ assert_syntax_error_backtrace {|req| require req}
+ end
+
+ def test_load_syntax_error
+ assert_syntax_error_backtrace {|req| load req}
+ end
+
def test_define_class
begin
require "socket"
@@ -74,33 +280,24 @@ class TestRequire < Test::Unit::TestCase
return
end
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
+ assert_separately([], <<-INPUT)
BasicSocket = 1
- begin
+ assert_raise(TypeError) do
require 'socket'
- p :ng
- rescue TypeError
- p :ok
end
INPUT
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
+ assert_separately([], <<-INPUT)
class BasicSocket; end
- begin
+ assert_raise(TypeError) do
require 'socket'
- p :ng
- rescue TypeError
- p :ok
end
INPUT
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
+ assert_separately([], <<-INPUT)
class BasicSocket < IO; end
- begin
+ assert_nothing_raised do
require 'socket'
- p :ok
- rescue Exception
- p :ng
end
INPUT
end
@@ -112,36 +309,27 @@ class TestRequire < Test::Unit::TestCase
return
end
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
+ assert_separately([], <<-INPUT)
module Zlib; end
Zlib::Error = 1
- begin
+ assert_raise(TypeError) do
require 'zlib'
- p :ng
- rescue TypeError
- p :ok
end
INPUT
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
+ assert_separately([], <<-INPUT)
module Zlib; end
class Zlib::Error; end
- begin
+ assert_raise(TypeError) do
require 'zlib'
- p :ng
- rescue NameError
- p :ok
end
INPUT
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
+ assert_separately([], <<-INPUT)
module Zlib; end
class Zlib::Error < StandardError; end
- begin
+ assert_nothing_raised do
require 'zlib'
- p :ok
- rescue Exception
- p :ng
end
INPUT
end
@@ -153,13 +341,10 @@ class TestRequire < Test::Unit::TestCase
return
end
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
+ assert_separately([], <<-INPUT)
Zlib = 1
- begin
+ assert_raise(TypeError) do
require 'zlib'
- p :ng
- rescue TypeError
- p :ok
end
INPUT
end
@@ -171,96 +356,101 @@ class TestRequire < Test::Unit::TestCase
return
end
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
+ assert_separately([], <<-INPUT)
class BasicSocket < IO; end
class Socket < BasicSocket; end
Socket::Constants = 1
- begin
+ assert_raise(TypeError) do
require 'socket'
- p :ng
- rescue TypeError
- p :ok
end
INPUT
end
def test_load
- t = Tempfile.new(["test_ruby_test_require", ".rb"])
- t.puts "module Foo; end"
- t.puts "at_exit { p :wrap_end }"
- t.puts "at_exit { raise 'error in at_exit test' }"
- t.puts "p :ok"
- t.close
-
- assert_in_out_err([], <<-INPUT, %w(:ok :end :wrap_end), /error in at_exit test/)
- load(#{ t.path.dump }, true)
- GC.start
- p :end
- INPUT
-
- assert_raise(ArgumentError) { at_exit }
+ Tempfile.create(["test_ruby_test_require", ".rb"]) {|t|
+ t.puts "module Foo; end"
+ t.puts "at_exit { p :wrap_end }"
+ t.puts "at_exit { raise 'error in at_exit test' }"
+ t.puts "p :ok"
+ t.close
+
+ assert_in_out_err([], <<-INPUT, %w(:ok :end :wrap_end), /error in at_exit test/)
+ load(#{ t.path.dump }, true)
+ GC.start
+ p :end
+ INPUT
+
+ assert_raise(ArgumentError) { at_exit }
+ }
end
- def test_load2 # [ruby-core:25039]
- t = Tempfile.new(["test_ruby_test_require", ".rb"])
- t.puts "Hello = 'hello'"
- t.puts "class Foo"
- t.puts " p Hello"
- t.puts "end"
- t.close
-
- assert_in_out_err([], <<-INPUT, %w("hello"), [])
- load(#{ t.path.dump }, true)
- INPUT
+ def test_load_scope
+ bug1982 = '[ruby-core:25039] [Bug #1982]'
+ Tempfile.create(["test_ruby_test_require", ".rb"]) {|t|
+ t.puts "Hello = 'hello'"
+ t.puts "class Foo"
+ t.puts " p Hello"
+ t.puts "end"
+ t.close
+
+ assert_in_out_err([], <<-INPUT, %w("hello"), [], bug1982)
+ load(#{ t.path.dump }, true)
+ INPUT
+ }
end
- def test_tainted_loadpath
- t = Tempfile.new(["test_ruby_test_require", ".rb"])
- abs_dir, file = File.split(t.path)
- abs_dir = File.expand_path(abs_dir).untaint
-
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
- abs_dir = "#{ abs_dir }"
- $: << abs_dir
- require "#{ file }"
- p :ok
- INPUT
-
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
- abs_dir = "#{ abs_dir }"
- $: << abs_dir.taint
- require "#{ file }"
- p :ok
- INPUT
+ 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
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
- abs_dir = "#{ abs_dir }"
- $: << abs_dir.taint
- $SAFE = 1
- begin
- require "#{ file }"
- rescue SecurityError
- p :ok
- end
- INPUT
+ assert_raise_with_message(LoadError, /#{base}/) {
+ load(File.join(File.dirname(path), base))
+ }
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
- abs_dir = "#{ abs_dir }"
- $: << abs_dir.taint
- $SAFE = 1
- begin
- require "#{ file }"
- rescue SecurityError
- p :ok
- end
- INPUT
+ t.puts "warn 'ok'"
+ t.close
+ assert_include(path, base)
+ assert_warn("ok\n", bug) {
+ assert_nothing_raised(LoadError, bug) {
+ load(path)
+ }
+ }
+ end
+ end
- assert_in_out_err([], <<-INPUT, %w(:ok), [])
- abs_dir = "#{ abs_dir }"
- $: << abs_dir << 'elsewhere'.taint
- require "#{ file }"
- p :ok
- INPUT
+ def test_tainted_loadpath
+ Tempfile.create(["test_ruby_test_require", ".rb"]) {|t|
+ abs_dir, file = File.split(t.path)
+ abs_dir = File.expand_path(abs_dir).untaint
+
+ assert_separately([], <<-INPUT)
+ abs_dir = "#{ abs_dir }"
+ $: << abs_dir
+ assert_nothing_raised {require "#{ file }"}
+ INPUT
+
+ assert_separately([], <<-INPUT)
+ abs_dir = "#{ abs_dir }"
+ $: << abs_dir.taint
+ assert_nothing_raised {require "#{ file }"}
+ INPUT
+
+ assert_separately([], <<-INPUT)
+ abs_dir = "#{ abs_dir }"
+ $: << abs_dir.taint
+ $SAFE = 1
+ assert_raise(SecurityError) {require "#{ file }"}
+ INPUT
+
+ assert_separately([], <<-INPUT)
+ abs_dir = "#{ abs_dir }"
+ $: << abs_dir << 'elsewhere'.taint
+ assert_nothing_raised {require "#{ file }"}
+ INPUT
+ }
end
def test_relative
@@ -296,12 +486,415 @@ class TestRequire < Test::Unit::TestCase
File.open("a/tst.rb", "w") {|f| f.puts 'require_relative "lib"' }
begin
File.symlink("../a/tst.rb", "b/tst.rb")
- result = IO.popen([EnvUtil.rubybin, "b/tst.rb"]).read
+ result = IO.popen([EnvUtil.rubybin, "b/tst.rb"], &:read)
assert_equal("a/lib.rb\n", result, "[ruby-dev:40040]")
- rescue NotImplementedError
+ 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"'], "",
+ [], /\$LOADED_FEATURES is frozen; cannot append feature \(RuntimeError\)$/,
+ bug3756)
+ end
+
+ def test_race_exception
+ bug5754 = '[ruby-core:41618]'
+ path = nil
+ stderr = $stderr
+ verbose = $VERBOSE
+ Tempfile.create(%w"bug5754 .rb") {|tmp|
+ path = tmp.path
+ tmp.print "#{<<~"begin;"}\n#{<<~"end;"}"
+ begin;
+ th = Thread.current
+ t = th[:t]
+ scratch = th[:scratch]
+
+ if scratch.empty?
+ scratch << :pre
+ Thread.pass until t.stop?
+ raise RuntimeError
+ else
+ scratch << :post
+ end
+ end;
+ tmp.close
+
+ class << (output = "")
+ alias write concat
+ end
+ $stderr = output
+
+ start = false
+
+ scratch = []
+ t1_res = nil
+ t2_res = nil
+
+ t1 = Thread.new do
+ Thread.pass until start
+ begin
+ require(path)
+ rescue RuntimeError
+ end
+
+ t1_res = require(path)
+ end
+
+ t2 = Thread.new do
+ Thread.pass until scratch[0]
+ t2_res = require(path)
+ end
+
+ t1[:scratch] = t2[:scratch] = scratch
+ t1[:t] = t2
+ t2[:t] = t1
+
+ $VERBOSE = true
+ start = true
+
+ assert_nothing_raised(ThreadError, bug5754) {t1.join}
+ assert_nothing_raised(ThreadError, bug5754) {t2.join}
+
+ $VERBOSE = false
+
+ 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
+ $stderr = stderr
+ $".delete(path)
+ end
+
+ def test_loaded_features_encoding
+ bug6377 = '[ruby-core:44750]'
+ loadpath = $:.dup
+ features = $".dup
+ $".clear
+ $:.clear
+ Dir.mktmpdir {|tmp|
+ $: << tmp
+ open(File.join(tmp, "foo.rb"), "w") {}
+ require "foo"
+ assert_send([Encoding, :compatible?, tmp, $"[0]], bug6377)
+ }
+ ensure
+ $:.replace(loadpath)
+ $".replace(features)
+ end
+
+ def test_require_changed_current_dir
+ bug7158 = '[ruby-core:47970]'
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ Dir.mkdir("a")
+ Dir.mkdir("b")
+ open(File.join("a", "foo.rb"), "w") {}
+ open(File.join("b", "bar.rb"), "w") {|f|
+ f.puts "p :ok"
+ }
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
+ begin;
+ $:.replace([IO::NULL])
+ $: << "."
+ Dir.chdir("a")
+ require "foo"
+ Dir.chdir("../b")
+ p :ng unless require "bar"
+ Dir.chdir("..")
+ p :ng if require "b/bar"
+ end;
+ }
+ }
+ end
+
+ def test_require_not_modified_load_path
+ bug7158 = '[ruby-core:47970]'
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("foo.rb", "w") {}
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
+ begin;
+ $:.replace([IO::NULL])
+ a = Object.new
+ def a.to_str
+ "#{tmp}"
+ end
+ $: << a
+ require "foo"
+ last_path = $:.pop
+ p :ok if last_path == a && last_path.class == Object
+ end;
+ }
+ }
+ end
+
+ def test_require_changed_home
+ bug7158 = '[ruby-core:47970]'
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("foo.rb", "w") {}
+ Dir.mkdir("a")
+ open(File.join("a", "bar.rb"), "w") {}
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
+ begin;
+ $:.replace([IO::NULL])
+ $: << '~'
+ ENV['HOME'] = "#{tmp}"
+ require "foo"
+ ENV['HOME'] = "#{tmp}/a"
+ p :ok if require "bar"
+ end;
+ }
+ }
+ end
+
+ def test_require_to_path_redefined_in_load_path
+ bug7158 = '[ruby-core:47970]'
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("foo.rb", "w") {}
+ assert_in_out_err([{"RUBYOPT"=>nil}, '--disable-gems'], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
+ begin;
+ $:.replace([IO::NULL])
+ a = Object.new
+ def a.to_path
+ "bar"
+ end
+ $: << a
+ begin
+ require "foo"
+ p [:ng, $LOAD_PATH, ENV['RUBYLIB']]
+ rescue LoadError => e
+ raise unless e.path == "foo"
+ end
+ def a.to_path
+ "#{tmp}"
+ end
+ p :ok if require "foo"
+ end;
+ }
+ }
+ end
+
+ def test_require_to_str_redefined_in_load_path
+ bug7158 = '[ruby-core:47970]'
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("foo.rb", "w") {}
+ assert_in_out_err([{"RUBYOPT"=>nil}, '--disable-gems'], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
+ begin;
+ $:.replace([IO::NULL])
+ a = Object.new
+ def a.to_str
+ "foo"
+ end
+ $: << a
+ begin
+ require "foo"
+ p [:ng, $LOAD_PATH, ENV['RUBYLIB']]
+ rescue LoadError => e
+ raise unless e.path == "foo"
+ end
+ def a.to_str
+ "#{tmp}"
+ end
+ p :ok if require "foo"
+ end;
+ }
+ }
+ end
+
+ def assert_require_with_shared_array_modified(add, del)
+ bug7383 = '[ruby-core:49518]'
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("foo.rb", "w") {}
+ Dir.mkdir("a")
+ open(File.join("a", "bar.rb"), "w") {}
+ assert_in_out_err(['--disable-gems'], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7383)
+ begin;
+ $:.replace([IO::NULL])
+ $:.#{add} "#{tmp}"
+ $:.#{add} "#{tmp}/a"
+ require "foo"
+ $:.#{del}
+ # Expanded load path cache should be rebuilt.
+ begin
+ require "bar"
+ rescue LoadError => e
+ if e.path == "bar"
+ p :ok
+ else
+ raise
+ end
+ end
+ end;
+ }
+ }
+ end
+
+ def test_require_with_array_pop
+ assert_require_with_shared_array_modified("push", "pop")
+ end
+
+ def test_require_with_array_shift
+ assert_require_with_shared_array_modified("unshift", "shift")
+ end
+
+ def test_require_local_var_on_toplevel
+ bug7536 = '[ruby-core:50701]'
+ Dir.mktmpdir {|tmp|
+ Dir.chdir(tmp) {
+ open("bar.rb", "w") {|f| f.puts 'TOPLEVEL_BINDING.eval("lib = 2")' }
+ assert_in_out_err(%w[-r./bar.rb], "#{<<~"begin;"}\n#{<<~"end;"}", %w([:lib] 2), [], bug7536)
+ begin;
+ puts TOPLEVEL_BINDING.eval("local_variables").inspect
+ puts TOPLEVEL_BINDING.eval("lib").inspect
+ end;
+ }
+ }
+ end
+
+ def test_require_with_loaded_features_pop
+ bug7530 = '[ruby-core:50645]'
+ Tempfile.create(%w'bug-7530- .rb') {|script|
+ script.close
+ 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.times.map {
+ Thread.new do
+ ITERATIONS_PER_THREAD.times do
+ require PATH
+ $".delete_if {|p| Regexp.new(PATH) =~ p}
+ end
+ end
+ }.each(&:join)
+ p :ok
+ end;
+ }
+ end
+
+ def test_loading_fifo_threading_raise
+ Tempfile.create(%w'fifo .rb') {|f|
+ f.close
+ File.unlink(f.path)
+ File.mkfifo(f.path)
+ assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 3)
+ begin;
+ th = Thread.current
+ Thread.start {begin sleep(0.001) end until th.stop?; th.raise(IOError)}
+ assert_raise(IOError) do
+ load(ARGV[0])
+ end
+ end;
+ }
+ end if File.respond_to?(:mkfifo)
+
+ def test_loading_fifo_threading_success
+ Tempfile.create(%w'fifo .rb') {|f|
+ f.close
+ File.unlink(f.path)
+ File.mkfifo(f.path)
+
+ assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 3)
+ begin;
+ path = ARGV[0]
+ th = Thread.current
+ $ok = false
+ Thread.start {
+ begin
+ sleep(0.001)
+ end until th.stop?
+ open(path, File::WRONLY | File::NONBLOCK) {|fifo_w|
+ fifo_w.print "$ok = true\n__END__\n" # ensure finishing
+ }
+ }
+
+ load(path)
+ assert($ok)
+ end;
+ }
+ end if File.respond_to?(:mkfifo)
+
+ def test_loading_fifo_fd_leak
+ Tempfile.create(%w'fifo .rb') {|f|
+ f.close
+ File.unlink(f.path)
+ File.mkfifo(f.path)
+ assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 3)
+ begin;
+ Process.setrlimit(Process::RLIMIT_NOFILE, 50)
+ th = Thread.current
+ 100.times do |i|
+ Thread.start {begin sleep(0.001) end until th.stop?; th.raise(IOError)}
+ assert_raise(IOError, "\#{i} time") do
+ begin
+ tap {tap {tap {load(ARGV[0])}}}
+ rescue LoadError
+ GC.start
+ retry
+ end
+ end
+ end
+ end;
+ }
+ end if File.respond_to?(:mkfifo) and defined?(Process::RLIMIT_NOFILE)
+
+ def test_throw_while_loading
+ Tempfile.create(%w'bug-11404 .rb') do |f|
+ f.puts 'sleep'
+ f.close
+
+ assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ path = ARGV[0]
+ class Error < RuntimeError
+ def exception(*)
+ begin
+ throw :blah
+ rescue UncaughtThrowError
+ end
+ self
+ end
+ end
+
+ assert_throw(:blah) do
+ x = Thread.current
+ Thread.start {
+ sleep 0.00001
+ x.raise Error.new
+ }
+ load path
+ end
+ end;
+ end
+ end
+
+ def test_symlink_load_path
+ Dir.mktmpdir {|tmp|
+ Dir.mkdir(File.join(tmp, "real"))
+ begin
+ File.symlink "real", File.join(tmp, "symlink")
+ rescue NotImplementedError, Errno::EACCES
+ skip "File.symlink is not implemented"
+ end
+ File.write(File.join(tmp, "real/a.rb"), "print __FILE__")
+ result = IO.popen([EnvUtil.rubybin, "-I#{tmp}/symlink", "-e", "require 'a.rb'"], &:read)
+ assert_operator(result, :end_with?, "/real/a.rb")
+ }
+ end
end
diff --git a/test/ruby/test_rubyoptions.rb b/test/ruby/test_rubyoptions.rb
index e41bdf2936..69521b1d23 100644
--- a/test/ruby/test_rubyoptions.rb
+++ b/test/ruby/test_rubyoptions.rb
@@ -1,22 +1,51 @@
+# -*- coding: us-ascii -*-
require 'test/unit'
+require 'timeout'
require 'tmpdir'
require 'tempfile'
-require_relative 'envutil'
+require_relative '../lib/jit_support'
class TestRubyOptions < Test::Unit::TestCase
+ NO_JIT_DESCRIPTION =
+ if RubyVM::MJIT.enabled? # checking -DMJIT_FORCE_ENABLE
+ RUBY_DESCRIPTION.sub(/\+JIT /, '')
+ else
+ RUBY_DESCRIPTION
+ end
+
+ def write_file(filename, content)
+ File.open(filename, "w") {|f|
+ f << content
+ }
+ end
+
+ def with_tmpchdir
+ Dir.mktmpdir {|d|
+ d = File.realpath(d)
+ Dir.chdir(d) {
+ yield d
+ }
+ }
+ end
+
def test_source_file
assert_in_out_err([], "", [], [])
end
def test_usage
assert_in_out_err(%w(-h)) do |r, e|
- assert_operator(r.size, :<=, 24)
+ assert_operator(r.size, :<=, 25)
+ longer = r[1..-1].select {|x| x.size > 80}
+ assert_equal([], longer)
assert_equal([], e)
end
+ end
+ def test_usage_long
assert_in_out_err(%w(--help)) do |r, e|
- assert_operator(r.size, :<=, 24)
+ longer = r[1..-1].select {|x| x.size > 80}
+ assert_equal([], longer)
assert_equal([], e)
end
end
@@ -28,7 +57,7 @@ class TestRubyOptions < Test::Unit::TestCase
end
assert_in_out_err(%w(-p -l -a -e) + ['p [$-p, $-l, $-a]'],
- "foo\nbar\nbaz\n") do |r, e|
+ "foo\nbar\nbaz") do |r, e|
assert_equal(
[ '[true, true, true]', 'foo',
'[true, true, true]', 'bar',
@@ -37,6 +66,7 @@ class TestRubyOptions < Test::Unit::TestCase
end
end
+
def test_warning
save_rubyopt = ENV['RUBYOPT']
ENV['RUBYOPT'] = nil
@@ -44,6 +74,7 @@ class TestRubyOptions < Test::Unit::TestCase
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(-W -e) + ['p $-W'], "", %w(2), [])
+ assert_in_out_err(%w(-w -W0 -e) + ['p $-W'], "", %w(0), [])
ensure
ENV['RUBYOPT'] = save_rubyopt
end
@@ -57,15 +88,40 @@ class TestRubyOptions < Test::Unit::TestCase
end
def test_debug
- assert_in_out_err(%w(-de) + ["p $DEBUG"], "", %w(true), [])
+ assert_in_out_err(["--disable-gems", "-de", "p $DEBUG"], "", %w(true), [])
- assert_in_out_err(%w(--debug -e) + ["p $DEBUG"], "", %w(true), [])
+ assert_in_out_err(["--disable-gems", "--debug", "-e", "p $DEBUG"],
+ "", %w(true), [])
end
+ q = Regexp.method(:quote)
+ VERSION_PATTERN =
+ case RUBY_ENGINE
+ when 'jruby'
+ /^jruby #{q[RUBY_ENGINE_VERSION]} \(#{q[RUBY_VERSION]}\).*? \[#{
+ q[RbConfig::CONFIG["host_os"]]}-#{q[RbConfig::CONFIG["host_cpu"]]}\]$/
+ else
+ /^ruby #{q[RUBY_VERSION]}(?:[p ]|dev|rc).*? \[#{q[RUBY_PLATFORM]}\]$/
+ end
+ private_constant :VERSION_PATTERN
+
+ VERSION_PATTERN_WITH_JIT =
+ case RUBY_ENGINE
+ when 'ruby'
+ /^ruby #{q[RUBY_VERSION]}(?:[p ]|dev|rc).*? \+JIT \[#{q[RUBY_PLATFORM]}\]$/
+ else
+ VERSION_PATTERN
+ end
+ private_constant :VERSION_PATTERN_WITH_JIT
+
def test_verbose
- assert_in_out_err(%w(-vve) + [""]) do |r, e|
- assert_match(/^ruby #{RUBY_VERSION}(?:[p ]|dev).*? \[#{RUBY_PLATFORM}\]$/, r.join)
- assert_equal RUBY_DESCRIPTION, r.join.chomp
+ assert_in_out_err(["-vve", ""]) do |r, e|
+ assert_match(VERSION_PATTERN, r[0])
+ if RubyVM::MJIT.enabled? && !mjit_force_enabled? # checking -DMJIT_FORCE_ENABLE
+ assert_equal(NO_JIT_DESCRIPTION, r[0])
+ else
+ assert_equal(RUBY_DESCRIPTION, r[0])
+ end
assert_equal([], e)
end
@@ -82,9 +138,11 @@ class TestRubyOptions < Test::Unit::TestCase
end
def test_enable
- assert_in_out_err(%w(--enable all -e) + [""], "", [], [])
- assert_in_out_err(%w(--enable-all -e) + [""], "", [], [])
- assert_in_out_err(%w(--enable=all -e) + [""], "", [], [])
+ if JITSupport.supported?
+ assert_in_out_err(%w(--enable all -e) + [""], "", [], [])
+ assert_in_out_err(%w(--enable-all -e) + [""], "", [], [])
+ assert_in_out_err(%w(--enable=all -e) + [""], "", [], [])
+ end
assert_in_out_err(%w(--enable foobarbazqux -e) + [""], "", [],
/unknown argument for --enable: `foobarbazqux'/)
assert_in_out_err(%w(--enable), "", [], /missing argument for --enable/)
@@ -97,23 +155,70 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err(%w(--disable foobarbazqux -e) + [""], "", [],
/unknown argument for --disable: `foobarbazqux'/)
assert_in_out_err(%w(--disable), "", [], /missing argument for --disable/)
+ assert_in_out_err(%w(--disable-gems -e) + ['p defined? Gem'], "", ["nil"], [])
+ assert_in_out_err(%w(--disable-did_you_mean -e) + ['p defined? DidYouMean'], "", ["nil"], [])
+ assert_in_out_err(%w(--disable-gems -e) + ['p defined? DidYouMean'], "", ["nil"], [])
end
def test_kanji
assert_in_out_err(%w(-KU), "p '\u3042'") do |r, e|
assert_equal("\"\u3042\"", r.join.force_encoding(Encoding::UTF_8))
end
- assert_in_out_err(%w(-KE -e) + [""], "", [], [])
- assert_in_out_err(%w(-KS -e) + [""], "", [], [])
- assert_in_out_err(%w(-KN -e) + [""], "", [], [])
+ line = '-eputs"\xc2\xa1".encoding'
+ env = {'RUBYOPT' => nil}
+ assert_in_out_err([env, '-Ke', line], "", ["EUC-JP"], [])
+ assert_in_out_err([env, '-KE', line], "", ["EUC-JP"], [])
+ assert_in_out_err([env, '-Ks', line], "", ["Windows-31J"], [])
+ assert_in_out_err([env, '-KS', line], "", ["Windows-31J"], [])
+ assert_in_out_err([env, '-Ku', line], "", ["UTF-8"], [])
+ assert_in_out_err([env, '-KU', line], "", ["UTF-8"], [])
+ assert_in_out_err([env, '-Kn', line], "", ["ASCII-8BIT"], [])
+ assert_in_out_err([env, '-KN', line], "", ["ASCII-8BIT"], [])
+ assert_in_out_err([env, '-wKe', line], "", ["EUC-JP"], /-K/)
end
def test_version
assert_in_out_err(%w(--version)) do |r, e|
- assert_match(/^ruby #{RUBY_VERSION}(?:[p ]|dev).*? \[#{RUBY_PLATFORM}\]$/, r.join)
- assert_equal RUBY_DESCRIPTION, r.join.chomp
+ assert_match(VERSION_PATTERN, r[0])
+ if RubyVM::MJIT.enabled? # checking -DMJIT_FORCE_ENABLE
+ assert_equal(EnvUtil.invoke_ruby(['-e', 'print RUBY_DESCRIPTION'], '', true).first, r[0])
+ else
+ assert_equal(RUBY_DESCRIPTION, r[0])
+ end
assert_equal([], e)
end
+
+ return if RbConfig::CONFIG["MJIT_SUPPORT"] == 'no'
+
+ [
+ %w(--version --jit --disable=jit),
+ %w(--version --enable=jit --disable=jit),
+ %w(--version --enable-jit --disable-jit),
+ ].each do |args|
+ assert_in_out_err(args) do |r, e|
+ assert_match(VERSION_PATTERN, r[0])
+ assert_match(NO_JIT_DESCRIPTION, r[0])
+ assert_equal([], e)
+ end
+ end
+
+ if JITSupport.supported?
+ [
+ %w(--version --jit),
+ %w(--version --enable=jit),
+ %w(--version --enable-jit),
+ ].each do |args|
+ assert_in_out_err(args) do |r, e|
+ assert_match(VERSION_PATTERN_WITH_JIT, r[0])
+ if 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])
+ end
+ assert_equal([], e)
+ end
+ end
+ end
end
def test_eval
@@ -124,6 +229,8 @@ class TestRubyOptions < Test::Unit::TestCase
require "pp"
assert_in_out_err(%w(-r pp -e) + ["pp 1"], "", %w(1), [])
assert_in_out_err(%w(-rpp -e) + ["pp 1"], "", %w(1), [])
+ assert_in_out_err(%w(-ep\ 1 -r), "", %w(1), [])
+ assert_in_out_err(%w(-r), "", [], [])
rescue LoadError
end
@@ -139,6 +246,10 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err(%w(-0141 -e) + ["print gets"], "foo\nbar\0baz", %w(foo ba), [])
assert_in_out_err(%w(-0e) + ["print gets"], "foo\nbar\0baz", %W(foo bar\0), [])
+
+ assert_in_out_err(%w(-00 -e) + ["p gets, gets"], "foo\nbar\n\nbaz\nzot\n\n\n", %w("foo\nbar\n\n" "baz\nzot\n\n"), [])
+
+ assert_in_out_err(%w(-00 -e) + ["p gets, gets"], "foo\nbar\n\n\n\nbaz\n", %w("foo\nbar\n\n" "baz\n"), [])
end
def test_autosplit
@@ -153,32 +264,41 @@ class TestRubyOptions < Test::Unit::TestCase
d = Dir.tmpdir
assert_in_out_err(["-C", d, "-e", "puts Dir.pwd"]) do |r, e|
- assert(File.identical?(r.join, d))
+ assert_file.identical?(r.join, d)
assert_equal([], e)
end
end
def test_yydebug
assert_in_out_err(["-ye", ""]) do |r, e|
- assert_equal([], r)
- assert_not_equal([], e)
+ assert_not_equal([], r)
+ assert_equal([], e)
end
assert_in_out_err(%w(--yydebug -e) + [""]) do |r, e|
- assert_equal([], r)
- assert_not_equal([], e)
+ assert_not_equal([], r)
+ assert_equal([], e)
end
end
def test_encoding
- assert_in_out_err(%w(-Eutf-8), "p '\u3042'", [], /invalid multibyte char/)
-
assert_in_out_err(%w(--encoding), "", [], /missing argument for --encoding/)
assert_in_out_err(%w(--encoding test_ruby_test_rubyoptions_foobarbazqux), "", [],
/unknown encoding name - test_ruby_test_rubyoptions_foobarbazqux \(RuntimeError\)/)
- assert_in_out_err(%w(--encoding utf-8), "p '\u3042'", [], /invalid multibyte char/)
+ if /mswin|mingw|aix/ =~ RUBY_PLATFORM &&
+ (str = "\u3042".force_encoding(Encoding.find("locale"))).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,
+ # which allows \u3042.
+ out, err = [str], []
+ else
+ out, err = [], /invalid multibyte char/
+ end
+ assert_in_out_err(%w(-Eutf-8), "puts '\u3042'", out, err)
+ assert_in_out_err(%w(--encoding utf-8), "puts '\u3042'", out, err)
end
def test_syntax_check
@@ -207,10 +327,10 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err([], "", [], /invalid switch in RUBYOPT: -e \(RuntimeError\)/)
ENV['RUBYOPT'] = '-T1'
- assert_in_out_err([], "", [], /no program input from stdin allowed in tainted mode \(SecurityError\)/)
+ assert_in_out_err(["--disable-gems"], "", [], /no program input from stdin allowed in tainted mode \(SecurityError\)/)
ENV['RUBYOPT'] = '-T4'
- assert_in_out_err([], "", [], /no program input from stdin allowed in tainted mode \(SecurityError\)/)
+ assert_in_out_err(["--disable-gems"], "", [], /no program input from stdin allowed in tainted mode \(SecurityError\)/)
ENV['RUBYOPT'] = '-Eus-ascii -KN'
assert_in_out_err(%w(-Eutf-8 -KU), "p '\u3042'") do |r, e|
@@ -218,6 +338,10 @@ class TestRubyOptions < Test::Unit::TestCase
assert_equal([], e)
end
+ ENV['RUBYOPT'] = '-w'
+ 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"])
ensure
if rubyopt_orig
ENV['RUBYOPT'] = rubyopt_orig
@@ -230,20 +354,23 @@ class TestRubyOptions < Test::Unit::TestCase
rubypath_orig = ENV['RUBYPATH']
path_orig = ENV['PATH']
- t = Tempfile.new(["test_ruby_test_rubyoption", ".rb"])
- t.puts "p 1"
- t.close
-
- @verbose = $VERBOSE
- $VERBOSE = nil
+ Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) {|t|
+ t.puts "p 1"
+ t.close
- ENV['PATH'] = File.dirname(t.path)
+ @verbose = $VERBOSE
+ $VERBOSE = nil
- assert_in_out_err(%w(-S) + [File.basename(t.path)], "", %w(1), [])
+ path, name = File.split(t.path)
- ENV['RUBYPATH'] = File.dirname(t.path)
+ ENV['PATH'] = (path_orig && RbConfig::CONFIG['LIBPATHENV'] == 'PATH') ?
+ [path, path_orig].join(File::PATH_SEPARATOR) : path
+ assert_in_out_err(%w(-S) + [name], "", %w(1), [])
+ ENV['PATH'] = path_orig
- assert_in_out_err(%w(-S) + [File.basename(t.path)], "", %w(1), [])
+ ENV['RUBYPATH'] = path
+ assert_in_out_err(%w(-S) + [name], "", %w(1), [])
+ }
ensure
if rubypath_orig
@@ -256,7 +383,6 @@ class TestRubyOptions < Test::Unit::TestCase
else
ENV.delete('PATH')
end
- t.close(true) if t
$VERBOSE = @verbose
end
@@ -267,9 +393,35 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err([], "#! /test_r_u_b_y_test_r_u_b_y_options_foobarbazqux -foo -bar\r\np 1\r\n",
[], /: no Ruby script found in input/)
- assert_in_out_err([], "#!ruby -KU -Eutf-8\r\np \"\u3042\"\r\n") do |r, e|
- assert_equal("\"\u3042\"", r.join.force_encoding(Encoding::UTF_8))
- assert_equal([], e)
+ warning = /mswin|mingw/ =~ RUBY_PLATFORM ? [] : /shebang line ending with \\r/
+ assert_in_out_err([{'RUBYOPT' => nil}], "#!ruby -KU -Eutf-8\r\np \"\u3042\"\r\n",
+ ["\"\u3042\""], warning,
+ encoding: Encoding::UTF_8)
+
+ bug4118 = '[ruby-dev:42680]'
+ assert_in_out_err(%w[], "#!/bin/sh\n""#!shebang\n""#!ruby\n""puts __LINE__\n",
+ %w[4], [], bug4118)
+ assert_in_out_err(%w[-x], "#!/bin/sh\n""#!shebang\n""#!ruby\n""puts __LINE__\n",
+ %w[4], [], bug4118)
+
+ assert_ruby_status(%w[], "#! ruby -- /", '[ruby-core:82267] [Bug #13786]')
+
+ assert_ruby_status(%w[], "#!")
+ assert_in_out_err(%w[-c], "#!", ["Syntax OK"])
+ end
+
+ def test_flag_in_shebang
+ Tempfile.create(%w"pflag .rb") do |script|
+ code = "#!ruby -p"
+ script.puts(code)
+ script.close
+ assert_in_out_err([script.path, script.path], '', [code])
+ end
+ Tempfile.create(%w"sflag .rb") do |script|
+ script.puts("#!ruby -s")
+ script.puts("p $abc")
+ script.close
+ assert_in_out_err([script.path, "-abc=foo"], '', ['"foo"'])
end
end
@@ -286,45 +438,132 @@ class TestRubyOptions < Test::Unit::TestCase
end
def test_assignment_in_conditional
- t = Tempfile.new(["test_ruby_test_rubyoption", ".rb"])
- t.puts "if a = 1"
- t.puts "end"
- t.puts "0.times do"
- t.puts " if b = 2"
- t.puts " a += b"
- t.puts " end"
- t.puts "end"
- t.close
- err = ["#{t.path}:1: warning: found = in conditional, should be ==",
- "#{t.path}:4: warning: found = in conditional, should be =="]
- err = /\A(#{Regexp.quote(t.path)}):1(: warning: found = in conditional, should be ==)\n\1:4\2\Z/
- bug2136 = '[ruby-dev:39363]'
- assert_in_out_err(["-w", t.path], "", [], err, bug2136)
- assert_in_out_err(["-wr", t.path, "-e", ""], "", [], err, bug2136)
- ensure
- t.close(true) if t
+ Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) {|t|
+ t.puts "if a = 1"
+ t.puts "end"
+ t.puts "0.times do"
+ t.puts " if b = 2"
+ t.puts " a += b"
+ t.puts " end"
+ t.puts "end"
+ t.flush
+ warning = ' warning: found `= literal\' in conditional, should be =='
+ err = ["#{t.path}:1:#{warning}",
+ "#{t.path}:4:#{warning}",
+ ]
+ bug2136 = '[ruby-dev:39363]'
+ assert_in_out_err(["-w", t.path], "", [], err, bug2136)
+ assert_in_out_err(["-wr", t.path, "-e", ""], "", [], err, bug2136)
+
+ t.rewind
+ t.truncate(0)
+ t.puts "if a = ''; end"
+ t.puts "if a = []; end"
+ t.puts "if a = [1]; end"
+ t.puts "if a = [a]; end"
+ t.puts "if a = {}; end"
+ t.puts "if a = {1=>2}; end"
+ t.puts "if a = {3=>a}; end"
+ t.flush
+ err = ["#{t.path}:1:#{warning}",
+ "#{t.path}:2:#{warning}",
+ "#{t.path}:3:#{warning}",
+ "#{t.path}:5:#{warning}",
+ "#{t.path}:6:#{warning}",
+ ]
+ feature4299 = '[ruby-dev:43083]'
+ assert_in_out_err(["-w", t.path], "", [], err, feature4299)
+ assert_in_out_err(["-wr", t.path, "-e", ""], "", [], err, feature4299)
+ }
end
def test_indentation_check
- t = Tempfile.new(["test_ruby_test_rubyoption", ".rb"])
- t.puts "begin"
- t.puts " end"
- t.close
- err = ["#{t.path}:2: warning: mismatched indentations at 'end' with 'begin' at 1"]
- assert_in_out_err(["-w", t.path], "", [], err)
- assert_in_out_err(["-wr", t.path, "-e", ""], "", [], err)
- ensure
- t.close(true) if t
+ all_assertions do |a|
+ Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) do |t|
+ [
+ "begin", "if false", "for _ in []", "while false",
+ "def foo", "class X", "module M",
+ ["-> do", "end"], ["-> {", "}"],
+ ["if false;", "else ; end"],
+ ["if false;", "elsif false ; end"],
+ ["begin", "rescue ; end"],
+ ["begin rescue", "else ; end"],
+ ["begin", "ensure ; end"],
+ [" case nil", "when true; end"],
+ ["case nil; when true", "end"],
+ ].each do
+ |b, e = 'end'|
+ src = ["#{b}\n", " #{e}\n"]
+ k = b[/\A\s*(\S+)/, 1]
+ e = e[/\A\s*(\S+)/, 1]
+
+ a.for("no directives with #{b}") do
+ err = ["#{t.path}:2: warning: mismatched indentations at '#{e}' with '#{k}' at 1"]
+ t.rewind
+ t.truncate(0)
+ t.puts src
+ t.flush
+ assert_in_out_err(["-w", t.path], "", [], err)
+ assert_in_out_err(["-wr", t.path, "-e", ""], "", [], err)
+ end
+
+ a.for("false directive with #{b}") do
+ t.rewind
+ t.truncate(0)
+ t.puts "# -*- warn-indent: false -*-"
+ t.puts src
+ t.flush
+ assert_in_out_err(["-w", t.path], "", [], [], '[ruby-core:25442]')
+ end
+
+ a.for("false and true directives with #{b}") do
+ err = ["#{t.path}:4: warning: mismatched indentations at '#{e}' with '#{k}' at 3"]
+ t.rewind
+ t.truncate(0)
+ t.puts "# -*- warn-indent: false -*-"
+ t.puts "# -*- warn-indent: true -*-"
+ t.puts src
+ t.flush
+ assert_in_out_err(["-w", t.path], "", [], err, '[ruby-core:25442]')
+ end
+
+ a.for("false directives after #{b}") do
+ t.rewind
+ t.truncate(0)
+ t.puts "# -*- warn-indent: true -*-"
+ t.puts src[0]
+ t.puts "# -*- warn-indent: false -*-"
+ t.puts src[1]
+ t.flush
+ assert_in_out_err(["-w", t.path], "", [], [], '[ruby-core:25442]')
+ end
+
+ a.for("BOM with #{b}") do
+ err = ["#{t.path}:2: warning: mismatched indentations at '#{e}' with '#{k}' at 1"]
+ t.rewind
+ t.truncate(0)
+ t.print "\u{feff}"
+ t.puts src
+ t.flush
+ assert_in_out_err(["-w", t.path], "", [], err)
+ assert_in_out_err(["-wr", t.path, "-e", ""], "", [], err)
+ end
+ end
+ end
+ end
end
def test_notfound
notexist = "./notexist.rb"
- rubybin = Regexp.quote(EnvUtil.rubybin)
+ dir, *rubybin = RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME', 'EXEEXT')
+ rubybin = "#{dir}/#{rubybin.join('')}"
+ rubybin.tr!('/', '\\') if /mswin|mingw/ =~ RUBY_PLATFORM
+ rubybin = Regexp.quote(rubybin)
pat = Regexp.quote(notexist)
bug1573 = '[ruby-core:23717]'
- assert_equal(false, File.exist?(notexist))
- assert_in_out_err(["-r", notexist, "-ep"], "", [], /\A-e:.* -- #{pat} \(LoadError\)\Z/, bug1573)
- assert_in_out_err([notexist], "", [], /\A#{rubybin}:.* -- #{pat} \(LoadError\)\Z/, bug1573)
+ assert_file.not_exist?(notexist)
+ assert_in_out_err(["-r", notexist, "-ep"], "", [], /.* -- #{pat} \(LoadError\)/, bug1573)
+ assert_in_out_err([notexist], "", [], /#{rubybin}:.* -- #{pat} \(LoadError\)/, bug1573)
end
def test_program_name
@@ -345,10 +584,14 @@ class TestRubyOptions < Test::Unit::TestCase
}
if File.respond_to? :symlink
n2 = File.join(d, 't2')
- File.symlink(n1, n2)
- IO.popen([ruby, n2]) {|f|
- assert_equal(n2, f.read)
- }
+ begin
+ File.symlink(n1, n2)
+ rescue Errno::EACCES
+ else
+ IO.popen([ruby, n2]) {|f|
+ assert_equal(n2, f.read)
+ }
+ end
end
Dir.chdir(d) {
n3 = '-e'
@@ -366,60 +609,177 @@ class TestRubyOptions < Test::Unit::TestCase
}
end
- def test_segv_test
+ if /linux|freebsd|netbsd|openbsd|darwin/ =~ RUBY_PLATFORM
+ PSCMD = EnvUtil.find_executable("ps", "-o", "command", "-p", $$.to_s) {|out| /ruby/=~out}
+ PSCMD&.pop
+ end
+
+ def test_set_program_name
+ skip "platform dependent feature" unless defined?(PSCMD) and PSCMD
+
+ with_tmpchdir do
+ write_file("test-script", "$0 = 'hello world'; /test-script/ =~ Process.argv0 or $0 = 'Process.argv0 changed!'; sleep 60")
+
+ pid = spawn(EnvUtil.rubybin, "test-script")
+ ps = nil
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ stop = now + 30
+ begin
+ sleep 0.1
+ ps = `#{PSCMD.join(' ')} #{pid}`
+ break if /hello world/ =~ ps
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ end until Process.wait(pid, Process::WNOHANG) || now > stop
+ assert_match(/hello world/, ps)
+ assert_operator now, :<, stop
+ Process.kill :KILL, pid
+ Timeout.timeout(5) { Process.wait(pid) }
+ end
+ end
+
+ def test_setproctitle
+ skip "platform dependent feature" unless defined?(PSCMD) and PSCMD
+
+ assert_separately([], "#{<<-"{#"}\n#{<<-'};'}")
+ {#
+ assert_raise(ArgumentError) do
+ Process.setproctitle("hello\0")
+ end
+ };
+
+ with_tmpchdir do
+ write_file("test-script", "$_0 = $0.dup; Process.setproctitle('hello world'); $0 == $_0 or Process.setproctitle('$0 changed!'); sleep 60")
+
+ pid = spawn(EnvUtil.rubybin, "test-script")
+ ps = nil
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ stop = now + 30
+ begin
+ sleep 0.1
+ ps = `#{PSCMD.join(' ')} #{pid}`
+ break if /hello world/ =~ ps
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ end until Process.wait(pid, Process::WNOHANG) || now > stop
+ assert_match(/hello world/, ps)
+ assert_operator now, :<, stop
+ Process.kill :KILL, pid
+ Timeout.timeout(5) { Process.wait(pid) }
+ end
+ end
+
+ module SEGVTest
opts = {}
if /mswin|mingw/ =~ RUBY_PLATFORM
- additional = '[\s\w\.\']*'
+ additional = /[\s\w\.\']*/
else
opts[:rlimit_core] = 0
- additional = ""
- end
- assert_in_out_err(["-e", "Process.kill :SEGV, $$"], "", [],
- %r(\A
- -e:(?:1:)?\s\[BUG\]\sSegmentation\sfault\n
- #{ Regexp.quote(RUBY_DESCRIPTION) }\n\n
- --\scontrol\sframe\s----------\n
- (?:c:.*\n)*
- ---------------------------\n
- (?:
- --\sRuby\slevel\sbacktrace\sinformation\s----------------------------------------\n
- -e:1:in\s\`<main>\'\n
- -e:1:in\s\`kill\'\n
- )?
- \n
- (?:
- --\sC\slevel\sbacktrace\sinformation\s-------------------------------------------\n
- (?:(?:.*\s)?\[0x\h+\]\n)*\n
- )?
- \[NOTE\]\n
- You\smay\shave\sencountered\sa\sbug\sin\sthe\sRuby\sinterpreter\sor\sextension\slibraries.\n
- Bug\sreports\sare\swelcome.\n
- For\sdetails:\shttp:\/\/www.ruby-lang.org/bugreport.html\n
- \n
- (?:#{additional})
- \z
+ additional = nil
+ end
+ ExecOptions = opts.freeze
+
+ ExpectedStderrList = [
+ %r(
+ -e:(?:1:)?\s\[BUG\]\sSegmentation\sfault.*\n
+ )x,
+ %r(
+ #{ Regexp.quote(NO_JIT_DESCRIPTION) }\n\n
+ )x,
+ %r(
+ (?:--\s(?:.+\n)*\n)?
+ --\sControl\sframe\sinformation\s-+\n
+ (?:c:.*\n)*
+ )x,
+ %r(
+ (?:
+ --\sRuby\slevel\sbacktrace\sinformation\s----------------------------------------\n
+ -e:1:in\s\`<main>\'\n
+ -e:1:in\s\`kill\'\n
+ )?
)x,
- nil,
- opts)
+ %r(
+ (?:
+ --\sC\slevel\sbacktrace\sinformation\s-------------------------------------------\n
+ (?:(?:.*\s)?\[0x\h+\]\n)*\n
+ )?
+ )x,
+ :*,
+ %r(
+ \[NOTE\]\n
+ You\smay\shave\sencountered\sa\sbug\sin\sthe\sRuby\sinterpreter\sor\sextension\slibraries.\n
+ Bug\sreports\sare\swelcome.\n
+ (?:.*\n)?
+ For\sdetails:\shttps:\/\/.*\.ruby-lang\.org/.*\n
+ \n
+ (?:
+ \[IMPORTANT\]\n
+ (?:.+\n)+
+ \n
+ )?
+ )x,
+ ]
+ ExpectedStderrList << additional if additional
+ end
+
+ def assert_segv(args, message=nil)
+ test_stdin = ""
+ opt = SEGVTest::ExecOptions.dup
+ list = SEGVTest::ExpectedStderrList
+
+ assert_in_out_err(args, test_stdin, //, list, encoding: "ASCII-8BIT", **opt)
+ end
+
+ def test_segv_test
+ assert_segv(["--disable-gems", "-e", "Process.kill :SEGV, $$"])
+ end
+
+ def test_segv_loaded_features
+ bug7402 = '[ruby-core:49573]'
+
+ status = assert_segv(['-e', 'END {Process.kill :SEGV, $$}',
+ '-e', 'class Bogus; def to_str; exit true; end; end',
+ '-e', '$".clear',
+ '-e', '$".unshift Bogus.new',
+ '-e', '(p $"; abort) unless $".size == 1',
+ ])
+ assert_not_predicate(status, :success?, "segv but success #{bug7402}")
+ end
+
+ def test_segv_setproctitle
+ bug7597 = '[ruby-dev:46786]'
+ Tempfile.create(["test_ruby_test_bug7597", ".rb"]) {|t|
+ t.write "f" * 100
+ t.flush
+ assert_segv(["--disable-gems", "-e", "$0=ARGV[0]; Process.kill :SEGV, $$", t.path], bug7597)
+ }
end
def test_DATA
- t = Tempfile.new(["test_ruby_test_rubyoption", ".rb"])
- t.puts "puts DATA.read.inspect"
- t.puts "__END__"
- t.puts "foo"
- t.puts "bar"
- t.puts "baz"
- t.close
- assert_in_out_err([t.path], "", %w("foo\\nbar\\nbaz\\n"), [])
- ensure
- t.close(true) if t
+ Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) {|t|
+ t.puts "puts DATA.read.inspect"
+ t.puts "__END__"
+ t.puts "foo"
+ t.puts "bar"
+ t.puts "baz"
+ t.flush
+ assert_in_out_err([t.path], "", %w("foo\\nbar\\nbaz\\n"), [])
+ }
end
def test_unused_variable
feature3446 = '[ruby-dev:41620]'
- assert_in_out_err(["-we", "a=1"], "", [], ["-e:1: warning: assigned but unused variable - a"], feature3446)
- assert_in_out_err(["-we", "1.times do\n a=1\nend"], "", [], ["-e:2: warning: assigned but unused variable - a"], feature3446)
+ assert_in_out_err(["-we", "a=1"], "", [], [], feature3446)
+ assert_in_out_err(["-we", "def foo\n a=1\nend"], "", [], ["-e:2: warning: assigned but unused variable - a"], feature3446)
+ assert_in_out_err(["-we", "def foo\n eval('a=1')\nend"], "", [], [], feature3446)
+ assert_in_out_err(["-we", "1.times do\n a=1\nend"], "", [], [], feature3446)
+ assert_in_out_err(["-we", "def foo\n 1.times do\n a=1\n end\nend"], "", [], ["-e:3: warning: assigned but unused variable - a"], feature3446)
+ assert_in_out_err(["-we", "def foo\n"" 1.times do |a| end\n""end"], "", [], [])
+ feature6693 = '[ruby-core:46160]'
+ assert_in_out_err(["-we", "def foo\n _a=1\nend"], "", [], [], feature6693)
+ bug7408 = '[ruby-core:49659]'
+ assert_in_out_err(["-we", "def foo\n a=1\n :a\nend"], "", [], ["-e:2: warning: assigned but unused variable - a"], bug7408)
+ feature7730 = '[ruby-core:51580]'
+ assert_in_out_err(["-w", "-"], "a=1", [], ["-:1: warning: assigned but unused variable - a"], feature7730)
+ assert_in_out_err(["-w", "-"], "eval('a=1')", [], [], feature7730)
end
def test_script_from_stdin
@@ -431,25 +791,284 @@ class TestRubyOptions < Test::Unit::TestCase
end
require 'timeout'
result = nil
- s, w = IO.pipe
- PTY.spawn(EnvUtil.rubybin, out: w) do |r, m|
- w.close
- m.print("\C-d")
- assert_nothing_raised('[ruby-dev:37798]') do
- result = Timeout.timeout(3) {s.read}
+ IO.pipe {|r, w|
+ begin
+ PTY.open {|m, s|
+ s.echo = false
+ m.print("\C-d")
+ pid = spawn(EnvUtil.rubybin, :in => s, :out => w)
+ w.close
+ assert_nothing_raised('[ruby-dev:37798]') do
+ result = Timeout.timeout(3) {r.read}
+ end
+ Process.wait pid
+ }
+ rescue RuntimeError
+ skip $!
end
- end
- s.close
+ }
assert_equal("", result, '[ruby-dev:37798]')
- s, w = IO.pipe
- PTY.spawn(EnvUtil.rubybin, out: w) do |r, m|
- w.close
- m.print("$stdin.read; p $stdin.gets\n\C-d")
- m.print("abc\n\C-d")
- m.print("zzz\n")
- result = s.read
- end
- s.close
+ IO.pipe {|r, w|
+ PTY.open {|m, s|
+ s.echo = false
+ pid = spawn(EnvUtil.rubybin, :in => s, :out => w)
+ w.close
+ m.print("$stdin.read; p $stdin.gets\n\C-d")
+ m.print("abc\n\C-d")
+ m.print("zzz\n")
+ result = r.read
+ Process.wait pid
+ }
+ }
assert_equal("\"zzz\\n\"\n", result, '[ruby-core:30910]')
end
+
+ def test_unmatching_glob
+ bug3851 = '[ruby-core:32478]'
+ a = "a[foo"
+ Dir.mktmpdir do |dir|
+ open(File.join(dir, a), "w") {|f| f.puts("p 42")}
+ assert_in_out_err(["-C", dir, a], "", ["42"], [], bug3851)
+ File.unlink(File.join(dir, a))
+ assert_in_out_err(["-C", dir, a], "", [], /LoadError/, bug3851)
+ end
+ end
+
+ case RUBY_PLATFORM
+ when /mswin|mingw/
+ def test_command_line_glob_nonascii
+ bug10555 = '[ruby-dev:48752] [Bug #10555]'
+ name = "\u{3042}.txt"
+ expected = name.encode("locale") rescue "?.txt"
+ with_tmpchdir do |dir|
+ open(name, "w") {}
+ assert_in_out_err(["-e", "puts ARGV", "?.txt"], "", [expected], [],
+ bug10555, encoding: "locale")
+ end
+ end
+
+ def test_command_line_progname_nonascii
+ bug10555 = '[ruby-dev:48752] [Bug #10555]'
+ name = expected = nil
+ unless (0x80..0x10000).any? {|c|
+ name = c.chr(Encoding::UTF_8)
+ expected = name.encode("locale") rescue nil
+ }
+ skip "can't make locale name"
+ end
+ name << ".rb"
+ expected << ".rb"
+ with_tmpchdir do |dir|
+ open(name, "w") {|f| f.puts "puts File.basename($0)"}
+ assert_in_out_err([name], "", [expected], [],
+ bug10555, encoding: "locale")
+ end
+ end
+
+ def test_command_line_glob_with_dir
+ bug10941 = '[ruby-core:68430] [Bug #10941]'
+ with_tmpchdir do |dir|
+ Dir.mkdir('test')
+ assert_in_out_err(["-e", "", "test/*"], "", [], [], bug10941)
+ end
+ end
+
+ Ougai = %W[\u{68ee}O\u{5916}.txt \u{68ee 9d0e 5916}.txt \u{68ee 9dd7 5916}.txt]
+ def test_command_line_glob_noncodepage
+ 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: "?")}
+ assert_in_out_err(["-e", "puts ARGV", "*.txt"], "", ougai)
+ end
+ end
+
+ def assert_e_script_encoding(str, args = [])
+ cmds = [
+ EnvUtil::LANG_ENVS.inject({}) {|h, k| h[k] = ENV[k]; h},
+ *args,
+ '-e', "s = '#{str}'",
+ '-e', 'puts s.encoding.name',
+ '-e', 'puts s.dump',
+ ]
+ assert_in_out_err(cmds, "", [str.encoding.name, str.dump], [],
+ "#{str.encoding}:#{str.dump} #{args.inspect}")
+ end
+
+ # tested codepages: 437 850 852 855 932 65001
+ # Since the codepage is shared all processes per conhost.exe, do
+ # not chcp, or parallel test may break.
+ def test_locale_codepage
+ locale = Encoding.find("locale")
+ list = %W"\u{c7} \u{452} \u{3066 3059 3068}"
+ list.each do |s|
+ assert_e_script_encoding(s, %w[-U])
+ end
+ list.each do |s|
+ s = s.encode(locale) rescue next
+ assert_e_script_encoding(s)
+ assert_e_script_encoding(s, %W[-E#{locale.name}])
+ end
+ end
+ when /cygwin/
+ def test_command_line_non_ascii
+ assert_separately([{"LC_ALL"=>"ja_JP.SJIS"}, "-", "\u{3042}".encode("SJIS")], <<-"end;")
+ bug12184 = '[ruby-dev:49519] [Bug #12184]'
+ a = ARGV[0]
+ assert_equal([Encoding::SJIS, 130, 160], [a.encoding, *a.bytes], bug12184)
+ end;
+ end
+ end
+
+ def test_script_is_directory
+ feature2408 = '[ruby-core:26925]'
+ assert_in_out_err(%w[.], "", [], /Is a directory -- \./, feature2408)
+ end
+
+ def test_pflag_gsub
+ bug7157 = '[ruby-core:47967]'
+ assert_in_out_err(['-p', '-e', 'gsub(/t.*/){"TEST"}'], %[test], %w[TEST], [], bug7157)
+ end
+
+ def test_pflag_sub
+ bug7157 = '[ruby-core:47967]'
+ assert_in_out_err(['-p', '-e', 'sub(/t.*/){"TEST"}'], %[test], %w[TEST], [], bug7157)
+ end
+
+ def assert_norun_with_rflag(*opt)
+ bug10435 = "[ruby-dev:48712] [Bug #10435]: should not run with #{opt} option"
+ stderr = []
+ Tempfile.create(%w"bug10435- .rb") do |script|
+ dir, base = File.split(script.path)
+ script.puts "abort ':run'"
+ script.close
+ opts = ['-C', dir, '-r', "./#{base}", *opt]
+ _, e = assert_in_out_err([*opts, '-ep'], "", //)
+ stderr.concat(e) if e
+ stderr << "---"
+ _, e = assert_in_out_err([*opts, base], "", //)
+ stderr.concat(e) if e
+ end
+ assert_not_include(stderr, ":run", bug10435)
+ end
+
+ def test_dump_syntax_with_rflag
+ assert_norun_with_rflag('-c')
+ assert_norun_with_rflag('--dump=syntax')
+ end
+
+ def test_dump_yydebug_with_rflag
+ assert_norun_with_rflag('-y')
+ assert_norun_with_rflag('--dump=yydebug')
+ end
+
+ def test_dump_parsetree_with_rflag
+ assert_norun_with_rflag('--dump=parsetree')
+ assert_norun_with_rflag('--dump=parsetree', '-e', '#frozen-string-literal: true')
+ end
+
+ def test_dump_insns_with_rflag
+ assert_norun_with_rflag('--dump=insns')
+ end
+
+ def test_frozen_string_literal
+ all_assertions do |a|
+ [["disable", "false"], ["enable", "true"]].each do |opt, exp|
+ %W[frozen_string_literal frozen-string-literal].each do |arg|
+ key = "#{opt}=#{arg}"
+ a.for(key) do
+ assert_in_out_err(["--disable=gems", "--#{key}"], 'p("foo".frozen?)', [exp])
+ end
+ end
+ end
+ %W"disable enable".product(%W[false true]) do |opt, exp|
+ a.for("#{opt}=>#{exp}") do
+ assert_in_out_err(["-w", "--disable=gems", "--#{opt}=frozen-string-literal"], <<-"end;", [exp])
+ #-*- frozen-string-literal: #{exp} -*-
+ p("foo".frozen?)
+ end;
+ end
+ end
+ end
+ end
+
+ def test_frozen_string_literal_debug
+ with_debug_pat = /created at/
+ wo_debug_pat = /can\'t modify frozen String \(FrozenError\)\n\z/
+ frozen = [
+ ["--enable-frozen-string-literal", true],
+ ["--disable-frozen-string-literal", false],
+ [nil, false],
+ ]
+ debugs = [
+ ["--debug-frozen-string-literal", true],
+ ["--debug=frozen-string-literal", true],
+ ["--debug", true],
+ [nil, false],
+ ]
+ opts = ["--disable=gems"]
+ frozen.product(debugs) do |(opt1, freeze), (opt2, debug)|
+ opt = opts + [opt1, opt2].compact
+ 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"', freeze && debug ? with_debug_pat : wo_debug_pat],
+ ].each do |code, expected|
+ assert_in_out_err(opt, code, [], expected, [opt, code])
+ end
+ end
+ end
+
+ def test___dir__encoding
+ lang = {"LC_ALL"=>ENV["LC_ALL"]||ENV["LANG"]}
+ with_tmpchdir do
+ testdir = "\u30c6\u30b9\u30c8"
+ Dir.mkdir(testdir)
+ Dir.chdir(testdir) do
+ open("test.rb", "w") do |f|
+ f.puts <<-END
+ if __FILE__.encoding == __dir__.encoding
+ p true
+ else
+ puts "__FILE__: \#{__FILE__.encoding}, __dir__: \#{__dir__.encoding}"
+ end
+ END
+ end
+ r, = EnvUtil.invoke_ruby([lang, "test.rb"], "", true)
+ assert_equal "true", r.chomp, "the encoding of __FILE__ and __dir__ should be same"
+ end
+ end
+ end
+
+ def test_cwd_encoding
+ with_tmpchdir do
+ testdir = "\u30c6\u30b9\u30c8"
+ Dir.mkdir(testdir)
+ Dir.chdir(testdir) do
+ File.write("a.rb", "require './b'")
+ File.write("b.rb", "puts 'ok'")
+ assert_ruby_status([{"RUBYLIB"=>"."}, *%w[-E cp932:utf-8 a.rb]])
+ end
+ end
+ 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)
+ end
+
+ def test_argv_tainted
+ assert_separately(%w[- arg], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_predicate(ARGV[0], :tainted?, '[ruby-dev:50596] [Bug #14941]')
+ end;
+ end
+
+ private
+
+ def mjit_force_enabled?
+ "#{RbConfig::CONFIG['CFLAGS']} #{RbConfig::CONFIG['CPPFLAGS']}".match?(/(\A|\s)-D ?MJIT_FORCE_ENABLE\b/)
+ end
end
diff --git a/test/ruby/test_rubyvm.rb b/test/ruby/test_rubyvm.rb
new file mode 100644
index 0000000000..7673d8dfbe
--- /dev/null
+++ b/test/ruby/test_rubyvm.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: false
+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]
+
+ RubyVM.stat(stat = {})
+ assert_not_empty stat
+ assert_equal stat[:global_method_state], RubyVM.stat(:global_method_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
+end
diff --git a/test/ruby/test_rubyvm_mjit.rb b/test/ruby/test_rubyvm_mjit.rb
new file mode 100644
index 0000000000..12772320f5
--- /dev/null
+++ b/test/ruby/test_rubyvm_mjit.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+require 'test/unit'
+require_relative '../lib/jit_support'
+
+return if RbConfig::CONFIG["MJIT_SUPPORT"] == 'no'
+
+class TestRubyVMMJIT < Test::Unit::TestCase
+ include JITSupport
+
+ def setup
+ unless JITSupport.supported?
+ skip 'JIT seems not supported on this platform'
+ end
+ end
+
+ def test_pause
+ out, err = eval_with_jit(<<~'EOS', verbose: 1, min_calls: 1, wait: false)
+ i = 0
+ while i < 5
+ eval("def mjit#{i}; end; mjit#{i}")
+ i += 1
+ end
+ print RubyVM::MJIT.pause
+ print RubyVM::MJIT.pause
+ while i < 10
+ eval("def mjit#{i}; end; mjit#{i}")
+ i += 1
+ end
+ print RubyVM::MJIT.pause # no JIT here
+ EOS
+ assert_equal('truefalsefalse', out)
+ assert_equal(
+ 5, err.scan(/#{JITSupport::JIT_SUCCESS_PREFIX}/).size,
+ "unexpected stdout:\n```\n#{out}```\n\nstderr:\n```\n#{err}```",
+ )
+ end
+
+ def test_pause_does_not_hang_on_full_units
+ out, _ = eval_with_jit(<<~'EOS', verbose: 1, min_calls: 1, max_cache: 10, wait: false)
+ i = 0
+ while i < 11
+ eval("def mjit#{i}; end; mjit#{i}")
+ i += 1
+ end
+ print RubyVM::MJIT.pause
+ EOS
+ assert_equal('true', out)
+ end
+
+ def test_pause_wait_false
+ out, err = eval_with_jit(<<~'EOS', verbose: 1, min_calls: 1, wait: false)
+ i = 0
+ while i < 10
+ eval("def mjit#{i}; end; mjit#{i}")
+ i += 1
+ end
+ print RubyVM::MJIT.pause(wait: false)
+ print RubyVM::MJIT.pause(wait: false)
+ EOS
+ assert_equal('truefalse', out)
+ assert_equal(true, err.scan(/#{JITSupport::JIT_SUCCESS_PREFIX}/).size < 10)
+ end
+
+ def test_resume
+ out, err = eval_with_jit(<<~'EOS', verbose: 1, min_calls: 1, wait: false)
+ print RubyVM::MJIT.resume
+ print RubyVM::MJIT.pause
+ print RubyVM::MJIT.resume
+ print RubyVM::MJIT.resume
+ print RubyVM::MJIT.pause
+ EOS
+ assert_equal('falsetruetruefalsetrue', out)
+ assert_equal(0, err.scan(/#{JITSupport::JIT_SUCCESS_PREFIX}/).size)
+ end
+end
diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb
index f885cb0042..29b69b7ce3 100644
--- a/test/ruby/test_settracefunc.rb
+++ b/test/ruby/test_settracefunc.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestSetTraceFunc < Test::Unit::TestCase
@@ -7,29 +8,36 @@ class TestSetTraceFunc < Test::Unit::TestCase
:trace_instruction => true,
:specialized_instruction => false
}
+ @target_thread = Thread.current
end
def teardown
set_trace_func(nil)
RubyVM::InstructionSequence.compile_option = @original_compile_option
+ @target_thread = nil
+ end
+
+ def target_thread?
+ Thread.current == @target_thread
end
def test_c_call
events = []
- eval <<-EOF.gsub(/^.*?: /, "")
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
- 2: events << [event, lineno, mid, klass]
+ 2: events << [event, lineno, mid, klass] if file == name
3: })
4: x = 1 + 1
5: set_trace_func(nil)
EOF
- assert_equal(["c-return", 3, :set_trace_func, Kernel],
+ assert_equal(["c-return", 1, :set_trace_func, Kernel],
events.shift)
assert_equal(["line", 4, __method__, self.class],
events.shift)
- assert_equal(["c-call", 4, :+, Fixnum],
+ assert_equal(["c-call", 4, :+, Integer],
events.shift)
- assert_equal(["c-return", 4, :+, Fixnum],
+ assert_equal(["c-return", 4, :+, Integer],
events.shift)
assert_equal(["line", 5, __method__, self.class],
events.shift)
@@ -40,9 +48,10 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_call
events = []
- eval <<-EOF.gsub(/^.*?: /, "")
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
- 2: events << [event, lineno, mid, klass]
+ 2: events << [event, lineno, mid, klass] if file == name
3: })
4: def add(x, y)
5: x + y
@@ -50,13 +59,13 @@ class TestSetTraceFunc < Test::Unit::TestCase
7: x = add(1, 1)
8: set_trace_func(nil)
EOF
- assert_equal(["c-return", 3, :set_trace_func, Kernel],
+ assert_equal(["c-return", 1, :set_trace_func, Kernel],
events.shift)
assert_equal(["line", 4, __method__, self.class],
events.shift)
- assert_equal(["c-call", 4, :method_added, Module],
+ assert_equal(["c-call", 4, :method_added, self.class],
events.shift)
- assert_equal(["c-return", 4, :method_added, Module],
+ assert_equal(["c-return", 4, :method_added, self.class],
events.shift)
assert_equal(["line", 7, __method__, self.class],
events.shift)
@@ -64,9 +73,9 @@ class TestSetTraceFunc < Test::Unit::TestCase
events.shift)
assert_equal(["line", 5, :add, self.class],
events.shift)
- assert_equal(["c-call", 5, :+, Fixnum],
+ assert_equal(["c-call", 5, :+, Integer],
events.shift)
- assert_equal(["c-return", 5, :+, Fixnum],
+ assert_equal(["c-return", 5, :+, Integer],
events.shift)
assert_equal(["return", 6, :add, self.class],
events.shift)
@@ -79,9 +88,10 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_class
events = []
- eval <<-EOF.gsub(/^.*?: /, "")
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
- 2: events << [event, lineno, mid, klass]
+ 2: events << [event, lineno, mid, klass] if file == name
3: })
4: class Foo
5: def bar
@@ -90,7 +100,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
8: x = Foo.new.bar
9: set_trace_func(nil)
EOF
- assert_equal(["c-return", 3, :set_trace_func, Kernel],
+ assert_equal(["c-return", 1, :set_trace_func, Kernel],
events.shift)
assert_equal(["line", 4, __method__, self.class],
events.shift)
@@ -131,41 +141,42 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_return # [ruby-dev:38701]
events = []
- eval <<-EOF.gsub(/^.*?: /, "")
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
- 2: events << [event, lineno, mid, klass]
+ 2: events << [event, lineno, mid, klass] if file == name
3: })
- 4: def foo(a)
+ 4: def meth_return(a)
5: return if a
6: return
7: end
- 8: foo(true)
- 9: foo(false)
+ 8: meth_return(true)
+ 9: meth_return(false)
10: set_trace_func(nil)
EOF
- assert_equal(["c-return", 3, :set_trace_func, Kernel],
+ assert_equal(["c-return", 1, :set_trace_func, Kernel],
events.shift)
assert_equal(["line", 4, __method__, self.class],
events.shift)
- assert_equal(["c-call", 4, :method_added, Module],
+ assert_equal(["c-call", 4, :method_added, self.class],
events.shift)
- assert_equal(["c-return", 4, :method_added, Module],
+ assert_equal(["c-return", 4, :method_added, self.class],
events.shift)
assert_equal(["line", 8, __method__, self.class],
events.shift)
- assert_equal(["call", 4, :foo, self.class],
+ assert_equal(["call", 4, :meth_return, self.class],
events.shift)
- assert_equal(["line", 5, :foo, self.class],
+ assert_equal(["line", 5, :meth_return, self.class],
events.shift)
- assert_equal(["return", 5, :foo, self.class],
+ assert_equal(["return", 5, :meth_return, self.class],
events.shift)
assert_equal(["line", 9, :test_return, self.class],
events.shift)
- assert_equal(["call", 4, :foo, self.class],
+ assert_equal(["call", 4, :meth_return, self.class],
events.shift)
- assert_equal(["line", 5, :foo, self.class],
+ assert_equal(["line", 5, :meth_return, self.class],
events.shift)
- assert_equal(["return", 7, :foo, self.class],
+ assert_equal(["return", 7, :meth_return, self.class],
events.shift)
assert_equal(["line", 10, :test_return, self.class],
events.shift)
@@ -176,34 +187,35 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_return2 # [ruby-core:24463]
events = []
- eval <<-EOF.gsub(/^.*?: /, "")
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
- 2: events << [event, lineno, mid, klass]
+ 2: events << [event, lineno, mid, klass] if file == name
3: })
- 4: def foo
+ 4: def meth_return2
5: a = 5
6: return a
7: end
- 8: foo
+ 8: meth_return2
9: set_trace_func(nil)
EOF
- assert_equal(["c-return", 3, :set_trace_func, Kernel],
+ assert_equal(["c-return", 1, :set_trace_func, Kernel],
events.shift)
assert_equal(["line", 4, __method__, self.class],
events.shift)
- assert_equal(["c-call", 4, :method_added, Module],
+ assert_equal(["c-call", 4, :method_added, self.class],
events.shift)
- assert_equal(["c-return", 4, :method_added, Module],
+ assert_equal(["c-return", 4, :method_added, self.class],
events.shift)
assert_equal(["line", 8, __method__, self.class],
events.shift)
- assert_equal(["call", 4, :foo, self.class],
+ assert_equal(["call", 4, :meth_return2, self.class],
events.shift)
- assert_equal(["line", 5, :foo, self.class],
+ assert_equal(["line", 5, :meth_return2, self.class],
events.shift)
- assert_equal(["line", 6, :foo, self.class],
+ assert_equal(["line", 6, :meth_return2, self.class],
events.shift)
- assert_equal(["return", 7, :foo, self.class],
+ assert_equal(["return", 7, :meth_return2, self.class],
events.shift)
assert_equal(["line", 9, :test_return2, self.class],
events.shift)
@@ -214,9 +226,10 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_raise
events = []
- eval <<-EOF.gsub(/^.*?: /, "")
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
- 2: events << [event, lineno, mid, klass]
+ 2: events << [event, lineno, mid, klass] if file == name
3: })
4: begin
5: raise TypeError, "error"
@@ -224,9 +237,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
7: end
8: set_trace_func(nil)
EOF
- assert_equal(["c-return", 3, :set_trace_func, Kernel],
- events.shift)
- assert_equal(["line", 4, __method__, self.class],
+ assert_equal(["c-return", 1, :set_trace_func, Kernel],
events.shift)
assert_equal(["line", 5, __method__, self.class],
events.shift)
@@ -240,18 +251,14 @@ class TestSetTraceFunc < Test::Unit::TestCase
events.shift)
assert_equal(["c-return", 5, :exception, Exception],
events.shift)
+ assert_equal(["c-return", 5, :raise, Kernel],
+ events.shift)
assert_equal(["c-call", 5, :backtrace, Exception],
events.shift)
assert_equal(["c-return", 5, :backtrace, Exception],
events.shift)
- assert_equal(["c-call", 5, :set_backtrace, Exception],
- events.shift)
- assert_equal(["c-return", 5, :set_backtrace, Exception],
- events.shift)
assert_equal(["raise", 5, :test_raise, TestSetTraceFunc],
events.shift)
- assert_equal(["c-return", 5, :raise, Kernel],
- events.shift)
assert_equal(["c-call", 6, :===, Module],
events.shift)
assert_equal(["c-return", 6, :===, Module],
@@ -265,24 +272,23 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_break # [ruby-core:27606] [Bug #2610]
events = []
- eval <<-EOF.gsub(/^.*?: /, "")
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
- 2: events << [event, lineno, mid, klass]
+ 2: events << [event, lineno, mid, klass] if file == name
3: })
4: [1,2,3].any? {|n| n}
8: set_trace_func(nil)
EOF
- [["c-return", 3, :set_trace_func, Kernel],
+ [["c-return", 1, :set_trace_func, Kernel],
["line", 4, __method__, self.class],
- ["c-call", 4, :any?, Enumerable],
- ["c-call", 4, :each, Array],
+ ["c-call", 4, :any?, Array],
["line", 4, __method__, self.class],
- ["c-return", 4, :each, Array],
- ["c-return", 4, :any?, Enumerable],
+ ["c-return", 4, :any?, Array],
["line", 5, __method__, self.class],
- ["c-call", 5, :set_trace_func, Kernel]].each{|e|
- assert_equal(e, events.shift)
+ ["c-call", 5, :set_trace_func, Kernel]].each.with_index{|e, i|
+ assert_equal(e, events.shift, "mismatch on #{i}th trace")
}
end
@@ -300,18 +306,21 @@ class TestSetTraceFunc < Test::Unit::TestCase
prc = Proc.new { |event, file, lineno, mid, binding, klass|
events[:set] << [event, lineno, mid, klass, :set]
}
+ prc = prc # suppress warning
prc2 = Proc.new { |event, file, lineno, mid, binding, klass|
events[:add] << [event, lineno, mid, klass, :add]
}
+ prc2 = prc2 # suppress warning
th = Thread.new do
th = Thread.current
- eval <<-EOF.gsub(/^.*?: /, "")
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
1: th.set_trace_func(prc)
2: th.add_trace_func(prc2)
3: class ThreadTraceInnerClass
4: def foo
- 5: x = 1 + 1
+ 5: _x = 1 + 1
6: end
7: end
8: ThreadTraceInnerClass.new.foo
@@ -342,8 +351,8 @@ class TestSetTraceFunc < Test::Unit::TestCase
["c-return", 8, :new, Class],
["call", 4, :foo, ThreadTraceInnerClass],
["line", 5, :foo, ThreadTraceInnerClass],
- ["c-call", 5, :+, Fixnum],
- ["c-return", 5, :+, Fixnum],
+ ["c-call", 5, :+, Integer],
+ ["c-return", 5, :+, Integer],
["return", 6, :foo, ThreadTraceInnerClass],
["line", 9, __method__, self.class],
["c-call", 9, :set_trace_func, Thread]].each do |e|
@@ -354,4 +363,1785 @@ class TestSetTraceFunc < Test::Unit::TestCase
assert_equal([], events[:set])
assert_equal([], events[:add])
end
+
+ def test_trace_defined_method
+ events = []
+ name = "#{self.class}\##{__method__}"
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, name
+ 1: class FooBar; define_method(:foobar){}; end
+ 2: fb = FooBar.new
+ 3: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
+ 4: events << [event, lineno, mid, klass] if file == name
+ 5: })
+ 6: fb.foobar
+ 7: set_trace_func(nil)
+ EOF
+
+ [["c-return", 3, :set_trace_func, Kernel],
+ ["line", 6, __method__, self.class],
+ ["call", 1, :foobar, FooBar],
+ ["return", 6, :foobar, FooBar],
+ ["line", 7, __method__, self.class],
+ ["c-call", 7, :set_trace_func, Kernel]].each{|e|
+ assert_equal(e, events.shift)
+ }
+ end
+
+ def test_remove_in_trace
+ bug3921 = '[ruby-dev:42350]'
+ ok = false
+ func = lambda{|e, f, l, i, b, k|
+ set_trace_func(nil)
+ ok = eval("self", b)
+ }
+
+ set_trace_func(func)
+ assert_equal(self, ok, bug3921)
+ end
+
+ class << self
+ define_method(:method_added, Module.method(:method_added))
+ end
+
+ def trace_by_tracepoint *trace_events
+ events = []
+ trace = nil
+ xyzzy = nil
+ _local_var = :outer
+ raised_exc = nil
+ method = :trace_by_tracepoint
+ _get_data = lambda{|tp|
+ case tp.event
+ when :return, :c_return
+ tp.return_value
+ when :raise
+ tp.raised_exception
+ else
+ :nothing
+ end
+ }
+ _defined_class = lambda{|tp|
+ klass = tp.defined_class
+ begin
+ # If it is singleton method, then return original class
+ # to make compatible with set_trace_func().
+ # This is very ad-hoc hack. I hope I can make more clean test on it.
+ case klass.inspect
+ when /Class:TracePoint/; return TracePoint
+ when /Class:Exception/; return Exception
+ else klass
+ end
+ rescue Exception => e
+ e
+ end if klass
+ }
+
+ trace = nil
+ begin
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, 'xyzzy'
+ 1: trace = TracePoint.trace(*trace_events){|tp| next if !target_thread?
+ 2: events << [tp.event, tp.lineno, tp.path, _defined_class.(tp), tp.method_id, tp.self, tp.binding.eval("_local_var"), _get_data.(tp)] if tp.path == 'xyzzy'
+ 3: }
+ 4: 1.times{|;_local_var| _local_var = :inner
+ 5: tap{}
+ 6: }
+ 7: class XYZZY
+ 8: _local_var = :XYZZY_outer
+ 9: def foo
+ 10: _local_var = :XYZZY_foo
+ 11: bar
+ 12: end
+ 13: def bar
+ 14: _local_var = :XYZZY_bar
+ 15: tap{}
+ 16: end
+ 17: end
+ 18: xyzzy = XYZZY.new
+ 19: xyzzy.foo
+ 20: begin; raise RuntimeError; rescue RuntimeError => raised_exc; end
+ 21: trace.disable
+ EOF
+ self.class.class_eval{remove_const(:XYZZY)}
+ ensure
+ trace.disable if trace&.enabled?
+ end
+
+ answer_events = [
+ #
+ [:c_return, 1, "xyzzy", TracePoint, :trace, TracePoint, :outer, trace],
+ [:line, 4, 'xyzzy', self.class, method, self, :outer, :nothing],
+ [: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],
+ [:c_return, 7, "xyzzy", Class, :inherited, Object, :outer, nil],
+ [:class, 7, "xyzzy", nil, nil, xyzzy.class, nil, :nothing],
+ [:line, 8, "xyzzy", nil, nil, xyzzy.class, nil, :nothing],
+ [:line, 9, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
+ [:c_call, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
+ [:c_return, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
+ [:line, 13, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
+ [:c_call, 13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
+ [:c_return,13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
+ [:end, 17, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
+ [:line, 18, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
+ [:c_call, 18, "xyzzy", Class, :new, xyzzy.class, :outer, :nothing],
+ [:c_call, 18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, :nothing],
+ [:c_return,18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, nil],
+ [:c_return,18, "xyzzy", Class, :new, xyzzy.class, :outer, xyzzy],
+ [:line, 19, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
+ [:call, 9, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing],
+ [:line, 10, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing],
+ [:line, 11, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, :nothing],
+ [: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],
+ [:c_call, 20, "xyzzy", Kernel, :raise, self, :outer, :nothing],
+ [:c_call, 20, "xyzzy", Exception, :exception, RuntimeError, :outer, :nothing],
+ [:c_call, 20, "xyzzy", Exception, :initialize, raised_exc, :outer, :nothing],
+ [:c_return,20, "xyzzy", Exception, :initialize, raised_exc, :outer, raised_exc],
+ [:c_return,20, "xyzzy", Exception, :exception, RuntimeError, :outer, raised_exc],
+ [:c_return,20, "xyzzy", Kernel, :raise, self, :outer, nil],
+ [:c_call, 20, "xyzzy", Exception, :backtrace, raised_exc, :outer, :nothing],
+ [:c_return,20, "xyzzy", Exception, :backtrace, raised_exc, :outer, nil],
+ [:raise, 20, "xyzzy", TestSetTraceFunc, :trace_by_tracepoint, self, :outer, raised_exc],
+ [:c_call, 20, "xyzzy", Module, :===, RuntimeError,:outer, :nothing],
+ [:c_return,20, "xyzzy", Module, :===, RuntimeError,:outer, true],
+ [:line, 21, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
+ [:c_call, 21, "xyzzy", TracePoint, :disable, trace, :outer, :nothing],
+ ]
+
+ return events, answer_events
+ end
+
+ def trace_by_set_trace_func
+ events = []
+ trace = nil
+ trace = trace
+ xyzzy = nil
+ xyzzy = xyzzy
+ _local_var = :outer
+ eval <<-EOF.gsub(/^.*?: /, ""), nil, 'xyzzy'
+ 1: set_trace_func(lambda{|event, file, line, id, binding, klass|
+ 2: events << [event, line, file, klass, id, binding.eval('self'), binding.eval("_local_var")] if file == 'xyzzy'
+ 3: })
+ 4: 1.times{|;_local_var| _local_var = :inner
+ 5: tap{}
+ 6: }
+ 7: class XYZZY
+ 8: _local_var = :XYZZY_outer
+ 9: def foo
+ 10: _local_var = :XYZZY_foo
+ 11: bar
+ 12: end
+ 13: def bar
+ 14: _local_var = :XYZZY_bar
+ 15: tap{}
+ 16: end
+ 17: end
+ 18: xyzzy = XYZZY.new
+ 19: xyzzy.foo
+ 20: begin; raise RuntimeError; rescue RuntimeError => raised_exc; end
+ 21: set_trace_func(nil)
+ EOF
+ self.class.class_eval{remove_const(:XYZZY)}
+ return events
+ end
+
+ def test_tracepoint
+ events1, answer_events = *trace_by_tracepoint(:line, :class, :end, :call, :return, :c_call, :c_return, :raise)
+
+ ms = [events1, answer_events].map{|evs|
+ evs.map{|e|
+ "#{e[0]} - #{e[2]}:#{e[1]} id: #{e[4]}"
+ }
+ }
+
+ mesg = ms[0].zip(ms[1]).map{|a, b|
+ if a != b
+ "#{a} <-> #{b}"
+ end
+ }.compact.join("\n")
+
+ answer_events.zip(events1){|answer, event|
+ assert_equal answer, event, mesg
+ }
+
+ events2 = trace_by_set_trace_func
+ events1.zip(events2){|ev1, ev2|
+ ev2[0] = ev2[0].sub('-', '_').to_sym
+ assert_equal ev1[0..2], ev2[0..2], ev1.inspect
+
+ # event, line, file, klass, id, binding.eval('self'), binding.eval("_local_var")
+ assert_equal ev1[3].nil?, ev2[3].nil? # klass
+ assert_equal ev1[4].nil?, ev2[4].nil? # id
+ assert_equal ev1[6], ev2[6] # _local_var
+ }
+
+ [:line, :class, :end, :call, :return, :c_call, :c_return, :raise].each{|event|
+ events1, answer_events = *trace_by_tracepoint(event)
+ answer_events.find_all{|e| e[0] == event}.zip(events1){|answer_line, event_line|
+ assert_equal answer_line, event_line
+ }
+ }
+ end
+
+ def test_tracepoint_object_id
+ tps = []
+ trace = TracePoint.trace(){|tp|
+ next if !target_thread?
+ tps << tp
+ }
+ tap{}
+ tap{}
+ tap{}
+ trace.disable
+
+ # passed tp is unique, `trace' object which is generated by TracePoint.trace
+ tps.each{|tp|
+ assert_equal trace, tp
+ }
+ end
+
+ def test_tracepoint_access_from_outside
+ tp_store = nil
+ trace = TracePoint.trace(){|tp|
+ next if !target_thread?
+ tp_store = tp
+ }
+ tap{}
+ trace.disable
+
+ assert_raise(RuntimeError){tp_store.lineno}
+ assert_raise(RuntimeError){tp_store.event}
+ assert_raise(RuntimeError){tp_store.path}
+ assert_raise(RuntimeError){tp_store.method_id}
+ assert_raise(RuntimeError){tp_store.defined_class}
+ assert_raise(RuntimeError){tp_store.binding}
+ assert_raise(RuntimeError){tp_store.self}
+ assert_raise(RuntimeError){tp_store.return_value}
+ assert_raise(RuntimeError){tp_store.raised_exception}
+ end
+
+ def foo
+ end
+
+ def test_tracepoint_enable
+ ary = []
+ args = nil
+ trace = TracePoint.new(:call){|tp|
+ next if !target_thread?
+ ary << tp.method_id
+ }
+ foo
+ trace.enable{|*a|
+ args = a
+ foo
+ }
+ foo
+ assert_equal([:foo], ary)
+ assert_equal([], args)
+
+ trace = TracePoint.new{}
+ begin
+ assert_equal(false, trace.enable)
+ assert_equal(true, trace.enable)
+ trace.enable{}
+ assert_equal(true, trace.enable)
+ ensure
+ trace.disable
+ end
+ end
+
+ def test_tracepoint_disable
+ ary = []
+ args = nil
+ trace = TracePoint.trace(:call){|tp|
+ next if !target_thread?
+ ary << tp.method_id
+ }
+ foo
+ trace.disable{|*a|
+ args = a
+ foo
+ }
+ foo
+ trace.disable
+ assert_equal([:foo, :foo], ary)
+ assert_equal([], args)
+
+ trace = TracePoint.new{}
+ trace.enable{
+ assert_equal(true, trace.disable)
+ assert_equal(false, trace.disable)
+ trace.disable{}
+ assert_equal(false, trace.disable)
+ }
+ end
+
+ def test_tracepoint_enabled
+ trace = TracePoint.trace(:call){|tp|
+ #
+ }
+ assert_equal(true, trace.enabled?)
+ trace.disable{
+ assert_equal(false, trace.enabled?)
+ trace.enable{
+ assert_equal(true, trace.enabled?)
+ }
+ }
+ trace.disable
+ assert_equal(false, trace.enabled?)
+ end
+
+ def parameter_test(a, b, c)
+ yield
+ end
+
+ def test_tracepoint_parameters
+ trace = TracePoint.new(:line, :class, :end, :call, :return, :b_call, :b_return, :c_call, :c_return, :raise){|tp|
+ next if !target_thread?
+ next if tp.path != __FILE__
+ case tp.event
+ when :call, :return
+ assert_equal([[:req, :a], [:req, :b], [:req, :c]], tp.parameters)
+ when :b_call, :b_return
+ next if tp.parameters == []
+ if tp.parameters.first == [:opt, :x]
+ assert_equal([[:opt, :x], [:opt, :y], [:opt, :z]], tp.parameters)
+ else
+ assert_equal([[:req, :p], [:req, :q], [:req, :r]], tp.parameters)
+ end
+ when :c_call, :c_return
+ assert_equal([[:req]], tp.parameters) if tp.method_id == :getbyte
+ when :line, :class, :end, :raise
+ assert_raise(RuntimeError) { tp.parameters }
+ end
+ }
+ obj = Object.new
+ trace.enable{
+ parameter_test(1, 2, 3) {|x, y, z|
+ }
+ lambda {|p, q, r| }.call(4, 5, 6)
+ "".getbyte(0)
+ class << obj
+ end
+ begin
+ raise
+ rescue
+ end
+ }
+ end
+
+ def method_test_tracepoint_return_value obj
+ obj
+ end
+
+ def test_tracepoint_return_value
+ trace = TracePoint.new(:call, :return){|tp|
+ next if !target_thread?
+ next if tp.path != __FILE__
+ case tp.event
+ when :call
+ assert_raise(RuntimeError) {tp.return_value}
+ when :return
+ assert_equal("xyzzy", tp.return_value)
+ end
+ }
+ trace.enable{
+ method_test_tracepoint_return_value "xyzzy"
+ }
+ end
+
+ class XYZZYException < Exception; end
+ def method_test_tracepoint_raised_exception err
+ raise err
+ end
+
+ def test_tracepoint_raised_exception
+ trace = TracePoint.new(:call, :return, :raise){|tp|
+ next if !target_thread?
+ case tp.event
+ when :call, :return
+ assert_raise(RuntimeError) { tp.raised_exception }
+ when :raise
+ assert_kind_of(XYZZYException, tp.raised_exception)
+ end
+ }
+ trace.enable{
+ begin
+ method_test_tracepoint_raised_exception XYZZYException
+ rescue XYZZYException
+ # ok
+ else
+ raise
+ end
+ }
+ end
+
+ def method_for_test_tracepoint_block
+ yield
+ end
+
+ def test_tracepoint_block
+ events = []
+ TracePoint.new(:call, :return, :c_call, :b_call, :c_return, :b_return){|tp|
+ next if !target_thread?
+ events << [
+ tp.event, tp.method_id, tp.defined_class, tp.self.class,
+ /return/ =~ tp.event ? tp.return_value : nil
+ ]
+ }.enable{
+ 1.times{
+ 3
+ }
+ method_for_test_tracepoint_block{
+ 4
+ }
+ }
+ # pp events
+ # expected_events =
+ [[:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
+ [:c_call, :times, Integer, Integer, nil],
+ [:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
+ [:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 3],
+ [:c_return, :times, Integer, Integer, 1],
+ [:call, :method_for_test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
+ [:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
+ [:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4],
+ [:return, :method_for_test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4],
+ [:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4]
+ ].zip(events){|expected, actual|
+ assert_equal(expected, actual)
+ }
+ end
+
+ def test_tracepoint_thread
+ events = []
+ thread_self = nil
+ created_thread = nil
+ TracePoint.new(:thread_begin, :thread_end){|tp|
+ events << [Thread.current,
+ tp.event,
+ tp.lineno, #=> 0
+ tp.path, #=> nil
+ tp.binding, #=> nil
+ tp.defined_class, #=> nil,
+ tp.self.class # tp.self return creating/ending thread
+ ]
+ }.enable{
+ created_thread = Thread.new{thread_self = self}
+ created_thread.join
+ }
+ events.reject!{|i| i[0] != created_thread}
+ assert_equal(self, thread_self)
+ assert_equal([created_thread, :thread_begin, 0, nil, nil, nil, Thread], events[0])
+ assert_equal([created_thread, :thread_end, 0, nil, nil, nil, Thread], events[1])
+ assert_equal(2, events.size)
+ end
+
+ def test_tracepoint_inspect
+ events = []
+ th = nil
+ trace = TracePoint.new{|tp|
+ next if !target_thread? && th != Thread.current
+ events << [tp.event, tp.inspect]
+ }
+ assert_equal("#<TracePoint:disabled>", trace.inspect)
+ trace.enable{
+ assert_equal("#<TracePoint:enabled>", trace.inspect)
+ th = Thread.new{}
+ th.join
+ }
+ assert_equal("#<TracePoint:disabled>", trace.inspect)
+ events.each{|(ev, str)|
+ case ev
+ when :line
+ assert_match(/ in /, str)
+ when :call, :c_call
+ 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>
+ when /thread/
+ assert_match(/\#<Thread:/, str) # #<TracePoint:thread_end of #<Thread:0x87076c0>>
+ else
+ assert_match(/\#<TracePoint:/, str)
+ end
+ }
+ end
+
+ def test_tracepoint_exception_at_line
+ assert_raise(RuntimeError) do
+ TracePoint.new(:line) {
+ next if !target_thread?
+ raise
+ }.enable {
+ 1
+ }
+ end
+ end
+
+ def test_tracepoint_exception_at_return
+ assert_nothing_raised(Timeout::Error, 'infinite trace') do
+ assert_normal_exit('def m; end; TracePoint.new(:return) {raise}.enable {m}', '', timeout: 3)
+ end
+ end
+
+ def test_tracepoint_exception_at_c_return
+ assert_nothing_raised(Timeout::Error, 'infinite trace') do
+ assert_normal_exit %q{
+ begin
+ TracePoint.new(:c_return){|tp|
+ raise
+ }.enable{
+ tap{ itself }
+ }
+ rescue
+ end
+ }, '', timeout: 3
+ end
+ end
+
+ def test_tracepoint_with_multithreads
+ assert_nothing_raised do
+ TracePoint.new{
+ 10.times{
+ Thread.pass
+ }
+ }.enable do
+ (1..10).map{
+ Thread.new{
+ 1000.times{
+ }
+ }
+ }.each{|th|
+ th.join
+ }
+ end
+ end
+ end
+
+ class FOO_ERROR < RuntimeError; end
+ class BAR_ERROR < RuntimeError; end
+ def m1_test_trace_point_at_return_when_exception
+ m2_test_trace_point_at_return_when_exception
+ end
+ def m2_test_trace_point_at_return_when_exception
+ raise BAR_ERROR
+ end
+
+ def test_trace_point_at_return_when_exception
+ bug_7624 = '[ruby-core:51128] [ruby-trunk - Bug #7624]'
+ TracePoint.new{|tp|
+ next if !target_thread?
+ if tp.event == :return &&
+ tp.method_id == :m2_test_trace_point_at_return_when_exception
+ raise FOO_ERROR
+ end
+ }.enable do
+ assert_raise(FOO_ERROR, bug_7624) do
+ m1_test_trace_point_at_return_when_exception
+ end
+ end
+
+ bug_7668 = '[Bug #7668]'
+ ary = []
+ trace = TracePoint.new{|tp|
+ next if !target_thread?
+ ary << tp.event
+ raise
+ }
+ begin
+ trace.enable{
+ 1.times{
+ raise
+ }
+ }
+ rescue
+ assert_equal([:b_call, :b_return], ary, bug_7668)
+ end
+ end
+
+ def m1_for_test_trace_point_binding_in_ifunc(arg)
+ arg + nil
+ rescue
+ end
+
+ def m2_for_test_trace_point_binding_in_ifunc(arg)
+ arg.inject(:+)
+ rescue
+ end
+
+ def test_trace_point_binding_in_ifunc
+ bug7774 = '[ruby-dev:46908]'
+ src = %q{
+ tp = TracePoint.new(:raise) do |tp|
+ tp.binding
+ end
+ tp.enable do
+ obj = Object.new
+ class << obj
+ include Enumerable
+ def each
+ yield 1
+ end
+ end
+ %s
+ end
+ }
+ assert_normal_exit src % %q{obj.zip({}) {}}, bug7774
+ assert_normal_exit src % %q{
+ require 'continuation'
+ begin
+ c = nil
+ obj.sort_by {|x| callcc {|c2| c ||= c2 }; x }
+ c.call
+ rescue RuntimeError
+ end
+ }, bug7774
+
+ # TracePoint
+ tp_b = nil
+ TracePoint.new(:raise) do |tp|
+ next if !target_thread?
+ tp_b = tp.binding
+ end.enable do
+ m1_for_test_trace_point_binding_in_ifunc(0)
+ assert_equal(self, eval('self', tp_b), '[ruby-dev:46960]')
+
+ m2_for_test_trace_point_binding_in_ifunc([0, nil])
+ assert_equal(self, eval('self', tp_b), '[ruby-dev:46960]')
+ end
+
+ # set_trace_func
+ stf_b = nil
+ set_trace_func ->(event, file, line, id, binding, klass) do
+ stf_b = binding if event == 'raise'
+ end
+ begin
+ m1_for_test_trace_point_binding_in_ifunc(0)
+ assert_equal(self, eval('self', stf_b), '[ruby-dev:46960]')
+
+ m2_for_test_trace_point_binding_in_ifunc([0, nil])
+ assert_equal(self, eval('self', stf_b), '[ruby-dev:46960]')
+ ensure
+ set_trace_func(nil)
+ end
+ end
+
+ def test_trace_point_binding_after_break
+ bug10689 = '[ruby-dev:48797]'
+ assert_in_out_err([], <<-INPUT, [], [], bug10689)
+ class Bug
+ include Enumerable
+
+ def each
+ [0].each do
+ yield
+ end
+ end
+ end
+
+ TracePoint.trace(:c_return) do |tp|
+ tp.binding
+ end
+
+ Bug.new.all? { false }
+ INPUT
+ end
+
+ def test_tracepoint_b_return_with_next
+ n = 0
+ TracePoint.new(:b_return){
+ next if !target_thread?
+ n += 1
+ }.enable{
+ 3.times{
+ next
+ } # 3 times b_retun
+ } # 1 time b_return
+
+ assert_equal 4, n
+ end
+
+ def test_tracepoint_b_return_with_lambda
+ n = 0
+ TracePoint.new(:b_return){
+ next if !target_thread?
+ n+=1
+ }.enable{
+ lambda{
+ return
+ }.call # n += 1 #=> 1
+ 3.times{
+ lambda{
+ return # n += 3 #=> 4
+ }.call
+ } # n += 3 #=> 7
+ begin
+ lambda{
+ raise
+ }.call # n += 1 #=> 8
+ rescue
+ # ignore
+ end # n += 1 #=> 9
+ }
+
+ assert_equal 9, n
+ end
+
+ def test_isolated_raise_in_trace
+ bug9088 = '[ruby-dev:47793] [Bug #9088]'
+ assert_in_out_err([], <<-END, [], [], bug9088)
+ set_trace_func proc {raise rescue nil}
+ 1.times {break}
+ END
+ end
+
+ def test_a_call
+ events = []
+ TracePoint.new(:a_call){|tp|
+ next if !target_thread?
+ events << tp.event
+ }.enable{
+ 1.times{
+ 3
+ }
+ method_for_test_tracepoint_block{
+ 4
+ }
+ }
+ assert_equal([
+ :b_call,
+ :c_call,
+ :b_call,
+ :call,
+ :b_call,
+ ], events)
+ end
+
+ def test_a_return
+ events = []
+ TracePoint.new(:a_return){|tp|
+ next if !target_thread?
+ events << tp.event
+ }.enable{
+ 1.times{
+ 3
+ }
+ method_for_test_tracepoint_block{
+ 4
+ }
+ }
+ assert_equal([
+ :b_return,
+ :c_return,
+ :b_return,
+ :return,
+ :b_return
+ ], events)
+ end
+
+ def test_const_missing
+ bug59398 = '[ruby-core:59398]'
+ events = []
+ assert !defined?(MISSING_CONSTANT_59398)
+ TracePoint.new(:c_call, :c_return, :call, :return){|tp|
+ next if !target_thread?
+ next unless tp.defined_class == Module
+ # rake/ext/module.rb aliases :const_missing and Ruby uses the aliased name
+ # but this only happens when running the full test suite
+ events << [tp.event,tp.method_id] if tp.method_id == :const_missing || tp.method_id == :rake_original_const_missing
+ }.enable{
+ MISSING_CONSTANT_59398 rescue nil
+ }
+ if events.map{|e|e[1]}.include?(:rake_original_const_missing)
+ assert_equal([
+ [:call, :const_missing],
+ [:c_call, :rake_original_const_missing],
+ [:c_return, :rake_original_const_missing],
+ [:return, :const_missing],
+ ], events, bug59398)
+ else
+ assert_equal([
+ [:c_call, :const_missing],
+ [:c_return, :const_missing]
+ ], events, bug59398)
+ end
+ end
+
+ class AliasedRubyMethod
+ def foo; 1; end;
+ alias bar foo
+ end
+ def test_aliased_ruby_method
+ events = []
+ aliased = AliasedRubyMethod.new
+ TracePoint.new(:call, :return){|tp|
+ next if !target_thread?
+ events << [tp.event, tp.method_id]
+ }.enable{
+ aliased.bar
+ }
+ assert_equal([
+ [:call, :foo],
+ [:return, :foo]
+ ], events, "should use original method name for tracing ruby methods")
+ end
+ class AliasedCMethod < Hash
+ alias original_size size
+ def size; original_size; end
+ end
+
+ def test_aliased_c_method
+ events = []
+ aliased = AliasedCMethod.new
+ TracePoint.new(:call, :return, :c_call, :c_return){|tp|
+ next if !target_thread?
+ events << [tp.event, tp.method_id]
+ }.enable{
+ aliased.size
+ }
+ assert_equal([
+ [:call, :size],
+ [:c_call, :size],
+ [:c_return, :size],
+ [:return, :size]
+ ], events, "should use alias method name for tracing c methods")
+ end
+
+ def test_method_missing
+ bug59398 = '[ruby-core:59398]'
+ events = []
+ assert !respond_to?(:missing_method_59398)
+ TracePoint.new(:c_call, :c_return, :call, :return){|tp|
+ next if !target_thread?
+ next unless tp.defined_class == BasicObject
+ # rake/ext/module.rb aliases :const_missing and Ruby uses the aliased name
+ # but this only happens when running the full test suite
+ events << [tp.event,tp.method_id] if tp.method_id == :method_missing
+ }.enable{
+ missing_method_59398 rescue nil
+ }
+ assert_equal([
+ [:c_call, :method_missing],
+ [:c_return, :method_missing]
+ ], events, bug59398)
+ end
+
+ class C9759
+ define_method(:foo){
+ raise
+ }
+ end
+
+ def test_define_method_on_exception
+ events = []
+ obj = C9759.new
+ TracePoint.new(:call, :return){|tp|
+ next unless target_thread?
+ events << [tp.event, tp.method_id]
+ }.enable{
+ obj.foo rescue nil
+ }
+ assert_equal([[:call, :foo], [:return, :foo]], events, 'Bug #9759')
+
+ events = []
+ begin
+ set_trace_func(lambda{|event, file, lineno, mid, binding, klass|
+ next unless target_thread?
+ case event
+ when 'call', 'return'
+ events << [event, mid]
+ end
+ })
+ obj.foo rescue nil
+ set_trace_func(nil)
+
+ assert_equal([['call', :foo], ['return', :foo]], events, 'Bug #9759')
+ ensure
+ end
+ end
+
+ class C11492
+ define_method(:foo_return){
+ return true
+ }
+ define_method(:foo_break){
+ break true
+ }
+ end
+
+ def test_define_method_on_return
+ # return
+ events = []
+ obj = C11492.new
+ TracePoint.new(:call, :return){|tp|
+ next unless target_thread?
+ events << [tp.event, tp.method_id]
+ }.enable{
+ obj.foo_return
+ }
+ assert_equal([[:call, :foo_return], [:return, :foo_return]], events, 'Bug #11492')
+
+ # break
+ events = []
+ obj = C11492.new
+ TracePoint.new(:call, :return){|tp|
+ next unless target_thread?
+ events << [tp.event, tp.method_id]
+ }.enable{
+ obj.foo_break
+ }
+ assert_equal([[:call, :foo_break], [:return, :foo_break]], events, 'Bug #11492')
+
+ # set_trace_func
+ # return
+ events = []
+ begin
+ set_trace_func(lambda{|event, file, lineno, mid, binding, klass|
+ next unless target_thread?
+ case event
+ when 'call', 'return'
+ events << [event, mid]
+ end
+ })
+ obj.foo_return
+ set_trace_func(nil)
+
+ assert_equal([['call', :foo_return], ['return', :foo_return]], events, 'Bug #11492')
+ ensure
+ end
+
+ # break
+ events = []
+ begin
+ set_trace_func(lambda{|event, file, lineno, mid, binding, klass|
+ next unless target_thread?
+ case event
+ when 'call', 'return'
+ events << [event, mid]
+ end
+ })
+ obj.foo_break
+ set_trace_func(nil)
+
+ assert_equal([['call', :foo_break], ['return', :foo_break]], events, 'Bug #11492')
+ ensure
+ end
+ end
+
+ def test_recursive
+ assert_in_out_err([], %q{\
+ TracePoint.new(:c_call){|tp|
+ p tp.method_id
+ }.enable{
+ p 1
+ }
+ }, %w[:p :to_s 1], [], '[Bug #9940]')
+ end
+
+ def method_prefix event
+ case event
+ when :call, :return
+ :n
+ when :c_call, :c_return
+ :c
+ when :b_call, :b_return
+ :b
+ end
+ end
+
+ def method_label tp
+ "#{method_prefix(tp.event)}##{tp.method_id}"
+ end
+
+ def assert_consistent_call_return message='', check_events: nil
+ check_events ||= %i(a_call a_return)
+ call_stack = []
+
+ TracePoint.new(*check_events){|tp|
+ next unless target_thread?
+
+ case tp.event.to_s
+ when /call/
+ call_stack << method_label(tp)
+ when /return/
+ frame = call_stack.pop
+ assert_equal(frame, method_label(tp))
+ end
+ }.enable do
+ yield
+ end
+
+ assert_equal true, call_stack.empty?
+ end
+
+ def method_test_rescue_should_not_cause_b_return
+ begin
+ raise
+ rescue
+ return
+ end
+ end
+
+ def method_test_ensure_should_not_cause_b_return
+ begin
+ raise
+ ensure
+ return
+ end
+ end
+
+ def test_rescue_and_ensure_should_not_cause_b_return
+ assert_consistent_call_return '[Bug #9957]' do
+ method_test_rescue_should_not_cause_b_return
+ begin
+ method_test_ensure_should_not_cause_b_return
+ rescue
+ # ignore
+ end
+ end
+ end
+
+ define_method(:method_test_argument_error_on_bmethod){|correct_key: 1|}
+
+ def test_argument_error_on_bmethod
+ assert_consistent_call_return '[Bug #9959]' do
+ begin
+ method_test_argument_error_on_bmethod(wrong_key: 2)
+ rescue
+ # ignore
+ end
+ end
+ end
+
+ def test_rb_rescue
+ assert_consistent_call_return '[Bug #9961]' do
+ begin
+ -Numeric.new
+ rescue
+ # ignore
+ end
+ end
+ end
+
+ def test_b_call_with_redo
+ assert_consistent_call_return '[Bug #9964]' do
+ i = 0
+ 1.times{
+ break if (i+=1) > 10
+ redo
+ }
+ end
+ end
+
+ def test_no_duplicate_line_events
+ lines = []
+ dummy = []
+
+ TracePoint.new(:line){|tp|
+ next unless target_thread?
+ lines << tp.lineno
+ }.enable{
+ dummy << (1) + (2)
+ dummy << (1) + (2)
+ }
+ assert_equal [__LINE__ - 3, __LINE__ - 2], lines, 'Bug #10449'
+ end
+
+ def test_elsif_line_event
+ bug10763 = '[ruby-core:67720] [Bug #10763]'
+ lines = []
+ line = nil
+
+ TracePoint.new(:line){|tp|
+ next unless target_thread?
+ lines << tp.lineno if line
+ }.enable{
+ line = __LINE__
+ if !line
+ 1
+ elsif line
+ 2
+ end
+ }
+ assert_equal [line+1, line+3, line+4], lines, bug10763
+ end
+
+ class Bug10724
+ def initialize
+ loop{return}
+ end
+ end
+
+ def test_throwing_return_with_finish_frame
+ target_th = Thread.current
+ evs = []
+
+ TracePoint.new(:call, :return){|tp|
+ next unless target_thread?
+ evs << tp.event
+ }.enable{
+ Bug10724.new
+ }
+
+ assert_equal([:call, :return], evs)
+ end
+
+ require 'fiber'
+ def test_fiber_switch
+ # test for resume/yield
+ evs = []
+ TracePoint.new(:fiber_switch){|tp|
+ next unless target_thread?
+ evs << tp.event
+ }.enable{
+ f = Fiber.new{
+ Fiber.yield
+ Fiber.yield
+ Fiber.yield
+ }
+ f.resume
+ f.resume
+ f.resume
+ f.resume
+ begin
+ f.resume
+ rescue FiberError
+ end
+ }
+ assert_equal 8, evs.size
+ evs.each{|ev|
+ assert_equal ev, :fiber_switch
+ }
+
+ # test for transfer
+ evs = []
+ TracePoint.new(:fiber_switch){|tp|
+ next unless target_thread?
+ evs << tp.event
+ }.enable{
+ f1 = f2 = nil
+ f1 = Fiber.new{
+ f2.transfer
+ f2.transfer
+ Fiber.yield :ok
+ }
+ f2 = Fiber.new{
+ f1.transfer
+ f1.transfer
+ }
+ assert_equal :ok, f1.resume
+ }
+ assert_equal 6, evs.size
+ evs.each{|ev|
+ assert_equal ev, :fiber_switch
+ }
+ end
+
+ def test_tracepoint_callee_id
+ events = []
+ capture_events = Proc.new{|tp|
+ next unless target_thread?
+ events << [tp.event, tp.method_id, tp.callee_id]
+ }
+
+ o = Class.new{
+ def m
+ raise
+ end
+ alias alias_m m
+ }.new
+ TracePoint.new(:raise, :call, :return, &capture_events).enable{
+ o.alias_m rescue nil
+ }
+ assert_equal [[:call, :m, :alias_m], [:raise, :m, :alias_m], [:return, :m, :alias_m]], events
+ events.clear
+
+ o = Class.new{
+ alias alias_raise raise
+ def m
+ alias_raise
+ end
+ }.new
+ TracePoint.new(:c_return, &capture_events).enable{
+ o.m rescue nil
+ }
+ assert_equal [:c_return, :raise, :alias_raise], events[0]
+ events.clear
+
+ o = Class.new(String){
+ include Enumerable
+ alias each each_char
+ }.new('foo')
+ TracePoint.new(:c_return, &capture_events).enable{
+ o.find{true}
+ }
+ assert_equal [:c_return, :each_char, :each], events[0]
+ events.clear
+
+ o = Class.new{
+ define_method(:m){}
+ alias alias_m m
+ }.new
+ TracePoint.new(:call, :return, &capture_events).enable{
+ o.alias_m
+ }
+ assert_equal [[:call, :m, :alias_m], [:return, :m, :alias_m]], events
+ events.clear
+
+ o = Class.new{
+ def m
+ tap{return}
+ end
+ alias alias_m m
+ }.new
+ TracePoint.new(:return, &capture_events).enable{
+ o.alias_m
+ }
+ assert_equal [[:return, :m, :alias_m]], events
+ events.clear
+
+ o = Class.new{
+ define_method(:m){raise}
+ alias alias_m m
+ }.new
+ TracePoint.new(:b_return, :return, &capture_events).enable{
+ o.alias_m rescue nil
+ }
+ assert_equal [[:b_return, :m, :alias_m], [:return, :m, :alias_m]], events[0..1]
+ events.clear
+
+ o = Class.new{
+ define_method(:m){tap{return}}
+ alias alias_m m
+ }.new
+ TracePoint.new(:b_return, &capture_events).enable{
+ o.alias_m
+ }
+ assert_equal [[:b_return, :m, :alias_m], [:b_return, :m, :alias_m]], events[0..1]
+ events.clear
+
+ o = Class.new{
+ alias alias_tap tap
+ define_method(:m){alias_tap{return}}
+ }.new
+ TracePoint.new(:c_return, &capture_events).enable{
+ o.m
+ }
+ assert_equal [[:c_return, :tap, :alias_tap]], events
+ events.clear
+
+ c = Class.new{
+ alias initialize itself
+ }
+ TracePoint.new(:c_call, &capture_events).enable{
+ c.new
+ }
+ assert_equal [:c_call, :itself, :initialize], events[1]
+ events.clear
+
+ o = Class.new{
+ alias alias_itself itself
+ }.new
+ TracePoint.new(:c_call, :c_return, &capture_events).enable{
+ o.alias_itself
+ }
+ assert_equal [[:c_call, :itself, :alias_itself], [:c_return, :itself, :alias_itself]], events
+ events.clear
+ end
+
+ # tests for `return_value` with non-local exit [Bug #13369]
+
+ def tp_return_value mid
+ ary = []
+ TracePoint.new(:return, :b_return){|tp| next if !target_thread?; ary << [tp.event, tp.method_id, tp.return_value]}.enable{
+ send mid
+ }
+ ary.pop # last b_return event is not required.
+ ary
+ end
+
+ def f_raise
+ raise
+ rescue
+ return :f_raise_return
+ end
+
+ def f_iter1
+ yield
+ return :f_iter1_return
+ end
+
+ def f_iter2
+ yield
+ return :f_iter2_return
+ end
+
+ def f_return_in_iter
+ f_iter1 do
+ f_iter2 do
+ return :f_return_in_iter_return
+ end
+ end
+ 2
+ end
+
+ def f_break_in_iter
+ f_iter1 do
+ f_iter2 do
+ break :f_break_in_iter_break
+ end
+ :f_iter1_block_value
+ end
+ :f_break_in_iter_return
+ end
+
+ def test_return_value_with_rescue
+ assert_equal [[:return, :f_raise, :f_raise_return]],
+ tp_return_value(:f_raise),
+ '[Bug #13369]'
+
+ assert_equal [[:b_return, :f_return_in_iter, nil],
+ [:return, :f_iter2, nil],
+ [:b_return, :f_return_in_iter, nil],
+ [:return, :f_iter1, nil],
+ [:return, :f_return_in_iter, :f_return_in_iter_return]],
+ tp_return_value(:f_return_in_iter),
+ '[Bug #13369]'
+
+ assert_equal [[:b_return, :f_break_in_iter, :f_break_in_iter_break],
+ [:return, :f_iter2, nil],
+ [:b_return, :f_break_in_iter, :f_iter1_block_value],
+ [:return, :f_iter1, :f_iter1_return],
+ [:return, :f_break_in_iter, :f_break_in_iter_return]],
+ tp_return_value(:f_break_in_iter),
+ '[Bug #13369]'
+ end
+
+ define_method(:f_last_defined) do
+ :f_last_defined
+ end
+
+ define_method(:f_return_defined) do
+ return :f_return_defined
+ end
+
+ define_method(:f_break_defined) do
+ return :f_break_defined
+ end
+
+ define_method(:f_raise_defined) do
+ raise
+ rescue
+ return :f_raise_defined
+ end
+
+ define_method(:f_break_in_rescue_defined) do
+ raise
+ rescue
+ break :f_break_in_rescue_defined
+ end
+
+ def test_return_value_with_rescue_and_defined_methods
+ assert_equal [[:b_return, :f_last_defined, :f_last_defined],
+ [:return, :f_last_defined, :f_last_defined]],
+ tp_return_value(:f_last_defined),
+ '[Bug #13369]'
+
+ assert_equal [[:b_return, :f_return_defined, nil], # current limitation
+ [:return, :f_return_defined, :f_return_defined]],
+ tp_return_value(:f_return_defined),
+ '[Bug #13369]'
+
+ assert_equal [[:b_return, :f_break_defined, nil],
+ [:return, :f_break_defined, :f_break_defined]],
+ tp_return_value(:f_break_defined),
+ '[Bug #13369]'
+
+ assert_equal [[:b_return, :f_raise_defined, nil],
+ [: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],
+ [:return, :f_break_in_rescue_defined, f_break_in_rescue_defined]],
+ tp_return_value(:f_break_in_rescue_defined),
+ '[Bug #13369]'
+ end
+
+ def f_iter
+ yield
+ end
+
+ def f_break_in_rescue
+ f_iter do
+ begin
+ raise
+ rescue
+ break :b
+ end
+ end
+ :f_break_in_rescue_return_value
+ end
+
+ def test_break_with_rescue
+ assert_equal [[:b_return, :f_break_in_rescue, :b],
+ [:return, :f_iter, nil],
+ [:return, :f_break_in_rescue, :f_break_in_rescue_return_value]],
+ tp_return_value(:f_break_in_rescue),
+ '[Bug #13369]'
+ end
+
+ def test_trace_point_raising_exception_in_bmethod_call
+ bug13705 = '[ruby-dev:50162]'
+ assert_normal_exit %q{
+ define_method(:m) {}
+
+ tp = TracePoint.new(:call) do
+ raise ''
+ end
+
+ tap do
+ tap do
+ begin
+ tp.enable
+ m
+ rescue
+ end
+ end
+ end
+ }, bug13705
+ end
+
+ def test_trace_point_require_block
+ assert_raise(ArgumentError) { TracePoint.new(:return) }
+ end
+
+ def method_for_test_thread_add_trace_func
+
+ end
+
+ def test_thread_add_trace_func
+ events = []
+ base_line = __LINE__
+ q = Queue.new
+ t = Thread.new{
+ Thread.current.add_trace_func proc{|ev, file, line, *args|
+ events << [ev, line]
+ } # do not stop trace. They will be stopped at Thread termination.
+ q.push 1
+ _x = 1
+ method_for_test_thread_add_trace_func
+ _y = 2
+ }
+ q.pop
+ method_for_test_thread_add_trace_func
+ t.join
+ assert_equal ["c-return", base_line + 3], events[0]
+ assert_equal ["line", base_line + 6], events[1]
+ assert_equal ["c-call", base_line + 6], events[2]
+ assert_equal ["c-return", base_line + 6], events[3]
+ assert_equal ["line", base_line + 7], events[4]
+ assert_equal ["line", base_line + 8], events[5]
+ assert_equal ["call", base_line + -6], events[6]
+ assert_equal ["return", base_line + -4], events[7]
+ assert_equal ["line", base_line + 9], events[8]
+ assert_equal nil, events[9]
+
+ # other thread
+ events = []
+ m2t_q = Queue.new
+
+ t = Thread.new{
+ Thread.current.abort_on_exception = true
+ assert_equal 1, m2t_q.pop
+ _x = 1
+ method_for_test_thread_add_trace_func
+ _y = 2
+ Thread.current.set_trace_func(nil)
+ method_for_test_thread_add_trace_func
+ }
+ # it is dirty hack. usually we shouldn't use such technique
+ 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?
+
+ t.add_trace_func proc{|ev, file, line, *args|
+ if file == __FILE__
+ events << [ev, line]
+ end
+ }
+
+ method_for_test_thread_add_trace_func
+
+ m2t_q.push 1
+ t.join
+
+ assert_equal ["c-return", base_line + 31], events[0]
+ assert_equal ["line", base_line + 32], events[1]
+ assert_equal ["line", base_line + 33], events[2]
+ assert_equal ["call", base_line + -6], events[3]
+ assert_equal ["return", base_line + -4], events[4]
+ assert_equal ["line", base_line + 34], events[5]
+ assert_equal ["line", base_line + 35], events[6]
+ assert_equal ["c-call", base_line + 35], events[7] # Thread.current
+ assert_equal ["c-return", base_line + 35], events[8] # Thread.current
+ assert_equal ["c-call", base_line + 35], events[9] # Thread#set_trace_func
+ assert_equal nil, events[10]
+ end
+
+ def test_lineno_in_optimized_insn
+ actual, _, _ = EnvUtil.invoke_ruby [], <<-EOF.gsub(/^.*?: */, ""), true
+ 1: class String
+ 2: def -@
+ 3: puts caller_locations(1, 1)[0].lineno
+ 4: end
+ 5: end
+ 6:
+ 7: -""
+ EOF
+ assert_equal "7\n", actual, '[Bug #14809]'
+ end
+
+ def method_for_enable_target1
+ a = 1
+ b = 2
+ 1.times{|i|
+ x = i
+ }
+ c = a + b
+ end
+
+ def method_for_enable_target2
+ a = 1
+ b = 2
+ 1.times{|i|
+ x = i
+ }
+ c = a + b
+ end
+
+ def check_with_events *trace_events
+ all_events = [[:call, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:b_call, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:b_return, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:return, :method_for_enable_target1],
+ # repeat
+ [:call, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:b_call, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:b_return, :method_for_enable_target1],
+ [:line, :method_for_enable_target1],
+ [:return, :method_for_enable_target1],
+ ]
+ events = []
+ TracePoint.new(*trace_events) do |tp|
+ next unless target_thread?
+ events << [tp.event, tp.method_id]
+ end.enable(target: method(:method_for_enable_target1)) do
+ method_for_enable_target1
+ method_for_enable_target2
+ method_for_enable_target1
+ end
+ assert_equal all_events.find_all{|(ev, m)| trace_events.include? ev}, events
+ end
+
+ def test_tracepoint_enable_target
+ check_with_events :line
+ check_with_events :call, :return
+ check_with_events :line, :call, :return
+ check_with_events :call, :return, :b_call, :b_return
+ check_with_events :line, :call, :return, :b_call, :b_return
+ end
+
+ def test_tracepoint_nested_enabled_with_target
+ code1 = proc{
+ a = 1
+ }
+ code2 = proc{
+ b = 2
+ }
+
+ ## error
+
+ # targetted TP and targetted TP
+ ex = assert_raise(ArgumentError) do
+ tp = TracePoint.new(:line){}
+ tp.enable(target: code1){
+ tp.enable(target: code2){}
+ }
+ end
+ assert_equal "can't nest-enable a targetting TracePoint", ex.message
+
+ # global TP and targetted TP
+ ex = assert_raise(ArgumentError) do
+ tp = TracePoint.new(:line){}
+ tp.enable{
+ tp.enable(target: code2){}
+ }
+ end
+ assert_equal "can't nest-enable a targetting TracePoint", ex.message
+
+ # targetted TP and global TP
+ ex = assert_raise(ArgumentError) do
+ tp = TracePoint.new(:line){}
+ tp.enable(target: code1){
+ tp.enable{}
+ }
+ end
+ assert_equal "can't nest-enable a targetting TracePoint", ex.message
+
+ # targetted TP and disable
+ ex = assert_raise(ArgumentError) do
+ tp = TracePoint.new(:line){}
+ tp.enable(target: code1){
+ tp.disable{}
+ }
+ end
+ assert_equal "can't disable a targetting TracePoint in a block", ex.message
+
+ ## success with two nesting targetting tracepoints
+ events = []
+ tp1 = TracePoint.new(:line){|tp| events << :tp1}
+ tp2 = TracePoint.new(:line){|tp| events << :tp2}
+ tp1.enable(target: code1) do
+ tp2.enable(target: code1) do
+ code1.call
+ events << :___
+ end
+ end
+ assert_equal [:tp2, :tp1, :___], events
+
+ # succss with two tracepoints (global/targetting)
+ events = []
+ tp1 = TracePoint.new(:line){|tp| events << :tp1}
+ tp2 = TracePoint.new(:line){|tp| events << :tp2}
+ tp1.enable do
+ tp2.enable(target: code1) do
+ code1.call
+ events << :___
+ end
+ end
+ assert_equal [:tp1, :tp1, :tp1, :tp1, :tp2, :tp1, :___], events
+
+ # succss with two tracepoints (targetting/global)
+ events = []
+ tp1 = TracePoint.new(:line){|tp| events << :tp1}
+ tp2 = TracePoint.new(:line){|tp| events << :tp2}
+ tp1.enable(target: code1) do
+ tp2.enable do
+ code1.call
+ events << :___
+ end
+ end
+ assert_equal [:tp2, :tp2, :tp1, :tp2, :___], events
+ end
+
+ def test_tracepoint_enable_with_target_line
+ events = []
+ line_0 = __LINE__
+ code1 = proc{
+ events << 1
+ events << 2
+ events << 3
+ }
+ tp = TracePoint.new(:line) do |tp|
+ events << :tp
+ end
+ tp.enable(target: code1, target_line: line_0 + 3) do
+ code1.call
+ end
+ assert_equal [1, :tp, 2, 3], events
+
+
+ e = assert_raise(ArgumentError) do
+ TracePoint.new(:line){}.enable(target_line: 10){}
+ end
+ assert_equal 'only target_line is specified', e.message
+
+ e = assert_raise(ArgumentError) do
+ TracePoint.new(:call){}.enable(target: code1, target_line: 10){}
+ end
+ assert_equal 'target_line is specified, but line event is not specified', e.message
+ end
+
+ def test_script_compiled
+ events = []
+ tp = TracePoint.new(:script_compiled){|tp|
+ next unless target_thread?
+ events << [tp.instruction_sequence.path,
+ tp.eval_script]
+ }
+
+ eval_script = 'a = 1'
+ tp.enable{
+ eval(eval_script, nil, __FILE__+"/eval")
+ nil.instance_eval(eval_script, __FILE__+"/instance_eval")
+ Object.class_eval(eval_script, __FILE__+"/class_eval")
+ }
+ assert_equal [[__FILE__+"/eval", eval_script],
+ [__FILE__+"/instance_eval", eval_script],
+ [__FILE__+"/class_eval", eval_script],
+ ], events
+ events.clear
+
+ # TODO: test for requires
+ return
+
+ tp.enable{
+ require ''
+ require_relative ''
+ load ''
+ }
+ assert_equal [], events
+ end
+
+ def test_return_event_with_rescue
+ obj = Object.new
+ def obj.example
+ 1 if 1 == 1
+ rescue
+ end
+ ok = false
+ tp = TracePoint.new(:return) {ok = true}
+ tp.enable {obj.example}
+ assert ok, "return event should be emitted"
+ end
+
+ def test_disable_local_tracepoint_in_trace
+ assert_normal_exit <<-EOS
+ def foo
+ trace = TracePoint.new(:b_return){|tp|
+ tp.disable
+ }
+ trace.enable(target: method(:bar))
+ end
+ def bar
+ 100.times{|i|
+ foo; foo
+ }
+ end
+ bar
+ EOS
+ end
end
diff --git a/test/ruby/test_signal.rb b/test/ruby/test_signal.rb
index 5d9d3cd691..425dc26574 100644
--- a/test/ruby/test_signal.rb
+++ b/test/ruby/test_signal.rb
@@ -1,18 +1,10 @@
+# frozen_string_literal: false
require 'test/unit'
require 'timeout'
+require 'tempfile'
class TestSignal < Test::Unit::TestCase
- def have_fork?
- begin
- Process.fork {}
- return true
- rescue NotImplementedError
- return false
- end
- end
-
def test_signal
- return unless Process.respond_to?(:kill)
begin
x = 0
oldtrap = Signal.trap(:INT) {|sig| x = 2 }
@@ -24,66 +16,80 @@ class TestSignal < Test::Unit::TestCase
assert_equal 2, x
Signal.trap(:INT) { raise "Interrupt" }
- ex = assert_raise(RuntimeError) {
+ assert_raise_with_message(RuntimeError, /Interrupt/) {
Process.kill :INT, Process.pid
sleep 0.1
}
- assert_kind_of Exception, ex
- assert_match(/Interrupt/, ex.message)
ensure
Signal.trap :INT, oldtrap if oldtrap
end
- end
+ end if Process.respond_to?(:kill)
+
+ def test_signal_process_group
+ bug4362 = '[ruby-dev:43169]'
+ assert_nothing_raised(bug4362) do
+ cmd = [ EnvUtil.rubybin, '--disable=gems' '-e', 'sleep 10' ]
+ pid = Process.spawn(*cmd, :pgroup => true)
+ Process.kill(:"-TERM", pid)
+ Process.waitpid(pid)
+ assert_equal(true, $?.signaled?)
+ assert_equal(Signal.list["TERM"], $?.termsig)
+ end
+ end if Process.respond_to?(:kill) and
+ Process.respond_to?(:pgroup) # for mswin32
def test_exit_action
- return unless have_fork? # skip this test
- begin
- r, w = IO.pipe
- r0, w0 = IO.pipe
- pid = Process.fork {
- Signal.trap(:USR1, "EXIT")
- w0.close
- w.syswrite("a")
+ if Signal.list[sig = "USR1"]
+ term = :TERM
+ else
+ sig = "INT"
+ term = :KILL
+ end
+ IO.popen([EnvUtil.rubybin, '--disable=gems', '-e', <<-"End"], 'r+') do |io|
+ Signal.trap(:#{sig}, "EXIT")
+ STDOUT.syswrite("a")
Thread.start { sleep(2) }
- r0.sysread(4096)
- }
- r.sysread(1)
+ STDIN.sysread(4096)
+ End
+ pid = io.pid
+ io.sysread(1)
sleep 0.1
assert_nothing_raised("[ruby-dev:26128]") {
- Process.kill(:USR1, pid)
+ Process.kill(term, pid)
begin
Timeout.timeout(3) {
Process.waitpid pid
}
rescue Timeout::Error
- Process.kill(:TERM, pid)
+ if term
+ Process.kill(term, pid)
+ term = (:KILL if term != :KILL)
+ retry
+ end
raise
end
}
- ensure
- r.close
- w.close
- r0.close
- w0.close
end
- end
+ end if Process.respond_to?(:kill)
def test_invalid_signal_name
- return unless Process.respond_to?(:kill)
-
assert_raise(ArgumentError) { Process.kill(:XXXXXXXXXX, $$) }
- end
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Process.kill("\u{30eb 30d3 30fc}", $$) }
+ end if Process.respond_to?(:kill)
def test_signal_exception
assert_raise(ArgumentError) { SignalException.new }
assert_raise(ArgumentError) { SignalException.new(-1) }
assert_raise(ArgumentError) { SignalException.new(:XXXXXXXXXX) }
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { SignalException.new("\u{30eb 30d3 30fc}") }
Signal.list.each do |signm, signo|
next if signm == "EXIT"
- assert_equal(SignalException.new(signm).signo, signo)
- assert_equal(SignalException.new(signm.to_sym).signo, signo)
- assert_equal(SignalException.new(signo).signo, signo)
+ assert_equal(signo, SignalException.new(signm).signo, signm)
+ assert_equal(signo, SignalException.new(signm.to_sym).signo, signm)
+ assert_equal(signo, SignalException.new(signo).signo, signo)
end
+ e = assert_raise(ArgumentError) {SignalException.new("-SIGEXIT")}
+ assert_not_match(/SIG-SIG/, e.message)
end
def test_interrupt
@@ -91,7 +97,6 @@ class TestSignal < Test::Unit::TestCase
end
def test_signal2
- return unless Process.respond_to?(:kill)
begin
x = false
oldtrap = Signal.trap(:INT) {|sig| x = true }
@@ -102,21 +107,21 @@ class TestSignal < Test::Unit::TestCase
Timeout.timeout(10) do
x = false
Process.kill(SignalException.new(:INT).signo, $$)
- nil until x
+ sleep(0.01) until x
x = false
Process.kill("INT", $$)
- nil until x
+ sleep(0.01) until x
x = false
Process.kill("SIGINT", $$)
- nil until x
+ sleep(0.01) until x
x = false
o = Object.new
def o.to_str; "SIGINT"; end
Process.kill(o, $$)
- nil until x
+ sleep(0.01) until x
end
assert_raise(ArgumentError) { Process.kill(Object.new, $$) }
@@ -124,10 +129,9 @@ class TestSignal < Test::Unit::TestCase
ensure
Signal.trap(:INT, oldtrap) if oldtrap
end
- end
+ end if Process.respond_to?(:kill)
def test_trap
- return unless Process.respond_to?(:kill)
begin
oldtrap = Signal.trap(:INT) {|sig| }
@@ -162,21 +166,227 @@ class TestSignal < Test::Unit::TestCase
assert_raise(ArgumentError) { Signal.trap("XXXXXXXXXX", "SIG_DFL") }
+ assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) { Signal.trap("\u{30eb 30d3 30fc}", "SIG_DFL") }
+
+ assert_raise(ArgumentError) { Signal.trap("EXIT\0") {} }
ensure
Signal.trap(:INT, oldtrap) if oldtrap
end
+ end if Process.respond_to?(:kill)
+
+ %w"KILL STOP".each do |sig|
+ if Signal.list.key?(sig)
+ define_method("test_trap_uncatchable_#{sig}") do
+ assert_raise(Errno::EINVAL, "SIG#{sig} is not allowed to be caught") { Signal.trap(sig) {} }
+ end
+ end
+ end
+
+ def test_sigexit
+ assert_in_out_err([], 'Signal.trap(:EXIT) {print "OK"}', ["OK"])
+ assert_in_out_err([], 'Signal.trap("EXIT") {print "OK"}', ["OK"])
+ assert_in_out_err([], 'Signal.trap(:SIGEXIT) {print "OK"}', ["OK"])
+ assert_in_out_err([], 'Signal.trap("SIGEXIT") {print "OK"}', ["OK"])
+ assert_in_out_err([], 'Signal.trap(0) {print "OK"}', ["OK"])
end
def test_kill_immediately_before_termination
- return unless have_fork? # skip this test
+ Signal.list[sig = "USR1"] or sig = "INT"
+ assert_in_out_err(["-e", <<-"end;"], "", %w"foo")
+ Signal.trap(:#{sig}) { STDOUT.syswrite("foo") }
+ Process.kill :#{sig}, $$
+ end;
+ end if Process.respond_to?(:kill)
+
+ def test_trap_system_default
+ assert_separately([], <<-End)
+ trap(:QUIT, "SYSTEM_DEFAULT")
+ assert_equal("SYSTEM_DEFAULT", trap(:QUIT, "DEFAULT"))
+ End
+ end if Signal.list.key?('QUIT')
+
+ def test_reserved_signal
+ assert_raise(ArgumentError) {
+ Signal.trap(:SEGV) {}
+ }
+ assert_raise(ArgumentError) {
+ Signal.trap(:BUS) {}
+ }
+ assert_raise(ArgumentError) {
+ Signal.trap(:ILL) {}
+ }
+ assert_raise(ArgumentError) {
+ Signal.trap(:FPE) {}
+ }
+ assert_raise(ArgumentError) {
+ Signal.trap(:VTALRM) {}
+ }
+ end
+
+ def test_signame
+ Signal.list.each do |name, num|
+ assert_equal(num, Signal.list[Signal.signame(num)], name)
+ end
+ assert_nil(Signal.signame(-1))
+ signums = Signal.list.invert
+ assert_nil(Signal.signame((1..1000).find {|num| !signums[num]}))
+ end
+
+ def test_signame_delivered
+ args = [EnvUtil.rubybin, "--disable=gems", "-e", <<"", :err => File::NULL]
+ Signal.trap("INT") do |signo|
+ signame = Signal.signame(signo)
+ Marshal.dump(signame, STDOUT)
+ STDOUT.flush
+ exit 0
+ end
+ Process.kill("INT", $$)
+ sleep 1 # wait signal deliver
- r, w = IO.pipe
- pid = Process.fork do
- r.close
- Signal.trap(:USR1) { w.syswrite("foo") }
- Process.kill :USR1, $$
+ 10.times do
+ IO.popen(args) do |child|
+ signame = Marshal.load(child)
+ assert_equal("INT", signame)
+ end
+ end
+ end if Process.respond_to?(:kill)
+
+ def test_trap_puts
+ assert_in_out_err([], <<-INPUT, ["a"*10000], [])
+ Signal.trap(:INT) {
+ # for enable internal io mutex
+ STDOUT.sync = false
+ # larger than internal io buffer
+ print "a"*10000
+ }
+ Process.kill :INT, $$
+ sleep 0.1
+ INPUT
+ end if Process.respond_to?(:kill)
+
+ def test_hup_me
+ # [Bug #7951] [ruby-core:52864]
+ # This is MRI specific spec. ruby has no guarantee
+ # that signal will be deliverd synchronously.
+ # This ugly workaround was introduced to don't break
+ # compatibility against silly example codes.
+ assert_separately([], <<-RUBY)
+ trap(:HUP, "DEFAULT")
+ assert_raise(SignalException) {
+ Process.kill('HUP', Process.pid)
+ }
+ RUBY
+ bug8137 = '[ruby-dev:47182] [Bug #8137]'
+ assert_nothing_raised(bug8137) {
+ Timeout.timeout(1) {
+ Process.kill(0, Process.pid)
+ }
+ }
+ end if Process.respond_to?(:kill) and Signal.list.key?('HUP')
+
+ def test_ignored_interrupt
+ bug9820 = '[ruby-dev:48203] [Bug #9820]'
+ assert_separately(['-', bug9820], <<-'end;') # begin
+ bug = ARGV.shift
+ trap(:INT, "IGNORE")
+ assert_nothing_raised(SignalException, bug) do
+ Process.kill(:INT, $$)
+ end
+ end;
+
+ if trap = Signal.list['TRAP']
+ bug9820 = '[ruby-dev:48592] [Bug #9820]'
+ status = assert_in_out_err(['-e', 'Process.kill(:TRAP, $$)'])
+ assert_predicate(status, :signaled?, bug9820)
+ assert_equal(trap, status.termsig, bug9820)
+ end
+
+ if Signal.list['CONT']
+ bug9820 = '[ruby-dev:48606] [Bug #9820]'
+ assert_ruby_status(['-e', 'Process.kill(:CONT, $$)'])
+ end
+ end if Process.respond_to?(:kill)
+
+ def test_signal_list_dedupe_keys
+ a = Signal.list.keys.map(&:object_id).sort
+ b = Signal.list.keys.map(&:object_id).sort
+ assert_equal a, b
+ end
+
+ def test_self_stop
+ assert_ruby_status([], <<-'end;')
+ begin
+ fork{
+ sleep 1
+ Process.kill(:CONT, Process.ppid)
+ }
+ Process.kill(:STOP, Process.pid)
+ rescue NotImplementedError
+ # ok
+ end
+ end;
+ end
+
+ def test_sigchld_ignore
+ skip 'no SIGCHLD' unless Signal.list['CHLD']
+ old = trap(:CHLD, 'IGNORE')
+ cmd = [ EnvUtil.rubybin, '--disable=gems', '-e' ]
+ assert(system(*cmd, 'exit!(0)'), 'no ECHILD')
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ IO.pipe do |r, w|
+ pid = spawn(*cmd, "STDIN.read", in: r)
+ nb = Process.wait(pid, Process::WNOHANG)
+ th = Thread.new(Thread.current) do |parent|
+ Thread.pass until parent.stop? # wait for parent to Process.wait
+ w.close
+ end
+ assert_raise(Errno::ECHILD) { Process.wait(pid) }
+ assert_nil nb
end
- w.close
- assert_equal(r.read, "foo")
+
+ IO.pipe do |r, w|
+ pids = 3.times.map { spawn(*cmd, 'exit!', out: w) }
+ w.close
+ zombies = pids.dup
+ assert_nil r.read(1), 'children dead'
+
+ Timeout.timeout(10) do
+ zombies.delete_if do |pid|
+ begin
+ Process.kill(0, pid)
+ false
+ rescue Errno::ESRCH
+ true
+ end
+ end while zombies[0]
+ end
+ assert_predicate zombies, :empty?, 'zombies leftover'
+
+ pids.each do |pid|
+ assert_raise(Errno::ECHILD) { Process.waitpid(pid) }
+ end
+ end
+ ensure
+ trap(:CHLD, old) if Signal.list['CHLD']
end
+
+ def test_sigwait_fd_unused
+ t = EnvUtil.apply_timeout_scale(0.1)
+ assert_separately([], <<-End)
+ tgt = $$
+ trap(:TERM) { exit(0) }
+ e = "Process.daemon; sleep #{t * 2}; Process.kill(:TERM,\#{tgt})"
+ term = [ '#{EnvUtil.rubybin}', '--disable=gems', '-e', e ]
+ t2 = Thread.new { sleep } # grab sigwait_fd
+ Thread.pass until t2.stop?
+ Thread.new do
+ sleep #{t}
+ t2.kill
+ t2.join
+ end
+ Process.spawn(*term)
+ # last thread remaining, ensure it can react to SIGTERM
+ loop { sleep }
+ End
+ end if Process.respond_to?(:kill) && Process.respond_to?(:daemon)
end
diff --git a/test/ruby/test_sleep.rb b/test/ruby/test_sleep.rb
index adc4876216..61002b8b18 100644
--- a/test/ruby/test_sleep.rb
+++ b/test/ruby/test_sleep.rb
@@ -1,12 +1,16 @@
+# frozen_string_literal: false
require 'test/unit'
+require 'etc'
class TestSleep < Test::Unit::TestCase
def test_sleep_5sec
GC.disable
- start = Time.now
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
sleep 5
- slept = Time.now-start
- assert_in_delta(5.0, slept, 0.1, "[ruby-core:18015]: longer than expected")
+ slept = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
+ bottom = 5.0
+ assert_operator(slept, :>=, bottom)
+ assert_operator(slept, :<=, 6.0, "[ruby-core:18015]: longer than expected")
ensure
GC.enable
end
diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb
index 05bccde066..7986e9d141 100644
--- a/test/ruby/test_sprintf.rb
+++ b/test/ruby/test_sprintf.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestSprintf < Test::Unit::TestCase
@@ -83,6 +84,18 @@ class TestSprintf < Test::Unit::TestCase
assert_equal("NaN", sprintf("%-f", nan))
assert_equal("+NaN", sprintf("%+f", nan))
+ assert_equal("NaN", sprintf("%3f", nan))
+ assert_equal("NaN", sprintf("%-3f", nan))
+ assert_equal("+NaN", sprintf("%+3f", nan))
+
+ assert_equal(" NaN", sprintf("% 3f", nan))
+ assert_equal(" NaN", sprintf("%- 3f", nan))
+ assert_equal("+NaN", sprintf("%+ 3f", nan))
+
+ assert_equal(" NaN", sprintf("% 03f", nan))
+ assert_equal(" NaN", sprintf("%- 03f", nan))
+ assert_equal("+NaN", sprintf("%+ 03f", nan))
+
assert_equal(" NaN", sprintf("%8f", nan))
assert_equal("NaN ", sprintf("%-8f", nan))
assert_equal(" +NaN", sprintf("%+8f", nan))
@@ -106,6 +119,26 @@ class TestSprintf < Test::Unit::TestCase
assert_equal("Inf", sprintf("%-f", inf))
assert_equal("+Inf", sprintf("%+f", inf))
+ assert_equal(" Inf", sprintf("% f", inf))
+ assert_equal(" Inf", sprintf("%- f", inf))
+ assert_equal("+Inf", sprintf("%+ f", inf))
+
+ assert_equal(" Inf", sprintf("% 0f", inf))
+ assert_equal(" Inf", sprintf("%- 0f", inf))
+ assert_equal("+Inf", sprintf("%+ 0f", inf))
+
+ assert_equal("Inf", sprintf("%3f", inf))
+ assert_equal("Inf", sprintf("%-3f", inf))
+ assert_equal("+Inf", sprintf("%+3f", inf))
+
+ assert_equal(" Inf", sprintf("% 3f", inf))
+ assert_equal(" Inf", sprintf("%- 3f", inf))
+ assert_equal("+Inf", sprintf("%+ 3f", inf))
+
+ assert_equal(" Inf", sprintf("% 03f", inf))
+ assert_equal(" Inf", sprintf("%- 03f", inf))
+ assert_equal("+Inf", sprintf("%+ 03f", inf))
+
assert_equal(" Inf", sprintf("%8f", inf))
assert_equal("Inf ", sprintf("%-8f", inf))
assert_equal(" +Inf", sprintf("%+8f", inf))
@@ -126,6 +159,26 @@ class TestSprintf < Test::Unit::TestCase
assert_equal("-Inf", sprintf("%-f", -inf))
assert_equal("-Inf", sprintf("%+f", -inf))
+ assert_equal("-Inf", sprintf("% f", -inf))
+ assert_equal("-Inf", sprintf("%- f", -inf))
+ assert_equal("-Inf", sprintf("%+ f", -inf))
+
+ assert_equal("-Inf", sprintf("% 0f", -inf))
+ assert_equal("-Inf", sprintf("%- 0f", -inf))
+ assert_equal("-Inf", sprintf("%+ 0f", -inf))
+
+ assert_equal("-Inf", sprintf("%4f", -inf))
+ assert_equal("-Inf", sprintf("%-4f", -inf))
+ assert_equal("-Inf", sprintf("%+4f", -inf))
+
+ assert_equal("-Inf", sprintf("% 4f", -inf))
+ assert_equal("-Inf", sprintf("%- 4f", -inf))
+ assert_equal("-Inf", sprintf("%+ 4f", -inf))
+
+ assert_equal("-Inf", sprintf("% 04f", -inf))
+ assert_equal("-Inf", sprintf("%- 04f", -inf))
+ assert_equal("-Inf", sprintf("%+ 04f", -inf))
+
assert_equal(" -Inf", sprintf("%8f", -inf))
assert_equal("-Inf ", sprintf("%-8f", -inf))
assert_equal(" -Inf", sprintf("%+8f", -inf))
@@ -148,6 +201,49 @@ class TestSprintf < Test::Unit::TestCase
assert_equal(" Inf", sprintf("% e", inf), '[ruby-dev:34002]')
end
+ def test_bignum
+ assert_match(/\A10{120}\.0+\z/, sprintf("%f", 100**60))
+ assert_match(/\A10{180}\.0+\z/, sprintf("%f", 1000**60))
+ end
+
+ def test_rational
+ assert_match(/\A0\.10+\z/, sprintf("%.60f", 0.1r))
+ assert_match(/\A0\.010+\z/, sprintf("%.60f", 0.01r))
+ assert_match(/\A0\.0010+\z/, sprintf("%.60f", 0.001r))
+ assert_match(/\A0\.3+\z/, sprintf("%.60f", 1/3r))
+ assert_match(/\A1\.20+\z/, sprintf("%.60f", 1.2r))
+
+ ["", *"0".."9"].each do |len|
+ ["", *".0"..".9"].each do |prec|
+ ['', '+', '-', ' ', '0', '+0', '-0', ' 0', '+ ', '- ', '+ 0', '- 0'].each do |flags|
+ fmt = "%#{flags}#{len}#{prec}f"
+ [0, 0.1, 0.01, 0.001, 1.001, 100.0, 100.001, 10000000000.0, 0.00000000001, 1/3r, 2/3r, 1.2r, 10r].each do |num|
+ assert_equal(sprintf(fmt, num.to_f), sprintf(fmt, num.to_r), "sprintf(#{fmt.inspect}, #{num.inspect}.to_r)")
+ assert_equal(sprintf(fmt, -num.to_f), sprintf(fmt, -num.to_r), "sprintf(#{fmt.inspect}, #{(-num).inspect}.to_r)") if num > 0
+ end
+ end
+ end
+ end
+
+ bug11766 = '[ruby-core:71806] [Bug #11766]'
+ assert_equal("x"*10+" 1.0", sprintf("x"*10+"%8.1f", 1r), bug11766)
+ end
+
+ def test_rational_precision
+ assert_match(/\A0\.\d{600}\z/, sprintf("%.600f", 600**~60))
+ end
+
+ def test_hash
+ options = {:capture=>/\d+/}
+ assert_equal("with options {:capture=>/\\d+/}", sprintf("with options %p" % options))
+ end
+
+ def test_inspect
+ obj = Object.new
+ def obj.inspect; "TEST"; end
+ assert_equal("<TEST>", sprintf("<%p>", obj))
+ end
+
def test_invalid
# Star precision before star width:
assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%.**d", 5, 10, 1)}
@@ -179,6 +275,10 @@ class TestSprintf < Test::Unit::TestCase
assert_raise(ArgumentError) { sprintf("%!", 1) }
assert_raise(ArgumentError) { sprintf("%1$1$d", 1) }
assert_raise(ArgumentError) { sprintf("%0%") }
+
+ assert_raise_with_message(ArgumentError, /unnumbered\(1\) mixed with numbered/) { sprintf("%1$*d", 3) }
+ assert_raise_with_message(ArgumentError, /unnumbered\(1\) mixed with numbered/) { sprintf("%1$.*d", 3) }
+
verbose, $VERBOSE = $VERBOSE, nil
assert_nothing_raised { sprintf("", 1) }
ensure
@@ -189,6 +289,10 @@ class TestSprintf < Test::Unit::TestCase
assert_equal("36893488147419111424",
sprintf("%20.0f", 36893488147419107329.0))
assert_equal(" Inf", sprintf("% 0e", 1.0/0.0), "moved from btest/knownbug")
+ assert_equal(" -0.", sprintf("%#10.0f", -0.5), "[ruby-dev:42552]")
+ # out of spec
+ #assert_equal("0x1p+2", sprintf('%.0a', Float('0x1.fp+1')), "[ruby-dev:42551]")
+ #assert_equal("-0x1.0p+2", sprintf('%.1a', Float('-0x1.ffp+1')), "[ruby-dev:42551]")
end
def test_float_hex
@@ -204,6 +308,49 @@ class TestSprintf < Test::Unit::TestCase
assert_equal("Inf", sprintf("%E", Float::INFINITY))
assert_equal("NaN", sprintf("%e", Float::NAN))
assert_equal("NaN", sprintf("%E", Float::NAN))
+
+ assert_equal(" -0x1p+0", sprintf("%10a", -1))
+ assert_equal(" -0x1.8p+0", sprintf("%10a", -1.5))
+ assert_equal(" -0x1.4p+0", sprintf("%10a", -1.25))
+ assert_equal(" -0x1.2p+0", sprintf("%10a", -1.125))
+ assert_equal(" -0x1.1p+0", sprintf("%10a", -1.0625))
+ assert_equal("-0x1.08p+0", sprintf("%10a", -1.03125))
+
+ bug3962 = '[ruby-core:32841]'
+ assert_equal("-0x0001p+0", sprintf("%010a", -1), bug3962)
+ assert_equal("-0x01.8p+0", sprintf("%010a", -1.5), bug3962)
+ assert_equal("-0x01.4p+0", sprintf("%010a", -1.25), bug3962)
+ assert_equal("-0x01.2p+0", sprintf("%010a", -1.125), bug3962)
+ assert_equal("-0x01.1p+0", sprintf("%010a", -1.0625), bug3962)
+ assert_equal("-0x1.08p+0", sprintf("%010a", -1.03125), bug3962)
+
+ bug3964 = '[ruby-core:32848]'
+ assert_equal("0x000000000000000p+0", sprintf("%020a", 0), bug3964)
+ assert_equal("0x000000000000001p+0", sprintf("%020a", 1), bug3964)
+ assert_equal("-0x00000000000001p+0", sprintf("%020a", -1), bug3964)
+ assert_equal("0x00000000000000.p+0", sprintf("%#020a", 0), bug3964)
+
+ bug3965 = '[ruby-dev:42431]'
+ assert_equal("0x1.p+0", sprintf("%#.0a", 1), bug3965)
+ assert_equal("0x00000000000000.p+0", sprintf("%#020a", 0), bug3965)
+ assert_equal("0x0000.0000000000p+0", sprintf("%#020.10a", 0), bug3965)
+
+ bug3979 = '[ruby-dev:42453]'
+ assert_equal(" 0x0.000p+0", sprintf("%20.3a", 0), bug3979)
+ assert_equal(" 0x1.000p+0", sprintf("%20.3a", 1), bug3979)
+ end
+
+ def test_float_prec
+ assert_equal("5.00", sprintf("%.2f",5.005))
+ assert_equal("5.02", sprintf("%.2f",5.015))
+ assert_equal("5.02", sprintf("%.2f",5.025))
+ assert_equal("5.04", sprintf("%.2f",5.035))
+ bug12889 = '[ruby-core:77864] [Bug #12889]'
+ assert_equal("1234567892", sprintf("%.0f", 1234567891.99999))
+ assert_equal("1234567892", sprintf("%.0f", 1234567892.49999))
+ assert_equal("1234567892", sprintf("%.0f", 1234567892.50000))
+ assert_equal("1234567894", sprintf("%.0f", 1234567893.50000))
+ assert_equal("1234567892", sprintf("%.0f", 1234567892.00000), bug12889)
end
BSIZ = 120
@@ -271,12 +418,28 @@ class TestSprintf < Test::Unit::TestCase
def test_star
assert_equal("-1 ", sprintf("%*d", -3, -1))
+ assert_raise_with_message(ArgumentError, /width too big/) {
+ sprintf("%*999999999999999999999999999999999999999999999999999999999999$d", 1)
+ }
+ assert_raise_with_message(ArgumentError, /prec too big/) {
+ sprintf("%.*999999999999999999999999999999999999999999999999999999999999$d", 1)
+ }
end
def test_escape
assert_equal("%" * BSIZ, sprintf("%%" * BSIZ))
end
+ def test_percent_sign_at_end
+ assert_raise_with_message(ArgumentError, "incomplete format specifier; use %% (double %) instead") do
+ sprintf("%")
+ end
+
+ assert_raise_with_message(ArgumentError, "incomplete format specifier; use %% (double %) instead") do
+ sprintf("abc%")
+ end
+ end
+
def test_rb_sprintf
assert_match(/^#<TestSprintf::T012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789:0x[0-9a-f]+>$/,
T012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.new.inspect)
@@ -287,12 +450,97 @@ class TestSprintf < Test::Unit::TestCase
s2 = sprintf("%0x", -0x40000001)
b1 = (/\.\./ =~ s1) != nil
b2 = (/\.\./ =~ s2) != nil
- assert(b1 == b2, "[ruby-dev:33224]")
+ assert_equal(b1, b2, "[ruby-dev:33224]")
end
- def test_named
+ def test_named_untyped
assert_equal("value", sprintf("%<key>s", :key => "value"))
- assert_raise(ArgumentError) {sprintf("%1$<key2>s", :key => "value")}
- assert_raise(ArgumentError) {sprintf("%<key><key2>s", :key => "value")}
+ assert_raise_with_message(ArgumentError, "named<key2> after numbered") {sprintf("%1$<key2>s", :key => "value")}
+ assert_raise_with_message(ArgumentError, "named<key2> after unnumbered(2)") {sprintf("%s%s%<key2>s", "foo", "bar", :key => "value")}
+ assert_raise_with_message(ArgumentError, "named<key2> after <key>") {sprintf("%<key><key2>s", :key => "value")}
+ h = {}
+ e = assert_raise_with_message(KeyError, "key<key> not found") {sprintf("%<key>s", h)}
+ assert_same(h, e.receiver)
+ assert_equal(:key, e.key)
+ end
+
+ def test_named_untyped_enc
+ key = "\u{3012}"
+ [Encoding::UTF_8, Encoding::EUC_JP].each do |enc|
+ k = key.encode(enc)
+ e = assert_raise_with_message(ArgumentError, "named<#{k}> after numbered") {sprintf("%1$<#{k}>s", key: "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(ArgumentError, "named<#{k}> after unnumbered(2)") {sprintf("%s%s%<#{k}>s", "foo", "bar", key: "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(ArgumentError, "named<#{k}> after <key>") {sprintf("%<key><#{k}>s", key: "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(ArgumentError, "named<key> after <#{k}>") {sprintf("%<#{k}><key>s", k.to_sym => "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(KeyError, "key<#{k}> not found") {sprintf("%<#{k}>s", {})}
+ assert_equal(enc, e.message.encoding)
+ end
+ end
+
+ def test_named_typed
+ assert_equal("value", sprintf("%{key}", :key => "value"))
+ assert_raise_with_message(ArgumentError, "named{key2} after numbered") {sprintf("%1${key2}", :key => "value")}
+ assert_raise_with_message(ArgumentError, "named{key2} after unnumbered(2)") {sprintf("%s%s%{key2}", "foo", "bar", :key => "value")}
+ assert_raise_with_message(ArgumentError, "named{key2} after <key>") {sprintf("%<key>{key2}", :key => "value")}
+ assert_equal("value{key2}", sprintf("%{key}{key2}", :key => "value"))
+ assert_raise_with_message(KeyError, "key{key} not found") {sprintf("%{key}", {})}
+ end
+
+ def test_named_typed_enc
+ key = "\u{3012}"
+ [Encoding::UTF_8, Encoding::EUC_JP].each do |enc|
+ k = key.encode(enc)
+ e = assert_raise_with_message(ArgumentError, "named{#{k}} after numbered") {sprintf("%1${#{k}}s", key: "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(ArgumentError, "named{#{k}} after unnumbered(2)") {sprintf("%s%s%{#{k}}s", "foo", "bar", key: "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(ArgumentError, "named{#{k}} after <key>") {sprintf("%<key>{#{k}}s", key: "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(ArgumentError, "named{key} after <#{k}>") {sprintf("%<#{k}>{key}s", k.to_sym => "value")}
+ assert_equal(enc, e.message.encoding)
+ e = assert_raise_with_message(KeyError, "key{#{k}} not found") {sprintf("%{#{k}}", {})}
+ assert_equal(enc, e.message.encoding)
+ end
+ end
+
+ def test_named_default
+ h = Hash.new('world')
+ assert_equal("hello world", "hello %{location}" % h)
+ assert_equal("hello world", "hello %<location>s" % h)
+ end
+
+ def test_named_with_nil
+ h = { key: nil, key2: "key2_val" }
+ assert_equal("key is , key2 is key2_val", "key is %{key}, key2 is %{key2}" % h)
+ end
+
+ def test_width_underflow
+ bug = 'https://github.com/mruby/mruby/issues/3347'
+ assert_equal("!", sprintf("%*c", 0, ?!.ord), bug)
+ end
+
+ def test_negative_width_overflow
+ assert_raise_with_message(ArgumentError, /too big/) do
+ 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_sprintf_comb.rb b/test/ruby/test_sprintf_comb.rb
index 261732bcbc..4113113030 100644
--- a/test/ruby/test_sprintf_comb.rb
+++ b/test/ruby/test_sprintf_comb.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
require_relative 'allpairs'
@@ -107,7 +108,9 @@ class TestSprintfComb < Test::Unit::TestCase
]
VS.reverse!
- def combination(*args, &b)
+ FLAGS = [['', ' '], ['', '#'], ['', '+'], ['', '-'], ['', '0']]
+
+ def self.combination(*args, &b)
#AllPairs.exhaustive_each(*args, &b)
AllPairs.each(*args, &b)
end
@@ -268,17 +271,8 @@ class TestSprintfComb < Test::Unit::TestCase
str
end
- def test_format_integer
- combination(
- %w[B b d o X x],
- [nil, 0, 5, 20],
- ["", ".", ".0", ".8", ".20"],
- ['', ' '],
- ['', '#'],
- ['', '+'],
- ['', '-'],
- ['', '0']) {|type, width, precision, sp, hs, pl, mi, zr|
- format = "%#{sp}#{hs}#{pl}#{mi}#{zr}#{width}#{precision}#{type}"
+ def self.assertions_format_integer(format)
+ proc {
VS.each {|v|
r = sprintf format, v
e = emu_int format, v
@@ -293,6 +287,14 @@ class TestSprintfComb < Test::Unit::TestCase
}
end
+ combination(%w[B b d o X x],
+ [nil, 0, 5, 20],
+ ["", ".", ".0", ".8", ".20"],
+ *FLAGS) {|type, width, precision, sp, hs, pl, mi, zr|
+ format = "%#{sp}#{hs}#{pl}#{mi}#{zr}#{width}#{precision}#{type}"
+ define_method("test_format_integer(#{format})", assertions_format_integer(format))
+ }
+
FLOAT_VALUES = [
-1e100,
-123456789.0,
@@ -526,17 +528,8 @@ class TestSprintfComb < Test::Unit::TestCase
end
- def test_format_float
- combination(
- %w[e E f g G],
- [nil, 0, 5, 20],
- ["", ".", ".0", ".8", ".20", ".200"],
- ['', ' '],
- ['', '#'],
- ['', '+'],
- ['', '-'],
- ['', '0']) {|type, width, precision, sp, hs, pl, mi, zr|
- format = "%#{sp}#{hs}#{pl}#{mi}#{zr}#{width}#{precision}#{type}"
+ def self.assertions_format_float(format)
+ proc {
FLOAT_VALUES.each {|v|
r = sprintf format, v
e = emu_float format, v
@@ -550,4 +543,12 @@ class TestSprintfComb < Test::Unit::TestCase
}
}
end
+
+ combination(%w[e E f g G],
+ [nil, 0, 5, 20],
+ ["", ".", ".0", ".8", ".20", ".200", ".9999"],
+ *FLAGS) {|type, width, precision, sp, hs, pl, mi, zr|
+ format = "%#{sp}#{hs}#{pl}#{mi}#{zr}#{width}#{precision}#{type}"
+ define_method("test_format_float(#{format})", assertions_format_float(format))
+ }
end
diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb
index 02271cefe6..23da909592 100644
--- a/test/ruby/test_string.rb
+++ b/test/ruby/test_string.rb
@@ -1,11 +1,13 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
-
-# use of $= is deprecated after 1.7.1
-def pre_1_7_1
-end
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,
+ ]
def initialize(*args)
@cls = String
@@ -15,12 +17,94 @@ class TestString < Test::Unit::TestCase
super
end
- def S(str)
- @cls.new(str)
+ def S(*args)
+ @cls.new(*args)
end
def test_s_new
- assert_equal("RUBY", S("RUBY"))
+ assert_equal("", S())
+ assert_equal(Encoding::ASCII_8BIT, S().encoding)
+
+ assert_equal("", S(""))
+ assert_equal(__ENCODING__, S("").encoding)
+
+ src = "RUBY"
+ assert_equal(src, S(src))
+ assert_equal(__ENCODING__, S(src).encoding)
+
+ src.force_encoding("euc-jp")
+ assert_equal(src, S(src))
+ assert_equal(Encoding::EUC_JP, S(src).encoding)
+
+
+ assert_equal("", S(encoding: "euc-jp"))
+ assert_equal(Encoding::EUC_JP, S(encoding: "euc-jp").encoding)
+
+ assert_equal("", S("", encoding: "euc-jp"))
+ assert_equal(Encoding::EUC_JP, S("", encoding: "euc-jp").encoding)
+
+ src = "RUBY"
+ assert_equal(src, S(src, encoding: "euc-jp"))
+ assert_equal(Encoding::EUC_JP, S(src, encoding: "euc-jp").encoding)
+
+ src.force_encoding("euc-jp")
+ assert_equal(src, S(src, encoding: "utf-8"))
+ assert_equal(Encoding::UTF_8, S(src, encoding: "utf-8").encoding)
+
+ assert_equal("", S(capacity: 1000))
+ assert_equal(Encoding::ASCII_8BIT, S(capacity: 1000).encoding)
+
+ assert_equal("", S(capacity: 1000, encoding: "euc-jp"))
+ assert_equal(Encoding::EUC_JP, S(capacity: 1000, encoding: "euc-jp").encoding)
+
+ assert_equal("", S("", capacity: 1000))
+ assert_equal(__ENCODING__, S("", capacity: 1000).encoding)
+
+ assert_equal("", S("", capacity: 1000, encoding: "euc-jp"))
+ assert_equal(Encoding::EUC_JP, S("", capacity: 1000, encoding: "euc-jp").encoding)
+ end
+
+ def test_initialize
+ str = S("").freeze
+ assert_equal("", str.__send__(:initialize))
+ assert_raise(FrozenError){ str.__send__(:initialize, 'abc') }
+ assert_raise(FrozenError){ str.__send__(:initialize, capacity: 1000) }
+ assert_raise(FrozenError){ str.__send__(:initialize, 'abc', capacity: 1000) }
+ assert_raise(FrozenError){ str.__send__(:initialize, encoding: 'euc-jp') }
+ assert_raise(FrozenError){ str.__send__(:initialize, 'abc', encoding: 'euc-jp') }
+ assert_raise(FrozenError){ str.__send__(:initialize, 'abc', capacity: 1000, encoding: 'euc-jp') }
+
+ str = S("")
+ assert_equal("mystring", str.__send__(:initialize, "mystring"))
+ str = S("mystring")
+ assert_equal("mystring", str.__send__(:initialize, str))
+ str = S("")
+ assert_equal("mystring", str.__send__(:initialize, "mystring", capacity: 1000))
+ str = S("mystring")
+ assert_equal("mystring", str.__send__(:initialize, str, capacity: 1000))
+ end
+
+ def test_initialize_shared
+ String.new(str = "mystring" * 10).__send__(:initialize, capacity: str.bytesize)
+ assert_equal("mystring", str[0, 8])
+ end
+
+ def test_initialize_nonstring
+ assert_raise(TypeError) {
+ S(1)
+ }
+ assert_raise(TypeError) {
+ S(1, capacity: 1000)
+ }
+ end
+
+ def test_initialize_memory_leak
+ assert_no_memory_leak([], <<-PREP, <<-CODE, rss: true)
+code = proc {('x'*100000).__send__(:initialize, '')}
+1_000.times(&code)
+PREP
+100_000.times(&code)
+CODE
end
def test_AREF # '[]'
@@ -126,20 +210,6 @@ class TestString < Test::Unit::TestCase
s[S("Foo")] = S("Bar")
assert_equal(S("BarBar"), s)
- pre_1_7_1 do
- s = S("FooBar")
- s[S("Foo")] = S("xyz")
- assert_equal(S("xyzBar"), s)
-
- $= = true
- s = S("FooBar")
- s[S("FOO")] = S("Bar")
- assert_equal(S("BarBar"), s)
- s[S("FOO")] = S("xyz")
- assert_equal(S("BarBar"), s)
- $= = false
- end
-
s = S("a string")
s[0..s.size] = S("another string")
assert_equal(S("another string"), s)
@@ -151,6 +221,8 @@ class TestString < Test::Unit::TestCase
assert_equal("fobar", s)
assert_raise(ArgumentError) { "foo"[1, 2, 3] = "" }
+
+ assert_raise(IndexError) {"foo"[RbConfig::LIMITS["LONG_MIN"]] = "l"}
end
def test_CMP # '<=>'
@@ -160,53 +232,44 @@ class TestString < Test::Unit::TestCase
assert_equal(-1, S("ABCDEF") <=> S("abcdef"))
- pre_1_7_1 do
- $= = true
- assert_equal(0, S("ABCDEF") <=> S("abcdef"))
- $= = false
- end
-
assert_nil("foo" <=> Object.new)
o = Object.new
def o.to_str; "bar"; end
- assert_nil("foo" <=> o)
+ assert_equal(1, "foo" <=> o)
+ class << o;remove_method :to_str;end
def o.<=>(x); nil; end
assert_nil("foo" <=> o)
+ class << o;remove_method :<=>;end
def o.<=>(x); 1; end
assert_equal(-1, "foo" <=> o)
+ class << o;remove_method :<=>;end
def o.<=>(x); 2**100; end
- assert_equal(-(2**100), "foo" <=> o)
+ assert_equal(-1, "foo" <=> o)
end
def test_EQUAL # '=='
- assert_equal(false, S("foo") == :foo)
- assert(S("abcdef") == S("abcdef"))
-
- pre_1_7_1 do
- $= = true
- assert(S("CAT") == S('cat'))
- assert(S("CaT") == S('cAt'))
- $= = false
- end
+ assert_not_equal(:foo, S("foo"))
+ assert_equal(S("abcdef"), S("abcdef"))
- assert(S("CAT") != S('cat'))
- assert(S("CaT") != S('cAt'))
+ assert_not_equal(S("CAT"), S('cat'))
+ assert_not_equal(S("CaT"), S('cAt'))
o = Object.new
def o.to_str; end
def o.==(x); false; end
assert_equal(false, "foo" == o)
+ class << o;remove_method :==;end
def o.==(x); true; end
assert_equal(true, "foo" == o)
end
def test_LSHIFT # '<<'
assert_equal(S("world!"), S("world") << 33)
- assert_equal(S("world!"), S("world") << S('!'))
+ assert_equal(S("world!"), S("world") << S("!"))
s = "a"
10.times {|i|
@@ -231,12 +294,6 @@ class TestString < Test::Unit::TestCase
assert_equal(10, S("FeeFieFoo-Fum") =~ /Fum$/)
assert_equal(nil, S("FeeFieFoo-Fum") =~ /FUM$/)
- pre_1_7_1 do
- $= = true
- assert_equal(10, S("FeeFieFoo-Fum") =~ /FUM$/)
- $= = false
- end
-
o = Object.new
def o.=~(x); x + "bar"; end
assert_equal("foobar", S("foo") =~ o)
@@ -275,11 +332,12 @@ class TestString < Test::Unit::TestCase
end
def casetest(a, b, rev=false)
+ msg = proc {"#{a} should#{' not' if rev} match #{b}"}
case a
- when b
- assert(!rev)
- else
- assert(rev)
+ when b
+ assert(!rev, msg)
+ else
+ assert(rev, msg)
end
end
@@ -287,13 +345,6 @@ class TestString < Test::Unit::TestCase
# assert_equal(true, S("foo") === :foo)
casetest(S("abcdef"), S("abcdef"))
- pre_1_7_1 do
- $= = true
- casetest(S("CAT"), S('cat'))
- casetest(S("CaT"), S('cAt'))
- $= = false
- end
-
casetest(S("CAT"), S('cat'), true) # Reverse the test - we don't want to
casetest(S("CaT"), S('cAt'), true) # find these in the case.
end
@@ -351,6 +402,56 @@ class TestString < Test::Unit::TestCase
$/ = save
assert_equal(S("a").hash, S("a\u0101").chomp(S("\u0101")).hash, '[ruby-core:22414]')
+
+ s = S("hello")
+ assert_equal("hel", s.chomp('lo'))
+ assert_equal("hello", s)
+
+ s = S("hello")
+ assert_equal("hello", s.chomp('he'))
+ assert_equal("hello", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{3053 3093 306b}", s.chomp("\u{3061 306f}"))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{3053 3093 306b 3061 306f}", s.chomp('lo'))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("hello")
+ assert_equal("hello", s.chomp("\u{3061 306f}"))
+ assert_equal("hello", s)
+
+ # skip if argument is a broken string
+ s = S("\xe3\x81\x82")
+ assert_equal("\xe3\x81\x82", s.chomp("\x82"))
+ assert_equal("\xe3\x81\x82", s)
+
+ s = S("\x95\x5c").force_encoding("Shift_JIS")
+ assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s.chomp("\x5c"))
+ assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s)
+
+ # clear coderange
+ s = S("hello\u{3053 3093}")
+ assert_not_predicate(s, :ascii_only?)
+ assert_predicate(s.chomp("\u{3053 3093}"), :ascii_only?)
+
+ # argument should be converted to String
+ klass = Class.new { def to_str; 'a'; end }
+ s = S("abba")
+ assert_equal("abb", s.chomp(klass.new))
+ assert_equal("abba", s)
+
+ # chomp removes any of "\n", "\r\n", "\r" when "\n" is specified
+ s = "foo\n"
+ assert_equal("foo", s.chomp("\n"))
+ s = "foo\r\n"
+ assert_equal("foo", s.chomp("\n"))
+ s = "foo\r"
+ assert_equal("foo", s.chomp("\n"))
+ ensure
+ $/ = save
end
def test_chomp!
@@ -407,6 +508,67 @@ class TestString < Test::Unit::TestCase
assert_equal("foo\r", s)
assert_equal(S("a").hash, S("a\u0101").chomp!(S("\u0101")).hash, '[ruby-core:22414]')
+
+ s = S("").freeze
+ assert_raise_with_message(FrozenError, /frozen/) {s.chomp!}
+
+ s = S("ax")
+ o = Struct.new(:s).new(s)
+ def o.to_str
+ s.freeze
+ "x"
+ end
+ assert_raise_with_message(FrozenError, /frozen/) {s.chomp!(o)}
+
+ s = S("hello")
+ assert_equal("hel", s.chomp!('lo'))
+ assert_equal("hel", s)
+
+ s = S("hello")
+ assert_equal(nil, s.chomp!('he'))
+ assert_equal("hello", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{3053 3093 306b}", s.chomp!("\u{3061 306f}"))
+ assert_equal("\u{3053 3093 306b}", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal(nil, s.chomp!('lo'))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("hello")
+ assert_equal(nil, s.chomp!("\u{3061 306f}"))
+ assert_equal("hello", s)
+
+ # skip if argument is a broken string
+ s = S("\xe3\x81\x82")
+ assert_equal(nil, s.chomp!("\x82"))
+ assert_equal("\xe3\x81\x82", s)
+
+ s = S("\x95\x5c").force_encoding("Shift_JIS")
+ assert_equal(nil, s.chomp!("\x5c"))
+ assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s)
+
+ # clear coderange
+ s = S("hello\u{3053 3093}")
+ assert_not_predicate(s, :ascii_only?)
+ assert_predicate(s.chomp!("\u{3053 3093}"), :ascii_only?)
+
+ # argument should be converted to String
+ klass = Class.new { def to_str; 'a'; end }
+ s = S("abba")
+ assert_equal("abb", s.chomp!(klass.new))
+ assert_equal("abb", s)
+
+ # chomp removes any of "\n", "\r\n", "\r" when "\n" is specified
+ s = "foo\n"
+ assert_equal("foo", s.chomp!("\n"))
+ s = "foo\r\n"
+ assert_equal("foo", s.chomp!("\n"))
+ s = "foo\r"
+ assert_equal("foo", s.chomp!("\n"))
+ ensure
+ $/ = save
end
def test_chop
@@ -446,30 +608,40 @@ class TestString < Test::Unit::TestCase
def test_clone
for taint in [ false, true ]
- for untrust in [ false, true ]
- for frozen in [ false, true ]
- a = S("Cool")
- a.taint if taint
- a.untrust if untrust
- a.freeze if frozen
- b = a.clone
-
- assert_equal(a, b)
- assert(a.__id__ != b.__id__)
- assert_equal(a.frozen?, b.frozen?)
- assert_equal(a.untrusted?, b.untrusted?)
- assert_equal(a.tainted?, b.tainted?)
- end
+ for frozen in [ false, true ]
+ a = S("Cool")
+ a.taint if taint
+ a.freeze if frozen
+ b = a.clone
+
+ assert_equal(a, b)
+ assert_not_same(a, b)
+ assert_equal(a.frozen?, b.frozen?)
+ assert_equal(a.tainted?, b.tainted?)
end
end
- null = File.exist?("/dev/null") ? "/dev/null" : "NUL" # maybe DOSISH
- assert_equal("", File.read(null).clone, '[ruby-dev:32819] reported by Kazuhiro NISHIYAMA')
+ assert_equal("", File.read(IO::NULL).clone, '[ruby-dev:32819] reported by Kazuhiro NISHIYAMA')
end
def test_concat
assert_equal(S("world!"), S("world").concat(33))
assert_equal(S("world!"), S("world").concat(S('!')))
+ b = S("sn")
+ assert_equal(S("snsnsn"), b.concat(b, b))
+
+ bug7090 = '[ruby-core:47751]'
+ result = S("").force_encoding(Encoding::UTF_16LE)
+ result << 0x0300
+ expected = S("\u0300".encode(Encoding::UTF_16LE))
+ assert_equal(expected, result, bug7090)
+ assert_raise(TypeError) { 'foo' << :foo }
+ assert_raise(FrozenError) { 'foo'.freeze.concat('bar') }
+ end
+
+ def test_concat_literals
+ s="." * 50
+ assert_equal(Encoding::UTF_8, "#{s}x".encoding)
end
def test_count
@@ -479,13 +651,37 @@ class TestString < Test::Unit::TestCase
assert_equal(4, a.count(S("hello"), S("^l")))
assert_equal(4, a.count(S("ej-m")))
assert_equal(0, S("y").count(S("a\\-z")))
+ assert_equal(5, "abc\u{3042 3044 3046}".count("^a"))
+ assert_equal(1, "abc\u{3042 3044 3046}".count("\u3042"))
+ assert_equal(5, "abc\u{3042 3044 3046}".count("^\u3042"))
+ assert_equal(2, "abc\u{3042 3044 3046}".count("a-z", "^a"))
+ assert_equal(0, "abc\u{3042 3044 3046}".count("a", "\u3042"))
+ assert_equal(0, "abc\u{3042 3044 3046}".count("\u3042", "a"))
+ assert_equal(0, "abc\u{3042 3044 3046}".count("\u3042", "\u3044"))
+ assert_equal(4, "abc\u{3042 3044 3046}".count("^a", "^\u3044"))
+ assert_equal(4, "abc\u{3042 3044 3046}".count("^\u3044", "^a"))
+ assert_equal(4, "abc\u{3042 3044 3046}".count("^\u3042", "^\u3044"))
assert_raise(ArgumentError) { "foo".count }
end
def test_crypt
assert_equal(S('aaGUC/JkO9/Sc'), S("mypassword").crypt(S("aa")))
- assert(S('aaGUC/JkO9/Sc') != S("mypassword").crypt(S("ab")))
+ assert_not_equal(S('aaGUC/JkO9/Sc'), S("mypassword").crypt(S("ab")))
+ assert_raise(ArgumentError) {S("mypassword").crypt(S(""))}
+ assert_raise(ArgumentError) {S("mypassword").crypt(S("\0a"))}
+ assert_raise(ArgumentError) {S("mypassword").crypt(S("a\0"))}
+ assert_raise(ArgumentError) {S("poison\u0000null").crypt(S("aa"))}
+ WIDE_ENCODINGS.each do |enc|
+ assert_raise(ArgumentError) {S("mypassword").crypt(S("aa".encode(enc)))}
+ assert_raise(ArgumentError) {S("mypassword".encode(enc)).crypt(S("aa"))}
+ end
+
+ @cls == String and
+ assert_no_memory_leak([], 's = ""', "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ 1000.times { s.crypt(-"..").clear }
+ end;
end
def test_delete
@@ -497,7 +693,14 @@ class TestString < Test::Unit::TestCase
assert_equal("a".hash, "a\u0101".delete("\u0101").hash, '[ruby-talk:329267]')
assert_equal(true, "a\u0101".delete("\u0101").ascii_only?)
assert_equal(true, "a\u3041".delete("\u3041").ascii_only?)
- assert_equal(false, "a\u3041\u3042".tr("\u3041", "a").ascii_only?)
+ assert_equal(false, "a\u3041\u3042".delete("\u3041").ascii_only?)
+
+ assert_equal("a", "abc\u{3042 3044 3046}".delete("^a"))
+ assert_equal("bc\u{3042 3044 3046}", "abc\u{3042 3044 3046}".delete("a"))
+ assert_equal("\u3042", "abc\u{3042 3044 3046}".delete("^\u3042"))
+
+ bug6160 = '[ruby-dev:45374]'
+ assert_equal("", '\\'.delete('\\'), bug6160)
end
def test_delete!
@@ -556,24 +759,96 @@ class TestString < Test::Unit::TestCase
def test_dump
a= S("Test") << 1 << 2 << 3 << 9 << 13 << 10
assert_equal(S('"Test\\x01\\x02\\x03\\t\\r\\n"'), a.dump)
+ b= S("\u{7F}")
+ assert_equal(S('"\\x7F"'), b.dump)
+ b= S("\u{AB}")
+ assert_equal(S('"\\u00AB"'), b.dump)
+ b= S("\u{ABC}")
+ assert_equal(S('"\\u0ABC"'), b.dump)
+ b= S("\uABCD")
+ assert_equal(S('"\\uABCD"'), b.dump)
+ b= S("\u{ABCDE}")
+ assert_equal(S('"\\u{ABCDE}"'), b.dump)
+ b= S("\u{10ABCD}")
+ assert_equal(S('"\\u{10ABCD}"'), b.dump)
+ end
+
+ def test_undump
+ a = S("Test") << 1 << 2 << 3 << 9 << 13 << 10
+ assert_equal(a, S('"Test\\x01\\x02\\x03\\t\\r\\n"').undump)
+ assert_equal(S("\\ca"), S('"\\ca"').undump)
+ assert_equal(S("\u{7F}"), S('"\\x7F"').undump)
+ assert_equal(S("\u{7F}A"), S('"\\x7FA"').undump)
+ assert_equal(S("\u{AB}"), S('"\\u00AB"').undump)
+ assert_equal(S("\u{ABC}"), S('"\\u0ABC"').undump)
+ assert_equal(S("\uABCD"), S('"\\uABCD"').undump)
+ assert_equal(S("\uABCD"), S('"\\uABCD"').undump)
+ assert_equal(S("\u{ABCDE}"), S('"\\u{ABCDE}"').undump)
+ assert_equal(S("\u{10ABCD}"), S('"\\u{10ABCD}"').undump)
+ assert_equal(S("\u{ABCDE 10ABCD}"), S('"\\u{ABCDE 10ABCD}"').undump)
+ assert_equal(S(""), S('"\\u{}"').undump)
+ assert_equal(S(""), S('"\\u{ }"').undump)
+
+ assert_equal(S("\u3042".encode("sjis")), S('"\x82\xA0"'.force_encoding("sjis")).undump)
+ assert_equal(S("\u8868".encode("sjis")), S("\"\\x95\\\\\"".force_encoding("sjis")).undump)
+
+ assert_equal(S("äöü"), S('"\u00E4\u00F6\u00FC"').undump)
+ assert_equal(S("äöü"), S('"\xC3\xA4\xC3\xB6\xC3\xBC"').undump)
+
+ assert_equal(Encoding::UTF_8, S('"\\u3042"').encode(Encoding::EUC_JP).undump.encoding)
+
+ assert_equal("abc".encode(Encoding::UTF_16LE),
+ '"a\x00b\x00c\x00".force_encoding("UTF-16LE")'.undump)
+
+ assert_equal('\#', '"\\\\#"'.undump)
+ assert_equal('\#{', '"\\\\\#{"'.undump)
+
+ assert_raise(RuntimeError) { S('\u3042').undump }
+ assert_raise(RuntimeError) { S('"\x82\xA0\u3042"'.force_encoding("SJIS")).undump }
+ assert_raise(RuntimeError) { S('"\u3042\x82\xA0"'.force_encoding("SJIS")).undump }
+ assert_raise(RuntimeError) { S('"".force_encoding()').undump }
+ assert_raise(RuntimeError) { S('"".force_encoding("').undump }
+ assert_raise(RuntimeError) { S('"".force_encoding("UNKNOWN")').undump }
+ assert_raise(RuntimeError) { S('"\u3042".force_encoding("UTF-16LE")').undump }
+ assert_raise(RuntimeError) { S('"\x00\x00".force_encoding("UTF-16LE")"').undump }
+ assert_raise(RuntimeError) { S('"\x00\x00".force_encoding("'+("a"*9999999)+'")"').undump }
+ assert_raise(RuntimeError) { S(%("\u00E4")).undump }
+ assert_raise(RuntimeError) { S('"').undump }
+ assert_raise(RuntimeError) { S('"""').undump }
+ assert_raise(RuntimeError) { S('""""').undump }
+
+ assert_raise(RuntimeError) { S('"a').undump }
+ assert_raise(RuntimeError) { S('"\u"').undump }
+ assert_raise(RuntimeError) { S('"\u{"').undump }
+ assert_raise(RuntimeError) { S('"\u304"').undump }
+ assert_raise(RuntimeError) { S('"\u304Z"').undump }
+ assert_raise(RuntimeError) { S('"\udfff"').undump }
+ assert_raise(RuntimeError) { S('"\u{dfff}"').undump }
+ assert_raise(RuntimeError) { S('"\u{3042"').undump }
+ assert_raise(RuntimeError) { S('"\u{3042 "').undump }
+ assert_raise(RuntimeError) { S('"\u{110000}"').undump }
+ assert_raise(RuntimeError) { S('"\u{1234567}"').undump }
+ assert_raise(RuntimeError) { S('"\x"').undump }
+ assert_raise(RuntimeError) { S('"\xA"').undump }
+ assert_raise(RuntimeError) { S('"\\"').undump }
+ assert_raise(RuntimeError) { S(%("\0")).undump }
+ assert_raise_with_message(RuntimeError, /invalid/) {
+ '"\\u{007F}".xxxxxx'.undump
+ }
end
def test_dup
for taint in [ false, true ]
- for untrust in [ false, true ]
- for frozen in [ false, true ]
- a = S("hello")
- a.taint if taint
- a.untrust if untrust
- a.freeze if frozen
- b = a.dup
-
- assert_equal(a, b)
- assert(a.__id__ != b.__id__)
- assert(!b.frozen?)
- assert_equal(a.tainted?, b.tainted?)
- assert_equal(a.untrusted?, b.untrusted?)
- end
+ for frozen in [ false, true ]
+ a = S("hello")
+ a.taint if taint
+ a.freeze if frozen
+ b = a.dup
+
+ assert_equal(a, b)
+ assert_not_same(a, b)
+ assert_not_predicate(b, :frozen?)
+ assert_equal(a.tainted?, b.tainted?)
end
end
end
@@ -588,61 +863,350 @@ class TestString < Test::Unit::TestCase
res=[]
S("hello\n\n\nworld").lines(S('')).each {|x| res << x}
- assert_equal(S("hello\n\n\n"), res[0])
- assert_equal(S("world"), res[1])
+ assert_equal(S("hello\n\n"), res[0])
+ assert_equal(S("world"), res[1])
$/ = "!"
res=[]
S("hello!world").lines.each {|x| res << x}
assert_equal(S("hello!"), res[0])
assert_equal(S("world"), res[1])
+ ensure
$/ = save
end
def test_each_byte
+ s = S("ABC")
+
res = []
- S("ABC").each_byte {|x| res << x }
+ assert_equal s.object_id, s.each_byte {|x| res << x }.object_id
assert_equal(65, res[0])
assert_equal(66, res[1])
assert_equal(67, res[2])
+
+ assert_equal 65, s.each_byte.next
+ end
+
+ def test_bytes
+ 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
+ warning = /passing a block to String#bytes is deprecated/
+ assert_warning(warning) {
+ 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])
+ }
+ assert_warning(warning) {
+ s = S("ABC")
+ res = []
+ assert_same s, s.bytes {|x| res << x }
+ assert_equal [65, 66, 67], res
+ }
+ end
+ end
+
+ def test_each_codepoint
+ # Single byte optimization
+ assert_equal 65, S("ABC").each_codepoint.next
+
+ s = S("\u3042\u3044\u3046")
+
+ res = []
+ assert_equal s.object_id, s.each_codepoint {|x| res << x }.object_id
+ assert_equal(0x3042, res[0])
+ assert_equal(0x3044, res[1])
+ assert_equal(0x3046, res[2])
+
+ assert_equal 0x3042, s.each_codepoint.next
+ end
+
+ def test_codepoints
+ # Single byte optimization
+ assert_equal [65, 66, 67], S("ABC").codepoints
+
+ 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
+ warning = /passing a block to String#codepoints is deprecated/
+ assert_warning(warning) {
+ 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])
+ }
+ assert_warning(warning) {
+ s = S("ABC")
+ res = []
+ assert_same s, s.codepoints {|x| res << x }
+ assert_equal [65, 66, 67], res
+ }
+ end
+ end
+
+ def test_each_char
+ s = S("ABC")
+
+ res = []
+ assert_equal s.object_id, s.each_char {|x| res << x }.object_id
+ assert_equal("A", res[0])
+ assert_equal("B", res[1])
+ assert_equal("C", res[2])
+
+ assert_equal "A", S("ABC").each_char.next
+ end
+
+ def test_chars
+ 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
+ warning = /passing a block to String#chars is deprecated/
+ assert_warning(warning) {
+ 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
+ end
+
+ def test_each_grapheme_cluster
+ [
+ "\u{0D 0A}",
+ "\u{20 200d}",
+ "\u{600 600}",
+ "\u{600 20}",
+ "\u{261d 1F3FB}",
+ "\u{1f600}",
+ "\u{20 308}",
+ "\u{1F477 1F3FF 200D 2640 FE0F}",
+ "\u{1F468 200D 1F393}",
+ "\u{1F46F 200D 2642 FE0F}",
+ "\u{1f469 200d 2764 fe0f 200d 1f469}",
+ ].each do |g|
+ assert_equal [g], g.each_grapheme_cluster.to_a
+ assert_equal 1, g.each_grapheme_cluster.size
+ assert_predicate g.dup.taint.each_grapheme_cluster.to_a[0], :tainted?
+ end
+
+ [
+ ["\u{a 324}", ["\u000A", "\u0324"]],
+ ["\u{d 324}", ["\u000D", "\u0324"]],
+ ["abc", ["a", "b", "c"]],
+ ].each do |str, grapheme_clusters|
+ assert_equal grapheme_clusters, str.each_grapheme_cluster.to_a
+ assert_equal grapheme_clusters.size, str.each_grapheme_cluster.size
+ str.dup.taint.each_grapheme_cluster do |g|
+ assert_predicate g, :tainted?
+ end
+ end
+
+ s = ("x"+"\u{10ABCD}"*250000)
+ assert_empty(s.each_grapheme_cluster {s.clear})
+ end
+
+ def test_grapheme_clusters
+ [
+ "\u{20 200d}",
+ "\u{600 600}",
+ "\u{600 20}",
+ "\u{261d 1F3FB}",
+ "\u{1f600}",
+ "\u{20 308}",
+ "\u{1F477 1F3FF 200D 2640 FE0F}",
+ "\u{1F468 200D 1F393}",
+ "\u{1F46F 200D 2642 FE0F}",
+ "\u{1f469 200d 2764 fe0f 200d 1f469}",
+ ].product([Encoding::UTF_8, *WIDE_ENCODINGS]) do |g, enc|
+ g = g.encode(enc)
+ assert_equal [g], g.grapheme_clusters
+ assert_predicate g.taint.grapheme_clusters[0], :tainted?
+ end
+
+ [
+ "\u{a 324}",
+ "\u{d 324}",
+ "abc",
+ ].product([Encoding::UTF_8, *WIDE_ENCODINGS]) do |g, enc|
+ g = g.encode(enc)
+ assert_equal g.chars, g.grapheme_clusters
+ 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
+ warning = /passing a block to String#grapheme_clusters is deprecated/
+ assert_warning(warning) {
+ s = "ABC".b.taint
+ 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])
+ res.each {|g| assert_predicate(g, :tainted?)}
+ }
+ end
end
def test_each_line
save = $/
$/ = "\n"
res=[]
- S("hello\nworld").lines.each {|x| res << x}
+ S("hello\nworld").each_line {|x| res << x}
assert_equal(S("hello\n"), res[0])
assert_equal(S("world"), res[1])
res=[]
- S("hello\n\n\nworld").lines(S('')).each {|x| res << x}
- assert_equal(S("hello\n\n\n"), res[0])
- assert_equal(S("world"), res[1])
+ S("hello\n\n\nworld").each_line(S('')) {|x| res << x}
+ assert_equal(S("hello\n\n"), res[0])
+ assert_equal(S("world"), res[1])
+
+ res=[]
+ S("hello\r\n\r\nworld").each_line(S('')) {|x| res << x}
+ assert_equal(S("hello\r\n\r\n"), res[0])
+ assert_equal(S("world"), res[1])
$/ = "!"
res=[]
- S("hello!world").lines.each {|x| res << x}
+ S("hello!world").each_line {|x| res << x}
assert_equal(S("hello!"), res[0])
assert_equal(S("world"), res[1])
+ $/ = "ab"
+
+ res=[]
+ S("a").lines.each {|x| res << x}
+ assert_equal(1, res.size)
+ assert_equal(S("a"), res[0])
+
$/ = save
s = nil
"foo\nbar".each_line(nil) {|s2| s = s2 }
assert_equal("foo\nbar", s)
+
+ assert_equal "hello\n", S("hello\nworld").each_line.next
+ assert_equal "hello\nworld", S("hello\nworld").each_line(nil).next
+
+ bug7646 = "[ruby-dev:46827]"
+ assert_nothing_raised(bug7646) do
+ "\n\u0100".each_line("\n") {}
+ end
+ ensure
+ $/ = save
+ end
+
+ def test_each_line_chomp
+ res = []
+ S("hello\nworld").each_line("\n", chomp: true) {|x| res << x}
+ assert_equal(S("hello"), res[0])
+ assert_equal(S("world"), res[1])
+
+ res = []
+ S("hello\n\n\nworld").each_line(S(''), chomp: true) {|x| res << x}
+ assert_equal(S("hello\n"), res[0])
+ assert_equal(S("world"), res[1])
+
+ res = []
+ S("hello\r\n\r\nworld").each_line(S(''), chomp: true) {|x| res << x}
+ assert_equal(S("hello\r\n"), res[0])
+ assert_equal(S("world"), res[1])
+
+ res = []
+ S("hello!world").each_line(S('!'), chomp: true) {|x| res << x}
+ assert_equal(S("hello"), res[0])
+ assert_equal(S("world"), res[1])
+
+ res = []
+ S("a").each_line(S('ab'), chomp: true).each {|x| res << x}
+ assert_equal(1, res.size)
+ assert_equal(S("a"), res[0])
+
+ s = nil
+ "foo\nbar".each_line(nil, chomp: true) {|s2| s = s2 }
+ assert_equal("foo\nbar", s)
+
+ assert_equal "hello", S("hello\nworld").each_line(chomp: true).next
+ assert_equal "hello\nworld", S("hello\nworld").each_line(nil, chomp: true).next
+
+ res = []
+ S("").each_line(chomp: true) {|x| res << x}
+ assert_equal([], res)
+
+ res = []
+ S("\n").each_line(chomp: true) {|x| res << x}
+ assert_equal([S("")], res)
+
+ res = []
+ S("\r\n").each_line(chomp: true) {|x| res << x}
+ assert_equal([S("")], res)
+
+ res = []
+ S("a\n b\n").each_line(" ", chomp: true) {|x| res << x}
+ assert_equal([S("a\n"), S("b\n")], res)
+ end
+
+ def test_lines
+ s = S("hello\nworld")
+ 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
+ assert_warning(/passing a block to String#lines is deprecated/) {
+ 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
end
def test_empty?
- assert(S("").empty?)
- assert(!S("not").empty?)
+ assert_empty(S(""))
+ assert_not_empty(S("not"))
+ end
+
+ def test_end_with?
+ 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")])
+
+ bug5536 = '[ruby-core:40623]'
+ assert_raise(TypeError, bug5536) {S("str").end_with? :not_convertible_to_string}
end
def test_eql?
a = S("hello")
- assert(a.eql?(S("hello")))
- assert(a.eql?(a))
+ assert_operator(a, :eql?, S("hello"))
+ assert_operator(a, :eql?, a)
end
def test_gsub
@@ -652,18 +1216,37 @@ class TestString < Test::Unit::TestCase
S("hello").gsub(/./) { |s| s[0].to_s + S(' ')})
assert_equal(S("HELL-o"),
S("hello").gsub(/(hell)(.)/) { |s| $1.upcase + S('-') + $2 })
+ assert_equal(S("<>h<>e<>l<>l<>o<>"), S("hello").gsub(S(''), S('<\0>')))
a = S("hello")
a.taint
- a.untrust
- assert(a.gsub(/./, S('X')).tainted?)
- assert(a.gsub(/./, S('X')).untrusted?)
+ assert_predicate(a.gsub(/./, S('X')), :tainted?)
assert_equal("z", "abc".gsub(/./, "a" => "z"), "moved from btest/knownbug")
assert_raise(ArgumentError) { "foo".gsub }
end
+ def test_gsub_encoding
+ a = S("hello world")
+ a.force_encoding Encoding::UTF_8
+
+ b = S("hi")
+ b.force_encoding Encoding::US_ASCII
+
+ assert_equal Encoding::UTF_8, a.gsub(/hello/, b).encoding
+
+ c = S("everybody")
+ c.force_encoding Encoding::US_ASCII
+
+ assert_equal Encoding::UTF_8, a.gsub(/world/, c).encoding
+
+ assert_equal S("a\u{e9}apos&lt;"), S("a\u{e9}'&lt;").gsub("'", "apos")
+
+ bug9849 = '[ruby-core:62669] [Bug #9849]'
+ assert_equal S("\u{3042 3042 3042}!foo!"), S("\u{3042 3042 3042}/foo/").gsub("/", "!"), bug9849
+ end
+
def test_gsub!
a = S("hello")
b = a.dup
@@ -685,10 +1268,8 @@ class TestString < Test::Unit::TestCase
r = S('X')
r.taint
- r.untrust
a.gsub!(/./, r)
- assert(a.tainted?)
- assert(a.untrusted?)
+ assert_predicate(a, :tainted?)
a = S("hello")
assert_nil(a.sub!(S('X'), S('Y')))
@@ -716,19 +1297,11 @@ class TestString < Test::Unit::TestCase
def test_hash
assert_equal(S("hello").hash, S("hello").hash)
- assert(S("hello").hash != S("helLO").hash)
- end
-
- def test_hash_random
- str = 'abc'
- a = [str.hash.to_s]
- 3.times {
- assert_in_out_err(["-e", "print #{str.dump}.hash"], "") do |r, e|
- a += r
- assert_equal([], e)
- end
- }
- assert_not_equal([str.hash.to_s], a.uniq)
+ assert_not_equal(S("hello").hash, S("helLO").hash)
+ bug4104 = '[ruby-core:33500]'
+ assert_not_equal(S("a").hash, S("a\0").hash, bug4104)
+ bug9172 = '[ruby-core:58658] [Bug #9172]'
+ assert_not_equal(S("sub-setter").hash, S("discover").hash, bug9172)
end
def test_hex
@@ -742,10 +1315,10 @@ class TestString < Test::Unit::TestCase
end
def test_include?
- assert( S("foobar").include?(?f))
- assert( S("foobar").include?(S("foo")))
- assert(!S("foobar").include?(S("baz")))
- assert(!S("foobar").include?(?z))
+ assert_include(S("foobar"), ?f)
+ assert_include(S("foobar"), S("foo"))
+ assert_not_include(S("foobar"), S("baz"))
+ assert_not_include(S("foobar"), ?z)
end
def test_index
@@ -765,6 +1338,20 @@ class TestString < Test::Unit::TestCase
assert_nil(S("hello").index(S("z")))
assert_nil(S("hello").index(/z./))
+ assert_equal(0, S("").index(S("")))
+ assert_equal(0, S("").index(//))
+ assert_nil(S("").index(S("hello")))
+ assert_nil(S("").index(/hello/))
+ assert_equal(0, S("hello").index(S("")))
+ assert_equal(0, S("hello").index(//))
+
+ s = S("long") * 1000 << "x"
+ assert_nil(s.index(S("y")))
+ assert_equal(4 * 1000, s.index(S("x")))
+ s << "yx"
+ assert_equal(4 * 1000, s.index(S("x")))
+ assert_equal(4 * 1000, s.index(S("xyx")))
+
o = Object.new
def o.to_str; "bar"; end
assert_equal(3, "foobarbarbaz".index(o))
@@ -774,9 +1361,17 @@ class TestString < Test::Unit::TestCase
assert_nil($~)
end
+ def test_insert
+ assert_equal("Xabcd", S("abcd").insert(0, 'X'))
+ assert_equal("abcXd", S("abcd").insert(3, 'X'))
+ assert_equal("abcdX", S("abcd").insert(4, 'X'))
+ assert_equal("abXcd", S("abcd").insert(-3, 'X'))
+ assert_equal("abcdX", S("abcd").insert(-1, 'X'))
+ end
+
def test_intern
assert_equal(:koala, S("koala").intern)
- assert(:koala != S("Koala").intern)
+ assert_not_equal(:koala, S("Koala").intern)
end
def test_length
@@ -805,6 +1400,9 @@ class TestString < Test::Unit::TestCase
assert_equal(S("AAAAA000"), S("ZZZZ999").next)
assert_equal(S("*+"), S("**").next)
+
+ assert_equal(S("!"), S(" ").next)
+ assert_equal(S(""), S("").next)
end
def test_next!
@@ -841,6 +1439,10 @@ class TestString < Test::Unit::TestCase
a = S("**")
assert_equal(S("*+"), a.next!)
assert_equal(S("*+"), a)
+
+ a = S(" ")
+ assert_equal(S("!"), a.next!)
+ assert_equal(S("!"), a)
end
def test_oct
@@ -861,11 +1463,9 @@ class TestString < Test::Unit::TestCase
a = S("foo")
a.taint
- a.untrust
b = a.replace(S("xyz"))
assert_equal(S("xyz"), b)
- assert(b.tainted?)
- assert(b.untrusted?)
+ assert_predicate(b, :tainted?)
s = "foo" * 100
s2 = ("bar" * 100).dup
@@ -877,10 +1477,10 @@ class TestString < Test::Unit::TestCase
assert_equal(s2, s)
fs = "".freeze
- assert_raise(RuntimeError) { fs.replace("a") }
- assert_raise(RuntimeError) { fs.replace(fs) }
+ assert_raise(FrozenError) { fs.replace("a") }
+ assert_raise(FrozenError) { fs.replace(fs) }
assert_raise(ArgumentError) { fs.replace() }
- assert_raise(RuntimeError) { fs.replace(42) }
+ assert_raise(FrozenError) { fs.replace(42) }
end
def test_reverse
@@ -930,6 +1530,9 @@ class TestString < Test::Unit::TestCase
assert_nil("foo".rindex(//, -100))
assert_nil($~)
+
+ assert_equal(3, "foo".rindex(//))
+ assert_equal([3, 3], $~.offset(0))
end
def test_rjust
@@ -956,6 +1559,24 @@ class TestString < Test::Unit::TestCase
res = []
a.scan(/(...)/) { |w| res << w }
assert_equal([[S("cru")], [S("el ")], [S("wor")]],res)
+
+ a = S("hello")
+ a.taint
+ res = []
+ a.scan(/./) { |w| res << w }
+ assert_predicate(res[0], :tainted?, '[ruby-core:33338] #4087')
+
+ /h/ =~ a
+ a.scan(/x/)
+ assert_nil($~)
+
+ /h/ =~ a
+ a.scan('x')
+ assert_nil($~)
+
+ assert_equal(3, S("hello hello hello").scan("hello".taint).count(&:tainted?))
+
+ assert_equal(%w[1 2 3], S("a1 a2 a3").scan(/a\K./))
end
def test_size
@@ -990,6 +1611,11 @@ class TestString < Test::Unit::TestCase
assert_equal(S("Bar"), S("FooBar").slice(S("Bar")))
assert_nil(S("FooBar").slice(S("xyzzy")))
assert_nil(S("FooBar").slice(S("plugh")))
+
+ bug9882 = '[ruby-core:62842] [Bug #9882]'
+ substr = S("\u{30c6 30b9 30c8 2019}#{bug9882}").slice(4..-1)
+ assert_equal(S(bug9882).hash, substr.hash, bug9882)
+ assert_predicate(substr, :ascii_only?, bug9882)
end
def test_slice!
@@ -1092,19 +1718,11 @@ class TestString < Test::Unit::TestCase
assert_equal(S("Bar"), a.slice!(S("Bar")))
assert_equal(S("Foo"), a)
- pre_1_7_1 do
- a=S("FooBar")
- assert_nil(a.slice!(S("xyzzy")))
- assert_equal(S("FooBar"), a)
- assert_nil(a.slice!(S("plugh")))
- assert_equal(S("FooBar"), a)
- end
-
assert_raise(ArgumentError) { "foo".slice! }
end
def test_split
- assert_nil($;)
+ fs, $; = $;, nil
assert_equal([S("a"), S("b"), S("c")], S(" a b\t c ").split)
assert_equal([S("a"), S("b"), S("c")], S(" a b\t c ").split(S(" ")))
@@ -1126,9 +1744,107 @@ class TestString < Test::Unit::TestCase
assert_equal([S("a"), S(""), S("b"), S("c"), S("")], S("a||b|c|").split(S('|'), -1))
assert_equal([], "".split(//, 1))
+ ensure
+ $; = fs
+ end
+
+ def test_split_with_block
+ fs, $; = $;, nil
+ result = []; S(" a b\t c ").split {|s| result << s}
+ assert_equal([S("a"), S("b"), S("c")], result)
+ result = []; S(" a b\t c ").split(S(" ")) {|s| result << s}
+ assert_equal([S("a"), S("b"), S("c")], result)
+
+ result = []; S(" a | b | c ").split(S("|")) {|s| result << s}
+ assert_equal([S(" a "), S(" b "), S(" c ")], result)
+
+ result = []; S("aXXbXXcXX").split(/X./) {|s| result << s}
+ assert_equal([S("a"), S("b"), S("c")], result)
+
+ result = []; S("abc").split(//) {|s| result << s}
+ assert_equal([S("a"), S("b"), S("c")], result)
+
+ result = []; S("a|b|c").split(S('|'), 1) {|s| result << s}
+ assert_equal([S("a|b|c")], result)
+
+ result = []; S("a|b|c").split(S('|'), 2) {|s| result << s}
+ assert_equal([S("a"), S("b|c")], result)
+ result = []; S("a|b|c").split(S('|'), 3) {|s| result << s}
+ assert_equal([S("a"), S("b"), S("c")], result)
+
+ result = []; S("a|b|c|").split(S('|'), -1) {|s| result << s}
+ assert_equal([S("a"), S("b"), S("c"), S("")], result)
+ result = []; S("a|b|c||").split(S('|'), -1) {|s| result << s}
+ assert_equal([S("a"), S("b"), S("c"), S(""), S("")], result)
+
+ result = []; S("a||b|c|").split(S('|')) {|s| result << s}
+ assert_equal([S("a"), S(""), S("b"), S("c")], result)
+ result = []; S("a||b|c|").split(S('|'), -1) {|s| result << s}
+ assert_equal([S("a"), S(""), S("b"), S("c"), S("")], result)
+
+ result = []; "".split(//, 1) {|s| result << s}
+ assert_equal([], result)
+
+ result = []; "aaa,bbb,ccc,ddd".split(/,/) {|s| result << s.gsub(/./, "A")}
+ assert_equal(["AAA"]*4, result)
+ ensure
+ $; = fs
+ end
+
+ def test_fs
+ assert_raise_with_message(TypeError, /\$;/) {
+ $; = []
+ }
+
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ bug = '[ruby-core:79582] $; must not be GCed'
+ begin;
+ $; = " "
+ $a = nil
+ alias $; $a
+ alias $-F $a
+ GC.start
+ assert_equal([], "".split, bug)
+ end;
+ end
+
+ def test_split_encoding
+ bug6206 = '[ruby-dev:45441]'
+ Encoding.list.each do |enc|
+ next unless enc.ascii_compatible?
+ s = S("a:".force_encoding(enc))
+ assert_equal([enc]*2, s.split(":", 2).map(&:encoding), bug6206)
+ end
+ end
- assert_equal("[2, 3]", [1,2,3].slice!(1,10000).inspect, "moved from btest/knownbug")
+ def test_split_wchar
+ bug8642 = '[ruby-core:56036] [Bug #8642]'
+ WIDE_ENCODINGS.each do |enc|
+ s = S("abc,def".encode(enc))
+ assert_equal(["abc", "def"].map {|c| c.encode(enc)},
+ s.split(",".encode(enc)),
+ "#{bug8642} in #{enc.name}")
+ end
+ end
+
+ def test_split_invalid_sequence
+ bug10886 = '[ruby-core:68229] [Bug #10886]'
+ broken = S("\xa1".force_encoding("utf-8"))
+ assert_raise(ArgumentError, bug10886) {
+ S("a,b").split(broken)
+ }
+ end
+ def test_split_invalid_argument
+ assert_raise(TypeError) {
+ S("a,b").split(BasicObject.new)
+ }
+ end
+
+ def test_split_dupped
+ s = "abc"
+ s.split("b", 1).map(&:upcase!)
+ assert_equal("abc", s)
end
def test_squeeze
@@ -1156,6 +1872,20 @@ class TestString < Test::Unit::TestCase
assert_nil(a.squeeze!)
end
+ def test_start_with?
+ assert_send([S("hello"), :start_with?, S("hel")])
+ assert_not_send([S("hello"), :start_with?, S("el")])
+ assert_send([S("hello"), :start_with?, S("el"), S("he")])
+
+ bug5536 = '[ruby-core:40623]'
+ assert_raise(TypeError, bug5536) {S("str").start_with? :not_convertible_to_string}
+
+ assert_equal(true, "hello".start_with?(/hel/))
+ assert_equal("hel", $&)
+ assert_equal(false, "hello".start_with?(/el/))
+ assert_nil($&)
+ end
+
def test_strip
assert_equal(S("x"), S(" x ").strip)
assert_equal(S("x"), S(" \n\r\t x \t\r\n\n ").strip)
@@ -1190,6 +1920,7 @@ class TestString < Test::Unit::TestCase
assert_equal(S("HELL-o"), S("hello").sub(/(hell)(.)/) {
|s| $1.upcase + S('-') + $2
})
+ assert_equal(S("h<e>llo"), S("hello").sub('e', S('<\0>')))
assert_equal(S("a\\aba"), S("ababa").sub(/b/, '\\'))
assert_equal(S("ab\\aba"), S("ababa").sub(/(b)/, '\1\\'))
@@ -1223,10 +1954,8 @@ class TestString < Test::Unit::TestCase
a = S("hello")
a.taint
- a.untrust
x = a.sub(/./, S('X'))
- assert(x.tainted?)
- assert(x.untrusted?)
+ assert_predicate(x, :tainted?)
o = Object.new
def o.to_str; "bar"; end
@@ -1241,6 +1970,16 @@ class TestString < Test::Unit::TestCase
o = Object.new
def o.to_s; self; end
assert_match(/^foo#<Object:0x.*>baz$/, "foobarbaz".sub("bar") { o })
+
+ assert_equal(S("Abc"), S("abc").sub("a", "A"))
+ m = nil
+ assert_equal(S("Abc"), S("abc").sub("a") {m = $~; "A"})
+ assert_equal(S("a"), m[0])
+ assert_equal(/a/, m.regexp)
+ bug = '[ruby-core:78686] [Bug #13042] other than regexp has no name references'
+ assert_raise_with_message(IndexError, /oops/, bug) {
+ 'hello'.gsub('hello', '\k<oops>')
+ }
end
def test_sub!
@@ -1267,10 +2006,14 @@ class TestString < Test::Unit::TestCase
r = S('X')
r.taint
- r.untrust
a.sub!(/./, r)
- assert(a.tainted?)
- assert(a.untrusted?)
+ assert_predicate(a, :tainted?)
+
+ bug16105 = '[Bug #16105] heap-use-after-free'
+ a = S("ABCDEFGHIJKLMNOPQRSTUVWXYZ012345678")
+ b = a.dup
+ c = a.slice(1, 100)
+ assert_equal("AABCDEFGHIJKLMNOPQRSTUVWXYZ012345678", b.sub!(c, b), bug16105)
end
def test_succ
@@ -1289,10 +2032,18 @@ class TestString < Test::Unit::TestCase
assert_equal("abce", "abcd".succ)
assert_equal("THX1139", "THX1138".succ)
- assert_equal("<<koalb>>", "<<koala>>".succ)
+ assert_equal("<\<koalb>>", "<\<koala>>".succ)
assert_equal("2000aaa", "1999zzz".succ)
assert_equal("AAAA0000", "ZZZ9999".succ)
assert_equal("**+", "***".succ)
+
+ assert_equal("!", " ".succ)
+ assert_equal("", "".succ)
+
+ bug = '[ruby-core:83062] [Bug #13952]'
+ s = "\xff".b
+ assert_not_predicate(s, :ascii_only?)
+ assert_predicate(s.succ, :ascii_only?, bug)
end
def test_succ!
@@ -1334,6 +2085,14 @@ class TestString < Test::Unit::TestCase
assert_equal(S("No.10"), a.succ!)
assert_equal(S("No.10"), a)
+ a = S(" ")
+ assert_equal(S("!"), a.succ!)
+ assert_equal(S("!"), a)
+
+ a = S("")
+ assert_equal(S(""), a.succ!)
+ assert_equal(S(""), a)
+
assert_equal("aaaaaaaaaaaa", "zzzzzzzzzzz".succ!)
assert_equal("aaaaaaaaaaaaaaaaaaaaaaaa", "zzzzzzzzzzzzzzzzzzzzzzz".succ!)
end
@@ -1344,7 +2103,9 @@ class TestString < Test::Unit::TestCase
n += S("\001")
assert_equal(16, n.sum(17))
n[0] = 2.chr
- assert(15 != n.sum)
+ assert_not_equal(15, n.sum)
+ assert_equal(17, n.sum(0))
+ assert_equal(17, n.sum(-1))
end
def check_sum(str, bits=16)
@@ -1359,7 +2120,7 @@ class TestString < Test::Unit::TestCase
assert_equal(294, "abc".sum)
check_sum("abc")
check_sum("\x80")
- 0.upto(70) {|bits|
+ -3.upto(70) {|bits|
check_sum("xyz", bits)
}
end
@@ -1427,6 +2188,13 @@ class TestString < Test::Unit::TestCase
assert_equal(0x4000000000000000, "4611686018427387904".to_i(10))
assert_equal(1, "1__2".to_i(10))
assert_equal(1, "1_z".to_i(10))
+
+ bug6192 = '[ruby-core:43566]'
+ assert_raise(Encoding::CompatibilityError, bug6192) {"0".encode("utf-16be").to_i}
+ assert_raise(Encoding::CompatibilityError, bug6192) {"0".encode("utf-16le").to_i}
+ assert_raise(Encoding::CompatibilityError, bug6192) {"0".encode("utf-32be").to_i}
+ assert_raise(Encoding::CompatibilityError, bug6192) {"0".encode("utf-32le").to_i}
+ assert_raise(Encoding::CompatibilityError, bug6192) {"0".encode("iso-2022-jp").to_i}
end
def test_to_s
@@ -1465,6 +2233,15 @@ class TestString < Test::Unit::TestCase
assert_equal(true, "\u0101".tr("\u0101", "a").ascii_only?)
assert_equal(true, "\u3041".tr("\u3041", "a").ascii_only?)
assert_equal(false, "\u3041\u3042".tr("\u3041", "a").ascii_only?)
+
+ bug6156 = '[ruby-core:43335]'
+ bug13950 = '[ruby-core:83056] [Bug #13950]'
+ str, range, star = %w[b a-z *].map{|s|s.encode("utf-16le")}
+ result = str.tr(range, star)
+ assert_equal(star, result, bug6156)
+ assert_not_predicate(str, :ascii_only?)
+ assert_not_predicate(star, :ascii_only?)
+ assert_not_predicate(result, :ascii_only?, bug13950)
end
def test_tr!
@@ -1652,20 +2429,12 @@ class TestString < Test::Unit::TestCase
end
def test_frozen_check
- assert_raise(RuntimeError) {
+ assert_raise(FrozenError) {
s = ""
s.sub!(/\A/) { s.freeze; "zzz" }
}
end
- def test_tainted_str_new
- a = []
- a << a
- s = a.inspect
- assert(s.tainted?)
- assert_equal("[[...]]", s)
- end
-
class S2 < String
end
def test_str_new4
@@ -1720,10 +2489,6 @@ class TestString < Test::Unit::TestCase
assert_nil(l.slice!(/\A.*\n/), "[ruby-dev:31665]")
end
- def test_end_with?
- assert("abc".end_with?("c"))
- end
-
def test_times2
s1 = ''
100.times {|n|
@@ -1746,7 +2511,7 @@ class TestString < Test::Unit::TestCase
def test_match_method
assert_equal("bar", "foobarbaz".match(/bar/).to_s)
- o = /foo/
+ o = Regexp.new('foo')
def o.match(x, y, z); x + y + z; end
assert_equal("foobarbaz", "foo".match(o, "bar", "baz"))
x = nil
@@ -1756,6 +2521,50 @@ class TestString < Test::Unit::TestCase
assert_raise(ArgumentError) { "foo".match }
end
+ def test_match_p_regexp
+ /backref/ =~ 'backref'
+ # must match here, but not in a separate method, e.g., assert_send,
+ # to check if $~ is affected or not.
+ assert_equal(true, "".match?(//))
+ assert_equal(true, :abc.match?(/.../))
+ assert_equal(true, 'abc'.match?(/b/))
+ assert_equal(true, 'abc'.match?(/b/, 1))
+ assert_equal(true, 'abc'.match?(/../, 1))
+ assert_equal(true, 'abc'.match?(/../, -2))
+ assert_equal(false, 'abc'.match?(/../, -4))
+ assert_equal(false, 'abc'.match?(/../, 4))
+ assert_equal(true, "\u3042xx".match?(/../, 1))
+ assert_equal(false, "\u3042x".match?(/../, 1))
+ assert_equal(true, ''.match?(/\z/))
+ assert_equal(true, 'abc'.match?(/\z/))
+ assert_equal(true, 'Ruby'.match?(/R.../))
+ assert_equal(false, 'Ruby'.match?(/R.../, 1))
+ assert_equal(false, 'Ruby'.match?(/P.../))
+ assert_equal('backref', $&)
+ end
+
+ def test_match_p_string
+ /backref/ =~ 'backref'
+ # must match here, but not in a separate method, e.g., assert_send,
+ # to check if $~ is affected or not.
+ assert_equal(true, "".match?(''))
+ assert_equal(true, :abc.match?('...'))
+ assert_equal(true, 'abc'.match?('b'))
+ assert_equal(true, 'abc'.match?('b', 1))
+ assert_equal(true, 'abc'.match?('..', 1))
+ assert_equal(true, 'abc'.match?('..', -2))
+ assert_equal(false, 'abc'.match?('..', -4))
+ assert_equal(false, 'abc'.match?('..', 4))
+ assert_equal(true, "\u3042xx".match?('..', 1))
+ assert_equal(false, "\u3042x".match?('..', 1))
+ assert_equal(true, ''.match?('\z'))
+ assert_equal(true, 'abc'.match?('\z'))
+ assert_equal(true, 'Ruby'.match?('R...'))
+ assert_equal(false, 'Ruby'.match?('R...', 1))
+ assert_equal(false, 'Ruby'.match?('P...'))
+ assert_equal('backref', $&)
+ end
+
def test_clear
s = "foo" * 100
s.clear
@@ -1770,12 +2579,35 @@ class TestString < Test::Unit::TestCase
assert_instance_of(String, s.to_s)
end
+ def test_inspect_nul
+ bug8290 = '[ruby-core:54458]'
+ s = "\0" + "12"
+ assert_equal '"\u000012"', s.inspect, bug8290
+ s = "\0".b + "12"
+ assert_equal '"\x0012"', s.inspect, bug8290
+ end
+
def test_partition
assert_equal(%w(he l lo), "hello".partition(/l/))
assert_equal(%w(he l lo), "hello".partition("l"))
assert_raise(TypeError) { "hello".partition(1) }
def (hyphen = Object.new).to_str; "-"; end
assert_equal(%w(foo - bar), "foo-bar".partition(hyphen), '[ruby-core:23540]')
+
+ bug6206 = '[ruby-dev:45441]'
+ Encoding.list.each do |enc|
+ next unless enc.ascii_compatible?
+ s = S("a:".force_encoding(enc))
+ assert_equal([enc]*3, s.partition("|").map(&:encoding), bug6206)
+ end
+
+ assert_equal(["\u30E6\u30FC\u30B6", "@", "\u30C9\u30E1.\u30A4\u30F3"],
+ "\u30E6\u30FC\u30B6@\u30C9\u30E1.\u30A4\u30F3".partition(/[@.]/))
+
+ bug = '[ruby-core:82911]'
+ hello = "hello"
+ hello.partition("hi").map(&:upcase!)
+ assert_equal("hello", hello, bug)
end
def test_rpartition
@@ -1784,10 +2616,31 @@ class TestString < Test::Unit::TestCase
assert_raise(TypeError) { "hello".rpartition(1) }
def (hyphen = Object.new).to_str; "-"; end
assert_equal(%w(foo - bar), "foo-bar".rpartition(hyphen), '[ruby-core:23540]')
+
+ bug6206 = '[ruby-dev:45441]'
+ Encoding.list.each do |enc|
+ next unless enc.ascii_compatible?
+ s = S("a:".force_encoding(enc))
+ assert_equal([enc]*3, s.rpartition("|").map(&:encoding), bug6206)
+ end
+
+ bug8138 = '[ruby-dev:47183]'
+ assert_equal(["\u30E6\u30FC\u30B6@\u30C9\u30E1", ".", "\u30A4\u30F3"],
+ "\u30E6\u30FC\u30B6@\u30C9\u30E1.\u30A4\u30F3".rpartition(/[@.]/), bug8138)
+
+ bug = '[ruby-core:82911]'
+ hello = "hello"
+ hello.rpartition("hi").map(&:upcase!)
+ assert_equal("hello", hello, bug)
end
def test_setter
assert_raise(TypeError) { $/ = 1 }
+ name = "\u{5206 884c}"
+ assert_separately([], <<-"end;") # do
+ alias $#{name} $/
+ assert_raise_with_message(TypeError, /\\$#{name}/) { $#{name} = 1 }
+ end;
end
def test_to_id
@@ -1808,6 +2661,7 @@ class TestString < Test::Unit::TestCase
c.class_eval { attr 1 }
end
+ class << o;remove_method :to_str;end
def o.to_str; "foo"; end
assert_nothing_raised do
c.class_eval { attr o }
@@ -1832,14 +2686,40 @@ class TestString < Test::Unit::TestCase
assert_equal("\u3042", ("\u3042" * 100)[-1])
end
+=begin
def test_compare_different_encoding_string
s1 = "\xff".force_encoding("UTF-8")
s2 = "\xff".force_encoding("ISO-2022-JP")
- #assert_equal([-1, 1], [s1 <=> s2, s2 <=> s1].sort)
+ assert_equal([-1, 1], [s1 <=> s2, s2 <=> s1].sort)
end
+=end
def test_casecmp
+ assert_equal(0, "FoO".casecmp("fOO"))
+ assert_equal(1, "FoO".casecmp("BaR"))
+ assert_equal(-1, "baR".casecmp("FoO"))
assert_equal(1, "\u3042B".casecmp("\u3042a"))
+
+ assert_nil("foo".casecmp(:foo))
+ assert_nil("foo".casecmp(Object.new))
+
+ o = Object.new
+ def o.to_str; "fOO"; end
+ assert_equal(0, "FoO".casecmp(o))
+ end
+
+ def test_casecmp?
+ assert_equal(true, 'FoO'.casecmp?('fOO'))
+ assert_equal(false, 'FoO'.casecmp?('BaR'))
+ assert_equal(false, 'baR'.casecmp?('FoO'))
+ assert_equal(true, 'äöü'.casecmp?('ÄÖÜ'))
+
+ assert_nil("foo".casecmp?(:foo))
+ assert_nil("foo".casecmp?(Object.new))
+
+ o = Object.new
+ def o.to_str; "fOO"; end
+ assert_equal(true, "FoO".casecmp?(o))
end
def test_upcase2
@@ -1851,16 +2731,294 @@ class TestString < Test::Unit::TestCase
end
def test_rstrip
+ assert_equal(" hello", " hello ".rstrip)
assert_equal("\u3042", "\u3042 ".rstrip)
assert_raise(Encoding::CompatibilityError) { "\u3042".encode("ISO-2022-JP").rstrip }
end
+ def test_rstrip_bang
+ s1 = S(" hello ")
+ assert_equal(" hello", s1.rstrip!)
+ assert_equal(" hello", s1)
+
+ s2 = S("\u3042 ")
+ assert_equal("\u3042", s2.rstrip!)
+ assert_equal("\u3042", s2)
+
+ s3 = S(" \u3042")
+ assert_equal(nil, s3.rstrip!)
+ assert_equal(" \u3042", s3)
+
+ s4 = S("\u3042")
+ assert_equal(nil, s4.rstrip!)
+ assert_equal("\u3042", s4)
+
+ assert_raise(Encoding::CompatibilityError) { "\u3042".encode("ISO-2022-JP").rstrip! }
+ end
+
+ def test_lstrip
+ assert_equal("hello ", " hello ".lstrip)
+ assert_equal("\u3042", " \u3042".lstrip)
+ end
+
+ def test_lstrip_bang
+ s1 = S(" hello ")
+ assert_equal("hello ", s1.lstrip!)
+ assert_equal("hello ", s1)
+
+ s2 = S("\u3042 ")
+ assert_equal(nil, s2.lstrip!)
+ assert_equal("\u3042 ", s2)
+
+ s3 = S(" \u3042")
+ assert_equal("\u3042", s3.lstrip!)
+ assert_equal("\u3042", s3)
+
+ s4 = S("\u3042")
+ assert_equal(nil, s4.lstrip!)
+ assert_equal("\u3042", s4)
+ end
+
+ def test_delete_prefix
+ assert_raise(TypeError) { 'hello'.delete_prefix(nil) }
+ assert_raise(TypeError) { 'hello'.delete_prefix(1) }
+ assert_raise(TypeError) { 'hello'.delete_prefix(/hel/) }
+
+ s = S("hello")
+ assert_equal("lo", s.delete_prefix('hel'))
+ assert_equal("hello", s)
+
+ s = S("hello")
+ assert_equal("hello", s.delete_prefix('lo'))
+ assert_equal("hello", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{306b 3061 306f}", s.delete_prefix("\u{3053 3093}"))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{3053 3093 306b 3061 306f}", s.delete_prefix('hel'))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("hello")
+ assert_equal("hello", s.delete_prefix("\u{3053 3093}"))
+ assert_equal("hello", s)
+
+ # skip if argument is a broken string
+ s = S("\xe3\x81\x82")
+ assert_equal("\xe3\x81\x82", s.delete_prefix("\xe3"))
+ assert_equal("\xe3\x81\x82", s)
+
+ s = S("\x95\x5c").force_encoding("Shift_JIS")
+ assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s.delete_prefix("\x95"))
+ assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s)
+
+ # clear coderange
+ s = S("\u{3053 3093}hello")
+ assert_not_predicate(s, :ascii_only?)
+ assert_predicate(s.delete_prefix("\u{3053 3093}"), :ascii_only?)
+
+ # argument should be converted to String
+ klass = Class.new { def to_str; 'a'; end }
+ s = S("abba")
+ assert_equal("bba", s.delete_prefix(klass.new))
+ assert_equal("abba", s)
+ end
+
+ def test_delete_prefix_bang
+ assert_raise(TypeError) { 'hello'.delete_prefix!(nil) }
+ assert_raise(TypeError) { 'hello'.delete_prefix!(1) }
+ assert_raise(TypeError) { 'hello'.delete_prefix!(/hel/) }
+
+ s = S("hello")
+ assert_equal("lo", s.delete_prefix!('hel'))
+ assert_equal("lo", s)
+
+ s = S("hello")
+ assert_equal(nil, s.delete_prefix!('lo'))
+ assert_equal("hello", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{306b 3061 306f}", s.delete_prefix!("\u{3053 3093}"))
+ assert_equal("\u{306b 3061 306f}", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal(nil, s.delete_prefix!('hel'))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("hello")
+ assert_equal(nil, s.delete_prefix!("\u{3053 3093}"))
+ assert_equal("hello", s)
+
+ # skip if argument is a broken string
+ s = S("\xe3\x81\x82")
+ assert_equal(nil, s.delete_prefix!("\xe3"))
+ assert_equal("\xe3\x81\x82", s)
+
+ # clear coderange
+ s = S("\u{3053 3093}hello")
+ assert_not_predicate(s, :ascii_only?)
+ assert_predicate(s.delete_prefix!("\u{3053 3093}"), :ascii_only?)
+
+ # argument should be converted to String
+ klass = Class.new { def to_str; 'a'; end }
+ s = S("abba")
+ assert_equal("bba", s.delete_prefix!(klass.new))
+ assert_equal("bba", s)
+
+ s = S("ax").freeze
+ assert_raise_with_message(FrozenError, /frozen/) {s.delete_prefix!("a")}
+
+ s = S("ax")
+ o = Struct.new(:s).new(s)
+ def o.to_str
+ s.freeze
+ "a"
+ end
+ assert_raise_with_message(FrozenError, /frozen/) {s.delete_prefix!(o)}
+ end
+
+ def test_delete_suffix
+ assert_raise(TypeError) { 'hello'.delete_suffix(nil) }
+ assert_raise(TypeError) { 'hello'.delete_suffix(1) }
+ assert_raise(TypeError) { 'hello'.delete_suffix(/hel/) }
+
+ s = S("hello")
+ assert_equal("hel", s.delete_suffix('lo'))
+ assert_equal("hello", s)
+
+ s = S("hello")
+ assert_equal("hello", s.delete_suffix('he'))
+ assert_equal("hello", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{3053 3093 306b}", s.delete_suffix("\u{3061 306f}"))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{3053 3093 306b 3061 306f}", s.delete_suffix('lo'))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("hello")
+ assert_equal("hello", s.delete_suffix("\u{3061 306f}"))
+ assert_equal("hello", s)
+
+ # skip if argument is a broken string
+ s = S("\xe3\x81\x82")
+ assert_equal("\xe3\x81\x82", s.delete_suffix("\x82"))
+ assert_equal("\xe3\x81\x82", s)
+
+ # clear coderange
+ s = S("hello\u{3053 3093}")
+ assert_not_predicate(s, :ascii_only?)
+ assert_predicate(s.delete_suffix("\u{3053 3093}"), :ascii_only?)
+
+ # argument should be converted to String
+ klass = Class.new { def to_str; 'a'; end }
+ s = S("abba")
+ assert_equal("abb", s.delete_suffix(klass.new))
+ assert_equal("abba", s)
+
+ # chomp removes any of "\n", "\r\n", "\r" when "\n" is specified,
+ # but delete_suffix does not
+ s = "foo\n"
+ assert_equal("foo", s.delete_suffix("\n"))
+ s = "foo\r\n"
+ assert_equal("foo\r", s.delete_suffix("\n"))
+ s = "foo\r"
+ assert_equal("foo\r", s.delete_suffix("\n"))
+ end
+
+ def test_delete_suffix_bang
+ assert_raise(TypeError) { 'hello'.delete_suffix!(nil) }
+ assert_raise(TypeError) { 'hello'.delete_suffix!(1) }
+ assert_raise(TypeError) { 'hello'.delete_suffix!(/hel/) }
+
+ s = S("hello").freeze
+ assert_raise_with_message(FrozenError, /frozen/) {s.delete_suffix!('lo')}
+
+ s = S("ax")
+ o = Struct.new(:s).new(s)
+ def o.to_str
+ s.freeze
+ "x"
+ end
+ assert_raise_with_message(FrozenError, /frozen/) {s.delete_suffix!(o)}
+
+ s = S("hello")
+ assert_equal("hel", s.delete_suffix!('lo'))
+ assert_equal("hel", s)
+
+ s = S("hello")
+ assert_equal(nil, s.delete_suffix!('he'))
+ assert_equal("hello", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal("\u{3053 3093 306b}", s.delete_suffix!("\u{3061 306f}"))
+ assert_equal("\u{3053 3093 306b}", s)
+
+ s = S("\u{3053 3093 306b 3061 306f}")
+ assert_equal(nil, s.delete_suffix!('lo'))
+ assert_equal("\u{3053 3093 306b 3061 306f}", s)
+
+ s = S("hello")
+ assert_equal(nil, s.delete_suffix!("\u{3061 306f}"))
+ assert_equal("hello", s)
+
+ # skip if argument is a broken string
+ s = S("\xe3\x81\x82")
+ assert_equal(nil, s.delete_suffix!("\x82"))
+ assert_equal("\xe3\x81\x82", s)
+
+ s = S("\x95\x5c").force_encoding("Shift_JIS")
+ assert_equal(nil, s.delete_suffix!("\x5c"))
+ assert_equal("\x95\x5c".force_encoding("Shift_JIS"), s)
+
+ # clear coderange
+ s = S("hello\u{3053 3093}")
+ assert_not_predicate(s, :ascii_only?)
+ assert_predicate(s.delete_suffix!("\u{3053 3093}"), :ascii_only?)
+
+ # argument should be converted to String
+ klass = Class.new { def to_str; 'a'; end }
+ s = S("abba")
+ assert_equal("abb", s.delete_suffix!(klass.new))
+ assert_equal("abb", s)
+
+ # chomp removes any of "\n", "\r\n", "\r" when "\n" is specified,
+ # but delete_suffix does not
+ s = "foo\n"
+ assert_equal("foo", s.delete_suffix!("\n"))
+ s = "foo\r\n"
+ assert_equal("foo\r", s.delete_suffix!("\n"))
+ s = "foo\r"
+ assert_equal(nil, s.delete_suffix!("\n"))
+ end
+
+=begin
def test_symbol_table_overflow
- return
assert_in_out_err([], <<-INPUT, [], /symbol table overflow \(symbol [a-z]{8}\) \(RuntimeError\)/)
("aaaaaaaa".."zzzzzzzz").each {|s| s.to_sym }
INPUT
end
+=end
+
+ def test_nesting_shared
+ a = ('a' * 24).encode(Encoding::ASCII).gsub('x', '')
+ hash = {}
+ hash[a] = true
+ assert_equal(('a' * 24), a)
+ 4.times { GC.start }
+ assert_equal(('a' * 24), a, '[Bug #15792]')
+ end
+
+ def test_nesting_shared_b
+ a = ('j' * 24).b.b
+ eval('', binding, a)
+ assert_equal(('j' * 24), a)
+ 4.times { GC.start }
+ assert_equal(('j' * 24), a, '[Bug #15934]')
+ end
def test_shared_force_encoding
s = "\u{3066}\u{3059}\u{3068}".gsub(//, '')
@@ -1876,10 +3034,178 @@ class TestString < Test::Unit::TestCase
end
def test_ascii_incomat_inspect
- [Encoding::UTF_16LE, Encoding::UTF_16BE,
- Encoding::UTF_32LE, Encoding::UTF_32BE].each do |e|
+ bug4081 = '[ruby-core:33283]'
+ WIDE_ENCODINGS.each do |e|
assert_equal('"abc"', "abc".encode(e).inspect)
assert_equal('"\\u3042\\u3044\\u3046"', "\u3042\u3044\u3046".encode(e).inspect)
+ assert_equal('"ab\\"c"', "ab\"c".encode(e).inspect, bug4081)
end
+ begin
+ verbose, $VERBOSE = $VERBOSE, nil
+ ext = Encoding.default_external
+ Encoding.default_external = "us-ascii"
+ $VERBOSE = verbose
+ i = "abc\"\\".force_encoding("utf-8").inspect
+ ensure
+ $VERBOSE = nil
+ Encoding.default_external = ext
+ $VERBOSE = verbose
+ end
+ assert_equal('"abc\\"\\\\"', i, bug4081)
+ end
+
+ def test_dummy_inspect
+ assert_equal('"\e\x24\x42\x22\x4C\x22\x68\e\x28\x42"',
+ "\u{ffe2}\u{2235}".encode("cp50220").inspect)
+ end
+
+ def test_prepend
+ assert_equal(S("hello world!"), "!".prepend("hello ", "world"))
+ b = S("ue")
+ assert_equal(S("ueueue"), b.prepend(b, b))
+
+ foo = Object.new
+ def foo.to_str
+ "b"
+ end
+ assert_equal(S("ba"), "a".prepend(foo))
+
+ a = S("world")
+ b = S("hello ")
+ a.prepend(b)
+ assert_equal(S("hello world"), a)
+ assert_equal(S("hello "), b)
+ end
+
+ def u(str)
+ str.force_encoding(Encoding::UTF_8)
+ end
+
+ def test_byteslice
+ assert_equal("h", "hello".byteslice(0))
+ assert_equal(nil, "hello".byteslice(5))
+ assert_equal("o", "hello".byteslice(-1))
+ assert_equal(nil, "hello".byteslice(-6))
+
+ assert_equal("", "hello".byteslice(0, 0))
+ assert_equal("hello", "hello".byteslice(0, 6))
+ assert_equal("hello", "hello".byteslice(0, 6))
+ assert_equal("", "hello".byteslice(5, 1))
+ assert_equal("o", "hello".byteslice(-1, 6))
+ assert_equal(nil, "hello".byteslice(-6, 1))
+ assert_equal(nil, "hello".byteslice(0, -1))
+
+ assert_equal("h", "hello".byteslice(0..0))
+ assert_equal("", "hello".byteslice(5..0))
+ assert_equal("o", "hello".byteslice(4..5))
+ assert_equal(nil, "hello".byteslice(6..0))
+ assert_equal("", "hello".byteslice(-1..0))
+ assert_equal("llo", "hello".byteslice(-3..5))
+
+ assert_equal(u("\x81"), "\u3042".byteslice(1))
+ assert_equal(u("\x81\x82"), "\u3042".byteslice(1, 2))
+ assert_equal(u("\x81\x82"), "\u3042".byteslice(1..2))
+
+ assert_equal(u("\x82")+("\u3042"*9), ("\u3042"*10).byteslice(2, 28))
+
+ bug7954 = '[ruby-dev:47108]'
+ assert_equal(false, "\u3042".byteslice(0, 2).valid_encoding?, bug7954)
+ assert_equal(false, ("\u3042"*10).byteslice(0, 20).valid_encoding?, bug7954)
+ end
+
+ def test_unknown_string_option
+ str = nil
+ assert_nothing_raised(SyntaxError) do
+ eval(%{
+ str = begin"hello"end
+ })
+ end
+ assert_equal "hello", str
+ end
+
+ def test_eq_tilde_can_be_overridden
+ assert_separately([], <<-RUBY)
+ class String
+ undef =~
+ def =~(str)
+ "foo"
+ end
+ end
+
+ assert_equal("foo", "" =~ //)
+ RUBY
+ end
+
+ class Bug9581 < String
+ def =~ re; :foo end
+ end
+
+ def test_regexp_match_subclass
+ s = Bug9581.new("abc")
+ r = /abc/
+ assert_equal(:foo, s =~ r)
+ assert_equal(:foo, s.send(:=~, r))
+ assert_equal(:foo, s.send(:=~, /abc/))
+ assert_equal(:foo, s =~ /abc/, "should not use optimized instruction")
+ end
+
+ def test_LSHIFT_neary_long_max
+ return unless @cls == String
+ assert_ruby_status([], <<-'end;', '[ruby-core:61886] [Bug #9709]', timeout: 20)
+ begin
+ a = "a" * 0x4000_0000
+ a << "a" * 0x1_0000
+ rescue NoMemoryError
+ end
+ end;
+ end if [0].pack("l!").bytesize < [nil].pack("p").bytesize
+ # enable only when string size range is smaller than memory space
+
+ def test_uplus_minus
+ str = "foo"
+ assert_not_predicate(str, :frozen?)
+ assert_not_predicate(+str, :frozen?)
+ assert_predicate(-str, :frozen?)
+
+ assert_same(str, +str)
+ assert_not_same(str, -str)
+
+ str = "bar".freeze
+ assert_predicate(str, :frozen?)
+ assert_not_predicate(+str, :frozen?)
+ assert_predicate(-str, :frozen?)
+
+ assert_not_same(str, +str)
+ assert_same(str, -str)
+
+ bar = %w(b a r).join('')
+ assert_same(str, -bar, "uminus deduplicates [Feature #13077]")
+ end
+
+ def test_ord
+ assert_equal(97, "a".ord)
+ assert_equal(97, "abc".ord)
+ assert_equal(0x3042, "\u3042\u3043".ord)
+ assert_raise(ArgumentError) { "".ord }
+ end
+
+ def test_chr
+ assert_equal("a", "abcde".chr)
+ assert_equal("a", "a".chr)
+ assert_equal("\u3042", "\u3042\u3043".chr)
+ assert_equal('', ''.chr)
+ end
+
+ def test_substr_code_range
+ data = "\xff" + "a"*200
+ assert_not_predicate(data, :valid_encoding?)
+ assert_predicate(data[100..-1], :valid_encoding?)
+ end
+end
+
+class TestString2 < TestString
+ def initialize(*args)
+ super
+ @cls = S2
end
end
diff --git a/test/ruby/test_stringchar.rb b/test/ruby/test_stringchar.rb
index b70f171c84..e13beef69c 100644
--- a/test/ruby/test_stringchar.rb
+++ b/test/ruby/test_stringchar.rb
@@ -1,17 +1,18 @@
+# frozen_string_literal: false
require 'test/unit'
class TestStringchar < Test::Unit::TestCase
def test_string
assert_equal("abcd", "abcd")
assert_match(/abcd/, "abcd")
- assert("abcd" === "abcd")
+ assert_operator("abcd", :===, "abcd")
# compile time string concatenation
assert_equal("abcd", "ab" "cd")
assert_equal("22aacd44", "#{22}aa" "cd#{44}")
assert_equal("22aacd445566", "#{22}aa" "cd#{44}" "55" "#{66}")
- assert("abc" !~ /^$/)
- assert("abc\n" !~ /^$/)
- assert("abc" !~ /^d*$/)
+ assert_operator("abc", :!~, /^$/)
+ assert_operator("abc\n", :!~, /^$/)
+ assert_operator("abc", :!~, /^d*$/)
assert_equal(3, ("abc" =~ /d*$/))
assert("" =~ /^$/)
assert("\n" =~ /^$/)
@@ -30,12 +31,12 @@ class TestStringchar < Test::Unit::TestCase
assert(/(\s+\d+){2}/ =~ " 1 2"); assert_equal(" 1 2", $&)
assert(/(?:\s+\d+){2}/ =~ " 1 2"); assert_equal(" 1 2", $&)
- $x = <<END;
+ x = <<END;
ABCD
ABCD
END
- $x.gsub!(/((.|\n)*?)B((.|\n)*?)D/m ,'\1\3')
- assert_equal("AC\nAC\n", $x)
+ x.gsub!(/((.|\n)*?)B((.|\n)*?)D/m ,'\1\3')
+ assert_equal("AC\nAC\n", x)
assert_match(/foo(?=(bar)|(baz))/, "foobar")
assert_match(/foo(?=(bar)|(baz))/, "foobaz")
@@ -56,12 +57,12 @@ END
assert_equal('-', foo * 1)
assert_equal('', foo * 0)
- $x = "a.gif"
- assert_equal("gif", $x.sub(/.*\.([^\.]+)$/, '\1'))
- assert_equal("b.gif", $x.sub(/.*\.([^\.]+)$/, 'b.\1'))
- assert_equal("", $x.sub(/.*\.([^\.]+)$/, '\2'))
- assert_equal("ab", $x.sub(/.*\.([^\.]+)$/, 'a\2b'))
- assert_equal("<a.gif>", $x.sub(/.*\.([^\.]+)$/, '<\&>'))
+ x = "a.gif"
+ assert_equal("gif", x.sub(/.*\.([^\.]+)$/, '\1'))
+ assert_equal("b.gif", x.sub(/.*\.([^\.]+)$/, 'b.\1'))
+ assert_equal("", x.sub(/.*\.([^\.]+)$/, '\2'))
+ assert_equal("ab", x.sub(/.*\.([^\.]+)$/, 'a\2b'))
+ assert_equal("<a.gif>", x.sub(/.*\.([^\.]+)$/, '<\&>'))
end
def test_char
@@ -78,16 +79,16 @@ END
assert_equal("abc", "abcc".squeeze!("a-z"))
assert_equal("ad", "abcd".delete!("bc"))
- $x = "abcdef"
- $y = [ ?a, ?b, ?c, ?d, ?e, ?f ]
- $bad = false
- $x.each_byte {|i|
- if i.chr != $y.shift
- $bad = true
+ x = "abcdef"
+ y = [ ?a, ?b, ?c, ?d, ?e, ?f ]
+ bad = false
+ x.each_byte {|i|
+ if i.chr != y.shift
+ bad = true
break
end
}
- assert(!$bad)
+ assert(!bad)
s = "a string"
s[0..s.size]="another string"
@@ -163,4 +164,19 @@ EOS
s.delete!("a-z")
assert_equal("BB", s)
end
+
+ def test_dump
+ bug3996 = '[ruby-core:32935]'
+ Encoding.list.find_all {|enc| enc.ascii_compatible?}.each do |enc|
+ (0..256).map do |c|
+ begin
+ s = c.chr(enc)
+ rescue RangeError, ArgumentError
+ break
+ else
+ assert_not_match(/\0/, s.dump, bug3996)
+ end
+ end
+ end
+ end
end
diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb
index 49dcdb45b2..0046e9bd04 100644
--- a/test/ruby/test_struct.rb
+++ b/test/ruby/test_struct.rb
@@ -1,10 +1,12 @@
+# -*- coding: us-ascii -*-
+# frozen_string_literal: false
require 'test/unit'
require 'timeout'
-class TestStruct < Test::Unit::TestCase
+module TestStruct
def test_struct
- struct_test = Struct.new("Test", :foo, :bar)
- assert_equal(Struct::Test, struct_test)
+ struct_test = @Struct.new("Test", :foo, :bar)
+ assert_equal(@Struct::Test, struct_test)
test = struct_test.new(1, 2)
assert_equal(1, test.foo)
@@ -27,7 +29,7 @@ class TestStruct < Test::Unit::TestCase
def test_morethan10members
list = %w( a b c d e f g h i j k l m n o p )
until list.empty?
- c = Struct.new(* list.map {|ch| ch.intern }).new
+ c = @Struct.new(* list.map {|ch| ch.intern }).new
list.each do |ch|
c.__send__(ch)
end
@@ -39,7 +41,7 @@ class TestStruct < Test::Unit::TestCase
names = [:a, :b, :c, :d]
1.upto(4) {|n|
fields = names[0, n]
- klass = Struct.new(*fields)
+ klass = @Struct.new(*fields)
o = klass.new(*(0...n).to_a)
fields.each_with_index {|name, i|
assert_equal(i, o[name])
@@ -52,39 +54,28 @@ class TestStruct < Test::Unit::TestCase
end
def test_inherit
- klass = Struct.new(:a)
+ klass = @Struct.new(:a)
klass2 = Class.new(klass)
o = klass2.new(1)
assert_equal(1, o.a)
end
def test_members
- klass = Struct.new(:a)
+ klass = @Struct.new(:a)
o = klass.new(1)
assert_equal([:a], klass.members)
assert_equal([:a], o.members)
end
def test_ref
- klass = Struct.new(:a)
+ klass = @Struct.new(:a)
o = klass.new(1)
assert_equal(1, o[:a])
assert_raise(NameError) { o[:b] }
end
- def test_modify
- klass = Struct.new(:a)
- o = klass.new(1)
- assert_raise(SecurityError) do
- Thread.new do
- $SAFE = 4
- o.a = 2
- end.value
- end
- end
-
def test_set
- klass = Struct.new(:a)
+ klass = @Struct.new(:a)
o = klass.new(1)
o[:a] = 2
assert_equal(2, o[:a])
@@ -92,161 +83,350 @@ class TestStruct < Test::Unit::TestCase
end
def test_struct_new
- assert_raise(NameError) { Struct.new("foo") }
- assert_nothing_raised { Struct.new("Foo") }
- Struct.instance_eval { remove_const(:Foo) }
- assert_nothing_raised { Struct.new(:a) { } }
- assert_raise(RuntimeError) { Struct.new(:a) { raise } }
+ assert_raise(NameError) { @Struct.new("foo") }
+ assert_nothing_raised { @Struct.new("Foo") }
+ @Struct.instance_eval { remove_const(:Foo) }
+ assert_nothing_raised { @Struct.new(:a) { } }
+ assert_raise(RuntimeError) { @Struct.new(:a) { raise } }
assert_equal([:utime, :stime, :cutime, :cstime], Process.times.members)
end
+ def test_struct_new_with_empty_hash
+ assert_equal({:a=>1}, Struct.new(:a, {}).new({:a=>1}).a)
+ end
+
+ def test_struct_new_with_keyword_init
+ @Struct.new("KeywordInitTrue", :a, :b, keyword_init: true)
+ @Struct.new("KeywordInitFalse", :a, :b, keyword_init: false)
+
+ assert_raise(ArgumentError) { @Struct::KeywordInitTrue.new(1, 2) }
+ assert_nothing_raised { @Struct::KeywordInitFalse.new(1, 2) }
+ assert_nothing_raised { @Struct::KeywordInitTrue.new(a: 1, b: 2) }
+ assert_raise(ArgumentError) { @Struct::KeywordInitTrue.new(1, b: 2) }
+ assert_raise(ArgumentError) { @Struct::KeywordInitTrue.new(a: 1, b: 2, c: 3) }
+ 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
+
+ @Struct.instance_eval do
+ remove_const(:KeywordInitTrue)
+ remove_const(:KeywordInitFalse)
+ end
+ end
+
def test_initialize
- klass = Struct.new(:a)
+ klass = @Struct.new(:a)
assert_raise(ArgumentError) { klass.new(1, 2) }
+ klass = @Struct.new(:total) do
+ def initialize(a, b)
+ super(a+b)
+ end
+ end
+ assert_equal 3, klass.new(1,2).total
end
def test_each
- klass = Struct.new(:a, :b)
+ klass = @Struct.new(:a, :b)
o = klass.new(1, 2)
assert_equal([1, 2], o.each.to_a)
end
def test_each_pair
- klass = Struct.new(:a, :b)
+ klass = @Struct.new(:a, :b)
o = klass.new(1, 2)
assert_equal([[:a, 1], [:b, 2]], o.each_pair.to_a)
+ bug7382 = '[ruby-dev:46533]'
+ a = []
+ o.each_pair {|x| a << x}
+ assert_equal([[:a, 1], [:b, 2]], a, bug7382)
end
def test_inspect
- klass = Struct.new(:a)
+ klass = @Struct.new(:a)
o = klass.new(1)
assert_equal("#<struct a=1>", o.inspect)
o.a = o
assert_match(/^#<struct a=#<struct #<.*?>:...>>$/, o.inspect)
- Struct.new("Foo", :a)
- o = Struct::Foo.new(1)
- assert_equal("#<struct Struct::Foo a=1>", o.inspect)
- Struct.instance_eval { remove_const(:Foo) }
+ @Struct.new("Foo", :a)
+ o = @Struct::Foo.new(1)
+ assert_equal("#<struct #@Struct::Foo a=1>", o.inspect)
+ @Struct.instance_eval { remove_const(:Foo) }
- klass = Struct.new(:a, :b)
+ klass = @Struct.new(:a, :b)
o = klass.new(1, 2)
assert_equal("#<struct a=1, b=2>", o.inspect)
- klass = Struct.new(:@a)
+ klass = @Struct.new(:@a)
o = klass.new(1)
+ assert_equal(1, o.__send__(:@a))
assert_equal("#<struct :@a=1>", o.inspect)
+ o.__send__(:"@a=", 2)
+ assert_equal(2, o.__send__(:@a))
+ assert_equal("#<struct :@a=2>", o.inspect)
+ o.__send__("@a=", 3)
+ assert_equal(3, o.__send__(:@a))
+ assert_equal("#<struct :@a=3>", o.inspect)
+
+ methods = klass.instance_methods(false)
+ assert_equal([:@a, :"@a="].sort.inspect, methods.sort.inspect, '[Bug #8756]')
+ assert_include(methods, :@a)
+ assert_include(methods, :"@a=")
end
def test_init_copy
- klass = Struct.new(:a)
+ klass = @Struct.new(:a)
o = klass.new(1)
assert_equal(o, o.dup)
end
def test_aref
- klass = Struct.new(:a)
+ klass = @Struct.new(:a)
o = klass.new(1)
assert_equal(1, o[0])
- assert_raise(IndexError) { o[-2] }
- assert_raise(IndexError) { o[1] }
+ assert_raise_with_message(IndexError, /offset -2\b/) {o[-2]}
+ assert_raise_with_message(IndexError, /offset 1\b/) {o[1]}
+ assert_raise_with_message(NameError, /foo/) {o["foo"]}
+ assert_raise_with_message(NameError, /foo/) {o[:foo]}
end
def test_aset
- klass = Struct.new(:a)
+ klass = @Struct.new(:a)
o = klass.new(1)
o[0] = 2
assert_equal(2, o[:a])
- assert_raise(IndexError) { o[-2] = 3 }
- assert_raise(IndexError) { o[1] = 3 }
+ assert_raise_with_message(IndexError, /offset -2\b/) {o[-2] = 3}
+ assert_raise_with_message(IndexError, /offset 1\b/) {o[1] = 3}
+ assert_raise_with_message(NameError, /foo/) {o["foo"] = 3}
+ assert_raise_with_message(NameError, /foo/) {o[:foo] = 3}
end
def test_values_at
- klass = Struct.new(:a, :b, :c, :d, :e, :f)
+ klass = @Struct.new(:a, :b, :c, :d, :e, :f)
o = klass.new(1, 2, 3, 4, 5, 6)
assert_equal([2, 4, 6], o.values_at(1, 3, 5))
assert_equal([2, 3, 4, 3, 4, 5], o.values_at(1..3, 2...5))
end
def test_select
- klass = Struct.new(:a, :b, :c, :d, :e, :f)
+ klass = @Struct.new(:a, :b, :c, :d, :e, :f)
o = klass.new(1, 2, 3, 4, 5, 6)
assert_equal([1, 3, 5], o.select {|v| v % 2 != 0 })
assert_raise(ArgumentError) { o.select(1) }
end
+ def test_filter
+ klass = @Struct.new(:a, :b, :c, :d, :e, :f)
+ o = klass.new(1, 2, 3, 4, 5, 6)
+ assert_equal([1, 3, 5], o.filter {|v| v % 2 != 0 })
+ assert_raise(ArgumentError) { o.filter(1) }
+ end
+
+ def test_big_struct
+ klass1 = @Struct.new(*('a'..'z').map(&:to_sym))
+ o = klass1.new
+ assert_nil o.z
+ assert_equal(:foo, o.z = :foo)
+ assert_equal(:foo, o.z)
+ assert_equal(:foo, o[25])
+ end
+
+ def test_overridden_aset
+ bug10601 = '[ruby-core:66846] [Bug #10601]: should not be affected by []= method'
+
+ struct = Class.new(Struct.new(*(:a..:z), :result)) do
+ def []=(*args)
+ raise args.inspect
+ end
+ end
+
+ obj = struct.new
+ assert_nothing_raised(RuntimeError, bug10601) do
+ obj.result = 42
+ end
+ assert_equal(42, obj.result, bug10601)
+ end
+
+ def test_overridden_aref
+ bug10601 = '[ruby-core:66846] [Bug #10601]: should not be affected by [] method'
+
+ struct = Class.new(Struct.new(*(:a..:z), :result)) do
+ def [](*args)
+ raise args.inspect
+ end
+ end
+
+ obj = struct.new
+ obj.result = 42
+ result = assert_nothing_raised(RuntimeError, bug10601) do
+ break obj.result
+ end
+ assert_equal(42, result, bug10601)
+ end
+
def test_equal
- klass1 = Struct.new(:a)
- klass2 = Struct.new(:a, :b)
+ klass1 = @Struct.new(:a)
+ klass2 = @Struct.new(:a, :b)
o1 = klass1.new(1)
o2 = klass1.new(1)
o3 = klass2.new(1)
- assert(o1.==(o2))
- assert(o1 != o3)
+ assert_equal(o1, o2)
+ assert_not_equal(o1, o3)
end
def test_hash
- klass = Struct.new(:a)
+ klass = @Struct.new(:a)
o = klass.new(1)
- assert(o.hash.is_a?(Fixnum))
+ assert_kind_of(Integer, o.hash)
end
def test_eql
- klass1 = Struct.new(:a)
- klass2 = Struct.new(:a, :b)
+ klass1 = @Struct.new(:a)
+ klass2 = @Struct.new(:a, :b)
o1 = klass1.new(1)
o2 = klass1.new(1)
o3 = klass2.new(1)
- assert(o1.eql?(o2))
- assert(!(o1.eql?(o3)))
+ assert_operator(o1, :eql?, o2)
+ assert_not_operator(o1, :eql?, o3)
end
def test_size
- klass = Struct.new(:a)
+ klass = @Struct.new(:a)
o = klass.new(1)
assert_equal(1, o.size)
end
def test_error
assert_raise(TypeError){
- Struct.new(0)
+ @Struct.new(0)
}
end
+ def test_redefinition_warning
+ @Struct.new("RedefinitionWarning")
+ e = EnvUtil.verbose_warning do
+ @Struct.new("RedefinitionWarning")
+ end
+ assert_match(/redefining constant #@Struct::RedefinitionWarning/, e)
+ end
+
def test_nonascii
- struct_test = Struct.new("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]')
+ struct_test = @Struct.new("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]')
+ assert_equal("#<struct #@Struct::R\u{e9}sum\u{e9} r\u{e9}sum\u{e9}=42>", a.inspect, '[ruby-core:24849]')
+ e = EnvUtil.verbose_warning do
+ @Struct.new("R\u{e9}sum\u{e9}", :"r\u{e9}sum\u{e9}")
+ end
+ assert_nothing_raised(Encoding::CompatibilityError) do
+ assert_match(/redefining constant #@Struct::R\u{e9}sum\u{e9}/, e)
+ end
+ end
+
+ def test_junk
+ struct_test = @Struct.new("Foo", "a\000")
+ o = struct_test.new(1)
+ assert_equal(1, o.send("a\000"))
+ @Struct.instance_eval { remove_const(:Foo) }
end
def test_comparison_when_recursive
- klass1 = Struct.new(:a, :b, :c)
+ klass1 = @Struct.new(:a, :b, :c)
x = klass1.new(1, 2, nil); x.c = x
y = klass1.new(1, 2, nil); y.c = y
Timeout.timeout(1) {
- assert x == y
- assert x.eql? y
+ assert_equal x, y
+ assert_operator x, :eql?, y
}
z = klass1.new(:something, :other, nil); z.c = z
Timeout.timeout(1) {
- assert x != z
- assert !x.eql?(z)
+ assert_not_equal x, z
+ assert_not_operator x, :eql?, z
}
x.c = y; y.c = x
Timeout.timeout(1) {
- assert x == y
- assert x.eql?(y)
+ assert_equal x, y
+ assert_operator x, :eql?, y
}
x.c = z; z.c = x
Timeout.timeout(1) {
- assert x != z
- assert !x.eql?(z)
+ assert_not_equal x, z
+ assert_not_operator x, :eql?, z
}
end
+
+ def test_to_h
+ klass = @Struct.new(:a, :b, :c, :d, :e, :f)
+ o = klass.new(1, 2, 3, 4, 5, 6)
+ assert_equal({a:1, b:2, c:3, d:4, e:5, f:6}, o.to_h)
+ end
+
+ def test_to_h_block
+ klass = @Struct.new(:a, :b, :c, :d, :e, :f)
+ o = klass.new(1, 2, 3, 4, 5, 6)
+ assert_equal({"a" => 1, "b" => 4, "c" => 9, "d" => 16, "e" => 25, "f" => 36},
+ o.to_h {|k, v| [k.to_s, v*v]})
+ end
+
+ def test_question_mark_in_member
+ klass = @Struct.new(:a, :b?)
+ x = Object.new
+ o = klass.new("test", x)
+ assert_same(x, o.b?)
+ o.send("b?=", 42)
+ assert_equal(42, o.b?)
+ end
+
+ def test_bang_mark_in_member
+ klass = @Struct.new(:a, :b!)
+ x = Object.new
+ o = klass.new("test", x)
+ assert_same(x, o.b!)
+ o.send("b!=", 42)
+ assert_equal(42, o.b!)
+ end
+
+ def test_setter_method_returns_value
+ klass = @Struct.new(:a)
+ x = klass.new
+ assert_equal "[Bug #9353]", x.send(:a=, "[Bug #9353]")
+ end
+
+ def test_dig
+ klass = @Struct.new(:a)
+ o = klass.new(klass.new({b: [1, 2, 3]}))
+ assert_equal(1, o.dig(:a, :a, :b, 0))
+ assert_nil(o.dig(:b, 0))
+ end
+
+ def test_new_dupilicate
+ bug12291 = '[ruby-core:74971] [Bug #12291]'
+ assert_raise_with_message(ArgumentError, /duplicate member/, bug12291) {
+ @Struct.new(:a, :a)
+ }
+ end
+
+ class TopStruct < Test::Unit::TestCase
+ include TestStruct
+
+ def initialize(*)
+ super
+ @Struct = Struct
+ end
+ end
+
+ class SubStruct < Test::Unit::TestCase
+ include TestStruct
+ SubStruct = Class.new(Struct)
+
+ def initialize(*)
+ super
+ @Struct = SubStruct
+ end
+ end
end
diff --git a/test/ruby/test_super.rb b/test/ruby/test_super.rb
index 8de1e2fa7e..bb78ab516f 100644
--- a/test/ruby/test_super.rb
+++ b/test/ruby/test_super.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestSuper < Test::Unit::TestCase
@@ -6,6 +7,7 @@ class TestSuper < Test::Unit::TestCase
def double(a, b) [a,b] end
def array(*a) a end
def optional(a = 0) a end
+ def keyword(**a) a end
end
class Single1 < Base
def single(*) super end
@@ -49,6 +51,18 @@ class TestSuper < Test::Unit::TestCase
class Optional5 < Base
def array(a = 1, b = 2, *) super end
end
+ class Keyword1 < Base
+ def keyword(foo: "keyword1") super end
+ end
+ class Keyword2 < Base
+ def keyword(foo: "keyword2")
+ foo = "changed1"
+ x = super
+ foo = "changed2"
+ y = super
+ [x, y]
+ end
+ end
def test_single1
assert_equal(1, Single1.new.single(1))
@@ -88,11 +102,11 @@ class TestSuper < Test::Unit::TestCase
def test_optional2
assert_raise(ArgumentError) do
# call Base#optional with 2 arguments; the 2nd arg is supplied
- assert_equal(9, Optional2.new.optional(9))
+ Optional2.new.optional(9)
end
assert_raise(ArgumentError) do
# call Base#optional with 2 arguments
- assert_equal(9, Optional2.new.optional(9, 2))
+ Optional2.new.optional(9, 2)
end
end
def test_optional3
@@ -111,6 +125,14 @@ class TestSuper < Test::Unit::TestCase
assert_equal([9, 8], Optional5.new.array(9, 8))
assert_equal([9, 8, 7], Optional5.new.array(9, 8, 7))
end
+ def test_keyword1
+ assert_equal({foo: "keyword1"}, Keyword1.new.keyword)
+ bug8008 = '[ruby-core:53114] [Bug #8008]'
+ assert_equal({foo: bug8008}, Keyword1.new.keyword(foo: bug8008))
+ end
+ def test_keyword2
+ assert_equal([{foo: "changed1"}, {foo: "changed2"}], Keyword2.new.keyword)
+ end
class A
def tt(aa)
@@ -130,13 +152,412 @@ class TestSuper < Test::Unit::TestCase
a = A.new
a.uu(12)
assert_equal("A#tt", a.tt(12), "[ruby-core:3856]")
- e = assert_raise(RuntimeError, "[ruby-core:24244]") {
+ assert_raise_with_message(RuntimeError, /implicit argument passing of super from method defined by define_method/, "[ruby-core:24244]") {
lambda {
- Class.new do
- define_method(:a) {super}.call
- end
+ Class.new {
+ define_method(:a) {super}
+ }.new.a
}.call
}
- assert_match(/implicit argument passing of super from method defined by define_method/, e.message)
+ end
+
+ class SubSeq
+ def initialize
+ @first=11
+ @first or fail
+ end
+
+ def subseq
+ @first or fail
+ end
+ end
+
+ class Indexed
+ def subseq
+ SubSeq.new
+ end
+ end
+
+ Overlaid = proc do
+ class << self
+ def subseq
+ super.instance_eval(& Overlaid)
+ end
+ end
+ end
+
+ def test_overlaid
+ assert_nothing_raised('[ruby-dev:40959]') do
+ overlaid = proc do |obj|
+ def obj.reverse
+ super
+ end
+ end
+ overlaid.call(str = "123")
+ overlaid.call([1,2,3])
+ str.reverse
+ end
+
+ assert_nothing_raised('[ruby-core:27230]') do
+ mid=Indexed.new
+ mid.instance_eval(&Overlaid)
+ mid.subseq
+ mid.subseq
+ end
+ end
+
+ module DoubleInclude
+ class Base
+ def foo
+ [:Base]
+ end
+ end
+
+ module Override
+ def foo
+ super << :Override
+ end
+ end
+
+ class A < Base
+ end
+
+ class B < A
+ end
+
+ B.send(:include, Override)
+ A.send(:include, Override)
+ end
+
+ def test_double_include
+ assert_equal([:Base, :Override, :Override], DoubleInclude::B.new.foo, "[Bug #3351]")
+ end
+
+ module DoubleInclude2
+ class Base
+ def foo
+ [:Base]
+ end
+ end
+
+ module Override
+ def foo
+ super << :Override
+ end
+ end
+
+ class A < Base
+ def foo
+ super << :A
+ end
+ end
+
+ class B < A
+ def foo
+ super << :B
+ end
+ end
+
+ B.send(:include, Override)
+ A.send(:include, Override)
+ end
+
+ def test_double_include2
+ assert_equal([:Base, :Override, :A, :Override, :B],
+ DoubleInclude2::B.new.foo)
+ end
+
+ def test_super_in_instance_eval
+ super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
+ def foo
+ return [:super, self]
+ end
+ }
+ sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
+ def foo
+ x = Object.new
+ x.instance_eval do
+ super()
+ end
+ end
+ }
+ obj = sub_class.new
+ assert_raise_with_message(TypeError, /Sub\u{30af 30e9 30b9}/) do
+ obj.foo
+ end
+ end
+
+ def test_super_in_instance_eval_with_define_method
+ super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
+ def foo
+ return [:super, self]
+ end
+ }
+ sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
+ define_method(:foo) do
+ x = Object.new
+ x.instance_eval do
+ super()
+ end
+ end
+ }
+ obj = sub_class.new
+ assert_raise_with_message(TypeError, /Sub\u{30af 30e9 30b9}/) do
+ obj.foo
+ end
+ end
+
+ def test_super_in_orphan_block
+ super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
+ def foo
+ return [:super, self]
+ end
+ }
+ sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
+ def foo
+ lambda { super() }
+ end
+ }
+ obj = sub_class.new
+ assert_equal([:super, obj], obj.foo.call)
+ end
+
+ def test_super_in_orphan_block_with_instance_eval
+ super_class = EnvUtil.labeled_class("Super\u{30af 30e9 30b9}") {
+ def foo
+ return [:super, self]
+ end
+ }
+ sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
+ def foo
+ x = Object.new
+ x.instance_eval do
+ lambda { super() }
+ end
+ end
+ }
+ obj = sub_class.new
+ assert_raise_with_message(TypeError, /Sub\u{30af 30e9 30b9}/) do
+ obj.foo.call
+ end
+ end
+
+ def test_yielding_super
+ a = Class.new { def yielder; yield; end }
+ x = Class.new { define_singleton_method(:hello) { 'hi' } }
+ y = Class.new(x) {
+ define_singleton_method(:hello) {
+ m = a.new
+ m.yielder { super() }
+ }
+ }
+ assert_equal 'hi', y.hello
+ end
+
+ def test_super_in_thread
+ hoge = Class.new {
+ def bar; 'hoge'; end
+ }
+ foo = Class.new(hoge) {
+ def bar; Thread.new { super }.join.value; end
+ }
+
+ assert_equal 'hoge', foo.new.bar
+ end
+
+ def assert_super_in_block(type)
+ bug7064 = '[ruby-core:47680]'
+ assert_normal_exit "#{type} {super}", bug7064
+ end
+
+ def test_super_in_at_exit
+ assert_super_in_block("at_exit")
+ end
+ def test_super_in_END
+ assert_super_in_block("END")
+ end
+
+ def test_super_in_BEGIN
+ assert_super_in_block("BEGIN")
+ end
+
+ class X
+ def foo(*args)
+ args
+ end
+ end
+
+ class Y < X
+ define_method(:foo) do |*args|
+ super(*args)
+ end
+ end
+
+ def test_super_splat
+ # [ruby-list:49575]
+ y = Y.new
+ assert_equal([1, 2], y.foo(1, 2))
+ assert_equal([1, false], y.foo(1, false))
+ assert_equal([1, 2, 3, 4, 5], y.foo(1, 2, 3, 4, 5))
+ assert_equal([false, true], y.foo(false, true))
+ assert_equal([false, false], y.foo(false, false))
+ assert_equal([1, 2, 3, false, 5], y.foo(1, 2, 3, false, 5))
+ end
+
+ def test_missing_super
+ o = Class.new {def foo; super; end}.new
+ e = assert_raise(NoMethodError) {o.foo}
+ assert_same(o, e.receiver)
+ assert_equal(:foo, e.name)
+ end
+
+ def test_missing_super_in_method_module
+ bug9315 = '[ruby-core:59358] [Bug #9315]'
+ a = Module.new do
+ def foo
+ super
+ end
+ end
+ b = Class.new do
+ include a
+ end
+ assert_raise(NoMethodError, bug9315) do
+ b.new.method(:foo).call
+ end
+ end
+
+ def test_module_super_in_method_module
+ bug9315 = '[ruby-core:59589] [Bug #9315]'
+ a = Module.new do
+ def foo
+ super
+ end
+ end
+ c = Class.new do
+ def foo
+ :ok
+ end
+ end
+ o = c.new.extend(a)
+ assert_nothing_raised(NoMethodError, bug9315) do
+ assert_equal(:ok, o.method(:foo).call, bug9315)
+ end
+ end
+
+ def test_missing_super_in_module_unbound_method
+ bug9377 = '[ruby-core:59619] [Bug #9377]'
+
+ a = Module.new do
+ def foo; super end
+ end
+
+ m = a.instance_method(:foo).bind(Object.new)
+ assert_raise(NoMethodError, bug9377) do
+ m.call
+ end
+ end
+
+ def test_super_in_module_unbound_method
+ bug9721 = '[ruby-core:61936] [Bug #9721]'
+
+ a = Module.new do
+ def foo(result)
+ result << "A"
+ end
+ end
+
+ b = Module.new do
+ def foo(result)
+ result << "B"
+ super
+ end
+ end
+
+ um = b.instance_method(:foo)
+
+ m = um.bind(Object.new.extend(a))
+ result = []
+ assert_nothing_raised(NoMethodError, bug9721) do
+ m.call(result)
+ end
+ assert_equal(%w[B A], result, bug9721)
+
+ bug9740 = '[ruby-core:62017] [Bug #9740]'
+
+ b.module_eval do
+ define_method(:foo) do |res|
+ um.bind(self).call(res)
+ end
+ end
+
+ result.clear
+ o = Object.new.extend(a).extend(b)
+ assert_nothing_raised(NoMethodError, SystemStackError, bug9740) do
+ o.foo(result)
+ end
+ assert_equal(%w[B A], result, bug9721)
+ end
+
+ def test_from_eval
+ bug10263 = '[ruby-core:65122] [Bug #10263a]'
+ a = Class.new do
+ def foo
+ "A"
+ end
+ end
+ b = Class.new(a) do
+ def foo
+ binding.eval("super")
+ end
+ end
+ assert_equal("A", b.new.foo, bug10263)
+ end
+
+ def test_super_with_block
+ a = Class.new do
+ def foo
+ yield
+ end
+ end
+
+ b = Class.new(a) do
+ def foo
+ super{
+ "b"
+ }
+ end
+ end
+
+ assert_equal "b", b.new.foo{"c"}
+ end
+
+ def test_public_zsuper_with_prepend
+ bug12876 = '[ruby-core:77784] [Bug #12876]'
+ m = EnvUtil.labeled_module("M")
+ c = EnvUtil.labeled_class("C") {prepend m; public :initialize}
+ o = assert_nothing_raised(Timeout::Error, bug12876) {
+ Timeout.timeout(3) {c.new}
+ }
+ assert_instance_of(c, o)
+ m.module_eval {def initialize; raise "exception in M"; end}
+ assert_raise_with_message(RuntimeError, "exception in M") {
+ c.new
+ }
+ end
+
+ class TestFor_super_with_modified_rest_parameter_base
+ def foo *args
+ args
+ end
+ end
+
+ class TestFor_super_with_modified_rest_parameter < TestFor_super_with_modified_rest_parameter_base
+ def foo *args
+ args = 13
+ super
+ end
+ end
+ def test_super_with_modified_rest_parameter
+ assert_equal [13], TestFor_super_with_modified_rest_parameter.new.foo
end
end
diff --git a/test/ruby/test_symbol.rb b/test/ruby/test_symbol.rb
index 9061f191bf..13d7cc9d57 100644
--- a/test/ruby/test_symbol.rb
+++ b/test/ruby/test_symbol.rb
@@ -1,23 +1,41 @@
+# frozen_string_literal: false
require 'test/unit'
class TestSymbol < Test::Unit::TestCase
# [ruby-core:3573]
- def assert_eval_inspected(sym)
+ def assert_eval_inspected(sym, valid = true)
n = sym.inspect
+ if valid
+ bug5136 = '[ruby-dev:44314]'
+ assert_not_match(/\A:"/, n, bug5136)
+ end
assert_nothing_raised(SyntaxError) {assert_equal(sym, eval(n))}
end
+ def test_intern
+ assert_equal(':""', ''.intern.inspect)
+ assert_equal(':$foo', '$foo'.intern.inspect)
+ assert_equal(':"!foo"', '!foo'.intern.inspect)
+ assert_equal(':"foo=="', "foo==".intern.inspect)
+ end
+
+ def test_all_symbols
+ x = Symbol.all_symbols
+ assert_kind_of(Array, x)
+ assert_empty(x.reject {|s| s.is_a?(Symbol) })
+ end
+
def test_inspect_invalid
# 2) Symbol#inspect sometimes returns invalid symbol representations:
assert_eval_inspected(:"!")
- assert_eval_inspected(:"=")
- assert_eval_inspected(:"0")
+ assert_eval_inspected(:"=", false)
+ assert_eval_inspected(:"0", false)
assert_eval_inspected(:"$1")
- assert_eval_inspected(:"@1")
- assert_eval_inspected(:"@@1")
- assert_eval_inspected(:"@")
- assert_eval_inspected(:"@@")
+ assert_eval_inspected(:"@1", false)
+ assert_eval_inspected(:"@@1", false)
+ assert_eval_inspected(:"@", false)
+ assert_eval_inspected(:"@@", false)
end
def assert_inspect_evaled(n)
@@ -29,7 +47,7 @@ class TestSymbol < Test::Unit::TestCase
assert_inspect_evaled(':foo')
assert_inspect_evaled(':foo!')
assert_inspect_evaled(':bar?')
- assert_inspect_evaled(':<<')
+ assert_inspect_evaled(":<<")
assert_inspect_evaled(':>>')
assert_inspect_evaled(':<=')
assert_inspect_evaled(':>=')
@@ -60,11 +78,10 @@ class TestSymbol < Test::Unit::TestCase
def test_inspect_dollar
# 4) :$- always treats next character literally:
- sym = "$-".intern
- assert_nothing_raised(SyntaxError) {assert_equal(sym, eval(':$-'))}
- assert_nothing_raised(SyntaxError) {assert_equal(sym, eval(":$-\n"))}
- assert_nothing_raised(SyntaxError) {assert_equal(sym, eval(":$- "))}
- assert_nothing_raised(SyntaxError) {assert_equal(sym, eval(":$-#"))}
+ assert_raise(SyntaxError) {eval ':$-'}
+ assert_raise(SyntaxError) {eval ":$-\n"}
+ assert_raise(SyntaxError) {eval ":$- "}
+ assert_raise(SyntaxError) {eval ":$-#"}
assert_raise(SyntaxError) {eval ':$-('}
end
@@ -75,6 +92,19 @@ class TestSymbol < Test::Unit::TestCase
assert_inspect_evaled(':$1')
end
+ def test_inspect
+ valid = %W{$a @a @@a < << <= <=> > >> >= =~ == === * ** + +@ - -@
+ | ^ & / % ~ \` [] []= ! != !~ a a? a! a= A A? A! A=}
+ valid.each do |sym|
+ assert_equal(':' + sym, sym.intern.inspect)
+ end
+
+ invalid = %w{$a? $a! $a= @a? @a! @a= @@a? @@a! @@a= =}
+ invalid.each do |sym|
+ assert_equal(':"' + sym + '"', sym.intern.inspect)
+ end
+ end
+
def test_to_proc
assert_equal %w(1 2 3), (1..3).map(&:to_s)
[
@@ -90,6 +120,129 @@ class TestSymbol < Test::Unit::TestCase
end
end
+ def test_to_proc_yield
+ assert_ruby_status([], "#{<<-"begin;"}\n#{<<-"end;"}", timeout: 5.0)
+ begin;
+ GC.stress = true
+ true.tap(&:itself)
+ end;
+ end
+
+ def test_to_proc_new_proc
+ assert_ruby_status([], "#{<<-"begin;"}\n#{<<-"end;"}", timeout: 5.0)
+ begin;
+ GC.stress = true
+ 2.times {Proc.new(&:itself)}
+ end;
+ end
+
+ def test_to_proc_no_method
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}", timeout: 5.0)
+ begin;
+ bug11566 = '[ruby-core:70980] [Bug #11566]'
+ assert_raise(NoMethodError, bug11566) {Proc.new(&:foo).(1)}
+ assert_raise(NoMethodError, bug11566) {:foo.to_proc.(1)}
+ end;
+ end
+
+ def test_to_proc_arg
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}", timeout: 5.0)
+ begin;
+ def (obj = Object.new).proc(&b) b; end
+ assert_same(:itself.to_proc, obj.proc(&:itself))
+ end;
+ end
+
+ def test_to_proc_call_with_symbol_proc
+ first = 1
+ bug11594 = "[ruby-core:71088] [Bug #11594] corrupted the first local variable"
+ # symbol which does not have a Proc
+ ->(&blk) {}.call(&:test_to_proc_call_with_symbol_proc)
+ assert_equal(1, first, bug11594)
+ end
+
+ class TestToPRocArgWithRefinements; end
+ def _test_to_proc_arg_with_refinements_call(&block)
+ block.call TestToPRocArgWithRefinements.new
+ end
+ using Module.new {
+ refine TestToPRocArgWithRefinements do
+ def hoge
+ :hoge
+ end
+ end
+ }
+ def test_to_proc_arg_with_refinements
+ assert_equal(:hoge, _test_to_proc_arg_with_refinements_call(&:hoge))
+ end
+
+ def self._test_to_proc_arg_with_refinements_call(&block)
+ block.call TestToPRocArgWithRefinements.new
+ end
+ _test_to_proc_arg_with_refinements_call(&:hoge)
+ using Module.new {
+ refine TestToPRocArgWithRefinements do
+ def hoge
+ :hogehoge
+ end
+ end
+ }
+ def test_to_proc_arg_with_refinements_override
+ assert_equal(:hogehoge, _test_to_proc_arg_with_refinements_call(&:hoge))
+ end
+
+ def test_to_proc_arg_with_refinements_undefined
+ assert_raise(NoMethodError) do
+ _test_to_proc_arg_with_refinements_call(&:foo)
+ end
+ end
+
+ private def return_from_proc
+ Proc.new { return 1 }.tap(&:call)
+ end
+
+ def test_return_from_symbol_proc
+ bug12462 = '[ruby-core:75856] [Bug #12462]'
+ assert_equal(1, return_from_proc, bug12462)
+ end
+
+ def test_to_proc_for_hash_each
+ bug11830 = '[ruby-core:72205] [Bug #11830]'
+ assert_normal_exit("#{<<-"begin;"}\n#{<<-'end;'}", bug11830)
+ begin;
+ {}.each(&:destroy)
+ end;
+ end
+
+ def test_to_proc_iseq
+ assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}", timeout: 5)
+ begin;
+ bug11845 = '[ruby-core:72381] [Bug #11845]'
+ assert_nil(:class.to_proc.source_location, bug11845)
+ assert_equal([[: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)
+ end;
+ end
+
+ def test_to_proc_binding
+ assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}", timeout: 5)
+ begin;
+ bug12137 = '[ruby-core:74100] [Bug #12137]'
+ assert_raise(ArgumentError, bug12137) {
+ :succ.to_proc.binding
+ }
+ end;
+ end
+
+ def test_to_proc_instance_exec
+ bug = '[ruby-core:78839] [Bug #13074] should evaluate on the argument'
+ assert_equal(2, BasicObject.new.instance_exec(1, &:succ), bug)
+ assert_equal(3, BasicObject.new.instance_exec(1, 2, &:+), bug)
+ end
+
def test_call
o = Object.new
def o.foo(x, y); x + y; end
@@ -98,6 +251,62 @@ class TestSymbol < Test::Unit::TestCase
assert_raise(ArgumentError) { :foo.to_proc.call }
end
+ def m_block_given?
+ block_given?
+ end
+
+ def m2_block_given?(m = nil)
+ if m
+ [block_given?, m.call(self)]
+ else
+ block_given?
+ end
+ end
+
+ def test_block_given_to_proc
+ bug8531 = '[Bug #8531]'
+ m = :m_block_given?.to_proc
+ assert(!m.call(self), "#{bug8531} without block")
+ assert(m.call(self) {}, "#{bug8531} with block")
+ assert(!m.call(self), "#{bug8531} without block second")
+ end
+
+ def test_block_persist_between_calls
+ bug8531 = '[Bug #8531]'
+ m2 = :m2_block_given?.to_proc
+ assert_equal([true, false], m2.call(self, m2) {}, "#{bug8531} nested with block")
+ assert_equal([false, false], m2.call(self, m2), "#{bug8531} nested without block")
+ end
+
+ def test_block_curry_proc
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ b = proc { true }.curry
+ assert(b.call, "without block")
+ assert(b.call { |o| o.to_s }, "with block")
+ assert(b.call(&:to_s), "with sym block")
+ end;
+ end
+
+ def test_block_curry_lambda
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ b = lambda { true }.curry
+ assert(b.call, "without block")
+ assert(b.call { |o| o.to_s }, "with block")
+ assert(b.call(&:to_s), "with sym block")
+ end;
+ end
+
+ def test_block_method_to_proc
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ b = method(:tap).to_proc
+ assert(b.call { |o| o.to_s }, "with block")
+ assert(b.call(&:to_s), "with sym block")
+ end;
+ end
+
def test_succ
assert_equal(:fop, :foo.succ)
end
@@ -113,7 +322,19 @@ class TestSymbol < Test::Unit::TestCase
assert_equal(0, :FoO.casecmp(:fOO))
assert_equal(1, :FoO.casecmp(:BaR))
assert_equal(-1, :baR.casecmp(:FoO))
+
assert_nil(:foo.casecmp("foo"))
+ assert_nil(:foo.casecmp(Object.new))
+ end
+
+ def test_casecmp?
+ assert_equal(true, :FoO.casecmp?(:fOO))
+ assert_equal(false, :FoO.casecmp?(:BaR))
+ assert_equal(false, :baR.casecmp?(:FoO))
+ assert_equal(true, :äöü.casecmp?(:ÄÖÜ))
+
+ assert_nil(:foo.casecmp?("foo"))
+ assert_nil(:foo.casecmp?(Object.new))
end
def test_length
@@ -133,7 +354,73 @@ class TestSymbol < Test::Unit::TestCase
assert_equal(:fOo, :FoO.swapcase)
end
- def test_symbol_poped
+ def test_MATCH # '=~'
+ assert_equal(10, :"FeeFieFoo-Fum" =~ /Fum$/)
+ assert_equal(nil, "FeeFieFoo-Fum" =~ /FUM$/)
+
+ o = Object.new
+ def o.=~(x); x + "bar"; end
+ assert_equal("foobar", :"foo" =~ o)
+
+ assert_raise(TypeError) { :"foo" =~ "foo" }
+ end
+
+ def test_match_method
+ assert_equal("bar", :"foobarbaz".match(/bar/).to_s)
+
+ o = Regexp.new('foo')
+ def o.match(x, y, z); x + y + z; end
+ assert_equal("foobarbaz", :"foo".match(o, "bar", "baz"))
+ x = nil
+ :"foo".match(o, "bar", "baz") {|y| x = y }
+ assert_equal("foobarbaz", x)
+
+ assert_raise(ArgumentError) { :"foo".match }
+ end
+
+ def test_match_p_regexp
+ /backref/ =~ 'backref'
+ # must match here, but not in a separate method, e.g., assert_send,
+ # to check if $~ is affected or not.
+ assert_equal(true, "".match?(//))
+ assert_equal(true, :abc.match?(/.../))
+ assert_equal(true, 'abc'.match?(/b/))
+ assert_equal(true, 'abc'.match?(/b/, 1))
+ assert_equal(true, 'abc'.match?(/../, 1))
+ assert_equal(true, 'abc'.match?(/../, -2))
+ assert_equal(false, 'abc'.match?(/../, -4))
+ assert_equal(false, 'abc'.match?(/../, 4))
+ assert_equal(true, ("\u3042" + '\x').match?(/../, 1))
+ assert_equal(true, ''.match?(/\z/))
+ assert_equal(true, 'abc'.match?(/\z/))
+ assert_equal(true, 'Ruby'.match?(/R.../))
+ assert_equal(false, 'Ruby'.match?(/R.../, 1))
+ assert_equal(false, 'Ruby'.match?(/P.../))
+ assert_equal('backref', $&)
+ end
+
+ def test_match_p_string
+ /backref/ =~ 'backref'
+ # must match here, but not in a separate method, e.g., assert_send,
+ # to check if $~ is affected or not.
+ assert_equal(true, "".match?(''))
+ assert_equal(true, :abc.match?('...'))
+ assert_equal(true, 'abc'.match?('b'))
+ assert_equal(true, 'abc'.match?('b', 1))
+ assert_equal(true, 'abc'.match?('..', 1))
+ assert_equal(true, 'abc'.match?('..', -2))
+ assert_equal(false, 'abc'.match?('..', -4))
+ assert_equal(false, 'abc'.match?('..', 4))
+ assert_equal(true, ("\u3042" + '\x').match?('..', 1))
+ assert_equal(true, ''.match?('\z'))
+ assert_equal(true, 'abc'.match?('\z'))
+ assert_equal(true, 'Ruby'.match?('R...'))
+ assert_equal(false, 'Ruby'.match?('R...', 1))
+ assert_equal(false, 'Ruby'.match?('P...'))
+ assert_equal('backref', $&)
+ end
+
+ def test_symbol_popped
assert_nothing_raised { eval('a = 1; :"#{ a }"; 1') }
end
@@ -144,4 +431,136 @@ class TestSymbol < Test::Unit::TestCase
assert_equal(':"\\u3042\\u3044\\u3046"', "\u3042\u3044\u3046".encode(e).to_sym.inspect)
end
end
+
+ def test_symbol_encoding
+ assert_equal(Encoding::US_ASCII, "$-A".force_encoding("iso-8859-15").intern.encoding)
+ assert_equal(Encoding::US_ASCII, "foobar~!".force_encoding("iso-8859-15").intern.encoding)
+ assert_equal(Encoding::UTF_8, "\u{2192}".intern.encoding)
+ assert_raise_with_message(EncodingError, /\\xb0/i) {"\xb0a".force_encoding("utf-8").intern}
+ end
+
+ def test_singleton_method
+ assert_raise(TypeError) { a = :foo; def a.foo; end }
+ end
+
+ SymbolsForEval = [
+ :foo,
+ "dynsym_#{Random.rand(10000)}_#{Time.now}".to_sym
+ ]
+
+ def test_instance_eval
+ bug11086 = '[ruby-core:68961] [Bug #11086]'
+ SymbolsForEval.each do |sym|
+ assert_nothing_raised(TypeError, sym, bug11086) {
+ sym.instance_eval {}
+ }
+ assert_raise(TypeError, sym, bug11086) {
+ sym.instance_eval {def foo; end}
+ }
+ end
+ end
+
+ def test_instance_exec
+ bug11086 = '[ruby-core:68961] [Bug #11086]'
+ SymbolsForEval.each do |sym|
+ assert_nothing_raised(TypeError, sym, bug11086) {
+ sym.instance_exec {}
+ }
+ assert_raise(TypeError, sym, bug11086) {
+ sym.instance_exec {def foo; end}
+ }
+ end
+ end
+
+ def test_frozen_symbol
+ assert_equal(true, :foo.frozen?)
+ assert_equal(true, :each.frozen?)
+ assert_equal(true, :+.frozen?)
+ assert_equal(true, "foo#{Time.now.to_i}".to_sym.frozen?)
+ assert_equal(true, :foo.to_sym.frozen?)
+ end
+
+ def test_symbol_gc_1
+ assert_normal_exit('".".intern;GC.start(immediate_sweep:false);eval %[GC.start;".".intern]',
+ '',
+ child_env: '--disable-gems')
+ assert_normal_exit('".".intern;GC.start(immediate_sweep:false);eval %[GC.start;:"."]',
+ '',
+ child_env: '--disable-gems')
+ assert_normal_exit('".".intern;GC.start(immediate_sweep:false);eval %[GC.start;%i"."]',
+ '',
+ child_env: '--disable-gems')
+ assert_normal_exit('tap{".".intern};GC.start(immediate_sweep:false);' +
+ 'eval %[syms=Symbol.all_symbols;GC.start;syms.each(&:to_sym)]',
+ '',
+ child_env: '--disable-gems')
+ end
+
+ def test_dynamic_attrset_id
+ bug10259 = '[ruby-dev:48559] [Bug #10259]'
+ class << (obj = Object.new)
+ attr_writer :unagi
+ end
+ assert_nothing_raised(NoMethodError, bug10259) {obj.send("unagi=".intern, 1)}
+ end
+
+ def test_symbol_fstr_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)
+ begin;
+ 200_000.times { |i| (i + 200_000).to_s.to_sym }
+ end;
+ end
+
+ def test_hash_redefinition
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ bug11035 = '[ruby-core:68767] [Bug #11035]'
+ class Symbol
+ def hash
+ raise
+ end
+ end
+
+ h = {}
+ assert_nothing_raised(RuntimeError, bug11035) {
+ h[:foo] = 1
+ }
+ assert_nothing_raised(RuntimeError, bug11035) {
+ h['bar'.to_sym] = 2
+ }
+ end;
+ end
+
+ def test_not_freeze
+ bug11721 = '[ruby-core:71611] [Bug #11721]'
+ str = "\u{1f363}".taint
+ assert_not_predicate(str, :frozen?)
+ assert_equal str, str.to_sym.to_s
+ assert_not_predicate(str, :frozen?, bug11721)
+ end
+
+ def test_hash_nondeterministic
+ ruby = EnvUtil.rubybin
+ assert_not_equal :foo.hash, `#{ruby} -e 'puts :foo.hash'`.to_i,
+ '[ruby-core:80430] [Bug #13376]'
+
+ sym = "dynsym_#{Random.rand(10000)}_#{Time.now}"
+ assert_not_equal sym.to_sym.hash,
+ `#{ruby} -e 'puts #{sym.inspect}.to_sym.hash'`.to_i
+ end
+
+ def test_eq_can_be_redefined
+ assert_in_out_err([], <<-RUBY, ["foo"], [])
+ class Symbol
+ remove_method :==
+ def ==(obj)
+ "foo"
+ end
+ end
+
+ puts :a == :a
+ RUBY
+ end
end
diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb
index e25ad75f7a..ab462bddf4 100644
--- a/test/ruby/test_syntax.rb
+++ b/test/ruby/test_syntax.rb
@@ -1,22 +1,1361 @@
+# frozen_string_literal: false
require 'test/unit'
class TestSyntax < Test::Unit::TestCase
- def valid_syntax?(code, fname)
- code = code.dup.force_encoding("ascii-8bit")
- code.sub!(/\A(?:\xef\xbb\xbf)?(\s*\#.*$)*(\n)?/n) {
- "#$&#{"\n" if $1 && !$2}BEGIN{throw tag, :ok}\n"
+ using Module.new {
+ refine(Object) do
+ def `(s) #`
+ s
+ end
+ end
+ }
+
+ def assert_syntax_files(test)
+ srcdir = File.expand_path("../../..", __FILE__)
+ srcdir = File.join(srcdir, test)
+ assert_separately(%W[--disable-gem - #{srcdir}],
+ __FILE__, __LINE__, <<-'eom', timeout: Float::INFINITY)
+ dir = ARGV.shift
+ for script in Dir["#{dir}/**/*.rb"].sort
+ assert_valid_syntax(IO::read(script), script)
+ end
+ eom
+ end
+
+ def test_syntax_lib; assert_syntax_files("lib"); end
+ def test_syntax_sample; assert_syntax_files("sample"); end
+ def test_syntax_ext; assert_syntax_files("ext"); end
+ def test_syntax_test; assert_syntax_files("test"); end
+
+ def test_defined_empty_argument
+ bug8220 = '[ruby-core:53999] [Bug #8220]'
+ assert_ruby_status(%w[--disable-gem], 'puts defined? ()', bug8220)
+ end
+
+ def test_must_ascii_compatible
+ require 'tempfile'
+ f = Tempfile.new("must_ac_")
+ Encoding.list.each do |enc|
+ next unless enc.ascii_compatible?
+ make_tmpsrc(f, "# -*- coding: #{enc.name} -*-")
+ assert_nothing_raised(ArgumentError, enc.name) {load(f.path)}
+ end
+ Encoding.list.each do |enc|
+ next if enc.ascii_compatible?
+ make_tmpsrc(f, "# -*- coding: #{enc.name} -*-")
+ assert_raise(ArgumentError, enc.name) {load(f.path)}
+ end
+ ensure
+ f&.close!
+ end
+
+ def test_script_lines
+ require 'tempfile'
+ f = Tempfile.new("bug4361_")
+ bug4361 = '[ruby-dev:43168]'
+ with_script_lines do |debug_lines|
+ Encoding.list.each do |enc|
+ next unless enc.ascii_compatible?
+ make_tmpsrc(f, "# -*- coding: #{enc.name} -*-\n#----------------")
+ load(f.path)
+ assert_equal([f.path], debug_lines.keys)
+ assert_equal([enc, enc], debug_lines[f.path].map(&:encoding), bug4361)
+ end
+ end
+ ensure
+ f&.close!
+ end
+
+ def test_newline_in_block_parameters
+ bug = '[ruby-dev:45292]'
+ ["", "a", "a, b"].product(["", ";x", [";", "x"]]) do |params|
+ params = ["|", *params, "|"].join("\n")
+ assert_valid_syntax("1.times{#{params}}", __FILE__, "#{bug} #{params.inspect}")
+ end
+ end
+
+ tap do |_,
+ bug6115 = '[ruby-dev:45308]',
+ blockcall = '["elem"].each_with_object [] do end',
+ methods = [['map', 'no'], ['inject([])', 'with']],
+ blocks = [['do end', 'do'], ['{}', 'brace']],
+ *|
+ [%w'. dot', %w':: colon'].product(methods, blocks) do |(c, n1), (m, n2), (b, n3)|
+ m = m.tr_s('()', ' ').strip if n2 == 'do'
+ name = "test_#{n3}_block_after_blockcall_#{n1}_#{n2}_arg"
+ code = "#{blockcall}#{c}#{m} #{b}"
+ define_method(name) {assert_valid_syntax(code, bug6115)}
+ end
+ end
+
+ def test_do_block_in_cmdarg
+ bug9726 = '[ruby-core:61950] [Bug #9726]'
+ assert_valid_syntax("tap (proc do end)", __FILE__, bug9726)
+ end
+
+ def test_normal_argument
+ assert_valid_syntax('def foo(x) end')
+ assert_syntax_error('def foo(X) end', /constant/)
+ assert_syntax_error('def foo(@x) end', /instance variable/)
+ assert_syntax_error('def foo(@@x) end', /class variable/)
+ end
+
+ def test_optional_argument
+ assert_valid_syntax('def foo(x=nil) end')
+ assert_syntax_error('def foo(X=nil) end', /constant/)
+ assert_syntax_error('def foo(@x=nil) end', /instance variable/)
+ assert_syntax_error('def foo(@@x=nil) end', /class variable/)
+ end
+
+ def test_keyword_rest
+ bug5989 = '[ruby-core:42455]'
+ assert_valid_syntax("def kwrest_test(**a) a; end", __FILE__, bug5989)
+ assert_valid_syntax("def kwrest_test2(**a, &b) end", __FILE__, bug5989)
+ o = Object.new
+ def o.kw(**a) a end
+ assert_equal({}, o.kw, bug5989)
+ assert_equal({foo: 1}, o.kw(foo: 1), bug5989)
+ assert_equal({foo: 1, bar: 2}, o.kw(foo: 1, bar: 2), bug5989)
+ EnvUtil.under_gc_stress do
+ eval("def o.m(k: 0) k end")
+ end
+ assert_equal(42, o.m(k: 42), '[ruby-core:45744]')
+ bug7922 = '[ruby-core:52744] [Bug #7922]'
+ def o.bug7922(**) end
+ assert_nothing_raised(ArgumentError, bug7922) {o.bug7922(foo: 42)}
+ end
+
+ class KW2
+ def kw(k1: 1, k2: 2) [k1, k2] end
+ end
+
+ def test_keyword_splat
+ assert_valid_syntax("foo(**h)", __FILE__)
+ o = KW2.new
+ h = {k1: 11, k2: 12}
+ assert_equal([11, 12], o.kw(**h))
+ assert_equal([11, 12], o.kw(k2: 22, **h))
+ assert_equal([11, 22], o.kw(**h, **{k2: 22}))
+ assert_equal([11, 12], o.kw(**{k2: 22}, **h))
+ end
+
+ def test_keyword_duplicated_splat
+ bug10315 = '[ruby-core:65368] [Bug #10315]'
+
+ o = KW2.new
+ assert_equal([23, 2], o.kw(**{k1: 22}, **{k1: 23}), bug10315)
+
+ h = {k3: 31}
+ assert_raise(ArgumentError) {o.kw(**h)}
+ h = {"k1"=>11, k2: 12}
+ assert_raise(TypeError) {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
+ a.clear
+ r = nil
+ assert_warn(/duplicated/) {r = eval("a.f(k: a.add(1), k: a.add(2))")}
+ assert_equal(2, r)
+ assert_equal([1, 2], a, bug10315)
+ a.clear
+ r = nil
+ assert_warn(/duplicated/) {r = eval("a.f({k: a.add(1), k: a.add(2)})")}
+ assert_equal(2, r)
+ assert_equal([1, 2], a, bug10315)
+ end
+
+ def test_keyword_empty_splat
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ bug10719 = '[ruby-core:67446] [Bug #10719]'
+ assert_valid_syntax("foo(a: 1, **{})", bug10719)
+ end;
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ bug13756 = '[ruby-core:82113] [Bug #13756]'
+ assert_valid_syntax("defined? foo(**{})", bug13756)
+ end;
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ bug15271 = '[ruby-core:89648] [Bug #15271]'
+ assert_valid_syntax("a **{}", bug15271)
+ end;
+ end
+
+ def test_keyword_self_reference
+ bug9593 = '[ruby-core:61299] [Bug #9593]'
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var: defined?(var)) var end")
+ end
+ assert_equal(42, o.foo(var: 42))
+ assert_equal("local-variable", o.foo, bug9593)
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var: var) var end")
+ end
+ assert_nil(o.foo, bug9593)
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var: bar(var)) var end")
+ end
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var: bar {var}) var end")
+ end
+
+ o = Object.new
+ assert_warn("") do
+ o.instance_eval("def foo(var: bar {|var| var}) var end")
+ end
+
+ o = Object.new
+ assert_warn("") do
+ o.instance_eval("def foo(var: def bar(var) var; end) var end")
+ end
+
+ o = Object.new
+ assert_warn("") do
+ o.instance_eval("proc {|var: 1| var}")
+ end
+ end
+
+ def test_keyword_invalid_name
+ bug11663 = '[ruby-core:71356] [Bug #11663]'
+
+ o = o = Object.new
+ assert_syntax_error('def o.foo(arg1?:) end', /arg1\?/, bug11663)
+ assert_syntax_error('def o.foo(arg1?:, arg2:) end', /arg1\?/, bug11663)
+ assert_syntax_error('proc {|arg1?:|}', /arg1\?/, bug11663)
+ assert_syntax_error('proc {|arg1?:, arg2:|}', /arg1\?/, bug11663)
+
+ bug10545 = '[ruby-dev:48742] [Bug #10545]'
+ assert_syntax_error('def o.foo(FOO: a) end', /constant/, bug10545)
+ assert_syntax_error('def o.foo(@foo: a) end', /instance variable/)
+ assert_syntax_error('def o.foo(@@foo: a) end', /class variable/)
+ end
+
+ def test_optional_self_reference
+ bug9593 = '[ruby-core:61299] [Bug #9593]'
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var = defined?(var)) var end")
+ end
+ assert_equal(42, o.foo(42))
+ assert_equal("local-variable", o.foo, bug9593)
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var = var) var end")
+ end
+ assert_nil(o.foo, bug9593)
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var = bar(var)) var end")
+ end
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var = bar {var}) var end")
+ end
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var = (def bar;end; var)) var end")
+ end
+
+ o = Object.new
+ assert_warn(/circular argument reference - var/) do
+ o.instance_eval("def foo(var = (def self.bar;end; var)) var end")
+ end
+
+ o = Object.new
+ assert_warn("") do
+ o.instance_eval("def foo(var = bar {|var| var}) var end")
+ end
+
+ o = Object.new
+ assert_warn("") do
+ o.instance_eval("def foo(var = def bar(var) var; end) var end")
+ end
+
+ o = Object.new
+ assert_warn("") do
+ o.instance_eval("proc {|var = 1| var}")
+ end
+ end
+
+ def test_warn_grouped_expression
+ bug5214 = '[ruby-core:39050]'
+ assert_warning("", bug5214) do
+ assert_valid_syntax("foo \\\n(\n true)", "test", verbose: true)
+ end
+ end
+
+ def test_warn_unreachable
+ assert_warning("test:3: warning: statement not reached\n") do
+ code = "loop do\n" "break\n" "foo\n" "end"
+ assert_valid_syntax(code, "test", verbose: true)
+ end
+ end
+
+ def test_warn_balanced
+ warning = <<WARN
+test:1: warning: `%s' after local variable or literal is interpreted as binary operator
+test:1: warning: even though it seems like %s
+WARN
+ [
+ [:**, "argument prefix"],
+ [:*, "argument prefix"],
+ [:<<, "here document"],
+ [:&, "argument prefix"],
+ [:+, "unary operator"],
+ [:-, "unary operator"],
+ [:/, "regexp literal"],
+ [:%, "string literal"],
+ ].each do |op, syn|
+ all_assertions do |a|
+ ["puts 1 #{op}0", "puts :a #{op}0", "m = 1; puts m #{op}0"].each do |src|
+ a.for(src) do
+ assert_warning(warning % [op, syn], src) do
+ assert_valid_syntax(src, "test", verbose: true)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def test_cmd_symbol_after_keyword
+ bug6347 = '[ruby-dev:45563]'
+ assert_not_label(:foo, 'if true then not_label:foo end', bug6347)
+ assert_not_label(:foo, 'if false; else not_label:foo end', bug6347)
+ assert_not_label(:foo, 'begin not_label:foo end', bug6347)
+ assert_not_label(:foo, 'begin ensure not_label:foo end', bug6347)
+ end
+
+ def test_cmd_symbol_in_string
+ bug6347 = '[ruby-dev:45563]'
+ assert_not_label(:foo, '"#{not_label:foo}"', bug6347)
+ end
+
+ def test_cmd_symbol_singleton_class
+ bug6347 = '[ruby-dev:45563]'
+ @not_label = self
+ assert_not_label(:foo, 'class << not_label:foo; end', bug6347)
+ end
+
+ def test_cmd_symbol_superclass
+ bug6347 = '[ruby-dev:45563]'
+ @not_label = Object
+ assert_not_label(:foo, 'class Foo < not_label:foo; end', bug6347)
+ end
+
+ def test_no_label_with_percent
+ assert_syntax_error('{%"a": 1}', /unexpected ':'/)
+ assert_syntax_error("{%'a': 1}", /unexpected ':'/)
+ assert_syntax_error('{%Q"a": 1}', /unexpected ':'/)
+ assert_syntax_error("{%Q'a': 1}", /unexpected ':'/)
+ assert_syntax_error('{%q"a": 1}', /unexpected ':'/)
+ assert_syntax_error("{%q'a': 1}", /unexpected ':'/)
+ end
+
+ def test_block_after_cond
+ bug10653 = '[ruby-dev:48790] [Bug #10653]'
+ assert_valid_syntax("false ? raise {} : tap {}", bug10653)
+ assert_valid_syntax("false ? raise do end : tap do end", bug10653)
+ end
+
+ def test_paren_after_label
+ bug11456 = '[ruby-dev:49221] [Bug #11456]'
+ assert_valid_syntax("{foo: (1 rescue 0)}", bug11456)
+ assert_valid_syntax("{foo: /=/}", bug11456)
+ end
+
+ def test_percent_string_after_label
+ bug11812 = '[ruby-core:72084]'
+ assert_valid_syntax('{label:%w(*)}', bug11812)
+ assert_valid_syntax('{label: %w(*)}', bug11812)
+ end
+
+ def test_heredoc_after_label
+ bug11849 = '[ruby-core:72396] [Bug #11849]'
+ assert_valid_syntax("{label:<<DOC\n""DOC\n""}", bug11849)
+ assert_valid_syntax("{label:<<-DOC\n""DOC\n""}", bug11849)
+ assert_valid_syntax("{label:<<~DOC\n""DOC\n""}", bug11849)
+ assert_valid_syntax("{label: <<DOC\n""DOC\n""}", bug11849)
+ assert_valid_syntax("{label: <<-DOC\n""DOC\n""}", bug11849)
+ assert_valid_syntax("{label: <<~DOC\n""DOC\n""}", bug11849)
+ end
+
+ def test_cmdarg_kwarg_lvar_clashing_method
+ bug12073 = '[ruby-core:73816] [Bug#12073]'
+ a = a = 1
+ assert_valid_syntax("a b: 1")
+ assert_valid_syntax("a = 1; a b: 1", bug12073)
+ end
+
+ def test_duplicated_arg
+ assert_syntax_error("def foo(a, a) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(_, _) end")
+ (obj = Object.new).instance_eval("def foo(_, x, _) x end")
+ assert_equal(2, obj.foo(1, 2, 3))
+ end
+
+ def test_duplicated_rest
+ assert_syntax_error("def foo(a, *a) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(_, *_) end")
+ (obj = Object.new).instance_eval("def foo(_, x, *_) x end")
+ assert_equal(2, obj.foo(1, 2, 3))
+ end
+
+ def test_duplicated_opt
+ assert_syntax_error("def foo(a, a=1) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(_, _=1) end")
+ (obj = Object.new).instance_eval("def foo(_, x, _=42) x end")
+ assert_equal(2, obj.foo(1, 2))
+ end
+
+ def test_duplicated_opt_rest
+ assert_syntax_error("def foo(a=1, *a) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(_=1, *_) end")
+ (obj = Object.new).instance_eval("def foo(_, x=42, *_) x end")
+ assert_equal(42, obj.foo(1))
+ assert_equal(2, obj.foo(1, 2))
+ end
+
+ def test_duplicated_rest_opt
+ assert_syntax_error("def foo(*a, a=1) end", /duplicated argument name/)
+ end
+
+ def test_duplicated_rest_post
+ assert_syntax_error("def foo(*a, a) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(*_, _) end")
+ (obj = Object.new).instance_eval("def foo(*_, x, _) x end")
+ assert_equal(2, obj.foo(1, 2, 3))
+ assert_equal(2, obj.foo(2, 3))
+ (obj = Object.new).instance_eval("def foo(*_, _, x) x end")
+ assert_equal(3, obj.foo(1, 2, 3))
+ assert_equal(3, obj.foo(2, 3))
+ end
+
+ def test_duplicated_opt_post
+ assert_syntax_error("def foo(a=1, a) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(_=1, _) end")
+ (obj = Object.new).instance_eval("def foo(_=1, x, _) x end")
+ assert_equal(2, obj.foo(1, 2, 3))
+ assert_equal(2, obj.foo(2, 3))
+ (obj = Object.new).instance_eval("def foo(_=1, _, x) x end")
+ assert_equal(3, obj.foo(1, 2, 3))
+ assert_equal(3, obj.foo(2, 3))
+ end
+
+ def test_duplicated_kw
+ assert_syntax_error("def foo(a, a: 1) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(_, _: 1) end")
+ (obj = Object.new).instance_eval("def foo(_, x, _: 1) x end")
+ assert_equal(3, obj.foo(2, 3))
+ assert_equal(3, obj.foo(2, 3, _: 42))
+ (obj = Object.new).instance_eval("def foo(x, _, _: 1) x end")
+ assert_equal(2, obj.foo(2, 3))
+ assert_equal(2, obj.foo(2, 3, _: 42))
+ end
+
+ def test_duplicated_rest_kw
+ assert_syntax_error("def foo(*a, a: 1) end", /duplicated argument name/)
+ assert_nothing_raised {def foo(*_, _: 1) end}
+ (obj = Object.new).instance_eval("def foo(*_, x: 42, _: 1) x end")
+ assert_equal(42, obj.foo(42))
+ assert_equal(42, obj.foo(2, _: 0))
+ assert_equal(2, obj.foo(x: 2, _: 0))
+ end
+
+ def test_duplicated_opt_kw
+ assert_syntax_error("def foo(a=1, a: 1) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(_=1, _: 1) end")
+ (obj = Object.new).instance_eval("def foo(_=42, x, _: 1) x end")
+ assert_equal(0, obj.foo(0))
+ assert_equal(0, obj.foo(0, _: 3))
+ end
+
+ def test_duplicated_kw_kwrest
+ assert_syntax_error("def foo(a: 1, **a) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(_: 1, **_) end")
+ (obj = Object.new).instance_eval("def foo(_: 1, x: 42, **_) x end")
+ assert_equal(42, obj.foo())
+ assert_equal(42, obj.foo(a: 0))
+ assert_equal(42, obj.foo(_: 0, a: 0))
+ assert_equal(1, obj.foo(_: 0, x: 1, a: 0))
+ end
+
+ def test_duplicated_rest_kwrest
+ assert_syntax_error("def foo(*a, **a) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(*_, **_) end")
+ (obj = Object.new).instance_eval("def foo(*_, x, **_) x end")
+ assert_equal(1, obj.foo(1))
+ assert_equal(1, obj.foo(1, a: 0))
+ assert_equal(2, obj.foo(1, 2, a: 0))
+ end
+
+ def test_duplicated_opt_kwrest
+ assert_syntax_error("def foo(a=1, **a) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(_=1, **_) end")
+ (obj = Object.new).instance_eval("def foo(_=42, x, **_) x end")
+ assert_equal(1, obj.foo(1))
+ assert_equal(1, obj.foo(1, a: 0))
+ assert_equal(1, obj.foo(0, 1, a: 0))
+ end
+
+ def test_duplicated_when
+ w = 'warning: duplicated when clause is ignored'
+ assert_warning(/3: #{w}.+4: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m){
+ eval %q{
+ case 1
+ when 1, 1
+ when 1, 1
+ when 1, 1
+ end
+ }
}
- code.force_encoding("us-ascii")
- catch {|tag| eval(code, binding, fname, 0)}
- rescue SyntaxError
- false
+ assert_warning(/#{w}/){#/3: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m){
+ a = a = 1
+ eval %q{
+ case 1
+ when 1, 1
+ when 1, a
+ when 1, 1
+ end
+ }
+ }
+ end
+
+ def test_invalid_break
+ assert_syntax_error("def m; break; end", /Invalid break/)
+ assert_syntax_error('/#{break}/', /Invalid break/)
+ assert_syntax_error('/#{break}/o', /Invalid break/)
+ end
+
+ def test_invalid_next
+ assert_syntax_error("def m; next; end", /Invalid next/)
+ assert_syntax_error('/#{next}/', /Invalid next/)
+ assert_syntax_error('/#{next}/o', /Invalid next/)
end
- def test_syntax
- assert_nothing_raised(Exception) do
- for script in Dir[File.expand_path("../../../{lib,sample,ext,test}/**/*.rb", __FILE__)].sort
- assert(valid_syntax?(IO::read(script), script))
+ def test_lambda_with_space
+ feature6390 = '[ruby-dev:45605]'
+ assert_valid_syntax("-> (x, y) {}", __FILE__, feature6390)
+ end
+
+ def test_do_block_in_cmdarg_begin
+ bug6419 = '[ruby-dev:45631]'
+ assert_valid_syntax("p begin 1.times do 1 end end", __FILE__, bug6419)
+ end
+
+ def test_do_block_in_call_args
+ bug9308 = '[ruby-core:59342] [Bug #9308]'
+ assert_valid_syntax("bar def foo; self.each do end end", bug9308)
+ end
+
+ def test_do_block_in_lambda
+ bug11107 = '[ruby-core:69017] [Bug #11107]'
+ assert_valid_syntax('p ->() do a() do end end', bug11107)
+ end
+
+ def test_do_block_after_lambda
+ bug11380 = '[ruby-core:70067] [Bug #11380]'
+ assert_valid_syntax('p -> { :hello }, a: 1 do end', bug11380)
+ end
+
+ def test_reserved_method_no_args
+ bug6403 = '[ruby-dev:45626]'
+ assert_valid_syntax("def self; :foo; end", __FILE__, bug6403)
+ end
+
+ def test_unassignable
+ gvar = global_variables
+ %w[self nil true false __FILE__ __LINE__ __ENCODING__].each do |kwd|
+ assert_raise(SyntaxError) {eval("#{kwd} = nil")}
+ assert_equal(gvar, global_variables)
+ end
+ end
+
+ Bug7559 = '[ruby-dev:46737]'
+
+ def test_lineno_command_call_quote
+ expected = __LINE__ + 1
+ actual = caller_lineno "a
+b
+c
+d
+e"
+ assert_equal(expected, actual, "#{Bug7559}: ")
+ end
+
+ def assert_dedented_heredoc(expect, result, mesg = "")
+ all_assertions(mesg) do |a|
+ %w[eos "eos" 'eos' `eos`].each do |eos|
+ a.for(eos) do
+ assert_equal(eval("<<-#{eos}\n#{expect}eos\n"),
+ eval("<<~#{eos}\n#{result}eos\n"))
+ end
end
end
end
+
+ def test_dedented_heredoc_without_indentation
+ result = " y\n" \
+ "z\n"
+ expect = result
+ assert_dedented_heredoc(expect, result)
+ end
+
+ def test_dedented_heredoc_with_indentation
+ result = " a\n" \
+ " b\n"
+ expect = " a\n" \
+ "b\n"
+ assert_dedented_heredoc(expect, result)
+ end
+
+ def test_dedented_heredoc_with_blank_less_indented_line
+ # the blank line has two leading spaces
+ result = " a\n" \
+ " \n" \
+ " b\n"
+ expect = "a\n" \
+ "\n" \
+ "b\n"
+ assert_dedented_heredoc(expect, result)
+ end
+
+ def test_dedented_heredoc_with_blank_less_indented_line_escaped
+ result = " a\n" \
+ "\\ \\ \n" \
+ " b\n"
+ expect = result
+ assert_dedented_heredoc(expect, result)
+ end
+
+ def test_dedented_heredoc_with_blank_more_indented_line
+ # the blank line has six leading spaces
+ result = " a\n" \
+ " \n" \
+ " b\n"
+ expect = "a\n" \
+ " \n" \
+ "b\n"
+ assert_dedented_heredoc(expect, result)
+ end
+
+ def test_dedented_heredoc_with_blank_more_indented_line_escaped
+ result = " a\n" \
+ "\\ \\ \\ \\ \\ \\ \n" \
+ " b\n"
+ expect = result
+ assert_dedented_heredoc(expect, result)
+ end
+
+ def test_dedented_heredoc_with_empty_line
+ result = " This would contain specially formatted text.\n" \
+ "\n" \
+ " That might span many lines\n"
+ expect = 'This would contain specially formatted text.'"\n" \
+ ''"\n" \
+ 'That might span many lines'"\n"
+ assert_dedented_heredoc(expect, result)
+ end
+
+ def test_dedented_heredoc_with_interpolated_expression
+ result = ' #{1}a'"\n" \
+ " zy\n"
+ expect = ' #{1}a'"\n" \
+ "zy\n"
+ assert_dedented_heredoc(expect, result)
+ end
+
+ def test_dedented_heredoc_with_interpolated_string
+ w = w = ""
+ result = " \#{mesg} a\n" \
+ " zy\n"
+ expect = '#{mesg} a'"\n" \
+ ' zy'"\n"
+ assert_dedented_heredoc(expect, result)
+ end
+
+ def test_dedented_heredoc_with_newline
+ bug11989 = '[ruby-core:72855] [Bug #11989] after escaped newline should not be dedented'
+ result = ' x\n'" y\n" \
+ " z\n"
+ expect = 'x\n'" y\n" \
+ "z\n"
+ assert_dedented_heredoc(expect, result, bug11989)
+ end
+
+ def test_dedented_heredoc_with_concatenation
+ bug11990 = '[ruby-core:72857] [Bug #11990] concatenated string should not be dedented'
+ %w[eos "eos" 'eos'].each do |eos|
+ assert_equal("x\n y",
+ eval("<<~#{eos} ' y'\n x\neos\n"),
+ "#{bug11990} with #{eos}")
+ end
+ %w[eos "eos" 'eos' `eos`].each do |eos|
+ _, expect = eval("[<<~#{eos}, ' x']\n"" y\n""eos\n")
+ assert_equal(' x', expect, bug11990)
+ end
+ end
+
+ def test_dedented_heredoc_expr_at_beginning
+ result = " a\n" \
+ '#{1}'"\n"
+ expected = " a\n" \
+ '#{1}'"\n"
+ assert_dedented_heredoc(expected, result)
+ end
+
+ def test_dedented_heredoc_expr_string
+ result = ' one#{" two "}'"\n"
+ expected = 'one#{" two "}'"\n"
+ assert_dedented_heredoc(expected, result)
+ end
+
+ def test_dedented_heredoc_continued_line
+ result = " 1\\\n" " 2\n"
+ expected = "1\\\n" "2\n"
+ assert_dedented_heredoc(expected, result)
+ assert_syntax_error("#{<<~"begin;"}\n#{<<~'end;'}", /can't find string "TEXT"/)
+ begin;
+ <<-TEXT
+ \
+ TEXT
+ end;
+ assert_syntax_error("#{<<~"begin;"}\n#{<<~'end;'}", /can't find string "TEXT"/)
+ begin;
+ <<~TEXT
+ \
+ TEXT
+ end;
+ end
+
+ def test_lineno_after_heredoc
+ bug7559 = '[ruby-dev:46737]'
+ expected, _, actual = __LINE__, <<eom, __LINE__
+ a
+ b
+ c
+ d
+eom
+ assert_equal(expected, actual, bug7559)
+ end
+
+ def test_dedented_heredoc_invalid_identifer
+ assert_syntax_error('<<~ "#{}"', /unexpected <</)
+ end
+
+ def test_dedented_heredoc_concatenation
+ assert_equal("\n0\n1", eval("<<~0 '1'\n \n0\#{}\n0"))
+ end
+
+ def test_heredoc_mixed_encoding
+ assert_syntax_error(<<-'HEREDOC', 'UTF-8 mixed within Windows-31J source')
+ #encoding: cp932
+ <<-TEXT
+ \xe9\x9d\u1234
+ TEXT
+ HEREDOC
+ assert_syntax_error(<<-'HEREDOC', 'UTF-8 mixed within Windows-31J source')
+ #encoding: cp932
+ <<-TEXT
+ \xe9\x9d
+ \u1234
+ TEXT
+ HEREDOC
+ assert_syntax_error(<<-'HEREDOC', 'UTF-8 mixed within Windows-31J source')
+ #encoding: cp932
+ <<-TEXT
+ \u1234\xe9\x9d
+ TEXT
+ HEREDOC
+ assert_syntax_error(<<-'HEREDOC', 'UTF-8 mixed within Windows-31J source')
+ #encoding: cp932
+ <<-TEXT
+ \u1234
+ \xe9\x9d
+ TEXT
+ HEREDOC
+ end
+
+ def test_lineno_operation_brace_block
+ expected = __LINE__ + 1
+ actual = caller_lineno\
+ {}
+ assert_equal(expected, actual)
+ end
+
+ def assert_constant_reassignment_nested(preset, op, expected, err = [], bug = '[Bug #5449]')
+ [
+ ["p ", ""], # no-pop
+ ["", "p Foo::Bar"], # pop
+ ].each do |p1, p2|
+ src = <<-EOM.gsub(/^\s*\n/, '')
+ class Foo
+ #{"Bar = " + preset if preset}
+ end
+ #{p1}Foo::Bar #{op}= 42
+ #{p2}
+ EOM
+ msg = "\# #{bug}\n#{src}"
+ assert_valid_syntax(src, caller_locations(1, 1)[0].path, msg)
+ assert_in_out_err([], src, expected, err, msg)
+ end
+ end
+
+ def test_constant_reassignment_nested
+ already = /already initialized constant Foo::Bar/
+ uninitialized = /uninitialized constant Foo::Bar/
+ assert_constant_reassignment_nested(nil, "||", %w[42])
+ assert_constant_reassignment_nested("false", "||", %w[42], already)
+ assert_constant_reassignment_nested("true", "||", %w[true])
+ assert_constant_reassignment_nested(nil, "&&", [], uninitialized)
+ assert_constant_reassignment_nested("false", "&&", %w[false])
+ assert_constant_reassignment_nested("true", "&&", %w[42], already)
+ assert_constant_reassignment_nested(nil, "+", [], uninitialized)
+ assert_constant_reassignment_nested("false", "+", [], /undefined method/)
+ assert_constant_reassignment_nested("11", "+", %w[53], already)
+ end
+
+ def assert_constant_reassignment_toplevel(preset, op, expected, err = [], bug = '[Bug #5449]')
+ [
+ ["p ", ""], # no-pop
+ ["", "p ::Bar"], # pop
+ ].each do |p1, p2|
+ src = <<-EOM.gsub(/^\s*\n/, '')
+ #{"Bar = " + preset if preset}
+ class Foo
+ #{p1}::Bar #{op}= 42
+ #{p2}
+ end
+ EOM
+ msg = "\# #{bug}\n#{src}"
+ assert_valid_syntax(src, caller_locations(1, 1)[0].path, msg)
+ assert_in_out_err([], src, expected, err, msg)
+ end
+ end
+
+ def test_constant_reassignment_toplevel
+ already = /already initialized constant Bar/
+ uninitialized = /uninitialized constant Bar/
+ assert_constant_reassignment_toplevel(nil, "||", %w[42])
+ assert_constant_reassignment_toplevel("false", "||", %w[42], already)
+ assert_constant_reassignment_toplevel("true", "||", %w[true])
+ assert_constant_reassignment_toplevel(nil, "&&", [], uninitialized)
+ assert_constant_reassignment_toplevel("false", "&&", %w[false])
+ assert_constant_reassignment_toplevel("true", "&&", %w[42], already)
+ assert_constant_reassignment_toplevel(nil, "+", [], uninitialized)
+ assert_constant_reassignment_toplevel("false", "+", [], /undefined method/)
+ assert_constant_reassignment_toplevel("11", "+", %w[53], already)
+ end
+
+ def test_integer_suffix
+ ["1if true", "begin 1end"].each do |src|
+ assert_valid_syntax(src)
+ assert_equal(1, eval(src), src)
+ end
+ end
+
+ def test_value_of_def
+ assert_separately [], <<-EOS
+ assert_equal(:foo, (def foo; end))
+ assert_equal(:foo, (def (Object.new).foo; end))
+ EOS
+ end
+
+ def test_heredoc_cr
+ assert_syntax_error("puts <<""EOS\n""ng\n""EOS\r""NO\n", /can't find string "EOS" anywhere before EOF/)
+ end
+
+ def test_heredoc_newline
+ assert_warn(/ends with a newline/) do
+ eval("<<\"EOS\n\"\nEOS\n")
+ end
+ end
+
+ def test__END___cr
+ assert_syntax_error("__END__\r<<<<<\n", /unexpected <</)
+ end
+
+ def test_warning_for_cr
+ feature8699 = '[ruby-core:56240] [Feature #8699]'
+ assert_warning(/encountered \\r/, feature8699) do
+ eval("\r""__id__\r")
+ end
+ end
+
+ def test_unexpected_fraction
+ msg = /unexpected fraction/
+ assert_syntax_error("0x0.0", msg)
+ assert_syntax_error("0b0.0", msg)
+ assert_syntax_error("0d0.0", msg)
+ assert_syntax_error("0o0.0", msg)
+ assert_syntax_error("0.0.0", msg)
+ end
+
+ def test_error_message_encoding
+ bug10114 = '[ruby-core:64228] [Bug #10114]'
+ code = "# -*- coding: utf-8 -*-\n" "def n \"\u{2208}\"; end"
+ assert_syntax_error(code, /def n "\u{2208}"; end/, bug10114)
+ end
+
+ def test_null_range_cmdarg
+ bug10957 = '[ruby-core:68477] [Bug #10957]'
+ assert_ruby_status(['-c', '-e', 'p ()..0'], "", bug10957)
+ assert_ruby_status(['-c', '-e', 'p ()...0'], "", bug10957)
+ assert_syntax_error('0..%w.', /unterminated string/, bug10957)
+ assert_syntax_error('0...%w.', /unterminated string/, bug10957)
+ end
+
+ def test_too_big_nth_ref
+ bug11192 = '[ruby-core:69393] [Bug #11192]'
+ assert_warn(/too big/, bug11192) do
+ eval('$99999999999999999')
+ end
+ end
+
+ def test_invalid_symbol_space
+ assert_syntax_error(": foo", /unexpected ':'/)
+ assert_syntax_error(": #\n foo", /unexpected ':'/)
+ assert_syntax_error(":#\n foo", /unexpected ':'/)
+ end
+
+ def test_fluent_dot
+ assert_valid_syntax("a\n.foo")
+ assert_valid_syntax("a\n&.foo")
+ end
+
+ def test_no_warning_logop_literal
+ assert_warning("") do
+ eval("true||raise;nil")
+ end
+ assert_warning("") do
+ eval("false&&raise;nil")
+ end
+ assert_warning("") do
+ eval("''||raise;nil")
+ end
+ end
+
+ def test_warning_literal_in_condition
+ assert_warn(/literal in condition/) do
+ eval('1 if ""')
+ end
+ assert_warn(/literal in condition/) do
+ eval('1 if //')
+ end
+ assert_warn(/literal in condition/) do
+ eval('1 if true..false')
+ end
+ assert_warning(/literal in condition/) do
+ eval('1 if 1')
+ end
+ assert_warning(/literal in condition/) do
+ eval('1 if :foo')
+ end
+ assert_warning(/literal in condition/) do
+ eval('1 if :"#{"foo".upcase}"')
+ end
+
+ assert_warn('') do
+ eval('1 if !""')
+ end
+ assert_warn('') do
+ eval('1 if !//')
+ end
+ assert_warn('') do
+ eval('1 if !(true..false)')
+ end
+ assert_warning('') do
+ eval('1 if !1')
+ end
+ assert_warning('') do
+ eval('1 if !:foo')
+ end
+ assert_warning('') do
+ eval('1 if !:"#{"foo".upcase}"')
+ end
+ end
+
+ def test_alias_symbol
+ bug8851 = '[ruby-dev:47681] [Bug #8851]'
+ formats = ['%s', ":'%s'", ':"%s"', '%%s(%s)']
+ all_assertions(bug8851) do |all|
+ formats.product(formats) do |form1, form2|
+ all.for(code = "alias #{form1 % 'a'} #{form2 % 'p'}") do
+ assert_valid_syntax(code)
+ end
+ end
+ end
+ end
+
+ def test_undef_symbol
+ bug8851 = '[ruby-dev:47681] [Bug #8851]'
+ formats = ['%s', ":'%s'", ':"%s"', '%%s(%s)']
+ all_assertions(bug8851) do |all|
+ formats.product(formats) do |form1, form2|
+ all.for(code = "undef #{form1 % 'a'}, #{form2 % 'p'}") do
+ assert_valid_syntax(code)
+ end
+ end
+ end
+ end
+
+ def test_parenthesised_statement_argument
+ assert_syntax_error("foo(bar rescue nil)", /unexpected rescue \(modifier\)/)
+ assert_valid_syntax("foo (bar rescue nil)")
+ end
+
+ def test_cmdarg_in_paren
+ bug11873 = '[ruby-core:72482] [Bug #11873]'
+ assert_valid_syntax %q{a b{c d}, :e do end}, bug11873
+ assert_valid_syntax %q{a b(c d), :e do end}, bug11873
+ assert_valid_syntax %q{a b{c(d)}, :e do end}, bug11873
+ assert_valid_syntax %q{a b(c(d)), :e do end}, bug11873
+ assert_valid_syntax %q{a b{c d}, 1 do end}, bug11873
+ assert_valid_syntax %q{a b(c d), 1 do end}, bug11873
+ assert_valid_syntax %q{a b{c(d)}, 1 do end}, bug11873
+ assert_valid_syntax %q{a b(c(d)), 1 do end}, bug11873
+ assert_valid_syntax %q{a b{c d}, "x" do end}, bug11873
+ assert_valid_syntax %q{a b(c d), "x" do end}, bug11873
+ assert_valid_syntax %q{a b{c(d)}, "x" do end}, bug11873
+ assert_valid_syntax %q{a b(c(d)), "x" do end}, bug11873
+ end
+
+ def test_block_after_cmdarg_in_paren
+ bug11873 = '[ruby-core:72482] [Bug #11873]'
+ def bug11873.p(*);end;
+
+ assert_raise(LocalJumpError, bug11873) do
+ bug11873.instance_eval do
+ p p{p p;p(p)}, tap do
+ raise SyntaxError, "should not be passed to tap"
+ end
+ end
+ end
+
+ assert_raise(LocalJumpError, bug11873) do
+ bug11873.instance_eval do
+ p p{p(p);p p}, tap do
+ raise SyntaxError, "should not be passed to tap"
+ end
+ end
+ end
+ end
+
+ def test_do_block_in_hash_brace
+ bug13073 = '[ruby-core:78837] [Bug #13073]'
+ assert_valid_syntax 'p :foo, {a: proc do end, b: proc do end}', bug13073
+ assert_valid_syntax 'p :foo, {:a => proc do end, b: proc do end}', bug13073
+ assert_valid_syntax 'p :foo, {"a": proc do end, b: proc do end}', bug13073
+ assert_valid_syntax 'p :foo, {** proc do end, b: proc do end}', bug13073
+ assert_valid_syntax 'p :foo, {proc do end => proc do end, b: proc do end}', bug13073
+ end
+
+ def test_do_after_local_variable
+ obj = Object.new
+ def obj.m; yield; end
+ result = assert_nothing_raised(SyntaxError) do
+ obj.instance_eval("m = 1; m do :ok end")
+ end
+ assert_equal(:ok, result)
+ end
+
+ def test_brace_after_local_variable
+ obj = Object.new
+ def obj.m; yield; end
+ result = assert_nothing_raised(SyntaxError) do
+ obj.instance_eval("m = 1; m {:ok}")
+ end
+ assert_equal(:ok, result)
+ end
+
+ def test_brace_after_literal_argument
+ bug = '[ruby-core:81037] [Bug #13547]'
+ error = /unexpected '{'/
+ assert_syntax_error('m "x" {}', error)
+ assert_syntax_error('m 1 {}', error, bug)
+ assert_syntax_error('m 1.0 {}', error, bug)
+ assert_syntax_error('m :m {}', error, bug)
+ assert_syntax_error('m :"#{m}" {}', error, bug)
+ assert_syntax_error('m ?x {}', error, bug)
+ assert_syntax_error('m %[] {}', error, bug)
+ assert_syntax_error('m 0..1 {}', error, bug)
+ assert_syntax_error('m [] {}', error, bug)
+ end
+
+ def test_return_toplevel
+ feature4840 = '[ruby-core:36785] [Feature #4840]'
+ line = __LINE__+2
+ code = "#{<<~"begin;"}#{<<~'end;'}"
+ begin;
+ return; raise
+ begin return; rescue SystemExit; exit false; end
+ begin return; ensure puts "ensured"; end #=> ensured
+ begin ensure return; end
+ begin raise; ensure; return; end
+ begin raise; rescue; return; end
+ return false; raise
+ return 1; raise
+ "#{return}"
+ raise((return; "should not raise"))
+ begin raise; ensure return; end; self
+ begin raise; ensure return; end and self
+ nil&defined?0--begin e=no_method_error(); return; 0;end
+ return puts('ignored') #=> ignored
+ end;
+ .split(/\n/).map {|s|[(line+=1), *s.split(/#=> /, 2)]}
+ failed = proc do |n, s|
+ RubyVM::InstructionSequence.compile(s, __FILE__, nil, n).disasm
+ end
+ Tempfile.create(%w"test_return_ .rb") do |lib|
+ lib.close
+ args = %W[-W0 -r#{lib.path}]
+ all_assertions_foreach(feature4840, *[:main, :lib].product([:class, :top], code)) do |main, klass, (n, s, *ex)|
+ if klass == :class
+ s = "class X; #{s}; end"
+ if main == :main
+ assert_in_out_err(%[-W0], s, [], /return/, proc {failed[n, s]}, success: false)
+ else
+ File.write(lib, s)
+ assert_in_out_err(args, "", [], /return/, proc {failed[n, s]}, success: false)
+ end
+ else
+ if main == :main
+ assert_in_out_err(%[-W0], s, ex, [], proc {failed[n, s]}, success: true)
+ else
+ File.write(lib, s)
+ assert_in_out_err(args, "", ex, [], proc {failed[n, s]}, success: true)
+ end
+ end
+ end
+ end
+ end
+
+ def test_syntax_error_in_rescue
+ bug12613 = '[ruby-core:76531] [Bug #12613]'
+ assert_syntax_error("#{<<-"begin;"}\n#{<<-"end;"}", /Invalid retry/, bug12613)
+ begin;
+ while true
+ begin
+ p
+ rescue
+ retry
+ else
+ retry
+ end
+ break
+ end
+ end;
+ end
+
+ def test_invalid_jump
+ assert_in_out_err(%w[-e redo], "", [], /^-e:1: /)
+ end
+
+ def test_keyword_not_parens
+ assert_valid_syntax("not()")
+ end
+
+ def test_rescue_do_end_raised
+ result = []
+ assert_raise(RuntimeError) do
+ eval("#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ tap do
+ result << :begin
+ raise "An exception occurred!"
+ ensure
+ result << :ensure
+ end
+ end;
+ end
+ assert_equal([:begin, :ensure], result)
+ end
+
+ def test_rescue_do_end_rescued
+ result = []
+ assert_nothing_raised(RuntimeError) do
+ eval("#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ tap do
+ result << :begin
+ raise "An exception occurred!"
+ rescue
+ result << :rescue
+ else
+ result << :else
+ ensure
+ result << :ensure
+ end
+ end;
+ end
+ assert_equal([:begin, :rescue, :ensure], result)
+ end
+
+ def test_rescue_do_end_no_raise
+ result = []
+ assert_nothing_raised(RuntimeError) do
+ eval("#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ tap do
+ result << :begin
+ rescue
+ result << :rescue
+ else
+ result << :else
+ ensure
+ result << :ensure
+ end
+ end;
+ end
+ assert_equal([:begin, :else, :ensure], result)
+ end
+
+ def test_rescue_do_end_ensure_result
+ result = eval("#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ proc do
+ :begin
+ ensure
+ :ensure
+ end.call
+ end;
+ assert_equal(:begin, result)
+ end
+
+ def test_rescue_do_end_ensure_in_lambda
+ result = []
+ eval("#{<<-"begin;"}\n#{<<-"end;"}")
+ begin;
+ -> do
+ result << :begin
+ raise "An exception occurred!"
+ rescue
+ result << :rescue
+ else
+ result << :else
+ ensure
+ result << :ensure
+ end.call
+ end;
+ assert_equal([:begin, :rescue, :ensure], result)
+ end
+
+ def test_return_in_loop
+ obj = Object.new
+ def obj.test
+ x = nil
+ return until x unless x
+ end
+ 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
+ 1.upto(0) do
+ end
+ .
+ foo(
+ 1,
+ 2,
+ )
+ end
+ assert_equal(line, e.backtrace_locations[0].lineno)
+
+ line = __LINE__+5
+ e = assert_raise(NoMethodError) do
+ 1.upto 0 do
+ end
+ .
+ foo(
+ 1,
+ 2,
+ )
+ end
+ assert_equal(line, e.backtrace_locations[0].lineno)
+ 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')
+ end
+
+ def test_classdef_in_cond
+ assert_valid_syntax('while class Foo; tap do end; end; break; end')
+ assert_valid_syntax('while class Foo a = tap do end; end; break; end')
+ end
+
+ def test_command_with_cmd_brace_block
+ assert_valid_syntax('obj.foo (1) {}')
+ assert_valid_syntax('obj::foo (1) {}')
+ end
+
+ def test_value_expr_in_condition
+ mesg = /void value expression/
+ assert_syntax_error("tap {a = (true ? next : break)}", mesg)
+ assert_valid_syntax("tap {a = (true ? true : break)}")
+ end
+
+ private
+
+ def not_label(x) @result = x; @not_label ||= nil end
+ def assert_not_label(expected, src, message = nil)
+ @result = nil
+ assert_nothing_raised(SyntaxError, message) {eval(src)}
+ assert_equal(expected, @result, message)
+ end
+
+ def make_tmpsrc(f, src)
+ f.open
+ f.truncate(0)
+ f.puts(src)
+ f.close
+ end
+
+ def with_script_lines
+ script_lines = nil
+ debug_lines = {}
+ Object.class_eval do
+ if defined?(SCRIPT_LINES__)
+ script_lines = SCRIPT_LINES__
+ remove_const :SCRIPT_LINES__
+ end
+ const_set(:SCRIPT_LINES__, debug_lines)
+ end
+ yield debug_lines
+ ensure
+ Object.class_eval do
+ remove_const :SCRIPT_LINES__
+ const_set(:SCRIPT_LINES__, script_lines) if script_lines
+ end
+ end
+
+ def caller_lineno(*)
+ caller_locations(1, 1)[0].lineno
+ end
end
diff --git a/test/ruby/test_system.rb b/test/ruby/test_system.rb
index 358fa54ef8..477767905b 100644
--- a/test/ruby/test_system.rb
+++ b/test/ruby/test_system.rb
@@ -1,6 +1,6 @@
+# frozen_string_literal: false
require 'test/unit'
require 'tmpdir'
-require_relative 'envutil'
class TestSystem < Test::Unit::TestCase
def test_system
@@ -84,11 +84,121 @@ class TestSystem < Test::Unit::TestCase
ENV["PATH"] = path
end
File.unlink tmpfilename
+
+ testname = '[ruby-core:44505]'
+ assert_match(/Windows/, `ver`, testname)
+ assert_equal 0, $?.to_i, testname
end
}
end
+ def test_system_at
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ bug4393 = '[ruby-core:35218]'
+
+ # @ + builtin command
+ assert_equal("foo\n", `@echo foo`, bug4393);
+ assert_equal("foo\n", `@@echo foo`, bug4393);
+ assert_equal("@@foo\n", `@@echo @@foo`, bug4393);
+
+ # @ + non builtin command
+ Dir.mktmpdir("ruby_script_tmp") {|tmpdir|
+ tmpfilename = "#{tmpdir}/ruby_script_tmp.#{$$}"
+
+ tmp = open(tmpfilename, "w")
+ tmp.print "foo\nbar\nbaz\n@foo";
+ tmp.close
+
+ assert_match(/\Abar\nbaz\n?\z/, `@@findstr "ba" #{tmpfilename.gsub("/", "\\")}`, bug4393);
+ }
+ end
+ end
+
+ def test_system_redirect_win
+ if /mswin|mingw/ !~ RUBY_PLATFORM
+ return
+ end
+
+ Dir.mktmpdir("ruby_script_tmp") do |tmpdir|
+ cmd = nil
+ message = proc do
+ [
+ '[ruby-talk:258939]',
+ "out.txt:",
+ *File.readlines("out.txt").map{|s|" "+s.inspect},
+ "err.txt:",
+ *File.readlines("err.txt").map{|s|" "+s.inspect},
+ "system(#{cmd.inspect})"
+ ].join("\n")
+ end
+ class << message
+ alias to_s call
+ end
+ Dir.chdir(tmpdir) do
+ open("input.txt", "w") {|f| f.puts "BFI3CHL671"}
+ cmd = "%WINDIR%/system32/find.exe \"BFI3CHL671\" input.txt > out.txt 2>err.txt"
+ assert_equal(true, system(cmd), message)
+ cmd = "\"%WINDIR%/system32/find.exe\" \"BFI3CHL671\" input.txt > out.txt 2>err.txt"
+ assert_equal(true, system(cmd), message)
+ cmd = "\"%WINDIR%/system32/find.exe BFI3CHL671\" input.txt > out.txt 2>err.txt"
+ assert_equal(false, system(cmd), message)
+ end
+ end
+ end
+
def test_empty_evstr
assert_equal("", eval('"#{}"', nil, __FILE__, __LINE__), "[ruby-dev:25113]")
end
+
+ def test_fallback_to_sh
+ Dir.mktmpdir("ruby_script_tmp") {|tmpdir|
+ tmpfilename = "#{tmpdir}/ruby_script_tmp.#{$$}"
+ open(tmpfilename, "w") {|f|
+ f.puts ": ;"
+ f.chmod(0755)
+ }
+ assert_equal(true, system(tmpfilename), '[ruby-core:32745]')
+ }
+ end if File.executable?("/bin/sh")
+
+ def test_system_exception
+ ruby = EnvUtil.rubybin
+ assert_nothing_raised do
+ system('feature_14235', exception: false)
+ end
+ assert_nothing_raised do
+ system(ruby, "-e", "abort", exception: false)
+ end
+ assert_nothing_raised do
+ system("'#{ruby}' -e abort", exception: false)
+ end
+ assert_raise(Errno::ENOENT) do
+ system('feature_14235', exception: true)
+ end
+ assert_raise_with_message(RuntimeError, /\ACommand failed with exit /) do
+ system(ruby, "-e", "abort", exception: true)
+ end
+ assert_raise_with_message(RuntimeError, /\ACommand failed with exit /) do
+ system("'#{ruby}' -e abort", exception: true)
+ end
+ end
+
+ def test_system_exception_nonascii
+ Dir.mktmpdir("ruby_script_tmp") do |tmpdir|
+ name = "\u{30c6 30b9 30c8}"
+ tmpfilename = "#{tmpdir}/#{name}.cmd"
+ message = /#{name}\.cmd/
+ e = assert_raise_with_message(Errno::ENOENT, message) do
+ system(tmpfilename, exception: true)
+ end
+ open(tmpfilename, "w") {|f|
+ f.print "@" if /mingw|mswin/ =~ RUBY_PLATFORM
+ f.puts "exit 127"
+ f.chmod(0755)
+ }
+ e = assert_raise_with_message(RuntimeError, message) do
+ system(tmpfilename, exception: true)
+ end
+ end
+ end
end
diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb
index 0322448c71..51c0338595 100644
--- a/test/ruby/test_thread.rb
+++ b/test/ruby/test_thread.rb
@@ -1,13 +1,14 @@
+# -*- coding: us-ascii -*-
+# frozen_string_literal: false
require 'test/unit'
-require 'thread'
-require_relative 'envutil'
+require "rbconfig/sizeof"
+require "timeout"
class TestThread < Test::Unit::TestCase
class Thread < ::Thread
Threads = []
def self.new(*)
th = super
- th.abort_on_exception = true
Threads << th
th
end
@@ -27,110 +28,148 @@ class TestThread < Test::Unit::TestCase
end
end
+ def test_inspect
+ th = Module.new {break module_eval("class C\u{30b9 30ec 30c3 30c9} < Thread; self; end")}.start{}
+ assert_match(/::C\u{30b9 30ec 30c3 30c9}:/, th.inspect)
+ ensure
+ th.join
+ end
+
+ def test_inspect_with_fiber
+ inspect1 = inspect2 = nil
+
+ Thread.new{
+ inspect1 = Thread.current.inspect
+ Fiber.new{
+ inspect2 = Thread.current.inspect
+ }.resume
+ }.join
+
+ assert_equal inspect1, inspect2, '[Bug #13689]'
+ end
+
+ def test_main_thread_variable_in_enumerator
+ assert_equal Thread.main, Thread.current
+
+ Thread.current.thread_variable_set :foo, "bar"
+
+ thread, value = Fiber.new {
+ Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)]
+ }.resume
+
+ assert_equal Thread.current, thread
+ assert_equal Thread.current.thread_variable_get(:foo), value
+ end
+
+ def test_thread_variable_in_enumerator
+ Thread.new {
+ Thread.current.thread_variable_set :foo, "bar"
+
+ thread, value = Fiber.new {
+ Fiber.yield [Thread.current, Thread.current.thread_variable_get(:foo)]
+ }.resume
+
+ assert_equal Thread.current, thread
+ assert_equal Thread.current.thread_variable_get(:foo), value
+ }.join
+ end
+
+ def test_thread_variables
+ assert_equal [], Thread.new { Thread.current.thread_variables }.join.value
+
+ t = Thread.new {
+ Thread.current.thread_variable_set(:foo, "bar")
+ Thread.current.thread_variables
+ }
+ assert_equal [:foo], t.join.value
+ end
+
+ def test_thread_variable?
+ Thread.new { assert_not_send([Thread.current, :thread_variable?, "foo"]) }.value
+ t = Thread.new {
+ Thread.current.thread_variable_set("foo", "bar")
+ }.join
+
+ assert_send([t, :thread_variable?, "foo"])
+ assert_send([t, :thread_variable?, :foo])
+ assert_not_send([t, :thread_variable?, :bar])
+ end
+
+ def test_thread_variable_strings_and_symbols_are_the_same_key
+ t = Thread.new {}.join
+ t.thread_variable_set("foo", "bar")
+ assert_equal "bar", t.thread_variable_get(:foo)
+ end
+
+ def test_thread_variable_frozen
+ t = Thread.new { }.join
+ t.freeze
+ assert_raise(FrozenError) do
+ t.thread_variable_set(:foo, "bar")
+ end
+ end
+
def test_mutex_synchronize
- m = Mutex.new
+ m = Thread::Mutex.new
r = 0
- max = 10
- (1..max).map{
+ num_threads = 10
+ loop=100
+ (1..num_threads).map{
Thread.new{
- i=0
- while i<max*max
- i+=1
+ loop.times{
m.synchronize{
- r += 1
+ tmp = r
+ # empty and waste loop for making thread preemption
+ 100.times {
+ }
+ r = tmp + 1
}
- end
+ }
}
}.each{|e|
e.join
}
- assert_equal(max * max * max, r)
- end
-
- def test_condvar
- mutex = Mutex.new
- condvar = ConditionVariable.new
- result = []
- mutex.synchronize do
- t = Thread.new do
- mutex.synchronize do
- result << 1
- condvar.signal
- end
- end
-
- result << 0
- condvar.wait(mutex)
- result << 2
- t.join
- end
- assert_equal([0, 1, 2], result)
+ assert_equal(num_threads*loop, r)
end
- def test_condvar_wait_not_owner
- mutex = Mutex.new
- condvar = ConditionVariable.new
-
- assert_raise(ThreadError) { condvar.wait(mutex) }
- 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
-
- locked = false
- thread = Thread.new do
- Thread.current.abort_on_exception = false
- mutex.synchronize do
- begin
- condvar.wait(mutex)
- rescue Exception
- locked = mutex.locked?
- raise
- end
- end
- end
-
- until thread.stop?
- sleep(0.1)
- end
-
- thread.raise Interrupt, "interrupt a dead condition variable"
- assert_raise(Interrupt) { thread.value }
- assert(locked)
+ def test_mutex_synchronize_yields_no_block_params
+ bug8097 = '[ruby-core:53424] [Bug #8097]'
+ assert_empty(Thread::Mutex.new.synchronize {|*params| break params}, bug8097)
end
def test_local_barrier
dir = File.dirname(__FILE__)
lbtest = File.join(dir, "lbtest.rb")
$:.unshift File.join(File.dirname(dir), 'ruby')
- require 'envutil'
$:.shift
3.times {
- result = `#{EnvUtil.rubybin} #{lbtest}`
- assert(!$?.coredump?, '[ruby-dev:30653]')
- assert_equal("exit.", result[/.*\Z/], '[ruby-dev:30653]')
+ `#{EnvUtil.rubybin} #{lbtest}`
+ assert_not_predicate($?, :coredump?, '[ruby-dev:30653]')
}
end
def test_priority
c1 = c2 = 0
- t1 = Thread.new { loop { c1 += 1 } }
- t1.priority = -1
- t2 = Thread.new { loop { c2 += 1 } }
+ run = true
+ t1 = Thread.new { c1 += 1 while run }
+ t1.priority = 3
+ t2 = Thread.new { c2 += 1 while run }
t2.priority = -3
- assert_equal(-1, t1.priority)
+ assert_equal(3, t1.priority)
assert_equal(-3, t2.priority)
sleep 0.5
5.times do
+ assert_not_predicate(t1, :stop?)
+ assert_not_predicate(t2, :stop?)
break if c1 > c2
sleep 0.1
end
+ run = false
t1.kill
t2.kill
- # assert_operator(c1, :>, c2, "[ruby-dev:33124]") # not guaranteed
+ assert_operator(c1, :>, c2, "[ruby-dev:33124]") # not guaranteed
+ t1.join
+ t2.join
end
def test_new
@@ -149,34 +188,77 @@ class TestThread < Test::Unit::TestCase
end
ensure
- t1.kill if t1
- t2.kill if t2
+ t1&.kill&.join
+ t2&.kill&.join
+ end
+
+ def test_new_symbol_proc
+ bug = '[ruby-core:80147] [Bug #13313]'
+ assert_ruby_status([], "#{<<-"begin;"}\n#{<<-'end;'}", bug)
+ begin;
+ exit("1" == Thread.start(1, &:to_s).value)
+ end;
end
def test_join
t = Thread.new { sleep }
- assert_nil(t.join(0.5))
+ assert_nil(t.join(0.05))
ensure
- t.kill if t
+ t&.kill&.join
end
def test_join2
- t1 = Thread.new { sleep(1.5) }
+ ok = false
+ t1 = Thread.new { ok = true; sleep }
+ Thread.pass until ok
+ Thread.pass until t1.stop?
t2 = Thread.new do
- t1.join(1)
+ Thread.pass while ok
+ t1.join(0.01)
end
t3 = Thread.new do
- sleep 0.5
+ ok = false
t1.join
end
assert_nil(t2.value)
+ t1.wakeup
assert_equal(t1, t3.value)
ensure
- t1.kill if t1
- t2.kill if t2
- t3.kill if t3
+ t1&.kill
+ t2&.kill
+ t3&.kill
+ end
+
+ { 'FIXNUM_MAX' => RbConfig::LIMITS['FIXNUM_MAX'],
+ 'UINT64_MAX' => RbConfig::LIMITS['UINT64_MAX'],
+ 'INFINITY' => Float::INFINITY
+ }.each do |name, limit|
+ define_method("test_join_limit_#{name}") do
+ t = Thread.new {}
+ assert_same t, t.join(limit), "limit=#{limit.inspect}"
+ end
+ end
+
+ { 'minus_1' => -1,
+ 'minus_0_1' => -0.1,
+ 'FIXNUM_MIN' => RbConfig::LIMITS['FIXNUM_MIN'],
+ 'INT64_MIN' => RbConfig::LIMITS['INT64_MIN'],
+ 'minus_INFINITY' => -Float::INFINITY
+ }.each do |name, limit|
+ define_method("test_join_limit_negative_#{name}") do
+ t = Thread.new { sleep }
+ begin
+ assert_nothing_raised(Timeout::Error) do
+ Timeout.timeout(30) do
+ assert_nil t.join(limit), "limit=#{limit.inspect}"
+ end
+ end
+ ensure
+ t.kill
+ end
+ end
end
def test_kill_main_thread
@@ -187,6 +269,24 @@ class TestThread < Test::Unit::TestCase
INPUT
end
+ def test_kill_wrong_argument
+ bug4367 = '[ruby-core:35086]'
+ assert_raise(TypeError, bug4367) {
+ Thread.kill(nil)
+ }
+ o = Object.new
+ assert_raise(TypeError, bug4367) {
+ Thread.kill(o)
+ }
+ end
+
+ def test_kill_thread_subclass
+ c = Class.new(Thread)
+ t = c.new { sleep 10 }
+ assert_nothing_raised { Thread.kill(t) }
+ assert_equal(nil, t.value)
+ end
+
def test_exit
s = 0
Thread.new do
@@ -204,15 +304,15 @@ class TestThread < Test::Unit::TestCase
Thread.stop
s += 1
end
- sleep 0.5
+ Thread.pass until t.stop?
+ sleep 1 if RubyVM::MJIT.enabled? # t.stop? behaves unexpectedly with --jit-wait
assert_equal(1, s)
t.wakeup
- sleep 0.5
+ Thread.pass while t.alive?
assert_equal(2, s)
assert_raise(ThreadError) { t.wakeup }
-
ensure
- t.kill if t
+ t&.kill&.join
end
def test_stop
@@ -230,7 +330,7 @@ class TestThread < Test::Unit::TestCase
assert_in_out_err([], <<-INPUT) do |r, e|
t1 = Thread.new { sleep }
Thread.pass
- t2 = Thread.new { loop { } }
+ t2 = Thread.new { loop { Thread.pass } }
Thread.new { }.join
p [Thread.current, t1, t2].map{|t| t.object_id }.sort
p Thread.list.map{|t| t.object_id }.sort
@@ -251,8 +351,11 @@ class TestThread < Test::Unit::TestCase
assert_in_out_err([], <<-INPUT, %w(false 1), [])
p Thread.abort_on_exception
begin
- Thread.new { raise }
- sleep 0.5
+ t = Thread.new {
+ Thread.current.report_on_exception = false
+ raise
+ }
+ Thread.pass until t.stop?
p 1
rescue
p 2
@@ -263,7 +366,10 @@ class TestThread < Test::Unit::TestCase
Thread.abort_on_exception = true
p Thread.abort_on_exception
begin
- Thread.new { raise }
+ Thread.new {
+ Thread.current.report_on_exception = false
+ raise
+ }
sleep 0.5
p 1
rescue
@@ -271,11 +377,11 @@ class TestThread < Test::Unit::TestCase
end
INPUT
- assert_in_out_err(%w(-d), <<-INPUT, %w(false 2), /.+/)
+ assert_in_out_err(%w(--disable-gems -d), <<-INPUT, %w(false 2), %r".+")
p Thread.abort_on_exception
begin
- Thread.new { raise }
- sleep 0.5
+ t = Thread.new { raise }
+ Thread.pass until t.stop?
p 1
rescue
p 2
@@ -285,9 +391,15 @@ class TestThread < Test::Unit::TestCase
assert_in_out_err([], <<-INPUT, %w(false true 2), [])
p Thread.abort_on_exception
begin
- t = Thread.new { sleep 0.5; raise }
+ ok = false
+ t = Thread.new {
+ Thread.current.report_on_exception = false
+ Thread.pass until ok
+ raise
+ }
t.abort_on_exception = true
p t.abort_on_exception
+ ok = 1
sleep 1
p 1
rescue
@@ -296,44 +408,146 @@ class TestThread < Test::Unit::TestCase
INPUT
end
+ def test_report_on_exception
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ q1 = Thread::Queue.new
+ q2 = Thread::Queue.new
+
+ assert_equal(true, Thread.report_on_exception,
+ "global flag is true by default")
+ assert_equal(true, Thread.current.report_on_exception,
+ "the main thread has report_on_exception=true")
+
+ Thread.report_on_exception = true
+ Thread.current.report_on_exception = false
+ assert_equal(true,
+ Thread.start {Thread.current.report_on_exception}.value,
+ "should not inherit from the parent thread but from the global flag")
+
+ assert_warn("", "exception should be ignored silently when false") {
+ th = Thread.start {
+ Thread.current.report_on_exception = false
+ q1.push(Thread.current.report_on_exception)
+ raise "report 1"
+ }
+ assert_equal(false, q1.pop)
+ Thread.pass while th.alive?
+ assert_raise(RuntimeError) { th.join }
+ }
+
+ assert_warn(/report 2/, "exception should be reported when true") {
+ th = Thread.start {
+ q1.push(Thread.current.report_on_exception = true)
+ raise "report 2"
+ }
+ assert_equal(true, q1.pop)
+ Thread.pass while th.alive?
+ assert_raise(RuntimeError) { th.join }
+ }
+
+ assert_warn("", "the global flag should not affect already started threads") {
+ Thread.report_on_exception = false
+ th = Thread.start {
+ q2.pop
+ q1.push(Thread.current.report_on_exception)
+ raise "report 3"
+ }
+ q2.push(Thread.report_on_exception = true)
+ assert_equal(false, q1.pop)
+ Thread.pass while th.alive?
+ assert_raise(RuntimeError) { th.join }
+ }
+
+ assert_warn(/report 4/, "should defaults to the global flag at the start") {
+ Thread.report_on_exception = true
+ th = Thread.start {
+ q1.push(Thread.current.report_on_exception)
+ raise "report 4"
+ }
+ assert_equal(true, q1.pop)
+ Thread.pass while th.alive?
+ assert_raise(RuntimeError) { th.join }
+ }
+
+ assert_warn(/report 5/, "should first report and then raise with report_on_exception + abort_on_exception") {
+ th = Thread.start {
+ Thread.current.report_on_exception = true
+ Thread.current.abort_on_exception = true
+ q2.pop
+ raise "report 5"
+ }
+ assert_raise_with_message(RuntimeError, "report 5") {
+ q2.push(true)
+ Thread.pass while th.alive?
+ }
+ assert_raise(RuntimeError) { th.join }
+ }
+ end;
+ end
+
def test_status_and_stop_p
- a = ::Thread.new { raise("die now") }
+ a = ::Thread.new {
+ Thread.current.report_on_exception = false
+ raise("die now")
+ }
b = Thread.new { Thread.stop }
c = Thread.new { Thread.exit }
- d = Thread.new { sleep }
e = Thread.current
- sleep 0.5
+ Thread.pass while a.alive? or !b.stop? or c.alive?
assert_equal(nil, a.status)
- assert(a.stop?)
+ assert_predicate(a, :stop?)
assert_equal("sleep", b.status)
- assert(b.stop?)
+ assert_predicate(b, :stop?)
assert_equal(false, c.status)
assert_match(/^#<TestThread::Thread:.* dead>$/, c.inspect)
- assert(c.stop?)
+ assert_predicate(c, :stop?)
- d.kill
- assert_equal(["aborting", false], [d.status, d.stop?])
-
- assert_equal(["run", false], [e.status, e.stop?])
+ es1 = e.status
+ es2 = e.stop?
+ assert_equal(["run", false], [es1, es2])
+ assert_raise(RuntimeError) { a.join }
+ ensure
+ b&.kill&.join
+ c&.join
+ end
+ def test_switch_while_busy_loop
+ bug1402 = "[ruby-dev:38319] [Bug #1402]"
+ flag = true
+ th = Thread.current
+ waiter = Thread.start {
+ sleep 0.1
+ flag = false
+ sleep 1
+ th.raise(bug1402)
+ }
+ assert_nothing_raised(RuntimeError, bug1402) do
+ nil while flag
+ end
+ assert(!flag, bug1402)
ensure
- a.kill if a
- b.kill if b
- c.kill if c
- d.kill if d
+ waiter&.kill&.join
end
def test_safe_level
- t = Thread.new { $SAFE = 3; sleep }
- sleep 0.5
- assert_equal(0, Thread.current.safe_level)
- assert_equal(3, t.safe_level)
-
+ ok = false
+ t = Thread.new do
+ EnvUtil.suppress_warning do
+ $SAFE = 1
+ end
+ ok = true
+ sleep
+ end
+ Thread.pass until ok
+ assert_equal($SAFE, Thread.current.safe_level)
+ assert_equal($SAFE, t.safe_level)
ensure
- t.kill if t
+ $SAFE = 0
+ t&.kill&.join
end
def test_thread_local
@@ -350,43 +564,77 @@ class TestThread < Test::Unit::TestCase
assert_equal(false, t.key?(:qux))
assert_equal(false, t.key?("qux"))
- assert_equal([:foo, :bar, :baz], t.keys)
+ assert_equal([:foo, :bar, :baz].sort, t.keys.sort)
ensure
- t.kill if t
+ t&.kill&.join
end
- def test_thread_local_security
+ def test_thread_local_fetch
t = Thread.new { sleep }
- assert_raise(SecurityError) do
- Thread.new { $SAFE = 4; t[:foo] }.join
- end
+ assert_equal(false, t.key?(:foo))
- assert_raise(SecurityError) do
- Thread.new { $SAFE = 4; t[:foo] = :baz }.join
- end
+ t["foo"] = "foo"
+ t["bar"] = "bar"
+ t["baz"] = "baz"
+
+ x = nil
+ assert_equal("foo", t.fetch(:foo, 0))
+ assert_equal("foo", t.fetch(:foo) {x = true})
+ assert_nil(x)
+ assert_equal("foo", t.fetch("foo", 0))
+ assert_equal("foo", t.fetch("foo") {x = true})
+ assert_nil(x)
+
+ x = nil
+ assert_equal(0, t.fetch(:qux, 0))
+ assert_equal(1, t.fetch(:qux) {x = 1})
+ assert_equal(1, x)
+ assert_equal(2, t.fetch("qux", 2))
+ assert_equal(3, t.fetch("qux") {x = 3})
+ assert_equal(3, x)
+
+ e = assert_raise(KeyError) {t.fetch(:qux)}
+ assert_equal(:qux, e.key)
+ assert_equal(t, e.receiver)
+ ensure
+ t&.kill&.join
+ end
- assert_raise(RuntimeError) do
- Thread.new do
- Thread.current[:foo] = :bar
- Thread.current.freeze
+ def test_thread_local_security
+ Thread.new do
+ Thread.current[:foo] = :bar
+ Thread.current.freeze
+ assert_raise(FrozenError) do
Thread.current[:foo] = :baz
- end.join
- end
+ end
+ end.join
+ end
+
+ def test_thread_local_dynamic_symbol
+ bug10667 = '[ruby-core:67185] [Bug #10667]'
+ t = Thread.new {}.join
+ key_str = "foo#{rand}"
+ key_sym = key_str.to_sym
+ t.thread_variable_set(key_str, "bar")
+ assert_equal("bar", t.thread_variable_get(key_str), "#{bug10667}: string key")
+ assert_equal("bar", t.thread_variable_get(key_sym), "#{bug10667}: symbol key")
end
def test_select_wait
- assert_nil(IO.select(nil, nil, nil, 1))
+ assert_nil(IO.select(nil, nil, nil, 0.001))
t = Thread.new do
IO.select(nil, nil, nil, nil)
end
- sleep 0.5
- t.kill
+ Thread.pass until t.stop?
+ assert_predicate(t, :alive?)
+ ensure
+ t&.kill
end
def test_mutex_deadlock
- m = Mutex.new
+ m = Thread::Mutex.new
m.synchronize do
assert_raise(ThreadError) do
m.synchronize do
@@ -397,30 +645,30 @@ class TestThread < Test::Unit::TestCase
end
def test_mutex_interrupt
- m = Mutex.new
+ m = Thread::Mutex.new
m.lock
t = Thread.new do
m.lock
:foo
end
- sleep 0.5
+ Thread.pass until t.stop?
t.kill
assert_nil(t.value)
end
def test_mutex_illegal_unlock
- m = Mutex.new
+ m = Thread::Mutex.new
m.lock
- assert_raise(ThreadError) do
- Thread.new do
+ Thread.new do
+ assert_raise(ThreadError) do
m.unlock
- end.join
- end
+ end
+ end.join
end
def test_mutex_fifo_like_lock
- m1 = Mutex.new
- m2 = Mutex.new
+ m1 = Thread::Mutex.new
+ m2 = Thread::Mutex.new
m1.lock
m2.lock
m1.unlock
@@ -428,7 +676,7 @@ class TestThread < Test::Unit::TestCase
assert_equal(false, m1.locked?)
assert_equal(false, m2.locked?)
- m3 = Mutex.new
+ m3 = Thread::Mutex.new
m1.lock
m2.lock
m3.lock
@@ -441,7 +689,7 @@ class TestThread < Test::Unit::TestCase
end
def test_mutex_trylock
- m = Mutex.new
+ m = Thread::Mutex.new
assert_equal(true, m.try_lock)
assert_equal(false, m.try_lock, '[ruby-core:20943]')
@@ -462,65 +710,239 @@ class TestThread < Test::Unit::TestCase
raise "recursive_outer should short circuit intermediate calls"
end
assert_nothing_raised {arr.hash}
- assert(obj[:visited])
+ assert(obj[:visited], "obj.hash was not called")
end
-end
-class TestThreadGroup < Test::Unit::TestCase
- def test_thread_init
- thgrp = ThreadGroup.new
- Thread.new{
- thgrp.add(Thread.current)
- assert_equal(thgrp, Thread.new{sleep 1}.group)
- }.join
+ def test_thread_instance_variable
+ bug4389 = '[ruby-core:35192]'
+ assert_in_out_err([], <<-INPUT, %w(), [], bug4389)
+ class << Thread.current
+ @data = :data
+ end
+ INPUT
end
- def test_frozen_thgroup
- thgrp = ThreadGroup.new
+ def test_no_valid_cfp
+ skip 'with win32ole, cannot run this testcase because win32ole redefines Thread#initialize' if defined?(WIN32OLE)
+ bug5083 = '[ruby-dev:44208]'
+ assert_equal([], Thread.new(&Module.method(:nesting)).value, bug5083)
+ assert_instance_of(Thread, Thread.new(:to_s, &Class.new.method(:undef_method)).join, bug5083)
+ end
- t = Thread.new{1}
- Thread.new{
- thgrp.add(Thread.current)
- thgrp.freeze
- assert_raise(ThreadError) do
- Thread.new{1}.join
- end
- assert_raise(ThreadError) do
- thgrp.add(t)
- end
- assert_raise(ThreadError) do
- ThreadGroup.new.add Thread.current
+ def make_handle_interrupt_test_thread1 flag
+ r = []
+ ready_q = Queue.new
+ done_q = Queue.new
+ th = Thread.new{
+ begin
+ Thread.handle_interrupt(RuntimeError => flag){
+ begin
+ ready_q << true
+ done_q.pop
+ rescue
+ r << :c1
+ end
+ }
+ rescue
+ r << :c2
end
- }.join
- t.join
+ }
+ ready_q.pop
+ th.raise
+ begin
+ done_q << true
+ th.join
+ rescue
+ r << :c3
+ end
+ r
end
- def test_enclosed_thgroup
- thgrp = ThreadGroup.new
- assert_equal(false, thgrp.enclosed?)
+ def test_handle_interrupt
+ [[:never, :c2],
+ [:immediate, :c1],
+ [:on_blocking, :c1]].each{|(flag, c)|
+ assert_equal([flag, c], [flag] + make_handle_interrupt_test_thread1(flag))
+ }
+ # TODO: complex cases are needed.
+ end
- t = Thread.new{1}
- Thread.new{
- thgrp.add(Thread.current)
- thgrp.enclose
- assert_equal(true, thgrp.enclosed?)
- assert_nothing_raised do
- Thread.new{1}.join
- end
- assert_raise(ThreadError) do
- thgrp.add t
+ def test_handle_interrupt_invalid_argument
+ assert_raise(ArgumentError) {
+ Thread.handle_interrupt(RuntimeError => :immediate) # no block
+ }
+ assert_raise(ArgumentError) {
+ Thread.handle_interrupt(RuntimeError => :xyzzy) {}
+ }
+ assert_raise(TypeError) {
+ Thread.handle_interrupt([]) {} # array
+ }
+ end
+
+ def for_test_handle_interrupt_with_return
+ Thread.handle_interrupt(Object => :never){
+ Thread.current.raise RuntimeError.new("have to be rescured")
+ return
+ }
+ rescue
+ end
+
+ def test_handle_interrupt_with_return
+ assert_nothing_raised do
+ for_test_handle_interrupt_with_return
+ _dummy_for_check_ints=nil
+ end
+ end
+
+ def test_handle_interrupt_with_break
+ assert_nothing_raised do
+ begin
+ Thread.handle_interrupt(Object => :never){
+ Thread.current.raise RuntimeError.new("have to be rescured")
+ break
+ }
+ rescue
end
- assert_raise(ThreadError) do
- ThreadGroup.new.add Thread.current
+ _dummy_for_check_ints=nil
+ end
+ end
+
+ def test_handle_interrupt_blocking
+ r=:ng
+ e=Class.new(Exception)
+ th_s = Thread.current
+ th = Thread.start{
+ assert_raise(RuntimeError) {
+ Thread.handle_interrupt(Object => :on_blocking){
+ begin
+ Thread.pass until r == :wait
+ Thread.current.raise RuntimeError, "will raise in sleep"
+ r = :ok
+ sleep
+ ensure
+ th_s.raise e, "raise from ensure", $@
+ end
+ }
+ }
+ }
+ assert_raise(e) {r = :wait; sleep 0.2}
+ th.join
+ assert_equal(:ok,r)
+ end
+
+ def test_handle_interrupt_and_io
+ assert_in_out_err([], <<-INPUT, %w(ok), [])
+ th_waiting = true
+
+ t = Thread.new {
+ Thread.current.report_on_exception = false
+ Thread.handle_interrupt(RuntimeError => :on_blocking) {
+ nil while th_waiting
+ # async interrupt should be raised _before_ writing puts arguments
+ puts "ng"
+ }
+ }
+
+ Thread.pass while t.stop?
+ t.raise RuntimeError
+ th_waiting = false
+ t.join rescue nil
+ puts "ok"
+ INPUT
+ end
+
+ def test_handle_interrupt_and_p
+ assert_in_out_err([], <<-INPUT, %w(:ok :ok), [])
+ th_waiting = false
+
+ t = Thread.new {
+ Thread.current.report_on_exception = false
+ Thread.handle_interrupt(RuntimeError => :on_blocking) {
+ th_waiting = true
+ nil while th_waiting
+ # p shouldn't provide interruptible point
+ p :ok
+ p :ok
+ }
+ }
+
+ Thread.pass until th_waiting
+ t.raise RuntimeError
+ th_waiting = false
+ t.join rescue nil
+ INPUT
+ end
+
+ def test_handle_interrupted?
+ q = Thread::Queue.new
+ Thread.handle_interrupt(RuntimeError => :never){
+ done = false
+ th = Thread.new{
+ q.push :e
+ begin
+ begin
+ Thread.pass until done
+ rescue => e
+ q.push :ng1
+ end
+ begin
+ Thread.handle_interrupt(Object => :immediate){} if Thread.pending_interrupt?
+ rescue RuntimeError => e
+ q.push :ok
+ end
+ rescue => e
+ q.push :ng2
+ ensure
+ q.push :ng3
+ end
+ }
+ q.pop
+ th.raise
+ done = true
+ th.join
+ assert_equal(:ok, q.pop)
+ }
+ end
+
+ def test_thread_timer_and_ensure
+ assert_normal_exit(<<_eom, 'r36492', timeout: 10)
+ flag = false
+ t = Thread.new do
+ begin
+ sleep
+ ensure
+ 1 until flag
end
- }.join
+ end
+
+ Thread.pass until t.status == "sleep"
+
+ t.kill
+ t.alive? == true
+ flag = true
t.join
+_eom
end
def test_uninitialized
- c = Class.new(Thread)
- c.class_eval { def initialize; end }
+ c = Class.new(Thread) {def initialize; end}
assert_raise(ThreadError) { c.new.start }
+
+ bug11959 = '[ruby-core:72732] [Bug #11959]'
+
+ c = Class.new(Thread) {def initialize; exit; end}
+ assert_raise(ThreadError, bug11959) { c.new }
+
+ c = Class.new(Thread) {def initialize; raise; end}
+ assert_raise(ThreadError, bug11959) { c.new }
+
+ c = Class.new(Thread) {
+ def initialize
+ pending = pending_interrupt?
+ super {pending}
+ end
+ }
+ assert_equal(false, c.new.value, bug11959)
end
def test_backtrace
@@ -532,4 +954,446 @@ class TestThreadGroup < Test::Unit::TestCase
t.join
assert_equal(nil, t.backtrace)
end
+
+ def test_thread_timer_and_interrupt
+ bug5757 = '[ruby-dev:44985]'
+ pid = nil
+ cmd = 'Signal.trap(:INT, "DEFAULT"); pipe=IO.pipe; Thread.start {Thread.pass until Thread.main.stop?; puts; STDOUT.flush}; pipe[0].read'
+ opt = {}
+ opt[:new_pgroup] = true if /mswin|mingw/ =~ RUBY_PLATFORM
+ s, t, _err = EnvUtil.invoke_ruby(['-e', cmd], "", true, true, opt) do |in_p, out_p, err_p, cpid|
+ assert IO.select([out_p], nil, nil, 10), 'subprocess not ready'
+ out_p.gets
+ pid = cpid
+ t0 = Time.now.to_f
+ Process.kill(:SIGINT, pid)
+ Timeout.timeout(10) { Process.wait(pid) }
+ t1 = Time.now.to_f
+ [$?, t1 - t0, err_p.read]
+ end
+ assert_equal(pid, s.pid, bug5757)
+ assert_equal([false, true, false, Signal.list["INT"]],
+ [s.exited?, s.signaled?, s.stopped?, s.termsig],
+ "[s.exited?, s.signaled?, s.stopped?, s.termsig]")
+ assert_include(0..2, t, bug5757)
+ end
+
+ def test_thread_join_in_trap
+ assert_separately [], <<-'EOS'
+ Signal.trap(:INT, "DEFAULT")
+ t0 = Thread.current
+ assert_nothing_raised{
+ t = Thread.new {Thread.pass until t0.stop?; Process.kill(:INT, $$)}
+
+ Signal.trap :INT do
+ t.join
+ end
+
+ t.join
+ }
+ EOS
+ end
+
+ def test_thread_value_in_trap
+ assert_separately [], <<-'EOS'
+ Signal.trap(:INT, "DEFAULT")
+ t0 = Thread.current
+ t = Thread.new {Thread.pass until t0.stop?; Process.kill(:INT, $$); :normal_end}
+
+ Signal.trap :INT do
+ t.value
+ end
+ assert_equal(:normal_end, t.value)
+ EOS
+ end
+
+ def test_thread_join_current
+ assert_raise(ThreadError) do
+ Thread.current.join
+ end
+ end
+
+ def test_thread_join_main_thread
+ Thread.new(Thread.current) {|t|
+ assert_raise(ThreadError) do
+ t.join
+ end
+ }.join
+ end
+
+ def test_main_thread_status_at_exit
+ assert_in_out_err([], <<-'INPUT', ["false false aborting"], [])
+q = Thread::Queue.new
+Thread.new(Thread.current) {|mth|
+ begin
+ q.push nil
+ mth.run
+ Thread.pass until mth.stop?
+ p :mth_stopped # don't run if killed by rb_thread_terminate_all
+ ensure
+ puts "#{mth.alive?} #{mth.status} #{Thread.current.status}"
+ end
+}
+q.pop
+ INPUT
+ end
+
+ def test_thread_status_in_trap
+ # when running trap handler, Thread#status must show "run"
+ # Even though interrupted from sleeping function
+ assert_in_out_err([], <<-INPUT, %w(sleep run), [])
+ Signal.trap(:INT) {
+ puts Thread.current.status
+ exit
+ }
+ t = Thread.current
+
+ Thread.new(Thread.current) {|mth|
+ Thread.pass until t.stop?
+ puts mth.status
+ Process.kill(:INT, $$)
+ }
+ sleep 0.1
+ INPUT
+ end
+
+ # Bug #7450
+ def test_thread_status_raise_after_kill
+ ary = []
+
+ t = Thread.new {
+ assert_raise(RuntimeError) do
+ begin
+ ary << Thread.current.status
+ sleep #1
+ ensure
+ begin
+ ary << Thread.current.status
+ sleep #2
+ ensure
+ ary << Thread.current.status
+ end
+ end
+ end
+ }
+
+ Thread.pass until ary.size >= 1
+ Thread.pass until t.stop?
+ t.kill # wake up sleep #1
+ Thread.pass until ary.size >= 2
+ Thread.pass until t.stop?
+ t.raise "wakeup" # wake up sleep #2
+ Thread.pass while t.alive?
+ assert_equal(ary, ["run", "aborting", "aborting"])
+ t.join
+ end
+
+ def test_mutex_owned
+ mutex = Thread::Mutex.new
+
+ assert_equal(mutex.owned?, false)
+ mutex.synchronize {
+ # Now, I have the mutex
+ assert_equal(mutex.owned?, true)
+ }
+ assert_equal(mutex.owned?, false)
+ end
+
+ def test_mutex_owned2
+ begin
+ mutex = Thread::Mutex.new
+ th = Thread.new {
+ # lock forever
+ mutex.lock
+ sleep
+ }
+
+ # acquired by another thread.
+ Thread.pass until mutex.locked?
+ assert_equal(mutex.owned?, false)
+ ensure
+ th&.kill
+ end
+ end
+
+ def test_mutex_unlock_on_trap
+ assert_in_out_err([], <<-INPUT, %w(locked unlocked false), [])
+ m = Thread::Mutex.new
+
+ trapped = false
+ Signal.trap("INT") { |signo|
+ m.unlock
+ trapped = true
+ puts "unlocked"
+ }
+
+ m.lock
+ puts "locked"
+ Process.kill("INT", $$)
+ Thread.pass until trapped
+ puts m.locked?
+ INPUT
+ end
+
+ def invoke_rec script, vm_stack_size, machine_stack_size, use_length = true
+ 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)
+ 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 * 10, 1024 * 1024 * 10, false))
+
+ assert_operator(h_default[:thread_vm_stack_size], :>, h_0[:thread_vm_stack_size],
+ "0 thread_vm_stack_size")
+ assert_operator(h_default[:thread_vm_stack_size], :<, h_large[:thread_vm_stack_size],
+ "large thread_vm_stack_size")
+ assert_operator(h_default[:thread_machine_stack_size], :>=, h_0[:thread_machine_stack_size],
+ "0 thread_machine_stack_size")
+ assert_operator(h_default[:thread_machine_stack_size], :<=, h_large[:thread_machine_stack_size],
+ "large thread_machine_stack_size")
+ end
+
+ def test_vm_machine_stack_size
+ script = 'def rec; print "."; STDOUT.flush; rec; end; rec'
+ size_default = invoke_rec script, nil, nil
+ assert_operator(size_default, :>, 0, "default size")
+ size_0 = invoke_rec script, 0, nil
+ assert_operator(size_default, :>, size_0, "0 size")
+ size_large = invoke_rec script, 1024 * 1024 * 10, nil
+ assert_operator(size_default, :<, size_large, "large size")
+ end
+
+ def test_machine_stack_size
+ # check machine stack size
+ # Note that machine stack size may not change size (depend on OSs)
+ script = 'def rec; print "."; STDOUT.flush; 1.times{1.times{1.times{rec}}}; end; Thread.new{rec}.join'
+ 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, "0 size")
+ size_large = invoke_rec script, vm_stack_size, 1024 * 1024 * 10
+ assert_operator(size_default, :<=, size_large, "large size")
+ end unless /mswin|mingw/ =~ RUBY_PLATFORM
+
+ def test_blocking_mutex_unlocked_on_fork
+ bug8433 = '[ruby-core:55102] [Bug #8433]'
+
+ mutex = Thread::Mutex.new
+ flag = false
+ mutex.lock
+
+ th = Thread.new do
+ mutex.synchronize do
+ flag = true
+ sleep
+ end
+ end
+
+ Thread.pass until th.stop?
+ mutex.unlock
+
+ pid = Process.fork do
+ exit(mutex.locked?)
+ end
+
+ th.kill
+
+ pid, status = Process.waitpid2(pid)
+ assert_equal(false, status.success?, bug8433)
+ end if Process.respond_to?(:fork)
+
+ def test_fork_in_thread
+ bug9751 = '[ruby-core:62070] [Bug #9751]'
+ f = nil
+ th = Thread.start do
+ unless f = IO.popen("-")
+ STDERR.reopen(STDOUT)
+ exit
+ end
+ Process.wait2(f.pid)
+ end
+ unless th.join(EnvUtil.apply_timeout_scale(30))
+ Process.kill(:QUIT, f.pid)
+ Process.kill(:KILL, f.pid) unless th.join(EnvUtil.apply_timeout_scale(1))
+ end
+ _, status = th.value
+ output = f.read
+ f.close
+ assert_not_predicate(status, :signaled?, FailDesc[status, bug9751, output])
+ assert_predicate(status, :success?, bug9751)
+ end if Process.respond_to?(:fork)
+
+ def test_fork_while_locked
+ m = Mutex.new
+ thrs = []
+ 3.times do |i|
+ thrs << Thread.new { m.synchronize { Process.waitpid2(fork{})[1] } }
+ end
+ thrs.each do |t|
+ assert_predicate t.value, :success?, '[ruby-core:85940] [Bug #14578]'
+ end
+ end if Process.respond_to?(:fork)
+
+ def test_fork_while_parent_locked
+ skip 'needs fork' unless Process.respond_to?(:fork)
+ m = Thread::Mutex.new
+ nr = 1
+ thrs = []
+ m.synchronize do
+ thrs = nr.times.map { Thread.new { m.synchronize {} } }
+ thrs.each { Thread.pass }
+ pid = fork do
+ m.locked? or exit!(2)
+ thrs = nr.times.map { Thread.new { m.synchronize {} } }
+ m.unlock
+ thrs.each { |t| t.join(1) == t or exit!(1) }
+ exit!(0)
+ end
+ _, st = Process.waitpid2(pid)
+ assert_predicate st, :success?, '[ruby-core:90312] [Bug #15383]'
+ end
+ thrs.each { |t| assert_same t, t.join(1) }
+ end
+
+ def test_fork_while_mutex_locked_by_forker
+ skip 'needs fork' unless Process.respond_to?(:fork)
+ m = Mutex.new
+ m.synchronize do
+ pid = fork do
+ exit!(2) unless m.locked?
+ m.unlock rescue exit!(3)
+ m.synchronize {} rescue exit!(4)
+ exit!(0)
+ end
+ _, st = Timeout.timeout(30) { Process.waitpid2(pid) }
+ assert_predicate st, :success?, '[ruby-core:90595] [Bug #15430]'
+ end
+ end
+
+ def test_subclass_no_initialize
+ t = Module.new do
+ break eval("class C\u{30b9 30ec 30c3 30c9} < Thread; self; end")
+ end
+ t.class_eval do
+ def initialize
+ end
+ end
+ assert_raise_with_message(ThreadError, /C\u{30b9 30ec 30c3 30c9}/) do
+ t.new {}
+ end
+ end
+
+ def test_thread_name
+ t = Thread.start {sleep}
+ sleep 0.001 until t.stop?
+ assert_nil t.name
+ s = t.inspect
+ t.name = 'foo'
+ assert_equal 'foo', t.name
+ t.name = nil
+ assert_nil t.name
+ assert_equal s, t.inspect
+ ensure
+ t.kill
+ t.join
+ end
+
+ def test_thread_invalid_name
+ bug11756 = '[ruby-core:71774] [Bug #11756]'
+ t = Thread.start {}
+ assert_raise(ArgumentError, bug11756) {t.name = "foo\0bar"}
+ assert_raise(ArgumentError, bug11756) {t.name = "foo".encode(Encoding::UTF_32BE)}
+ ensure
+ t.kill
+ t.join
+ end
+
+ def test_thread_invalid_object
+ bug11756 = '[ruby-core:71774] [Bug #11756]'
+ t = Thread.start {}
+ assert_raise(TypeError, bug11756) {t.name = []}
+ ensure
+ t.kill
+ t.join
+ end
+
+ def test_thread_setname_in_initialize
+ bug12290 = '[ruby-core:74963] [Bug #12290]'
+ c = Class.new(Thread) {def initialize() self.name = "foo"; super; end}
+ assert_equal("foo", c.new {Thread.current.name}.value, bug12290)
+ 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?
+
+ assert_normal_exit(<<-_end, '[Bug #8996]', opts)
+ Thread.report_on_exception = false
+ trap(:TERM){exit}
+ while true
+ t = Thread.new{sleep 0}
+ t.raise Interrupt
+ Thread.pass # allow t to finish
+ end
+ _end
+ end
+
+ def test_signal_at_join
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ skip "can't trap a signal from another process on Windows"
+ # opt = {new_pgroup: true}
+ end
+ assert_separately([], "#{<<~"{#"}\n#{<<~'};'}", timeout: 120)
+ {#
+ n = 1000
+ sig = :INT
+ trap(sig) {}
+ IO.popen([EnvUtil.rubybin, "-e", "#{<<~"{#1"}\n#{<<~'};#1'}"], "r+") do |f|
+ tpid = #{$$}
+ sig = :#{sig}
+ {#1
+ STDOUT.sync = true
+ while gets
+ puts
+ Process.kill(sig, tpid)
+ end
+ };#1
+ assert_nothing_raised do
+ n.times do
+ w = Thread.start do
+ sleep 30
+ end
+ begin
+ f.puts
+ f.gets
+ ensure
+ w.kill
+ w.join
+ end
+ end
+ end
+ n.times do
+ w = Thread.start { sleep 30 }
+ begin
+ f.puts
+ f.gets
+ ensure
+ w.kill
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ w.join(30)
+ t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ diff = t1 - t0
+ assert_operator diff, :<=, 2
+ end
+ end
+ end
+ };
+ end
end
diff --git a/test/ruby/test_thread_cv.rb b/test/ruby/test_thread_cv.rb
new file mode 100644
index 0000000000..38bcc3b8fa
--- /dev/null
+++ b/test/ruby/test_thread_cv.rb
@@ -0,0 +1,245 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'tmpdir'
+
+class TestThreadConditionVariable < Test::Unit::TestCase
+ ConditionVariable = Thread::ConditionVariable
+ Mutex = Thread::Mutex
+
+ def test_initialized
+ assert_raise(TypeError) {
+ ConditionVariable.allocate.wait(nil)
+ }
+ end
+
+ def test_condvar_signal_and_wait
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+ result = []
+ mutex.synchronize do
+ t = Thread.new do
+ mutex.synchronize do
+ result << 1
+ condvar.signal
+ end
+ end
+
+ result << 0
+ condvar.wait(mutex)
+ result << 2
+ t.join
+ end
+ assert_equal([0, 1, 2], result)
+ 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
+
+ locked = false
+ thread = Thread.new do
+ Thread.current.abort_on_exception = false
+ mutex.synchronize do
+ assert_raise(Interrupt) {
+ condvar.wait(mutex)
+ }
+ locked = mutex.locked?
+ end
+ end
+
+ until thread.stop?
+ sleep(0.1)
+ end
+
+ thread.raise Interrupt, "interrupt a dead condition variable"
+ thread.join
+ assert(locked)
+ end
+
+ def test_condvar_wait_and_broadcast
+ nr_threads = 3
+ threads = Array.new
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+ result = []
+
+ nr_threads.times do |i|
+ threads[i] = Thread.new do
+ mutex.synchronize do
+ result << "C1"
+ condvar.wait mutex
+ result << "C2"
+ end
+ end
+ end
+ sleep 0.1
+ mutex.synchronize do
+ result << "P1"
+ condvar.broadcast
+ result << "P2"
+ end
+ Timeout.timeout(5) do
+ nr_threads.times do |i|
+ threads[i].join
+ end
+ end
+
+ assert_equal ["C1", "C1", "C1", "P1", "P2", "C2", "C2", "C2"], result
+ end
+
+ def test_condvar_wait_deadlock
+ assert_in_out_err([], <<-INPUT, /\Afatal\nNo live threads left\. Deadlock/, [])
+ mutex = Mutex.new
+ cv = ConditionVariable.new
+
+ klass = nil
+ mesg = nil
+ begin
+ mutex.lock
+ cv.wait mutex
+ mutex.unlock
+ rescue Exception => e
+ klass = e.class
+ mesg = e.message
+ end
+ puts klass
+ print mesg
+INPUT
+ end
+
+ def test_condvar_wait_deadlock_2
+ nr_threads = 3
+ threads = Array.new
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ nr_threads.times do |i|
+ if (i != 0)
+ mutex.unlock
+ end
+ threads[i] = Thread.new do
+ mutex.synchronize do
+ condvar.wait mutex
+ end
+ end
+ mutex.lock
+ end
+
+ assert_raise(Timeout::Error) do
+ Timeout.timeout(0.1) { condvar.wait mutex }
+ end
+ mutex.unlock
+ threads.each(&:kill)
+ threads.each(&:join)
+ end
+
+ def test_condvar_timed_wait
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+ timeout = 0.3
+ locked = false
+
+ t0 = Time.now
+ mutex.synchronize do
+ begin
+ condvar.wait(mutex, timeout)
+ ensure
+ locked = mutex.locked?
+ end
+ end
+ t1 = Time.now
+ t = t1-t0
+
+ assert_operator(timeout*0.9, :<, t)
+ assert(locked)
+ end
+
+ def test_condvar_nolock
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ assert_raise(ThreadError) {condvar.wait(mutex)}
+ end
+
+ def test_condvar_nolock_2
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ Thread.new do
+ assert_raise(ThreadError) {condvar.wait(mutex)}
+ end.join
+ end
+
+ def test_condvar_nolock_3
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ Thread.new do
+ assert_raise(ThreadError) {condvar.wait(mutex, 0.1)}
+ end.join
+ end
+
+ def test_condvar_empty_signal
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ assert_nothing_raised(Exception) { mutex.synchronize {condvar.signal} }
+ end
+
+ def test_condvar_empty_broadcast
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+
+ assert_nothing_raised(Exception) { mutex.synchronize {condvar.broadcast} }
+ end
+
+ def test_dup
+ bug9440 = '[ruby-core:59961] [Bug #9440]'
+ condvar = ConditionVariable.new
+ assert_raise(NoMethodError, bug9440) do
+ condvar.dup
+ end
+ end
+
+ (DumpableCV = ConditionVariable.dup).class_eval {remove_method :marshal_dump}
+
+ def test_dump
+ bug9674 = '[ruby-core:61677] [Bug #9674]'
+ condvar = ConditionVariable.new
+ assert_raise_with_message(TypeError, /#{ConditionVariable}/, bug9674) do
+ Marshal.dump(condvar)
+ end
+
+ condvar = DumpableCV.new
+ assert_raise(TypeError, bug9674) do
+ Marshal.dump(condvar)
+ end
+ end
+
+ def test_condvar_fork
+ mutex = Mutex.new
+ condvar = ConditionVariable.new
+ thrs = (1..10).map do
+ Thread.new { mutex.synchronize { condvar.wait(mutex) } }
+ end
+ thrs.each { 3.times { Thread.pass } }
+ pid = fork do
+ th = Thread.new do
+ mutex.synchronize { condvar.wait(mutex) }
+ :ok
+ end
+ until th.join(0.01)
+ mutex.synchronize { condvar.broadcast }
+ end
+ exit!(th.value == :ok ? 0 : 1)
+ end
+ _, s = Process.waitpid2(pid)
+ assert_predicate s, :success?, 'no segfault [ruby-core:86316] [Bug #14634]'
+ until thrs.empty?
+ mutex.synchronize { condvar.broadcast }
+ thrs.delete_if { |t| t.join(0.01) }
+ end
+ end if Process.respond_to?(:fork)
+end
diff --git a/test/ruby/test_thread_queue.rb b/test/ruby/test_thread_queue.rb
new file mode 100644
index 0000000000..8cebbbecb4
--- /dev/null
+++ b/test/ruby/test_thread_queue.rb
@@ -0,0 +1,619 @@
+# frozen_string_literal: false
+require 'test/unit'
+require 'tmpdir'
+require 'timeout'
+
+class TestThreadQueue < Test::Unit::TestCase
+ Queue = Thread::Queue
+ SizedQueue = Thread::SizedQueue
+
+ def test_queue_initialized
+ assert_raise(TypeError) {
+ Queue.allocate.push(nil)
+ }
+ end
+
+ def test_sized_queue_initialized
+ assert_raise(TypeError) {
+ SizedQueue.allocate.push(nil)
+ }
+ end
+
+ def test_queue
+ grind(5, 1000, 15, Queue)
+ end
+
+ def test_sized_queue
+ grind(5, 1000, 15, SizedQueue, 1000)
+ end
+
+ def grind(num_threads, num_objects, num_iterations, klass, *args)
+ from_workers = klass.new(*args)
+ to_workers = klass.new(*args)
+
+ workers = (1..num_threads).map {
+ Thread.new {
+ while object = to_workers.pop
+ from_workers.push object
+ end
+ }
+ }
+
+ Thread.new {
+ num_iterations.times {
+ num_objects.times { to_workers.push 99 }
+ num_objects.times { from_workers.pop }
+ }
+ }.join
+
+ # close the queue the old way to test for backwards-compatibility
+ num_threads.times { to_workers.push nil }
+ workers.each { |t| t.join }
+
+ assert_equal 0, from_workers.size
+ assert_equal 0, to_workers.size
+ end
+
+ def test_sized_queue_initialize
+ q = SizedQueue.new(1)
+ assert_equal 1, q.max
+ assert_raise(ArgumentError) { SizedQueue.new(0) }
+ assert_raise(ArgumentError) { SizedQueue.new(-1) }
+ end
+
+ def test_sized_queue_assign_max
+ q = SizedQueue.new(2)
+ assert_equal(2, q.max)
+ q.max = 1
+ assert_equal(1, q.max)
+ assert_raise(ArgumentError) { q.max = 0 }
+ assert_equal(1, q.max)
+ assert_raise(ArgumentError) { q.max = -1 }
+ assert_equal(1, q.max)
+
+ before = q.max
+ q.max.times { q << 1 }
+ t1 = Thread.new { q << 1 }
+ sleep 0.01 until t1.stop?
+ q.max = q.max + 1
+ assert_equal before + 1, q.max
+ ensure
+ t1.join if t1
+ end
+
+ def test_queue_pop_interrupt
+ q = Queue.new
+ t1 = Thread.new { q.pop }
+ sleep 0.01 until t1.stop?
+ t1.kill.join
+ assert_equal(0, q.num_waiting)
+ end
+
+ def test_queue_pop_non_block
+ q = Queue.new
+ assert_raise_with_message(ThreadError, /empty/) do
+ q.pop(true)
+ end
+ end
+
+ def test_sized_queue_pop_interrupt
+ q = SizedQueue.new(1)
+ t1 = Thread.new { q.pop }
+ sleep 0.01 until t1.stop?
+ t1.kill.join
+ assert_equal(0, q.num_waiting)
+ end
+
+ def test_sized_queue_pop_non_block
+ q = 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.push(1)
+ assert_raise_with_message(ThreadError, /full/) do
+ q.push(2, true)
+ end
+ end
+
+ def test_sized_queue_push_non_block
+ q = SizedQueue.new(1)
+ q.push(1)
+ t1 = Thread.new { q.push(2) }
+ sleep 0.01 until t1.stop?
+ t1.kill.join
+ assert_equal(0, q.num_waiting)
+ end
+
+ def test_thr_kill
+ bug5343 = '[ruby-core:39634]'
+ Dir.mktmpdir {|d|
+ timeout = 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
+ r, w = IO.pipe
+ th = Thread.start {
+ queue.push(nil)
+ r.read 1
+ }
+ queue.pop
+ th.kill
+ th.join
+ end
+ _eom
+ rescue Timeout::Error
+ count = File.read("#{d}/test_thr_kill_count").to_i
+ flunk "only #{count}/#{total_count} done in #{timeout} seconds."
+ end
+ }
+ end
+
+ def test_queue_push_return_value
+ q = Queue.new
+ retval = q.push(1)
+ assert_same q, retval
+ end
+
+ def test_queue_clear_return_value
+ q = 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)
+ 2.times { sq << 1 }
+
+ t1 = Thread.new do
+ sq << 1
+ end
+
+ t2 = Thread.new do
+ sq << 1
+ end
+
+ t3 = Thread.new do
+ Thread.pass
+ sq.clear
+ end
+
+ [t3, t2, t1].each(&:join)
+ assert_equal sq.length, 2
+ end
+
+ def test_sized_queue_push_return_value
+ q = SizedQueue.new(1)
+ retval = q.push(1)
+ assert_same q, retval
+ end
+
+ def test_sized_queue_clear_return_value
+ q = SizedQueue.new(1)
+ retval = q.clear
+ assert_same q, retval
+ end
+
+ def test_sized_queue_throttle
+ q = SizedQueue.new(1)
+ i = 0
+ consumer = Thread.new do
+ while q.pop
+ i += 1
+ Thread.pass
+ end
+ end
+ nprod = 4
+ npush = 100
+
+ producer = nprod.times.map do
+ Thread.new do
+ npush.times { q.push(true) }
+ end
+ end
+ producer.each(&:join)
+ q.push(nil)
+ consumer.join
+ assert_equal(nprod * npush, i)
+ end
+
+ def test_queue_thread_raise
+ q = Queue.new
+ th1 = Thread.new do
+ begin
+ q.pop
+ rescue RuntimeError
+ sleep
+ end
+ end
+ th2 = Thread.new do
+ sleep 0.1
+ q.pop
+ end
+ sleep 0.1
+ th1.raise
+ sleep 0.1
+ q << :s
+ assert_nothing_raised(Timeout::Error) do
+ Timeout.timeout(1) { th2.join }
+ end
+ ensure
+ [th1, th2].each do |th|
+ if th and th.alive?
+ th.wakeup
+ th.join
+ end
+ end
+ end
+
+ def test_dup
+ bug9440 = '[ruby-core:59961] [Bug #9440]'
+ q = Queue.new
+ assert_raise(NoMethodError, bug9440) do
+ q.dup
+ end
+ end
+
+ (DumpableQueue = Queue.dup).class_eval {remove_method :marshal_dump}
+
+ def test_dump
+ bug9674 = '[ruby-core:61677] [Bug #9674]'
+ q = Queue.new
+ assert_raise_with_message(TypeError, /#{Queue}/, bug9674) do
+ Marshal.dump(q)
+ end
+
+ sq = SizedQueue.new(1)
+ assert_raise_with_message(TypeError, /#{SizedQueue}/, bug9674) do
+ Marshal.dump(sq)
+ end
+
+ q = DumpableQueue.new
+ assert_raise(TypeError, bug9674) do
+ Marshal.dump(q)
+ end
+ end
+
+ def test_close
+ [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
+ q = qcreate.call
+ assert_equal false, q.closed?
+ q << :something
+ assert_equal q, q.close
+ assert q.closed?
+ assert_raise_with_message(ClosedQueueError, /closed/){q << :nothing}
+ assert_equal q.pop, :something
+ assert_nil q.pop
+ assert_nil q.pop
+ # non-blocking
+ assert_raise_with_message(ThreadError, /queue empty/){q.pop(non_block=true)}
+ end
+ end
+
+ # test that waiting producers are woken up on close
+ def close_wakeup( num_items, num_threads, &qcreate )
+ raise "This test won't work with num_items(#{num_items}) >= num_threads(#{num_threads})" if num_items >= num_threads
+
+ # create the Queue
+ q = yield
+ threads = num_threads.times.map{Thread.new{q.pop}}
+ num_items.times{|i| q << i}
+
+ # wait until queue empty
+ (Thread.pass; sleep 0.01) until q.size == 0
+
+ # close the queue so remaining threads will wake up
+ q.close
+
+ # wait for them to go away
+ Thread.pass until threads.all?{|thr| thr.status == false}
+
+ # check that they've gone away. Convert nil to -1 so we can sort and do the comparison
+ expected_values = [-1] * (num_threads - num_items) + num_items.times.to_a
+ assert_equal expected_values, threads.map{|thr| thr.value || -1 }.sort
+ end
+
+ def test_queue_close_wakeup
+ close_wakeup(15, 18){Queue.new}
+ end
+
+ def test_size_queue_close_wakeup
+ close_wakeup(5, 8){SizedQueue.new 9}
+ end
+
+ def test_sized_queue_one_closed_interrupt
+ q = SizedQueue.new 1
+ q << :one
+ t1 = Thread.new { q << :two }
+ sleep 0.01 until t1.stop?
+ q.close
+
+ t1.kill.join
+ assert_equal 1, q.size
+ assert_equal :one, q.pop
+ assert q.empty?, "queue not empty"
+ end
+
+ # make sure that shutdown state is handled properly by empty? for the non-blocking case
+ def test_empty_non_blocking
+ return
+ q = SizedQueue.new 3
+ 3.times{|i| q << i}
+
+ # these all block cos the queue is full
+ prod_threads = 4.times.map{|i| Thread.new{q << 3+i}}
+ sleep 0.01 until prod_threads.all?{|thr| thr.status == 'sleep'}
+ q.close
+
+ items = []
+ # sometimes empty? is false but pop will raise ThreadError('empty'),
+ # meaning a value is not immediately available but will be soon.
+ until q.empty?
+ items << q.pop(non_block=true) rescue nil
+ end
+ items.compact!
+
+ assert_equal 7.times.to_a, items.sort
+ assert q.empty?
+ end
+
+ def test_sized_queue_closed_push_non_blocking
+ q = 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
+ prod_threads = 6.times.map do |i|
+ thr = Thread.new{
+ Thread.current.report_on_exception = false
+ q << i
+ }
+ thr[:pc] = i
+ thr
+ end
+
+ # wait until some producer threads have finished, and the other 3 are blocked
+ sleep 0.01 while prod_threads.reject{|t| t.status}.count < 3
+ # this would ensure that all producer threads call push before close
+ # sleep 0.01 while prod_threads.select{|t| t.status == 'sleep'}.count < 3
+ q.close
+
+ # more than prod_threads
+ cons_threads = 10.times.map do |i|
+ thr = Thread.new{q.pop}; thr[:pc] = i; thr
+ end
+
+ # values that came from the queue
+ popped_values = cons_threads.map &:value
+
+ # wait untl all threads have finished
+ sleep 0.01 until prod_threads.find_all{|t| t.status}.count == 0
+
+ # pick only the producer threads that got in before close
+ successful_prod_threads = prod_threads.reject{|thr| thr.status == nil}
+ assert_nothing_raised{ successful_prod_threads.map(&:value) }
+
+ # the producer threads that tried to push after q.close should all fail
+ unsuccessful_prod_threads = prod_threads - successful_prod_threads
+ unsuccessful_prod_threads.each do |thr|
+ assert_raise(ClosedQueueError){ thr.value }
+ end
+
+ assert_equal cons_threads.size, popped_values.size
+ assert_equal 0, q.size
+
+ # check that consumer threads with values match producers that called push before close
+ assert_equal successful_prod_threads.map{|thr| thr[:pc]}, popped_values.compact.sort
+ assert_nil q.pop
+ end
+
+ def test_deny_pushers
+ [->{Queue.new}, ->{SizedQueue.new 3}].each do |qcreate|
+ q = qcreate[]
+ synq = Queue.new
+ prod_threads = 20.times.map do |i|
+ Thread.new {
+ synq.pop
+ assert_raise(ClosedQueueError) {
+ q << i
+ }
+ }
+ end
+ q.close
+ synq.close # start producer threads
+
+ prod_threads.each(&:join)
+ end
+ end
+
+ # size should account for waiting pushers during shutdown
+ def sized_queue_size_close
+ q = SizedQueue.new 4
+ 4.times{|i| q << i}
+ Thread.new{ q << 5 }
+ Thread.new{ q << 6 }
+ assert_equal 4, q.size
+ assert_equal 4, q.items
+ q.close
+ assert_equal 6, q.size
+ assert_equal 4, q.items
+ end
+
+ def test_blocked_pushers_empty
+ q = SizedQueue.new 3
+ prod_threads = 6.times.map do |i|
+ Thread.new{
+ Thread.current.report_on_exception = false
+ q << i
+ }
+ end
+
+ # this ensures that all producer threads call push before close
+ sleep 0.01 while prod_threads.select{|t| t.status == 'sleep'}.count < 3
+ q.close
+
+ ary = []
+ until q.empty?
+ ary << q.pop
+ end
+ assert_equal 0, q.size
+
+ assert_equal 3, ary.size
+ ary.each{|e| assert [0,1,2,3,4,5].include?(e)}
+ assert_nil q.pop
+
+ prod_threads.each{|t|
+ begin
+ t.join
+ rescue => e
+ end
+ }
+ end
+
+ # test thread wakeup on one-element SizedQueue with close
+ def test_one_element_sized_queue
+ q = 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|
+ q = qcreate[]
+ q.close
+ assert_nothing_raised(ClosedQueueError){q.close}
+ end
+ end
+
+ def test_queue_close_multi_multi
+ q = SizedQueue.new rand(800..1200)
+
+ count_items = rand(3000..5000)
+ count_producers = rand(10..20)
+
+ producers = count_producers.times.map do
+ Thread.new do
+ sleep(rand / 100)
+ count_items.times{|i| q << [i,"#{i} for #{Thread.current.inspect}"]}
+ end
+ end
+
+ consumers = rand(7..12).times.map do
+ Thread.new do
+ count = 0
+ while e = q.pop
+ i, st = e
+ count += 1 if i.is_a?(Integer) && st.is_a?(String)
+ end
+ count
+ end
+ end
+
+ # No dead or finished threads, give up to 10 seconds to start running
+ t = Time.now
+ Thread.pass until Time.now - t > 10 || (consumers + producers).all?{|thr| thr.status =~ /\A(?:run|sleep)\z/}
+
+ assert (consumers + producers).all?{|thr| thr.status =~ /\A(?:run|sleep)\z/}, 'no threads running'
+
+ # just exercising the concurrency of the support methods.
+ counter = Thread.new do
+ until q.closed? && q.empty?
+ raise if q.size > q.max
+ # otherwise this exercise causes too much contention on the lock
+ sleep 0.01
+ end
+ end
+
+ producers.each &:join
+ q.close
+
+ # results not randomly distributed. Not sure why.
+ # consumers.map{|thr| thr.value}.each do |x|
+ # assert_not_equal 0, x
+ # end
+
+ all_items_count = consumers.map{|thr| thr.value}.inject(:+)
+ assert_equal count_items * count_producers, all_items_count
+
+ # don't leak this thread
+ assert_nothing_raised{counter.join}
+ end
+
+ def test_queue_with_trap
+ if ENV['APPVEYOR'] == 'True' && RUBY_PLATFORM.match?(/mswin/)
+ skip 'This test fails too often on AppVeyor vs140'
+ end
+ assert_in_out_err([], <<-INPUT, %w(INT INT exit), [])
+ q = Queue.new
+ trap(:INT){
+ q.push 'INT'
+ }
+ Thread.new{
+ loop{
+ Process.kill :INT, $$
+ }
+ }
+ puts q.pop
+ puts q.pop
+ puts 'exit'
+ INPUT
+ end
+
+ def test_fork_while_queue_waiting
+ q = Queue.new
+ sq = SizedQueue.new(1)
+ thq = Thread.new { q.pop }
+ thsq = Thread.new { sq.pop }
+ Thread.pass until thq.stop? && thsq.stop?
+
+ pid = fork do
+ exit!(1) if q.num_waiting != 0
+ exit!(2) if sq.num_waiting != 0
+ exit!(6) unless q.empty?
+ exit!(7) unless sq.empty?
+ q.push :child_q
+ sq.push :child_sq
+ exit!(3) if q.pop != :child_q
+ exit!(4) if sq.pop != :child_sq
+ exit!(0)
+ end
+ _, s = Process.waitpid2(pid)
+ assert_predicate s, :success?, 'no segfault [ruby-core:86316] [Bug #14634]'
+
+ q.push :thq
+ sq.push :thsq
+ assert_equal :thq, thq.value
+ assert_equal :thsq, thsq.value
+
+ sq.push(1)
+ th = Thread.new { q.pop; sq.pop }
+ thsq = Thread.new { sq.push(2) }
+ Thread.pass until th.stop? && thsq.stop?
+ pid = fork do
+ exit!(1) if q.num_waiting != 0
+ exit!(2) if sq.num_waiting != 0
+ exit!(3) unless q.empty?
+ exit!(4) if sq.empty?
+ exit!(5) if sq.pop != 1
+ exit!(0)
+ end
+ _, s = Process.waitpid2(pid)
+ assert_predicate s, :success?, 'no segfault [ruby-core:86316] [Bug #14634]'
+
+ assert_predicate thsq, :stop?
+ assert_equal 1, sq.pop
+ assert_same sq, thsq.value
+ q.push('restart th')
+ assert_equal 2, th.value
+ end if Process.respond_to?(:fork)
+end
diff --git a/test/ruby/test_threadgroup.rb b/test/ruby/test_threadgroup.rb
new file mode 100644
index 0000000000..ec95bd6419
--- /dev/null
+++ b/test/ruby/test_threadgroup.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: false
+require 'test/unit'
+
+class TestThreadGroup < Test::Unit::TestCase
+ def test_thread_init
+ thgrp = ThreadGroup.new
+ th = Thread.new{
+ thgrp.add(Thread.current)
+ Thread.new{sleep 1}
+ }.value
+ assert_equal(thgrp, th.group)
+ ensure
+ th.join
+ end
+
+ def test_frozen_thgroup
+ thgrp = ThreadGroup.new
+
+ t = Thread.new{1}
+ Thread.new{
+ thgrp.add(Thread.current)
+ thgrp.freeze
+ assert_raise(ThreadError) do
+ Thread.new{1}.join
+ end
+ assert_raise(ThreadError) do
+ thgrp.add(t)
+ end
+ assert_raise(ThreadError) do
+ ThreadGroup.new.add Thread.current
+ end
+ }.join
+ t.join
+ end
+
+ def test_enclosed_thgroup
+ thgrp = ThreadGroup.new
+ assert_equal(false, thgrp.enclosed?)
+
+ t = Thread.new{1}
+ Thread.new{
+ thgrp.add(Thread.current)
+ thgrp.enclose
+ assert_equal(true, thgrp.enclosed?)
+ assert_nothing_raised do
+ Thread.new{1}.join
+ end
+ assert_raise(ThreadError) do
+ thgrp.add t
+ end
+ assert_raise(ThreadError) do
+ ThreadGroup.new.add Thread.current
+ end
+ }.join
+ t.join
+ end
+end
diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb
index 95d36c597f..3b3711b805 100644
--- a/test/ruby/test_time.rb
+++ b/test/ruby/test_time.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require 'rational'
require 'delegate'
require 'timeout'
require 'delegate'
@@ -14,11 +14,39 @@ class TestTime < Test::Unit::TestCase
$VERBOSE = @verbose
end
+ def in_timezone(zone)
+ orig_zone = ENV['TZ']
+
+ ENV['TZ'] = zone
+ yield
+ ensure
+ ENV['TZ'] = orig_zone
+ end
+
+ def no_leap_seconds?
+ # 1972-06-30T23:59:60Z is the first leap second.
+ Time.utc(1972, 7, 1, 0, 0, 0) - Time.utc(1972, 6, 30, 23, 59, 59) == 1
+ end
+
+ def get_t2000
+ if no_leap_seconds?
+ # Sat Jan 01 00:00:00 UTC 2000
+ Time.at(946684800).gmtime
+ else
+ Time.utc(2000, 1, 1)
+ end
+ end
+
def test_new
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,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]'
+ tm = [2001,2,28,23,59,30]
+ 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") }
end
def test_time_add()
@@ -28,14 +56,14 @@ class TestTime < Test::Unit::TestCase
Time.utc(2000, 3, 21, 0, 30))
assert_equal(0, (Time.at(1.1) + 0.9).usec)
- assert((Time.utc(2000, 4, 1) + 24).utc?)
- assert(!(Time.local(2000, 4, 1) + 24).utc?)
+ assert_predicate((Time.utc(2000, 4, 1) + 24), :utc?)
+ assert_not_predicate((Time.local(2000, 4, 1) + 24), :utc?)
t = Time.new(2000, 4, 1, 0, 0, 0, "+01:00") + 24
- assert(!t.utc?)
+ assert_not_predicate(t, :utc?)
assert_equal(3600, t.utc_offset)
t = Time.new(2000, 4, 1, 0, 0, 0, "+02:00") + 24
- assert(!t.utc?)
+ assert_not_predicate(t, :utc?)
assert_equal(7200, t.utc_offset)
end
@@ -76,15 +104,13 @@ class TestTime < Test::Unit::TestCase
assert_equal(31536000, Time.utc(1971, 1, 1, 0, 0, 0).tv_sec)
assert_equal(78796799, Time.utc(1972, 6, 30, 23, 59, 59).tv_sec)
- # 1972-06-30T23:59:60Z is the first leap second.
- if Time.utc(1972, 7, 1, 0, 0, 0) - Time.utc(1972, 6, 30, 23, 59, 59) == 1
- # no leap second.
+ if no_leap_seconds?
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)
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
- # leap seconds supported.
assert_equal(2, Time.utc(1972, 7, 1, 0, 0, 0) - Time.utc(1972, 6, 30, 23, 59, 59))
assert_equal(78796800, Time.utc(1972, 6, 30, 23, 59, 60).tv_sec)
assert_equal(78796801, Time.utc(1972, 7, 1, 0, 0, 0).tv_sec)
@@ -146,6 +172,7 @@ class TestTime < Test::Unit::TestCase
assert_equal(100000, Time.at(0.0001).nsec)
assert_equal(10000, Time.at(0.00001).nsec)
assert_equal(3000, Time.at(0.000003).nsec)
+ assert_equal(200, Time.at(0.0000002r).nsec)
assert_equal(199, Time.at(0.0000002).nsec)
assert_equal(10, Time.at(0.00000001).nsec)
assert_equal(1, Time.at(0.000000001).nsec)
@@ -172,7 +199,7 @@ class TestTime < Test::Unit::TestCase
t = Time.at(2**40 + "1/3".to_r, 9999999999999).utc
assert_equal(36812, t.year)
-
+
t = Time.at(-0x3fff_ffff_ffff_ffff)
assert_equal(-146138510344, t.year)
t = Time.at(-0x4000_0000_0000_0000)
@@ -209,6 +236,17 @@ class TestTime < Test::Unit::TestCase
assert_equal(1, Time.at(0, 0.001).nsec)
end
+ def test_at_with_unit
+ assert_equal(123456789, Time.at(0, 123456789, :nanosecond).nsec)
+ assert_equal(123456789, Time.at(0, 123456789, :nsec).nsec)
+ assert_equal(123456000, Time.at(0, 123456, :microsecond).nsec)
+ assert_equal(123456000, Time.at(0, 123456, :usec).nsec)
+ assert_equal(123000000, Time.at(0, 123, :millisecond).nsec)
+ assert_raise(ArgumentError){ Time.at(0, 1, 2) }
+ assert_raise(ArgumentError){ Time.at(0, 1, :invalid) }
+ assert_raise(ArgumentError){ Time.at(0, 1, nil) }
+ end
+
def test_at_rational
assert_equal(1, Time.at(Rational(1,1) / 1000000000).nsec)
assert_equal(1, Time.at(1167609600 + Rational(1,1) / 1000000000).nsec)
@@ -273,21 +311,73 @@ class TestTime < Test::Unit::TestCase
assert_equal(29700, t2.utc_offset, bug)
end
- # Sat Jan 01 00:00:00 UTC 2000
- T2000 = Time.at(946684800).gmtime
+ def test_marshal_zone
+ t = Time.utc(2013, 2, 24)
+ assert_equal('UTC', t.zone)
+ assert_equal('UTC', Marshal.load(Marshal.dump(t)).zone)
+
+ in_timezone('JST-9') do
+ t = Time.local(2013, 2, 24)
+ assert_equal('JST', Time.local(2013, 2, 24).zone)
+ t = Marshal.load(Marshal.dump(t))
+ assert_equal('JST', t.zone)
+ assert_equal('JST', (t+1).zone, '[ruby-core:81892] [Bug #13710]')
+ end
+ end
+
+ def test_marshal_zone_gc
+ assert_separately(%w(--disable-gems), <<-'end;', timeout: 30)
+ ENV["TZ"] = "JST-9"
+ s = Marshal.dump(Time.now)
+ t = Marshal.load(s)
+ n = 0
+ done = 100000
+ while t.zone.dup == "JST" && n < done
+ n += 1
+ end
+ assert_equal done, n, "Bug #9652"
+ assert_equal "JST", t.zone, "Bug #9652"
+ end;
+ end
+
+ def test_marshal_to_s
+ t1 = Time.new(2011,11,8, 0,42,25, 9*3600)
+ t2 = Time.at(Marshal.load(Marshal.dump(t1)))
+ assert_equal("2011-11-08 00:42:25 +0900", t2.to_s,
+ "[ruby-dev:44827] [Bug #5586]")
+ end
+
+ Bug8795 = '[ruby-core:56648] [Bug #8795]'
+
+ def test_marshal_broken_offset
+ data = "\x04\bIu:\tTime\r\xEFF\x1C\x80\x00\x00\x00\x00\x06:\voffset"
+ t1 = t2 = nil
+ in_timezone('UTC') do
+ assert_nothing_raised(TypeError, ArgumentError, Bug8795) do
+ t1 = Marshal.load(data + "T")
+ t2 = Marshal.load(data + "\"\x0ebadoffset")
+ end
+ assert_equal(0, t1.utc_offset)
+ assert_equal(0, t2.utc_offset)
+ end
+ end
- def test_security_error
- assert_raise(SecurityError) do
- Thread.new do
- t = Time.gm(2000)
- $SAFE = 4
- t.localtime
- end.join
+ def test_marshal_broken_zone
+ data = "\x04\bIu:\tTime\r\xEFF\x1C\x80\x00\x00\x00\x00\x06:\tzone"
+ t1 = t2 = nil
+ in_timezone('UTC') do
+ assert_nothing_raised(TypeError, ArgumentError, Bug8795) do
+ t1 = Marshal.load(data + "T")
+ t2 = Marshal.load(data + "\"\b\0\0\0")
+ end
+ assert_equal('UTC', t1.zone)
+ assert_equal('UTC', t2.zone)
end
end
def test_at3
- assert_equal(T2000, Time.at(T2000))
+ t2000 = get_t2000
+ assert_equal(t2000, Time.at(t2000))
# assert_raise(RangeError) do
# Time.at(2**31-1, 1_000_000)
# Time.at(2**63-1, 1_000_000)
@@ -299,13 +389,14 @@ class TestTime < Test::Unit::TestCase
end
def test_utc_or_local
- assert_equal(T2000, Time.gm(2000))
- assert_equal(T2000, Time.gm(0, 0, 0, 1, 1, 2000, :foo, :bar, false, :baz))
- assert_equal(T2000, Time.gm(2000, "jan"))
- assert_equal(T2000, Time.gm(2000, "1"))
- assert_equal(T2000, Time.gm(2000, 1, 1, 0, 0, 0, 0))
- assert_equal(T2000, Time.gm(2000, 1, 1, 0, 0, 0, "0"))
- assert_equal(T2000, Time.gm(2000, 1, 1, 0, 0, "0", :foo, :foo))
+ t2000 = get_t2000
+ assert_equal(t2000, Time.gm(2000))
+ assert_equal(t2000, Time.gm(0, 0, 0, 1, 1, 2000, :foo, :bar, false, :baz))
+ assert_equal(t2000, Time.gm(2000, "jan"))
+ assert_equal(t2000, Time.gm(2000, "1"))
+ assert_equal(t2000, Time.gm(2000, 1, 1, 0, 0, 0, 0))
+ assert_equal(t2000, Time.gm(2000, 1, 1, 0, 0, 0, "0"))
+ assert_equal(t2000, Time.gm(2000, 1, 1, 0, 0, "0", :foo, :foo))
assert_raise(ArgumentError) { Time.gm(2000, 1, 1, 0, 0, -1, :foo, :foo) }
assert_raise(ArgumentError) { Time.gm(2000, 1, 1, 0, 0, -1.0, :foo, :foo) }
assert_raise(RangeError) do
@@ -313,6 +404,7 @@ class TestTime < Test::Unit::TestCase
end
assert_raise(ArgumentError) { Time.gm(2000, 1, 1, 0, 0, -(2**31), :foo, :foo) }
o = Object.new
+ 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) }
def o.to_r; ""; end
@@ -325,66 +417,91 @@ class TestTime < Test::Unit::TestCase
assert_raise(ArgumentError) { Time.gm(2000, 13) }
t = Time.local(2000)
- assert_equal(t.gmt_offset, T2000 - t)
+ assert_equal(t.gmt_offset, t2000 - t)
assert_equal(-4427700000, Time.utc(-4427700000,12,1).year)
assert_equal(-2**30+10, Time.utc(-2**30+10,1,1).year)
end
def test_time_interval
- m = Mutex.new.lock
+ m = Thread::Mutex.new.lock
assert_nothing_raised {
Timeout.timeout(10) {
m.sleep(0)
}
}
assert_raise(ArgumentError) { m.sleep(-1) }
+ assert_raise(TypeError) { m.sleep("") }
+ assert_raise(TypeError) { sleep("") }
+ obj = eval("class C\u{1f5ff}; self; end").new
+ assert_raise_with_message(TypeError, /C\u{1f5ff}/) {m.sleep(obj)}
+ assert_raise_with_message(TypeError, /C\u{1f5ff}/) {sleep(obj)}
end
def test_to_f
- assert_equal(946684800.0, T2000.to_f)
+ t2000 = Time.at(946684800).gmtime
+ assert_equal(946684800.0, t2000.to_f)
+ end
+
+ def test_to_f_accuracy
+ # https://bugs.ruby-lang.org/issues/10135#note-1
+ f = 1381089302.195
+ assert_equal(f, Time.at(f).to_f, "[ruby-core:64373] [Bug #10135] note-1")
end
def test_cmp
- assert_equal(-1, T2000 <=> Time.gm(2001))
- assert_equal(1, T2000 <=> Time.gm(1999))
- assert_nil(T2000 <=> 0)
+ t2000 = get_t2000
+ assert_equal(-1, t2000 <=> Time.gm(2001))
+ assert_equal(1, t2000 <=> Time.gm(1999))
+ assert_nil(t2000 <=> 0)
end
def test_eql
- assert(T2000.eql?(T2000))
- assert(!T2000.eql?(Time.gm(2001)))
+ t2000 = get_t2000
+ assert_operator(t2000, :eql?, t2000)
+ assert_not_operator(t2000, :eql?, Time.gm(2001))
end
def test_utc_p
- assert(Time.gm(2000).gmt?)
- assert(!Time.local(2000).gmt?)
- assert(!Time.at(0).gmt?)
+ assert_predicate(Time.gm(2000), :gmt?)
+ assert_not_predicate(Time.local(2000), :gmt?)
+ assert_not_predicate(Time.at(0), :gmt?)
end
def test_hash
- assert_kind_of(Integer, T2000.hash)
+ t2000 = get_t2000
+ assert_kind_of(Integer, t2000.hash)
+ end
+
+ def test_reinitialize
+ bug8099 = '[ruby-core:53436] [Bug #8099]'
+ t2000 = get_t2000
+ assert_raise(TypeError, bug8099) {
+ t2000.send(:initialize, 2013, 03, 14)
+ }
+ assert_equal(get_t2000, t2000, bug8099)
end
def test_init_copy
- assert_equal(T2000, T2000.dup)
+ t2000 = get_t2000
+ assert_equal(t2000, t2000.dup)
assert_raise(TypeError) do
- T2000.instance_eval { initialize_copy(nil) }
+ t2000.instance_eval { initialize_copy(nil) }
end
end
def test_localtime_gmtime
assert_nothing_raised do
t = Time.gm(2000)
- assert(t.gmt?)
+ assert_predicate(t, :gmt?)
t.localtime
- assert(!t.gmt?)
+ assert_not_predicate(t, :gmt?)
t.localtime
- assert(!t.gmt?)
+ assert_not_predicate(t, :gmt?)
t.gmtime
- assert(t.gmt?)
+ assert_predicate(t, :gmt?)
t.gmtime
- assert(t.gmt?)
+ assert_predicate(t, :gmt?)
end
t1 = Time.gm(2000)
@@ -393,6 +510,7 @@ class TestTime < Test::Unit::TestCase
t3 = t1.getlocal("-02:00")
assert_equal(t1, t3)
assert_equal(-7200, t3.utc_offset)
+ assert_equal([1999, 12, 31, 22, 0, 0], [t3.year, t3.mon, t3.mday, t3.hour, t3.min, t3.sec])
t1.localtime
assert_equal(t1, t2)
assert_equal(t1.gmt?, t2.gmt?)
@@ -411,22 +529,46 @@ class TestTime < Test::Unit::TestCase
end
def test_asctime
- assert_equal("Sat Jan 1 00:00:00 2000", T2000.asctime)
+ t2000 = get_t2000
+ assert_equal("Sat Jan 1 00:00:00 2000", t2000.asctime)
+ assert_equal(Encoding::US_ASCII, t2000.asctime.encoding)
assert_kind_of(String, Time.at(0).asctime)
end
def test_to_s
- assert_equal("2000-01-01 00:00:00 UTC", T2000.to_s)
+ t2000 = get_t2000
+ assert_equal("2000-01-01 00:00:00 UTC", t2000.to_s)
+ assert_equal(Encoding::US_ASCII, t2000.to_s.encoding)
assert_kind_of(String, Time.at(946684800).getlocal.to_s)
assert_equal(Time.at(946684800).getlocal.to_s, Time.at(946684800).to_s)
end
+ def assert_zone_encoding(time)
+ zone = time.zone
+ assert_predicate(zone, :valid_encoding?)
+ if zone.ascii_only?
+ assert_equal(Encoding::US_ASCII, zone.encoding)
+ else
+ enc = Encoding.default_internal || Encoding.find('locale')
+ assert_equal(enc, zone.encoding)
+ end
+ end
+
+ def test_zone
+ assert_zone_encoding Time.now
+ t = Time.now.utc
+ assert_equal("UTC", t.zone)
+ assert_nil(t.getlocal(0).zone)
+ assert_nil(t.getlocal("+02:00").zone)
+ end
+
def test_plus_minus_succ
- # 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)
+ 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
@@ -455,25 +597,27 @@ class TestTime < Test::Unit::TestCase
end
def test_readers
- assert_equal(0, T2000.sec)
- assert_equal(0, T2000.min)
- assert_equal(0, T2000.hour)
- assert_equal(1, T2000.mday)
- assert_equal(1, T2000.mon)
- assert_equal(2000, T2000.year)
- assert_equal(6, T2000.wday)
- assert_equal(1, T2000.yday)
- assert_equal(false, T2000.isdst)
- assert_equal("UTC", T2000.zone)
- assert_equal(0, T2000.gmt_offset)
- assert(!T2000.sunday?)
- assert(!T2000.monday?)
- assert(!T2000.tuesday?)
- assert(!T2000.wednesday?)
- assert(!T2000.thursday?)
- assert(!T2000.friday?)
- assert(T2000.saturday?)
- assert_equal([0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], T2000.to_a)
+ t2000 = get_t2000
+ assert_equal(0, t2000.sec)
+ assert_equal(0, t2000.min)
+ assert_equal(0, t2000.hour)
+ assert_equal(1, t2000.mday)
+ assert_equal(1, t2000.mon)
+ assert_equal(2000, t2000.year)
+ assert_equal(6, t2000.wday)
+ assert_equal(1, t2000.yday)
+ assert_equal(false, t2000.isdst)
+ assert_equal("UTC", t2000.zone)
+ assert_zone_encoding(t2000)
+ assert_equal(0, t2000.gmt_offset)
+ assert_not_predicate(t2000, :sunday?)
+ assert_not_predicate(t2000, :monday?)
+ assert_not_predicate(t2000, :tuesday?)
+ assert_not_predicate(t2000, :wednesday?)
+ assert_not_predicate(t2000, :thursday?)
+ assert_not_predicate(t2000, :friday?)
+ assert_predicate(t2000, :saturday?)
+ assert_equal([0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], t2000.to_a)
t = Time.at(946684800).getlocal
assert_equal(t.sec, Time.at(946684800).sec)
@@ -486,6 +630,7 @@ class TestTime < Test::Unit::TestCase
assert_equal(t.yday, Time.at(946684800).yday)
assert_equal(t.isdst, Time.at(946684800).isdst)
assert_equal(t.zone, Time.at(946684800).zone)
+ assert_zone_encoding(Time.at(946684800))
assert_equal(t.gmt_offset, Time.at(946684800).gmt_offset)
assert_equal(t.sunday?, Time.at(946684800).sunday?)
assert_equal(t.monday?, Time.at(946684800).monday?)
@@ -498,51 +643,76 @@ class TestTime < Test::Unit::TestCase
end
def test_strftime
+ t2000 = get_t2000
t = Time.at(946684800).getlocal
- assert_equal("Sat", T2000.strftime("%a"))
- assert_equal("Saturday", T2000.strftime("%A"))
- assert_equal("Jan", T2000.strftime("%b"))
- assert_equal("January", T2000.strftime("%B"))
- assert_kind_of(String, T2000.strftime("%c"))
- assert_equal("01", T2000.strftime("%d"))
- assert_equal("00", T2000.strftime("%H"))
- assert_equal("12", T2000.strftime("%I"))
- assert_equal("001", T2000.strftime("%j"))
- assert_equal("01", T2000.strftime("%m"))
- assert_equal("00", T2000.strftime("%M"))
- assert_equal("AM", T2000.strftime("%p"))
- assert_equal("00", T2000.strftime("%S"))
- assert_equal("00", T2000.strftime("%U"))
- assert_equal("00", T2000.strftime("%W"))
- assert_equal("6", T2000.strftime("%w"))
- assert_equal("01/01/00", T2000.strftime("%x"))
- assert_equal("00:00:00", T2000.strftime("%X"))
- assert_equal("00", T2000.strftime("%y"))
- assert_equal("2000", T2000.strftime("%Y"))
- assert_equal("UTC", T2000.strftime("%Z"))
- assert_equal("%", T2000.strftime("%%"))
- assert_equal("0", T2000.strftime("%-S"))
-
- assert_equal("", T2000.strftime(""))
- 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))
+ assert_equal("Sat", t2000.strftime("%a"))
+ assert_equal("Saturday", t2000.strftime("%A"))
+ assert_equal("Jan", t2000.strftime("%b"))
+ assert_equal("January", t2000.strftime("%B"))
+ assert_kind_of(String, t2000.strftime("%c"))
+ assert_equal("01", t2000.strftime("%d"))
+ assert_equal("00", t2000.strftime("%H"))
+ assert_equal("12", t2000.strftime("%I"))
+ assert_equal("001", t2000.strftime("%j"))
+ assert_equal("01", t2000.strftime("%m"))
+ assert_equal("00", t2000.strftime("%M"))
+ assert_equal("AM", t2000.strftime("%p"))
+ assert_equal("00", t2000.strftime("%S"))
+ assert_equal("00", t2000.strftime("%U"))
+ assert_equal("00", t2000.strftime("%W"))
+ assert_equal("6", t2000.strftime("%w"))
+ assert_equal("01/01/00", t2000.strftime("%x"))
+ assert_equal("00:00:00", t2000.strftime("%X"))
+ assert_equal("00", t2000.strftime("%y"))
+ assert_equal("2000", t2000.strftime("%Y"))
+ assert_equal("UTC", t2000.strftime("%Z"))
+ assert_equal("%", t2000.strftime("%%"))
+ assert_equal("0", t2000.strftime("%-S"))
+ 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_equal("foo\0bar\x0000\x0000\x0000", t2000.strftime("foo\0bar\0%H\0%M\0%S"))
+ assert_equal("foo" * 1000, t2000.strftime("foo" * 1000))
t = Time.mktime(2000, 1, 1)
assert_equal("Sat", t.strftime("%a"))
+ end
+ def test_strftime_subsec
t = Time.at(946684800, 123456.789)
assert_equal("123", t.strftime("%3N"))
assert_equal("123456", t.strftime("%6N"))
assert_equal("123456789", t.strftime("%9N"))
assert_equal("1234567890", t.strftime("%10N"))
assert_equal("123456789", t.strftime("%0N"))
+ end
+
+ def test_strftime_sec
+ t = get_t2000.getlocal
assert_equal("000", t.strftime("%3S"))
+ end
+
+ def test_strftime_seconds_from_epoch
+ t = Time.at(946684800, 123456.789)
assert_equal("946684800", t.strftime("%s"))
assert_equal("946684800", t.utc.strftime("%s"))
+ t = Time.at(10000000000000000000000)
+ assert_equal("<<10000000000000000000000>>", t.strftime("<<%s>>"))
+ assert_equal("<<010000000000000000000000>>", t.strftime("<<%24s>>"))
+ assert_equal("<<010000000000000000000000>>", t.strftime("<<%024s>>"))
+ assert_equal("<< 10000000000000000000000>>", t.strftime("<<%_24s>>"))
+ end
+
+ def test_strftime_zone
t = Time.mktime(2001, 10, 1)
assert_equal("2001-10-01", t.strftime("%F"))
+ assert_equal(Encoding::UTF_8, t.strftime("\u3042%Z").encoding)
+ assert_equal(true, t.strftime("\u3042%Z").valid_encoding?)
+ end
+ def test_strftime_flags
t = Time.mktime(2001, 10, 1, 2, 0, 0)
assert_equal("01", t.strftime("%d"))
assert_equal("01", t.strftime("%0d"))
@@ -583,32 +753,74 @@ class TestTime < Test::Unit::TestCase
assert_equal(" 2", t.strftime("%l"))
assert_equal("02", t.strftime("%0l"))
assert_equal(" 2", t.strftime("%_l"))
+ assert_equal("MON", t.strftime("%^a"))
+ assert_equal("OCT", t.strftime("%^b"))
+
+ t = get_t2000
+ assert_equal("UTC", t.strftime("%^Z"))
+ assert_equal("utc", t.strftime("%#Z"))
+ assert_equal("SAT JAN 1 00:00:00 2000", t.strftime("%^c"))
+ end
+
+ def test_strftime_invalid_flags
+ t = Time.mktime(2001, 10, 1, 2, 0, 0)
+ assert_equal("%4^p", t.strftime("%4^p"), 'prec after flag')
+ end
+
+ def test_strftime_year
+ t = Time.utc(1,1,4)
+ assert_equal("0001", t.strftime("%Y"))
+ assert_equal("0001", t.strftime("%G"))
+ t = Time.utc(0,1,4)
+ assert_equal("0000", t.strftime("%Y"))
+ assert_equal("0000", t.strftime("%G"))
+
+ t = Time.utc(-1,1,4)
+ assert_equal("-0001", t.strftime("%Y"))
+ assert_equal("-0001", t.strftime("%G"))
+
+ t = Time.utc(10000000000000000000000,1,1)
+ assert_equal("<<10000000000000000000000>>", t.strftime("<<%Y>>"))
+ assert_equal("<<010000000000000000000000>>", t.strftime("<<%24Y>>"))
+ assert_equal("<<010000000000000000000000>>", t.strftime("<<%024Y>>"))
+ assert_equal("<< 10000000000000000000000>>", t.strftime("<<%_24Y>>"))
+ end
+
+ def test_strftime_weeknum
# [ruby-dev:37155]
t = Time.mktime(1970, 1, 18)
assert_equal("0", t.strftime("%w"))
assert_equal("7", t.strftime("%u"))
+ end
+ def test_strftime_ctrlchar
# [ruby-dev:37160]
- assert_equal("\t", T2000.strftime("%t"))
- assert_equal("\t", T2000.strftime("%0t"))
- assert_equal("\t", T2000.strftime("%1t"))
- assert_equal(" \t", T2000.strftime("%3t"))
- assert_equal("00\t", T2000.strftime("%03t"))
- assert_equal("\n", T2000.strftime("%n"))
- assert_equal("\n", T2000.strftime("%0n"))
- assert_equal("\n", T2000.strftime("%1n"))
- assert_equal(" \n", T2000.strftime("%3n"))
- assert_equal("00\n", T2000.strftime("%03n"))
+ t2000 = get_t2000
+ assert_equal("\t", t2000.strftime("%t"))
+ assert_equal("\t", t2000.strftime("%0t"))
+ assert_equal("\t", t2000.strftime("%1t"))
+ assert_equal(" \t", t2000.strftime("%3t"))
+ assert_equal("00\t", t2000.strftime("%03t"))
+ assert_equal("\n", t2000.strftime("%n"))
+ assert_equal("\n", t2000.strftime("%0n"))
+ assert_equal("\n", t2000.strftime("%1n"))
+ assert_equal(" \n", t2000.strftime("%3n"))
+ assert_equal("00\n", t2000.strftime("%03n"))
+ end
+ def test_strftime_weekflags
# [ruby-dev:37162]
- assert_equal("SAT", T2000.strftime("%#a"))
- assert_equal("SATURDAY", T2000.strftime("%#A"))
- assert_equal("JAN", T2000.strftime("%#b"))
- assert_equal("JANUARY", T2000.strftime("%#B"))
- assert_equal("JAN", T2000.strftime("%#h"))
+ t2000 = get_t2000
+ assert_equal("SAT", t2000.strftime("%#a"))
+ assert_equal("SATURDAY", t2000.strftime("%#A"))
+ assert_equal("JAN", t2000.strftime("%#b"))
+ assert_equal("JANUARY", t2000.strftime("%#B"))
+ assert_equal("JAN", t2000.strftime("%#h"))
assert_equal("FRIDAY", Time.local(2008,1,4).strftime("%#A"))
+ end
+ def test_strftime_rational
t = Time.utc(2000,3,14, 6,53,"58.979323846".to_r) # Pi Day
assert_equal("03/14/2000 6:53:58.97932384600000000000000000000",
t.strftime("%m/%d/%Y %l:%M:%S.%29N"))
@@ -630,6 +842,94 @@ class TestTime < Test::Unit::TestCase
t.strftime("%m/%d/%Y %l:%M:%S.%8N"))
end
+ def test_strftime_far_future
+ # [ruby-core:33985]
+ assert_equal("3000000000", Time.at(3000000000).strftime('%s'))
+ end
+
+ def test_strftime_too_wide
+ 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")
+ assert_equal("+0900", t.strftime("%z"))
+ assert_equal("+09:00", t.strftime("%:z"))
+ assert_equal("+09:00:00", t.strftime("%::z"))
+ assert_equal("+09", t.strftime("%:::z"))
+
+ t = t2000.getlocal("+09:00:01")
+ assert_equal("+0900", t.strftime("%z"))
+ assert_equal("+09:00", t.strftime("%:z"))
+ assert_equal("+09:00:01", t.strftime("%::z"))
+ assert_equal("+09:00:01", t.strftime("%:::z"))
+ end
+
+ def test_strftime_padding
+ bug4458 = '[ruby-dev:43287]'
+ t2000 = get_t2000
+ t = t2000.getlocal("+09:00")
+ assert_equal("+0900", t.strftime("%z"))
+ assert_equal("+09:00", t.strftime("%:z"))
+ assert_equal(" +900", t.strftime("%_10z"), bug4458)
+ assert_equal("+000000900", t.strftime("%10z"), bug4458)
+ assert_equal(" +9:00", t.strftime("%_10:z"), bug4458)
+ assert_equal("+000009:00", t.strftime("%10:z"), bug4458)
+ assert_equal(" +9:00:00", t.strftime("%_10::z"), bug4458)
+ assert_equal("+009:00:00", t.strftime("%10::z"), bug4458)
+ assert_equal("+000000009", t.strftime("%10:::z"))
+ t = t2000.getlocal("-05:00")
+ assert_equal("-0500", t.strftime("%z"))
+ assert_equal("-05:00", t.strftime("%:z"))
+ assert_equal(" -500", t.strftime("%_10z"), bug4458)
+ assert_equal("-000000500", t.strftime("%10z"), bug4458)
+ assert_equal(" -5:00", t.strftime("%_10:z"), bug4458)
+ assert_equal("-000005:00", t.strftime("%10:z"), bug4458)
+ assert_equal(" -5:00:00", t.strftime("%_10::z"), bug4458)
+ assert_equal("-005:00:00", t.strftime("%10::z"), bug4458)
+ assert_equal("-000000005", t.strftime("%10:::z"))
+
+ bug6323 = '[ruby-core:44447]'
+ t = t2000.getlocal("+00:36")
+ assert_equal(" +036", t.strftime("%_10z"), bug6323)
+ assert_equal("+000000036", t.strftime("%10z"), bug6323)
+ assert_equal(" +0:36", t.strftime("%_10:z"), bug6323)
+ assert_equal("+000000:36", t.strftime("%10:z"), bug6323)
+ assert_equal(" +0:36:00", t.strftime("%_10::z"), bug6323)
+ assert_equal("+000:36:00", t.strftime("%10::z"), bug6323)
+ assert_equal("+000000:36", t.strftime("%10:::z"))
+ t = t2000.getlocal("-00:55")
+ assert_equal(" -055", t.strftime("%_10z"), bug6323)
+ assert_equal("-000000055", t.strftime("%10z"), bug6323)
+ assert_equal(" -0:55", t.strftime("%_10:z"), bug6323)
+ assert_equal("-000000:55", t.strftime("%10:z"), bug6323)
+ assert_equal(" -0:55:00", t.strftime("%_10::z"), bug6323)
+ assert_equal("-000:55:00", t.strftime("%10::z"), bug6323)
+ assert_equal("-000000:55", t.strftime("%10:::z"))
+ end
+
+ def test_strftime_invalid_modifier
+ t2000 = get_t2000
+ t = t2000.getlocal("+09:00")
+ assert_equal("%:y", t.strftime("%:y"), 'invalid conversion after : modifier')
+ assert_equal("%:0z", t.strftime("%:0z"), 'flag after : modifier')
+ assert_equal("%:10z", t.strftime("%:10z"), 'prec after : modifier')
+ assert_equal("%Ob", t.strftime("%Ob"), 'invalid conversion after locale modifier')
+ assert_equal("%Eb", t.strftime("%Eb"), 'invalid conversion after locale modifier')
+ assert_equal("%O0y", t.strftime("%O0y"), 'flag after locale modifier')
+ assert_equal("%E0y", t.strftime("%E0y"), 'flag after locale modifier')
+ assert_equal("%O10y", t.strftime("%O10y"), 'prec after locale modifier')
+ assert_equal("%E10y", t.strftime("%E10y"), 'prec after locale modifier')
+ end
+
def test_delegate
d1 = SimpleDelegator.new(t1 = Time.utc(2000))
d2 = SimpleDelegator.new(t2 = Time.utc(2001))
@@ -676,4 +976,216 @@ class TestTime < Test::Unit::TestCase
off += 0.1
}
end
+
+ def test_getlocal_dont_share_eigenclass
+ bug5012 = "[ruby-dev:44071]"
+
+ t0 = Time.now
+ class << t0; end
+ t1 = t0.getlocal
+
+ def t0.m
+ 0
+ end
+
+ assert_raise(NoMethodError, bug5012) { t1.m }
+ end
+
+ def test_sec_str
+ bug6193 = '[ruby-core:43569]'
+ t = nil
+ assert_nothing_raised(bug6193) {t = Time.new(2012, 1, 2, 3, 4, "5")}
+ assert_equal(Time.new(2012, 1, 2, 3, 4, 5), t, bug6193)
+ end
+
+ def test_past
+ [
+ [-(1 << 100), 1, 1, 0, 0, 0],
+ [-4000, 1, 1, 0, 0, 0],
+ [-3000, 1, 1, 0, 0, 0],
+ ].each {|year, mon, day, hour, min, sec|
+ t = Time.utc(year, mon, day, hour, min, sec)
+ assert_equal(year, t.year)
+ assert_equal(mon, t.mon)
+ assert_equal(day, t.day)
+ assert_equal(hour, t.hour)
+ assert_equal(min, t.min)
+ assert_equal(sec, t.sec)
+ }
+ end
+
+ def test_1901
+ assert_equal(-0x80000001, Time.utc(1901, 12, 13, 20, 45, 51).tv_sec)
+ [
+ [1901, 12, 13, 20, 45, 50],
+ [1901, 12, 13, 20, 45, 51],
+ [1901, 12, 13, 20, 45, 52], # -0x80000000
+ [1901, 12, 13, 20, 45, 53],
+ ].each {|year, mon, day, hour, min, sec|
+ t = Time.utc(year, mon, day, hour, min, sec)
+ assert_equal(year, t.year)
+ assert_equal(mon, t.mon)
+ assert_equal(day, t.day)
+ assert_equal(hour, t.hour)
+ assert_equal(min, t.min)
+ assert_equal(sec, t.sec)
+ }
+ end
+
+ def test_1970
+ assert_equal(0, Time.utc(1970, 1, 1, 0, 0, 0).tv_sec)
+ [
+ [1969, 12, 31, 23, 59, 59],
+ [1970, 1, 1, 0, 0, 0],
+ [1970, 1, 1, 0, 0, 1],
+ ].each {|year, mon, day, hour, min, sec|
+ t = Time.utc(year, mon, day, hour, min, sec)
+ assert_equal(year, t.year)
+ assert_equal(mon, t.mon)
+ assert_equal(day, t.day)
+ assert_equal(hour, t.hour)
+ assert_equal(min, t.min)
+ assert_equal(sec, t.sec)
+ }
+ end
+
+ def test_2038
+ if no_leap_seconds?
+ assert_equal(0x80000000, Time.utc(2038, 1, 19, 3, 14, 8).tv_sec)
+ end
+ [
+ [2038, 1, 19, 3, 14, 7],
+ [2038, 1, 19, 3, 14, 8],
+ [2038, 1, 19, 3, 14, 9],
+ [2039, 1, 1, 0, 0, 0],
+ ].each {|year, mon, day, hour, min, sec|
+ t = Time.utc(year, mon, day, hour, min, sec)
+ assert_equal(year, t.year)
+ assert_equal(mon, t.mon)
+ assert_equal(day, t.day)
+ assert_equal(hour, t.hour)
+ assert_equal(min, t.min)
+ assert_equal(sec, t.sec)
+ }
+ assert_equal(Time.local(2038,3,1), Time.local(2038,2,29))
+ assert_equal(Time.local(2038,3,2), Time.local(2038,2,30))
+ assert_equal(Time.local(2038,3,3), Time.local(2038,2,31))
+ assert_equal(Time.local(2040,2,29), Time.local(2040,2,29))
+ assert_equal(Time.local(2040,3,1), Time.local(2040,2,30))
+ assert_equal(Time.local(2040,3,2), Time.local(2040,2,31))
+ n = 2 ** 64
+ n += 400 - n % 400 # n is over 2^64 and multiple of 400
+ assert_equal(Time.local(n,2,29),Time.local(n,2,29))
+ assert_equal(Time.local(n,3,1), Time.local(n,2,30))
+ assert_equal(Time.local(n,3,2), Time.local(n,2,31))
+ n += 100
+ assert_equal(Time.local(n,3,1), Time.local(n,2,29))
+ assert_equal(Time.local(n,3,2), Time.local(n,2,30))
+ assert_equal(Time.local(n,3,3), Time.local(n,2,31))
+ n += 4
+ assert_equal(Time.local(n,2,29),Time.local(n,2,29))
+ assert_equal(Time.local(n,3,1), Time.local(n,2,30))
+ assert_equal(Time.local(n,3,2), Time.local(n,2,31))
+ n += 1
+ assert_equal(Time.local(n,3,1), Time.local(n,2,29))
+ assert_equal(Time.local(n,3,2), Time.local(n,2,30))
+ assert_equal(Time.local(n,3,3), Time.local(n,2,31))
+ end
+
+ def test_future
+ [
+ [3000, 1, 1, 0, 0, 0],
+ [4000, 1, 1, 0, 0, 0],
+ [1 << 100, 1, 1, 0, 0, 0],
+ ].each {|year, mon, day, hour, min, sec|
+ t = Time.utc(year, mon, day, hour, min, sec)
+ assert_equal(year, t.year)
+ assert_equal(mon, t.mon)
+ assert_equal(day, t.day)
+ assert_equal(hour, t.hour)
+ assert_equal(min, t.min)
+ assert_equal(sec, t.sec)
+ }
+ 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]
+ end
+
+ def test_getlocal_nil
+ now = Time.now
+ now2 = nil
+ now3 = nil
+ assert_nothing_raised {
+ now2 = now.getlocal
+ now3 = now.getlocal(nil)
+ }
+ assert_equal now2, now3
+ assert_equal now2.zone, now3.zone
+ end
+
+ def test_strftime_yearday_on_last_day_of_year
+ t = Time.utc(2015, 12, 31, 0, 0, 0)
+ assert_equal("365", t.strftime("%j"))
+ t = Time.utc(2016, 12, 31, 0, 0, 0)
+ assert_equal("366", t.strftime("%j"))
+
+ t = Time.utc(2015, 12, 30, 20, 0, 0).getlocal("+05:00")
+ assert_equal("365", t.strftime("%j"))
+ t = Time.utc(2016, 12, 30, 20, 0, 0).getlocal("+05:00")
+ assert_equal("366", t.strftime("%j"))
+
+ t = Time.utc(2016, 1, 1, 1, 0, 0).getlocal("-05:00")
+ assert_equal("365", t.strftime("%j"))
+ t = Time.utc(2017, 1, 1, 1, 0, 0).getlocal("-05:00")
+ 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
+ def to_s; "Inexact"; end
+ define_method(:to_int) {bad}
+ define_method(:to_r) {bad}
+ end.new
+ assert_raise_with_message(TypeError, /Inexact/) {Time.at(x)}
+ end
+
+ 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
+ require 'objspace'
+ t = Time.at(0)
+ size = GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
+ case size
+ when 20 then expect = 50
+ when 40 then expect = 86
+ when 48 then expect = 94
+ else
+ flunk "Unsupported RVALUE_SIZE=#{size}, update test_memsize"
+ end
+ assert_equal expect, ObjectSpace.memsize_of(t)
+ rescue LoadError => e
+ skip "failed to load objspace: #{e.message}"
+ end
end
diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb
index 42c4607bf3..9bba30e577 100644
--- a/test/ruby/test_time_tz.rb
+++ b/test/ruby/test_time_tz.rb
@@ -1,23 +1,46 @@
+# frozen_string_literal: false
require 'test/unit'
+require '-test-/time'
class TestTimeTZ < Test::Unit::TestCase
- def with_tz(tz)
- if /linux/ =~ RUBY_PLATFORM || ENV["RUBY_FORCE_TIME_TZ_TEST"] == "yes"
- old = ENV["TZ"]
- begin
- ENV["TZ"] = tz
- yield
- ensure
- ENV["TZ"] = old
+ has_right_tz = true
+ has_lisbon_tz = true
+ force_tz_test = ENV["RUBY_FORCE_TIME_TZ_TEST"] == "yes"
+ case RUBY_PLATFORM
+ when /linux/
+ force_tz_test = true
+ when /darwin|freebsd/
+ has_lisbon_tz = false
+ force_tz_test = true
+ end
+
+ if force_tz_test
+ module Util
+ def with_tz(tz)
+ old = ENV["TZ"]
+ begin
+ ENV["TZ"] = tz
+ yield
+ ensure
+ ENV["TZ"] = old
+ end
end
- else
- if ENV["TZ"] == tz
- yield
+ end
+ else
+ module Util
+ def with_tz(tz)
+ if ENV["TZ"] == tz
+ yield
+ end
end
end
end
module Util
+ def have_tz_offset?(tz)
+ with_tz(tz) {!Time.now.utc_offset.zero?}
+ end
+
def format_gmtoff(gmtoff, colon=false)
if gmtoff < 0
expected = "-"
@@ -60,12 +83,21 @@ class TestTimeTZ < Test::Unit::TestCase
include Util
extend Util
- def time_to_s(t)
- if RUBY_VERSION < "1.9"
- t.strftime("%Y-%m-%d %H:%M:%S ") + format_gmtoff(t.gmtoff)
- else
- t.to_s
+ has_right_tz &&= have_tz_offset?("right/America/Los_Angeles")
+ has_lisbon_tz &&= have_tz_offset?("Europe/Lisbon")
+ CORRECT_TOKYO_DST_1951 = with_tz("Asia/Tokyo") {
+ if Time.local(1951, 5, 6, 12, 0, 0).dst? # noon, DST
+ if Time.local(1951, 5, 6, 1, 0, 0).dst? # DST with fixed tzdata
+ Time.local(1951, 9, 8, 23, 0, 0).dst? ? "2018f" : "2018e"
+ end
end
+ }
+ CORRECT_KIRITIMATI_SKIP_1994 = with_tz("Pacific/Kiritimati") {
+ Time.local(1994, 12, 31, 0, 0, 0).year == 1995
+ }
+
+ def time_to_s(t)
+ t.to_s
end
@@ -76,6 +108,18 @@ class TestTimeTZ < Test::Unit::TestCase
assert_equal(expected, real, m)
end
+ def test_localtime_zone
+ t = with_tz("America/Los_Angeles") {
+ Time.local(2000, 1, 1)
+ }
+ skip "force_tz_test is false on this environment" unless t
+ z1 = t.zone
+ z2 = with_tz(tz="Asia/Singapore") {
+ t.localtime.zone
+ }
+ assert_equal(z2, z1)
+ end
+
def test_america_los_angeles
with_tz(tz="America/Los_Angeles") {
assert_time_constructor(tz, "2007-03-11 03:00:00 -0700", :local, [2007,3,11,2,0,0])
@@ -105,14 +149,21 @@ class TestTimeTZ < Test::Unit::TestCase
def test_asia_tokyo
with_tz(tz="Asia/Tokyo") {
- assert_time_constructor(tz, "1951-05-06 03:00:00 +1000", :local, [1951,5,6,2,0,0])
- assert_time_constructor(tz, "1951-05-06 03:59:59 +1000", :local, [1951,5,6,2,59,59])
+ h = CORRECT_TOKYO_DST_1951 ? 0 : 2
+ assert_time_constructor(tz, "1951-05-06 0#{h+1}:00:00 +1000", :local, [1951,5,6,h,0,0])
+ assert_time_constructor(tz, "1951-05-06 0#{h+1}:59:59 +1000", :local, [1951,5,6,h,59,59])
assert_time_constructor(tz, "2010-06-10 06:13:28 +0900", :local, [2010,6,10,6,13,28])
}
end
+ def test_asia_kuala_lumpur
+ with_tz(tz="Asia/Kuala_Lumpur") {
+ assert_time_constructor(tz, "1933-01-01 00:20:00 +0720", :local, [1933])
+ }
+ end
+
def test_canada_newfoundland
- with_tz(tz="Canada/Newfoundland") {
+ with_tz(tz="America/St_Johns") {
assert_time_constructor(tz, "2007-11-03 23:00:59 -0230", :new, [2007,11,3,23,0,59,:dst])
assert_time_constructor(tz, "2007-11-03 23:01:00 -0230", :new, [2007,11,3,23,1,0,:dst])
assert_time_constructor(tz, "2007-11-03 23:59:59 -0230", :new, [2007,11,3,23,59,59,:dst])
@@ -128,43 +179,78 @@ class TestTimeTZ < Test::Unit::TestCase
def test_europe_brussels
with_tz(tz="Europe/Brussels") {
assert_time_constructor(tz, "1916-04-30 23:59:59 +0100", :local, [1916,4,30,23,59,59])
- assert_time_constructor(tz, "1916-05-01 01:00:00 +0200", :local, [1916,5,1], "[ruby-core:30672]")
+ assert_time_constructor(tz, "1916-05-01 01:00:00 +0200", :local, [1916,5,1], "[ruby-core:30672] [Bug #3411]")
assert_time_constructor(tz, "1916-05-01 01:59:59 +0200", :local, [1916,5,1,0,59,59])
assert_time_constructor(tz, "1916-05-01 01:00:00 +0200", :local, [1916,5,1,1,0,0])
assert_time_constructor(tz, "1916-05-01 01:59:59 +0200", :local, [1916,5,1,1,59,59])
}
end
- def test_europe_lisbon
- with_tz(tz="Europe/Lisbon") {
- assert_equal("LMT", Time.new(-0x1_0000_0000_0000_0000).zone)
+ def test_europe_berlin
+ with_tz(tz="Europe/Berlin") {
+ assert_time_constructor(tz, "2011-10-30 02:00:00 +0100", :local, [2011,10,30,2,0,0], "[ruby-core:67345] [Bug #10698]")
+ assert_time_constructor(tz, "2011-10-30 02:00:00 +0100", :local, [0,0,2,30,10,2011,nil,nil,false,nil])
+ assert_time_constructor(tz, "2011-10-30 02:00:00 +0200", :local, [0,0,2,30,10,2011,nil,nil,true,nil])
}
end
- def test_europe_moscow
- with_tz(tz="Europe/Moscow") {
- assert_time_constructor(tz, "1992-03-29 00:00:00 +0400", :local, [1992,3,28,23,0,0])
- assert_time_constructor(tz, "1992-03-29 00:59:59 +0400", :local, [1992,3,28,23,59,59])
+ def test_europe_lisbon
+ with_tz("Europe/Lisbon") {
+ assert_equal("LMT", Time.new(-0x1_0000_0000_0000_0000).zone)
}
- end
+ end if has_lisbon_tz
def test_pacific_kiritimati
with_tz(tz="Pacific/Kiritimati") {
- assert_time_constructor(tz, "1994-12-31 23:59:59 -1000", :local, [1994,12,31,23,59,59])
- assert_time_constructor(tz, "1995-01-02 00:00:00 +1400", :local, [1995,1,1,0,0,0])
- assert_time_constructor(tz, "1995-01-02 23:59:59 +1400", :local, [1995,1,1,23,59,59])
+ assert_time_constructor(tz, "1994-12-30 00:00:00 -1000", :local, [1994,12,30,0,0,0])
+ assert_time_constructor(tz, "1994-12-30 23:59:59 -1000", :local, [1994,12,30,23,59,59])
+ if CORRECT_KIRITIMATI_SKIP_1994
+ assert_time_constructor(tz, "1995-01-01 00:00:00 +1400", :local, [1994,12,31,0,0,0])
+ assert_time_constructor(tz, "1995-01-01 23:59:59 +1400", :local, [1994,12,31,23,59,59])
+ assert_time_constructor(tz, "1995-01-01 00:00:00 +1400", :local, [1995,1,1,0,0,0])
+ else
+ assert_time_constructor(tz, "1994-12-31 23:59:59 -1000", :local, [1994,12,31,23,59,59])
+ assert_time_constructor(tz, "1995-01-02 00:00:00 +1400", :local, [1995,1,1,0,0,0])
+ assert_time_constructor(tz, "1995-01-02 23:59:59 +1400", :local, [1995,1,1,23,59,59])
+ end
assert_time_constructor(tz, "1995-01-02 00:00:00 +1400", :local, [1995,1,2,0,0,0])
}
end
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])
assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2009,1,1,0,0,0])
}
- end
+ end if has_right_tz
+
+ 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, "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)
+ }
+ }
+ 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)
+ }
+ }
+ end if has_right_tz
def test_right_america_los_angeles
with_tz(tz="right/America/Los_Angeles") {
@@ -172,7 +258,7 @@ class TestTimeTZ < Test::Unit::TestCase
assert_time_constructor(tz, "2008-12-31 15:59:60 -0800", :local, [2008,12,31,15,59,60])
assert_time_constructor(tz, "2008-12-31 16:00:00 -0800", :local, [2008,12,31,16,0,0])
}
- end
+ end if has_right_tz
MON2NUM = {
"Jan" => 1, "Feb" => 2, "Mar" => 3, "Apr" => 4, "May" => 5, "Jun" => 6,
@@ -186,35 +272,42 @@ class TestTimeTZ < Test::Unit::TestCase
s.sub(/gen_/) { "gen" + "_#{hint}_".gsub(/[^0-9A-Za-z]+/, '_') }
end
- def self.gen_zdump_test
+ def self.parse_zdump_line(line)
+ return nil if /\A\#/ =~ line || /\A\s*\z/ =~ line
+ if /\A(\S+)\s+
+ \S+\s+(\S+)\s+(\d+)\s+(\d\d):(\d\d):(\d\d)\s+(\d+)\s+UTC?
+ \s+=\s+
+ \S+\s+(\S+)\s+(\d+)\s+(\d\d):(\d\d):(\d\d)\s+(\d+)\s+\S+
+ \s+isdst=\d+\s+gmtoff=(-?\d+)\n
+ \z/x !~ line
+ raise "unexpected zdump line: #{line.inspect}"
+ end
+ tz, u_mon, u_day, u_hour, u_min, u_sec, u_year,
+ l_mon, l_day, l_hour, l_min, l_sec, l_year, gmtoff = $~.captures
+ u_year = u_year.to_i
+ u_mon = MON2NUM[u_mon]
+ u_day = u_day.to_i
+ u_hour = u_hour.to_i
+ u_min = u_min.to_i
+ u_sec = u_sec.to_i
+ l_year = l_year.to_i
+ l_mon = MON2NUM[l_mon]
+ l_day = l_day.to_i
+ l_hour = l_hour.to_i
+ l_min = l_min.to_i
+ l_sec = l_sec.to_i
+ gmtoff = gmtoff.to_i
+ [tz,
+ [u_year, u_mon, u_day, u_hour, u_min, u_sec],
+ [l_year, l_mon, l_day, l_hour, l_min, l_sec],
+ gmtoff]
+ end
+
+ def self.gen_zdump_test(data)
sample = []
- ZDUMP_SAMPLE.each_line {|line|
- next if /\A\#/ =~ line || /\A\s*\z/ =~ line
- /\A(\S+)\s+
- \S+\s+(\S+)\s+(\d+)\s+(\d\d):(\d\d):(\d\d)\s+(\d+)\s+UTC
- \s+=\s+
- \S+\s+(\S+)\s+(\d+)\s+(\d\d):(\d\d):(\d\d)\s+(\d+)\s+\S+
- \s+isdst=\d+\s+gmtoff=(-?\d+)\n
- \z/x =~ line
- tz, u_mon, u_day, u_hour, u_min, u_sec, u_year,
- l_mon, l_day, l_hour, l_min, l_sec, l_year, gmtoff = $~.captures
- u_year = u_year.to_i
- u_mon = MON2NUM[u_mon]
- u_day = u_day.to_i
- u_hour = u_hour.to_i
- u_min = u_min.to_i
- u_sec = u_sec.to_i
- l_year = l_year.to_i
- l_mon = MON2NUM[l_mon]
- l_day = l_day.to_i
- l_hour = l_hour.to_i
- l_min = l_min.to_i
- l_sec = l_sec.to_i
- gmtoff = gmtoff.to_i
- sample << [tz,
- [u_year, u_mon, u_day, u_hour, u_min, u_sec],
- [l_year, l_mon, l_day, l_hour, l_min, l_sec],
- gmtoff]
+ data.each_line {|line|
+ s = parse_zdump_line(line)
+ sample << s if s
}
sample.each {|tz, u, l, gmtoff|
expected_utc = "%04d-%02d-%02d %02d:%02d:%02d UTC" % u
@@ -223,6 +316,7 @@ 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)
@@ -232,11 +326,13 @@ class TestTimeTZ < Test::Unit::TestCase
assert_equal(format_gmtoff(gmtoff), t.strftime("%z"))
assert_equal(format_gmtoff(gmtoff, true), t.strftime("%:z"))
assert_equal(format_gmtoff2(gmtoff), t.strftime("%::z"))
+ assert_equal(Encoding::US_ASCII, t.zone.encoding)
}
}
}
+
group_by(sample) {|tz, _, _, _| tz }.each {|tz, a|
- a.each_with_index {|(_, u, l, gmtoff), i|
+ a.each_with_index {|(_, _, l, gmtoff), i|
expected = "%04d-%02d-%02d %02d:%02d:%02d %s" % (l+[format_gmtoff(gmtoff)])
monotonic_to_past = i == 0 || (a[i-1][2] <=> l) < 0
monotonic_to_future = i == a.length-1 || (l <=> a[i+1][2]) < 0
@@ -274,7 +370,7 @@ class TestTimeTZ < Test::Unit::TestCase
}
end
- ZDUMP_SAMPLE = <<'End'
+ gen_zdump_test <<'End'
America/Lima Sun Apr 1 03:59:59 1990 UTC = Sat Mar 31 23:59:59 1990 PEST isdst=1 gmtoff=-14400
America/Lima Sun Apr 1 04:00:00 1990 UTC = Sat Mar 31 23:00:00 1990 PET isdst=0 gmtoff=-18000
America/Lima Sat Jan 1 04:59:59 1994 UTC = Fri Dec 31 23:59:59 1993 PET isdst=0 gmtoff=-18000
@@ -298,14 +394,27 @@ America/Managua Wed Jan 1 05:00:00 1997 UTC = Tue Dec 31 23:00:00 1996 CST isd
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 16:30:00 1981 UTC = Fri Jan 1 00:30: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
+End
+Asia/Tokyo Sat Sep 8 13:59:59 1951 UTC = Sat Sep 8 23:59:59 1951 JDT isdst=1 gmtoff=36000
+Asia/Tokyo Sat Sep 8 14:00:00 1951 UTC = Sat Sep 8 23:00:00 1951 JST isdst=0 gmtoff=32400
+2018e
+Asia/Tokyo Sat Sep 8 14:59:59 1951 UTC = Sun Sep 9 00:59:59 1951 JDT isdst=1 gmtoff=36000
+Asia/Tokyo Sat Sep 8 15:00:00 1951 UTC = Sun Sep 9 00:00:00 1951 JST isdst=0 gmtoff=32400
+2018f
Asia/Tokyo Sat May 5 16:59:59 1951 UTC = Sun May 6 01:59:59 1951 JST isdst=0 gmtoff=32400
Asia/Tokyo Sat May 5 17:00:00 1951 UTC = Sun May 6 03:00:00 1951 JDT isdst=1 gmtoff=36000
Asia/Tokyo Fri Sep 7 15:59:59 1951 UTC = Sat Sep 8 01:59:59 1951 JDT isdst=1 gmtoff=36000
Asia/Tokyo Fri Sep 7 16:00:00 1951 UTC = Sat Sep 8 01:00:00 1951 JST isdst=0 gmtoff=32400
-Canada/Newfoundland Sun Mar 11 03:30:59 2007 UTC = Sun Mar 11 00:00:59 2007 NST isdst=0 gmtoff=-12600
-Canada/Newfoundland Sun Mar 11 03:31:00 2007 UTC = Sun Mar 11 01:01:00 2007 NDT isdst=1 gmtoff=-9000
-Canada/Newfoundland Sun Nov 4 02:30:59 2007 UTC = Sun Nov 4 00:00:59 2007 NDT isdst=1 gmtoff=-9000
-Canada/Newfoundland Sun Nov 4 02:31:00 2007 UTC = Sat Nov 3 23:01:00 2007 NST isdst=0 gmtoff=-12600
+End
+ gen_zdump_test <<'End'
+America/St_Johns Sun Mar 11 03:30:59 2007 UTC = Sun Mar 11 00:00:59 2007 NST isdst=0 gmtoff=-12600
+America/St_Johns Sun Mar 11 03:31:00 2007 UTC = Sun Mar 11 01:01:00 2007 NDT isdst=1 gmtoff=-9000
+America/St_Johns Sun Nov 4 02:30:59 2007 UTC = Sun Nov 4 00:00:59 2007 NDT isdst=1 gmtoff=-9000
+America/St_Johns Sun Nov 4 02:31:00 2007 UTC = Sat Nov 3 23:01:00 2007 NST isdst=0 gmtoff=-12600
Europe/Brussels Sun Apr 30 22:59:59 1916 UTC = Sun Apr 30 23:59:59 1916 CET isdst=0 gmtoff=3600
Europe/Brussels Sun Apr 30 23:00:00 1916 UTC = Mon May 1 01:00:00 1916 CEST isdst=1 gmtoff=7200
Europe/Brussels Sat Sep 30 22:59:59 1916 UTC = Sun Oct 1 00:59:59 1916 CEST isdst=1 gmtoff=7200
@@ -318,21 +427,265 @@ Europe/London Sun Aug 10 00:59:59 1947 UTC = Sun Aug 10 02:59:59 1947 BDST isds
Europe/London Sun Aug 10 01:00:00 1947 UTC = Sun Aug 10 02:00:00 1947 BST isdst=1 gmtoff=3600
Europe/London Sun Nov 2 01:59:59 1947 UTC = Sun Nov 2 02:59:59 1947 BST isdst=1 gmtoff=3600
Europe/London Sun Nov 2 02:00:00 1947 UTC = Sun Nov 2 02:00:00 1947 GMT isdst=0 gmtoff=0
-Europe/Moscow Sat Jan 18 23:59:59 1992 UTC = Sun Jan 19 01:59:59 1992 MSK isdst=0 gmtoff=7200
-Europe/Moscow Sun Jan 19 00:00:00 1992 UTC = Sun Jan 19 03:00:00 1992 MSK isdst=0 gmtoff=10800
-Europe/Moscow Sat Mar 28 19:59:59 1992 UTC = Sat Mar 28 22:59:59 1992 MSK isdst=0 gmtoff=10800
-Europe/Moscow Sat Mar 28 20:00:00 1992 UTC = Sun Mar 29 00:00:00 1992 MSD isdst=1 gmtoff=14400
-Europe/Moscow Sat Sep 26 18:59:59 1992 UTC = Sat Sep 26 22:59:59 1992 MSD isdst=1 gmtoff=14400
-Europe/Moscow Sat Sep 26 19:00:00 1992 UTC = Sat Sep 26 22:00:00 1992 MSK isdst=0 gmtoff=10800
+End
+ if CORRECT_KIRITIMATI_SKIP_1994
+ gen_zdump_test <<'End'
+Pacific/Kiritimati Sat Dec 31 09:59:59 1994 UTC = Fri Dec 30 23:59:59 1994 LINT isdst=0 gmtoff=-36000
+Pacific/Kiritimati Sat Dec 31 10:00:00 1994 UTC = Sun Jan 1 00:00:00 1995 LINT isdst=0 gmtoff=50400
+End
+ else
+ gen_zdump_test <<'End'
Pacific/Kiritimati Sun Jan 1 09:59:59 1995 UTC = Sat Dec 31 23:59:59 1994 LINT isdst=0 gmtoff=-36000
Pacific/Kiritimati Sun Jan 1 10:00:00 1995 UTC = Mon Jan 2 00:00:00 1995 LINT isdst=0 gmtoff=50400
+End
+ end
+ gen_zdump_test <<'End' if has_right_tz
right/America/Los_Angeles Fri Jun 30 23:59:60 1972 UTC = Fri Jun 30 16:59:60 1972 PDT isdst=1 gmtoff=-25200
right/America/Los_Angeles Wed Dec 31 23:59:60 2008 UTC = Wed Dec 31 15:59:60 2008 PST isdst=0 gmtoff=-28800
#right/Asia/Tokyo Fri Jun 30 23:59:60 1972 UTC = Sat Jul 1 08:59:60 1972 JST isdst=0 gmtoff=32400
#right/Asia/Tokyo Sat Dec 31 23:59:60 2005 UTC = Sun Jan 1 08:59:60 2006 JST isdst=0 gmtoff=32400
right/Europe/Paris Fri Jun 30 23:59:60 1972 UTC = Sat Jul 1 00:59:60 1972 CET isdst=0 gmtoff=3600
right/Europe/Paris Wed Dec 31 23:59:60 2008 UTC = Thu Jan 1 00:59:60 2009 CET isdst=0 gmtoff=3600
+End
+
+ def self.gen_variational_zdump_test(hint, data)
+ sample = []
+ data.each_line {|line|
+ s = parse_zdump_line(line)
+ sample << s if s
+ }
+
+ define_method(gen_test_name(hint)) {
+ results = []
+ sample.each {|tz, u, l, gmtoff|
+ expected_utc = "%04d-%02d-%02d %02d:%02d:%02d UTC" % u
+ expected = "%04d-%02d-%02d %02d:%02d:%02d %s" % (l+[format_gmtoff(gmtoff)])
+ mesg_utc = "TZ=#{tz} Time.utc(#{u.map {|arg| arg.inspect }.join(', ')})"
+ mesg = "#{mesg_utc}.localtime"
+ with_tz(tz) {
+ t = nil
+ assert_nothing_raised(mesg) { t = Time.utc(*u) }
+ assert_equal(expected_utc, time_to_s(t), mesg_utc)
+ assert_nothing_raised(mesg) { t.localtime }
+
+ results << [
+ expected == time_to_s(t),
+ gmtoff == t.gmtoff,
+ format_gmtoff(gmtoff) == t.strftime("%z"),
+ format_gmtoff(gmtoff, true) == t.strftime("%:z"),
+ format_gmtoff2(gmtoff) == t.strftime("%::z")
+ ]
+ }
+ }
+ assert_include(results, [true, true, true, true, true])
+ }
+ end
+
+ # tzdata-2014g fixed the offset for lisbon from -0:36:32 to -0:36:45.
+ # [ruby-core:65058] [Bug #10245]
+ gen_variational_zdump_test "lisbon", <<'End' if has_lisbon_tz
Europe/Lisbon Mon Jan 1 00:36:31 1912 UTC = Sun Dec 31 23:59:59 1911 LMT isdst=0 gmtoff=-2192
+Europe/Lisbon Mon Jan 1 00:36:44 1912 UT = Sun Dec 31 23:59:59 1911 LMT isdst=0 gmtoff=-2205
+Europe/Lisbon Sun Dec 31 23:59:59 1911 UT = Sun Dec 31 23:23:14 1911 LMT isdst=0 gmtoff=-2205
End
- gen_zdump_test
+
+ class TZ
+ attr_reader :name, :abbr, :offset
+
+ def initialize(name, abbr, offset)
+ @name = name
+ @abbr = abbr
+ @offset = offset
+ end
+
+ def local_to_utc(t)
+ t - @offset
+ end
+
+ def utc_to_local(t)
+ t + @offset
+ end
+
+ def abbr(t)
+ @abbr
+ end
+
+ def ==(other)
+ @name == other.name and @abbr == other.abbr(0) and @offset == other.offset
+ end
+
+ def inspect
+ "#<TZ: #@name #@abbr #@offset>"
+ end
+ end
+end
+
+module TestTimeTZ::WithTZ
+ def subtest_new(time_class, tz, tzarg, tzname, abbr, utc_offset)
+ t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg)
+ 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)
+ end
+
+ def subtest_getlocal(time_class, tz, tzarg, tzname, abbr, utc_offset)
+ t = time_class.utc(2018, 9, 1, 12, 0, 0).getlocal(tzarg)
+ h, m = (utc_offset / 60).divmod(60)
+ assert_equal([2018, 9, 1, 12+h, m, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone])
+ assert_equal(time_class.utc(2018, 9, 1, 12, 0, 0), t)
+ end
+
+ def subtest_strftime(time_class, tz, tzarg, tzname, abbr, utc_offset)
+ t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg)
+ 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"))
+ end
+
+ def subtest_plus(time_class, tz, tzarg, tzname, abbr, utc_offset)
+ t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg) + 4000
+ assert_equal([2018, 9, 1, 13, 6, 40, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone])
+ m, s = (4000-utc_offset).divmod(60)
+ h, m = m.divmod(60)
+ assert_equal(time_class.utc(2018, 9, 1, 12+h, m, s), t)
+ assert_equal(6, t.wday)
+ assert_equal(244, t.yday)
+ end
+
+ def subtest_at(time_class, tz, tzarg, tzname, abbr, utc_offset)
+ h, m = (utc_offset / 60).divmod(60)
+ utc = time_class.utc(2018, 9, 1, 12, 0, 0)
+ t = time_class.at(utc, in: tzarg)
+ assert_equal([2018, 9, 1, 12+h, m, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone])
+ assert_equal(utc.to_i, t.to_i)
+ utc = utc.to_i
+ t = time_class.at(utc, in: tzarg)
+ assert_equal([2018, 9, 1, 12+h, m, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone])
+ assert_equal(utc, t.to_i)
+ 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))
+ assert_equal(t, t2)
+ assert_equal(t.utc_offset, t2.utc_offset)
+ assert_equal(t.utc_offset, (t2+1).utc_offset)
+ assert_instance_of(t.zone.class, t2.zone)
+ end
+
+ def test_invalid_zone
+ make_timezone("INVALID", "INV", 0)
+ rescue => e
+ assert_kind_of(StandardError, e)
+ else
+ assert false, "ArgumentError expected but nothing was raised."
+ end
+
+ def nametest_marshal_compatibility(time_class, tzname, abbr, utc_offset)
+ data = [
+ "\x04\x08Iu:".b, Marshal.dump(time_class)[3..-1],
+ "\x0d""\xEF\xA7\x1D\x80\x00\x00\x00\x00".b,
+ Marshal.dump({offset: utc_offset, zone: abbr})[3..-1],
+ ].join('')
+ t = Marshal.load(data)
+ assert_equal(utc_offset, t.utc_offset)
+ assert_equal(utc_offset, (t+1).utc_offset)
+ # t.zone may be a mere String or timezone object.
+ end
+
+ ZONES = {
+ "Asia/Tokyo" => ["JST", +9*3600],
+ "America/Los_Angeles" => ["PDT", -7*3600],
+ "Africa/Ndjamena" => ["WAT", +1*3600],
+ }
+
+ def make_timezone(tzname, abbr, utc_offset)
+ self.class::TIME_CLASS.find_timezone(tzname)
+ end
+
+ instance_methods(false).grep(/\Asub(?=test_)/) do |subtest|
+ test = $'
+ ZONES.each_pair do |tzname, (abbr, utc_offset)|
+ define_method("#{test}@#{tzname}") do
+ tz = make_timezone(tzname, abbr, utc_offset)
+ time_class = self.class::TIME_CLASS
+ __send__(subtest, time_class, tz, tz, tzname, abbr, utc_offset)
+ __send__(subtest, time_class, tz, tzname, tzname, abbr, utc_offset)
+ end
+ end
+ end
+
+ instance_methods(false).grep(/\Aname(?=test_)/) do |subtest|
+ test = $'
+ ZONES.each_pair do |tzname, (abbr, utc_offset)|
+ define_method("#{test}@#{tzname}") do
+ time_class = self.class::TIME_CLASS
+ __send__(subtest, time_class, tzname, abbr, utc_offset)
+ end
+ end
+ end
+end
+
+class TestTimeTZ::DummyTZ < Test::Unit::TestCase
+ include TestTimeTZ::WithTZ
+
+ class TIME_CLASS < ::Time
+ ZONES = TestTimeTZ::WithTZ::ZONES
+ def self.find_timezone(tzname)
+ tz = ZONES[tzname] or raise ArgumentError, "Unknown timezone: #{name}"
+ TestTimeTZ::TZ.new(tzname, *tz)
+ end
+ end
+
+ def self.make_timezone(tzname, abbr, utc_offset)
+ TestTimeTZ::TZ.new(tzname, abbr, utc_offset)
+ end
+end
+
+begin
+ require "tzinfo"
+rescue LoadError
+else
+ class TestTimeTZ::GemTZInfo < Test::Unit::TestCase
+ include TestTimeTZ::WithTZ
+
+ class TIME_CLASS < ::Time
+ def self.find_timezone(tzname)
+ TZInfo::Timezone.get(tzname)
+ end
+ end
+
+ def tz
+ @tz ||= TZInfo::Timezone.get(tzname)
+ end
+ 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
+ require "timezone"
+rescue LoadError
+else
+ class TestTimeTZ::GemTimezone < Test::Unit::TestCase
+ include TestTimeTZ::WithTZ
+
+ class TIME_CLASS < ::Time
+ def self.find_timezone(name)
+ Timezone.fetch(name)
+ end
+ end
+
+ def tz
+ @tz ||= Timezone[tzname]
+ end
+ end
end
diff --git a/test/ruby/test_trace.rb b/test/ruby/test_trace.rb
index 45bc599314..77be94e9be 100644
--- a/test/ruby/test_trace.rb
+++ b/test/ruby/test_trace.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestTrace < Test::Unit::TestCase
@@ -46,4 +47,16 @@ class TestTrace < Test::Unit::TestCase
ensure
untrace_var :$x
end
+
+ def test_trace_break
+ bug2722 = '[ruby-core:31783]'
+ a = Object.new.extend(Enumerable)
+ def a.each
+ yield
+ end
+ assert(Thread.start {
+ Thread.current.add_trace_func(proc{})
+ a.any? {true}
+ }.value, bug2722)
+ end
end
diff --git a/test/ruby/test_transcode.rb b/test/ruby/test_transcode.rb
index 1b15a5b556..7f81cbf424 100644
--- a/test/ruby/test_transcode.rb
+++ b/test/ruby/test_transcode.rb
@@ -1,7 +1,9 @@
-# -*- encoding: ASCII-8BIT -*- # make sure this runs in binary mode
+# encoding: ASCII-8BIT # make sure this runs in binary mode
+# frozen_string_literal: false
# some of the comments are in UTF-8
require 'test/unit'
+
class TestTranscode < Test::Unit::TestCase
def test_errors
assert_raise(Encoding::ConverterNotFoundError) { 'abc'.encode('foo', 'bar') }
@@ -11,8 +13,8 @@ class TestTranscode < Test::Unit::TestCase
assert_raise(Encoding::UndefinedConversionError) { "\x80".encode('utf-8','ASCII-8BIT') }
assert_raise(Encoding::InvalidByteSequenceError) { "\x80".encode('utf-8','US-ASCII') }
assert_raise(Encoding::UndefinedConversionError) { "\xA5".encode('utf-8','iso-8859-3') }
- assert_raise(RuntimeError) { 'hello'.freeze.encode!('iso-8859-1') }
- assert_raise(RuntimeError) { '\u3053\u3093\u306b\u3061\u306f'.freeze.encode!('iso-8859-1') } # こんにちは
+ assert_raise(FrozenError) { 'hello'.freeze.encode!('iso-8859-1') }
+ assert_raise(FrozenError) { '\u3053\u3093\u306b\u3061\u306f'.freeze.encode!('iso-8859-1') } # こんにちは
end
def test_arguments
@@ -29,16 +31,16 @@ class TestTranscode < Test::Unit::TestCase
end
def test_noargument
- default_default_internal = Encoding.default_internal
- Encoding.default_internal = nil
- assert_equal("\u3042".encode, "\u3042")
- assert_equal("\xE3\x81\x82\x81".force_encoding("utf-8").encode,
- "\xE3\x81\x82\x81".force_encoding("utf-8"))
- Encoding.default_internal = 'EUC-JP'
- assert_equal("\u3042".encode, "\xA4\xA2".force_encoding('EUC-JP'))
- assert_equal("\xE3\x81\x82\x81".force_encoding("utf-8").encode,
- "\xA4\xA2?".force_encoding('EUC-JP'))
- Encoding.default_internal = default_default_internal
+ EnvUtil.with_default_internal(nil) do
+ assert_equal("\u3042".encode, "\u3042")
+ assert_equal("\xE3\x81\x82\x81".force_encoding("utf-8").encode,
+ "\xE3\x81\x82\x81".force_encoding("utf-8"))
+ end
+ EnvUtil.with_default_internal('EUC-JP') do
+ assert_equal("\u3042".encode, "\xA4\xA2".force_encoding('EUC-JP'))
+ assert_equal("\xE3\x81\x82\x81".force_encoding("utf-8").encode,
+ "\xA4\xA2?".force_encoding('EUC-JP'))
+ end
end
def test_length
@@ -51,7 +53,7 @@ class TestTranscode < Test::Unit::TestCase
end
def check_both_ways(utf8, raw, encoding)
- assert_equal(utf8.force_encoding('utf-8'), raw.encode('utf-8', encoding))
+ assert_equal(utf8.force_encoding('utf-8'), raw.encode('utf-8', encoding),utf8.dump+raw.dump)
assert_equal(raw.force_encoding(encoding), utf8.encode(encoding, 'utf-8'))
end
@@ -60,13 +62,42 @@ class TestTranscode < Test::Unit::TestCase
assert_equal(str2.force_encoding(enc2), str1.encode(enc2, enc1))
end
+ def test_encoding_of_ascii_originating_from_binary
+ binary_string = [0x82, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
+ 0x61, 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, 0x6c, 0x6f,
+ 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67]
+ class << binary_string
+ # create a copy on write substring that contains
+ # just the ascii characters (i.e. this is...), in JRuby
+ # the underlying string have the same buffer backing
+ # it up, but the offset of the string will be 1 instead
+ # of 0.
+ def make_cow_substring
+ pack('C27').slice(1, 26)
+ end
+ end
+
+ ascii_string = binary_string.make_cow_substring
+ assert_equal("this is a very long string", ascii_string)
+ assert_equal(Encoding::ASCII_8BIT, ascii_string.encoding)
+ utf8_string = nil
+ assert_nothing_raised("JRUBY-6764") do
+ utf8_string = ascii_string.encode(Encoding::UTF_8)
+ end
+ assert_equal("this is a very long string", utf8_string)
+ assert_equal(Encoding::UTF_8, utf8_string.encoding)
+ end
+
def test_encodings
check_both_ways("\u307E\u3064\u3082\u3068 \u3086\u304D\u3072\u308D",
"\x82\xdc\x82\xc2\x82\xe0\x82\xc6 \x82\xe4\x82\xab\x82\xd0\x82\xeb", 'shift_jis') # まつもと ゆきひろ
check_both_ways("\u307E\u3064\u3082\u3068 \u3086\u304D\u3072\u308D",
"\xa4\xde\xa4\xc4\xa4\xe2\xa4\xc8 \xa4\xe6\xa4\xad\xa4\xd2\xa4\xed", 'euc-jp')
+ check_both_ways("\u307E\u3064\u3082\u3068 \u3086\u304D\u3072\u308D",
+ "\xa4\xde\xa4\xc4\xa4\xe2\xa4\xc8 \xa4\xe6\xa4\xad\xa4\xd2\xa4\xed", 'euc-jis-2004')
check_both_ways("\u677E\u672C\u884C\u5F18", "\x8f\xbc\x96\x7b\x8d\x73\x8d\x4f", 'shift_jis') # 松本行弘
check_both_ways("\u677E\u672C\u884C\u5F18", "\xbe\xbe\xcb\xdc\xb9\xd4\xb9\xb0", 'euc-jp')
+ check_both_ways("\u677E\u672C\u884C\u5F18", "\xbe\xbe\xcb\xdc\xb9\xd4\xb9\xb0", 'euc-jis-2004')
check_both_ways("D\u00FCrst", "D\xFCrst", 'iso-8859-1') # Dürst
check_both_ways("D\u00FCrst", "D\xFCrst", 'iso-8859-2')
check_both_ways("D\u00FCrst", "D\xFCrst", 'iso-8859-3')
@@ -83,6 +114,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u0643\u062A\u0628", "\xE3\xCA\xC8", 'iso-8859-6') # كتب
check_both_ways("\u65E5\u8A18", "\x93\xFA\x8BL", 'shift_jis') # 日記
check_both_ways("\u65E5\u8A18", "\xC6\xFC\xB5\xAD", 'euc-jp')
+ check_both_ways("\u65E5\u8A18", "\xC6\xFC\xB5\xAD", 'euc-jis-2004')
check_both_ways("\uC560\uC778\uAD6C\uD568\u0020\u6734\uC9C0\uC778",
"\xBE\xD6\xC0\xCE\xB1\xB8\xC7\xD4\x20\xDA\xD3\xC1\xF6\xC0\xCE", 'euc-kr') # 애인구함 朴지인
check_both_ways("\uC544\uD58F\uD58F\u0020\uB620\uBC29\uD6BD\uB2D8\u0020\uC0AC\uB791\uD716",
@@ -331,7 +363,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u00BF", "\xBF", 'windows-1255') # ¿
check_both_ways("\u05B0", "\xC0", 'windows-1255') # ְ
check_both_ways("\u05B9", "\xC9", 'windows-1255') # ֹ
- assert_raise(Encoding::UndefinedConversionError) { "\xCA".encode("utf-8", 'windows-1255') }
+ check_both_ways("\u05BA", "\xCA", 'windows-1255') # ֺ
check_both_ways("\u05BB", "\xCB", 'windows-1255') # ֻ
check_both_ways("\u05BF", "\xCF", 'windows-1255') # ֿ
check_both_ways("\u05C0", "\xD0", 'windows-1255') # ׀
@@ -452,7 +484,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u2580", "\xDF", 'IBM775') # ▀
check_both_ways("\u00D3", "\xE0", 'IBM775') # Ó
check_both_ways("\u2019", "\xEF", 'IBM775') # ’
- check_both_ways("\u00AD", "\xF0", 'IBM775') # osft hyphen
+ check_both_ways("\u00AD", "\xF0", 'IBM775') # soft hyphen
check_both_ways("\u00A0", "\xFF", 'IBM775') # non-breaking space
end
@@ -471,7 +503,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u2580", "\xDF", 'IBM852') # ▀
check_both_ways("\u00D3", "\xE0", 'IBM852') # Ó
check_both_ways("\u00B4", "\xEF", 'IBM852') # ´
- check_both_ways("\u00AD", "\xF0", 'IBM852') # osft hyphen
+ check_both_ways("\u00AD", "\xF0", 'IBM852') # soft hyphen
check_both_ways("\u00A0", "\xFF", 'IBM852') # non-breaking space
end
@@ -490,7 +522,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u2580", "\xDF", 'IBM855') # ▀
check_both_ways("\u042F", "\xE0", 'IBM855') # Я
check_both_ways("\u2116", "\xEF", 'IBM855') # №
- check_both_ways("\u00AD", "\xF0", 'IBM855') # osft hyphen
+ check_both_ways("\u00AD", "\xF0", 'IBM855') # soft hyphen
check_both_ways("\u00A0", "\xFF", 'IBM855') # non-breaking space
end
@@ -966,6 +998,92 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u00A0", "\xFF", 'CP855') # non-breaking space
end
+ def test_ill_formed_utf_8_replace
+ fffd1 = "\uFFFD".encode 'UTF-16BE'
+ fffd2 = "\uFFFD\uFFFD".encode 'UTF-16BE'
+ fffd3 = "\uFFFD\uFFFD\uFFFD".encode 'UTF-16BE'
+ fffd4 = "\uFFFD\uFFFD\uFFFD\uFFFD".encode 'UTF-16BE'
+ fffd5 = "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD".encode 'UTF-16BE'
+ fffd6 = "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD".encode 'UTF-16BE'
+
+ assert_equal fffd1, "\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xC3".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xDF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xE0".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xE0\xA0".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xE0\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xE1".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xEC".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xE1\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xEC\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+
+ assert_equal fffd2, "\xC0\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xC0\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xC1\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xC1\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xE0\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xE0\x9F".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xE0\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xE0\x9F\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xED\xA0".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xED\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xED\xA0\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xED\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xF0\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xF0\x8F".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xF0\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xF0\x8F\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xF0\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xF0\x8F\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xF4\x90".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xF4\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xF4\x90\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xF4\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xF4\x90\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xF4\xBF\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xF5\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xF7\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xF5\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xF7\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xF5\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xF7\xBF\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xF8".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xFB".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xF8\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xFB\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xF8\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xFB\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xF8\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xFB\xBF\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd5, "\xF8\x80\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd5, "\xFB\xBF\xBF\xBF\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xFC".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xFD".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xFC\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xFD\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xFC\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xFD\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xFC\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xFD\xBF\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd5, "\xFC\x80\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd5, "\xFD\xBF\xBF\xBF\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd6, "\xFC\x80\x80\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd6, "\xFD\xBF\xBF\xBF\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xFE".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd1, "\xFF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xFE\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd2, "\xFF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xFE\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd3, "\xFF\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xFE\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd4, "\xFF\xBF\xBF\xBF".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd5, "\xFE\x80\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd5, "\xFF\xBF\xBF\xBF\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd6, "\xFE\x80\x80\x80\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ assert_equal fffd6, "\xFF\xBF\xBF\xBF\x80\x80".encode("utf-16be", "utf-8", invalid: :replace)
+ end
+
def check_utf_16_both_ways(utf8, raw)
copy = raw.dup
0.step(copy.length-1, 2) { |i| copy[i+1], copy[i] = copy[i], copy[i+1] }
@@ -1019,6 +1137,21 @@ class TestTranscode < Test::Unit::TestCase
check_utf_16_both_ways("\u{F00FF}", "\xDB\x80\xDC\xFF")
end
+ def test_utf_16_bom
+ expected = "\u{3042}\u{3044}\u{20bb7}"
+ assert_equal(expected, %w/fffe4230443042d8b7df/.pack("H*").encode("UTF-8","UTF-16"))
+ check_both_ways(expected, %w/feff30423044d842dfb7/.pack("H*"), "UTF-16")
+ assert_raise(Encoding::InvalidByteSequenceError){%w/feffdfb7/.pack("H*").encode("UTF-8","UTF-16")}
+ assert_raise(Encoding::InvalidByteSequenceError){%w/fffeb7df/.pack("H*").encode("UTF-8","UTF-16")}
+ end
+
+ def test_utf_32_bom
+ expected = "\u{3042}\u{3044}\u{20bb7}"
+ assert_equal(expected, %w/fffe00004230000044300000b70b0200/.pack("H*").encode("UTF-8","UTF-32"))
+ check_both_ways(expected, %w/0000feff000030420000304400020bb7/.pack("H*"), "UTF-32")
+ assert_raise(Encoding::InvalidByteSequenceError){%w/0000feff00110000/.pack("H*").encode("UTF-8","UTF-32")}
+ end
+
def check_utf_32_both_ways(utf8, raw)
copy = raw.dup
0.step(copy.length-1, 4) do |i|
@@ -1140,9 +1273,15 @@ class TestTranscode < Test::Unit::TestCase
assert_equal("\uFFFD!",
"\xff!".encode("utf-8", "euc-jp", :invalid=>:replace))
assert_equal("\uFFFD!",
+ "\xff!".encode("utf-8", "euc-jis-2004", :invalid=>:replace))
+ assert_equal("\uFFFD!",
"\xa1!".encode("utf-8", "euc-jp", :invalid=>:replace))
assert_equal("\uFFFD!",
+ "\xa1!".encode("utf-8", "euc-jis-2004", :invalid=>:replace))
+ assert_equal("\uFFFD!",
"\x8f\xa1!".encode("utf-8", "euc-jp", :invalid=>:replace))
+ assert_equal("\uFFFD!",
+ "\x8f\xa1!".encode("utf-8", "euc-jis-2004", :invalid=>:replace))
assert_equal("?",
"\xdc\x00".encode("EUC-JP", "UTF-16BE", :invalid=>:replace), "[ruby-dev:35776]")
@@ -1159,6 +1298,10 @@ class TestTranscode < Test::Unit::TestCase
def test_invalid_replace_string
assert_equal("a<x>A", "a\x80A".encode("us-ascii", "euc-jp", :invalid=>:replace, :replace=>"<x>"))
+ assert_equal("a<x>A", "a\x80A".encode("us-ascii", "euc-jis-2004", :invalid=>:replace, :replace=>"<x>"))
+ s = "abcd\u{c1}"
+ r = s.b.encode("UTF-8", "UTF-8", invalid: :replace, replace: "\u{fffd}")
+ assert_equal(s, r)
end
def test_undef_replace
@@ -1273,6 +1416,64 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u795E\u6797\u7FA9\u535A", "\xBF\xC0\xCE\xD3\xB5\xC1\xC7\xEE", 'euc-jp') # 神林義博
end
+ def test_euc_jis_2004
+ check_both_ways("\u3000", "\xA1\xA1", 'euc-jis-2004') # full-width space
+ check_both_ways("\u00D7", "\xA1\xDF", 'euc-jis-2004') # ×
+ check_both_ways("\u00F7", "\xA1\xE0", 'euc-jis-2004') # ÷
+ check_both_ways("\u25C7", "\xA1\xFE", 'euc-jis-2004') # ◇
+ check_both_ways("\u25C6", "\xA2\xA1", 'euc-jis-2004') # ◆
+ check_both_ways("\uFF07", "\xA2\xAF", 'euc-jis-2004') # '
+ check_both_ways("\u309F", "\xA2\xB9", 'euc-jis-2004') # ゟ
+ check_both_ways("\u2284", "\xA2\xC2", 'euc-jis-2004') # ⊄
+ check_both_ways("\u2306", "\xA2\xC9", 'euc-jis-2004') # ⌆
+ check_both_ways("\u2295", "\xA2\xD1", 'euc-jis-2004') # ⊕
+ check_both_ways("\u3017", "\xA2\xDB", 'euc-jis-2004') # 〗
+ check_both_ways("\u2262", "\xA2\xEB", 'euc-jis-2004') # ≢
+ check_both_ways("\u2194", "\xA2\xF1", 'euc-jis-2004') # ↔
+ check_both_ways("\u266E", "\xA2\xFA", 'euc-jis-2004') # ♮
+ check_both_ways("\u2669", "\xA2\xFD", 'euc-jis-2004') # ♩
+ check_both_ways("\u25EF", "\xA2\xFE", 'euc-jis-2004') # ◯
+ check_both_ways("\u2935", "\xA3\xAF", 'euc-jis-2004') # ⤵
+ check_both_ways("\u29BF", "\xA3\xBA", 'euc-jis-2004') # ⦿
+ check_both_ways("\u2022", "\xA3\xC0", 'euc-jis-2004') # •
+ check_both_ways("\u2213", "\xA3\xDB", 'euc-jis-2004') # ∓
+ check_both_ways("\u2127", "\xA3\xE0", 'euc-jis-2004') # ℧
+ check_both_ways("\u30A0", "\xA3\xFB", 'euc-jis-2004') # ゠
+ check_both_ways("\uFF54", "\xA3\xF4", 'euc-jis-2004') # t
+ assert_raise(Encoding::UndefinedConversionError) { "\xA5\xF7".encode("utf-8", 'euc-jis-2004') }
+ check_both_ways("\u2664", "\xA6\xB9", 'euc-jis-2004') # ♤
+ check_both_ways("\u2663", "\xA6\xC0", 'euc-jis-2004') # ♣
+ check_both_ways("\u03C2", "\xA6\xD9", 'euc-jis-2004') # ς
+ check_both_ways("\u23BE", "\xA7\xC2", 'euc-jis-2004') # ⎾
+ check_both_ways("\u23CC", "\xA7\xD0", 'euc-jis-2004') # ⏌
+ check_both_ways("\u30F7", "\xA7\xF2", 'euc-jis-2004') # ヷ
+ check_both_ways("\u3251", "\xA8\xC1", 'euc-jis-2004') # ㉑
+ check_both_ways("\u{20B9F}", "\xCF\xD4", 'euc-jis-2004') # 𠮑
+ check_both_ways("\u541E", "\xCF\xFE", 'euc-jis-2004') # 吞
+ check_both_ways("\u6A97", "\xDD\xA1", 'euc-jis-2004') # 檗
+ check_both_ways("\u6BEF", "\xDD\xDF", 'euc-jis-2004') # 毯
+ check_both_ways("\u9EBE", "\xDD\xE0", 'euc-jis-2004') # 麾
+ check_both_ways("\u6CBE", "\xDD\xFE", 'euc-jis-2004') # 沾
+ check_both_ways("\u6CBA", "\xDE\xA1", 'euc-jis-2004') # 沺
+ check_both_ways("\u6ECC", "\xDE\xFE", 'euc-jis-2004') # 滌
+ check_both_ways("\u6F3E", "\xDF\xA1", 'euc-jis-2004') # 漾
+ check_both_ways("\u70DD", "\xDF\xDF", 'euc-jis-2004') # 烝
+ check_both_ways("\u70D9", "\xDF\xE0", 'euc-jis-2004') # 烙
+ check_both_ways("\u71FC", "\xDF\xFE", 'euc-jis-2004') # 燼
+ check_both_ways("\u71F9", "\xE0\xA1", 'euc-jis-2004') # 燹
+ check_both_ways("\u73F1", "\xE0\xFE", 'euc-jis-2004') # 珱
+ check_both_ways("\u5653", "\xF4\xA7", 'euc-jis-2004') # 噓
+ #check_both_ways("\u9ADC", "\xFC\xE3", 'euc-jp') # 髜 (IBM extended)
+
+ check_both_ways("\u9DD7", "\xFE\xE5", 'euc-jis-2004') # 鷗
+ check_both_ways("\u{2000B}", "\xAE\xA2", 'euc-jis-2004') # 𠀋
+ check_both_ways("\u{2A6B2}", "\x8F\xFE\xF6", 'euc-jis-2004') # 𪚲
+
+ check_both_ways("\u677E\u672C\u884C\u5F18", "\xBE\xBE\xCB\xDC\xB9\xD4\xB9\xB0", 'euc-jis-2004') # 松本行弘
+ check_both_ways("\u9752\u5C71\u5B66\u9662\u5927\u5B66", "\xC0\xC4\xBB\xB3\xB3\xD8\xB1\xA1\xC2\xE7\xB3\xD8", 'euc-jis-2004') # 青山学院大学
+ check_both_ways("\u795E\u6797\u7FA9\u535A", "\xBF\xC0\xCE\xD3\xB5\xC1\xC7\xEE", 'euc-jis-2004') # 神林義博
+ end
+
def test_eucjp_ms
check_both_ways("\u2116", "\xAD\xE2", 'eucJP-ms') # NUMERO SIGN
check_both_ways("\u221A", "\xA2\xE5", 'eucJP-ms') # SQUARE ROOT
@@ -1357,7 +1558,7 @@ class TestTranscode < Test::Unit::TestCase
"\xA1\xA1".encode("ISO-2022-JP", "EUC-JP"))
end
- def test_cp50221
+ def test_from_cp50221
assert_equal("!", "\e(B\x21".encode("utf-8", "cp50221"))
assert_equal("!", "\e(J\x21".encode("utf-8", "cp50221"))
assert_equal("\uFF71", "\xB1".encode("utf-8", "cp50221"))
@@ -1373,10 +1574,11 @@ class TestTranscode < Test::Unit::TestCase
assert_equal("\u5fde", "\e$B\x7A\x21".encode("utf-8", "cp50221"))
assert_equal("\u72be", "\e$B\x7B\x21".encode("utf-8", "cp50221"))
assert_equal("\u91d7", "\e$B\x7C\x21".encode("utf-8", "cp50221"))
- assert_equal("\e(I!_\e(B", "\xA1\xDF".encode("cp50220","sjis"))
+ assert_equal("\xA1\xDF".force_encoding("sjis"),
+ "\e(I!_\e(B".encode("sjis","cp50220"))
end
- def test_cp50221
+ def test_to_cp50221
assert_equal("\e$B!#!,\e(B".force_encoding("cp50220"),
"\xA1\xDF".encode("cp50220","sjis"))
assert_equal("\e$B%*!+%,%I%J!+%N!+%P%\\%^!+%Q%]%\"\e(B".force_encoding("cp50220"),
@@ -1389,14 +1591,16 @@ class TestTranscode < Test::Unit::TestCase
end
def test_unicode_public_review_issue_121 # see http://www.unicode.org/review/pr-121.html
- # assert_equal("\x00\x61\xFF\xFD\x00\x62".force_encoding('UTF-16BE'),
- # "\x61\xF1\x80\x80\xE1\x80\xC2\x62".encode('UTF-16BE', 'UTF-8', invalid: :replace)) # option 1
assert_equal("\x00\x61\xFF\xFD\xFF\xFD\xFF\xFD\x00\x62".force_encoding('UTF-16BE'),
"\x61\xF1\x80\x80\xE1\x80\xC2\x62".encode('UTF-16BE', 'UTF-8', invalid: :replace)) # option 2
assert_equal("\x61\x00\xFD\xFF\xFD\xFF\xFD\xFF\x62\x00".force_encoding('UTF-16LE'),
"\x61\xF1\x80\x80\xE1\x80\xC2\x62".encode('UTF-16LE', 'UTF-8', invalid: :replace)) # option 2
- # assert_equal("\x00\x61\xFF\xFD\xFF\xFD\xFF\xFD\xFF\xFD\xFF\xFD\xFF\xFD\x00\x62".force_encoding('UTF-16BE'),
- # "\x61\xF1\x80\x80\xE1\x80\xC2\x62".encode('UTF-16BE', 'UTF-8', invalid: :replace)) # option 3
+
+ # additional clarification
+ assert_equal("\xFF\xFD\xFF\xFD\xFF\xFD\xFF\xFD".force_encoding('UTF-16BE'),
+ "\xF0\x80\x80\x80".encode('UTF-16BE', 'UTF-8', invalid: :replace))
+ assert_equal("\xFD\xFF\xFD\xFF\xFD\xFF\xFD\xFF".force_encoding('UTF-16LE'),
+ "\xF0\x80\x80\x80".encode('UTF-16LE', 'UTF-8', invalid: :replace))
end
def test_yen_sign
@@ -1794,9 +1998,9 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u77AC", "\xC0\xFE", 'Big5') # 瞬
check_both_ways("\u8B96", "\xC6\x40", 'Big5') # 讖
check_both_ways("\u7C72", "\xC6\x7E", 'Big5') # 籲
- #assert_raise(Encoding::UndefinedConversionError) { "\xC6\xA1".encode("utf-8", 'Big5') }
+ #assert_raise(Encoding::UndefinedConversionError) { "\xC6\xA1".encode("utf-8", 'Big5') }
#assert_raise(Encoding::UndefinedConversionError) { "\xC7\x40".encode("utf-8", 'Big5') }
- assert_raise(Encoding::UndefinedConversionError) { "\xC8\x40".encode("utf-8", 'Big5') }
+ #assert_raise(Encoding::UndefinedConversionError) { "\xC8\x40".encode("utf-8", 'Big5') }
check_both_ways("\u4E42", "\xC9\x40", 'Big5') # 乂
check_both_ways("\u6C15", "\xC9\x7E", 'Big5') # 氕
check_both_ways("\u6C36", "\xC9\xA1", 'Big5') # 氶
@@ -1829,7 +2033,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u9F0A", "\xF9\x7E", 'Big5') # 鼊
check_both_ways("\u9FA4", "\xF9\xA1", 'Big5') # 龤
check_both_ways("\u9F98", "\xF9\xD5", 'Big5') # 龘
- assert_raise(Encoding::UndefinedConversionError) { "\xF9\xD6".encode("utf-8", 'Big5') }
+ #assert_raise(Encoding::UndefinedConversionError) { "\xF9\xD6".encode("utf-8", 'Big5') }
check_both_ways("\u795E\u6797\u7FA9\u535A", "\xAF\xAB\xAA\x4C\xB8\x71\xB3\xD5", 'Big5') # 神林義博
end
@@ -1861,7 +2065,7 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u77AC", "\xC0\xFE", 'Big5-HKSCS') # 瞬
check_both_ways("\u8B96", "\xC6\x40", 'Big5-HKSCS') # 讖
check_both_ways("\u7C72", "\xC6\x7E", 'Big5-HKSCS') # 籲
- #assert_raise(Encoding::UndefinedConversionError) { "\xC6\xA1".encode("utf-8", 'Big5-HKSCS') }
+ #assert_raise(Encoding::UndefinedConversionError) { "\xC6\xA1".encode("utf-8", 'Big5-HKSCS') }
#assert_raise(Encoding::UndefinedConversionError) { "\xC7\x40".encode("utf-8", 'Big5-HKSCS') }
#assert_raise(Encoding::UndefinedConversionError) { "\xC8\x40".encode("utf-8", 'Big5-HKSCS') }
check_both_ways("\u4E42", "\xC9\x40", 'Big5-HKSCS') # 乂
@@ -1896,15 +2100,22 @@ class TestTranscode < Test::Unit::TestCase
check_both_ways("\u9F0A", "\xF9\x7E", 'Big5-HKSCS') # 鼊
check_both_ways("\u9FA4", "\xF9\xA1", 'Big5-HKSCS') # 龤
check_both_ways("\u9F98", "\xF9\xD5", 'Big5-HKSCS') # 龘
- check_both_ways("\u{23ED7}", "\x8E\x40", 'Big5-HKSCS') # 𣻗
+ #check_both_ways("\u{23ED7}", "\x8E\x40", 'Big5-HKSCS') # 𣻗
#assert_raise(Encoding::UndefinedConversionError) { "\xF9\xD6".encode("utf-8", 'Big5-HKSCS') }
check_both_ways("\u795E\u6797\u7FA9\u535A", "\xAF\xAB\xAA\x4C\xB8\x71\xB3\xD5", 'Big5-HKSCS') # 神林義博
end
-
+
def test_Big5_UAO
check_both_ways("\u4e17", "\x81\x40", 'Big5-UAO') # 丗
end
-
+
+ def test_EBCDIC
+ check_both_ways("abcdeABCDE", "\x81\x82\x83\x84\x85\xC1\xC2\xC3\xC4\xC5", 'IBM037')
+ check_both_ways("aijrszAIJRSZ09", "\x81\x89\x91\x99\xA2\xA9\xC1\xC9\xD1\xD9\xE2\xE9\xF0\xF9", 'IBM037')
+ check_both_ways("Matz", "\xD4\x81\xA3\xA9", 'IBM037')
+ check_both_ways("D\u00FCrst", "\xC4\xDC\x99\xA2\xA3", 'IBM037') # Dürst
+ end
+
def test_nothing_changed
a = "James".force_encoding("US-ASCII")
b = a.encode("Shift_JIS")
@@ -1913,11 +2124,13 @@ class TestTranscode < Test::Unit::TestCase
end
def test_utf8_mac
- assert_equal("\u{fb4d}", "\u05DB\u05BF".encode("UTF-8", "UTF8-MAC"))
- assert_equal("\u{1ff7}", "\u03C9\u0345\u0342".encode("UTF-8", "UTF8-MAC"))
+ # composition exclusion
+ assert_equal("\u05DB\u05BF", "\u05DB\u05BF".encode("UTF-8", "UTF8-MAC")) #"\u{fb4d}"
+
+ assert_equal("\u{1ff7}", "\u03C9\u0342\u0345".encode("UTF-8", "UTF8-MAC"))
assert_equal("\u05DB\u05BF", "\u{fb4d}".encode("UTF8-MAC").force_encoding("UTF-8"))
- assert_equal("\u03C9\u0345\u0342", "\u{1ff7}".encode("UTF8-MAC").force_encoding("UTF-8"))
+ assert_equal("\u03C9\u0342\u0345", "\u{1ff7}".encode("UTF8-MAC").force_encoding("UTF-8"))
check_both_ways("\u{e9 74 e8}", "e\u0301te\u0300", 'UTF8-MAC')
end
@@ -1930,4 +2143,97 @@ class TestTranscode < Test::Unit::TestCase
assert_equal("[ISU]", "\u{1F4BA}".encode("SJIS-KDDI",
fallback: {"\u{1F4BA}" => "[ISU]"}))
end
+
+ def test_fallback_hash_default
+ fallback = Hash.new {|h, x| "U+%.4X" % x.unpack("U")}
+ assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback))
+ end
+
+ def test_fallback_proc
+ fallback = proc {|x| "U+%.4X" % x.unpack("U")}
+ assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback))
+ end
+
+ def test_fallback_method
+ def (fallback = "U+%.4X").escape(x)
+ self % x.unpack("U")
+ end
+ 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
+ assert_normal_exit("'aaa'.encode('#{enc}').inspect", bug8940)
+ assert_equal(4, 'aaa'.encode(enc).length, "should count in #{enc} with BOM")
+ end
+ end
+
+ def test_encode_with_invalid_chars
+ bug8995 = '[ruby-dev:47747]'
+ EnvUtil.with_default_internal(Encoding::UTF_8) do
+ str = "\xff".force_encoding('utf-8')
+ assert_equal str, str.encode, bug8995
+ assert_equal "\ufffd", str.encode(invalid: :replace), bug8995
+ end
+ end
+
+ def test_valid_dummy_encoding
+ bug9314 = '[ruby-core:59354] [Bug #9314]'
+ assert_separately(%W[- -- #{bug9314}], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ bug = ARGV.shift
+ result = assert_nothing_raised(TypeError, bug) {break "test".encode(Encoding::UTF_16)}
+ assert_equal("\xFE\xFF\x00t\x00e\x00s\x00t", result.b, bug)
+ result = assert_nothing_raised(TypeError, bug) {break "test".encode(Encoding::UTF_32)}
+ assert_equal("\x00\x00\xFE\xFF\x00\x00\x00t\x00\x00\x00e\x00\x00\x00s\x00\x00\x00t", result.b, bug)
+ end;
+ end
+
+ def test_loading_race
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ bug11277 = '[ruby-dev:49106] [Bug #11277]'
+ num = 2
+ th = (0...num).map do |i|
+ Thread.new {"\u3042".encode("EUC-JP")}
+ end
+ result = nil
+ assert_warning("", bug11277) do
+ assert_nothing_raised(Encoding::ConverterNotFoundError, bug11277) do
+ result = th.map(&:value)
+ end
+ end
+ expected = "\xa4\xa2".force_encoding(Encoding::EUC_JP)
+ assert_equal([expected]*num, result, bug11277)
+ end;
+ end
+
+ def test_scrub_encode_with_coderange
+ bug = '[ruby-core:82674] [Bug #13874]'
+ s = "\xe5".b
+ u = Encoding::UTF_8
+ assert_equal("?", s.encode(u, u, invalid: :replace, replace: "?"),
+ "should replace invalid byte")
+ assert_predicate(s, :valid_encoding?, "any char is valid in binary")
+ assert_equal("?", s.encode(u, u, invalid: :replace, replace: "?"),
+ "#{bug} coderange should not have side effects")
+ end
+
+ def test_universal_newline
+ 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)
+ end
end
diff --git a/test/ruby/test_undef.rb b/test/ruby/test_undef.rb
index e1c98076c0..e0add7c3ab 100644
--- a/test/ruby/test_undef.rb
+++ b/test/ruby/test_undef.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestUndef < Test::Unit::TestCase
@@ -24,7 +25,7 @@ class TestUndef < Test::Unit::TestCase
y = Undef1.new
assert_equal "bar", y.bar
z = Undef2.new
- assert_raise(NoMethodError) { z.foo }
+ assert_raise(NoMethodError) { z.bar }
end
def test_special_const_undef
diff --git a/test/ruby/test_unicode_escape.rb b/test/ruby/test_unicode_escape.rb
index 088f81ce14..5913bb0130 100644
--- a/test/ruby/test_unicode_escape.rb
+++ b/test/ruby/test_unicode_escape.rb
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
+# frozen_string_literal: false
require 'test/unit'
@@ -47,7 +48,8 @@ EOS
# \u in %x strings
assert_match(/^("?)A\1$/, `echo "\u0041"`) #"
assert_match(/^("?)A\1$/, %x{echo "\u0041"}) #"
- assert_match(/^("?)ü\1$/, `echo "\u{FC}"`.force_encoding("utf-8")) #"
+ assert_match(/^("?)ü\1$/,
+ `#{EnvUtil.rubybin} -e "#coding:utf-8\nputs \\"\\u{FC}\\""`.force_encoding("utf-8")) #"
# \u in quoted symbols
assert_equal(:A, :"\u0041")
@@ -254,16 +256,17 @@ EOS
assert_raise(SyntaxError) { eval %q("\ughij") } # bad hex digits
assert_raise(SyntaxError) { eval %q("\u{ghij}") } # bad hex digits
- assert_raise(SyntaxError) { eval %q("\u{123 456 }")} # extra space
- assert_raise(SyntaxError) { eval %q("\u{ 123 456}")} # extra space
- assert_raise(SyntaxError) { eval %q("\u{123 456}")} # extra space
+ assert_raise_with_message(SyntaxError, /invalid/) {
+ eval %q("\u{123.}") # bad char
+ }
-# The utf-8 encoding object currently does not object to codepoints
-# in the surrogate blocks, so these do not raise an error.
-# assert_raise(SyntaxError) { "\uD800" } # surrogate block
-# assert_raise(SyntaxError) { "\uDCBA" } # surrogate block
-# assert_raise(SyntaxError) { "\uDFFF" } # surrogate block
-# assert_raise(SyntaxError) { "\uD847\uDD9A" } # surrogate pair
+ # assert_raise(SyntaxError) { eval %q("\u{123 456 }")} # extra space
+ # assert_raise(SyntaxError) { eval %q("\u{ 123 456}")} # extra space
+ # assert_raise(SyntaxError) { eval %q("\u{123 456}")} # extra space
+ assert_raise(SyntaxError) { eval %q("\uD800") } # surrogate block
+ assert_raise(SyntaxError) { eval %q("\uDCBA") } # surrogate block
+ assert_raise(SyntaxError) { eval %q("\uDFFF") } # surrogate block
+ assert_raise(SyntaxError) { eval %q("\uD847\uDD9A") } # surrogate pair
end
end
diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb
index 53e00301dc..a9b1fd50ff 100644
--- a/test/ruby/test_variable.rb
+++ b/test/ruby/test_variable.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestVariable < Test::Unit::TestCase
class Gods
@@ -29,12 +29,14 @@ class TestVariable < Test::Unit::TestCase
@@rule = "Cronus" # modifies @@rule in Gods
include Olympians
def ruler4
- @@rule
+ EnvUtil.suppress_warning {
+ @@rule
+ }
end
end
def test_variable
- assert_instance_of(Fixnum, $$)
+ assert_instance_of(Integer, $$)
# read-only variable
assert_raise(NameError) do
@@ -55,11 +57,17 @@ class TestVariable < Test::Unit::TestCase
assert_equal("Cronus", atlas.ruler0)
assert_equal("Zeus", atlas.ruler3)
assert_equal("Cronus", atlas.ruler4)
+ assert_nothing_raised do
+ class << Gods
+ defined?(@@rule) && @@rule
+ end
+ end
end
def test_local_variables
lvar = 1
assert_instance_of(Symbol, local_variables[0], "[ruby-dev:34008]")
+ lvar
end
def test_local_variables2
@@ -67,6 +75,7 @@ class TestVariable < Test::Unit::TestCase
proc do |y|
assert_equal([:x, :y], local_variables.sort)
end.call
+ x
end
def test_local_variables3
@@ -76,17 +85,79 @@ class TestVariable < Test::Unit::TestCase
assert_equal([:x, :y, :z], local_variables.sort)
end
end.call
+ x
+ end
+
+ def test_shadowing_local_variables
+ bug9486 = '[ruby-core:60501] [Bug #9486]'
+ assert_equal([:x, :bug9486], tap {|x| break local_variables}, bug9486)
+ end
+
+ def test_shadowing_block_local_variables
+ bug9486 = '[ruby-core:60501] [Bug #9486]'
+ assert_equal([:x, :bug9486], tap {|;x| x = x; break local_variables}, bug9486)
+ end
+
+ def test_global_variables
+ gv = global_variables
+ assert_empty(gv.grep(/\A(?!\$)/))
+ assert_nil($~)
+ assert_not_include(gv, :$1)
+ /(\w)(\d)?(.)(.)(.)(.)(.)(.)(.)(.)(\d)?(.)/ =~ "globalglobalglobal"
+ assert_not_nil($~)
+ gv = global_variables - gv
+ assert_include(gv, :$1)
+ assert_not_include(gv, :$2)
+ assert_not_include(gv, :$11)
+ assert_include(gv, :$12)
end
def test_global_variable_0
assert_in_out_err(["-e", "$0='t'*1000;print $0"], "", /\At+\z/, [])
end
- def test_global_variable_poped
- assert_nothing_raised { eval("$foo; 1") }
+ def test_global_variable_popped
+ assert_nothing_raised {
+ EnvUtil.suppress_warning {
+ eval("$foo; 1")
+ }
+ }
+ end
+
+ def test_constant_popped
+ assert_nothing_raised {
+ EnvUtil.suppress_warning {
+ eval("TestVariable::Gods; 1")
+ }
+ }
+ end
+
+ def test_special_constant_ivars
+ [ true, false, :symbol, "dsym#{rand(9999)}".to_sym, 1, 1.0 ].each do |v|
+ assert_empty v.instance_variables
+ msg = "can't modify frozen #{v.class}"
+
+ assert_raise_with_message(FrozenError, msg) do
+ v.instance_variable_set(:@foo, :bar)
+ end
+
+ assert_nil EnvUtil.suppress_warning {v.instance_variable_get(:@foo)}
+ assert_not_send([v, :instance_variable_defined?, :@foo])
+
+ assert_raise_with_message(FrozenError, msg) do
+ v.remove_instance_variable(:@foo)
+ end
+ end
+ end
+
+ def test_local_variables_with_kwarg
+ bug11674 = '[ruby-core:71437] [Bug #11674]'
+ v = with_kwargs_11(v1:1,v2:2,v3:3,v4:4,v5:5,v6:6,v7:7,v8:8,v9:9,v10:10,v11:11)
+ assert_equal(%i(v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11), v, bug11674)
end
- def test_constant_poped
- assert_nothing_raised { eval("TestVariable::Gods; 1") }
+ private
+ def with_kwargs_11(v1:, v2:, v3:, v4:, v5:, v6:, v7:, v8:, v9:, v10:, v11:)
+ local_variables
end
end
diff --git a/test/ruby/test_vm_dump.rb b/test/ruby/test_vm_dump.rb
new file mode 100644
index 0000000000..68f0fa7f27
--- /dev/null
+++ b/test/ruby/test_vm_dump.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+require 'test/unit'
+
+class TestVMDump < Test::Unit::TestCase
+ def assert_darwin_vm_dump_works(args)
+ skip if RUBY_PLATFORM !~ /darwin/
+ assert_in_out_err(args, "", [], /^\[IMPORTANT\]/)
+ end
+
+ def test_darwin_invalid_call
+ assert_darwin_vm_dump_works(['-rfiddle', '-eFiddle::Function.new(Fiddle::Pointer.new(1), [], Fiddle::TYPE_VOID).call'])
+ end
+
+ def test_darwin_segv_in_syscall
+ assert_darwin_vm_dump_works('-e1.times{Process.kill :SEGV,$$}')
+ end
+
+ def test_darwin_invalid_access
+ assert_darwin_vm_dump_works(['-rfiddle', '-eFiddle.dlunwrap(100).class'])
+ end
+end
diff --git a/test/ruby/test_weakmap.rb b/test/ruby/test_weakmap.rb
new file mode 100644
index 0000000000..fb57903c98
--- /dev/null
+++ b/test/ruby/test_weakmap.rb
@@ -0,0 +1,144 @@
+# frozen_string_literal: false
+require 'test/unit'
+
+class TestWeakMap < Test::Unit::TestCase
+ def setup
+ @wm = ObjectSpace::WeakMap.new
+ end
+
+ def test_map
+ x = Object.new
+ k = "foo"
+ @wm[k] = x
+ assert_same(x, @wm[k])
+ assert_not_same(x, @wm["FOO".downcase])
+ end
+
+ def test_aset_const
+ x = Object.new
+ assert_raise(ArgumentError) {@wm[true] = x}
+ assert_raise(ArgumentError) {@wm[false] = x}
+ assert_raise(ArgumentError) {@wm[nil] = x}
+ assert_raise(ArgumentError) {@wm[42] = x}
+ assert_raise(ArgumentError) {@wm[:foo] = x}
+ assert_raise(ArgumentError) {@wm[x] = true}
+ assert_raise(ArgumentError) {@wm[x] = false}
+ assert_raise(ArgumentError) {@wm[x] = nil}
+ assert_raise(ArgumentError) {@wm[x] = 42}
+ assert_raise(ArgumentError) {@wm[x] = :foo}
+ end
+
+ def assert_weak_include(m, k, n = 100)
+ if n > 0
+ return assert_weak_include(m, k, n-1)
+ end
+ 1.times do
+ x = Object.new
+ @wm[k] = x
+ assert_send([@wm, m, k])
+ assert_not_send([@wm, m, "FOO".downcase])
+ x = Object.new
+ end
+ end
+
+ def test_include?
+ m = __callee__[/test_(.*)/, 1]
+ k = "foo"
+ 1.times do
+ assert_weak_include(m, k)
+ end
+ GC.start
+ skip('TODO: failure introduced from r60440')
+ assert_not_send([@wm, m, k])
+ end
+ alias test_member? test_include?
+ alias test_key? test_include?
+
+ def test_inspect
+ x = Object.new
+ k = BasicObject.new
+ @wm[k] = x
+ assert_match(/\A\#<#{@wm.class.name}:[^:]+:\s\#<BasicObject:[^:]*>\s=>\s\#<Object:[^:]*>>\z/,
+ @wm.inspect)
+ end
+
+ def test_each
+ m = __callee__[/test_(.*)/, 1]
+ x1 = Object.new
+ k1 = "foo"
+ @wm[k1] = x1
+ x2 = Object.new
+ k2 = "bar"
+ @wm[k2] = x2
+ n = 0
+ @wm.__send__(m) do |k, v|
+ assert_match(/\A(?:foo|bar)\z/, k)
+ case k
+ when /foo/
+ assert_same(k1, k)
+ assert_same(x1, v)
+ when /bar/
+ assert_same(k2, k)
+ assert_same(x2, v)
+ end
+ n += 1
+ end
+ assert_equal(2, n)
+ end
+
+ def test_each_key
+ x1 = Object.new
+ k1 = "foo"
+ @wm[k1] = x1
+ x2 = Object.new
+ k2 = "bar"
+ @wm[k2] = x2
+ n = 0
+ @wm.each_key do |k|
+ assert_match(/\A(?:foo|bar)\z/, k)
+ case k
+ when /foo/
+ assert_same(k1, k)
+ when /bar/
+ assert_same(k2, k)
+ end
+ n += 1
+ end
+ assert_equal(2, n)
+ end
+
+ def test_each_value
+ x1 = "foo"
+ k1 = Object.new
+ @wm[k1] = x1
+ x2 = "bar"
+ k2 = Object.new
+ @wm[k2] = x2
+ n = 0
+ @wm.each_value do |v|
+ assert_match(/\A(?:foo|bar)\z/, v)
+ case v
+ when /foo/
+ assert_same(x1, v)
+ when /bar/
+ assert_same(x2, v)
+ end
+ n += 1
+ end
+ assert_equal(2, n)
+ end
+
+ def test_size
+ m = __callee__[/test_(.*)/, 1]
+ assert_equal(0, @wm.__send__(m))
+ x1 = "foo"
+ k1 = Object.new
+ @wm[k1] = x1
+ assert_equal(1, @wm.__send__(m))
+ x2 = "bar"
+ k2 = Object.new
+ @wm[k2] = x2
+ assert_equal(2, @wm.__send__(m))
+ end
+ alias test_length test_size
+end
diff --git a/test/ruby/test_whileuntil.rb b/test/ruby/test_whileuntil.rb
index 5628317cb8..121c44817d 100644
--- a/test/ruby/test_whileuntil.rb
+++ b/test/ruby/test_whileuntil.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
require 'tmpdir'
@@ -21,7 +22,7 @@ class TestWhileuntil < Test::Unit::TestCase
break if /vt100/ =~ line
end
- assert(!tmp.eof?)
+ assert_not_predicate(tmp, :eof?)
assert_match(/vt100/, line)
tmp.close
@@ -30,7 +31,7 @@ class TestWhileuntil < Test::Unit::TestCase
next if /vt100/ =~ line
assert_no_match(/vt100/, line)
end
- assert(tmp.eof?)
+ assert_predicate(tmp, :eof?)
assert_no_match(/vt100/, line)
tmp.close
@@ -45,7 +46,7 @@ class TestWhileuntil < Test::Unit::TestCase
assert_no_match(/vt100/, line)
assert_no_match(/VT100/, line)
end
- assert(tmp.eof?)
+ assert_predicate(tmp, :eof?)
tmp.close
sum=0
@@ -60,7 +61,7 @@ class TestWhileuntil < Test::Unit::TestCase
tmp = open(tmpfilename, "r")
while line = tmp.gets()
- break if 3
+ break if $. == 3
assert_no_match(/vt100/, line)
assert_no_match(/Amiga/, line)
assert_no_match(/paper/, line)
@@ -68,7 +69,7 @@ class TestWhileuntil < Test::Unit::TestCase
tmp.close
File.unlink tmpfilename or `/bin/rm -f "#{tmpfilename}"`
- assert(!File.exist?(tmpfilename))
+ assert_file.not_exist?(tmpfilename)
}
end
@@ -77,6 +78,6 @@ class TestWhileuntil < Test::Unit::TestCase
until i>4
i+=1
end
- assert(i>4)
+ assert_operator(i, :>, 4)
end
end
diff --git a/test/ruby/test_yield.rb b/test/ruby/test_yield.rb
index 3337aea078..9b2b2f37e0 100644
--- a/test/ruby/test_yield.rb
+++ b/test/ruby/test_yield.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
require 'stringio'
@@ -66,7 +67,7 @@ class TestRubyYield < Test::Unit::TestCase
end
def test_with_enum
- obj = Object
+ obj = Object.new
def obj.each
yield(*[])
end
@@ -340,11 +341,12 @@ class TestRubyYieldGen < Test::Unit::TestCase
t = t.subst('vars') { " [#{vars.join(",")}]" }
emu_values = emu(t, vars, islambda)
s = t.to_s
+ o = Object.new
#print "#{s}\t\t"
#STDOUT.flush
eval_values = disable_stderr {
begin
- eval(s, nil, 'generated_code_in_check_nofork')
+ o.instance_eval(s, 'generated_code_in_check_nofork')
rescue ArgumentError
ArgumentError
end
@@ -354,23 +356,29 @@ class TestRubyYieldGen < Test::Unit::TestCase
assert_equal(emu_values, eval_values, s)
end
+ def assert_all_sentences(syntax, *args)
+ syntax = Sentence.expand_syntax(syntax)
+ all_assertions do |a|
+ Sentence.each(syntax, *args) {|t|
+ a.for(t) {yield t}
+ }
+ end
+ end
+
def test_yield
- syntax = Sentence.expand_syntax(Syntax)
- Sentence.each(syntax, :test_proc, 4) {|t|
+ assert_all_sentences(Syntax, :test_proc, 4) {|t|
check_nofork(t)
}
end
def test_yield_lambda
- syntax = Sentence.expand_syntax(Syntax)
- Sentence.each(syntax, :test_lambda, 4) {|t|
+ assert_all_sentences(Syntax, :test_lambda, 4) {|t|
check_nofork(t, true)
}
end
def test_yield_enum
- syntax = Sentence.expand_syntax(Syntax)
- Sentence.each(syntax, :test_enum, 4) {|t|
+ assert_all_sentences(Syntax, :test_enum, 4) {|t|
code = t.to_s
r1, r2 = disable_stderr {
eval(code, nil, 'generated_code_in_test_yield_enum')
@@ -379,4 +387,39 @@ class TestRubyYieldGen < Test::Unit::TestCase
}
end
+ def test_block_with_mock
+ y = Object.new
+ def y.s(a)
+ yield(a)
+ end
+ m = Object.new
+ def m.method_missing(*a)
+ super
+ end
+ assert_equal [m, nil], y.s(m){|a,b|[a,b]}
+ end
+
+ def test_block_cached_argc
+ # [Bug #11451]
+ assert_separately([], <<-"end;")
+ class Yielder
+ def each
+ yield :x, :y, :z
+ end
+ end
+ class Getter1
+ include Enumerable
+ def each(&block)
+ Yielder.new.each(&block)
+ end
+ end
+ class Getter2
+ include Enumerable
+ def each
+ Yielder.new.each { |a, b, c, d| yield(a) }
+ end
+ end
+ Getter1.new.map{Getter2.new.each{|x|}}
+ end;
+ end
end
diff --git a/test/ruby/ut_eof.rb b/test/ruby/ut_eof.rb
index b7219ddb51..fcd7a63988 100644
--- a/test/ruby/ut_eof.rb
+++ b/test/ruby/ut_eof.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
module TestEOF
@@ -86,13 +87,13 @@ module TestEOF
def test_eof_2
open_file("") {|f|
assert_equal("", f.read)
- assert(f.eof?)
+ assert_predicate(f, :eof?)
}
end
def test_eof_3
open_file("") {|f|
- assert(f.eof?)
+ assert_predicate(f, :eof?)
}
end