mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
ffd0820ab3
This removes the related tests, and puts the related specs behind version guards. This affects all code in lib, including some libraries that may want to support older versions of Ruby.
476 lines
14 KiB
Ruby
476 lines
14 KiB
Ruby
# -*- encoding: utf-8 -*-
|
|
|
|
require_relative '../../spec_helper'
|
|
require_relative 'fixtures/classes'
|
|
require_relative 'shared/slice'
|
|
|
|
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 #{frozen_error_class} if self is frozen" do
|
|
-> { "hello".freeze.slice!(1) }.should raise_error(frozen_error_class)
|
|
-> { "hello".freeze.slice!(10) }.should raise_error(frozen_error_class)
|
|
-> { "".freeze.slice!(0) }.should raise_error(frozen_error_class)
|
|
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
|
|
|
|
|
|
it "returns the character given by the character index" do
|
|
"hellö there".slice!(1).should == "e"
|
|
"hellö there".slice!(4).should == "ö"
|
|
"hellö there".slice!(6).should == "t"
|
|
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
|
|
|
|
ruby_version_is ''...'2.7' do
|
|
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
|
|
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 #{frozen_error_class} if self is frozen" do
|
|
-> { "hello".freeze.slice!(1, 2) }.should raise_error(frozen_error_class)
|
|
-> { "hello".freeze.slice!(10, 3) }.should raise_error(frozen_error_class)
|
|
-> { "hello".freeze.slice!(-10, 3)}.should raise_error(frozen_error_class)
|
|
-> { "hello".freeze.slice!(4, -3) }.should raise_error(frozen_error_class)
|
|
-> { "hello".freeze.slice!(10, 3) }.should raise_error(frozen_error_class)
|
|
-> { "hello".freeze.slice!(-10, 3)}.should raise_error(frozen_error_class)
|
|
-> { "hello".freeze.slice!(4, -3) }.should raise_error(frozen_error_class)
|
|
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
|
|
|
|
|
|
it "returns the substring given by the character offsets" do
|
|
"hellö there".slice!(1,0).should == ""
|
|
"hellö there".slice!(1,3).should == "ell"
|
|
"hellö there".slice!(1,6).should == "ellö t"
|
|
"hellö there".slice!(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".slice!(1, 2).should == xE6xCB
|
|
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
|
|
|
|
ruby_version_is ''...'2.7' do
|
|
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
|
|
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
|
|
|
|
|
|
it "returns the substring given by the character offsets of the range" do
|
|
"hellö there".slice!(1..1).should == "e"
|
|
"hellö there".slice!(1..3).should == "ell"
|
|
"hellö there".slice!(1...3).should == "el"
|
|
"hellö there".slice!(-4..-2).should == "her"
|
|
"hellö there".slice!(-4...-2).should == "he"
|
|
"hellö there".slice!(5..-1).should == " there"
|
|
"hellö there".slice!(5...-1).should == " ther"
|
|
end
|
|
|
|
|
|
it "raises a #{frozen_error_class} on a frozen instance that is modified" do
|
|
-> { "hello".freeze.slice!(1..3) }.should raise_error(frozen_error_class)
|
|
end
|
|
|
|
# see redmine #1551
|
|
it "raises a #{frozen_error_class} on a frozen instance that would not be modified" do
|
|
-> { "hello".freeze.slice!(10..20)}.should raise_error(frozen_error_class)
|
|
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
|
|
|
|
ruby_version_is ''...'2.7' 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 = 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
|
|
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
|
|
|
|
it "returns the matching portion of self with a multi byte character" do
|
|
"hëllo there".slice!(/[ë](.)\1/).should == "ëll"
|
|
"".slice!(//).should == ""
|
|
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 #{frozen_error_class} on a frozen instance that is modified" do
|
|
-> { "this is a string".freeze.slice!(/s.*t/) }.should raise_error(frozen_error_class)
|
|
end
|
|
|
|
it "raises a #{frozen_error_class} on a frozen instance that would not be modified" do
|
|
-> { "this is a string".freeze.slice!(/zzz/) }.should raise_error(frozen_error_class)
|
|
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
|
|
|
|
ruby_version_is ''...'2.7' 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 = 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
|
|
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
|
|
|
|
it "returns the encoding aware capture for the given index" do
|
|
"hår".slice!(/(.)(.)(.)/, 0).should == "hår"
|
|
"hår".slice!(/(.)(.)(.)/, 1).should == "h"
|
|
"hår".slice!(/(.)(.)(.)/, 2).should == "å"
|
|
"hår".slice!(/(.)(.)(.)/, 3).should == "r"
|
|
"hår".slice!(/(.)(.)(.)/, -1).should == "r"
|
|
"hår".slice!(/(.)(.)(.)/, -2).should == "å"
|
|
"hår".slice!(/(.)(.)(.)/, -3).should == "h"
|
|
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 #{frozen_error_class} if self is frozen" do
|
|
-> { "this is a string".freeze.slice!(/s.*t/) }.should raise_error(frozen_error_class)
|
|
-> { "this is a string".freeze.slice!(/zzz/, 0)}.should raise_error(frozen_error_class)
|
|
-> { "this is a string".freeze.slice!(/(.)/, 2)}.should raise_error(frozen_error_class)
|
|
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
|
|
|
|
ruby_version_is ''...'2.7' do
|
|
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
|
|
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)
|
|
|
|
-> { "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 #{frozen_error_class} if self is frozen" do
|
|
-> { "hello hello".freeze.slice!('llo') }.should raise_error(frozen_error_class)
|
|
-> { "this is a string".freeze.slice!('zzz')}.should raise_error(frozen_error_class)
|
|
-> { "this is a string".freeze.slice!('zzz')}.should raise_error(frozen_error_class)
|
|
end
|
|
end
|