mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00

This removes the related tests, and puts the related specs behind version guards. This affects all code in lib, including some libraries that may want to support older versions of Ruby.
1010 lines
30 KiB
Ruby
1010 lines
30 KiB
Ruby
# encoding: utf-8
|
|
require_relative 'spec_helper'
|
|
require_relative '../../shared/string/times'
|
|
|
|
load_extension('string')
|
|
|
|
class CApiStringSpecs
|
|
class ValidTostrTest
|
|
def to_str
|
|
"ruby"
|
|
end
|
|
end
|
|
|
|
class InvalidTostrTest
|
|
def to_str
|
|
[]
|
|
end
|
|
end
|
|
|
|
class ToSOrInspect
|
|
def to_s
|
|
'A string'
|
|
end
|
|
|
|
def inspect
|
|
'A different string'
|
|
end
|
|
end
|
|
end
|
|
|
|
describe :rb_str_new2, shared: true do
|
|
it "returns a new string object calling strlen on the passed C string" do
|
|
# Hardcoded to pass const char * = "hello\0invisible"
|
|
@s.send(@method, "hello\0not used").should == "hello"
|
|
end
|
|
|
|
it "encodes the string with ASCII_8BIT" do
|
|
@s.send(@method, "hello").encoding.should == Encoding::BINARY
|
|
end
|
|
end
|
|
|
|
describe "C-API String function" do
|
|
before :each do
|
|
@s = CApiStringSpecs.new
|
|
end
|
|
|
|
[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)
|
|
# Make sure to unshare the string
|
|
@s.rb_str_modify(@str)
|
|
end
|
|
|
|
it "reduces the size of the string" do
|
|
@s.rb_str_set_len(@str, 5).should == "abcde"
|
|
end
|
|
|
|
it "inserts a NULL byte at the length" do
|
|
@s.rb_str_set_len(@str, 5).should == "abcde"
|
|
@s.rb_str_set_len(@str, 8).should == "abcde\x00gh"
|
|
end
|
|
|
|
it "updates the byte size" do
|
|
@s.rb_str_set_len(@str, 4)
|
|
@str.bytesize.should == 4
|
|
@str.should == "abcd"
|
|
end
|
|
|
|
it "invalidates the character size" do
|
|
@str.size.should == 10
|
|
@s.rb_str_set_len(@str, 4)
|
|
@str.size.should == 4
|
|
@str.should == "abcd"
|
|
end
|
|
|
|
it "invalidates the code range" do
|
|
@s.rb_str_set_len(@str, 4)
|
|
@str.ascii_only?.should == true
|
|
end
|
|
|
|
it "updates the string's attributes visible in C code" do
|
|
@s.rb_str_set_len_RSTRING_LEN(@str, 4).should == 4
|
|
end
|
|
|
|
it "can reveal characters written from C with RSTRING_PTR" do
|
|
@s.rb_str_set_len(@str, 1)
|
|
@str.should == "a"
|
|
|
|
@s.RSTRING_PTR_set(@str, 1, 'B'.ord)
|
|
@s.RSTRING_PTR_set(@str, 2, 'C'.ord)
|
|
@s.rb_str_set_len(@str, 3)
|
|
|
|
@str.bytesize.should == 3
|
|
@str.should == "aBC"
|
|
end
|
|
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)
|
|
buf.should == ""
|
|
buf.bytesize.should == 0
|
|
buf.size.should == 0
|
|
@s.RSTRING_LEN(buf).should == 0
|
|
end
|
|
|
|
it "returns a string with the given capacity" do
|
|
buf = @s.rb_str_buf_new(256, nil)
|
|
@s.rb_str_capacity(buf).should == 256
|
|
end
|
|
|
|
it "returns a string that can be appended to" do
|
|
str = @s.rb_str_buf_new(10, "defg")
|
|
str << "abcde"
|
|
str.should == "abcde"
|
|
end
|
|
|
|
it "returns a string that can be concatenated to another string" do
|
|
str = @s.rb_str_buf_new(10, "defg")
|
|
("abcde" + str).should == "abcde"
|
|
end
|
|
|
|
it "returns a string whose bytes can be accessed by RSTRING_PTR" do
|
|
str = @s.rb_str_buf_new(10, "abcdefghi")
|
|
@s.rb_str_new(str, 10).should == "abcdefghi\x00"
|
|
end
|
|
|
|
it "returns a string that can be modified by rb_str_set_len" do
|
|
str = @s.rb_str_buf_new(10, "abcdef")
|
|
@s.rb_str_set_len(str, 4)
|
|
str.should == "abcd"
|
|
|
|
@s.rb_str_set_len(str, 8)
|
|
str[0, 6].should == "abcd\x00f"
|
|
@s.RSTRING_LEN(str).should == 8
|
|
end
|
|
|
|
it "can be used as a general buffer and reveal characters with rb_str_set_len" do
|
|
str = @s.rb_str_buf_new(10, "abcdef")
|
|
|
|
@s.RSTRING_PTR_set(str, 0, 195)
|
|
@s.RSTRING_PTR_set(str, 1, 169)
|
|
@s.rb_str_set_len(str, 2)
|
|
|
|
str.force_encoding(Encoding::UTF_8)
|
|
str.bytesize.should == 2
|
|
str.size.should == 1
|
|
str.should == "é"
|
|
end
|
|
end
|
|
|
|
describe "rb_str_buf_new2" do
|
|
it "returns a new string object calling strlen on the passed C string" do
|
|
# Hardcoded to pass const char * = "hello\0invisible"
|
|
@s.rb_str_buf_new2.should == "hello"
|
|
end
|
|
end
|
|
|
|
describe "rb_str_new" do
|
|
it "creates a new String with BINARY Encoding" do
|
|
@s.rb_str_new("", 0).encoding.should == Encoding::BINARY
|
|
end
|
|
|
|
it "returns a new string object from a char buffer of len characters" 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).tainted?.should == false
|
|
end
|
|
end
|
|
|
|
it "returns an empty string if len is 0" do
|
|
@s.rb_str_new("hello", 0).should == ""
|
|
end
|
|
|
|
it "copy length bytes and does not stop at the first \\0 byte" do
|
|
@s.rb_str_new("he\x00llo", 6).should == "he\x00llo"
|
|
@s.rb_str_new_native("he\x00llo", 6).should == "he\x00llo"
|
|
end
|
|
|
|
it "returns a string from an offset char buffer" do
|
|
@s.rb_str_new_offset("hello", 1, 3).should == "ell"
|
|
end
|
|
end
|
|
|
|
describe "rb_str_new2" do
|
|
it_behaves_like :rb_str_new2, :rb_str_new2
|
|
end
|
|
|
|
describe "rb_str_new_cstr" do
|
|
it_behaves_like :rb_str_new2, :rb_str_new_cstr
|
|
end
|
|
|
|
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")
|
|
result = @s.rb_usascii_str_new("abcdef", 3)
|
|
result.should == str
|
|
result.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")
|
|
result = @s.rb_usascii_str_new_cstr("abc")
|
|
result.should == str
|
|
result.encoding.should == Encoding::US_ASCII
|
|
end
|
|
end
|
|
|
|
describe "rb_str_encode" do
|
|
it "returns a String in the destination encoding" do
|
|
result = @s.rb_str_encode("abc", Encoding::ISO_8859_1, 0, nil)
|
|
result.encoding.should == Encoding::ISO_8859_1
|
|
end
|
|
|
|
it "transcodes the String" do
|
|
result = @s.rb_str_encode("ありがとう", "euc-jp", 0, nil)
|
|
euc_jp = [0xa4, 0xa2, 0xa4, 0xea, 0xa4, 0xac, 0xa4, 0xc8, 0xa4, 0xa6].pack('C*').force_encoding("euc-jp")
|
|
result.should == euc_jp
|
|
result.encoding.should == Encoding::EUC_JP
|
|
end
|
|
|
|
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)
|
|
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)
|
|
end
|
|
|
|
it "accepts encoding flags" do
|
|
xFF = [0xFF].pack('C').force_encoding('utf-8')
|
|
result = @s.rb_str_encode("a#{xFF}c", "us-ascii",
|
|
Encoding::Converter::INVALID_REPLACE, nil)
|
|
result.should == "a?c"
|
|
result.encoding.should == Encoding::US_ASCII
|
|
end
|
|
|
|
it "accepts an encoding options Hash specifying replacement String" do
|
|
# Yeah, MRI aborts with rb_bug() if the options Hash is not frozen
|
|
options = { replace: "b" }.freeze
|
|
xFF = [0xFF].pack('C').force_encoding('utf-8')
|
|
result = @s.rb_str_encode("a#{xFF}c", "us-ascii",
|
|
Encoding::Converter::INVALID_REPLACE,
|
|
options)
|
|
result.should == "abc"
|
|
result.encoding.should == Encoding::US_ASCII
|
|
end
|
|
end
|
|
|
|
describe "rb_str_new3" do
|
|
it "returns a copy of the string" do
|
|
str1 = "hi"
|
|
str2 = @s.rb_str_new3 str1
|
|
str1.should == str2
|
|
str1.should_not equal str2
|
|
end
|
|
end
|
|
|
|
describe "rb_str_new4" do
|
|
it "returns the original string if it is already frozen" do
|
|
str1 = "hi"
|
|
str1.freeze
|
|
str2 = @s.rb_str_new4 str1
|
|
str1.should == str2
|
|
str1.should equal(str2)
|
|
str1.frozen?.should == true
|
|
str2.frozen?.should == true
|
|
end
|
|
|
|
it "returns a frozen copy of the string" do
|
|
str1 = "hi"
|
|
str2 = @s.rb_str_new4 str1
|
|
str1.should == str2
|
|
str1.should_not equal(str2)
|
|
str2.frozen?.should == true
|
|
end
|
|
end
|
|
|
|
describe "rb_str_dup" do
|
|
it "returns a copy of the string" do
|
|
str1 = "hi"
|
|
str2 = @s.rb_str_dup str1
|
|
str1.should == str2
|
|
str1.should_not equal str2
|
|
end
|
|
end
|
|
|
|
describe "rb_str_new5" do
|
|
it "returns a new string with the same class as the passed string" do
|
|
string_class = Class.new(String)
|
|
template_string = string_class.new("hello world")
|
|
new_string = @s.rb_str_new5(template_string, "hello world", 11)
|
|
|
|
new_string.should == "hello world"
|
|
new_string.class.should == string_class
|
|
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)
|
|
end
|
|
|
|
it "changes Encoding if a string is appended to an empty string" do
|
|
string = "パスタ".encode(Encoding::ISO_2022_JP)
|
|
@s.rb_str_append("", string).encoding.should == Encoding::ISO_2022_JP
|
|
end
|
|
end
|
|
|
|
describe "rb_str_plus" do
|
|
it "returns a new string from concatenating two other strings" do
|
|
@s.rb_str_plus("Hello", " Goodbye").should == "Hello Goodbye"
|
|
end
|
|
end
|
|
|
|
describe "rb_str_times" do
|
|
it_behaves_like :string_times, :rb_str_times, -> str, times { @s.rb_str_times(str, times) }
|
|
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?"
|
|
end
|
|
end
|
|
|
|
describe "rb_str_cat" do
|
|
it "concatenates a C string to ruby string" do
|
|
@s.rb_str_cat("Your house is on fire").should == "Your house is on fire?"
|
|
end
|
|
end
|
|
|
|
describe "rb_str_cat2" do
|
|
it "concatenates a C string to a ruby string" do
|
|
@s.rb_str_cat2("Your house is on fire").should == "Your house is on fire?"
|
|
end
|
|
end
|
|
|
|
describe "rb_str_cmp" do
|
|
it "returns 0 if two strings are identical" do
|
|
@s.rb_str_cmp("ppp", "ppp").should == 0
|
|
end
|
|
|
|
it "returns -1 if the first string is shorter than the second" do
|
|
@s.rb_str_cmp("xxx", "xxxx").should == -1
|
|
end
|
|
|
|
it "returns -1 if the first string is lexically less than the second" do
|
|
@s.rb_str_cmp("xxx", "yyy").should == -1
|
|
end
|
|
|
|
it "returns 1 if the first string is longer than the second" do
|
|
@s.rb_str_cmp("xxxx", "xxx").should == 1
|
|
end
|
|
|
|
it "returns 1 if the first string is lexically greater than the second" do
|
|
@s.rb_str_cmp("yyy", "xxx").should == 1
|
|
end
|
|
end
|
|
|
|
describe "rb_str_split" do
|
|
it "splits strings over a splitter" do
|
|
@s.rb_str_split("Hello,Goodbye").should == ["Hello", "Goodbye"]
|
|
end
|
|
end
|
|
|
|
describe "rb_str2inum" do
|
|
it "converts a string to a number given a base" do
|
|
@s.rb_str2inum("10", 10).should == 10
|
|
@s.rb_str2inum("A", 16).should == 10
|
|
end
|
|
end
|
|
|
|
describe "rb_cstr2inum" do
|
|
it "converts a C string to a Fixnum given a base" do
|
|
@s.rb_cstr2inum("10", 10).should == 10
|
|
@s.rb_cstr2inum("10", 16).should == 16
|
|
end
|
|
|
|
it "converts a C string to a Bignum given a base" do
|
|
@s.rb_cstr2inum(bignum_value.to_s, 10).should == bignum_value
|
|
end
|
|
|
|
it "converts a C string to a Fixnum non-strictly if base is not 0" do
|
|
@s.rb_cstr2inum("1234a", 10).should == 1234
|
|
end
|
|
|
|
it "converts a C string to a Fixnum strictly if base is 0" do
|
|
-> { @s.rb_cstr2inum("1234a", 0) }.should raise_error(ArgumentError)
|
|
end
|
|
end
|
|
|
|
describe "rb_cstr_to_inum" do
|
|
it "converts a C string to a Fixnum given a base" do
|
|
@s.rb_cstr_to_inum("1234", 10, true).should == 1234
|
|
end
|
|
|
|
it "converts a C string to a Bignum given a base" do
|
|
@s.rb_cstr_to_inum(bignum_value.to_s, 10, true).should == bignum_value
|
|
end
|
|
|
|
it "converts a C string to a Fixnum non-strictly" do
|
|
@s.rb_cstr_to_inum("1234a", 10, false).should == 1234
|
|
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_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")
|
|
end
|
|
end
|
|
|
|
describe "rb_str_substr" do
|
|
it "returns a substring" do
|
|
"hello".length.times do |time|
|
|
@s.rb_str_substr("hello", 0, time + 1).should == "hello"[0..time]
|
|
end
|
|
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"
|
|
@s.rb_str_to_str(CApiStringSpecs::ValidTostrTest.new).should == "ruby"
|
|
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)
|
|
end
|
|
end
|
|
|
|
describe "RSTRING_PTR" do
|
|
it "returns a pointer to the string's contents" do
|
|
str = "abc"
|
|
chars = []
|
|
@s.RSTRING_PTR_iterate(str) do |c|
|
|
chars << c
|
|
end
|
|
chars.should == [97, 98, 99]
|
|
end
|
|
|
|
it "allows changing the characters in the string" do
|
|
str = "abc"
|
|
@s.RSTRING_PTR_assign(str, 'A'.ord)
|
|
str.should == "AAA"
|
|
end
|
|
|
|
it "reflects changes after a rb_funcall" do
|
|
lamb = proc { |s| s.replace "NEW CONTENT" }
|
|
|
|
str = "beforebefore"
|
|
|
|
ret = @s.RSTRING_PTR_after_funcall(str, lamb)
|
|
|
|
str.should == "NEW CONTENT"
|
|
ret.should == str
|
|
end
|
|
|
|
it "reflects changes from native memory and from String#setbyte in bounds" do
|
|
str = "abc"
|
|
from_rstring_ptr = @s.RSTRING_PTR_after_yield(str) { str.setbyte(1, 'B'.ord) }
|
|
from_rstring_ptr.should == "1B2"
|
|
str.should == "1B2"
|
|
end
|
|
|
|
it "returns a pointer to the contents of encoded pointer-sized string" do
|
|
s = "70パク".
|
|
encode(Encoding::UTF_16LE).
|
|
force_encoding(Encoding::UTF_16LE).
|
|
encode(Encoding::UTF_8)
|
|
|
|
chars = []
|
|
@s.RSTRING_PTR_iterate(s) do |c|
|
|
chars << c
|
|
end
|
|
chars.should == [55, 48, 227, 131, 145, 227, 130, 175]
|
|
end
|
|
end
|
|
|
|
describe "RSTRING_LEN" do
|
|
it "returns the size of the string" do
|
|
@s.RSTRING_LEN("gumdrops").should == 8
|
|
end
|
|
end
|
|
|
|
describe "RSTRING_LENINT" do
|
|
it "returns the size of a string" do
|
|
@s.RSTRING_LENINT("silly").should == 5
|
|
end
|
|
end
|
|
|
|
describe :string_value_macro, shared: true do
|
|
before :each do
|
|
@s = CApiStringSpecs.new
|
|
end
|
|
|
|
it "does not call #to_str on a String" do
|
|
str = "genuine"
|
|
str.should_not_receive(:to_str)
|
|
@s.send(@method, str)
|
|
end
|
|
|
|
it "does not call #to_s on a String" do
|
|
str = "genuine"
|
|
str.should_not_receive(:to_str)
|
|
@s.send(@method, str)
|
|
end
|
|
|
|
it "calls #to_str on non-String objects" do
|
|
str = mock("fake")
|
|
str.should_receive(:to_str).and_return("wannabe")
|
|
@s.send(@method, str).should == "wannabe"
|
|
end
|
|
|
|
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)
|
|
end
|
|
end
|
|
|
|
describe "StringValue" do
|
|
it_behaves_like :string_value_macro, :StringValue
|
|
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
|
|
|
|
it_behaves_like :string_value_macro, :SafeStringValue
|
|
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_set_len(str, 3)
|
|
str.bytesize.should == 3
|
|
@s.RSTRING_LEN(str).should == 3
|
|
@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_modify_expand(str, 1024)
|
|
str.bytesize.should == 3
|
|
@s.RSTRING_LEN(str).should == 3
|
|
@s.rb_str_capacity(str).should == 1027
|
|
end
|
|
end
|
|
|
|
describe "rb_str_resize" do
|
|
it "reduces the size of the string" do
|
|
str = @s.rb_str_resize("test", 2)
|
|
str.size.should == 2
|
|
str.bytesize.should == 2
|
|
@s.RSTRING_LEN(str).should == 2
|
|
str.should == "te"
|
|
end
|
|
|
|
it "updates the string's attributes visible in C code" do
|
|
@s.rb_str_resize_RSTRING_LEN("test", 2).should == 2
|
|
end
|
|
|
|
it "increases the size of the string" do
|
|
expected = "test".force_encoding("US-ASCII")
|
|
str = @s.rb_str_resize(expected.dup, 12)
|
|
str.size.should == 12
|
|
str.bytesize.should == 12
|
|
@s.RSTRING_LEN(str).should == 12
|
|
str[0, 4].should == expected
|
|
end
|
|
end
|
|
|
|
describe "rb_str_inspect" do
|
|
it "returns the equivalent of calling #inspect on the String" do
|
|
@s.rb_str_inspect("value").should == %["value"]
|
|
end
|
|
end
|
|
|
|
describe "rb_str_intern" do
|
|
it "returns a symbol created from the string" do
|
|
@s.rb_str_intern("symbol").should == :symbol
|
|
end
|
|
|
|
it "returns a symbol even if passed an empty string" do
|
|
@s.rb_str_intern("").should == "".to_sym
|
|
end
|
|
|
|
it "returns a symbol even if the passed string contains NULL characters" do
|
|
@s.rb_str_intern("no\0no").should == "no\0no".to_sym
|
|
end
|
|
end
|
|
|
|
describe "rb_str_freeze" do
|
|
it "freezes the string" do
|
|
s = ""
|
|
@s.rb_str_freeze(s).should == s
|
|
s.frozen?.should be_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)
|
|
end
|
|
end
|
|
|
|
describe "rb_str_update" do
|
|
it "splices the replacement string into the original at the given location" do
|
|
@s.rb_str_update("hello", 2, 3, "wuh").should == "hewuh"
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "rb_str_free" do
|
|
# This spec only really exists to make sure the symbol
|
|
# 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
|
|
end
|
|
end
|
|
|
|
describe :rb_external_str_new, shared: true do
|
|
it "returns a String in the default external encoding" do
|
|
Encoding.default_external = "UTF-8"
|
|
@s.send(@method, "abc").encoding.should == Encoding::UTF_8
|
|
end
|
|
|
|
it "returns a binary encoded string if any non-ascii bytes are present and default external is US-ASCII" do
|
|
Encoding.default_external = "US-ASCII"
|
|
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
|
|
before :each do
|
|
@s = CApiStringSpecs.new
|
|
@external = Encoding.default_external
|
|
@internal = Encoding.default_internal
|
|
end
|
|
|
|
after :each do
|
|
Encoding.default_external = @external
|
|
Encoding.default_internal = @internal
|
|
end
|
|
|
|
describe "rb_str_length" do
|
|
it "returns the string's length" do
|
|
@s.rb_str_length("dewdrops").should == 8
|
|
end
|
|
|
|
it "counts characters in multi byte encodings" do
|
|
@s.rb_str_length("düwdrops").should == 8
|
|
end
|
|
end
|
|
|
|
describe "rb_str_equal" do
|
|
it "compares two same strings" do
|
|
s = "hello"
|
|
@s.rb_str_equal(s, "hello").should be_true
|
|
end
|
|
|
|
it "compares two different strings" do
|
|
s = "hello"
|
|
@s.rb_str_equal(s, "hella").should be_false
|
|
end
|
|
end
|
|
|
|
describe "rb_external_str_new" do
|
|
it_behaves_like :rb_external_str_new, :rb_external_str_new
|
|
end
|
|
|
|
describe "rb_external_str_new_cstr" do
|
|
it_behaves_like :rb_external_str_new, :rb_external_str_new_cstr
|
|
end
|
|
|
|
describe "rb_external_str_new_with_enc" do
|
|
it "returns a String in the specified encoding" do
|
|
s = @s.rb_external_str_new_with_enc("abc", 3, Encoding::UTF_8)
|
|
s.encoding.should == Encoding::UTF_8
|
|
end
|
|
|
|
it "returns a binary encoded String if any non-ascii bytes are present and the specified encoding is US-ASCII" do
|
|
x80 = [0x80].pack('C')
|
|
s = @s.rb_external_str_new_with_enc("#{x80}abc", 4, Encoding::US_ASCII)
|
|
s.encoding.should == Encoding::BINARY
|
|
end
|
|
|
|
|
|
# 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 = [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")
|
|
# + x = [0xA4, 0xA2, 0xA4, 0xEC].pack('C4')#.force_encoding('binary')
|
|
# + s.should == x
|
|
# s.encoding.should equal(Encoding::EUC_JP)
|
|
# end
|
|
|
|
it "transcodes a String to Encoding.default_internal if it is set" do
|
|
Encoding.default_internal = Encoding::EUC_JP
|
|
|
|
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)
|
|
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
|
|
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"))
|
|
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"))
|
|
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)
|
|
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)
|
|
end
|
|
|
|
it "returns a transcoded String" do
|
|
a = "\xE3\x81\x82\xE3\x82\x8C".force_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)
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
end
|
|
|
|
it "returns a transcoded String" do
|
|
a = "\xE3\x81\x82\xE3\x82\x8C".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)
|
|
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_opts(a, Encoding::US_ASCII,
|
|
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)
|
|
end
|
|
|
|
it "returns the origin String if the destination encoding is BINARY" do
|
|
a = "abc".force_encoding("binary")
|
|
@s.rb_str_conv_enc_opts(a, Encoding::US_ASCII,
|
|
Encoding::BINARY, 0, nil).should equal(a)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "rb_str_export" 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)
|
|
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"))
|
|
end
|
|
end
|
|
|
|
describe "rb_str_export_to_enc" do
|
|
it "returns a copy of an ascii string converted to the new encoding" do
|
|
source = "A simple string".encode(Encoding::US_ASCII)
|
|
result = @s.rb_str_export_to_enc(source, Encoding::UTF_8)
|
|
result.should == source.encode(Encoding::UTF_8)
|
|
result.encoding.should == Encoding::UTF_8
|
|
end
|
|
|
|
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)
|
|
end
|
|
|
|
it "does not alter 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)
|
|
source.bytes.should == [0, 255]
|
|
end
|
|
end
|
|
|
|
describe "rb_sprintf" do
|
|
it "replaces the parts like sprintf" do
|
|
@s.rb_sprintf1("Awesome %s is replaced", "string").should == "Awesome string is replaced"
|
|
@s.rb_sprintf1("%s", "TestFoobarTest").should == "TestFoobarTest"
|
|
end
|
|
|
|
it "accepts multiple arguments" do
|
|
s = "Awesome %s is here with %s"
|
|
@s.rb_sprintf2(s, "string", "content").should == "Awesome string is here with content"
|
|
end
|
|
|
|
it "formats a string VALUE using to_s if sign not specified in format" do
|
|
s = 'Result: A string.'
|
|
@s.rb_sprintf3(CApiStringSpecs::ToSOrInspect.new).should == s
|
|
end
|
|
|
|
it "formats a string VALUE using inspect if sign specified in format" do
|
|
s = 'Result: A different string.'
|
|
@s.rb_sprintf4(CApiStringSpecs::ToSOrInspect.new).should == s
|
|
end
|
|
|
|
it "formats a TrueClass VALUE as `TrueClass` if sign not specified in format" do
|
|
s = 'Result: TrueClass.'
|
|
@s.rb_sprintf3(true.class).should == s
|
|
end
|
|
|
|
it "formats a TrueClass VALUE as 'true' if sign specified in format" do
|
|
s = 'Result: true.'
|
|
@s.rb_sprintf4(true.class).should == s
|
|
end
|
|
end
|
|
|
|
describe "rb_vsprintf" do
|
|
it "returns a formatted String from a variable number of arguments" do
|
|
s = @s.rb_vsprintf("%s, %d, %.2f", "abc", 42, 2.7);
|
|
s.should == "abc, 42, 2.70"
|
|
end
|
|
end
|
|
|
|
describe "rb_String" do
|
|
it "returns the passed argument if it is a string" do
|
|
@s.rb_String("a").should == "a"
|
|
end
|
|
|
|
it "tries to convert the passed argument to a string by calling #to_str first" do
|
|
@s.rb_String(CApiStringSpecs::ValidTostrTest.new).should == "ruby"
|
|
end
|
|
|
|
it "raises a TypeError if #to_str does not return a string" do
|
|
-> { @s.rb_String(CApiStringSpecs::InvalidTostrTest.new) }.should raise_error(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"}'
|
|
end
|
|
end
|
|
|
|
describe "rb_string_value_cstr" do
|
|
it "returns a non-null pointer for a simple string" do
|
|
@s.rb_string_value_cstr("Hello").should == true
|
|
end
|
|
|
|
it "returns a non-null pointer for a UTF-16 string" do
|
|
@s.rb_string_value_cstr("Hello".encode('UTF-16BE')).should == true
|
|
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)
|
|
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)
|
|
end
|
|
|
|
end
|
|
|
|
describe "rb_str_drop_bytes" do
|
|
it "drops N characters for an ASCII string" do
|
|
str = "12345678".encode("US-ASCII")
|
|
@s.rb_str_drop_bytes(str, 4)
|
|
str.should == "5678".encode("US-ASCII")
|
|
end
|
|
|
|
it "drop N/2 characters for a UTF-16 string" do
|
|
str = "12345678".encode("UTF-16LE")
|
|
@s.rb_str_drop_bytes(str, 4)
|
|
str.should == "345678".encode("UTF-16LE")
|
|
end
|
|
|
|
it "drop N/4 characters for a UTF-32 string" do
|
|
str = "12345678".encode("UTF-32LE")
|
|
@s.rb_str_drop_bytes(str, 4)
|
|
str.should == "2345678".encode("UTF-32LE")
|
|
end
|
|
end
|
|
end
|