diff options
author | Benoit Daloze <eregontp@gmail.com> | 2023-01-05 19:05:29 +0100 |
---|---|---|
committer | Benoit Daloze <eregontp@gmail.com> | 2023-01-05 19:05:29 +0100 |
commit | bbf54ec334fe2edd7669a944d88d17efde49a412 (patch) | |
tree | 2941c7b711319b295aa3664b6a2b984e70a523b7 | |
parent | cd5e6cc0ea48353c88d921b885b552dc76da255c (diff) | |
download | ruby-bbf54ec334fe2edd7669a944d88d17efde49a412.tar.gz |
Update to ruby/spec@9d69b95
113 files changed, 1954 insertions, 236 deletions
diff --git a/spec/ruby/core/array/keep_if_spec.rb b/spec/ruby/core/array/keep_if_spec.rb index bf2bdeaf91..40f7329b7c 100644 --- a/spec/ruby/core/array/keep_if_spec.rb +++ b/spec/ruby/core/array/keep_if_spec.rb @@ -1,3 +1,4 @@ +require_relative '../../spec_helper' require_relative 'shared/keep_if' describe "Array#keep_if" do diff --git a/spec/ruby/core/array/pack/c_spec.rb b/spec/ruby/core/array/pack/c_spec.rb index 7200830331..be03551629 100644 --- a/spec/ruby/core/array/pack/c_spec.rb +++ b/spec/ruby/core/array/pack/c_spec.rb @@ -45,8 +45,18 @@ describe :array_pack_8bit, shared: true do [1, 2, 3, 4, 5].pack(pack_format('*')).should == "\x01\x02\x03\x04\x05" end - it "ignores NULL bytes between directives" do - [1, 2, 3].pack(pack_format("\000", 2)).should == "\x01\x02" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + [1, 2, 3].pack(pack_format("\000", 2)).should == "\x01\x02" + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [1, 2, 3].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/array/pack/shared/basic.rb b/spec/ruby/core/array/pack/shared/basic.rb index 23e239d3de..65fdaa45d8 100644 --- a/spec/ruby/core/array/pack/shared/basic.rb +++ b/spec/ruby/core/array/pack/shared/basic.rb @@ -27,6 +27,42 @@ describe :array_pack_basic_non_float, shared: true do [@obj, @obj].pack("a \t\n\v\f\r"+pack_format).should be_an_instance_of(String) end + it "ignores comments in the format string" do + # 2 additional directives ('a') are required for the X directive + [@obj, @obj, @obj, @obj].pack("aa #{pack_format} # some comment \n#{pack_format}").should be_an_instance_of(String) + end + + ruby_version_is ""..."3.2" do + it "warns in verbose mode that a directive is unknown" do + # additional directive ('a') is required for the X directive + -> { [@obj, @obj].pack("a R" + pack_format) }.should complain(/unknown pack directive 'R'/, verbose: true) + -> { [@obj, @obj].pack("a 0" + pack_format) }.should complain(/unknown pack directive '0'/, verbose: true) + -> { [@obj, @obj].pack("a :" + pack_format) }.should complain(/unknown pack directive ':'/, verbose: true) + end + end + + ruby_version_is "3.2"..."3.3" do + # https://bugs.ruby-lang.org/issues/19150 + # NOTE: it's just a plan of the Ruby core team + it "warns that a directive is unknown" do + # additional directive ('a') is required for the X directive + -> { [@obj, @obj].pack("a R" + pack_format) }.should complain(/unknown pack directive 'R'/) + -> { [@obj, @obj].pack("a 0" + pack_format) }.should complain(/unknown pack directive '0'/) + -> { [@obj, @obj].pack("a :" + pack_format) }.should complain(/unknown pack directive ':'/) + end + end + + ruby_version_is "3.3" do + # https://bugs.ruby-lang.org/issues/19150 + # NOTE: Added this case just to not forget about the decision in the ticket + it "raise ArgumentError when a directive is unknown" do + # additional directive ('a') is required for the X directive + -> { [@obj, @obj].pack("a R" + pack_format) }.should raise_error(ArgumentError) + -> { [@obj, @obj].pack("a 0" + pack_format) }.should raise_error(ArgumentError) + -> { [@obj, @obj].pack("a :" + pack_format) }.should raise_error(ArgumentError) + end + end + it "calls #to_str to coerce the directives string" do d = mock("pack directive") d.should_receive(:to_str).and_return("x"+pack_format) @@ -39,6 +75,10 @@ describe :array_pack_basic_float, shared: true do [9.3, 4.7].pack(" \t\n\v\f\r"+pack_format).should be_an_instance_of(String) end + it "ignores comments in the format string" do + [9.3, 4.7].pack(pack_format + "# some comment \n" + pack_format).should be_an_instance_of(String) + end + it "calls #to_str to coerce the directives string" do d = mock("pack directive") d.should_receive(:to_str).and_return("x"+pack_format) diff --git a/spec/ruby/core/array/pack/shared/float.rb b/spec/ruby/core/array/pack/shared/float.rb index ba174a071a..9510cffed7 100644 --- a/spec/ruby/core/array/pack/shared/float.rb +++ b/spec/ruby/core/array/pack/shared/float.rb @@ -25,8 +25,18 @@ describe :array_pack_float_le, shared: true do [2.9, 1.4, 8.2].pack(pack_format("*")).should == "\x9a\x999@33\xb3?33\x03A" end - it "ignores NULL bytes between directives" do - [5.3, 9.2].pack(pack_format("\000", 2)).should == "\x9a\x99\xa9@33\x13A" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + [5.3, 9.2].pack(pack_format("\000", 2)).should == "\x9a\x99\xa9@33\x13A" + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [5.3, 9.2].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -74,6 +84,11 @@ describe :array_pack_float_be, shared: true do it "converts an Integer to a Float" do [8].pack(pack_format).should == "A\x00\x00\x00" + [bignum_value].pack(pack_format).should == "_\x80\x00\x00" + end + + it "converts a Rational to a Float" do + [Rational(8)].pack(pack_format).should == "A\x00\x00\x00" end it "raises a TypeError if passed a String representation of a floating point number" do @@ -88,8 +103,18 @@ describe :array_pack_float_be, shared: true do [2.9, 1.4, 8.2].pack(pack_format("*")).should == "@9\x99\x9a?\xb333A\x0333" end - it "ignores NULL bytes between directives" do - [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\xa9\x99\x9aA\x1333" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\xa9\x99\x9aA\x1333" + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [5.3, 9.2].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -129,6 +154,11 @@ describe :array_pack_double_le, shared: true do it "converts an Integer to a Float" do [8].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\x20@" + [bignum_value].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00\xF0C" + end + + it "converts a Rational to a Float" do + [Rational(8)].pack(pack_format).should == "\x00\x00\x00\x00\x00\x00 @" end it "raises a TypeError if passed a String representation of a floating point number" do @@ -143,8 +173,18 @@ describe :array_pack_double_le, shared: true do [2.9, 1.4, 8.2].pack(pack_format("*")).should == "333333\x07@ffffff\xf6?ffffff\x20@" end - it "ignores NULL bytes between directives" do - [5.3, 9.2].pack(pack_format("\000", 2)).should == "333333\x15@ffffff\x22@" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + [5.3, 9.2].pack(pack_format("\000", 2)).should == "333333\x15@ffffff\x22@" + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [5.3, 9.2].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -202,8 +242,18 @@ describe :array_pack_double_be, shared: true do [2.9, 1.4, 8.2].pack(pack_format("*")).should == "@\x07333333?\xf6ffffff@\x20ffffff" end - it "ignores NULL bytes between directives" do - [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\x15333333@\x22ffffff" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + [5.3, 9.2].pack(pack_format("\000", 2)).should == "@\x15333333@\x22ffffff" + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [5.3, 9.2].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/array/pack/shared/integer.rb b/spec/ruby/core/array/pack/shared/integer.rb index 6592f85022..d3ce9b5792 100644 --- a/spec/ruby/core/array/pack/shared/integer.rb +++ b/spec/ruby/core/array/pack/shared/integer.rb @@ -41,9 +41,19 @@ describe :array_pack_16bit_le, shared: true do str.should == "\x78\x65\xcd\xab\x21\x43" end - it "ignores NULL bytes between directives" do - str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) - str.should == "\x78\x65\xcd\xab" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + str.should == "\x78\x65\xcd\xab" + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -93,9 +103,19 @@ describe :array_pack_16bit_be, shared: true do str.should == "\x65\x78\xab\xcd\x43\x21" end - it "ignores NULL bytes between directives" do - str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) - str.should == "\x65\x78\xab\xcd" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + str.should == "\x65\x78\xab\xcd" + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -145,9 +165,19 @@ describe :array_pack_32bit_le, shared: true do str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde\x21\x43\x65\x78" end - it "ignores NULL bytes between directives" do - str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) - str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + str.should == "\x78\x65\x43\x12\xcd\xab\xf0\xde" + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -197,9 +227,19 @@ describe :array_pack_32bit_be, shared: true do str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd\x78\x65\x43\x21" end - it "ignores NULL bytes between directives" do - str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) - str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + str = [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + str.should == "\x12\x43\x65\x78\xde\xf0\xab\xcd" + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [0x1243_6578, 0xdef0_abcd].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -309,9 +349,19 @@ describe :array_pack_64bit_le, shared: true do str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78" end - it "ignores NULL bytes between directives" do - str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2)) - str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2)) + str.should == "\x56\x78\x12\x34\xcd\xab\xf0\xde\xf0\xde\xba\xdc\x21\x43\x65\x78" + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do @@ -369,9 +419,19 @@ describe :array_pack_64bit_be, shared: true do str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0" end - it "ignores NULL bytes between directives" do - str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2)) - str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + str = [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2)) + str.should == "\xde\xf0\xab\xcd\x34\x12\x78\x56\x78\x65\x43\x21\xdc\xba\xde\xf0" + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [0xdef0_abcd_3412_7856, 0x7865_4321_dcba_def0].pack(pack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/array/pack/shared/numeric_basic.rb b/spec/ruby/core/array/pack/shared/numeric_basic.rb index 7c36ba4a32..545e215e64 100644 --- a/spec/ruby/core/array/pack/shared/numeric_basic.rb +++ b/spec/ruby/core/array/pack/shared/numeric_basic.rb @@ -37,8 +37,14 @@ describe :array_pack_float, shared: true do -> { ["a"].pack(pack_format) }.should raise_error(TypeError) end - it "raises a TypeError when the object does not respond to #to_f" do - obj = mock('not an float') + it "raises a TypeError when the object is not Numeric" do + obj = Object.new + -> { [obj].pack(pack_format) }.should raise_error(TypeError, /can't convert Object into Float/) + end + + it "raises a TypeError when the Numeric object does not respond to #to_f" do + klass = Class.new(Numeric) + obj = klass.new -> { [obj].pack(pack_format) }.should raise_error(TypeError) end end diff --git a/spec/ruby/core/array/pack/shared/unicode.rb b/spec/ruby/core/array/pack/shared/unicode.rb index dd0f8b38aa..130c447bb7 100644 --- a/spec/ruby/core/array/pack/shared/unicode.rb +++ b/spec/ruby/core/array/pack/shared/unicode.rb @@ -67,8 +67,18 @@ describe :array_pack_unicode, shared: true do -> { [obj].pack("U") }.should raise_error(TypeError) end - it "ignores NULL bytes between directives" do - [1, 2, 3].pack("U\x00U").should == "\x01\x02" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + [1, 2, 3].pack("U\x00U").should == "\x01\x02" + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [1, 2, 3].pack("U\x00U") + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/array/pack/w_spec.rb b/spec/ruby/core/array/pack/w_spec.rb index 439fa02198..e241d1519c 100644 --- a/spec/ruby/core/array/pack/w_spec.rb +++ b/spec/ruby/core/array/pack/w_spec.rb @@ -24,8 +24,18 @@ describe "Array#pack with format 'w'" do [obj].pack("w").should == "\x05" end - it "ignores NULL bytes between directives" do - [1, 2, 3].pack("w\x00w").should == "\x01\x02" + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + [1, 2, 3].pack("w\x00w").should == "\x01\x02" + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + [1, 2, 3].pack("w\x00w") + }.should raise_error(ArgumentError, /unknown pack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/array/shared/unshift.rb b/spec/ruby/core/array/shared/unshift.rb index fc82e19e2a..4941e098f6 100644 --- a/spec/ruby/core/array/shared/unshift.rb +++ b/spec/ruby/core/array/shared/unshift.rb @@ -22,6 +22,11 @@ describe :array_unshift, shared: true do a.should == [3, 4] end + it "returns self" do + a = [1, 2, 3] + a.send(@method, "a").should.equal?(a) + end + it "quietly ignores unshifting nothing" do [].send(@method).should == [] end @@ -43,4 +48,17 @@ describe :array_unshift, shared: true do it "raises a FrozenError on a frozen array when the array would not be modified" do -> { ArraySpecs.frozen_array.send(@method) }.should raise_error(FrozenError) end + + # https://github.com/oracle/truffleruby/issues/2772 + it "doesn't rely on Array#[]= so it can be overridden" do + subclass = Class.new(Array) do + def []=(*) + raise "[]= is called" + end + end + + array = subclass.new + array.send(@method, 1) + array.should == [1] + end end diff --git a/spec/ruby/core/array/values_at_spec.rb b/spec/ruby/core/array/values_at_spec.rb index 2c6fd16947..e85bbee400 100644 --- a/spec/ruby/core/array/values_at_spec.rb +++ b/spec/ruby/core/array/values_at_spec.rb @@ -1,6 +1,7 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +# Should be synchronized with core/struct/values_at_spec.rb describe "Array#values_at" do it "returns an array of elements at the indexes when passed indexes" do [1, 2, 3, 4, 5].values_at().should == [] diff --git a/spec/ruby/core/array/zip_spec.rb b/spec/ruby/core/array/zip_spec.rb index af4013debe..2a0f64cb49 100644 --- a/spec/ruby/core/array/zip_spec.rb +++ b/spec/ruby/core/array/zip_spec.rb @@ -62,4 +62,10 @@ describe "Array#zip" do it "does not return subclass instance on Array subclasses" do ArraySpecs::MyArray[1, 2, 3].zip(["a", "b"]).should be_an_instance_of(Array) end + + it "raises TypeError when some argument isn't Array and doesn't respond to #to_ary and #to_enum" do + -> { [1, 2, 3].zip(Object.new) }.should raise_error(TypeError, "wrong argument type Object (must respond to :each)") + -> { [1, 2, 3].zip(1) }.should raise_error(TypeError, "wrong argument type Integer (must respond to :each)") + -> { [1, 2, 3].zip(true) }.should raise_error(TypeError, "wrong argument type TrueClass (must respond to :each)") + end end diff --git a/spec/ruby/core/class/subclasses_spec.rb b/spec/ruby/core/class/subclasses_spec.rb index ddbcfb02c0..a16b934d4f 100644 --- a/spec/ruby/core/class/subclasses_spec.rb +++ b/spec/ruby/core/class/subclasses_spec.rb @@ -31,6 +31,28 @@ ruby_version_is '3.1' do ModuleSpecs::Parent.subclasses.should == ModuleSpecs::Parent.subclasses.uniq end + it "works when creating subclasses concurrently" do + t = 16 + n = 1000 + go = false + superclass = Class.new + + threads = t.times.map do + Thread.new do + Thread.pass until go + n.times.map do + Class.new(superclass) + end + end + end + + go = true + classes = threads.map(&:value) + + superclass.subclasses.size.should == t * n + superclass.subclasses.each { |c| c.should be_kind_of(Class) } + end + def assert_subclasses(mod,subclasses) mod.subclasses.sort_by(&:inspect).should == subclasses.sort_by(&:inspect) end diff --git a/spec/ruby/core/dir/home_spec.rb b/spec/ruby/core/dir/home_spec.rb index cde6899df2..95bb00ad03 100644 --- a/spec/ruby/core/dir/home_spec.rb +++ b/spec/ruby/core/dir/home_spec.rb @@ -20,6 +20,18 @@ describe "Dir.home" do Dir.home.should_not.frozen? end + it "returns a string with the filesystem encoding" do + Dir.home.encoding.should == Encoding.find("filesystem") + end + + platform_is_not :windows do + it "works even if HOME is unset" do + ENV.delete('HOME') + Dir.home.should.start_with?('/') + Dir.home.encoding.should == Encoding.find("filesystem") + end + end + platform_is :windows do ruby_version_is "3.0" do it "returns the home directory with forward slashs and as UTF-8" do @@ -29,23 +41,21 @@ describe "Dir.home" do home.encoding.should == Encoding::UTF_8 end end - end - end - platform_is :windows do - it "retrieves the directory from HOME, USERPROFILE, HOMEDRIVE/HOMEPATH and the WinAPI in that order" do - old_dirs = [ENV.delete('HOME'), ENV.delete('USERPROFILE'), ENV.delete('HOMEDRIVE'), ENV.delete('HOMEPATH')] + it "retrieves the directory from HOME, USERPROFILE, HOMEDRIVE/HOMEPATH and the WinAPI in that order" do + old_dirs = [ENV.delete('HOME'), ENV.delete('USERPROFILE'), ENV.delete('HOMEDRIVE'), ENV.delete('HOMEPATH')] - Dir.home.should == old_dirs[1].gsub("\\", "/") - ENV['HOMEDRIVE'] = "C:" - ENV['HOMEPATH'] = "\\rubyspec\\home1" - Dir.home.should == "C:/rubyspec/home1" - ENV['USERPROFILE'] = "C:\\rubyspec\\home2" - Dir.home.should == "C:/rubyspec/home2" - ENV['HOME'] = "C:\\rubyspec\\home3" - Dir.home.should == "C:/rubyspec/home3" - ensure - ENV['HOME'], ENV['USERPROFILE'], ENV['HOMEDRIVE'], ENV['HOMEPATH'] = *old_dirs + Dir.home.should == old_dirs[1].gsub("\\", "/") + ENV['HOMEDRIVE'] = "C:" + ENV['HOMEPATH'] = "\\rubyspec\\home1" + Dir.home.should == "C:/rubyspec/home1" + ENV['USERPROFILE'] = "C:\\rubyspec\\home2" + Dir.home.should == "C:/rubyspec/home2" + ENV['HOME'] = "C:\\rubyspec\\home3" + Dir.home.should == "C:/rubyspec/home3" + ensure + ENV['HOME'], ENV['USERPROFILE'], ENV['HOMEDRIVE'], ENV['HOMEPATH'] = *old_dirs + end end end @@ -65,6 +75,10 @@ describe "Dir.home" do it "returns a non-frozen string" do Dir.home(ENV['USER']).should_not.frozen? end + + it "returns a string with the filesystem encoding" do + Dir.home(ENV['USER']).encoding.should == Encoding.find("filesystem") + end end it "raises an ArgumentError if the named user doesn't exist" do diff --git a/spec/ruby/core/dir/mkdir_spec.rb b/spec/ruby/core/dir/mkdir_spec.rb index 0ed28f5a99..076ec19dd9 100644 --- a/spec/ruby/core/dir/mkdir_spec.rb +++ b/spec/ruby/core/dir/mkdir_spec.rb @@ -46,7 +46,7 @@ describe "Dir.mkdir" do end end - it "calls #to_path on non-String arguments" do + it "calls #to_path on non-String path arguments" do DirSpecs.clear_dirs p = mock('path') p.should_receive(:to_path).and_return(DirSpecs.mock_dir('nonexisting')) @@ -54,6 +54,22 @@ describe "Dir.mkdir" do DirSpecs.clear_dirs end + it "calls #to_int on non-Integer permissions argument" do + DirSpecs.clear_dirs + path = DirSpecs.mock_dir('nonexisting') + permissions = mock('permissions') + permissions.should_receive(:to_int).and_return(0666) + Dir.mkdir(path, permissions) + DirSpecs.clear_dirs + end + + it "raises TypeError if non-Integer permissions argument does not have #to_int method" do + path = DirSpecs.mock_dir('nonexisting') + permissions = Object.new + + -> { Dir.mkdir(path, permissions) }.should raise_error(TypeError, 'no implicit conversion of Object into Integer') + end + it "raises a SystemCallError if any of the directories in the path before the last does not exist" do -> { Dir.mkdir "#{DirSpecs.nonexistent}/subdir" }.should raise_error(SystemCallError) end diff --git a/spec/ruby/core/enumerable/zip_spec.rb b/spec/ruby/core/enumerable/zip_spec.rb index 9ec15aa030..ab148f2a6e 100644 --- a/spec/ruby/core/enumerable/zip_spec.rb +++ b/spec/ruby/core/enumerable/zip_spec.rb @@ -38,4 +38,9 @@ describe "Enumerable#zip" do multi.zip(multi).should == [[[1, 2], [1, 2]], [[3, 4, 5], [3, 4, 5]], [[6, 7, 8, 9], [6, 7, 8, 9]]] end + it "raises TypeError when some argument isn't Array and doesn't respond to #to_ary and #to_enum" do + -> { EnumerableSpecs::Numerous.new(1,2,3).zip(Object.new) }.should raise_error(TypeError, "wrong argument type Object (must respond to :each)") + -> { EnumerableSpecs::Numerous.new(1,2,3).zip(1) }.should raise_error(TypeError, "wrong argument type Integer (must respond to :each)") + -> { EnumerableSpecs::Numerous.new(1,2,3).zip(true) }.should raise_error(TypeError, "wrong argument type TrueClass (must respond to :each)") + end end diff --git a/spec/ruby/core/float/comparison_spec.rb b/spec/ruby/core/float/comparison_spec.rb index 53e7ec332a..1373b3a1fb 100644 --- a/spec/ruby/core/float/comparison_spec.rb +++ b/spec/ruby/core/float/comparison_spec.rb @@ -7,9 +7,25 @@ describe "Float#<=>" do ((bignum_value*1.1) <=> bignum_value).should == 1 end - it "returns nil when either argument is NaN" do - (nan_value <=> 71.2).should be_nil - (1771.176 <=> nan_value).should be_nil + it "returns nil if one side is NaN" do + [1.0, 42, bignum_value].each { |n| + (nan_value <=> n).should == nil + (n <=> nan_value).should == nil + } + end + + it "handles positive infinity" do + [1.0, 42, bignum_value].each { |n| + (infinity_value <=> n).should == 1 + (n <=> infinity_value).should == -1 + } + end + + it "handles negative infinity" do + [1.0, 42, bignum_value].each { |n| + (-infinity_value <=> n).should == -1 + (n <=> -infinity_value).should == 1 + } end it "returns nil when the given argument is not a Float" do @@ -49,21 +65,10 @@ describe "Float#<=>" do }.should raise_error(TypeError, "coerce must return [x, y]") end - # The 4 tests below are taken from matz's revision 23730 for Ruby trunk - # - it "returns 1 when self is Infinity and other is an Integer" do + it "returns the correct result when one side is infinite" do (infinity_value <=> Float::MAX.to_i*2).should == 1 - end - - it "returns -1 when self is negative and other is Infinity" do (-Float::MAX.to_i*2 <=> infinity_value).should == -1 - end - - it "returns -1 when self is -Infinity and other is negative" do (-infinity_value <=> -Float::MAX.to_i*2).should == -1 - end - - it "returns 1 when self is negative and other is -Infinity" do (-Float::MAX.to_i*2 <=> -infinity_value).should == 1 end diff --git a/spec/ruby/core/float/divmod_spec.rb b/spec/ruby/core/float/divmod_spec.rb index 523217ac1f..dad45a9b89 100644 --- a/spec/ruby/core/float/divmod_spec.rb +++ b/spec/ruby/core/float/divmod_spec.rb @@ -23,7 +23,7 @@ describe "Float#divmod" do # Behaviour established as correct in r23953 it "raises a FloatDomainError if other is NaN" do - -> { 1.divmod(nan_value) }.should raise_error(FloatDomainError) + -> { 1.0.divmod(nan_value) }.should raise_error(FloatDomainError) end # Behaviour established as correct in r23953 diff --git a/spec/ruby/core/float/gt_spec.rb b/spec/ruby/core/float/gt_spec.rb index 0d73f1c3df..33078e07ce 100644 --- a/spec/ruby/core/float/gt_spec.rb +++ b/spec/ruby/core/float/gt_spec.rb @@ -14,4 +14,25 @@ describe "Float#>" do -> { 5.0 > "4" }.should raise_error(ArgumentError) -> { 5.0 > mock('x') }.should raise_error(ArgumentError) end + + it "returns false if one side is NaN" do + [1.0, 42, bignum_value].each { |n| + (nan_value > n).should == false + (n > nan_value).should == false + } + end + + it "handles positive infinity" do + [1.0, 42, bignum_value].each { |n| + (infinity_value > n).should == true + (n > infinity_value).should == false + } + end + + it "handles negative infinity" do + [1.0, 42, bignum_value].each { |n| + (-infinity_value > n).should == false + (n > -infinity_value).should == true + } + end end diff --git a/spec/ruby/core/float/gte_spec.rb b/spec/ruby/core/float/gte_spec.rb index 98ec60b70b..44c0a81b43 100644 --- a/spec/ruby/core/float/gte_spec.rb +++ b/spec/ruby/core/float/gte_spec.rb @@ -14,4 +14,25 @@ describe "Float#>=" do -> { 5.0 >= "4" }.should raise_error(ArgumentError) -> { 5.0 >= mock('x') }.should raise_error(ArgumentError) end + + it "returns false if one side is NaN" do + [1.0, 42, bignum_value].each { |n| + (nan_value >= n).should == false + (n >= nan_value).should == false + } + end + + it "handles positive infinity" do + [1.0, 42, bignum_value].each { |n| + (infinity_value >= n).should == true + (n >= infinity_value).should == false + } + end + + it "handles negative infinity" do + [1.0, 42, bignum_value].each { |n| + (-infinity_value >= n).should == false + (n >= -infinity_value).should == true + } + end end diff --git a/spec/ruby/core/float/lt_spec.rb b/spec/ruby/core/float/lt_spec.rb index c01b6e0e02..94dcfc42f8 100644 --- a/spec/ruby/core/float/lt_spec.rb +++ b/spec/ruby/core/float/lt_spec.rb @@ -14,4 +14,25 @@ describe "Float#<" do -> { 5.0 < "4" }.should raise_error(ArgumentError) -> { 5.0 < mock('x') }.should raise_error(ArgumentError) end + + it "returns false if one side is NaN" do + [1.0, 42, bignum_value].each { |n| + (nan_value < n).should == false + (n < nan_value).should == false + } + end + + it "handles positive infinity" do + [1.0, 42, bignum_value].each { |n| + (infinity_value < n).should == false + (n < infinity_value).should == true + } + end + + it "handles negative infinity" do + [1.0, 42, bignum_value].each { |n| + (-infinity_value < n).should == true + (n < -infinity_value).should == false + } + end end diff --git a/spec/ruby/core/float/lte_spec.rb b/spec/ruby/core/float/lte_spec.rb index 66f2ddc2c7..7b5a86ee76 100644 --- a/spec/ruby/core/float/lte_spec.rb +++ b/spec/ruby/core/float/lte_spec.rb @@ -15,4 +15,25 @@ describe "Float#<=" do -> { 5.0 <= "4" }.should raise_error(ArgumentError) -> { 5.0 <= mock('x') }.should raise_error(ArgumentError) end + + it "returns false if one side is NaN" do + [1.0, 42, bignum_value].each { |n| + (nan_value <= n).should == false + (n <= nan_value).should == false + } + end + + it "handles positive infinity" do + [1.0, 42, bignum_value].each { |n| + (infinity_value <= n).should == false + (n <= infinity_value).should == true + } + end + + it "handles negative infinity" do + [1.0, 42, bignum_value].each { |n| + (-infinity_value <= n).should == true + (n <= -infinity_value).should == false + } + end end diff --git a/spec/ruby/core/float/shared/equal.rb b/spec/ruby/core/float/shared/equal.rb index 668aa069b5..4d524e1cf2 100644 --- a/spec/ruby/core/float/shared/equal.rb +++ b/spec/ruby/core/float/shared/equal.rb @@ -14,4 +14,25 @@ describe :float_equal, shared: true do 1.0.send(@method, x).should == false 2.0.send(@method, x).should == true end + + it "returns false if one side is NaN" do + [1.0, 42, bignum_value].each { |n| + (nan_value.send(@method, n)).should == false + (n.send(@method, nan_value)).should == false + } + end + + it "handles positive infinity" do + [1.0, 42, bignum_value].each { |n| + (infinity_value.send(@method, n)).should == false + (n.send(@method, infinity_value)).should == false + } + end + + it "handles negative infinity" do + [1.0, 42, bignum_value].each { |n| + ((-infinity_value).send(@method, n)).should == false + (n.send(@method, -infinity_value)).should == false + } + end end diff --git a/spec/ruby/core/io/gets_spec.rb b/spec/ruby/core/io/gets_spec.rb index 2d5e3d1ae3..1f1c3bb254 100644 --- a/spec/ruby/core/io/gets_spec.rb +++ b/spec/ruby/core/io/gets_spec.rb @@ -216,6 +216,10 @@ describe "IO#gets" do @io.gets(nil, 0).should == "" @io.gets("", 0).should == "" end + + it "does not accept limit that doesn't fit in a C off_t" do + -> { @io.gets(2**128) }.should raise_error(RangeError) + end end describe "IO#gets" do diff --git a/spec/ruby/core/io/lineno_spec.rb b/spec/ruby/core/io/lineno_spec.rb index 99266ecca1..9a4ad90880 100644 --- a/spec/ruby/core/io/lineno_spec.rb +++ b/spec/ruby/core/io/lineno_spec.rb @@ -92,8 +92,13 @@ describe "IO#lineno=" do @io.lineno.should == 92233 end - it "raises TypeError on nil argument" do - -> { @io.lineno = nil }.should raise_error(TypeError) + it "raises TypeError if cannot convert argument to Integer implicitly" do + -> { @io.lineno = "1" }.should raise_error(TypeError, 'no implicit conversion of String into Integer') + -> { @io.lineno = nil }.should raise_error(TypeError, 'no implicit conversion from nil to integer') + end + + it "does not accept Integers that don't fit in a C int" do + -> { @io.lineno = 2**32 }.should raise_error(RangeError) end it "sets the current line number to the given value" do diff --git a/spec/ruby/core/io/new_spec.rb b/spec/ruby/core/io/new_spec.rb index 3597098caf..0ef30991fd 100644 --- a/spec/ruby/core/io/new_spec.rb +++ b/spec/ruby/core/io/new_spec.rb @@ -1,6 +1,8 @@ require_relative '../../spec_helper' require_relative 'shared/new' +# NOTE: should be syncronized with library/stringio/initialize_spec.rb + describe "IO.new" do it_behaves_like :io_new, :new end diff --git a/spec/ruby/core/io/readline_spec.rb b/spec/ruby/core/io/readline_spec.rb index ca30f31e39..cf9f0dfc11 100644 --- a/spec/ruby/core/io/readline_spec.rb +++ b/spec/ruby/core/io/readline_spec.rb @@ -51,6 +51,10 @@ describe "IO#readline" do it "returns an empty string when passed 0 as a limit" do @io.readline(0).should == "" end + + it "does not accept Integers that don't fit in a C off_t" do + -> { @io.readline(2**128) }.should raise_error(RangeError) + end end describe "when passed separator and limit" do diff --git a/spec/ruby/core/io/readlines_spec.rb b/spec/ruby/core/io/readlines_spec.rb index 15af6debbe..496003002d 100644 --- a/spec/ruby/core/io/readlines_spec.rb +++ b/spec/ruby/core/io/readlines_spec.rb @@ -106,6 +106,10 @@ describe "IO#readlines" do it "raises ArgumentError when passed 0 as a limit" do -> { @io.readlines(0) }.should raise_error(ArgumentError) end + + it "does not accept Integers that don't fit in a C off_t" do + -> { @io.readlines(2**128) }.should raise_error(RangeError) + end end describe "when passed chomp" do diff --git a/spec/ruby/core/io/shared/each.rb b/spec/ruby/core/io/shared/each.rb index 1b8abce5ff..02bbe19c1a 100644 --- a/spec/ruby/core/io/shared/each.rb +++ b/spec/ruby/core/io/shared/each.rb @@ -77,6 +77,10 @@ describe :io_each, shared: true do -> { @io.send(@method, 0){} }.should raise_error(ArgumentError) end end + + it "does not accept Integers that don't fit in a C off_t" do + -> { @io.send(@method, 2**128){} }.should raise_error(RangeError) + end end describe "when passed a String containing one space as a separator" do diff --git a/spec/ruby/core/io/shared/new.rb b/spec/ruby/core/io/shared/new.rb index f2a0970a40..7677aada6e 100644 --- a/spec/ruby/core/io/shared/new.rb +++ b/spec/ruby/core/io/shared/new.rb @@ -1,5 +1,7 @@ require_relative '../fixtures/classes' +# NOTE: should be syncronized with library/stringio/initialize_spec.rb + # This group of specs may ONLY contain specs that do successfully create # an IO instance from the file descriptor returned by #new_fd helper. describe :io_new, shared: true do diff --git a/spec/ruby/core/io/shared/pos.rb b/spec/ruby/core/io/shared/pos.rb index d83a6c6692..3fdd3eb2b3 100644 --- a/spec/ruby/core/io/shared/pos.rb +++ b/spec/ruby/core/io/shared/pos.rb @@ -60,7 +60,13 @@ describe :io_set_pos, shared: true do end end - it "does not accept Integers that don't fit in a C long" do + it "raises TypeError when cannot convert implicitly argument to Integer" do + File.open @fname do |io| + -> { io.send @method, Object.new }.should raise_error(TypeError, "no implicit conversion of Object into Integer") + end + end + + it "does not accept Integers that don't fit in a C off_t" do File.open @fname do |io| -> { io.send @method, 2**128 }.should raise_error(RangeError) end diff --git a/spec/ruby/core/io/shared/readlines.rb b/spec/ruby/core/io/shared/readlines.rb index 479452b71c..7681e1b5c1 100644 --- a/spec/ruby/core/io/shared/readlines.rb +++ b/spec/ruby/core/io/shared/readlines.rb @@ -79,6 +79,10 @@ describe :io_readlines_options_19, shared: true do (result ? result : ScratchPad.recorded).should == IOSpecs.lines end + it "does not accept Integers that don't fit in a C off_t" do + -> { IO.send(@method, @name, 2**128, &@object) }.should raise_error(RangeError) + end + ruby_bug "#18767", ""..."3.3" do describe "when passed limit" do it "raises ArgumentError when passed 0 as a limit" do diff --git a/spec/ruby/core/io/sysseek_spec.rb b/spec/ruby/core/io/sysseek_spec.rb index e631939bce..002f2a14eb 100644 --- a/spec/ruby/core/io/sysseek_spec.rb +++ b/spec/ruby/core/io/sysseek_spec.rb @@ -4,7 +4,7 @@ require_relative 'fixtures/classes' require_relative 'shared/pos' describe "IO#sysseek" do - it_behaves_like :io_set_pos, :seek + it_behaves_like :io_set_pos, :sysseek end describe "IO#sysseek" do diff --git a/spec/ruby/core/kernel/shared/load.rb b/spec/ruby/core/kernel/shared/load.rb index cc84daeb88..5c41c19bf6 100644 --- a/spec/ruby/core/kernel/shared/load.rb +++ b/spec/ruby/core/kernel/shared/load.rb @@ -88,12 +88,12 @@ describe :kernel_load, shared: true do describe "when passed true for 'wrap'" do it "loads from an existing path" do - path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR + path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR @object.load(path, true).should be_true end it "sets the enclosing scope to an anonymous module" do - path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR + path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR @object.load(path, true) Object.const_defined?(:LoadSpecWrap).should be_false @@ -103,14 +103,14 @@ describe :kernel_load, shared: true do end it "allows referencing outside namespaces" do - path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR + path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR @object.load(path, true) ScratchPad.recorded[0].should equal(String) end it "sets self as a copy of the top-level main" do - path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR + path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR @object.load(path, true) top_level = ScratchPad.recorded[2] @@ -127,7 +127,7 @@ describe :kernel_load, shared: true do main_ancestors = main.singleton_class.ancestors[1..-1] main_ancestors.first.should == mod - path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR + path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR @object.load(path, true) top_level = ScratchPad.recorded[2] @@ -157,7 +157,7 @@ describe :kernel_load, shared: true do describe "when passed a module for 'wrap'" do ruby_version_is "3.1" do it "sets the enclosing scope to the supplied module" do - path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR + path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR mod = Module.new @object.load(path, mod) @@ -167,6 +167,25 @@ describe :kernel_load, shared: true do wrap_module = ScratchPad.recorded[1] wrap_module.should == mod end + + it "makes constants and instance methods in the source file reachable with the supplied module" do + path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR + mod = Module.new + @object.load(path, mod) + + mod::LOAD_WRAP_SPECS_TOP_LEVEL_CONSTANT.should == 1 + obj = Object.new + obj.extend(mod) + obj.send(:load_wrap_specs_top_level_method).should == :load_wrap_specs_top_level_method + end + + it "makes instance methods in the source file private" do + path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR + mod = Module.new + @object.load(path, mod) + + mod.private_instance_methods.include?(:load_wrap_specs_top_level_method).should == true + end end end diff --git a/spec/ruby/core/kernel/singleton_class_spec.rb b/spec/ruby/core/kernel/singleton_class_spec.rb index a8bcd916d1..c56fa08cc1 100644 --- a/spec/ruby/core/kernel/singleton_class_spec.rb +++ b/spec/ruby/core/kernel/singleton_class_spec.rb @@ -1,3 +1,5 @@ +require_relative '../../spec_helper' + describe "Kernel#singleton_class" do it "returns class extended from an object" do x = Object.new diff --git a/spec/ruby/core/marshal/dump_spec.rb b/spec/ruby/core/marshal/dump_spec.rb index 079010e554..879ea287ce 100644 --- a/spec/ruby/core/marshal/dump_spec.rb +++ b/spec/ruby/core/marshal/dump_spec.rb @@ -1,5 +1,6 @@ # -*- encoding: binary -*- require_relative '../../spec_helper' +require_relative 'fixtures/classes' require_relative 'fixtures/marshal_data' describe "Marshal.dump" do @@ -106,7 +107,7 @@ describe "Marshal.dump" do end describe "with an object responding to #_dump" do - it "dumps the object returned by #marshal_dump" do + it "dumps the object returned by #_dump" do Marshal.dump(UserDefined.new).should == "\004\bu:\020UserDefined\022\004\b[\a:\nstuff;\000" end @@ -122,6 +123,34 @@ describe "Marshal.dump" do m.should_not_receive(:_dump) Marshal.dump(m) end + + it "indexes instance variables of a String returned by #_dump at first and then indexes the object itself" do + class MarshalSpec::M1::A + def _dump(level) + s = "<dump>" + s.instance_variable_set(:@foo, "bar") + s + end + end + + a = MarshalSpec::M1::A.new + + # 0-based index of the object a = 2, that is encoded as \x07 and printed as "\a" character. + # Objects are serialized in the following order: Array, a, "bar". + # But they are indexed in different order: Array (index=0), "bar" (index=1), a (index=2) + # So the second occurenc of the object a is encoded as an index 2. + reference = "@\a" + Marshal.dump([a, a]).should == "\x04\b[\aIu:\x17MarshalSpec::M1::A\v<dump>\x06:\t@foo\"\bbar#{reference}" + end + + describe "Core library classes with #_dump returning a String with instance variables" do + it "indexes instance variables and then a Time object itself" do + t = Time.utc(2022) + reference = "@\a" + + Marshal.dump([t, t]).should == "\x04\b[\aIu:\tTime\r \x80\x1E\xC0\x00\x00\x00\x00\x06:\tzoneI\"\bUTC\x06:\x06EF#{reference}" + end + end end describe "with a Class" do @@ -185,6 +214,20 @@ describe "Marshal.dump" do [Marshal, -2**64, "\004\bl-\n\000\000\000\000\000\000\000\000\001\000"], ].should be_computed_by(:dump) end + + it "increases the object links counter" do + obj = Object.new + object_1_link = "\x06" # representing of (0-based) index=1 (by adding 5 for small Integers) + object_2_link = "\x07" # representing of index=2 + + # objects: Array, Object, Object + Marshal.dump([obj, obj]).should == "\x04\b[\ao:\vObject\x00@#{object_1_link}" + + # objects: Array, Bignum, Object, Object + Marshal.dump([2**64, obj, obj]).should == "\x04\b[\bl+\n\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00o:\vObject\x00@#{object_2_link}" + Marshal.dump([2**48, obj, obj]).should == "\x04\b[\bl+\t\x00\x00\x00\x00\x00\x00\x01\x00o:\vObject\x00@#{object_2_link}" + Marshal.dump([2**32, obj, obj]).should == "\x04\b[\bl+\b\x00\x00\x00\x00\x01\x00o:\vObject\x00@#{object_2_link}" + end end describe "with a String" do diff --git a/spec/ruby/core/marshal/fixtures/classes.rb b/spec/ruby/core/marshal/fixtures/classes.rb new file mode 100644 index 0000000000..7c81c64927 --- /dev/null +++ b/spec/ruby/core/marshal/fixtures/classes.rb @@ -0,0 +1,4 @@ +module MarshalSpec + # empty modules + module M1 end +end diff --git a/spec/ruby/core/matchdata/values_at_spec.rb b/spec/ruby/core/matchdata/values_at_spec.rb index 8f7fdf557c..4fd0bfc42a 100644 --- a/spec/ruby/core/matchdata/values_at_spec.rb +++ b/spec/ruby/core/matchdata/values_at_spec.rb @@ -1,21 +1,76 @@ require_relative '../../spec_helper' -describe "MatchData#values_at" do - it "returns an array of the matching value" do - /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0, 2, -2).should == ["HX1138", "X", "113"] +describe "Struct#values_at" do + # Should be synchronized with core/array/values_at_spec.rb and core/struct/values_at_spec.rb + # + # /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").to_a # => ["HX1138", "H", "X", "113", "8"] + + context "when passed a list of Integers" do + it "returns an array containing each value given by one of integers" do + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0, 2, -2).should == ["HX1138", "X", "113"] + end + + it "returns nil value for any integer that is out of range" do + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(5).should == [nil] + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(-6).should == [nil] + end end - describe "when passed a Range" do - it "returns an array of the matching value" do - /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(2..4, 0..1).should == ["X", "113", "8", "HX1138", "H"] + context "when passed an integer Range" do + it "returns an array containing each value given by the elements of the range" do + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0..2).should == ["HX1138", "H", "X"] + end + + it "fills with nil values for range elements larger than the captured values number" do + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0..5).should == ["HX1138", "H", "X", "113", "8", nil] + end + + it "raises RangeError if any element of the range is negative and out of range" do + -> { /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(-6..3) }.should raise_error(RangeError, "-6..3 out of range") + end + + it "supports endless Range" do + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0..).should == ["HX1138", "H", "X", "113", "8"] + end + + it "supports beginningless Range" do + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(0..2).should == ["HX1138", "H", "X"] + end + + it "returns an empty Array when Range is empty" do + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(2..0).should == [] + end + end + + context "when passed names" do + it 'slices captures with the given names' do + /(?<a>.)(?<b>.)(?<c>.)/.match('012').values_at(:c, :a).should == ['2', '0'] + end + + it 'slices captures with the given String names' do + /(?<a>.)(?<b>.)(?<c>.)/.match('012').values_at('c', 'a').should == ['2', '0'] end end - it 'slices captures with the given names' do - /(?<a>.)(?<b>.)(?<c>.)/.match('012').values_at(:c, :a).should == ['2', '0'] + it "supports multiple integer Ranges" do + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(1..2, 2..3).should == ["H", "X", "X", "113"] end - it 'takes names and indices' do + it "supports mixing integer Ranges and Integers" do + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(1..2, 4).should == ["H", "X", "8"] + end + + it 'supports mixing of names and indices' do /\A(?<a>.)(?<b>.)\z/.match('01').values_at(0, 1, 2, :a, :b).should == ['01', '0', '1', '0', '1'] end + + it "returns a new empty Array if no arguments given" do + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at().should == [] + end + + it "fails when passed arguments of unsupported types" do + -> { + /(.)(.)(\d+)(\d)/.match("THX1138: The Movie").values_at(Object.new) + }.should raise_error(TypeError, "no implicit conversion of Object into Integer") + end end diff --git a/spec/ruby/core/module/fixtures/classes.rb b/spec/ruby/core/module/fixtures/classes.rb index 2b47ff112e..bc6b940a6c 100644 --- a/spec/ruby/core/module/fixtures/classes.rb +++ b/spec/ruby/core/module/fixtures/classes.rb @@ -388,6 +388,7 @@ module ModuleSpecs # empty modules module M1; end module M2; end + module M3; end module Autoload def self.use_ex1 diff --git a/spec/ruby/core/module/include_spec.rb b/spec/ruby/core/module/include_spec.rb index c47e052d22..c073bc31ca 100644 --- a/spec/ruby/core/module/include_spec.rb +++ b/spec/ruby/core/module/include_spec.rb @@ -104,9 +104,9 @@ describe "Module#include" do class A; include M; end class B < A; include M; end - all = [A,B,M] + all = [A, B, M] - (B.ancestors & all).should == [B, A, M] + (B.ancestors.filter { |a| all.include?(a) }).should == [B, A, M] end end diff --git a/spec/ruby/core/module/prepend_spec.rb b/spec/ruby/core/module/prepend_spec.rb index d636e023ed..976b09b105 100644 --- a/spec/ruby/core/module/prepend_spec.rb +++ b/spec/ruby/core/module/prepend_spec.rb @@ -611,6 +611,18 @@ describe "Module#prepend" do ScratchPad.recorded.should == [[:prepend_features, c], [:prepended, c]] end + it "prepends a module if it is included in a super class" do + module ModuleSpecs::M3 + module M; end + class A; include M; end + class B < A; prepend M; end + + all = [A, B, M] + + (B.ancestors.filter { |a| all.include?(a) }).should == [M, B, A, M] + end + end + it "detects cyclic prepends" do -> { module ModuleSpecs::P diff --git a/spec/ruby/core/objectspace/define_finalizer_spec.rb b/spec/ruby/core/objectspace/define_finalizer_spec.rb index 281785b0a4..d9db027e0b 100644 --- a/spec/ruby/core/objectspace/define_finalizer_spec.rb +++ b/spec/ruby/core/objectspace/define_finalizer_spec.rb @@ -169,4 +169,26 @@ describe "ObjectSpace.define_finalizer" do ruby_exe(code).lines.sort.should == ["finalized1\n", "finalized2\n"] end + + ruby_version_is "3.1" do + describe "when $VERBOSE is not nil" do + it "warns if an exception is raised in finalizer" do + code = <<-RUBY + ObjectSpace.define_finalizer(Object.new) { raise "finalizing" } + RUBY + + ruby_exe(code, args: "2>&1").should include("warning: Exception in finalizer", "finalizing") + end + end + + describe "when $VERBOSE is nil" do + it "does not warn even if an exception is raised in finalizer" do + code = <<-RUBY + ObjectSpace.define_finalizer(Object.new) { raise "finalizing" } + RUBY + + ruby_exe(code, args: "2>&1", options: "-W0").should == "" + end + end + end end diff --git a/spec/ruby/core/process/constants_spec.rb b/spec/ruby/core/process/constants_spec.rb index b61f5ab64e..4130bb58a5 100644 --- a/spec/ruby/core/process/constants_spec.rb +++ b/spec/ruby/core/process/constants_spec.rb @@ -1,3 +1,4 @@ +require_relative '../../spec_helper' describe "Process::Constants" do platform_is :darwin, :netbsd, :freebsd do diff --git a/spec/ruby/core/process/detach_spec.rb b/spec/ruby/core/process/detach_spec.rb index 1c27ed9c2c..91661afcea 100644 --- a/spec/ruby/core/process/detach_spec.rb +++ b/spec/ruby/core/process/detach_spec.rb @@ -42,5 +42,34 @@ describe "Process.detach" do thr.pid.should == pid end + + it "tolerates not existing child process pid" do + # ensure there is no child process with this hardcoded pid + # `kill 0 pid` for existing process returns "1" and raises Errno::ESRCH if process doesn't exist + -> { Process.kill(0, 100500) }.should raise_error(Errno::ESRCH) + + thr = Process.detach(100500) + thr.join + + thr.should be_kind_of(Thread) + end + + it "calls #to_int to implicitly convert non-Integer pid to Integer" do + pid = MockObject.new('mock-enumerable') + pid.should_receive(:to_int).and_return(100500) + + Process.detach(pid).join + end + + it "raises TypeError when pid argument does not have #to_int method" do + -> { Process.detach(Object.new) }.should raise_error(TypeError, "no implicit conversion of Object into Integer") + end + + it "raises TypeError when #to_int returns non-Integer value" do + pid = MockObject.new('mock-enumerable') + pid.should_receive(:to_int).and_return(:symbol) + + -> { Process.detach(pid) }.should raise_error(TypeError, "can't convert MockObject to Integer (MockObject#to_int gives Symbol)") + end end end diff --git a/spec/ruby/core/process/spawn_spec.rb b/spec/ruby/core/process/spawn_spec.rb index ad4800d4f8..7a280ccdf4 100644 --- a/spec/ruby/core/process/spawn_spec.rb +++ b/spec/ruby/core/process/spawn_spec.rb @@ -477,6 +477,16 @@ describe "Process.spawn" do # redirection + it 'redirects to the wrapped IO using wrapped_io.to_io if out: wrapped_io' do + File.open(@name, 'w') do |file| + -> do + wrapped_io = mock('wrapped IO') + wrapped_io.should_receive(:to_io).and_return(file) + Process.wait Process.spawn('echo "Hello World"', out: wrapped_io) + end.should output_to_fd("Hello World\n", file) + end + end + it "redirects STDOUT to the given file descriptor if out: Integer" do File.open(@name, 'w') do |file| -> do diff --git a/spec/ruby/core/process/times_spec.rb b/spec/ruby/core/process/times_spec.rb index d114061eb6..6142cd257c 100644 --- a/spec/ruby/core/process/times_spec.rb +++ b/spec/ruby/core/process/times_spec.rb @@ -7,7 +7,7 @@ describe "Process.times" do # TODO: Intel C Compiler does not work this example # http://rubyci.s3.amazonaws.com/icc-x64/ruby-master/log/20221013T030005Z.fail.html.gz - unless RbConfig::CONFIG['CC'].include?("icx") + unless RbConfig::CONFIG['CC']&.include?("icx") it "returns current cpu times" do t = Process.times user = t.utime diff --git a/spec/ruby/core/queue/initialize_spec.rb b/spec/ruby/core/queue/initialize_spec.rb index 83c7e595fe..c45abcd29d 100644 --- a/spec/ruby/core/queue/initialize_spec.rb +++ b/spec/ruby/core/queue/initialize_spec.rb @@ -7,6 +7,10 @@ describe "Queue#initialize" do q.should.empty? end + it "is a private method" do + Queue.private_instance_methods.include?(:initialize).should == true + end + ruby_version_is '3.1' do it "adds all elements of the passed Enumerable to self" do q = Queue.new([1, 2, 3]) @@ -30,9 +34,16 @@ describe "Queue#initialize" do q.should.empty? end - it "raises if the provided Enumerable does not respond to #to_a" do + it "raises TypeError if the provided Enumerable does not respond to #to_a" do enumerable = MockObject.new('mock-enumerable') -> { Queue.new(enumerable) }.should raise_error(TypeError, "can't convert MockObject into Array") end + + it "raises TypeError if #to_a does not return Array" do + enumerable = MockObject.new('mock-enumerable') + enumerable.should_receive(:to_a).and_return("string") + + -> { Queue.new(enumerable) }.should raise_error(TypeError, "can't convert MockObject to Array (MockObject#to_a gives String)") + end end end diff --git a/spec/ruby/core/refinement/import_methods_spec.rb b/spec/ruby/core/refinement/import_methods_spec.rb new file mode 100644 index 0000000000..1c526f5822 --- /dev/null +++ b/spec/ruby/core/refinement/import_methods_spec.rb @@ -0,0 +1,34 @@ +require_relative '../../spec_helper' + +describe "Refinement#import_methods" do + ruby_version_is "3.1" do + context "when methods are defined in Ruby code" do + it "imports methods" do + str_utils = Module.new do + def indent(level) + " " * level + self + end + end + + Module.new do + refine String do + import_methods str_utils + "foo".indent(3).should == " foo" + end + end + end + end + + context "when methods are not defined in Ruby code" do + it "raises ArgumentError" do + Module.new do + refine String do + -> { + import_methods Kernel + }.should raise_error(ArgumentError) + end + end + end + end + end +end diff --git a/spec/ruby/core/refinement/include_spec.rb b/spec/ruby/core/refinement/include_spec.rb new file mode 100644 index 0000000000..25a53f0ec7 --- /dev/null +++ b/spec/ruby/core/refinement/include_spec.rb @@ -0,0 +1,27 @@ +require_relative '../../spec_helper' + +describe "Refinement#include" do + ruby_version_is "3.1"..."3.2" do + it "warns about deprecation" do + Module.new do + refine String do + -> { + include Module.new + }.should complain(/warning: Refinement#include is deprecated and will be removed in Ruby 3.2/) + end + end + end + end + + ruby_version_is "3.2" do + it "raises a TypeError" do + Module.new do + refine String do + -> { + include Module.new + }.should raise_error(TypeError, "Refinement#include has been removed") + end + end + end + end +end diff --git a/spec/ruby/core/refinement/prepend_spec.rb b/spec/ruby/core/refinement/prepend_spec.rb new file mode 100644 index 0000000000..27b70d392a --- /dev/null +++ b/spec/ruby/core/refinement/prepend_spec.rb @@ -0,0 +1,27 @@ +require_relative '../../spec_helper' + +describe "Refinement#prepend" do + ruby_version_is "3.1"..."3.2" do + it "warns about deprecation" do + Module.new do + refine String do + -> { + prepend Module.new + }.should complain(/warning: Refinement#prepend is deprecated and will be removed in Ruby 3.2/) + end + end + end + end + + ruby_version_is "3.2" do + it "raises a TypeError" do + Module.new do + refine String do + -> { + prepend Module.new + }.should raise_error(TypeError, "Refinement#prepend has been removed") + end + end + end + end +end diff --git a/spec/ruby/core/regexp/initialize_spec.rb b/spec/ruby/core/regexp/initialize_spec.rb index 772a233e82..a1583384af 100644 --- a/spec/ruby/core/regexp/initialize_spec.rb +++ b/spec/ruby/core/regexp/initialize_spec.rb @@ -2,7 +2,7 @@ require_relative '../../spec_helper' describe "Regexp#initialize" do it "is a private method" do - Regexp.should have_private_method(:initialize) + Regexp.should have_private_instance_method(:initialize) end ruby_version_is ""..."3.0" do diff --git a/spec/ruby/core/signal/trap_spec.rb b/spec/ruby/core/signal/trap_spec.rb index 2a94a81634..10e122e072 100644 --- a/spec/ruby/core/signal/trap_spec.rb +++ b/spec/ruby/core/signal/trap_spec.rb @@ -221,6 +221,18 @@ describe "Signal.trap" do Signal.trap(:HUP, @saved_trap).should equal(@proc) end + it "raises ArgumentError when passed unknown signal" do + -> { Signal.trap(300) { } }.should raise_error(ArgumentError, "invalid signal number (300)") + -> { Signal.trap("USR10") { } }.should raise_error(ArgumentError, "unsupported signal `SIGUSR10'") + -> { Signal.trap("SIGUSR10") { } }.should raise_error(ArgumentError, "unsupported signal `SIGUSR10'") + end + + it "raises ArgumentError when passed signal is not Integer, String or Symbol" do + -> { Signal.trap(nil) { } }.should raise_error(ArgumentError, "bad signal type NilClass") + -> { Signal.trap(100.0) { } }.should raise_error(ArgumentError, "bad signal type Float") + -> { Signal.trap(Rational(100)) { } }.should raise_error(ArgumentError, "bad signal type Rational") + end + # See man 2 signal %w[KILL STOP].each do |signal| it "raises ArgumentError or Errno::EINVAL for SIG#{signal}" do diff --git a/spec/ruby/core/string/byteslice_spec.rb b/spec/ruby/core/string/byteslice_spec.rb index a49da040eb..312229523d 100644 --- a/spec/ruby/core/string/byteslice_spec.rb +++ b/spec/ruby/core/string/byteslice_spec.rb @@ -24,4 +24,10 @@ describe "String#byteslice on on non ASCII strings" do "\u3042".byteslice(1..2).should == "\x81\x82".force_encoding("UTF-8") "\u3042".byteslice(-1).should == "\x82".force_encoding("UTF-8") end + + it "returns a String in the same encoding as self" do + "ruby".encode("UTF-8").slice(0).encoding.should == Encoding::UTF_8 + "ruby".encode("US-ASCII").slice(0).encoding.should == Encoding::US_ASCII + "ruby".encode("Windows-1251").slice(0).encoding.should == Encoding::Windows_1251 + end end diff --git a/spec/ruby/core/string/capitalize_spec.rb b/spec/ruby/core/string/capitalize_spec.rb index 751f4160a6..3f85cf5ae4 100644 --- a/spec/ruby/core/string/capitalize_spec.rb +++ b/spec/ruby/core/string/capitalize_spec.rb @@ -91,6 +91,10 @@ describe "String#capitalize" do StringSpecs::MyString.new("Hello").capitalize.should be_an_instance_of(String) end end + + it "returns a String in the same encoding as self" do + "h".encode("US-ASCII").capitalize.encoding.should == Encoding::US_ASCII + end end describe "String#capitalize!" do diff --git a/spec/ruby/core/string/chars_spec.rb b/spec/ruby/core/string/chars_spec.rb index e4f26bc0cc..715e65dc90 100644 --- a/spec/ruby/core/string/chars_spec.rb +++ b/spec/ruby/core/string/chars_spec.rb @@ -1,5 +1,4 @@ require_relative 'shared/chars' -require_relative 'shared/each_char_without_block' describe "String#chars" do it_behaves_like :string_chars, :chars @@ -7,4 +6,10 @@ describe "String#chars" do it "returns an array when no block given" do "hello".chars.should == ['h', 'e', 'l', 'l', 'o'] end + + it "returns Strings in the same encoding as self" do + "hello".encode("US-ASCII").chars.each do |c| + c.encoding.should == Encoding::US_ASCII + end + end end diff --git a/spec/ruby/core/string/chomp_spec.rb b/spec/ruby/core/string/chomp_spec.rb index c03bfc7951..d0508d938f 100644 --- a/spec/ruby/core/string/chomp_spec.rb +++ b/spec/ruby/core/string/chomp_spec.rb @@ -40,6 +40,10 @@ describe "String#chomp" do "".chomp.should == "" end + it "returns a String in the same encoding as self" do + "abc\n\n".encode("US-ASCII").chomp.encoding.should == Encoding::US_ASCII + end + ruby_version_is ''...'3.0' do it "returns subclass instances when called on a subclass" do str = StringSpecs::MyString.new("hello\n").chomp diff --git a/spec/ruby/core/string/chop_spec.rb b/spec/ruby/core/string/chop_spec.rb index 266d973f67..f598d34bc8 100644 --- a/spec/ruby/core/string/chop_spec.rb +++ b/spec/ruby/core/string/chop_spec.rb @@ -60,6 +60,10 @@ describe "String#chop" do StringSpecs::MyString.new("hello\n").chop.should be_an_instance_of(String) end end + + it "returns a String in the same encoding as self" do + "abc\n\n".encode("US-ASCII").chop.encoding.should == Encoding::US_ASCII + end end describe "String#chop!" do diff --git a/spec/ruby/core/string/clone_spec.rb b/spec/ruby/core/string/clone_spec.rb index f8d40423f0..a2ba2f9877 100644 --- a/spec/ruby/core/string/clone_spec.rb +++ b/spec/ruby/core/string/clone_spec.rb @@ -54,4 +54,8 @@ describe "String#clone" do orig.should == "xtring" clone.should == "string" end + + it "returns a String in the same encoding as self" do + "a".encode("US-ASCII").clone.encoding.should == Encoding::US_ASCII + end end diff --git a/spec/ruby/core/string/delete_prefix_spec.rb b/spec/ruby/core/string/delete_prefix_spec.rb index 17ce18bcca..238de85f05 100644 --- a/spec/ruby/core/string/delete_prefix_spec.rb +++ b/spec/ruby/core/string/delete_prefix_spec.rb @@ -51,6 +51,10 @@ describe "String#delete_prefix" do s.delete_prefix('hell').should be_an_instance_of(String) end end + + it "returns a String in the same encoding as self" do + 'hello'.encode("US-ASCII").delete_prefix('hell').encoding.should == Encoding::US_ASCII + end end describe "String#delete_prefix!" do diff --git a/spec/ruby/core/string/delete_spec.rb b/spec/ruby/core/string/delete_spec.rb index b91e88b76f..87831a9d19 100644 --- a/spec/ruby/core/string/delete_spec.rb +++ b/spec/ruby/core/string/delete_spec.rb @@ -95,6 +95,10 @@ describe "String#delete" do StringSpecs::MyString.new("oh no!!!").delete("!").should be_an_instance_of(String) end end + + it "returns a String in the same encoding as self" do + "hello".encode("US-ASCII").delete("lo").encoding.should == Encoding::US_ASCII + end end describe "String#delete!" do diff --git a/spec/ruby/core/string/delete_suffix_spec.rb b/spec/ruby/core/string/delete_suffix_spec.rb index 0705c73246..6883d6938c 100644 --- a/spec/ruby/core/string/delete_suffix_spec.rb +++ b/spec/ruby/core/string/delete_suffix_spec.rb @@ -51,6 +51,10 @@ describe "String#delete_suffix" do s.delete_suffix('ello').should be_an_instance_of(String) end end + + it "returns a String in the same encoding as self" do + "hello".encode("US-ASCII").delete_suffix("ello").encoding.should == Encoding::US_ASCII + end end describe "String#delete_suffix!" do diff --git a/spec/ruby/core/string/downcase_spec.rb b/spec/ruby/core/string/downcase_spec.rb index f0a15f1e25..153b4ce191 100644 --- a/spec/ruby/core/string/downcase_spec.rb +++ b/spec/ruby/core/string/downcase_spec.rb @@ -8,6 +8,10 @@ describe "String#downcase" do "hello".downcase.should == "hello" end + it "returns a String in the same encoding as self" do + "hELLO".encode("US-ASCII").downcase.encoding.should == Encoding::US_ASCII + end + describe "full Unicode case mapping" do it "works for all of Unicode with no option" do "ÄÖÜ".downcase.should == "äöü" diff --git a/spec/ruby/core/string/dump_spec.rb b/spec/ruby/core/string/dump_spec.rb index 79a8b55e6d..81de0cfae4 100644 --- a/spec/ruby/core/string/dump_spec.rb +++ b/spec/ruby/core/string/dump_spec.rb @@ -350,7 +350,7 @@ describe "String#dump" do ].should be_computed_by(:dump) end - it "returns a string with multi-byte UTF-8 characters replaced by \\u{} notation with upper-case hex digits" do + it "returns a string with multi-byte UTF-8 characters less than or equal 0xFFFF replaced by \\uXXXX notation with upper-case hex digits" do [ [0200.chr('utf-8'), '"\u0080"'], [0201.chr('utf-8'), '"\u0081"'], [0202.chr('utf-8'), '"\u0082"'], @@ -382,15 +382,21 @@ describe "String#dump" do [0235.chr('utf-8'), '"\u009D"'], [0236.chr('utf-8'), '"\u009E"'], [0237.chr('utf-8'), '"\u009F"'], + [0177777.chr('utf-8'), '"\uFFFF"'], ].should be_computed_by(:dump) end + it "returns a string with multi-byte UTF-8 characters greater than 0xFFFF replaced by \\u{XXXXXX} notation with upper-case hex digits" do + 0x10000.chr('utf-8').dump.should == '"\u{10000}"' + 0x10FFFF.chr('utf-8').dump.should == '"\u{10FFFF}"' + end + it "includes .force_encoding(name) if the encoding isn't ASCII compatible" do "\u{876}".encode('utf-16be').dump.should.end_with?(".force_encoding(\"UTF-16BE\")") "\u{876}".encode('utf-16le').dump.should.end_with?(".force_encoding(\"UTF-16LE\")") end - it "keeps origin encoding" do + it "returns a String in the same encoding as self" do "foo".encode("ISO-8859-1").dump.encoding.should == Encoding::ISO_8859_1 "foo".encode('windows-1251').dump.encoding.should == Encoding::Windows_1251 1.chr.dump.encoding.should == Encoding::US_ASCII diff --git a/spec/ruby/core/string/dup_spec.rb b/spec/ruby/core/string/dup_spec.rb index eec3cf0a70..73f71b8ffc 100644 --- a/spec/ruby/core/string/dup_spec.rb +++ b/spec/ruby/core/string/dup_spec.rb @@ -58,4 +58,8 @@ describe "String#dup" do orig.should == "c" copy.should == "b" end + + it "returns a String in the same encoding as self" do + "hello".encode("US-ASCII").dup.encoding.should == Encoding::US_ASCII + end end diff --git a/spec/ruby/core/string/lines_spec.rb b/spec/ruby/core/string/lines_spec.rb index ad4b119074..40ab5f71d8 100644 --- a/spec/ruby/core/string/lines_spec.rb +++ b/spec/ruby/core/string/lines_spec.rb @@ -1,7 +1,6 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' require_relative 'shared/each_line' -require_relative 'shared/each_line_without_block' describe "String#lines" do it_behaves_like :string_each_line, :lines diff --git a/spec/ruby/core/string/reverse_spec.rb b/spec/ruby/core/string/reverse_spec.rb index 4206b8af90..73526256ef 100644 --- a/spec/ruby/core/string/reverse_spec.rb +++ b/spec/ruby/core/string/reverse_spec.rb @@ -37,6 +37,10 @@ describe "String#reverse" do str.reverse.should == "體黑正\xDE\xDF軟微" end + + it "returns a String in the same encoding as self" do + "stressed".encode("US-ASCII").reverse.encoding.should == Encoding::US_ASCII + end end describe "String#reverse!" do diff --git a/spec/ruby/core/string/scan_spec.rb b/spec/ruby/core/string/scan_spec.rb index ab73f5747b..a2d1815132 100644 --- a/spec/ruby/core/string/scan_spec.rb +++ b/spec/ruby/core/string/scan_spec.rb @@ -69,6 +69,12 @@ describe "String#scan" do it "does not raise any errors when passed a multi-byte string" do "あああaaaあああ".scan("あああ").should == ["あああ", "あああ"] end + + it "returns Strings in the same encoding as self" do + "cruel world".encode("US-ASCII").scan(/\w+/).each do |s| + s.encoding.should == Encoding::US_ASCII + end + end end describe "String#scan with pattern and block" do diff --git a/spec/ruby/core/string/scrub_spec.rb b/spec/ruby/core/string/scrub_spec.rb index 66755bcc7b..a51fbd020a 100644 --- a/spec/ruby/core/string/scrub_spec.rb +++ b/spec/ruby/core/string/scrub_spec.rb @@ -31,6 +31,11 @@ describe "String#scrub with a default replacement" do input.scrub.should == "abc?????" end + it "returns a String in the same encoding as self" do + x81 = [0x81].pack('C').force_encoding('utf-8') + "abc\u3042#{x81}".scrub.encoding.should == Encoding::UTF_8 + end + ruby_version_is '3.0' do it "returns String instances when called on a subclass" do StringSpecs::MyString.new("foo").scrub.should be_an_instance_of(String) @@ -80,6 +85,11 @@ describe "String#scrub with a custom replacement" do block.should raise_error(ArgumentError) end + it "returns a String in the same encoding as self" do + x81 = [0x81].pack('C').force_encoding('utf-8') + "abc\u3042#{x81}".scrub("*").encoding.should == Encoding::UTF_8 + end + it "raises TypeError when a non String replacement is given" do x81 = [0x81].pack('C').force_encoding('utf-8') block = -> { "foo#{x81}".scrub(1) } diff --git a/spec/ruby/core/string/shared/each_line.rb b/spec/ruby/core/string/shared/each_line.rb index bfedf8f35a..df78bd2186 100644 --- a/spec/ruby/core/string/shared/each_line.rb +++ b/spec/ruby/core/string/shared/each_line.rb @@ -122,6 +122,12 @@ describe :string_each_line, shared: true do out.should == ["hello\n", "world."] end + it "returns Strings in the same encoding as self" do + "one\ntwo\r\nthree".encode("US-ASCII").send(@method) do |s| + s.encoding.should == Encoding::US_ASCII + end + end + it "raises a TypeError when the separator can't be converted to a string" do -> { "hello world".send(@method, false) {} }.should raise_error(TypeError) -> { "hello world".send(@method, mock('x')) {} }.should raise_error(TypeError) diff --git a/spec/ruby/core/string/shared/partition.rb b/spec/ruby/core/string/shared/partition.rb index 7dc3d9cc0b..41b3c7e0c9 100644 --- a/spec/ruby/core/string/shared/partition.rb +++ b/spec/ruby/core/string/shared/partition.rb @@ -33,4 +33,19 @@ describe :string_partition, shared: true do end end end + + it "returns before- and after- parts in the same encoding as self" do + strings = "hello".encode("US-ASCII").send(@method, "ello") + strings[0].encoding.should == Encoding::US_ASCII + strings[2].encoding.should == Encoding::US_ASCII + + strings = "hello".encode("US-ASCII").send(@method, /ello/) + strings[0].encoding.should == Encoding::US_ASCII + strings[2].encoding.should == Encoding::US_ASCII + end + + it "returns the matching part in the separator's encoding" do + strings = "hello".encode("US-ASCII").send(@method, "ello") + strings[1].encoding.should == Encoding::UTF_8 + end end diff --git a/spec/ruby/core/string/shared/slice.rb b/spec/ruby/core/string/shared/slice.rb index 713234fffd..a7c1d05b56 100644 --- a/spec/ruby/core/string/shared/slice.rb +++ b/spec/ruby/core/string/shared/slice.rb @@ -80,7 +80,7 @@ describe :string_slice_index_length, shared: true do "hello there".send(@method, -3,2).should == "er" end - it "returns a string with the same encoding" do + it "returns a string with the same encoding as self" do s = "hello there" s.send(@method, 1, 9).encoding.should == s.encoding @@ -206,6 +206,10 @@ describe :string_slice_range, shared: true do "x".send(@method, 1..-1).should == "" end + it "returns a String in the same encoding as self" do + "hello there".encode("US-ASCII").send(@method, 1..1).encoding.should == Encoding::US_ASCII + end + it "returns nil if the beginning of the range falls outside of self" do "hello there".send(@method, 12..-1).should == nil "hello there".send(@method, 20..25).should == nil @@ -328,7 +332,8 @@ describe :string_slice_regexp, shared: true do "hello there".send(@method, /xyz/).should == nil end - not_supported_on :opal do + it "returns a String in the same encoding as self" do + "hello there".encode("US-ASCII").send(@method, /[aeiou](.)\1/).encoding.should == Encoding::US_ASCII end ruby_version_is ''...'3.0' do @@ -391,6 +396,10 @@ describe :string_slice_regexp_index, shared: true do $~[1].should == nil end + it "returns a String in the same encoding as self" do + "hello there".encode("US-ASCII").send(@method, /[aeiou](.)\1/, 0).encoding.should == Encoding::US_ASCII + end + it "calls to_int on the given index" do obj = mock('2') obj.should_receive(:to_int).and_return(2) diff --git a/spec/ruby/core/string/shared/strip.rb b/spec/ruby/core/string/shared/strip.rb index 9c232b4694..0c0aae20f3 100644 --- a/spec/ruby/core/string/shared/strip.rb +++ b/spec/ruby/core/string/shared/strip.rb @@ -2,6 +2,10 @@ require_relative '../../../spec_helper' require_relative '../fixtures/classes' describe :string_strip, shared: true do + it "returns a String in the same encoding as self" do + " hello ".encode("US-ASCII").send(@method).encoding.should == Encoding::US_ASCII + end + ruby_version_is '3.0' do it "returns String instances when called on a subclass" do StringSpecs::MyString.new(" hello ").send(@method).should be_an_instance_of(String) diff --git a/spec/ruby/core/string/shared/succ.rb b/spec/ruby/core/string/shared/succ.rb index 66edf6dc82..3605fa99a2 100644 --- a/spec/ruby/core/string/shared/succ.rb +++ b/spec/ruby/core/string/shared/succ.rb @@ -74,6 +74,10 @@ describe :string_succ, shared: true do StringSpecs::MyString.new("z").send(@method).should be_an_instance_of(String) end end + + it "returns a String in the same encoding as self" do + "z".encode("US-ASCII").send(@method).encoding.should == Encoding::US_ASCII + end end describe :string_succ_bang, shared: true do diff --git a/spec/ruby/core/string/split_spec.rb b/spec/ruby/core/string/split_spec.rb index 0417486692..519c5d845d 100644 --- a/spec/ruby/core/string/split_spec.rb +++ b/spec/ruby/core/string/split_spec.rb @@ -246,6 +246,13 @@ describe "String#split with String" do it "doesn't split on non-ascii whitespace" do "a\u{2008}b".split(" ").should == ["a\u{2008}b"] end + + it "returns Strings in the same encoding as self" do + strings = "hello world".encode("US-ASCII").split(" ") + + strings[0].encoding.should == Encoding::US_ASCII + strings[1].encoding.should == Encoding::US_ASCII + end end describe "String#split with Regexp" do @@ -443,13 +450,12 @@ describe "String#split with Regexp" do end end - it "retains the encoding of the source string" do + it "returns Strings in the same encoding as self" do ary = "а б в".split encodings = ary.map { |s| s.encoding } encodings.should == [Encoding::UTF_8, Encoding::UTF_8, Encoding::UTF_8] end - it "splits a string on each character for a multibyte encoding and empty split" do "That's why efficiency could not be helped".split("").size.should == 39 end @@ -598,4 +604,11 @@ describe "String#split with Regexp" do -> { "hello".split(false) }.should raise_error(TypeError) -> { "hello".split(Object.new) }.should raise_error(TypeError) end + + it "returns Strings in the same encoding as self" do + strings = "hello world".encode("US-ASCII").split(/ /) + + strings[0].encoding.should == Encoding::US_ASCII + strings[1].encoding.should == Encoding::US_ASCII + end end diff --git a/spec/ruby/core/string/squeeze_spec.rb b/spec/ruby/core/string/squeeze_spec.rb index 5dc12a4247..2f3fa65745 100644 --- a/spec/ruby/core/string/squeeze_spec.rb +++ b/spec/ruby/core/string/squeeze_spec.rb @@ -64,6 +64,11 @@ describe "String#squeeze" do "hello room".squeeze(other_string, other_string2).should == "hello rom" end + it "returns a String in the same encoding as self" do + "yellow moon".encode("US-ASCII").squeeze.encoding.should == Encoding::US_ASCII + "yellow moon".encode("US-ASCII").squeeze("a").encoding.should == Encoding::US_ASCII + end + it "raises a TypeError when one set arg can't be converted to a string" do -> { "hello world".squeeze([]) }.should raise_error(TypeError) -> { "hello world".squeeze(Object.new)}.should raise_error(TypeError) diff --git a/spec/ruby/core/string/swapcase_spec.rb b/spec/ruby/core/string/swapcase_spec.rb index 6307a1eaaf..d369ab3e4e 100644 --- a/spec/ruby/core/string/swapcase_spec.rb +++ b/spec/ruby/core/string/swapcase_spec.rb @@ -9,6 +9,10 @@ describe "String#swapcase" do "+++---111222???".swapcase.should == "+++---111222???" end + it "returns a String in the same encoding as self" do + "Hello".encode("US-ASCII").swapcase.encoding.should == Encoding::US_ASCII + end + describe "full Unicode case mapping" do it "works for all of Unicode with no option" do "äÖü".swapcase.should == "ÄöÜ" diff --git a/spec/ruby/core/string/undump_spec.rb b/spec/ruby/core/string/undump_spec.rb index 08058d9bd1..6ff220161c 100644 --- a/spec/ruby/core/string/undump_spec.rb +++ b/spec/ruby/core/string/undump_spec.rb @@ -389,7 +389,7 @@ describe "String#undump" do '"\\bv".force_encoding("UTF-16BE")'.undump.should == "\u0876".encode('utf-16be') end - it "keeps origin encoding" do + it "returns a String in the same encoding as self" do '"foo"'.encode("ISO-8859-1").undump.encoding.should == Encoding::ISO_8859_1 '"foo"'.encode('windows-1251').undump.encoding.should == Encoding::Windows_1251 end diff --git a/spec/ruby/core/string/unpack/b_spec.rb b/spec/ruby/core/string/unpack/b_spec.rb index fcabc99731..2cf5ebad34 100644 --- a/spec/ruby/core/string/unpack/b_spec.rb +++ b/spec/ruby/core/string/unpack/b_spec.rb @@ -86,8 +86,18 @@ describe "String#unpack with format 'B'" do ].should be_computed_by(:unpack, "BBB") end - it "ignores NULL bytes between directives" do - "\x80\x00".unpack("B\x00B").should == ["1", "0"] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + "\x80\x00".unpack("B\x00B").should == ["1", "0"] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "\x80\x00".unpack("B\x00B") + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do @@ -182,8 +192,18 @@ describe "String#unpack with format 'b'" do ].should be_computed_by(:unpack, "bbb") end - it "ignores NULL bytes between directives" do - "\x01\x00".unpack("b\x00b").should == ["1", "0"] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + "\x01\x00".unpack("b\x00b").should == ["1", "0"] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "\x01\x00".unpack("b\x00b") + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/string/unpack/c_spec.rb b/spec/ruby/core/string/unpack/c_spec.rb index ed8caa4895..dbcbacc74d 100644 --- a/spec/ruby/core/string/unpack/c_spec.rb +++ b/spec/ruby/core/string/unpack/c_spec.rb @@ -35,8 +35,18 @@ describe :string_unpack_8bit, shared: true do ].should be_computed_by(:unpack, unpack_format(3)) end - it "ignores NULL bytes between directives" do - "abc".unpack(unpack_format("\000", 2)).should == [97, 98] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + "abc".unpack(unpack_format("\000", 2)).should == [97, 98] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "abc".unpack(unpack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/string/unpack/h_spec.rb b/spec/ruby/core/string/unpack/h_spec.rb index f2f5dcf396..ee08d20926 100644 --- a/spec/ruby/core/string/unpack/h_spec.rb +++ b/spec/ruby/core/string/unpack/h_spec.rb @@ -56,8 +56,18 @@ describe "String#unpack with format 'H'" do ].should be_computed_by(:unpack, "HHH") end - it "ignores NULL bytes between directives" do - "\x01\x10".unpack("H\x00H").should == ["0", "1"] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + "\x01\x10".unpack("H\x00H").should == ["0", "1"] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "\x01\x10".unpack("H\x00H") + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do @@ -121,8 +131,18 @@ describe "String#unpack with format 'h'" do ].should be_computed_by(:unpack, "hhh") end - it "ignores NULL bytes between directives" do - "\x01\x10".unpack("h\x00h").should == ["1", "0"] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + "\x01\x10".unpack("h\x00h").should == ["1", "0"] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "\x01\x10".unpack("h\x00h") + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/string/unpack/shared/basic.rb b/spec/ruby/core/string/unpack/shared/basic.rb index f636f4689f..bb5302edc5 100644 --- a/spec/ruby/core/string/unpack/shared/basic.rb +++ b/spec/ruby/core/string/unpack/shared/basic.rb @@ -8,20 +8,6 @@ describe :string_unpack_basic, shared: true do d.should_receive(:to_str).and_return("a"+unpack_format) "abc".unpack(d).should be_an_instance_of(Array) end - - it "raises a TypeError when passed nil" do - -> { "abc".unpack(nil) }.should raise_error(TypeError) - end - - it "raises a TypeError when passed an Integer" do - -> { "abc".unpack(1) }.should raise_error(TypeError) - end - - ruby_version_is "3.1" do - it "starts unpacking from the given offset" do - "abc".unpack("CC", offset: 1).should == [98, 99] - end - end end describe :string_unpack_no_platform, shared: true do @@ -32,18 +18,4 @@ describe :string_unpack_no_platform, shared: true do it "raises an ArgumentError when the format modifier is '!'" do -> { "abcdefgh".unpack(unpack_format("!")) }.should raise_error(ArgumentError) end - - ruby_version_is "3.1" do - it "raises an ArgumentError when the offset is negative" do - -> { "a".unpack("C", offset: -1) }.should raise_error(ArgumentError) - end - - it "returns nil if the offset is at the end of the string" do - "a".unpack("C", offset: 1).should == [nil] - end - - it "raises an ArgumentError when the offset is larget than the string" do - -> { "a".unpack("C", offset: 2) }.should raise_error(ArgumentError) - end - end end diff --git a/spec/ruby/core/string/unpack/shared/float.rb b/spec/ruby/core/string/unpack/shared/float.rb index 99bd8a3401..ccddf94f99 100644 --- a/spec/ruby/core/string/unpack/shared/float.rb +++ b/spec/ruby/core/string/unpack/shared/float.rb @@ -56,9 +56,19 @@ describe :string_unpack_float_le, shared: true do [nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true end - it "ignores NULL bytes between directives" do - array = "\x9a\x999@33\xb3?".unpack(unpack_format("\000", 2)) - array.should == [2.9000000953674316, 1.399999976158142] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + array = "\x9a\x999@33\xb3?".unpack(unpack_format("\000", 2)) + array.should == [2.9000000953674316, 1.399999976158142] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "\x9a\x999@33\xb3?".unpack(unpack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do @@ -123,9 +133,19 @@ describe :string_unpack_float_be, shared: true do [nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true end - it "ignores NULL bytes between directives" do - array = "@9\x99\x9a?\xb333".unpack(unpack_format("\000", 2)) - array.should == [2.9000000953674316, 1.399999976158142] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + array = "@9\x99\x9a?\xb333".unpack(unpack_format("\000", 2)) + array.should == [2.9000000953674316, 1.399999976158142] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "@9\x99\x9a?\xb333".unpack(unpack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do @@ -193,8 +213,18 @@ describe :string_unpack_double_le, shared: true do [nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true end - it "ignores NULL bytes between directives" do - "333333\x07@ffffff\xf6?".unpack(unpack_format("\000", 2)).should == [2.9, 1.4] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + "333333\x07@ffffff\xf6?".unpack(unpack_format("\000", 2)).should == [2.9, 1.4] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "333333\x07@ffffff\xf6?".unpack(unpack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do @@ -261,8 +291,18 @@ describe :string_unpack_double_be, shared: true do [nan_value].pack(unpack_format).unpack(unpack_format).first.nan?.should be_true end - it "ignores NULL bytes between directives" do - "@\x07333333?\xf6ffffff".unpack(unpack_format("\000", 2)).should == [2.9, 1.4] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + "@\x07333333?\xf6ffffff".unpack(unpack_format("\000", 2)).should == [2.9, 1.4] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "@\x07333333?\xf6ffffff".unpack(unpack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/string/unpack/shared/integer.rb b/spec/ruby/core/string/unpack/shared/integer.rb index cbaa743683..ba4f149dad 100644 --- a/spec/ruby/core/string/unpack/shared/integer.rb +++ b/spec/ruby/core/string/unpack/shared/integer.rb @@ -32,8 +32,18 @@ describe :string_unpack_16bit_le, shared: true do ].should be_computed_by(:unpack, unpack_format(3)) end - it "ignores NULL bytes between directives" do - "abcd".unpack(unpack_format("\000", 2)).should == [25185, 25699] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + "abcd".unpack(unpack_format("\000", 2)).should == [25185, 25699] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "abcd".unpack(unpack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do @@ -85,8 +95,18 @@ describe :string_unpack_16bit_be, shared: true do ].should be_computed_by(:unpack, unpack_format(3)) end - it "ignores NULL bytes between directives" do - "badc".unpack(unpack_format("\000", 2)).should == [25185, 25699] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + "badc".unpack(unpack_format("\000", 2)).should == [25185, 25699] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "badc".unpack(unpack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do @@ -139,8 +159,18 @@ describe :string_unpack_32bit_le, shared: true do ].should be_computed_by(:unpack, unpack_format(3)) end - it "ignores NULL bytes between directives" do - "abcdefgh".unpack(unpack_format("\000", 2)).should == [1684234849, 1751606885] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + "abcdefgh".unpack(unpack_format("\000", 2)).should == [1684234849, 1751606885] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "abcdefgh".unpack(unpack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do @@ -193,8 +223,18 @@ describe :string_unpack_32bit_be, shared: true do ].should be_computed_by(:unpack, unpack_format(3)) end - it "ignores NULL bytes between directives" do - "dcbahgfe".unpack(unpack_format("\000", 2)).should == [1684234849, 1751606885] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + "dcbahgfe".unpack(unpack_format("\000", 2)).should == [1684234849, 1751606885] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "dcbahgfe".unpack(unpack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do @@ -243,9 +283,19 @@ describe :string_unpack_64bit_le, shared: true do "abc".unpack(unpack_format('*')).should == [] end - it "ignores NULL bytes between directives" do - array = "abcdefghabghefcd".unpack(unpack_format("\000", 2)) - array.should == [7523094288207667809, 7233738012216484449] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + array = "abcdefghabghefcd".unpack(unpack_format("\000", 2)) + array.should == [7523094288207667809, 7233738012216484449] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "badc".unpack(unpack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do @@ -305,9 +355,19 @@ describe :string_unpack_64bit_be, shared: true do "abc".unpack(unpack_format('*')).should == [] end - it "ignores NULL bytes between directives" do - array = "hgfedcbadcfehgba".unpack(unpack_format("\000", 2)) - array.should == [7523094288207667809, 7233738012216484449] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + array = "hgfedcbadcfehgba".unpack(unpack_format("\000", 2)) + array.should == [7523094288207667809, 7233738012216484449] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "hgfedcbadcfehgba".unpack(unpack_format("\000", 2)) + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/string/unpack/shared/unicode.rb b/spec/ruby/core/string/unpack/shared/unicode.rb index a2b4e142b2..ce1f29fe87 100644 --- a/spec/ruby/core/string/unpack/shared/unicode.rb +++ b/spec/ruby/core/string/unpack/shared/unicode.rb @@ -50,8 +50,18 @@ describe :string_unpack_unicode, shared: true do "\xc2\x80".unpack("UUUU").should == [0x80] end - it "ignores NULL bytes between directives" do - "\x01\x02".unpack("U\x00U").should == [1, 2] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + "\x01\x02".unpack("U\x00U").should == [1, 2] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "\x01\x02".unpack("U\x00U") + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/string/unpack/w_spec.rb b/spec/ruby/core/string/unpack/w_spec.rb index 011c75f5c4..b213b32921 100644 --- a/spec/ruby/core/string/unpack/w_spec.rb +++ b/spec/ruby/core/string/unpack/w_spec.rb @@ -15,8 +15,18 @@ describe "String#unpack with directive 'w'" do ].should be_computed_by(:unpack, "w") end - it "ignores NULL bytes between directives" do - "\x01\x02\x03".unpack("w\x00w").should == [1, 2] + ruby_version_is ""..."3.3" do + it "ignores NULL bytes between directives" do + "\x01\x02\x03".unpack("w\x00w").should == [1, 2] + end + end + + ruby_version_is "3.3" do + it "raise ArgumentError for NULL bytes between directives" do + -> { + "\x01\x02\x03".unpack("w\x00w") + }.should raise_error(ArgumentError, /unknown unpack directive/) + end end it "ignores spaces between directives" do diff --git a/spec/ruby/core/string/unpack1_spec.rb b/spec/ruby/core/string/unpack1_spec.rb index f59bd92d6a..df830916a3 100644 --- a/spec/ruby/core/string/unpack1_spec.rb +++ b/spec/ruby/core/string/unpack1_spec.rb @@ -15,16 +15,22 @@ describe "String#unpack1" do "ZA".unpack1("B*", offset: 1).should == "01000001" end + it "traits offset as a bytes offset" do + "؈".unpack("CC").should == [216, 136] + "؈".unpack1("C").should == 216 + "؈".unpack1("C", offset: 1).should == 136 + end + it "raises an ArgumentError when the offset is negative" do - -> { "a".unpack1("C", offset: -1) }.should raise_error(ArgumentError) + -> { "a".unpack1("C", offset: -1) }.should raise_error(ArgumentError, "offset can't be negative") end it "returns nil if the offset is at the end of the string" do "a".unpack1("C", offset: 1).should == nil end - it "raises an ArgumentError when the offset is larget than the string" do - -> { "a".unpack1("C", offset: 2) }.should raise_error(ArgumentError) + it "raises an ArgumentError when the offset is larger than the string bytesize" do + -> { "a".unpack1("C", offset: 2) }.should raise_error(ArgumentError, "offset outside of string") end end end diff --git a/spec/ruby/core/string/unpack_spec.rb b/spec/ruby/core/string/unpack_spec.rb new file mode 100644 index 0000000000..4ff7d07460 --- /dev/null +++ b/spec/ruby/core/string/unpack_spec.rb @@ -0,0 +1,34 @@ +require_relative '../../spec_helper' + +describe "String#unpack" do + it "raises a TypeError when passed nil" do + -> { "abc".unpack(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed an Integer" do + -> { "abc".unpack(1) }.should raise_error(TypeError) + end + + ruby_version_is "3.1" do + it "starts unpacking from the given offset" do + "abc".unpack("CC", offset: 1).should == [98, 99] + end + + it "traits offset as a bytes offset" do + "؈".unpack("CC").should == [216, 136] + "؈".unpack("CC", offset: 1).should == [136, nil] + end + + it "raises an ArgumentError when the offset is negative" do + -> { "a".unpack("C", offset: -1) }.should raise_error(ArgumentError, "offset can't be negative") + end + + it "returns nil if the offset is at the end of the string" do + "a".unpack("C", offset: 1).should == [nil] + end + + it "raises an ArgumentError when the offset is larget than the string" do + -> { "a".unpack("C", offset: 2) }.should raise_error(ArgumentError, "offset outside of string") + end + end +end
\ No newline at end of file diff --git a/spec/ruby/core/string/upcase_spec.rb b/spec/ruby/core/string/upcase_spec.rb index 209fe73b6e..5ce7b0b95f 100644 --- a/spec/ruby/core/string/upcase_spec.rb +++ b/spec/ruby/core/string/upcase_spec.rb @@ -8,6 +8,10 @@ describe "String#upcase" do "hello".upcase.should == "HELLO" end + it "returns a String in the same encoding as self" do + "hello".encode("US-ASCII").upcase.encoding.should == Encoding::US_ASCII + end + describe "full Unicode case mapping" do it "works for all of Unicode with no option" do "äöü".upcase.should == "ÄÖÜ" diff --git a/spec/ruby/core/string/valid_encoding/utf_8_spec.rb b/spec/ruby/core/string/valid_encoding/utf_8_spec.rb new file mode 100644 index 0000000000..a14c3af830 --- /dev/null +++ b/spec/ruby/core/string/valid_encoding/utf_8_spec.rb @@ -0,0 +1,214 @@ +# -*- encoding: utf-8 -*- +require_relative '../../../spec_helper' + +describe "String#valid_encoding? and UTF-8" do + def utf8(bytes) + bytes.pack("C*").force_encoding("UTF-8") + end + + describe "1-byte character" do + it "is valid if is in format 0xxxxxxx" do + utf8([0b00000000]).valid_encoding?.should == true + utf8([0b01111111]).valid_encoding?.should == true + end + + it "is not valid if is not in format 0xxxxxxx" do + utf8([0b10000000]).valid_encoding?.should == false + utf8([0b11111111]).valid_encoding?.should == false + end + end + + describe "2-bytes character" do + it "is valid if in format [110xxxxx 10xxxxx]" do + utf8([0b11000010, 0b10000000]).valid_encoding?.should == true + utf8([0b11000010, 0b10111111]).valid_encoding?.should == true + + utf8([0b11011111, 0b10000000]).valid_encoding?.should == true + utf8([0b11011111, 0b10111111]).valid_encoding?.should == true + end + + it "is not valid if the first byte is not in format 110xxxxx" do + utf8([0b00000010, 0b10000000]).valid_encoding?.should == false + utf8([0b00100010, 0b10000000]).valid_encoding?.should == false + utf8([0b01000010, 0b10000000]).valid_encoding?.should == false + utf8([0b01100010, 0b10000000]).valid_encoding?.should == false + utf8([0b10000010, 0b10000000]).valid_encoding?.should == false + utf8([0b10100010, 0b10000000]).valid_encoding?.should == false + utf8([0b11000010, 0b10000000]).valid_encoding?.should == true # correct bytes + utf8([0b11100010, 0b10000000]).valid_encoding?.should == false + end + + it "is not valid if the second byte is not in format 10xxxxxx" do + utf8([0b11000010, 0b00000000]).valid_encoding?.should == false + utf8([0b11000010, 0b01000000]).valid_encoding?.should == false + utf8([0b11000010, 0b11000000]).valid_encoding?.should == false + end + + it "is not valid if is smaller than [xxxxxx10 xx000000] (codepoints < U+007F, that are encoded with the 1-byte format)" do + utf8([0b11000000, 0b10111111]).valid_encoding?.should == false + utf8([0b11000001, 0b10111111]).valid_encoding?.should == false + end + + it "is not valid if the first byte is missing" do + bytes = [0b11000010, 0b10000000] + utf8(bytes[1..1]).valid_encoding?.should == false + end + + it "is not valid if the second byte is missing" do + bytes = [0b11000010, 0b10000000] + utf8(bytes[0..0]).valid_encoding?.should == false + end + end + + describe "3-bytes character" do + it "is valid if in format [1110xxxx 10xxxxxx 10xxxxxx]" do + utf8([0b11100000, 0b10100000, 0b10000000]).valid_encoding?.should == true + utf8([0b11100000, 0b10100000, 0b10111111]).valid_encoding?.should == true + utf8([0b11100000, 0b10111111, 0b10111111]).valid_encoding?.should == true + utf8([0b11101111, 0b10111111, 0b10111111]).valid_encoding?.should == true + end + + it "is not valid if the first byte is not in format 1110xxxx" do + utf8([0b00000000, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b00010000, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b00100000, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b00110000, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b01000000, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b01010000, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b01100000, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b01110000, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b10000000, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b10010000, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b10100000, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b10110000, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b11000000, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b11010000, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b11100000, 0b10100000, 0b10000000]).valid_encoding?.should == true # correct bytes + utf8([0b11110000, 0b10100000, 0b10000000]).valid_encoding?.should == false + end + + it "is not valid if the second byte is not in format 10xxxxxx" do + utf8([0b11100000, 0b00100000, 0b10000000]).valid_encoding?.should == false + utf8([0b11100000, 0b01100000, 0b10000000]).valid_encoding?.should == false + utf8([0b11100000, 0b11100000, 0b10000000]).valid_encoding?.should == false + end + + it "is not valid if the third byte is not in format 10xxxxxx" do + utf8([0b11100000, 0b10100000, 0b00000000]).valid_encoding?.should == false + utf8([0b11100000, 0b10100000, 0b01000000]).valid_encoding?.should == false + utf8([0b11100000, 0b10100000, 0b01000000]).valid_encoding?.should == false + end + + it "is not valid if is smaller than [xxxx0000 xx100000 xx000000] (codepoints < U+07FF that are encoded with the 2-byte format)" do + utf8([0b11100000, 0b10010000, 0b10000000]).valid_encoding?.should == false + utf8([0b11100000, 0b10001000, 0b10000000]).valid_encoding?.should == false + utf8([0b11100000, 0b10000100, 0b10000000]).valid_encoding?.should == false + utf8([0b11100000, 0b10000010, 0b10000000]).valid_encoding?.should == false + utf8([0b11100000, 0b10000001, 0b10000000]).valid_encoding?.should == false + utf8([0b11100000, 0b10000000, 0b10000000]).valid_encoding?.should == false + end + + it "is not valid if in range [xxxx1101 xx100000 xx000000] - [xxxx1101 xx111111 xx111111] (codepoints U+D800 - U+DFFF)" do + utf8([0b11101101, 0b10100000, 0b10000000]).valid_encoding?.should == false + utf8([0b11101101, 0b10100000, 0b10000001]).valid_encoding?.should == false + utf8([0b11101101, 0b10111111, 0b10111111]).valid_encoding?.should == false + + utf8([0b11101101, 0b10011111, 0b10111111]).valid_encoding?.should == true # lower boundary - 1 + utf8([0b11101110, 0b10000000, 0b10000000]).valid_encoding?.should == true # upper boundary + 1 + end + + it "is not valid if the first byte is missing" do + bytes = [0b11100000, 0b10100000, 0b10000000] + utf8(bytes[2..3]).valid_encoding?.should == false + end + + it "is not valid if the second byte is missing" do + bytes = [0b11100000, 0b10100000, 0b10000000] + utf8([bytes[0], bytes[2]]).valid_encoding?.should == false + end + + it "is not valid if the second and the third bytes are missing" do + bytes = [0b11100000, 0b10100000, 0b10000000] + utf8(bytes[0..0]).valid_encoding?.should == false + end + end + + describe "4-bytes character" do + it "is valid if in format [11110xxx 10xxxxxx 10xxxxxx 10xxxxxx]" do + utf8([0b11110000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == true + utf8([0b11110000, 0b10010000, 0b10000000, 0b10111111]).valid_encoding?.should == true + utf8([0b11110000, 0b10010000, 0b10111111, 0b10111111]).valid_encoding?.should == true + utf8([0b11110000, 0b10111111, 0b10111111, 0b10111111]).valid_encoding?.should == true + utf8([0b11110100, 0b10001111, 0b10111111, 0b10111111]).valid_encoding?.should == true + end + + it "is not valid if the first byte is not in format 11110xxx" do + utf8([0b11100000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == false + utf8([0b11010000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == false + utf8([0b10110000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == false + utf8([0b01110000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == false + end + + it "is not valid if the second byte is not in format 10xxxxxx" do + utf8([0b11110000, 0b00010000, 0b10000000, 0b10000000]).valid_encoding?.should == false + utf8([0b11110000, 0b01010000, 0b10000000, 0b10000000]).valid_encoding?.should == false + utf8([0b11110000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == true # correct bytes + utf8([0b11110000, 0b11010000, 0b10000000, 0b10000000]).valid_encoding?.should == false + end + + it "is not valid if the third byte is not in format 10xxxxxx" do + utf8([0b11110000, 0b10010000, 0b00000000, 0b10000000]).valid_encoding?.should == false + utf8([0b11110000, 0b10010000, 0b01000000, 0b10000000]).valid_encoding?.should == false + utf8([0b11110000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == true # correct bytes + utf8([0b11110000, 0b10010000, 0b11000000, 0b10000000]).valid_encoding?.should == false + end + + it "is not valid if the forth byte is not in format 10xxxxxx" do + utf8([0b11110000, 0b10010000, 0b10000000, 0b00000000]).valid_encoding?.should == false + utf8([0b11110000, 0b10010000, 0b10000000, 0b01000000]).valid_encoding?.should == false + utf8([0b11110000, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == true # correct bytes + utf8([0b11110000, 0b10010000, 0b10000000, 0b11000000]).valid_encoding?.should == false + end + + it "is not valid if is smaller than [xxxxx000 xx001000 xx000000 xx000000] (codepoint < U+10000)" do + utf8([0b11110000, 0b10000111, 0b10000000, 0b10000000]).valid_encoding?.should == false + utf8([0b11110000, 0b10000110, 0b10000000, 0b10000000]).valid_encoding?.should == false + utf8([0b11110000, 0b10000101, 0b10000000, 0b10000000]).valid_encoding?.should == false + utf8([0b11110000, 0b10000100, 0b10000000, 0b10000000]).valid_encoding?.should == false + utf8([0b11110000, 0b10000011, 0b10000000, 0b10000000]).valid_encoding?.should == false + utf8([0b11110000, 0b10000010, 0b10000000, 0b10000000]).valid_encoding?.should == false + utf8([0b11110000, 0b10000001, 0b10000000, 0b10000000]).valid_encoding?.should == false + utf8([0b11110000, 0b10000000, 0b10000000, 0b10000000]).valid_encoding?.should == false + end + + it "is not valid if is greater than [xxxxx100 xx001111 xx111111 xx111111] (codepoint > U+10FFFF)" do + utf8([0b11110100, 0b10010000, 0b10000000, 0b10000000]).valid_encoding?.should == false + utf8([0b11110100, 0b10100000, 0b10000000, 0b10000000]).valid_encoding?.should == false + utf8([0b11110100, 0b10110000, 0b10000000, 0b10000000]).valid_encoding?.should == false + + utf8([0b11110101, 0b10001111, 0b10111111, 0b10111111]).valid_encoding?.should == false + utf8([0b11110110, 0b10001111, 0b10111111, 0b10111111]).valid_encoding?.should == false + utf8([0b11110111, 0b10001111, 0b10111111, 0b10111111]).valid_encoding?.should == false + end + + it "is not valid if the first byte is missing" do + bytes = [0b11110000, 0b10010000, 0b10000000, 0b10000000] + utf8(bytes[1..3]).valid_encoding?.should == false + end + + it "is not valid if the second byte is missing" do + bytes = [0b11110000, 0b10010000, 0b10000000, 0b10000000] + utf8([bytes[0], bytes[2], bytes[3]]).valid_encoding?.should == false + end + + it "is not valid if the second and the third bytes are missing" do + bytes = [0b11110000, 0b10010000, 0b10000000, 0b10000000] + utf8([bytes[0], bytes[3]]).valid_encoding?.should == false + end + + it "is not valid if the second, the third and the fourth bytes are missing" do + bytes = [0b11110000, 0b10010000, 0b10000000, 0b10000000] + utf8(bytes[0..0]).valid_encoding?.should == false + end + end +end diff --git a/spec/ruby/core/struct/values_at_spec.rb b/spec/ruby/core/struct/values_at_spec.rb index e7d287cba2..5e5a496600 100644 --- a/spec/ruby/core/struct/values_at_spec.rb +++ b/spec/ruby/core/struct/values_at_spec.rb @@ -1,16 +1,59 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +# Should be synchronized with core/array/values_at_spec.rb describe "Struct#values_at" do - it "returns an array of values" do + before do clazz = Struct.new(:name, :director, :year) - movie = clazz.new('Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002) - movie.values_at(0, 1).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park'] - movie.values_at(0..2).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002] + @movie = clazz.new('Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002) + end + + context "when passed a list of Integers" do + it "returns an array containing each value given by one of integers" do + @movie.values_at(0, 1).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park'] + end + + it "raises IndexError if any of integers is out of range" do + -> { @movie.values_at(3) }.should raise_error(IndexError, "offset 3 too large for struct(size:3)") + -> { @movie.values_at(-4) }.should raise_error(IndexError, "offset -4 too small for struct(size:3)") + end + end + + context "when passed an integer Range" do + it "returns an array containing each value given by the elements of the range" do + @movie.values_at(0..2).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002] + end + + it "fills with nil values for range elements larger than the structure" do + @movie.values_at(0..3).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002, nil] + end + + it "raises RangeError if any element of the range is negative and out of range" do + -> { @movie.values_at(-4..3) }.should raise_error(RangeError, "-4..3 out of range") + end + + it "supports endless Range" do + @movie.values_at(0..).should == ["Sympathy for Mr. Vengeance", "Chan-wook Park", 2002] + end + + it "supports beginningless Range" do + @movie.values_at(..2).should == ["Sympathy for Mr. Vengeance", "Chan-wook Park", 2002] + end + end + + it "supports multiple integer Ranges" do + @movie.values_at(0..2, 1..2).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002, 'Chan-wook Park', 2002] + end + + it "supports mixing integer Ranges and Integers" do + @movie.values_at(0..2, 2).should == ['Sympathy for Mr. Vengeance', 'Chan-wook Park', 2002, 2002] + end + + it "returns a new empty Array if no arguments given" do + @movie.values_at().should == [] end it "fails when passed unsupported types" do - car = StructClasses::Car.new('Ford', 'Ranger') - -> { car.values_at('make') }.should raise_error(TypeError) + -> { @movie.values_at('make') }.should raise_error(TypeError, "no implicit conversion of String into Integer") end end diff --git a/spec/ruby/core/symbol/shared/id2name.rb b/spec/ruby/core/symbol/shared/id2name.rb index 47f97bd332..d012b7634e 100644 --- a/spec/ruby/core/symbol/shared/id2name.rb +++ b/spec/ruby/core/symbol/shared/id2name.rb @@ -6,4 +6,11 @@ describe :symbol_id2name, shared: true do :@ruby.send(@method).should == "@ruby" :@@ruby.send(@method).should == "@@ruby" end + + it "returns a String in the same encoding as self" do + string = "ruby".encode("US-ASCII") + symbol = string.to_sym + + symbol.send(@method).encoding.should == Encoding::US_ASCII + end end diff --git a/spec/ruby/core/time/at_spec.rb b/spec/ruby/core/time/at_spec.rb index 74b1962a95..0459589f01 100644 --- a/spec/ruby/core/time/at_spec.rb +++ b/spec/ruby/core/time/at_spec.rb @@ -251,6 +251,22 @@ describe "Time.at" do time.to_i.should == @epoch_time end + it "could be UTC offset as a 'UTC' String" do + time = Time.at(@epoch_time, in: "UTC") + + time.utc_offset.should == 0 + time.zone.should == "UTC" + time.to_i.should == @epoch_time + end + + it "could be UTC offset as a military zone A-Z" do + time = Time.at(@epoch_time, in: "B") + + time.utc_offset.should == 3600 * 2 + time.zone.should == nil + time.to_i.should == @epoch_time + end + it "could be a timezone object" do zone = TimeSpecs::TimezoneWithName.new(name: "Asia/Colombo") time = Time.at(@epoch_time, in: zone) diff --git a/spec/ruby/core/time/localtime_spec.rb b/spec/ruby/core/time/localtime_spec.rb index 2975e112d0..609b6532a1 100644 --- a/spec/ruby/core/time/localtime_spec.rb +++ b/spec/ruby/core/time/localtime_spec.rb @@ -29,10 +29,10 @@ describe "Time#localtime" do time.localtime.should equal(time) end - it "raises a RuntimeError if the time has a different time zone" do + it "raises a FrozenError if the time has a different time zone" do time = Time.gm(2007, 1, 9, 12, 0, 0) time.freeze - -> { time.localtime }.should raise_error(RuntimeError) + -> { time.localtime }.should raise_error(FrozenError) end end @@ -79,6 +79,18 @@ describe "Time#localtime" do t.utc_offset.should == -3600 end + it "returns a Time with a UTC offset specified as UTC" do + t = Time.new(2007, 1, 9, 12, 0, 0, 3600) + t.localtime("UTC") + t.utc_offset.should == 0 + end + + it "returns a Time with a UTC offset specified as A-Z military zone" do + t = Time.new(2007, 1, 9, 12, 0, 0, 3600) + t.localtime("B") + t.utc_offset.should == 3600 * 2 + end + platform_is_not :windows do it "changes the timezone according to the set one" do t = Time.new(2005, 2, 27, 22, 50, 0, -3600) diff --git a/spec/ruby/core/time/new_spec.rb b/spec/ruby/core/time/new_spec.rb index aabf28e712..727fdf92c2 100644 --- a/spec/ruby/core/time/new_spec.rb +++ b/spec/ruby/core/time/new_spec.rb @@ -58,6 +58,32 @@ describe "Time.new with a utc_offset argument" do Time.new(2000, 1, 1, 0, 0, 0, "-04:10:43").utc_offset.should == -15043 end + ruby_bug '#13669', '3.0'...'3.1' do + it "returns a Time with a UTC offset specified as +HH" do + Time.new(2000, 1, 1, 0, 0, 0, "+05").utc_offset.should == 3600 * 5 + end + + it "returns a Time with a UTC offset specified as -HH" do + Time.new(2000, 1, 1, 0, 0, 0, "-05").utc_offset.should == -3600 * 5 + end + + it "returns a Time with a UTC offset specified as +HHMM" do + Time.new(2000, 1, 1, 0, 0, 0, "+0530").utc_offset.should == 19800 + end + + it "returns a Time with a UTC offset specified as -HHMM" do + Time.new(2000, 1, 1, 0, 0, 0, "-0530").utc_offset.should == -19800 + end + + it "returns a Time with a UTC offset specified as +HHMMSS" do + Time.new(2000, 1, 1, 0, 0, 0, "+053037").utc_offset.should == 19837 + end + + it "returns a Time with a UTC offset specified as -HHMMSS" do + Time.new(2000, 1, 1, 0, 0, 0, "-053037").utc_offset.should == -19837 + end + end + describe "with an argument that responds to #to_str" do it "coerces using #to_str" do o = mock('string') @@ -66,6 +92,57 @@ describe "Time.new with a utc_offset argument" do end end + it "returns a Time with UTC offset specified as UTC" do + Time.new(2000, 1, 1, 0, 0, 0, "UTC").utc_offset.should == 0 + end + + it "returns a Time with UTC offset specified as a single letter military timezone" do + [ + ["A", 3600], + ["B", 3600 * 2], + ["C", 3600 * 3], + ["D", 3600 * 4], + ["E", 3600 * 5], + ["F", 3600 * 6], + ["G", 3600 * 7], + ["H", 3600 * 8], + ["I", 3600 * 9], + # J is not supported + ["K", 3600 * 10], + ["L", 3600 * 11], + ["M", 3600 * 12], + ["N", 3600 * -1], + ["O", 3600 * -2], + ["P", 3600 * -3], + ["Q", 3600 * -4], + ["R", 3600 * -5], + ["S", 3600 * -6], + ["T", 3600 * -7], + ["U", 3600 * -8], + ["V", 3600 * -9], + ["W", 3600 * -10], + ["X", 3600 * -11], + ["Y", 3600 * -12], + ["Z", 0] + ].each do |letter, offset| + Time.new(2000, 1, 1, 0, 0, 0, letter).utc_offset.should == offset + end + end + + ruby_version_is ""..."3.1" do + it "raises ArgumentError if the string argument is J" do + message = '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset' + -> { Time.new(2000, 1, 1, 0, 0, 0, "J") }.should raise_error(ArgumentError, message) + end + end + + ruby_version_is "3.1" do + it "raises ArgumentError if the string argument is J" do + message = '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: J' + -> { Time.new(2000, 1, 1, 0, 0, 0, "J") }.should raise_error(ArgumentError, message) + end + end + it "returns a local Time if the argument is nil" do with_timezone("PST", -8) do t = Time.new(2000, 1, 1, 0, 0, 0, nil) @@ -93,7 +170,12 @@ describe "Time.new with a utc_offset argument" do end it "raises ArgumentError if the String argument is not in an ASCII-compatible encoding" do - -> { Time.new(2000, 1, 1, 0, 0, 0, "-04:10".encode("UTF-16LE")) }.should raise_error(ArgumentError) + # Don't check exception message - it was changed in previous CRuby versions: + # - "string contains null byte" + # - '"+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset' + -> { + Time.new(2000, 1, 1, 0, 0, 0, "-04:10".encode("UTF-16LE")) + }.should raise_error(ArgumentError) end it "raises ArgumentError if the argument represents a value less than or equal to -86400 seconds" do @@ -106,19 +188,9 @@ describe "Time.new with a utc_offset argument" do -> { Time.new(2000, 1, 1, 0, 0, 0, 86400) }.should raise_error(ArgumentError) end - it "raises ArgumentError if the seconds argument is negative" do - -> { Time.new(2000, 1, 1, 0, 0, -1) }.should raise_error(ArgumentError) - end - it "raises ArgumentError if the utc_offset argument is greater than or equal to 10e9" do -> { Time.new(2000, 1, 1, 0, 0, 0, 1000000000) }.should raise_error(ArgumentError) end - - it "raises ArgumentError if the month is greater than 12" do - # For some reason MRI uses a different message for month in 13-15 and month>=16 - -> { Time.new(2000, 13, 1, 0, 0, 0, "+01:00") }.should raise_error(ArgumentError, /(mon|argument) out of range/) - -> { Time.new(2000, 16, 1, 0, 0, 0, "+01:00") }.should raise_error(ArgumentError, "argument out of range") - end end describe "Time.new with a timezone argument" do diff --git a/spec/ruby/core/time/shared/gmtime.rb b/spec/ruby/core/time/shared/gmtime.rb index 5ed64c2ab6..bae19da462 100644 --- a/spec/ruby/core/time/shared/gmtime.rb +++ b/spec/ruby/core/time/shared/gmtime.rb @@ -22,11 +22,11 @@ describe :time_gmtime, shared: true do time.send(@method).should equal(time) end - it "raises a RuntimeError if the time is not UTC" do + it "raises a FrozenError if the time is not UTC" do with_timezone("CST", -6) do time = Time.now time.freeze - -> { time.send(@method) }.should raise_error(RuntimeError) + -> { time.send(@method) }.should raise_error(FrozenError) end end end diff --git a/spec/ruby/core/time/shared/time_params.rb b/spec/ruby/core/time/shared/time_params.rb index 63d0dbc120..b6a6c88c8e 100644 --- a/spec/ruby/core/time/shared/time_params.rb +++ b/spec/ruby/core/time/shared/time_params.rb @@ -145,9 +145,10 @@ describe :time_params, shared: true do end it "raises an ArgumentError for out of range month" do + # For some reason MRI uses a different message for month in 13-15 and month>=16 -> { - Time.send(@method, 2008, 13, 31, 23, 59, 59) - }.should raise_error(ArgumentError) + Time.send(@method, 2008, 16, 31, 23, 59, 59) + }.should raise_error(ArgumentError, /(mon|argument) out of range/) end it "raises an ArgumentError for out of range day" do @@ -169,9 +170,13 @@ describe :time_params, shared: true do end it "raises an ArgumentError for out of range second" do + # For some reason MRI uses different messages for seconds 61-63 and seconds >= 64 -> { Time.send(@method, 2008, 12, 31, 23, 59, 61) - }.should raise_error(ArgumentError) + }.should raise_error(ArgumentError, /(sec|argument) out of range/) + -> { + Time.send(@method, 2008, 12, 31, 23, 59, -1) + }.should raise_error(ArgumentError, "argument out of range") end it "raises ArgumentError when given 9 arguments" do diff --git a/spec/ruby/core/time/strftime_spec.rb b/spec/ruby/core/time/strftime_spec.rb index c133e22008..4cb300c916 100644 --- a/spec/ruby/core/time/strftime_spec.rb +++ b/spec/ruby/core/time/strftime_spec.rb @@ -52,10 +52,42 @@ describe "Time#strftime" do ruby_version_is "3.1" do it "supports RFC 3339 UTC for unknown offset local time, -0000, as %-z" do - @time.strftime("%z").should == "+0000" - @time.strftime("%-z").should == "-0000" - @time.strftime("%-:z").should == "-00:00" - @time.strftime("%-::z").should == "-00:00:00" + time = Time.gm(2022) + + time.strftime("%z").should == "+0000" + time.strftime("%-z").should == "-0000" + time.strftime("%-:z").should == "-00:00" + time.strftime("%-::z").should == "-00:00:00" + end + + it "applies '-' flag to UTC time" do + time = Time.utc(2022) + time.strftime("%-z").should == "-0000" + + time = Time.gm(2022) + time.strftime("%-z").should == "-0000" + + time = Time.new(2022, 1, 1, 0, 0, 0, "Z") + time.strftime("%-z").should == "-0000" + + time = Time.new(2022, 1, 1, 0, 0, 0, "-00:00") + time.strftime("%-z").should == "-0000" + + time = Time.new(2022, 1, 1, 0, 0, 0, "+03:00").utc + time.strftime("%-z").should == "-0000" + end + + it "ignores '-' flag for non-UTC time" do + time = Time.new(2022, 1, 1, 0, 0, 0, "+03:00") + time.strftime("%-z").should == "+0300" + end + + it "works correctly with width, _ and 0 flags, and :" do + Time.now.utc.strftime("%-_10z").should == " -000" + Time.now.utc.strftime("%-10z").should == "-000000000" + Time.now.utc.strftime("%-010:z").should == "-000000:00" + Time.now.utc.strftime("%-_10:z").should == " -0:00" + Time.now.utc.strftime("%-_10::z").should == " -0:00:00" end end end diff --git a/spec/ruby/core/time/utc_spec.rb b/spec/ruby/core/time/utc_spec.rb index 74c17a93d1..809accc809 100644 --- a/spec/ruby/core/time/utc_spec.rb +++ b/spec/ruby/core/time/utc_spec.rb @@ -4,8 +4,45 @@ require_relative 'shared/gmtime' require_relative 'shared/time_params' describe "Time#utc?" do - it "returns true if time represents a time in UTC (GMT)" do - Time.now.should_not.utc? + it "returns true only if time represents a time in UTC (GMT)" do + Time.now.utc?.should == false + Time.now.utc.utc?.should == true + end + + it "treats time as UTC what was created in different ways" do + Time.now.utc.utc?.should == true + Time.now.gmtime.utc?.should == true + Time.now.getgm.utc?.should == true + Time.now.getutc.utc?.should == true + Time.utc(2022).utc?.should == true + end + + it "does treat time with 'UTC' offset as UTC" do + Time.new(2022, 1, 1, 0, 0, 0, "UTC").utc?.should == true + Time.now.localtime("UTC").utc?.should == true + Time.at(Time.now, in: 'UTC').utc?.should == true + end + + it "does treat time with Z offset as UTC" do + Time.new(2022, 1, 1, 0, 0, 0, "Z").utc?.should == true + Time.now.localtime("Z").utc?.should == true + Time.at(Time.now, in: 'Z').utc?.should == true + end + + ruby_version_is "3.1" do + it "does treat time with -00:00 offset as UTC" do + Time.new(2022, 1, 1, 0, 0, 0, "-00:00").utc?.should == true + Time.now.localtime("-00:00").utc?.should == true + Time.at(Time.now, in: '-00:00').utc?.should == true + end + end + + it "does not treat time with +00:00 offset as UTC" do + Time.new(2022, 1, 1, 0, 0, 0, "+00:00").utc?.should == false + end + + it "does not treat time with 0 offset as UTC" do + Time.new(2022, 1, 1, 0, 0, 0, 0).utc?.should == false end end diff --git a/spec/ruby/core/time/zone_spec.rb b/spec/ruby/core/time/zone_spec.rb index 907ccf9f4b..cbb0977f24 100644 --- a/spec/ruby/core/time/zone_spec.rb +++ b/spec/ruby/core/time/zone_spec.rb @@ -52,14 +52,28 @@ describe "Time#zone" do end it "doesn't raise errors for a Time with a fixed offset" do - -> { - Time.new(2001, 1, 1, 0, 0, 0, "+05:00").zone - }.should_not raise_error + Time.new(2001, 1, 1, 0, 0, 0, "+05:00").zone.should == nil end end it "returns UTC when called on a UTC time" do Time.now.utc.zone.should == "UTC" + Time.now.gmtime.zone.should == "UTC" + Time.now.getgm.zone.should == "UTC" + Time.now.getutc.zone.should == "UTC" + Time.utc(2022).zone.should == "UTC" + Time.new(2022, 1, 1, 0, 0, 0, "UTC").zone.should == "UTC" + Time.new(2022, 1, 1, 0, 0, 0, "Z").zone.should == "UTC" + Time.now.localtime("UTC").zone.should == "UTC" + Time.now.localtime("Z").zone.should == "UTC" + Time.at(Time.now, in: 'UTC').zone.should == "UTC" + Time.at(Time.now, in: 'Z').zone.should == "UTC" + + ruby_version_is "3.1" do + Time.new(2022, 1, 1, 0, 0, 0, "-00:00").zone.should == "UTC" + Time.now.localtime("-00:00").zone.should == "UTC" + Time.at(Time.now, in: '-00:00').zone.should == "UTC" + end end platform_is_not :aix, :windows do diff --git a/spec/ruby/core/tracepoint/inspect_spec.rb b/spec/ruby/core/tracepoint/inspect_spec.rb index a07b626212..151a08e7b4 100644 --- a/spec/ruby/core/tracepoint/inspect_spec.rb +++ b/spec/ruby/core/tracepoint/inspect_spec.rb @@ -16,6 +16,15 @@ describe 'TracePoint#inspect' do TracePoint.new(:line) {}.inspect.should == '#<TracePoint:disabled>' end + it "shows only whether it's enabled when outside the TracePoint handler" do + trace = TracePoint.new(:line) {} + trace.enable + + trace.inspect.should == '#<TracePoint:enabled>' + + trace.disable + end + it 'returns a String showing the event, path and line' do inspect = nil line = nil diff --git a/spec/ruby/fixtures/code/wrap_fixture.rb b/spec/ruby/fixtures/code/load_wrap_fixture.rb index 7ddf5a62e0..526bbf8c82 100644 --- a/spec/ruby/fixtures/code/wrap_fixture.rb +++ b/spec/ruby/fixtures/code/load_wrap_fixture.rb @@ -2,7 +2,10 @@ class LoadSpecWrap ScratchPad << String end +LOAD_WRAP_SPECS_TOP_LEVEL_CONSTANT = 1 + def load_wrap_specs_top_level_method + :load_wrap_specs_top_level_method end ScratchPad << method(:load_wrap_specs_top_level_method).owner diff --git a/spec/ruby/language/case_spec.rb b/spec/ruby/language/case_spec.rb index bf06803764..915c032a71 100644 --- a/spec/ruby/language/case_spec.rb +++ b/spec/ruby/language/case_spec.rb @@ -103,7 +103,7 @@ describe "The 'case'-construct" do $1.should == "42" end - it "tests with a regexp interpolated within another regexp" do + it "tests with a string interpolated in a regexp" do digits = '\d+' case "foo44" when /oo(#{digits})/ @@ -116,7 +116,7 @@ describe "The 'case'-construct" do $1.should == "44" end - it "tests with a string interpolated in a regexp" do + it "tests with a regexp interpolated within another regexp" do digits_regexp = /\d+/ case "foo43" when /oo(#{digits_regexp})/ diff --git a/spec/ruby/language/regexp/character_classes_spec.rb b/spec/ruby/language/regexp/character_classes_spec.rb index 0cf1e9b6f4..12a51178b2 100644 --- a/spec/ruby/language/regexp/character_classes_spec.rb +++ b/spec/ruby/language/regexp/character_classes_spec.rb @@ -609,6 +609,11 @@ describe "Regexp with character classes" do "루비(Ruby)".match(/\p{Hangul}+/u).to_a.should == ["루비"] end + it "supports negated property condition" do + "a".match(/\P{L}/).should be_nil + "1".match(/\P{N}/).should be_nil + end + ruby_bug "#17340", ''...'3.0' do it "raises a RegexpError for an unterminated unicode property" do -> { Regexp.new('\p{') }.should raise_error(RegexpError) diff --git a/spec/ruby/library/coverage/running_spec.rb b/spec/ruby/library/coverage/running_spec.rb new file mode 100644 index 0000000000..745270164f --- /dev/null +++ b/spec/ruby/library/coverage/running_spec.rb @@ -0,0 +1,20 @@ +require_relative '../../spec_helper' +require 'coverage' + +describe 'Coverage.running?' do + it "returns false if coverage is not started" do + Coverage.running?.should == false + end + + it "returns true if coverage is started" do + Coverage.start + Coverage.running?.should == true + Coverage.result + end + + it "returns false if coverage was started and stopped" do + Coverage.start + Coverage.result + Coverage.running?.should == false + end +end
\ No newline at end of file diff --git a/spec/ruby/library/date/civil_spec.rb b/spec/ruby/library/date/civil_spec.rb index f3537c2f84..1c780fce56 100644 --- a/spec/ruby/library/date/civil_spec.rb +++ b/spec/ruby/library/date/civil_spec.rb @@ -2,11 +2,6 @@ require_relative '../../spec_helper' require_relative 'shared/civil' require 'date' -describe "Date#civil" do - it_behaves_like :date_civil, :civil -end - - describe "Date.civil" do - it "needs to be reviewed for spec completeness" + it_behaves_like :date_civil, :civil end diff --git a/spec/ruby/library/objectspace/fixtures/trace.rb b/spec/ruby/library/objectspace/fixtures/trace.rb new file mode 100644 index 0000000000..fd4524b0ba --- /dev/null +++ b/spec/ruby/library/objectspace/fixtures/trace.rb @@ -0,0 +1,5 @@ +require "objspace/trace" +a = "foo" +b = "b" + "a" + "r" +c = 42 +p a, b, c diff --git a/spec/ruby/library/objectspace/trace_spec.rb b/spec/ruby/library/objectspace/trace_spec.rb new file mode 100644 index 0000000000..59952a006c --- /dev/null +++ b/spec/ruby/library/objectspace/trace_spec.rb @@ -0,0 +1,15 @@ +require_relative '../../spec_helper' + +ruby_version_is "3.1" do + describe 'require "objspace/trace"' do + it "shows object allocation sites" do + file = fixture(__FILE__ , "trace.rb") + ruby_exe(file, args: "2>&1").lines(chomp: true).should == [ + "objspace/trace is enabled", + "\"foo\" @ #{file}:2", + "\"bar\" @ #{file}:3", + "42" + ] + end + end +end diff --git a/spec/ruby/library/openssl/x509/name/verify_spec.rb b/spec/ruby/library/openssl/x509/name/verify_spec.rb index a8bf865bda..6dcfc99466 100644 --- a/spec/ruby/library/openssl/x509/name/verify_spec.rb +++ b/spec/ruby/library/openssl/x509/name/verify_spec.rb @@ -12,7 +12,7 @@ describe "OpenSSL::X509::Name.verify" do cert.public_key = key.public_key cert.not_before = Time.now - 10 cert.not_after = cert.not_before + 365 * 24 * 60 * 60 - cert.sign key, OpenSSL::Digest.new('SHA1') + cert.sign key, OpenSSL::Digest.new('SHA256') store = OpenSSL::X509::Store.new store.add_cert(cert) [store.verify(cert), store.error, store.error_string].should == [true, 0, "ok"] @@ -28,7 +28,7 @@ describe "OpenSSL::X509::Name.verify" do cert.public_key = key.public_key cert.not_before = Time.now - 10 cert.not_after = Time.now - 5 - cert.sign key, OpenSSL::Digest.new('SHA1') + cert.sign key, OpenSSL::Digest.new('SHA256') store = OpenSSL::X509::Store.new store.add_cert(cert) store.verify(cert).should == false diff --git a/spec/ruby/library/stringio/initialize_spec.rb b/spec/ruby/library/stringio/initialize_spec.rb index 1e8096e3bb..c597e328d3 100644 --- a/spec/ruby/library/stringio/initialize_spec.rb +++ b/spec/ruby/library/stringio/initialize_spec.rb @@ -163,6 +163,91 @@ describe "StringIO#initialize when passed [Object]" do end end +# NOTE: Synchronise with core/io/new_spec.rb (core/io/shared/new.rb) +describe "StringIO#initialize when passed keyword arguments" do + it "sets the mode based on the passed :mode option" do + io = StringIO.new("example", "r") + io.closed_read?.should be_false + io.closed_write?.should be_true + end + + it "accepts a mode argument set to nil with a valid :mode option" do + @io = StringIO.new('', nil, mode: "w") + @io.write("foo").should == 3 + end + + it "accepts a mode argument with a :mode option set to nil" do + @io = StringIO.new('', "w", mode: nil) + @io.write("foo").should == 3 + end + + it "sets binmode from :binmode option" do + @io = StringIO.new('', 'w', binmode: true) + @io.external_encoding.to_s.should == "ASCII-8BIT" # #binmode? isn't implemented in StringIO + end + + it "does not set binmode from false :binmode" do + @io = StringIO.new('', 'w', binmode: false) + @io.external_encoding.to_s.should == "UTF-8" # #binmode? isn't implemented in StringIO + end +end + +# NOTE: Synchronise with core/io/new_spec.rb (core/io/shared/new.rb) +describe "StringIO#initialize when passed keyword arguments and error happens" do + it "raises an error if passed encodings two ways" do + -> { + @io = StringIO.new('', 'w:ISO-8859-1', encoding: 'ISO-8859-1') + }.should raise_error(ArgumentError) + -> { + @io = StringIO.new('', 'w:ISO-8859-1', external_encoding: 'ISO-8859-1') + }.should raise_error(ArgumentError) + -> { + @io = StringIO.new('', 'w:ISO-8859-1:UTF-8', internal_encoding: 'ISO-8859-1') + }.should raise_error(ArgumentError) + end + + it "raises an error if passed matching binary/text mode two ways" do + -> { + @io = StringIO.new('', "wb", binmode: true) + }.should raise_error(ArgumentError) + -> { + @io = StringIO.new('', "wt", textmode: true) + }.should raise_error(ArgumentError) + + -> { + @io = StringIO.new('', "wb", textmode: false) + }.should raise_error(ArgumentError) + -> { + @io = StringIO.new('', "wt", binmode: false) + }.should raise_error(ArgumentError) + end + + it "raises an error if passed conflicting binary/text mode two ways" do + -> { + @io = StringIO.new('', "wb", binmode: false) + }.should raise_error(ArgumentError) + -> { + @io = StringIO.new('', "wt", textmode: false) + }.should raise_error(ArgumentError) + + -> { + @io = StringIO.new('', "wb", textmode: true) + }.should raise_error(ArgumentError) + -> { + @io = StringIO.new('', "wt", binmode: true) + }.should raise_error(ArgumentError) + end + + it "raises an error when trying to set both binmode and textmode" do + -> { + @io = StringIO.new('', "w", textmode: true, binmode: true) + }.should raise_error(ArgumentError) + -> { + @io = StringIO.new('', File::Constants::WRONLY, textmode: true, binmode: true) + }.should raise_error(ArgumentError) + end +end + describe "StringIO#initialize when passed no arguments" do before :each do @io = StringIO.allocate diff --git a/spec/ruby/library/stringio/new_spec.rb b/spec/ruby/library/stringio/new_spec.rb new file mode 100644 index 0000000000..e36d210caa --- /dev/null +++ b/spec/ruby/library/stringio/new_spec.rb @@ -0,0 +1,8 @@ +require_relative '../../spec_helper' +require 'stringio' + +describe "StringIO.new" do + it "warns when called with a block" do + -> { eval("StringIO.new {}") }.should complain(/StringIO::new\(\) does not take block; use StringIO::open\(\) instead/) + end +end
\ No newline at end of file diff --git a/spec/ruby/library/stringio/shared/write.rb b/spec/ruby/library/stringio/shared/write.rb index c5a0f8f513..d9c21028e0 100644 --- a/spec/ruby/library/stringio/shared/write.rb +++ b/spec/ruby/library/stringio/shared/write.rb @@ -66,6 +66,28 @@ describe :stringio_write_string, shared: true do @io.tainted?.should be_false end end + + it "handles writing non-ASCII UTF-8 after seek" do + @io.binmode + @io << "\x80" + @io.pos = 0 + @io << "\x81" + @io.string.should == "\x812345".b + end + + it "handles writing with position < buffer size" do + @io.pos = 2 + @io.write "abc" + @io.string.should == "12abc" + + @io.pos = 2 + @io.write "de" + @io.string.should == "12dec" + + @io.pos = 2 + @io.write "fghi" + @io.string.should == "12fghi" + end end describe :stringio_write_not_writable, shared: true do diff --git a/spec/ruby/optional/capi/ext/io_spec.c b/spec/ruby/optional/capi/ext/io_spec.c index b4ffe9207a..f257cef554 100644 --- a/spec/ruby/optional/capi/ext/io_spec.c +++ b/spec/ruby/optional/capi/ext/io_spec.c @@ -182,6 +182,46 @@ VALUE io_spec_rb_thread_fd_writable(VALUE self, VALUE io) { return Qnil; } +VALUE io_spec_rb_thread_fd_select_read(VALUE self, VALUE io) { + int fd = io_spec_get_fd(io); + + rb_fdset_t fds; + rb_fd_init(&fds); + rb_fd_set(fd, &fds); + + int r = rb_thread_fd_select(fd + 1, &fds, NULL, NULL, NULL); + rb_fd_term(&fds); + return INT2FIX(r); +} + +VALUE io_spec_rb_thread_fd_select_write(VALUE self, VALUE io) { + int fd = io_spec_get_fd(io); + + rb_fdset_t fds; + rb_fd_init(&fds); + rb_fd_set(fd, &fds); + + int r = rb_thread_fd_select(fd + 1, NULL, &fds, NULL, NULL); + rb_fd_term(&fds); + return INT2FIX(r); +} + +VALUE io_spec_rb_thread_fd_select_timeout(VALUE self, VALUE io) { + int fd = io_spec_get_fd(io); + + struct timeval timeout; + timeout.tv_sec = 10; + timeout.tv_usec = 20; + + rb_fdset_t fds; + rb_fd_init(&fds); + rb_fd_set(fd, &fds); + + int r = rb_thread_fd_select(fd + 1, NULL, &fds, NULL, &timeout); + rb_fd_term(&fds); + return INT2FIX(r); +} + VALUE io_spec_rb_io_binmode(VALUE self, VALUE io) { return rb_io_binmode(io); } @@ -256,6 +296,9 @@ void Init_io_spec(void) { rb_define_method(cls, "rb_io_wait_writable", io_spec_rb_io_wait_writable, 1); rb_define_method(cls, "rb_thread_wait_fd", io_spec_rb_thread_wait_fd, 1); rb_define_method(cls, "rb_thread_fd_writable", io_spec_rb_thread_fd_writable, 1); + rb_define_method(cls, "rb_thread_fd_select_read", io_spec_rb_thread_fd_select_read, 1); + rb_define_method(cls, "rb_thread_fd_select_write", io_spec_rb_thread_fd_select_write, 1); + rb_define_method(cls, "rb_thread_fd_select_timeout", io_spec_rb_thread_fd_select_timeout, 1); rb_define_method(cls, "rb_wait_for_single_fd", io_spec_rb_wait_for_single_fd, 4); rb_define_method(cls, "rb_io_binmode", io_spec_rb_io_binmode, 1); rb_define_method(cls, "rb_fd_fix_cloexec", io_spec_rb_fd_fix_cloexec, 1); diff --git a/spec/ruby/optional/capi/io_spec.rb b/spec/ruby/optional/capi/io_spec.rb index 489a01c515..95717351a6 100644 --- a/spec/ruby/optional/capi/io_spec.rb +++ b/spec/ruby/optional/capi/io_spec.rb @@ -262,6 +262,21 @@ describe "C-API IO function" do end end + describe "rb_thread_fd_select" do + it "waits until an fd is ready for reading" do + @w_io.write "rb_thread_fd_select" + @o.rb_thread_fd_select_read(@r_io).should == 1 + end + + it "waits until an fd is ready for writing" do + @o.rb_thread_fd_select_write(@w_io).should == 1 + end + + it "waits until an fd is ready for writing with timeout" do + @o.rb_thread_fd_select_timeout(@w_io).should == 1 + end + end + platform_is_not :windows do describe "rb_io_wait_readable" do it "returns false if there is no error condition" do diff --git a/spec/ruby/shared/rational/Rational.rb b/spec/ruby/shared/rational/Rational.rb index 936a90c086..2dc49c869c 100644 --- a/spec/ruby/shared/rational/Rational.rb +++ b/spec/ruby/shared/rational/Rational.rb @@ -65,40 +65,40 @@ describe :kernel_Rational, shared: true do r_s.should == r r_s.should_not == f_r end + end - describe "when passed a Numeric" do - it "calls #to_r to convert the first argument to a Rational" do - num = RationalSpecs::SubNumeric.new(2) + describe "when passed a Numeric" do + it "calls #to_r to convert the first argument to a Rational" do + num = RationalSpecs::SubNumeric.new(2) - Rational(num).should == Rational(2) - end + Rational(num).should == Rational(2) end + end - describe "when passed a Complex" do - it "returns a Rational from the real part if the imaginary part is 0" do - Rational(Complex(1, 0)).should == Rational(1) - end - - it "raises a RangeError if the imaginary part is not 0" do - -> { Rational(Complex(1, 2)) }.should raise_error(RangeError) - end + describe "when passed a Complex" do + it "returns a Rational from the real part if the imaginary part is 0" do + Rational(Complex(1, 0)).should == Rational(1) end - it "raises a TypeError if the first argument is nil" do - -> { Rational(nil) }.should raise_error(TypeError) + it "raises a RangeError if the imaginary part is not 0" do + -> { Rational(Complex(1, 2)) }.should raise_error(RangeError) end + end - it "raises a TypeError if the second argument is nil" do - -> { Rational(1, nil) }.should raise_error(TypeError) - end + it "raises a TypeError if the first argument is nil" do + -> { Rational(nil) }.should raise_error(TypeError) + end - it "raises a TypeError if the first argument is a Symbol" do - -> { Rational(:sym) }.should raise_error(TypeError) - end + it "raises a TypeError if the second argument is nil" do + -> { Rational(1, nil) }.should raise_error(TypeError) + end - it "raises a TypeError if the second argument is a Symbol" do - -> { Rational(1, :sym) }.should raise_error(TypeError) - end + it "raises a TypeError if the first argument is a Symbol" do + -> { Rational(:sym) }.should raise_error(TypeError) + end + + it "raises a TypeError if the second argument is a Symbol" do + -> { Rational(1, :sym) }.should raise_error(TypeError) end describe "when passed exception: false" do |