diff --git a/spec/ruby/.rubocop_todo.yml b/spec/ruby/.rubocop_todo.yml index a59e64bd58b6e0..3ebb23a8bb7f55 100644 --- a/spec/ruby/.rubocop_todo.yml +++ b/spec/ruby/.rubocop_todo.yml @@ -63,7 +63,6 @@ Lint/LiteralInInterpolation: - 'language/string_spec.rb' - 'language/symbol_spec.rb' - 'language/undef_spec.rb' - - 'library/net/ftp/connect_spec.rb' # Offense count: 8 # Cop supports --auto-correct. diff --git a/spec/ruby/core/encoding/replicate_spec.rb b/spec/ruby/core/encoding/replicate_spec.rb index 498d03581ac6e5..68c285158d386c 100644 --- a/spec/ruby/core/encoding/replicate_spec.rb +++ b/spec/ruby/core/encoding/replicate_spec.rb @@ -73,6 +73,11 @@ Encoding::US_ASCII.replicate('MY-US-ASCII') }.should complain(/warning: Encoding#replicate is deprecated and will be removed in Ruby 3.3; use the original encoding instead/) end + + it "raises EncodingError if too many encodings" do + code = '1_000.times {|i| Encoding::US_ASCII.replicate("R_#{i}") }' + ruby_exe(code, args: "2>&1", exit_status: 1).should.include?('too many encoding (> 256) (EncodingError)') + end end ruby_version_is "3.3" do diff --git a/spec/ruby/core/enumerable/fixtures/classes.rb b/spec/ruby/core/enumerable/fixtures/classes.rb index fb4951c6e626a0..2701c6999c437a 100644 --- a/spec/ruby/core/enumerable/fixtures/classes.rb +++ b/spec/ruby/core/enumerable/fixtures/classes.rb @@ -342,4 +342,10 @@ def ===(*args) @block.call(*args) end end + + # Set is a core class since Ruby 3.2 + ruby_version_is '3.2' do + class SetSubclass < Set + end + end end # EnumerableSpecs utility classes diff --git a/spec/ruby/core/enumerable/to_set_spec.rb b/spec/ruby/core/enumerable/to_set_spec.rb new file mode 100644 index 00000000000000..c21a2772c4785d --- /dev/null +++ b/spec/ruby/core/enumerable/to_set_spec.rb @@ -0,0 +1,29 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +ruby_version_is "3.2" do + describe "Enumerable#to_set" do + it "returns a new Set created from self" do + [1, 2, 3].to_set.should == Set[1, 2, 3] + {a: 1, b: 2}.to_set.should == Set[[:b, 2], [:a, 1]] + end + + it "passes down passed blocks" do + [1, 2, 3].to_set { |x| x * x }.should == Set[1, 4, 9] + end + + it "instantiates an object of provided as the first argument set class" do + set = [1, 2, 3].to_set(EnumerableSpecs::SetSubclass) + set.should be_kind_of(EnumerableSpecs::SetSubclass) + set.to_a.sort.should == [1, 2, 3] + end + + it "does not need explicit `require 'set'`" do + output = ruby_exe(<<~RUBY, options: '--disable-gems', args: '2>&1') + puts [1, 2, 3].to_set + RUBY + + output.chomp.should == "#" + end + end +end diff --git a/spec/ruby/core/file/lutime_spec.rb b/spec/ruby/core/file/lutime_spec.rb index 1f0625f61ebc2f..0f6df42ea37df5 100644 --- a/spec/ruby/core/file/lutime_spec.rb +++ b/spec/ruby/core/file/lutime_spec.rb @@ -1,7 +1,12 @@ require_relative '../../spec_helper' +require_relative 'shared/update_time' -describe "File.lutime" do - platform_is_not :windows do +platform_is_not :windows do + describe "File.lutime" do + it_behaves_like :update_time, :lutime + end + + describe "File.lutime" do before :each do @atime = Time.utc(2000) @mtime = Time.utc(2001) diff --git a/spec/ruby/core/file/shared/update_time.rb b/spec/ruby/core/file/shared/update_time.rb new file mode 100644 index 00000000000000..9c063a8e935484 --- /dev/null +++ b/spec/ruby/core/file/shared/update_time.rb @@ -0,0 +1,105 @@ +describe :update_time, shared: true do + before :all do + @time_is_float = platform_is :windows + end + + before :each do + @atime = Time.now + @mtime = Time.now + @file1 = tmp("specs_file_utime1") + @file2 = tmp("specs_file_utime2") + touch @file1 + touch @file2 + end + + after :each do + rm_r @file1, @file2 + end + + it "sets the access and modification time of each file" do + File.send(@method, @atime, @mtime, @file1, @file2) + + if @time_is_float + File.atime(@file1).should be_close(@atime, 0.0001) + File.mtime(@file1).should be_close(@mtime, 0.0001) + File.atime(@file2).should be_close(@atime, 0.0001) + File.mtime(@file2).should be_close(@mtime, 0.0001) + else + File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) + File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) + File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) + File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) + end + end + + it "uses the current times if two nil values are passed" do + tn = Time.now + File.send(@method, nil, nil, @file1, @file2) + + if @time_is_float + File.atime(@file1).should be_close(tn, 0.050) + File.mtime(@file1).should be_close(tn, 0.050) + File.atime(@file2).should be_close(tn, 0.050) + File.mtime(@file2).should be_close(tn, 0.050) + else + File.atime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) + File.mtime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) + File.atime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) + File.mtime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) + end + end + + it "accepts an object that has a #to_path method" do + File.send(@method, @atime, @mtime, mock_to_path(@file1), mock_to_path(@file2)) + end + + it "accepts numeric atime and mtime arguments" do + if @time_is_float + File.send(@method, @atime.to_f, @mtime.to_f, @file1, @file2) + + File.atime(@file1).should be_close(@atime, 0.0001) + File.mtime(@file1).should be_close(@mtime, 0.0001) + File.atime(@file2).should be_close(@atime, 0.0001) + File.mtime(@file2).should be_close(@mtime, 0.0001) + else + File.send(@method, @atime.to_i, @mtime.to_i, @file1, @file2) + + File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) + File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) + File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) + File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) + end + end + + it "may set nanosecond precision" do + t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r) + File.send(@method, t, t, @file1) + + File.atime(@file1).nsec.should.between?(0, 123500000) + File.mtime(@file1).nsec.should.between?(0, 123500000) + end + + it "returns the number of filenames in the arguments" do + File.send(@method, @atime.to_f, @mtime.to_f, @file1, @file2).should == 2 + end + + platform_is :linux do + platform_is wordsize: 64 do + it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19 or 2486-07-02)" do + # https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps + # "Therefore, timestamps should not overflow until May 2446." + # https://lwn.net/Articles/804382/ + # "On-disk timestamps hitting the y2038 limit..." + # The problem seems to be being improved, but currently it actually fails on XFS on RHEL8 + # https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20201112T123004Z.fail.html.gz + # Amazon Linux 2023 returns 2486-07-02 in this example + # http://rubyci.s3.amazonaws.com/amazon2023/ruby-master/log/20230322T063004Z.fail.html.gz + time = Time.at(1<<44) + File.send(@method, time, time, @file1) + + [559444, 2486, 2446, 2038].should.include? File.atime(@file1).year + [559444, 2486, 2446, 2038].should.include? File.mtime(@file1).year + end + end + end +end diff --git a/spec/ruby/core/file/utime_spec.rb b/spec/ruby/core/file/utime_spec.rb index 0b0e4f979c9356..d87626be509458 100644 --- a/spec/ruby/core/file/utime_spec.rb +++ b/spec/ruby/core/file/utime_spec.rb @@ -1,102 +1,6 @@ require_relative '../../spec_helper' +require_relative 'shared/update_time' describe "File.utime" do - - before :all do - @time_is_float = platform_is :windows - end - - before :each do - @atime = Time.now - @mtime = Time.now - @file1 = tmp("specs_file_utime1") - @file2 = tmp("specs_file_utime2") - touch @file1 - touch @file2 - end - - after :each do - rm_r @file1, @file2 - end - - it "sets the access and modification time of each file" do - File.utime(@atime, @mtime, @file1, @file2) - if @time_is_float - File.atime(@file1).should be_close(@atime, 0.0001) - File.mtime(@file1).should be_close(@mtime, 0.0001) - File.atime(@file2).should be_close(@atime, 0.0001) - File.mtime(@file2).should be_close(@mtime, 0.0001) - else - File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) - File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) - File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) - File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) - end - end - - it "uses the current times if two nil values are passed" do - tn = Time.now - File.utime(nil, nil, @file1, @file2) - if @time_is_float - File.atime(@file1).should be_close(tn, 0.050) - File.mtime(@file1).should be_close(tn, 0.050) - File.atime(@file2).should be_close(tn, 0.050) - File.mtime(@file2).should be_close(tn, 0.050) - else - File.atime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) - File.mtime(@file1).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) - File.atime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) - File.mtime(@file2).to_i.should be_close(Time.now.to_i, TIME_TOLERANCE) - end - end - - it "accepts an object that has a #to_path method" do - File.utime(@atime, @mtime, mock_to_path(@file1), mock_to_path(@file2)) - end - - it "accepts numeric atime and mtime arguments" do - if @time_is_float - File.utime(@atime.to_f, @mtime.to_f, @file1, @file2) - File.atime(@file1).should be_close(@atime, 0.0001) - File.mtime(@file1).should be_close(@mtime, 0.0001) - File.atime(@file2).should be_close(@atime, 0.0001) - File.mtime(@file2).should be_close(@mtime, 0.0001) - else - File.utime(@atime.to_i, @mtime.to_i, @file1, @file2) - File.atime(@file1).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) - File.mtime(@file1).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) - File.atime(@file2).to_i.should be_close(@atime.to_i, TIME_TOLERANCE) - File.mtime(@file2).to_i.should be_close(@mtime.to_i, TIME_TOLERANCE) - end - end - - it "may set nanosecond precision" do - t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r) - File.utime(t, t, @file1) - File.atime(@file1).nsec.should.between?(0, 123500000) - File.mtime(@file1).nsec.should.between?(0, 123500000) - end - - it "returns the number of filenames in the arguments" do - File.utime(@atime.to_f, @mtime.to_f, @file1, @file2).should == 2 - end - - platform_is :linux do - platform_is wordsize: 64 do - it "allows Time instances in the far future to set mtime and atime (but some filesystems limit it up to 2446-05-10 or 2038-01-19 or 2486-07-02)" do - # https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps - # "Therefore, timestamps should not overflow until May 2446." - # https://lwn.net/Articles/804382/ - # "On-disk timestamps hitting the y2038 limit..." - # The problem seems to be being improved, but currently it actually fails on XFS on RHEL8 - # https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20201112T123004Z.fail.html.gz - # Amazon Linux 2023 returns 2486-07-02 in this example - # http://rubyci.s3.amazonaws.com/amazon2023/ruby-master/log/20230322T063004Z.fail.html.gz - time = Time.at(1<<44) - File.utime(time, time, @file1) - [559444, 2486, 2446, 2038].should.include? File.atime(@file1).year - [559444, 2486, 2446, 2038].should.include? File.mtime(@file1).year - end - end - end + it_behaves_like :update_time, :utime end diff --git a/spec/ruby/core/filetest/exist_spec.rb b/spec/ruby/core/filetest/exist_spec.rb index 4d14bea2311738..a95d3f91a18d96 100644 --- a/spec/ruby/core/filetest/exist_spec.rb +++ b/spec/ruby/core/filetest/exist_spec.rb @@ -4,3 +4,11 @@ describe "FileTest.exist?" do it_behaves_like :file_exist, :exist?, FileTest end + +ruby_version_is "3.2" do + describe "FileTest.exists?" do + it "has been removed" do + FileTest.should_not.respond_to?(:exists?) + end + end +end diff --git a/spec/ruby/core/kernel/not_match_spec.rb b/spec/ruby/core/kernel/not_match_spec.rb index 906f18df2c66e1..f8dd82fad86543 100644 --- a/spec/ruby/core/kernel/not_match_spec.rb +++ b/spec/ruby/core/kernel/not_match_spec.rb @@ -14,6 +14,20 @@ def !~(obj) (obj !~ :foo).should == false end + ruby_version_is ""..."3.2" do + it "returns true if self does not respond to #=~" do + suppress_warning do + (Object.new !~ :foo).should == true + end + end + end + + ruby_version_is "3.2" do + it "raises NoMethodError if self does not respond to #=~" do + -> { Object.new !~ :foo }.should raise_error(NoMethodError) + end + end + it 'can be overridden in subclasses' do obj = KernelSpecs::NotMatch.new (obj !~ :bar).should == :foo diff --git a/spec/ruby/core/method/parameters_spec.rb b/spec/ruby/core/method/parameters_spec.rb index 7d2b37fac75194..8495aef4d2d32d 100644 --- a/spec/ruby/core/method/parameters_spec.rb +++ b/spec/ruby/core/method/parameters_spec.rb @@ -22,6 +22,8 @@ def one_splat_one_block(*args, &block) local_is_not_parameter = {} end + def forward_parameters(...) end + def underscore_parameters(_, _, _ = 1, *_, _:, _: 2, **_, &_); end define_method(:one_optional_defined_method) {|x = 1|} @@ -267,6 +269,20 @@ def object.foo(&) end end + ruby_version_is ""..."3.1" do + it "returns [:rest, :*], [:block, :&] for forward parameters operator" do + m = MethodSpecs::Methods.new + m.method(:forward_parameters).parameters.should == [[:rest, :*], [:block, :&]] + end + end + + ruby_version_is "3.1" do + it "returns [:rest, :*], [:keyrest, :**], [:block, :&] for forward parameters operator" do + m = MethodSpecs::Methods.new + m.method(:forward_parameters).parameters.should == [[:rest, :*], [:keyrest, :**], [:block, :&]] + end + end + it "returns the args and block for a splat and block argument" do m = MethodSpecs::Methods.new m.method(:one_splat_one_block).parameters.should == [[:rest, :args], [:block, :block]] diff --git a/spec/ruby/core/proc/parameters_spec.rb b/spec/ruby/core/proc/parameters_spec.rb index 6c21784ab65d9d..972596d2ea8809 100644 --- a/spec/ruby/core/proc/parameters_spec.rb +++ b/spec/ruby/core/proc/parameters_spec.rb @@ -33,6 +33,16 @@ it "regards named parameters in lambda as optional if lambda: false keyword used" do -> x { }.parameters(lambda: false).first.first.should == :opt end + + it "regards named parameters in procs and lambdas as required if lambda keyword is truthy" do + proc {|x| }.parameters(lambda: 123).first.first.should == :req + -> x { }.parameters(lambda: 123).first.first.should == :req + end + + it "ignores the lambda keyword if it is nil" do + proc {|x|}.parameters(lambda: nil).first.first.should == :opt + -> x { }.parameters(lambda: nil).first.first.should == :req + end end it "regards optional keyword parameters in procs as optional" do @@ -160,4 +170,8 @@ [:block, :_] ] end + + it "returns :nokey for **nil parameter" do + proc { |**nil| }.parameters.should == [[:nokey]] + end end diff --git a/spec/ruby/core/string/to_i_spec.rb b/spec/ruby/core/string/to_i_spec.rb index e4fa89aab3e268..9931502baa2459 100644 --- a/spec/ruby/core/string/to_i_spec.rb +++ b/spec/ruby/core/string/to_i_spec.rb @@ -10,6 +10,18 @@ "1_2_3asdf".to_i.should == 123 end + it "ignores multiple non-consecutive underscoes when the first digit is 0" do + (2..16).each do |base| + "0_0_010".to_i(base).should == base; + end + end + + it "bails out at the first double underscore if the first digit is 0" do + (2..16).each do |base| + "010__1".to_i(base).should == base; + end + end + it "ignores leading whitespaces" do [ " 123", " 123", "\r\n\r\n123", "\t\t123", "\r\n\t\n123", " \t\n\r\t 123"].each do |str| diff --git a/spec/ruby/library/net-ftp/connect_spec.rb b/spec/ruby/library/net-ftp/connect_spec.rb index b5060bd1ca744c..4330d430b47c8d 100644 --- a/spec/ruby/library/net-ftp/connect_spec.rb +++ b/spec/ruby/library/net-ftp/connect_spec.rb @@ -29,7 +29,7 @@ ruby_version_is ""..."3.1" do it "prints a small debug line when in debug mode" do @ftp.debug_mode = true - -> { @ftp.connect(@server.hostname, @server.server_port) }.should output(/#{"connect: "}#{@server.hostname}#{", "}#{@server.server_port}#{"\\nget: 220 Dummy FTP Server ready!"}/) + -> { @ftp.connect(@server.hostname, @server.server_port) }.should output(/connect: #{@server.hostname}, #{@server.server_port}\nget: 220 Dummy FTP Server ready!/) @ftp.debug_mode = false end end diff --git a/spec/ruby/library/set/set_spec.rb b/spec/ruby/library/set/set_spec.rb new file mode 100644 index 00000000000000..2a4edc6dfc63b4 --- /dev/null +++ b/spec/ruby/library/set/set_spec.rb @@ -0,0 +1,12 @@ +require_relative '../../spec_helper' + +describe 'Set' do + ruby_version_is '3.2' do + it 'is available without explicit requiring' do + output = ruby_exe(<<~RUBY, options: '--disable-gems', args: '2>&1') + puts Set.new([1, 2, 3]) + RUBY + output.chomp.should == "#" + end + end +end diff --git a/spec/ruby/optional/capi/encoding_spec.rb b/spec/ruby/optional/capi/encoding_spec.rb index 36437cc7b683b5..b0c38d75a93538 100644 --- a/spec/ruby/optional/capi/encoding_spec.rb +++ b/spec/ruby/optional/capi/encoding_spec.rb @@ -2,7 +2,7 @@ require_relative 'spec_helper' require_relative 'fixtures/encoding' -load_extension('encoding') +extension_path = load_extension('encoding') describe :rb_enc_get_index, shared: true do it "returns the index of the encoding of a String" do @@ -559,19 +559,19 @@ describe "rb_ascii8bit_encindex" do it "returns an index for the ASCII-8BIT encoding" do - @s.rb_ascii8bit_encindex().should >= 0 + @s.rb_ascii8bit_encindex().should == 0 end end describe "rb_utf8_encindex" do it "returns an index for the UTF-8 encoding" do - @s.rb_utf8_encindex().should >= 0 + @s.rb_utf8_encindex().should == 1 end end describe "rb_usascii_encindex" do it "returns an index for the US-ASCII encoding" do - @s.rb_usascii_encindex().should >= 0 + @s.rb_usascii_encindex().should == 2 end end @@ -721,4 +721,27 @@ str.bytes.should == [0, 0x24] end end + + describe "rb_define_dummy_encoding" do + it "defines the dummy encoding" do + @s.rb_define_dummy_encoding("FOO") + enc = Encoding.find("FOO") + enc.should.dummy? + end + + it "returns the index of the dummy encoding" do + index = @s.rb_define_dummy_encoding("BAR") + index.should == Encoding.list.size - 1 + end + + ruby_version_is "3.2" do + it "raises EncodingError if too many encodings" do + code = <<-RUBY + require #{extension_path.dump} + 1_000.times {|i| CApiEncodingSpecs.new.rb_define_dummy_encoding("R_\#{i}") } + RUBY + ruby_exe(code, args: "2>&1", exit_status: 1).should.include?('too many encoding (> 256) (EncodingError)') + end + end + end end diff --git a/spec/ruby/optional/capi/ext/encoding_spec.c b/spec/ruby/optional/capi/ext/encoding_spec.c index 3343848b542958..aa8662cfbd6426 100644 --- a/spec/ruby/optional/capi/ext/encoding_spec.c +++ b/spec/ruby/optional/capi/ext/encoding_spec.c @@ -320,6 +320,10 @@ static VALUE encoding_spec_rb_enc_left_char_head(VALUE self, VALUE str, VALUE of return LONG2NUM(result - ptr); } +static VALUE encoding_spec_rb_define_dummy_encoding(VALUE self, VALUE name) { + return INT2NUM(rb_define_dummy_encoding(RSTRING_PTR(name))); +} + void Init_encoding_spec(void) { VALUE cls; native_rb_encoding_pointer = (rb_encoding**) malloc(sizeof(rb_encoding*)); @@ -379,6 +383,7 @@ void Init_encoding_spec(void) { rb_define_method(cls, "rb_uv_to_utf8", encoding_spec_rb_uv_to_utf8, 2); rb_define_method(cls, "ONIGENC_MBC_CASE_FOLD", encoding_spec_ONIGENC_MBC_CASE_FOLD, 1); rb_define_method(cls, "rb_enc_left_char_head", encoding_spec_rb_enc_left_char_head, 2); + rb_define_method(cls, "rb_define_dummy_encoding", encoding_spec_rb_define_dummy_encoding, 1); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/ext/string_spec.c b/spec/ruby/optional/capi/ext/string_spec.c index c9c5277732ecbf..070a88759ba297 100644 --- a/spec/ruby/optional/capi/ext/string_spec.c +++ b/spec/ruby/optional/capi/ext/string_spec.c @@ -51,10 +51,6 @@ VALUE string_spec_rb_str_set_len_RSTRING_LEN(VALUE self, VALUE str, VALUE len) { return INT2FIX(RSTRING_LEN(str)); } -VALUE string_spec_rb_str_fstring(VALUE self, VALUE str) { - return rb_str_to_interned_str(str); -} - VALUE string_spec_rb_str_buf_new(VALUE self, VALUE len, VALUE str) { VALUE buf; @@ -576,11 +572,19 @@ static VALUE string_spec_rb_str_unlocktmp(VALUE self, VALUE str) { return rb_str_unlocktmp(str); } +static VALUE string_spec_rb_enc_interned_str_cstr(VALUE self, VALUE str, VALUE enc) { + rb_encoding *e = rb_to_encoding(enc); + return rb_enc_interned_str_cstr(RSTRING_PTR(str), e); +} + +static VALUE string_spec_rb_str_to_interned_str(VALUE self, VALUE str) { + return rb_str_to_interned_str(str); +} + void Init_string_spec(void) { VALUE cls = rb_define_class("CApiStringSpecs", rb_cObject); rb_define_method(cls, "rb_cstr2inum", string_spec_rb_cstr2inum, 2); rb_define_method(cls, "rb_cstr_to_inum", string_spec_rb_cstr_to_inum, 3); - rb_define_method(cls, "rb_fstring", string_spec_rb_str_fstring, 1); rb_define_method(cls, "rb_str2inum", string_spec_rb_str2inum, 2); rb_define_method(cls, "rb_str_append", string_spec_rb_str_append, 2); rb_define_method(cls, "rb_str_buf_new", string_spec_rb_str_buf_new, 2); @@ -677,6 +681,8 @@ void Init_string_spec(void) { rb_define_method(cls, "rb_str_catf", string_spec_rb_str_catf, 1); rb_define_method(cls, "rb_str_locktmp", string_spec_rb_str_locktmp, 1); rb_define_method(cls, "rb_str_unlocktmp", string_spec_rb_str_unlocktmp, 1); + rb_define_method(cls, "rb_enc_interned_str_cstr", string_spec_rb_enc_interned_str_cstr, 2); + rb_define_method(cls, "rb_str_to_interned_str", string_spec_rb_str_to_interned_str, 1); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/string_spec.rb b/spec/ruby/optional/capi/string_spec.rb index 3a47fa9762fddc..d9c20cf1760ff9 100644 --- a/spec/ruby/optional/capi/string_spec.rb +++ b/spec/ruby/optional/capi/string_spec.rb @@ -498,25 +498,6 @@ def inspect end end - describe "rb_fstring" do - it 'returns self if the String is frozen' do - input = 'foo'.freeze - output = @s.rb_fstring(input) - - output.should equal(input) - output.should.frozen? - end - - it 'returns a frozen copy if the String is not frozen' do - input = 'foo' - output = @s.rb_fstring(input) - - output.should.frozen? - output.should_not equal(input) - output.should == 'foo' - end - end - describe "rb_str_subseq" do it "returns a byte-indexed substring" do str = "\x00\x01\x02\x03\x04".force_encoding("binary") @@ -1227,4 +1208,59 @@ def inspect -> { @s.rb_str_unlocktmp("test") }.should raise_error(RuntimeError, 'temporal unlocking already unlocked string') end end + + describe "rb_enc_interned_str_cstr" do + it "returns a frozen string" do + str = "hello" + val = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII) + + val.should.is_a?(String) + val.encoding.should == Encoding::US_ASCII + val.should.frozen? + end + + it "returns the same frozen string" do + str = "hello" + result1 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII) + result2 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII) + result1.should.equal?(result2) + end + + it "returns different frozen strings for different encodings" do + str = "hello" + result1 = @s.rb_enc_interned_str_cstr(str, Encoding::US_ASCII) + result2 = @s.rb_enc_interned_str_cstr(str, Encoding::UTF_8) + result1.should_not.equal?(result2) + end + + it "returns the same string as String#-@" do + @s.rb_enc_interned_str_cstr("hello", Encoding::UTF_8).should.equal?(-"hello") + end + end + + describe "rb_str_to_interned_str" do + it "returns a frozen string" do + str = "hello" + result = @s.rb_str_to_interned_str(str) + result.should.is_a?(String) + result.should.frozen? + end + + it "returns the same frozen string" do + str = "hello" + result1 = @s.rb_str_to_interned_str(str) + result2 = @s.rb_str_to_interned_str(str) + result1.should.equal?(result2) + end + + it "returns different frozen strings for different encodings" do + result1 = @s.rb_str_to_interned_str("hello".force_encoding(Encoding::US_ASCII)) + result2 = @s.rb_str_to_interned_str("hello".force_encoding(Encoding::UTF_8)) + result1.should_not.equal?(result2) + end + + it "returns the same string as String#-@" do + @s.rb_str_to_interned_str("hello").should.equal?(-"hello") + end + end end