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

Move spec/rubyspec to spec/ruby for consistency

* Other ruby implementations use the spec/ruby directory.
  [Misc #13792] [ruby-core:82287]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59979 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
eregon 2017-09-20 20:18:52 +00:00
parent 75bfc6440d
commit 1d15d5f080
4370 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,19 @@
require File.expand_path('../../../spec_helper', __FILE__)
describe "String.allocate" do
it "returns an instance of String" do
str = String.allocate
str.should be_an_instance_of(String)
end
it "returns a fully-formed String" do
str = String.allocate
str.size.should == 0
str << "more"
str.should == "more"
end
it "returns a binary String" do
String.new.encoding.should == Encoding::BINARY
end
end

View file

@ -0,0 +1,8 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
require File.expand_path('../shared/concat', __FILE__)
describe "String#<<" do
it_behaves_like :string_concat, :<<
it_behaves_like :string_concat_encoding, :<<
end

View file

@ -0,0 +1,85 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
with_feature :encoding do
describe "String#ascii_only?" do
describe "with ASCII only characters" do
it "returns true if the encoding is UTF-8" do
[ ["hello", true],
["hello".encode('UTF-8'), true],
["hello".force_encoding('UTF-8'), true],
].should be_computed_by(:ascii_only?)
end
it "returns true if the encoding is US-ASCII" do
"hello".force_encoding(Encoding::US_ASCII).ascii_only?.should be_true
"hello".encode(Encoding::US_ASCII).ascii_only?.should be_true
end
it "returns true for all single-character UTF-8 Strings" do
0.upto(127) do |n|
n.chr.ascii_only?.should be_true
end
end
end
describe "with non-ASCII only characters" do
it "returns false if the encoding is ASCII-8BIT" do
chr = 128.chr
chr.encoding.should == Encoding::ASCII_8BIT
chr.ascii_only?.should be_false
end
it "returns false if the String contains any non-ASCII characters" do
[ ["\u{6666}", false],
["hello, \u{6666}", false],
["\u{6666}".encode('UTF-8'), false],
["\u{6666}".force_encoding('UTF-8'), false],
].should be_computed_by(:ascii_only?)
end
it "returns false if the encoding is US-ASCII" do
[ ["\u{6666}".force_encoding(Encoding::US_ASCII), false],
["hello, \u{6666}".force_encoding(Encoding::US_ASCII), false],
].should be_computed_by(:ascii_only?)
end
end
it "returns true for the empty String with an ASCII-compatible encoding" do
"".ascii_only?.should be_true
"".encode('UTF-8').ascii_only?.should be_true
end
it "returns false for the empty String with a non-ASCII-compatible encoding" do
"".force_encoding('UTF-16LE').ascii_only?.should be_false
"".encode('UTF-16BE').ascii_only?.should be_false
end
it "returns false for a non-empty String with non-ASCII-compatible encoding" do
"\x78\x00".force_encoding("UTF-16LE").ascii_only?.should be_false
end
it "returns false when interpolating non ascii strings" do
base = "EU currency is"
base.force_encoding(Encoding::US_ASCII)
euro = "\u20AC"
interp = "#{base} #{euro}"
euro.ascii_only?.should be_false
base.ascii_only?.should be_true
interp.ascii_only?.should be_false
end
it "returns false after appending non ASCII characters to an empty String" do
("" << "λ").ascii_only?.should be_false
end
it "returns false when concatenating an ASCII and non-ASCII String" do
"".concat("λ").ascii_only?.should be_false
end
it "returns false when replacing an ASCII String with a non-ASCII String" do
"".replace("λ").ascii_only?.should be_false
end
end
end

View file

@ -0,0 +1,24 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
describe "String#b" do
with_feature :encoding do
it "returns an ASCII-8BIT encoded string" do
"Hello".b.should == "Hello".force_encoding(Encoding::ASCII_8BIT)
"こんちには".b.should == "こんちには".force_encoding(Encoding::ASCII_8BIT)
end
it "returns new string without modifying self" do
str = "こんちには"
str.b.should_not equal(str)
str.should == "こんちには"
end
it "copies own tainted/untrusted status to the returning value" do
utf_8 = "こんちには".taint.untrust
ret = utf_8.b
ret.tainted?.should be_true
ret.untrusted?.should be_true
end
end
end

View file

@ -0,0 +1,57 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
describe "String#bytes" do
before :each do
@utf8 = "東京"
@ascii = 'Tokyo'
@utf8_ascii = @utf8 + @ascii
end
it "returns an Array when no block is given" do
@utf8.bytes.should be_an_instance_of(Array)
end
it "yields each byte to a block if one is given, returning self" do
bytes = []
@utf8.bytes {|b| bytes << b}.should == @utf8
bytes.should == @utf8.bytes.to_a
end
it "returns #bytesize bytes" do
@utf8_ascii.bytes.to_a.size.should == @utf8_ascii.bytesize
end
it "returns bytes as Fixnums" do
@ascii.bytes.to_a.each {|b| b.should be_an_instance_of(Fixnum)}
@utf8_ascii.bytes { |b| b.should be_an_instance_of(Fixnum) }
end
it "agrees with #unpack('C*')" do
@utf8_ascii.bytes.to_a.should == @utf8_ascii.unpack("C*")
end
it "yields/returns no bytes for the empty string" do
''.bytes.to_a.should == []
end
end
with_feature :encoding do
describe "String#bytes" do
before :each do
@utf8 = "東京"
@ascii = 'Tokyo'
@utf8_ascii = @utf8 + @ascii
end
it "agrees with #getbyte" do
@utf8_ascii.bytes.to_a.each_with_index do |byte,index|
byte.should == @utf8_ascii.getbyte(index)
end
end
it "is unaffected by #force_encoding" do
@utf8.force_encoding('ASCII').bytes.to_a.should == @utf8.bytes.to_a
end
end
end

View file

@ -0,0 +1,37 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
with_feature :encoding do
describe "#String#bytesize" do
it "needs to be reviewed for spec completeness"
it "returns the length of self in bytes" do
"hello".bytesize.should == 5
" ".bytesize.should == 1
end
it "works with strings containing single UTF-8 characters" do
"\u{6666}".bytesize.should == 3
end
it "works with pseudo-ASCII strings containing single UTF-8 characters" do
"\u{6666}".force_encoding('ASCII').bytesize.should == 3
end
it "works with strings containing UTF-8 characters" do
"c \u{6666}".force_encoding('UTF-8').bytesize.should == 5
"c \u{6666}".bytesize.should == 5
end
it "works with pseudo-ASCII strings containing UTF-8 characters" do
"c \u{6666}".force_encoding('ASCII').bytesize.should == 5
end
it "returns 0 for the empty string" do
"".bytesize.should == 0
"".force_encoding('ASCII').bytesize.should == 0
"".force_encoding('UTF-8').bytesize.should == 0
end
end
end

View file

@ -0,0 +1,29 @@
# -*- encoding: binary -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
require File.expand_path('../shared/slice.rb', __FILE__)
describe "String#byteslice" do
it "needs to reviewed for spec completeness"
it_behaves_like :string_slice, :byteslice
end
describe "String#byteslice with index, length" do
it_behaves_like :string_slice_index_length, :byteslice
end
describe "String#byteslice with Range" do
it_behaves_like :string_slice_range, :byteslice
end
with_feature :encoding do
describe "String#byteslice on on non ASCII strings" do
it "returns byteslice of unicode strings" do
"\u3042".byteslice(1).should == "\x81".force_encoding("UTF-8")
"\u3042".byteslice(1, 2).should == "\x81\x82".force_encoding("UTF-8")
"\u3042".byteslice(1..2).should == "\x81\x82".force_encoding("UTF-8")
"\u3042".byteslice(-1).should == "\x82".force_encoding("UTF-8")
end
end
end

View file

@ -0,0 +1,62 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#capitalize" do
it "returns a copy of self with the first character converted to uppercase and the remainder to lowercase" do
"".capitalize.should == ""
"h".capitalize.should == "H"
"H".capitalize.should == "H"
"hello".capitalize.should == "Hello"
"HELLO".capitalize.should == "Hello"
"123ABC".capitalize.should == "123abc"
end
it "taints resulting string when self is tainted" do
"".taint.capitalize.tainted?.should == true
"hello".taint.capitalize.tainted?.should == true
end
ruby_version_is ''...'2.4' do
it "is locale insensitive (only upcases a-z and only downcases A-Z)" do
"ÄÖÜ".capitalize.should == "ÄÖÜ"
"ärger".capitalize.should == "ärger"
"BÄR".capitalize.should == "BÄr"
end
end
ruby_version_is '2.4' do
it "works for all of Unicode" do
"äöü".capitalize.should == "Äöü"
end
end
it "returns subclass instances when called on a subclass" do
StringSpecs::MyString.new("hello").capitalize.should be_an_instance_of(StringSpecs::MyString)
StringSpecs::MyString.new("Hello").capitalize.should be_an_instance_of(StringSpecs::MyString)
end
end
describe "String#capitalize!" do
it "capitalizes self in place" do
a = "hello"
a.capitalize!.should equal(a)
a.should == "Hello"
end
it "returns nil when no changes are made" do
a = "Hello"
a.capitalize!.should == nil
a.should == "Hello"
"".capitalize!.should == nil
"H".capitalize!.should == nil
end
it "raises a RuntimeError when self is frozen" do
["", "Hello", "hello"].each do |a|
a.freeze
lambda { a.capitalize! }.should raise_error(RuntimeError)
end
end
end

View file

@ -0,0 +1,8 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../shared/eql', __FILE__)
require File.expand_path('../shared/equal_value', __FILE__)
describe "String#===" do
it_behaves_like(:string_eql_value, :===)
it_behaves_like(:string_equal_value, :===)
end

View file

@ -0,0 +1,120 @@
# -*- encoding: ascii-8bit -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#casecmp independent of case" do
it "returns -1 when less than other" do
"a".casecmp("b").should == -1
"A".casecmp("b").should == -1
end
it "returns 0 when equal to other" do
"a".casecmp("a").should == 0
"A".casecmp("a").should == 0
end
it "returns 1 when greater than other" do
"b".casecmp("a").should == 1
"B".casecmp("a").should == 1
end
it "tries to convert other to string using to_str" do
other = mock('x')
other.should_receive(:to_str).and_return("abc")
"abc".casecmp(other).should == 0
end
ruby_version_is ""..."2.5" do
it "raises a TypeError if other can't be converted to a string" do
lambda { "abc".casecmp(mock('abc')) }.should raise_error(TypeError)
end
end
ruby_version_is "2.5" do
it "returns nil if other can't be converted to a string" do
"abc".casecmp(mock('abc')).should be_nil
end
end
describe "in UTF-8 mode" do
describe "for non-ASCII characters" do
before :each do
@upper_a_tilde = "\xc3\x83"
@lower_a_tilde = "\xc3\xa3"
@upper_a_umlaut = "\xc3\x84"
@lower_a_umlaut = "\xc3\xa4"
end
it "returns -1 when numerically less than other" do
@upper_a_tilde.casecmp(@lower_a_tilde).should == -1
@upper_a_tilde.casecmp(@upper_a_umlaut).should == -1
end
it "returns 0 when numerically equal to other" do
@upper_a_tilde.casecmp(@upper_a_tilde).should == 0
end
it "returns 1 when numerically greater than other" do
@lower_a_umlaut.casecmp(@upper_a_umlaut).should == 1
@lower_a_umlaut.casecmp(@lower_a_tilde).should == 1
end
end
describe "for ASCII characters" do
it "returns -1 when less than other" do
"a".casecmp("b").should == -1
"A".casecmp("b").should == -1
end
it "returns 0 when equal to other" do
"a".casecmp("a").should == 0
"A".casecmp("a").should == 0
end
it "returns 1 when greater than other" do
"b".casecmp("a").should == 1
"B".casecmp("a").should == 1
end
end
end
describe "for non-ASCII characters" do
before :each do
@upper_a_tilde = "\xc3"
@lower_a_tilde = "\xe3"
end
it "returns -1 when numerically less than other" do
@upper_a_tilde.casecmp(@lower_a_tilde).should == -1
end
it "returns 0 when equal to other" do
@upper_a_tilde.casecmp("\xc3").should == 0
end
it "returns 1 when numerically greater than other" do
@lower_a_tilde.casecmp(@upper_a_tilde).should == 1
end
end
describe "when comparing a subclass instance" do
it "returns -1 when less than other" do
b = StringSpecs::MyString.new "b"
"a".casecmp(b).should == -1
"A".casecmp(b).should == -1
end
it "returns 0 when equal to other" do
a = StringSpecs::MyString.new "a"
"a".casecmp(a).should == 0
"A".casecmp(a).should == 0
end
it "returns 1 when greater than other" do
a = StringSpecs::MyString.new "a"
"b".casecmp(a).should == 1
"B".casecmp(a).should == 1
end
end
end

View file

@ -0,0 +1,133 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#center with length, padding" do
it "returns a new string of specified length with self centered and padded with padstr" do
"one".center(9, '.').should == "...one..."
"hello".center(20, '123').should == "1231231hello12312312"
"middle".center(13, '-').should == "---middle----"
"".center(1, "abcd").should == "a"
"".center(2, "abcd").should == "aa"
"".center(3, "abcd").should == "aab"
"".center(4, "abcd").should == "abab"
"".center(6, "xy").should == "xyxxyx"
"".center(11, "12345").should == "12345123451"
"|".center(2, "abcd").should == "|a"
"|".center(3, "abcd").should == "a|a"
"|".center(4, "abcd").should == "a|ab"
"|".center(5, "abcd").should == "ab|ab"
"|".center(6, "xy").should == "xy|xyx"
"|".center(7, "xy").should == "xyx|xyx"
"|".center(11, "12345").should == "12345|12345"
"|".center(12, "12345").should == "12345|123451"
"||".center(3, "abcd").should == "||a"
"||".center(4, "abcd").should == "a||a"
"||".center(5, "abcd").should == "a||ab"
"||".center(6, "abcd").should == "ab||ab"
"||".center(8, "xy").should == "xyx||xyx"
"||".center(12, "12345").should == "12345||12345"
"||".center(13, "12345").should == "12345||123451"
end
it "pads with whitespace if no padstr is given" do
"two".center(5).should == " two "
"hello".center(20).should == " hello "
end
it "returns self if it's longer than or as long as the specified length" do
"".center(0).should == ""
"".center(-1).should == ""
"hello".center(4).should == "hello"
"hello".center(-1).should == "hello"
"this".center(3).should == "this"
"radiology".center(8, '-').should == "radiology"
end
it "taints result when self or padstr is tainted" do
"x".taint.center(4).tainted?.should == true
"x".taint.center(0).tainted?.should == true
"".taint.center(0).tainted?.should == true
"x".taint.center(4, "*").tainted?.should == true
"x".center(4, "*".taint).tainted?.should == true
end
it "calls #to_int to convert length to an integer" do
"_".center(3.8, "^").should == "^_^"
obj = mock('3')
obj.should_receive(:to_int).and_return(3)
"_".center(obj, "o").should == "o_o"
end
it "raises a TypeError when length can't be converted to an integer" do
lambda { "hello".center("x") }.should raise_error(TypeError)
lambda { "hello".center("x", "y") }.should raise_error(TypeError)
lambda { "hello".center([]) }.should raise_error(TypeError)
lambda { "hello".center(mock('x')) }.should raise_error(TypeError)
end
it "calls #to_str to convert padstr to a String" do
padstr = mock('123')
padstr.should_receive(:to_str).and_return("123")
"hello".center(20, padstr).should == "1231231hello12312312"
end
it "raises a TypeError when padstr can't be converted to a string" do
lambda { "hello".center(20, 100) }.should raise_error(TypeError)
lambda { "hello".center(20, []) }.should raise_error(TypeError)
lambda { "hello".center(20, mock('x')) }.should raise_error(TypeError)
end
it "raises an ArgumentError if padstr is empty" do
lambda { "hello".center(10, "") }.should raise_error(ArgumentError)
lambda { "hello".center(0, "") }.should raise_error(ArgumentError)
end
it "returns subclass instances when called on subclasses" do
StringSpecs::MyString.new("").center(10).should be_an_instance_of(StringSpecs::MyString)
StringSpecs::MyString.new("foo").center(10).should be_an_instance_of(StringSpecs::MyString)
StringSpecs::MyString.new("foo").center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString)
"".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
"foo".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
end
it "when padding is tainted and self is untainted returns a tainted string if and only if length is longer than self" do
"hello".center(4, 'X'.taint).tainted?.should be_false
"hello".center(5, 'X'.taint).tainted?.should be_false
"hello".center(6, 'X'.taint).tainted?.should be_true
end
with_feature :encoding do
describe "with width" do
it "returns a String in the same encoding as the original" do
str = "abc".force_encoding Encoding::IBM437
result = str.center 6
result.should == " abc "
result.encoding.should equal(Encoding::IBM437)
end
end
describe "with width, pattern" do
it "returns a String in the compatible encoding" do
str = "abc".force_encoding Encoding::IBM437
result = str.center 6, ""
result.should == "あabcああ"
result.encoding.should equal(Encoding::UTF_8)
end
it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
pat = "".encode Encoding::EUC_JP
lambda do
"あれ".center 5, pat
end.should raise_error(Encoding::CompatibilityError)
end
end
end
end

View file

@ -0,0 +1,11 @@
require File.expand_path('../shared/chars', __FILE__)
require File.expand_path('../shared/each_char_without_block', __FILE__)
describe "String#chars" do
it_behaves_like(:string_chars, :chars)
it "returns an array when no block given" do
ary = "hello".send(@method)
ary.should == ['h', 'e', 'l', 'l', 'o']
end
end

View file

@ -0,0 +1,387 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#chomp" do
describe "when passed no argument" do
before do
# Ensure that $/ is set to the default value
@dollar_slash, $/ = $/, "\n"
end
after do
$/ = @dollar_slash
end
it "does not modify a String with no trailing carriage return or newline" do
"abc".chomp.should == "abc"
end
it "returns a copy of the String when it is not modified" do
str = "abc"
str.chomp.should_not equal(str)
end
it "removes one trailing newline" do
"abc\n\n".chomp.should == "abc\n"
end
it "removes one trailing carriage return" do
"abc\r\r".chomp.should == "abc\r"
end
it "removes one trailing carrige return, newline pair" do
"abc\r\n\r\n".chomp.should == "abc\r\n"
end
it "returns an empty String when self is empty" do
"".chomp.should == ""
end
it "taints the result if self is tainted" do
"abc".taint.chomp.tainted?.should be_true
end
it "returns subclass instances when called on a subclass" do
str = StringSpecs::MyString.new("hello\n").chomp
str.should be_an_instance_of(StringSpecs::MyString)
end
it "removes trailing characters that match $/ when it has been assigned a value" do
$/ = "cdef"
"abcdef".chomp.should == "ab"
end
end
describe "when passed nil" do
it "does not modify the String" do
"abc\r\n".chomp(nil).should == "abc\r\n"
end
it "returns a copy of the String" do
str = "abc"
str.chomp(nil).should_not equal(str)
end
it "taints the result if self is tainted" do
"abc".taint.chomp(nil).tainted?.should be_true
end
it "returns an empty String when self is empty" do
"".chomp(nil).should == ""
end
end
describe "when passed ''" do
it "removes a final newline" do
"abc\n".chomp("").should == "abc"
end
it "removes a final carriage return, newline" do
"abc\r\n".chomp("").should == "abc"
end
it "does not remove a final carriage return" do
"abc\r".chomp("").should == "abc\r"
end
it "removes more than one trailing newlines" do
"abc\n\n\n".chomp("").should == "abc"
end
it "removes more than one trailing carriage return, newline pairs" do
"abc\r\n\r\n\r\n".chomp("").should == "abc"
end
it "taints the result if self is tainted" do
"abc".taint.chomp("").tainted?.should be_true
end
it "returns an empty String when self is empty" do
"".chomp("").should == ""
end
end
describe "when passed '\\n'" do
it "removes one trailing newline" do
"abc\n\n".chomp("\n").should == "abc\n"
end
it "removes one trailing carriage return" do
"abc\r\r".chomp("\n").should == "abc\r"
end
it "removes one trailing carrige return, newline pair" do
"abc\r\n\r\n".chomp("\n").should == "abc\r\n"
end
it "taints the result if self is tainted" do
"abc".taint.chomp("\n").tainted?.should be_true
end
it "returns an empty String when self is empty" do
"".chomp("\n").should == ""
end
end
describe "when passed an Object" do
it "calls #to_str to convert to a String" do
arg = mock("string chomp")
arg.should_receive(:to_str).and_return("bc")
"abc".chomp(arg).should == "a"
end
it "raises a TypeError if #to_str does not return a String" do
arg = mock("string chomp")
arg.should_receive(:to_str).and_return(1)
lambda { "abc".chomp(arg) }.should raise_error(TypeError)
end
end
describe "when passed a String" do
it "removes the trailing characters if they match the argument" do
"abcabc".chomp("abc").should == "abc"
end
it "does not modify the String if the argument does not match the trailing characters" do
"abc".chomp("def").should == "abc"
end
it "returns an empty String when self is empty" do
"".chomp("abc").should == ""
end
it "taints the result if self is tainted" do
"abc".taint.chomp("abc").tainted?.should be_true
end
it "does not taint the result when the argument is tainted" do
"abc".chomp("abc".taint).tainted?.should be_false
end
end
end
describe "String#chomp!" do
describe "when passed no argument" do
before do
# Ensure that $/ is set to the default value
@dollar_slash, $/ = $/, "\n"
end
after do
$/ = @dollar_slash
end
it "modifies self" do
str = "abc\n"
str.chomp!.should equal(str)
end
it "returns nil if self is not modified" do
"abc".chomp!.should be_nil
end
it "removes one trailing newline" do
"abc\n\n".chomp!.should == "abc\n"
end
it "removes one trailing carriage return" do
"abc\r\r".chomp!.should == "abc\r"
end
it "removes one trailing carrige return, newline pair" do
"abc\r\n\r\n".chomp!.should == "abc\r\n"
end
it "returns nil when self is empty" do
"".chomp!.should be_nil
end
it "taints the result if self is tainted" do
"abc\n".taint.chomp!.tainted?.should be_true
end
it "returns subclass instances when called on a subclass" do
str = StringSpecs::MyString.new("hello\n").chomp!
str.should be_an_instance_of(StringSpecs::MyString)
end
it "removes trailing characters that match $/ when it has been assigned a value" do
$/ = "cdef"
"abcdef".chomp!.should == "ab"
end
end
describe "when passed nil" do
it "returns nil" do
"abc\r\n".chomp!(nil).should be_nil
end
it "returns nil when self is empty" do
"".chomp!(nil).should be_nil
end
end
describe "when passed ''" do
it "removes a final newline" do
"abc\n".chomp!("").should == "abc"
end
it "removes a final carriage return, newline" do
"abc\r\n".chomp!("").should == "abc"
end
it "does not remove a final carriage return" do
"abc\r".chomp!("").should be_nil
end
it "removes more than one trailing newlines" do
"abc\n\n\n".chomp!("").should == "abc"
end
it "removes more than one trailing carriage return, newline pairs" do
"abc\r\n\r\n\r\n".chomp!("").should == "abc"
end
it "taints the result if self is tainted" do
"abc\n".taint.chomp!("").tainted?.should be_true
end
it "returns nil when self is empty" do
"".chomp!("").should be_nil
end
end
describe "when passed '\\n'" do
it "removes one trailing newline" do
"abc\n\n".chomp!("\n").should == "abc\n"
end
it "removes one trailing carriage return" do
"abc\r\r".chomp!("\n").should == "abc\r"
end
it "removes one trailing carrige return, newline pair" do
"abc\r\n\r\n".chomp!("\n").should == "abc\r\n"
end
it "taints the result if self is tainted" do
"abc\n".taint.chomp!("\n").tainted?.should be_true
end
it "returns nil when self is empty" do
"".chomp!("\n").should be_nil
end
end
describe "when passed an Object" do
it "calls #to_str to convert to a String" do
arg = mock("string chomp")
arg.should_receive(:to_str).and_return("bc")
"abc".chomp!(arg).should == "a"
end
it "raises a TypeError if #to_str does not return a String" do
arg = mock("string chomp")
arg.should_receive(:to_str).and_return(1)
lambda { "abc".chomp!(arg) }.should raise_error(TypeError)
end
end
describe "when passed a String" do
it "removes the trailing characters if they match the argument" do
"abcabc".chomp!("abc").should == "abc"
end
it "returns nil if the argument does not match the trailing characters" do
"abc".chomp!("def").should be_nil
end
it "returns nil when self is empty" do
"".chomp!("abc").should be_nil
end
it "taints the result if self is tainted" do
"abc".taint.chomp!("abc").tainted?.should be_true
end
it "does not taint the result when the argument is tainted" do
"abc".chomp!("abc".taint).tainted?.should be_false
end
end
it "raises a RuntimeError on a frozen instance when it is modified" do
a = "string\n\r"
a.freeze
lambda { a.chomp! }.should raise_error(RuntimeError)
end
# see [ruby-core:23666]
it "raises a RuntimeError on a frozen instance when it would not be modified" do
a = "string\n\r"
a.freeze
lambda { a.chomp!(nil) }.should raise_error(RuntimeError)
lambda { a.chomp!("x") }.should raise_error(RuntimeError)
end
end
with_feature :encoding do
describe "String#chomp" do
before :each do
@before_separator = $/
end
after :each do
$/ = @before_separator
end
it "does not modify a multi-byte character" do
"あれ".chomp.should == "あれ"
end
it "removes the final carriage return, newline from a multibyte String" do
"あれ\r\n".chomp.should == "あれ"
end
it "removes the final carriage return, newline from a non-ASCII String" do
str = "abc\r\n".encode "utf-32be"
str.chomp.should == "abc".encode("utf-32be")
end
it "removes the final carriage return, newline from a non-ASCII String when the record separator is changed" do
$/ = "\n".encode("utf-8")
str = "abc\r\n".encode "utf-32be"
str.chomp.should == "abc".encode("utf-32be")
end
end
describe "String#chomp!" do
before :each do
@before_separator = $/
end
after :each do
$/ = @before_separator
end
it "returns nil when the String is not modified" do
"あれ".chomp!.should be_nil
end
it "removes the final carriage return, newline from a multibyte String" do
"あれ\r\n".chomp!.should == "あれ"
end
it "removes the final carriage return, newline from a non-ASCII String" do
str = "abc\r\n".encode "utf-32be"
str.chomp!.should == "abc".encode("utf-32be")
end
it "removes the final carriage return, newline from a non-ASCII String when the record separator is changed" do
$/ = "\n".encode("utf-8")
str = "abc\r\n".encode "utf-32be"
str.chomp!.should == "abc".encode("utf-32be")
end
end
end

View file

@ -0,0 +1,128 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#chop" do
it "removes the final character" do
"abc".chop.should == "ab"
end
it "removes the final carriage return" do
"abc\r".chop.should == "abc"
end
it "removes the final newline" do
"abc\n".chop.should == "abc"
end
it "removes the final carriage return, newline" do
"abc\r\n".chop.should == "abc"
end
it "removes the carrige return, newline if they are the only characters" do
"\r\n".chop.should == ""
end
it "does not remove more than the final carriage return, newline" do
"abc\r\n\r\n".chop.should == "abc\r\n"
end
with_feature :encoding do
it "removes a multi-byte character" do
"あれ".chop.should == ""
end
it "removes the final carriage return, newline from a multibyte String" do
"あれ\r\n".chop.should == "あれ"
end
it "removes the final carriage return, newline from a non-ASCII String" do
str = "abc\r\n".encode "utf-32be"
str.chop.should == "abc".encode("utf-32be")
end
end
it "returns an empty string when applied to an empty string" do
"".chop.should == ""
end
it "returns a new string when applied to an empty string" do
s = ""
s.chop.should_not equal(s)
end
it "taints result when self is tainted" do
"hello".taint.chop.tainted?.should == true
"".taint.chop.tainted?.should == true
end
it "untrusts result when self is untrusted" do
"hello".untrust.chop.untrusted?.should == true
"".untrust.chop.untrusted?.should == true
end
it "returns subclass instances when called on a subclass" do
StringSpecs::MyString.new("hello\n").chop.should be_an_instance_of(StringSpecs::MyString)
end
end
describe "String#chop!" do
it "removes the final character" do
"abc".chop!.should == "ab"
end
it "removes the final carriage return" do
"abc\r".chop!.should == "abc"
end
it "removes the final newline" do
"abc\n".chop!.should == "abc"
end
it "removes the final carriage return, newline" do
"abc\r\n".chop!.should == "abc"
end
it "removes the carrige return, newline if they are the only characters" do
"\r\n".chop!.should == ""
end
it "does not remove more than the final carriage return, newline" do
"abc\r\n\r\n".chop!.should == "abc\r\n"
end
with_feature :encoding do
it "removes a multi-byte character" do
"あれ".chop!.should == ""
end
it "removes the final carriage return, newline from a multibyte String" do
"あれ\r\n".chop!.should == "あれ"
end
it "removes the final carriage return, newline from a non-ASCII String" do
str = "abc\r\n".encode "utf-32be"
str.chop!.should == "abc".encode("utf-32be")
end
end
it "returns self if modifications were made" do
str = "hello"
str.chop!.should equal(str)
end
it "returns nil when called on an empty string" do
"".chop!.should be_nil
end
it "raises a RuntimeError on a frozen instance that is modified" do
lambda { "string\n\r".freeze.chop! }.should raise_error(RuntimeError)
end
# see [ruby-core:23666]
it "raises a RuntimeError on a frozen instance that would not be modified" do
a = ""
a.freeze
lambda { a.chop! }.should raise_error(RuntimeError)
end
end

View file

@ -0,0 +1,44 @@
require File.expand_path('../../../spec_helper', __FILE__)
with_feature :encoding do
describe "String#chr" do
it "returns a copy of self" do
s = 'e'
s.object_id.should_not == s.chr.object_id
end
it "returns a String" do
'glark'.chr.should be_an_instance_of(String)
end
it "returns an empty String if self is an empty String" do
"".chr.should == ""
end
it "returns a 1-character String" do
"glark".chr.size.should == 1
end
it "returns the character at the start of the String" do
"Goodbye, world".chr.should == "G"
end
it "returns a String in the same encoding as self" do
"\x24".encode(Encoding::US_ASCII).chr.encoding.should == Encoding::US_ASCII
end
it "understands multi-byte characters" do
s = "\u{9879}"
s.bytesize.should == 3
s.chr.should == s
end
it "understands Strings that contain a mixture of character widths" do
three = "\u{8082}"
three.bytesize.should == 3
four = "\u{77082}"
four.bytesize.should == 4
"#{three}#{four}".chr.should == three
end
end
end

View file

@ -0,0 +1,39 @@
require File.expand_path('../../../spec_helper', __FILE__)
with_feature :encoding do
describe "String#clear" do
before :each do
@s = "Jolene"
end
it "sets self equal to the empty String" do
@s.clear
@s.should == ""
end
it "returns self after emptying it" do
cleared = @s.clear
cleared.should == ""
cleared.object_id.should == @s.object_id
end
it "preserves its encoding" do
@s.encode!(Encoding::SHIFT_JIS)
@s.encoding.should == Encoding::SHIFT_JIS
@s.clear.encoding.should == Encoding::SHIFT_JIS
@s.encoding.should == Encoding::SHIFT_JIS
end
it "works with multibyte Strings" do
s = "\u{9765}\u{876}"
s.clear
s.should == ""
end
it "raises a RuntimeError if self is frozen" do
@s.freeze
lambda { @s.clear }.should raise_error(RuntimeError)
lambda { "".freeze.clear }.should raise_error(RuntimeError)
end
end
end

View file

@ -0,0 +1,58 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
describe "String#clone" do
before :each do
ScratchPad.clear
@obj = StringSpecs::InitializeString.new "string"
end
it "calls #initialize_copy on the new instance" do
clone = @obj.clone
ScratchPad.recorded.should_not == @obj.object_id
ScratchPad.recorded.should == clone.object_id
end
it "copies instance variables" do
clone = @obj.clone
clone.ivar.should == 1
end
it "copies singleton methods" do
def @obj.special() :the_one end
clone = @obj.clone
clone.special.should == :the_one
end
it "copies modules included in the singleton class" do
class << @obj
include StringSpecs::StringModule
end
clone = @obj.clone
clone.repr.should == 1
end
it "copies constants defined in the singleton class" do
class << @obj
CLONE = :clone
end
clone = @obj.clone
(class << clone; CLONE; end).should == :clone
end
it "copies frozen state" do
@obj.freeze.clone.frozen?.should be_true
"".freeze.clone.frozen?.should be_true
end
it "does not modify the original string when changing cloned string" do
orig = "string"[0..100]
clone = orig.clone
orig[0] = 'x'
orig.should == "xtring"
clone.should == "string"
end
end

View file

@ -0,0 +1,20 @@
# -*- encoding: binary -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../shared/codepoints', __FILE__)
require File.expand_path('../shared/each_codepoint_without_block', __FILE__)
with_feature :encoding do
describe "String#codepoints" do
it_behaves_like(:string_codepoints, :codepoints)
it "returns an Array when no block is given" do
"abc".send(@method).should == [?a.ord, ?b.ord, ?c.ord]
end
it "raises an ArgumentError when no block is given if self has an invalid encoding" do
s = "\xDF".force_encoding(Encoding::UTF_8)
s.valid_encoding?.should be_false
lambda {s.send(@method)}.should raise_error(ArgumentError)
end
end
end

View file

@ -0,0 +1,108 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#<=> with String" do
it "compares individual characters based on their ascii value" do
ascii_order = Array.new(256) { |x| x.chr }
sort_order = ascii_order.sort
sort_order.should == ascii_order
end
it "returns -1 when self is less than other" do
("this" <=> "those").should == -1
end
it "returns 0 when self is equal to other" do
("yep" <=> "yep").should == 0
end
it "returns 1 when self is greater than other" do
("yoddle" <=> "griddle").should == 1
end
it "considers string that comes lexicographically first to be less if strings have same size" do
("aba" <=> "abc").should == -1
("abc" <=> "aba").should == 1
end
it "doesn't consider shorter string to be less if longer string starts with shorter one" do
("abc" <=> "abcd").should == -1
("abcd" <=> "abc").should == 1
end
it "compares shorter string with corresponding number of first chars of longer string" do
("abx" <=> "abcd").should == 1
("abcd" <=> "abx").should == -1
end
it "ignores subclass differences" do
a = "hello"
b = StringSpecs::MyString.new("hello")
(a <=> b).should == 0
(b <=> a).should == 0
end
it "returns 0 if self and other are bytewise identical and have the same encoding" do
("ÄÖÜ" <=> "ÄÖÜ").should == 0
end
it "returns 0 if self and other are bytewise identical and have the same encoding" do
("ÄÖÜ" <=> "ÄÖÜ").should == 0
end
it "returns -1 if self is bytewise less than other" do
("ÄÖÛ" <=> "ÄÖÜ").should == -1
end
it "returns 1 if self is bytewise greater than other" do
("ÄÖÜ" <=> "ÄÖÛ").should == 1
end
it "ignores encoding difference" do
("ÄÖÛ".force_encoding("utf-8") <=> "ÄÖÜ".force_encoding("iso-8859-1")).should == -1
("ÄÖÜ".force_encoding("utf-8") <=> "ÄÖÛ".force_encoding("iso-8859-1")).should == 1
end
it "returns 0 with identical ASCII-compatible bytes of different encodings" do
("abc".force_encoding("utf-8") <=> "abc".force_encoding("iso-8859-1")).should == 0
end
it "compares the indices of the encodings when the strings have identical non-ASCII-compatible bytes" do
xff_1 = [0xFF].pack('C').force_encoding("utf-8")
xff_2 = [0xFF].pack('C').force_encoding("iso-8859-1")
(xff_1 <=> xff_2).should == -1
(xff_2 <=> xff_1).should == 1
end
end
# Note: This is inconsistent with Array#<=> which calls #to_ary instead of
# just using it as an indicator.
describe "String#<=>" do
it "returns nil if its argument provides neither #to_str nor #<=>" do
("abc" <=> mock('x')).should be_nil
end
it "uses the result of calling #to_str for comparison when #to_str is defined" do
obj = mock('x')
obj.should_receive(:to_str).and_return("aaa")
("abc" <=> obj).should == 1
end
it "uses the result of calling #<=> on its argument when #<=> is defined but #to_str is not" do
obj = mock('x')
obj.should_receive(:<=>).and_return(-1)
("abc" <=> obj).should == 1
end
it "returns nil if argument also uses an inverse comparison for <=>" do
obj = mock('x')
def obj.<=>(other); other <=> self; end
obj.should_receive(:<=>).once
("abc" <=> obj).should be_nil
end
end

View file

@ -0,0 +1,28 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
require File.expand_path('../shared/concat', __FILE__)
describe "String#concat" do
it_behaves_like :string_concat, :concat
it_behaves_like :string_concat_encoding, :concat
ruby_version_is "2.4" do
it "takes multiple arguments" do
str = "hello "
str.concat "wo", "", "rld"
str.should == "hello world"
end
it "concatenates the initial value when given arguments contain 2 self" do
str = "hello"
str.concat str, str
str.should == "hellohellohello"
end
it "returns self when given no arguments" do
str = "hello"
str.concat.should equal(str)
str.should == "hello"
end
end
end

View file

@ -0,0 +1,105 @@
# -*- encoding: binary -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#count" do
it "counts occurrences of chars from the intersection of the specified sets" do
s = "hello\nworld\x00\x00"
s.count(s).should == s.size
s.count("lo").should == 5
s.count("eo").should == 3
s.count("l").should == 3
s.count("\n").should == 1
s.count("\x00").should == 2
s.count("").should == 0
"".count("").should == 0
s.count("l", "lo").should == s.count("l")
s.count("l", "lo", "o").should == s.count("")
s.count("helo", "hel", "h").should == s.count("h")
s.count("helo", "", "x").should == 0
end
it "raises an ArgumentError when given no arguments" do
lambda { "hell yeah".count }.should raise_error(ArgumentError)
end
it "negates sets starting with ^" do
s = "^hello\nworld\x00\x00"
s.count("^").should == 1 # no negation, counts ^
s.count("^leh").should == 9
s.count("^o").should == 12
s.count("helo", "^el").should == s.count("ho")
s.count("aeiou", "^e").should == s.count("aiou")
"^_^".count("^^").should == 1
"oa^_^o".count("a^").should == 3
end
it "counts all chars in a sequence" do
s = "hel-[()]-lo012^"
s.count("\x00-\xFF").should == s.size
s.count("ej-m").should == 3
s.count("e-h").should == 2
# no sequences
s.count("-").should == 2
s.count("e-").should == s.count("e") + s.count("-")
s.count("-h").should == s.count("h") + s.count("-")
s.count("---").should == s.count("-")
# see an ASCII table for reference
s.count("--2").should == s.count("-./012")
s.count("(--").should == s.count("()*+,-")
s.count("A-a").should == s.count("A-Z[\\]^_`a")
# negated sequences
s.count("^e-h").should == s.size - s.count("e-h")
s.count("^^-^").should == s.size - s.count("^")
s.count("^---").should == s.size - s.count("-")
"abcdefgh".count("a-ce-fh").should == 6
"abcdefgh".count("he-fa-c").should == 6
"abcdefgh".count("e-fha-c").should == 6
"abcde".count("ac-e").should == 4
"abcde".count("^ac-e").should == 1
end
it "raises if the given sequences are invalid" do
s = "hel-[()]-lo012^"
lambda { s.count("h-e") }.should raise_error(ArgumentError)
lambda { s.count("^h-e") }.should raise_error(ArgumentError)
end
it 'returns the number of occurrences of a multi-byte character' do
str = "\u{2605}"
str.count(str).should == 1
"asd#{str}zzz#{str}ggg".count(str).should == 2
end
it "calls #to_str to convert each set arg to a String" do
other_string = mock('lo')
other_string.should_receive(:to_str).and_return("lo")
other_string2 = mock('o')
other_string2.should_receive(:to_str).and_return("o")
s = "hello world"
s.count(other_string, other_string2).should == s.count("o")
end
it "raises a TypeError when a set arg can't be converted to a string" do
lambda { "hello world".count(100) }.should raise_error(TypeError)
lambda { "hello world".count([]) }.should raise_error(TypeError)
lambda { "hello world".count(mock('x')) }.should raise_error(TypeError)
end
end

View file

@ -0,0 +1,75 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#crypt" do
# Note: MRI's documentation just says that the C stdlib function crypt() is
# called.
#
# I'm not sure if crypt() is guaranteed to produce the same result across
# different platforms. It seems that there is one standard UNIX implementation
# of crypt(), but that alternative implementations are possible. See
# http://www.unix.org.ua/orelly/networking/puis/ch08_06.htm
it "returns a cryptographic hash of self by applying the UNIX crypt algorithm with the specified salt" do
"".crypt("aa").should == "aaQSqAReePlq6"
"nutmeg".crypt("Mi").should == "MiqkFWCm1fNJI"
"ellen1".crypt("ri").should == "ri79kNd7V6.Sk"
"Sharon".crypt("./").should == "./UY9Q7TvYJDg"
"norahs".crypt("am").should == "amfIADT2iqjA."
"norahs".crypt("7a").should == "7azfT5tIdyh0I"
# Only uses first 8 chars of string
"01234567".crypt("aa").should == "aa4c4gpuvCkSE"
"012345678".crypt("aa").should == "aa4c4gpuvCkSE"
"0123456789".crypt("aa").should == "aa4c4gpuvCkSE"
# Only uses first 2 chars of salt
"hello world".crypt("aa").should == "aayPz4hyPS1wI"
"hello world".crypt("aab").should == "aayPz4hyPS1wI"
"hello world".crypt("aabc").should == "aayPz4hyPS1wI"
end
it "raises an ArgumentError when the salt is shorter than two characters" do
lambda { "hello".crypt("") }.should raise_error(ArgumentError)
lambda { "hello".crypt("f") }.should raise_error(ArgumentError)
lambda { "hello".crypt("\x00\x00") }.should raise_error(ArgumentError)
lambda { "hello".crypt("\x00a") }.should raise_error(ArgumentError)
lambda { "hello".crypt("a\x00") }.should raise_error(ArgumentError)
end
ruby_version_is "2.3" do
it "raises an ArgumentError when the string contains NUL character" do
lambda { "poison\0null".crypt("aa") }.should raise_error(ArgumentError)
end
end
it "calls #to_str to converts the salt arg to a String" do
obj = mock('aa')
obj.should_receive(:to_str).and_return("aa")
"".crypt(obj).should == "aaQSqAReePlq6"
end
it "raises a type error when the salt arg can't be converted to a string" do
lambda { "".crypt(5) }.should raise_error(TypeError)
lambda { "".crypt(mock('x')) }.should raise_error(TypeError)
end
it "taints the result if either salt or self is tainted" do
tainted_salt = "aa"
tainted_str = "hello"
tainted_salt.taint
tainted_str.taint
"hello".crypt("aa").tainted?.should == false
tainted_str.crypt("aa").tainted?.should == true
"hello".crypt(tainted_salt).tainted?.should == true
tainted_str.crypt(tainted_salt).tainted?.should == true
end
it "doesn't return subclass instances" do
StringSpecs::MyString.new("hello").crypt("aa").should be_an_instance_of(String)
"hello".crypt(StringSpecs::MyString.new("aa")).should be_an_instance_of(String)
StringSpecs::MyString.new("hello").crypt(StringSpecs::MyString.new("aa")).should be_an_instance_of(String)
end
end

View file

@ -0,0 +1,119 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#delete" do
it "returns a new string with the chars from the intersection of sets removed" do
s = "hello"
s.delete("lo").should == "he"
s.should == "hello"
"hello".delete("l", "lo").should == "heo"
"hell yeah".delete("").should == "hell yeah"
end
it "raises an ArgumentError when given no arguments" do
lambda { "hell yeah".delete }.should raise_error(ArgumentError)
end
it "negates sets starting with ^" do
"hello".delete("aeiou", "^e").should == "hell"
"hello".delete("^leh").should == "hell"
"hello".delete("^o").should == "o"
"hello".delete("^").should == "hello"
"^_^".delete("^^").should == "^^"
"oa^_^o".delete("a^").should == "o_o"
end
it "deletes all chars in a sequence" do
"hello".delete("ej-m").should == "ho"
"hello".delete("e-h").should == "llo"
"hel-lo".delete("e-").should == "hllo"
"hel-lo".delete("-h").should == "ello"
"hel-lo".delete("---").should == "hello"
"hel-012".delete("--2").should == "hel"
"hel-()".delete("(--").should == "hel"
"hello".delete("^e-h").should == "he"
"hello^".delete("^^-^").should == "^"
"hel--lo".delete("^---").should == "--"
"abcdefgh".delete("a-ce-fh").should == "dg"
"abcdefgh".delete("he-fa-c").should == "dg"
"abcdefgh".delete("e-fha-c").should == "dg"
"abcde".delete("ac-e").should == "b"
"abcde".delete("^ac-e").should == "acde"
"ABCabc[]".delete("A-a").should == "bc"
end
it "deletes multibyte characters" do
"四月".delete("").should == ""
'哥哥我倒'.delete('哥').should == "我倒"
end
it "respects backslash for escaping a -" do
'Non-Authoritative Information'.delete(' \-\'').should ==
'NonAuthoritativeInformation'
end
it "raises if the given ranges are invalid" do
not_supported_on :opal do
xFF = [0xFF].pack('C')
range = "\x00 - #{xFF}".force_encoding('utf-8')
lambda { "hello".delete(range).should == "" }.should raise_error(ArgumentError)
end
lambda { "hello".delete("h-e") }.should raise_error(ArgumentError)
lambda { "hello".delete("^h-e") }.should raise_error(ArgumentError)
end
it "taints result when self is tainted" do
"hello".taint.delete("e").tainted?.should == true
"hello".taint.delete("a-z").tainted?.should == true
"hello".delete("e".taint).tainted?.should == false
end
it "tries to convert each set arg to a string using to_str" do
other_string = mock('lo')
other_string.should_receive(:to_str).and_return("lo")
other_string2 = mock('o')
other_string2.should_receive(:to_str).and_return("o")
"hello world".delete(other_string, other_string2).should == "hell wrld"
end
it "raises a TypeError when one set arg can't be converted to a string" do
lambda { "hello world".delete(100) }.should raise_error(TypeError)
lambda { "hello world".delete([]) }.should raise_error(TypeError)
lambda { "hello world".delete(mock('x')) }.should raise_error(TypeError)
end
it "returns subclass instances when called on a subclass" do
StringSpecs::MyString.new("oh no!!!").delete("!").should be_an_instance_of(StringSpecs::MyString)
end
end
describe "String#delete!" do
it "modifies self in place and returns self" do
a = "hello"
a.delete!("aeiou", "^e").should equal(a)
a.should == "hell"
end
it "returns nil if no modifications were made" do
a = "hello"
a.delete!("z").should == nil
a.should == "hello"
end
it "raises a RuntimeError when self is frozen" do
a = "hello"
a.freeze
lambda { a.delete!("") }.should raise_error(RuntimeError)
lambda { a.delete!("aeiou", "^e") }.should raise_error(RuntimeError)
end
end

View file

@ -0,0 +1,65 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#downcase" do
it "returns a copy of self with all uppercase letters downcased" do
"hELLO".downcase.should == "hello"
"hello".downcase.should == "hello"
end
ruby_version_is ''...'2.4' do
it "is locale insensitive (only replaces A-Z)" do
"ÄÖÜ".downcase.should == "ÄÖÜ"
str = Array.new(256) { |c| c.chr }.join
expected = Array.new(256) do |i|
c = i.chr
c.between?("A", "Z") ? c.downcase : c
end.join
str.downcase.should == expected
end
end
ruby_version_is '2.4' do
it "works for all of Unicode" do
"ÄÖÜ".downcase.should == "äöü"
end
end
it "taints result when self is tainted" do
"".taint.downcase.tainted?.should == true
"x".taint.downcase.tainted?.should == true
"X".taint.downcase.tainted?.should == true
end
it "returns a subclass instance for subclasses" do
StringSpecs::MyString.new("FOObar").downcase.should be_an_instance_of(StringSpecs::MyString)
end
end
describe "String#downcase!" do
it "modifies self in place" do
a = "HeLlO"
a.downcase!.should equal(a)
a.should == "hello"
end
it "returns nil if no modifications were made" do
a = "hello"
a.downcase!.should == nil
a.should == "hello"
end
it "raises a RuntimeError when self is frozen" do
lambda { "HeLlo".freeze.downcase! }.should raise_error(RuntimeError)
lambda { "hello".freeze.downcase! }.should raise_error(RuntimeError)
end
with_feature :encoding do
it "sets the result String encoding to the source String encoding" do
"ABC".downcase.encoding.should equal(Encoding::UTF_8)
end
end
end

View file

@ -0,0 +1,424 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
describe "String#dump" do
it "taints the result if self is tainted" do
"foo".taint.dump.tainted?.should == true
"foo\n".taint.dump.tainted?.should == true
end
it "untrusts the result if self is untrusted" do
"foo".untrust.dump.untrusted?.should == true
"foo\n".untrust.dump.untrusted?.should == true
end
it "returns a subclass instance" do
StringSpecs::MyString.new.dump.should be_an_instance_of(StringSpecs::MyString)
end
it "returns a string with special characters replaced with \\<char> notation" do
[ ["\a", '"\\a"'],
["\b", '"\\b"'],
["\t", '"\\t"'],
["\n", '"\\n"'],
["\v", '"\\v"'],
["\f", '"\\f"'],
["\r", '"\\r"'],
["\e", '"\\e"']
].should be_computed_by(:dump)
end
it "returns a string with \" and \\ escaped with a backslash" do
[ ["\"", '"\\""'],
["\\", '"\\\\"']
].should be_computed_by(:dump)
end
it "returns a string with \\#<char> when # is followed by $, @, {" do
[ ["\#$", '"\\#$"'],
["\#@", '"\\#@"'],
["\#{", '"\\#{"']
].should be_computed_by(:dump)
end
it "returns a string with # not escaped when followed by any other character" do
[ ["#", '"#"'],
["#1", '"#1"']
].should be_computed_by(:dump)
end
it "returns a string with printable non-alphanumeric characters unescaped" do
[ [" ", '" "'],
["!", '"!"'],
["$", '"$"'],
["%", '"%"'],
["&", '"&"'],
["'", '"\'"'],
["(", '"("'],
[")", '")"'],
["*", '"*"'],
["+", '"+"'],
[",", '","'],
["-", '"-"'],
[".", '"."'],
["/", '"/"'],
[":", '":"'],
[";", '";"'],
["<", '"<"'],
["=", '"="'],
[">", '">"'],
["?", '"?"'],
["@", '"@"'],
["[", '"["'],
["]", '"]"'],
["^", '"^"'],
["_", '"_"'],
["`", '"`"'],
["{", '"{"'],
["|", '"|"'],
["}", '"}"'],
["~", '"~"']
].should be_computed_by(:dump)
end
it "returns a string with numeric characters unescaped" do
[ ["0", '"0"'],
["1", '"1"'],
["2", '"2"'],
["3", '"3"'],
["4", '"4"'],
["5", '"5"'],
["6", '"6"'],
["7", '"7"'],
["8", '"8"'],
["9", '"9"'],
].should be_computed_by(:dump)
end
it "returns a string with upper-case alpha characters unescaped" do
[ ["A", '"A"'],
["B", '"B"'],
["C", '"C"'],
["D", '"D"'],
["E", '"E"'],
["F", '"F"'],
["G", '"G"'],
["H", '"H"'],
["I", '"I"'],
["J", '"J"'],
["K", '"K"'],
["L", '"L"'],
["M", '"M"'],
["N", '"N"'],
["O", '"O"'],
["P", '"P"'],
["Q", '"Q"'],
["R", '"R"'],
["S", '"S"'],
["T", '"T"'],
["U", '"U"'],
["V", '"V"'],
["W", '"W"'],
["X", '"X"'],
["Y", '"Y"'],
["Z", '"Z"']
].should be_computed_by(:dump)
end
it "returns a string with lower-case alpha characters unescaped" do
[ ["a", '"a"'],
["b", '"b"'],
["c", '"c"'],
["d", '"d"'],
["e", '"e"'],
["f", '"f"'],
["g", '"g"'],
["h", '"h"'],
["i", '"i"'],
["j", '"j"'],
["k", '"k"'],
["l", '"l"'],
["m", '"m"'],
["n", '"n"'],
["o", '"o"'],
["p", '"p"'],
["q", '"q"'],
["r", '"r"'],
["s", '"s"'],
["t", '"t"'],
["u", '"u"'],
["v", '"v"'],
["w", '"w"'],
["x", '"x"'],
["y", '"y"'],
["z", '"z"']
].should be_computed_by(:dump)
end
it "returns a string with non-printing ASCII characters replaced by \\x notation" do
# Avoid the file encoding by computing the string with #chr.
[ [0000.chr, '"\\x00"'],
[0001.chr, '"\\x01"'],
[0002.chr, '"\\x02"'],
[0003.chr, '"\\x03"'],
[0004.chr, '"\\x04"'],
[0005.chr, '"\\x05"'],
[0006.chr, '"\\x06"'],
[0016.chr, '"\\x0E"'],
[0017.chr, '"\\x0F"'],
[0020.chr, '"\\x10"'],
[0021.chr, '"\\x11"'],
[0022.chr, '"\\x12"'],
[0023.chr, '"\\x13"'],
[0024.chr, '"\\x14"'],
[0025.chr, '"\\x15"'],
[0026.chr, '"\\x16"'],
[0027.chr, '"\\x17"'],
[0030.chr, '"\\x18"'],
[0031.chr, '"\\x19"'],
[0032.chr, '"\\x1A"'],
[0034.chr, '"\\x1C"'],
[0035.chr, '"\\x1D"'],
[0036.chr, '"\\x1E"'],
[0037.chr, '"\\x1F"'],
[0177.chr, '"\\x7F"'],
[0200.chr, '"\\x80"'],
[0201.chr, '"\\x81"'],
[0202.chr, '"\\x82"'],
[0203.chr, '"\\x83"'],
[0204.chr, '"\\x84"'],
[0205.chr, '"\\x85"'],
[0206.chr, '"\\x86"'],
[0207.chr, '"\\x87"'],
[0210.chr, '"\\x88"'],
[0211.chr, '"\\x89"'],
[0212.chr, '"\\x8A"'],
[0213.chr, '"\\x8B"'],
[0214.chr, '"\\x8C"'],
[0215.chr, '"\\x8D"'],
[0216.chr, '"\\x8E"'],
[0217.chr, '"\\x8F"'],
[0220.chr, '"\\x90"'],
[0221.chr, '"\\x91"'],
[0222.chr, '"\\x92"'],
[0223.chr, '"\\x93"'],
[0224.chr, '"\\x94"'],
[0225.chr, '"\\x95"'],
[0226.chr, '"\\x96"'],
[0227.chr, '"\\x97"'],
[0230.chr, '"\\x98"'],
[0231.chr, '"\\x99"'],
[0232.chr, '"\\x9A"'],
[0233.chr, '"\\x9B"'],
[0234.chr, '"\\x9C"'],
[0235.chr, '"\\x9D"'],
[0236.chr, '"\\x9E"'],
[0237.chr, '"\\x9F"'],
[0240.chr, '"\\xA0"'],
[0241.chr, '"\\xA1"'],
[0242.chr, '"\\xA2"'],
[0243.chr, '"\\xA3"'],
[0244.chr, '"\\xA4"'],
[0245.chr, '"\\xA5"'],
[0246.chr, '"\\xA6"'],
[0247.chr, '"\\xA7"'],
[0250.chr, '"\\xA8"'],
[0251.chr, '"\\xA9"'],
[0252.chr, '"\\xAA"'],
[0253.chr, '"\\xAB"'],
[0254.chr, '"\\xAC"'],
[0255.chr, '"\\xAD"'],
[0256.chr, '"\\xAE"'],
[0257.chr, '"\\xAF"'],
[0260.chr, '"\\xB0"'],
[0261.chr, '"\\xB1"'],
[0262.chr, '"\\xB2"'],
[0263.chr, '"\\xB3"'],
[0264.chr, '"\\xB4"'],
[0265.chr, '"\\xB5"'],
[0266.chr, '"\\xB6"'],
[0267.chr, '"\\xB7"'],
[0270.chr, '"\\xB8"'],
[0271.chr, '"\\xB9"'],
[0272.chr, '"\\xBA"'],
[0273.chr, '"\\xBB"'],
[0274.chr, '"\\xBC"'],
[0275.chr, '"\\xBD"'],
[0276.chr, '"\\xBE"'],
[0277.chr, '"\\xBF"'],
[0300.chr, '"\\xC0"'],
[0301.chr, '"\\xC1"'],
[0302.chr, '"\\xC2"'],
[0303.chr, '"\\xC3"'],
[0304.chr, '"\\xC4"'],
[0305.chr, '"\\xC5"'],
[0306.chr, '"\\xC6"'],
[0307.chr, '"\\xC7"'],
[0310.chr, '"\\xC8"'],
[0311.chr, '"\\xC9"'],
[0312.chr, '"\\xCA"'],
[0313.chr, '"\\xCB"'],
[0314.chr, '"\\xCC"'],
[0315.chr, '"\\xCD"'],
[0316.chr, '"\\xCE"'],
[0317.chr, '"\\xCF"'],
[0320.chr, '"\\xD0"'],
[0321.chr, '"\\xD1"'],
[0322.chr, '"\\xD2"'],
[0323.chr, '"\\xD3"'],
[0324.chr, '"\\xD4"'],
[0325.chr, '"\\xD5"'],
[0326.chr, '"\\xD6"'],
[0327.chr, '"\\xD7"'],
[0330.chr, '"\\xD8"'],
[0331.chr, '"\\xD9"'],
[0332.chr, '"\\xDA"'],
[0333.chr, '"\\xDB"'],
[0334.chr, '"\\xDC"'],
[0335.chr, '"\\xDD"'],
[0336.chr, '"\\xDE"'],
[0337.chr, '"\\xDF"'],
[0340.chr, '"\\xE0"'],
[0341.chr, '"\\xE1"'],
[0342.chr, '"\\xE2"'],
[0343.chr, '"\\xE3"'],
[0344.chr, '"\\xE4"'],
[0345.chr, '"\\xE5"'],
[0346.chr, '"\\xE6"'],
[0347.chr, '"\\xE7"'],
[0350.chr, '"\\xE8"'],
[0351.chr, '"\\xE9"'],
[0352.chr, '"\\xEA"'],
[0353.chr, '"\\xEB"'],
[0354.chr, '"\\xEC"'],
[0355.chr, '"\\xED"'],
[0356.chr, '"\\xEE"'],
[0357.chr, '"\\xEF"'],
[0360.chr, '"\\xF0"'],
[0361.chr, '"\\xF1"'],
[0362.chr, '"\\xF2"'],
[0363.chr, '"\\xF3"'],
[0364.chr, '"\\xF4"'],
[0365.chr, '"\\xF5"'],
[0366.chr, '"\\xF6"'],
[0367.chr, '"\\xF7"'],
[0370.chr, '"\\xF8"'],
[0371.chr, '"\\xF9"'],
[0372.chr, '"\\xFA"'],
[0373.chr, '"\\xFB"'],
[0374.chr, '"\\xFC"'],
[0375.chr, '"\\xFD"'],
[0376.chr, '"\\xFE"'],
[0377.chr, '"\\xFF"']
].should be_computed_by(:dump)
end
it "returns a string with non-printing single-byte UTF-8 characters replaced by \\x notation" do
[ [0000.chr('utf-8'), '"\x00"'],
[0001.chr('utf-8'), '"\x01"'],
[0002.chr('utf-8'), '"\x02"'],
[0003.chr('utf-8'), '"\x03"'],
[0004.chr('utf-8'), '"\x04"'],
[0005.chr('utf-8'), '"\x05"'],
[0006.chr('utf-8'), '"\x06"'],
[0016.chr('utf-8'), '"\x0E"'],
[0017.chr('utf-8'), '"\x0F"'],
[0020.chr('utf-8'), '"\x10"'],
[0021.chr('utf-8'), '"\x11"'],
[0022.chr('utf-8'), '"\x12"'],
[0023.chr('utf-8'), '"\x13"'],
[0024.chr('utf-8'), '"\x14"'],
[0025.chr('utf-8'), '"\x15"'],
[0026.chr('utf-8'), '"\x16"'],
[0027.chr('utf-8'), '"\x17"'],
[0030.chr('utf-8'), '"\x18"'],
[0031.chr('utf-8'), '"\x19"'],
[0032.chr('utf-8'), '"\x1A"'],
[0034.chr('utf-8'), '"\x1C"'],
[0035.chr('utf-8'), '"\x1D"'],
[0036.chr('utf-8'), '"\x1E"'],
[0037.chr('utf-8'), '"\x1F"'],
[0177.chr('utf-8'), '"\x7F"']
].should be_computed_by(:dump)
end
ruby_version_is ''...'2.4' do
it "returns a string with multi-byte UTF-8 characters replaced by \\u{} notation with lower-case hex digits" do
[ [0200.chr('utf-8'), '"\u{80}"'],
[0201.chr('utf-8'), '"\u{81}"'],
[0202.chr('utf-8'), '"\u{82}"'],
[0203.chr('utf-8'), '"\u{83}"'],
[0204.chr('utf-8'), '"\u{84}"'],
[0206.chr('utf-8'), '"\u{86}"'],
[0207.chr('utf-8'), '"\u{87}"'],
[0210.chr('utf-8'), '"\u{88}"'],
[0211.chr('utf-8'), '"\u{89}"'],
[0212.chr('utf-8'), '"\u{8a}"'],
[0213.chr('utf-8'), '"\u{8b}"'],
[0214.chr('utf-8'), '"\u{8c}"'],
[0215.chr('utf-8'), '"\u{8d}"'],
[0216.chr('utf-8'), '"\u{8e}"'],
[0217.chr('utf-8'), '"\u{8f}"'],
[0220.chr('utf-8'), '"\u{90}"'],
[0221.chr('utf-8'), '"\u{91}"'],
[0222.chr('utf-8'), '"\u{92}"'],
[0223.chr('utf-8'), '"\u{93}"'],
[0224.chr('utf-8'), '"\u{94}"'],
[0225.chr('utf-8'), '"\u{95}"'],
[0226.chr('utf-8'), '"\u{96}"'],
[0227.chr('utf-8'), '"\u{97}"'],
[0230.chr('utf-8'), '"\u{98}"'],
[0231.chr('utf-8'), '"\u{99}"'],
[0232.chr('utf-8'), '"\u{9a}"'],
[0233.chr('utf-8'), '"\u{9b}"'],
[0234.chr('utf-8'), '"\u{9c}"'],
[0235.chr('utf-8'), '"\u{9d}"'],
[0236.chr('utf-8'), '"\u{9e}"'],
[0237.chr('utf-8'), '"\u{9f}"'],
].should be_computed_by(:dump)
end
end
ruby_version_is '2.4' do
it "returns a string with multi-byte UTF-8 characters replaced by \\u{} notation with lower-case hex digits" do
[ [0200.chr('utf-8'), '"\u0080"'],
[0201.chr('utf-8'), '"\u0081"'],
[0202.chr('utf-8'), '"\u0082"'],
[0203.chr('utf-8'), '"\u0083"'],
[0204.chr('utf-8'), '"\u0084"'],
[0206.chr('utf-8'), '"\u0086"'],
[0207.chr('utf-8'), '"\u0087"'],
[0210.chr('utf-8'), '"\u0088"'],
[0211.chr('utf-8'), '"\u0089"'],
[0212.chr('utf-8'), '"\u008A"'],
[0213.chr('utf-8'), '"\u008B"'],
[0214.chr('utf-8'), '"\u008C"'],
[0215.chr('utf-8'), '"\u008D"'],
[0216.chr('utf-8'), '"\u008E"'],
[0217.chr('utf-8'), '"\u008F"'],
[0220.chr('utf-8'), '"\u0090"'],
[0221.chr('utf-8'), '"\u0091"'],
[0222.chr('utf-8'), '"\u0092"'],
[0223.chr('utf-8'), '"\u0093"'],
[0224.chr('utf-8'), '"\u0094"'],
[0225.chr('utf-8'), '"\u0095"'],
[0226.chr('utf-8'), '"\u0096"'],
[0227.chr('utf-8'), '"\u0097"'],
[0230.chr('utf-8'), '"\u0098"'],
[0231.chr('utf-8'), '"\u0099"'],
[0232.chr('utf-8'), '"\u009A"'],
[0233.chr('utf-8'), '"\u009B"'],
[0234.chr('utf-8'), '"\u009C"'],
[0235.chr('utf-8'), '"\u009D"'],
[0236.chr('utf-8'), '"\u009E"'],
[0237.chr('utf-8'), '"\u009F"'],
].should be_computed_by(:dump)
end
end
it "includes .force_encoding(name) if the encoding isn't ASCII compatible" do
"\u{876}".encode('utf-16be').dump.should == "\"\\bv\".force_encoding(\"UTF-16BE\")"
"\u{876}".encode('utf-16le').dump.should == "\"v\\b\".force_encoding(\"UTF-16LE\")"
end
end

View file

@ -0,0 +1,52 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
describe "String#dup" do
before :each do
ScratchPad.clear
@obj = StringSpecs::InitializeString.new "string"
end
it "calls #initialize_copy on the new instance" do
dup = @obj.dup
ScratchPad.recorded.should_not == @obj.object_id
ScratchPad.recorded.should == dup.object_id
end
it "copies instance variables" do
dup = @obj.dup
dup.ivar.should == 1
end
it "does not copy singleton methods" do
def @obj.special() :the_one end
dup = @obj.dup
lambda { dup.special }.should raise_error(NameError)
end
it "does not copy modules included in the singleton class" do
class << @obj
include StringSpecs::StringModule
end
dup = @obj.dup
lambda { dup.repr }.should raise_error(NameError)
end
it "does not copy constants defined in the singleton class" do
class << @obj
CLONE = :clone
end
dup = @obj.dup
lambda { class << dup; CLONE; end }.should raise_error(NameError)
end
it "does not modify the original string when changing dupped string" do
orig = "string"[0..100]
dup = orig.dup
orig[0] = 'x'
orig.should == "xtring"
dup.should == "string"
end
end

View file

@ -0,0 +1,61 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#each_byte" do
it "passes each byte in self to the given block" do
a = []
"hello\x00".each_byte { |c| a << c }
a.should == [104, 101, 108, 108, 111, 0]
end
it "keeps iterating from the old position (to new string end) when self changes" do
r = ""
s = "hello world"
s.each_byte do |c|
r << c
s.insert(0, "<>") if r.size < 3
end
r.should == "h><>hello world"
r = ""
s = "hello world"
s.each_byte { |c| s.slice!(-1); r << c }
r.should == "hello "
r = ""
s = "hello world"
s.each_byte { |c| s.slice!(0); r << c }
r.should == "hlowrd"
r = ""
s = "hello world"
s.each_byte { |c| s.slice!(0..-1); r << c }
r.should == "h"
end
it "returns self" do
s = "hello"
(s.each_byte {}).should equal(s)
end
describe "when no block is given" do
it "returns an enumerator" do
enum = "hello".each_byte
enum.should be_an_instance_of(Enumerator)
enum.to_a.should == [104, 101, 108, 108, 111]
end
describe "returned enumerator" do
describe "size" do
it "should return the bytesize of the string" do
str = "hello"
str.each_byte.size.should == str.bytesize
str = "ola"
str.each_byte.size.should == str.bytesize
str = "\303\207\342\210\202\303\251\306\222g"
str.each_byte.size.should == str.bytesize
end
end
end
end
end

View file

@ -0,0 +1,7 @@
require File.expand_path('../shared/chars', __FILE__)
require File.expand_path('../shared/each_char_without_block', __FILE__)
describe "String#each_char" do
it_behaves_like(:string_chars, :each_char)
it_behaves_like(:string_each_char_without_block, :each_char)
end

View file

@ -0,0 +1,10 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../shared/codepoints', __FILE__)
require File.expand_path('../shared/each_codepoint_without_block', __FILE__)
with_feature :encoding do
describe "String#each_codepoint" do
it_behaves_like(:string_codepoints, :each_codepoint)
it_behaves_like(:string_each_codepoint_without_block, :each_codepoint)
end
end

View file

@ -0,0 +1,9 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
require File.expand_path('../shared/each_line', __FILE__)
require File.expand_path('../shared/each_line_without_block', __FILE__)
describe "String#each_line" do
it_behaves_like(:string_each_line, :each_line)
it_behaves_like(:string_each_line_without_block, :each_line)
end

View file

@ -0,0 +1,35 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
require File.expand_path('../shared/slice.rb', __FILE__)
describe "String#[]" do
it_behaves_like :string_slice, :[]
end
describe "String#[] with index, length" do
it_behaves_like :string_slice_index_length, :[]
end
describe "String#[] with Range" do
it_behaves_like :string_slice_range, :[]
end
describe "String#[] with Regexp" do
it_behaves_like :string_slice_regexp, :[]
end
describe "String#[] with Regexp, index" do
it_behaves_like :string_slice_regexp_index, :[]
end
describe "String#[] with Regexp, group" do
it_behaves_like :string_slice_regexp_group, :[]
end
describe "String#[] with String" do
it_behaves_like :string_slice_string, :[]
end
describe "String#[] with Symbol" do
it_behaves_like :string_slice_symbol, :[]
end

View file

@ -0,0 +1,612 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
# TODO: Add missing String#[]= specs:
# String#[re, idx] = obj
describe "String#[]= with Fixnum index" do
it "replaces the char at idx with other_str" do
a = "hello"
a[0] = "bam"
a.should == "bamello"
a[-2] = ""
a.should == "bamelo"
end
it "taints self if other_str is tainted" do
a = "hello"
a[0] = "".taint
a.tainted?.should == true
a = "hello"
a[0] = "x".taint
a.tainted?.should == true
end
it "raises an IndexError without changing self if idx is outside of self" do
str = "hello"
lambda { str[20] = "bam" }.should raise_error(IndexError)
str.should == "hello"
lambda { str[-20] = "bam" }.should raise_error(IndexError)
str.should == "hello"
lambda { ""[-1] = "bam" }.should raise_error(IndexError)
end
# Behaviour verfieid correct by matz in
# http://redmine.ruby-lang.org/issues/show/1750
it "allows assignment to the zero'th element of an empty String" do
str = ""
str[0] = "bam"
str.should == "bam"
end
it "raises IndexError if the string index doesn't match a position in the string" do
str = "hello"
lambda { str['y'] = "bam" }.should raise_error(IndexError)
str.should == "hello"
end
it "raises a RuntimeError when self is frozen" do
a = "hello"
a.freeze
lambda { a[0] = "bam" }.should raise_error(RuntimeError)
end
it "calls to_int on index" do
str = "hello"
str[0.5] = "hi "
str.should == "hi ello"
obj = mock('-1')
obj.should_receive(:to_int).and_return(-1)
str[obj] = "!"
str.should == "hi ell!"
end
it "calls #to_str to convert other to a String" do
other_str = mock('-test-')
other_str.should_receive(:to_str).and_return("-test-")
a = "abc"
a[1] = other_str
a.should == "a-test-c"
end
it "raises a TypeError if other_str can't be converted to a String" do
lambda { "test"[1] = [] }.should raise_error(TypeError)
lambda { "test"[1] = mock('x') }.should raise_error(TypeError)
lambda { "test"[1] = nil }.should raise_error(TypeError)
end
with_feature :encoding do
it "raises a TypeError if passed a Fixnum replacement" do
lambda { "abc"[1] = 65 }.should raise_error(TypeError)
end
it "raises an IndexError if the index is greater than character size" do
lambda { "あれ"[4] = "a" }.should raise_error(IndexError)
end
it "calls #to_int to convert the index" do
index = mock("string element set")
index.should_receive(:to_int).and_return(1)
str = "あれ"
str[index] = "a"
str.should == "あa"
end
it "raises a TypeError if #to_int does not return an Fixnum" do
index = mock("string element set")
index.should_receive(:to_int).and_return('1')
lambda { "abc"[index] = "d" }.should raise_error(TypeError)
end
it "raises an IndexError if #to_int returns a value out of range" do
index = mock("string element set")
index.should_receive(:to_int).and_return(4)
lambda { "ab"[index] = "c" }.should raise_error(IndexError)
end
it "replaces a character with a multibyte character" do
str = "ありがとu"
str[4] = ""
str.should == "ありがとう"
end
it "replaces a multibyte character with a character" do
str = "ありがとう"
str[4] = "u"
str.should == "ありがとu"
end
it "replaces a multibyte character with a multibyte character" do
str = "ありがとお"
str[4] = ""
str.should == "ありがとう"
end
it "encodes the String in an encoding compatible with the replacement" do
str = " ".force_encoding Encoding::US_ASCII
rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT
str[0] = rep
str.encoding.should equal(Encoding::ASCII_8BIT)
end
it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do
str = "あれ"
rep = "".encode Encoding::EUC_JP
lambda { str[0] = rep }.should raise_error(Encoding::CompatibilityError)
end
end
end
describe "String#[]= with String index" do
it "replaces fewer characters with more characters" do
str = "abcde"
str["cd"] = "ghi"
str.should == "abghie"
end
it "replaces more characters with fewer characters" do
str = "abcde"
str["bcd"] = "f"
str.should == "afe"
end
it "replaces characters with no characters" do
str = "abcde"
str["cd"] = ""
str.should == "abe"
end
it "raises an IndexError if the search String is not found" do
str = "abcde"
lambda { str["g"] = "h" }.should raise_error(IndexError)
end
with_feature :encoding do
it "replaces characters with a multibyte character" do
str = "ありgaとう"
str["ga"] = ""
str.should == "ありがとう"
end
it "replaces multibyte characters with characters" do
str = "ありがとう"
str[""] = "ga"
str.should == "ありgaとう"
end
it "replaces multibyte characters with multibyte characters" do
str = "ありがとう"
str[""] = ""
str.should == "ありかとう"
end
it "encodes the String in an encoding compatible with the replacement" do
str = " ".force_encoding Encoding::US_ASCII
rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT
str[" "] = rep
str.encoding.should equal(Encoding::ASCII_8BIT)
end
it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do
str = "あれ"
rep = "".encode Encoding::EUC_JP
lambda { str[""] = rep }.should raise_error(Encoding::CompatibilityError)
end
end
end
describe "String#[]= with a Regexp index" do
it "replaces the matched text with the rhs" do
str = "hello"
str[/lo/] = "x"
str.should == "helx"
end
it "raises IndexError if the regexp index doesn't match a position in the string" do
str = "hello"
lambda { str[/y/] = "bam" }.should raise_error(IndexError)
str.should == "hello"
end
it "calls #to_str to convert the replacement" do
rep = mock("string element set regexp")
rep.should_receive(:to_str).and_return("b")
str = "abc"
str[/ab/] = rep
str.should == "bc"
end
it "checks the match before calling #to_str to convert the replacement" do
rep = mock("string element set regexp")
rep.should_not_receive(:to_str)
lambda { "abc"[/def/] = rep }.should raise_error(IndexError)
end
describe "with 3 arguments" do
it "calls #to_int to convert the second object" do
ref = mock("string element set regexp ref")
ref.should_receive(:to_int).and_return(1)
str = "abc"
str[/a(b)/, ref] = "x"
str.should == "axc"
end
it "raises a TypeError if #to_int does not return a Fixnum" do
ref = mock("string element set regexp ref")
ref.should_receive(:to_int).and_return(nil)
lambda { "abc"[/a(b)/, ref] = "x" }.should raise_error(TypeError)
end
it "uses the 2nd of 3 arguments as which capture should be replaced" do
str = "aaa bbb ccc"
str[/a (bbb) c/, 1] = "ddd"
str.should == "aaa ddd ccc"
end
it "allows the specified capture to be negative and count from the end" do
str = "abcd"
str[/(a)(b)(c)(d)/, -2] = "e"
str.should == "abed"
end
it "checks the match index before calling #to_str to convert the replacement" do
rep = mock("string element set regexp")
rep.should_not_receive(:to_str)
lambda { "abc"[/a(b)/, 2] = rep }.should raise_error(IndexError)
end
it "raises IndexError if the specified capture isn't available" do
str = "aaa bbb ccc"
lambda { str[/a (bbb) c/, 2] = "ddd" }.should raise_error(IndexError)
lambda { str[/a (bbb) c/, -2] = "ddd" }.should raise_error(IndexError)
end
describe "when the optional capture does not match" do
it "raises an IndexError before setting the replacement" do
str1 = "a b c"
str2 = str1.dup
lambda { str2[/a (b) (Z)?/, 2] = "d" }.should raise_error(IndexError)
str2.should == str1
end
end
end
with_feature :encoding do
it "replaces characters with a multibyte character" do
str = "ありgaとう"
str[/ga/] = ""
str.should == "ありがとう"
end
it "replaces multibyte characters with characters" do
str = "ありがとう"
str[//] = "ga"
str.should == "ありgaとう"
end
it "replaces multibyte characters with multibyte characters" do
str = "ありがとう"
str[//] = ""
str.should == "ありかとう"
end
it "encodes the String in an encoding compatible with the replacement" do
str = " ".force_encoding Encoding::US_ASCII
rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT
str[/ /] = rep
str.encoding.should equal(Encoding::ASCII_8BIT)
end
it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do
str = "あれ"
rep = "".encode Encoding::EUC_JP
lambda { str[//] = rep }.should raise_error(Encoding::CompatibilityError)
end
end
end
describe "String#[]= with a Range index" do
describe "with an empty replacement" do
it "does not replace a character with a zero-index, zero exclude-end range" do
str = "abc"
str[0...0] = ""
str.should == "abc"
end
it "does not replace a character with a zero exclude-end range" do
str = "abc"
str[1...1] = ""
str.should == "abc"
end
it "replaces a character with zero-index, zero non-exclude-end range" do
str = "abc"
str[0..0] = ""
str.should == "bc"
end
it "replaces a character with a zero non-exclude-end range" do
str = "abc"
str[1..1] = ""
str.should == "ac"
end
end
it "replaces the contents with a shorter String" do
str = "abcde"
str[0..-1] = "hg"
str.should == "hg"
end
it "replaces the contents with a longer String" do
str = "abc"
str[0...4] = "uvwxyz"
str.should == "uvwxyz"
end
it "replaces a partial string" do
str = "abcde"
str[1..3] = "B"
str.should == "aBe"
end
it "raises a RangeError if negative Range begin is out of range" do
lambda { "abc"[-4..-2] = "x" }.should raise_error(RangeError)
end
it "raises a RangeError if positive Range begin is greater than String size" do
lambda { "abc"[4..2] = "x" }.should raise_error(RangeError)
end
it "uses the Range end as an index rather than a count" do
str = "abcdefg"
str[-5..3] = "xyz"
str.should == "abxyzefg"
end
it "treats a negative out-of-range Range end with a positive Range begin as a zero count" do
str = "abc"
str[1..-4] = "x"
str.should == "axbc"
end
it "treats a negative out-of-range Range end with a negative Range begin as a zero count" do
str = "abcd"
str[-1..-4] = "x"
str.should == "abcxd"
end
with_feature :encoding do
it "replaces characters with a multibyte character" do
str = "ありgaとう"
str[2..3] = ""
str.should == "ありがとう"
end
it "replaces multibyte characters with characters" do
str = "ありがとう"
str[2...3] = "ga"
str.should == "ありgaとう"
end
it "replaces multibyte characters by negative indexes" do
str = "ありがとう"
str[-3...-2] = "ga"
str.should == "ありgaとう"
end
it "replaces multibyte characters with multibyte characters" do
str = "ありがとう"
str[2..2] = ""
str.should == "ありかとう"
end
it "deletes a multibyte character" do
str = "ありとう"
str[2..3] = ""
str.should == "あり"
end
it "inserts a multibyte character" do
str = "ありとう"
str[2...2] = ""
str.should == "ありがとう"
end
it "encodes the String in an encoding compatible with the replacement" do
str = " ".force_encoding Encoding::US_ASCII
rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT
str[0..1] = rep
str.encoding.should equal(Encoding::ASCII_8BIT)
end
it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do
str = "あれ"
rep = "".encode Encoding::EUC_JP
lambda { str[0..1] = rep }.should raise_error(Encoding::CompatibilityError)
end
end
end
describe "String#[]= with Fixnum index, count" do
it "starts at idx and overwrites count characters before inserting the rest of other_str" do
a = "hello"
a[0, 2] = "xx"
a.should == "xxllo"
a = "hello"
a[0, 2] = "jello"
a.should == "jellollo"
end
it "counts negative idx values from end of the string" do
a = "hello"
a[-1, 0] = "bob"
a.should == "hellbobo"
a = "hello"
a[-5, 0] = "bob"
a.should == "bobhello"
end
it "overwrites and deletes characters if count is more than the length of other_str" do
a = "hello"
a[0, 4] = "x"
a.should == "xo"
a = "hello"
a[0, 5] = "x"
a.should == "x"
end
it "deletes characters if other_str is an empty string" do
a = "hello"
a[0, 2] = ""
a.should == "llo"
end
it "deletes characters up to the maximum length of the existing string" do
a = "hello"
a[0, 6] = "x"
a.should == "x"
a = "hello"
a[0, 100] = ""
a.should == ""
end
it "appends other_str to the end of the string if idx == the length of the string" do
a = "hello"
a[5, 0] = "bob"
a.should == "hellobob"
end
it "taints self if other_str is tainted" do
a = "hello"
a[0, 0] = "".taint
a.tainted?.should == true
a = "hello"
a[1, 4] = "x".taint
a.tainted?.should == true
end
it "calls #to_int to convert the index and count objects" do
index = mock("string element set index")
index.should_receive(:to_int).and_return(-4)
count = mock("string element set count")
count.should_receive(:to_int).and_return(2)
str = "abcde"
str[index, count] = "xyz"
str.should == "axyzde"
end
it "raises a TypeError if #to_int for index does not return an Integer" do
index = mock("string element set index")
index.should_receive(:to_int).and_return("1")
lambda { "abc"[index, 2] = "xyz" }.should raise_error(TypeError)
end
it "raises a TypeError if #to_int for count does not return an Integer" do
count = mock("string element set count")
count.should_receive(:to_int).and_return("1")
lambda { "abc"[1, count] = "xyz" }.should raise_error(TypeError)
end
it "calls #to_str to convert the replacement object" do
r = mock("string element set replacement")
r.should_receive(:to_str).and_return("xyz")
str = "abcde"
str[2, 2] = r
str.should == "abxyze"
end
it "raises a TypeError of #to_str does not return a String" do
r = mock("string element set replacement")
r.should_receive(:to_str).and_return(nil)
lambda { "abc"[1, 1] = r }.should raise_error(TypeError)
end
it "raises an IndexError if |idx| is greater than the length of the string" do
lambda { "hello"[6, 0] = "bob" }.should raise_error(IndexError)
lambda { "hello"[-6, 0] = "bob" }.should raise_error(IndexError)
end
it "raises an IndexError if count < 0" do
lambda { "hello"[0, -1] = "bob" }.should raise_error(IndexError)
lambda { "hello"[1, -1] = "bob" }.should raise_error(IndexError)
end
it "raises a TypeError if other_str is a type other than String" do
lambda { "hello"[0, 2] = nil }.should raise_error(TypeError)
lambda { "hello"[0, 2] = [] }.should raise_error(TypeError)
lambda { "hello"[0, 2] = 33 }.should raise_error(TypeError)
end
with_feature :encoding do
it "replaces characters with a multibyte character" do
str = "ありgaとう"
str[2, 2] = ""
str.should == "ありがとう"
end
it "replaces multibyte characters with characters" do
str = "ありがとう"
str[2, 1] = "ga"
str.should == "ありgaとう"
end
it "replaces multibyte characters with multibyte characters" do
str = "ありがとう"
str[2, 1] = ""
str.should == "ありかとう"
end
it "deletes a multibyte character" do
str = "ありとう"
str[2, 2] = ""
str.should == "あり"
end
it "inserts a multibyte character" do
str = "ありとう"
str[2, 0] = ""
str.should == "ありがとう"
end
it "raises an IndexError if the character index is out of range of a multibyte String" do
lambda { "あれ"[3, 0] = "" }.should raise_error(IndexError)
end
it "encodes the String in an encoding compatible with the replacement" do
str = " ".force_encoding Encoding::US_ASCII
rep = [160].pack('C').force_encoding Encoding::ASCII_8BIT
str[0, 1] = rep
str.encoding.should equal(Encoding::ASCII_8BIT)
end
it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do
str = "あれ"
rep = "".encode Encoding::EUC_JP
lambda { str[0, 1] = rep }.should raise_error(Encoding::CompatibilityError)
end
end
end

View file

@ -0,0 +1,12 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#empty?" do
it "returns true if the string has a length of zero" do
"hello".empty?.should == false
" ".empty?.should == false
"\x00".empty?.should == false
"".empty?.should == true
StringSpecs::MyString.new("").empty?.should == true
end
end

View file

@ -0,0 +1,159 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../shared/encode', __FILE__)
with_feature :encoding do
describe "String#encode" do
before :each do
@external = Encoding.default_external
@internal = Encoding.default_internal
end
after :each do
Encoding.default_external = @external
Encoding.default_internal = @internal
end
it_behaves_like :string_encode, :encode
describe "when passed no options" do
it "returns a copy when Encoding.default_internal is nil" do
Encoding.default_internal = nil
str = ""
str.encode.should_not equal(str)
end
it "returns a copy for a ASCII-only String when Encoding.default_internal is nil" do
Encoding.default_internal = nil
str = "abc"
str.encode.should_not equal(str)
end
it "encodes an ascii substring of a binary string to UTF-8" do
x82 = [0x82].pack('C')
str = "#{x82}foo".force_encoding("ascii-8bit")[1..-1].encode("utf-8")
str.should == "foo".force_encoding("utf-8")
str.encoding.should equal(Encoding::UTF_8)
end
end
describe "when passed to encoding" do
it "returns a copy when passed the same encoding as the String" do
str = ""
str.encode(Encoding::UTF_8).should_not equal(str)
end
it "round trips a String" do
str = "abc def".force_encoding Encoding::US_ASCII
str.encode("utf-32be").encode("ascii").should == "abc def"
end
end
describe "when passed options" do
it "returns a copy when Encoding.default_internal is nil" do
Encoding.default_internal = nil
str = ""
str.encode(invalid: :replace).should_not equal(str)
end
it "normalizes newlines" do
"\r\nfoo".encode(universal_newline: true).should == "\nfoo"
"\rfoo".encode(universal_newline: true).should == "\nfoo"
end
end
describe "when passed to, from" do
it "returns a copy in the destination encoding when both encodings are the same" do
str = ""
str.force_encoding("ascii-8bit")
encoded = str.encode("utf-8", "utf-8")
encoded.should_not equal(str)
encoded.encoding.should == Encoding::UTF_8
end
it "returns the transcoded string" do
str = "\x00\x00\x00\x1F"
str.encode(Encoding::UTF_8, Encoding::UTF_16BE).should == "\u0000\u001f"
end
end
describe "when passed to, options" do
it "returns a copy when the destination encoding is the same as the String encoding" do
str = ""
str.encode(Encoding::UTF_8, undef: :replace).should_not equal(str)
end
end
describe "when passed to, from, options" do
it "returns a copy when both encodings are the same" do
str = ""
str.encode("utf-8", "utf-8", invalid: :replace).should_not equal(str)
end
end
end
describe "String#encode!" do
before :each do
@external = Encoding.default_external
@internal = Encoding.default_internal
end
after :each do
Encoding.default_external = @external
Encoding.default_internal = @internal
end
it_behaves_like :string_encode, :encode!
it "raises a RuntimeError when called on a frozen String" do
lambda { "foo".freeze.encode!("euc-jp") }.should raise_error(RuntimeError)
end
# http://redmine.ruby-lang.org/issues/show/1836
it "raises a RuntimeError when called on a frozen String when it's a no-op" do
lambda { "foo".freeze.encode!("utf-8") }.should raise_error(RuntimeError)
end
describe "when passed no options" do
it "returns self when Encoding.default_internal is nil" do
Encoding.default_internal = nil
str = ""
str.encode!.should equal(str)
end
it "returns self for a ASCII-only String when Encoding.default_internal is nil" do
Encoding.default_internal = nil
str = "abc"
str.encode!.should equal(str)
end
end
describe "when passed options" do
it "returns self for ASCII-only String when Encoding.default_internal is nil" do
Encoding.default_internal = nil
str = "abc"
str.encode!(invalid: :replace).should equal(str)
end
end
describe "when passed to encoding" do
it "returns self" do
str = "abc"
result = str.encode!(Encoding::BINARY)
result.encoding.should equal(Encoding::BINARY)
result.should equal(str)
end
end
describe "when passed to, from" do
it "returns self" do
str = "ああ"
result = str.encode!("euc-jp", "utf-8")
result.encoding.should equal(Encoding::EUC_JP)
result.should equal(str)
end
end
end
end

View file

@ -0,0 +1,189 @@
# -*- encoding: us-ascii -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/iso-8859-9-encoding', __FILE__)
with_feature :encoding do
describe "String#encoding" do
it "returns an Encoding object" do
String.new.encoding.should be_an_instance_of(Encoding)
end
it "is equal to the source encoding by default" do
s = StringSpecs::ISO88599Encoding.new
s.cedilla.encoding.should == s.source_encoding
end
it "returns the given encoding if #force_encoding has been called" do
"a".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
end
it "returns the given encoding if #encode!has been called" do
"a".encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
end
end
describe "String#encoding for US-ASCII Strings" do
it "returns US-ASCII if self is US-ASCII" do
"a".encoding.should == Encoding::US_ASCII
end
it "returns US-ASCII if self is US-ASCII only, despite the default internal encoding being different" do
default_internal = Encoding.default_internal
Encoding.default_internal = Encoding::UTF_8
"a".encoding.should == Encoding::US_ASCII
Encoding.default_internal = default_internal
end
it "returns US-ASCII if self is US-ASCII only, despite the default external encoding being different" do
default_external = Encoding.default_external
Encoding.default_external = Encoding::UTF_8
"a".encoding.should == Encoding::US_ASCII
Encoding.default_external = default_external
end
it "returns US-ASCII if self is US-ASCII only, despite the default internal and external encodings being different" do
default_internal = Encoding.default_internal
default_external = Encoding.default_external
Encoding.default_internal = Encoding::UTF_8
Encoding.default_external = Encoding::UTF_8
"a".encoding.should == Encoding::US_ASCII
Encoding.default_external = default_external
Encoding.default_internal = default_internal
end
it "returns US-ASCII if self is US-ASCII only, despite the default encodings being different" do
default_internal = Encoding.default_internal
default_external = Encoding.default_external
Encoding.default_internal = Encoding::UTF_8
Encoding.default_external = Encoding::UTF_8
"a".encoding.should == Encoding::US_ASCII
Encoding.default_external = default_external
Encoding.default_internal = default_internal
end
end
describe "String#encoding for Strings with \\u escapes" do
it "returns UTF-8" do
"\u{4040}".encoding.should == Encoding::UTF_8
end
it "returns US-ASCII if self is US-ASCII only" do
s = "\u{40}"
s.ascii_only?.should be_true
s.encoding.should == Encoding::US_ASCII
end
it "returns UTF-8 if self isn't US-ASCII only" do
s = "\u{4076}\u{619}"
s.ascii_only?.should be_false
s.encoding.should == Encoding::UTF_8
end
it "is not affected by the default internal encoding" do
default_internal = Encoding.default_internal
Encoding.default_internal = Encoding::ISO_8859_15
"\u{5050}".encoding.should == Encoding::UTF_8
"\u{50}".encoding.should == Encoding::US_ASCII
Encoding.default_internal = default_internal
end
it "is not affected by the default external encoding" do
default_external = Encoding.default_external
Encoding.default_external = Encoding::SHIFT_JIS
"\u{50}".encoding.should == Encoding::US_ASCII
"\u{5050}".encoding.should == Encoding::UTF_8
Encoding.default_external = default_external
end
it "is not affected by both the default internal and external encoding being set at the same time" do
default_internal = Encoding.default_internal
default_external = Encoding.default_external
Encoding.default_internal = Encoding::EUC_JP
Encoding.default_external = Encoding::SHIFT_JIS
"\u{50}".encoding.should == Encoding::US_ASCII
"\u{507}".encoding.should == Encoding::UTF_8
Encoding.default_external = default_external
Encoding.default_internal = default_internal
end
it "returns the given encoding if #force_encoding has been called" do
"\u{20}".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
"\u{2020}".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
end
it "returns the given encoding if #encode!has been called" do
"\u{20}".encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
"\u{2020}".encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
end
end
describe "String#encoding for Strings with \\x escapes" do
it "returns US-ASCII if self is US-ASCII only" do
s = "\x61"
s.ascii_only?.should be_true
s.encoding.should == Encoding::US_ASCII
end
it "returns ASCII-8BIT when an escape creates a byte with the 8th bit set if the source encoding is US-ASCII" do
__ENCODING__.should == Encoding::US_ASCII
str = " "
str.encoding.should == Encoding::US_ASCII
str += [0xDF].pack('C')
str.ascii_only?.should be_false
str.encoding.should == Encoding::ASCII_8BIT
end
# TODO: Deal with case when the byte in question isn't valid in the source
# encoding?
it "returns the source encoding when an escape creates a byte with the 8th bit set if the source encoding isn't US-ASCII" do
fixture = StringSpecs::ISO88599Encoding.new
fixture.source_encoding.should == Encoding::ISO8859_9
fixture.x_escape.ascii_only?.should be_false
fixture.x_escape.encoding.should == Encoding::ISO8859_9
end
it "is not affected by the default internal encoding" do
default_internal = Encoding.default_internal
Encoding.default_internal = Encoding::ISO_8859_15
"\x50".encoding.should == Encoding::US_ASCII
"\x50".encoding.should == Encoding::US_ASCII
Encoding.default_internal = default_internal
end
it "is not affected by the default external encoding" do
default_external = Encoding.default_external
Encoding.default_external = Encoding::SHIFT_JIS
"\x50".encoding.should == Encoding::US_ASCII
[0xD4].pack('C').encoding.should == Encoding::ASCII_8BIT
Encoding.default_external = default_external
end
it "is not affected by both the default internal and external encoding being set at the same time" do
default_internal = Encoding.default_internal
default_external = Encoding.default_external
Encoding.default_internal = Encoding::EUC_JP
Encoding.default_external = Encoding::SHIFT_JIS
x50 = "\x50"
x50.encoding.should == Encoding::US_ASCII
[0xD4].pack('C').encoding.should == Encoding::ASCII_8BIT
Encoding.default_external = default_external
Encoding.default_internal = default_internal
end
it "returns the given encoding if #force_encoding has been called" do
x50 = "\x50"
x50.force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
xD4 = [212].pack('C')
xD4.force_encoding(Encoding::ISO_8859_9).encoding.should == Encoding::ISO_8859_9
end
it "returns the given encoding if #encode!has been called" do
x50 = "\x50"
x50.encode!(Encoding::SHIFT_JIS).encoding.should == Encoding::SHIFT_JIS
x00 = "x\00"
x00.encode!(Encoding::UTF_8).encoding.should == Encoding::UTF_8
end
end
end

View file

@ -0,0 +1,50 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#end_with?" do
it "returns true only if ends match" do
s = "hello"
s.end_with?('o').should be_true
s.end_with?('llo').should be_true
end
it 'returns false if the end does not match' do
s = 'hello'
s.end_with?('ll').should be_false
end
it "returns true if the search string is empty" do
"hello".end_with?("").should be_true
"".end_with?("").should be_true
end
it "returns true only if any ending match" do
"hello".end_with?('x', 'y', 'llo', 'z').should be_true
end
it "converts its argument using :to_str" do
s = "hello"
find = mock('o')
find.should_receive(:to_str).and_return("o")
s.end_with?(find).should be_true
end
it "ignores arguments not convertible to string" do
"hello".end_with?().should be_false
lambda { "hello".end_with?(1) }.should raise_error(TypeError)
lambda { "hello".end_with?(["o"]) }.should raise_error(TypeError)
lambda { "hello".end_with?(1, nil, "o") }.should raise_error(TypeError)
end
it "uses only the needed arguments" do
find = mock('h')
find.should_not_receive(:to_str)
"hello".end_with?("o",find).should be_true
end
it "works for multibyte strings" do
"céréale".end_with?("réale").should be_true
end
end

View file

@ -0,0 +1,21 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../shared/eql', __FILE__)
describe "String#eql?" do
it_behaves_like(:string_eql_value, :eql?)
describe "when given a non-String" do
it "returns false" do
'hello'.should_not eql(5)
not_supported_on :opal do
'hello'.should_not eql(:hello)
end
'hello'.should_not eql(mock('x'))
end
it "does not try to call #to_str on the given argument" do
(obj = mock('x')).should_not_receive(:to_str)
'hello'.should_not eql(obj)
end
end
end

View file

@ -0,0 +1,8 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../shared/eql', __FILE__)
require File.expand_path('../shared/equal_value', __FILE__)
describe "String#==" do
it_behaves_like(:string_eql_value, :==)
it_behaves_like(:string_equal_value, :==)
end

View file

@ -0,0 +1,49 @@
class Object
# This helper is defined here rather than in MSpec because
# it is only used in #unpack specs.
def unpack_format(count=nil, repeat=nil)
format = "#{instance_variable_get(:@method)}#{count}"
format *= repeat if repeat
format
end
end
module StringSpecs
class MyString < String; end
class MyArray < Array; end
class MyRange < Range; end
class SubString < String
attr_reader :special
def initialize(str=nil)
@special = str
end
end
class InitializeString < String
attr_reader :ivar
def initialize(other)
super
@ivar = 1
end
def initialize_copy(other)
ScratchPad.record object_id
end
end
module StringModule
def repr
1
end
end
class StringWithRaisingConstructor < String
def initialize(str)
raise ArgumentError.new('constructor was called') unless str == 'silly:string'
self.replace(str)
end
end
end

View file

@ -0,0 +1,3 @@
# frozen_string_literal: true
print (+ 'frozen string').frozen? ? 'immutable' : 'mutable'

View file

@ -0,0 +1,9 @@
# -*- encoding: iso-8859-9 -*-
module StringSpecs
class ISO88599Encoding
def source_encoding; __ENCODING__; end
def x_escape; [0xDF].pack('C').force_encoding("iso-8859-9"); end
def ascii_only; "glark"; end
def cedilla; "Ş"; end
end
end

View file

@ -0,0 +1,7 @@
# -*- encoding: utf-8 -*-
module StringSpecs
class UTF8Encoding
def self.source_encoding; __ENCODING__; end
def self.egrave; "é"; end
end
end

View file

@ -0,0 +1,53 @@
require File.expand_path('../../../spec_helper', __FILE__)
with_feature :encoding do
describe "String#force_encoding" do
it "accepts a String as the name of an Encoding" do
"abc".force_encoding('shift_jis').encoding.should == Encoding::Shift_JIS
end
it "accepts an Encoding instance" do
"abc".force_encoding(Encoding::SHIFT_JIS).encoding.should == Encoding::Shift_JIS
end
it "calls #to_str to convert an object to an encoding name" do
obj = mock("force_encoding")
obj.should_receive(:to_str).and_return("utf-8")
"abc".force_encoding(obj).encoding.should == Encoding::UTF_8
end
it "raises a TypeError if #to_str does not return a String" do
obj = mock("force_encoding")
obj.should_receive(:to_str).and_return(1)
lambda { "abc".force_encoding(obj) }.should raise_error(TypeError)
end
it "raises a TypeError if passed nil" do
lambda { "abc".force_encoding(nil) }.should raise_error(TypeError)
end
it "returns self" do
str = "abc"
str.force_encoding('utf-8').should equal(str)
end
it "sets the encoding even if the String contents are invalid in that encoding" do
str = "\u{9765}"
str.force_encoding('euc-jp')
str.encoding.should == Encoding::EUC_JP
str.valid_encoding?.should be_false
end
it "does not transcode self" do
str = "\u{8612}"
str.dup.force_encoding('utf-16le').should_not == str.encode('utf-16le')
end
it "raises a RuntimeError if self is frozen" do
str = "abcd".freeze
lambda { str.force_encoding(str.encoding) }.should raise_error(RuntimeError)
end
end
end

View file

@ -0,0 +1,18 @@
require File.expand_path('../../../spec_helper', __FILE__)
describe "String#freeze" do
it "produces the same object whenever called on an instance of a literal in the source" do
ids = Array.new(2) { "abc".freeze.object_id }
ids.first.should == ids.last
end
it "doesn't produce the same object for different instances of literals in the source" do
"abc".object_id.should_not == "abc".object_id
end
it "being a special form doesn't change the value of defined?" do
defined?("abc".freeze).should == "method"
end
end

View file

@ -0,0 +1,69 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
describe "String#getbyte" do
it "returns an Integer if given a valid index" do
"a".getbyte(0).should be_kind_of(Integer)
end
it "starts indexing at 0" do
"b".getbyte(0).should == 98
# copy-on-write case
_str1, str2 = "fooXbar".split("X")
str2.getbyte(0).should == 98
end
it "counts from the end of the String if given a negative argument" do
"glark".getbyte(-1).should == "glark".getbyte(4)
# copy-on-write case
_str1, str2 = "fooXbar".split("X")
str2.getbyte(-1).should == 114
end
it "returns an Integer between 0 and 255" do
"\x00".getbyte(0).should == 0
[0xFF].pack('C').getbyte(0).should == 255
256.chr('utf-8').getbyte(0).should == 196
256.chr('utf-8').getbyte(1).should == 128
end
it "regards a multi-byte character as having multiple bytes" do
chr = "\u{998}"
chr.bytesize.should == 3
chr.getbyte(0).should == 224
chr.getbyte(1).should == 166
chr.getbyte(2).should == 152
end
it "mirrors the output of #bytes" do
xDE = [0xDE].pack('C').force_encoding('utf-8')
str = "UTF-8 (\u{9865}} characters and hex escapes (#{xDE})"
str.bytes.to_a.each_with_index do |byte, index|
str.getbyte(index).should == byte
end
end
it "interprets bytes relative to the String's encoding" do
str = "\u{333}"
str.encode('utf-8').getbyte(0).should_not == str.encode('utf-16le').getbyte(0)
end
it "returns nil for out-of-bound indexes" do
"g".getbyte(1).should be_nil
end
it "regards the empty String as containing no bytes" do
"".getbyte(0).should be_nil
end
it "raises an ArgumentError unless given one argument" do
lambda { "glark".getbyte }.should raise_error(ArgumentError)
lambda { "food".getbyte(0,0) }.should raise_error(ArgumentError)
end
it "raises a TypeError unless its argument can be coerced into an Integer" do
lambda { "a".getbyte('a') }.should raise_error(TypeError)
end
end

View file

@ -0,0 +1,696 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe :string_gsub_named_capture, shared: true do
it "replaces \\k named backreferences with the regexp's corresponding capture" do
str = "hello"
str.gsub(/(?<foo>[aeiou])/, '<\k<foo>>').should == "h<e>ll<o>"
str.gsub(/(?<foo>.)/, '\k<foo>\k<foo>').should == "hheelllloo"
end
end
describe "String#gsub with pattern and replacement" do
it "inserts the replacement around every character when the pattern collapses" do
"hello".gsub(//, ".").should == ".h.e.l.l.o."
end
it "respects unicode when the pattern collapses" do
str = "こにちわ"
reg = %r!!
str.gsub(reg, ".").should == ".こ.に.ち.わ."
end
it "doesn't freak out when replacing ^" do
"Text\n".gsub(/^/, ' ').should == " Text\n"
"Text\nFoo".gsub(/^/, ' ').should == " Text\n Foo"
end
it "returns a copy of self with all occurrences of pattern replaced with replacement" do
"hello".gsub(/[aeiou]/, '*').should == "h*ll*"
str = "hello homely world. hah!"
str.gsub(/\Ah\S+\s*/, "huh? ").should == "huh? homely world. hah!"
str = "¿por qué?"
str.gsub(/([a-z\d]*)/, "*").should == "*¿** **é*?*"
end
it "ignores a block if supplied" do
"food".gsub(/f/, "g") { "w" }.should == "good"
end
it "supports \\G which matches at the beginning of the remaining (non-matched) string" do
str = "hello homely world. hah!"
str.gsub(/\Gh\S+\s*/, "huh? ").should == "huh? huh? world. hah!"
end
it "supports /i for ignoring case" do
str = "Hello. How happy are you?"
str.gsub(/h/i, "j").should == "jello. jow jappy are you?"
str.gsub(/H/i, "j").should == "jello. jow jappy are you?"
end
it "doesn't interpret regexp metacharacters if pattern is a string" do
"12345".gsub('\d', 'a').should == "12345"
'\d'.gsub('\d', 'a').should == "a"
end
it "replaces \\1 sequences with the regexp's corresponding capture" do
str = "hello"
str.gsub(/([aeiou])/, '<\1>').should == "h<e>ll<o>"
str.gsub(/(.)/, '\1\1').should == "hheelllloo"
str.gsub(/.(.?)/, '<\0>(\1)').should == "<he>(e)<ll>(l)<o>()"
str.gsub(/.(.)+/, '\1').should == "o"
str = "ABCDEFGHIJKLabcdefghijkl"
re = /#{"(.)" * 12}/
str.gsub(re, '\1').should == "Aa"
str.gsub(re, '\9').should == "Ii"
# Only the first 9 captures can be accessed in MRI
str.gsub(re, '\10').should == "A0a0"
end
it "treats \\1 sequences without corresponding captures as empty strings" do
str = "hello!"
str.gsub("", '<\1>').should == "<>h<>e<>l<>l<>o<>!<>"
str.gsub("h", '<\1>').should == "<>ello!"
str.gsub(//, '<\1>').should == "<>h<>e<>l<>l<>o<>!<>"
str.gsub(/./, '\1\2\3').should == ""
str.gsub(/.(.{20})?/, '\1').should == ""
end
it "replaces \\& and \\0 with the complete match" do
str = "hello!"
str.gsub("", '<\0>').should == "<>h<>e<>l<>l<>o<>!<>"
str.gsub("", '<\&>').should == "<>h<>e<>l<>l<>o<>!<>"
str.gsub("he", '<\0>').should == "<he>llo!"
str.gsub("he", '<\&>').should == "<he>llo!"
str.gsub("l", '<\0>').should == "he<l><l>o!"
str.gsub("l", '<\&>').should == "he<l><l>o!"
str.gsub(//, '<\0>').should == "<>h<>e<>l<>l<>o<>!<>"
str.gsub(//, '<\&>').should == "<>h<>e<>l<>l<>o<>!<>"
str.gsub(/../, '<\0>').should == "<he><ll><o!>"
str.gsub(/../, '<\&>').should == "<he><ll><o!>"
str.gsub(/(.)./, '<\0>').should == "<he><ll><o!>"
end
it "replaces \\` with everything before the current match" do
str = "hello!"
str.gsub("", '<\`>').should == "<>h<h>e<he>l<hel>l<hell>o<hello>!<hello!>"
str.gsub("h", '<\`>').should == "<>ello!"
str.gsub("l", '<\`>').should == "he<he><hel>o!"
str.gsub("!", '<\`>').should == "hello<hello>"
str.gsub(//, '<\`>').should == "<>h<h>e<he>l<hel>l<hell>o<hello>!<hello!>"
str.gsub(/../, '<\`>').should == "<><he><hell>"
end
it "replaces \\' with everything after the current match" do
str = "hello!"
str.gsub("", '<\\\'>').should == "<hello!>h<ello!>e<llo!>l<lo!>l<o!>o<!>!<>"
str.gsub("h", '<\\\'>').should == "<ello!>ello!"
str.gsub("ll", '<\\\'>').should == "he<o!>o!"
str.gsub("!", '<\\\'>').should == "hello<>"
str.gsub(//, '<\\\'>').should == "<hello!>h<ello!>e<llo!>l<lo!>l<o!>o<!>!<>"
str.gsub(/../, '<\\\'>').should == "<llo!><o!><>"
end
it "replaces \\+ with the last paren that actually matched" do
str = "hello!"
str.gsub(/(.)(.)/, '\+').should == "el!"
str.gsub(/(.)(.)+/, '\+').should == "!"
str.gsub(/(.)()/, '\+').should == ""
str.gsub(/(.)(.{20})?/, '<\+>').should == "<h><e><l><l><o><!>"
str = "ABCDEFGHIJKLabcdefghijkl"
re = /#{"(.)" * 12}/
str.gsub(re, '\+').should == "Ll"
end
it "treats \\+ as an empty string if there was no captures" do
"hello!".gsub(/./, '\+').should == ""
end
it "maps \\\\ in replacement to \\" do
"hello".gsub(/./, '\\\\').should == '\\' * 5
end
it "leaves unknown \\x escapes in replacement untouched" do
"hello".gsub(/./, '\\x').should == '\\x' * 5
"hello".gsub(/./, '\\y').should == '\\y' * 5
end
it "leaves \\ at the end of replacement untouched" do
"hello".gsub(/./, 'hah\\').should == 'hah\\' * 5
end
it_behaves_like :string_gsub_named_capture, :gsub
it "taints the result if the original string or replacement is tainted" do
hello = "hello"
hello_t = "hello"
a = "a"
a_t = "a"
empty = ""
empty_t = ""
hello_t.taint; a_t.taint; empty_t.taint
hello_t.gsub(/./, a).tainted?.should == true
hello_t.gsub(/./, empty).tainted?.should == true
hello.gsub(/./, a_t).tainted?.should == true
hello.gsub(/./, empty_t).tainted?.should == true
hello.gsub(//, empty_t).tainted?.should == true
hello.gsub(//.taint, "foo").tainted?.should == false
end
it "handles pattern collapse" do
str = "こにちわ"
reg = %r!!
str.gsub(reg, ".").should == ".こ.に.ち.わ."
end
it "untrusts the result if the original string or replacement is untrusted" do
hello = "hello"
hello_t = "hello"
a = "a"
a_t = "a"
empty = ""
empty_t = ""
hello_t.untrust; a_t.untrust; empty_t.untrust
hello_t.gsub(/./, a).untrusted?.should == true
hello_t.gsub(/./, empty).untrusted?.should == true
hello.gsub(/./, a_t).untrusted?.should == true
hello.gsub(/./, empty_t).untrusted?.should == true
hello.gsub(//, empty_t).untrusted?.should == true
hello.gsub(//.untrust, "foo").untrusted?.should == false
end
it "tries to convert pattern to a string using to_str" do
pattern = mock('.')
def pattern.to_str() "." end
"hello.".gsub(pattern, "!").should == "hello!"
end
it "raises a TypeError when pattern can't be converted to a string" do
lambda { "hello".gsub([], "x") }.should raise_error(TypeError)
lambda { "hello".gsub(Object.new, "x") }.should raise_error(TypeError)
lambda { "hello".gsub(nil, "x") }.should raise_error(TypeError)
end
it "tries to convert replacement to a string using to_str" do
replacement = mock('hello_replacement')
def replacement.to_str() "hello_replacement" end
"hello".gsub(/hello/, replacement).should == "hello_replacement"
end
it "raises a TypeError when replacement can't be converted to a string" do
lambda { "hello".gsub(/[aeiou]/, []) }.should raise_error(TypeError)
lambda { "hello".gsub(/[aeiou]/, Object.new) }.should raise_error(TypeError)
lambda { "hello".gsub(/[aeiou]/, nil) }.should raise_error(TypeError)
end
it "returns subclass instances when called on a subclass" do
StringSpecs::MyString.new("").gsub(//, "").should be_an_instance_of(StringSpecs::MyString)
StringSpecs::MyString.new("").gsub(/foo/, "").should be_an_instance_of(StringSpecs::MyString)
StringSpecs::MyString.new("foo").gsub(/foo/, "").should be_an_instance_of(StringSpecs::MyString)
StringSpecs::MyString.new("foo").gsub("foo", "").should be_an_instance_of(StringSpecs::MyString)
end
# Note: $~ cannot be tested because mspec messes with it
it "sets $~ to MatchData of last match and nil when there's none" do
'hello.'.gsub('hello', 'x')
$~[0].should == 'hello'
'hello.'.gsub('not', 'x')
$~.should == nil
'hello.'.gsub(/.(.)/, 'x')
$~[0].should == 'o.'
'hello.'.gsub(/not/, 'x')
$~.should == nil
end
end
describe "String#gsub with pattern and Hash" do
it "returns a copy of self with all occurrences of pattern replaced with the value of the corresponding hash key" do
"hello".gsub(/./, 'l' => 'L').should == "LL"
"hello!".gsub(/(.)(.)/, 'he' => 'she ', 'll' => 'said').should == 'she said'
"hello".gsub('l', 'l' => 'el').should == 'heelelo'
end
it "ignores keys that don't correspond to matches" do
"hello".gsub(/./, 'z' => 'L', 'h' => 'b', 'o' => 'ow').should == "bow"
end
it "returns an empty string if the pattern matches but the hash specifies no replacements" do
"hello".gsub(/./, 'z' => 'L').should == ""
end
it "ignores non-String keys" do
"tattoo".gsub(/(tt)/, 'tt' => 'b', tt: 'z').should == "taboo"
end
it "uses a key's value as many times as needed" do
"food".gsub(/o/, 'o' => '0').should == "f00d"
end
it "uses the hash's default value for missing keys" do
hsh = {}
hsh.default='?'
hsh['o'] = '0'
"food".gsub(/./, hsh).should == "?00?"
end
it "coerces the hash values with #to_s" do
hsh = {}
hsh.default=[]
hsh['o'] = 0
obj = mock('!')
obj.should_receive(:to_s).and_return('!')
hsh['!'] = obj
"food!".gsub(/./, hsh).should == "[]00[]!"
end
it "uses the hash's value set from default_proc for missing keys" do
hsh = {}
hsh.default_proc = lambda { |k,v| 'lamb' }
"food!".gsub(/./, hsh).should == "lamblamblamblamblamb"
end
it "sets $~ to MatchData of last match and nil when there's none for access from outside" do
'hello.'.gsub('l', 'l' => 'L')
$~.begin(0).should == 3
$~[0].should == 'l'
'hello.'.gsub('not', 'ot' => 'to')
$~.should == nil
'hello.'.gsub(/.(.)/, 'o' => ' hole')
$~[0].should == 'o.'
'hello.'.gsub(/not/, 'z' => 'glark')
$~.should == nil
end
it "doesn't interpolate special sequences like \\1 for the block's return value" do
repl = '\& \0 \1 \` \\\' \+ \\\\ foo'
"hello".gsub(/(.+)/, 'hello' => repl ).should == repl
end
it "untrusts the result if the original string is untrusted" do
str = "Ghana".untrust
str.gsub(/[Aa]na/, 'ana' => '').untrusted?.should be_true
end
it "untrusts the result if a hash value is untrusted" do
str = "Ghana"
str.gsub(/a$/, 'a' => 'di'.untrust).untrusted?.should be_true
end
it "taints the result if the original string is tainted" do
str = "Ghana".taint
str.gsub(/[Aa]na/, 'ana' => '').tainted?.should be_true
end
it "taints the result if a hash value is tainted" do
str = "Ghana"
str.gsub(/a$/, 'a' => 'di'.taint).tainted?.should be_true
end
end
describe "String#gsub! with pattern and Hash" do
it "returns self with all occurrences of pattern replaced with the value of the corresponding hash key" do
"hello".gsub!(/./, 'l' => 'L').should == "LL"
"hello!".gsub!(/(.)(.)/, 'he' => 'she ', 'll' => 'said').should == 'she said'
"hello".gsub!('l', 'l' => 'el').should == 'heelelo'
end
it "ignores keys that don't correspond to matches" do
"hello".gsub!(/./, 'z' => 'L', 'h' => 'b', 'o' => 'ow').should == "bow"
end
it "replaces self with an empty string if the pattern matches but the hash specifies no replacements" do
"hello".gsub!(/./, 'z' => 'L').should == ""
end
it "ignores non-String keys" do
"hello".gsub!(/(ll)/, 'll' => 'r', ll: 'z').should == "hero"
end
it "uses a key's value as many times as needed" do
"food".gsub!(/o/, 'o' => '0').should == "f00d"
end
it "uses the hash's default value for missing keys" do
hsh = {}
hsh.default='?'
hsh['o'] = '0'
"food".gsub!(/./, hsh).should == "?00?"
end
it "coerces the hash values with #to_s" do
hsh = {}
hsh.default=[]
hsh['o'] = 0
obj = mock('!')
obj.should_receive(:to_s).and_return('!')
hsh['!'] = obj
"food!".gsub!(/./, hsh).should == "[]00[]!"
end
it "uses the hash's value set from default_proc for missing keys" do
hsh = {}
hsh.default_proc = lambda { |k,v| 'lamb' }
"food!".gsub!(/./, hsh).should == "lamblamblamblamblamb"
end
it "sets $~ to MatchData of last match and nil when there's none for access from outside" do
'hello.'.gsub!('l', 'l' => 'L')
$~.begin(0).should == 3
$~[0].should == 'l'
'hello.'.gsub!('not', 'ot' => 'to')
$~.should == nil
'hello.'.gsub!(/.(.)/, 'o' => ' hole')
$~[0].should == 'o.'
'hello.'.gsub!(/not/, 'z' => 'glark')
$~.should == nil
end
it "doesn't interpolate special sequences like \\1 for the block's return value" do
repl = '\& \0 \1 \` \\\' \+ \\\\ foo'
"hello".gsub!(/(.+)/, 'hello' => repl ).should == repl
end
it "keeps untrusted state" do
str = "Ghana".untrust
str.gsub!(/[Aa]na/, 'ana' => '').untrusted?.should be_true
end
it "untrusts self if a hash value is untrusted" do
str = "Ghana"
str.gsub!(/a$/, 'a' => 'di'.untrust).untrusted?.should be_true
end
it "keeps tainted state" do
str = "Ghana".taint
str.gsub!(/[Aa]na/, 'ana' => '').tainted?.should be_true
end
it "taints self if a hash value is tainted" do
str = "Ghana"
str.gsub!(/a$/, 'a' => 'di'.taint).tainted?.should be_true
end
end
describe "String#gsub with pattern and block" do
it "returns a copy of self with all occurrences of pattern replaced with the block's return value" do
"hello".gsub(/./) { |s| s.succ + ' ' }.should == "i f m m p "
"hello!".gsub(/(.)(.)/) { |*a| a.inspect }.should == '["he"]["ll"]["o!"]'
"hello".gsub('l') { 'x'}.should == 'hexxo'
end
it "sets $~ for access from the block" do
str = "hello"
str.gsub(/([aeiou])/) { "<#{$~[1]}>" }.should == "h<e>ll<o>"
str.gsub(/([aeiou])/) { "<#{$1}>" }.should == "h<e>ll<o>"
str.gsub("l") { "<#{$~[0]}>" }.should == "he<l><l>o"
offsets = []
str.gsub(/([aeiou])/) do
md = $~
md.string.should == str
offsets << md.offset(0)
str
end.should == "hhellollhello"
offsets.should == [[1, 2], [4, 5]]
end
it "restores $~ after leaving the block" do
[/./, "l"].each do |pattern|
old_md = nil
"hello".gsub(pattern) do
old_md = $~
"ok".match(/./)
"x"
end
$~[0].should == old_md[0]
$~.string.should == "hello"
end
end
it "sets $~ to MatchData of last match and nil when there's none for access from outside" do
'hello.'.gsub('l') { 'x' }
$~.begin(0).should == 3
$~[0].should == 'l'
'hello.'.gsub('not') { 'x' }
$~.should == nil
'hello.'.gsub(/.(.)/) { 'x' }
$~[0].should == 'o.'
'hello.'.gsub(/not/) { 'x' }
$~.should == nil
end
it "doesn't interpolate special sequences like \\1 for the block's return value" do
repl = '\& \0 \1 \` \\\' \+ \\\\ foo'
"hello".gsub(/(.+)/) { repl }.should == repl
end
it "converts the block's return value to a string using to_s" do
replacement = mock('hello_replacement')
def replacement.to_s() "hello_replacement" end
"hello".gsub(/hello/) { replacement }.should == "hello_replacement"
obj = mock('ok')
def obj.to_s() "ok" end
"hello".gsub(/.+/) { obj }.should == "ok"
end
it "untrusts the result if the original string or replacement is untrusted" do
hello = "hello"
hello_t = "hello"
a = "a"
a_t = "a"
empty = ""
empty_t = ""
hello_t.untrust; a_t.untrust; empty_t.untrust
hello_t.gsub(/./) { a }.untrusted?.should == true
hello_t.gsub(/./) { empty }.untrusted?.should == true
hello.gsub(/./) { a_t }.untrusted?.should == true
hello.gsub(/./) { empty_t }.untrusted?.should == true
hello.gsub(//) { empty_t }.untrusted?.should == true
hello.gsub(//.untrust) { "foo" }.untrusted?.should == false
end
it "uses the compatible encoding if they are compatible" do
s = "hello"
s2 = "#{195.chr}#{192.chr}#{195.chr}"
s.gsub(/l/) { |bar| 195.chr }.encoding.should == Encoding::ASCII_8BIT
s2.gsub("#{192.chr}") { |bar| "hello" }.encoding.should == Encoding::ASCII_8BIT
end
it "raises an Encoding::CompatibilityError if the encodings are not compatible" do
s = "hllëllo"
s2 = "hellö"
lambda { s.gsub(/l/) { |bar| "Русский".force_encoding("iso-8859-5") } }.should raise_error(Encoding::CompatibilityError)
lambda { s2.gsub(/l/) { |bar| "Русский".force_encoding("iso-8859-5") } }.should raise_error(Encoding::CompatibilityError)
end
it "replaces the incompatible part properly even if the encodings are not compatible" do
s = "hllëllo"
s.gsub(/ë/) { |bar| "Русский".force_encoding("iso-8859-5") }.encoding.should == Encoding::ISO_8859_5
end
not_supported_on :opal do
it "raises an ArgumentError if encoding is not valid" do
x92 = [0x92].pack('C').force_encoding('utf-8')
lambda { "a#{x92}b".gsub(/[^\x00-\x7f]/u, '') }.should raise_error(ArgumentError)
end
end
end
describe "String#gsub with pattern and without replacement and block" do
it "returns an enumerator" do
enum = "abca".gsub(/a/)
enum.should be_an_instance_of(Enumerator)
enum.to_a.should == ["a", "a"]
end
describe "returned Enumerator" do
describe "size" do
it "should return nil" do
"abca".gsub(/a/).size.should == nil
end
end
end
end
describe "String#gsub! with pattern and replacement" do
it "modifies self in place and returns self" do
a = "hello"
a.gsub!(/[aeiou]/, '*').should equal(a)
a.should == "h*ll*"
end
it "modifies self in place with multi-byte characters and returns self" do
a = "¿por qué?"
a.gsub!(/([a-z\d]*)/, "*").should equal(a)
a.should == "*¿** **é*?*"
end
it "taints self if replacement is tainted" do
a = "hello"
a.gsub!(/./.taint, "foo").tainted?.should == false
a.gsub!(/./, "foo".taint).tainted?.should == true
end
it "untrusts self if replacement is untrusted" do
a = "hello"
a.gsub!(/./.untrust, "foo").untrusted?.should == false
a.gsub!(/./, "foo".untrust).untrusted?.should == true
end
it "returns nil if no modifications were made" do
a = "hello"
a.gsub!(/z/, '*').should == nil
a.gsub!(/z/, 'z').should == nil
a.should == "hello"
end
# See [ruby-core:23666]
it "raises a RuntimeError when self is frozen" do
s = "hello"
s.freeze
lambda { s.gsub!(/ROAR/, "x") }.should raise_error(RuntimeError)
lambda { s.gsub!(/e/, "e") }.should raise_error(RuntimeError)
lambda { s.gsub!(/[aeiou]/, '*') }.should raise_error(RuntimeError)
end
end
describe "String#gsub! with pattern and block" do
it "modifies self in place and returns self" do
a = "hello"
a.gsub!(/[aeiou]/) { '*' }.should equal(a)
a.should == "h*ll*"
end
it "taints self if block's result is tainted" do
a = "hello"
a.gsub!(/./.taint) { "foo" }.tainted?.should == false
a.gsub!(/./) { "foo".taint }.tainted?.should == true
end
it "untrusts self if block's result is untrusted" do
a = "hello"
a.gsub!(/./.untrust) { "foo" }.untrusted?.should == false
a.gsub!(/./) { "foo".untrust }.untrusted?.should == true
end
it "returns nil if no modifications were made" do
a = "hello"
a.gsub!(/z/) { '*' }.should == nil
a.gsub!(/z/) { 'z' }.should == nil
a.should == "hello"
end
# See [ruby-core:23663]
it "raises a RuntimeError when self is frozen" do
s = "hello"
s.freeze
lambda { s.gsub!(/ROAR/) { "x" } }.should raise_error(RuntimeError)
lambda { s.gsub!(/e/) { "e" } }.should raise_error(RuntimeError)
lambda { s.gsub!(/[aeiou]/) { '*' } }.should raise_error(RuntimeError)
end
it "uses the compatible encoding if they are compatible" do
s = "hello"
s2 = "#{195.chr}#{192.chr}#{195.chr}"
s.gsub!(/l/) { |bar| 195.chr }.encoding.should == Encoding::ASCII_8BIT
s2.gsub!("#{192.chr}") { |bar| "hello" }.encoding.should == Encoding::ASCII_8BIT
end
it "raises an Encoding::CompatibilityError if the encodings are not compatible" do
s = "hllëllo"
s2 = "hellö"
lambda { s.gsub!(/l/) { |bar| "Русский".force_encoding("iso-8859-5") } }.should raise_error(Encoding::CompatibilityError)
lambda { s2.gsub!(/l/) { |bar| "Русский".force_encoding("iso-8859-5") } }.should raise_error(Encoding::CompatibilityError)
end
it "replaces the incompatible part properly even if the encodings are not compatible" do
s = "hllëllo"
s.gsub!(/ë/) { |bar| "Русский".force_encoding("iso-8859-5") }.encoding.should == Encoding::ISO_8859_5
end
not_supported_on :opal do
it "raises an ArgumentError if encoding is not valid" do
x92 = [0x92].pack('C').force_encoding('utf-8')
lambda { "a#{x92}b".gsub!(/[^\x00-\x7f]/u, '') }.should raise_error(ArgumentError)
end
end
end
describe "String#gsub! with pattern and without replacement and block" do
it "returns an enumerator" do
enum = "abca".gsub!(/a/)
enum.should be_an_instance_of(Enumerator)
enum.to_a.should == ["a", "a"]
end
describe "returned Enumerator" do
describe "size" do
it "should return nil" do
"abca".gsub!(/a/).size.should == nil
end
end
end
end

View file

@ -0,0 +1,9 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#hash" do
it "returns a hash based on a string's length and content" do
"abc".hash.should == "abc".hash
"abc".hash.should_not == "cba".hash
end
end

View file

@ -0,0 +1,49 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
# TODO: Move actual results to String#to_int() and spec in terms of it
describe "String#hex" do
it "treats leading characters of self as a string of hex digits" do
"0a".hex.should == 10
"0o".hex.should == 0
"0x".hex.should == 0
"A_BAD_BABE".hex.should == 0xABADBABE
"0b1010".hex.should == "b1010".hex
"0d500".hex.should == "d500".hex
"abcdefG".hex.should == 0xabcdef
end
it "does not accept a sequence of underscores as part of a number" do
"a__b".hex.should == 0xa
"a____b".hex.should == 0xa
"a___f".hex.should == 0xa
end
it "takes an optional sign" do
"-1234".hex.should == -4660
"+1234".hex.should == 4660
end
it "takes an optional 0x" do
"0x0a".hex.should == 10
"0a".hex.should == 10
end
it "requires that the sign is in front of the 0x if present" do
"-0x1".hex.should == -1
"0x-1".hex.should == 0
end
it "returns 0 on error" do
"".hex.should == 0
"+-5".hex.should == 0
"wombat".hex.should == 0
"0x0x42".hex.should == 0
end
it "returns 0 if sequence begins with underscore" do
"_a".hex.should == 0
"___b".hex.should == 0
"___0xc".hex.should == 0
end
end

View file

@ -0,0 +1,28 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#include? with String" do
it "returns true if self contains other_str" do
"hello".include?("lo").should == true
"hello".include?("ol").should == false
end
it "ignores subclass differences" do
"hello".include?(StringSpecs::MyString.new("lo")).should == true
StringSpecs::MyString.new("hello").include?("lo").should == true
StringSpecs::MyString.new("hello").include?(StringSpecs::MyString.new("lo")).should == true
end
it "tries to convert other to string using to_str" do
other = mock('lo')
other.should_receive(:to_str).and_return("lo")
"hello".include?(other).should == true
end
it "raises a TypeError if other can't be converted to string" do
lambda { "hello".include?([]) }.should raise_error(TypeError)
lambda { "hello".include?('h'.ord) }.should raise_error(TypeError)
lambda { "hello".include?(mock('x')) }.should raise_error(TypeError)
end
end

View file

@ -0,0 +1,315 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#index" do
it "raises a TypeError if passed nil" do
lambda { "abc".index nil }.should raise_error(TypeError)
end
it "raises a TypeError if passed a boolean" do
lambda { "abc".index true }.should raise_error(TypeError)
end
it "raises a TypeError if passed a Symbol" do
lambda { "abc".index :a }.should raise_error(TypeError)
end
it "calls #to_str to convert the first argument" do
char = mock("string index char")
char.should_receive(:to_str).and_return("b")
"abc".index(char).should == 1
end
it "calls #to_int to convert the second argument" do
offset = mock("string index offset")
offset.should_receive(:to_int).and_return(1)
"abc".index("c", offset).should == 2
end
it "raises a TypeError if passed a Fixnum" do
lambda { "abc".index 97 }.should raise_error(TypeError)
end
end
describe "String#index with String" do
it "behaves the same as String#index(char) for one-character strings" do
"blablabla hello cruel world...!".split("").uniq.each do |str|
chr = str[0]
str.index(str).should == str.index(chr)
0.upto(str.size + 1) do |start|
str.index(str, start).should == str.index(chr, start)
end
(-str.size - 1).upto(-1) do |start|
str.index(str, start).should == str.index(chr, start)
end
end
end
it "returns the index of the first occurrence of the given substring" do
"blablabla".index("").should == 0
"blablabla".index("b").should == 0
"blablabla".index("bla").should == 0
"blablabla".index("blabla").should == 0
"blablabla".index("blablabla").should == 0
"blablabla".index("l").should == 1
"blablabla".index("la").should == 1
"blablabla".index("labla").should == 1
"blablabla".index("lablabla").should == 1
"blablabla".index("a").should == 2
"blablabla".index("abla").should == 2
"blablabla".index("ablabla").should == 2
end
it "doesn't set $~" do
$~ = nil
'hello.'.index('ll')
$~.should == nil
end
it "ignores string subclasses" do
"blablabla".index(StringSpecs::MyString.new("bla")).should == 0
StringSpecs::MyString.new("blablabla").index("bla").should == 0
StringSpecs::MyString.new("blablabla").index(StringSpecs::MyString.new("bla")).should == 0
end
it "starts the search at the given offset" do
"blablabla".index("bl", 0).should == 0
"blablabla".index("bl", 1).should == 3
"blablabla".index("bl", 2).should == 3
"blablabla".index("bl", 3).should == 3
"blablabla".index("bla", 0).should == 0
"blablabla".index("bla", 1).should == 3
"blablabla".index("bla", 2).should == 3
"blablabla".index("bla", 3).should == 3
"blablabla".index("blab", 0).should == 0
"blablabla".index("blab", 1).should == 3
"blablabla".index("blab", 2).should == 3
"blablabla".index("blab", 3).should == 3
"blablabla".index("la", 1).should == 1
"blablabla".index("la", 2).should == 4
"blablabla".index("la", 3).should == 4
"blablabla".index("la", 4).should == 4
"blablabla".index("lab", 1).should == 1
"blablabla".index("lab", 2).should == 4
"blablabla".index("lab", 3).should == 4
"blablabla".index("lab", 4).should == 4
"blablabla".index("ab", 2).should == 2
"blablabla".index("ab", 3).should == 5
"blablabla".index("ab", 4).should == 5
"blablabla".index("ab", 5).should == 5
"blablabla".index("", 0).should == 0
"blablabla".index("", 1).should == 1
"blablabla".index("", 2).should == 2
"blablabla".index("", 7).should == 7
"blablabla".index("", 8).should == 8
"blablabla".index("", 9).should == 9
end
it "starts the search at offset + self.length if offset is negative" do
str = "blablabla"
["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle|
(-str.length .. -1).each do |offset|
str.index(needle, offset).should ==
str.index(needle, offset + str.length)
end
end
end
it "returns nil if the substring isn't found" do
"blablabla".index("B").should == nil
"blablabla".index("z").should == nil
"blablabla".index("BLA").should == nil
"blablabla".index("blablablabla").should == nil
"blablabla".index("", 10).should == nil
"hello".index("he", 1).should == nil
"hello".index("he", 2).should == nil
end
with_feature :encoding do
it "returns the character index of a multibyte character" do
"ありがとう".index("").should == 2
end
it "returns the character index after offset" do
"われわれ".index("", 1).should == 2
end
it "returns the character index after a partial first match" do
"</</h".index("</h").should == 2
end
it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
char = "".encode Encoding::EUC_JP
lambda do
"あれ".index char
end.should raise_error(Encoding::CompatibilityError)
end
end
end
describe "String#index with Regexp" do
it "behaves the same as String#index(string) for escaped string regexps" do
["blablabla", "hello cruel world...!"].each do |str|
["", "b", "bla", "lab", "o c", "d."].each do |needle|
regexp = Regexp.new(Regexp.escape(needle))
str.index(regexp).should == str.index(needle)
0.upto(str.size + 1) do |start|
str.index(regexp, start).should == str.index(needle, start)
end
(-str.size - 1).upto(-1) do |start|
str.index(regexp, start).should == str.index(needle, start)
end
end
end
end
it "returns the index of the first match of regexp" do
"blablabla".index(/bla/).should == 0
"blablabla".index(/BLA/i).should == 0
"blablabla".index(/.{0}/).should == 0
"blablabla".index(/.{6}/).should == 0
"blablabla".index(/.{9}/).should == 0
"blablabla".index(/.*/).should == 0
"blablabla".index(/.+/).should == 0
"blablabla".index(/lab|b/).should == 0
not_supported_on :opal do
"blablabla".index(/\A/).should == 0
"blablabla".index(/\Z/).should == 9
"blablabla".index(/\z/).should == 9
"blablabla\n".index(/\Z/).should == 9
"blablabla\n".index(/\z/).should == 10
end
"blablabla".index(/^/).should == 0
"\nblablabla".index(/^/).should == 0
"b\nablabla".index(/$/).should == 1
"bl\nablabla".index(/$/).should == 2
"blablabla".index(/.l./).should == 0
end
it "sets $~ to MatchData of match and nil when there's none" do
'hello.'.index(/.(.)/)
$~[0].should == 'he'
'hello.'.index(/not/)
$~.should == nil
end
it "starts the search at the given offset" do
"blablabla".index(/.{0}/, 5).should == 5
"blablabla".index(/.{1}/, 5).should == 5
"blablabla".index(/.{2}/, 5).should == 5
"blablabla".index(/.{3}/, 5).should == 5
"blablabla".index(/.{4}/, 5).should == 5
"blablabla".index(/.{0}/, 3).should == 3
"blablabla".index(/.{1}/, 3).should == 3
"blablabla".index(/.{2}/, 3).should == 3
"blablabla".index(/.{5}/, 3).should == 3
"blablabla".index(/.{6}/, 3).should == 3
"blablabla".index(/.l./, 0).should == 0
"blablabla".index(/.l./, 1).should == 3
"blablabla".index(/.l./, 2).should == 3
"blablabla".index(/.l./, 3).should == 3
"xblaxbla".index(/x./, 0).should == 0
"xblaxbla".index(/x./, 1).should == 4
"xblaxbla".index(/x./, 2).should == 4
not_supported_on :opal do
"blablabla\n".index(/\Z/, 9).should == 9
end
end
it "starts the search at offset + self.length if offset is negative" do
str = "blablabla"
["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle|
(-str.length .. -1).each do |offset|
str.index(needle, offset).should ==
str.index(needle, offset + str.length)
end
end
end
it "returns nil if the substring isn't found" do
"blablabla".index(/BLA/).should == nil
"blablabla".index(/.{10}/).should == nil
"blaxbla".index(/.x/, 3).should == nil
"blaxbla".index(/..x/, 2).should == nil
end
it "returns nil if the Regexp matches the empty string and the offset is out of range" do
"ruby".index(//,12).should be_nil
end
it "supports \\G which matches at the given start offset" do
"helloYOU.".index(/\GYOU/, 5).should == 5
"helloYOU.".index(/\GYOU/).should == nil
re = /\G.+YOU/
# The # marks where \G will match.
[
["#hi!YOUall.", 0],
["h#i!YOUall.", 1],
["hi#!YOUall.", 2],
["hi!#YOUall.", nil]
].each do |spec|
start = spec[0].index("#")
str = spec[0].delete("#")
str.index(re, start).should == spec[1]
end
end
it "converts start_offset to an integer via to_int" do
obj = mock('1')
obj.should_receive(:to_int).and_return(1)
"RWOARW".index(/R./, obj).should == 4
end
with_feature :encoding do
it "returns the character index of a multibyte character" do
"ありがとう".index(//).should == 2
end
it "returns the character index after offset" do
"われわれ".index(//, 1).should == 2
end
it "treats the offset as a character index" do
"われわわれ".index(//, 3).should == 3
end
it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
re = Regexp.new "".encode(Encoding::EUC_JP)
lambda do
"あれ".index re
end.should raise_error(Encoding::CompatibilityError)
end
end
end

View file

@ -0,0 +1,26 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
require File.expand_path('../shared/replace', __FILE__)
describe "String#initialize" do
it "is a private method" do
String.should have_private_instance_method(:initialize)
end
describe "with no arguments" do
it "does not change self" do
s = "some string"
s.send :initialize
s.should == "some string"
end
it "does not raise an exception when frozen" do
a = "hello".freeze
a.send(:initialize).should equal(a)
end
end
describe "with an argument" do
it_behaves_like :string_replace, :initialize
end
end

View file

@ -0,0 +1,84 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#insert with index, other" do
it "inserts other before the character at the given index" do
"abcd".insert(0, 'X').should == "Xabcd"
"abcd".insert(3, 'X').should == "abcXd"
"abcd".insert(4, 'X').should == "abcdX"
end
it "modifies self in place" do
a = "abcd"
a.insert(4, 'X').should == "abcdX"
a.should == "abcdX"
end
it "inserts after the given character on an negative count" do
"abcd".insert(-5, 'X').should == "Xabcd"
"abcd".insert(-3, 'X').should == "abXcd"
"abcd".insert(-1, 'X').should == "abcdX"
end
it "raises an IndexError if the index is beyond string" do
lambda { "abcd".insert(5, 'X') }.should raise_error(IndexError)
lambda { "abcd".insert(-6, 'X') }.should raise_error(IndexError)
end
it "converts index to an integer using to_int" do
other = mock('-3')
other.should_receive(:to_int).and_return(-3)
"abcd".insert(other, "XYZ").should == "abXYZcd"
end
it "converts other to a string using to_str" do
other = mock('XYZ')
other.should_receive(:to_str).and_return("XYZ")
"abcd".insert(-3, other).should == "abXYZcd"
end
it "taints self if string to insert is tainted" do
str = "abcd"
str.insert(0, "T".taint).tainted?.should == true
str = "abcd"
other = mock('T')
def other.to_str() "T".taint end
str.insert(0, other).tainted?.should == true
end
it "raises a TypeError if other can't be converted to string" do
lambda { "abcd".insert(-6, Object.new)}.should raise_error(TypeError)
lambda { "abcd".insert(-6, []) }.should raise_error(TypeError)
lambda { "abcd".insert(-6, mock('x')) }.should raise_error(TypeError)
end
it "raises a RuntimeError if self is frozen" do
str = "abcd".freeze
lambda { str.insert(4, '') }.should raise_error(RuntimeError)
lambda { str.insert(4, 'X') }.should raise_error(RuntimeError)
end
with_feature :encoding do
it "inserts a character into a multibyte encoded string" do
"ありがとう".insert(1, 'ü').should == "あüりがとう"
end
it "returns a String in the compatible encoding" do
str = "".force_encoding(Encoding::US_ASCII)
str.insert(0, "ありがとう")
str.encoding.should == Encoding::UTF_8
end
it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
pat = "".encode Encoding::EUC_JP
lambda do
"あれ".insert 0, pat
end.should raise_error(Encoding::CompatibilityError)
end
end
end

View file

@ -0,0 +1,492 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#inspect" do
it "taints the result if self is tainted" do
"foo".taint.inspect.tainted?.should == true
"foo\n".taint.inspect.tainted?.should == true
end
it "untrusts the result if self is untrusted" do
"foo".untrust.inspect.untrusted?.should == true
"foo\n".untrust.inspect.untrusted?.should == true
end
it "does not return a subclass instance" do
StringSpecs::MyString.new.inspect.should be_an_instance_of(String)
end
it "returns a string with special characters replaced with \\<char> notation" do
[ ["\a", '"\\a"'],
["\b", '"\\b"'],
["\t", '"\\t"'],
["\n", '"\\n"'],
["\v", '"\\v"'],
["\f", '"\\f"'],
["\r", '"\\r"'],
["\e", '"\\e"']
].should be_computed_by(:inspect)
end
it "returns a string with \" and \\ escaped with a backslash" do
[ ["\"", '"\\""'],
["\\", '"\\\\"']
].should be_computed_by(:inspect)
end
it "returns a string with \\#<char> when # is followed by $, @, {" do
[ ["\#$", '"\\#$"'],
["\#@", '"\\#@"'],
["\#{", '"\\#{"']
].should be_computed_by(:inspect)
end
it "returns a string with # not escaped when followed by any other character" do
[ ["#", '"#"'],
["#1", '"#1"']
].should be_computed_by(:inspect)
end
it "returns a string with printable non-alphanumeric characters unescaped" do
[ [" ", '" "'],
["!", '"!"'],
["$", '"$"'],
["%", '"%"'],
["&", '"&"'],
["'", '"\'"'],
["(", '"("'],
[")", '")"'],
["*", '"*"'],
["+", '"+"'],
[",", '","'],
["-", '"-"'],
[".", '"."'],
["/", '"/"'],
[":", '":"'],
[";", '";"'],
["<", '"<"'],
["=", '"="'],
[">", '">"'],
["?", '"?"'],
["@", '"@"'],
["[", '"["'],
["]", '"]"'],
["^", '"^"'],
["_", '"_"'],
["`", '"`"'],
["{", '"{"'],
["|", '"|"'],
["}", '"}"'],
["~", '"~"']
].should be_computed_by(:inspect)
end
it "returns a string with numeric characters unescaped" do
[ ["0", '"0"'],
["1", '"1"'],
["2", '"2"'],
["3", '"3"'],
["4", '"4"'],
["5", '"5"'],
["6", '"6"'],
["7", '"7"'],
["8", '"8"'],
["9", '"9"'],
].should be_computed_by(:inspect)
end
it "returns a string with upper-case alpha characters unescaped" do
[ ["A", '"A"'],
["B", '"B"'],
["C", '"C"'],
["D", '"D"'],
["E", '"E"'],
["F", '"F"'],
["G", '"G"'],
["H", '"H"'],
["I", '"I"'],
["J", '"J"'],
["K", '"K"'],
["L", '"L"'],
["M", '"M"'],
["N", '"N"'],
["O", '"O"'],
["P", '"P"'],
["Q", '"Q"'],
["R", '"R"'],
["S", '"S"'],
["T", '"T"'],
["U", '"U"'],
["V", '"V"'],
["W", '"W"'],
["X", '"X"'],
["Y", '"Y"'],
["Z", '"Z"']
].should be_computed_by(:inspect)
end
it "returns a string with lower-case alpha characters unescaped" do
[ ["a", '"a"'],
["b", '"b"'],
["c", '"c"'],
["d", '"d"'],
["e", '"e"'],
["f", '"f"'],
["g", '"g"'],
["h", '"h"'],
["i", '"i"'],
["j", '"j"'],
["k", '"k"'],
["l", '"l"'],
["m", '"m"'],
["n", '"n"'],
["o", '"o"'],
["p", '"p"'],
["q", '"q"'],
["r", '"r"'],
["s", '"s"'],
["t", '"t"'],
["u", '"u"'],
["v", '"v"'],
["w", '"w"'],
["x", '"x"'],
["y", '"y"'],
["z", '"z"']
].should be_computed_by(:inspect)
end
it "returns a string with non-printing characters replaced by \\x notation" do
# Avoid the file encoding by computing the string with #chr.
[ [0001.chr, '"\\x01"'],
[0002.chr, '"\\x02"'],
[0003.chr, '"\\x03"'],
[0004.chr, '"\\x04"'],
[0005.chr, '"\\x05"'],
[0006.chr, '"\\x06"'],
[0016.chr, '"\\x0E"'],
[0017.chr, '"\\x0F"'],
[0020.chr, '"\\x10"'],
[0021.chr, '"\\x11"'],
[0022.chr, '"\\x12"'],
[0023.chr, '"\\x13"'],
[0024.chr, '"\\x14"'],
[0025.chr, '"\\x15"'],
[0026.chr, '"\\x16"'],
[0027.chr, '"\\x17"'],
[0030.chr, '"\\x18"'],
[0031.chr, '"\\x19"'],
[0032.chr, '"\\x1A"'],
[0034.chr, '"\\x1C"'],
[0035.chr, '"\\x1D"'],
[0036.chr, '"\\x1E"'],
[0037.chr, '"\\x1F"'],
[0177.chr, '"\\x7F"'],
[0200.chr, '"\\x80"'],
[0201.chr, '"\\x81"'],
[0202.chr, '"\\x82"'],
[0203.chr, '"\\x83"'],
[0204.chr, '"\\x84"'],
[0205.chr, '"\\x85"'],
[0206.chr, '"\\x86"'],
[0207.chr, '"\\x87"'],
[0210.chr, '"\\x88"'],
[0211.chr, '"\\x89"'],
[0212.chr, '"\\x8A"'],
[0213.chr, '"\\x8B"'],
[0214.chr, '"\\x8C"'],
[0215.chr, '"\\x8D"'],
[0216.chr, '"\\x8E"'],
[0217.chr, '"\\x8F"'],
[0220.chr, '"\\x90"'],
[0221.chr, '"\\x91"'],
[0222.chr, '"\\x92"'],
[0223.chr, '"\\x93"'],
[0224.chr, '"\\x94"'],
[0225.chr, '"\\x95"'],
[0226.chr, '"\\x96"'],
[0227.chr, '"\\x97"'],
[0230.chr, '"\\x98"'],
[0231.chr, '"\\x99"'],
[0232.chr, '"\\x9A"'],
[0233.chr, '"\\x9B"'],
[0234.chr, '"\\x9C"'],
[0235.chr, '"\\x9D"'],
[0236.chr, '"\\x9E"'],
[0237.chr, '"\\x9F"'],
[0240.chr, '"\\xA0"'],
[0241.chr, '"\\xA1"'],
[0242.chr, '"\\xA2"'],
[0243.chr, '"\\xA3"'],
[0244.chr, '"\\xA4"'],
[0245.chr, '"\\xA5"'],
[0246.chr, '"\\xA6"'],
[0247.chr, '"\\xA7"'],
[0250.chr, '"\\xA8"'],
[0251.chr, '"\\xA9"'],
[0252.chr, '"\\xAA"'],
[0253.chr, '"\\xAB"'],
[0254.chr, '"\\xAC"'],
[0255.chr, '"\\xAD"'],
[0256.chr, '"\\xAE"'],
[0257.chr, '"\\xAF"'],
[0260.chr, '"\\xB0"'],
[0261.chr, '"\\xB1"'],
[0262.chr, '"\\xB2"'],
[0263.chr, '"\\xB3"'],
[0264.chr, '"\\xB4"'],
[0265.chr, '"\\xB5"'],
[0266.chr, '"\\xB6"'],
[0267.chr, '"\\xB7"'],
[0270.chr, '"\\xB8"'],
[0271.chr, '"\\xB9"'],
[0272.chr, '"\\xBA"'],
[0273.chr, '"\\xBB"'],
[0274.chr, '"\\xBC"'],
[0275.chr, '"\\xBD"'],
[0276.chr, '"\\xBE"'],
[0277.chr, '"\\xBF"'],
[0300.chr, '"\\xC0"'],
[0301.chr, '"\\xC1"'],
[0302.chr, '"\\xC2"'],
[0303.chr, '"\\xC3"'],
[0304.chr, '"\\xC4"'],
[0305.chr, '"\\xC5"'],
[0306.chr, '"\\xC6"'],
[0307.chr, '"\\xC7"'],
[0310.chr, '"\\xC8"'],
[0311.chr, '"\\xC9"'],
[0312.chr, '"\\xCA"'],
[0313.chr, '"\\xCB"'],
[0314.chr, '"\\xCC"'],
[0315.chr, '"\\xCD"'],
[0316.chr, '"\\xCE"'],
[0317.chr, '"\\xCF"'],
[0320.chr, '"\\xD0"'],
[0321.chr, '"\\xD1"'],
[0322.chr, '"\\xD2"'],
[0323.chr, '"\\xD3"'],
[0324.chr, '"\\xD4"'],
[0325.chr, '"\\xD5"'],
[0326.chr, '"\\xD6"'],
[0327.chr, '"\\xD7"'],
[0330.chr, '"\\xD8"'],
[0331.chr, '"\\xD9"'],
[0332.chr, '"\\xDA"'],
[0333.chr, '"\\xDB"'],
[0334.chr, '"\\xDC"'],
[0335.chr, '"\\xDD"'],
[0336.chr, '"\\xDE"'],
[0337.chr, '"\\xDF"'],
[0340.chr, '"\\xE0"'],
[0341.chr, '"\\xE1"'],
[0342.chr, '"\\xE2"'],
[0343.chr, '"\\xE3"'],
[0344.chr, '"\\xE4"'],
[0345.chr, '"\\xE5"'],
[0346.chr, '"\\xE6"'],
[0347.chr, '"\\xE7"'],
[0350.chr, '"\\xE8"'],
[0351.chr, '"\\xE9"'],
[0352.chr, '"\\xEA"'],
[0353.chr, '"\\xEB"'],
[0354.chr, '"\\xEC"'],
[0355.chr, '"\\xED"'],
[0356.chr, '"\\xEE"'],
[0357.chr, '"\\xEF"'],
[0360.chr, '"\\xF0"'],
[0361.chr, '"\\xF1"'],
[0362.chr, '"\\xF2"'],
[0363.chr, '"\\xF3"'],
[0364.chr, '"\\xF4"'],
[0365.chr, '"\\xF5"'],
[0366.chr, '"\\xF6"'],
[0367.chr, '"\\xF7"'],
[0370.chr, '"\\xF8"'],
[0371.chr, '"\\xF9"'],
[0372.chr, '"\\xFA"'],
[0373.chr, '"\\xFB"'],
[0374.chr, '"\\xFC"'],
[0375.chr, '"\\xFD"'],
[0376.chr, '"\\xFE"'],
[0377.chr, '"\\xFF"']
].should be_computed_by(:inspect)
end
it "returns a string with a NUL character replaced by \\x notation" do
0.chr.inspect.should == '"\\x00"'
end
describe "when default external is UTF-8" do
before :each do
@extenc, Encoding.default_external = Encoding.default_external, Encoding::UTF_8
end
after :each do
Encoding.default_external = @extenc
end
it "returns a string with non-printing characters replaced by \\u notation for Unicode strings" do
[ [0001.chr('utf-8'), '"\u0001"'],
[0002.chr('utf-8'), '"\u0002"'],
[0003.chr('utf-8'), '"\u0003"'],
[0004.chr('utf-8'), '"\u0004"'],
[0005.chr('utf-8'), '"\u0005"'],
[0006.chr('utf-8'), '"\u0006"'],
[0016.chr('utf-8'), '"\u000E"'],
[0017.chr('utf-8'), '"\u000F"'],
[0020.chr('utf-8'), '"\u0010"'],
[0021.chr('utf-8'), '"\u0011"'],
[0022.chr('utf-8'), '"\u0012"'],
[0023.chr('utf-8'), '"\u0013"'],
[0024.chr('utf-8'), '"\u0014"'],
[0025.chr('utf-8'), '"\u0015"'],
[0026.chr('utf-8'), '"\u0016"'],
[0027.chr('utf-8'), '"\u0017"'],
[0030.chr('utf-8'), '"\u0018"'],
[0031.chr('utf-8'), '"\u0019"'],
[0032.chr('utf-8'), '"\u001A"'],
[0034.chr('utf-8'), '"\u001C"'],
[0035.chr('utf-8'), '"\u001D"'],
[0036.chr('utf-8'), '"\u001E"'],
[0037.chr('utf-8'), '"\u001F"'],
[0177.chr('utf-8'), '"\u007F"'],
[0200.chr('utf-8'), '"\u0080"'],
[0201.chr('utf-8'), '"\u0081"'],
[0202.chr('utf-8'), '"\u0082"'],
[0203.chr('utf-8'), '"\u0083"'],
[0204.chr('utf-8'), '"\u0084"'],
[0206.chr('utf-8'), '"\u0086"'],
[0207.chr('utf-8'), '"\u0087"'],
[0210.chr('utf-8'), '"\u0088"'],
[0211.chr('utf-8'), '"\u0089"'],
[0212.chr('utf-8'), '"\u008A"'],
[0213.chr('utf-8'), '"\u008B"'],
[0214.chr('utf-8'), '"\u008C"'],
[0215.chr('utf-8'), '"\u008D"'],
[0216.chr('utf-8'), '"\u008E"'],
[0217.chr('utf-8'), '"\u008F"'],
[0220.chr('utf-8'), '"\u0090"'],
[0221.chr('utf-8'), '"\u0091"'],
[0222.chr('utf-8'), '"\u0092"'],
[0223.chr('utf-8'), '"\u0093"'],
[0224.chr('utf-8'), '"\u0094"'],
[0225.chr('utf-8'), '"\u0095"'],
[0226.chr('utf-8'), '"\u0096"'],
[0227.chr('utf-8'), '"\u0097"'],
[0230.chr('utf-8'), '"\u0098"'],
[0231.chr('utf-8'), '"\u0099"'],
[0232.chr('utf-8'), '"\u009A"'],
[0233.chr('utf-8'), '"\u009B"'],
[0234.chr('utf-8'), '"\u009C"'],
[0235.chr('utf-8'), '"\u009D"'],
[0236.chr('utf-8'), '"\u009E"'],
[0237.chr('utf-8'), '"\u009F"'],
].should be_computed_by(:inspect)
end
it "returns a string with a NUL character replaced by \\u notation" do
0.chr('utf-8').inspect.should == '"\\u0000"'
end
it "returns a string with extended characters for Unicode strings" do
[ [0240.chr('utf-8'), '" "'],
[0241.chr('utf-8'), '"¡"'],
[0242.chr('utf-8'), '"¢"'],
[0243.chr('utf-8'), '"£"'],
[0244.chr('utf-8'), '"¤"'],
[0245.chr('utf-8'), '"¥"'],
[0246.chr('utf-8'), '"¦"'],
[0247.chr('utf-8'), '"§"'],
[0250.chr('utf-8'), '"¨"'],
[0251.chr('utf-8'), '"©"'],
[0252.chr('utf-8'), '"ª"'],
[0253.chr('utf-8'), '"«"'],
[0254.chr('utf-8'), '"¬"'],
[0255.chr('utf-8'), '"­"'],
[0256.chr('utf-8'), '"®"'],
[0257.chr('utf-8'), '"¯"'],
[0260.chr('utf-8'), '"°"'],
[0261.chr('utf-8'), '"±"'],
[0262.chr('utf-8'), '"²"'],
[0263.chr('utf-8'), '"³"'],
[0264.chr('utf-8'), '"´"'],
[0265.chr('utf-8'), '"µ"'],
[0266.chr('utf-8'), '"¶"'],
[0267.chr('utf-8'), '"·"'],
[0270.chr('utf-8'), '"¸"'],
[0271.chr('utf-8'), '"¹"'],
[0272.chr('utf-8'), '"º"'],
[0273.chr('utf-8'), '"»"'],
[0274.chr('utf-8'), '"¼"'],
[0275.chr('utf-8'), '"½"'],
[0276.chr('utf-8'), '"¾"'],
[0277.chr('utf-8'), '"¿"'],
[0300.chr('utf-8'), '"À"'],
[0301.chr('utf-8'), '"Á"'],
[0302.chr('utf-8'), '"Â"'],
[0303.chr('utf-8'), '"Ã"'],
[0304.chr('utf-8'), '"Ä"'],
[0305.chr('utf-8'), '"Å"'],
[0306.chr('utf-8'), '"Æ"'],
[0307.chr('utf-8'), '"Ç"'],
[0310.chr('utf-8'), '"È"'],
[0311.chr('utf-8'), '"É"'],
[0312.chr('utf-8'), '"Ê"'],
[0313.chr('utf-8'), '"Ë"'],
[0314.chr('utf-8'), '"Ì"'],
[0315.chr('utf-8'), '"Í"'],
[0316.chr('utf-8'), '"Î"'],
[0317.chr('utf-8'), '"Ï"'],
[0320.chr('utf-8'), '"Ð"'],
[0321.chr('utf-8'), '"Ñ"'],
[0322.chr('utf-8'), '"Ò"'],
[0323.chr('utf-8'), '"Ó"'],
[0324.chr('utf-8'), '"Ô"'],
[0325.chr('utf-8'), '"Õ"'],
[0326.chr('utf-8'), '"Ö"'],
[0327.chr('utf-8'), '"×"'],
[0330.chr('utf-8'), '"Ø"'],
[0331.chr('utf-8'), '"Ù"'],
[0332.chr('utf-8'), '"Ú"'],
[0333.chr('utf-8'), '"Û"'],
[0334.chr('utf-8'), '"Ü"'],
[0335.chr('utf-8'), '"Ý"'],
[0336.chr('utf-8'), '"Þ"'],
[0337.chr('utf-8'), '"ß"'],
[0340.chr('utf-8'), '"à"'],
[0341.chr('utf-8'), '"á"'],
[0342.chr('utf-8'), '"â"'],
[0343.chr('utf-8'), '"ã"'],
[0344.chr('utf-8'), '"ä"'],
[0345.chr('utf-8'), '"å"'],
[0346.chr('utf-8'), '"æ"'],
[0347.chr('utf-8'), '"ç"'],
[0350.chr('utf-8'), '"è"'],
[0351.chr('utf-8'), '"é"'],
[0352.chr('utf-8'), '"ê"'],
[0353.chr('utf-8'), '"ë"'],
[0354.chr('utf-8'), '"ì"'],
[0355.chr('utf-8'), '"í"'],
[0356.chr('utf-8'), '"î"'],
[0357.chr('utf-8'), '"ï"'],
[0360.chr('utf-8'), '"ð"'],
[0361.chr('utf-8'), '"ñ"'],
[0362.chr('utf-8'), '"ò"'],
[0363.chr('utf-8'), '"ó"'],
[0364.chr('utf-8'), '"ô"'],
[0365.chr('utf-8'), '"õ"'],
[0366.chr('utf-8'), '"ö"'],
[0367.chr('utf-8'), '"÷"'],
[0370.chr('utf-8'), '"ø"'],
[0371.chr('utf-8'), '"ù"'],
[0372.chr('utf-8'), '"ú"'],
[0373.chr('utf-8'), '"û"'],
[0374.chr('utf-8'), '"ü"'],
[0375.chr('utf-8'), '"ý"'],
[0376.chr('utf-8'), '"þ"'],
[0377.chr('utf-8'), '"ÿ"']
].should be_computed_by(:inspect)
end
end
end

View file

@ -0,0 +1,7 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
require File.expand_path('../shared/to_sym.rb', __FILE__)
describe "String#intern" do
it_behaves_like(:string_to_sym, :intern)
end

View file

@ -0,0 +1,7 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
require File.expand_path('../shared/length', __FILE__)
describe "String#length" do
it_behaves_like(:string_length, :length)
end

View file

@ -0,0 +1,13 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
require File.expand_path('../shared/each_line', __FILE__)
require File.expand_path('../shared/each_line_without_block', __FILE__)
describe "String#lines" do
it_behaves_like(:string_each_line, :lines)
it "returns an array when no block given" do
ary = "hello world".send(@method, ' ')
ary.should == ["hello ", "world"]
end
end

View file

@ -0,0 +1,116 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#ljust with length, padding" do
it "returns a new string of specified length with self left justified and padded with padstr" do
"hello".ljust(20, '1234').should == "hello123412341234123"
"".ljust(1, "abcd").should == "a"
"".ljust(2, "abcd").should == "ab"
"".ljust(3, "abcd").should == "abc"
"".ljust(4, "abcd").should == "abcd"
"".ljust(6, "abcd").should == "abcdab"
"OK".ljust(3, "abcd").should == "OKa"
"OK".ljust(4, "abcd").should == "OKab"
"OK".ljust(6, "abcd").should == "OKabcd"
"OK".ljust(8, "abcd").should == "OKabcdab"
end
it "pads with whitespace if no padstr is given" do
"hello".ljust(20).should == "hello "
end
it "returns self if it's longer than or as long as the specified length" do
"".ljust(0).should == ""
"".ljust(-1).should == ""
"hello".ljust(4).should == "hello"
"hello".ljust(-1).should == "hello"
"this".ljust(3).should == "this"
"radiology".ljust(8, '-').should == "radiology"
end
it "taints result when self or padstr is tainted" do
"x".taint.ljust(4).tainted?.should == true
"x".taint.ljust(0).tainted?.should == true
"".taint.ljust(0).tainted?.should == true
"x".taint.ljust(4, "*").tainted?.should == true
"x".ljust(4, "*".taint).tainted?.should == true
end
it "tries to convert length to an integer using to_int" do
"^".ljust(3.8, "_^").should == "^_^"
obj = mock('3')
obj.should_receive(:to_int).and_return(3)
"o".ljust(obj, "_o").should == "o_o"
end
it "raises a TypeError when length can't be converted to an integer" do
lambda { "hello".ljust("x") }.should raise_error(TypeError)
lambda { "hello".ljust("x", "y") }.should raise_error(TypeError)
lambda { "hello".ljust([]) }.should raise_error(TypeError)
lambda { "hello".ljust(mock('x')) }.should raise_error(TypeError)
end
it "tries to convert padstr to a string using to_str" do
padstr = mock('123')
padstr.should_receive(:to_str).and_return("123")
"hello".ljust(10, padstr).should == "hello12312"
end
it "raises a TypeError when padstr can't be converted" do
lambda { "hello".ljust(20, []) }.should raise_error(TypeError)
lambda { "hello".ljust(20, Object.new)}.should raise_error(TypeError)
lambda { "hello".ljust(20, mock('x')) }.should raise_error(TypeError)
end
it "raises an ArgumentError when padstr is empty" do
lambda { "hello".ljust(10, '') }.should raise_error(ArgumentError)
end
it "returns subclass instances when called on subclasses" do
StringSpecs::MyString.new("").ljust(10).should be_an_instance_of(StringSpecs::MyString)
StringSpecs::MyString.new("foo").ljust(10).should be_an_instance_of(StringSpecs::MyString)
StringSpecs::MyString.new("foo").ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString)
"".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
"foo".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
end
it "when padding is tainted and self is untainted returns a tainted string if and only if length is longer than self" do
"hello".ljust(4, 'X'.taint).tainted?.should be_false
"hello".ljust(5, 'X'.taint).tainted?.should be_false
"hello".ljust(6, 'X'.taint).tainted?.should be_true
end
with_feature :encoding do
describe "with width" do
it "returns a String in the same encoding as the original" do
str = "abc".force_encoding Encoding::IBM437
result = str.ljust 5
result.should == "abc "
result.encoding.should equal(Encoding::IBM437)
end
end
describe "with width, pattern" do
it "returns a String in the compatible encoding" do
str = "abc".force_encoding Encoding::IBM437
result = str.ljust 5, ""
result.should == "abcああ"
result.encoding.should equal(Encoding::UTF_8)
end
it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
pat = "".encode Encoding::EUC_JP
lambda do
"あれ".ljust 5, pat
end.should raise_error(Encoding::CompatibilityError)
end
end
end
end

View file

@ -0,0 +1,50 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#lstrip" do
it "returns a copy of self with leading whitespace removed" do
" hello ".lstrip.should == "hello "
" hello world ".lstrip.should == "hello world "
"\n\r\t\n\v\r hello world ".lstrip.should == "hello world "
"hello".lstrip.should == "hello"
"\000 \000hello\000 \000".lstrip.should == "\000 \000hello\000 \000"
end
it "does not strip leading \\0" do
"\x00hello".lstrip.should == "\x00hello"
end
it "taints the result when self is tainted" do
"".taint.lstrip.tainted?.should == true
"ok".taint.lstrip.tainted?.should == true
" ok".taint.lstrip.tainted?.should == true
end
end
describe "String#lstrip!" do
it "modifies self in place and returns self" do
a = " hello "
a.lstrip!.should equal(a)
a.should == "hello "
a = "\000 \000hello\000 \000"
a.lstrip!
a.should == "\000 \000hello\000 \000"
end
it "returns nil if no modifications were made" do
a = "hello"
a.lstrip!.should == nil
a.should == "hello"
end
it "raises a RuntimeError on a frozen instance that is modified" do
lambda { " hello ".freeze.lstrip! }.should raise_error(RuntimeError)
end
# see [ruby-core:23657]
it "raises a RuntimeError on a frozen instance that would not be modified" do
lambda { "hello".freeze.lstrip! }.should raise_error(RuntimeError)
lambda { "".freeze.lstrip! }.should raise_error(RuntimeError)
end
end

View file

@ -0,0 +1,175 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe :string_match_escaped_literal, shared: true do
not_supported_on :opal do
it "matches a literal Regexp that uses ASCII-only UTF-8 escape sequences" do
"a b".match(/([\u{20}-\u{7e}])/)[0].should == "a"
end
end
end
describe "String#=~" do
it "behaves the same way as index() when given a regexp" do
("rudder" =~ /udder/).should == "rudder".index(/udder/)
("boat" =~ /[^fl]oat/).should == "boat".index(/[^fl]oat/)
("bean" =~ /bag/).should == "bean".index(/bag/)
("true" =~ /false/).should == "true".index(/false/)
end
it "raises a TypeError if a obj is a string" do
lambda { "some string" =~ "another string" }.should raise_error(TypeError)
lambda { "a" =~ StringSpecs::MyString.new("b") }.should raise_error(TypeError)
end
it "invokes obj.=~ with self if obj is neither a string nor regexp" do
str = "w00t"
obj = mock('x')
obj.should_receive(:=~).with(str).any_number_of_times.and_return(true)
str.should =~ obj
obj = mock('y')
obj.should_receive(:=~).with(str).any_number_of_times.and_return(false)
str.should_not =~ obj
end
it "sets $~ to MatchData when there is a match and nil when there's none" do
'hello' =~ /./
$~[0].should == 'h'
'hello' =~ /not/
$~.should == nil
end
with_feature :encoding do
it "returns the character index of a found match" do
("こにちわ" =~ //).should == 1
end
end
end
describe "String#match" do
it "matches the pattern against self" do
'hello'.match(/(.)\1/)[0].should == 'll'
end
it_behaves_like :string_match_escaped_literal, :match
describe "with [pattern, position]" do
describe "when given a positive position" do
it "matches the pattern against self starting at an optional index" do
"01234".match(/(.).(.)/, 1).captures.should == ["1", "3"]
end
with_feature :encoding do
it "uses the start as a character offset" do
"零一二三四".match(/(.).(.)/, 1).captures.should == ["", ""]
end
end
end
describe "when given a negative position" do
it "matches the pattern against self starting at an optional index" do
"01234".match(/(.).(.)/, -4).captures.should == ["1", "3"]
end
with_feature :encoding do
it "uses the start as a character offset" do
"零一二三四".match(/(.).(.)/, -4).captures.should == ["", ""]
end
end
end
end
describe "when passed a block" do
it "yields the MatchData" do
"abc".match(/./) {|m| ScratchPad.record m }
ScratchPad.recorded.should be_kind_of(MatchData)
end
it "returns the block result" do
"abc".match(/./) { :result }.should == :result
end
it "does not yield if there is no match" do
ScratchPad.record []
"b".match(/a/) {|m| ScratchPad << m }
ScratchPad.recorded.should == []
end
end
it "tries to convert pattern to a string via to_str" do
obj = mock('.')
def obj.to_str() "." end
"hello".match(obj)[0].should == "h"
obj = mock('.')
def obj.respond_to?(type, *) true end
def obj.method_missing(*args) "." end
"hello".match(obj)[0].should == "h"
end
it "raises a TypeError if pattern is not a regexp or a string" do
lambda { 'hello'.match(10) }.should raise_error(TypeError)
not_supported_on :opal do
lambda { 'hello'.match(:ell) }.should raise_error(TypeError)
end
end
it "converts string patterns to regexps without escaping" do
'hello'.match('(.)\1')[0].should == 'll'
end
it "returns nil if there's no match" do
'hello'.match('xx').should == nil
end
it "matches \\G at the start of the string" do
'hello'.match(/\Gh/)[0].should == 'h'
'hello'.match(/\Go/).should == nil
end
it "sets $~ to MatchData of match or nil when there is none" do
'hello'.match(/./)
$~[0].should == 'h'
Regexp.last_match[0].should == 'h'
'hello'.match(/X/)
$~.should == nil
Regexp.last_match.should == nil
end
it "calls match on the regular expression" do
regexp = /./
regexp.should_receive(:match).and_return(:foo)
'hello'.match(regexp).should == :foo
end
end
ruby_version_is "2.4" do
describe "String#match?" do
before :each do
# Resetting Regexp.last_match
/DONTMATCH/.match ''
end
context "when matches the given regex" do
it "returns true but does not set Regexp.last_match" do
'string'.match?(/string/i).should be_true
Regexp.last_match.should be_nil
end
end
it "returns false when does not match the given regex" do
'string'.match?(/STRING/).should be_false
end
it "takes matching position as the 2nd argument" do
'string'.match?(/str/i, 0).should be_true
'string'.match?(/str/i, 1).should be_false
end
end
end

View file

@ -0,0 +1,789 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#%" do
it "formats multiple expressions" do
("%b %x %d %s" % [10, 10, 10, 10]).should == "1010 a 10 10"
end
it "formats expressions mid string" do
("hello %s!" % "world").should == "hello world!"
end
it "formats %% into %" do
("%d%% %s" % [10, "of chickens!"]).should == "10% of chickens!"
end
ruby_version_is ""..."2.5" do
it "formats single % character at the end as literal %" do
("%" % []).should == "%"
("foo%" % []).should == "foo%"
end
end
ruby_version_is "2.5" do
it "raises an error if single % appears at the end" do
lambda { ("%" % []) }.should raise_error(ArgumentError)
lambda { ("foo%" % [])}.should raise_error(ArgumentError)
end
end
it "formats single % character before a newline as literal %" do
("%\n" % []).should == "%\n"
("foo%\n" % []).should == "foo%\n"
("%\n.3f" % 1.2).should == "%\n.3f"
end
it "formats single % character before a NUL as literal %" do
("%\0" % []).should == "%\0"
("foo%\0" % []).should == "foo%\0"
("%\0.3f" % 1.2).should == "%\0.3f"
end
it "raises an error if single % appears anywhere else" do
lambda { (" % " % []) }.should raise_error(ArgumentError)
lambda { ("foo%quux" % []) }.should raise_error(ArgumentError)
end
it "raises an error if NULL or \\n appear anywhere else in the format string" do
begin
old_debug, $DEBUG = $DEBUG, false
lambda { "%.\n3f" % 1.2 }.should raise_error(ArgumentError)
lambda { "%.3\nf" % 1.2 }.should raise_error(ArgumentError)
lambda { "%.\03f" % 1.2 }.should raise_error(ArgumentError)
lambda { "%.3\0f" % 1.2 }.should raise_error(ArgumentError)
ensure
$DEBUG = old_debug
end
end
it "ignores unused arguments when $DEBUG is false" do
begin
old_debug = $DEBUG
$DEBUG = false
("" % [1, 2, 3]).should == ""
("%s" % [1, 2, 3]).should == "1"
ensure
$DEBUG = old_debug
end
end
it "raises an ArgumentError for unused arguments when $DEBUG is true" do
begin
old_debug = $DEBUG
$DEBUG = true
s = $stderr
$stderr = IOStub.new
lambda { "" % [1, 2, 3] }.should raise_error(ArgumentError)
lambda { "%s" % [1, 2, 3] }.should raise_error(ArgumentError)
ensure
$DEBUG = old_debug
$stderr = s
end
end
it "always allows unused arguments when positional argument style is used" do
begin
old_debug = $DEBUG
$DEBUG = false
("%2$s" % [1, 2, 3]).should == "2"
$DEBUG = true
("%2$s" % [1, 2, 3]).should == "2"
ensure
$DEBUG = old_debug
end
end
it "replaces trailing absolute argument specifier without type with percent sign" do
("hello %1$" % "foo").should == "hello %"
end
it "raises an ArgumentError when given invalid argument specifiers" do
lambda { "%1" % [] }.should raise_error(ArgumentError)
lambda { "%+" % [] }.should raise_error(ArgumentError)
lambda { "%-" % [] }.should raise_error(ArgumentError)
lambda { "%#" % [] }.should raise_error(ArgumentError)
lambda { "%0" % [] }.should raise_error(ArgumentError)
lambda { "%*" % [] }.should raise_error(ArgumentError)
lambda { "%." % [] }.should raise_error(ArgumentError)
lambda { "%_" % [] }.should raise_error(ArgumentError)
lambda { "%0$s" % "x" }.should raise_error(ArgumentError)
lambda { "%*0$s" % [5, "x"] }.should raise_error(ArgumentError)
lambda { "%*1$.*0$1$s" % [1, 2, 3] }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when multiple positional argument tokens are given for one format specifier" do
lambda { "%1$1$s" % "foo" }.should raise_error(ArgumentError)
end
it "respects positional arguments and precision tokens given for one format specifier" do
("%2$1d" % [1, 0]).should == "0"
("%2$1d" % [0, 1]).should == "1"
("%2$.2f" % [1, 0]).should == "0.00"
("%2$.2f" % [0, 1]).should == "1.00"
end
it "allows more than one digit of position" do
("%50$d" % (0..100).to_a).should == "49"
end
it "raises an ArgumentError when multiple width star tokens are given for one format specifier" do
lambda { "%**s" % [5, 5, 5] }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when a width star token is seen after a width token" do
lambda { "%5*s" % [5, 5] }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when multiple precision tokens are given" do
lambda { "%.5.5s" % 5 }.should raise_error(ArgumentError)
lambda { "%.5.*s" % [5, 5] }.should raise_error(ArgumentError)
lambda { "%.*.5s" % [5, 5] }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when there are less arguments than format specifiers" do
("foo" % []).should == "foo"
lambda { "%s" % [] }.should raise_error(ArgumentError)
lambda { "%s %s" % [1] }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when absolute and relative argument numbers are mixed" do
lambda { "%s %1$s" % "foo" }.should raise_error(ArgumentError)
lambda { "%1$s %s" % "foo" }.should raise_error(ArgumentError)
lambda { "%s %2$s" % ["foo", "bar"] }.should raise_error(ArgumentError)
lambda { "%2$s %s" % ["foo", "bar"] }.should raise_error(ArgumentError)
lambda { "%*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
lambda { "%*.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
lambda { "%*2$.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
lambda { "%*.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
end
it "allows reuse of the one argument multiple via absolute argument numbers" do
("%1$s %1$s" % "foo").should == "foo foo"
("%1$s %2$s %1$s %2$s" % ["foo", "bar"]).should == "foo bar foo bar"
end
it "always interprets an array argument as a list of argument parameters" do
lambda { "%p" % [] }.should raise_error(ArgumentError)
("%p" % [1]).should == "1"
("%p %p" % [1, 2]).should == "1 2"
end
it "always interprets an array subclass argument as a list of argument parameters" do
lambda { "%p" % StringSpecs::MyArray[] }.should raise_error(ArgumentError)
("%p" % StringSpecs::MyArray[1]).should == "1"
("%p %p" % StringSpecs::MyArray[1, 2]).should == "1 2"
end
it "allows positional arguments for width star and precision star arguments" do
("%*1$.*2$3$d" % [10, 5, 1]).should == " 00001"
end
it "allows negative width to imply '-' flag" do
("%*1$.*2$3$d" % [-10, 5, 1]).should == "00001 "
("%-*1$.*2$3$d" % [10, 5, 1]).should == "00001 "
("%-*1$.*2$3$d" % [-10, 5, 1]).should == "00001 "
end
it "ignores negative precision" do
("%*1$.*2$3$d" % [10, -5, 1]).should == " 1"
end
it "allows a star to take an argument number to use as the width" do
("%1$*2$s" % ["a", 8]).should == " a"
("%1$*10$s" % ["a",0,0,0,0,0,0,0,0,8]).should == " a"
end
it "calls to_int on width star and precision star tokens" do
w = mock('10')
w.should_receive(:to_int).and_return(10)
p = mock('5')
p.should_receive(:to_int).and_return(5)
("%*.*f" % [w, p, 1]).should == " 1.00000"
w = mock('10')
w.should_receive(:to_int).and_return(10)
p = mock('5')
p.should_receive(:to_int).and_return(5)
("%*.*d" % [w, p, 1]).should == " 00001"
end
it "does not call #to_a to convert the argument" do
x = mock("string modulo to_a")
x.should_not_receive(:to_a)
x.should_receive(:to_s).and_return("x")
("%s" % x).should == "x"
end
it "calls #to_ary to convert the argument" do
x = mock("string modulo to_ary")
x.should_not_receive(:to_s)
x.should_receive(:to_ary).and_return(["x"])
("%s" % x).should == "x"
end
it "wraps the object in an Array if #to_ary returns nil" do
x = mock("string modulo to_ary")
x.should_receive(:to_ary).and_return(nil)
x.should_receive(:to_s).and_return("x")
("%s" % x).should == "x"
end
it "raises a TypeError if #to_ary does not return an Array" do
x = mock("string modulo to_ary")
x.should_receive(:to_ary).and_return("x")
lambda { "%s" % x }.should raise_error(TypeError)
end
it "tries to convert the argument to Array by calling #to_ary" do
obj = mock('[1,2]')
def obj.to_ary() [1, 2] end
def obj.to_s() "obj" end
("%s %s" % obj).should == "1 2"
("%s" % obj).should == "1"
end
it "doesn't return subclass instances when called on a subclass" do
universal = mock('0')
def universal.to_int() 0 end
def universal.to_str() "0" end
def universal.to_f() 0.0 end
[
"", "foo",
"%b", "%B", "%c", "%d", "%e", "%E",
"%f", "%g", "%G", "%i", "%o", "%p",
"%s", "%u", "%x", "%X"
].each do |format|
(StringSpecs::MyString.new(format) % universal).should be_an_instance_of(String)
end
end
it "always taints the result when the format string is tainted" do
universal = mock('0')
def universal.to_int() 0 end
def universal.to_str() "0" end
def universal.to_f() 0.0 end
[
"", "foo",
"%b", "%B", "%c", "%d", "%e", "%E",
"%f", "%g", "%G", "%i", "%o", "%p",
"%s", "%u", "%x", "%X"
].each do |format|
subcls_format = StringSpecs::MyString.new(format)
subcls_format.taint
format.taint
(format % universal).tainted?.should == true
(subcls_format % universal).tainted?.should == true
end
end
it "supports binary formats using %b for positive numbers" do
("%b" % 10).should == "1010"
("% b" % 10).should == " 1010"
("%1$b" % [10, 20]).should == "1010"
("%#b" % 10).should == "0b1010"
("%+b" % 10).should == "+1010"
("%-9b" % 10).should == "1010 "
("%05b" % 10).should == "01010"
("%*b" % [10, 6]).should == " 110"
("%*b" % [-10, 6]).should == "110 "
("%.4b" % 2).should == "0010"
("%.32b" % 2147483648).should == "10000000000000000000000000000000"
end
it "supports binary formats using %b for negative numbers" do
("%b" % -5).should == "..1011"
("%0b" % -5).should == "..1011"
("%.1b" % -5).should == "..1011"
("%.7b" % -5).should == "..11011"
("%.10b" % -5).should == "..11111011"
("% b" % -5).should == "-101"
("%+b" % -5).should == "-101"
not_supported_on :opal do
("%b" % -(2 ** 64 + 5)).should ==
"..101111111111111111111111111111111111111111111111111111111111111011"
end
end
it "supports binary formats using %B with same behaviour as %b except for using 0B instead of 0b for #" do
("%B" % 10).should == ("%b" % 10)
("% B" % 10).should == ("% b" % 10)
("%1$B" % [10, 20]).should == ("%1$b" % [10, 20])
("%+B" % 10).should == ("%+b" % 10)
("%-9B" % 10).should == ("%-9b" % 10)
("%05B" % 10).should == ("%05b" % 10)
("%*B" % [10, 6]).should == ("%*b" % [10, 6])
("%*B" % [-10, 6]).should == ("%*b" % [-10, 6])
("%B" % -5).should == ("%b" % -5)
("%0B" % -5).should == ("%0b" % -5)
("%.1B" % -5).should == ("%.1b" % -5)
("%.7B" % -5).should == ("%.7b" % -5)
("%.10B" % -5).should == ("%.10b" % -5)
("% B" % -5).should == ("% b" % -5)
("%+B" % -5).should == ("%+b" % -5)
not_supported_on :opal do
("%B" % -(2 ** 64 + 5)).should == ("%b" % -(2 ** 64 + 5))
end
("%#B" % 10).should == "0B1010"
end
it "supports character formats using %c" do
("%c" % 10).should == "\n"
("%2$c" % [10, 11, 14]).should == "\v"
("%-4c" % 10).should == "\n "
("%*c" % [10, 3]).should == " \003"
("%c" % 42).should == "*"
lambda { "%c" % Object }.should raise_error(TypeError)
end
it "supports single character strings as argument for %c" do
("%c" % 'A').should == "A"
end
it "raises an exception for multiple character strings as argument for %c" do
lambda { "%c" % 'AA' }.should raise_error(ArgumentError)
end
it "calls to_str on argument for %c formats" do
obj = mock('A')
obj.should_receive(:to_str).and_return('A')
("%c" % obj).should == "A"
end
it "calls #to_ary on argument for %c formats" do
obj = mock('65')
obj.should_receive(:to_ary).and_return([65])
("%c" % obj).should == ("%c" % [65])
end
it "calls #to_int on argument for %c formats, if the argument does not respond to #to_ary" do
obj = mock('65')
obj.should_receive(:to_int).and_return(65)
("%c" % obj).should == ("%c" % 65)
end
%w(d i).each do |f|
format = "%" + f
it "supports integer formats using #{format}" do
("%#{f}" % 10).should == "10"
("% #{f}" % 10).should == " 10"
("%1$#{f}" % [10, 20]).should == "10"
("%+#{f}" % 10).should == "+10"
("%-7#{f}" % 10).should == "10 "
("%04#{f}" % 10).should == "0010"
("%*#{f}" % [10, 4]).should == " 4"
("%6.4#{f}" % 123).should == " 0123"
end
it "supports negative integers using #{format}" do
("%#{f}" % -5).should == "-5"
("%3#{f}" % -5).should == " -5"
("%03#{f}" % -5).should == "-05"
("%+03#{f}" % -5).should == "-05"
("%+.2#{f}" % -5).should == "-05"
("%-3#{f}" % -5).should == "-5 "
("%6.4#{f}" % -123).should == " -0123"
end
it "supports negative integers using #{format}, giving priority to `-`" do
("%-03#{f}" % -5).should == "-5 "
("%+-03#{f}" % -5).should == "-5 "
end
end
it "supports float formats using %e" do
("%e" % 10).should == "1.000000e+01"
("% e" % 10).should == " 1.000000e+01"
("%1$e" % 10).should == "1.000000e+01"
("%#e" % 10).should == "1.000000e+01"
("%+e" % 10).should == "+1.000000e+01"
("%-7e" % 10).should == "1.000000e+01"
("%05e" % 10).should == "1.000000e+01"
("%*e" % [10, 9]).should == "9.000000e+00"
end
it "supports float formats using %e, but Inf, -Inf, and NaN are not floats" do
("%e" % 1e1020).should == "Inf"
("%e" % -1e1020).should == "-Inf"
("%e" % -Float::NAN).should == "NaN"
("%e" % Float::NAN).should == "NaN"
end
it "supports float formats using %E, but Inf, -Inf, and NaN are not floats" do
("%E" % 1e1020).should == "Inf"
("%E" % -1e1020).should == "-Inf"
("%-10E" % 1e1020).should == "Inf "
("%10E" % 1e1020).should == " Inf"
("%+E" % 1e1020).should == "+Inf"
("% E" % 1e1020).should == " Inf"
("%E" % Float::NAN).should == "NaN"
("%E" % -Float::NAN).should == "NaN"
end
it "supports float formats using %E" do
("%E" % 10).should == "1.000000E+01"
("% E" % 10).should == " 1.000000E+01"
("%1$E" % 10).should == "1.000000E+01"
("%#E" % 10).should == "1.000000E+01"
("%+E" % 10).should == "+1.000000E+01"
("%-7E" % 10).should == "1.000000E+01"
("%05E" % 10).should == "1.000000E+01"
("%*E" % [10, 9]).should == "9.000000E+00"
end
it "pads with spaces for %E with Inf, -Inf, and NaN" do
("%010E" % -1e1020).should == " -Inf"
("%010E" % 1e1020).should == " Inf"
("%010E" % Float::NAN).should == " NaN"
end
it "supports float formats using %f" do
("%f" % 10).should == "10.000000"
("% f" % 10).should == " 10.000000"
("%1$f" % 10).should == "10.000000"
("%#f" % 10).should == "10.000000"
("%#0.3f" % 10).should == "10.000"
("%+f" % 10).should == "+10.000000"
("%-7f" % 10).should == "10.000000"
("%05f" % 10).should == "10.000000"
("%0.5f" % 10).should == "10.00000"
("%*f" % [10, 9]).should == " 9.000000"
end
it "supports float formats using %g" do
("%g" % 10).should == "10"
("% g" % 10).should == " 10"
("%1$g" % 10).should == "10"
("%#g" % 10).should == "10.0000"
("%#.3g" % 10).should == "10.0"
("%+g" % 10).should == "+10"
("%-7g" % 10).should == "10 "
("%05g" % 10).should == "00010"
("%g" % 10**10).should == "1e+10"
("%*g" % [10, 9]).should == " 9"
end
it "supports float formats using %G" do
("%G" % 10).should == "10"
("% G" % 10).should == " 10"
("%1$G" % 10).should == "10"
("%#G" % 10).should == "10.0000"
("%#.3G" % 10).should == "10.0"
("%+G" % 10).should == "+10"
("%-7G" % 10).should == "10 "
("%05G" % 10).should == "00010"
("%G" % 10**10).should == "1E+10"
("%*G" % [10, 9]).should == " 9"
end
it "supports octal formats using %o for positive numbers" do
("%o" % 10).should == "12"
("% o" % 10).should == " 12"
("%1$o" % [10, 20]).should == "12"
("%#o" % 10).should == "012"
("%+o" % 10).should == "+12"
("%-9o" % 10).should == "12 "
("%05o" % 10).should == "00012"
("%*o" % [10, 6]).should == " 6"
end
it "supports octal formats using %o for negative numbers" do
# These are incredibly wrong. -05 == -5, not 7177777...whatever
("%o" % -5).should == "..73"
("%0o" % -5).should == "..73"
("%.4o" % 20).should == "0024"
("%.1o" % -5).should == "..73"
("%.7o" % -5).should == "..77773"
("%.10o" % -5).should == "..77777773"
("% o" % -26).should == "-32"
("%+o" % -26).should == "-32"
not_supported_on :opal do
("%o" % -(2 ** 64 + 5)).should == "..75777777777777777777773"
end
end
it "supports inspect formats using %p" do
("%p" % 10).should == "10"
("%1$p" % [10, 5]).should == "10"
("%-22p" % 10).should == "10 "
("%*p" % [10, 10]).should == " 10"
("%p" % {capture: 1}).should == "{:capture=>1}"
("%p" % "str").should == "\"str\""
end
it "calls inspect on arguments for %p format" do
obj = mock('obj')
def obj.inspect() "obj" end
("%p" % obj).should == "obj"
# undef is not working
# obj = mock('obj')
# class << obj; undef :inspect; end
# def obj.method_missing(*args) "obj" end
# ("%p" % obj).should == "obj"
end
it "taints result for %p when argument.inspect is tainted" do
obj = mock('x')
def obj.inspect() "x".taint end
("%p" % obj).tainted?.should == true
obj = mock('x'); obj.taint
def obj.inspect() "x" end
("%p" % obj).tainted?.should == false
end
it "supports string formats using %s" do
("%s" % "hello").should == "hello"
("%s" % "").should == ""
("%s" % 10).should == "10"
("%1$s" % [10, 8]).should == "10"
("%-5s" % 10).should == "10 "
("%*s" % [10, 9]).should == " 9"
end
it "respects a space padding request not as part of the width" do
x = "% -5s" % ["foo"]
x.should == "foo "
end
it "calls to_s on non-String arguments for %s format" do
obj = mock('obj')
def obj.to_s() "obj" end
("%s" % obj).should == "obj"
# undef doesn't work
# obj = mock('obj')
# class << obj; undef :to_s; end
# def obj.method_missing(*args) "obj" end
#
# ("%s" % obj).should == "obj"
end
it "taints result for %s when argument is tainted" do
("%s" % "x".taint).tainted?.should == true
("%s" % mock('x').taint).tainted?.should == true
end
# MRI crashes on this one.
# See http://groups.google.com/group/ruby-core-google/t/c285c18cd94c216d
it "raises an ArgumentError for huge precisions for %s" do
block = lambda { "%.25555555555555555555555555555555555555s" % "hello world" }
block.should raise_error(ArgumentError)
end
# Note: %u has been changed to an alias for %d in 1.9.
it "supports unsigned formats using %u" do
("%u" % 10).should == "10"
("% u" % 10).should == " 10"
("%1$u" % [10, 20]).should == "10"
("%+u" % 10).should == "+10"
("%-7u" % 10).should == "10 "
("%04u" % 10).should == "0010"
("%*u" % [10, 4]).should == " 4"
end
it "formats negative values with a leading sign using %u" do
("% u" % -26).should == "-26"
("%+u" % -26).should == "-26"
end
it "supports negative bignums with %u or %d" do
("%u" % -(2 ** 64 + 5)).should == "-18446744073709551621"
("%d" % -(2 ** 64 + 5)).should == "-18446744073709551621"
end
it "supports hex formats using %x for positive numbers" do
("%x" % 10).should == "a"
("% x" % 10).should == " a"
("%1$x" % [10, 20]).should == "a"
("%#x" % 10).should == "0xa"
("%+x" % 10).should == "+a"
("%-9x" % 10).should == "a "
("%05x" % 10).should == "0000a"
("%*x" % [10, 6]).should == " 6"
("%.4x" % 20).should == "0014"
("%x" % 0xFFFFFFFF).should == "ffffffff"
end
it "supports hex formats using %x for negative numbers" do
("%x" % -5).should == "..fb"
("%0x" % -5).should == "..fb"
("%.1x" % -5).should == "..fb"
("%.7x" % -5).should == "..ffffb"
("%.10x" % -5).should == "..fffffffb"
("% x" % -26).should == "-1a"
("%+x" % -26).should == "-1a"
not_supported_on :opal do
("%x" % -(2 ** 64 + 5)).should == "..fefffffffffffffffb"
end
end
it "supports hex formats using %X for positive numbers" do
("%X" % 10).should == "A"
("% X" % 10).should == " A"
("%1$X" % [10, 20]).should == "A"
("%#X" % 10).should == "0XA"
("%+X" % 10).should == "+A"
("%-9X" % 10).should == "A "
("%05X" % 10).should == "0000A"
("%*X" % [10, 6]).should == " 6"
("%X" % 0xFFFFFFFF).should == "FFFFFFFF"
end
it "supports hex formats using %X for negative numbers" do
("%X" % -5).should == "..FB"
("%0X" % -5).should == "..FB"
("%.1X" % -5).should == "..FB"
("%.7X" % -5).should == "..FFFFB"
("%.10X" % -5).should == "..FFFFFFFB"
("% X" % -26).should == "-1A"
("%+X" % -26).should == "-1A"
not_supported_on :opal do
("%X" % -(2 ** 64 + 5)).should == "..FEFFFFFFFFFFFFFFFB"
end
end
it "formats zero without prefix using %#x" do
("%#x" % 0).should == "0"
end
it "formats zero without prefix using %#X" do
("%#X" % 0).should == "0"
end
%w(b d i o u x X).each do |f|
format = "%" + f
it "behaves as if calling Kernel#Integer for #{format} argument, if it does not respond to #to_ary" do
(format % "10").should == (format % Kernel.Integer("10"))
(format % "0x42").should == (format % Kernel.Integer("0x42"))
(format % "0b1101").should == (format % Kernel.Integer("0b1101"))
(format % "0b1101_0000").should == (format % Kernel.Integer("0b1101_0000"))
(format % "0777").should == (format % Kernel.Integer("0777"))
lambda {
# see [ruby-core:14139] for more details
(format % "0777").should == (format % Kernel.Integer("0777"))
}.should_not raise_error(ArgumentError)
lambda { format % "0__7_7_7" }.should raise_error(ArgumentError)
lambda { format % "" }.should raise_error(ArgumentError)
lambda { format % "x" }.should raise_error(ArgumentError)
lambda { format % "5x" }.should raise_error(ArgumentError)
lambda { format % "08" }.should raise_error(ArgumentError)
lambda { format % "0b2" }.should raise_error(ArgumentError)
lambda { format % "123__456" }.should raise_error(ArgumentError)
obj = mock('5')
obj.should_receive(:to_i).and_return(5)
(format % obj).should == (format % 5)
obj = mock('6')
obj.stub!(:to_i).and_return(5)
obj.should_receive(:to_int).and_return(6)
(format % obj).should == (format % 6)
end
end
%w(e E f g G).each do |f|
format = "%" + f
it "tries to convert the passed argument to an Array using #to_ary" do
obj = mock('3.14')
obj.should_receive(:to_ary).and_return([3.14])
(format % obj).should == (format % [3.14])
end
it "behaves as if calling Kernel#Float for #{format} arguments, when the passed argument does not respond to #to_ary" do
(format % 10).should == (format % 10.0)
(format % "-10.4e-20").should == (format % -10.4e-20)
(format % ".5").should == (format % 0.5)
(format % "-.5").should == (format % -0.5)
# Something's strange with this spec:
# it works just fine in individual mode, but not when run as part of a group
(format % "10_1_0.5_5_5").should == (format % 1010.555)
(format % "0777").should == (format % 777)
lambda { format % "" }.should raise_error(ArgumentError)
lambda { format % "x" }.should raise_error(ArgumentError)
lambda { format % "." }.should raise_error(ArgumentError)
lambda { format % "10." }.should raise_error(ArgumentError)
lambda { format % "5x" }.should raise_error(ArgumentError)
lambda { format % "0b1" }.should raise_error(ArgumentError)
lambda { format % "10e10.5" }.should raise_error(ArgumentError)
lambda { format % "10__10" }.should raise_error(ArgumentError)
lambda { format % "10.10__10" }.should raise_error(ArgumentError)
obj = mock('5.0')
obj.should_receive(:to_f).and_return(5.0)
(format % obj).should == (format % 5.0)
end
it "behaves as if calling Kernel#Float for #{format} arguments, when the passed argument is hexadecimal string" do
(format % "0xA").should == (format % 0xA)
end
it "doesn't taint the result for #{format} when argument is tainted" do
(format % "5".taint).tainted?.should == false
end
end
describe "when format string contains %{} sections" do
it "replaces %{} sections with values from passed-in hash" do
("%{foo}bar" % {foo: 'oof'}).should == "oofbar"
end
it "raises KeyError if key is missing from passed-in hash" do
lambda {"%{foo}" % {}}.should raise_error(KeyError)
end
it "should raise ArgumentError if no hash given" do
lambda {"%{foo}" % []}.should raise_error(ArgumentError)
end
end
describe "when format string contains %<> formats" do
it "uses the named argument for the format's value" do
("%<foo>d" % {foo: 1}).should == "1"
end
it "raises KeyError if key is missing from passed-in hash" do
lambda {"%<foo>d" % {}}.should raise_error(KeyError)
end
it "should raise ArgumentError if no hash given" do
lambda {"%<foo>" % []}.should raise_error(ArgumentError)
end
end
end

View file

@ -0,0 +1,7 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
require File.expand_path('../../../shared/string/times', __FILE__)
describe "String#*" do
it_behaves_like :string_times, :*, ->(str, times) { str * times }
end

View file

@ -0,0 +1,58 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
describe "String.new" do
it "returns an instance of String" do
str = String.new
str.should be_an_instance_of(String)
end
ruby_version_is "2.3" do
it "accepts an encoding argument" do
xA4xA2 = [0xA4, 0xA2].pack('CC').force_encoding 'utf-8'
str = String.new(xA4xA2, encoding: 'euc-jp')
str.encoding.should == Encoding::EUC_JP
end
end
it "returns a fully-formed String" do
str = String.new
str.size.should == 0
str << "more"
str.should == "more"
end
it "returns a new string given a string argument" do
str1 = "test"
str = String.new(str1)
str.should be_an_instance_of(String)
str.should == str1
str << "more"
str.should == "testmore"
end
it "returns an instance of a subclass" do
a = StringSpecs::MyString.new("blah")
a.should be_an_instance_of(StringSpecs::MyString)
a.should == "blah"
end
it "is called on subclasses" do
s = StringSpecs::SubString.new
s.special.should == nil
s.should == ""
s = StringSpecs::SubString.new "subclass"
s.special.should == "subclass"
s.should == ""
end
it "raises TypeError on inconvertible object" do
lambda { String.new 5 }.should raise_error(TypeError)
lambda { String.new nil }.should raise_error(TypeError)
end
it "returns a binary String" do
String.new.encoding.should == Encoding::BINARY
end
end

View file

@ -0,0 +1,11 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
require File.expand_path('../shared/succ.rb', __FILE__)
describe "String#next" do
it_behaves_like(:string_succ, :next)
end
describe "String#next!" do
it_behaves_like(:string_succ_bang, :"next!")
end

View file

@ -0,0 +1,88 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
# Note: We can't completely spec this in terms of to_int() because hex()
# allows the base to be changed by a base specifier in the string.
# See http://groups.google.com/group/ruby-core-google/browse_frm/thread/b53e9c2003425703
describe "String#oct" do
it "treats numeric digits as base-8 digits by default" do
"0".oct.should == 0
"77".oct.should == 077
"077".oct.should == 077
end
it "accepts numbers formatted as binary" do
"0b1010".oct.should == 0b1010
end
it "accepts numbers formatted as hexadecimal" do
"0xFF".oct.should == 0xFF
end
it "accepts numbers formatted as decimal" do
"0d500".oct.should == 500
end
describe "with a leading minus sign" do
it "treats numeric digits as base-8 digits by default" do
"-12348".oct.should == -01234
end
it "accepts numbers formatted as binary" do
"-0b0101".oct.should == -0b0101
end
it "accepts numbers formatted as hexadecimal" do
"-0xEE".oct.should == -0xEE
end
it "accepts numbers formatted as decimal" do
"-0d500".oct.should == -500
end
end
describe "with a leading plus sign" do
it "treats numeric digits as base-8 digits by default" do
"+12348".oct.should == 01234
end
it "accepts numbers formatted as binary" do
"+0b1010".oct.should == 0b1010
end
it "accepts numbers formatted as hexadecimal" do
"+0xFF".oct.should == 0xFF
end
it "accepts numbers formatted as decimal" do
"+0d500".oct.should == 500
end
end
it "accepts a single underscore separating digits" do
"755_333".oct.should == 0755_333
end
it "does not accept a sequence of underscores as part of a number" do
"7__3".oct.should == 07
"7___3".oct.should == 07
"7__5".oct.should == 07
end
it "ignores characters that are incorrect for the base-8 digits" do
"0o".oct.should == 0
"5678".oct.should == 0567
end
it "returns 0 if no characters can be interpreted as a base-8 number" do
"".oct.should == 0
"+-5".oct.should == 0
"wombat".oct.should == 0
end
it "returns 0 for strings with leading underscores" do
"_7".oct.should == 0
"_07".oct.should == 0
" _7".oct.should == 0
end
end

View file

@ -0,0 +1,30 @@
require File.expand_path('../../../spec_helper', __FILE__)
with_feature :encoding do
describe "String#ord" do
it "returns a Fixnum" do
'a'.ord.should be_an_instance_of(Fixnum)
end
it "returns the codepoint of the first character in the String" do
'a'.ord.should == 97
end
it "ignores subsequent characters" do
"\u{287}a".ord.should == "\u{287}".ord
end
it "understands multibyte characters" do
"\u{9879}".ord.should == 39033
end
it "is equivalent to #codepoints.first" do
"\u{981}\u{982}".ord.should == "\u{981}\u{982}".codepoints.first
end
it "raises an ArgumentError if called on an empty String" do
lambda { ''.ord }.should raise_error(ArgumentError)
end
end
end

View file

@ -0,0 +1,38 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#partition with String" do
it "returns an array of substrings based on splitting on the given string" do
"hello world".partition("o").should == ["hell", "o", " world"]
end
it "always returns 3 elements" do
"hello".partition("x").should == ["hello", "", ""]
"hello".partition("hello").should == ["", "hello", ""]
end
it "accepts regexp" do
"hello!".partition(/l./).should == ["he", "ll", "o!"]
end
it "sets global vars if regexp used" do
"hello!".partition(/(.l)(.o)/)
$1.should == "el"
$2.should == "lo"
end
it "converts its argument using :to_str" do
find = mock('l')
find.should_receive(:to_str).and_return("l")
"hello".partition(find).should == ["he","l","lo"]
end
it "raises an error if not convertible to string" do
lambda{ "hello".partition(5) }.should raise_error(TypeError)
lambda{ "hello".partition(nil) }.should raise_error(TypeError)
end
it "takes precedence over a given block" do
"hello world".partition("o") { true }.should == ["hell", "o", " world"]
end
end

View file

@ -0,0 +1,47 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
require File.expand_path('../shared/concat', __FILE__)
describe "String#+" do
it "returns a new string containing the given string concatenated to self" do
("" + "").should == ""
("" + "Hello").should == "Hello"
("Hello" + "").should == "Hello"
("Ruby !" + "= Rubinius").should == "Ruby != Rubinius"
end
it "converts any non-String argument with #to_str" do
c = mock 'str'
c.should_receive(:to_str).any_number_of_times.and_return(' + 1 = 2')
("1" + c).should == '1 + 1 = 2'
end
it "raises a TypeError when given any object that fails #to_str" do
lambda { "" + Object.new }.should raise_error(TypeError)
lambda { "" + 65 }.should raise_error(TypeError)
end
it "doesn't return subclass instances" do
(StringSpecs::MyString.new("hello") + "").should be_an_instance_of(String)
(StringSpecs::MyString.new("hello") + "foo").should be_an_instance_of(String)
(StringSpecs::MyString.new("hello") + StringSpecs::MyString.new("foo")).should be_an_instance_of(String)
(StringSpecs::MyString.new("hello") + StringSpecs::MyString.new("")).should be_an_instance_of(String)
(StringSpecs::MyString.new("") + StringSpecs::MyString.new("")).should be_an_instance_of(String)
("hello" + StringSpecs::MyString.new("foo")).should be_an_instance_of(String)
("hello" + StringSpecs::MyString.new("")).should be_an_instance_of(String)
end
it "taints the result when self or other is tainted" do
strs = ["", "OK", StringSpecs::MyString.new(""), StringSpecs::MyString.new("OK")]
strs += strs.map { |s| s.dup.taint }
strs.each do |str|
strs.each do |other|
(str + other).tainted?.should == (str.tainted? | other.tainted?)
end
end
end
it_behaves_like :string_concat_encoding, :+
end

View file

@ -0,0 +1,64 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#prepend" do
it "prepends the given argument to self and returns self" do
str = "world"
str.prepend("hello ").should equal(str)
str.should == "hello world"
end
it "converts the given argument to a String using to_str" do
obj = mock("hello")
obj.should_receive(:to_str).and_return("hello")
a = " world!".prepend(obj)
a.should == "hello world!"
end
it "raises a TypeError if the given argument can't be converted to a String" do
lambda { "hello ".prepend [] }.should raise_error(TypeError)
lambda { 'hello '.prepend mock('x') }.should raise_error(TypeError)
end
it "raises a RuntimeError when self is frozen" do
a = "hello"
a.freeze
lambda { a.prepend "" }.should raise_error(RuntimeError)
lambda { a.prepend "test" }.should raise_error(RuntimeError)
end
it "works when given a subclass instance" do
a = " world"
a.prepend StringSpecs::MyString.new("hello")
a.should == "hello world"
end
it "taints self if other is tainted" do
x = "x"
x.prepend("".taint).tainted?.should be_true
x = "x"
x.prepend("y".taint).tainted?.should be_true
end
ruby_version_is "2.4" do
it "takes multiple arguments" do
str = " world"
str.prepend "he", "", "llo"
str.should == "hello world"
end
it "prepends the initial value when given arguments contain 2 self" do
str = "hello"
str.prepend str, str
str.should == "hellohellohello"
end
it "returns self when given no arguments" do
str = "hello"
str.prepend.should equal(str)
str.should == "hello"
end
end
end

View file

@ -0,0 +1,7 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
require File.expand_path('../shared/replace', __FILE__)
describe "String#replace" do
it_behaves_like :string_replace, :replace
end

View file

@ -0,0 +1,52 @@
# encoding: utf-8
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#reverse" do
it "returns a new string with the characters of self in reverse order" do
"stressed".reverse.should == "desserts"
"m".reverse.should == "m"
"".reverse.should == ""
end
it "taints the result if self is tainted" do
"".taint.reverse.tainted?.should == true
"m".taint.reverse.tainted?.should == true
end
with_feature :encoding do
it "reverses a string with multi byte characters" do
"微軟正黑體".reverse.should == "體黑正軟微"
end
end
end
describe "String#reverse!" do
it "reverses self in place and always returns self" do
a = "stressed"
a.reverse!.should equal(a)
a.should == "desserts"
"".reverse!.should == ""
end
it "raises a RuntimeError on a frozen instance that is modified" do
lambda { "anna".freeze.reverse! }.should raise_error(RuntimeError)
lambda { "hello".freeze.reverse! }.should raise_error(RuntimeError)
end
# see [ruby-core:23666]
it "raises a RuntimeError on a frozen instance that would not be modified" do
lambda { "".freeze.reverse! }.should raise_error(RuntimeError)
end
with_feature :encoding do
it "reverses a string with multi byte characters" do
str = "微軟正黑體"
str.reverse!
str.should == "體黑正軟微"
end
end
end

View file

@ -0,0 +1,368 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
require File.expand_path('../fixtures/utf-8-encoding.rb', __FILE__)
describe "String#rindex with object" do
it "raises a TypeError if obj isn't a String, Fixnum or Regexp" do
not_supported_on :opal do
lambda { "hello".rindex(:sym) }.should raise_error(TypeError)
end
lambda { "hello".rindex(mock('x')) }.should raise_error(TypeError)
end
it "doesn't try to convert obj to an integer via to_int" do
obj = mock('x')
obj.should_not_receive(:to_int)
lambda { "hello".rindex(obj) }.should raise_error(TypeError)
end
it "tries to convert obj to a string via to_str" do
obj = mock('lo')
def obj.to_str() "lo" end
"hello".rindex(obj).should == "hello".rindex("lo")
obj = mock('o')
def obj.respond_to?(arg, *) true end
def obj.method_missing(*args) "o" end
"hello".rindex(obj).should == "hello".rindex("o")
end
end
describe "String#rindex with String" do
it "behaves the same as String#rindex(char) for one-character strings" do
"blablabla hello cruel world...!".split("").uniq.each do |str|
chr = str[0]
str.rindex(str).should == str.rindex(chr)
0.upto(str.size + 1) do |start|
str.rindex(str, start).should == str.rindex(chr, start)
end
(-str.size - 1).upto(-1) do |start|
str.rindex(str, start).should == str.rindex(chr, start)
end
end
end
it "behaves the same as String#rindex(?char) for one-character strings" do
"blablabla hello cruel world...!".split("").uniq.each do |str|
chr = str[0] =~ / / ? str[0] : eval("?#{str[0]}")
str.rindex(str).should == str.rindex(chr)
0.upto(str.size + 1) do |start|
str.rindex(str, start).should == str.rindex(chr, start)
end
(-str.size - 1).upto(-1) do |start|
str.rindex(str, start).should == str.rindex(chr, start)
end
end
end
it "returns the index of the last occurrence of the given substring" do
"blablabla".rindex("").should == 9
"blablabla".rindex("a").should == 8
"blablabla".rindex("la").should == 7
"blablabla".rindex("bla").should == 6
"blablabla".rindex("abla").should == 5
"blablabla".rindex("labla").should == 4
"blablabla".rindex("blabla").should == 3
"blablabla".rindex("ablabla").should == 2
"blablabla".rindex("lablabla").should == 1
"blablabla".rindex("blablabla").should == 0
"blablabla".rindex("l").should == 7
"blablabla".rindex("bl").should == 6
"blablabla".rindex("abl").should == 5
"blablabla".rindex("labl").should == 4
"blablabla".rindex("blabl").should == 3
"blablabla".rindex("ablabl").should == 2
"blablabla".rindex("lablabl").should == 1
"blablabla".rindex("blablabl").should == 0
"blablabla".rindex("b").should == 6
"blablabla".rindex("ab").should == 5
"blablabla".rindex("lab").should == 4
"blablabla".rindex("blab").should == 3
"blablabla".rindex("ablab").should == 2
"blablabla".rindex("lablab").should == 1
"blablabla".rindex("blablab").should == 0
end
it "doesn't set $~" do
$~ = nil
'hello.'.rindex('ll')
$~.should == nil
end
it "ignores string subclasses" do
"blablabla".rindex(StringSpecs::MyString.new("bla")).should == 6
StringSpecs::MyString.new("blablabla").rindex("bla").should == 6
StringSpecs::MyString.new("blablabla").rindex(StringSpecs::MyString.new("bla")).should == 6
end
it "starts the search at the given offset" do
"blablabla".rindex("bl", 0).should == 0
"blablabla".rindex("bl", 1).should == 0
"blablabla".rindex("bl", 2).should == 0
"blablabla".rindex("bl", 3).should == 3
"blablabla".rindex("bla", 0).should == 0
"blablabla".rindex("bla", 1).should == 0
"blablabla".rindex("bla", 2).should == 0
"blablabla".rindex("bla", 3).should == 3
"blablabla".rindex("blab", 0).should == 0
"blablabla".rindex("blab", 1).should == 0
"blablabla".rindex("blab", 2).should == 0
"blablabla".rindex("blab", 3).should == 3
"blablabla".rindex("blab", 6).should == 3
"blablablax".rindex("blab", 6).should == 3
"blablabla".rindex("la", 1).should == 1
"blablabla".rindex("la", 2).should == 1
"blablabla".rindex("la", 3).should == 1
"blablabla".rindex("la", 4).should == 4
"blablabla".rindex("lab", 1).should == 1
"blablabla".rindex("lab", 2).should == 1
"blablabla".rindex("lab", 3).should == 1
"blablabla".rindex("lab", 4).should == 4
"blablabla".rindex("ab", 2).should == 2
"blablabla".rindex("ab", 3).should == 2
"blablabla".rindex("ab", 4).should == 2
"blablabla".rindex("ab", 5).should == 5
"blablabla".rindex("", 0).should == 0
"blablabla".rindex("", 1).should == 1
"blablabla".rindex("", 2).should == 2
"blablabla".rindex("", 7).should == 7
"blablabla".rindex("", 8).should == 8
"blablabla".rindex("", 9).should == 9
"blablabla".rindex("", 10).should == 9
end
it "starts the search at offset + self.length if offset is negative" do
str = "blablabla"
["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle|
(-str.length .. -1).each do |offset|
str.rindex(needle, offset).should ==
str.rindex(needle, offset + str.length)
end
end
end
it "returns nil if the substring isn't found" do
"blablabla".rindex("B").should == nil
"blablabla".rindex("z").should == nil
"blablabla".rindex("BLA").should == nil
"blablabla".rindex("blablablabla").should == nil
"hello".rindex("lo", 0).should == nil
"hello".rindex("lo", 1).should == nil
"hello".rindex("lo", 2).should == nil
"hello".rindex("llo", 0).should == nil
"hello".rindex("llo", 1).should == nil
"hello".rindex("el", 0).should == nil
"hello".rindex("ello", 0).should == nil
"hello".rindex("", -6).should == nil
"hello".rindex("", -7).should == nil
"hello".rindex("h", -6).should == nil
end
it "tries to convert start_offset to an integer via to_int" do
obj = mock('5')
def obj.to_int() 5 end
"str".rindex("st", obj).should == 0
obj = mock('5')
def obj.respond_to?(arg, *) true end
def obj.method_missing(*args) 5 end
"str".rindex("st", obj).should == 0
end
it "raises a TypeError when given offset is nil" do
lambda { "str".rindex("st", nil) }.should raise_error(TypeError)
end
end
describe "String#rindex with Regexp" do
it "behaves the same as String#rindex(string) for escaped string regexps" do
["blablabla", "hello cruel world...!"].each do |str|
["", "b", "bla", "lab", "o c", "d."].each do |needle|
regexp = Regexp.new(Regexp.escape(needle))
str.rindex(regexp).should == str.rindex(needle)
0.upto(str.size + 1) do |start|
str.rindex(regexp, start).should == str.rindex(needle, start)
end
(-str.size - 1).upto(-1) do |start|
str.rindex(regexp, start).should == str.rindex(needle, start)
end
end
end
end
it "returns the index of the first match from the end of string of regexp" do
"blablabla".rindex(/bla/).should == 6
"blablabla".rindex(/BLA/i).should == 6
"blablabla".rindex(/.{0}/).should == 9
"blablabla".rindex(/.{1}/).should == 8
"blablabla".rindex(/.{2}/).should == 7
"blablabla".rindex(/.{6}/).should == 3
"blablabla".rindex(/.{9}/).should == 0
"blablabla".rindex(/.*/).should == 9
"blablabla".rindex(/.+/).should == 8
"blablabla".rindex(/bla|a/).should == 8
not_supported_on :opal do
"blablabla".rindex(/\A/).should == 0
"blablabla".rindex(/\Z/).should == 9
"blablabla".rindex(/\z/).should == 9
"blablabla\n".rindex(/\Z/).should == 10
"blablabla\n".rindex(/\z/).should == 10
end
"blablabla".rindex(/^/).should == 0
not_supported_on :opal do
"\nblablabla".rindex(/^/).should == 1
"b\nlablabla".rindex(/^/).should == 2
end
"blablabla".rindex(/$/).should == 9
"blablabla".rindex(/.l./).should == 6
end
it "sets $~ to MatchData of match and nil when there's none" do
'hello.'.rindex(/.(.)/)
$~[0].should == 'o.'
'hello.'.rindex(/not/)
$~.should == nil
end
it "starts the search at the given offset" do
"blablabla".rindex(/.{0}/, 5).should == 5
"blablabla".rindex(/.{1}/, 5).should == 5
"blablabla".rindex(/.{2}/, 5).should == 5
"blablabla".rindex(/.{3}/, 5).should == 5
"blablabla".rindex(/.{4}/, 5).should == 5
"blablabla".rindex(/.{0}/, 3).should == 3
"blablabla".rindex(/.{1}/, 3).should == 3
"blablabla".rindex(/.{2}/, 3).should == 3
"blablabla".rindex(/.{5}/, 3).should == 3
"blablabla".rindex(/.{6}/, 3).should == 3
"blablabla".rindex(/.l./, 0).should == 0
"blablabla".rindex(/.l./, 1).should == 0
"blablabla".rindex(/.l./, 2).should == 0
"blablabla".rindex(/.l./, 3).should == 3
"blablablax".rindex(/.x/, 10).should == 8
"blablablax".rindex(/.x/, 9).should == 8
"blablablax".rindex(/.x/, 8).should == 8
"blablablax".rindex(/..x/, 10).should == 7
"blablablax".rindex(/..x/, 9).should == 7
"blablablax".rindex(/..x/, 8).should == 7
"blablablax".rindex(/..x/, 7).should == 7
not_supported_on :opal do
"blablabla\n".rindex(/\Z/, 9).should == 9
end
end
it "starts the search at offset + self.length if offset is negative" do
str = "blablabla"
["bl", "bla", "blab", "la", "lab", "ab", ""].each do |needle|
(-str.length .. -1).each do |offset|
str.rindex(needle, offset).should ==
str.rindex(needle, offset + str.length)
end
end
end
it "returns nil if the substring isn't found" do
"blablabla".rindex(/BLA/).should == nil
"blablabla".rindex(/.{10}/).should == nil
"blablablax".rindex(/.x/, 7).should == nil
"blablablax".rindex(/..x/, 6).should == nil
not_supported_on :opal do
"blablabla".rindex(/\Z/, 5).should == nil
"blablabla".rindex(/\z/, 5).should == nil
"blablabla\n".rindex(/\z/, 9).should == nil
end
end
not_supported_on :opal do
it "supports \\G which matches at the given start offset" do
"helloYOU.".rindex(/YOU\G/, 8).should == 5
"helloYOU.".rindex(/YOU\G/).should == nil
idx = "helloYOUall!".index("YOU")
re = /YOU.+\G.+/
# The # marks where \G will match.
[
["helloYOU#all.", nil],
["helloYOUa#ll.", idx],
["helloYOUal#l.", idx],
["helloYOUall#.", idx],
["helloYOUall.#", nil]
].each do |i|
start = i[0].index("#")
str = i[0].delete("#")
str.rindex(re, start).should == i[1]
end
end
end
it "tries to convert start_offset to an integer via to_int" do
obj = mock('5')
def obj.to_int() 5 end
"str".rindex(/../, obj).should == 1
obj = mock('5')
def obj.respond_to?(arg, *) true end
def obj.method_missing(*args); 5; end
"str".rindex(/../, obj).should == 1
end
it "raises a TypeError when given offset is nil" do
lambda { "str".rindex(/../, nil) }.should raise_error(TypeError)
end
with_feature :encoding do
it "returns the reverse character index of a multibyte character" do
"ありがりがとう".rindex("").should == 4
"ありがりがとう".rindex(//).should == 4
end
it "returns the character index before the finish" do
"ありがりがとう".rindex("", 3).should == 2
"ありがりがとう".rindex(//, 3).should == 2
end
it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
re = Regexp.new "".encode(Encoding::EUC_JP)
lambda do
"あれ".rindex re
end.should raise_error(Encoding::CompatibilityError)
end
end
end

View file

@ -0,0 +1,116 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#rjust with length, padding" do
it "returns a new string of specified length with self right justified and padded with padstr" do
"hello".rjust(20, '1234').should == "123412341234123hello"
"".rjust(1, "abcd").should == "a"
"".rjust(2, "abcd").should == "ab"
"".rjust(3, "abcd").should == "abc"
"".rjust(4, "abcd").should == "abcd"
"".rjust(6, "abcd").should == "abcdab"
"OK".rjust(3, "abcd").should == "aOK"
"OK".rjust(4, "abcd").should == "abOK"
"OK".rjust(6, "abcd").should == "abcdOK"
"OK".rjust(8, "abcd").should == "abcdabOK"
end
it "pads with whitespace if no padstr is given" do
"hello".rjust(20).should == " hello"
end
it "returns self if it's longer than or as long as the specified length" do
"".rjust(0).should == ""
"".rjust(-1).should == ""
"hello".rjust(4).should == "hello"
"hello".rjust(-1).should == "hello"
"this".rjust(3).should == "this"
"radiology".rjust(8, '-').should == "radiology"
end
it "taints result when self or padstr is tainted" do
"x".taint.rjust(4).tainted?.should == true
"x".taint.rjust(0).tainted?.should == true
"".taint.rjust(0).tainted?.should == true
"x".taint.rjust(4, "*").tainted?.should == true
"x".rjust(4, "*".taint).tainted?.should == true
end
it "tries to convert length to an integer using to_int" do
"^".rjust(3.8, "^_").should == "^_^"
obj = mock('3')
obj.should_receive(:to_int).and_return(3)
"o".rjust(obj, "o_").should == "o_o"
end
it "raises a TypeError when length can't be converted to an integer" do
lambda { "hello".rjust("x") }.should raise_error(TypeError)
lambda { "hello".rjust("x", "y") }.should raise_error(TypeError)
lambda { "hello".rjust([]) }.should raise_error(TypeError)
lambda { "hello".rjust(mock('x')) }.should raise_error(TypeError)
end
it "tries to convert padstr to a string using to_str" do
padstr = mock('123')
padstr.should_receive(:to_str).and_return("123")
"hello".rjust(10, padstr).should == "12312hello"
end
it "raises a TypeError when padstr can't be converted" do
lambda { "hello".rjust(20, []) }.should raise_error(TypeError)
lambda { "hello".rjust(20, Object.new)}.should raise_error(TypeError)
lambda { "hello".rjust(20, mock('x')) }.should raise_error(TypeError)
end
it "raises an ArgumentError when padstr is empty" do
lambda { "hello".rjust(10, '') }.should raise_error(ArgumentError)
end
it "returns subclass instances when called on subclasses" do
StringSpecs::MyString.new("").rjust(10).should be_an_instance_of(StringSpecs::MyString)
StringSpecs::MyString.new("foo").rjust(10).should be_an_instance_of(StringSpecs::MyString)
StringSpecs::MyString.new("foo").rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString)
"".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
"foo".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String)
end
it "when padding is tainted and self is untainted returns a tainted string if and only if length is longer than self" do
"hello".rjust(4, 'X'.taint).tainted?.should be_false
"hello".rjust(5, 'X'.taint).tainted?.should be_false
"hello".rjust(6, 'X'.taint).tainted?.should be_true
end
with_feature :encoding do
describe "with width" do
it "returns a String in the same encoding as the original" do
str = "abc".force_encoding Encoding::IBM437
result = str.rjust 5
result.should == " abc"
result.encoding.should equal(Encoding::IBM437)
end
end
describe "with width, pattern" do
it "returns a String in the compatible encoding" do
str = "abc".force_encoding Encoding::IBM437
result = str.rjust 5, ""
result.should == "ああabc"
result.encoding.should equal(Encoding::UTF_8)
end
it "raises an Encoding::CompatibilityError if the encodings are incompatible" do
pat = "".encode Encoding::EUC_JP
lambda do
"あれ".rjust 5, pat
end.should raise_error(Encoding::CompatibilityError)
end
end
end
end

View file

@ -0,0 +1,33 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#rpartition with String" do
it "returns an array of substrings based on splitting on the given string" do
"hello world".rpartition("o").should == ["hello w", "o", "rld"]
end
it "always returns 3 elements" do
"hello".rpartition("x").should == ["", "", "hello"]
"hello".rpartition("hello").should == ["", "hello", ""]
end
it "accepts regexp" do
"hello!".rpartition(/l./).should == ["hel", "lo", "!"]
end
it "affects $~" do
matched_string = "hello!".rpartition(/l./)[1]
matched_string.should == $~[0]
end
it "converts its argument using :to_str" do
find = mock('l')
find.should_receive(:to_str).and_return("l")
"hello".rpartition(find).should == ["hel","l","o"]
end
it "raises an error if not convertible to string" do
lambda{ "hello".rpartition(5) }.should raise_error(TypeError)
lambda{ "hello".rpartition(nil) }.should raise_error(TypeError)
end
end

View file

@ -0,0 +1,52 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#rstrip" do
it "returns a copy of self with trailing whitespace removed" do
" hello ".rstrip.should == " hello"
" hello world ".rstrip.should == " hello world"
" hello world \n\r\t\n\v\r".rstrip.should == " hello world"
"hello".rstrip.should == "hello"
"hello\x00".rstrip.should == "hello"
end
it "returns a copy of self with all trailing whitespace and NULL bytes removed" do
"\x00 \x00hello\x00 \x00".rstrip.should == "\x00 \x00hello"
end
it "taints the result when self is tainted" do
"".taint.rstrip.tainted?.should == true
"ok".taint.rstrip.tainted?.should == true
"ok ".taint.rstrip.tainted?.should == true
end
end
describe "String#rstrip!" do
it "modifies self in place and returns self" do
a = " hello "
a.rstrip!.should equal(a)
a.should == " hello"
end
it "modifies self removing trailing NULL bytes and whitespace" do
a = "\x00 \x00hello\x00 \x00"
a.rstrip!
a.should == "\x00 \x00hello"
end
it "returns nil if no modifications were made" do
a = "hello"
a.rstrip!.should == nil
a.should == "hello"
end
it "raises a RuntimeError on a frozen instance that is modified" do
lambda { " hello ".freeze.rstrip! }.should raise_error(RuntimeError)
end
# see [ruby-core:23666]
it "raises a RuntimeError on a frozen instance that would not be modified" do
lambda { "hello".freeze.rstrip! }.should raise_error(RuntimeError)
lambda { "".freeze.rstrip! }.should raise_error(RuntimeError)
end
end

View file

@ -0,0 +1,192 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#scan" do
it "returns an array containing all matches" do
"cruel world".scan(/\w+/).should == ["cruel", "world"]
"cruel world".scan(/.../).should == ["cru", "el ", "wor"]
# Edge case
"hello".scan(//).should == ["", "", "", "", "", ""]
"".scan(//).should == [""]
end
it "respects unicode when the pattern collapses to nothing" do
str = "こにちわ"
reg = %r!!
str.scan(reg).should == ["", "", "", "", ""]
end
it "stores groups as arrays in the returned arrays" do
"hello".scan(/()/).should == [[""]] * 6
"hello".scan(/()()/).should == [["", ""]] * 6
"cruel world".scan(/(...)/).should == [["cru"], ["el "], ["wor"]]
"cruel world".scan(/(..)(..)/).should == [["cr", "ue"], ["l ", "wo"]]
end
it "scans for occurrences of the string if pattern is a string" do
"one two one two".scan('one').should == ["one", "one"]
"hello.".scan('.').should == ['.']
end
it "sets $~ to MatchData of last match and nil when there's none" do
'hello.'.scan(/.(.)/)
$~[0].should == 'o.'
'hello.'.scan(/not/)
$~.should == nil
'hello.'.scan('l')
$~.begin(0).should == 3
$~[0].should == 'l'
'hello.'.scan('not')
$~.should == nil
end
it "supports \\G which matches the end of the previous match / string start for first match" do
"one two one two".scan(/\G\w+/).should == ["one"]
"one two one two".scan(/\G\w+\s*/).should == ["one ", "two ", "one ", "two"]
"one two one two".scan(/\G\s*\w+/).should == ["one", " two", " one", " two"]
end
it "tries to convert pattern to a string via to_str" do
obj = mock('o')
obj.should_receive(:to_str).and_return("o")
"o_o".scan(obj).should == ["o", "o"]
end
it "raises a TypeError if pattern isn't a Regexp and can't be converted to a String" do
lambda { "cruel world".scan(5) }.should raise_error(TypeError)
not_supported_on :opal do
lambda { "cruel world".scan(:test) }.should raise_error(TypeError)
end
lambda { "cruel world".scan(mock('x')) }.should raise_error(TypeError)
end
it "taints the results if the String argument is tainted" do
a = "hello hello hello".scan("hello".taint)
a.each { |m| m.tainted?.should be_true }
end
it "taints the results when passed a String argument if self is tainted" do
a = "hello hello hello".taint.scan("hello")
a.each { |m| m.tainted?.should be_true }
end
it "taints the results if the Regexp argument is tainted" do
a = "hello".scan(/./.taint)
a.each { |m| m.tainted?.should be_true }
end
it "taints the results when passed a Regexp argument if self is tainted" do
a = "hello".taint.scan(/./)
a.each { |m| m.tainted?.should be_true }
end
end
describe "String#scan with pattern and block" do
it "returns self" do
s = "foo"
s.scan(/./) {}.should equal(s)
s.scan(/roar/) {}.should equal(s)
end
it "passes each match to the block as one argument: an array" do
a = []
"cruel world".scan(/\w+/) { |*w| a << w }
a.should == [["cruel"], ["world"]]
end
it "passes groups to the block as one argument: an array" do
a = []
"cruel world".scan(/(..)(..)/) { |w| a << w }
a.should == [["cr", "ue"], ["l ", "wo"]]
end
it "sets $~ for access from the block" do
str = "hello"
matches = []
offsets = []
str.scan(/([aeiou])/) do
md = $~
md.string.should == str
matches << md.to_a
offsets << md.offset(0)
str
end
matches.should == [["e", "e"], ["o", "o"]]
offsets.should == [[1, 2], [4, 5]]
matches = []
offsets = []
str.scan("l") do
md = $~
md.string.should == str
matches << md.to_a
offsets << md.offset(0)
str
end
matches.should == [["l"], ["l"]]
offsets.should == [[2, 3], [3, 4]]
end
it "restores $~ after leaving the block" do
[/./, "l"].each do |pattern|
old_md = nil
"hello".scan(pattern) do
old_md = $~
"ok".match(/./)
"x"
end
$~[0].should == old_md[0]
$~.string.should == "hello"
end
end
it "sets $~ to MatchData of last match and nil when there's none for access from outside" do
'hello.'.scan('l') { 'x' }
$~.begin(0).should == 3
$~[0].should == 'l'
'hello.'.scan('not') { 'x' }
$~.should == nil
'hello.'.scan(/.(.)/) { 'x' }
$~[0].should == 'o.'
'hello.'.scan(/not/) { 'x' }
$~.should == nil
end
it "taints the results if the String argument is tainted" do
"hello hello hello".scan("hello".taint).each { |m| m.tainted?.should be_true }
end
it "taints the results when passed a String argument if self is tainted" do
"hello hello hello".taint.scan("hello").each { |m| m.tainted?.should be_true }
end
it "taints the results if the Regexp argument is tainted" do
"hello".scan(/./.taint).each { |m| m.tainted?.should be_true }
end
it "taints the results when passed a Regexp argument if self is tainted" do
"hello".taint.scan(/./).each { |m| m.tainted?.should be_true }
end
it "passes block arguments as individual arguments when blocks are provided" do
"a b c\na b c\na b c".scan(/(\w*) (\w*) (\w*)/) do |first,second,third|
first.should == 'a';
second.should == 'b';
third.should == 'c';
end
end
end

View file

@ -0,0 +1,101 @@
# -*- encoding: utf-8 -*-
require File.expand_path("../../../spec_helper", __FILE__)
describe "String#scrub with a default replacement" do
it "returns self for valid strings" do
input = "foo"
input.scrub.should == input
end
it "replaces invalid byte sequences" do
x81 = [0x81].pack('C').force_encoding('utf-8')
"abc\u3042#{x81}".scrub.should == "abc\u3042\uFFFD"
end
it "returns a copy of self when the input encoding is BINARY" do
input = "foo".encode('BINARY')
input.scrub.should == "foo"
end
it "replaces invalid byte sequences when using ASCII as the input encoding" do
xE3x80 = [0xE3, 0x80].pack('CC').force_encoding 'utf-8'
input = "abc\u3042#{xE3x80}".force_encoding('ASCII')
input.scrub.should == "abc?????"
end
end
describe "String#scrub with a custom replacement" do
it "returns self for valid strings" do
input = "foo"
input.scrub("*").should == input
end
it "replaces invalid byte sequences" do
x81 = [0x81].pack('C').force_encoding('utf-8')
"abc\u3042#{x81}".scrub("*").should == "abc\u3042*"
end
it "replaces an incomplete character at the end with a single replacement" do
xE3x80 = [0xE3, 0x80].pack('CC').force_encoding 'utf-8'
xE3x80.scrub("*").should == "*"
end
it "raises ArgumentError for replacements with an invalid encoding" do
x81 = [0x81].pack('C').force_encoding('utf-8')
xE4 = [0xE4].pack('C').force_encoding('utf-8')
block = lambda { "foo#{x81}".scrub(xE4) }
block.should raise_error(ArgumentError)
end
it "raises TypeError when a non String replacement is given" do
x81 = [0x81].pack('C').force_encoding('utf-8')
block = lambda { "foo#{x81}".scrub(1) }
block.should raise_error(TypeError)
end
end
describe "String#scrub with a block" do
it "returns self for valid strings" do
input = "foo"
input.scrub { |b| "*" }.should == input
end
it "replaces invalid byte sequences" do
xE3x80 = [0xE3, 0x80].pack('CC').force_encoding 'utf-8'
replaced = "abc\u3042#{xE3x80}".scrub { |b| "<#{b.unpack("H*")[0]}>" }
replaced.should == "abc\u3042<e380>"
end
it "replaces invalid byte sequences using a custom encoding" do
x80x80 = [0x80, 0x80].pack('CC').force_encoding 'utf-8'
replaced = x80x80.scrub do |bad|
bad.encode(Encoding::UTF_8, Encoding::Windows_1252)
end
replaced.should == "€€"
end
end
describe "String#scrub!" do
it "modifies self for valid strings" do
x81 = [0x81].pack('C').force_encoding('utf-8')
input = "a#{x81}"
input.scrub!
input.should == "a\uFFFD"
end
it "accepts blocks" do
x81 = [0x81].pack('C').force_encoding('utf-8')
input = "a#{x81}"
input.scrub! { |b| "<?>" }
input.should == "a<?>"
end
end

View file

@ -0,0 +1,105 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
describe "String#setbyte" do
it "returns an Integer" do
"a".setbyte(0,1).should be_kind_of(Integer)
end
it "modifies the receiver" do
str = "glark"
old_id = str.object_id
str.setbyte(0,88)
str.object_id.should == old_id
end
it "changes the byte at the given index to the new byte" do
str = "a"
str.setbyte(0,98)
str.should == 'b'
# copy-on-write case
str1, str2 = "fooXbar".split("X")
str2.setbyte(0, 50)
str2.should == "2ar"
str1.should == "foo"
end
it "allows changing bytes in multi-byte characters" do
str = "\u{915}"
str.setbyte(1,254)
str.getbyte(1).should == 254
end
it "can invalidate a String's encoding" do
str = "glark"
str.valid_encoding?.should be_true
str.setbyte(2,253)
str.valid_encoding?.should be_false
end
it "regards a negative index as counting from the end of the String" do
str = "hedgehog"
str.setbyte(-3, 108)
str.should == "hedgelog"
# copy-on-write case
str1, str2 = "fooXbar".split("X")
str2.setbyte(-1, 50)
str2.should == "ba2"
str1.should == "foo"
end
it "raises an IndexError if the index is greater than the String bytesize" do
lambda { "?".setbyte(1, 97) }.should raise_error(IndexError)
end
it "raises an IndexError if the nexgative index is greater magnitude than the String bytesize" do
lambda { "???".setbyte(-5, 97) }.should raise_error(IndexError)
end
it "sets a byte at an index greater than String size" do
chr = "\u{998}"
chr.bytesize.should == 3
chr.setbyte(2, 150)
chr.should == "\xe0\xa6\x96"
end
it "does not modify the original string when using String.new" do
str1 = "hedgehog"
str2 = String.new(str1)
str2.setbyte(0, 108)
str2.should == "ledgehog"
str2.should_not == "hedgehog"
str1.should == "hedgehog"
str1.should_not == "ledgehog"
end
it "raises a RuntimeError if self is frozen" do
str = "cold".freeze
str.frozen?.should be_true
lambda { str.setbyte(3,96) }.should raise_error(RuntimeError)
end
it "raises a TypeError unless the second argument is an Integer" do
lambda { "a".setbyte(0,'a') }.should raise_error(TypeError)
end
it "calls #to_int to convert the index" do
index = mock("setbyte index")
index.should_receive(:to_int).and_return(1)
str = "hat"
str.setbyte(index, "i".ord)
str.should == "hit"
end
it "calls to_int to convert the value" do
value = mock("setbyte value")
value.should_receive(:to_int).and_return("i".ord)
str = "hat"
str.setbyte(1, value)
str.should == "hit"
end
end

View file

@ -0,0 +1,82 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../../spec_helper', __FILE__)
require File.expand_path('../../fixtures/classes', __FILE__)
describe :string_chars, shared: true do
it "passes each char in self to the given block" do
a = []
"hello".send(@method) { |c| a << c }
a.should == ['h', 'e', 'l', 'l', 'o']
end
it "returns self" do
s = StringSpecs::MyString.new "hello"
s.send(@method){}.should equal(s)
end
it "is unicode aware" do
"\303\207\342\210\202\303\251\306\222g".send(@method).to_a.should ==
["\303\207", "\342\210\202", "\303\251", "\306\222", "g"]
end
with_feature :encoding do
it "returns characters in the same encoding as self" do
"&%".force_encoding('Shift_JIS').send(@method).to_a.all? {|c| c.encoding.name.should == 'Shift_JIS'}
"&%".encode('ASCII-8BIT').send(@method).to_a.all? {|c| c.encoding.name.should == 'ASCII-8BIT'}
end
it "works with multibyte characters" do
s = "\u{8987}".force_encoding("UTF-8")
s.bytesize.should == 3
s.send(@method).to_a.should == [s]
end
it "works if the String's contents is invalid for its encoding" do
xA4 = [0xA4].pack('C')
xA4.force_encoding('UTF-8')
xA4.valid_encoding?.should be_false
xA4.send(@method).to_a.should == [xA4.force_encoding("UTF-8")]
end
it "returns a different character if the String is transcoded" do
s = "\u{20AC}".force_encoding('UTF-8')
s.encode('UTF-8').send(@method).to_a.should == ["\u{20AC}".force_encoding('UTF-8')]
s.encode('iso-8859-15').send(@method).to_a.should == [
[0xA4].pack('C').force_encoding('iso-8859-15')]
s.encode('iso-8859-15').encode('UTF-8').send(@method).to_a.should == [
"\u{20AC}".force_encoding('UTF-8')]
end
it "uses the String's encoding to determine what characters it contains" do
s = "\u{24B62}"
s.force_encoding('UTF-8').send(@method).to_a.should == [
s.force_encoding('UTF-8')
]
s.force_encoding('BINARY').send(@method).to_a.should == [
[0xF0].pack('C').force_encoding('BINARY'),
[0xA4].pack('C').force_encoding('BINARY'),
[0xAD].pack('C').force_encoding('BINARY'),
[0xA2].pack('C').force_encoding('BINARY')
]
s.force_encoding('SJIS').send(@method).to_a.should == [
[0xF0,0xA4].pack('CC').force_encoding('SJIS'),
[0xAD].pack('C').force_encoding('SJIS'),
[0xA2].pack('C').force_encoding('SJIS')
]
end
it "taints resulting strings when self is tainted" do
str = "hello"
str.send(@method) do |x|
x.tainted?.should == false
end
str.dup.taint.send(@method) do |x|
x.tainted?.should == true
end
end
end
end

View file

@ -0,0 +1,56 @@
# -*- encoding: binary -*-
describe :string_codepoints, shared: true do
it "raises an ArgumentError when self has an invalid encoding and a method is called on the returned Enumerator" do
s = "\xDF".force_encoding(Encoding::UTF_8)
s.valid_encoding?.should be_false
lambda { s.send(@method).to_a }.should raise_error(ArgumentError)
end
it "yields each codepoint to the block if one is given" do
codepoints = []
"abcd".send(@method) do |codepoint|
codepoints << codepoint
end
codepoints.should == [97, 98, 99, 100]
end
it "raises an ArgumentError if self's encoding is invalid and a block is given" do
s = "\xDF".force_encoding(Encoding::UTF_8)
s.valid_encoding?.should be_false
lambda { s.send(@method) { } }.should raise_error(ArgumentError)
end
it "returns codepoints as Fixnums" do
"glark\u{20}".send(@method).to_a.each do |codepoint|
codepoint.should be_an_instance_of(Fixnum)
end
end
it "returns one codepoint for each character" do
s = "\u{9876}\u{28}\u{1987}"
s.send(@method).to_a.size.should == s.chars.to_a.size
end
it "works for multibyte characters" do
s = "\u{9819}"
s.bytesize.should == 3
s.send(@method).to_a.should == [38937]
end
it "returns the codepoint corresponding to the character's position in the String's encoding" do
"\u{787}".send(@method).to_a.should == [1927]
end
it "round-trips to the original String using Integer#chr" do
s = "\u{13}\u{7711}\u{1010}"
s2 = ""
s.send(@method) {|n| s2 << n.chr(Encoding::UTF_8)}
s.should == s2
end
it "is synonomous with #bytes for Strings which are single-byte optimisable" do
s = "(){}".encode('ascii')
s.ascii_only?.should be_true
s.send(@method).to_a.should == s.bytes.to_a
end
end

View file

@ -0,0 +1,160 @@
describe :string_concat, shared: true do
it "concatenates the given argument to self and returns self" do
str = 'hello '
str.send(@method, 'world').should equal(str)
str.should == "hello world"
end
it "converts the given argument to a String using to_str" do
obj = mock('world!')
obj.should_receive(:to_str).and_return("world!")
a = 'hello '.send(@method, obj)
a.should == 'hello world!'
end
it "raises a TypeError if the given argument can't be converted to a String" do
lambda { 'hello '.send(@method, []) }.should raise_error(TypeError)
lambda { 'hello '.send(@method, mock('x')) }.should raise_error(TypeError)
end
it "raises a RuntimeError when self is frozen" do
a = "hello"
a.freeze
lambda { a.send(@method, "") }.should raise_error(RuntimeError)
lambda { a.send(@method, "test") }.should raise_error(RuntimeError)
end
it "returns a String when given a subclass instance" do
a = "hello"
a.send(@method, StringSpecs::MyString.new(" world"))
a.should == "hello world"
a.should be_an_instance_of(String)
end
it "returns an instance of same class when called on a subclass" do
str = StringSpecs::MyString.new("hello")
str.send(@method, " world")
str.should == "hello world"
str.should be_an_instance_of(StringSpecs::MyString)
end
it "taints self if other is tainted" do
"x".send(@method, "".taint).tainted?.should == true
"x".send(@method, "y".taint).tainted?.should == true
end
it "untrusts self if other is untrusted" do
"x".send(@method, "".untrust).untrusted?.should == true
"x".send(@method, "y".untrust).untrusted?.should == true
end
describe "with Integer" do
it "concatencates the argument interpreted as a codepoint" do
b = "".send(@method, 33)
b.should == "!"
b.encode!(Encoding::UTF_8)
b.send(@method, 0x203D)
b.should == "!\u203D"
end
# #5855
it "returns a ASCII-8BIT string if self is US-ASCII and the argument is between 128-255 (inclusive)" do
a = ("".encode(Encoding::US_ASCII).send(@method, 128))
a.encoding.should == Encoding::ASCII_8BIT
a.should == 128.chr
a = ("".encode(Encoding::US_ASCII).send(@method, 255))
a.encoding.should == Encoding::ASCII_8BIT
a.should == 255.chr
end
it "raises RangeError if the argument is an invalid codepoint for self's encoding" do
lambda { "".encode(Encoding::US_ASCII).send(@method, 256) }.should raise_error(RangeError)
lambda { "".encode(Encoding::EUC_JP).send(@method, 0x81) }.should raise_error(RangeError)
end
it "raises RangeError if the argument is negative" do
lambda { "".send(@method, -200) }.should raise_error(RangeError)
lambda { "".send(@method, -bignum_value) }.should raise_error(RangeError)
end
it "doesn't call to_int on its argument" do
x = mock('x')
x.should_not_receive(:to_int)
lambda { "".send(@method, x) }.should raise_error(TypeError)
end
it "raises a RuntimeError when self is frozen" do
a = "hello"
a.freeze
lambda { a.send(@method, 0) }.should raise_error(RuntimeError)
lambda { a.send(@method, 33) }.should raise_error(RuntimeError)
end
end
end
describe :string_concat_encoding, shared: true do
describe "when self is in an ASCII-incompatible encoding incompatible with the argument's encoding" do
it "uses self's encoding if both are empty" do
"".encode("UTF-16LE").send(@method, "").encoding.should == Encoding::UTF_16LE
end
it "uses self's encoding if the argument is empty" do
"x".encode("UTF-16LE").send(@method, "").encoding.should == Encoding::UTF_16LE
end
it "uses the argument's encoding if self is empty" do
"".encode("UTF-16LE").send(@method, "x".encode("UTF-8")).encoding.should == Encoding::UTF_8
end
it "raises Encoding::CompatibilityError if neither are empty" do
lambda { "x".encode("UTF-16LE").send(@method, "y".encode("UTF-8")) }.should raise_error(Encoding::CompatibilityError)
end
end
describe "when the argument is in an ASCII-incompatible encoding incompatible with self's encoding" do
it "uses self's encoding if both are empty" do
"".encode("UTF-8").send(@method, "".encode("UTF-16LE")).encoding.should == Encoding::UTF_8
end
it "uses self's encoding if the argument is empty" do
"x".encode("UTF-8").send(@method, "".encode("UTF-16LE")).encoding.should == Encoding::UTF_8
end
it "uses the argument's encoding if self is empty" do
"".encode("UTF-8").send(@method, "x".encode("UTF-16LE")).encoding.should == Encoding::UTF_16LE
end
it "raises Encoding::CompatibilityError if neither are empty" do
lambda { "x".encode("UTF-8").send(@method, "y".encode("UTF-16LE")) }.should raise_error(Encoding::CompatibilityError)
end
end
describe "when self and the argument are in different ASCII-compatible encodings" do
it "uses self's encoding if both are ASCII-only" do
"abc".encode("UTF-8").send(@method, "123".encode("SHIFT_JIS")).encoding.should == Encoding::UTF_8
end
it "uses self's encoding if the argument is ASCII-only" do
"\u00E9".encode("UTF-8").send(@method, "123".encode("ISO-8859-1")).encoding.should == Encoding::UTF_8
end
it "uses the argument's encoding if self is ASCII-only" do
"abc".encode("UTF-8").send(@method, "\u00E9".encode("ISO-8859-1")).encoding.should == Encoding::ISO_8859_1
end
it "raises Encoding::CompatibilityError if neither are ASCII-only" do
lambda { "\u00E9".encode("UTF-8").send(@method, "\u00E9".encode("ISO-8859-1")) }.should raise_error(Encoding::CompatibilityError)
end
end
describe "when self is ASCII-8BIT and argument is US-ASCII" do
it "uses ASCII-8BIT encoding" do
"abc".encode("ASCII-8BIT").send(@method, "123".encode("US-ASCII")).encoding.should == Encoding::ASCII_8BIT
end
end
end

View file

@ -0,0 +1,26 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../../spec_helper', __FILE__)
require File.expand_path('../../fixtures/classes', __FILE__)
describe :string_each_char_without_block, shared: true do
describe "when no block is given" do
it "returns an enumerator" do
enum = "hello".send(@method)
enum.should be_an_instance_of(Enumerator)
enum.to_a.should == ['h', 'e', 'l', 'l', 'o']
end
describe "returned enumerator" do
describe "size" do
it "should return the size of the string" do
str = "hello"
str.send(@method).size.should == str.size
str = "ola"
str.send(@method).size.should == str.size
str = "\303\207\342\210\202\303\251\306\222g"
str.send(@method).size.should == str.size
end
end
end
end
end

View file

@ -0,0 +1,33 @@
# -*- encoding: binary -*-
describe :string_each_codepoint_without_block, shared: true do
describe "when no block is given" do
it "returns an Enumerator" do
"".send(@method).should be_an_instance_of(Enumerator)
end
it "returns an Enumerator even when self has an invalid encoding" do
s = "\xDF".force_encoding(Encoding::UTF_8)
s.valid_encoding?.should be_false
s.send(@method).should be_an_instance_of(Enumerator)
end
describe "returned Enumerator" do
describe "size" do
it "should return the size of the string" do
str = "hello"
str.send(@method).size.should == str.size
str = "ola"
str.send(@method).size.should == str.size
str = "\303\207\342\210\202\303\251\306\222g"
str.send(@method).size.should == str.size
end
it "should return the size of the string even when the string has an invalid encoding" do
s = "\xDF".force_encoding(Encoding::UTF_8)
s.valid_encoding?.should be_false
s.send(@method).size.should == 1
end
end
end
end
end

View file

@ -0,0 +1,136 @@
describe :string_each_line, shared: true do
it "splits using default newline separator when none is specified" do
a = []
"one\ntwo\r\nthree".send(@method) { |s| a << s }
a.should == ["one\n", "two\r\n", "three"]
b = []
"hello\n\n\nworld".send(@method) { |s| b << s }
b.should == ["hello\n", "\n", "\n", "world"]
c = []
"\n\n\n\n\n".send(@method) {|s| c << s}
c.should == ["\n", "\n", "\n", "\n", "\n"]
end
it "splits self using the supplied record separator and passes each substring to the block" do
a = []
"one\ntwo\r\nthree".send(@method, "\n") { |s| a << s }
a.should == ["one\n", "two\r\n", "three"]
b = []
"hello\nworld".send(@method, 'l') { |s| b << s }
b.should == [ "hel", "l", "o\nworl", "d" ]
c = []
"hello\n\n\nworld".send(@method, "\n") { |s| c << s }
c.should == ["hello\n", "\n", "\n", "world"]
end
it "taints substrings that are passed to the block if self is tainted" do
"one\ntwo\r\nthree".taint.send(@method) { |s| s.tainted?.should == true }
"x.y.".send(@method, ".".taint) { |s| s.tainted?.should == false }
end
it "passes self as a whole to the block if the separator is nil" do
a = []
"one\ntwo\r\nthree".send(@method, nil) { |s| a << s }
a.should == ["one\ntwo\r\nthree"]
end
ruby_version_is ''...'2.5' do
it "yields paragraphs (broken by 2 or more successive newlines) when passed ''" do
a = []
"hello\nworld\n\n\nand\nuniverse\n\n\n\n\n".send(@method, '') { |s| a << s }
a.should == ["hello\nworld\n\n\n", "and\nuniverse\n\n\n\n\n"]
a = []
"hello\nworld\n\n\nand\nuniverse\n\n\n\n\ndog".send(@method, '') { |s| a << s }
a.should == ["hello\nworld\n\n\n", "and\nuniverse\n\n\n\n\n", "dog"]
end
end
quarantine! do # Currently fails on Travis
ruby_version_is '2.5' do
it "yields paragraphs (broken by 2 or more successive newlines) when passed ''" do
a = []
"hello\nworld\n\n\nand\nuniverse\n\n\n\n\n".send(@method, '') { |s| a << s }
a.should == ["hello\nworld\n\n", "and\nuniverse\n\n"]
a = []
"hello\nworld\n\n\nand\nuniverse\n\n\n\n\ndog".send(@method, '') { |s| a << s }
a.should == ["hello\nworld\n\n", "and\nuniverse\n\n", "dog"]
end
end
end
describe "uses $/" do
before :each do
@before_separator = $/
end
after :each do
$/ = @before_separator
end
it "as the separator when none is given" do
[
"", "x", "x\ny", "x\ry", "x\r\ny", "x\n\r\r\ny",
"hello hullo bello"
].each do |str|
["", "llo", "\n", "\r", nil].each do |sep|
expected = []
str.send(@method, sep) { |x| expected << x }
$/ = sep
actual = []
str.send(@method) { |x| actual << x }
actual.should == expected
end
end
end
end
it "yields subclass instances for subclasses" do
a = []
StringSpecs::MyString.new("hello\nworld").send(@method) { |s| a << s.class }
a.should == [StringSpecs::MyString, StringSpecs::MyString]
end
it "returns self" do
s = "hello\nworld"
(s.send(@method) {}).should equal(s)
end
it "tries to convert the separator to a string using to_str" do
separator = mock('l')
separator.should_receive(:to_str).and_return("l")
a = []
"hello\nworld".send(@method, separator) { |s| a << s }
a.should == [ "hel", "l", "o\nworl", "d" ]
end
it "does not care if the string is modified while substituting" do
str = "hello\nworld."
out = []
str.send(@method){|x| out << x; str[-1] = '!' }.should == "hello\nworld!"
out.should == ["hello\n", "world."]
end
it "raises a TypeError when the separator can't be converted to a string" do
lambda { "hello world".send(@method, false) {} }.should raise_error(TypeError)
lambda { "hello world".send(@method, mock('x')) {} }.should raise_error(TypeError)
end
it "accepts a string separator" do
"hello world".send(@method, ?o).to_a.should == ["hello", " wo", "rld"]
end
it "raises a TypeError when the separator is a symbol" do
lambda { "hello world".send(@method, :o).to_a }.should raise_error(TypeError)
end
end

View file

@ -0,0 +1,17 @@
describe :string_each_line_without_block, shared: true do
describe "when no block is given" do
it "returns an enumerator" do
enum = "hello world".send(@method, ' ')
enum.should be_an_instance_of(Enumerator)
enum.to_a.should == ["hello ", "world"]
end
describe "returned Enumerator" do
describe "size" do
it "should return nil" do
"hello world".send(@method, ' ').size.should == nil
end
end
end
end
end

View file

@ -0,0 +1,247 @@
# -*- encoding: utf-8 -*-
describe :string_encode, shared: true do
describe "when passed no options" do
it "transcodes to Encoding.default_internal when set" do
Encoding.default_internal = Encoding::UTF_8
str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP
str.send(@method).should == ""
end
it "transcodes a 7-bit String despite no generic converting being available" do
lambda do
Encoding::Converter.new Encoding::Emacs_Mule, Encoding::ASCII_8BIT
end.should raise_error(Encoding::ConverterNotFoundError)
Encoding.default_internal = Encoding::Emacs_Mule
str = "\x79".force_encoding Encoding::ASCII_8BIT
str.send(@method).should == "y".force_encoding(Encoding::ASCII_8BIT)
end
it "raises an Encoding::ConverterNotFoundError when no conversion is possible" do
Encoding.default_internal = Encoding::Emacs_Mule
str = [0x80].pack('C').force_encoding Encoding::ASCII_8BIT
lambda { str.send(@method) }.should raise_error(Encoding::ConverterNotFoundError)
end
end
describe "when passed to encoding" do
it "accepts a String argument" do
str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP
str.send(@method, "utf-8").should == ""
end
it "calls #to_str to convert the object to an Encoding" do
enc = mock("string encode encoding")
enc.should_receive(:to_str).and_return("utf-8")
str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP
str.send(@method, enc).should == ""
end
it "transcodes to the passed encoding" do
str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP
str.send(@method, Encoding::UTF_8).should == ""
end
it "transcodes Japanese multibyte characters" do
str = "あいうえお"
str.send(@method, Encoding::ISO_2022_JP).should ==
"\e\x24\x42\x24\x22\x24\x24\x24\x26\x24\x28\x24\x2A\e\x28\x42".force_encoding(Encoding::ISO_2022_JP)
end
it "transcodes a 7-bit String despite no generic converting being available" do
lambda do
Encoding::Converter.new Encoding::Emacs_Mule, Encoding::ASCII_8BIT
end.should raise_error(Encoding::ConverterNotFoundError)
str = "\x79".force_encoding Encoding::ASCII_8BIT
str.send(@method, Encoding::Emacs_Mule).should == "y".force_encoding(Encoding::ASCII_8BIT)
end
it "raises an Encoding::ConverterNotFoundError when no conversion is possible" do
str = [0x80].pack('C').force_encoding Encoding::ASCII_8BIT
lambda do
str.send(@method, Encoding::Emacs_Mule)
end.should raise_error(Encoding::ConverterNotFoundError)
end
it "raises an Encoding::ConverterNotFoundError for an invalid encoding" do
lambda do
"abc".send(@method, "xyz")
end.should raise_error(Encoding::ConverterNotFoundError)
end
end
describe "when passed options" do
it "does not process transcoding options if not transcoding" do
result = "\ufffdあ".send(@method, undef: :replace)
result.should == "\ufffdあ"
end
it "calls #to_hash to convert the object" do
options = mock("string encode options")
options.should_receive(:to_hash).and_return({ undef: :replace })
result = "\ufffdあ".send(@method, options)
result.should == "\ufffdあ"
end
it "transcodes to Encoding.default_internal when set" do
Encoding.default_internal = Encoding::UTF_8
str = [0xA4, 0xA2].pack('CC').force_encoding Encoding::EUC_JP
str.send(@method, invalid: :replace).should == ""
end
it "raises an Encoding::ConverterNotFoundError when no conversion is possible despite 'invalid: :replace, undef: :replace'" do
Encoding.default_internal = Encoding::Emacs_Mule
str = [0x80].pack('C').force_encoding Encoding::ASCII_8BIT
lambda do
str.send(@method, invalid: :replace, undef: :replace)
end.should raise_error(Encoding::ConverterNotFoundError)
end
it "replaces invalid characters when replacing Emacs-Mule encoded strings" do
got = [0x80].pack('C').force_encoding('Emacs-Mule').send(@method, invalid: :replace)
got.should == "?".encode('Emacs-Mule')
end
end
describe "when passed to, from" do
it "transcodes between the encodings ignoring the String encoding" do
str = ""
result = [0xA6, 0xD0, 0x8F, 0xAB, 0xE4, 0x8F, 0xAB, 0xB1].pack('C8')
result.force_encoding Encoding::EUC_JP
str.send(@method, "euc-jp", "ibm437").should == result
end
it "calls #to_str to convert the from object to an Encoding" do
enc = mock("string encode encoding")
enc.should_receive(:to_str).and_return("ibm437")
str = ""
result = [0xA6, 0xD0, 0x8F, 0xAB, 0xE4, 0x8F, 0xAB, 0xB1].pack('C8')
result.force_encoding Encoding::EUC_JP
str.send(@method, "euc-jp", enc).should == result
end
end
describe "when passed to, options" do
it "replaces undefined characters in the destination encoding" do
result = "あ?あ".send(@method, Encoding::EUC_JP, undef: :replace)
# testing for: "\xA4\xA2?\xA4\xA2"
xA4xA2 = [0xA4, 0xA2].pack('CC')
result.should == "#{xA4xA2}?#{xA4xA2}".force_encoding("euc-jp")
end
it "replaces invalid characters in the destination encoding" do
xFF = [0xFF].pack('C').force_encoding('utf-8')
"ab#{xFF}c".send(@method, Encoding::ISO_8859_1, invalid: :replace).should == "ab?c"
end
it "calls #to_hash to convert the options object" do
options = mock("string encode options")
options.should_receive(:to_hash).and_return({ undef: :replace })
result = "あ?あ".send(@method, Encoding::EUC_JP, options)
xA4xA2 = [0xA4, 0xA2].pack('CC').force_encoding('utf-8')
result.should == "#{xA4xA2}?#{xA4xA2}".force_encoding("euc-jp")
end
end
describe "when passed to, from, options" do
it "replaces undefined characters in the destination encoding" do
str = "あ?あ".force_encoding Encoding::ASCII_8BIT
result = str.send(@method, "euc-jp", "utf-8", undef: :replace)
xA4xA2 = [0xA4, 0xA2].pack('CC').force_encoding('utf-8')
result.should == "#{xA4xA2}?#{xA4xA2}".force_encoding("euc-jp")
end
it "replaces invalid characters in the destination encoding" do
xFF = [0xFF].pack('C').force_encoding('utf-8')
str = "ab#{xFF}c".force_encoding Encoding::ASCII_8BIT
str.send(@method, "iso-8859-1", "utf-8", invalid: :replace).should == "ab?c"
end
it "calls #to_str to convert the to object to an encoding" do
to = mock("string encode to encoding")
to.should_receive(:to_str).and_return("iso-8859-1")
xFF = [0xFF].pack('C').force_encoding('utf-8')
str = "ab#{xFF}c".force_encoding Encoding::ASCII_8BIT
str.send(@method, to, "utf-8", invalid: :replace).should == "ab?c"
end
it "calls #to_str to convert the from object to an encoding" do
from = mock("string encode to encoding")
from.should_receive(:to_str).and_return("utf-8")
xFF = [0xFF].pack('C').force_encoding('utf-8')
str = "ab#{xFF}c".force_encoding Encoding::ASCII_8BIT
str.send(@method, "iso-8859-1", from, invalid: :replace).should == "ab?c"
end
it "calls #to_hash to convert the options object" do
options = mock("string encode options")
options.should_receive(:to_hash).and_return({ invalid: :replace })
xFF = [0xFF].pack('C').force_encoding('utf-8')
str = "ab#{xFF}c".force_encoding Encoding::ASCII_8BIT
str.send(@method, "iso-8859-1", "utf-8", options).should == "ab?c"
end
end
describe "given the xml: :text option" do
it "replaces all instances of '&' with '&amp;'" do
'& and &'.send(@method, "UTF-8", xml: :text).should == '&amp; and &amp;'
end
it "replaces all instances of '<' with '&lt;'" do
'< and <'.send(@method, "UTF-8", xml: :text).should == '&lt; and &lt;'
end
it "replaces all instances of '>' with '&gt;'" do
'> and >'.send(@method, "UTF-8", xml: :text).should == '&gt; and &gt;'
end
it "does not replace '\"'" do
'" and "'.send(@method, "UTF-8", xml: :text).should == '" and "'
end
it "replaces undefined characters with their upper-case hexadecimal numeric character references" do
'ürst'.send(@method, Encoding::US_ASCII, xml: :text).should == '&#xFC;rst'
end
end
describe "given the xml: :attr option" do
it "surrounds the encoded text with double-quotes" do
'abc'.send(@method, "UTF-8", xml: :attr).should == '"abc"'
end
it "replaces all instances of '&' with '&amp;'" do
'& and &'.send(@method, "UTF-8", xml: :attr).should == '"&amp; and &amp;"'
end
it "replaces all instances of '<' with '&lt;'" do
'< and <'.send(@method, "UTF-8", xml: :attr).should == '"&lt; and &lt;"'
end
it "replaces all instances of '>' with '&gt;'" do
'> and >'.send(@method, "UTF-8", xml: :attr).should == '"&gt; and &gt;"'
end
it "replaces all instances of '\"' with '&quot;'" do
'" and "'.send(@method, "UTF-8", xml: :attr).should == '"&quot; and &quot;"'
end
it "replaces undefined characters with their upper-case hexadecimal numeric character references" do
'ürst'.send(@method, Encoding::US_ASCII, xml: :attr).should == '"&#xFC;rst"'
end
end
it "raises ArgumentError if the value of the :xml option is not :text or :attr" do
lambda { ''.send(@method, "UTF-8", xml: :other) }.should raise_error(ArgumentError)
end
end

View file

@ -0,0 +1,34 @@
# -*- encoding: binary -*-
require File.expand_path('../../../../spec_helper', __FILE__)
require File.expand_path('../../fixtures/classes', __FILE__)
describe :string_eql_value, shared: true do
it "returns true if self <=> string returns 0" do
'hello'.send(@method, 'hello').should be_true
end
it "returns false if self <=> string does not return 0" do
"more".send(@method, "MORE").should be_false
"less".send(@method, "greater").should be_false
end
it "ignores encoding difference of compatible string" do
"hello".force_encoding("utf-8").send(@method, "hello".force_encoding("iso-8859-1")).should be_true
end
it "considers encoding difference of incompatible string" do
"\xff".force_encoding("utf-8").send(@method, "\xff".force_encoding("iso-8859-1")).should be_false
end
it "considers encoding compatibility" do
"hello".force_encoding("utf-8").send(@method, "hello".force_encoding("utf-32le")).should be_false
end
it "ignores subclass differences" do
a = "hello"
b = StringSpecs::MyString.new("hello")
a.send(@method, b).should be_true
b.send(@method, a).should be_true
end
end

View file

@ -0,0 +1,29 @@
require File.expand_path('../../../../spec_helper', __FILE__)
require File.expand_path('../../fixtures/classes', __FILE__)
describe :string_equal_value, shared: true do
it "returns false if obj does not respond to to_str" do
'hello'.send(@method, 5).should be_false
not_supported_on :opal do
'hello'.send(@method, :hello).should be_false
end
'hello'.send(@method, mock('x')).should be_false
end
it "returns obj == self if obj responds to to_str" do
obj = Object.new
# String#== merely checks if #to_str is defined. It does
# not call it.
obj.stub!(:to_str)
# Don't use @method for :== in `obj.should_recerive(:==)`
obj.should_receive(:==).and_return(true)
'hello'.send(@method, obj).should be_true
end
it "is not fooled by NUL characters" do
"abc\0def".send(@method, "abc\0xyz").should be_false
end
end

View file

@ -0,0 +1,28 @@
# encoding: utf-8
describe :string_length, shared: true do
it "returns the length of self" do
"".send(@method).should == 0
"\x00".send(@method).should == 1
"one".send(@method).should == 3
"two".send(@method).should == 3
"three".send(@method).should == 5
"four".send(@method).should == 4
end
with_feature :encoding do
it "returns the length of a string in different encodings" do
utf8_str = 'こにちわ' * 100
utf8_str.size.should == 400
utf8_str.encode(Encoding::UTF_32BE).size.should == 400
utf8_str.encode(Encoding::SHIFT_JIS).size.should == 400
end
it "returns the length of the new self after encoding is changed" do
str = 'こにちわ'
str.send(@method)
str.force_encoding('ASCII-8BIT').send(@method).should == 12
end
end
end

View file

@ -0,0 +1,75 @@
describe :string_replace, shared: true do
it "returns self" do
a = "a"
a.send(@method, "b").should equal(a)
end
it "replaces the content of self with other" do
a = "some string"
a.send(@method, "another string")
a.should == "another string"
end
it "taints self if other is tainted" do
a = ""
b = "".taint
a.send(@method, b)
a.tainted?.should == true
end
it "does not untaint self if other is untainted" do
a = "".taint
b = ""
a.send(@method, b)
a.tainted?.should == true
end
it "untrusts self if other is untrusted" do
a = ""
b = "".untrust
a.send(@method, b)
a.untrusted?.should == true
end
it "does not trust self if other is trusted" do
a = "".untrust
b = ""
a.send(@method, b)
a.untrusted?.should == true
end
it "replaces the encoding of self with that of other" do
a = "".encode("UTF-16LE")
b = "".encode("UTF-8")
a.send(@method, b)
a.encoding.should == Encoding::UTF_8
end
it "carries over the encoding invalidity" do
a = "\u{8765}".force_encoding('ascii')
"".send(@method, a).valid_encoding?.should be_false
end
it "tries to convert other to string using to_str" do
other = mock('x')
other.should_receive(:to_str).and_return("converted to a string")
"hello".send(@method, other).should == "converted to a string"
end
it "raises a TypeError if other can't be converted to string" do
lambda { "hello".send(@method, 123) }.should raise_error(TypeError)
lambda { "hello".send(@method, []) }.should raise_error(TypeError)
lambda { "hello".send(@method, mock('x')) }.should raise_error(TypeError)
end
it "raises a RuntimeError on a frozen instance that is modified" do
a = "hello".freeze
lambda { a.send(@method, "world") }.should raise_error(RuntimeError)
end
# see [ruby-core:23666]
it "raises a RuntimeError on a frozen instance when self-replacing" do
a = "hello".freeze
lambda { a.send(@method, a) }.should raise_error(RuntimeError)
end
end

View file

@ -0,0 +1,557 @@
describe :string_slice, shared: true do
it "returns the character code of the character at the given index" do
"hello".send(@method, 0).should == ?h
"hello".send(@method, -1).should == ?o
end
it "returns nil if index is outside of self" do
"hello".send(@method, 20).should == nil
"hello".send(@method, -20).should == nil
"".send(@method, 0).should == nil
"".send(@method, -1).should == nil
end
it "calls to_int on the given index" do
"hello".send(@method, 0.5).should == ?h
obj = mock('1')
obj.should_receive(:to_int).and_return(1)
"hello".send(@method, obj).should == ?e
end
it "raises a TypeError if the given index is nil" do
lambda { "hello".send(@method, nil) }.should raise_error(TypeError)
end
it "raises a TypeError if the given index can't be converted to an Integer" do
lambda { "hello".send(@method, mock('x')) }.should raise_error(TypeError)
lambda { "hello".send(@method, {}) }.should raise_error(TypeError)
lambda { "hello".send(@method, []) }.should raise_error(TypeError)
end
it "raises a RangeError if the index is too big" do
lambda { "hello".send(@method, bignum_value) }.should raise_error(RangeError)
end
end
describe :string_slice_index_length, shared: true do
it "returns the substring starting at the given index with the given length" do
"hello there".send(@method, 0,0).should == ""
"hello there".send(@method, 0,1).should == "h"
"hello there".send(@method, 0,3).should == "hel"
"hello there".send(@method, 0,6).should == "hello "
"hello there".send(@method, 0,9).should == "hello the"
"hello there".send(@method, 0,12).should == "hello there"
"hello there".send(@method, 1,0).should == ""
"hello there".send(@method, 1,1).should == "e"
"hello there".send(@method, 1,3).should == "ell"
"hello there".send(@method, 1,6).should == "ello t"
"hello there".send(@method, 1,9).should == "ello ther"
"hello there".send(@method, 1,12).should == "ello there"
"hello there".send(@method, 3,0).should == ""
"hello there".send(@method, 3,1).should == "l"
"hello there".send(@method, 3,3).should == "lo "
"hello there".send(@method, 3,6).should == "lo the"
"hello there".send(@method, 3,9).should == "lo there"
"hello there".send(@method, 4,0).should == ""
"hello there".send(@method, 4,3).should == "o t"
"hello there".send(@method, 4,6).should == "o ther"
"hello there".send(@method, 4,9).should == "o there"
"foo".send(@method, 2,1).should == "o"
"foo".send(@method, 3,0).should == ""
"foo".send(@method, 3,1).should == ""
"".send(@method, 0,0).should == ""
"".send(@method, 0,1).should == ""
"x".send(@method, 0,0).should == ""
"x".send(@method, 0,1).should == "x"
"x".send(@method, 1,0).should == ""
"x".send(@method, 1,1).should == ""
"x".send(@method, -1,0).should == ""
"x".send(@method, -1,1).should == "x"
"hello there".send(@method, -3,2).should == "er"
end
it "always taints resulting strings when self is tainted" do
str = "hello world"
str.taint
str.send(@method, 0,0).tainted?.should == true
str.send(@method, 0,1).tainted?.should == true
str.send(@method, 2,1).tainted?.should == true
end
it "returns a string with the same encoding" do
s = "hello there"
s.send(@method, 1, 9).encoding.should == s.encoding
a = "hello".force_encoding("binary")
b = " there".force_encoding("ISO-8859-1")
c = (a + b).force_encoding(Encoding::US_ASCII)
c.send(@method, 0, 5).encoding.should == Encoding::US_ASCII
c.send(@method, 5, 6).encoding.should == Encoding::US_ASCII
c.send(@method, 1, 3).encoding.should == Encoding::US_ASCII
c.send(@method, 8, 2).encoding.should == Encoding::US_ASCII
c.send(@method, 1, 10).encoding.should == Encoding::US_ASCII
end
it "returns nil if the offset falls outside of self" do
"hello there".send(@method, 20,3).should == nil
"hello there".send(@method, -20,3).should == nil
"".send(@method, 1,0).should == nil
"".send(@method, 1,1).should == nil
"".send(@method, -1,0).should == nil
"".send(@method, -1,1).should == nil
"x".send(@method, 2,0).should == nil
"x".send(@method, 2,1).should == nil
"x".send(@method, -2,0).should == nil
"x".send(@method, -2,1).should == nil
end
it "returns nil if the length is negative" do
"hello there".send(@method, 4,-3).should == nil
"hello there".send(@method, -4,-3).should == nil
end
it "calls to_int on the given index and the given length" do
"hello".send(@method, 0.5, 1).should == "h"
"hello".send(@method, 0.5, 2.5).should == "he"
"hello".send(@method, 1, 2.5).should == "el"
obj = mock('2')
obj.should_receive(:to_int).exactly(4).times.and_return(2)
"hello".send(@method, obj, 1).should == "l"
"hello".send(@method, obj, obj).should == "ll"
"hello".send(@method, 0, obj).should == "he"
end
it "raises a TypeError when idx or length can't be converted to an integer" do
lambda { "hello".send(@method, mock('x'), 0) }.should raise_error(TypeError)
lambda { "hello".send(@method, 0, mock('x')) }.should raise_error(TypeError)
# I'm deliberately including this here.
# It means that str.send(@method, other, idx) isn't supported.
lambda { "hello".send(@method, "", 0) }.should raise_error(TypeError)
end
it "raises a TypeError when the given index or the given length is nil" do
lambda { "hello".send(@method, 1, nil) }.should raise_error(TypeError)
lambda { "hello".send(@method, nil, 1) }.should raise_error(TypeError)
lambda { "hello".send(@method, nil, nil) }.should raise_error(TypeError)
end
it "raises a RangeError if the index or length is too big" do
lambda { "hello".send(@method, bignum_value, 1) }.should raise_error(RangeError)
lambda { "hello".send(@method, 0, bignum_value) }.should raise_error(RangeError)
end
it "returns subclass instances" do
s = StringSpecs::MyString.new("hello")
s.send(@method, 0,0).should be_an_instance_of(StringSpecs::MyString)
s.send(@method, 0,4).should be_an_instance_of(StringSpecs::MyString)
s.send(@method, 1,4).should be_an_instance_of(StringSpecs::MyString)
end
it "handles repeated application" do
"hello world".send(@method, 6, 5).send(@method, 0, 1).should == 'w'
"hello world".send(@method, 6, 5).send(@method, 0, 5).should == 'world'
"hello world".send(@method, 6, 5).send(@method, 1, 1).should == 'o'
"hello world".send(@method, 6, 5).send(@method, 1, 4).should == 'orld'
"hello world".send(@method, 6, 5).send(@method, 4, 1).should == 'd'
"hello world".send(@method, 6, 5).send(@method, 5, 0).should == ''
"hello world".send(@method, 6, 0).send(@method, -1, 0).should == nil
"hello world".send(@method, 6, 0).send(@method, 1, 1).should == nil
end
end
describe :string_slice_range, shared: true do
it "returns the substring given by the offsets of the range" do
"hello there".send(@method, 1..1).should == "e"
"hello there".send(@method, 1..3).should == "ell"
"hello there".send(@method, 1...3).should == "el"
"hello there".send(@method, -4..-2).should == "her"
"hello there".send(@method, -4...-2).should == "he"
"hello there".send(@method, 5..-1).should == " there"
"hello there".send(@method, 5...-1).should == " ther"
"".send(@method, 0..0).should == ""
"x".send(@method, 0..0).should == "x"
"x".send(@method, 0..1).should == "x"
"x".send(@method, 0...1).should == "x"
"x".send(@method, 0..-1).should == "x"
"x".send(@method, 1..1).should == ""
"x".send(@method, 1..-1).should == ""
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
"hello there".send(@method, 20..1).should == nil
"hello there".send(@method, -20..1).should == nil
"hello there".send(@method, -20..-1).should == nil
"".send(@method, -1..-1).should == nil
"".send(@method, -1...-1).should == nil
"".send(@method, -1..0).should == nil
"".send(@method, -1...0).should == nil
end
it "returns an empty string if range.begin is inside self and > real end" do
"hello there".send(@method, 1...1).should == ""
"hello there".send(@method, 4..2).should == ""
"hello".send(@method, 4..-4).should == ""
"hello there".send(@method, -5..-6).should == ""
"hello there".send(@method, -2..-4).should == ""
"hello there".send(@method, -5..-6).should == ""
"hello there".send(@method, -5..2).should == ""
"".send(@method, 0...0).should == ""
"".send(@method, 0..-1).should == ""
"".send(@method, 0...-1).should == ""
"x".send(@method, 0...0).should == ""
"x".send(@method, 0...-1).should == ""
"x".send(@method, 1...1).should == ""
"x".send(@method, 1...-1).should == ""
end
it "always taints resulting strings when self is tainted" do
str = "hello world"
str.taint
str.send(@method, 0..0).tainted?.should == true
str.send(@method, 0...0).tainted?.should == true
str.send(@method, 0..1).tainted?.should == true
str.send(@method, 0...1).tainted?.should == true
str.send(@method, 2..3).tainted?.should == true
str.send(@method, 2..0).tainted?.should == true
end
it "returns subclass instances" do
s = StringSpecs::MyString.new("hello")
s.send(@method, 0...0).should be_an_instance_of(StringSpecs::MyString)
s.send(@method, 0..4).should be_an_instance_of(StringSpecs::MyString)
s.send(@method, 1..4).should be_an_instance_of(StringSpecs::MyString)
end
it "calls to_int on range arguments" do
from = mock('from')
to = mock('to')
# So we can construct a range out of them...
from.should_receive(:<=>).twice.and_return(0)
from.should_receive(:to_int).twice.and_return(1)
to.should_receive(:to_int).twice.and_return(-2)
"hello there".send(@method, from..to).should == "ello ther"
"hello there".send(@method, from...to).should == "ello the"
end
it "works with Range subclasses" do
a = "GOOD"
range_incl = StringSpecs::MyRange.new(1, 2)
range_excl = StringSpecs::MyRange.new(-3, -1, true)
a.send(@method, range_incl).should == "OO"
a.send(@method, range_excl).should == "OO"
end
it "handles repeated application" do
"hello world".send(@method, 6..11).send(@method, 0..0).should == 'w'
"hello world".send(@method, 6..11).send(@method, 0..4).should == 'world'
"hello world".send(@method, 6..11).send(@method, 1..1).should == 'o'
"hello world".send(@method, 6..11).send(@method, 1..4).should == 'orld'
"hello world".send(@method, 6..11).send(@method, 4..4).should == 'd'
"hello world".send(@method, 6..11).send(@method, 5..4).should == ''
"hello world".send(@method, 6..5).send(@method, -1..-1).should == nil
"hello world".send(@method, 6..5).send(@method, 1..1).should == nil
end
end
describe :string_slice_regexp, shared: true do
it "returns the matching portion of self" do
"hello there".send(@method, /[aeiou](.)\1/).should == "ell"
"".send(@method, //).should == ""
end
it "returns nil if there is no match" do
"hello there".send(@method, /xyz/).should == nil
end
not_supported_on :opal do
it "always taints resulting strings when self or regexp is tainted" do
strs = ["hello world"]
strs += strs.map { |s| s.dup.taint }
strs.each do |str|
str.send(@method, //).tainted?.should == str.tainted?
str.send(@method, /hello/).tainted?.should == str.tainted?
tainted_re = /./
tainted_re.taint
str.send(@method, tainted_re).tainted?.should == true
end
end
it "returns an untrusted string if the regexp is untrusted" do
"hello".send(@method, /./.untrust).untrusted?.should be_true
end
end
it "returns subclass instances" do
s = StringSpecs::MyString.new("hello")
s.send(@method, //).should be_an_instance_of(StringSpecs::MyString)
s.send(@method, /../).should be_an_instance_of(StringSpecs::MyString)
end
it "sets $~ to MatchData when there is a match and nil when there's none" do
'hello'.send(@method, /./)
$~[0].should == 'h'
'hello'.send(@method, /not/)
$~.should == nil
end
end
describe :string_slice_regexp_index, shared: true do
it "returns the capture for the given index" do
"hello there".send(@method, /[aeiou](.)\1/, 0).should == "ell"
"hello there".send(@method, /[aeiou](.)\1/, 1).should == "l"
"hello there".send(@method, /[aeiou](.)\1/, -1).should == "l"
"har".send(@method, /(.)(.)(.)/, 0).should == "har"
"har".send(@method, /(.)(.)(.)/, 1).should == "h"
"har".send(@method, /(.)(.)(.)/, 2).should == "a"
"har".send(@method, /(.)(.)(.)/, 3).should == "r"
"har".send(@method, /(.)(.)(.)/, -1).should == "r"
"har".send(@method, /(.)(.)(.)/, -2).should == "a"
"har".send(@method, /(.)(.)(.)/, -3).should == "h"
end
it "always taints resulting strings when self or regexp is tainted" do
strs = ["hello world"]
strs += strs.map { |s| s.dup.taint }
strs.each do |str|
str.send(@method, //, 0).tainted?.should == str.tainted?
str.send(@method, /hello/, 0).tainted?.should == str.tainted?
str.send(@method, /(.)(.)(.)/, 0).tainted?.should == str.tainted?
str.send(@method, /(.)(.)(.)/, 1).tainted?.should == str.tainted?
str.send(@method, /(.)(.)(.)/, -1).tainted?.should == str.tainted?
str.send(@method, /(.)(.)(.)/, -2).tainted?.should == str.tainted?
tainted_re = /(.)(.)(.)/
tainted_re.taint
str.send(@method, tainted_re, 0).tainted?.should == true
str.send(@method, tainted_re, 1).tainted?.should == true
str.send(@method, tainted_re, -1).tainted?.should == true
end
end
not_supported_on :opal do
it "returns an untrusted string if the regexp is untrusted" do
"hello".send(@method, /(.)/.untrust, 1).untrusted?.should be_true
end
end
it "returns nil if there is no match" do
"hello there".send(@method, /(what?)/, 1).should == nil
end
it "returns nil if there is no capture for the given index" do
"hello there".send(@method, /[aeiou](.)\1/, 2).should == nil
# You can't refer to 0 using negative indices
"hello there".send(@method, /[aeiou](.)\1/, -2).should == nil
end
it "calls to_int on the given index" do
obj = mock('2')
obj.should_receive(:to_int).and_return(2)
"har".send(@method, /(.)(.)(.)/, 1.5).should == "h"
"har".send(@method, /(.)(.)(.)/, obj).should == "a"
end
it "raises a TypeError when the given index can't be converted to Integer" do
lambda { "hello".send(@method, /(.)(.)(.)/, mock('x')) }.should raise_error(TypeError)
lambda { "hello".send(@method, /(.)(.)(.)/, {}) }.should raise_error(TypeError)
lambda { "hello".send(@method, /(.)(.)(.)/, []) }.should raise_error(TypeError)
end
it "raises a TypeError when the given index is nil" do
lambda { "hello".send(@method, /(.)(.)(.)/, nil) }.should raise_error(TypeError)
end
it "returns subclass instances" do
s = StringSpecs::MyString.new("hello")
s.send(@method, /(.)(.)/, 0).should be_an_instance_of(StringSpecs::MyString)
s.send(@method, /(.)(.)/, 1).should be_an_instance_of(StringSpecs::MyString)
end
it "sets $~ to MatchData when there is a match and nil when there's none" do
'hello'.send(@method, /.(.)/, 0)
$~[0].should == 'he'
'hello'.send(@method, /.(.)/, 1)
$~[1].should == 'e'
'hello'.send(@method, /not/, 0)
$~.should == nil
end
end
describe :string_slice_string, shared: true do
it "returns other_str if it occurs in self" do
s = "lo"
"hello there".send(@method, s).should == s
end
it "taints resulting strings when other is tainted" do
strs = ["", "hello world", "hello"]
strs += strs.map { |s| s.dup.taint }
strs.each do |str|
strs.each do |other|
r = str.send(@method, other)
r.tainted?.should == !r.nil? & other.tainted?
end
end
end
it "doesn't set $~" do
$~ = nil
'hello'.send(@method, 'll')
$~.should == nil
end
it "returns nil if there is no match" do
"hello there".send(@method, "bye").should == nil
end
it "doesn't call to_str on its argument" do
o = mock('x')
o.should_not_receive(:to_str)
lambda { "hello".send(@method, o) }.should raise_error(TypeError)
end
it "returns a subclass instance when given a subclass instance" do
s = StringSpecs::MyString.new("el")
r = "hello".send(@method, s)
r.should == "el"
r.should be_an_instance_of(StringSpecs::MyString)
end
end
describe :string_slice_regexp_group, shared: true do
not_supported_on :opal do
it "returns the capture for the given name" do
"hello there".send(@method, /(?<g>[aeiou](.))/, 'g').should == "el"
"hello there".send(@method, /[aeiou](?<g>.)/, 'g').should == "l"
"har".send(@method, /(?<g>(.)(.)(.))/, 'g').should == "har"
"har".send(@method, /(?<h>.)(.)(.)/, 'h').should == "h"
"har".send(@method, /(.)(?<a>.)(.)/, 'a').should == "a"
"har".send(@method, /(.)(.)(?<r>.)/, 'r').should == "r"
"har".send(@method, /(?<h>.)(?<a>.)(?<r>.)/, 'r').should == "r"
end
it "returns the last capture for duplicate names" do
"hello there".send(@method, /(?<g>h)(?<g>.)/, 'g').should == "e"
"hello there".send(@method, /(?<g>h)(?<g>.)(?<f>.)/, 'g').should == "e"
end
it "returns the innermost capture for nested duplicate names" do
"hello there".send(@method, /(?<g>h(?<g>.))/, 'g').should == "e"
end
it "always taints resulting strings when self or regexp is tainted" do
strs = ["hello world"]
strs += strs.map { |s| s.dup.taint }
strs.each do |str|
str.send(@method, /(?<hi>hello)/, 'hi').tainted?.should == str.tainted?
str.send(@method, /(?<g>(.)(.)(.))/, 'g').tainted?.should == str.tainted?
str.send(@method, /(?<h>.)(.)(.)/, 'h').tainted?.should == str.tainted?
str.send(@method, /(.)(?<a>.)(.)/, 'a').tainted?.should == str.tainted?
str.send(@method, /(.)(.)(?<r>.)/, 'r').tainted?.should == str.tainted?
str.send(@method, /(?<h>.)(?<a>.)(?<r>.)/, 'r').tainted?.should == str.tainted?
tainted_re = /(?<a>.)(?<b>.)(?<c>.)/
tainted_re.taint
str.send(@method, tainted_re, 'a').tainted?.should be_true
str.send(@method, tainted_re, 'b').tainted?.should be_true
str.send(@method, tainted_re, 'c').tainted?.should be_true
end
end
it "returns nil if there is no match" do
"hello there".send(@method, /(?<whut>what?)/, 'whut').should be_nil
end
it "raises an IndexError if there is no capture for the given name" do
lambda do
"hello there".send(@method, /[aeiou](.)\1/, 'non')
end.should raise_error(IndexError)
end
it "raises a TypeError when the given name is not a String" do
lambda { "hello".send(@method, /(?<q>.)/, mock('x')) }.should raise_error(TypeError)
lambda { "hello".send(@method, /(?<q>.)/, {}) }.should raise_error(TypeError)
lambda { "hello".send(@method, /(?<q>.)/, []) }.should raise_error(TypeError)
end
it "raises an IndexError when given the empty String as a group name" do
lambda { "hello".send(@method, /(?<q>)/, '') }.should raise_error(IndexError)
end
it "returns subclass instances" do
s = StringSpecs::MyString.new("hello")
s.send(@method, /(?<q>.)/, 'q').should be_an_instance_of(StringSpecs::MyString)
end
it "sets $~ to MatchData when there is a match and nil when there's none" do
'hello'.send(@method, /(?<hi>.(.))/, 'hi')
$~[0].should == 'he'
'hello'.send(@method, /(?<non>not)/, 'non')
$~.should be_nil
end
end
end
describe :string_slice_symbol, shared: true do
it "raises TypeError" do
lambda { 'hello'.send(@method, :hello) }.should raise_error(TypeError)
end
end

View file

@ -0,0 +1,88 @@
# -*- encoding: binary -*-
describe :string_succ, shared: true do
it "returns an empty string for empty strings" do
"".send(@method).should == ""
end
it "returns the successor by increasing the rightmost alphanumeric (digit => digit, letter => letter with same case)" do
"abcd".send(@method).should == "abce"
"THX1138".send(@method).should == "THX1139"
"<<koala>>".send(@method).should == "<<koalb>>"
"==A??".send(@method).should == "==B??"
end
it "increases non-alphanumerics (via ascii rules) if there are no alphanumerics" do
"***".send(@method).should == "**+"
"**`".send(@method).should == "**a"
end
it "increases the next best alphanumeric (jumping over non-alphanumerics) if there is a carry" do
"dz".send(@method).should == "ea"
"HZ".send(@method).should == "IA"
"49".send(@method).should == "50"
"izz".send(@method).should == "jaa"
"IZZ".send(@method).should == "JAA"
"699".send(@method).should == "700"
"6Z99z99Z".send(@method).should == "7A00a00A"
"1999zzz".send(@method).should == "2000aaa"
"NZ/[]ZZZ9999".send(@method).should == "OA/[]AAA0000"
end
it "increases the next best character if there is a carry for non-alphanumerics" do
"(\xFF".send(@method).should == ")\x00"
"`\xFF".send(@method).should == "a\x00"
"<\xFF\xFF".send(@method).should == "=\x00\x00"
end
it "adds an additional character (just left to the last increased one) if there is a carry and no character left to increase" do
"z".send(@method).should == "aa"
"Z".send(@method).should == "AA"
"9".send(@method).should == "10"
"zz".send(@method).should == "aaa"
"ZZ".send(@method).should == "AAA"
"99".send(@method).should == "100"
"9Z99z99Z".send(@method).should == "10A00a00A"
"ZZZ9999".send(@method).should == "AAAA0000"
"/[]9999".send(@method).should == "/[]10000"
"/[]ZZZ9999".send(@method).should == "/[]AAAA0000"
"Z/[]ZZZ9999".send(@method).should == "AA/[]AAA0000"
# non-alphanumeric cases
"\xFF".send(@method).should == "\x01\x00"
"\xFF\xFF".send(@method).should == "\x01\x00\x00"
end
it "returns subclass instances when called on a subclass" do
StringSpecs::MyString.new("").send(@method).should be_an_instance_of(StringSpecs::MyString)
StringSpecs::MyString.new("a").send(@method).should be_an_instance_of(StringSpecs::MyString)
StringSpecs::MyString.new("z").send(@method).should be_an_instance_of(StringSpecs::MyString)
end
it "taints the result if self is tainted" do
["", "a", "z", "Z", "9", "\xFF", "\xFF\xFF"].each do |s|
s.taint.send(@method).tainted?.should == true
end
end
end
describe :string_succ_bang, shared: true do
it "is equivalent to succ, but modifies self in place (still returns self)" do
["", "abcd", "THX1138"].each do |s|
r = s.dup.send(@method)
s.send(@method).should equal(s)
s.should == r
end
end
it "raises a RuntimeError if self is frozen" do
lambda { "".freeze.send(@method) }.should raise_error(RuntimeError)
lambda { "abcd".freeze.send(@method) }.should raise_error(RuntimeError)
end
end

View file

@ -0,0 +1,9 @@
describe :string_to_a, shared: true do
it "returns an empty array for empty strings" do
"".send(@method).should == []
end
it "returns an array containing the string for non-empty strings" do
"hello".send(@method).should == ["hello"]
end
end

View file

@ -0,0 +1,18 @@
describe :string_to_s, shared: true do
it "returns self when self.class == String" do
a = "a string"
a.should equal(a.send(@method))
end
it "returns a new instance of String when called on a subclass" do
a = StringSpecs::MyString.new("a string")
s = a.send(@method)
s.should == "a string"
s.should be_an_instance_of(String)
end
it "taints the result when self is tainted" do
"x".taint.send(@method).tainted?.should == true
StringSpecs::MyString.new("x").taint.send(@method).tainted?.should == true
end
end

View file

@ -0,0 +1,24 @@
describe :string_to_sym, shared: true do
it "returns the symbol corresponding to self" do
"Koala".send(@method).should == :Koala
'cat'.send(@method).should == :cat
'@cat'.send(@method).should == :@cat
'cat and dog'.send(@method).should == :"cat and dog"
"abc=".send(@method).should == :abc=
end
it "does not special case +(binary) and -(binary)" do
"+(binary)".send(@method).should == :"+(binary)"
"-(binary)".send(@method).should == :"-(binary)"
end
it "does not special case certain operators" do
[ ["!@", :"!@"],
["~@", :"~@"],
["!(unary)", :"!(unary)"],
["~(unary)", :"~(unary)"],
["+(unary)", :"+(unary)"],
["-(unary)", :"-(unary)"]
].should be_computed_by(@method)
end
end

View file

@ -0,0 +1,7 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
require File.expand_path('../shared/length', __FILE__)
describe "String#size" do
it_behaves_like(:string_length, :size)
end

View file

@ -0,0 +1,476 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
require File.expand_path('../shared/slice.rb', __FILE__)
describe "String#slice" do
it_behaves_like :string_slice, :slice
end
describe "String#slice with index, length" do
it_behaves_like :string_slice_index_length, :slice
end
describe "String#slice with Range" do
it_behaves_like :string_slice_range, :slice
end
describe "String#slice with Regexp" do
it_behaves_like :string_slice_regexp, :slice
end
describe "String#slice with Regexp, index" do
it_behaves_like :string_slice_regexp_index, :slice
end
describe "String#slice with Regexp, group" do
it_behaves_like :string_slice_regexp_group, :slice
end
describe "String#slice with String" do
it_behaves_like :string_slice_string, :slice
end
describe "String#slice with Symbol" do
it_behaves_like :string_slice_symbol, :slice
end
describe "String#slice! with index" do
it "deletes and return the char at the given position" do
a = "hello"
a.slice!(1).should == ?e
a.should == "hllo"
a.slice!(-1).should == ?o
a.should == "hll"
end
it "returns nil if idx is outside of self" do
a = "hello"
a.slice!(20).should == nil
a.should == "hello"
a.slice!(-20).should == nil
a.should == "hello"
end
it "raises a RuntimeError if self is frozen" do
lambda { "hello".freeze.slice!(1) }.should raise_error(RuntimeError)
lambda { "hello".freeze.slice!(10) }.should raise_error(RuntimeError)
lambda { "".freeze.slice!(0) }.should raise_error(RuntimeError)
end
it "calls to_int on index" do
"hello".slice!(0.5).should == ?h
obj = mock('1')
obj.should_receive(:to_int).at_least(1).and_return(1)
"hello".slice!(obj).should == ?e
obj = mock('1')
obj.should_receive(:respond_to?).at_least(1).with(:to_int, true).and_return(true)
obj.should_receive(:method_missing).at_least(1).with(:to_int).and_return(1)
"hello".slice!(obj).should == ?e
end
with_feature :encoding do
it "returns the character given by the character index" do
"hellö there".send(@method, 1).should == "e"
"hellö there".send(@method, 4).should == "ö"
"hellö there".send(@method, 6).should == "t"
end
end
end
describe "String#slice! with index, length" do
it "deletes and returns the substring at idx and the given length" do
a = "hello"
a.slice!(1, 2).should == "el"
a.should == "hlo"
a.slice!(1, 0).should == ""
a.should == "hlo"
a.slice!(-2, 4).should == "lo"
a.should == "h"
end
it "always taints resulting strings when self is tainted" do
str = "hello world"
str.taint
str.slice!(0, 0).tainted?.should == true
str.slice!(2, 1).tainted?.should == true
end
it "returns nil if the given position is out of self" do
a = "hello"
a.slice(10, 3).should == nil
a.should == "hello"
a.slice(-10, 20).should == nil
a.should == "hello"
end
it "returns nil if the length is negative" do
a = "hello"
a.slice(4, -3).should == nil
a.should == "hello"
end
it "raises a RuntimeError if self is frozen" do
lambda { "hello".freeze.slice!(1, 2) }.should raise_error(RuntimeError)
lambda { "hello".freeze.slice!(10, 3) }.should raise_error(RuntimeError)
lambda { "hello".freeze.slice!(-10, 3)}.should raise_error(RuntimeError)
lambda { "hello".freeze.slice!(4, -3) }.should raise_error(RuntimeError)
lambda { "hello".freeze.slice!(10, 3) }.should raise_error(RuntimeError)
lambda { "hello".freeze.slice!(-10, 3)}.should raise_error(RuntimeError)
lambda { "hello".freeze.slice!(4, -3) }.should raise_error(RuntimeError)
end
it "calls to_int on idx and length" do
"hello".slice!(0.5, 2.5).should == "he"
obj = mock('2')
def obj.to_int() 2 end
"hello".slice!(obj, obj).should == "ll"
obj = mock('2')
def obj.respond_to?(name, *) name == :to_int; end
def obj.method_missing(name, *) name == :to_int ? 2 : super; end
"hello".slice!(obj, obj).should == "ll"
end
it "returns subclass instances" do
s = StringSpecs::MyString.new("hello")
s.slice!(0, 0).should be_an_instance_of(StringSpecs::MyString)
s.slice!(0, 4).should be_an_instance_of(StringSpecs::MyString)
end
with_feature :encoding do
it "returns the substring given by the character offsets" do
"hellö there".send(@method, 1,0).should == ""
"hellö there".send(@method, 1,3).should == "ell"
"hellö there".send(@method, 1,6).should == "ellö t"
"hellö there".send(@method, 1,9).should == "ellö ther"
end
it "treats invalid bytes as single bytes" do
xE6xCB = [0xE6,0xCB].pack('CC').force_encoding('utf-8')
"a#{xE6xCB}b".send(@method, 1, 2).should == xE6xCB
end
end
end
describe "String#slice! Range" do
it "deletes and return the substring given by the offsets of the range" do
a = "hello"
a.slice!(1..3).should == "ell"
a.should == "ho"
a.slice!(0..0).should == "h"
a.should == "o"
a.slice!(0...0).should == ""
a.should == "o"
# Edge Case?
"hello".slice!(-3..-9).should == ""
end
it "returns nil if the given range is out of self" do
a = "hello"
a.slice!(-6..-9).should == nil
a.should == "hello"
b = "hello"
b.slice!(10..20).should == nil
b.should == "hello"
end
it "always taints resulting strings when self is tainted" do
str = "hello world"
str.taint
str.slice!(0..0).tainted?.should == true
str.slice!(2..3).tainted?.should == true
end
it "returns subclass instances" do
s = StringSpecs::MyString.new("hello")
s.slice!(0...0).should be_an_instance_of(StringSpecs::MyString)
s.slice!(0..4).should be_an_instance_of(StringSpecs::MyString)
end
it "calls to_int on range arguments" do
from = mock('from')
to = mock('to')
# So we can construct a range out of them...
def from.<=>(o) 0 end
def to.<=>(o) 0 end
def from.to_int() 1 end
def to.to_int() -2 end
"hello there".slice!(from..to).should == "ello ther"
from = mock('from')
to = mock('to')
def from.<=>(o) 0 end
def to.<=>(o) 0 end
def from.respond_to?(name, *) name == :to_int; end
def from.method_missing(name) name == :to_int ? 1 : super; end
def to.respond_to?(name, *) name == :to_int; end
def to.method_missing(name) name == :to_int ? -2 : super; end
"hello there".slice!(from..to).should == "ello ther"
end
it "works with Range subclasses" do
a = "GOOD"
range_incl = StringSpecs::MyRange.new(1, 2)
a.slice!(range_incl).should == "OO"
end
with_feature :encoding do
it "returns the substring given by the character offsets of the range" do
"hellö there".send(@method, 1..1).should == "e"
"hellö there".send(@method, 1..3).should == "ell"
"hellö there".send(@method, 1...3).should == "el"
"hellö there".send(@method, -4..-2).should == "her"
"hellö there".send(@method, -4...-2).should == "he"
"hellö there".send(@method, 5..-1).should == " there"
"hellö there".send(@method, 5...-1).should == " ther"
end
end
it "raises a RuntimeError on a frozen instance that is modified" do
lambda { "hello".freeze.slice!(1..3) }.should raise_error(RuntimeError)
end
# see redmine #1551
it "raises a RuntimeError on a frozen instance that would not be modified" do
lambda { "hello".freeze.slice!(10..20)}.should raise_error(RuntimeError)
end
end
describe "String#slice! with Regexp" do
it "deletes and returns the first match from self" do
s = "this is a string"
s.slice!(/s.*t/).should == 's is a st'
s.should == 'thiring'
c = "hello hello"
c.slice!(/llo/).should == "llo"
c.should == "he hello"
end
it "returns nil if there was no match" do
s = "this is a string"
s.slice!(/zzz/).should == nil
s.should == "this is a string"
end
it "always taints resulting strings when self or regexp is tainted" do
strs = ["hello world"]
strs += strs.map { |s| s.dup.taint }
strs.each do |str|
str = str.dup
str.slice!(//).tainted?.should == str.tainted?
str.slice!(/hello/).tainted?.should == str.tainted?
tainted_re = /./
tainted_re.taint
str.slice!(tainted_re).tainted?.should == true
end
end
it "doesn't taint self when regexp is tainted" do
s = "hello"
s.slice!(/./.taint)
s.tainted?.should == false
end
it "returns subclass instances" do
s = StringSpecs::MyString.new("hello")
s.slice!(//).should be_an_instance_of(StringSpecs::MyString)
s.slice!(/../).should be_an_instance_of(StringSpecs::MyString)
end
with_feature :encoding do
it "returns the matching portion of self with a multi byte character" do
"hëllo there".send(@method, /[ë](.)\1/).should == "ëll"
"".send(@method, //).should == ""
end
end
it "sets $~ to MatchData when there is a match and nil when there's none" do
'hello'.slice!(/./)
$~[0].should == 'h'
'hello'.slice!(/not/)
$~.should == nil
end
it "raises a RuntimeError on a frozen instance that is modified" do
lambda { "this is a string".freeze.slice!(/s.*t/) }.should raise_error(RuntimeError)
end
it "raises a RuntimeError on a frozen instance that would not be modified" do
lambda { "this is a string".freeze.slice!(/zzz/) }.should raise_error(RuntimeError)
end
end
describe "String#slice! with Regexp, index" do
it "deletes and returns the capture for idx from self" do
str = "hello there"
str.slice!(/[aeiou](.)\1/, 0).should == "ell"
str.should == "ho there"
str.slice!(/(t)h/, 1).should == "t"
str.should == "ho here"
end
it "always taints resulting strings when self or regexp is tainted" do
strs = ["hello world"]
strs += strs.map { |s| s.dup.taint }
strs.each do |str|
str = str.dup
str.slice!(//, 0).tainted?.should == str.tainted?
str.slice!(/hello/, 0).tainted?.should == str.tainted?
tainted_re = /(.)(.)(.)/
tainted_re.taint
str.slice!(tainted_re, 1).tainted?.should == true
end
end
it "doesn't taint self when regexp is tainted" do
s = "hello"
s.slice!(/(.)(.)/.taint, 1)
s.tainted?.should == false
end
it "returns nil if there was no match" do
s = "this is a string"
s.slice!(/x(zzz)/, 1).should == nil
s.should == "this is a string"
end
it "returns nil if there is no capture for idx" do
"hello there".slice!(/[aeiou](.)\1/, 2).should == nil
# You can't refer to 0 using negative indices
"hello there".slice!(/[aeiou](.)\1/, -2).should == nil
end
it "accepts a Float for capture index" do
"har".slice!(/(.)(.)(.)/, 1.5).should == "h"
end
it "calls #to_int to convert an Object to capture index" do
obj = mock('2')
obj.should_receive(:to_int).at_least(1).times.and_return(2)
"har".slice!(/(.)(.)(.)/, obj).should == "a"
end
it "returns subclass instances" do
s = StringSpecs::MyString.new("hello")
s.slice!(/(.)(.)/, 0).should be_an_instance_of(StringSpecs::MyString)
s.slice!(/(.)(.)/, 1).should be_an_instance_of(StringSpecs::MyString)
end
with_feature :encoding do
it "returns the encoding aware capture for the given index" do
"hår".send(@method, /(.)(.)(.)/, 0).should == "hår"
"hår".send(@method, /(.)(.)(.)/, 1).should == "h"
"hår".send(@method, /(.)(.)(.)/, 2).should == "å"
"hår".send(@method, /(.)(.)(.)/, 3).should == "r"
"hår".send(@method, /(.)(.)(.)/, -1).should == "r"
"hår".send(@method, /(.)(.)(.)/, -2).should == "å"
"hår".send(@method, /(.)(.)(.)/, -3).should == "h"
end
end
it "sets $~ to MatchData when there is a match and nil when there's none" do
'hello'[/.(.)/, 0]
$~[0].should == 'he'
'hello'[/.(.)/, 1]
$~[1].should == 'e'
'hello'[/not/, 0]
$~.should == nil
end
it "raises a RuntimeError if self is frozen" do
lambda { "this is a string".freeze.slice!(/s.*t/) }.should raise_error(RuntimeError)
lambda { "this is a string".freeze.slice!(/zzz/, 0)}.should raise_error(RuntimeError)
lambda { "this is a string".freeze.slice!(/(.)/, 2)}.should raise_error(RuntimeError)
end
end
describe "String#slice! with String" do
it "removes and returns the first occurrence of other_str from self" do
c = "hello hello"
c.slice!('llo').should == "llo"
c.should == "he hello"
end
it "taints resulting strings when other is tainted" do
strs = ["", "hello world", "hello"]
strs += strs.map { |s| s.dup.taint }
strs.each do |str|
str = str.dup
strs.each do |other|
other = other.dup
r = str.slice!(other)
r.tainted?.should == !r.nil? & other.tainted?
end
end
end
it "doesn't set $~" do
$~ = nil
'hello'.slice!('ll')
$~.should == nil
end
it "returns nil if self does not contain other" do
a = "hello"
a.slice!('zzz').should == nil
a.should == "hello"
end
it "doesn't call to_str on its argument" do
o = mock('x')
o.should_not_receive(:to_str)
lambda { "hello".slice!(o) }.should raise_error(TypeError)
end
it "returns a subclass instance when given a subclass instance" do
s = StringSpecs::MyString.new("el")
r = "hello".slice!(s)
r.should == "el"
r.should be_an_instance_of(StringSpecs::MyString)
end
it "raises a RuntimeError if self is frozen" do
lambda { "hello hello".freeze.slice!('llo') }.should raise_error(RuntimeError)
lambda { "this is a string".freeze.slice!('zzz')}.should raise_error(RuntimeError)
lambda { "this is a string".freeze.slice!('zzz')}.should raise_error(RuntimeError)
end
end

View file

@ -0,0 +1,405 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#split with String" do
with_feature :encoding do
it "throws an ArgumentError if the pattern is not a valid string" do
str = 'проверка'
broken_str = 'проверка'
broken_str.force_encoding('binary')
broken_str.chop!
broken_str.force_encoding('utf-8')
lambda { str.split(broken_str) }.should raise_error(ArgumentError)
end
it "splits on multibyte characters" do
"ありがりがとう".split("").should == ["あり", "", "とう"]
end
end
it "returns an array of substrings based on splitting on the given string" do
"mellow yellow".split("ello").should == ["m", "w y", "w"]
end
it "suppresses trailing empty fields when limit isn't given or 0" do
"1,2,,3,4,,".split(',').should == ["1", "2", "", "3", "4"]
"1,2,,3,4,,".split(',', 0).should == ["1", "2", "", "3", "4"]
" a b c\nd ".split(" ").should == ["", "a", "b", "c\nd"]
"hai".split("hai").should == []
",".split(",").should == []
",".split(",", 0).should == []
end
it "returns an array with one entry if limit is 1: the original string" do
"hai".split("hai", 1).should == ["hai"]
"x.y.z".split(".", 1).should == ["x.y.z"]
"hello world ".split(" ", 1).should == ["hello world "]
"hi!".split("", 1).should == ["hi!"]
end
it "returns at most limit fields when limit > 1" do
"hai".split("hai", 2).should == ["", ""]
"1,2".split(",", 3).should == ["1", "2"]
"1,2,,3,4,,".split(',', 2).should == ["1", "2,,3,4,,"]
"1,2,,3,4,,".split(',', 3).should == ["1", "2", ",3,4,,"]
"1,2,,3,4,,".split(',', 4).should == ["1", "2", "", "3,4,,"]
"1,2,,3,4,,".split(',', 5).should == ["1", "2", "", "3", "4,,"]
"1,2,,3,4,,".split(',', 6).should == ["1", "2", "", "3", "4", ","]
"x".split('x', 2).should == ["", ""]
"xx".split('x', 2).should == ["", "x"]
"xx".split('x', 3).should == ["", "", ""]
"xxx".split('x', 2).should == ["", "xx"]
"xxx".split('x', 3).should == ["", "", "x"]
"xxx".split('x', 4).should == ["", "", "", ""]
end
it "doesn't suppress or limit fields when limit is negative" do
"1,2,,3,4,,".split(',', -1).should == ["1", "2", "", "3", "4", "", ""]
"1,2,,3,4,,".split(',', -5).should == ["1", "2", "", "3", "4", "", ""]
" a b c\nd ".split(" ", -1).should == ["", "a", "b", "c\nd", ""]
",".split(",", -1).should == ["", ""]
end
it "defaults to $; when string isn't given or nil" do
begin
old_fs = $;
[",", ":", "", "XY", nil].each do |fs|
$; = fs
["x,y,z,,,", "1:2:", "aXYbXYcXY", ""].each do |str|
expected = str.split(fs || " ")
str.split(nil).should == expected
str.split.should == expected
str.split(nil, -1).should == str.split(fs || " ", -1)
str.split(nil, 0).should == str.split(fs || " ", 0)
str.split(nil, 2).should == str.split(fs || " ", 2)
end
end
ensure
$; = old_fs
end
end
it "ignores leading and continuous whitespace when string is a single space" do
" now's the time ".split(' ').should == ["now's", "the", "time"]
" now's the time ".split(' ', -1).should == ["now's", "the", "time", ""]
" now's the time ".split(' ', 3).should == ["now's", "the", "time "]
"\t\n a\t\tb \n\r\r\nc\v\vd\v ".split(' ').should == ["a", "b", "c", "d"]
"a\x00a b".split(' ').should == ["a\x00a", "b"]
end
describe "when limit is zero" do
it "ignores leading and continuous whitespace when string is a single space" do
" now's the time ".split(' ', 0).should == ["now's", "the", "time"]
end
end
it "splits between characters when its argument is an empty string" do
"hi!".split("").should == ["h", "i", "!"]
"hi!".split("", -1).should == ["h", "i", "!", ""]
"hi!".split("", 0).should == ["h", "i", "!"]
"hi!".split("", 1).should == ["hi!"]
"hi!".split("", 2).should == ["h", "i!"]
"hi!".split("", 3).should == ["h", "i", "!"]
"hi!".split("", 4).should == ["h", "i", "!", ""]
"hi!".split("", 5).should == ["h", "i", "!", ""]
end
it "tries converting its pattern argument to a string via to_str" do
obj = mock('::')
obj.should_receive(:to_str).and_return("::")
"hello::world".split(obj).should == ["hello", "world"]
end
it "tries converting limit to an integer via to_int" do
obj = mock('2')
obj.should_receive(:to_int).and_return(2)
"1.2.3.4".split(".", obj).should == ["1", "2.3.4"]
end
it "doesn't set $~" do
$~ = nil
"x.y.z".split(".")
$~.should == nil
end
it "returns the original string if no matches are found" do
"foo".split("bar").should == ["foo"]
"foo".split("bar", -1).should == ["foo"]
"foo".split("bar", 0).should == ["foo"]
"foo".split("bar", 1).should == ["foo"]
"foo".split("bar", 2).should == ["foo"]
"foo".split("bar", 3).should == ["foo"]
end
it "returns subclass instances based on self" do
["", "x.y.z.", " x y "].each do |str|
["", ".", " "].each do |pat|
[-1, 0, 1, 2].each do |limit|
StringSpecs::MyString.new(str).split(pat, limit).each do |x|
x.should be_an_instance_of(StringSpecs::MyString)
end
str.split(StringSpecs::MyString.new(pat), limit).each do |x|
x.should be_an_instance_of(String)
end
end
end
end
end
it "does not call constructor on created subclass instances" do
# can't call should_not_receive on an object that doesn't yet exist
# so failure here is signalled by exception, not expectation failure
s = StringSpecs::StringWithRaisingConstructor.new('silly:string')
s.split(':').first.should == 'silly'
end
it "taints the resulting strings if self is tainted" do
["", "x.y.z.", " x y "].each do |str|
["", ".", " "].each do |pat|
[-1, 0, 1, 2].each do |limit|
str.dup.taint.split(pat).each do |x|
x.tainted?.should == true
end
str.split(pat.dup.taint).each do |x|
x.tainted?.should == false
end
end
end
end
end
end
describe "String#split with Regexp" do
it "divides self on regexp matches" do
" now's the time".split(/ /).should == ["", "now's", "", "the", "time"]
" x\ny ".split(/ /).should == ["", "x\ny"]
"1, 2.34,56, 7".split(/,\s*/).should == ["1", "2.34", "56", "7"]
"1x2X3".split(/x/i).should == ["1", "2", "3"]
end
it "treats negative limits as no limit" do
"".split(%r!/+!, -1).should == []
end
it "suppresses trailing empty fields when limit isn't given or 0" do
"1,2,,3,4,,".split(/,/).should == ["1", "2", "", "3", "4"]
"1,2,,3,4,,".split(/,/, 0).should == ["1", "2", "", "3", "4"]
" a b c\nd ".split(/\s+/).should == ["", "a", "b", "c", "d"]
"hai".split(/hai/).should == []
",".split(/,/).should == []
",".split(/,/, 0).should == []
end
it "returns an array with one entry if limit is 1: the original string" do
"hai".split(/hai/, 1).should == ["hai"]
"xAyBzC".split(/[A-Z]/, 1).should == ["xAyBzC"]
"hello world ".split(/\s+/, 1).should == ["hello world "]
"hi!".split(//, 1).should == ["hi!"]
end
it "returns at most limit fields when limit > 1" do
"hai".split(/hai/, 2).should == ["", ""]
"1,2".split(/,/, 3).should == ["1", "2"]
"1,2,,3,4,,".split(/,/, 2).should == ["1", "2,,3,4,,"]
"1,2,,3,4,,".split(/,/, 3).should == ["1", "2", ",3,4,,"]
"1,2,,3,4,,".split(/,/, 4).should == ["1", "2", "", "3,4,,"]
"1,2,,3,4,,".split(/,/, 5).should == ["1", "2", "", "3", "4,,"]
"1,2,,3,4,,".split(/,/, 6).should == ["1", "2", "", "3", "4", ","]
"x".split(/x/, 2).should == ["", ""]
"xx".split(/x/, 2).should == ["", "x"]
"xx".split(/x/, 3).should == ["", "", ""]
"xxx".split(/x/, 2).should == ["", "xx"]
"xxx".split(/x/, 3).should == ["", "", "x"]
"xxx".split(/x/, 4).should == ["", "", "", ""]
end
it "doesn't suppress or limit fields when limit is negative" do
"1,2,,3,4,,".split(/,/, -1).should == ["1", "2", "", "3", "4", "", ""]
"1,2,,3,4,,".split(/,/, -5).should == ["1", "2", "", "3", "4", "", ""]
" a b c\nd ".split(/\s+/, -1).should == ["", "a", "b", "c", "d", ""]
",".split(/,/, -1).should == ["", ""]
end
it "defaults to $; when regexp isn't given or nil" do
begin
old_fs = $;
[/,/, /:/, //, /XY/, /./].each do |fs|
$; = fs
["x,y,z,,,", "1:2:", "aXYbXYcXY", ""].each do |str|
expected = str.split(fs)
str.split(nil).should == expected
str.split.should == expected
str.split(nil, -1).should == str.split(fs, -1)
str.split(nil, 0).should == str.split(fs, 0)
str.split(nil, 2).should == str.split(fs, 2)
end
end
ensure
$; = old_fs
end
end
it "splits between characters when regexp matches a zero-length string" do
"hello".split(//).should == ["h", "e", "l", "l", "o"]
"hello".split(//, -1).should == ["h", "e", "l", "l", "o", ""]
"hello".split(//, 0).should == ["h", "e", "l", "l", "o"]
"hello".split(//, 1).should == ["hello"]
"hello".split(//, 2).should == ["h", "ello"]
"hello".split(//, 5).should == ["h", "e", "l", "l", "o"]
"hello".split(//, 6).should == ["h", "e", "l", "l", "o", ""]
"hello".split(//, 7).should == ["h", "e", "l", "l", "o", ""]
"hi mom".split(/\s*/).should == ["h", "i", "m", "o", "m"]
"AABCCBAA".split(/(?=B)/).should == ["AA", "BCC", "BAA"]
"AABCCBAA".split(/(?=B)/, -1).should == ["AA", "BCC", "BAA"]
"AABCCBAA".split(/(?=B)/, 2).should == ["AA", "BCCBAA"]
end
it "respects unicode when splitting between characters" do
str = "こにちわ"
reg = %r!!
ary = str.split(reg)
ary.size.should == 4
ary.should == ["", "", "", ""]
end
it "respects the encoding of the regexp when splitting between characters" do
str = "\303\202"
ary = str.split(//u)
ary.size.should == 1
ary.should == ["\303\202"]
end
it "includes all captures in the result array" do
"hello".split(/(el)/).should == ["h", "el", "lo"]
"hi!".split(/()/).should == ["h", "", "i", "", "!"]
"hi!".split(/()/, -1).should == ["h", "", "i", "", "!", "", ""]
"hello".split(/((el))()/).should == ["h", "el", "el", "", "lo"]
"AabB".split(/([a-z])+/).should == ["A", "b", "B"]
end
it "applies the limit to the number of split substrings, without counting captures" do
"aBaBa".split(/(B)()()/, 2).should == ["a", "B", "", "", "aBa"]
end
it "does not include non-matching captures in the result array" do
"hello".split(/(el)|(xx)/).should == ["h", "el", "lo"]
end
it "tries converting limit to an integer via to_int" do
obj = mock('2')
obj.should_receive(:to_int).and_return(2)
"1.2.3.4".split(".", obj).should == ["1", "2.3.4"]
end
it "returns a type error if limit can't be converted to an integer" do
lambda {"1.2.3.4".split(".", "three")}.should raise_error(TypeError)
lambda {"1.2.3.4".split(".", nil) }.should raise_error(TypeError)
end
it "doesn't set $~" do
$~ = nil
"x:y:z".split(/:/)
$~.should == nil
end
it "returns the original string if no matches are found" do
"foo".split(/bar/).should == ["foo"]
"foo".split(/bar/, -1).should == ["foo"]
"foo".split(/bar/, 0).should == ["foo"]
"foo".split(/bar/, 1).should == ["foo"]
"foo".split(/bar/, 2).should == ["foo"]
"foo".split(/bar/, 3).should == ["foo"]
end
it "returns subclass instances based on self" do
["", "x:y:z:", " x y "].each do |str|
[//, /:/, /\s+/].each do |pat|
[-1, 0, 1, 2].each do |limit|
StringSpecs::MyString.new(str).split(pat, limit).each do |x|
x.should be_an_instance_of(StringSpecs::MyString)
end
end
end
end
end
it "does not call constructor on created subclass instances" do
# can't call should_not_receive on an object that doesn't yet exist
# so failure here is signalled by exception, not expectation failure
s = StringSpecs::StringWithRaisingConstructor.new('silly:string')
s.split(/:/).first.should == 'silly'
end
it "taints the resulting strings if self is tainted" do
["", "x:y:z:", " x y "].each do |str|
[//, /:/, /\s+/].each do |pat|
[-1, 0, 1, 2].each do |limit|
str.dup.taint.split(pat, limit).each do |x|
# See the spec below for why the conditional is here
x.tainted?.should be_true unless x.empty?
end
end
end
end
end
it "taints an empty string if self is tainted" do
":".taint.split(//, -1).last.tainted?.should be_true
end
it "doesn't taints the resulting strings if the Regexp is tainted" do
["", "x:y:z:", " x y "].each do |str|
[//, /:/, /\s+/].each do |pat|
[-1, 0, 1, 2].each do |limit|
str.split(pat.dup.taint, limit).each do |x|
x.tainted?.should be_false
end
end
end
end
end
it "retains the encoding of the source string" 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
it "returns an ArgumentError if an invalid UTF-8 string is supplied" do
broken_str = 'проверка' # in russian, means "test"
broken_str.force_encoding('binary')
broken_str.chop!
broken_str.force_encoding('utf-8')
lambda{ broken_str.split(/\r\n|\r|\n/) }.should raise_error(ArgumentError)
end
end

View file

@ -0,0 +1,113 @@
# -*- encoding: binary -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
# TODO: rewrite all these specs
describe "String#squeeze" do
it "returns new string where runs of the same character are replaced by a single character when no args are given" do
"yellow moon".squeeze.should == "yelow mon"
end
it "only squeezes chars that are in the intersection of all sets given" do
"woot squeeze cheese".squeeze("eost", "queo").should == "wot squeze chese"
" now is the".squeeze(" ").should == " now is the"
end
it "negates sets starting with ^" do
s = "<<subbookkeeper!!!>>"
s.squeeze("beko", "^e").should == s.squeeze("bko")
s.squeeze("^<bek!>").should == s.squeeze("o")
s.squeeze("^o").should == s.squeeze("<bek!>")
s.squeeze("^").should == s
"^__^".squeeze("^^").should == "^_^"
"((^^__^^))".squeeze("_^").should == "((^_^))"
end
it "squeezes all chars in a sequence" do
s = "--subbookkeeper--"
s.squeeze("\x00-\xFF").should == s.squeeze
s.squeeze("bk-o").should == s.squeeze("bklmno")
s.squeeze("b-e").should == s.squeeze("bcde")
s.squeeze("e-").should == "-subbookkeper-"
s.squeeze("-e").should == "-subbookkeper-"
s.squeeze("---").should == "-subbookkeeper-"
"ook--001122".squeeze("--2").should == "ook-012"
"ook--(())".squeeze("(--").should == "ook-()"
s.squeeze("^b-e").should == "-subbokeeper-"
"^^__^^".squeeze("^^-^").should == "^^_^^"
"^^--^^".squeeze("^---").should == "^--^"
s.squeeze("b-dk-o-").should == "-subokeeper-"
s.squeeze("-b-dk-o").should == "-subokeeper-"
s.squeeze("b-d-k-o").should == "-subokeeper-"
s.squeeze("bc-e").should == "--subookkeper--"
s.squeeze("^bc-e").should == "-subbokeeper-"
"AABBCCaabbcc[[]]".squeeze("A-a").should == "ABCabbcc[]"
end
it "raises an ArgumentError when the parameter is out of sequence" do
s = "--subbookkeeper--"
lambda { s.squeeze("e-b") }.should raise_error(ArgumentError)
lambda { s.squeeze("^e-b") }.should raise_error(ArgumentError)
end
it "taints the result when self is tainted" do
"hello".taint.squeeze("e").tainted?.should == true
"hello".taint.squeeze("a-z").tainted?.should == true
"hello".squeeze("e".taint).tainted?.should == false
"hello".squeeze("l".taint).tainted?.should == false
end
it "tries to convert each set arg to a string using to_str" do
other_string = mock('lo')
other_string.should_receive(:to_str).and_return("lo")
other_string2 = mock('o')
other_string2.should_receive(:to_str).and_return("o")
"hello room".squeeze(other_string, other_string2).should == "hello rom"
end
it "raises a TypeError when one set arg can't be converted to a string" do
lambda { "hello world".squeeze([]) }.should raise_error(TypeError)
lambda { "hello world".squeeze(Object.new)}.should raise_error(TypeError)
lambda { "hello world".squeeze(mock('x')) }.should raise_error(TypeError)
end
it "returns subclass instances when called on a subclass" do
StringSpecs::MyString.new("oh no!!!").squeeze("!").should be_an_instance_of(StringSpecs::MyString)
end
end
describe "String#squeeze!" do
it "modifies self in place and returns self" do
a = "yellow moon"
a.squeeze!.should equal(a)
a.should == "yelow mon"
end
it "returns nil if no modifications were made" do
a = "squeeze"
a.squeeze!("u", "sq").should == nil
a.squeeze!("q").should == nil
a.should == "squeeze"
end
it "raises an ArgumentError when the parameter is out of sequence" do
s = "--subbookkeeper--"
lambda { s.squeeze!("e-b") }.should raise_error(ArgumentError)
lambda { s.squeeze!("^e-b") }.should raise_error(ArgumentError)
end
it "raises a RuntimeError when self is frozen" do
a = "yellow moon"
a.freeze
lambda { a.squeeze!("") }.should raise_error(RuntimeError)
lambda { a.squeeze! }.should raise_error(RuntimeError)
end
end

View file

@ -0,0 +1,45 @@
# -*- encoding: utf-8 -*-
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes.rb', __FILE__)
describe "String#start_with?" do
it "returns true only if beginning match" do
s = "hello"
s.start_with?('h').should be_true
s.start_with?('hel').should be_true
s.start_with?('el').should be_false
end
it "returns true only if any beginning match" do
"hello".start_with?('x', 'y', 'he', 'z').should be_true
end
it "returns true if the search string is empty" do
"hello".start_with?("").should be_true
"".start_with?("").should be_true
end
it "converts its argument using :to_str" do
s = "hello"
find = mock('h')
find.should_receive(:to_str).and_return("h")
s.start_with?(find).should be_true
end
it "ignores arguments not convertible to string" do
"hello".start_with?().should be_false
lambda { "hello".start_with?(1) }.should raise_error(TypeError)
lambda { "hello".start_with?(["h"]) }.should raise_error(TypeError)
lambda { "hello".start_with?(1, nil, "h").should }.should raise_error(TypeError)
end
it "uses only the needed arguments" do
find = mock('h')
find.should_not_receive(:to_str)
"hello".start_with?("h",find).should be_true
end
it "works for multibyte strings" do
"céréale".start_with?("cér").should be_true
end
end

View file

@ -0,0 +1,7 @@
require File.expand_path('../../../spec_helper', __FILE__)
describe "String" do
it "includes Comparable" do
String.include?(Comparable).should == true
end
end

Some files were not shown because too many files have changed in this diff Show more