diff options
Diffstat (limited to 'spec/ruby/optional/capi/string_spec.rb')
| -rw-r--r-- | spec/ruby/optional/capi/string_spec.rb | 733 |
1 files changed, 586 insertions, 147 deletions
diff --git a/spec/ruby/optional/capi/string_spec.rb b/spec/ruby/optional/capi/string_spec.rb index 7c33b79348..3e095f05c8 100644 --- a/spec/ruby/optional/capi/string_spec.rb +++ b/spec/ruby/optional/capi/string_spec.rb @@ -1,4 +1,5 @@ # encoding: utf-8 +# frozen_string_literal: false require_relative 'spec_helper' require_relative '../../shared/string/times' @@ -47,7 +48,7 @@ describe "C-API String function" do [Encoding::BINARY, Encoding::UTF_8].each do |enc| describe "rb_str_set_len on a #{enc.name} String" do before :each do - @str = "abcdefghij".force_encoding(enc) + @str = "abcdefghij".dup.force_encoding(enc) # Make sure to unshare the string @s.rb_str_modify(@str) end @@ -97,6 +98,32 @@ describe "C-API String function" do end end + describe "rb_str_set_len on a UTF-16 String" do + before :each do + @str = "abcdefghij".dup.force_encoding(Encoding::UTF_16BE) + # Make sure to unshare the string + @s.rb_str_modify(@str) + end + + it "inserts two NULL bytes at the length" do + @s.rb_str_set_len(@str, 4).b.should == "abcd".b + @s.rb_str_set_len(@str, 8).b.should == "abcd\x00\x00gh".b + end + end + + describe "rb_str_set_len on a UTF-32 String" do + before :each do + @str = "abcdefghijkl".dup.force_encoding(Encoding::UTF_32BE) + # Make sure to unshare the string + @s.rb_str_modify(@str) + end + + it "inserts four NULL bytes at the length" do + @s.rb_str_set_len(@str, 4).b.should == "abcd".b + @s.rb_str_set_len(@str, 12).b.should == "abcd\x00\x00\x00\x00ijkl".b + end + end + describe "rb_str_buf_new" do it "returns the equivalent of an empty string" do buf = @s.rb_str_buf_new(10, nil) @@ -108,7 +135,7 @@ describe "C-API String function" do it "returns a string with the given capacity" do buf = @s.rb_str_buf_new(256, nil) - @s.rb_str_capacity(buf).should == 256 + @s.rb_str_capacity(buf).should >= 256 end it "returns a string that can be appended to" do @@ -164,11 +191,19 @@ describe "C-API String function" do end it "returns a new String object filled with \\0 bytes" do - s = @s.rb_str_tmp_new(4) - s.encoding.should == Encoding::BINARY - s.bytesize.should == 4 - s.size.should == 4 - s.should == "\x00\x00\x00\x00" + lens = [4] + + ruby_version_is "4.0" do + lens << 100 + end + + lens.each do |len| + s = @s.rb_str_tmp_new(len) + s.encoding.should == Encoding::BINARY + s.bytesize.should == len + s.size.should == len + s.should == "\x00" * len + end end end @@ -181,12 +216,6 @@ describe "C-API String function" do @s.rb_str_new("hello", 3).should == "hel" end - ruby_version_is ''...'2.7' do - it "returns a non-tainted string" do - @s.rb_str_new("hello", 5).should_not.tainted? - end - end - it "returns an empty string if len is 0" do @s.rb_str_new("hello", 0).should == "" end @@ -211,7 +240,7 @@ describe "C-API String function" do describe "rb_usascii_str_new" do it "creates a new String with US-ASCII Encoding from a char buffer of len characters" do - str = "abc".force_encoding("us-ascii") + str = "abc".dup.force_encoding("us-ascii") result = @s.rb_usascii_str_new("abcdef", 3) result.should == str result.encoding.should == Encoding::US_ASCII @@ -227,14 +256,14 @@ describe "C-API String function" do it "returns US-ASCII string for non-US-ASCII string literal" do str = @s.rb_usascii_str_new_lit_non_ascii - str.should == "r\xC3\xA9sum\xC3\xA9".force_encoding(Encoding::US_ASCII) + str.should == "r\xC3\xA9sum\xC3\xA9".dup.force_encoding(Encoding::US_ASCII) str.encoding.should == Encoding::US_ASCII end end describe "rb_usascii_str_new_cstr" do it "creates a new String with US-ASCII Encoding" do - str = "abc".force_encoding("us-ascii") + str = "abc".dup.force_encoding("us-ascii") result = @s.rb_usascii_str_new_cstr("abc") result.should == str result.encoding.should == Encoding::US_ASCII @@ -257,13 +286,13 @@ describe "C-API String function" do it "returns a dup of the original String" do a = "abc" b = @s.rb_str_encode("abc", "us-ascii", 0, nil) - a.should_not equal(b) + a.should_not.equal?(b) end it "returns a duplicate of the original when the encoding doesn't change" do a = "abc" b = @s.rb_str_encode("abc", Encoding::UTF_8, 0, nil) - a.should_not equal(b) + a.should_not.equal?(b) end it "accepts encoding flags" do @@ -291,7 +320,7 @@ describe "C-API String function" do str1 = "hi" str2 = @s.rb_str_new3 str1 str1.should == str2 - str1.should_not equal str2 + str1.should_not.equal? str2 end end @@ -301,7 +330,7 @@ describe "C-API String function" do str1.freeze str2 = @s.rb_str_new4 str1 str1.should == str2 - str1.should equal(str2) + str1.should.equal?(str2) str1.should.frozen? str2.should.frozen? end @@ -310,7 +339,7 @@ describe "C-API String function" do str1 = "hi" str2 = @s.rb_str_new4 str1 str1.should == str2 - str1.should_not equal(str2) + str1.should_not.equal?(str2) str2.should.frozen? end end @@ -320,7 +349,7 @@ describe "C-API String function" do str1 = "hi" str2 = @s.rb_str_dup str1 str1.should == str2 - str1.should_not equal str2 + str1.should_not.equal? str2 end end @@ -335,31 +364,13 @@ describe "C-API String function" do end end - ruby_version_is ''...'2.7' do - describe "rb_tainted_str_new" do - it "creates a new tainted String" do - newstring = @s.rb_tainted_str_new("test", 4) - newstring.should == "test" - newstring.tainted?.should be_true - end - end - - describe "rb_tainted_str_new2" do - it "creates a new tainted String" do - newstring = @s.rb_tainted_str_new2("test") - newstring.should == "test" - newstring.tainted?.should be_true - end - end - end - describe "rb_str_append" do it "appends a string to another string" do @s.rb_str_append("Hello", " Goodbye").should == "Hello Goodbye" end it "raises a TypeError trying to append non-String-like object" do - -> { @s.rb_str_append("Hello", 32323)}.should raise_error(TypeError) + -> { @s.rb_str_append("Hello", 32323)}.should.raise(TypeError) end it "changes Encoding if a string is appended to an empty string" do @@ -378,6 +389,14 @@ describe "C-API String function" do it_behaves_like :string_times, :rb_str_times, -> str, times { @s.rb_str_times(str, times) } end + describe "rb_str_buf_append" do + it "concatenates a string to another string" do + str = "Your house " + @s.rb_str_buf_append(str, "is on fire?").should.equal?(str) + str.should == "Your house is on fire?" + end + end + describe "rb_str_buf_cat" do it "concatenates a C string to a ruby string" do @s.rb_str_buf_cat("Your house is on fire").should == "Your house is on fire?" @@ -408,7 +427,7 @@ describe "C-API String function" do describe "rb_enc_str_buf_cat" do it "concatenates a C string literal to a ruby string with the given encoding" do - input = "hello ".force_encoding(Encoding::US_ASCII) + input = "hello ".dup.force_encoding(Encoding::US_ASCII) result = @s.rb_enc_str_buf_cat(input, "résumé", Encoding::UTF_8) result.should == "hello résumé" result.encoding.should == Encoding::UTF_8 @@ -438,6 +457,20 @@ describe "C-API String function" do end end + describe "rb_str_strlen" do + it 'returns 0 as the length of an empty string' do + @s.rb_str_strlen('').should == 0 + end + + it 'returns the number of characters in a string' do + @s.rb_str_strlen('hello').should == 5 + end + + it 'returns the number of characters in a string with multi-byte characters' do + @s.rb_str_strlen('こんにちは').should == 5 + end + end + describe "rb_str_split" do it "splits strings over a splitter" do @s.rb_str_split("Hello,Goodbye").should == ["Hello", "Goodbye"] @@ -466,7 +499,7 @@ describe "C-API String function" do end it "converts a C string to a Fixnum strictly if base is 0" do - -> { @s.rb_cstr2inum("1234a", 0) }.should raise_error(ArgumentError) + -> { @s.rb_cstr2inum("1234a", 0) }.should.raise(ArgumentError) end end @@ -484,33 +517,14 @@ describe "C-API String function" do end it "converts a C string to a Fixnum strictly" do - -> { @s.rb_cstr_to_inum("1234a", 10, true) }.should raise_error(ArgumentError) - 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' + -> { @s.rb_cstr_to_inum("1234a", 10, true) }.should.raise(ArgumentError) end end describe "rb_str_subseq" do it "returns a byte-indexed substring" do - str = "\x00\x01\x02\x03\x04".force_encoding("binary") - @s.rb_str_subseq(str, 1, 2).should == "\x01\x02".force_encoding("binary") + str = "\x00\x01\x02\x03\x04".dup.force_encoding("binary") + @s.rb_str_subseq(str, 1, 2).should == "\x01\x02".dup.force_encoding("binary") end end @@ -522,6 +536,61 @@ describe "C-API String function" do end end + describe "rb_str_sublen" do + it "returns the character length for a given byte offset in an ASCII string" do + @s.rb_str_sublen("hello", 3).should == 3 + end + + it "returns the character length for a given byte offset in a multibyte string" do + # "hëllo" where 'ë' is 2 bytes in UTF-8, total 6 bytes + str = "hëllo" + @s.rb_str_sublen(str, 3).should == 2 + end + + it "returns 0 for byte offset 0" do + @s.rb_str_sublen("hello", 0).should == 0 + end + + it "returns the full character length for the total byte length" do + str = "hëllo" + @s.rb_str_sublen(str, str.bytesize).should == str.length + end + end + + describe "rb_str_subpos" do + it "returns [byte_offset, byte_length] for a valid character offset in an ASCII string" do + @s.rb_str_subpos("hello", 1).should == [1, 4] + end + + it "returns [byte_offset, byte_length] for a valid character offset in a multibyte string" do + # "hëllo" where 'ë' is 2 bytes in UTF-8 + str = "hëllo" + @s.rb_str_subpos(str, 0).should == [0, 6] + @s.rb_str_subpos(str, 1).should == [1, 5] + @s.rb_str_subpos(str, 2).should == [3, 3] + end + + it "returns [0, byte_length] for offset 0" do + @s.rb_str_subpos("hello", 0).should == [0, 5] + end + + it "returns nil for a negative offset that is out of range" do + @s.rb_str_subpos("hello", -6).should == nil + end + + it "returns the correct position for a negative offset" do + @s.rb_str_subpos("hello", -2).should == [3, 2] + end + + it "returns [byte_length, 0] when offset equals string length" do + @s.rb_str_subpos("hello", 5).should == [5, 0] + end + + it "returns nil when offset is beyond string length" do + @s.rb_str_subpos("hello", 6).should == nil + end + end + describe "rb_str_to_str" do it "calls #to_str to coerce the value to a String" do @s.rb_str_to_str("foo").should == "foo" @@ -529,8 +598,8 @@ describe "C-API String function" do end it "raises a TypeError if coercion fails" do - -> { @s.rb_str_to_str(0) }.should raise_error(TypeError) - -> { @s.rb_str_to_str(CApiStringSpecs::InvalidTostrTest.new) }.should raise_error(TypeError) + -> { @s.rb_str_to_str(0) }.should.raise(TypeError) + -> { @s.rb_str_to_str(CApiStringSpecs::InvalidTostrTest.new) }.should.raise(TypeError) end end @@ -598,6 +667,22 @@ describe "C-API String function" do str = " " @s.RSTRING_PTR_short_memcpy(str).should == "Infinity" end + + it "allows read() to update the string contents" do + filename = fixture(__FILE__, "read.txt") + str = "" + capacities = @s.RSTRING_PTR_read(str, filename) + capacities[0].should >= 30 + capacities[1].should >= 53 + capacities[0].should < capacities[1] + str.should == "fixture file contents to test read() with RSTRING_PTR" + end + + it "terminates the string with at least (encoding min length) \\0 bytes" do + @s.RSTRING_PTR_null_terminate("abc", 1).should == "\x00" + @s.RSTRING_PTR_null_terminate("abc".encode("UTF-16BE"), 2).should == "\x00\x00" + @s.RSTRING_PTR_null_terminate("abc".encode("UTF-32BE"), 4).should == "\x00\x00\x00\x00" + end end describe "RSTRING_LEN" do @@ -638,7 +723,7 @@ describe "C-API String function" do it "does not call #to_s on non-String objects" do str = mock("fake") str.should_not_receive(:to_s) - -> { @s.send(@method, str) }.should raise_error(TypeError) + -> { @s.send(@method, str) }.should.raise(TypeError) end end @@ -647,43 +732,42 @@ describe "C-API String function" do end describe "SafeStringValue" do - ruby_version_is ''...'2.7' do - it "raises for tained string when $SAFE is 1" do - begin - Thread.new { - $SAFE = 1 - -> { - @s.SafeStringValue("str".taint) - }.should raise_error(SecurityError) - }.join - ensure - $SAFE = 0 - end - end + end - it_behaves_like :string_value_macro, :SafeStringValue + describe "rb_str_modify" do + it "raises an error if the string is frozen" do + -> { @s.rb_str_modify("frozen".freeze) }.should.raise(FrozenError) end end describe "rb_str_modify_expand" do it "grows the capacity to bytesize + expand, not changing the bytesize" do str = @s.rb_str_buf_new(256, "abcd") - @s.rb_str_capacity(str).should == 256 + @s.rb_str_capacity(str).should >= 256 @s.rb_str_set_len(str, 3) str.bytesize.should == 3 @s.RSTRING_LEN(str).should == 3 - @s.rb_str_capacity(str).should == 256 + @s.rb_str_capacity(str).should >= 256 @s.rb_str_modify_expand(str, 4) str.bytesize.should == 3 @s.RSTRING_LEN(str).should == 3 - @s.rb_str_capacity(str).should == 7 + @s.rb_str_capacity(str).should >= 7 @s.rb_str_modify_expand(str, 1024) str.bytesize.should == 3 @s.RSTRING_LEN(str).should == 3 - @s.rb_str_capacity(str).should == 1027 + @s.rb_str_capacity(str).should >= 1027 + + @s.rb_str_modify_expand(str, 1) + str.bytesize.should == 3 + @s.RSTRING_LEN(str).should == 3 + @s.rb_str_capacity(str).should >= 4 + end + + it "raises an error if the string is frozen" do + -> { @s.rb_str_modify_expand("frozen".freeze, 10) }.should.raise(FrozenError) end end @@ -700,8 +784,13 @@ describe "C-API String function" do @s.rb_str_resize_RSTRING_LEN("test", 2).should == 2 end + it "copies the existing bytes" do + str = "t" + @s.rb_str_resize_copy(str).should == "test" + end + it "increases the size of the string" do - expected = "test".force_encoding("US-ASCII") + expected = "test".dup.force_encoding("US-ASCII") str = @s.rb_str_resize(expected.dup, 12) str.size.should == 12 str.bytesize.should == 12 @@ -734,14 +823,14 @@ describe "C-API String function" do it "freezes the string" do s = "" @s.rb_str_freeze(s).should == s - s.frozen?.should be_true + s.frozen?.should == true end end describe "rb_str_hash" do it "hashes the string into a number" do s = "hello" - @s.rb_str_hash(s).should be_kind_of(Integer) + @s.rb_str_hash(s).should.is_a?(Integer) end end @@ -757,7 +846,7 @@ describe "rb_str_free" do # is available. There is no guarantee this even does # anything at all it "indicates data for a string might be freed" do - @s.rb_str_free("xyz").should be_nil + @s.rb_str_free("xyz").should == nil end end @@ -772,12 +861,6 @@ describe :rb_external_str_new, shared: true do x80 = [0x80].pack('C') @s.send(@method, "#{x80}abc").encoding.should == Encoding::BINARY end - - ruby_version_is ''...'2.7' do - it "returns a tainted String" do - @s.send(@method, "abc").tainted?.should be_true - end - end end describe "C-API String function" do @@ -805,12 +888,12 @@ describe "C-API String function" do describe "rb_str_equal" do it "compares two same strings" do s = "hello" - @s.rb_str_equal(s, "hello").should be_true + @s.rb_str_equal(s, "hello").should == true end it "compares two different strings" do s = "hello" - @s.rb_str_equal(s, "hella").should be_false + @s.rb_str_equal(s, "hella").should == false end end @@ -838,14 +921,14 @@ describe "C-API String function" do # it "transcodes a String to Encoding.default_internal if it is set" do # Encoding.default_internal = Encoding::EUC_JP # -# - a = "\xE3\x81\x82\xe3\x82\x8c".force_encoding("utf-8") +# - a = "\xE3\x81\x82\xe3\x82\x8c".dup.force_encoding("utf-8") # + a = [0xE3, 0x81, 0x82, 0xe3, 0x82, 0x8c].pack('C6').force_encoding("utf-8") # s = @s.rb_external_str_new_with_enc(a, a.bytesize, Encoding::UTF_8) # - -# - s.should == "\xA4\xA2\xA4\xEC".force_encoding("euc-jp") +# - s.should == "\xA4\xA2\xA4\xEC".dup.force_encoding("euc-jp") # + x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4')#.force_encoding('binary') # + s.should == x -# s.encoding.should equal(Encoding::EUC_JP) +# s.encoding.should.equal?(Encoding::EUC_JP) # end it "transcodes a String to Encoding.default_internal if it is set" do @@ -855,107 +938,104 @@ describe "C-API String function" do s = @s.rb_external_str_new_with_enc(a, a.bytesize, Encoding::UTF_8) x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('euc-jp') s.should == x - s.encoding.should equal(Encoding::EUC_JP) - end - - ruby_version_is ''...'2.7' do - it "returns a tainted String" do - s = @s.rb_external_str_new_with_enc("abc", 3, Encoding::US_ASCII) - s.tainted?.should be_true - end + s.encoding.should.equal?(Encoding::EUC_JP) end end describe "rb_locale_str_new" do it "returns a String with 'locale' encoding" do s = @s.rb_locale_str_new("abc", 3) - s.should == "abc".force_encoding(Encoding.find("locale")) - s.encoding.should equal(Encoding.find("locale")) + s.should == "abc".dup.force_encoding(Encoding.find("locale")) + s.encoding.should.equal?(Encoding.find("locale")) end end describe "rb_locale_str_new_cstr" do it "returns a String with 'locale' encoding" do s = @s.rb_locale_str_new_cstr("abc") - s.should == "abc".force_encoding(Encoding.find("locale")) - s.encoding.should equal(Encoding.find("locale")) + s.should == "abc".dup.force_encoding(Encoding.find("locale")) + s.encoding.should.equal?(Encoding.find("locale")) end end describe "rb_str_conv_enc" do it "returns the original String when to encoding is not specified" do - a = "abc".force_encoding("us-ascii") - @s.rb_str_conv_enc(a, Encoding::US_ASCII, nil).should equal(a) + a = "abc".dup.force_encoding("us-ascii") + @s.rb_str_conv_enc(a, Encoding::US_ASCII, nil).should.equal?(a) end it "returns the original String if a transcoding error occurs" do - a = [0xEE].pack('C').force_encoding("utf-8") - @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP).should equal(a) + a = [0xEE].pack('C').force_encoding(Encoding::UTF_8) + @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP).should.equal?(a) + a.encoding.should == Encoding::UTF_8 + + a = "\x80".b + @s.rb_str_conv_enc(a, Encoding::BINARY, Encoding::UTF_8).should.equal?(a) + a.encoding.should == Encoding::BINARY end it "returns a transcoded String" do - a = "\xE3\x81\x82\xE3\x82\x8C".force_encoding("utf-8") + a = "\xE3\x81\x82\xE3\x82\x8C".dup.force_encoding(Encoding::UTF_8) result = @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::EUC_JP) - x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8') - result.should == x.force_encoding("euc-jp") - result.encoding.should equal(Encoding::EUC_JP) + result.should == [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding(Encoding::EUC_JP) + result.encoding.should == Encoding::EUC_JP end describe "when the String encoding is equal to the destination encoding" do it "returns the original String" do - a = "abc".force_encoding("us-ascii") - @s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::US_ASCII).should equal(a) + a = "abc".dup.force_encoding("us-ascii") + @s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::US_ASCII).should.equal?(a) end it "returns the original String if the destination encoding is ASCII compatible and the String has no high bits set" do a = "abc".encode("us-ascii") - @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::US_ASCII).should equal(a) + @s.rb_str_conv_enc(a, Encoding::UTF_8, Encoding::US_ASCII).should.equal?(a) end it "returns the origin String if the destination encoding is BINARY" do - a = "abc".force_encoding("binary") - @s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::BINARY).should equal(a) + a = "abc".dup.force_encoding("binary") + @s.rb_str_conv_enc(a, Encoding::US_ASCII, Encoding::BINARY).should.equal?(a) end end end describe "rb_str_conv_enc_opts" do it "returns the original String when to encoding is not specified" do - a = "abc".force_encoding("us-ascii") - @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, nil, 0, nil).should equal(a) + a = "abc".dup.force_encoding("us-ascii") + @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, nil, 0, nil).should.equal?(a) end it "returns the original String if a transcoding error occurs" do a = [0xEE].pack('C').force_encoding("utf-8") @s.rb_str_conv_enc_opts(a, Encoding::UTF_8, - Encoding::EUC_JP, 0, nil).should equal(a) + Encoding::EUC_JP, 0, nil).should.equal?(a) end it "returns a transcoded String" do - a = "\xE3\x81\x82\xE3\x82\x8C".force_encoding("utf-8") + a = "\xE3\x81\x82\xE3\x82\x8C".dup.force_encoding("utf-8") result = @s.rb_str_conv_enc_opts(a, Encoding::UTF_8, Encoding::EUC_JP, 0, nil) x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4').force_encoding('utf-8') result.should == x.force_encoding("euc-jp") - result.encoding.should equal(Encoding::EUC_JP) + result.encoding.should.equal?(Encoding::EUC_JP) end describe "when the String encoding is equal to the destination encoding" do it "returns the original String" do - a = "abc".force_encoding("us-ascii") + a = "abc".dup.force_encoding("us-ascii") @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, - Encoding::US_ASCII, 0, nil).should equal(a) + Encoding::US_ASCII, 0, nil).should.equal?(a) end it "returns the original String if the destination encoding is ASCII compatible and the String has no high bits set" do a = "abc".encode("us-ascii") @s.rb_str_conv_enc_opts(a, Encoding::UTF_8, - Encoding::US_ASCII, 0, nil).should equal(a) + Encoding::US_ASCII, 0, nil).should.equal?(a) end it "returns the origin String if the destination encoding is BINARY" do - a = "abc".force_encoding("binary") + a = "abc".dup.force_encoding("binary") @s.rb_str_conv_enc_opts(a, Encoding::US_ASCII, - Encoding::BINARY, 0, nil).should equal(a) + Encoding::BINARY, 0, nil).should.equal?(a) end end end @@ -964,15 +1044,15 @@ describe "C-API String function" do it "returns the original String with the external encoding" do Encoding.default_external = Encoding::ISO_8859_1 s = @s.rb_str_export("Hëllo") - s.encoding.should equal(Encoding::ISO_8859_1) + s.encoding.should.equal?(Encoding::ISO_8859_1) end end describe "rb_str_export_locale" do it "returns the original String with the locale encoding" do s = @s.rb_str_export_locale("abc") - s.should == "abc".force_encoding(Encoding.find("locale")) - s.encoding.should equal(Encoding.find("locale")) + s.should == "abc".dup.force_encoding(Encoding.find("locale")) + s.encoding.should.equal?(Encoding.find("locale")) end end @@ -987,7 +1067,7 @@ describe "C-API String function" do it "returns the source string if it can not be converted" do source = ["00ff"].pack("H*"); result = @s.rb_str_export_to_enc(source, Encoding::UTF_8) - result.should equal(source) + result.should.equal?(source) end it "does not alter the source string if it can not be converted" do @@ -995,7 +1075,7 @@ describe "C-API String function" do result = @s.rb_str_export_to_enc(source, Encoding::UTF_8) source.bytes.should == [0, 255] end -end + end describe "rb_sprintf" do it "replaces the parts like sprintf" do @@ -1024,9 +1104,64 @@ end end it "formats a TrueClass VALUE as 'true' if sign specified in format" do - s = 'Result: true.' + s = 'Result: TrueClass.' @s.rb_sprintf4(true.class).should == s end + + it "formats nil using to_s if sign not specified in format" do + s = 'Result: .' + @s.rb_sprintf3(nil).should == s + end + + it "formats nil using inspect if sign specified in format" do + s = 'Result: nil.' + @s.rb_sprintf4(nil).should == s + end + + it "truncates a string to a supplied precision if that is shorter than the string" do + s = 'Result: Hel.' + @s.rb_sprintf5(0, 3, "Hello").should == s + end + + it "does not truncates a string to a supplied precision if that is longer than the string" do + s = 'Result: Hello.' + @s.rb_sprintf5(0, 8, "Hello").should == s + end + + it "pads a string to a supplied width if that is longer than the string" do + s = 'Result: Hello.' + @s.rb_sprintf5(8, 5, "Hello").should == s + end + + it "truncates a VALUE string to a supplied precision if that is shorter than the VALUE string" do + s = 'Result: Hel.' + @s.rb_sprintf6(0, 3, "Hello").should == s + end + + it "does not truncates a VALUE string to a supplied precision if that is longer than the VALUE string" do + s = 'Result: Hello.' + @s.rb_sprintf6(0, 8, "Hello").should == s + end + + it "pads a VALUE string to a supplied width if that is longer than the VALUE string" do + s = 'Result: Hello.' + @s.rb_sprintf6(8, 5, "Hello").should == s + end + + it "can format a nil VALUE as a pointer and gives the same output as sprintf in C" do + res = @s.rb_sprintf7("%p", nil); + res[0].should == res[1] + end + + it "can format a string VALUE as a pointer and gives the same output as sprintf in C" do + res = @s.rb_sprintf7("%p", "Hello") + res[0].should == res[1] + end + + it "can format a raw number a pointer and gives the same output as sprintf in C" do + res = @s.rb_sprintf7("%p", 0x223643); + res[0].should == res[1] + end end describe "rb_vsprintf" do @@ -1046,11 +1181,11 @@ end end it "raises a TypeError if #to_str does not return a string" do - -> { @s.rb_String(CApiStringSpecs::InvalidTostrTest.new) }.should raise_error(TypeError) + -> { @s.rb_String(CApiStringSpecs::InvalidTostrTest.new) }.should.raise(TypeError) end it "tries to convert the passed argument to a string by calling #to_s" do - @s.rb_String({"bar" => "foo"}).should == '{"bar"=>"foo"}' + @s.rb_String({"bar" => "foo"}).should == {"bar" => "foo"}.to_s end end @@ -1064,11 +1199,11 @@ end end it "raises an error if a string contains a null" do - -> { @s.rb_string_value_cstr("Hello\0 with a null.") }.should raise_error(ArgumentError) + -> { @s.rb_string_value_cstr("Hello\0 with a null.") }.should.raise(ArgumentError) end it "raises an error if a UTF-16 string contains a null" do - -> { @s.rb_string_value_cstr("Hello\0 with a null.".encode('UTF-16BE')) }.should raise_error(ArgumentError) + -> { @s.rb_string_value_cstr("Hello\0 with a null.".encode('UTF-16BE')) }.should.raise(ArgumentError) end end @@ -1095,9 +1230,11 @@ end describe "rb_utf8_str_new_static" do it "returns a UTF-8 string of the correct characters and length" do - str = @s.rb_utf8_str_new_static + str, ptr = @s.rb_utf8_str_new_static str.should == "nokogiri" str.encoding.should == Encoding::UTF_8 + + @s.RSTRING_PTR(str).should == ptr end end @@ -1116,4 +1253,306 @@ end str.encoding.should == Encoding::UTF_8 end end + + describe "rb_str_vcatf" do + it "appends the message to the string" do + @s.rb_str_vcatf("").should == "fmt 42 7 number" + + str = "test " + @s.rb_str_vcatf(str) + str.should == "test fmt 42 7 number" + end + end + + describe "rb_str_catf" do + it "appends the message to the string" do + @s.rb_str_catf("").should == "fmt 41 6 number" + + str = "test " + @s.rb_str_catf(str) + str.should == "test fmt 41 6 number" + end + end + + describe "rb_str_locktmp" do + it "raises an error when trying to lock an already locked string" do + str = +"test" + @s.rb_str_locktmp(str).should == str + -> { @s.rb_str_locktmp(str) }.should.raise(RuntimeError, 'temporal locking already locked string') + end + + it "locks a string so that modifications would raise an error" do + str = +"test" + @s.rb_str_locktmp(str).should == str + -> { str.upcase! }.should.raise(RuntimeError, 'can\'t modify string; temporarily locked') + end + + ruby_version_is "4.0" do + it "raises FrozenError if string is frozen" do + str = -"rb_str_locktmp" + -> { @s.rb_str_locktmp(str) }.should.raise(FrozenError) + + str = +"rb_str_locktmp" + str.freeze + -> { @s.rb_str_locktmp(str) }.should.raise(FrozenError) + end + end + end + + describe "rb_str_unlocktmp" do + it "unlocks a locked string" do + str = +"test" + @s.rb_str_locktmp(str) + @s.rb_str_unlocktmp(str).should == str + str.upcase!.should == "TEST" + end + + it "raises an error when trying to unlock an already unlocked string" do + -> { @s.rb_str_unlocktmp(+"test") }.should.raise(RuntimeError, 'temporal unlocking already unlocked string') + end + + ruby_version_is "4.0" do + it "raises FrozenError if string is frozen" do + str = -"rb_str_locktmp" + -> { @s.rb_str_unlocktmp(str) }.should.raise(FrozenError) + + str = +"rb_str_locktmp" + str.freeze + -> { @s.rb_str_unlocktmp(str) }.should.raise(FrozenError) + end + 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 + + ruby_bug "#20322", ""..."3.4" do + it "uses the default encoding if encoding is null" do + str = "hello" + val = @s.rb_enc_interned_str_cstr(str, nil) + val.encoding.should == Encoding::ASCII_8BIT + end + end + end + + describe "rb_enc_interned_str" do + it "returns a frozen string" do + str = "hello" + val = @s.rb_enc_interned_str(str, str.bytesize, 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(str, str.bytesize, Encoding::US_ASCII) + result2 = @s.rb_enc_interned_str(str, str.bytesize, 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(str, str.bytesize, Encoding::US_ASCII) + result2 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::UTF_8) + result1.should_not.equal?(result2) + end + + it 'returns the same string when using non-ascii characters' do + str = 'こんにちは' + result1 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::UTF_8) + result2 = @s.rb_enc_interned_str(str, str.bytesize, Encoding::UTF_8) + result1.should.equal?(result2) + end + + it "returns the same string as String#-@" do + str = "hello" + @s.rb_enc_interned_str(str, str.bytesize, Encoding::UTF_8).should.equal?(-str) + end + + ruby_bug "#20322", ""..."3.4" do + it "uses the default encoding if encoding is null" do + str = "hello" + val = @s.rb_enc_interned_str(str, str.bytesize, nil) + val.encoding.should == Encoding::ASCII_8BIT + end + 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".dup.force_encoding(Encoding::US_ASCII)) + result2 = @s.rb_str_to_interned_str("hello".dup.force_encoding(Encoding::UTF_8)) + result1.should_not.equal?(result2) + end + + it "preserves the encoding of the original string" do + result1 = @s.rb_str_to_interned_str("hello".dup.force_encoding(Encoding::US_ASCII)) + result2 = @s.rb_str_to_interned_str("hello".dup.force_encoding(Encoding::UTF_8)) + result1.encoding.should == Encoding::US_ASCII + result2.encoding.should == Encoding::UTF_8 + end + + it "returns the same string as String#-@" do + @s.rb_str_to_interned_str("hello").should.equal?(-"hello") + end + end + + describe "rb_interned_str" do + it "returns a frozen string" do + str = "hello" + result = @s.rb_interned_str(str, str.bytesize) + result.should.is_a?(String) + result.should.frozen? + result.encoding.should == Encoding::US_ASCII + end + + it "returns the same frozen string" do + str = "hello" + result1 = @s.rb_interned_str(str, str.bytesize) + result2 = @s.rb_interned_str(str, str.bytesize) + result1.should.equal?(result2) + end + + it "supports strings with embedded null bytes" do + str = "foo\x00bar\x00baz".b + result = @s.rb_interned_str(str, str.bytesize) + result.should == str + end + + it "return US_ASCII encoding for an empty string" do + result = @s.rb_interned_str("", 0) + result.should == "" + result.encoding.should == Encoding::US_ASCII + end + + it "returns US_ASCII encoding for strings of only 7 bit ASCII" do + 0x00.upto(0x7f).each do |char| + result = @s.rb_interned_str(char.chr, 1) + result.encoding.should == Encoding::US_ASCII + end + end + + ruby_bug "21842", ""..."4.1" do + it "returns BINARY encoding for strings that use the 8th bit" do + 0x80.upto(0xff) do |char| + result = @s.rb_interned_str(char.chr, 1) + result.encoding.should == Encoding::BINARY + end + end + end + + it 'returns the same string when using non-ascii characters' do + str = 'こんにちは' + result1 = @s.rb_interned_str(str, str.bytesize) + result2 = @s.rb_interned_str(str, str.bytesize) + result1.should.equal?(result2) + end + + ruby_bug "21842", ""..."4.1" do + it "returns the same string as String#-@" do + str = "hello".dup.force_encoding(Encoding::US_ASCII) + @s.rb_interned_str(str, str.bytesize).should.equal?(-str) + end + end + end + + describe "rb_interned_str_cstr" do + it "returns a frozen string" do + str = "hello" + result = @s.rb_interned_str_cstr(str) + result.should.is_a?(String) + result.should.frozen? + result.encoding.should == Encoding::US_ASCII + end + + it "returns the same frozen string" do + str = "hello" + result1 = @s.rb_interned_str_cstr(str) + result2 = @s.rb_interned_str_cstr(str) + result1.should.equal?(result2) + end + + it "does not support strings with embedded null bytes" do + str = "foo\x00bar\x00baz".b + result = @s.rb_interned_str_cstr(str) + result.should == "foo" + end + + it "return US_ASCII encoding for an empty string" do + result = @s.rb_interned_str_cstr("") + result.should == "" + result.encoding.should == Encoding::US_ASCII + end + + it "returns US_ASCII encoding for strings of only 7 bit ASCII" do + 0x01.upto(0x7f).each do |char| + result = @s.rb_interned_str_cstr(char.chr) + result.encoding.should == Encoding::US_ASCII + end + end + + ruby_bug "21842", ""..."4.1" do + it "returns BINARY encoding for strings that use the 8th bit" do + 0x80.upto(0xff) do |char| + result = @s.rb_interned_str_cstr(char.chr) + result.encoding.should == Encoding::BINARY + end + end + end + + it 'returns the same string when using non-ascii characters' do + str = 'こんにちは' + result1 = @s.rb_interned_str_cstr(str) + result2 = @s.rb_interned_str_cstr(str) + result1.should.equal?(result2) + end + + ruby_bug "21842", ""..."4.1" do + it "returns the same string as String#-@" do + str = "hello".dup.force_encoding(Encoding::US_ASCII) + @s.rb_interned_str_cstr(str).should.equal?(-str) + end + end + end end |
