From d3d5ef0cca160fca538c7f556c5a6e08df5847e6 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Sun, 26 Jun 2022 14:50:14 +0200 Subject: Update to ruby/spec@ab32a1a --- spec/ruby/core/array/fill_spec.rb | 6 + spec/ruby/core/array/fixtures/classes.rb | 62 ++++++++++ spec/ruby/core/array/sample_spec.rb | 18 ++- spec/ruby/core/dir/foreach_spec.rb | 10 +- spec/ruby/core/exception/signal_exception_spec.rb | 6 +- spec/ruby/core/file/open_spec.rb | 8 ++ spec/ruby/core/float/divide_spec.rb | 4 + spec/ruby/core/float/round_spec.rb | 64 ++++++++++ spec/ruby/core/integer/chr_spec.rb | 39 +++--- spec/ruby/core/integer/fdiv_spec.rb | 5 + spec/ruby/core/io/advise_spec.rb | 14 +-- spec/ruby/core/io/fixtures/classes.rb | 14 +++ spec/ruby/core/io/gets_spec.rb | 14 +++ spec/ruby/core/io/readline_spec.rb | 31 +++++ spec/ruby/core/io/readlines_spec.rb | 22 ++++ spec/ruby/core/io/shared/each.rb | 48 ++++++++ spec/ruby/core/io/shared/readlines.rb | 133 ++++++++++++++------- .../ruby/core/kernel/instance_variable_get_spec.rb | 6 + .../ruby/core/kernel/instance_variable_set_spec.rb | 6 + .../core/kernel/remove_instance_variable_spec.rb | 13 ++ spec/ruby/core/kernel/shared/require.rb | 19 +++ spec/ruby/core/math/ldexp_spec.rb | 6 + spec/ruby/core/module/class_variables_spec.rb | 8 ++ spec/ruby/core/module/fixtures/classes.rb | 4 + spec/ruby/core/process/clock_gettime_spec.rb | 99 ++++++++------- spec/ruby/core/process/egid_spec.rb | 41 ++++++- spec/ruby/core/process/euid_spec.rb | 12 +- spec/ruby/core/process/spawn_spec.rb | 2 +- spec/ruby/core/process/status/equal_value_spec.rb | 2 +- spec/ruby/core/process/status/exited_spec.rb | 2 +- spec/ruby/core/process/status/exitstatus_spec.rb | 2 +- spec/ruby/core/process/status/signaled_spec.rb | 2 +- spec/ruby/core/process/status/success_spec.rb | 2 +- spec/ruby/core/process/status/termsig_spec.rb | 4 +- spec/ruby/core/process/status/to_i_spec.rb | 2 +- spec/ruby/core/regexp/shared/quote.rb | 5 + spec/ruby/core/signal/trap_spec.rb | 4 +- spec/ruby/core/string/capitalize_spec.rb | 4 + spec/ruby/core/string/dup_spec.rb | 9 ++ spec/ruby/core/string/insert_spec.rb | 9 ++ spec/ruby/core/string/lstrip_spec.rb | 6 + spec/ruby/core/string/rstrip_spec.rb | 6 + spec/ruby/core/string/scrub_spec.rb | 5 + spec/ruby/core/string/shared/slice.rb | 14 ++- spec/ruby/core/string/split_spec.rb | 19 ++- spec/ruby/core/string/unpack/z_spec.rb | 5 + spec/ruby/core/thread/raise_spec.rb | 24 ++++ 47 files changed, 683 insertions(+), 157 deletions(-) (limited to 'spec/ruby/core') diff --git a/spec/ruby/core/array/fill_spec.rb b/spec/ruby/core/array/fill_spec.rb index 6369f23544..23728414be 100644 --- a/spec/ruby/core/array/fill_spec.rb +++ b/spec/ruby/core/array/fill_spec.rb @@ -205,6 +205,12 @@ describe "Array#fill with (filler, index, length)" do -> { [].fill('a', obj) }.should raise_error(TypeError) end + it "raises a TypeError when the length is not numeric" do + -> { [1, 2, 3].fill("x", 1, "foo") }.should raise_error(TypeError, /no implicit conversion of String into Integer/) + -> { [1, 2, 3].fill("x", 1, :"foo") }.should raise_error(TypeError, /no implicit conversion of Symbol into Integer/) + -> { [1, 2, 3].fill("x", 1, Object.new) }.should raise_error(TypeError, /no implicit conversion of Object into Integer/) + end + not_supported_on :opal do it "raises an ArgumentError or RangeError for too-large sizes" do error_types = [RangeError, ArgumentError] diff --git a/spec/ruby/core/array/fixtures/classes.rb b/spec/ruby/core/array/fixtures/classes.rb index affb3b49e6..aa5fecd96b 100644 --- a/spec/ruby/core/array/fixtures/classes.rb +++ b/spec/ruby/core/array/fixtures/classes.rb @@ -40,6 +40,68 @@ module ArraySpecs a end + # Chi squared critical values for tests with n degrees of freedom at 99% confidence. + # Values obtained from NIST Engineering Statistic Handbook at + # https://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm + + CHI_SQUARED_CRITICAL_VALUES = [ + 0, + 6.635, 9.210, 11.345, 13.277, 15.086, 16.812, 18.475, 20.090, 21.666, 23.209, + 24.725, 26.217, 27.688, 29.141, 30.578, 32.000, 33.409, 34.805, 36.191, 37.566, + 38.932, 40.289, 41.638, 42.980, 44.314, 45.642, 46.963, 48.278, 49.588, 50.892, + 52.191, 53.486, 54.776, 56.061, 57.342, 58.619, 59.893, 61.162, 62.428, 63.691, + 64.950, 66.206, 67.459, 68.710, 69.957, 71.201, 72.443, 73.683, 74.919, 76.154, + 77.386, 78.616, 79.843, 81.069, 82.292, 83.513, 84.733, 85.950, 87.166, 88.379, + 89.591, 90.802, 92.010, 93.217, 94.422, 95.626, 96.828, 98.028, 99.228, 100.425, + 101.621, 102.816, 104.010, 105.202, 106.393, 107.583, 108.771, 109.958, 111.144, 112.329, + 113.512, 114.695, 115.876, 117.057, 118.236, 119.414, 120.591, 121.767, 122.942, 124.116, + 125.289, 126.462, 127.633, 128.803, 129.973, 131.141, 132.309, 133.476, 134.642, 135.807, + ] + + def self.measure_sample_fairness(size, samples, iters) + ary = Array.new(size) { |x| x } + (samples).times do |i| + chi_results = [] + 3.times do + counts = Array.new(size) { 0 } + expected = iters / size + iters.times do + x = ary.sample(samples)[i] + counts[x] += 1 + end + chi_squared = 0.0 + counts.each do |count| + chi_squared += (((count - expected) ** 2) * 1.0 / expected) + end + chi_results << chi_squared + break if chi_squared <= CHI_SQUARED_CRITICAL_VALUES[size] + end + + chi_results.min.should <= CHI_SQUARED_CRITICAL_VALUES[size] + end + end + + def self.measure_sample_fairness_large_sample_size(size, samples, iters) + ary = Array.new(size) { |x| x } + counts = Array.new(size) { 0 } + expected = iters * samples / size + iters.times do + ary.sample(samples).each do |sample| + counts[sample] += 1 + end + end + chi_squared = 0.0 + counts.each do |count| + chi_squared += (((count - expected) ** 2) * 1.0 / expected) + end + + # Chi squared critical values for tests with 4 degrees of freedom + # Values obtained from NIST Engineering Statistic Handbook at + # https://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm + + chi_squared.should <= CHI_SQUARED_CRITICAL_VALUES[size] + end + class MyArray < Array # The #initialize method has a different signature than Array to help # catch places in the specs that do not assert the #initialize is not diff --git a/spec/ruby/core/array/sample_spec.rb b/spec/ruby/core/array/sample_spec.rb index 755b46f126..5b3aac9aed 100644 --- a/spec/ruby/core/array/sample_spec.rb +++ b/spec/ruby/core/array/sample_spec.rb @@ -3,16 +3,14 @@ require_relative 'fixtures/classes' describe "Array#sample" do it "samples evenly" do - ary = [0, 1, 2, 3] - 3.times do |i| - counts = [0, 0, 0, 0] - 4000.times do - counts[ary.sample(3)[i]] += 1 - end - counts.each do |count| - (800..1200).should include(count) - end - end + ArraySpecs.measure_sample_fairness(4, 1, 400) + ArraySpecs.measure_sample_fairness(4, 2, 400) + ArraySpecs.measure_sample_fairness(4, 3, 400) + ArraySpecs.measure_sample_fairness(40, 3, 400) + ArraySpecs.measure_sample_fairness(40, 4, 400) + ArraySpecs.measure_sample_fairness(40, 8, 400) + ArraySpecs.measure_sample_fairness(40, 16, 400) + ArraySpecs.measure_sample_fairness_large_sample_size(100, 80, 4000) end it "returns nil for an empty Array" do diff --git a/spec/ruby/core/dir/foreach_spec.rb b/spec/ruby/core/dir/foreach_spec.rb index b0e18afeb5..9cf34b1d71 100644 --- a/spec/ruby/core/dir/foreach_spec.rb +++ b/spec/ruby/core/dir/foreach_spec.rb @@ -41,13 +41,13 @@ describe "Dir.foreach" do it "accepts an encoding keyword for the encoding of the entries" do dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").to_a.sort - dirs.each {|dir| dir.encoding.should == Encoding::UTF_8} + dirs.each { |dir| dir.encoding.should == Encoding::UTF_8 } - dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: Encoding::UTF_16LE).to_a.sort - dirs.each {|dir| dir.encoding.should == Encoding::UTF_16LE} + dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: Encoding::ISO_8859_1).to_a.sort + dirs.each { |dir| dir.encoding.should == Encoding::ISO_8859_1 } - Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: Encoding::UTF_16LE) do |f| - f.encoding.should == Encoding::UTF_16LE + Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: Encoding::ISO_8859_1) do |f| + f.encoding.should == Encoding::ISO_8859_1 end end diff --git a/spec/ruby/core/exception/signal_exception_spec.rb b/spec/ruby/core/exception/signal_exception_spec.rb index 566bcb4672..1a0940743f 100644 --- a/spec/ruby/core/exception/signal_exception_spec.rb +++ b/spec/ruby/core/exception/signal_exception_spec.rb @@ -93,7 +93,7 @@ describe "SignalException" do platform_is_not :windows do it "runs after at_exit" do - output = ruby_exe(<<-RUBY, exit_status: nil) + output = ruby_exe(<<-RUBY, exit_status: :SIGKILL) at_exit do puts "hello" $stdout.flush @@ -107,7 +107,7 @@ describe "SignalException" do end it "cannot be trapped with Signal.trap" do - ruby_exe(<<-RUBY, exit_status: nil) + ruby_exe(<<-RUBY, exit_status: :SIGPROF) Signal.trap("PROF") {} raise(SignalException, "PROF") RUBY @@ -116,7 +116,7 @@ describe "SignalException" do end it "self-signals for USR1" do - ruby_exe("raise(SignalException, 'USR1')", exit_status: nil) + ruby_exe("raise(SignalException, 'USR1')", exit_status: :SIGUSR1) $?.termsig.should == Signal.list.fetch('USR1') end end diff --git a/spec/ruby/core/file/open_spec.rb b/spec/ruby/core/file/open_spec.rb index c7dd34d5c6..1729780570 100644 --- a/spec/ruby/core/file/open_spec.rb +++ b/spec/ruby/core/file/open_spec.rb @@ -494,6 +494,14 @@ describe "File.open" do File.open(@file, "w") { |f| f.puts "testing" } File.size(@file).should > 0 File.open(@file, "rb+") do |f| + f.binmode?.should == true + f.external_encoding.should == Encoding::ASCII_8BIT + f.pos.should == 0 + f.should_not.eof? + end + File.open(@file, "r+b") do |f| + f.binmode?.should == true + f.external_encoding.should == Encoding::ASCII_8BIT f.pos.should == 0 f.should_not.eof? end diff --git a/spec/ruby/core/float/divide_spec.rb b/spec/ruby/core/float/divide_spec.rb index d8f71a6b98..72ab7527bd 100644 --- a/spec/ruby/core/float/divide_spec.rb +++ b/spec/ruby/core/float/divide_spec.rb @@ -36,4 +36,8 @@ describe "Float#/" do -> { 13.0 / "10" }.should raise_error(TypeError) -> { 13.0 / :symbol }.should raise_error(TypeError) end + + it "divides correctly by Rational numbers" do + (1.2345678901234567 / Rational(1, 10000000000000000000)).should == 1.2345678901234567e+19 + end end diff --git a/spec/ruby/core/float/round_spec.rb b/spec/ruby/core/float/round_spec.rb index 4bd2dc460c..e5a8f534e7 100644 --- a/spec/ruby/core/float/round_spec.rb +++ b/spec/ruby/core/float/round_spec.rb @@ -103,6 +103,70 @@ describe "Float#round" do 5.55.round(1, half: :up).should eql(5.6) 5.55.round(1, half: :down).should eql(5.5) 5.55.round(1, half: :even).should eql(5.6) + -5.55.round(1, half: nil).should eql(-5.6) + -5.55.round(1, half: :up).should eql(-5.6) + -5.55.round(1, half: :down).should eql(-5.5) + -5.55.round(1, half: :even).should eql(-5.6) + end + + it "preserves cases where neighbouring floating pointer number increase the decimal places" do + 4.8100000000000005.round(5, half: nil).should eql(4.81) + 4.8100000000000005.round(5, half: :up).should eql(4.81) + 4.8100000000000005.round(5, half: :down).should eql(4.81) + 4.8100000000000005.round(5, half: :even).should eql(4.81) + -4.8100000000000005.round(5, half: nil).should eql(-4.81) + -4.8100000000000005.round(5, half: :up).should eql(-4.81) + -4.8100000000000005.round(5, half: :down).should eql(-4.81) + -4.8100000000000005.round(5, half: :even).should eql(-4.81) + 4.81.round(5, half: nil).should eql(4.81) + 4.81.round(5, half: :up).should eql(4.81) + 4.81.round(5, half: :down).should eql(4.81) + 4.81.round(5, half: :even).should eql(4.81) + -4.81.round(5, half: nil).should eql(-4.81) + -4.81.round(5, half: :up).should eql(-4.81) + -4.81.round(5, half: :down).should eql(-4.81) + -4.81.round(5, half: :even).should eql(-4.81) + 4.809999999999999.round(5, half: nil).should eql(4.81) + 4.809999999999999.round(5, half: :up).should eql(4.81) + 4.809999999999999.round(5, half: :down).should eql(4.81) + 4.809999999999999.round(5, half: :even).should eql(4.81) + -4.809999999999999.round(5, half: nil).should eql(-4.81) + -4.809999999999999.round(5, half: :up).should eql(-4.81) + -4.809999999999999.round(5, half: :down).should eql(-4.81) + -4.809999999999999.round(5, half: :even).should eql(-4.81) + end + + ruby_bug "", ""..."3.3" do + # These numbers are neighbouring floating point numbers round a + # precise value. They test that the rounding modes work correctly + # round that value and precision is not lost which might cause + # incorrect results. + it "does not lose precision during the rounding process" do + 767573.1875850001.round(5, half: nil).should eql(767573.18759) + 767573.1875850001.round(5, half: :up).should eql(767573.18759) + 767573.1875850001.round(5, half: :down).should eql(767573.18759) + 767573.1875850001.round(5, half: :even).should eql(767573.18759) + -767573.1875850001.round(5, half: nil).should eql(-767573.18759) + -767573.1875850001.round(5, half: :up).should eql(-767573.18759) + -767573.1875850001.round(5, half: :down).should eql(-767573.18759) + -767573.1875850001.round(5, half: :even).should eql(-767573.18759) + 767573.187585.round(5, half: nil).should eql(767573.18759) + 767573.187585.round(5, half: :up).should eql(767573.18759) + 767573.187585.round(5, half: :down).should eql(767573.18758) + 767573.187585.round(5, half: :even).should eql(767573.18758) + -767573.187585.round(5, half: nil).should eql(-767573.18759) + -767573.187585.round(5, half: :up).should eql(-767573.18759) + -767573.187585.round(5, half: :down).should eql(-767573.18758) + -767573.187585.round(5, half: :even).should eql(-767573.18758) + 767573.1875849998.round(5, half: nil).should eql(767573.18758) + 767573.1875849998.round(5, half: :up).should eql(767573.18758) + 767573.1875849998.round(5, half: :down).should eql(767573.18758) + 767573.1875849998.round(5, half: :even).should eql(767573.18758) + -767573.1875849998.round(5, half: nil).should eql(-767573.18758) + -767573.1875849998.round(5, half: :up).should eql(-767573.18758) + -767573.1875849998.round(5, half: :down).should eql(-767573.18758) + -767573.1875849998.round(5, half: :even).should eql(-767573.18758) + end end it "raises FloatDomainError for exceptional values with a half option" do diff --git a/spec/ruby/core/integer/chr_spec.rb b/spec/ruby/core/integer/chr_spec.rb index 3e13f45480..8fe20ff812 100644 --- a/spec/ruby/core/integer/chr_spec.rb +++ b/spec/ruby/core/integer/chr_spec.rb @@ -223,26 +223,25 @@ describe "Integer#chr with an encoding argument" do # #5864 it "raises RangeError if self is invalid as a codepoint in the specified encoding" do - [ [0x80, "US-ASCII"], - [0x0100, "BINARY"], - [0x0100, "EUC-JP"], - [0xA1A0, "EUC-JP"], - [0xA1, "EUC-JP"], - [0x80, "SHIFT_JIS"], - [0xE0, "SHIFT_JIS"], - [0x0100, "ISO-8859-9"], - [620, "TIS-620"], - [0xD800, "UTF-8"], - [0xDBFF, "UTF-8"], - [0xDC00, "UTF-8"], - [0xDFFF, "UTF-8"], - [0xD800, "UTF-16"], - [0xDBFF, "UTF-16"], - [0xDC00, "UTF-16"], - [0xDFFF, "UTF-16"], - ].each do |integer, encoding_name| - -> { integer.chr(encoding_name) }.should raise_error(RangeError) - end + -> { 0x80.chr("US-ASCII") }.should raise_error(RangeError) + -> { 0x0100.chr("BINARY") }.should raise_error(RangeError) + -> { 0x0100.chr("EUC-JP") }.should raise_error(RangeError) + -> { 0xA1A0.chr("EUC-JP") }.should raise_error(RangeError) + -> { 0xA1.chr("EUC-JP") }.should raise_error(RangeError) + -> { 0x80.chr("SHIFT_JIS") }.should raise_error(RangeError) + -> { 0xE0.chr("SHIFT_JIS") }.should raise_error(RangeError) + -> { 0x0100.chr("ISO-8859-9") }.should raise_error(RangeError) + -> { 620.chr("TIS-620") }.should raise_error(RangeError) + # UTF-16 surrogate range + -> { 0xD800.chr("UTF-8") }.should raise_error(RangeError) + -> { 0xDBFF.chr("UTF-8") }.should raise_error(RangeError) + -> { 0xDC00.chr("UTF-8") }.should raise_error(RangeError) + -> { 0xDFFF.chr("UTF-8") }.should raise_error(RangeError) + # UTF-16 surrogate range + -> { 0xD800.chr("UTF-16") }.should raise_error(RangeError) + -> { 0xDBFF.chr("UTF-16") }.should raise_error(RangeError) + -> { 0xDC00.chr("UTF-16") }.should raise_error(RangeError) + -> { 0xDFFF.chr("UTF-16") }.should raise_error(RangeError) end it 'returns a String encoding self interpreted as a codepoint in the CESU-8 encoding' do diff --git a/spec/ruby/core/integer/fdiv_spec.rb b/spec/ruby/core/integer/fdiv_spec.rb index d99a19eb0f..d9ea2fdf8d 100644 --- a/spec/ruby/core/integer/fdiv_spec.rb +++ b/spec/ruby/core/integer/fdiv_spec.rb @@ -55,6 +55,11 @@ describe "Integer#fdiv" do num.fdiv(den).should == -0.5555555555555556 end + it "rounds to the correct float for bignum denominators" do + 1.fdiv(10**324).should == 0.0 + 1.fdiv(10**323).should == 1.0e-323 + end + it "performs floating-point division between self and a Float" do 8.fdiv(9.0).should be_close(0.888888888888889, TOLERANCE) end diff --git a/spec/ruby/core/io/advise_spec.rb b/spec/ruby/core/io/advise_spec.rb index 0a845487e2..651fc52378 100644 --- a/spec/ruby/core/io/advise_spec.rb +++ b/spec/ruby/core/io/advise_spec.rb @@ -73,19 +73,9 @@ describe "IO#advise" do end end - platform_is :linux do + guard -> { platform_is :linux and kernel_version_is '3.6' } do # [ruby-core:65355] tmpfs is not supported it "supports the willneed advice type" do - require 'etc' - uname = if Etc.respond_to?(:uname) - Etc.uname[:release] - else - `uname -r`.chomp - end - if (uname.split('.').map(&:to_i) <=> [3,6]) < 0 - skip "[ruby-core:65355] tmpfs is not supported" - else - @io.advise(:willneed).should be_nil - end + @io.advise(:willneed).should be_nil end end diff --git a/spec/ruby/core/io/fixtures/classes.rb b/spec/ruby/core/io/fixtures/classes.rb index 5d81d5fcd9..067ab59d93 100644 --- a/spec/ruby/core/io/fixtures/classes.rb +++ b/spec/ruby/core/io/fixtures/classes.rb @@ -108,6 +108,14 @@ module IOSpecs "linha ", "cinco.\nHere ", "is ", "line ", "six.\n" ] end + def self.lines_space_separator_without_trailing_spaces + [ "Voici", "la", "ligne", "une.\nQui", + "\303\250", "la", "linea", "due.\n\n\nAqu\303\255", + "est\303\241", "la", "l\303\255nea", "tres.\nHier", + "ist", "Zeile", "vier.\n\nEst\303\241", "aqui", "a", + "linha", "cinco.\nHere", "is", "line", "six.\n" ] + end + def self.lines_arbitrary_separator [ "Voici la ligne une.\nQui \303\250", " la linea due.\n\n\nAqu\303\255 est\303\241 la l\303\255nea tres.\nHier ist Zeile vier.\n\nEst\303\241 aqui a linha cinco.\nHere is line six.\n" ] @@ -119,6 +127,12 @@ module IOSpecs "Est\303\241 aqui a linha cinco.\nHere is line six.\n" ] end + def self.paragraphs_without_trailing_new_line_characters + [ "Voici la ligne une.\nQui \303\250 la linea due.", + "Aqu\303\255 est\303\241 la l\303\255nea tres.\nHier ist Zeile vier.", + "Est\303\241 aqui a linha cinco.\nHere is line six.\n" ] + end + # Creates an IO instance for an existing fixture file. The # file should obviously not be deleted. def self.io_fixture(name, mode = "r:utf-8") diff --git a/spec/ruby/core/io/gets_spec.rb b/spec/ruby/core/io/gets_spec.rb index 42238f6201..b9f82f8133 100644 --- a/spec/ruby/core/io/gets_spec.rb +++ b/spec/ruby/core/io/gets_spec.rb @@ -119,6 +119,16 @@ describe "IO#gets" do it "returns the first line without a trailing newline character" do @io.gets(chomp: true).should == IOSpecs.lines_without_newline_characters[0] end + + ruby_version_is "3.0" do + it "raises exception when options passed as Hash" do + -> { @io.gets({ chomp: true }) }.should raise_error(TypeError) + + -> { + @io.gets("\n", 1, { chomp: true }) + }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)") + end + end end end @@ -200,6 +210,10 @@ describe "IO#gets" do it "reads all bytes when pass a separator and reading more than all bytes" do @io.gets("\t", 100).should == "one\n\ntwo\n\nthree\nfour\n" end + + it "returns empty string when 0 passed as a limit" do + @io.gets(0).should == "" + end end describe "IO#gets" do diff --git a/spec/ruby/core/io/readline_spec.rb b/spec/ruby/core/io/readline_spec.rb index 7cb1601816..ca30f31e39 100644 --- a/spec/ruby/core/io/readline_spec.rb +++ b/spec/ruby/core/io/readline_spec.rb @@ -43,9 +43,40 @@ describe "IO#readline" do end end + describe "when passed limit" do + it "reads limit bytes" do + @io.readline(3).should == "Voi" + end + + it "returns an empty string when passed 0 as a limit" do + @io.readline(0).should == "" + end + end + + describe "when passed separator and limit" do + it "reads limit bytes till the separator" do + # Voici la ligne une.\ + @io.readline(" ", 4).should == "Voic" + @io.readline(" ", 4).should == "i " + @io.readline(" ", 4).should == "la " + @io.readline(" ", 4).should == "lign" + @io.readline(" ", 4).should == "e " + end + end + describe "when passed chomp" do it "returns the first line without a trailing newline character" do @io.readline(chomp: true).should == IOSpecs.lines_without_newline_characters[0] end + + ruby_version_is "3.0" do + it "raises exception when options passed as Hash" do + -> { @io.readline({ chomp: true }) }.should raise_error(TypeError) + + -> { + @io.readline("\n", 1, { chomp: true }) + }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)") + end + end end end diff --git a/spec/ruby/core/io/readlines_spec.rb b/spec/ruby/core/io/readlines_spec.rb index 254f2927b5..15af6debbe 100644 --- a/spec/ruby/core/io/readlines_spec.rb +++ b/spec/ruby/core/io/readlines_spec.rb @@ -101,6 +101,28 @@ describe "IO#readlines" do @io.readlines(obj).should == IOSpecs.lines_r_separator end end + + describe "when passed limit" do + it "raises ArgumentError when passed 0 as a limit" do + -> { @io.readlines(0) }.should raise_error(ArgumentError) + end + end + + describe "when passed chomp" do + it "returns the first line without a trailing newline character" do + @io.readlines(chomp: true).should == IOSpecs.lines_without_newline_characters + end + + ruby_version_is "3.0" do + it "raises exception when options passed as Hash" do + -> { @io.readlines({ chomp: true }) }.should raise_error(TypeError) + + -> { + @io.readlines("\n", 1, { chomp: true }) + }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)") + end + end + end end describe "IO#readlines" do diff --git a/spec/ruby/core/io/shared/each.rb b/spec/ruby/core/io/shared/each.rb index 91766fbe03..badf8985e0 100644 --- a/spec/ruby/core/io/shared/each.rb +++ b/spec/ruby/core/io/shared/each.rb @@ -161,6 +161,54 @@ describe :io_each, shared: true do @io.send(@method, chomp: true) { |s| ScratchPad << s } ScratchPad.recorded.should == IOSpecs.lines_without_newline_characters end + + ruby_version_is "3.0" do + it "raises exception when options passed as Hash" do + -> { + @io.send(@method, { chomp: true }) { |s| } + }.should raise_error(TypeError) + + -> { + @io.send(@method, "\n", 1, { chomp: true }) { |s| } + }.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)") + end + end + end + + describe "when passed chomp and a separator" do + it "yields each line without separator to the passed block" do + @io.send(@method, " ", chomp: true) { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.lines_space_separator_without_trailing_spaces + end + end + + describe "when passed chomp and empty line as a separator" do + it "yields each paragraph without trailing new line characters" do + @io.send(@method, "", 1024, chomp: true) { |s| ScratchPad << s } + ScratchPad.recorded.should == IOSpecs.paragraphs_without_trailing_new_line_characters + end + end + + describe "when passed chomp and nil as a separator" do + it "yields self's content without trailing new line character" do + @io.pos = 100 + @io.send(@method, nil, chomp: true) { |s| ScratchPad << s } + ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six."] + end + end + + describe "when passed chomp, nil as a separator, and a limit" do + it "yields each line of limit size without truncating trailing new line character" do + # 43 - is a size of the 1st paragraph in the file + @io.send(@method, nil, 43, chomp: true) { |s| ScratchPad << s } + + ScratchPad.recorded.should == [ + "Voici la ligne une.\nQui è la linea due.\n\n\n", + "Aquí está la línea tres.\n" + "Hier ist Zeile ", + "vier.\n\nEstá aqui a linha cinco.\nHere is li", + "ne six.\n" + ] + end end end diff --git a/spec/ruby/core/io/shared/readlines.rb b/spec/ruby/core/io/shared/readlines.rb index 52b20364ef..479452b71c 100644 --- a/spec/ruby/core/io/shared/readlines.rb +++ b/spec/ruby/core/io/shared/readlines.rb @@ -78,6 +78,14 @@ describe :io_readlines_options_19, shared: true do result = IO.send(@method, @name, -2, &@object) (result ? result : ScratchPad.recorded).should == IOSpecs.lines end + + ruby_bug "#18767", ""..."3.3" do + describe "when passed limit" do + it "raises ArgumentError when passed 0 as a limit" do + -> { IO.send(@method, @name, 0, &@object) }.should raise_error(ArgumentError) + end + end + end end describe "when the object is a String" do @@ -92,31 +100,35 @@ describe :io_readlines_options_19, shared: true do end end - describe "when the object is a Hash" do - it "uses the value as the options hash" do - result = IO.send(@method, @name, mode: "r", &@object) - (result ? result : ScratchPad.recorded).should == IOSpecs.lines + describe "when the object is an options Hash" do + ruby_version_is "3.0" do + it "raises TypeError exception" do + -> { + IO.send(@method, @name, { chomp: true }, &@object) + }.should raise_error(TypeError) + end end end - end - describe "when passed name, object, object" do - describe "when the first object is an Integer" do - it "uses the second object as an options Hash" do - -> do - IO.send(@method, @filename, 10, mode: "w", &@object) - end.should raise_error(IOError) - end + describe "when the object is neither Integer nor String" do + it "raises TypeError exception" do + obj = mock("not io readlines limit") - it "calls #to_hash to convert the second object to a Hash" do - options = mock("io readlines options Hash") - options.should_receive(:to_hash).and_return({ mode: "w" }) - -> do - IO.send(@method, @filename, 10, **options, &@object) - end.should raise_error(IOError) + -> { + IO.send(@method, @name, obj, &@object) + }.should raise_error(TypeError) end end + end + describe "when passed name, keyword arguments" do + it "uses the keyword arguments as options" do + result = IO.send(@method, @name, mode: "r", &@object) + (result ? result : ScratchPad.recorded).should == IOSpecs.lines + end + end + + describe "when passed name, object, object" do describe "when the first object is a String" do it "uses the second object as a limit if it is an Integer" do result = IO.send(@method, @name, " ", 10, &@object) @@ -129,32 +141,18 @@ describe :io_readlines_options_19, shared: true do result = IO.send(@method, @name, " ", limit, &@object) (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit end - - it "uses the second object as an options Hash" do - -> do - IO.send(@method, @filename, " ", mode: "w", &@object) - end.should raise_error(IOError) - end - - it "calls #to_hash to convert the second object to a Hash" do - options = mock("io readlines options Hash") - options.should_receive(:to_hash).and_return({ mode: "w" }) - -> do - IO.send(@method, @filename, " ", **options, &@object) - end.should raise_error(IOError) - end end describe "when the first object is not a String or Integer" do it "calls #to_str to convert the object to a String" do sep = mock("io readlines separator") sep.should_receive(:to_str).at_least(1).and_return(" ") - result = IO.send(@method, @name, sep, 10, mode: "r", &@object) + result = IO.send(@method, @name, sep, 10, &@object) (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit end it "uses the second object as a limit if it is an Integer" do - result = IO.send(@method, @name, " ", 10, mode: "r", &@object) + result = IO.send(@method, @name, " ", 10, &@object) (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit end @@ -164,24 +162,59 @@ describe :io_readlines_options_19, shared: true do result = IO.send(@method, @name, " ", limit, &@object) (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit end + end + + describe "when the second object is neither Integer nor String" do + it "raises TypeError exception" do + obj = mock("not io readlines limit") + + -> { + IO.send(@method, @name, " ", obj, &@object) + }.should raise_error(TypeError) + end + end + + describe "when the second object is an options Hash" do + ruby_version_is "3.0" do + it "raises TypeError exception" do + -> { + IO.send(@method, @name, "", { chomp: true }, &@object) + }.should raise_error(TypeError) + end + end + end + end + + describe "when passed name, object, keyword arguments" do + describe "when the first object is an Integer" do + it "uses the keyword arguments as options" do + -> do + IO.send(@method, @filename, 10, mode: "w", &@object) + end.should raise_error(IOError) + end + end - it "uses the second object as an options Hash" do + describe "when the first object is a String" do + it "uses the keyword arguments as options" do -> do IO.send(@method, @filename, " ", mode: "w", &@object) end.should raise_error(IOError) end + end + + describe "when the first object is not a String or Integer" do + it "uses the keyword arguments as options" do + sep = mock("io readlines separator") + sep.should_receive(:to_str).at_least(1).and_return(" ") - it "calls #to_hash to convert the second object to a Hash" do - options = mock("io readlines options Hash") - options.should_receive(:to_hash).and_return({ mode: "w" }) -> do - IO.send(@method, @filename, " ", **options, &@object) + IO.send(@method, @filename, sep, mode: "w", &@object) end.should raise_error(IOError) end end end - describe "when passed name, separator, limit, options" do + describe "when passed name, separator, limit, keyword arguments" do it "calls #to_path to convert the name object" do name = mock("io name to_path") name.should_receive(:to_path).and_return(@name) @@ -203,12 +236,24 @@ describe :io_readlines_options_19, shared: true do (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit end - it "calls #to_hash to convert the options object" do - options = mock("io readlines options Hash") - options.should_receive(:to_hash).and_return({ mode: "w" }) + it "uses the keyword arguments as options" do -> do - IO.send(@method, @filename, " ", 10, **options, &@object) + IO.send(@method, @filename, " ", 10, mode: "w", &@object) end.should raise_error(IOError) end + + describe "when passed chomp, nil as a separator, and a limit" do + it "yields each line of limit size without truncating trailing new line character" do + # 43 - is a size of the 1st paragraph in the file + result = IO.send(@method, @name, nil, 43, chomp: true, &@object) + + (result ? result : ScratchPad.recorded).should == [ + "Voici la ligne une.\nQui è la linea due.\n\n\n", + "Aquí está la línea tres.\n" + "Hier ist Zeile ", + "vier.\n\nEstá aqui a linha cinco.\nHere is li", + "ne six.\n" + ] + end + end end end diff --git a/spec/ruby/core/kernel/instance_variable_get_spec.rb b/spec/ruby/core/kernel/instance_variable_get_spec.rb index bb6f03d3bf..f1d2a45df8 100644 --- a/spec/ruby/core/kernel/instance_variable_get_spec.rb +++ b/spec/ruby/core/kernel/instance_variable_get_spec.rb @@ -67,6 +67,12 @@ describe "Kernel#instance_variable_get when passed Symbol" do it "raises a NameError when the passed Symbol is an invalid instance variable name" do -> { @obj.instance_variable_get(:"@0") }.should raise_error(NameError) end + + it "returns nil or raises for frozen objects" do + nil.instance_variable_get(:@foo).should == nil + -> { nil.instance_variable_get(:foo) }.should raise_error(NameError) + :foo.instance_variable_get(:@foo).should == nil + end end describe "Kernel#instance_variable_get when passed String" do diff --git a/spec/ruby/core/kernel/instance_variable_set_spec.rb b/spec/ruby/core/kernel/instance_variable_set_spec.rb index dbd257f7b9..2c25f4366f 100644 --- a/spec/ruby/core/kernel/instance_variable_set_spec.rb +++ b/spec/ruby/core/kernel/instance_variable_set_spec.rb @@ -95,5 +95,11 @@ describe "Kernel#instance_variable_set" do o.instance_variable_set(:@💙, 42) o.instance_variable_get(:@💙).should == 42 end + + it "raises for frozen objects" do + -> { nil.instance_variable_set(:@foo, 42) }.should raise_error(FrozenError) + -> { nil.instance_variable_set(:foo, 42) }.should raise_error(NameError) + -> { :foo.instance_variable_set(:@foo, 42) }.should raise_error(FrozenError) + end end end diff --git a/spec/ruby/core/kernel/remove_instance_variable_spec.rb b/spec/ruby/core/kernel/remove_instance_variable_spec.rb index e90efc8aed..4e5ba5e018 100644 --- a/spec/ruby/core/kernel/remove_instance_variable_spec.rb +++ b/spec/ruby/core/kernel/remove_instance_variable_spec.rb @@ -41,6 +41,19 @@ describe "Kernel#remove_instance_variable" do end.should raise_error(TypeError) end + it "raises a FrozenError if self is frozen" do + o = Object.new + o.freeze + -> { o.remove_instance_variable(:@foo) }.should raise_error(FrozenError) + -> { o.remove_instance_variable(:foo) }.should raise_error(NameError) + end + + it "raises for frozen objects" do + -> { nil.remove_instance_variable(:@foo) }.should raise_error(FrozenError) + -> { nil.remove_instance_variable(:foo) }.should raise_error(NameError) + -> { :foo.remove_instance_variable(:@foo) }.should raise_error(FrozenError) + end + describe "when passed a String" do it_behaves_like :kernel_remove_instance_variable, nil, "@greeting" end diff --git a/spec/ruby/core/kernel/shared/require.rb b/spec/ruby/core/kernel/shared/require.rb index cf01b9dc52..666ca15e11 100644 --- a/spec/ruby/core/kernel/shared/require.rb +++ b/spec/ruby/core/kernel/shared/require.rb @@ -568,6 +568,25 @@ describe :kernel_require, shared: true do -> { @object.require("unicode_normalize") }.should raise_error(LoadError) end + + ruby_version_is "3.0" do + it "does not load a file earlier on the $LOAD_PATH when other similar features were already loaded" do + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, "../code/load_fixture").should be_true + end + ScratchPad.recorded.should == [:loaded] + + $LOAD_PATH.unshift "#{CODE_LOADING_DIR}/b" + # This loads because the above load was not on the $LOAD_PATH + @object.send(@method, "load_fixture").should be_true + ScratchPad.recorded.should == [:loaded, :loaded] + + $LOAD_PATH.unshift "#{CODE_LOADING_DIR}/c" + # This does not load because the above load was on the $LOAD_PATH + @object.send(@method, "load_fixture").should be_false + ScratchPad.recorded.should == [:loaded, :loaded] + end + end end describe "(shell expansion)" do diff --git a/spec/ruby/core/math/ldexp_spec.rb b/spec/ruby/core/math/ldexp_spec.rb index fb7799cf26..6dcf94a663 100644 --- a/spec/ruby/core/math/ldexp_spec.rb +++ b/spec/ruby/core/math/ldexp_spec.rb @@ -45,6 +45,12 @@ describe "Math.ldexp" do it "accepts any second argument that can be coerced with Integer()" do Math.ldexp(3.23, MathSpecs::Integer.new).should be_close(12.92, TOLERANCE) end + + it "returns correct value that closes to the max value of double type" do + Math.ldexp(0.5122058490966879, 1024).should == 9.207889385574391e+307 + Math.ldexp(0.9999999999999999, 1024).should == 1.7976931348623157e+308 + Math.ldexp(0.99999999999999999, 1024).should == Float::INFINITY + end end describe "Math#ldexp" do diff --git a/spec/ruby/core/module/class_variables_spec.rb b/spec/ruby/core/module/class_variables_spec.rb index fd7aa93aa8..e155f1deac 100644 --- a/spec/ruby/core/module/class_variables_spec.rb +++ b/spec/ruby/core/module/class_variables_spec.rb @@ -23,4 +23,12 @@ describe "Module#class_variables" do c.extend ModuleSpecs::MVars c.class_variables.should_not include(:@@mvar) end + + it "returns the correct class variables when inherit is given" do + ModuleSpecs::SubCVars.class_variables(false).should == [:@@sub] + ModuleSpecs::SubCVars.new.singleton_class.class_variables(false).should == [] + + ModuleSpecs::SubCVars.class_variables(true).should == [:@@sub, :@@cls, :@@meta] + ModuleSpecs::SubCVars.new.singleton_class.class_variables(true).should == [:@@sub, :@@cls, :@@meta] + end end diff --git a/spec/ruby/core/module/fixtures/classes.rb b/spec/ruby/core/module/fixtures/classes.rb index 40777cdbbd..a64f672b2f 100644 --- a/spec/ruby/core/module/fixtures/classes.rb +++ b/spec/ruby/core/module/fixtures/classes.rb @@ -352,6 +352,10 @@ module ModuleSpecs end end + class SubCVars < CVars + @@sub = :sub + end + module MVars @@mvar = :mvar end diff --git a/spec/ruby/core/process/clock_gettime_spec.rb b/spec/ruby/core/process/clock_gettime_spec.rb index 59e1406e02..f00b8db043 100644 --- a/spec/ruby/core/process/clock_gettime_spec.rb +++ b/spec/ruby/core/process/clock_gettime_spec.rb @@ -52,7 +52,7 @@ describe "Process.clock_gettime" do end # These specs need macOS 10.12+ / darwin 16+ - guard_not -> { platform_is_not(:darwin) or RUBY_PLATFORM[/darwin\d+/].to_i >= 16 } do + guard -> { platform_is_not(:darwin) or kernel_version_is '16' } do platform_is :linux, :openbsd, :darwin do it "CLOCK_PROCESS_CPUTIME_ID" do Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID).should be_an_instance_of(Float) @@ -65,20 +65,6 @@ describe "Process.clock_gettime" do end end - platform_is :freebsd, :openbsd do - it "CLOCK_VIRTUAL" do - Process.clock_gettime(Process::CLOCK_VIRTUAL).should be_an_instance_of(Float) - end - - it "CLOCK_PROF" do - Process.clock_gettime(Process::CLOCK_PROF).should be_an_instance_of(Float) - end - - it "CLOCK_UPTIME" do - Process.clock_gettime(Process::CLOCK_UPTIME).should be_an_instance_of(Float) - end - end - platform_is :linux, :darwin do it "CLOCK_MONOTONIC_RAW" do Process.clock_gettime(Process::CLOCK_MONOTONIC_RAW).should be_an_instance_of(Float) @@ -95,42 +81,69 @@ describe "Process.clock_gettime" do Process.clock_gettime(Process::CLOCK_UPTIME_RAW_APPROX).should be_an_instance_of(Float) end end + end - platform_is :freebsd do - it "CLOCK_REALTIME_FAST and CLOCK_REALTIME_PRECISE" do - Process.clock_gettime(Process::CLOCK_REALTIME_FAST).should be_an_instance_of(Float) - Process.clock_gettime(Process::CLOCK_REALTIME_PRECISE).should be_an_instance_of(Float) - end + platform_is :freebsd, :openbsd do + it "CLOCK_VIRTUAL" do + Process.clock_gettime(Process::CLOCK_VIRTUAL).should be_an_instance_of(Float) + end - it "CLOCK_MONOTONIC_FAST and CLOCK_MONOTONIC_PRECISE" do - Process.clock_gettime(Process::CLOCK_MONOTONIC_FAST).should be_an_instance_of(Float) - Process.clock_gettime(Process::CLOCK_MONOTONIC_PRECISE).should be_an_instance_of(Float) - end + it "CLOCK_PROF" do + Process.clock_gettime(Process::CLOCK_PROF).should be_an_instance_of(Float) + end - it "CLOCK_UPTIME_FAST and CLOCK_UPTIME_PRECISE" do - Process.clock_gettime(Process::CLOCK_UPTIME_FAST).should be_an_instance_of(Float) - Process.clock_gettime(Process::CLOCK_UPTIME_PRECISE).should be_an_instance_of(Float) - end + it "CLOCK_UPTIME" do + Process.clock_gettime(Process::CLOCK_UPTIME).should be_an_instance_of(Float) + end + end - it "CLOCK_SECOND" do - Process.clock_gettime(Process::CLOCK_SECOND).should be_an_instance_of(Float) - end + platform_is :freebsd do + it "CLOCK_REALTIME_FAST and CLOCK_REALTIME_PRECISE" do + Process.clock_gettime(Process::CLOCK_REALTIME_FAST).should be_an_instance_of(Float) + Process.clock_gettime(Process::CLOCK_REALTIME_PRECISE).should be_an_instance_of(Float) end - platform_is :linux do - it "CLOCK_REALTIME_COARSE and CLOCK_REALTIME_ALARM" do - Process.clock_gettime(Process::CLOCK_REALTIME_COARSE).should be_an_instance_of(Float) - Process.clock_gettime(Process::CLOCK_REALTIME_ALARM).should be_an_instance_of(Float) - end + it "CLOCK_MONOTONIC_FAST and CLOCK_MONOTONIC_PRECISE" do + Process.clock_gettime(Process::CLOCK_MONOTONIC_FAST).should be_an_instance_of(Float) + Process.clock_gettime(Process::CLOCK_MONOTONIC_PRECISE).should be_an_instance_of(Float) + end - it "CLOCK_MONOTONIC_COARSE" do - Process.clock_gettime(Process::CLOCK_MONOTONIC_COARSE).should be_an_instance_of(Float) - end + it "CLOCK_UPTIME_FAST and CLOCK_UPTIME_PRECISE" do + Process.clock_gettime(Process::CLOCK_UPTIME_FAST).should be_an_instance_of(Float) + Process.clock_gettime(Process::CLOCK_UPTIME_PRECISE).should be_an_instance_of(Float) + end - it "CLOCK_BOOTTIME and CLOCK_BOOTTIME_ALARM" do - Process.clock_gettime(Process::CLOCK_BOOTTIME).should be_an_instance_of(Float) - Process.clock_gettime(Process::CLOCK_BOOTTIME_ALARM).should be_an_instance_of(Float) - end + it "CLOCK_SECOND" do + Process.clock_gettime(Process::CLOCK_SECOND).should be_an_instance_of(Float) + end + end + + guard -> { platform_is :linux and kernel_version_is '2.6.32' } do + it "CLOCK_REALTIME_COARSE" do + Process.clock_gettime(Process::CLOCK_REALTIME_COARSE).should be_an_instance_of(Float) + end + + it "CLOCK_MONOTONIC_COARSE" do + Process.clock_gettime(Process::CLOCK_MONOTONIC_COARSE).should be_an_instance_of(Float) + end + end + + guard -> { platform_is :linux and kernel_version_is '2.6.39' } do + it "CLOCK_BOOTTIME" do + skip "No Process::CLOCK_BOOTTIME" unless defined?(Process::CLOCK_BOOTTIME) + Process.clock_gettime(Process::CLOCK_BOOTTIME).should be_an_instance_of(Float) + end + end + + guard -> { platform_is "x86_64-linux" and kernel_version_is '3.0' } do + it "CLOCK_REALTIME_ALARM" do + skip "No Process::CLOCK_REALTIME_ALARM" unless defined?(Process::CLOCK_REALTIME_ALARM) + Process.clock_gettime(Process::CLOCK_REALTIME_ALARM).should be_an_instance_of(Float) + end + + it "CLOCK_BOOTTIME_ALARM" do + skip "No Process::CLOCK_BOOTTIME_ALARM" unless defined?(Process::CLOCK_BOOTTIME_ALARM) + Process.clock_gettime(Process::CLOCK_BOOTTIME_ALARM).should be_an_instance_of(Float) end end end diff --git a/spec/ruby/core/process/egid_spec.rb b/spec/ruby/core/process/egid_spec.rb index 24dda43804..a67b623d5c 100644 --- a/spec/ruby/core/process/egid_spec.rb +++ b/spec/ruby/core/process/egid_spec.rb @@ -15,5 +15,44 @@ describe "Process.egid" do end describe "Process.egid=" do - it "needs to be reviewed for spec completeness" + + platform_is_not :windows do + it "raises TypeError if not passed an Integer or String" do + -> { Process.egid = Object.new }.should raise_error(TypeError) + end + + it "sets the effective group id to its own gid if given the username corresponding to its own gid" do + raise unless Process.gid == Process.egid + + require "etc" + group = Etc.getgrgid(Process.gid).name + + Process.egid = group + Process.egid.should == Process.gid + end + + as_user do + it "raises Errno::ERPERM if run by a non superuser trying to set the root group id" do + -> { Process.egid = 0 }.should raise_error(Errno::EPERM) + end + + platform_is :linux do + it "raises Errno::ERPERM if run by a non superuser trying to set the group id from group name" do + -> { Process.egid = "root" }.should raise_error(Errno::EPERM) + end + end + end + + as_superuser do + context "when ran by a superuser" do + it "sets the effective group id for the current process if run by a superuser" do + code = <<-RUBY + Process.egid = 1 + puts Process.egid + RUBY + ruby_exe(code).should == "1\n" + end + end + end + end end diff --git a/spec/ruby/core/process/euid_spec.rb b/spec/ruby/core/process/euid_spec.rb index a2f1bbf42e..c1ec4171d0 100644 --- a/spec/ruby/core/process/euid_spec.rb +++ b/spec/ruby/core/process/euid_spec.rb @@ -21,9 +21,19 @@ describe "Process.euid=" do -> { Process.euid = Object.new }.should raise_error(TypeError) end + it "sets the effective user id to its own uid if given the username corresponding to its own uid" do + raise unless Process.uid == Process.euid + + require "etc" + user = Etc.getpwuid(Process.uid).name + + Process.euid = user + Process.euid.should == Process.uid + end + as_user do it "raises Errno::ERPERM if run by a non superuser trying to set the superuser id" do - -> { (Process.euid = 0)}.should raise_error(Errno::EPERM) + -> { Process.euid = 0 }.should raise_error(Errno::EPERM) end it "raises Errno::ERPERM if run by a non superuser trying to set the superuser id from username" do diff --git a/spec/ruby/core/process/spawn_spec.rb b/spec/ruby/core/process/spawn_spec.rb index 6be3f41a87..9aa8da8125 100644 --- a/spec/ruby/core/process/spawn_spec.rb +++ b/spec/ruby/core/process/spawn_spec.rb @@ -349,7 +349,7 @@ describe "Process.spawn" do pgid = Process.getpgid(Process.pid) # The process group is not available on all platforms. # See "man proc" - /proc/[pid]/stat - (5) pgrp - # In Travis arm64 environment, the value is 0. + # In Travis aarch64 environment, the value is 0. # # $ cat /proc/[pid]/stat # 19179 (ruby) S 19160 0 0 ... diff --git a/spec/ruby/core/process/status/equal_value_spec.rb b/spec/ruby/core/process/status/equal_value_spec.rb index d85bb22214..d8a2be26b8 100644 --- a/spec/ruby/core/process/status/equal_value_spec.rb +++ b/spec/ruby/core/process/status/equal_value_spec.rb @@ -8,7 +8,7 @@ describe "Process::Status#==" do end it "returns true when compared to the integer status of a terminated child" do - ruby_exe("Process.kill(:KILL, $$); exit(29)", exit_status: platform_is(:windows) ? 0 : nil) + ruby_exe("Process.kill(:KILL, $$); exit(29)", exit_status: platform_is(:windows) ? 0 : :SIGKILL) $?.to_i.should == $? $?.should == $?.to_i end diff --git a/spec/ruby/core/process/status/exited_spec.rb b/spec/ruby/core/process/status/exited_spec.rb index 059cd5b1aa..a61292b146 100644 --- a/spec/ruby/core/process/status/exited_spec.rb +++ b/spec/ruby/core/process/status/exited_spec.rb @@ -14,7 +14,7 @@ describe "Process::Status#exited?" do describe "for a terminated child" do before :each do - ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : nil) + ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : :SIGKILL) end platform_is_not :windows do diff --git a/spec/ruby/core/process/status/exitstatus_spec.rb b/spec/ruby/core/process/status/exitstatus_spec.rb index 3087bd619e..5c86c2b3c8 100644 --- a/spec/ruby/core/process/status/exitstatus_spec.rb +++ b/spec/ruby/core/process/status/exitstatus_spec.rb @@ -11,7 +11,7 @@ describe "Process::Status#exitstatus" do describe "for a child that raised SignalException" do before :each do - ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : nil) + ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : :SIGKILL) end platform_is_not :windows do diff --git a/spec/ruby/core/process/status/signaled_spec.rb b/spec/ruby/core/process/status/signaled_spec.rb index 389092a533..c0de7b8006 100644 --- a/spec/ruby/core/process/status/signaled_spec.rb +++ b/spec/ruby/core/process/status/signaled_spec.rb @@ -13,7 +13,7 @@ describe "Process::Status#signaled?" do describe "for a terminated child" do before :each do - ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : nil) + ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : :SIGKILL) end platform_is_not :windows do diff --git a/spec/ruby/core/process/status/success_spec.rb b/spec/ruby/core/process/status/success_spec.rb index c531121f08..3589cc611f 100644 --- a/spec/ruby/core/process/status/success_spec.rb +++ b/spec/ruby/core/process/status/success_spec.rb @@ -23,7 +23,7 @@ describe "Process::Status#success?" do describe "for a child that was terminated" do before :each do - ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : nil) + ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : :SIGKILL) end platform_is_not :windows do diff --git a/spec/ruby/core/process/status/termsig_spec.rb b/spec/ruby/core/process/status/termsig_spec.rb index 1c87a6f455..5d286950f8 100644 --- a/spec/ruby/core/process/status/termsig_spec.rb +++ b/spec/ruby/core/process/status/termsig_spec.rb @@ -13,7 +13,7 @@ describe "Process::Status#termsig" do describe "for a child that raised SignalException" do before :each do - ruby_exe("raise SignalException, 'SIGTERM'", exit_status: nil) + ruby_exe("raise SignalException, 'SIGTERM'", exit_status: :SIGTERM) end platform_is_not :windows do @@ -25,7 +25,7 @@ describe "Process::Status#termsig" do describe "for a child that was sent a signal" do before :each do - ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : nil) + ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : :SIGKILL) end platform_is_not :windows do diff --git a/spec/ruby/core/process/status/to_i_spec.rb b/spec/ruby/core/process/status/to_i_spec.rb index 7cde9b915b..39f8e2d84c 100644 --- a/spec/ruby/core/process/status/to_i_spec.rb +++ b/spec/ruby/core/process/status/to_i_spec.rb @@ -7,7 +7,7 @@ describe "Process::Status#to_i" do end it "returns an integer when the child is signaled" do - ruby_exe('raise SignalException, "TERM"', exit_status: platform_is(:windows) ? 3 : nil) + ruby_exe('raise SignalException, "TERM"', exit_status: platform_is(:windows) ? 3 : :SIGTERM) $?.to_i.should be_an_instance_of(Integer) end end diff --git a/spec/ruby/core/regexp/shared/quote.rb b/spec/ruby/core/regexp/shared/quote.rb index a55adb5bf2..33bdfd9979 100644 --- a/spec/ruby/core/regexp/shared/quote.rb +++ b/spec/ruby/core/regexp/shared/quote.rb @@ -12,6 +12,11 @@ describe :regexp_quote, shared: true do Regexp.send(@method, :symbol).should == 'symbol' end + it "works with substrings" do + str = ".+[]()"[1...-1] + Regexp.send(@method, str).should == '\+\[\]\(' + end + it "sets the encoding of the result to US-ASCII if there are only US-ASCII characters present in the input String" do str = "abc".force_encoding("euc-jp") Regexp.send(@method, str).encoding.should == Encoding::US_ASCII diff --git a/spec/ruby/core/signal/trap_spec.rb b/spec/ruby/core/signal/trap_spec.rb index 3c78922694..2a94a81634 100644 --- a/spec/ruby/core/signal/trap_spec.rb +++ b/spec/ruby/core/signal/trap_spec.rb @@ -254,12 +254,10 @@ describe "Signal.trap" do r.close loop { w.write("a"*1024) } RUBY - out = ruby_exe(code, exit_status: nil) + out = ruby_exe(code, exit_status: :SIGPIPE) status = $? out.should == "nil\n" status.should.signaled? - status.termsig.should be_kind_of(Integer) - Signal.signame(status.termsig).should == "PIPE" end end diff --git a/spec/ruby/core/string/capitalize_spec.rb b/spec/ruby/core/string/capitalize_spec.rb index ca8df88fec..8afaefc021 100644 --- a/spec/ruby/core/string/capitalize_spec.rb +++ b/spec/ruby/core/string/capitalize_spec.rb @@ -35,6 +35,10 @@ describe "String#capitalize" do it "does not capitalize non-ASCII characters" do "ßet".capitalize(:ascii).should == "ßet" end + + it "handles non-ASCII substrings properly" do + "garçon"[1..-1].capitalize(:ascii).should == "Arçon" + end end describe "full Unicode case mapping adapted for Turkic languages" do diff --git a/spec/ruby/core/string/dup_spec.rb b/spec/ruby/core/string/dup_spec.rb index d650788210..eec3cf0a70 100644 --- a/spec/ruby/core/string/dup_spec.rb +++ b/spec/ruby/core/string/dup_spec.rb @@ -49,4 +49,13 @@ describe "String#dup" do orig.should == "xtring" dup.should == "string" end + + it "does not modify the original setbyte-mutated string when changing dupped string" do + orig = "a" + orig.setbyte 0, "b".ord + copy = orig.dup + orig.setbyte 0, "c".ord + orig.should == "c" + copy.should == "b" + end end diff --git a/spec/ruby/core/string/insert_spec.rb b/spec/ruby/core/string/insert_spec.rb index db42a37941..0c87df3a95 100644 --- a/spec/ruby/core/string/insert_spec.rb +++ b/spec/ruby/core/string/insert_spec.rb @@ -69,4 +69,13 @@ describe "String#insert with index, other" do "あれ".insert 0, pat end.should raise_error(Encoding::CompatibilityError) end + + it "should not call subclassed string methods" do + cls = Class.new(String) do + def replace(arg) + raise "should not call replace" + end + end + cls.new("abcd").insert(0, 'X').should == "Xabcd" + end end diff --git a/spec/ruby/core/string/lstrip_spec.rb b/spec/ruby/core/string/lstrip_spec.rb index 6e868eac4f..02bc6b4322 100644 --- a/spec/ruby/core/string/lstrip_spec.rb +++ b/spec/ruby/core/string/lstrip_spec.rb @@ -50,4 +50,10 @@ describe "String#lstrip!" do -> { "hello".freeze.lstrip! }.should raise_error(FrozenError) -> { "".freeze.lstrip! }.should raise_error(FrozenError) end + + it "raises an ArgumentError if the first codepoint is invalid" do + s = "\xDFabc".force_encoding(Encoding::UTF_8) + s.valid_encoding?.should be_false + -> { s.lstrip! }.should raise_error(ArgumentError) + end end diff --git a/spec/ruby/core/string/rstrip_spec.rb b/spec/ruby/core/string/rstrip_spec.rb index 2dc55182ae..dc34b12719 100644 --- a/spec/ruby/core/string/rstrip_spec.rb +++ b/spec/ruby/core/string/rstrip_spec.rb @@ -46,4 +46,10 @@ describe "String#rstrip!" do -> { "hello".freeze.rstrip! }.should raise_error(FrozenError) -> { "".freeze.rstrip! }.should raise_error(FrozenError) end + + it "raises an ArgumentError if the last codepoint is invalid" do + s = "abc\xDF".force_encoding(Encoding::UTF_8) + s.valid_encoding?.should be_false + -> { s.rstrip! }.should raise_error(ArgumentError) + end end diff --git a/spec/ruby/core/string/scrub_spec.rb b/spec/ruby/core/string/scrub_spec.rb index 3137399291..66755bcc7b 100644 --- a/spec/ruby/core/string/scrub_spec.rb +++ b/spec/ruby/core/string/scrub_spec.rb @@ -14,6 +14,11 @@ describe "String#scrub with a default replacement" do "abc\u3042#{x81}".scrub.should == "abc\u3042\uFFFD" end + it "replaces invalid byte sequences in lazy substrings" do + x81 = [0x81].pack('C').force_encoding('utf-8') + "abc\u3042#{x81}def"[1...-1].scrub.should == "bc\u3042\uFFFDde" + end + it "returns a copy of self when the input encoding is BINARY" do input = "foo".encode('BINARY') diff --git a/spec/ruby/core/string/shared/slice.rb b/spec/ruby/core/string/shared/slice.rb index 228af5f824..713234fffd 100644 --- a/spec/ruby/core/string/shared/slice.rb +++ b/spec/ruby/core/string/shared/slice.rb @@ -375,10 +375,20 @@ describe :string_slice_regexp_index, shared: true do "hello there".send(@method, /(what?)/, 1).should == nil end + it "returns nil if the index is larger than the number of captures" do + "hello there".send(@method, /hello (.)/, 2).should == nil + # You can't refer to 0 using negative indices + "hello there".send(@method, /hello (.)/, -2).should == nil + end + it "returns nil if there is no capture for the given index" do "hello there".send(@method, /[aeiou](.)\1/, 2).should == nil - # You can't refer to 0 using negative indices - "hello there".send(@method, /[aeiou](.)\1/, -2).should == nil + end + + it "returns nil if the given capture group was not matched but still sets $~" do + "test".send(@method, /te(z)?/, 1).should == nil + $~[0].should == "te" + $~[1].should == nil end it "calls to_int on the given index" do diff --git a/spec/ruby/core/string/split_spec.rb b/spec/ruby/core/string/split_spec.rb index 82911ef50b..7ef34c65da 100644 --- a/spec/ruby/core/string/split_spec.rb +++ b/spec/ruby/core/string/split_spec.rb @@ -3,12 +3,17 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "String#split with String" do + it "throws an ArgumentError if the string is not a valid" do + s = "\xDF".force_encoding(Encoding::UTF_8) + + -> { s.split }.should raise_error(ArgumentError) + -> { s.split(':') }.should raise_error(ArgumentError) + end + it "throws an ArgumentError if the pattern is not a valid string" do str = 'проверка' - broken_str = 'проверка' - broken_str.force_encoding('binary') - broken_str.chop! - broken_str.force_encoding('utf-8') + broken_str = "\xDF".force_encoding(Encoding::UTF_8) + -> { str.split(broken_str) }.should raise_error(ArgumentError) end @@ -218,6 +223,12 @@ describe "String#split with String" do end describe "String#split with Regexp" do + it "throws an ArgumentError if the string is not a valid" do + s = "\xDF".force_encoding(Encoding::UTF_8) + + -> { s.split(/./) }.should raise_error(ArgumentError) + end + it "divides self on regexp matches" do " now's the time".split(/ /).should == ["", "now's", "", "the", "time"] " x\ny ".split(/ /).should == ["", "x\ny"] diff --git a/spec/ruby/core/string/unpack/z_spec.rb b/spec/ruby/core/string/unpack/z_spec.rb index 552851ce04..ce8da4b29e 100644 --- a/spec/ruby/core/string/unpack/z_spec.rb +++ b/spec/ruby/core/string/unpack/z_spec.rb @@ -20,4 +20,9 @@ describe "String#unpack with format 'Z'" do ["\x00a\x00 bc \x00", ["", "c"]] ].should be_computed_by(:unpack, "Z5Z") end + + it "does not advance past the null byte when given a 'Z' format specifier" do + "a\x00\x0f".unpack('Zxc').should == ['a', 15] + "a\x00\x0f".unpack('Zcc').should == ['a', 0, 15] + end end diff --git a/spec/ruby/core/thread/raise_spec.rb b/spec/ruby/core/thread/raise_spec.rb index 27de3cc627..49323cf270 100644 --- a/spec/ruby/core/thread/raise_spec.rb +++ b/spec/ruby/core/thread/raise_spec.rb @@ -102,6 +102,30 @@ describe "Thread#raise on a sleeping thread" do raised_again.backtrace.first.should_not include("#{__FILE__}:#{raise_again_line}:") end end + + it "calls #exception in both the caller and in the target thread" do + cls = Class.new(Exception) do + attr_accessor :log + def initialize(*args) + @log = [] # This is shared because the super #exception uses a shallow clone + super + end + + def exception(*args) + @log << [self, Thread.current, args] + super + end + end + exc = cls.new + + @thr.raise exc, "Thread#raise #exception spec" + @thr.join + ScratchPad.recorded.should.is_a?(cls) + exc.log.should == [ + [exc, Thread.current, ["Thread#raise #exception spec"]], + [ScratchPad.recorded, @thr, []] + ] + end end describe "Thread#raise on a running thread" do -- cgit v1.2.3