summaryrefslogtreecommitdiff
path: root/test/ruby
diff options
context:
space:
mode:
Diffstat (limited to 'test/ruby')
-rw-r--r--test/ruby/allpairs.rb1
-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.rb303
-rw-r--r--test/ruby/enc/test_case_mapping.rb198
-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.rb1
-rw-r--r--test/ruby/enc/test_euc_jp.rb1
-rw-r--r--test/ruby/enc/test_euc_kr.rb1
-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.rb1
-rw-r--r--test/ruby/enc/test_utf16.rb113
-rw-r--r--test/ruby/enc/test_utf32.rb69
-rw-r--r--test/ruby/enc/test_windows_1251.rb1
-rw-r--r--test/ruby/enc/test_windows_1252.rb1
-rw-r--r--test/ruby/endblockwarn_rb12
-rw-r--r--test/ruby/envutil.rb509
-rw-r--r--test/ruby/lbtest.rb6
-rw-r--r--test/ruby/marshaltestlib.rb1
-rw-r--r--test/ruby/memory_status.rb127
-rw-r--r--test/ruby/sentence.rb1
-rw-r--r--test/ruby/test_alias.rb47
-rw-r--r--test/ruby/test_argf.rb769
-rw-r--r--test/ruby/test_arity.rb45
-rw-r--r--test/ruby/test_array.rb640
-rw-r--r--test/ruby/test_assignment.rb89
-rw-r--r--test/ruby/test_autoload.rb199
-rw-r--r--test/ruby/test_backtrace.rb90
-rw-r--r--test/ruby/test_basicinstructions.rb33
-rw-r--r--test/ruby/test_beginendblock.rb225
-rw-r--r--test/ruby/test_bignum.rb233
-rw-r--r--test/ruby/test_call.rb68
-rw-r--r--test/ruby/test_case.rb25
-rw-r--r--test/ruby/test_class.rb261
-rw-r--r--test/ruby/test_clone.rb1
-rw-r--r--test/ruby/test_comparable.rb31
-rw-r--r--test/ruby/test_complex.rb554
-rw-r--r--test/ruby/test_complex2.rb1
-rw-r--r--test/ruby/test_complexrational.rb19
-rw-r--r--test/ruby/test_condition.rb1
-rw-r--r--test/ruby/test_const.rb26
-rw-r--r--test/ruby/test_continuation.rb13
-rw-r--r--test/ruby/test_defined.rb27
-rw-r--r--test/ruby/test_dir.rb183
-rw-r--r--test/ruby/test_dir_m17n.rb150
-rw-r--r--test/ruby/test_econv.rb17
-rw-r--r--test/ruby/test_encoding.rb11
-rw-r--r--test/ruby/test_enum.rb692
-rw-r--r--test/ruby/test_enumerator.rb44
-rw-r--r--test/ruby/test_env.rb180
-rw-r--r--test/ruby/test_eval.rb78
-rw-r--r--test/ruby/test_exception.rb680
-rw-r--r--test/ruby/test_fiber.rb83
-rw-r--r--test/ruby/test_file.rb144
-rw-r--r--test/ruby/test_file_exhaustive.rb1246
-rw-r--r--test/ruby/test_fixnum.rb46
-rw-r--r--test/ruby/test_flip.rb34
-rw-r--r--test/ruby/test_float.rb312
-rw-r--r--test/ruby/test_fnmatch.rb8
-rw-r--r--test/ruby/test_gc.rb201
-rw-r--r--test/ruby/test_hash.rb393
-rw-r--r--test/ruby/test_ifunless.rb1
-rw-r--r--test/ruby/test_integer.rb386
-rw-r--r--test/ruby/test_integer_comb.rb99
-rw-r--r--test/ruby/test_io.rb1049
-rw-r--r--test/ruby/test_io_m17n.rb188
-rw-r--r--test/ruby/test_iseq.rb316
-rw-r--r--test/ruby/test_iterator.rb13
-rw-r--r--test/ruby/test_keyword.rb176
-rw-r--r--test/ruby/test_lambda.rb59
-rw-r--r--test/ruby/test_lazy_enumerator.rb102
-rw-r--r--test/ruby/test_literal.rb115
-rw-r--r--test/ruby/test_m17n.rb107
-rw-r--r--test/ruby/test_m17n_comb.rb89
-rw-r--r--test/ruby/test_marshal.rb208
-rw-r--r--test/ruby/test_math.rb168
-rw-r--r--test/ruby/test_metaclass.rb1
-rw-r--r--test/ruby/test_method.rb343
-rw-r--r--test/ruby/test_mixed_unicode_escapes.rb11
-rw-r--r--test/ruby/test_module.rb404
-rw-r--r--test/ruby/test_not.rb1
-rw-r--r--test/ruby/test_notimp.rb3
-rw-r--r--test/ruby/test_numeric.rb276
-rw-r--r--test/ruby/test_object.rb179
-rw-r--r--test/ruby/test_objectspace.rb100
-rw-r--r--test/ruby/test_optimization.rb701
-rw-r--r--test/ruby/test_pack.rb173
-rw-r--r--test/ruby/test_parse.rb338
-rw-r--r--test/ruby/test_path.rb4
-rw-r--r--test/ruby/test_pipe.rb1
-rw-r--r--test/ruby/test_primitive.rb3
-rw-r--r--test/ruby/test_proc.rb123
-rw-r--r--test/ruby/test_process.rb675
-rw-r--r--test/ruby/test_rand.rb76
-rw-r--r--test/ruby/test_range.rb200
-rw-r--r--test/ruby/test_rational.rb691
-rw-r--r--test/ruby/test_rational2.rb1
-rw-r--r--test/ruby/test_readpartial.rb9
-rw-r--r--test/ruby/test_refinement.rb792
-rw-r--r--test/ruby/test_regexp.rb241
-rw-r--r--test/ruby/test_require.rb314
-rw-r--r--test/ruby/test_rubyoptions.rb532
-rw-r--r--test/ruby/test_rubyvm.rb4
-rw-r--r--test/ruby/test_settracefunc.rb668
-rw-r--r--test/ruby/test_signal.rb80
-rw-r--r--test/ruby/test_sleep.rb15
-rw-r--r--test/ruby/test_sprintf.rb156
-rw-r--r--test/ruby/test_sprintf_comb.rb1
-rw-r--r--test/ruby/test_string.rb1091
-rw-r--r--test/ruby/test_stringchar.rb1
-rw-r--r--test/ruby/test_struct.rb107
-rw-r--r--test/ruby/test_super.rb69
-rw-r--r--test/ruby/test_symbol.rb325
-rw-r--r--test/ruby/test_syntax.rb790
-rw-r--r--test/ruby/test_system.rb4
-rw-r--r--test/ruby/test_thread.rb512
-rw-r--r--test/ruby/test_threadgroup.rb12
-rw-r--r--test/ruby/test_time.rb164
-rw-r--r--test/ruby/test_time_tz.rb101
-rw-r--r--test/ruby/test_trace.rb1
-rw-r--r--test/ruby/test_transcode.rb138
-rw-r--r--test/ruby/test_undef.rb1
-rw-r--r--test/ruby/test_unicode_escape.rb21
-rw-r--r--test/ruby/test_variable.rb76
-rw-r--r--test/ruby/test_vm_dump.rb21
-rw-r--r--test/ruby/test_weakmap.rb11
-rw-r--r--test/ruby/test_whileuntil.rb4
-rw-r--r--test/ruby/test_yield.rb48
-rw-r--r--test/ruby/ut_eof.rb1
138 files changed, 18662 insertions, 4620 deletions
diff --git a/test/ruby/allpairs.rb b/test/ruby/allpairs.rb
index 27b6f5988f..e5893e252a 100644
--- a/test/ruby/allpairs.rb
+++ b/test/ruby/allpairs.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
module AllPairs
module_function
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..cd6447e928
--- /dev/null
+++ b/test/ruby/enc/test_case_comprehensive.rb
@@ -0,0 +1,303 @@
+# 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?
+ titlecase[code] = hex2utf8 data[14] unless data[14].empty?
+ 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..d095cd569c
--- /dev/null
+++ b/test/ruby/enc/test_case_mapping.rb
@@ -0,0 +1,198 @@
+# 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 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 1f80c5a79e..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
diff --git a/test/ruby/enc/test_euc_jp.rb b/test/ruby/enc/test_euc_jp.rb
index 510ee4611e..4aec69e4db 100644
--- a/test/ruby/enc/test_euc_jp.rb
+++ b/test/ruby/enc/test_euc_jp.rb
@@ -1,4 +1,5 @@
# vim: set fileencoding=euc-jp
+# frozen_string_literal: false
require "test/unit"
diff --git a/test/ruby/enc/test_euc_kr.rb b/test/ruby/enc/test_euc_kr.rb
index 5413fa6062..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
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 1bd47fa859..059992d167 100644
--- a/test/ruby/enc/test_shift_jis.rb
+++ b/test/ruby/enc/test_shift_jis.rb
@@ -1,4 +1,5 @@
# vim: set fileencoding=shift_jis
+# frozen_string_literal: false
require "test/unit"
diff --git a/test/ruby/enc/test_utf16.rb b/test/ruby/enc/test_utf16.rb
index 63929c6f4b..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
@@ -55,59 +56,71 @@ EOT
# 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
diff --git a/test/ruby/enc/test_utf32.rb b/test/ruby/enc/test_utf32.rb
index 29a2240598..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
@@ -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
index 72ee3d201a..f264cba759 100644
--- a/test/ruby/enc/test_windows_1252.rb
+++ b/test/ruby/enc/test_windows_1252.rb
@@ -1,4 +1,5 @@
# encoding:windows-1252
+# frozen_string_literal: false
require "test/unit"
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 1fe706df49..0000000000
--- a/test/ruby/envutil.rb
+++ /dev/null
@@ -1,509 +0,0 @@
-# -*- coding: us-ascii -*-
-require "open3"
-require "timeout"
-require "test/unit"
-
-module EnvUtil
- def rubybin
- 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,
- encoding: nil, timeout: 10, reprieve: 1,
- stdout_filter: nil, stderr_filter: nil,
- **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 && capture_stderr != :merge_to_stdout
- opt[:in] = in_c
- opt[:out] = out_c if capture_stdout
- opt[:err] = capture_stderr == :merge_to_stdout ? out_c : err_c if capture_stderr
- if encoding
- out_p.set_encoding(encoding) if out_p
- err_p.set_encoding(encoding) 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 && capture_stderr != :merge_to_stdout
- if block_given?
- return yield in_p, out_p, err_p, pid
- else
- th_stdout = Thread.new { out_p.read } if capture_stdout
- th_stderr = Thread.new { err_p.read } if capture_stderr && capture_stderr != :merge_to_stdout
- in_p.write stdin_data.to_str unless stdin_data.empty?
- in_p.close
- if (!th_stdout || th_stdout.join(timeout)) && (!th_stderr || th_stderr.join(timeout))
- stdout = th_stdout.value if capture_stdout
- stderr = th_stderr.value if capture_stderr && capture_stderr != :merge_to_stdout
- else
- signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :KILL : :TERM
- begin
- Process.kill signal, pid
- Timeout.timeout((reprieve unless signal == :KILL)) do
- Process.wait(pid)
- end
- rescue Errno::ESRCH
- break
- rescue Timeout::Error
- raise if signal == :KILL
- signal = :KILL
- else
- break
- end while true
- bt = caller_locations
- raise Timeout::Error, "execution of #{bt.shift.label} expired", bt.map(&:to_s)
- end
- out_p.close if capture_stdout
- err_p.close if capture_stderr && capture_stderr != :merge_to_stdout
- Process.wait pid
- status = $?
- stdout = stdout_filter.call(stdout) if stdout_filter
- stderr = stderr_filter.call(stderr) if stderr_filter
- return stdout, stderr, status
- end
- ensure
- [th_stdout, th_stderr].each do |th|
- th.kill if th
- end
- [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.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
- return $stderr
- ensure
- stderr, $stderr, $VERBOSE = $stderr, stderr, verbose
- end
- module_function :verbose_warning
-
- def suppress_warning
- verbose, $VERBOSE = $VERBOSE, nil
- yield
- ensure
- $VERBOSE = verbose
- end
- module_function :suppress_warning
-
- def under_gc_stress(stress = true)
- stress, GC.stress = GC.stress, stress
- yield
- ensure
- GC.stress = stress
- end
- module_function :under_gc_stress
-
- def with_default_external(enc)
- verbose, $VERBOSE = $VERBOSE, nil
- origenc, Encoding.default_external = Encoding.default_external, enc
- $VERBOSE = verbose
- yield
- ensure
- verbose, $VERBOSE = $VERBOSE, nil
- Encoding.default_external = origenc
- $VERBOSE = verbose
- end
- module_function :with_default_external
-
- def with_default_internal(enc)
- verbose, $VERBOSE = $VERBOSE, nil
- origenc, Encoding.default_internal = Encoding.default_internal, enc
- $VERBOSE = verbose
- yield
- ensure
- verbose, $VERBOSE = $VERBOSE, nil
- Encoding.default_internal = origenc
- $VERBOSE = verbose
- end
- module_function :with_default_internal
-
- def labeled_module(name, &block)
- Module.new do
- singleton_class.class_eval {define_method(:to_s) {name}; alias inspect to_s}
- class_eval(&block) if block
- end
- end
- module_function :labeled_module
-
- def labeled_class(name, superclass = Object, &block)
- Class.new(superclass) do
- singleton_class.class_eval {define_method(:to_s) {name}; alias inspect to_s}
- class_eval(&block) if block
- end
- end
- module_function :labeled_class
-
- if /darwin/ =~ RUBY_PLATFORM
- DIAGNOSTIC_REPORTS_PATH = File.expand_path("~/Library/Logs/DiagnosticReports")
- DIAGNOSTIC_REPORTS_TIMEFORMAT = '%Y-%m-%d-%H%M%S'
- def self.diagnostic_reports(signame, cmd, pid, now)
- return unless %w[ABRT QUIT SEGV ILL].include?(signame)
- cmd = File.basename(cmd)
- path = DIAGNOSTIC_REPORTS_PATH
- timeformat = DIAGNOSTIC_REPORTS_TIMEFORMAT
- pat = "#{path}/#{cmd}_#{now.strftime(timeformat)}[-_]*.crash"
- first = true
- 30.times do
- first ? (first = false) : sleep(0.1)
- Dir.glob(pat) do |name|
- log = File.read(name) rescue next
- if /\AProcess:\s+#{cmd} \[#{pid}\]$/ =~ log
- File.unlink(name)
- File.unlink("#{path}/.#{File.basename(name)}.plist") rescue nil
- return log
- end
- end
- end
- nil
- end
- else
- def self.diagnostic_reports(signame, cmd, pid, now)
- end
- end
-end
-
-module Test
- module Unit
- module Assertions
- public
- def assert_valid_syntax(code, fname = caller_locations(1, 1)[0], mesg = fname.to_s)
- 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"
- }
- code.force_encoding(Encoding::UTF_8)
- verbose, $VERBOSE = $VERBOSE, nil
- yield if defined?(yield)
- case
- when Array === fname
- fname, line = *fname
- when defined?(fname.path) && defined?(fname.lineno)
- fname, line = fname.path, fname.lineno
- else
- line = 0
- end
- assert_nothing_raised(SyntaxError, mesg) do
- assert_equal(:ok, catch {|tag| eval(code, binding, fname, line)}, mesg)
- end
- ensure
- $VERBOSE = verbose
- end
-
- def assert_syntax_error(code, error, fname = caller_locations(1, 1)[0], mesg = fname.to_s)
- code = code.dup.force_encoding("ascii-8bit")
- code.sub!(/\A(?:\xef\xbb\xbf)?(\s*\#.*$)*(\n)?/n) {
- "#$&#{"\n" if $1 && !$2}BEGIN{throw tag, :ng}\n"
- }
- code.force_encoding("us-ascii")
- verbose, $VERBOSE = $VERBOSE, nil
- yield if defined?(yield)
- case
- when Array === fname
- fname, line = *fname
- when defined?(fname.path) && defined?(fname.lineno)
- fname, line = fname.path, fname.lineno
- else
- line = 0
- end
- e = assert_raise(SyntaxError, mesg) do
- catch {|tag| eval(code, binding, fname, line)}
- end
- assert_match(error, e.message, mesg)
- ensure
- $VERBOSE = verbose
- end
-
- def assert_normal_exit(testsrc, message = '', child_env: nil, **opt)
- assert_valid_syntax(testsrc, caller_locations(1, 1)[0])
- if child_env
- child_env = [child_env]
- else
- child_env = []
- end
- out, _, status = EnvUtil.invoke_ruby(child_env + %W'-W0', testsrc, true, :merge_to_stdout, **opt)
- assert !status.signaled?, FailDesc[status, message, out]
- end
-
- FailDesc = proc do |status, message = "", out = ""|
- pid = status.pid
- now = Time.now
- faildesc = proc do
- if signo = status.termsig
- signame = Signal.signame(signo)
- sigdesc = "signal #{signo}"
- end
- log = EnvUtil.diagnostic_reports(signame, EnvUtil.rubybin, pid, now)
- if signame
- sigdesc = "SIG#{signame} (#{sigdesc})"
- end
- if status.coredump?
- sigdesc << " (core dumped)"
- end
- full_message = ''
- if message and !message.empty?
- full_message << message << "\n"
- end
- full_message << "pid #{pid} killed by #{sigdesc}"
- if out and !out.empty?
- full_message << "\n#{out.gsub(/^/, '| ')}"
- full_message << "\n" if /\n\z/ !~ full_message
- end
- if log
- full_message << "\n#{log.gsub(/^/, '| ')}"
- end
- full_message
- end
- faildesc
- 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 signo = status.termsig
- EnvUtil.diagnostic_reports(Signal.signame(signo), EnvUtil.rubybin, status.pid, Time.now)
- end
- if block_given?
- raise "test_stdout ignored, use block only or without block" if test_stdout != []
- raise "test_stderr ignored, use block only or without block" if test_stderr != []
- yield(stdout.lines.map {|l| l.chomp }, stderr.lines.map {|l| l.chomp }, status)
- else
- errs = []
- [[test_stdout, stdout], [test_stderr, stderr]].each do |exp, act|
- begin
- if exp.is_a?(Regexp)
- assert_match(exp, act, message)
- else
- assert_equal(exp, act.lines.map {|l| l.chomp }, message)
- end
- rescue MiniTest::Assertion => e
- errs << e.message
- message = nil
- end
- end
- raise MiniTest::Assertion, errs.join("\n---\n") unless errs.empty?
- status
- end
- end
-
- def assert_ruby_status(args, test_stdin="", message=nil, **opt)
- out, _, status = EnvUtil.invoke_ruby(args, test_stdin, true, :merge_to_stdout, **opt)
- assert(!status.signaled?, FailDesc[status, message, out])
- message ||= "ruby exit status is not success:"
- assert(status.success?, "#{message} (#{status.inspect})")
- end
-
- ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV")
-
- def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **opt)
- unless file and line
- loc, = caller_locations(1,1)
- file ||= loc.path
- line ||= loc.lineno
- end
- line -= 2
- src = <<eom
-# -*- coding: #{src.encoding}; -*-
- require #{__dir__.dump}'/envutil';include Test::Unit::Assertions
- END {
- puts [Marshal.dump($!)].pack('m'), "assertions=\#{self._assertions}"
- }
-#{src}
- class Test::Unit::Runner
- @@stop_auto_run = true
- end
-eom
- args = args.dup
- args.insert((Hash === args.first ? 1 : 0), "--disable=gems", *$:.map {|l| "-I#{l}"})
- stdout, stderr, status = EnvUtil.invoke_ruby(args, src, true, true, **opt)
- abort = status.coredump? || (status.signaled? && ABORT_SIGNALS.include?(status.termsig))
- assert(!abort, FailDesc[status, nil, stderr])
- self._assertions += stdout[/^assertions=(\d+)/, 1].to_i
- begin
- res = Marshal.load(stdout.unpack("m")[0])
- rescue => marshal_error
- ignore_stderr = nil
- end
- if res
- if bt = res.backtrace
- bt.each do |l|
- l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"}
- end
- end
- raise res
- end
-
- # really is it succeed?
- unless ignore_stderr
- # the body of assert_separately must not output anything to detect errror
- assert_equal("", stderr, "assert_separately failed with error message")
- end
- assert_equal(0, status, "assert_separately failed: '#{stderr}'")
- raise marshal_error if marshal_error
- end
-
- def assert_warning(pat, msg = nil)
- stderr = EnvUtil.verbose_warning { yield }
- msg = message(msg) {diff stderr, pat}
- assert(pat === stderr, msg)
- end
-
- def assert_warn(*args)
- assert_warning(*args) {$VERBOSE = false; yield}
- end
-
- def assert_no_memory_leak(args, prepare, code, message=nil, limit: 1.5, rss: false, **opt)
- require_relative 'memory_status'
- token = "\e[7;1m#{$$.to_s}:#{Time.now.strftime('%s.%L')}:#{rand(0x10000).to_s(16)}:\e[m"
- token_dump = token.dump
- token_re = Regexp.quote(token)
- envs = args.shift if Array === args and Hash === args.first
- args = [
- "--disable=gems",
- "-r", File.expand_path("../memory_status", __FILE__),
- *args,
- "-v", "-",
- ]
- args.unshift(envs) if envs
- cmd = [
- 'END {STDERR.puts '"#{token_dump}"'"FINAL=#{Memory::Status.new}"}',
- prepare,
- 'STDERR.puts('"#{token_dump}"'"START=#{$initial_status = Memory::Status.new}")',
- '$initial_size = $initial_status.size',
- code,
- 'GC.start',
- ].join("\n")
- _, err, status = EnvUtil.invoke_ruby(args, cmd, true, true, **opt)
- before = err.sub!(/^#{token_re}START=(\{.*\})\n/, '') && Memory::Status.parse($1)
- after = err.sub!(/^#{token_re}FINAL=(\{.*\})\n/, '') && Memory::Status.parse($1)
- assert_equal([true, ""], [status.success?, err], message)
- ([:size, (rss && :rss)] & after.members).each do |n|
- b = before[n]
- a = after[n]
- next unless a > 0 and b > 0
- assert_operator(a.fdiv(b), :<, limit, message(message) {"#{n}: #{b} => #{a}"})
- end
- end
-
- def assert_is_minus_zero(f)
- assert(1.0/f == -Float::INFINITY, "#{f} is not -0.0")
- end
-
- def assert_file
- AssertFile
- end
-
- # threads should respond to shift method.
- # Array and Queue can be used.
- def assert_join_threads(threads, message = nil)
- errs = []
- values = []
- while th = threads.shift
- begin
- values << th.value
- rescue Exception
- errs << $!
- end
- end
- if !errs.empty?
- msg = errs.map {|err|
- err.backtrace.map.with_index {|line, i|
- if i == 0
- "#{line}: #{err.message} (#{err.class})"
- else
- "\tfrom #{line}"
- end
- }.join("\n")
- }.join("\n---\n")
- if message
- msg = "#{message}\n#{msg}"
- end
- raise MiniTest::Assertion, msg
- end
- values
- end
-
- class << (AssertFile = Struct.new(:failure_message).new)
- include Assertions
- def assert_file_predicate(predicate, *args)
- if /\Anot_/ =~ predicate
- predicate = $'
- neg = " not"
- end
- result = File.__send__(predicate, *args)
- result = !result if neg
- mesg = "Expected file " << args.shift.inspect
- mesg << "#{neg} to be #{predicate}"
- mesg << mu_pp(args).sub(/\A\[(.*)\]\z/m, '(\1)') unless args.empty?
- mesg << " #{failure_message}" if failure_message
- assert(result, mesg)
- end
- alias method_missing assert_file_predicate
-
- def for(message)
- clone.tap {|a| a.failure_message = message}
- end
- 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::ConfigMap)
- end
-end
diff --git a/test/ruby/lbtest.rb b/test/ruby/lbtest.rb
index ae047fb187..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
diff --git a/test/ruby/marshaltestlib.rb b/test/ruby/marshaltestlib.rb
index 665d365a9a..358d3c5133 100644
--- a/test/ruby/marshaltestlib.rb
+++ b/test/ruby/marshaltestlib.rb
@@ -1,4 +1,5 @@
# coding: utf-8
+# frozen_string_literal: false
module MarshalTestLib
# include this module to a Test::Unit::TestCase and define encode(o) and
# decode(s) methods. e.g.
diff --git a/test/ruby/memory_status.rb b/test/ruby/memory_status.rb
deleted file mode 100644
index bfbfbd6e88..0000000000
--- a/test/ruby/memory_status.rb
+++ /dev/null
@@ -1,127 +0,0 @@
-module Memory
- keys = []
- vals = []
-
- case
- when File.exist?(procfile = "/proc/self/status") && (pat = /^Vm(\w+):\s+(\d+)/) =~ File.binread(procfile)
- PROC_FILE = procfile
- VM_PAT = pat
- def self.read_status
- IO.foreach(PROC_FILE, encoding: Encoding::ASCII_8BIT) do |l|
- yield($1.downcase.intern, $2.to_i * 1024) if VM_PAT =~ l
- end
- end
-
- read_status {|k, v| keys << k; vals << v}
-
- when /mswin|mingw/ =~ RUBY_PLATFORM
- begin
- require 'fiddle/import'
- rescue LoadError
- EnvUtil.suppress_warning do
- require 'dl/import'
- end
- end
- begin
- require 'fiddle/types'
- rescue LoadError
- EnvUtil.suppress_warning do
- require 'dl/types'
- end
- end
-
- module Win32
- begin
- extend Fiddle::Importer
- rescue NameError
- extend DL::Importer
- end
- dlload "kernel32.dll", "psapi.dll"
- begin
- include Fiddle::Win32Types
- rescue NameError
- include DL::Win32Types
- end
- typealias "SIZE_T", "size_t"
-
- PROCESS_MEMORY_COUNTERS = struct [
- "DWORD cb",
- "DWORD PageFaultCount",
- "SIZE_T PeakWorkingSetSize",
- "SIZE_T WorkingSetSize",
- "SIZE_T QuotaPeakPagedPoolUsage",
- "SIZE_T QuotaPagedPoolUsage",
- "SIZE_T QuotaPeakNonPagedPoolUsage",
- "SIZE_T QuotaNonPagedPoolUsage",
- "SIZE_T PagefileUsage",
- "SIZE_T PeakPagefileUsage",
- ]
-
- typealias "PPROCESS_MEMORY_COUNTERS", "PROCESS_MEMORY_COUNTERS*"
-
- extern "HANDLE GetCurrentProcess()", :stdcall
- extern "BOOL GetProcessMemoryInfo(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD)", :stdcall
-
- module_function
- def memory_info
- size = PROCESS_MEMORY_COUNTERS.size
- data = PROCESS_MEMORY_COUNTERS.malloc
- data.cb = size
- data if GetProcessMemoryInfo(GetCurrentProcess(), data, size)
- end
- end
-
- keys << :peak << :size
- def self.read_status
- if info = Win32.memory_info
- yield :peak, info.PeakPagefileUsage
- yield :size, info.PagefileUsage
- end
- end
- else
- PSCMD = ["ps", "-ovsz=","-orss=", "-p"]
- PAT = /^\s*(\d+)\s+(\d+)$/
-
- keys << :size << :rss
- def self.read_status
- if PAT =~ IO.popen(PSCMD + [$$.to_s], "r", err: [:child, :out], &:read)
- yield :size, $1.to_i*1024
- yield :rss, $2.to_i*1024
- end
- end
- end
-
- Status = Struct.new(*keys)
-
- class Status
- def _update
- Memory.read_status do |key, val|
- self[key] = val
- end
- end
- end
-
- class Status
- Header = members.map {|k| k.to_s.upcase.rjust(6)}.join('')
- Format = "%6d"
-
- def initialize
- _update
- end
-
- def to_s
- status = each_pair.map {|n,v|
- "#{n}:#{v}"
- }
- "{#{status.join(",")}}"
- end
-
- def self.parse(str)
- status = allocate
- str.scan(/(?:\A\{|\G,)(#{members.join('|')}):(\d+)(?=,|\}\z)/) do
- status[$1] = $2.to_i
- end
- status
- end
- end
-end
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 dec61f6d63..e81636fa43 100644
--- a/test/ruby/test_alias.rb
+++ b/test/ruby/test_alias.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestAlias < Test::Unit::TestCase
class Alias0
@@ -122,7 +122,8 @@ class TestAlias < Test::Unit::TestCase
end
def test_alias_wb_miss
- assert_normal_exit %q{
+ assert_normal_exit "#{<<-"begin;"}\n#{<<-'end;'}"
+ begin;
require 'stringio'
GC.verify_internal_consistency
GC.start
@@ -130,7 +131,7 @@ class TestAlias < Test::Unit::TestCase
alias_method :read_nonblock, :sysread
end
GC.verify_internal_consistency
- }
+ end;
end
def test_cyclic_zsuper
@@ -183,7 +184,8 @@ class TestAlias < Test::Unit::TestCase
def test_alias_in_module
bug9663 = '[ruby-core:61635] [Bug #9663]'
- assert_separately(['-', bug9663], <<-'end;')
+ assert_separately(['-', bug9663], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
bug = ARGV[0]
m = Module.new do
@@ -194,4 +196,41 @@ class TestAlias < Test::Unit::TestCase
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 104a681ee0..311469aad9 100644
--- a/test/ruby/test_argf.rb
+++ b/test/ruby/test_argf.rb
@@ -1,9 +1,9 @@
+# frozen_string_literal: false
require 'test/unit'
require 'timeout'
require 'tmpdir'
require 'tempfile'
require 'fileutils'
-require_relative 'envutil'
class TestArgf < Test::Unit::TestCase
def setup
@@ -57,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|
@@ -71,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))
@@ -153,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'
@@ -167,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))
@@ -177,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
@@ -190,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))
@@ -202,27 +235,36 @@ 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
+ 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_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
@@ -236,63 +278,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
- 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
- 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)
@@ -300,7 +404,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)
@@ -312,11 +417,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
@@ -328,11 +434,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"
@@ -347,28 +454,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)
@@ -380,12 +488,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)
@@ -399,16 +508,17 @@ 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)
@@ -435,66 +545,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', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- s = "0123456789"
- ARGF.read(8, s)
- p s
- SRC
+ 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
- # not empty buffer
- u = "abcdef"; ARGF.readpartial(1, u); s << u
+ 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")
@@ -505,76 +620,82 @@ class TestArgf < Test::Unit::TestCase
end
def test_readpartial_eof_twice
- ruby('-W1', '-e', <<-SRC, @t1.path) do |f|
- $stderr = $stdout
- print ARGF.readpartial(256)
- ARGF.readpartial(256) rescue p($!.class)
- ARGF.readpartial(256) rescue p($!.class)
- SRC
+ 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
@@ -585,32 +706,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)
@@ -624,12 +748,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)
@@ -643,12 +768,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)
@@ -680,33 +806,37 @@ class TestArgf < Test::Unit::TestCase
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', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- ARGF.each_line {|l| print l; ARGF.skip}
- SRC
+ 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', <<-SRC, @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("+")}
- SRC
+ 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', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- ARGF.each_byte {|l| print l; ARGF.skip}
- SRC
+ 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
@@ -715,9 +845,10 @@ class TestArgf < Test::Unit::TestCase
[[@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', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- ARGF.each_char {|l| print l; ARGF.skip}
- SRC
+ 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
@@ -726,43 +857,48 @@ class TestArgf < Test::Unit::TestCase
[[@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', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- ARGF.each_codepoint {|l| printf "%x:", l; ARGF.skip}
- SRC
+ 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', <<-SRC) do |f|
- ARGF.close
- ARGV.replace ['#{@t1.path}', '#{@t2.path}', '#{@t3.path}']
- puts ARGF.read
- SRC
+ 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
@@ -821,44 +957,101 @@ class TestArgf < Test::Unit::TestCase
end
def test_lines
- ruby('-W1', '-e', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- $stderr = $stdout
- s = []
- ARGF.lines {|l| s << l }
- p s
- SRC
+ 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', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- $stderr = $stdout
- print Marshal.dump(ARGF.bytes.to_a)
- SRC
+ 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', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- $stderr = $stdout
- print [Marshal.dump(ARGF.chars.to_a)].pack('m')
- SRC
- 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))
+ 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', <<-SRC, @t1.path, @t2.path, @t3.path) do |f|
- $stderr = $stdout
- print Marshal.dump(ARGF.codepoints.to_a)
- SRC
+ 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_arity.rb b/test/ruby/test_arity.rb
index e026841749..b98248f603 100644
--- a/test/ruby/test_arity.rb
+++ b/test/ruby/test_arity.rb
@@ -1,9 +1,10 @@
+# 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 \((.*)\)/) do
+ assert_raise_with_message(ArgumentError, /wrong number of arguments \(.*\b(\d+)\b.* (\d\S*?)\)/) do
case method_proc
when nil
yield
@@ -13,7 +14,7 @@ class TestArity < Test::Unit::TestCase
method_proc.call(*args)
end
end
- $1
+ [$1, $2]
end
def a
@@ -35,22 +36,22 @@ class TestArity < Test::Unit::TestCase
end
def test_method_err_mess
- assert_equal "1 for 0", err_mess(:a, 1)
- assert_equal "10 for 7..9", err_mess(:b, 10)
- assert_equal "2 for 3+", err_mess(:c, 2)
- assert_equal "2 for 1", err_mess(:d, 2)
- assert_equal "0 for 1", err_mess(:d, 0)
- assert_equal "2 for 1", err_mess(:e, 2)
- assert_equal "0 for 1", err_mess(:e, 0)
- assert_equal "1 for 2+", err_mess(:f, 1)
+ 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 "0 for 1..2", err_mess(->(b, c=42){}, 0)
- assert_equal "1 for 2+", err_mess(->(a, b, c=42, *d){}, 1)
- assert_equal "3 for 4+", err_mess(->(a, b, *c, d, e){}, 3)
- assert_equal "3 for 1..2", err_mess(->(b, c=42){}, 3)
- assert_equal "1 for 0", err_mess(->(&block){}, 1)
+ 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)
@@ -58,12 +59,12 @@ class TestArity < Test::Unit::TestCase
end
def test_message_change_issue_6085
- assert_equal "3 for 1..2", err_mess{ SignalException.new(1, "", nil) }
- assert_equal "1 for 0", err_mess{ Hash.new(1){} }
- assert_equal "3 for 1..2", err_mess{ Module.send :define_method, 1, 2, 3 }
- assert_equal "1 for 2", err_mess{ "".sub!(//) }
- assert_equal "0 for 1..2", err_mess{ "".sub!{} }
- assert_equal "0 for 1+", err_mess{ exec }
- assert_equal "0 for 1+", err_mess{ Struct.new }
+ 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 f10023edfd..3212ed3aca 100644
--- a/test/ruby/test_array.rb
+++ b/test/ruby/test_array.rb
@@ -1,6 +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
@@ -222,6 +224,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)
@@ -258,6 +267,18 @@ class TestArray < Test::Unit::TestCase
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)
+ # Ruby 1.8 feature change
+ #assert_equal(@cls[1], @cls[1, 2, 1, 3, 1, 4, 1, 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], a - @cls[2])
+ assert_equal(@cls[1] * 1000, a - @cls[2])
+ end
+
def test_LSHIFT # '<<'
a = @cls[]
a << 1
@@ -441,6 +462,17 @@ class TestArray < Test::Unit::TestCase
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
a1 = @cls[*%w( cat feline )]
a2 = @cls[*%w( dog canine )]
@@ -493,7 +525,7 @@ class TestArray < Test::Unit::TestCase
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 })
@@ -502,13 +534,15 @@ class TestArray < Test::Unit::TestCase
# 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_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 } )
@@ -554,7 +588,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]]))
@@ -562,8 +598,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
@@ -586,7 +626,7 @@ class TestArray < Test::Unit::TestCase
assert_in_out_err [], <<-EOS, ["[]", "0"], [], bug8654
ARY = Array.new(100) { |i| i }
- class Fixnum
+ class Integer
alias old_equal ==
def == other
ARY.replace([]) if self.equal?(0)
@@ -662,7 +702,7 @@ class TestArray < Test::Unit::TestCase
bug2545 = '[ruby-core:27366]'
a = @cls[ 5, 6, 7, 8, 9, 10 ]
- assert_equal(9, a.delete_if {|i| break i if i > 8; assert_equal(a[0], i) || true if i < 7})
+ assert_equal(9, a.delete_if {|i| break i if i > 8; i < 7})
assert_equal(@cls[7, 8, 9, 10], a, bug2545)
end
@@ -768,23 +808,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
a7 = a6.flatten
assert_equal(true, a7.tainted?)
+ 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 ]
@@ -797,16 +866,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
@@ -819,8 +914,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]
@@ -837,7 +954,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]
@@ -854,7 +971,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]
@@ -871,7 +988,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]
@@ -888,7 +1005,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]
@@ -989,6 +1106,7 @@ class TestArray < Test::Unit::TestCase
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)
@@ -1013,8 +1131,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 } )
@@ -1119,6 +1237,14 @@ 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))
@@ -1161,10 +1287,69 @@ class TestArray < Test::Unit::TestCase
bug2545 = '[ruby-core:27366]'
a = @cls[ 5, 6, 7, 8, 9, 10 ]
- assert_equal(9, a.reject! {|i| break i if i > 8; assert_equal(a[0], i) || true if i < 7})
+ 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
a = @cls[ 1, 2, 3]
a_id = a.__id__
@@ -1175,10 +1360,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
@@ -1366,7 +1551,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
@@ -1403,13 +1588,23 @@ class TestArray < Test::Unit::TestCase
1
}
}
- o2 = o1.dup
+ o2 = o1.clone
ary << o1 << o2
orig = ary.dup
- assert_raise(RuntimeError, "frozen during comparison") {ary.sort!}
+ 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__
@@ -1475,12 +1670,57 @@ class TestArray < Test::Unit::TestCase
[[: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_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
@@ -1595,7 +1835,7 @@ 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"} ]
@@ -1641,7 +1881,7 @@ class TestArray < Test::Unit::TestCase
def test_uniq_bang_with_freeze
ary = [1,2]
orig = ary.dup
- assert_raise(RuntimeError, "frozen during comparison") {
+ assert_raise(FrozenError, "frozen during comparison") {
ary.uniq! {|v| ary.freeze; 1}
}
assert_equal(orig, ary, "must not be modified once frozen")
@@ -1655,6 +1895,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[])
@@ -1689,13 +1940,48 @@ class TestArray < Test::Unit::TestCase
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_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
@@ -1727,7 +2013,16 @@ class TestArray < Test::Unit::TestCase
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]],
@@ -1750,8 +2045,26 @@ class TestArray < Test::Unit::TestCase
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]],
@@ -1775,8 +2088,25 @@ class TestArray < Test::Unit::TestCase
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]],
@@ -1804,6 +2134,15 @@ class TestArray < Test::Unit::TestCase
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
assert_equal([1,2,3], [1,2,3,4,5,0].take(3))
assert_raise(ArgumentError, '[ruby-dev:34123]') { [1,2].take(-1) }
@@ -1866,7 +2205,7 @@ class TestArray < Test::Unit::TestCase
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
@@ -1886,11 +2225,13 @@ 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
@@ -1947,9 +2288,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
@@ -1989,7 +2332,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 ]
@@ -1999,6 +2341,36 @@ 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_delete2
@@ -2012,7 +2384,7 @@ class TestArray < Test::Unit::TestCase
end
def test_reject_with_callcc
- respond_to?(:callcc, true) or require 'continuation'
+ need_continuation
bug9727 = '[ruby-dev:48101] [Bug #9727]'
cont = nil
a = [*1..10].reject do |i|
@@ -2101,8 +2473,8 @@ 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
@@ -2158,6 +2530,13 @@ class TestArray < Test::Unit::TestCase
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
@@ -2258,6 +2637,10 @@ class TestArray < Test::Unit::TestCase
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
@@ -2274,6 +2657,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
@@ -2293,11 +2679,18 @@ class TestArray < Test::Unit::TestCase
def test_combination_clear
bug9939 = '[ruby-core:63149] [Bug #9939]'
- assert_separately([], <<-'end;')
- 100_000.times {Array.new(1000)}
+ assert_nothing_raised(bug9939) {
a = [*0..100]
a.combination(3) {|*,x| a.clear}
- end;
+ }
+
+ 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
@@ -2315,8 +2708,8 @@ 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
@@ -2338,6 +2731,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
@@ -2414,7 +2812,7 @@ class TestArray < Test::Unit::TestCase
assert_equal([], a.rotate!(13))
assert_equal([], a.rotate!(-13))
a = [].freeze
- assert_raise_with_message(RuntimeError, /can\'t modify frozen/) {a.rotate!}
+ assert_raise_with_message(FrozenError, /can\'t modify frozen/) {a.rotate!}
a = [1,2,3]
assert_raise(ArgumentError) { a.rotate!(1, 1) }
end
@@ -2423,6 +2821,10 @@ class TestArray < Test::Unit::TestCase
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
@@ -2454,6 +2856,41 @@ class TestArray < Test::Unit::TestCase
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
@@ -2485,24 +2922,131 @@ class TestArray < Test::Unit::TestCase
Bug11235 = '[ruby-dev:49043] [Bug #11235]'
def test_push_over_ary_max
- assert_separately(['-', ARY_MAX.to_s, Bug11235], <<-"end;")
+ 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], <<-"end;")
+ 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], <<-"end;")
+ 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..45c4d6e058 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) {
+ assert_equal(43, o.instance_eval {self.foo += 1})
+ }
+ assert_raise(NoMethodError, bug11096) {
+ assert_equal(1, o.instance_eval {self.foo &&= 1})
+ }
+
+ assert_raise(NoMethodError, bug11096) {
+ assert_equal(43, o.instance_eval {self[0] += 1})
+ }
+ assert_raise(NoMethodError, bug11096) {
+ assert_equal(1, 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
@@ -496,6 +552,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 +753,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_autoload.rb b/test/ruby/test_autoload.rb
index f88bcac579..3095052a81 100644
--- a/test/ruby/test_autoload.rb
+++ b/test/ruby/test_autoload.rb
@@ -1,14 +1,13 @@
+# frozen_string_literal: false
require 'test/unit'
require 'tempfile'
-require 'thread'
-require_relative 'envutil'
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
@@ -99,36 +98,42 @@ p Foo::Bar
end
def test_threaded_accessing_constant
- 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)
+ # 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
- ensure
- remove_autoload_constant
- end
+ }
}
end
def test_threaded_accessing_inner_constant
- 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)
+ # 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
- ensure
- remove_autoload_constant
- end
+ }
}
end
@@ -182,21 +187,137 @@ p Foo::Bar
}
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 add_autoload(path)
(@autoload_paths ||= []) << path
- eval <<-END
- class ::Object
- autoload :AutoloadTest, #{path.dump}
- end
- END
+ ::Object.class_eval {autoload(:AutoloadTest, path)}
end
def remove_autoload_constant
$".replace($" - @autoload_paths)
- eval <<-END
- class ::Object
- remove_const(:AutoloadTest)
- end
- END
+ ::Object.class_eval {remove_const(:AutoloadTest)}
end
end
diff --git a/test/ruby/test_backtrace.rb b/test/ruby/test_backtrace.rb
index ed36fe95f6..d38628cdb2 100644
--- a/test/ruby/test_backtrace.rb
+++ b/test/ruby/test_backtrace.rb
@@ -1,5 +1,6 @@
+# frozen_string_literal: false
require 'test/unit'
-require 'thread'
+require 'tempfile'
class TestBacktrace < Test::Unit::TestCase
def test_exception
@@ -78,10 +79,10 @@ class TestBacktrace < Test::Unit::TestCase
cs << caller(5)
}.call
}.resume
- assert_equal(3, cs[0].size)
- assert_equal(2, cs[1].size)
- assert_equal(1, cs[2].size)
- assert_equal(0, cs[3].size)
+ 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])
#
@@ -101,7 +102,7 @@ class TestBacktrace < Test::Unit::TestCase
}
end
}
- bt = Fiber.new{
+ Fiber.new{
rec[max]
}.resume
end
@@ -111,21 +112,21 @@ class TestBacktrace < Test::Unit::TestCase
rec = lambda{|n|
if n < 0
(m*6).times{|lev|
- (m*6).times{|n|
+ (m*6).times{|i|
t = caller(0).size
- r = caller(lev, n)
+ r = caller(lev, i)
r = r.size if r.respond_to? :size
- # STDERR.puts [t, lev, n, r].inspect
- if n == 0
- assert_equal(0, r, [t, lev, n, r].inspect)
+ # 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, n, r].inspect)
+ assert_equal(nil, r, [t, lev, i, r].inspect)
else
- if t - lev > n
- assert_equal(n, r, [t, lev, n, r].inspect)
+ if t - lev > i
+ assert_equal(i, r, [t, lev, i, r].inspect)
else
- assert_equal(t - lev, r, [t, lev, n, r].inspect)
+ assert_equal(t - lev, r, [t, lev, i, r].inspect)
end
end
}
@@ -163,6 +164,59 @@ class TestBacktrace < Test::Unit::TestCase
}
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
@@ -173,7 +227,7 @@ class TestBacktrace < Test::Unit::TestCase
def test_thread_backtrace
begin
- q = Queue.new
+ q = Thread::Queue.new
th = Thread.new{
th_rec q
}
@@ -195,12 +249,13 @@ class TestBacktrace < Test::Unit::TestCase
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 = Queue.new
+ q = Thread::Queue.new
th = Thread.new{
th_rec q
}
@@ -212,6 +267,7 @@ class TestBacktrace < Test::Unit::TestCase
assert_equal(bt, locs)
ensure
q << true
+ th.join
end
end
diff --git a/test/ruby/test_basicinstructions.rb b/test/ruby/test_basicinstructions.rb
index 4a1dc9ce12..dd3ca4dd22 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
@@ -210,9 +211,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
@@ -610,8 +611,8 @@ class TestBasicInstructions < Test::Unit::TestCase
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 (-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
@@ -697,4 +698,26 @@ class TestBasicInstructions < Test::Unit::TestCase
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 d9c1f56916..eb8394864f 100644
--- a/test/ruby/test_beginendblock.rb
+++ b/test/ruby/test_beginendblock.rb
@@ -1,34 +1,17 @@
+# 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 e1-1 e4 e4-2 e4-1 e4-1-1 e3 e2), result.split)
-
- Tempfile.create(self.class.name) {|input|
- inputpath = input.path
- 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.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
@@ -48,56 +31,38 @@ class TestBeginEndBlock < Test::Unit::TestCase
end
def test_endblockwarn
- ruby = EnvUtil.rubybin
- # Use Tempfile to create temporary file path.
- Tempfile.create(self.class.name) {|launcher|
- Tempfile.create(self.class.name) {|errout|
-
- 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))
- }
- }
+ 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
- }
- status = $?
- 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]'
- ruby = EnvUtil.rubybin
- out = IO.popen([ruby, '-e', 'STDERR.reopen(STDOUT)',
- '-e', 'o = Object.new; def o.inspect; raise "[Bug #8501]"; end',
- '-e', 'at_exit{o.nope}']) {|f|
- f.read
- }
- status = $?
- assert_match(/undefined method `nope'/, out, bug8501)
+ 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
@@ -109,57 +74,42 @@ EOW
end
def test_propagate_signaled
- ruby = EnvUtil.rubybin
- out = IO.popen(
- [ruby,
- '-e', 'trap(:INT, "DEFAULT")',
- '-e', 'STDERR.reopen(STDOUT)',
- '-e', 'at_exit{Process.kill(:INT, $$); sleep 5 }']) {|f|
- timeout(10) {
- f.read
- }
- }
- assert_match(/Interrupt$/, out)
+ 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
- Tempfile.create(["test_nested_at_exit_", ".rb"]) {|t|
- t.puts "at_exit { puts :outer0 }"
- t.puts "at_exit { puts :outer1_begin; at_exit { puts :inner1 }; puts :outer1_end }"
- t.puts "at_exit { puts :outer2_begin; at_exit { puts :inner2 }; puts :outer2_end }"
- t.puts "at_exit { puts :outer3 }"
- t.flush
-
- expected = [ "outer3",
- "outer2_begin",
- "outer2_end",
- "inner2",
- "outer1_begin",
- "outer1_end",
- "inner1",
- "outer0" ]
-
- assert_in_out_err(t.path, "", expected, [], "[ruby-core:35237]")
- }
+ 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
@@ -177,12 +127,53 @@ EOW
def test_callcc_at_exit
bug9110 = '[ruby-core:58329][Bug #9110]'
- script = <<EOS
-require "continuation"
-c = nil
-at_exit { c.call }
-at_exit { callcc {|_c| c = _c } }
-EOS
- assert_normal_exit(script, bug9110)
+ 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 2c6eda0d8e..65d974005e 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,6 +56,21 @@ 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)
@@ -35,8 +83,10 @@ class TestBignum < Test::Unit::TestCase
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)
@@ -110,41 +160,6 @@ class TestBignum < Test::Unit::TestCase
assert_match(/\A10{900}9{100}\z/, (10**1000+(10**100-1)).to_s)
end
- b = 2**64
- b *= b until Bignum === b
-
- T_ZERO = b.coerce(0).first
- T_ONE = b.coerce(1).first
- T_MONE = b.coerce(-1).first
- T31 = b.coerce(2**31).first # 2147483648
- T31P = b.coerce(T31 - 1).first # 2147483647
- T32 = b.coerce(2**32).first # 4294967296
- T32P = b.coerce(T32 - 1).first # 4294967295
- T64 = b.coerce(2**64).first # 18446744073709551616
- T64P = b.coerce(T64 - 1).first # 18446744073709551615
- T1024 = b.coerce(2**1024).first
- T1024P = b.coerce(T1024 - 1).first
-
- f = b
- while Bignum === f-1
- f = f >> 1
- end
- FIXNUM_MAX = f-1
-
- def test_prepare
- assert_instance_of(Bignum, T_ZERO)
- assert_instance_of(Bignum, T_ONE)
- assert_instance_of(Bignum, T_MONE)
- assert_instance_of(Bignum, T31)
- assert_instance_of(Bignum, T31P)
- assert_instance_of(Bignum, T32)
- assert_instance_of(Bignum, T32P)
- assert_instance_of(Bignum, T64)
- assert_instance_of(Bignum, T64P)
- assert_instance_of(Bignum, T1024)
- assert_instance_of(Bignum, T1024P)
- end
-
def test_big_2comp
assert_equal("-4294967296", (~T32P).to_s)
assert_equal("..f00000000", "%x" % -T32)
@@ -268,6 +283,18 @@ 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
@@ -448,7 +475,7 @@ class TestBignum < Test::Unit::TestCase
assert_raise(TypeError, ArgumentError) { T32**"foo" }
feature3429 = '[ruby-core:30735]'
- assert_instance_of(Bignum, (2 ** 7830457), feature3429)
+ assert_kind_of(Integer, (2 ** 7830457), feature3429)
end
def test_and
@@ -515,24 +542,26 @@ class TestBignum < Test::Unit::TestCase
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
@@ -541,12 +570,12 @@ class TestBignum < Test::Unit::TestCase
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
@@ -556,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
@@ -567,17 +598,17 @@ class TestBignum < Test::Unit::TestCase
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 test_interrupt_during_to_s
- if defined?(Bignum::GMP_VERSION)
+ if defined?(Integer::GMP_VERSION)
return # GMP doesn't support interrupt during an operation.
end
time = Time.now
@@ -586,19 +617,21 @@ class TestBignum < Test::Unit::TestCase
num = (65536 ** 65536)
thread = Thread.new do
start_flag = true
- num.to_s
- end_flag = true
+ assert_raise(RuntimeError) {
+ num.to_s
+ end_flag = true
+ }
end
sleep 0.001 until start_flag
thread.raise
- thread.join rescue nil
+ thread.join
time = Time.now - time
skip "too fast cpu" if end_flag
assert_operator(time, :<, 10)
end
def test_interrupt_during_bigdivrem
- if defined?(Bignum::GMP_VERSION)
+ if defined?(Integer::GMP_VERSION)
return # GMP doesn't support interrupt during an operation.
end
return unless Process.respond_to?(:kill)
@@ -631,7 +664,7 @@ class TestBignum < Test::Unit::TestCase
end
def test_too_big_to_s
- if (big = 2**31-1).is_a?(Fixnum)
+ if (big = 2**31-1).fixnum?
return
end
assert_raise_with_message(RangeError, /too big to convert/) {(1 << big).to_s}
@@ -662,6 +695,10 @@ 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
@@ -708,4 +745,54 @@ class TestBignum < Test::Unit::TestCase
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 5b81eb187a..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
@@ -31,4 +32,71 @@ class TestCall < Test::Unit::TestCase
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 f17f9aad9b..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,7 @@ 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
@@ -122,4 +122,25 @@ class TestCase < Test::Unit::TestCase
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 b40cab8050..ad2caeb8be 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
@@ -194,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
@@ -238,13 +241,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
@@ -294,7 +312,8 @@ class TestClass < Test::Unit::TestCase
end
def test_cannot_reinitialize_class_with_initialize_copy # [ruby-core:50869]
- assert_in_out_err([], <<-'end;', ["Object"], [])
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", ["Object"], [])
+ begin;
class Class
def initialize_copy(*); super; end
end
@@ -357,6 +376,41 @@ class TestClass < Test::Unit::TestCase
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
@@ -378,4 +432,197 @@ class TestClass < Test::Unit::TestCase
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..93ef438461 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
diff --git a/test/ruby/test_comparable.rb b/test/ruby/test_comparable.rb
index 747f1e29a7..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,9 +76,27 @@ 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
diff --git a/test/ruby/test_complex.rb b/test/ruby/test_complex.rb
index aaad5ee088..316e3e21ff 100644
--- a/test/ruby/test_complex.rb
+++ b/test/ruby/test_complex.rb
@@ -1,18 +1,10 @@
+# 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
- end
-
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]')
@@ -24,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]')
@@ -53,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
@@ -91,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
@@ -133,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()}
@@ -209,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?)
@@ -267,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
@@ -305,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
@@ -322,10 +265,8 @@ 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_sub
@@ -337,10 +278,8 @@ 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_mul
@@ -352,24 +291,22 @@ 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))
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)
@@ -381,30 +318,18 @@ 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))
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)
@@ -416,17 +341,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
@@ -460,13 +379,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)
@@ -475,21 +389,17 @@ 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)
end
def test_cmp
@@ -521,10 +431,13 @@ 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
+ def +(x) Rational(1) end
alias - +
alias * +
alias / +
@@ -540,18 +453,6 @@ class Complex_Test < Test::Unit::TestCase
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
- end
- end
-
def test_math
c = Complex(1,2)
@@ -573,8 +474,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)
@@ -602,23 +501,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
@@ -640,26 +537,21 @@ 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)
- end
+ 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)
- c.freeze
assert_predicate(c, :frozen?)
result = c.marshal_load([2,3]) rescue :fail
assert_equal(:fail, result, bug3656)
@@ -835,57 +727,54 @@ 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_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
@@ -903,12 +792,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
@@ -924,10 +811,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])
@@ -940,9 +825,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)
@@ -1011,134 +932,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|(?<!add)\/)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
@@ -1148,9 +948,7 @@ 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)
diff --git a/test/ruby/test_complex2.rb b/test/ruby/test_complex2.rb
index 3ee7810dc6..594fc3f45a 100644
--- a/test/ruby/test_complex2.rb
+++ b/test/ruby/test_complex2.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class Complex_Test2 < Test::Unit::TestCase
diff --git a/test/ruby/test_complexrational.rb b/test/ruby/test_complexrational.rb
index cef4074afa..0360f5ee42 100644
--- a/test/ruby/test_complexrational.rb
+++ b/test/ruby/test_complexrational.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class ComplexRational_Test < Test::Unit::TestCase
@@ -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 c4a4d93249..8784e0e988 100644
--- a/test/ruby/test_const.rb
+++ b/test/ruby/test_const.rb
@@ -1,6 +1,6 @@
# -*- coding: us-ascii -*-
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestConst < Test::Unit::TestCase
TEST1 = 1
@@ -37,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)
@@ -50,17 +50,23 @@ class TestConst < Test::Unit::TestCase
def test_redefinition
c = Class.new
- c.const_set(:X, 1)
- assert_output(nil, <<-WARNING) {c.const_set(:X, 2)}
-#{__FILE__}:#{__LINE__-1}: warning: already initialized constant #{c}::X
-#{__FILE__}:#{__LINE__-3}: warning: previous definition of X was here
+ 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
-olderr = $stderr.dup
-$stderr.reopen(File::NULL, "wb")
350000.times { FOO = :BAR }
-$stderr.reopen(olderr)
PRE
- assert_no_memory_leak([], '', code, 'redefined constant')
+ 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 52d8de482c..a06ac98c8c 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
}
@@ -132,4 +134,3 @@ class TestContinuation < Test::Unit::TestCase
assert_equal 3, @memo
end
end
-
diff --git a/test/ruby/test_defined.rb b/test/ruby/test_defined.rb
index fdae6789f1..9976db3b6f 100644
--- a/test/ruby/test_defined.rb
+++ b/test/ruby/test_defined.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestDefined < Test::Unit::TestCase
class Foo
@@ -99,6 +99,10 @@ class TestDefined < Test::Unit::TestCase
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)
@@ -203,7 +207,7 @@ class TestDefined < Test::Unit::TestCase
o = c.new
o.extend(m)
- assert_equal("super", o.x)
+ assert_equal("super", o.x, bug8367)
end
def test_super_toplevel
@@ -224,6 +228,14 @@ class TestDefined < Test::Unit::TestCase
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
@@ -233,5 +245,16 @@ class TestDefined < Test::Unit::TestCase
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 6054b8f7fe..000bc24e85 100644
--- a/test/ruby/test_dir.rb
+++ b/test/ruby/test_dir.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
require 'tmpdir'
@@ -10,7 +11,7 @@ class TestDir < Test::Unit::TestCase
$VERBOSE = nil
@root = File.realpath(Dir.mktmpdir('__test_dir__'))
@nodir = File.join(@root, "dummy")
- for i in ?a..?z
+ for i in "a".."z"
if i.ord % 2 == 0
FileUtils.touch(File.join(@root, i))
else
@@ -126,19 +127,21 @@ 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,
+ assert_equal([@root] + ("a".."z").map {|f| File.join(@root, f) }.sort,
Dir.glob(@root + "\0\0\0" + File.join(@root, "*")).sort)
- 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) },
@@ -147,7 +150,15 @@ 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)
+
+ 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
@@ -176,8 +187,68 @@ class TestDir < Test::Unit::TestCase
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), "")}
+ 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})
+ end
+
+ def test_glob_base_dir
+ files = %w[a/foo.c c/bar.c]
+ files.each {|n| File.write(File.join(@root, n), "")}
+ 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("*", base: d)}})
+ 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.children(@root), true)
+ assert_raise(ArgumentError) {Dir.children(@root+"\0")}
+ end
+
+ def test_each_child
+ assert_entries(Dir.each_child(@root).to_a, true)
+ assert_raise(ArgumentError) {Dir.each_child(@root+"\0").to_a}
end
def test_dir_enc
@@ -209,18 +280,19 @@ class TestDir < Test::Unit::TestCase
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
@@ -228,18 +300,57 @@ class TestDir < Test::Unit::TestCase
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")
- assert_raise(ArgumentError) { Dir.home }
- assert_raise(ArgumentError) { Dir.home("") }
ENV["HOME"] = @nodir
assert_nothing_raised(ArgumentError) {
assert_equal(@nodir, Dir.home)
assert_equal(@nodir, Dir.home(""))
+ if user = ENV["USER"]
+ ENV["HOME"] = env_home
+ assert_equal(File.expand_path(env_home), Dir.home(user))
+ end
}
%W[no:such:user \u{7559 5b88}:\u{756a}].each do |user|
assert_raise_with_message(ArgumentError, /#{user}/) {Dir.home(user)}
@@ -254,7 +365,7 @@ class TestDir < Test::Unit::TestCase
Dir.chdir(dirname) do
begin
File.symlink('some-dir', 'dir-symlink')
- rescue NotImplementedError
+ rescue NotImplementedError, Errno::EACCES
return
end
@@ -266,4 +377,48 @@ class TestDir < Test::Unit::TestCase
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 a8fab3d216..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,7 +12,7 @@ class TestDir_M17N < Test::Unit::TestCase
}
end
- def create_and_check_raw_file_name(code, encoding)
+ 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}")
@@ -21,6 +22,7 @@ class TestDir_M17N < Test::Unit::TestCase
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
@@ -57,6 +59,9 @@ class TestDir_M17N < Test::Unit::TestCase
end
def test_filename_extutf8_invalid
+ 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_separately(%w[-EASCII-8BIT], <<-'EOS', :chdir=>d)
filename = "\xff".force_encoding("ASCII-8BIT") # invalid byte sequence as UTF-8
@@ -172,6 +177,7 @@ class TestDir_M17N < Test::Unit::TestCase
## others
def test_filename_bytes_euc_jp
+ return if /cygwin/ =~ RUBY_PLATFORM
with_tmpdir {|d|
assert_separately(%w[-EEUC-JP], <<-'EOS', :chdir=>d)
filename = "\xA4\xA2".force_encoding("euc-jp")
@@ -188,6 +194,7 @@ class TestDir_M17N < Test::Unit::TestCase
end
def test_filename_euc_jp
+ return if /cygwin/ =~ RUBY_PLATFORM
with_tmpdir {|d|
assert_separately(%w[-EEUC-JP], <<-'EOS', :chdir=>d)
filename = "\xA4\xA2".force_encoding("euc-jp")
@@ -209,7 +216,7 @@ class TestDir_M17N < Test::Unit::TestCase
when /darwin/
filename = filename.encode("utf-8", "euc-jp").b
when /mswin|mingw/
- if ents.include?(win_expected_filename.dup.force_encoding("ASCII-8BIT"))
+ if ents.include?(win_expected_filename.b)
ents = Dir.entries(".", {:encoding => Encoding.find("filesystem")})
filename = win_expected_filename
end
@@ -221,18 +228,19 @@ class TestDir_M17N < Test::Unit::TestCase
end
def test_filename_utf8_raw_jp_name
- create_and_check_raw_file_name(0x3042, "UTF-8")
+ assert_raw_file_name(0x3042, "UTF-8")
end
def test_filename_utf8_raw_windows_1251_name
- create_and_check_raw_file_name(0x0424, "UTF-8")
+ assert_raw_file_name(0x0424, "UTF-8")
end
def test_filename_utf8_raw_windows_1252_name
- create_and_check_raw_file_name(0x00c6, "UTF-8")
+ 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_separately(%w[-EEUC-JP], <<-'EOS', :chdir=>d)
filename = "\xA4\xA2".force_encoding("euc-jp")
@@ -304,28 +312,138 @@ class TestDir_M17N < Test::Unit::TestCase
}
end
- def test_entries_compose
- bug7267 = '[ruby-core:48745] [Bug #7267]'
+ 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
- pp = Object.new.extend(Test::Unit::Assertions)
- def pp.mu_pp(ary) #:nodoc:
- '[' << ary.map {|str| "#{str.dump}(#{str.encoding})"}.join(', ') << ']'
+ 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 => Encoding.default_external}
- orig.map! {|o| o.encode(Encoding.find("filesystem")) rescue o.tr("^a-z", "?")}
+ opts = {:encoding => enc}
+ orig.map! {|o| o.encode("filesystem") rescue o.tr("^a-z", "?")}
else
- enc = Encoding.find("filesystem")
- enc = Encoding::ASCII_8BIT if enc == Encoding::US_ASCII
orig.each {|o| o.force_encoding(enc) }
end
ents = Dir.entries(".", opts).reject {|n| /\A\./ =~ n}
ents.sort!
- pp.assert_equal(orig, ents, bug7267)
+ 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 b2425ee7ea..6f098db454 100644
--- a/test/ruby/test_econv.rb
+++ b/test/ruby/test_econv.rb
@@ -1,15 +1,11 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
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)
@@ -23,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
@@ -909,6 +905,9 @@ class TestEncodingConverter < Test::Unit::TestCase
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
diff --git a/test/ruby/test_encoding.rb b/test/ruby/test_encoding.rb
index bae8e763b3..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
@@ -110,12 +110,17 @@ class TestEncoding < Test::Unit::TestCase
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]'
- assert_separately(%w[--disable=gems], <<-"end;")
- assert_raise_with_message(SyntaxError, /unknown regexp option - Q/, #{bug9038.dump}) {
+ begin;
+ assert_raise_with_message(SyntaxError, /unknown regexp option - Q/, bug9038) {
eval("/regexp/sQ")
}
end;
diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb
index 3ee6846b33..2167271886 100644
--- a/test/ruby/test_enum.rb
+++ b/test/ruby/test_enum.rb
@@ -1,5 +1,6 @@
+# frozen_string_literal: false
require 'test/unit'
-require 'continuation'
+EnvUtil.suppress_warning {require 'continuation'}
require 'stringio'
class TestEnumerable < Test::Unit::TestCase
@@ -33,18 +34,16 @@ class TestEnumerable < Test::Unit::TestCase
$VERBOSE = @verbose
end
- def assert_not_warn
- begin
- org_stderr = $stderr
- v = $VERBOSE
- $stderr = StringIO.new(warn = '')
- $VERBOSE = true
- yield
- ensure
- $stderr = org_stderr
- $VERBOSE = v
- end
- assert_equal("", warn)
+ 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
@@ -55,7 +54,13 @@ class TestEnumerable < Test::Unit::TestCase
bug5801 = '[ruby-dev:45041]'
@empty.grep(//)
- assert_nothing_raised(bug5801) {100.times {@empty.block.call}}
+ 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
@@ -81,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
@@ -93,20 +100,54 @@ 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
+
def test_to_h
obj = Object.new
def obj.each(*args)
- yield *args
+ yield(*args)
yield [:key, :value]
yield :other_key, :other_value
kvp = Object.new
@@ -140,30 +181,126 @@ class TestEnumerable < Test::Unit::TestCase
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
@@ -171,6 +308,10 @@ 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_any
@@ -178,46 +319,81 @@ 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_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_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_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
@@ -229,24 +405,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 })
@@ -294,15 +481,65 @@ 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)
@@ -313,6 +550,8 @@ class TestEnumerable < Test::Unit::TestCase
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
@@ -321,10 +560,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}
- assert_nothing_raised(bug5801) {100.times {@empty.block.call}}
+ block = @empty.block
+ assert_nothing_raised(bug5801) {100.times {block.call}}
end
def test_drop
@@ -333,10 +575,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
@@ -371,6 +622,22 @@ 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
@@ -384,22 +651,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]],
@@ -417,6 +668,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
@@ -429,26 +686,347 @@ 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
- hs = [{}]
- e = [:foo].slice_before(hs[0]) {|elt, h|
- hs << h
+ 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
+
+ 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)
- assert_not_warn{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
end
diff --git a/test/ruby/test_enumerator.rb b/test/ruby/test_enumerator.rb
index 981d8bd434..66a45cc14e 100644
--- a/test/ruby/test_enumerator.rb
+++ b/test/ruby/test_enumerator.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestEnumerator < Test::Unit::TestCase
def setup
@@ -25,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
@@ -47,6 +47,14 @@ class TestEnumerator < Test::Unit::TestCase
}
end
+ 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
@@ -71,7 +79,7 @@ class TestEnumerator < Test::Unit::TestCase
enum = @obj.to_enum
assert_raise(NoMethodError) { enum.each {} }
enum.freeze
- assert_raise(RuntimeError) {
+ assert_raise(FrozenError) {
capture_io do
# warning: Enumerator.new without a block is deprecated; use Object#to_enum
enum.__send__(:initialize, @obj, :foo)
@@ -95,6 +103,7 @@ class TestEnumerator < Test::Unit::TestCase
1.times do
foo = [1,2,3].to_enum
GC.start
+ foo
end
GC.start
end
@@ -431,9 +440,21 @@ class TestEnumerator < Test::Unit::TestCase
assert_equal([1, 2, 3], a)
g.freeze
- assert_raise(RuntimeError) {
+ 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
@@ -555,13 +576,22 @@ class TestEnumerator < Test::Unit::TestCase
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
@@ -625,5 +655,11 @@ class TestEnumerator < Test::Unit::TestCase
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
end
diff --git a/test/ruby/test_env.rb b/test/ruby/test_env.rb
index ddbdcf24bc..ffed94efa6 100644
--- a/test/ruby/test_env.rb
+++ b/test/ruby/test_env.rb
@@ -1,9 +1,24 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
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
@@ -31,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
@@ -38,7 +54,7 @@ class TestEnv < Test::Unit::TestCase
end
assert_raise(TypeError) {
- tmp = ENV[1]
+ ENV[1]
}
assert_raise(TypeError) {
ENV[1] = 'foo'
@@ -88,15 +104,16 @@ 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
@@ -105,23 +122,28 @@ class TestEnv < Test::Unit::TestCase
assert_equal("foo", ENV.fetch("test"))
ENV.delete("test")
feature8649 = '[ruby-core:56062] [Feature #8649]'
- assert_raise_with_message(KeyError, 'key not found: "test"', feature8649) do
+ 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_nothing_raised { ENV["test"] = nil }
assert_equal(nil, ENV["test"])
- assert_raise(ArgumentError) { ENV["foo\0bar"] = "test" }
- assert_raise(ArgumentError) { ENV["test"] = "foo\0bar" }
+ assert_invalid_env {|v| ENV[v] = "test"}
+ assert_invalid_env {|v| ENV["test"] = v}
begin
# setenv(3) allowed the name includes '=',
@@ -173,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"
@@ -180,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
@@ -191,6 +219,10 @@ 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_keep_if
h1 = {}
ENV.each_pair {|k, v| h1[k] = v }
ENV["test"] = "foo"
@@ -198,6 +230,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
@@ -277,7 +311,7 @@ class TestEnv < Test::Unit::TestCase
assert_not_send([ENV, :has_key?, "test"])
ENV["test"] = "foo"
assert_send([ENV, :has_key?, "test"])
- assert_raise(ArgumentError) { ENV.has_key?("foo\0bar") }
+ assert_invalid_env {|v| ENV.has_key?(v)}
end
def test_assoc
@@ -291,7 +325,9 @@ 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
@@ -395,7 +431,7 @@ class TestEnv < Test::Unit::TestCase
if /mswin|mingw/ =~ RUBY_PLATFORM
def test_win32_blocksize
keys = []
- len = 32767 - ENV.to_a.flatten.inject(0) {|r,e| r + e.bytesize + 1}
+ 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
@@ -410,6 +446,38 @@ class TestEnv < Test::Unit::TestCase
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]'
@@ -450,86 +518,16 @@ class TestEnv < Test::Unit::TestCase
500.times(&doit)
end;
end
- end
-
- def test_taint_aref
- assert_raise(SecurityError) do
- proc do
- $SAFE = 2
- ENV["FOO".taint]
- end.call
- end
- end
-
- def test_taint_fetch
- assert_raise(SecurityError) do
- proc do
- $SAFE = 2
- ENV.fetch("FOO".taint)
- end.call
- end
- end
-
- def test_taint_assoc
- assert_raise(SecurityError) do
- proc do
- $SAFE = 2
- ENV.assoc("FOO".taint)
- end.call
- end
- end
-
- def test_taint_rassoc
- assert_raise(SecurityError) do
- proc do
- $SAFE = 2
- ENV.rassoc("FOO".taint)
- end.call
- end
- end
-
- def test_taint_key
- assert_raise(SecurityError) do
- proc do
- $SAFE = 2
- ENV.key("FOO".taint)
- end.call
- end
- end
-
- def test_taint_key_p
- assert_raise(SecurityError) do
- proc do
- $SAFE = 2
- ENV.key?("FOO".taint)
- end.call
- end
- end
- def test_taint_value_p
- assert_raise(SecurityError) do
- proc do
- $SAFE = 2
- ENV.value?("FOO".taint)
- end.call
- end
- end
-
- def test_taint_aset_value
- assert_raise(SecurityError) do
- proc do
- $SAFE = 2
- ENV["FOO"] = "BAR".taint
- end.call
- end
- end
-
- def test_taint_aset_key
- assert_raise(SecurityError) do
- proc do
- $SAFE = 2
- ENV["FOO".taint] = "BAR"
- end.call
+ 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 28d797e2c8..0bc0390d64 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,10 +127,14 @@ class TestEval < Test::Unit::TestCase
}
end
+ 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
+ obj.instance_variable_set :@ivar, 12 unless obj.frozen?
yield obj
end
end
@@ -145,7 +149,7 @@ class TestEval < Test::Unit::TestCase
assert_equal :sym, o.instance_eval(":sym")
assert_equal 11, o.instance_eval("11")
- assert_equal 12, o.instance_eval("@ivar")
+ 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")
@@ -155,7 +159,7 @@ class TestEval < Test::Unit::TestCase
assert_equal "19", o.instance_eval(%q("1#{9}"))
1.times {
- assert_equal 12, o.instance_eval("@ivar")
+ 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")
@@ -173,7 +177,7 @@ class TestEval < Test::Unit::TestCase
assert_equal :sym, o.instance_eval { :sym }
assert_equal 11, o.instance_eval { 11 }
- assert_equal 12, o.instance_eval { @ivar }
+ 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 }
@@ -183,7 +187,7 @@ class TestEval < Test::Unit::TestCase
assert_equal "19", o.instance_eval { "1#{9}" }
1.times {
- assert_equal 12, o.instance_eval { @ivar }
+ 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 }
@@ -191,6 +195,21 @@ class TestEval < Test::Unit::TestCase
end
end
+ 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
[Object.new, [], 7, :sym, true, false, nil].each do |obj|
assert_equal(13, obj.instance_eval("@@cvar"))
@@ -238,10 +257,10 @@ class TestEval < Test::Unit::TestCase
# 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
@@ -252,11 +271,9 @@ class TestEval < Test::Unit::TestCase
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)'
@@ -268,12 +285,12 @@ 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
+ x = make_test_binding
assert_equal("local1", eval("local1", x)) # normal local var
assert_equal("local2", eval("local2", x)) # nested local var
bad = true
@@ -289,6 +306,7 @@ class TestEval < Test::Unit::TestCase
module EvTest
EVTEST1 = 25
evtest2 = 125
+ evtest2 = evtest2
binding
end
)
@@ -335,7 +353,7 @@ 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"))
@@ -382,10 +400,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
@@ -485,6 +503,14 @@ class TestEval < Test::Unit::TestCase
assert_same a, b
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
@@ -499,4 +525,22 @@ class TestEval < Test::Unit::TestCase
b.eval('yield')
}, '[Bug #10368]'
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 c3314568c1..605248aebd 100644
--- a/test/ruby/test_exception.rb
+++ b/test/ruby/test_exception.rb
@@ -1,6 +1,6 @@
+# frozen_string_literal: false
require 'test/unit'
require 'tempfile'
-require_relative 'envutil'
class TestException < Test::Unit::TestCase
def test_exception_rescued
@@ -20,8 +20,8 @@ class TestException < Test::Unit::TestCase
if bad
bad = false
retry
- assert(false)
end
+ assert(!bad)
end
assert(true)
end
@@ -100,6 +100,23 @@ class TestException < Test::Unit::TestCase
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
@@ -112,18 +129,47 @@ class TestException < Test::Unit::TestCase
assert(!bad)
end
+ def test_catch_no_throw
+ assert_equal(:foo, catch {:foo})
+ end
+
def test_catch_throw
- assert(catch(:foo) {
- loop do
- loop do
- throw :foo, true
- break
- end
- break
- assert(false) # should no reach here
- end
- false
- })
+ 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
def test_catch_throw_in_require
@@ -131,10 +177,20 @@ class TestException < Test::Unit::TestCase
Tempfile.create(["dep", ".rb"]) {|t|
t.puts("throw :extdep, 42")
t.close
- assert_equal(42, catch(:extdep) {require t.path}, bug7185)
+ 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_no_exception
begin
assert(true)
@@ -243,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), [])
@@ -276,8 +352,9 @@ class TestException < Test::Unit::TestCase
end
def test_thread_signal_location
- _, stderr, _ = EnvUtil.invoke_ruby("--disable-gems -d", <<-RUBY, false, true)
+ _, 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
@@ -364,7 +441,7 @@ end.join
bug3237 = '[ruby-core:29948]'
str = "\u2600"
id = :"\u2604"
- msg = "undefined method `#{id}' for #{str.inspect}:String"
+ msg = "undefined method `#{id}' for \"#{str}\":String"
assert_raise_with_message(NoMethodError, msg, bug3237) do
str.__send__(id)
end
@@ -490,18 +567,25 @@ end.join
assert_equal(false, s.tainted?)
end
- def m;
- m &->{return 0};
- 42;
+ def m
+ m(&->{return 0})
+ 42
end
def test_stackoverflow
- assert_raise(SystemStackError){m}
+ 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([], <<-SRC)
+ assert_separately(%w[--disable-gem], <<-SRC)
assert_raise(SystemStackError, #{bug9109.dump}) {
h = {a: ->{h[:a].call}}
h[:a].call
@@ -512,7 +596,7 @@ end.join
def test_machine_stackoverflow_by_define_method
bug9454 = '[ruby-core:60113] [Bug #9454]'
- assert_separately([], <<-SRC)
+ assert_separately(%w[--disable-gem], <<-SRC)
assert_raise(SystemStackError, #{bug9454.dump}) {
define_method(:foo) {self.foo}
self.foo
@@ -521,6 +605,30 @@ end.join
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
@@ -540,7 +648,6 @@ end.join
def test_cause_reraised
msg = "[Feature #8257]"
- cause = nil
e = assert_raise(RuntimeError) {
begin
raise msg
@@ -550,4 +657,531 @@ end.join
}
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
+ assert_in_out_err([], <<-'end;', [], [/-: unexpected return\n/, /.*undefined local variable or method `n'.*\n/])
+ 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: "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_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_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])
+ 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_full_message
+ 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(status1.success?)
+ assert(err1.empty?, "expected nothing wrote to $stdout by #long_message")
+
+ _, err2, status1 = EnvUtil.invoke_ruby(['-e', "#{test_method}; begin; foo; end"], '', true, true)
+ assert_equal(err2, out1)
+
+ 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}
+
+ message = e.full_message(highlight: false, order: :top)
+ assert_not_match(/\e/, message)
+ assert_operator(message.count("\n"), :>, 2)
+ assert_operator(message, :start_with?, "test:100: testerror (RuntimeError)\n")
+ assert_operator(message, :end_with?, "test:1\n")
+
+ message = e.full_message(highlight: false, order: :bottom)
+ assert_not_match(/\e/, message)
+ assert_operator(message.count("\n"), :>, 2)
+ assert_operator(message, :start_with?, "Traceback (most recent call last):")
+ assert_operator(message, :end_with?, "test:100: testerror (RuntimeError)\n")
+
+ message = e.full_message(highlight: true)
+ assert_match(/\e/, message)
+ 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: 1)
+ end
end
diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb
index 3f620c69f8..0d070dcbc4 100644
--- a/test/ruby/test_fiber.rb
+++ b/test/ruby/test_fiber.rb
@@ -1,8 +1,8 @@
+# frozen_string_literal: false
require 'test/unit'
require 'fiber'
-require 'continuation'
+EnvUtil.suppress_warning {require 'continuation'}
require 'tmpdir'
-require_relative './envutil'
class TestFiber < Test::Unit::TestCase
def test_normal
@@ -70,10 +70,12 @@ class TestFiber < Test::Unit::TestCase
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
@@ -118,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
@@ -199,11 +201,11 @@ class TestFiber < Test::Unit::TestCase
end
def test_resume_root_fiber
- assert_raise(FiberError) do
- Thread.new do
+ Thread.new do
+ assert_raise(FiberError) do
Fiber.current.resume
- end.join
- end
+ end
+ end.join
end
def test_gc_root_fiber
@@ -217,13 +219,24 @@ class TestFiber < Test::Unit::TestCase
}, 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)
- assert_instance_of(Class, Fiber.new(&Class.new.method(:undef_method)).resume(:to_s))
+ 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_transfered_fiber
+ def test_prohibit_resume_transferred_fiber
assert_raise(FiberError){
root_fiber = Fiber.current
f = Fiber.new{
@@ -256,7 +269,11 @@ class TestFiber < Test::Unit::TestCase
end
bug5700 = '[ruby-core:41456]'
assert_nothing_raised(bug5700) do
- Fiber.new{ pid = fork {} }.resume
+ Fiber.new do
+ pid = fork do
+ Fiber.new {}.transfer
+ end
+ end.resume
end
pid, status = Process.waitpid2(pid)
assert_equal(0, status.exitstatus, bug5700)
@@ -283,8 +300,8 @@ class TestFiber < Test::Unit::TestCase
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, err = Dir.mktmpdir("test_fiber") {|tmpdir|
- EnvUtil.invoke_ruby([env, '-e', script], '', true, true, chdir: tmpdir)
+ out, _ = Dir.mktmpdir("test_fiber") {|tmpdir|
+ EnvUtil.invoke_ruby([env, '-e', script], '', true, true, chdir: tmpdir, timeout: 30)
}
use_length ? out.length : out
end
@@ -344,5 +361,35 @@ class TestFiber < Test::Unit::TestCase
assert_equal("inner", s2)
assert_equal(s1, $_, bug7678)
end
-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_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 01992a83d4..10bfbd9ae0 100644
--- a/test/ruby/test_file.rb
+++ b/test/ruby/test_file.rb
@@ -1,7 +1,7 @@
+# frozen_string_literal: false
require 'test/unit'
require 'tempfile'
-require "thread"
-require_relative 'envutil'
+require "-test-/file"
require_relative 'ut_eof'
class TestFile < Test::Unit::TestCase
@@ -87,7 +87,7 @@ class TestFile < Test::Unit::TestCase
end
def test_bom_32le
- assert_bom(["\xFF\xFE\0", "\0"], __method__)
+ assert_bom(["\xFF", "\xFE\0\0"], __method__)
end
def test_truncate_wbuf
@@ -121,10 +121,10 @@ class TestFile < Test::Unit::TestCase
def test_truncate_size
Tempfile.create("test-truncate") do |f|
- q1 = Queue.new
- q2 = Queue.new
+ q1 = Thread::Queue.new
+ q2 = Thread::Queue.new
- Thread.new do
+ th = Thread.new do
data = ''
64.times do |i|
data << i.to_s
@@ -142,6 +142,7 @@ class TestFile < Test::Unit::TestCase
assert_equal size, f.size
q2.push true
end
+ th.join
end
end
@@ -258,6 +259,58 @@ 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)
@@ -276,6 +329,16 @@ class TestFile < Test::Unit::TestCase
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], [])
@@ -311,6 +374,42 @@ class TestFile < Test::Unit::TestCase
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|
@@ -344,6 +443,19 @@ class TestFile < Test::Unit::TestCase
}
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')
@@ -383,4 +495,24 @@ class TestFile < Test::Unit::TestCase
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::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 c0f92ec820..817c3580d1 100644
--- a/test/ruby/test_file_exhaustive.rb
+++ b/test/ruby/test_file_exhaustive.rb
@@ -1,10 +1,14 @@
+# frozen_string_literal: false
require "test/unit"
require "fileutils"
require "tmpdir"
-require_relative "envutil"
+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")
@@ -15,51 +19,172 @@ class TestFileExhaustive < Test::Unit::TestCase
def setup
@dir = Dir.mktmpdir("rubytest-file")
- @rootdir = "#{DRIVE}/"
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)
@@ -67,7 +192,7 @@ class TestFileExhaustive < Test::Unit::TestCase
end
def assert_integer_or_nil(n)
- msg = ->{"#{n.inspect} is neither Fixnum nor nil."}
+ msg = ->{"#{n.inspect} is neither Integer nor nil."}
if n
assert_kind_of(Integer, n, msg)
else
@@ -76,9 +201,12 @@ class TestFileExhaustive < Test::Unit::TestCase
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)
@@ -95,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)
@@ -107,10 +235,10 @@ 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
@@ -133,232 +261,371 @@ class TestFileExhaustive < Test::Unit::TestCase
assert_nothing_raised { File.stat(File.basename(prefix)) }
end
end
- end if /mswin|mingw|cygwin/ =~ RUBY_PLATFORM
+ 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.not_directory?(@dir+"/...")
- assert_file.not_directory?(@file)
- assert_file.not_directory?(@nofile)
+ assert_file.not_directory?(regular_file)
+ assert_file.not_directory?(utf8_file)
+ assert_file.not_directory?(nofile)
end
- def test_pipe_p ## xxx
+ def test_pipe_p
assert_file.not_pipe?(@dir)
- assert_file.not_pipe?(@file)
- assert_file.not_pipe?(@nofile)
+ 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.not_symlink?(@dir)
- assert_file.not_symlink?(@file)
- assert_file.symlink?(@symlinkfile) if @symlinkfile
- assert_file.not_symlink?(@hardlinkfile) if @hardlinkfile
- assert_file.not_symlink?(@nofile)
+ 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
+ def test_socket_p
assert_file.not_socket?(@dir)
- assert_file.not_socket?(@file)
- assert_file.not_socket?(@nofile)
+ 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
+ def test_blockdev_p
assert_file.not_blockdev?(@dir)
- assert_file.not_blockdev?(@file)
- assert_file.not_blockdev?(@nofile)
+ 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
+ def test_chardev_p
assert_file.not_chardev?(@dir)
- assert_file.not_chardev?(@file)
- assert_file.not_chardev?(@nofile)
+ 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.not_exist?(@nofile)
+ 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.not_readable?(@file)
- File.chmod(0600, @file)
- assert_file.readable?(@file)
- assert_file.not_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.not_readable_real?(@file)
- File.chmod(0600, @file)
- assert_file.readable_real?(@file)
- assert_file.not_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.not_world_readable?(@file)
- File.chmod(0600, @file)
- assert_file.not_world_readable?(@file)
- assert_file.not_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.not_writable?(@file)
- File.chmod(0600, @file)
- assert_file.writable?(@file)
- assert_file.not_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.not_writable_real?(@file)
- File.chmod(0600, @file)
- assert_file.writable_real?(@file)
- assert_file.not_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.not_world_writable?(@file)
- File.chmod(0600, @file)
- assert_file.not_world_writable?(@file)
- assert_file.not_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.not_executable?(@file)
- assert_file.not_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.not_executable_real?(@file)
- assert_file.not_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.not_file?(@dir)
- assert_file.file?(@file)
- assert_file.not_file?(@nofile)
+ 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.not_zero?(@file)
- assert_file.zero?(@zerofile)
- assert_file.not_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.not_size?(@zerofile)
- assert_file.not_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 ## xxx
- return if /cygwin|mswin|bccwin|mingw|emx/ =~ RUBY_PLATFORM
- assert_file.owned?(@file)
- assert_file.grpowned?(@file)
+ 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 test_suid
+ assert_file.not_setuid?(regular_file)
+ assert_file.not_setuid?(utf8_file)
+ assert_file.setuid?(suidfile) if suidfile
+ end
+
+ def test_sgid
+ assert_file.not_setgid?(regular_file)
+ assert_file.not_setgid?(utf8_file)
+ assert_file.setgid?(sgidfile) if sgidfile
+ end
+
+ def test_sticky
+ assert_file.not_sticky?(regular_file)
+ assert_file.not_sticky?(utf8_file)
+ assert_file.sticky?(stickyfile) if stickyfile
end
- def test_suid_sgid_sticky ## xxx
- assert_file.not_setuid?(@file)
- assert_file.not_setgid?(@file)
- assert_file.not_sticky?(@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_identical_p
- assert_file.identical?(@file, @file)
- assert_file.not_identical?(@file, @zerofile)
- assert_file.not_identical?(@file, @nofile)
- assert_file.not_identical?(@nofile, @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_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
@@ -367,39 +634,71 @@ 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_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_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(@file, @file) }
+ 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(@file, File.readlink(@symlinkfile))
- assert_raise(Errno::EINVAL) { File.readlink(@file) }
- assert_raise(Errno::ENOENT) { File.readlink(@nofile) }
+ 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)
+ assert_equal(fs, File.readlink(symlinkfile).encoding)
end
rescue NotImplementedError
end
def test_readlink_long_path
- return unless @symlinkfile
+ return unless symlinkfile
bug9157 = '[ruby-core:58592] [Bug #9157]'
- assert_separately(["-", @symlinkfile, bug9157], <<-"end;")
+ assert_separately(["-", symlinkfile, bug9157], "#{<<~begin}#{<<~"end;"}")
+ begin
symlinkfile, bug9157 = *ARGV
100.step(1000, 100) do |n|
File.unlink(symlinkfile)
@@ -414,42 +713,85 @@ class TestFileExhaustive < Test::Unit::TestCase
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.not_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)))
+ if 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
+ case RUBY_PLATFORM
+ when /darwin/
+ ["\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
if DRIVE
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"))
@@ -463,9 +805,9 @@ class TestFileExhaustive < Test::Unit::TestCase
bug9934 = '[ruby-core:63114] [Bug #9934]'
require "objspace"
path = File.expand_path("/foo")
- assert_operator(ObjectSpace.memsize_of(path), :<=, path.bytesize, bug9934)
+ 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, ObjectSpace.memsize_of(path), bug9934)
+ assert_equal(path.bytesize+1 + GC::INTERNAL_CONSTANTS[:RVALUE_SIZE], ObjectSpace.memsize_of(path), bug9934)
end
def test_expand_path_encoding
@@ -479,6 +821,8 @@ class TestFileExhaustive < Test::Unit::TestCase
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|
@@ -524,7 +868,6 @@ class TestFileExhaustive < Test::Unit::TestCase
ENV["HOMEDRIVE"] = nil
ENV["HOMEPATH"] = nil
ENV["USERPROFILE"] = nil
- assert_raise(ArgumentError) { File.expand_path("~") }
ENV["HOME"] = "~"
assert_raise(ArgumentError, bug3630) { File.expand_path("~") }
ENV["HOME"] = "."
@@ -548,6 +891,8 @@ class TestFileExhaustive < Test::Unit::TestCase
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
@@ -580,9 +925,9 @@ class TestFileExhaustive < Test::Unit::TestCase
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")
+ 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
@@ -626,20 +971,20 @@ class TestFileExhaustive < Test::Unit::TestCase
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}"))
+ 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:/"))
+ 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/"))
+ 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 "))
+ 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
@@ -791,27 +1136,64 @@ class TestFileExhaustive < Test::Unit::TestCase
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", ".*"))
+ if NTFS
+ [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
+ else
+ [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
if File::ALT_SEPARATOR == '\\'
a = "foo/\225\\\\"
@@ -832,7 +1214,8 @@ class TestFileExhaustive < Test::Unit::TestCase
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(""))
assert_incompatible_encoding {|d| File.dirname(d)}
if File::ALT_SEPARATOR == '\\'
@@ -844,12 +1227,13 @@ class TestFileExhaustive < Test::Unit::TestCase
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|
@@ -871,9 +1255,11 @@ 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
@@ -915,43 +1301,118 @@ class TestFileExhaustive < Test::Unit::TestCase
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))
@@ -978,28 +1439,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)
@@ -1016,7 +1480,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)
@@ -1028,159 +1492,177 @@ 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
@@ -1195,10 +1677,12 @@ class TestFileExhaustive < Test::Unit::TestCase
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 c86cba3b7c..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
@@ -101,6 +109,7 @@ class TestFixnum < Test::Unit::TestCase
assert_equal(r, a.modulo(b))
}
}
+ assert_raise(FloatDomainError) { 2.divmod(Float::NAN) }
end
def test_not
@@ -205,6 +214,7 @@ class TestFixnum < Test::Unit::TestCase
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)
@@ -296,7 +306,7 @@ class TestFixnum < Test::Unit::TestCase
big = 1 << 66
assert_eql 1, 1 ** -big , bug5715
assert_eql 1, (-1) ** -big , bug5715
- assert_eql -1, (-1) ** -(big+1) , bug5715
+ assert_eql (-1), (-1) ** -(big+1), bug5715
end
def test_power_of_0
@@ -305,4 +315,38 @@ class TestFixnum < Test::Unit::TestCase
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
index fc62d93ae6..b8b05aed6d 100644
--- a/test/ruby/test_flip.rb
+++ b/test/ruby/test_flip.rb
@@ -1,7 +1,17 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestFlip < Test::Unit::TestCase
+ def test_flip_flop
+ 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
+
def test_hidden_key
bug6899 = '[ruby-core:47253]'
foo = "foor"
@@ -9,6 +19,7 @@ class TestFlip < Test::Unit::TestCase
assert_nothing_raised(NotImplementedError, bug6899) do
2000.times {eval %[(foo..bar) ? 1 : 2]}
end
+ foo = bar
end
def test_shared_eval
@@ -39,4 +50,25 @@ class TestFlip < Test::Unit::TestCase
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 a959f60a69..7fabfd351d 100644
--- a/test/ruby/test_float.rb
+++ b/test/ruby/test_float.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestFloat < Test::Unit::TestCase
include EnvUtil
@@ -16,6 +16,8 @@ class TestFloat < Test::Unit::TestCase
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)
@@ -161,6 +163,14 @@ class TestFloat < Test::Unit::TestCase
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
end
def test_divmod
@@ -168,6 +178,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
@@ -175,6 +187,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
@@ -189,6 +204,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
@@ -215,6 +232,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
@@ -222,6 +241,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
@@ -229,6 +250,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
@@ -236,6 +258,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
@@ -248,14 +271,20 @@ class TestFloat < Test::Unit::TestCase
def test_modulo3
bug6048 = '[ruby-core:42726]'
- assert_equal(4.2, 4.2.send(:%, Float::INFINITY))
- assert_equal(4.2, 4.2 % Float::INFINITY)
+ 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) { 4.2.send(:%, 0.0) }
- assert_raise(ZeroDivisionError) { 4.2 % 0.0 }
- assert_raise(ZeroDivisionError) { 42.send(:%, 0) }
- assert_raise(ZeroDivisionError) { 42 % 0 }
+ 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
@@ -338,6 +367,30 @@ class TestFloat < Test::Unit::TestCase
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
inf = Float::INFINITY
assert_equal(1, inf.infinite?)
@@ -385,6 +438,11 @@ class TestFloat < Test::Unit::TestCase
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))
@@ -399,6 +457,93 @@ class TestFloat < Test::Unit::TestCase
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 = [
@@ -528,9 +673,108 @@ class TestFloat < Test::Unit::TestCase
}
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_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") }
@@ -558,7 +802,7 @@ class TestFloat < Test::Unit::TestCase
end
def test_num2dbl
- assert_raise(ArgumentError) do
+ assert_raise(ArgumentError, "comparison of String with 0 failed") do
1.0.step(2.0, "0.5") {}
end
assert_raise(TypeError) do
@@ -620,6 +864,41 @@ class TestFloat < Test::Unit::TestCase
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)
@@ -627,4 +906,21 @@ class TestFloat < Test::Unit::TestCase
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 15e5d79e35..30250b5a19 100644
--- a/test/ruby/test_fnmatch.rb
+++ b/test/ruby/test_fnmatch.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestFnmatch < Test::Unit::TestCase
@@ -129,4 +129,10 @@ class TestFnmatch < Test::Unit::TestCase
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 402d560bff..b3528ddacd 100644
--- a/test/ruby/test_gc.rb
+++ b/test/ruby/test_gc.rb
@@ -1,7 +1,6 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative "envutil"
-
class TestGc < Test::Unit::TestCase
class S
def initialize(a)
@@ -14,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 {
@@ -35,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)
@@ -49,6 +52,8 @@ class TestGc < Test::Unit::TestCase
end
def test_start_full_mark
+ return unless use_rgengc?
+
GC.start(full_mark: false)
assert_nil GC.latest_gc_info(:major_by)
@@ -85,14 +90,18 @@ class TestGc < Test::Unit::TestCase
GC.start
GC.stat(stat)
ObjectSpace.count_objects(count)
- assert_equal(count[:TOTAL]-count[:FREE], stat[:heap_live_slot])
- assert_equal(count[:FREE], stat[:heap_free_slot])
+ 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_slot])
+ 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
@@ -101,18 +110,34 @@ class TestGc < Test::Unit::TestCase
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
- GC.stat[:heap_free_slot].times{ "a" + "b" }
+ 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 :nofree, GC.latest_gc_info[:major_by]
+ 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 :stress, GC.latest_gc_info[:major_by]
+ assert_equal :force, GC.latest_gc_info[:major_by]
ensure
GC.stress = false
end
@@ -123,7 +148,8 @@ class TestGc < Test::Unit::TestCase
assert_not_empty info
assert_equal info[:gc_by], GC.latest_gc_info(:gc_by)
- assert_raises(ArgumentError){ GC.latest_gc_info(:invalid) }
+ 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
@@ -184,8 +210,9 @@ class TestGc < Test::Unit::TestCase
}
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'], //, "")
+ 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'], '', [],
@@ -203,15 +230,17 @@ class TestGc < Test::Unit::TestCase
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/, "")
- 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/, "")
+ 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
@@ -224,7 +253,7 @@ class TestGc < Test::Unit::TestCase
end
def test_profiler_clear
- assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom'
+ assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom', timeout: 30
GC::Profiler.enable
GC.start
@@ -258,28 +287,31 @@ class TestGc < Test::Unit::TestCase
def test_expand_heap
assert_separately %w[--disable-gem], __FILE__, __LINE__, <<-'eom'
GC.start
- base_length = GC.stat[:heap_eden_page_length]
+ 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_delta base_length, (v = GC.stat[:heap_eden_page_length]), 1,
- "invalid heap expanding (base_length: #{base_length}, GC.stat[:heap_eden_page_length]: #{v})"
+ 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_page_length] + 1
+ assert_operator base_length, :<, GC.stat[:heap_eden_pages] + 1
eom
end
def test_gc_internals
- assert_not_nil GC::INTERNAL_CONSTANTS[:HEAP_OBJ_LIMIT]
+ 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: 30)
+ assert_ruby_status([], <<-'end;', bug9205, timeout: 60)
raise_proc = proc do |id|
GC.start
end
@@ -292,7 +324,7 @@ class TestGc < Test::Unit::TestCase
def test_exception_in_finalizer
bug9168 = '[ruby-core:58652] [Bug #9168]'
- assert_normal_exit(<<-'end;', bug9168)
+ assert_normal_exit(<<-'end;', bug9168, encoding: Encoding::ASCII_8BIT)
raise_proc = proc {raise}
10000.times do
ObjectSpace.define_finalizer(Object.new, raise_proc)
@@ -303,7 +335,120 @@ class TestGc < Test::Unit::TestCase
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_stress_at_startup
+ skip # it'll be fixed later
+ assert_in_out_err([{"RUBY_DEBUG"=>"gc_stress"}], '', [], [], '[Bug #15784]', success: true)
+ 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 69783981d5..bdcf022668 100644
--- a/test/ruby/test_hash.rb
+++ b/test/ruby/test_hash.rb
@@ -1,7 +1,7 @@
# -*- coding: us-ascii -*-
+# frozen_string_literal: false
require 'test/unit'
-require 'continuation'
-require_relative 'envutil'
+EnvUtil.suppress_warning {require 'continuation'}
class TestHash < Test::Unit::TestCase
@@ -133,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
@@ -145,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 # '[]'
@@ -215,6 +266,33 @@ 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_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}
@@ -222,6 +300,17 @@ class TestHash < Test::Unit::TestCase
assert_same "ABC".freeze, a.keys[0]
end
+ def test_tainted_string_key
+ str = 'str'.taint
+ h = {}
+ h[str] = nil
+ key = h.keys.first
+ assert_equal true, str.tainted?
+ assert_equal false, str.frozen?
+ assert_equal true, key.tainted?
+ assert_equal true, key.frozen?
+ end
+
def test_EQUAL # '=='
h1 = @cls[ "a" => 1, "c" => 2 ]
h2 = @cls[ "a" => 1, "c" => 2, 7 => 35 ]
@@ -332,6 +421,15 @@ class TestHash < Test::Unit::TestCase
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 frozen in [ false, true ]
@@ -433,6 +531,8 @@ class TestHash < Test::Unit::TestCase
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?
@@ -485,6 +585,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
@@ -564,19 +681,6 @@ class TestHash < Test::Unit::TestCase
assert_equal(h3, h.reject {|k,v| v })
assert_equal(base, h)
- unless RUBY_VERSION >= "2.2.0"
- # [ruby-core:59154] [Bug #9223]
- if @cls == Hash
- assert_empty(EnvUtil.verbose_warning {h.reject {false}})
- bug9275 = '[ruby-core:59254] [Bug #9275]'
- c = Class.new(Hash)
- assert_empty(EnvUtil.verbose_warning {c.new.reject {false}}, bug9275)
- else
- assert_match(/extra states/, EnvUtil.verbose_warning {h.reject {false}})
- end
- return
- end
-
h.instance_variable_set(:@foo, :foo)
h.default = 42
h.taint
@@ -718,6 +822,28 @@ class TestHash < Test::Unit::TestCase
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_nil_to_h
h = nil.to_h
assert_equal({}, h)
@@ -771,7 +897,7 @@ class TestHash < Test::Unit::TestCase
assert_equal([], expected - vals)
end
- def test_intialize_wrong_arguments
+ def test_initialize_wrong_arguments
assert_raise(ArgumentError) do
Hash.new(0) { }
end
@@ -780,7 +906,7 @@ class TestHash < Test::Unit::TestCase
def test_create
assert_equal({1=>2, 3=>4}, @cls[[[1,2],[3,4]]])
assert_raise(ArgumentError) { Hash[0, 1, 2] }
- assert_warning(/wrong element type Fixnum at 1 /) {@cls[[[1, 2], 3]]}
+ 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])
@@ -805,7 +931,7 @@ class TestHash < Test::Unit::TestCase
assert_equal("foobarbaz", h.default_proc.call("foo", "bar"))
assert_nil(h.default_proc = nil)
assert_nil(h.default_proc)
- h.default_proc = ->(h, k){ true }
+ h.default_proc = ->(_,_){ true }
assert_equal(true, h[:nope])
h = @cls[]
assert_nil(h.default_proc)
@@ -880,6 +1006,14 @@ class TestHash < Test::Unit::TestCase
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_clear2
assert_equal({}, @cls[1=>2,3=>4,5=>6].clear)
h = @cls[1=>2,3=>4,5=>6]
@@ -897,8 +1031,8 @@ 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
@@ -965,7 +1099,7 @@ class TestHash < Test::Unit::TestCase
h = @cls[]
h.compare_by_identity
h["a"] = 1
- h["a"] = 2
+ h["a".dup] = 2
assert_equal(["a",1], h.assoc("a"))
end
@@ -984,7 +1118,12 @@ class TestHash < Test::Unit::TestCase
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(Object) }
+ 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
@@ -1030,7 +1169,7 @@ class TestHash < Test::Unit::TestCase
assert_nothing_raised(RuntimeError, bug9105) do
h=@cls[]
cnt=0
- c = callcc {|c|c}
+ c = callcc {|cc|cc}
h[cnt] = true
h.each{|i|
cnt+=1
@@ -1128,7 +1267,7 @@ class TestHash < Test::Unit::TestCase
assert_equal({o=>1}.hash, @cls[o=>1].hash)
end
- def test_hash_poped
+ def test_hash_popped
assert_nothing_raised { eval("a = 1; @cls[a => a]; a") }
end
@@ -1217,7 +1356,9 @@ class TestHash < Test::Unit::TestCase
end
end
- def test_exception_in_rehash
+ def test_exception_in_rehash_memory_leak
+ return unless @cls == Hash
+
bug9187 = '[ruby-core:58728] [Bug #9187]'
prepare = <<-EOS
@@ -1232,10 +1373,10 @@ class TestHash < Test::Unit::TestCase
return 0
end
end
+ h = {Foo.new => true}
EOS
code = <<-EOS
- h = {Foo.new => true}
10_0000.times do
h.rehash rescue nil
end
@@ -1245,7 +1386,7 @@ class TestHash < Test::Unit::TestCase
assert_no_memory_leak([], prepare, code, bug9187)
end
- def test_wrapper_of_special_const
+ def test_wrapper
bug9381 = '[ruby-core:59638] [Bug #9381]'
wrapper = Class.new do
@@ -1265,6 +1406,8 @@ class TestHash < Test::Unit::TestCase
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
@@ -1272,6 +1415,202 @@ class TestHash < Test::Unit::TestCase
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
+
class TestSubHash < TestHash
class SubHash < Hash
def reject(*)
diff --git a/test/ruby/test_ifunless.rb b/test/ruby/test_ifunless.rb
index e144ff8efd..d533e155bc 100644
--- a/test/ruby/test_ifunless.rb
+++ b/test/ruby/test_ifunless.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestIfunless < Test::Unit::TestCase
diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb
index 3dbf365a7c..1b485760e3 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
@@ -97,6 +98,22 @@ class TestInteger < Test::Unit::TestCase
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"))}
+
+ 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_int_p
@@ -104,42 +121,8 @@ class TestInteger < Test::Unit::TestCase
assert_predicate(1, :integer?)
end
- def test_odd_p_even_p
- Fixnum.class_eval do
- alias odd_bak odd?
- alias even_bak even?
- remove_method :odd?, :even?
- end
-
- assert_predicate(1, :odd?)
- assert_not_predicate(2, :odd?)
- assert_not_predicate(1, :even?)
- assert_predicate(2, :even?)
-
- ensure
- Fixnum.class_eval do
- alias odd? odd_bak
- alias even? even_bak
- remove_method :odd_bak, :even_bak
- end
- end
-
def test_succ
assert_equal(2, 1.send(:succ))
-
- Fixnum.class_eval do
- alias succ_bak succ
- remove_method :succ
- end
-
- assert_equal(2, 1.succ)
- assert_equal(4294967297, 4294967296.succ)
-
- ensure
- Fixnum.class_eval do
- alias succ succ_bak
- remove_method :succ_bak
- end
end
def test_chr
@@ -184,61 +167,238 @@ 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_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
- 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)
+ 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
- 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)
+ 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
- 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)
+ 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
- def test_bitwise_and_with_integer_mimic_object
- def (obj = Object.new).to_int
- 10
+ MimicInteger = Struct.new(:to_int)
+ module CoercionToInt
+ def coerce(other)
+ [other, to_int]
end
- assert_raise(TypeError, '[ruby-core:39491]') { 3 & obj }
+ end
- def obj.coerce(other)
- [other, 10]
- 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
- def (obj = Object.new).to_int
- 10
- end
+ obj = MimicInteger.new(10)
assert_raise(TypeError, '[ruby-core:39491]') { 3 | obj }
-
- def obj.coerce(other)
- [other, 10]
- end
+ obj.extend(CoercionToInt)
assert_equal(3 | 10, 3 | obj)
end
def test_bitwise_xor_with_integer_mimic_object
- def (obj = Object.new).to_int
- 10
- end
+ obj = MimicInteger.new(10)
assert_raise(TypeError, '[ruby-core:39491]') { 3 ^ obj }
+ obj.extend(CoercionToInt)
+ assert_equal(3 ^ 10, 3 ^ obj)
+ end
- def obj.coerce(other)
- [other, 10]
+ 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
@@ -277,4 +437,92 @@ class TestInteger < Test::Unit::TestCase
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)
+ 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 e00e7588fd..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
@@ -109,28 +110,6 @@ class TestIntegerComb < Test::Unit::TestCase
#VS.concat VS.find_all {|v| Fixnum === v }.map {|v| 0x4000000000000000.coerce(v)[0] }
#VS.sort! {|a, b| a.abs <=> b.abs }
- 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
-
def test_aref
VS.each {|a|
100.times {|i|
@@ -141,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
@@ -155,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
@@ -170,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
@@ -185,7 +164,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(b.send(:*, a), c, "#{a} * #{b}")
assert_equal(b, c / a, "(#{a} * #{b}) / #{a}") if a != 0
@@ -203,8 +182,8 @@ 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_operator(r.abs, :<, b.abs)
if 0 < b
@@ -228,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}")
@@ -244,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}")
@@ -256,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
@@ -269,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
@@ -282,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}")
@@ -295,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}]")
}
}
@@ -312,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}]")
}
}
@@ -327,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")
@@ -337,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")
@@ -347,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
@@ -355,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}))")
@@ -387,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
@@ -399,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
@@ -407,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
@@ -415,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
@@ -423,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
@@ -435,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})")
@@ -460,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?")
}
@@ -478,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 03a7faa2dc..178e3c3d7e 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -1,14 +1,15 @@
# 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 'weakref'
-require_relative 'envutil'
class TestIO < Test::Unit::TestCase
module Feature
@@ -145,7 +146,19 @@ class TestIO < Test::Unit::TestCase
end
def test_gets_rs
- # default_rs
+ 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
@@ -155,8 +168,9 @@ class TestIO < Test::Unit::TestCase
assert_nil r.gets
r.close
end)
+ end
- # nil
+ def test_gets_rs_nil
pipe(proc do |w|
w.print "a\n\nb\n\n"
w.close
@@ -165,8 +179,9 @@ class TestIO < Test::Unit::TestCase
assert_nil r.gets("")
r.close
end)
+ end
- # "\377"
+ def test_gets_rs_377
pipe(proc do |w|
w.print "\377xyz"
w.close
@@ -175,8 +190,9 @@ class TestIO < Test::Unit::TestCase
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
@@ -188,6 +204,68 @@ class TestIO < Test::Unit::TestCase
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
pipe(proc do |w|
w << "0123456789\n0123456789"
@@ -301,6 +379,16 @@ class TestIO < Test::Unit::TestCase
}
end
+ 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_smaller
with_srccontent {|src, content|
@@ -467,6 +555,19 @@ class TestIO < Test::Unit::TestCase
end
if have_nonblock?
+ def test_copy_stream_no_busy_wait
+ 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
+
def test_copy_stream_pipe_nonblock
mkcdtmpdir {
with_read_pipe("abc") {|r1|
@@ -874,6 +975,32 @@ 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 {
@@ -1063,6 +1190,18 @@ class TestIO < Test::Unit::TestCase
}
end
+ def test_copy_stream_to_duplex_io
+ result = IO.pipe {|a,w|
+ Thread.start {w.puts "yes"; w.close}
+ IO.popen([EnvUtil.rubybin, '-pe$_="#$.:#$_"'], "r+") {|b|
+ IO.copy_stream(a, b)
+ b.close_write
+ b.read
+ }
+ }
+ assert_equal("1:yes\n", result)
+ end
+
def ruby(*args)
args = ['-e', '$>.write($<.read)'] if args.empty?
ruby = EnvUtil.rubybin
@@ -1100,6 +1239,63 @@ 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_non_writable
with_pipe do |r, w|
assert_raise(IOError) do
@@ -1110,40 +1306,32 @@ 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|
- if defined?(Process::RLIMIT_NOFILE)
- lim = Process.getrlimit(Process::RLIMIT_NOFILE)[0]
- Process.setrlimit(Process::RLIMIT_NOFILE, [lim, 1024].min)
- end
- 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
@@ -1208,7 +1396,7 @@ class TestIO < Test::Unit::TestCase
t.value
assert_equal("", s)
end
- end
+ end if /cygwin/ !~ RUBY_PLATFORM
def test_read
pipe(proc do |w|
@@ -1261,10 +1449,9 @@ class TestIO < Test::Unit::TestCase
t.value
assert_equal("xxx", s)
end
- 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
@@ -1274,7 +1461,6 @@ class TestIO < Test::Unit::TestCase
end
def test_read_nonblock_with_not_empty_buffer
- skip "IO#read_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
with_pipe {|r, w|
w.write "foob"
w.close
@@ -1284,7 +1470,6 @@ class TestIO < Test::Unit::TestCase
end
def test_write_nonblock_simple_no_exceptions
- skip "IO#write_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
pipe(proc do |w|
w.write_nonblock('1', exception: false)
w.close
@@ -1294,8 +1479,6 @@ class TestIO < Test::Unit::TestCase
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
@@ -1311,11 +1494,9 @@ class TestIO < Test::Unit::TestCase
assert_kind_of(IO::WaitReadable, $!)
end
}
- end
+ end if have_nonblock?
def test_read_nonblock_no_exceptions
- return if !have_nonblock?
- skip "IO#read_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
with_pipe {|r, w|
assert_equal :wait_readable, r.read_nonblock(4096, exception: false)
w.puts "HI!"
@@ -1323,11 +1504,9 @@ class TestIO < Test::Unit::TestCase
w.close
assert_equal nil, r.read_nonblock(4096, exception: false)
}
- end
+ end if have_nonblock?
def test_read_nonblock_with_buffer_no_exceptions
- return if !have_nonblock?
- skip "IO#read_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
with_pipe {|r, w|
assert_equal :wait_readable, r.read_nonblock(4096, "", exception: false)
w.puts "HI!"
@@ -1338,11 +1517,9 @@ class TestIO < Test::Unit::TestCase
w.close
assert_equal nil, r.read_nonblock(4096, "", exception: false)
}
- end
+ 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 {
@@ -1352,11 +1529,9 @@ class TestIO < Test::Unit::TestCase
assert_kind_of(IO::WaitWritable, $!)
end
}
- end
+ end if have_nonblock?
def test_write_nonblock_no_exceptions
- return if !have_nonblock?
- skip "IO#write_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
with_pipe {|r, w|
loop {
ret = w.write_nonblock("a"*100000, exception: false)
@@ -1366,7 +1541,7 @@ class TestIO < Test::Unit::TestCase
end
}
}
- end
+ end if have_nonblock?
def test_gets
pipe(proc do |w|
@@ -1383,6 +1558,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
@@ -1390,9 +1568,23 @@ 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_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
with_pipe do |r, w|
assert_raise(IOError) do
@@ -1406,6 +1598,9 @@ 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
@@ -1431,18 +1626,22 @@ class TestIO < Test::Unit::TestCase
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
@@ -1477,23 +1676,21 @@ class TestIO < Test::Unit::TestCase
def test_set_lineno
make_tempfile {|t|
- ruby("-e", <<-SRC, t.path) do |f|
+ assert_separately(["-", t.path], <<-SRC)
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 $.
+ 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
- assert_equal("0,1,2,2,1001,1001,1001,1,2,3,3", f.read.chomp.gsub("\n", ","))
- end
pipe(proc do |w|
w.puts "foo"
@@ -1633,7 +1830,6 @@ 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
@@ -1661,7 +1857,7 @@ class TestIO < Test::Unit::TestCase
w.close_on_exec = false
assert_equal(false, w.close_on_exec?)
end
- end
+ end if have_close_on_exec?
def test_pos
make_tempfile {|t|
@@ -1680,7 +1876,7 @@ class TestIO < Test::Unit::TestCase
end
def test_pos_with_getc
- bug6179 = '[ruby-core:43497]'
+ _bug6179 = '[ruby-core:43497]'
make_tempfile {|t|
["", "t", "b"].each do |mode|
open(t.path, "w#{mode}") do |f|
@@ -1703,6 +1899,25 @@ class TestIO < Test::Unit::TestCase
}
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_seek
make_tempfile {|t|
@@ -1729,16 +1944,28 @@ class TestIO < Test::Unit::TestCase
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|2
+ 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
@@ -1765,16 +1992,28 @@ class TestIO < Test::Unit::TestCase
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
@@ -1836,6 +2075,10 @@ class TestIO < Test::Unit::TestCase
assert_raise(ArgumentError) do
open(t.path, "rr") { }
end
+
+ assert_raise(ArgumentError) do
+ open(t.path, "rbt") { }
+ end
}
end
@@ -1916,7 +2159,7 @@ class TestIO < Test::Unit::TestCase
assert_raise(Errno::EBADF, feature2250) {t.close}
end
ensure
- t.unlink
+ t.close!
end
def test_autoclose_false_closed_by_finalizer
@@ -1932,7 +2175,7 @@ class TestIO < Test::Unit::TestCase
assert_nothing_raised(Errno::EBADF, feature2250) {t.close}
end
ensure
- t.unlink
+ t.close!
end
def test_open_redirect
@@ -1954,6 +2197,22 @@ class TestIO < Test::Unit::TestCase
end
end
+ def test_read_command
+ assert_equal("foo\n", IO.read("|echo foo"))
+ assert_warn(/invoke external command/) do
+ File.read("|#{EnvUtil.rubybin} -e puts")
+ end
+ assert_warn(/invoke external command/) do
+ File.binread("|#{EnvUtil.rubybin} -e puts")
+ end
+ assert_raise(Errno::ENOENT, Errno::EINVAL) do
+ Class.new(IO).read("|#{EnvUtil.rubybin} -e puts")
+ end
+ 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|
@@ -2102,11 +2361,35 @@ End
}
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("|" + EnvUtil.rubybin + " -e 'puts :foo; puts :bar; puts :baz'") {|x| a << x }
assert_equal(["foo\n", "bar\n", "baz\n"], a)
+ a = []
+ IO.foreach("|" + EnvUtil.rubybin + " -e 'puts :zot'", :open_args => ["r"]) {|x| a << x }
+ assert_equal(["zot\n"], a)
+
make_tempfile {|t|
a = []
IO.foreach(t.path) {|x| a << x }
@@ -2218,6 +2501,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)
@@ -2233,6 +2546,14 @@ End
assert_raise(TypeError) { $> = Object.new }
assert_in_out_err([], "$> = $stderr\nputs 'foo'", [], %w(foo))
+
+ assert_separately(%w[-Eutf-8], <<-"end;") # do
+ 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
@@ -2267,7 +2588,16 @@ 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
@@ -2302,8 +2632,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
@@ -2318,7 +2646,7 @@ End
File.foreach("slnk", :open_args=>[File::RDONLY|File::NOFOLLOW]) {}
}
}
- end
+ end if /freebsd|linux/ =~ RUBY_PLATFORM and defined? File::NOFOLLOW
def test_tainted
make_tempfile {|t|
@@ -2333,13 +2661,21 @@ End
}
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 = %q{\
+ src = "#{<<~"begin;"}\n#{<<~'end;'}"
+ begin;
t = Thread.new { sleep 3 }
Thread.new {sleep 1; t.kill; p 'hi!'}
t.join
- }.gsub(/^\s+/, '')
+ end;
10.times.map do
Thread.start do
assert_in_out_err([], src) {|stdout, stderr|
@@ -2352,21 +2688,27 @@ End
def test_flush_in_finalizer1
require 'tempfile'
bug3910 = '[ruby-dev:42341]'
- Tempfile.open("bug3910") {|t|
+ 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.unlink
+ t
}
ensure
- GC.start
+ 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
@@ -2375,14 +2717,23 @@ End
Tempfile.open("bug3910") {|t|
path = t.path
t.close
- 1.times do
- io = open(path,"w")
- io.print "hoge"
- end
- assert_nothing_raised(TypeError, bug3910) do
- GC.start
+ 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.unlink
+ t.close!
}
end
@@ -2408,13 +2759,56 @@ 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|
- assert_equal(t.advise(adv, offset, len), nil)
+ 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
@@ -2441,10 +2835,10 @@ 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|
+ %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(NotImplementedError, feature4204) { t.advise(adv, offset, len) }
+ assert_raise_with_message(NotImplementedError, /#{Regexp.quote(adv.inspect)}/, feature4204) { t.advise(adv, offset, len) }
end
end
end
@@ -2452,9 +2846,6 @@ End
end
def test_fcntl_lock_linux
- return if /x86_64-linux/ !~ RUBY_PLATFORM # A binary form of struct flock depend on platform
- return if [nil].pack("p").bytesize != 8 # Return if x32 platform.
-
pad=0
Tempfile.create(self.class.name) do |f|
r, w = IO.pipe
@@ -2483,11 +2874,10 @@ End
Process.kill :TERM, pid
Process.waitpid2(pid)
end
- 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
- return if /freebsd/ !~ RUBY_PLATFORM # A binary form of struct flock depend on platform
-
start = 12
len = 34
sysid = 0
@@ -2518,7 +2908,7 @@ End
Process.kill :TERM, pid
Process.waitpid2(pid)
end
- 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|
@@ -2532,7 +2922,6 @@ End
end
def test_cross_thread_close_fd
- skip "cross thread close causes hung-up if pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
with_pipe do |r,w|
read_thread = Thread.new do
begin
@@ -2555,15 +2944,41 @@ End
$stdin.reopen(r)
r.close
read_thread = Thread.new do
- $stdin.read(1)
+ begin
+ $stdin.read(1)
+ rescue IOError => e
+ e
+ end
end
sleep(0.1) until read_thread.stop?
$stdin.close
- assert_raise(IOError) {read_thread.join}
+ 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]'
@@ -2618,6 +3033,10 @@ End
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"
@@ -2703,11 +3122,8 @@ End
end
def test_ioctl_linux
- return if /linux/ !~ RUBY_PLATFORM
# Alpha, mips, sparc and ppc have an another ioctl request number scheme.
# So, hardcoded 0x80045200 may fail.
- return if /^i.?86|^x86_64/ !~ RUBY_PLATFORM
-
assert_nothing_raised do
File.open('/dev/urandom'){|f1|
entropy_count = ""
@@ -2724,21 +3140,24 @@ End
}
end
assert_equal(File.size(__FILE__), buf.unpack('i!')[0])
- end
+ end if /^(?:i.?86|x86_64)-linux/ =~ RUBY_PLATFORM
def test_ioctl_linux2
- return if /linux/ !~ RUBY_PLATFORM
- return if /^i.?86|^x86_64/ !~ RUBY_PLATFORM
-
- return unless system('tty', '-s') # stdin is not a terminal
- File.open('/dev/tty') { |f|
+ 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)
}
- }
- end
+ ensure
+ f.close if f
+ end
+ end if /^(?:i.?86|x86_64)-linux/ =~ RUBY_PLATFORM
def test_setpos
mkcdtmpdir {
@@ -2799,7 +3218,6 @@ End
def test_frozen_autoclose
with_pipe do |r,w|
- fd = r.fileno
assert_equal(true, r.freeze.autoclose?)
end
end
@@ -2821,33 +3239,46 @@ End
end
def test_readpartial_locktmp
- skip "nonblocking mode is not supported for pipe on this platform" if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
bug6099 = '[ruby-dev:45297]'
buf = " " * 100
data = "a" * 100
+ th = nil
with_pipe do |r,w|
- r.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
+ r.nonblock = true
th = Thread.new {r.readpartial(100, buf)}
+
Thread.pass until th.stop?
- buf.replace("")
+
+ assert_equal 100, buf.bytesize
+
+ begin
+ buf.replace("")
+ rescue RuntimeError => e
+ assert_match(/can't modify string; temporarily locked/, e.message)
+ Thread.pass
+ end until buf.empty?
+
assert_empty(buf, bug6099)
+ assert_predicate(th, :alive?)
w.write(data)
Thread.pass while th.alive?
th.join
end
assert_equal(data, buf, bug6099)
- rescue RuntimeError # can't modify string; temporarily locked
end
def test_advise_pipe
# we don't know if other platforms have a real posix_fadvise()
- return if /linux/ !~ RUBY_PLATFORM
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) }
- assert_raise(Errno::ESPIPE, Errno::EINVAL) { w.advise(:willneed) }
+ 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
+ end if /linux/ =~ RUBY_PLATFORM
def assert_buffer_not_raise_shared_string_error
bug6764 = '[ruby-core:46586]'
@@ -2915,14 +3346,13 @@ End
assert_normal_exit %q{
require "tempfile"
- # try to raise RLIM_NOFILE to >FD_SETSIZE
- # Unfortunately, ruby export FD_SETSIZE. then we assume it's 1024.
+ # 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+10)
- rescue =>e
- # Process::RLIMIT_NOFILE couldn't be raised. skip the test
+ Process.setrlimit(Process::RLIMIT_NOFILE, fd_setsize+20)
+ rescue Errno::EPERM
exit 0
end
@@ -2932,8 +3362,8 @@ End
}
IO.select(tempfiles)
- }, bug8080, timeout: 30
- end
+ }, bug8080, timeout: 100
+ end if defined?(Process::RLIMIT_NOFILE)
def test_read_32bit_boundary
bug8431 = '[ruby-core:55098] [Bug #8431]'
@@ -2994,29 +3424,37 @@ End
bug8669 = '[ruby-core:56121] [Bug #8669]'
str = ""
- r, = IO.pipe
- t = Thread.new { 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 }
- ensure
- t.kill
- end
+ 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 = ""
- r, = IO.pipe
- t = Thread.new { 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 }
- ensure
- t.kill
- end
+ 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|
@@ -3033,15 +3471,19 @@ End
bug8669 = '[ruby-core:56121] [Bug #8669]'
str = ""
- r, = IO.pipe
- t = Thread.new { 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 }
- ensure
- t.kill
- end
+ 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'
@@ -3051,4 +3493,283 @@ End
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_race_gets_and_close
+ assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
+ 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
+ assert_nothing_raised(IOError, bug13076) {
+ t.each(&:join)
+ }
+ 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
+ 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_select_leak
+ assert_no_memory_leak([], <<-"end;", <<-"end;", rss: true, timeout: 240)
+ r, w = IO.pipe
+ rset = [r]
+ wset = [w]
+ Thread.new { IO.select(rset, wset, nil, 0) }.join
+ end;
+ 20_000.times do
+ th = Thread.new { IO.select(rset, wset) }
+ Thread.pass until th.stop?
+ th.kill
+ th.join
+ GC.start
+ end
+ end;
+ end
end
diff --git a/test/ruby/test_io_m17n.rb b/test/ruby/test_io_m17n.rb
index 95081322d4..9ff5307fc3 100644
--- a/test/ruby/test_io_m17n.rb
+++ b/test/ruby/test_io_m17n.rb
@@ -1,9 +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 = [
@@ -313,6 +313,17 @@ 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")
@@ -459,7 +470,7 @@ EOT
w.close
end,
proc do |r|
- timeout(1) {
+ Timeout.timeout(1) {
assert_equal("before \xa2\xa2".encode("utf-8", "euc-jp"),
r.gets(rs))
}
@@ -1214,7 +1225,6 @@ EOT
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)
@@ -1230,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|
@@ -1598,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")
@@ -1702,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
@@ -1883,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
@@ -2035,14 +2082,14 @@ EOT
def test_strip_bom
with_tmpdir {
- text = "\uFEFFa"
- stripped = "a"
+ 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)
@@ -2052,10 +2099,10 @@ EOT
bug3407 = '[ruby-core:30641]'
path = 'UTF-8-bom.txt'
result = File.read(path, encoding: 'BOM|UTF-8')
- assert_equal("a", result.force_encoding("ascii-8bit"), bug3407)
+ assert_equal(stripped.b, result.force_encoding("ascii-8bit"), bug3407)
bug8323 = '[ruby-core:54563] [Bug #8323]'
- expected = "a\xff".force_encoding("utf-8")
+ 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)
@@ -2086,6 +2133,55 @@ EOT
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
+
def test_cbuf
with_tmpdir {
fn = "tst"
@@ -2156,32 +2252,34 @@ EOT
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)
- 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
+ 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)
- 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
+ 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
@@ -2191,7 +2289,7 @@ EOT
w.binmode
w.puts(0x010a.chr(Encoding::UTF_32BE))
w.puts(0x010a.chr(Encoding::UTF_16BE))
- w.puts(0x0a010000.chr(Encoding::UTF_32LE))
+ w.puts(0x0a01.chr(Encoding::UTF_32LE))
w.puts(0x0a01.chr(Encoding::UTF_16LE))
w.close
end,
@@ -2199,7 +2297,7 @@ EOT
r.binmode
assert_equal("\x00\x00\x01\x0a\n", r.read(5), bug)
assert_equal("\x01\x0a\n", r.read(3), bug)
- assert_equal("\x00\x00\x01\x0a\n", r.read(5), 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
@@ -2550,6 +2648,22 @@ EOT
}
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 = [
@@ -2577,7 +2691,7 @@ EOT
begin
assert_in_out_err(args, "", out, err,
"#{bug11444}: #{test} in #{mode} mode",
- timeout: 1)
+ timeout: 10)
rescue Exception => e
failure << e
end
diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb
index 7fc6dba53e..ed88c9b43d 100644
--- a/test/ruby/test_iseq.rb
+++ b/test/ruby/test_iseq.rb
@@ -1,5 +1,5 @@
require 'test/unit'
-require_relative 'envutil'
+require 'tempfile'
class TestISeq < Test::Unit::TestCase
ISeq = RubyVM::InstructionSequence
@@ -9,9 +9,19 @@ class TestISeq < Test::Unit::TestCase
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 = RubyVM::InstructionSequence.new(src).to_a[13]
- lines = body.find_all{|e| e.kind_of? Fixnum}
+ 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
@@ -47,13 +57,13 @@ class TestISeq < Test::Unit::TestCase
end
def test_unsupport_type
- ary = RubyVM::InstructionSequence.compile("p").to_a
+ ary = compile("p").to_a
ary[9] = :foobar
- assert_raise_with_message(TypeError, /:foobar/) {RubyVM::InstructionSequence.load(ary)}
+ assert_raise_with_message(TypeError, /:foobar/) {ISeq.load(ary)}
end if defined?(RubyVM::InstructionSequence.load)
def test_loaded_cdhash_mark
- iseq = RubyVM::InstructionSequence.compile(<<-'end;', __FILE__, __FILE__, __LINE__+1)
+ iseq = compile(<<-'end;', __LINE__+1)
def bug(kw)
case kw
when "false" then false
@@ -73,26 +83,27 @@ class TestISeq < Test::Unit::TestCase
end if defined?(RubyVM::InstructionSequence.load)
def test_disasm_encoding
- src = "\u{3042} = 1; \u{3042}"
- enc, Encoding.default_internal = Encoding.default_internal, src.encoding
- assert_equal(src.encoding, RubyVM::InstructionSequence.compile(src).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)
- assert_equal(true, RubyVM::InstructionSequence.compile(src).disasm.ascii_only?)
- ensure
- Encoding.default_internal = enc
+ asm = compile(src).disasm
+ assert_equal(src.encoding, asm.encoding)
+ assert_predicate(asm, :valid_encoding?)
end
LINE_BEFORE_METHOD = __LINE__
def method_test_line_trace
- a = 1
+ _a = 1
- b = 2
+ _b = 2
end
def test_line_trace
- iseq = ISeq.compile \
+ iseq = compile \
%q{ a = 1
b = 2
c = 3
@@ -145,10 +156,279 @@ class TestISeq < Test::Unit::TestCase
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) {ISeq.compile(nil)}
- assert_raise(TypeError, bug11159) {ISeq.compile(:foo)}
- assert_raise(TypeError, bug11159) {ISeq.compile(1)}
+ 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;"}", /keyword_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 sample_iseq
+ ISeq.compile <<-EOS.gsub(/^.*?: /, "")
+ 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 test_to_binary_with_objects
+ # conceptually backport from r62856.
+ # ISeq binary dump doesn't consider alignment in 2.5 and older
+ skip "does not work on other than x86" unless /x(?:86|64)|i\d86/ =~ RUBY_PLATFORM
+ code = "[]"+100.times.map{|i|"<</#{i}/"}.join
+ iseq = RubyVM::InstructionSequence.compile(code)
+ bin = assert_nothing_raised do
+ iseq.to_binary
+ rescue RuntimeError => e
+ skip e.message if /compile with coverage/ =~ e.message
+ raise
+ end
+ iseq2 = RubyVM::InstructionSequence.load_from_binary(bin)
+ assert_equal(iseq2.to_a, iseq.to_a)
+ end
+
+ def test_to_binary_tracepoint
+ # conceptually backport from r62856.
+ # ISeq binary dump doesn't consider alignment in 2.5 and older
+ skip "does not work on other than x86" unless /x(?:86|64)|i\d86/ =~ RUBY_PLATFORM
+ filename = "#{File.basename(__FILE__)}_#{__LINE__}"
+ iseq = RubyVM::InstructionSequence.compile("x = 1\n y = 2", filename)
+ # conceptually partial backport from r63103, r65567.
+ # All ISeq#to_binary should rescue to skip when running with coverage.
+ # current trunk (2.6-) has assert_iseq_to_binary.
+ begin
+ iseq_bin = iseq.to_binary
+ rescue RuntimeError => e
+ skip e.message if /compile with coverage/ =~ e.message
+ raise
+ end
+ ary = []
+ TracePoint.new(:line){|tp|
+ next unless tp.path == filename
+ ary << [tp.path, tp.lineno]
+ }.enable{
+ ISeq.load_from_binary(iseq_bin).eval
+ }
+ assert_equal [[filename, 1], [filename, 2]], ary, '[Bug #14702]'
end
end
diff --git a/test/ruby/test_iterator.rb b/test/ruby/test_iterator.rb
index 34652db2bb..9bfa947607 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
@@ -73,7 +74,7 @@ class TestIterator < Test::Unit::TestCase
def test_break
done = true
loop{
- break
+ break if true
done = false # should not reach here
}
assert(done)
@@ -83,7 +84,7 @@ class TestIterator < Test::Unit::TestCase
loop {
break if done
done = true
- next
+ next if true
bad = true # should not reach here
}
assert(!bad)
@@ -93,7 +94,7 @@ class TestIterator < Test::Unit::TestCase
loop {
break if done
done = true
- redo
+ redo if true
bad = true # should not reach here
}
assert(!bad)
@@ -108,7 +109,7 @@ class TestIterator < Test::Unit::TestCase
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})
+ assert_equal(x.iter_test1{|e|e}, x.iter_test2{|e|e})
end
class IterTest
@@ -278,6 +279,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 +303,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_keyword.rb b/test/ruby/test_keyword.rb
index 13bac278f2..8a45016c13 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestKeywordArguments < Test::Unit::TestCase
def f1(str: "foo", num: 424242)
@@ -311,11 +311,16 @@ class TestKeywordArguments < Test::Unit::TestCase
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")
+ 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)
@@ -363,22 +368,33 @@ class TestKeywordArguments < Test::Unit::TestCase
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}")
+ break eval("proc {|a:| a}", nil, 'xyzzy', __LINE__)
end
assert_raise_with_message(ArgumentError, /missing keyword/, feature7701) {b.call}
- assert_raise_with_message(ArgumentError, /unknown keyword/, feature7701) {b.call(a:0, b:1)}
+ 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:, **b| [a, b]}")
+ 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, :b]], b.parameters, 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
@@ -440,7 +456,7 @@ class TestKeywordArguments < Test::Unit::TestCase
end
end
assert_equal([{}, {}], a.new.foo({}))
- assert_equal([{}, {:bar=>"x"}], a.new.foo({}, bar: "x"))
+ assert_equal([{}, {:bar=>"x"}], a.new.foo({}, bar: "x"), bug8040)
end
def test_precedence_of_keyword_arguments_with_post_argument
@@ -487,6 +503,29 @@ class TestKeywordArguments < Test::Unit::TestCase
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.f2(a = nil) 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_equal({a: 42}, m.f2(**o), '[ruby-core:82280] [Bug #13791]')
+
+ assert_equal({a: 42}, m.f2("a".to_sym => 42), '[ruby-core:82291] [Bug #13793]')
+
+ 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{
@@ -506,6 +545,16 @@ class TestKeywordArguments < Test::Unit::TestCase
}, 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)
@@ -520,4 +569,117 @@ class TestKeywordArguments < Test::Unit::TestCase
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 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 24efa71bc1..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
@@ -25,8 +26,15 @@ class TestLambdaParameters < Test::Unit::TestCase
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
@@ -55,9 +63,48 @@ class TestLambdaParameters < Test::Unit::TestCase
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
@@ -91,7 +138,7 @@ class TestLambdaParameters < Test::Unit::TestCase
end
def return_in_current(val)
- 1.tap &->(*) {return 0}
+ 1.tap(&->(*) {return 0})
val
end
@@ -100,7 +147,7 @@ class TestLambdaParameters < Test::Unit::TestCase
end
def return_in_callee(val)
- yield_block &->(*) {return 0}
+ yield_block(&->(*) {return 0})
val
end
diff --git a/test/ruby/test_lazy_enumerator.rb b/test/ruby/test_lazy_enumerator.rb
index 760db95f7b..03371c912a 100644
--- a/test/ruby/test_lazy_enumerator.rb
+++ b/test/ruby/test_lazy_enumerator.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestLazyEnumerator < Test::Unit::TestCase
class Step
@@ -14,7 +14,14 @@ class TestLazyEnumerator < Test::Unit::TestCase
def each(*args)
@args = args
- @enum.each {|i| @current = i; yield i}
+ @enum.each do |v|
+ @current = v
+ if v.is_a? Enumerable
+ yield *v
+ else
+ yield v
+ end
+ end
end
end
@@ -25,7 +32,7 @@ class TestLazyEnumerator < Test::Unit::TestCase
a = [1, 2, 3].lazy
a.freeze
- assert_raise(RuntimeError) {
+ assert_raise(FrozenError) {
a.__send__ :initialize, [4, 5], &->(y, *v) { y << yield(*v) }
}
end
@@ -100,6 +107,15 @@ class TestLazyEnumerator < Test::Unit::TestCase
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)
@@ -198,6 +214,34 @@ class TestLazyEnumerator < Test::Unit::TestCase
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)
@@ -247,6 +291,11 @@ class TestLazyEnumerator < Test::Unit::TestCase
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)
@@ -313,11 +362,11 @@ class TestLazyEnumerator < Test::Unit::TestCase
def test_take_rewound
bug7696 = '[ruby-core:51470]'
e=(1..42).lazy.take(2)
- assert_equal 1, e.next
- assert_equal 2, e.next
+ assert_equal 1, e.next, bug7696
+ assert_equal 2, e.next, bug7696
e.rewind
- assert_equal 1, e.next
- assert_equal 2, e.next
+ assert_equal 1, e.next, bug7696
+ assert_equal 2, e.next, bug7696
end
def test_take_while
@@ -452,6 +501,15 @@ EOS
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
@@ -470,6 +528,7 @@ EOS
bug7507 = '[ruby-core:51510]'
{
slice_before: //,
+ slice_after: //,
with_index: nil,
cycle: nil,
each_with_object: 42,
@@ -480,6 +539,19 @@ EOS
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
@@ -490,4 +562,20 @@ EOS
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 6e6831968d..cf1a2babd7 100644
--- a/test/ruby/test_literal.rb
+++ b/test/ruby/test_literal.rb
@@ -1,6 +1,6 @@
# -*- coding: us-ascii -*-
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestRubyLiteral < Test::Unit::TestCase
@@ -14,20 +14,20 @@ 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
@@ -90,6 +90,9 @@ class TestRubyLiteral < Test::Unit::TestCase
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
@@ -116,12 +119,78 @@ class TestRubyLiteral < Test::Unit::TestCase
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
+
+ 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
+ end
+
def test_regexp
assert_instance_of Regexp, //
assert_match(//, 'a')
@@ -354,6 +423,35 @@ class TestRubyLiteral < Test::Unit::TestCase
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)
@@ -390,7 +488,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
@@ -456,6 +554,7 @@ class TestRubyLiteral < Test::Unit::TestCase
}
}
}
+ assert_equal(100.0, 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100e100)
end
def test_symbol_list
diff --git a/test/ruby/test_m17n.rb b/test/ruby/test_m17n.rb
index e933475997..9c7df4cb03 100644
--- a/test/ruby/test_m17n.rb
+++ b/test/ruby/test_m17n.rb
@@ -1,6 +1,6 @@
# coding: US-ASCII
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestM17N < Test::Unit::TestCase
def assert_encoding(encname, actual, message=nil)
@@ -9,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
@@ -261,6 +261,21 @@ class TestM17N < Test::Unit::TestCase
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
@@ -271,7 +286,7 @@ class TestM17N < Test::Unit::TestCase
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_raise(Encoding::CompatibilityError) { [o].inspect }
+ assert_equal '[abc]', [o].inspect
end
ensure
Encoding.default_internal = orig_int
@@ -295,13 +310,18 @@ class TestM17N < Test::Unit::TestCase
def o.inspect
"abc".encode(Encoding.default_external)
end
- assert_raise(Encoding::CompatibilityError) { [o].inspect }
+ assert_equal '[abc]', [o].inspect
Encoding.default_external = Encoding::US_ASCII
def o.inspect
"\u3042"
end
- assert_raise(Encoding::CompatibilityError) { [o].inspect }
+ 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
@@ -452,7 +472,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)
}
@@ -461,13 +481,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"))
}
@@ -480,6 +500,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|
@@ -709,7 +732,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))
@@ -752,7 +775,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)
@@ -1115,7 +1138,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)
}
@@ -1123,7 +1146,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
@@ -1478,6 +1501,31 @@ 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_compatible
@@ -1582,8 +1630,9 @@ class TestM17N < Test::Unit::TestCase
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\x81").scrub {|c| break c})
+ 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
@@ -1599,6 +1648,18 @@ class TestM17N < Test::Unit::TestCase
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
@@ -1614,6 +1675,24 @@ class TestM17N < Test::Unit::TestCase
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)
diff --git a/test/ruby/test_m17n_comb.rb b/test/ruby/test_m17n_comb.rb
index 55bfe39553..99c162a92f 100644
--- a/test/ruby/test_m17n_comb.rb
+++ b/test/ruby/test_m17n_comb.rb
@@ -1,4 +1,6 @@
+# frozen_string_literal: false
require 'test/unit'
+require 'etc'
require_relative 'allpairs'
class TestM17NComb < Test::Unit::TestCase
@@ -658,7 +660,9 @@ 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
@@ -671,6 +675,17 @@ 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
@@ -729,29 +744,43 @@ class TestM17NComb < Test::Unit::TestCase
}
end
- def test_str_crypt
- begin
- # glibc 2.16 or later denies salt contained other than [0-9A-Za-z./] #7312
- glibcpath = `ldd #{RbConfig.ruby}`[/\S+\/libc.so\S+/]
- glibcver = `#{glibcpath}`[/\AGNU C Library.*version ([0-9.]+)/, 1].split('.').map(&:to_i)
- strict_crypt = (glibcver <=> [2, 16]) > -1
- rescue
- 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 strict_crypt
- next unless salt.ascii_only? && /\A[0-9a-zA-Z.\/]+\z/ =~ salt
- end
- if b(salt).length < 2
- assert_raise(ArgumentError) { str.crypt(salt) }
- next
- end
- t = str.crypt(salt)
- assert_equal(b(str).crypt(b(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?
@@ -779,7 +808,7 @@ 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
@@ -1042,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_match(/invalid byte sequence/, assert_raise(ArgumentError) { s1.scan(s2) }.message)
+ 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
@@ -1546,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
@@ -1606,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 8e0bca46ba..0565a1c04f 100644
--- a/test/ruby/test_marshal.rb
+++ b/test/ruby/test_marshal.rb
@@ -1,6 +1,6 @@
+# frozen_string_literal: false
require 'test/unit'
require 'tempfile'
-require_relative 'envutil'
require_relative 'marshaltestlib'
class TestMarshal < Test::Unit::TestCase
@@ -103,17 +103,19 @@ class TestMarshal < Test::Unit::TestCase
def test_pipe
o1 = C.new("a" * 10000)
- o2 = IO.pipe do |r, w|
- Thread.new {Marshal.dump(o1, w)}
- Marshal.load(r)
+ 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
- assert_equal(o1.str, o2.str)
- o2 = IO.pipe do |r, w|
- Thread.new {Marshal.dump(o1, w, 2)}
- Marshal.load(r)
+ 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_equal(o1.str, o2.str)
assert_raise(TypeError) { Marshal.dump("foo", Object.new) }
assert_raise(TypeError) { Marshal.load(Object.new) }
@@ -247,6 +249,10 @@ 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" +
@@ -562,7 +568,7 @@ class TestMarshal < Test::Unit::TestCase
s.instance_variable_set(:@t, 42)
t = Bug8276.new(s)
s = Marshal.dump(t)
- assert_raise(RuntimeError) {Marshal.load(s)}
+ assert_raise(FrozenError) {Marshal.load(s)}
end
def test_marshal_load_ivar
@@ -608,6 +614,107 @@ class TestMarshal < Test::Unit::TestCase
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]
@@ -615,10 +722,12 @@ class TestMarshal < Test::Unit::TestCase
end
def test_marshal_load_extended_class_crash
- crash = "\x04\be:\x0F\x00omparableo:\vObject\x00"
-
- opt = %w[--disable=gems]
- assert_ruby_status(opt, "Marshal.load(#{crash.dump})")
+ 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
@@ -631,4 +740,75 @@ class TestMarshal < Test::Unit::TestCase
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
+ self.foo.baz = :problem
+ {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
end
diff --git a/test/ruby/test_math.rb b/test/ruby/test_math.rb
index 74b3940c1d..f226287442 100644
--- a/test/ruby/test_math.rb
+++ b/test/ruby/test_math.rb
@@ -1,9 +1,10 @@
+# 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_not_predicate(a, :finite?, *rest)
+ assert_predicate(a, :infinite?, *rest)
end
def assert_nan(a, *rest)
@@ -11,20 +12,32 @@ class TestMath < Test::Unit::TestCase
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
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))
- 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) }
+
+ 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))
@@ -36,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
@@ -97,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
@@ -137,11 +153,14 @@ class TestMath < Test::Unit::TestCase
check(1, Math.log(10, 10))
check(2, Math.log(100, 10))
check(Math.log(2.0 ** 64), Math.log(1 << 64))
- assert_equal(1.0/0, Math.log(1.0/0))
+ 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
@@ -149,7 +168,8 @@ class TestMath < Test::Unit::TestCase
check(1, Math.log2(2))
check(2, Math.log2(4))
check(Math.log2(2.0 ** 64), Math.log2(1 << 64))
- assert_equal(1.0/0, Math.log2(1.0/0))
+ 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) }
@@ -160,7 +180,8 @@ class TestMath < Test::Unit::TestCase
check(1, Math.log10(10))
check(2, Math.log10(100))
check(Math.log10(2.0 ** 64), Math.log10(1 << 64))
- assert_equal(1.0/0, Math.log10(1.0/0))
+ 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) }
@@ -170,22 +191,25 @@ 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)) }
+ 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
@@ -222,6 +246,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|
@@ -231,54 +257,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 f478e11486..77273dade5 100644
--- a/test/ruby/test_method.rb
+++ b/test/ruby/test_method.rb
@@ -1,6 +1,6 @@
# -*- coding: us-ascii -*-
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestMethod < Test::Unit::TestCase
def setup
@@ -125,6 +125,11 @@ class TestMethod < Test::Unit::TestCase
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
@@ -192,6 +197,7 @@ class TestMethod < Test::Unit::TestCase
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
@@ -247,6 +253,13 @@ class TestMethod < Test::Unit::TestCase
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)
@@ -272,21 +285,44 @@ 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_singleton_method
+ 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
@@ -382,11 +418,7 @@ class TestMethod < Test::Unit::TestCase
end
}
c2 = Class.new(c1) { define_method(:m) { Proc.new { super() } } }
- # c2.new.m.call should return :m1, but currently it raise NoMethodError.
- # see [Bug #4881] and [Bug #3136]
- assert_raise(NoMethodError) {
- c2.new.m.call
- }
+ assert_equal(:m1, c2.new.m.call, 'see [Bug #4881] and [Bug #3136]')
end
def test_clone
@@ -423,6 +455,9 @@ class TestMethod < Test::Unit::TestCase
c3.class_eval { alias bar foo }
m3 = c3.new.method(:bar)
assert_equal("#<Method: #{c3.inspect}(#{c.inspect})#bar(foo)>", m3.inspect, bug7806)
+
+ m.taint
+ assert_predicate(m.inspect, :tainted?, "inspect result should be infected")
end
def test_callee_top_level
@@ -564,6 +599,11 @@ class TestMethod < Test::Unit::TestCase
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
c = Class.new
c.class_eval do
@@ -653,6 +693,15 @@ class TestMethod < Test::Unit::TestCase
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__)
@@ -727,23 +776,6 @@ class TestMethod < Test::Unit::TestCase
}, '[Bug #7825]'
end
- def test_unlinked_method_entry_in_method_object_bug
- bug8100 = '[ruby-core:53640] [Bug #8100]'
- begin
- assert_normal_exit %q{
- loop do
- def x
- "hello" * 1000
- end
- method(:x).call
- end
- }, bug8100, timeout: 2
- rescue Timeout::Error => e
- else
- end
- assert_raise(Timeout::Error, bug8100) {raise e if e}
- end
-
def test_singleton_method
feature8391 = '[ruby-core:54914] [Feature #8391]'
c1 = Class.new
@@ -755,4 +787,263 @@ class TestMethod < Test::Unit::TestCase
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 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
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 9910e261d5..076ea0901f 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
@@ -75,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
@@ -250,7 +258,7 @@ class TestModule < Test::Unit::TestCase
"\u3042",
"Name?",
].each do |name, msg|
- expected = "wrong constant name %s" % quote(name)
+ 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
@@ -302,8 +310,8 @@ class TestModule < Test::Unit::TestCase
classes = []
klass = Class.new {
define_singleton_method(:const_missing) { |name|
- classes << name
- klass
+ classes << name
+ klass
}
}
klass.const_get("Foo::Bar::Baz")
@@ -432,6 +440,10 @@ class TestModule < Test::Unit::TestCase
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)
@@ -443,8 +455,8 @@ class TestModule < Test::Unit::TestCase
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))
@@ -459,12 +471,17 @@ class TestModule < Test::Unit::TestCase
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)
+ assert !User.method_defined?(:wombat)
+ assert User.method_defined?(:mixin)
+ assert User.method_defined?(:user)
+ assert User.method_defined?(:user2)
+ assert !User.method_defined?(:user3)
+
+ assert !User.method_defined?("wombat")
+ assert User.method_defined?("mixin")
+ assert User.method_defined?("user")
+ assert User.method_defined?("user2")
+ assert !User.method_defined?("user3")
end
def module_exec_aux
@@ -506,7 +523,7 @@ class TestModule < Test::Unit::TestCase
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
@@ -519,9 +536,9 @@ class TestModule < Test::Unit::TestCase
assert_nil(n.name)
assert_equal([:N], m.constants)
m.module_eval("module O end")
- assert_equal([:N, :O], m.constants)
+ assert_equal([:N, :O], m.constants.sort)
m.module_eval("class C; end")
- assert_equal([:N, :O, :C], m.constants)
+ 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)
@@ -590,6 +607,8 @@ class TestModule < Test::Unit::TestCase
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
@@ -612,13 +631,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
@@ -678,9 +706,16 @@ class TestModule < Test::Unit::TestCase
def test_const_set_invalid_name
c1 = Class.new
- assert_raise(NameError) { c1.const_set(:foo, :foo) }
- assert_raise(NameError) { c1.const_set("bar", :foo) }
- assert_raise(TypeError) { c1.const_set(1, :foo) }
+ 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
@@ -818,6 +853,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
@@ -828,7 +866,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
@@ -836,7 +874,6 @@ class TestModule < Test::Unit::TestCase
attr_reader :foo
end
o = c.new
- o.foo rescue p(:ok)
p(o.instance_eval { foo })
INPUT
@@ -909,25 +946,34 @@ class TestModule < Test::Unit::TestCase
assert_include(c.constants(false), :Foo, bug9413)
end
- def test_frozen_class
+ 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
@@ -945,13 +991,28 @@ class TestModule < Test::Unit::TestCase
assert_equal(false, c.public_method_defined?(:bar))
assert_equal(false, c.public_method_defined?(:baz))
+ # Test if string arguments are converted to symbols
+ 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))
+ # Test if string arguments are converted to symbols
+ 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))
+
+ # Test if string arguments are converted to symbols
+ 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_top_public_private
@@ -1070,6 +1131,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
@@ -1093,13 +1156,13 @@ 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
@@ -1222,6 +1285,20 @@ class TestModule < Test::Unit::TestCase
undef foo
end
end
+
+ 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
@@ -1272,21 +1349,58 @@ class TestModule < Test::Unit::TestCase
c = Class.new do
attr_writer :foo
end
- assert_raise(ArgumentError) { c.new.send :foo= }
+ assert_raise(ArgumentError, bug8540) { c.new.send :foo= }
end
- def test_private_constant
+ def test_private_constant_in_class
c = Class.new
c.const_set(:FOO, "foo")
assert_equal("foo", c::FOO)
c.private_constant(:FOO)
- assert_raise(NameError) { c::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
- assert_raise(NameError) { c::FOO }
+ 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
@@ -1339,8 +1453,20 @@ class TestModule < Test::Unit::TestCase
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
+
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
@@ -1486,6 +1612,12 @@ class TestModule < Test::Unit::TestCase
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")
@@ -1609,12 +1741,46 @@ class TestModule < Test::Unit::TestCase
to_f / other
end
end
- Fixnum.send(:prepend, M)
+ 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
@@ -1629,7 +1795,7 @@ class TestModule < Test::Unit::TestCase
def test_prepend_visibility_inherited
bug8238 = '[ruby-core:54105] [Bug #8238]'
- assert_separately [], <<-"end;", timeout: 3
+ assert_separately [], <<-"end;", timeout: 20
class A
def foo() A; end
private :foo
@@ -1708,6 +1874,27 @@ class TestModule < Test::Unit::TestCase
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]'
@@ -1719,6 +1906,29 @@ class TestModule < Test::Unit::TestCase
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)
@@ -1726,8 +1936,8 @@ class TestModule < Test::Unit::TestCase
m2.send(:include, m)
m2.class_variable_set(:@@bar, 2)
assert_equal([:@@foo], m.class_variables)
- assert_equal([:@@bar, :@@foo], m2.class_variables)
- assert_equal([:@@bar, :@@foo], m2.class_variables(true))
+ 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
@@ -1785,6 +1995,10 @@ class TestModule < Test::Unit::TestCase
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?
@@ -1823,6 +2037,11 @@ class TestModule < Test::Unit::TestCase
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
@@ -1864,6 +2083,22 @@ class TestModule < Test::Unit::TestCase
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)
@@ -1891,6 +2126,25 @@ class TestModule < Test::Unit::TestCase
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
@@ -1907,13 +2161,13 @@ class TestModule < Test::Unit::TestCase
def test_visibility_by_public_class_method
bug8284 = '[ruby-core:54404] [Bug #8284]'
- assert_raise(NoMethodError) {Object.define_method}
- Module.new.public_class_method(:define_method)
- assert_raise(NoMethodError, bug8284) {Object.define_method}
+ 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_invalidates_method_cache
- assert_in_out_err([], <<-RUBY, %w(123 456), [])
+ def test_include_module_with_constants_does_not_invalidate_method_cache
+ assert_in_out_err([], <<-RUBY, %w(123 456 true), [])
A = 123
class Foo
@@ -1927,8 +2181,13 @@ class TestModule < Test::Unit::TestCase
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
@@ -1987,11 +2246,68 @@ class TestModule < Test::Unit::TestCase
A.prepend InspectIsShallow
- expect = "#<Method: A(Object)#inspect(shallow_inspect)>"
+ 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)
diff --git a/test/ruby/test_not.rb b/test/ruby/test_not.rb
index 486075bf83..721f868a5a 100644
--- a/test/ruby/test_not.rb
+++ b/test/ruby/test_not.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestIfunless < Test::Unit::TestCase
diff --git a/test/ruby/test_notimp.rb b/test/ruby/test_notimp.rb
index 9721723b29..ddebb657bf 100644
--- a/test/ruby/test_notimp.rb
+++ b/test/ruby/test_notimp.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
require 'timeout'
require 'tmpdir'
@@ -35,7 +36,7 @@ class TestNotImplement < Test::Unit::TestCase
proc {`ps -l #{pid}`}
end
assert_nothing_raised(Timeout::Error, ps) do
- Timeout.timeout(5) {
+ Timeout.timeout(EnvUtil.apply_timeout_scale(5)) {
pid = fork {}
Process.wait pid
pid = nil
diff --git a/test/ruby/test_numeric.rb b/test/ruby/test_numeric.rb
index 98fabc4e13..6efc40320a 100644
--- a/test/ruby/test_numeric.rb
+++ b/test/ruby/test_numeric.rb
@@ -1,14 +1,11 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
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)
@@ -16,98 +13,107 @@ class TestNumeric < Test::Unit::TestCase
assert_raise(TypeError) { -Numeric.new }
- EnvUtil.with_default_external(Encoding::UTF_8) do
- 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}"}
- end
- EnvUtil.with_default_external(Encoding::US_ASCII) do
- 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}"}
- end
+ 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 Fixnum"
+ 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
- remove_method :coerce
+ 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_operator(1, :<=, a)
- DummyNumeric.class_eval do
- remove_method :coerce
+ 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_dup
+ a = Numeric.new
+ assert_same a, a.dup
end
- def test_numeric
+ def test_clone
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.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(TypeError) {DummyNumeric.new.quo(1)}
+ a = Numeric.new
+ assert_raise(TypeError) {a.quo(1)}
end
def test_quo_ruby_core_41575
- x = DummyNumeric.new
rat = 84.quo(1)
- DummyNumeric.class_eval do
+ x = Class.new(Numeric) do
define_method(:to_r) { rat }
- end
+ end.new
assert_equal(2.quo(1), x.quo(42), '[ruby-core:41575]')
- ensure
- DummyNumeric.class_eval do
- remove_method :to_r
- end
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
@@ -119,51 +125,70 @@ class TestNumeric < Test::Unit::TestCase
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
- remove_method :<
+ 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_predicate(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
@@ -173,42 +198,32 @@ 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
- remove_method :to_f
+ 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
- remove_method :to_f
+ 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 assert_step(expected, (from, *args), inf: false)
@@ -241,8 +256,7 @@ class TestNumeric < Test::Unit::TestCase
end
def test_step
- i, bignum = 32, 1 << 30
- bignum <<= (i <<= 1) - 32 until bignum.is_a?(Bignum)
+ 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) { } }
@@ -333,4 +347,64 @@ class TestNumeric < Test::Unit::TestCase
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 596bddf1e5..c25dcf9c37 100644
--- a/test/ruby/test_object.rb
+++ b/test/ruby/test_object.rb
@@ -1,6 +1,6 @@
# -*- coding: us-ascii -*-
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestObject < Test::Unit::TestCase
def setup
@@ -12,16 +12,71 @@ 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
@@ -29,8 +84,8 @@ class TestObject < Test::Unit::TestCase
end
obj = cls.new
- assert_throws(:initialize_clone) {obj.clone}
- assert_throws(:initialize_dup) {obj.dup}
+ assert_throw(:initialize_clone) {obj.clone}
+ assert_throw(:initialize_dup) {obj.dup}
end
def test_instance_of
@@ -44,12 +99,12 @@ 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 }
+ assert_raise(FrozenError) { o.untaint }
end
def test_freeze_immediate
@@ -57,6 +112,20 @@ class TestObject < Test::Unit::TestCase
1.freeze
assert_equal(true, 1.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
@@ -213,28 +282,46 @@ class TestObject < Test::Unit::TestCase
end
def test_remove_instance_variable
- o = Object.new
- o.instance_eval { @foo = :foo }
- o.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
@@ -289,12 +376,12 @@ 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
@@ -312,7 +399,7 @@ class TestObject < Test::Unit::TestCase
def test_remove_method
c = Class.new
c.freeze
- assert_raise(RuntimeError) do
+ assert_raise(FrozenError) do
c.instance_eval { remove_method(:foo) }
end
@@ -337,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}) }
@@ -346,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
@@ -571,7 +671,7 @@ class TestObject < Test::Unit::TestCase
end
begin
nil.public_send(o) { x = :ng }
- rescue
+ rescue TypeError
end
assert_equal(:ok, x)
end
@@ -701,6 +801,16 @@ class TestObject < Test::Unit::TestCase
end
EOS
assert_match(/\bToS\u{3042}:/, x)
+
+ 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
def test_inspect
@@ -739,10 +849,12 @@ class TestObject < Test::Unit::TestCase
def initialize
@\u{3044} = 42
end
- new.inspect
+ new
end
EOS
- assert_match(/\bInspect\u{3042}:.* @\u{3044}=42\b/, x)
+ 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
@@ -769,18 +881,16 @@ class TestObject < Test::Unit::TestCase
def test_redef_method_missing
bug5473 = '[ruby-core:40287]'
['ArgumentError.new("bug5473")', 'ArgumentError, "bug5473"', '"bug5473"'].each do |code|
- out, err, status = EnvUtil.invoke_ruby([], <<-SRC, true, true)
+ exc = code[/\A[A-Z]\w+/] || 'RuntimeError'
+ assert_separately([], <<-SRC)
class ::Object
def method_missing(m, *a, &b)
raise #{code}
end
end
- p((1.foo rescue $!))
+ assert_raise_with_message(#{exc}, "bug5473", #{bug5473.dump}) {1.foo}
SRC
- assert_send([status, :success?], bug5473)
- assert_equal("", err, bug5473)
- assert_equal((eval("raise #{code}") rescue $!.inspect), out.chomp, bug5473)
end
end
@@ -789,7 +899,7 @@ class TestObject < Test::Unit::TestCase
b = yield
assert_nothing_raised("copy") {a.instance_eval {initialize_copy(b)}}
c = a.dup.freeze
- assert_raise(RuntimeError, "frozen") {c.instance_eval {initialize_copy(b)}}
+ assert_raise(FrozenError, "frozen") {c.instance_eval {initialize_copy(b)}}
d = a.dup.trust
[a, b, c, d]
end
@@ -810,18 +920,29 @@ class TestObject < Test::Unit::TestCase
end
def test_type_error_message
- issue = "Bug #7539"
+ _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, rss: true, timeout: 60, limit: 2.5)
+ 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 6eb7c8b205..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)
@@ -78,9 +78,84 @@ End
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')
@@ -105,4 +180,27 @@ End
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 6e22891ddd..2a4cc19699 100644
--- a/test/ruby/test_optimization.rb
+++ b/test/ruby/test_optimization.rb
@@ -1,98 +1,124 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
+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_equal 6.66, redefine_method('Float', '/') { 4.2 / 6.66 }, "bug 9238"
+ 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_nil redefine_method('String', 'empty?') { "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
@@ -100,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
@@ -111,34 +136,110 @@ 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_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
@@ -157,67 +258,248 @@ 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]'
- option = {
- tailcall_optimization: true,
- trace_instruction: false,
- }
- iseq = RubyVM::InstructionSequence.new(<<-EOF, "Bug#4082", bug4082, nil, option).eval
- class #{self.class}::Tailcall
- 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)
+ tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
+ begin;
+ def fact_helper(n, res)
+ if n == 1
+ res
+ else
+ fact_helper(n - 1, n * res)
end
end
- EOF
- assert_equal(9131, Tailcall.new.fact(3000).to_s.size, bug4082)
+ 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]'
- option = {
- tailcall_optimization: true,
- trace_instruction: false,
+ 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
}
- iseq = RubyVM::InstructionSequence.new(<<-EOF, "Bug#6901", bug6901, nil, option).eval
- def identity(val)
- val
+ assert_instance_of(RuntimeError, result, bug12082)
+ assert_equal("should be rescued", result.message, bug12082)
end
- def delay
- -> {
- identity(yield)
+ 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
- EOF
- assert_equal(123, delay { 123 }.call, bug6901)
+
+ 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 = <<-EOF
+ 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
- EOF
+ end;
check = {
'foo' => :foo,
+ true => true,
+ false => false,
:sym => :sym,
6 => :fix,
+ nil => nil,
0.1 => :float,
0xffffffffffffffff => :big,
}
@@ -229,7 +511,8 @@ class TestRubyOptimization < Test::Unit::TestCase
assert_equal :nomatch, eval("foo = :blah\n#{code}")
check.each do |foo, _|
klass = foo.class.to_s
- assert_separately([], <<-"end;") # do
+ assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}")
+ begin;
class #{klass}
undef ===
def ===(*args)
@@ -243,6 +526,13 @@ class TestRubyOptimization < Test::Unit::TestCase
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
@@ -253,4 +543,245 @@ class TestRubyOptimization < Test::Unit::TestCase
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;
+ iseq = RubyVM::InstructionSequence.compile(code)
+ 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
+
+ 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 0, 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: 15
+ 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_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: 10)
+ 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 4b089f7322..aec418913e 100644
--- a/test/ruby/test_pack.rb
+++ b/test/ruby/test_pack.rb
@@ -1,4 +1,5 @@
# coding: US-ASCII
+# frozen_string_literal: false
require 'test/unit'
class TestPack < Test::Unit::TestCase
@@ -78,6 +79,14 @@ class TestPack < Test::Unit::TestCase
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))
@@ -86,7 +95,14 @@ class TestPack < Test::Unit::TestCase
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))
- %w[s S l L q Q s! S! i I i! I! l! L!].each {|fmt|
+ 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
@@ -111,6 +127,14 @@ class TestPack < Test::Unit::TestCase
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))
@@ -119,7 +143,14 @@ class TestPack < Test::Unit::TestCase
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))
- %w[s S l L q Q s! S! i I i! I! l! L!].each {|fmt|
+ 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
@@ -181,7 +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_raise(ArgumentError) { (a.pack("p") << "d").unpack("p*") }
+ assert_equal a, (a.pack("p") << "d").unpack("p*")
end
def test_format_string_modified
@@ -417,6 +448,43 @@ class TestPack < Test::Unit::TestCase
assert_operator(8, :<=, [1].pack("Q!").bytesize)
end
+ 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
assert_equal("\000\000\000\001\377\377\177\377\200\000\377\377", [0,1,-1,32767,-32768,65535].pack("n*"))
assert_equal("\000\000\000\000\000\000\000\001\377\377\377\377", [0,1,-1].pack("N*"))
@@ -480,6 +548,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
@@ -526,6 +597,11 @@ EXPECTED
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
@@ -613,6 +689,11 @@ EXPECTED
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
@@ -709,4 +790,90 @@ EXPECTED
$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}")
+ }
+ 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 9ef311b934..b725634a38 100644
--- a/test/ruby/test_parse.rb
+++ b/test/ruby/test_parse.rb
@@ -1,4 +1,5 @@
# coding: US-ASCII
+# frozen_string_literal: false
require 'test/unit'
require 'stringio'
@@ -173,6 +174,7 @@ class TestParse < Test::Unit::TestCase
end
c = Class.new
+ c.freeze
assert_nothing_raised(SyntaxError) do
eval <<-END, nil, __FILE__, __LINE__+1
if false
@@ -182,7 +184,6 @@ class TestParse < Test::Unit::TestCase
END
end
- c = Class.new
assert_raise(SyntaxError) do
eval <<-END, nil, __FILE__, __LINE__+1
$1 &= 1
@@ -206,9 +207,9 @@ class TestParse < Test::Unit::TestCase
END
end
- assert_raise(SyntaxError) do
+ assert_nothing_raised(SyntaxError) do
eval <<-END, nil, __FILE__, __LINE__+1
- class Foo Bar; end
+ class Foo 1; end
END
end
end
@@ -218,7 +219,6 @@ 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, __FILE__, __LINE__+1
undef >, /
@@ -363,7 +363,7 @@ class TestParse < Test::Unit::TestCase
def test_dstr_disallowed_variable
bug8375 = '[ruby-core:54885] [Bug #8375]'
- %w[@ @1 @@. @@ @@1 @@. $ $%].each do |src|
+ %w[@ @1 @. @@ @@1 @@. $ $%].each do |src|
src = '#'+src+' '
str = assert_nothing_raised(SyntaxError, "#{bug8375} #{src.dump}") do
break eval('"'+src+'"')
@@ -376,6 +376,25 @@ class TestParse < Test::Unit::TestCase
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
@@ -465,21 +484,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?"'))
@@ -493,6 +532,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
@@ -516,8 +557,9 @@ 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
@@ -651,10 +693,12 @@ 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
@@ -664,11 +708,13 @@ x = __ENCODING__
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
@@ -700,6 +746,12 @@ x = __ENCODING__
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
@@ -741,6 +793,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")
@@ -750,6 +803,7 @@ 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)
@@ -760,7 +814,7 @@ x = __ENCODING__
end
def test_assign_in_conditional
- assert_raise(SyntaxError) do
+ assert_nothing_raised do
eval <<-END, nil, __FILE__, __LINE__+1
(x, y = 1, 2) ? 1 : 2
END
@@ -808,6 +862,7 @@ x = __ENCODING__
eval <<-END, nil, __FILE__, __LINE__+1
:"foo#{"x"}baz" ? 1 : 2
END
+ assert_equal "bar", x
end
end
@@ -819,26 +874,6 @@ x = __ENCODING__
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)
- 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_is_class_id
- c = Class.new
- assert_raise(NameError) do
- c.instance_eval { remove_class_variable(:@var) }
- end
- end
-
def test_method_block_location
bug5614 = '[ruby-core:40936]'
expected = nil
@@ -852,4 +887,227 @@ x = __ENCODING__
actual = e.backtrace.first[/\A#{Regexp.quote(__FILE__)}:(\d+):/o, 1].to_i
assert_equal(expected, actual, bug5614)
end
+
+ def test_shadowing_variable
+ assert_warning(/shadowing outer local variable/) {eval("a=1; tap {|a|}")}
+ a = "\u{3042}"
+ assert_warning(/#{a}/o) {eval("#{a}=1; tap {|#{a}|}")}
+ end
+
+ 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_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_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 2db7be0b76..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 = ''
diff --git a/test/ruby/test_pipe.rb b/test/ruby/test_pipe.rb
index bcea91bebb..efca8f28c1 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'
diff --git a/test/ruby/test_primitive.rb b/test/ruby/test_primitive.rb
index 9d451e2ae0..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
diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb
index 06f137fdd1..df9bb5f549 100644
--- a/test/ruby/test_proc.rb
+++ b/test/ruby/test_proc.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestProc < Test::Unit::TestCase
def setup
@@ -205,7 +205,14 @@ class TestProc < Test::Unit::TestCase
b = b.binding
assert_instance_of(Binding, b, '[ruby-core:25589]')
bug10432 = '[ruby-core:65919] [Bug #10432]'
- assert_same(self, b.eval("self"), bug10432)
+ 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
@@ -239,48 +246,66 @@ class TestProc < Test::Unit::TestCase
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
@@ -315,16 +340,11 @@ 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
@@ -410,6 +430,7 @@ class TestProc < Test::Unit::TestCase
t = Thread.new { sleep }
assert_raise(ThreadError) { t.instance_eval { initialize { } } }
t.kill
+ t.join
end
def test_to_proc
@@ -425,7 +446,7 @@ class TestProc < Test::Unit::TestCase
assert_equal(:noreason, exc.reason)
end
- def test_binding2
+ def test_curry_binding
assert_raise(ArgumentError) { proc {}.curry.binding }
end
@@ -572,7 +593,7 @@ class TestProc < Test::Unit::TestCase
assert_equal [1, 2, 3], pr.call([1,2,3,4,5,6])
end
- def test_proc_args_opt_signle
+ def test_proc_args_opt_single
bug7621 = '[ruby-dev:46801]'
pr = proc {|a=:a|
a
@@ -1099,6 +1120,9 @@ class TestProc < Test::Unit::TestCase
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
@@ -1141,7 +1165,7 @@ class TestProc < Test::Unit::TestCase
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(:upcase).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
@@ -1153,6 +1177,8 @@ class TestProc < Test::Unit::TestCase
x = proc {}
x.taint
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
@@ -1215,7 +1241,10 @@ class TestProc < Test::Unit::TestCase
def test_curry_with_trace
# bug3751 = '[ruby-core:31871]'
set_trace_func(proc {})
- test_curry
+ methods.grep(/\Atest_curry/) do |test|
+ next if test == __method__
+ __send__(test)
+ end
ensure
set_trace_func(nil)
end
@@ -1253,6 +1282,28 @@ class TestProc < Test::Unit::TestCase
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))
@@ -1276,9 +1327,53 @@ class TestProc < Test::Unit::TestCase
assert_equal(20, b.eval("b"))
end
+ def test_local_variable_set_wb
+ assert_ruby_status([], <<-'end;', '[Bug #13605]', timeout: 15)
+ 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
end
diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb
index 0a97ba76ec..ba7b0f1177 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 'tempfile'
require 'timeout'
-require_relative 'envutil'
+require 'io/wait'
require 'rbconfig'
class TestProcess < Test::Unit::TestCase
@@ -65,8 +67,11 @@ class TestProcess < Test::Unit::TestCase
return unless rlimit_exist?
with_tmpchdir {
write_file 's', <<-"End"
- # if limit=0, this test freeze pn OpenBSD
- limit = /openbsd/ =~ RUBY_PLATFORM ? 1 : 0
+ # 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, limit)
@@ -116,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)
@@ -131,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)
@@ -141,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
@@ -172,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])
@@ -229,6 +242,22 @@ 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([],<<-"end;") # [ruby-core:82033] [Bug #13744]
+ assert(system("#{RUBY}", "-e",
+ "exit([3600,3600] == Process.getrlimit(:CPU))",
+ 'rlimit_cpu'.to_sym => 3600))
+ assert_raise(ArgumentError) 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]
@@ -307,6 +336,16 @@ 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|
@@ -397,9 +436,13 @@ 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
@@ -416,13 +459,32 @@ class TestProcess < Test::Unit::TestCase
def test_execopts_open_chdir_m17n_path
with_tmpchdir {|d|
Dir.mkdir "テスト"
- system(*PWD, :chdir => "テスト", :out => "open_chdir_テスト")
+ (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_テスト").chomp.encode(__ENCODING__))
+ 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
@@ -464,7 +526,7 @@ 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)
@@ -536,76 +598,169 @@ 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
- 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)
- }
+ 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
- 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;
- }
+ 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
- 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
- }
+ 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|
+ trap(:USR1) { print "trap\n" }
+ system("cat", :in => "fifo")
+ EOS
+ sleep 1
+ Process.kill(:USR1, io.pid)
+ sleep 1
+ File.write("fifo", "ok\n")
+ assert_equal("trap\nok\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)
+ }
+ }
- closed_fd = nil
- with_pipes(5) {|pipes|
- io = pipes.last.last
- closed_fd = io.fileno
+ 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)
}
- 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
+ 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)
}
- end
+ Process.wait pid
+ }
+
+ 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
+ }
+ 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")
@@ -626,6 +781,17 @@ class TestProcess < Test::Unit::TestCase
}
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'",
@@ -670,6 +836,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| }
}
@@ -679,7 +850,12 @@ class TestProcess < Test::Unit::TestCase
assert_raise(ArgumentError) {
IO.popen([*ECHO["fuga"], STDOUT=>"out"]) {|io| }
}
- skip "inheritance of fd other than stdin,stdout and stderr is not supported" if windows?
+ }
+ 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)
@@ -735,11 +911,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)"`
@@ -787,29 +966,38 @@ 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
}
}
@@ -828,7 +1016,7 @@ class TestProcess < Test::Unit::TestCase
rescue NotImplementedError
skip "IO#close_on_exec= is not supported"
end
- end
+ end unless windows? # passing non-stdio fds is not supported on Windows
def test_execopts_redirect_tempfile
bug6269 = '[ruby-core:44181]'
@@ -1133,8 +1321,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")
@@ -1186,6 +1373,14 @@ class TestProcess < Test::Unit::TestCase
}
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")
@@ -1223,25 +1418,12 @@ class TestProcess < Test::Unit::TestCase
return unless Signal.list.include?("QUIT")
with_tmpchdir do
- write_file("foo", "puts;STDOUT.flush;sleep 30")
- pid = nil
- IO.pipe do |r, w|
- pid = spawn(RUBY, "foo", out: w)
- w.close
- Thread.new { r.read(1); Process.kill(:SIGQUIT, pid) }
- Process.wait(pid)
- end
- t = Time.now
- s = $?
+ 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_send(
- [["#<Process::Status: pid #{ s.pid } SIGQUIT (signal #{ s.termsig })>",
- "#<Process::Status: pid #{ s.pid } SIGQUIT (signal #{ s.termsig }) (core dumped)>"],
- :include?,
- s.inspect])
- EnvUtil.diagnostic_reports("QUIT", RUBY, pid, t)
+ assert_equal("#<Process::Status: pid #{ s.pid } SIGQUIT (signal #{ s.termsig })>",
+ s.inspect.sub(/ \(core dumped\)(?=>\z)/, ''))
end
end
@@ -1338,8 +1520,14 @@ 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)
+ 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
@@ -1401,7 +1589,10 @@ class TestProcess < Test::Unit::TestCase
pid = nil
IO.pipe do |r, w|
pid = fork { r.read(1); exit }
- Thread.start { raise }
+ Thread.start {
+ Thread.current.report_on_exception = false
+ raise
+ }
w.puts
end
Process.wait pid
@@ -1458,6 +1649,9 @@ class TestProcess < Test::Unit::TestCase
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
@@ -1497,7 +1691,7 @@ class TestProcess < Test::Unit::TestCase
with_tmpchdir do
assert_nothing_raised('[ruby-dev:12261]') do
- timeout(3) do
+ Timeout.timeout(3) do
pid = spawn('yes | ls')
Process.waitpid pid
end
@@ -1589,6 +1783,35 @@ class TestProcess < Test::Unit::TestCase
}
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_execopts_new_pgroup
return unless windows?
@@ -1741,6 +1964,27 @@ EOS
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
@@ -1845,6 +2089,8 @@ EOS
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
@@ -1928,4 +2174,203 @@ EOS
assert_kind_of(Float, t, "Process.clock_getres(:#{n})")
end
+ def test_deadlock_by_signal_at_forking
+ assert_separately(["-", RUBY], <<-INPUT, timeout: 80)
+ 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, '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_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 7b68bc38d0..882d33fb40 100644
--- a/test/ruby/test_rand.rb
+++ b/test/ruby/test_rand.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestRand < Test::Unit::TestCase
def assert_random_int(ws, m, init = 0)
@@ -210,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
@@ -420,7 +421,7 @@ END
(1..10).to_a.shuffle
raise 'default seed is not set' if srand == 0
end
- p2, st = Process.waitpid2(pid)
+ _, st = Process.waitpid2(pid)
assert_predicate(st, :success?, "#{st.inspect}")
rescue NotImplementedError, ArgumentError
end
@@ -428,9 +429,20 @@ END
def assert_fork_status(n, mesg, &block)
IO.pipe do |r, w|
(1..n).map do
- p1 = fork {w.puts(block.call.to_s)}
- _, st = Process.waitpid2(p1)
- assert_send([st, :success?], mesg)
+ 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
@@ -452,6 +464,10 @@ END
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
@@ -491,7 +507,7 @@ END
def test_initialize_frozen
r = Random.new(0)
r.freeze
- assert_raise(RuntimeError, '[Bug #6540]') do
+ assert_raise(FrozenError, '[Bug #6540]') do
r.__send__(:initialize, r)
end
end
@@ -500,7 +516,7 @@ END
r = Random.new(0)
d = r.__send__(:marshal_dump)
r.freeze
- assert_raise(RuntimeError, '[Bug #6540]') do
+ assert_raise(FrozenError, '[Bug #6540]') do
r.__send__(:marshal_load, d)
end
end
@@ -524,4 +540,48 @@ 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 6afa0ea565..da883767bc 100644
--- a/test/ruby/test_range.rb
+++ b/test/ruby/test_range.rb
@@ -1,10 +1,30 @@
+# frozen_string_literal: false
require 'test/unit'
require 'delegate'
require 'timeout'
require 'bigdecimal'
-require_relative 'envutil'
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") }
+
+ 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)
@@ -60,6 +80,9 @@ class TestRange < Test::Unit::TestCase
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))
end
def test_max
@@ -77,6 +100,9 @@ class TestRange < Test::Unit::TestCase
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))
end
def test_initialize_twice
@@ -92,6 +118,16 @@ 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)))
+ s = Marshal.dump(r)
+ s.sub!(/endi./n, 'end0')
+ assert_raise(ArgumentError) {Marshal.load(s)}
+ end
+
def test_bad_value
assert_raise(ArgumentError) { (1 .. :a) }
end
@@ -126,7 +162,10 @@ class TestRange < Test::Unit::TestCase
end
def test_hash
- assert_kind_of(Fixnum, (0..1).hash)
+ 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_kind_of(String, (0..1).hash.to_s)
end
def test_step
@@ -246,6 +285,7 @@ class TestRange < Test::Unit::TestCase
def test_begin_end
assert_equal(0, (0..1).begin)
assert_equal(1, (0..1).end)
+ assert_equal(1, (0...1).end)
end
def test_first_last
@@ -260,7 +300,10 @@ class TestRange < Test::Unit::TestCase
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)
end
def test_to_s
@@ -296,6 +339,30 @@ class TestRange < Test::Unit::TestCase
}
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_include
assert_include("a".."z", "c")
assert_not_include("a".."z", "5")
@@ -321,12 +388,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])
@@ -391,6 +461,10 @@ class TestRange < Test::Unit::TestCase
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
@@ -475,20 +549,22 @@ class TestRange < Test::Unit::TestCase
assert_in_delta(7.0, (0.0..10).bsearch {|x| 7.0 - x })
end
- def check_bsearch_values(range, search)
+ def check_bsearch_values(range, search, a)
from, to = range.begin, range.end
cmp = range.exclude_end? ? :< : :<=
+ r = nil
- # (0) trivial test
- r = Range.new(to, from, range.exclude_end?).bsearch do |x|
- fail "#{to}, #{from}, #{range.exclude_end?}, #{x}"
- end
- assert_equal nil, r
+ 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
+ r = (to...to).bsearch do
+ fail
+ end
+ assert_nil r
end
- assert_equal nil, r
# prepare for others
yielded = []
@@ -497,46 +573,53 @@ class TestRange < Test::Unit::TestCase
val >= search
end
- # (1) log test
- 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
-
- # (2) coverage test
- expect = if search < from
- from
- elsif search.send(cmp, to)
- search
- else
- nil
- end
- assert_equal expect, r
-
- # (3) uniqueness test
- assert_equal nil, yielded.uniq!
-
- # (4) end of range test
- 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
+ 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
- # start of range test
- if search <= from
- assert_include yielded, from
- assert_equal from, r
+ 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
- # (5) out of range test
- yielded.each do |val|
- assert_operator from, :<=, val
- assert_send [val, cmp, to]
+ 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
@@ -544,10 +627,12 @@ class TestRange < Test::Unit::TestCase
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]
- [ints, floats].each do |values|
- values.combination(2).to_a.product(values).each do |(from, to), search|
- check_bsearch_values(from..to, search)
- check_bsearch_values(from...to, search)
+ 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
@@ -567,17 +652,6 @@ class TestRange < Test::Unit::TestCase
assert_raise(TypeError) { ("a".."z").bsearch {} }
end
- def test_bsearch_with_mathn
- assert_separately ['-r', 'mathn'], %q{
- msg = '[ruby-core:25740]'
- answer = (1..(1 << 100)).bsearch{|x|
- assert_predicate(x, :integer?, msg)
- x >= 42
- }
- assert_equal(42, answer, msg)
- }
- end
-
def test_each_no_blockarg
a = "a"
def a.upto(x, e, &b)
diff --git a/test/ruby/test_rational.rb b/test/ruby/test_rational.rb
index 67f895abc6..2152486742 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,8 +110,15 @@ 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, 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_raise(TypeError){Rational(Object.new)}
assert_raise(ArgumentError){Rational()}
assert_raise(ArgumentError){Rational(1,2,3)}
@@ -178,57 +166,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?)
-=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_predicate(Rational(0), :zero?)
+ assert_predicate(Rational(0,1), :zero?)
+ assert_not_predicate(Rational(1,1), :zero?)
+
+ assert_nil(Rational(0).nonzero?)
+ assert_nil(Rational(0,1).nonzero?)
assert_equal(Rational(1,1), Rational(1,1).nonzero?)
end
@@ -248,12 +194,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
@@ -341,15 +281,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
@@ -376,15 +314,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
@@ -411,54 +347,15 @@ 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)
+ 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
+ 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
-=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(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
- end
-=end
-
def test_remainder
c = Rational(1,2)
c2 = Rational(2,3)
@@ -483,54 +380,15 @@ 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)
+ 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
+ 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
-=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))
-
- 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
- end
-=end
-
def test_quo
c = Rational(1,2)
c2 = Rational(2,3)
@@ -578,50 +436,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)
@@ -663,9 +509,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
@@ -698,23 +542,23 @@ 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_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
@@ -728,7 +572,7 @@ class Rational_Test < Test::Unit::TestCase
end
class ObjectX
- def + (x) Rational(1) end
+ def +(x) Rational(1) end
alias - +
alias * +
alias / +
@@ -747,42 +591,32 @@ class Rational_Test < Test::Unit::TestCase
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)
- 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
@@ -792,13 +626,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)
@@ -815,21 +644,22 @@ 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)
- c.freeze
assert_predicate(c, :frozen?)
result = c.marshal_load([2,3]) rescue :fail
assert_equal(:fail, result, bug3656)
@@ -841,132 +671,113 @@ class Rational_Test < Test::Unit::TestCase
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 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(5e1), '5e1'.to_r)
- assert_equal(Rational(-5e2), '-5e2'.to_r)
- assert_equal(Rational(5e3,3), '5e003/3'.to_r)
- assert_equal(Rational(-5e4,3), '-5e004/3'.to_r)
-# assert_equal(Rational(5e1,-3), '5e1/-3'.to_r)
-# assert_equal(Rational(-5e2,-3), '-5e2/-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(5e1), Rational('5e1'))
- assert_equal(Rational(-5e2), Rational('-5e2'))
- assert_equal(Rational(5e3,3), Rational('5e003/3'))
- assert_equal(Rational(-5e4,3), Rational('-5e004/3'))
-# assert_equal(Rational(5e1,-3), Rational('5e1/-3'))
-# assert_equal(Rational(-5e2,-3), Rational('-5e2/-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')}
+ 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
-=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
+ 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
+
+ def test_parse_zero_denominator
+ assert_raise(ZeroDivisionError) {"1/0".to_r}
+ assert_raise(ZeroDivisionError) {Rational("1/0")}
end
-=end
def test_to_i
assert_equal(1, Rational(3,2).to_i)
@@ -979,15 +790,8 @@ class Rational_Test < Test::Unit::TestCase
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
@@ -1007,13 +811,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}
@@ -1063,12 +861,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}
@@ -1095,9 +888,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)
@@ -1109,13 +912,6 @@ 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))
@@ -1127,6 +923,13 @@ class Rational_Test < Test::Unit::TestCase
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
@@ -1135,12 +938,9 @@ 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
@@ -1149,7 +949,7 @@ class Rational_Test < Test::Unit::TestCase
one = Rational( 1, 1)
assert_eql one, one ** -big , bug5715
assert_eql one, (-one) ** -big , bug5715
- assert_eql -one, (-one) ** -(big+1) , bug5715
+ assert_eql (-one), (-one) ** -(big+1) , bug5715
assert_equal Complex, ((-one) ** Rational(1,3)).class
end
@@ -1163,7 +963,34 @@ class Rational_Test < Test::Unit::TestCase
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
index b116dbbf62..7725820038 100644
--- a/test/ruby/test_refinement.rb
+++ b/test/ruby/test_refinement.rb
@@ -1,14 +1,11 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
-
-# to supress warnings for future calls of Module#refine
-EnvUtil.suppress_warning do
- Module.new {
- refine(Object) {}
- }
-end
class TestRefinement < Test::Unit::TestCase
+ module Sandbox
+ BINDING = binding
+ end
+
class Foo
def x
return "Foo#x"
@@ -73,10 +70,14 @@ class TestRefinement < Test::Unit::TestCase
end
end
- eval <<-EOF, TOPLEVEL_BINDING
+ class FooExtClient
using TestRefinement::FooExt
- class TestRefinement::FooExtClient
+ begin
+ def self.map_x_on(foo)
+ [foo].map(&:x)[0]
+ end
+
def self.invoke_x_on(foo)
return foo.x
end
@@ -100,14 +101,18 @@ class TestRefinement < Test::Unit::TestCase
def self.invoke_call_x_on(foo)
return foo.call_x
end
+
+ def self.return_proc(&block)
+ block
+ end
end
- EOF
+ end
- eval <<-EOF, TOPLEVEL_BINDING
+ class TestRefinement::FooExtClient2
using TestRefinement::FooExt
using TestRefinement::FooExt2
- class TestRefinement::FooExtClient2
+ begin
def self.invoke_y_on(foo)
return foo.y
end
@@ -116,7 +121,7 @@ class TestRefinement < Test::Unit::TestCase
return foo.a
end
end
- EOF
+ end
def test_override
foo = Foo.new
@@ -170,10 +175,10 @@ class TestRefinement < Test::Unit::TestCase
end
end
- def test_send_should_not_use_refinements
+ def test_send_should_use_refinements
foo = Foo.new
assert_raise(NoMethodError) { foo.send(:z) }
- assert_raise(NoMethodError) { FooExtClient.send_z_on(foo) }
+ assert_equal("FooExt#z", FooExtClient.send_z_on(foo))
assert_raise(NoMethodError) { foo.send(:z) }
assert_equal(true, RespondTo::Sub.new.respond_to?(:foo))
@@ -231,20 +236,20 @@ class TestRefinement < Test::Unit::TestCase
assert_equal("Foo#x", foo.x)
end
- module FixnumSlashExt
- refine Fixnum do
+ 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(FixnumSlashExt, "1 / 2"))
+ assert_equal(Rational(1, 2), eval_using(IntegerSlashExt, "1 / 2"))
assert_equal(0, 1 / 2)
end
- module FixnumPlusExt
- refine Fixnum do
+ module IntegerPlusExt
+ refine Integer do
def self.method_added(*args); end
def +(other) "overridden" end
end
@@ -252,14 +257,14 @@ class TestRefinement < Test::Unit::TestCase
def test_override_builtin_method_with_method_added
assert_equal(3, 1 + 2)
- assert_equal("overridden", eval_using(FixnumPlusExt, "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
- m = Module.new {
+ Module.new {
result = refine(Object) {
mod = self
}
@@ -268,10 +273,10 @@ class TestRefinement < Test::Unit::TestCase
end
module RefineSameClass
- REFINEMENT1 = refine(Fixnum) {
+ REFINEMENT1 = refine(Integer) {
def foo; return "foo" end
}
- REFINEMENT2 = refine(Fixnum) {
+ REFINEMENT2 = refine(Integer) {
def bar; return "bar" end
}
REFINEMENT3 = refine(String) {
@@ -286,15 +291,15 @@ class TestRefinement < Test::Unit::TestCase
assert_not_equal(RefineSameClass::REFINEMENT1, RefineSameClass::REFINEMENT3)
end
- module FixnumFooExt
- refine Fixnum do
+ module IntegerFooExt
+ refine Integer do
def foo; "foo"; end
end
end
def test_respond_to_should_not_use_refinements
assert_equal(false, 1.respond_to?(:foo))
- assert_equal(false, eval_using(FixnumFooExt, "1.respond_to?(:foo)"))
+ assert_equal(false, eval_using(IntegerFooExt, "1.respond_to?(:foo)"))
end
module StringCmpExt
@@ -348,19 +353,66 @@ class TestRefinement < Test::Unit::TestCase
assert_equal([:c, :m1, :m2], x)
end
- def test_refine_module
- m1 = Module.new
- assert_raise(TypeError) do
- Module.new {
- refine m1 do
+ 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
- :m2
+ "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 {
@@ -385,7 +437,7 @@ class TestRefinement < Test::Unit::TestCase
def test_refine_in_class
assert_raise(NoMethodError) do
Class.new {
- refine Fixnum do
+ refine Integer do
def foo
"c"
end
@@ -419,7 +471,7 @@ class TestRefinement < Test::Unit::TestCase
def test_main_using_is_private
assert_raise(NoMethodError) do
- eval("self.using Module.new", TOPLEVEL_BINDING)
+ eval("self.using Module.new", Sandbox::BINDING)
end
end
@@ -433,9 +485,8 @@ class TestRefinement < Test::Unit::TestCase
end
def test_module_using_class
- c = Class.new
assert_raise(TypeError) do
- eval("using TestRefinement::UsingClass", TOPLEVEL_BINDING)
+ eval("using TestRefinement::UsingClass", Sandbox::BINDING)
end
end
@@ -450,13 +501,13 @@ class TestRefinement < Test::Unit::TestCase
module Inspect
module M
- Fixnum = refine(Fixnum) {}
+ Integer = refine(Integer) {}
end
end
def test_inspect
- assert_equal("#<refinement:Fixnum@TestRefinement::Inspect::M>",
- Inspect::M::Fixnum.inspect)
+ assert_equal("#<refinement:Integer@TestRefinement::Inspect::M>",
+ Inspect::M::Integer.inspect)
end
def test_using_method_cache
@@ -507,8 +558,10 @@ class TestRefinement < Test::Unit::TestCase
end
class C
- def foo
- "redefined"
+ EnvUtil.suppress_warning do
+ def foo
+ "redefined"
+ end
end
end
end
@@ -596,7 +649,7 @@ class TestRefinement < Test::Unit::TestCase
def test_using_in_module
assert_raise(RuntimeError) do
- eval(<<-EOF, TOPLEVEL_BINDING)
+ eval(<<-EOF, Sandbox::BINDING)
$main = self
module M
end
@@ -609,14 +662,16 @@ class TestRefinement < Test::Unit::TestCase
def test_using_in_method
assert_raise(RuntimeError) do
- eval(<<-EOF, TOPLEVEL_BINDING)
+ eval(<<-EOF, Sandbox::BINDING)
$main = self
module M
end
- def call_using_in_method
- $main.send(:using, M)
+ class C
+ def call_using_in_method
+ $main.send(:using, M)
+ end
end
- call_using_in_method
+ C.new.call_using_in_method
EOF
end
end
@@ -657,7 +712,7 @@ class TestRefinement < Test::Unit::TestCase
end
end
- eval <<-EOF, TOPLEVEL_BINDING
+ eval <<-EOF, Sandbox::BINDING
using TestRefinement::IncludeIntoRefinement::M
module TestRefinement::IncludeIntoRefinement::User
@@ -720,7 +775,7 @@ class TestRefinement < Test::Unit::TestCase
end
end
- eval <<-EOF, TOPLEVEL_BINDING
+ eval <<-EOF, Sandbox::BINDING
using TestRefinement::PrependIntoRefinement::M
module TestRefinement::PrependIntoRefinement::User
@@ -747,6 +802,7 @@ class TestRefinement < Test::Unit::TestCase
PrependIntoRefinement::User.invoke_baz_on(x))
end
+ PrependAfterRefine_CODE = <<-EOC
module PrependAfterRefine
class C
def foo
@@ -780,6 +836,21 @@ class TestRefinement < Test::Unit::TestCase
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,
@@ -866,7 +937,7 @@ class TestRefinement < Test::Unit::TestCase
def test_module_using_invalid_self
assert_raise(RuntimeError) do
- eval <<-EOF, TOPLEVEL_BINDING
+ eval <<-EOF, Sandbox::BINDING
module TestRefinement::TestModuleUsingInvalidSelf
Module.new.send(:using, TestRefinement::FooExt)
end
@@ -945,7 +1016,7 @@ class TestRefinement < Test::Unit::TestCase
end
def test_eval_with_binding_scoping
- assert_in_out_err([], <<-INPUT, ["HELLO WORLD", "dlrow olleh", "HELLO WORLD"], [])
+ assert_in_out_err([], <<-INPUT, ["HELLO WORLD", "dlrow olleh", "dlrow olleh"], [])
module M
refine String do
def upcase
@@ -955,8 +1026,9 @@ class TestRefinement < Test::Unit::TestCase
end
puts "hello world".upcase
- puts eval(%{using M; "hello world".upcase}, TOPLEVEL_BINDING)
- puts eval(%{"hello world".upcase}, TOPLEVEL_BINDING)
+ b = binding
+ puts eval(%{using M; "hello world".upcase}, b)
+ puts eval(%{"hello world".upcase}, b)
INPUT
end
@@ -1219,6 +1291,24 @@ class TestRefinement < Test::Unit::TestCase
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]'
@@ -1371,6 +1461,38 @@ class TestRefinement < Test::Unit::TestCase
: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)
@@ -1426,9 +1548,567 @@ class TestRefinement < Test::Unit::TestCase
}
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
+
+ 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}", TOPLEVEL_BINDING)
+ eval("using #{mod}; #{s}", Sandbox::BINDING)
end
end
diff --git a/test/ruby/test_regexp.rb b/test/ruby/test_regexp.rb
index 8223a143b3..fe271dc3d7 100644
--- a/test/ruby/test_regexp.rb
+++ b/test/ruby/test_regexp.rb
@@ -1,6 +1,6 @@
# coding: US-ASCII
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestRegexp < Test::Unit::TestCase
def setup
@@ -74,6 +74,12 @@ class TestRegexp < Test::Unit::TestCase
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
assert_equal :ok, begin
Regexp.union(
@@ -111,8 +117,10 @@ 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)
@@ -142,10 +150,16 @@ 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
@@ -173,8 +187,23 @@ class TestRegexp < Test::Unit::TestCase
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"') }
@@ -186,6 +215,15 @@ class TestRegexp < Test::Unit::TestCase
assert_not_include(local_variables, :nil, "[ruby-dev:32675]")
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
r = /./
m = r.match("a")
@@ -355,15 +393,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
@@ -389,6 +458,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
@@ -476,6 +548,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
@@ -490,6 +586,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
@@ -544,6 +644,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 = $~
@@ -552,6 +662,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))
@@ -579,20 +716,7 @@ 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 = 3
- /foo/.match("foo")
- end.value
- assert_predicate(m, :tainted?)
- assert_nothing_raised('[ruby-core:26137]') {
- m = proc {$SAFE = 3; %r"#{ }"o}.call
- }
- assert_predicate(m, :tainted?)
- end
-
- def check(re, ss, fs = [], msg = nil)
+ 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|
@@ -604,10 +728,12 @@ class TestRegexp < Test::Unit::TestCase
fs = [fs] unless fs.is_a?(Array)
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(/\*\+\?\{\}\|\(\)\<\>\`\'/, "*+?{}|()<>`'")
@@ -814,6 +940,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 308}"
+ assert_match /\A\X\X\z/, "\u{a 308}"
+ assert_match /\A\X\X\z/, "\u{d 308}"
+ 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))
@@ -839,6 +988,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 }
@@ -918,15 +1068,22 @@ class TestRegexp < Test::Unit::TestCase
assert_no_match(/^\p{age=1.1}$/u, "\u2754")
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
@@ -977,7 +1134,7 @@ class TestRegexp < Test::Unit::TestCase
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 Fixnum into String', bug7539) {
+ assert_raise_with_message(TypeError, 'no implicit conversion of Integer into String', bug7539) {
Regexp.quote(42)
}
end
@@ -988,6 +1145,9 @@ class TestRegexp < Test::Unit::TestCase
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
@@ -1009,8 +1169,9 @@ class TestRegexp < Test::Unit::TestCase
assert_equal(/0/, pr1.call(0))
assert_equal(/0/, pr1.call(1))
assert_equal(/0/, pr1.call(2))
+ end
- # recursive
+ def test_once_recursive
pr2 = proc{|i|
if i > 0
/#{pr2.call(i-1).to_s}#{i}/
@@ -1019,9 +1180,10 @@ class TestRegexp < Test::Unit::TestCase
end
}
assert_equal(/(?-mix:(?-mix:(?-mix:)1)2)3/, pr2.call(3))
+ end
- # multi-thread
- m = Mutex.new
+ def test_once_multithread
+ m = Thread::Mutex.new
pr3 = proc{|i|
/#{m.unlock; sleep 0.5; i}/o
}
@@ -1031,8 +1193,9 @@ class TestRegexp < Test::Unit::TestCase
th2 = Thread.new{m.lock; ary << pr3.call(n+=1)}
th1.join; th2.join
assert_equal([/1/, /1/], ary)
+ end
- # escape
+ def test_once_escape
pr4 = proc{|i|
catch(:xyzzy){
/#{throw :xyzzy, i}/o =~ ""
@@ -1056,6 +1219,36 @@ class TestRegexp < Test::Unit::TestCase
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)
diff --git a/test/ruby/test_require.rb b/test/ruby/test_require.rb
index 562f0c13ea..28cf686a26 100644
--- a/test/ruby/test_require.rb
+++ b/test/ruby/test_require.rb
@@ -1,7 +1,7 @@
+# frozen_string_literal: false
require 'test/unit'
require 'tempfile'
-require_relative 'envutil'
require 'tmpdir'
class TestRequire < Test::Unit::TestCase
@@ -18,22 +18,24 @@ class TestRequire < Test::Unit::TestCase
t.puts "dummy"
t.close
- assert_separately([], <<-INPUT)
+ assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}")
+ begin;
$:.replace([IO::NULL])
assert_raise(LoadError) do
require \"#{ t.path }\"
end
- INPUT
+ end;
}
end
def test_require_too_long_filename
- assert_separately(["RUBYOPT"=>nil], <<-INPUT)
+ assert_separately(["RUBYOPT"=>nil], "#{<<~"begin;"}\n#{<<~"end;"}")
+ begin;
$:.replace([IO::NULL])
assert_raise(LoadError) do
require '#{ "foo/" * 10000 }foo'
end
- INPUT
+ end;
begin
assert_in_out_err(["-S", "-w", "foo/" * 1024 + "foo"], "") do |r, e|
@@ -60,6 +62,12 @@ class TestRequire < Test::Unit::TestCase
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
@@ -67,6 +75,12 @@ class TestRequire < Test::Unit::TestCase
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
@@ -74,6 +88,12 @@ class TestRequire < Test::Unit::TestCase
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)
@@ -85,9 +105,18 @@ class TestRequire < Test::Unit::TestCase
end
end
- def assert_require_nonascii_path(encoding, bug)
+ SECURITY_WARNING =
+ if /mswin|mingw/ =~ RUBY_PLATFORM
+ nil
+ else
+ proc do |require_path|
+ $SAFE = 1
+ require(require_path)
+ end
+ end
+
+ def prepare_require_path(dir, encoding)
Dir.mktmpdir {|tmp|
- dir = "\u3042" * 5
begin
require_path = File.join(tmp, dir, 'foo.rb').encode(encoding)
rescue
@@ -98,6 +127,17 @@ class TestRequire < Test::Unit::TestCase
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}"
@@ -107,13 +147,35 @@ class TestRequire < Test::Unit::TestCase
assert_equal(self.class.ospath_encoding(require_path), $:.last.encoding, '[Bug #8753]')
assert(!require(require_path), bug)
}
- ensure
- $:.replace(load_path)
- $".replace(features)
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
@@ -160,13 +222,20 @@ class TestRequire < Test::Unit::TestCase
end
def test_require_with_unc
- ruby = File.expand_path(EnvUtil.rubybin).sub(/\A(\w):/, '//127.0.0.1/\1$/')
- skip "local drive #$1: is not shared" unless File.exist?(ruby)
- pid = nil
- assert_nothing_raised {pid = spawn(ruby, "-rabbrev", "-e0")}
- ret, status = Process.wait2(pid)
- assert_equal(pid, ret)
- assert_predicate(status, :success?)
+ 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
@@ -182,6 +251,26 @@ class TestRequire < Test::Unit::TestCase
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"
@@ -229,7 +318,7 @@ class TestRequire < Test::Unit::TestCase
assert_separately([], <<-INPUT)
module Zlib; end
class Zlib::Error; end
- assert_raise(NameError) do
+ assert_raise(TypeError) do
require 'zlib'
end
INPUT
@@ -293,7 +382,8 @@ class TestRequire < Test::Unit::TestCase
}
end
- def test_load2 # [ruby-core:25039]
+ 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"
@@ -301,7 +391,7 @@ class TestRequire < Test::Unit::TestCase
t.puts "end"
t.close
- assert_in_out_err([], <<-INPUT, %w("hello"), [])
+ assert_in_out_err([], <<-INPUT, %w("hello"), [], bug1982)
load(#{ t.path.dump }, true)
INPUT
}
@@ -355,13 +445,6 @@ class TestRequire < Test::Unit::TestCase
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
@@ -403,7 +486,7 @@ class TestRequire < Test::Unit::TestCase
File.symlink("../a/tst.rb", "b/tst.rb")
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
}
@@ -424,7 +507,8 @@ class TestRequire < Test::Unit::TestCase
verbose = $VERBOSE
Tempfile.create(%w"bug5754 .rb") {|tmp|
path = tmp.path
- tmp.print %{\
+ tmp.print "#{<<~"begin;"}\n#{<<~"end;"}"
+ begin;
th = Thread.current
t = th[:t]
scratch = th[:scratch]
@@ -436,7 +520,7 @@ class TestRequire < Test::Unit::TestCase
else
scratch << :post
end
- }
+ end;
tmp.close
class << (output = "")
@@ -516,7 +600,8 @@ class TestRequire < Test::Unit::TestCase
open(File.join("b", "bar.rb"), "w") {|f|
f.puts "p :ok"
}
- assert_in_out_err([], <<-INPUT, %w(:ok), [], bug7158)
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
+ begin;
$:.replace([IO::NULL])
$: << "."
Dir.chdir("a")
@@ -525,7 +610,7 @@ class TestRequire < Test::Unit::TestCase
p :ng unless require "bar"
Dir.chdir("..")
p :ng if require "b/bar"
- INPUT
+ end;
}
}
end
@@ -535,7 +620,8 @@ class TestRequire < Test::Unit::TestCase
Dir.mktmpdir {|tmp|
Dir.chdir(tmp) {
open("foo.rb", "w") {}
- assert_in_out_err([], <<-INPUT, %w(:ok), [], bug7158)
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7158)
+ begin;
$:.replace([IO::NULL])
a = Object.new
def a.to_str
@@ -545,7 +631,7 @@ class TestRequire < Test::Unit::TestCase
require "foo"
last_path = $:.pop
p :ok if last_path == a && last_path.class == Object
- INPUT
+ end;
}
}
end
@@ -557,14 +643,15 @@ class TestRequire < Test::Unit::TestCase
open("foo.rb", "w") {}
Dir.mkdir("a")
open(File.join("a", "bar.rb"), "w") {}
- assert_in_out_err([], <<-INPUT, %w(:ok), [], bug7158)
+ 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"
- INPUT
+ end;
}
}
end
@@ -574,7 +661,8 @@ class TestRequire < Test::Unit::TestCase
Dir.mktmpdir {|tmp|
Dir.chdir(tmp) {
open("foo.rb", "w") {}
- assert_in_out_err(["RUBYOPT"=>nil], <<-INPUT, %w(:ok), [], bug7158)
+ 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
@@ -584,13 +672,14 @@ class TestRequire < Test::Unit::TestCase
begin
require "foo"
p [:ng, $LOAD_PATH, ENV['RUBYLIB']]
- rescue LoadError
+ rescue LoadError => e
+ raise unless e.path == "foo"
end
def a.to_path
"#{tmp}"
end
p :ok if require "foo"
- INPUT
+ end;
}
}
end
@@ -600,7 +689,8 @@ class TestRequire < Test::Unit::TestCase
Dir.mktmpdir {|tmp|
Dir.chdir(tmp) {
open("foo.rb", "w") {}
- assert_in_out_err(["RUBYOPT"=>nil], <<-INPUT, %w(:ok), [], bug7158)
+ 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
@@ -610,13 +700,14 @@ class TestRequire < Test::Unit::TestCase
begin
require "foo"
p [:ng, $LOAD_PATH, ENV['RUBYLIB']]
- rescue LoadError
+ rescue LoadError => e
+ raise unless e.path == "foo"
end
def a.to_str
"#{tmp}"
end
p :ok if require "foo"
- INPUT
+ end;
}
}
end
@@ -628,7 +719,8 @@ class TestRequire < Test::Unit::TestCase
open("foo.rb", "w") {}
Dir.mkdir("a")
open(File.join("a", "bar.rb"), "w") {}
- assert_in_out_err([], <<-INPUT, %w(:ok), [], bug7383)
+ assert_in_out_err(['--disable-gems'], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7383)
+ begin;
$:.replace([IO::NULL])
$:.#{add} "#{tmp}"
$:.#{add} "#{tmp}/a"
@@ -637,10 +729,14 @@ class TestRequire < Test::Unit::TestCase
# Expanded load path cache should be rebuilt.
begin
require "bar"
- rescue LoadError
- p :ok
+ rescue LoadError => e
+ if e.path == "bar"
+ p :ok
+ else
+ raise
+ end
end
- INPUT
+ end;
}
}
end
@@ -658,10 +754,11 @@ class TestRequire < Test::Unit::TestCase
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], <<-INPUT, %w([:lib] 2), [], bug7536)
+ 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
- INPUT
+ end;
}
}
end
@@ -670,35 +767,132 @@ class TestRequire < Test::Unit::TestCase
bug7530 = '[ruby-core:50645]'
Tempfile.create(%w'bug-7530- .rb') {|script|
script.close
- assert_in_out_err([{"RUBYOPT" => nil}, "-", script.path], <<-INPUT, %w(:ok), [], bug7530)
+ assert_in_out_err([{"RUBYOPT" => nil}, "-", script.path], "#{<<~"begin;"}\n#{<<~"end;"}", %w(:ok), [], bug7530, timeout: 60)
+ begin;
PATH = ARGV.shift
- THREADS = 2
+ THREADS = 4
ITERATIONS_PER_THREAD = 1000
THREADS.times.map {
Thread.new do
ITERATIONS_PER_THREAD.times do
require PATH
- $".pop
+ $".delete_if {|p| Regexp.new(PATH) =~ p}
end
end
}.each(&:join)
p :ok
- INPUT
+ end;
}
end
- def test_loading_fifo_threading
+ 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], <<-END, timeout: 3)
- th = Thread.current
- Thread.start {begin sleep(0.001) end until th.stop?; th.raise(IOError)}
- assert_raise(IOError) {load(ARGV[0])}
- END
+ 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;
}
- rescue Errno::ENOENT
- end unless /mswin|mingw/ =~ RUBY_PLATFORM
+ 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 b6be5011cd..54213c4698 100644
--- a/test/ruby/test_rubyoptions.rb
+++ b/test/ruby/test_rubyoptions.rb
@@ -4,8 +4,6 @@ require 'test/unit'
require 'tmpdir'
require 'tempfile'
-require_relative 'envutil'
-
class TestRubyOptions < Test::Unit::TestCase
def write_file(filename, content)
File.open(filename, "w") {|f|
@@ -50,7 +48,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',
@@ -59,6 +57,7 @@ class TestRubyOptions < Test::Unit::TestCase
end
end
+
def test_warning
save_rubyopt = ENV['RUBYOPT']
ENV['RUBYOPT'] = nil
@@ -66,6 +65,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
@@ -85,10 +85,21 @@ class TestRubyOptions < Test::Unit::TestCase
"", %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
+
def test_verbose
assert_in_out_err(["-vve", ""]) 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])
+ assert_equal(RUBY_DESCRIPTION, r[0])
assert_equal([], e)
end
@@ -120,6 +131,8 @@ 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"], [])
end
def test_kanji
@@ -141,8 +154,8 @@ class TestRubyOptions < Test::Unit::TestCase
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])
+ assert_equal(RUBY_DESCRIPTION, r[0])
assert_equal([], e)
end
end
@@ -172,6 +185,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
@@ -193,13 +210,13 @@ class TestRubyOptions < Test::Unit::TestCase
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
@@ -209,10 +226,12 @@ class TestRubyOptions < Test::Unit::TestCase
assert_in_out_err(%w(--encoding test_ruby_test_rubyoptions_foobarbazqux), "", [],
/unknown encoding name - test_ruby_test_rubyoptions_foobarbazqux \(RuntimeError\)/)
- if /mswin|mingw/ =~ RUBY_PLATFORM &&
+ 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/
@@ -258,6 +277,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
@@ -277,13 +300,15 @@ class TestRubyOptions < Test::Unit::TestCase
@verbose = $VERBOSE
$VERBOSE = nil
- ENV['PATH'] = File.dirname(t.path)
-
- 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
@@ -307,16 +332,36 @@ 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([{'RUBYOPT' => nil}], "#!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)
- end
+ 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
def test_sflag
@@ -372,45 +417,78 @@ class TestRubyOptions < Test::Unit::TestCase
end
def test_indentation_check
- Tempfile.create(["test_ruby_test_rubyoption", ".rb"]) {|t|
- t.puts "begin"
- t.puts " end"
- t.flush
- 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)
+ 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"], ["-> {", "}"],
+ ].each do
+ |b, e = 'end'|
+ src = ["#{b}\n", " #{e}\n"]
+ k = b[/\A\S+/]
+
+ 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
- t.rewind
- t.puts "# -*- warn-indent: false -*-"
- t.puts "begin"
- t.puts " end"
- t.flush
- assert_in_out_err(["-w", t.path], "", [], [], '[ruby-core:25442]')
+ 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
- err = ["#{t.path}:4: warning: mismatched indentations at 'end' with 'begin' at 3"]
- t.rewind
- t.puts "# -*- warn-indent: false -*-"
- t.puts "# -*- warn-indent: true -*-"
- t.puts "begin"
- t.puts " end"
- t.flush
- assert_in_out_err(["-w", t.path], "", [], err, '[ruby-core:25442]')
+ 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
- err = ["#{t.path}:4: warning: mismatched indentations at 'end' with 'begin' at 2"]
- t.rewind
- t.puts "# -*- warn-indent: true -*-"
- t.puts "begin"
- t.puts "# -*- warn-indent: false -*-"
- t.puts " end"
- t.flush
- assert_in_out_err(["-w", t.path], "", [], [], '[ruby-core:25442]')
- }
+ 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 = EnvUtil.rubybin.dup
- rubybin.gsub!(%r(/), '\\') if /mswin|mingw/ =~ RUBY_PLATFORM
+ 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]'
@@ -437,10 +515,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'
@@ -458,8 +540,13 @@ class TestRubyOptions < Test::Unit::TestCase
}
end
+ if /linux|freebsd|netbsd|openbsd|darwin/ =~ RUBY_PLATFORM
+ PSCMD = EnvUtil.find_executable("ps", "-o", "command", "-p", $$.to_s) {|out| /ruby/=~out}
+ PSCMD.pop if PSCMD
+ end
+
def test_set_program_name
- skip "platform dependent feature" if /linux|freebsd|netbsd|openbsd|darwin/ !~ RUBY_PLATFORM
+ 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")
@@ -468,7 +555,7 @@ class TestRubyOptions < Test::Unit::TestCase
ps = nil
10.times do
sleep 0.1
- ps = `ps -p #{pid} -o command`
+ ps = `#{PSCMD.join(' ')} #{pid}`
break if /hello world/ =~ ps
end
assert_match(/hello world/, ps)
@@ -478,7 +565,14 @@ class TestRubyOptions < Test::Unit::TestCase
end
def test_setproctitle
- skip "platform dependent feature" if /linux|freebsd|netbsd|openbsd|darwin/ !~ RUBY_PLATFORM
+ 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")
@@ -487,7 +581,7 @@ class TestRubyOptions < Test::Unit::TestCase
ps = nil
10.times do
sleep 0.1
- ps = `ps -p #{pid} -o command`
+ ps = `#{PSCMD.join(' ')} #{pid}`
break if /hello world/ =~ ps
end
assert_match(/hello world/, ps)
@@ -499,74 +593,86 @@ class TestRubyOptions < Test::Unit::TestCase
module SEGVTest
opts = {}
if /mswin|mingw/ =~ RUBY_PLATFORM
- additional = '[\s\w\.\']*'
+ additional = /[\s\w\.\']*/
else
opts[:rlimit_core] = 0
- additional = ""
+ additional = nil
end
ExecOptions = opts.freeze
- ExpectedStderr =
- %r(\A
- -e:(?:1:)?\s\[BUG\]\sSegmentation\sfault.*\n
- #{ Regexp.quote(RUBY_DESCRIPTION) }\n\n
- (?:--\s(?:.+\n)*\n)?
- --\sControl\sframe\sinformation\s-+\n
- (?:c:.*\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
- )?
- (?m:.*)
- \[NOTE\]\n
- You\smay\shave\sencountered\sa\sbug\sin\sthe\sRuby\sinterpreter\sor\sextension\slibraries.\n
- Bug\sreports\sare\swelcome.\n
- (?:.*\n)?
- For\sdetails:\shttp:\/\/.*\.ruby-lang\.org/.*\n
- \n
- (?:#{additional})
- \z
- )x
+ ExpectedStderrList = [
+ %r(
+ -e:(?:1:)?\s\[BUG\]\sSegmentation\sfault.*\n
+ )x,
+ %r(
+ #{ Regexp.quote(RUBY_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,
+ %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:\shttp:\/\/.*\.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
- opts = SEGVTest::ExecOptions.dup
- expected_stderr = SEGVTest::ExpectedStderr
-
- assert_in_out_err(["--disable-gems", "-e", "Process.kill :SEGV, $$"], "", [], expected_stderr, nil, opts)
+ assert_segv(["--disable-gems", "-e", "Process.kill :SEGV, $$"])
end
def test_segv_loaded_features
- opts = SEGVTest::ExecOptions.dup
-
bug7402 = '[ruby-core:49573]'
- status = assert_in_out_err(['-e', 'class Bogus; def to_str; exit true; end; end',
- '-e', '$".clear',
- '-e', '$".unshift Bogus.new',
- '-e', '(p $"; abort) unless $".size == 1',
- '-e', 'Process.kill :SEGV, $$'],
- "", [], /#<Bogus:/,
- nil,
- opts)
+
+ 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
- opts = SEGVTest::ExecOptions.dup
- expected_stderr = SEGVTest::ExpectedStderr
-
bug7597 = '[ruby-dev:46786]'
Tempfile.create(["test_ruby_test_bug7597", ".rb"]) {|t|
t.write "f" * 100
t.flush
- assert_in_out_err(["--disable-gems", "-e", "$0=ARGV[0]; Process.kill :SEGV, $$", t.path],
- "", [], expected_stderr, bug7597, opts)
+ assert_segv(["--disable-gems", "-e", "$0=ARGV[0]; Process.kill :SEGV, $$", t.path], bug7597)
}
end
@@ -665,6 +771,92 @@ class TestRubyOptions < Test::Unit::TestCase
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)
@@ -679,4 +871,126 @@ class TestRubyOptions < Test::Unit::TestCase
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
+ assert_in_out_err(opt, '"foo" << "bar"', [], err)
+ if freeze
+ assert_in_out_err(opt, '"foo#{123}bar" << "bar"', [], err)
+ 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_argv_tainted
+ assert_separately(%w[- arg], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ assert_predicate(ARGV[0], :tainted?, '[ruby-dev:50596] [Bug #14941]')
+ end;
+ end
end
diff --git a/test/ruby/test_rubyvm.rb b/test/ruby/test_rubyvm.rb
index 613cfe2161..7673d8dfbe 100644
--- a/test/ruby/test_rubyvm.rb
+++ b/test/ruby/test_rubyvm.rb
@@ -1,9 +1,10 @@
+# frozen_string_literal: false
require 'test/unit'
class TestRubyVM < Test::Unit::TestCase
def test_stat
assert_kind_of Hash, RubyVM.stat
- assert_kind_of Fixnum, RubyVM.stat[:global_method_state]
+ assert_kind_of Integer, RubyVM.stat[:global_method_state]
RubyVM.stat(stat = {})
assert_not_empty stat
@@ -12,5 +13,6 @@ class TestRubyVM < Test::Unit::TestCase
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_settracefunc.rb b/test/ruby/test_settracefunc.rb
index 2e9caa3e8c..3085c0902a 100644
--- a/test/ruby/test_settracefunc.rb
+++ b/test/ruby/test_settracefunc.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestSetTraceFunc < Test::Unit::TestCase
def setup
@@ -35,9 +35,9 @@ class TestSetTraceFunc < Test::Unit::TestCase
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)
@@ -73,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)
@@ -239,8 +239,6 @@ class TestSetTraceFunc < Test::Unit::TestCase
EOF
assert_equal(["c-return", 1, :set_trace_func, Kernel],
events.shift)
- assert_equal(["line", 4, __method__, self.class],
- events.shift)
assert_equal(["line", 5, __method__, self.class],
events.shift)
assert_equal(["c-call", 5, :raise, Kernel],
@@ -285,14 +283,12 @@ class TestSetTraceFunc < Test::Unit::TestCase
[["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
@@ -355,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|
@@ -467,7 +463,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
EOF
self.class.class_eval{remove_const(:XYZZY)}
ensure
- trace.disable if trace && trace.enabled?
+ trace.disable if trace&.enabled?
end
answer_events = [
@@ -566,7 +562,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
ms = [events1, answer_events].map{|evs|
evs.map{|e|
- "#{e[0]} - #{e[2]}:#{e[1]} id; #{e[4]}"
+ "#{e[0]} - #{e[2]}:#{e[1]} id: #{e[4]}"
}
}
@@ -575,6 +571,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
"#{a} <-> #{b}"
end
}.compact.join("\n")
+
answer_events.zip(events1){|answer, event|
assert_equal answer, event, mesg
}
@@ -640,16 +637,19 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_tracepoint_enable
ary = []
+ args = nil
trace = TracePoint.new(:call){|tp|
next if !target_thread?
ary << tp.method_id
}
foo
- trace.enable{
+ trace.enable{|*a|
+ args = a
foo
}
foo
assert_equal([:foo], ary)
+ assert_equal([], args)
trace = TracePoint.new{}
begin
@@ -664,17 +664,20 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_tracepoint_disable
ary = []
+ args = nil
trace = TracePoint.trace(:call){|tp|
next if !target_thread?
ary << tp.method_id
}
foo
- trace.disable{
+ trace.disable{|*a|
+ args = a
foo
}
foo
trace.disable
assert_equal([:foo, :foo], ary)
+ assert_equal([], args)
trace = TracePoint.new{}
trace.enable{
@@ -769,10 +772,10 @@ class TestSetTraceFunc < Test::Unit::TestCase
# pp events
# expected_events =
[[:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
- [:c_call, :times, Integer, Fixnum, 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, Fixnum, 1],
+ [: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],
@@ -852,6 +855,21 @@ class TestSetTraceFunc < Test::Unit::TestCase
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{
@@ -979,6 +997,27 @@ class TestSetTraceFunc < Test::Unit::TestCase
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){
@@ -1021,7 +1060,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_isolated_raise_in_trace
bug9088 = '[ruby-dev:47793] [Bug #9088]'
- assert_ruby_status([], <<-END, bug9088)
+ assert_in_out_err([], <<-END, [], [], bug9088)
set_trace_func proc {raise rescue nil}
1.times {break}
END
@@ -1030,6 +1069,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_a_call
events = []
TracePoint.new(:a_call){|tp|
+ next if !target_thread?
events << tp.event
}.enable{
1.times{
@@ -1051,6 +1091,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
def test_a_return
events = []
TracePoint.new(:a_return){|tp|
+ next if !target_thread?
events << tp.event
}.enable{
1.times{
@@ -1074,6 +1115,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
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
@@ -1104,6 +1146,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
events = []
aliased = AliasedRubyMethod.new
TracePoint.new(:call, :return){|tp|
+ next if !target_thread?
events << [tp.event, tp.method_id]
}.enable{
aliased.bar
@@ -1122,14 +1165,15 @@ class TestSetTraceFunc < Test::Unit::TestCase
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, :original_size],
- [:c_return, :original_size],
+ [:c_call, :size],
+ [:c_return, :size],
[:return, :size]
], events, "should use alias method name for tracing c methods")
end
@@ -1139,6 +1183,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
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
@@ -1255,16 +1300,49 @@ class TestSetTraceFunc < Test::Unit::TestCase
end
def test_recursive
- assert_ruby_status [], %q{
- stack = []
+ assert_in_out_err([], %q{\
TracePoint.new(:c_call){|tp|
- p 2
- stack << tp.method_id
+ p tp.method_id
}.enable{
p 1
}
- raise if stack != [:p, :hash, :inspect]
- }, '[Bug #9940]'
+ }, %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
@@ -1284,133 +1362,40 @@ class TestSetTraceFunc < Test::Unit::TestCase
end
def test_rescue_and_ensure_should_not_cause_b_return
- curr_thread = Thread.current
- trace = TracePoint.new(:b_call, :b_return){
- next if curr_thread != Thread.current
- flunk("Should not reach here because there is no block.")
- }
-
- begin
- trace.enable
+ 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
- ensure
- trace.disable
end
end
define_method(:method_test_argument_error_on_bmethod){|correct_key: 1|}
def test_argument_error_on_bmethod
- events = []
- curr_thread = Thread.current
- TracePoint.new(:call, :return){|tp|
- next if curr_thread != Thread.current
- events << [tp.event, tp.method_id]
- }.enable do
+ assert_consistent_call_return '[Bug #9959]' do
begin
method_test_argument_error_on_bmethod(wrong_key: 2)
- rescue => e
+ rescue
# ignore
end
end
-
- assert_equal [], events # should be empty.
- 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_events = []
- return_events = []
-
- TracePoint.new(*check_events){|tp|
- next unless target_thread?
-
- case tp.event.to_s
- when /call/
- call_events << method_label(tp)
- when /return/
- return_events << method_label(tp)
- end
- }.enable do
- yield
- end
-
- assert_equal false, call_events.empty?
- assert_equal false, return_events.empty?
- assert_equal call_events, return_events.reverse, message
end
def test_rb_rescue
- events = []
- curr_thread = Thread.current
- TracePoint.new(:a_call, :a_return){|tp|
- next if curr_thread != Thread.current
- events << [tp.event, tp.method_id]
- }.enable do
+ assert_consistent_call_return '[Bug #9961]' do
begin
-Numeric.new
- rescue => e
+ rescue
# ignore
end
end
-
- assert_equal [
- [:b_call, :test_rb_rescue],
- [:c_call, :new],
- [:c_call, :initialize],
- [:c_return, :initialize],
- [:c_return, :new],
- [:c_call, :-@],
- [:c_call, :coerce],
- [:c_call, :new],
- [:c_call, :initialize],
- [:c_return, :initialize],
- [:c_return, :new],
- [:c_call, :exception],
- [:c_return, :exception],
- [:c_call, :backtrace],
- [:c_return, :backtrace],
- [:c_return, :coerce], # don't miss it!
- [:c_call, :to_s],
- [:c_return, :to_s],
- [:c_call, :to_s],
- [:c_return, :to_s],
- [:c_call, :new],
- [:c_call, :initialize],
- [:c_return, :initialize],
- [:c_return, :new],
- [:c_call, :exception],
- [:c_return, :exception],
- [:c_call, :backtrace],
- [:c_return, :backtrace],
- [:c_return, :-@],
- [:c_call, :===],
- [:c_return, :===],
- [:b_return, :test_rb_rescue]], events
end
def test_b_call_with_redo
- assert_consistent_call_return do
+ assert_consistent_call_return '[Bug #9964]' do
i = 0
1.times{
break if (i+=1) > 10
@@ -1433,6 +1418,25 @@ class TestSetTraceFunc < Test::Unit::TestCase
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}
@@ -1444,12 +1448,414 @@ class TestSetTraceFunc < Test::Unit::TestCase
evs = []
TracePoint.new(:call, :return){|tp|
- return if Thread.current != target_th
+ next unless target_thread?
evs << tp.event
}.enable{
- a = Bug10724.new
+ 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
+ next unless target_thread?
+ 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'
+
+ 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
end
diff --git a/test/ruby/test_signal.rb b/test/ruby/test_signal.rb
index d3e8f864c9..2d98b0b564 100644
--- a/test/ruby/test_signal.rb
+++ b/test/ruby/test_signal.rb
@@ -1,7 +1,7 @@
+# frozen_string_literal: false
require 'test/unit'
require 'timeout'
require 'tempfile'
-require_relative 'envutil'
class TestSignal < Test::Unit::TestCase
def test_signal
@@ -73,12 +73,14 @@ class TestSignal < Test::Unit::TestCase
def test_invalid_signal_name
assert_raise(ArgumentError) { Process.kill(:XXXXXXXXXX, $$) }
+ 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)
@@ -161,11 +163,28 @@ 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") }
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
Signal.list[sig = "USR1"] or sig = "INT"
assert_in_out_err(["-e", <<-"end;"], "", %w"foo")
@@ -174,30 +193,12 @@ class TestSignal < Test::Unit::TestCase
end;
end if Process.respond_to?(:kill)
- def test_signal_requiring
- t = Tempfile.new(%w"require_ensure_test .rb")
- t.puts "sleep"
- t.close
- error = IO.popen([EnvUtil.rubybin, "-e", <<EOS, t.path, :err => File::NULL]) do |child|
-trap(:INT, "DEFAULT")
-th = Thread.new do
- begin
- require ARGV[0]
- ensure
- err = $! ? [$!, $!.backtrace] : $!
- Marshal.dump(err, STDOUT)
- STDOUT.flush
- end
-end
-Thread.pass while th.running?
-Process.kill(:INT, $$)
-th.join
-EOS
- Marshal.load(child)
- end
- t.close!
- assert_nil(error)
- 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) {
@@ -218,6 +219,15 @@ EOS
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
10.times do
IO.popen([EnvUtil.rubybin, "-e", <<EOS, :err => File::NULL]) do |child|
Signal.trap("INT") do |signo|
@@ -291,4 +301,24 @@ EOS
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
end
diff --git a/test/ruby/test_sleep.rb b/test/ruby/test_sleep.rb
index fdf08aafa1..61002b8b18 100644
--- a/test/ruby/test_sleep.rb
+++ b/test/ruby/test_sleep.rb
@@ -1,19 +1,14 @@
+# 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
- bottom =
- case RUBY_PLATFORM
- when /linux/
- 4.98 if /Linux ([\d.]+)/ =~ `uname -sr` && ($1.split('.')<=>%w/2 6 18/)<1
- when /mswin|mingw/
- 4.98
- end
- bottom ||= 5.0
+ 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
diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb
index 9fc3fd059f..a07ac7908b 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,43 @@ 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_invalid
# Star precision before star width:
assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%.**d", 5, 10, 1)}
@@ -244,6 +334,19 @@ class TestSprintf < Test::Unit::TestCase
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
def test_skip
@@ -309,12 +412,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)
@@ -333,7 +452,10 @@ class TestSprintf < Test::Unit::TestCase
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")}
- assert_raise_with_message(KeyError, "key<key> not found") {sprintf("%<key>s", {})}
+ 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
@@ -378,4 +500,36 @@ class TestSprintf < Test::Unit::TestCase
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
+ fmt = [4, 2, 2].map { |x| "%0#{x}d" }.join('-') # defeats optimization
+ ObjectSpace.count_objects(res = {}) # creates strings on first call
+ 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
+ end
end
diff --git a/test/ruby/test_sprintf_comb.rb b/test/ruby/test_sprintf_comb.rb
index c58ddf4f15..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'
diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb
index 4942621392..9574ed31c9 100644
--- a/test/ruby/test_string.rb
+++ b/test/ruby/test_string.rb
@@ -1,9 +1,5 @@
+# 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"
@@ -16,12 +12,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 # '[]'
@@ -127,20 +205,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)
@@ -152,6 +216,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 # '<=>'
@@ -161,12 +227,6 @@ 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
@@ -190,13 +250,6 @@ class TestString < Test::Unit::TestCase
assert_not_equal(:foo, S("foo"))
assert_equal(S("abcdef"), S("abcdef"))
- pre_1_7_1 do
- $= = true
- assert_equal(S("CAT"), S('cat'))
- assert_equal(S("CaT"), S('cAt'))
- $= = false
- end
-
assert_not_equal(S("CAT"), S('cat'))
assert_not_equal(S("CaT"), S('cAt'))
@@ -236,12 +289,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)
@@ -282,10 +329,10 @@ class TestString < Test::Unit::TestCase
def casetest(a, b, rev=false)
msg = proc {"#{a} should#{' not' if rev} match #{b}"}
case a
- when b
- assert(!rev, msg)
- else
- assert(rev, msg)
+ when b
+ assert(!rev, msg)
+ else
+ assert(rev, msg)
end
end
@@ -293,13 +340,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
@@ -357,6 +397,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!
@@ -413,6 +503,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
@@ -465,13 +616,14 @@ class TestString < Test::Unit::TestCase
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)
@@ -479,6 +631,12 @@ class TestString < Test::Unit::TestCase
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
@@ -505,6 +663,19 @@ class TestString < Test::Unit::TestCase
def test_crypt
assert_equal(S('aaGUC/JkO9/Sc'), S("mypassword").crypt(S("aa")))
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"))}
+ [Encoding::UTF_16BE, Encoding::UTF_16LE,
+ Encoding::UTF_32BE, Encoding::UTF_32LE].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 = ""', <<~'end;') # do
+ 1000.times { s.crypt(-"..").clear }
+ end;
end
def test_delete
@@ -516,7 +687,7 @@ 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"))
@@ -582,6 +753,79 @@ 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 }
end
def test_dup
@@ -610,14 +854,15 @@ 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
@@ -642,13 +887,20 @@ class TestString < Test::Unit::TestCase
assert_equal [65, 66, 67], s.bytes {}
}
else
- assert_warning(/deprecated/) {
+ 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
@@ -679,13 +931,20 @@ class TestString < Test::Unit::TestCase
assert_equal [0x3042, 0x3044, 0x3046], s.codepoints {}
}
else
- assert_warning(/deprecated/) {
+ 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
@@ -710,7 +969,8 @@ class TestString < Test::Unit::TestCase
assert_equal ["A", "B", "C"], s.chars {}
}
else
- assert_warning(/deprecated/) {
+ 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])
@@ -720,6 +980,74 @@ class TestString < Test::Unit::TestCase
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
+ end
+
+ [
+ ["\u{a 308}", ["\u000A", "\u0308"]],
+ ["\u{d 308}", ["\u000D", "\u0308"]],
+ ["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
+ 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}",
+ ].each do |g|
+ assert_equal [g], g.grapheme_clusters
+ end
+
+ assert_equal ["\u000A", "\u0308"], "\u{a 308}".grapheme_clusters
+ assert_equal ["\u000D", "\u0308"], "\u{d 308}".grapheme_clusters
+ 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
+ res = []
+ assert_same s, s.grapheme_clusters {|x| res << x }
+ assert_equal("A", res[0])
+ assert_equal("B", res[1])
+ assert_equal("C", res[2])
+ }
+ end
+ end
+
def test_each_line
save = $/
$/ = "\n"
@@ -730,8 +1058,13 @@ class TestString < Test::Unit::TestCase
res=[]
S("hello\n\n\nworld").each_line(S('')) {|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\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])
$/ = "!"
@@ -760,6 +1093,58 @@ class TestString < Test::Unit::TestCase
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
@@ -772,7 +1157,7 @@ class TestString < Test::Unit::TestCase
assert_equal ["hello\n", "world"], s.lines {}
}
else
- assert_warning(/deprecated/) {
+ 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])
@@ -808,6 +1193,7 @@ 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
@@ -831,6 +1217,11 @@ class TestString < Test::Unit::TestCase
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!
@@ -890,18 +1281,6 @@ class TestString < Test::Unit::TestCase
assert_not_equal(S("sub-setter").hash, S("discover").hash, bug9172)
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)
- end
-
def test_hex
assert_equal(255, S("0xff").hex)
assert_equal(-255, S("-0xff").hex)
@@ -959,6 +1338,14 @@ 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_not_equal(:koala, S("Koala").intern)
@@ -990,6 +1377,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!
@@ -1026,6 +1416,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
@@ -1060,10 +1454,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
@@ -1113,6 +1507,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
@@ -1145,6 +1542,18 @@ class TestString < Test::Unit::TestCase
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
@@ -1286,19 +1695,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(" ")))
@@ -1322,14 +1723,37 @@ class TestString < Test::Unit::TestCase
assert_equal([], "".split(//, 1))
assert_equal("[2, 3]", [1,2,3].slice!(1,10000).inspect, "moved from btest/knownbug")
+ 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
+ def test_split_wchar
bug8642 = '[ruby-core:56036] [Bug #8642]'
[
Encoding::UTF_16BE, Encoding::UTF_16LE,
@@ -1342,6 +1766,25 @@ class TestString < Test::Unit::TestCase
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
assert_equal(S("abc"), S("aaabbbbccc").squeeze)
assert_equal(S("aa bb cc"), S("aa bb cc").squeeze(S(" ")))
@@ -1374,6 +1817,11 @@ class TestString < Test::Unit::TestCase
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
@@ -1410,6 +1858,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\\'))
@@ -1459,6 +1908,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!
@@ -1487,6 +1946,12 @@ class TestString < Test::Unit::TestCase
r.taint
a.sub!(/./, r)
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
@@ -1509,6 +1974,14 @@ class TestString < Test::Unit::TestCase
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!
@@ -1550,6 +2023,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
@@ -1561,6 +2042,8 @@ class TestString < Test::Unit::TestCase
assert_equal(16, n.sum(17))
n[0] = 2.chr
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)
@@ -1575,7 +2058,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
@@ -1690,8 +2173,13 @@ class TestString < Test::Unit::TestCase
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")}
- assert_equal(star, str.tr(range, star), bug6156)
+ 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!
@@ -1879,7 +2367,7 @@ class TestString < Test::Unit::TestCase
end
def test_frozen_check
- assert_raise(RuntimeError) {
+ assert_raise(FrozenError) {
s = ""
s.sub!(/\A/) { s.freeze; "zzz" }
}
@@ -1971,6 +2459,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
@@ -2008,7 +2540,12 @@ class TestString < Test::Unit::TestCase
end
assert_equal(["\u30E6\u30FC\u30B6", "@", "\u30C9\u30E1.\u30A4\u30F3"],
- "\u30E6\u30FC\u30B6@\u30C9\u30E1.\u30A4\u30F3".partition(/[@.]/))
+ "\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
@@ -2028,10 +2565,20 @@ class TestString < Test::Unit::TestCase
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
@@ -2086,7 +2633,31 @@ class TestString < Test::Unit::TestCase
=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
@@ -2098,10 +2669,270 @@ 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
assert_in_out_err([], <<-INPUT, [], /symbol table overflow \(symbol [a-z]{8}\) \(RuntimeError\)/)
@@ -2110,6 +2941,23 @@ class TestString < Test::Unit::TestCase
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(//, '')
h = {}
@@ -2151,7 +2999,9 @@ class TestString < Test::Unit::TestCase
end
def test_prepend
- assert_equal(S("hello world!"), "world!".prepend("hello "))
+ 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
@@ -2225,15 +3075,70 @@ class TestString < Test::Unit::TestCase
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: 60)
+ 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
diff --git a/test/ruby/test_stringchar.rb b/test/ruby/test_stringchar.rb
index 7f7342c9ab..e13beef69c 100644
--- a/test/ruby/test_stringchar.rb
+++ b/test/ruby/test_stringchar.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestStringchar < Test::Unit::TestCase
diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb
index e23b5b021c..af68346442 100644
--- a/test/ruby/test_struct.rb
+++ b/test/ruby/test_struct.rb
@@ -1,7 +1,7 @@
# -*- coding: us-ascii -*-
+# frozen_string_literal: false
require 'test/unit'
require 'timeout'
-require_relative 'envutil'
module TestStruct
def test_struct
@@ -92,9 +92,38 @@ module TestStruct
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)
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
@@ -141,7 +170,7 @@ module TestStruct
assert_equal("#<struct :@a=3>", o.inspect)
methods = klass.instance_methods(false)
- assert_equal([:@a, :"@a="].inspect, methods.inspect, '[Bug #8756]')
+ assert_equal([:@a, :"@a="].sort.inspect, methods.sort.inspect, '[Bug #8756]')
assert_include(methods, :@a)
assert_include(methods, :"@a=")
end
@@ -156,8 +185,10 @@ module TestStruct
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
@@ -165,8 +196,10 @@ module TestStruct
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
@@ -183,6 +216,48 @@ module TestStruct
assert_raise(ArgumentError) { o.select(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)
@@ -196,7 +271,7 @@ module TestStruct
def test_hash
klass = @Struct.new(:a)
o = klass.new(1)
- assert_kind_of(Fixnum, o.hash)
+ assert_kind_of(Integer, o.hash)
end
def test_eql
@@ -289,6 +364,8 @@ module TestStruct
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
@@ -296,6 +373,8 @@ module TestStruct
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
@@ -304,6 +383,20 @@ module TestStruct
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
diff --git a/test/ruby/test_super.rb b/test/ruby/test_super.rb
index 6278e4b88a..cf7580ab00 100644
--- a/test/ruby/test_super.rb
+++ b/test/ruby/test_super.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestSuper < Test::Unit::TestCase
class Base
@@ -194,7 +194,7 @@ class TestSuper < Test::Unit::TestCase
end
end
overlaid.call(str = "123")
- overlaid.call(ary = [1,2,3])
+ overlaid.call([1,2,3])
str.reverse
end
@@ -229,11 +229,8 @@ class TestSuper < Test::Unit::TestCase
A.send(:include, Override)
end
- # [Bug #3351]
def test_double_include
- assert_equal([:Base, :Override], DoubleInclude::B.new.foo)
- # should be changed as follows?
- # assert_equal([:Base, :Override, :Override], DoubleInclude::B.new.foo)
+ assert_equal([:Base, :Override, :Override], DoubleInclude::B.new.foo, "[Bug #3351]")
end
module DoubleInclude2
@@ -318,7 +315,6 @@ class TestSuper < Test::Unit::TestCase
}
sub_class = EnvUtil.labeled_class("Sub\u{30af 30e9 30b9}", super_class) {
def foo
- x = Object.new
lambda { super() }
end
}
@@ -408,6 +404,13 @@ class TestSuper < Test::Unit::TestCase
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
@@ -482,8 +485,8 @@ class TestSuper < Test::Unit::TestCase
bug9740 = '[ruby-core:62017] [Bug #9740]'
b.module_eval do
- define_method(:foo) do |result|
- um.bind(self).call(result)
+ define_method(:foo) do |res|
+ um.bind(self).call(res)
end
end
@@ -509,4 +512,52 @@ class TestSuper < Test::Unit::TestCase
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 7f261b68bb..36cbf4a710 100644
--- a/test/ruby/test_symbol.rb
+++ b/test/ruby/test_symbol.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestSymbol < Test::Unit::TestCase
@@ -12,6 +13,19 @@ class TestSymbol < Test::Unit::TestCase
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(:"!")
@@ -106,6 +120,93 @@ 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
+
+ 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
@@ -141,6 +242,35 @@ class TestSymbol < Test::Unit::TestCase
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
@@ -156,7 +286,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
@@ -176,7 +318,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
@@ -192,13 +400,42 @@ class TestSymbol < Test::Unit::TestCase
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(EncodingError) {"\xb0a".force_encoding("utf-8").intern}
+ 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?)
@@ -206,4 +443,88 @@ class TestSymbol < Test::Unit::TestCase
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 cf38b5974c..f8d28a4f8e 100644
--- a/test/ruby/test_syntax.rb
+++ b/test/ruby/test_syntax.rb
@@ -1,11 +1,19 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestSyntax < Test::Unit::TestCase
+ 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 -r#{__dir__}/envutil - #{srcdir}],
+ assert_separately(%W[--disable-gem - #{srcdir}],
__FILE__, __LINE__, <<-'eom', timeout: Float::INFINITY)
dir = ARGV.shift
for script in Dir["#{dir}/**/*.rb"].sort
@@ -37,7 +45,8 @@ class TestSyntax < Test::Unit::TestCase
make_tmpsrc(f, "# -*- coding: #{enc.name} -*-")
assert_raise(ArgumentError, enc.name) {load(f.path)}
end
- f.close!
+ ensure
+ f.close! if f
end
def test_script_lines
@@ -53,7 +62,8 @@ class TestSyntax < Test::Unit::TestCase
assert_equal([enc, enc], debug_lines[f.path].map(&:encoding), bug4361)
end
end
- f.close!
+ ensure
+ f.close! if f
end
def test_newline_in_block_parameters
@@ -83,6 +93,20 @@ class TestSyntax < Test::Unit::TestCase
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)
@@ -101,32 +125,185 @@ class TestSyntax < Test::Unit::TestCase
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 = Object.new
- def o.kw(k1: 1, k2: 2) [k1, k2] end
+ o = KW2.new
h = {k1: 11, k2: 12}
assert_equal([11, 12], o.kw(**h))
- assert_equal([11, 22], o.kw(k2: 22, **h))
- assert_equal([11, 12], o.kw(**h, **{k2: 22}))
- assert_equal([11, 22], o.kw(**{k2: 22}, **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}
+ 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}
+ assert_valid_syntax(code, "test", verbose: true)
end
end
@@ -145,8 +322,14 @@ WARN
[:/, "regexp literal"],
[:%, "string literal"],
].each do |op, syn|
- assert_warning(warning % [op, syn]) do
- assert_valid_syntax("puts 1 #{op}0", "test") {$VERBOSE = true}
+ 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
@@ -176,20 +359,68 @@ WARN
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")
end
def test_duplicated_rest
assert_syntax_error("def foo(a, *a) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(_, *_) end")
end
def test_duplicated_opt
assert_syntax_error("def foo(a, a=1) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(_, _=1) end")
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")
end
def test_duplicated_rest_opt
@@ -202,30 +433,37 @@ WARN
def test_duplicated_opt_post
assert_syntax_error("def foo(a=1, a) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(_=1, _) end")
end
def test_duplicated_kw
assert_syntax_error("def foo(a, a: 1) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(_, _: 1) end")
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}
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")
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")
end
def test_duplicated_rest_kwrest
assert_syntax_error("def foo(*a, **a) end", /duplicated argument name/)
+ assert_valid_syntax("def foo(*_, **_) end")
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")
end
def test_duplicated_when
@@ -240,7 +478,7 @@ WARN
}
}
assert_warning(/#{w}/){#/3: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m){
- a = 1
+ a = a = 1
eval %q{
case 1
when 1, 1
@@ -251,6 +489,18 @@ WARN
}
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_lambda_with_space
feature6390 = '[ruby-dev:45605]'
assert_valid_syntax("-> (x, y) {}", __FILE__, feature6390)
@@ -301,6 +551,151 @@ 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__
@@ -312,6 +707,39 @@ eom
assert_equal(expected, actual, bug7559)
end
+ def test_dedented_heredoc_invalid_identifer
+ assert_syntax_error('<<~ "#{}"', /unexpected <</)
+ 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\
@@ -401,6 +829,12 @@ eom
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
@@ -412,6 +846,15 @@ eom
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"
@@ -433,6 +876,325 @@ eom
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 modifier_rescue/)
+ 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_return_in_loop
+ obj = Object.new
+ def obj.test
+ x = nil
+ return until x unless x
+ end
+ assert_nil obj.test
+ end
+
private
def not_label(x) @result = x; @not_label ||= nil end
diff --git a/test/ruby/test_system.rb b/test/ruby/test_system.rb
index db19a3c2cd..60037ab044 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
@@ -86,7 +86,7 @@ class TestSystem < Test::Unit::TestCase
File.unlink tmpfilename
testname = '[ruby-core:44505]'
- assert_match /Windows/, `ver`, testname
+ assert_match(/Windows/, `ver`, testname)
assert_equal 0, $?.to_i, testname
end
}
diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb
index 6568b8dfbc..135d17237e 100644
--- a/test/ruby/test_thread.rb
+++ b/test/ruby/test_thread.rb
@@ -1,7 +1,6 @@
# -*- coding: us-ascii -*-
+# frozen_string_literal: false
require 'test/unit'
-require 'thread'
-require_relative 'envutil'
class TestThread < Test::Unit::TestCase
class Thread < ::Thread
@@ -27,6 +26,26 @@ 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
@@ -83,41 +102,43 @@ class TestThread < Test::Unit::TestCase
def test_thread_variable_frozen
t = Thread.new { }.join
t.freeze
- assert_raise(RuntimeError) do
+ 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)
+ assert_equal(num_threads*loop, r)
end
def test_mutex_synchronize_yields_no_block_params
bug8097 = '[ruby-core:53424] [Bug #8097]'
- assert_empty(Mutex.new.synchronize {|*params| break params}, bug8097)
+ 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 {
`#{EnvUtil.rubybin} #{lbtest}`
@@ -127,17 +148,21 @@ class TestThread < Test::Unit::TestCase
def test_priority
c1 = c2 = 0
- t1 = Thread.new { loop { c1 += 1 } }
+ run = true
+ t1 = Thread.new { c1 += 1 while run }
t1.priority = 3
- t2 = Thread.new { loop { c2 += 1 } }
+ t2 = Thread.new { c2 += 1 while run }
t2.priority = -3
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
@@ -163,6 +188,14 @@ class TestThread < Test::Unit::TestCase
t2.kill if t2
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.05))
@@ -284,7 +317,10 @@ class TestThread < Test::Unit::TestCase
assert_in_out_err([], <<-INPUT, %w(false 1), [])
p Thread.abort_on_exception
begin
- t = Thread.new { raise }
+ t = Thread.new {
+ Thread.current.report_on_exception = false
+ raise
+ }
Thread.pass until t.stop?
p 1
rescue
@@ -296,7 +332,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
@@ -319,7 +358,11 @@ class TestThread < Test::Unit::TestCase
p Thread.abort_on_exception
begin
ok = false
- t = Thread.new { Thread.pass until ok; raise }
+ 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
@@ -331,8 +374,84 @@ 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_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_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_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_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?
+ }
+ }
+ 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 }
e = Thread.current
@@ -380,14 +499,14 @@ class TestThread < Test::Unit::TestCase
ok = false
t = Thread.new do
EnvUtil.suppress_warning do
- $SAFE = 3
+ $SAFE = 1
end
ok = true
sleep
end
Thread.pass until ok
assert_equal(0, Thread.current.safe_level)
- assert_equal(3, t.safe_level)
+ assert_equal(1, t.safe_level)
ensure
t.kill if t
@@ -407,20 +526,62 @@ 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
+ end
+
+ def test_thread_local_fetch
+ t = Thread.new { sleep }
+
+ assert_equal(false, t.key?(:foo))
+
+ 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 if t
end
def test_thread_local_security
- assert_raise(RuntimeError) do
- Thread.new do
- Thread.current[:foo] = :bar
- Thread.current.freeze
+ 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
@@ -434,7 +595,7 @@ class TestThread < Test::Unit::TestCase
end
def test_mutex_deadlock
- m = Mutex.new
+ m = Thread::Mutex.new
m.synchronize do
assert_raise(ThreadError) do
m.synchronize do
@@ -445,7 +606,7 @@ 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
@@ -457,18 +618,18 @@ class TestThread < Test::Unit::TestCase
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
@@ -476,7 +637,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
@@ -489,7 +650,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]')
@@ -523,22 +684,22 @@ class TestThread < Test::Unit::TestCase
end
def test_no_valid_cfp
- skip 'with win32ole, cannot run this testcase because win32ole redefines Thread#intialize' if defined?(WIN32OLE)
+ 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)
- assert_instance_of(Thread, Thread.new(:to_s, &Class.new.method(:undef_method)).join)
+ 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
def make_handle_interrupt_test_thread1 flag
r = []
- ready_p = false
- done = false
+ ready_q = Queue.new
+ done_q = Queue.new
th = Thread.new{
begin
Thread.handle_interrupt(RuntimeError => flag){
begin
- ready_p = true
- sleep 0.01 until done
+ ready_q << true
+ done_q.pop
rescue
r << :c1
end
@@ -547,10 +708,10 @@ class TestThread < Test::Unit::TestCase
r << :c2
end
}
- Thread.pass until ready_p
+ ready_q.pop
th.raise
begin
- done = true
+ done_q << true
th.join
rescue
r << :c3
@@ -611,24 +772,22 @@ class TestThread < Test::Unit::TestCase
r=:ng
e=Class.new(Exception)
th_s = Thread.current
- begin
- th = Thread.start{
+ th = Thread.start{
+ assert_raise(RuntimeError) {
Thread.handle_interrupt(Object => :on_blocking){
begin
- Thread.current.raise RuntimeError
- r=:ok
+ Thread.pass until r == :wait
+ Thread.current.raise RuntimeError, "will raise in sleep"
+ r = :ok
sleep
ensure
- th_s.raise e
+ th_s.raise e, "raise from ensure", $@
end
}
}
- sleep 1
- r=:ng
- th.raise RuntimeError
- th.join
- rescue e
- end
+ }
+ assert_raise(e) {r = :wait; sleep 0.2}
+ th.join
assert_equal(:ok,r)
end
@@ -637,6 +796,7 @@ class TestThread < Test::Unit::TestCase
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
@@ -657,6 +817,7 @@ class TestThread < Test::Unit::TestCase
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
@@ -674,7 +835,7 @@ class TestThread < Test::Unit::TestCase
end
def test_handle_interrupted?
- q = Queue.new
+ q = Thread::Queue.new
Thread.handle_interrupt(RuntimeError => :never){
done = false
th = Thread.new{
@@ -705,7 +866,7 @@ class TestThread < Test::Unit::TestCase
end
def test_thread_timer_and_ensure
- assert_normal_exit(<<_eom, 'r36492', timeout: 3)
+ assert_normal_exit(<<_eom, 'r36492', timeout: 10)
flag = false
t = Thread.new do
begin
@@ -725,9 +886,24 @@ _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
@@ -742,24 +918,24 @@ _eom
def test_thread_timer_and_interrupt
bug5757 = '[ruby-dev:44985]'
- t0 = Time.now.to_f
pid = nil
cmd = 'Signal.trap(:INT, "DEFAULT"); r,=IO.pipe; Thread.start {Thread.pass until Thread.main.stop?; puts; STDOUT.flush}; r.read'
opt = {}
opt[:new_pgroup] = true if /mswin|mingw/ =~ RUBY_PLATFORM
- s, _err = EnvUtil.invoke_ruby(['-e', cmd], "", true, true, opt) do |in_p, out_p, err_p, cpid|
+ s, t, _err = EnvUtil.invoke_ruby(['-e', cmd], "", true, true, opt) do |in_p, out_p, err_p, cpid|
out_p.gets
pid = cpid
+ t0 = Time.now.to_f
Process.kill(:SIGINT, pid)
Process.wait(pid)
- [$?, err_p.read]
+ t1 = Time.now.to_f
+ [$?, t1 - t0, err_p.read]
end
- t1 = Time.now.to_f
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_in_delta(t1 - t0, 1, 1, bug5757)
+ assert_include(0..2, t, bug5757)
end
def test_thread_join_in_trap
@@ -798,22 +974,27 @@ _eom
end
def test_thread_join_main_thread
- assert_raise(ThreadError) do
- Thread.new(Thread.current) {|t|
+ Thread.new(Thread.current) {|t|
+ assert_raise(ThreadError) do
t.join
- }.join
- end
+ end
+ }.join
end
def test_main_thread_status_at_exit
- assert_in_out_err([], <<-INPUT, %w(false), [])
+ assert_in_out_err([], <<-'INPUT', ["false false aborting"], [])
+q = Thread::Queue.new
Thread.new(Thread.current) {|mth|
begin
- Thead.pass until mth.stop?
+ q.push nil
+ mth.run
+ Thread.pass until mth.stop?
+ p :mth_stopped # don't run if killed by rb_thread_terminate_all
ensure
- p mth.alive?
+ puts "#{mth.alive?} #{mth.status} #{Thread.current.status}"
end
}
+q.pop
INPUT
end
@@ -841,35 +1022,34 @@ Thread.new(Thread.current) {|mth|
ary = []
t = Thread.new {
- begin
- ary << Thread.current.status
- sleep #1
- ensure
+ assert_raise(RuntimeError) do
begin
ary << Thread.current.status
- sleep #2
+ sleep #1
ensure
- ary << Thread.current.status
+ begin
+ ary << Thread.current.status
+ sleep #2
+ ensure
+ ary << Thread.current.status
+ end
end
end
}
- begin
- 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"])
- ensure
- t.join rescue nil
- 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 = Mutex.new
+ mutex = Thread::Mutex.new
assert_equal(mutex.owned?, false)
mutex.synchronize {
@@ -881,16 +1061,15 @@ Thread.new(Thread.current) {|mth|
def test_mutex_owned2
begin
- mutex = Mutex.new
+ mutex = Thread::Mutex.new
th = Thread.new {
# lock forever
mutex.lock
sleep
}
- Thread.pass until th.status == "sleep"
- # acquired another thread.
- assert_equal(mutex.locked?, true)
+ # acquired by another thread.
+ Thread.pass until mutex.locked?
assert_equal(mutex.owned?, false)
ensure
th.kill if th
@@ -899,7 +1078,7 @@ Thread.new(Thread.current) {|mth|
def test_mutex_unlock_on_trap
assert_in_out_err([], <<-INPUT, %w(locked unlocked false), [])
- m = Mutex.new
+ m = Thread::Mutex.new
trapped = false
Signal.trap("INT") { |signo|
@@ -964,7 +1143,7 @@ Thread.new(Thread.current) {|mth|
def test_blocking_mutex_unlocked_on_fork
bug8433 = '[ruby-core:55102] [Bug #8433]'
- mutex = Mutex.new
+ mutex = Thread::Mutex.new
flag = false
mutex.lock
@@ -998,9 +1177,9 @@ Thread.new(Thread.current) {|mth|
end
Process.wait2(f.pid)
end
- unless th.join(3)
+ unless th.join(EnvUtil.apply_timeout_scale(30))
Process.kill(:QUIT, f.pid)
- Process.kill(:KILL, f.pid) unless th.join(1)
+ Process.kill(:KILL, f.pid) unless th.join(EnvUtil.apply_timeout_scale(1))
end
_, status = th.value
output = f.read
@@ -1008,4 +1187,131 @@ Thread.new(Thread.current) {|mth|
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_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
+ assert_normal_exit(<<-_end, '[Bug #8996]', timeout: 5, timeout_error: nil)
+ Thread.report_on_exception = false
+ trap(:TERM){exit}
+ while true
+ t = Thread.new{sleep 0}
+ t.raise Interrupt
+ 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#{<<~'};'}")
+ {#
+ 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_threadgroup.rb b/test/ruby/test_threadgroup.rb
index e29c477247..ec95bd6419 100644
--- a/test/ruby/test_threadgroup.rb
+++ b/test/ruby/test_threadgroup.rb
@@ -1,14 +1,16 @@
+# frozen_string_literal: false
require 'test/unit'
-require 'thread'
-require_relative 'envutil'
class TestThreadGroup < Test::Unit::TestCase
def test_thread_init
thgrp = ThreadGroup.new
- Thread.new{
+ th = Thread.new{
thgrp.add(Thread.current)
- assert_equal(thgrp, Thread.new{sleep 1}.group)
- }.join
+ Thread.new{sleep 1}
+ }.value
+ assert_equal(thgrp, th.group)
+ ensure
+ th.join
end
def test_frozen_thgroup
diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb
index d016812e3a..5ddcc9dcfe 100644
--- a/test/ruby/test_time.rb
+++ b/test/ruby/test_time.rb
@@ -1,9 +1,8 @@
+# frozen_string_literal: false
require 'test/unit'
-require 'rational'
require 'delegate'
require 'timeout'
require 'delegate'
-require_relative 'envutil'
class TestTime < Test::Unit::TestCase
def setup
@@ -47,6 +46,7 @@ class TestTime < Test::Unit::TestCase
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()
@@ -172,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)
@@ -235,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)
@@ -307,7 +319,9 @@ class TestTime < Test::Unit::TestCase
in_timezone('JST-9') do
t = Time.local(2013, 2, 24)
assert_equal('JST', Time.local(2013, 2, 24).zone)
- assert_equal('JST', Marshal.load(Marshal.dump(t)).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
@@ -410,13 +424,18 @@ class TestTime < Test::Unit::TestCase
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
@@ -424,6 +443,12 @@ class TestTime < Test::Unit::TestCase
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
t2000 = get_t2000
assert_equal(-1, t2000 <=> Time.gm(2001))
@@ -485,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?)
@@ -517,8 +543,19 @@ class TestTime < Test::Unit::TestCase
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_equal(Encoding.find('locale'), Time.now.zone.encoding)
+ assert_zone_encoding Time.now
end
def test_plus_minus_succ
@@ -567,7 +604,7 @@ class TestTime < Test::Unit::TestCase
assert_equal(1, t2000.yday)
assert_equal(false, t2000.isdst)
assert_equal("UTC", t2000.zone)
- assert_equal(Encoding.find("locale"), t2000.zone.encoding)
+ assert_zone_encoding(t2000)
assert_equal(0, t2000.gmt_offset)
assert_not_predicate(t2000, :sunday?)
assert_not_predicate(t2000, :monday?)
@@ -589,7 +626,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_equal(Encoding.find("locale"), Time.at(946684800).zone.encoding)
+ 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?)
@@ -627,6 +664,8 @@ class TestTime < Test::Unit::TestCase
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"))
@@ -654,6 +693,12 @@ class TestTime < Test::Unit::TestCase
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
@@ -704,6 +749,13 @@ 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
@@ -723,6 +775,12 @@ class TestTime < Test::Unit::TestCase
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
@@ -786,8 +844,7 @@ class TestTime < Test::Unit::TestCase
end
def test_strftime_too_wide
- bug4457 = '[ruby-dev:43285]'
- assert_raise(Errno::ERANGE, bug4457) {Time.now.strftime('%8192z')}
+ assert_equal(8192, Time.now.strftime('%8192z').size)
end
def test_strfimte_zoneoffset
@@ -999,6 +1056,29 @@ class TestTime < Test::Unit::TestCase
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
@@ -1017,4 +1097,70 @@ class TestTime < Test::Unit::TestCase
}
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
+ 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
+ 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
+ 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
+ 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 533ceb3d8e..bfe9b4eef3 100644
--- a/test/ruby/test_time_tz.rb
+++ b/test/ruby/test_time_tz.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestTimeTZ < Test::Unit::TestCase
@@ -83,6 +84,16 @@ class TestTimeTZ < Test::Unit::TestCase
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
@@ -96,6 +107,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])
@@ -125,12 +148,19 @@ 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="America/St_Johns") {
assert_time_constructor(tz, "2007-11-03 23:00:59 -0230", :new, [2007,11,3,23,0,59,:dst])
@@ -148,31 +178,40 @@ 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_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_lisbon
- with_tz(tz="Europe/Lisbon") {
+ with_tz("Europe/Lisbon") {
assert_equal("LMT", Time.new(-0x1_0000_0000_0000_0000).zone)
}
end if has_lisbon_tz
- 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])
- }
- end
-
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
@@ -259,12 +298,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
@@ -326,10 +366,23 @@ 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
+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
@@ -346,15 +399,18 @@ 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
@@ -393,7 +449,7 @@ End
]
}
}
- assert_includes(results, [true, true, true, true, true])
+ assert_include(results, [true, true, true, true, true])
}
end
@@ -402,5 +458,6 @@ End
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
end
diff --git a/test/ruby/test_trace.rb b/test/ruby/test_trace.rb
index 775c458fb1..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
diff --git a/test/ruby/test_transcode.rb b/test/ruby/test_transcode.rb
index d18953dc70..44d238ffd2 100644
--- a/test/ruby/test_transcode.rb
+++ b/test/ruby/test_transcode.rb
@@ -1,8 +1,9 @@
# 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'
-require_relative 'envutil'
+
class TestTranscode < Test::Unit::TestCase
def test_errors
assert_raise(Encoding::ConverterNotFoundError) { 'abc'.encode('foo', 'bar') }
@@ -12,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
@@ -362,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') # ׀
@@ -483,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
@@ -502,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
@@ -521,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
@@ -997,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] }
@@ -1212,6 +1299,9 @@ 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
@@ -2019,6 +2109,13 @@ class TestTranscode < Test::Unit::TestCase
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")
@@ -2083,17 +2180,19 @@ class TestTranscode < Test::Unit::TestCase
def test_valid_dummy_encoding
bug9314 = '[ruby-core:59354] [Bug #9314]'
- assert_separately(%W[- -- #{bug9314}], <<-'end;')
- 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)
+ 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([], <<-'end;') #do
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
bug11277 = '[ruby-dev:49106] [Bug #11277]'
num = 2
th = (0...num).map do |i|
@@ -2110,6 +2209,17 @@ class TestTranscode < Test::Unit::TestCase
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
diff --git a/test/ruby/test_undef.rb b/test/ruby/test_undef.rb
index e1c98076c0..6d513a238f 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
diff --git a/test/ruby/test_unicode_escape.rb b/test/ruby/test_unicode_escape.rb
index 2561b49486..5913bb0130 100644
--- a/test/ruby/test_unicode_escape.rb
+++ b/test/ruby/test_unicode_escape.rb
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestUnicodeEscape < Test::Unit::TestCase
def test_basic
@@ -256,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 b902288fdd..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
@@ -65,6 +67,7 @@ class TestVariable < Test::Unit::TestCase
def test_local_variables
lvar = 1
assert_instance_of(Symbol, local_variables[0], "[ruby-dev:34008]")
+ lvar
end
def test_local_variables2
@@ -72,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
@@ -81,29 +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]'
- x = tap {|x| break local_variables}
- assert_equal([:x, :bug9486, :x], x)
+ assert_equal([:x, :bug9486], tap {|x| break local_variables}, bug9486)
end
def test_shadowing_block_local_variables
bug9486 = '[ruby-core:60501] [Bug #9486]'
- x = tap {|;x| break local_variables}
- assert_equal([:x, :bug9486, :x], x)
+ 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
index bcaab894a7..cde186c5f3 100644
--- a/test/ruby/test_weakmap.rb
+++ b/test/ruby/test_weakmap.rb
@@ -1,5 +1,5 @@
+# frozen_string_literal: false
require 'test/unit'
-require_relative 'envutil'
class TestWeakMap < Test::Unit::TestCase
def setup
@@ -19,13 +19,13 @@ class TestWeakMap < Test::Unit::TestCase
assert_raise(ArgumentError) {@wm[true] = x}
assert_raise(ArgumentError) {@wm[false] = x}
assert_raise(ArgumentError) {@wm[nil] = x}
- assert_raise(RuntimeError) {@wm[42] = x}
- assert_raise(RuntimeError) {@wm[:foo] = 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(RuntimeError) {@wm[x] = 42}
- assert_raise(RuntimeError) {@wm[x] = :foo}
+ assert_raise(ArgumentError) {@wm[x] = 42}
+ assert_raise(ArgumentError) {@wm[x] = :foo}
end
def test_include?
@@ -39,6 +39,7 @@ class TestWeakMap < Test::Unit::TestCase
x = nil
end
GC.start
+ skip # TODO: failure introduced from r60440
assert_not_send([@wm, m, k])
end
alias test_member? test_include?
diff --git a/test/ruby/test_whileuntil.rb b/test/ruby/test_whileuntil.rb
index ca853af944..121c44817d 100644
--- a/test/ruby/test_whileuntil.rb
+++ b/test/ruby/test_whileuntil.rb
@@ -1,6 +1,6 @@
+# frozen_string_literal: false
require 'test/unit'
require 'tmpdir'
-require_relative 'envutil'
class TestWhileuntil < Test::Unit::TestCase
def test_while
@@ -61,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)
diff --git a/test/ruby/test_yield.rb b/test/ruby/test_yield.rb
index 143ee55a9f..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')
@@ -390,4 +398,28 @@ class TestRubyYieldGen < Test::Unit::TestCase
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 83325f2efc..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