This commit is contained in:
Benoit Daloze 2021-11-29 15:50:28 +01:00
parent e6d93a27af
commit 67a1e22589
62 changed files with 656 additions and 59 deletions

View File

@ -743,6 +743,30 @@ describe :array_slice, shared: true do
@array.send(@method, eval("(-2..-4).step(10)")).should == [] @array.send(@method, eval("(-2..-4).step(10)")).should == []
@array.send(@method, eval("(-2...-4).step(10)")).should == [] @array.send(@method, eval("(-2...-4).step(10)")).should == []
end end
it "has range with bounds outside of array" do
# end is equal to array's length
@array.send(@method, (0..6).step(1)).should == [0, 1, 2, 3, 4, 5]
-> { @array.send(@method, (0..6).step(2)) }.should raise_error(RangeError)
# end is greater than length with positive steps
@array.send(@method, (1..6).step(2)).should == [1, 3, 5]
@array.send(@method, (2..7).step(2)).should == [2, 4]
-> { @array.send(@method, (2..8).step(2)) }.should raise_error(RangeError)
# begin is greater than length with negative steps
@array.send(@method, (6..1).step(-2)).should == [5, 3, 1]
@array.send(@method, (7..2).step(-2)).should == [5, 3]
-> { @array.send(@method, (8..2).step(-2)) }.should raise_error(RangeError)
end
it "has endless range with start outside of array's bounds" do
@array.send(@method, eval("(6..).step(1)")).should == []
@array.send(@method, eval("(7..).step(1)")).should == nil
@array.send(@method, eval("(6..).step(2)")).should == []
-> { @array.send(@method, eval("(7..).step(2)")) }.should raise_error(RangeError)
end
end end
end end

View File

@ -55,12 +55,24 @@ describe :dir_glob, shared: true do
end end
end end
ruby_version_is "3.0"..."3.1" do
it "result is sorted with any non false value of sort:" do
result = Dir.send(@method, '*', sort: 0)
result.should == result.sort
result = Dir.send(@method, '*', sort: nil)
result.should == result.sort
result = Dir.send(@method, '*', sort: 'false')
result.should == result.sort
end
end
ruby_version_is "3.1" do ruby_version_is "3.1" do
it "true or false is expected as sort:" do it "raises an ArgumentError if sort: is not true or false" do
-> {Dir.send(@method, '*', sort: nil)}.should raise_error ArgumentError, /true or false/ -> { Dir.send(@method, '*', sort: 0) }.should raise_error ArgumentError, /expected true or false/
-> {Dir.send(@method, '*', sort: 0)}.should raise_error ArgumentError, /true or false/ -> { Dir.send(@method, '*', sort: nil) }.should raise_error ArgumentError, /expected true or false/
-> {Dir.send(@method, '*', sort: "")}.should raise_error ArgumentError, /true or false/ -> { Dir.send(@method, '*', sort: 'false') }.should raise_error ArgumentError, /expected true or false/
-> {Dir.send(@method, '*', sort: Object.new)}.should raise_error ArgumentError, /true or false/
end end
end end

View File

@ -54,7 +54,7 @@ describe "Encoding.compatible? String, String" do
it "returns nil if the second's Encoding is not ASCII compatible" do it "returns nil if the second's Encoding is not ASCII compatible" do
a = "abc".force_encoding("UTF-8") a = "abc".force_encoding("UTF-8")
b = "123".force_encoding("UTF-16LE") b = "1234".force_encoding("UTF-16LE")
Encoding.compatible?(a, b).should be_nil Encoding.compatible?(a, b).should be_nil
end end
end end

View File

@ -34,14 +34,23 @@ describe "Encoding::Converter#putback" do
@ec.putback.should == "" @ec.putback.should == ""
end end
it "returns the problematic bytes for UTF-16LE" do
ec = Encoding::Converter.new("utf-16le", "iso-8859-1")
src = "\x00\xd8\x61\x00"
dst = ""
ec.primitive_convert(src, dst).should == :invalid_byte_sequence
ec.primitive_errinfo.should == [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"]
ec.putback.should == "a\x00".force_encoding("utf-16le")
ec.putback.should == ""
end
it "accepts an integer argument corresponding to the number of bytes to be put back" do it "accepts an integer argument corresponding to the number of bytes to be put back" do
ec = Encoding::Converter.new("utf-16le", "iso-8859-1") ec = Encoding::Converter.new("utf-16le", "iso-8859-1")
src = "\x00\xd8\x61\x00" src = "\x00\xd8\x61\x00"
dst = "" dst = ""
ec.primitive_convert(src, dst).should == :invalid_byte_sequence ec.primitive_convert(src, dst).should == :invalid_byte_sequence
ec.primitive_errinfo.should == [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"] ec.primitive_errinfo.should == [:invalid_byte_sequence, "UTF-16LE", "UTF-8", "\x00\xD8", "a\x00"]
ec.putback(1).should == "\x00".force_encoding("utf-16le") ec.putback(2).should == "a\x00".force_encoding("utf-16le")
ec.putback.should == "a".force_encoding("utf-16le")
ec.putback.should == "" ec.putback.should == ""
end end
end end

View File

@ -65,6 +65,18 @@ describe "Enumerable#grep" do
["abc", "def"].grep(/b/).should == ["abc"] ["abc", "def"].grep(/b/).should == ["abc"]
Regexp.last_match[0].should == "z" Regexp.last_match[0].should == "z"
end end
it "correctly handles non-string elements" do
'set last match' =~ /set last (.*)/
[:a, 'b', 'z', :c, 42, nil].grep(/[a-d]/).should == [:a, 'b', :c]
$1.should == 'match'
o = Object.new
def o.to_str
'hello'
end
[o].grep(/ll/).first.should.equal?(o)
end
end end
describe "with a block" do describe "with a block" do

View File

@ -45,6 +45,18 @@ describe "Enumerable#grep_v" do
["abc", "def"].grep_v(/e/).should == ["abc"] ["abc", "def"].grep_v(/e/).should == ["abc"]
Regexp.last_match[0].should == "z" Regexp.last_match[0].should == "z"
end end
it "correctly handles non-string elements" do
'set last match' =~ /set last (.*)/
[:a, 'b', 'z', :c, 42, nil].grep_v(/[a-d]/).should == ['z', 42, nil]
$1.should == 'match'
o = Object.new
def o.to_str
'hello'
end
[o].grep_v(/mm/).first.should.equal?(o)
end
end end
describe "without block" do describe "without block" do

View File

@ -6,4 +6,13 @@ describe "Enumerator::ArithmeticSequence#begin" do
(1..10).step.begin.should == 1 (1..10).step.begin.should == 1
(1...10).step.begin.should == 1 (1...10).step.begin.should == 1
end end
ruby_version_is "2.7" do
context "with beginless" do
it "returns nil as begin of the sequence" do
eval("(..10).step(1)").begin.should == nil
eval("(...10).step(1)").begin.should == nil
end
end
end
end end

View File

@ -6,4 +6,11 @@ describe "Enumerator::ArithmeticSequence#end" do
(1..10).step.end.should == 10 (1..10).step.end.should == 10
(1...10).step(17).end.should == 10 (1...10).step(17).end.should == 10
end end
context "with endless" do
it "returns nil as end of the sequence" do
(1..).step(1).end.should == nil
(1...).step(1).end.should == nil
end
end
end end

View File

@ -56,3 +56,12 @@ describe "Errno::ENOTSUP" do
end end
end end
end end
describe "Errno::ENOENT" do
it "lets subclasses inherit the default error message" do
c = Class.new(Errno::ENOENT)
raise c, "custom message"
rescue => e
e.message.should == "No such file or directory - custom message"
end
end

View File

@ -12,6 +12,11 @@ ruby_version_is "3.0" do
original = GC.auto_compact original = GC.auto_compact
begin begin
GC.auto_compact = !original GC.auto_compact = !original
rescue NotImplementedError # platform does not support autocompact
skip
end
begin
GC.auto_compact.should == !original GC.auto_compact.should == !original
ensure ensure
GC.auto_compact = original GC.auto_compact = original

View File

@ -0,0 +1,19 @@
require_relative '../../spec_helper'
ruby_version_is "3.1" do
describe "GC.measure_total_time" do
before :each do
@default = GC.measure_total_time
end
after :each do
GC.measure_total_time = @default
end
it "can set and get a boolean value" do
original = GC.measure_total_time
GC.measure_total_time = !original
GC.measure_total_time.should == !original
end
end
end

View File

@ -7,6 +7,16 @@ describe "GC.stat" do
stat.keys.should.include?(:count) stat.keys.should.include?(:count)
end end
it "updates the given hash values" do
hash = { count: "hello", __other__: "world" }
stat = GC.stat(hash)
stat.should be_kind_of(Hash)
stat.should equal hash
stat[:count].should be_kind_of(Integer)
stat[:__other__].should == "world"
end
it "the values are all Integer since rb_gc_stat() returns size_t" do it "the values are all Integer since rb_gc_stat() returns size_t" do
GC.stat.values.each { |value| value.should be_kind_of(Integer) } GC.stat.values.each { |value| value.should be_kind_of(Integer) }
end end
@ -41,4 +51,12 @@ describe "GC.stat" do
GC.stat(:total_allocated_objects).should be_kind_of(Integer) GC.stat(:total_allocated_objects).should be_kind_of(Integer)
GC.stat[:total_allocated_objects].should be_kind_of(Integer) GC.stat[:total_allocated_objects].should be_kind_of(Integer)
end end
it "raises an error if argument is not nil, a symbol, or a hash" do
-> { GC.stat(7) }.should raise_error(TypeError, "non-hash or symbol given")
end
it "raises an error if an unknown key is given" do
-> { GC.stat(:foo) }.should raise_error(ArgumentError, "unknown key: foo")
end
end end

View File

@ -0,0 +1,15 @@
require_relative '../../spec_helper'
ruby_version_is "3.1" do
describe "GC.total_time" do
it "returns an Integer" do
GC.total_time.should be_kind_of(Integer)
end
it "increases as collections are run" do
time_before = GC.total_time
GC.start
GC.total_time.should > time_before
end
end
end

View File

@ -40,4 +40,19 @@ describe "Integer#+" do
-> { @bignum + :symbol}.should raise_error(TypeError) -> { @bignum + :symbol}.should raise_error(TypeError)
end end
end end
it "can be redefined" do
code = <<~RUBY
class Integer
alias_method :old_plus, :+
def +(other)
self - other
end
end
result = 1 + 2
Integer.alias_method :+, :old_plus
print result
RUBY
ruby_exe(code).should == "-1"
end
end end

View File

@ -31,10 +31,10 @@ describe "Kernel#clone" do
describe "with no arguments" do describe "with no arguments" do
it "copies frozen state from the original" do it "copies frozen state from the original" do
o2 = @obj.clone o2 = @obj.clone
o2.should_not.frozen?
@obj.freeze @obj.freeze
o3 = @obj.clone o3 = @obj.clone
o2.should_not.frozen?
o3.should.frozen? o3.should.frozen?
end end
@ -44,6 +44,30 @@ describe "Kernel#clone" do
end end
end end
describe "with freeze: nil" do
ruby_version_is ""..."3.0" do
it "raises ArgumentError" do
-> { @obj.clone(freeze: nil) }.should raise_error(ArgumentError, /unexpected value for freeze: NilClass/)
end
end
ruby_version_is "3.0" do
it "copies frozen state from the original, like #clone without arguments" do
o2 = @obj.clone(freeze: nil)
o2.should_not.frozen?
@obj.freeze
o3 = @obj.clone(freeze: nil)
o3.should.frozen?
end
it "copies frozen?" do
o = "".freeze.clone(freeze: nil)
o.frozen?.should be_true
end
end
end
describe "with freeze: true" do describe "with freeze: true" do
it 'makes a frozen copy if the original is frozen' do it 'makes a frozen copy if the original is frozen' do
@obj.freeze @obj.freeze
@ -112,6 +136,14 @@ describe "Kernel#clone" do
end end
end end
describe "with freeze: anything else" do
it 'raises ArgumentError when passed not true/false/nil' do
-> { @obj.clone(freeze: 1) }.should raise_error(ArgumentError, /unexpected value for freeze: Integer/)
-> { @obj.clone(freeze: "") }.should raise_error(ArgumentError, /unexpected value for freeze: String/)
-> { @obj.clone(freeze: Object.new) }.should raise_error(ArgumentError, /unexpected value for freeze: Object/)
end
end
it "copies instance variables" do it "copies instance variables" do
clone = @obj.clone clone = @obj.clone
clone.one.should == 1 clone.one.should == 1

View File

@ -281,9 +281,14 @@ module KernelSpecs
@two = two @two = two
end end
def initialize_copy(other) def initialize_copy(other, **kw)
ScratchPad.record object_id ScratchPad.record object_id
end end
# define to support calling #clone with optional :freeze keyword argument
def initialize_clone(other, **kw)
super(other) # to call #initialize_copy
end
end end
class Clone class Clone

View File

@ -0,0 +1,28 @@
require_relative '../../spec_helper'
describe "Kernel#initialize_clone" do
it "is a private instance method" do
Kernel.should have_private_instance_method(:initialize_clone)
end
it "returns the receiver" do
a = Object.new
b = Object.new
a.send(:initialize_clone, b).should == a
end
it "calls #initialize_copy" do
a = Object.new
b = Object.new
a.should_receive(:initialize_copy).with(b)
a.send(:initialize_clone, b)
end
ruby_version_is "3.0" do
it "accepts a :freeze keyword argument for obj.clone(freeze: value)" do
a = Object.new
b = Object.new
a.send(:initialize_clone, b, freeze: true).should == a
end
end
end

View File

@ -0,0 +1,20 @@
require_relative '../../spec_helper'
describe "Kernel#initialize_dup" do
it "is a private instance method" do
Kernel.should have_private_instance_method(:initialize_dup)
end
it "returns the receiver" do
a = Object.new
b = Object.new
a.send(:initialize_dup, b).should == a
end
it "calls #initialize_copy" do
a = Object.new
b = Object.new
a.should_receive(:initialize_copy).with(b)
a.send(:initialize_dup, b)
end
end

View File

@ -25,5 +25,16 @@ describe "Kernel#instance_variables" do
a.instance_variable_set("@test", 1) a.instance_variable_set("@test", 1)
a.instance_variables.should == [:@test] a.instance_variables.should == [:@test]
end end
it "returns the instances variables in the order declared" do
c = Class.new do
def initialize
@c = 1
@a = 2
@b = 3
end
end
c.new.instance_variables.should == [:@c, :@a, :@b]
end
end end
end end

View File

@ -5,6 +5,18 @@ describe "Kernel#print" do
it "is a private method" do it "is a private method" do
Kernel.should have_private_instance_method(:print) Kernel.should have_private_instance_method(:print)
end end
it "delegates to $stdout" do
-> { print :arg }.should output("arg")
end
it "prints $_ when no arguments are given" do
orig_value = $_
$_ = 'foo'
-> { print }.should output("foo")
ensure
$_ = orig_value
end
end end
describe "Kernel.print" do describe "Kernel.print" do

View File

@ -212,6 +212,70 @@ describe "Kernel#warn" do
-> { warn('foo', **h) }.should complain("foo\n") -> { warn('foo', **h) }.should complain("foo\n")
end end
ruby_version_is '3.0' do
it "calls Warning.warn without keyword arguments if Warning.warn does not accept keyword arguments" do
verbose = $VERBOSE
$VERBOSE = false
class << Warning
alias_method :_warn, :warn
def warn(message)
ScratchPad.record(message)
end
end
begin
ScratchPad.clear
Kernel.warn("Chunky bacon!")
ScratchPad.recorded.should == "Chunky bacon!\n"
Kernel.warn("Deprecated bacon!", category: :deprecated)
ScratchPad.recorded.should == "Deprecated bacon!\n"
ensure
class << Warning
remove_method :warn
alias_method :warn, :_warn
remove_method :_warn
end
$VERBOSE = verbose
end
end
it "calls Warning.warn with category: nil if Warning.warn accepts keyword arguments" do
Warning.should_receive(:warn).with("Chunky bacon!\n", category: nil)
verbose = $VERBOSE
$VERBOSE = false
begin
Kernel.warn("Chunky bacon!")
ensure
$VERBOSE = verbose
end
end
it "calls Warning.warn with given category keyword converted to a symbol" do
Warning.should_receive(:warn).with("Chunky bacon!\n", category: :deprecated)
verbose = $VERBOSE
$VERBOSE = false
begin
Kernel.warn("Chunky bacon!", category: 'deprecated')
ensure
$VERBOSE = verbose
end
end
end
ruby_version_is ''...'3.0' do
it "calls Warning.warn with no keyword arguments" do
Warning.should_receive(:warn).with("Chunky bacon!\n")
verbose = $VERBOSE
$VERBOSE = false
begin
Kernel.warn("Chunky bacon!")
ensure
$VERBOSE = verbose
end
end
end
it "does not call Warning.warn if self is the Warning module" do it "does not call Warning.warn if self is the Warning module" do
# RubyGems redefines Kernel#warn so we need to use a subprocess and disable RubyGems here # RubyGems redefines Kernel#warn so we need to use a subprocess and disable RubyGems here
code = <<-RUBY code = <<-RUBY

View File

@ -1,7 +1,15 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "MatchData#captures" do describe "MatchData#captures" do
it "returns an array of the match captures" do it "returns an array of the match captures" do
/(.)(.)(\d+)(\d)/.match("THX1138.").captures.should == ["H","X","113","8"] /(.)(.)(\d+)(\d)/.match("THX1138.").captures.should == ["H","X","113","8"]
end end
ruby_version_is "3.0" do
it "returns instances of String when given a String subclass" do
str = MatchDataSpecs::MyString.new("THX1138: The Movie")
/(.)(.)(\d+)(\d)/.match(str).captures.each { |c| c.should be_an_instance_of(String) }
end
end
end end

View File

@ -1,4 +1,5 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "MatchData#[]" do describe "MatchData#[]" do
it "acts as normal array indexing [index]" do it "acts as normal array indexing [index]" do
@ -20,6 +21,13 @@ describe "MatchData#[]" do
it "supports ranges [start..end]" do it "supports ranges [start..end]" do
/(.)(.)(\d+)(\d)/.match("THX1138.")[1..3].should == %w|H X 113| /(.)(.)(\d+)(\d)/.match("THX1138.")[1..3].should == %w|H X 113|
end end
ruby_version_is "3.0" do
it "returns instances of String when given a String subclass" do
str = MatchDataSpecs::MyString.new("THX1138.")
/(.)(.)(\d+)(\d)/.match(str)[0..-1].each { |m| m.should be_an_instance_of(String) }
end
end
end end
describe "MatchData#[Symbol]" do describe "MatchData#[Symbol]" do

View File

@ -0,0 +1,3 @@
module MatchDataSpecs
class MyString < String; end
end

View File

@ -1,4 +1,5 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "MatchData#post_match" do describe "MatchData#post_match" do
it "returns the string after the match equiv. special var $'" do it "returns the string after the match equiv. special var $'" do
@ -33,4 +34,11 @@ describe "MatchData#post_match" do
str = "abc".force_encoding Encoding::ISO_8859_1 str = "abc".force_encoding Encoding::ISO_8859_1
str.match(/c/).post_match.encoding.should equal(Encoding::ISO_8859_1) str.match(/c/).post_match.encoding.should equal(Encoding::ISO_8859_1)
end end
ruby_version_is "3.0" do
it "returns an instance of String when given a String subclass" do
str = MatchDataSpecs::MyString.new("THX1138: The Movie")
/(.)(.)(\d+)(\d)/.match(str).post_match.should be_an_instance_of(String)
end
end
end end

View File

@ -1,4 +1,5 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "MatchData#pre_match" do describe "MatchData#pre_match" do
it "returns the string before the match, equiv. special var $`" do it "returns the string before the match, equiv. special var $`" do
@ -33,4 +34,11 @@ describe "MatchData#pre_match" do
str = "abc".force_encoding Encoding::ISO_8859_1 str = "abc".force_encoding Encoding::ISO_8859_1
str.match(/a/).pre_match.encoding.should equal(Encoding::ISO_8859_1) str.match(/a/).pre_match.encoding.should equal(Encoding::ISO_8859_1)
end end
ruby_version_is "3.0" do
it "returns an instance of String when given a String subclass" do
str = MatchDataSpecs::MyString.new("THX1138: The Movie")
/(.)(.)(\d+)(\d)/.match(str).pre_match.should be_an_instance_of(String)
end
end
end end

View File

@ -1,7 +1,15 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "MatchData#to_a" do describe "MatchData#to_a" do
it "returns an array of matches" do it "returns an array of matches" do
/(.)(.)(\d+)(\d)/.match("THX1138.").to_a.should == ["HX1138", "H", "X", "113", "8"] /(.)(.)(\d+)(\d)/.match("THX1138.").to_a.should == ["HX1138", "H", "X", "113", "8"]
end end
ruby_version_is "3.0" do
it "returns instances of String when given a String subclass" do
str = MatchDataSpecs::MyString.new("THX1138.")
/(.)(.)(\d+)(\d)/.match(str)[0..-1].to_a.each { |m| m.should be_an_instance_of(String) }
end
end
end end

View File

@ -1,7 +1,15 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "MatchData#to_s" do describe "MatchData#to_s" do
it "returns the entire matched string" do it "returns the entire matched string" do
/(.)(.)(\d+)(\d)/.match("THX1138.").to_s.should == "HX1138" /(.)(.)(\d+)(\d)/.match("THX1138.").to_s.should == "HX1138"
end end
ruby_version_is "3.0" do
it "returns an instance of String when given a String subclass" do
str = MatchDataSpecs::MyString.new("THX1138.")
/(.)(.)(\d+)(\d)/.match(str).to_s.should be_an_instance_of(String)
end
end
end end

View File

@ -22,4 +22,11 @@ describe "Numeric#clone" do
it "raises ArgumentError if passed freeze: false" do it "raises ArgumentError if passed freeze: false" do
-> { 1.clone(freeze: false) }.should raise_error(ArgumentError, /can't unfreeze/) -> { 1.clone(freeze: false) }.should raise_error(ArgumentError, /can't unfreeze/)
end end
ruby_version_is "3.0" do
it "does not change frozen status if passed freeze: nil" do
value = 1
value.clone(freeze: nil).should equal(value)
end
end
end end

View File

@ -52,4 +52,13 @@ describe "Numeric#quo" do
obj.quo(19).should == 1.quo(20) obj.quo(19).should == 1.quo(20)
end end
it "raises a ZeroDivisionError if the given argument is zero and not a Float" do
-> { 1.quo(0) }.should raise_error(ZeroDivisionError)
end
it "returns infinity if the given argument is zero and is a Float" do
(1.quo(0.0)).to_s.should == 'Infinity'
(-1.quo(0.0)).to_s.should == '-Infinity'
end
end end

View File

@ -481,6 +481,32 @@ describe "Range#step" do
end end
end end
ruby_version_is "2.7" do
context "when begin is not defined and end is numeric" do
it "returns an instance of Enumerator::ArithmeticSequence" do
eval("(..10)").step.class.should == Enumerator::ArithmeticSequence
end
end
end
context "when range is endless" do
it "returns an instance of Enumerator::ArithmeticSequence when begin is numeric" do
(1..).step.class.should == Enumerator::ArithmeticSequence
end
it "returns an instance of Enumerator when begin is not numeric" do
("a"..).step.class.should == Enumerator
end
end
ruby_version_is "2.7" do
context "when range is beginless and endless" do
it "returns an instance of Enumerator" do
Range.new(nil, nil).step.class.should == Enumerator
end
end
end
context "when begin and end are not numerics" do context "when begin and end are not numerics" do
it "returns an instance of Enumerator" do it "returns an instance of Enumerator" do
("a".."z").step.class.should == Enumerator ("a".."z").step.class.should == Enumerator

View File

@ -6,4 +6,12 @@ describe "String#each_grapheme_cluster" do
it_behaves_like :string_chars, :each_grapheme_cluster it_behaves_like :string_chars, :each_grapheme_cluster
it_behaves_like :string_grapheme_clusters, :each_grapheme_cluster it_behaves_like :string_grapheme_clusters, :each_grapheme_cluster
it_behaves_like :string_each_char_without_block, :each_grapheme_cluster it_behaves_like :string_each_char_without_block, :each_grapheme_cluster
ruby_version_is '3.0' do
it "yields String instances for subclasses" do
a = []
StringSpecs::MyString.new("abc").each_grapheme_cluster { |s| a << s.class }
a.should == [String, String, String]
end
end
end end

View File

@ -60,7 +60,7 @@ describe "String#force_encoding" do
end end
it "does not transcode self" do it "does not transcode self" do
str = "\u{8612}" str = "é"
str.dup.force_encoding('utf-16le').should_not == str.encode('utf-16le') str.dup.force_encoding('utf-16le').should_not == str.encode('utf-16le')
end end

View File

@ -198,4 +198,12 @@ describe "String#scan with pattern and block" do
third.should == 'c'; third.should == 'c';
end end
end end
ruby_version_is '3.0' do
it "yields String instances for subclasses" do
a = []
StringSpecs::MyString.new("abc").scan(/./) { |s| a << s.class }
a.should == [String, String, String]
end
end
end end

View File

@ -56,7 +56,9 @@ describe "String#scrub with a custom replacement" do
it "replaces invalid byte sequences in frozen strings" do it "replaces invalid byte sequences in frozen strings" do
x81 = [0x81].pack('C').force_encoding('utf-8') x81 = [0x81].pack('C').force_encoding('utf-8')
(-"abc\u3042#{x81}").scrub("*").should == "abc\u3042*" (-"abc\u3042#{x81}").scrub("*").should == "abc\u3042*"
utf16_str = ("abc".encode('UTF-16LE').bytes + [0x81]).pack('c*').force_encoding('UTF-16LE')
leading_surrogate = [0x00, 0xD8]
utf16_str = ("abc".encode('UTF-16LE').bytes + leading_surrogate).pack('c*').force_encoding('UTF-16LE')
(-(utf16_str)).scrub("*".encode('UTF-16LE')).should == "abc*".encode('UTF-16LE') (-(utf16_str)).scrub("*".encode('UTF-16LE')).should == "abc*".encode('UTF-16LE')
end end

View File

@ -21,7 +21,7 @@ describe :string_eql_value, shared: true do
end end
it "considers encoding compatibility" do it "considers encoding compatibility" do
"hello".force_encoding("utf-8").send(@method, "hello".force_encoding("utf-32le")).should be_false "abcd".force_encoding("utf-8").send(@method, "abcd".force_encoding("utf-32le")).should be_false
end end
it "ignores subclass differences" do it "ignores subclass differences" do

View File

@ -36,4 +36,20 @@ describe :string_length, shared: true do
concat.force_encoding(Encoding::ASCII_8BIT) concat.force_encoding(Encoding::ASCII_8BIT)
concat.size.should == 4 concat.size.should == 4
end end
it "adds 1 for every invalid byte in UTF-8" do
"\xF4\x90\x80\x80".size.should == 4
"a\xF4\x90\x80\x80b".size.should == 6
"é\xF4\x90\x80\x80è".size.should == 6
end
it "adds 1 (and not 2) for a incomplete surrogate in UTF-16" do
"\x00\xd8".force_encoding("UTF-16LE").size.should == 1
"\xd8\x00".force_encoding("UTF-16BE").size.should == 1
end
it "adds 1 for a broken sequence in UTF-32" do
"\x04\x03\x02\x01".force_encoding("UTF-32LE").size.should == 1
"\x01\x02\x03\x04".force_encoding("UTF-32BE").size.should == 1
end
end end

View File

@ -43,10 +43,10 @@ describe "String#valid_encoding?" do
str.force_encoding('KOI8-R').valid_encoding?.should be_true str.force_encoding('KOI8-R').valid_encoding?.should be_true
str.force_encoding('KOI8-U').valid_encoding?.should be_true str.force_encoding('KOI8-U').valid_encoding?.should be_true
str.force_encoding('Shift_JIS').valid_encoding?.should be_false str.force_encoding('Shift_JIS').valid_encoding?.should be_false
str.force_encoding('UTF-16BE').valid_encoding?.should be_false "\xD8\x00".force_encoding('UTF-16BE').valid_encoding?.should be_false
str.force_encoding('UTF-16LE').valid_encoding?.should be_false "\x00\xD8".force_encoding('UTF-16LE').valid_encoding?.should be_false
str.force_encoding('UTF-32BE').valid_encoding?.should be_false "\x04\x03\x02\x01".force_encoding('UTF-32BE').valid_encoding?.should be_false
str.force_encoding('UTF-32LE').valid_encoding?.should be_false "\x01\x02\x03\x04".force_encoding('UTF-32LE').valid_encoding?.should be_false
str.force_encoding('Windows-1251').valid_encoding?.should be_true str.force_encoding('Windows-1251').valid_encoding?.should be_true
str.force_encoding('IBM437').valid_encoding?.should be_true str.force_encoding('IBM437').valid_encoding?.should be_true
str.force_encoding('IBM737').valid_encoding?.should be_true str.force_encoding('IBM737').valid_encoding?.should be_true

View File

@ -12,5 +12,11 @@ ruby_version_is '2.7' do
it "raises for unknown category" do it "raises for unknown category" do
-> { Warning[:noop] }.should raise_error(ArgumentError, /unknown category: noop/) -> { Warning[:noop] }.should raise_error(ArgumentError, /unknown category: noop/)
end end
it "raises for non-Symbol category" do
-> { Warning[42] }.should raise_error(TypeError)
-> { Warning[false] }.should raise_error(TypeError)
-> { Warning["noop"] }.should raise_error(TypeError)
end
end end
end end

View File

@ -27,5 +27,11 @@ ruby_version_is '2.7' do
it "raises for unknown category" do it "raises for unknown category" do
-> { Warning[:noop] = false }.should raise_error(ArgumentError, /unknown category: noop/) -> { Warning[:noop] = false }.should raise_error(ArgumentError, /unknown category: noop/)
end end
it "raises for non-Symbol category" do
-> { Warning[42] = false }.should raise_error(TypeError)
-> { Warning[false] = false }.should raise_error(TypeError)
-> { Warning["noop"] = false }.should raise_error(TypeError)
end
end end
end end

View File

@ -51,7 +51,6 @@ describe "Warning.warn" do
end end
end end
ruby_version_is '3.0' do ruby_version_is '3.0' do
it "is called by Kernel.warn with nil category keyword" do it "is called by Kernel.warn with nil category keyword" do
Warning.should_receive(:warn).with("Chunky bacon!\n", category: nil) Warning.should_receive(:warn).with("Chunky bacon!\n", category: nil)
@ -69,7 +68,7 @@ describe "Warning.warn" do
verbose = $VERBOSE verbose = $VERBOSE
$VERBOSE = false $VERBOSE = false
begin begin
Kernel.warn("Chunky bacon!", category: 'deprecated') Kernel.warn("Chunky bacon!", category: "deprecated")
ensure ensure
$VERBOSE = verbose $VERBOSE = verbose
end end

View File

@ -94,6 +94,12 @@ ruby_version_is "3.0" do
}.should raise_error(RuntimeError, 'class variable access from toplevel') }.should raise_error(RuntimeError, 'class variable access from toplevel')
end end
it "does not raise an error when checking if defined from the toplevel scope" do
-> {
eval "defined?(@@cvar_toplevel1)"
}.should_not raise_error
end
it "raises a RuntimeError when a class variable is overtaken in an ancestor class" do it "raises a RuntimeError when a class variable is overtaken in an ancestor class" do
parent = Class.new() parent = Class.new()
subclass = Class.new(parent) subclass = Class.new(parent)

View File

@ -38,6 +38,10 @@ describe "Regexps with encoding modifiers" do
/#{/./}/n.match("\303\251").to_a.should == ["\303"] /#{/./}/n.match("\303\251").to_a.should == ["\303"]
end end
it "warns when using /n with a match string with non-ASCII characters and an encoding other than ASCII-8BIT" do
-> { /./n.match("\303\251".force_encoding('utf-8')) }.should complain(%r{historical binary regexp match /.../n against UTF-8 string})
end
it 'uses US-ASCII as /n encoding if all chars are 7-bit' do it 'uses US-ASCII as /n encoding if all chars are 7-bit' do
/./n.encoding.should == Encoding::US_ASCII /./n.encoding.should == Encoding::US_ASCII
end end
@ -117,6 +121,19 @@ describe "Regexps with encoding modifiers" do
-> { /\A[[:space:]]*\z/ =~ " ".encode("UTF-16LE") }.should raise_error(Encoding::CompatibilityError) -> { /\A[[:space:]]*\z/ =~ " ".encode("UTF-16LE") }.should raise_error(Encoding::CompatibilityError)
end end
it "raises Encoding::CompatibilityError when the regexp has a fixed, non-ASCII-compatible encoding" do
-> { Regexp.new("".force_encoding("UTF-16LE"), Regexp::FIXEDENCODING) =~ " ".encode("UTF-8") }.should raise_error(Encoding::CompatibilityError)
end
it "raises Encoding::CompatibilityError when the regexp has a fixed encoding and the match string has non-ASCII characters" do
-> { Regexp.new("".force_encoding("US-ASCII"), Regexp::FIXEDENCODING) =~ "\303\251".force_encoding('UTF-8') }.should raise_error(Encoding::CompatibilityError)
end
it "raises ArgumentError when trying to match a broken String" do
s = "\x80".force_encoding('UTF-8')
-> { s =~ /./ }.should raise_error(ArgumentError, "invalid byte sequence in UTF-8")
end
it "computes the Regexp Encoding for each interpolated Regexp instance" do it "computes the Regexp Encoding for each interpolated Regexp instance" do
make_regexp = -> str { /#{str}/ } make_regexp = -> str { /#{str}/ }

View File

@ -10,6 +10,12 @@ describe "Fiber#resume" do
fiber2.resume fiber2.resume
-> { fiber2.resume }.should raise_error(FiberError) -> { fiber2.resume }.should raise_error(FiberError)
end end
it "raises a FiberError if the Fiber attempts to resume a resuming fiber" do
root_fiber = Fiber.current
fiber1 = Fiber.new { root_fiber.resume }
-> { fiber1.resume }.should raise_error(FiberError, /double resume/)
end
end end
ruby_version_is '3.0' do ruby_version_is '3.0' do
@ -19,5 +25,11 @@ describe "Fiber#resume" do
fiber2.resume.should == 10 fiber2.resume.should == 10
fiber2.resume.should == 20 fiber2.resume.should == 20
end end
it "raises a FiberError if the Fiber attempts to resume a resuming fiber" do
root_fiber = Fiber.current
fiber1 = Fiber.new { root_fiber.resume }
-> { fiber1.resume }.should raise_error(FiberError, /attempt to resume a resuming fiber/)
end
end end
end end

View File

@ -12,23 +12,23 @@ ruby_version_is ''...'3.0' do
@son.attributes["name"] = "Fred" @son.attributes["name"] = "Fred"
@document.root << @father @document.root << @father
@document.root << @son @document.root << @son
@childs = [] @children = []
end end
it "returns childs with attribute" do it "returns children with attribute" do
@document.each_element_with_attribute("name") { |elem| @childs << elem } @document.each_element_with_attribute("name") { |elem| @children << elem }
@childs[0].should == @father @children[0].should == @father
@childs[1].should == @son @children[1].should == @son
end end
it "takes attribute value as second argument" do it "takes attribute value as second argument" do
@document.each_element_with_attribute("name", "Fred"){ |elem| elem.should == @son } @document.each_element_with_attribute("name", "Fred"){ |elem| elem.should == @son }
end end
it "takes max number of childs as third argument" do it "takes max number of children as third argument" do
@document.each_element_with_attribute("name", nil, 1) { |elem| @childs << elem } @document.each_element_with_attribute("name", nil, 1) { |elem| @children << elem }
@childs.size.should == 1 @children.size.should == 1
@childs[0].should == @father @children[0].should == @father
end end
it "takes XPath filter as fourth argument" do it "takes XPath filter as fourth argument" do

View File

@ -16,10 +16,10 @@ ruby_version_is ''...'3.0' do
@document.root << @joe @document.root << @joe
@document.root << @fred @document.root << @fred
@document.root << @another @document.root << @another
@childs = [] @children = []
end end
it "returns childs with text" do it "returns children with text" do
@document.each_element_with_text("Joe"){|c| c.should == @joe} @document.each_element_with_text("Joe"){|c| c.should == @joe}
end end

View File

@ -11,7 +11,7 @@ ruby_version_is ''...'3.0' do
e.has_text?.should be_true e.has_text?.should be_true
end end
it "returns false if it has no Text childs" do it "returns false if it has no Text children" do
e = REXML::Element.new("Person") e = REXML::Element.new("Person")
e.has_text?.should be_false e.has_text?.should be_false
end end

View File

@ -14,7 +14,7 @@ ruby_version_is ''...'3.0' do
e.should.parent? e.should.parent?
end end
# This includes attributes, CDatas and declarations. # This includes attributes, CData and declarations.
it "returns false for Texts" do it "returns false for Texts" do
e = REXML::Text.new("foo") e = REXML::Text.new("foo")
e.should_not.parent? e.should_not.parent?

View File

@ -19,7 +19,7 @@ describe :rexml_elements_to_a, shared: true do
# always needs the first param (even if it's nil). # always needs the first param (even if it's nil).
# A patch was submitted: # A patch was submitted:
# http://rubyforge.org/tracker/index.php?func=detail&aid=19354&group_id=426&atid=1698 # http://rubyforge.org/tracker/index.php?func=detail&aid=19354&group_id=426&atid=1698
it "returns all childs if xpath is nil" do it "returns all children if xpath is nil" do
@e.elements.send(@method).should == [@first, @second] @e.elements.send(@method).should == [@first, @second]
end end

View File

@ -12,7 +12,7 @@ describe "StringIO#ungetbyte" do
io.string.should == 'Shis is a simple string.' io.string.should == 'Shis is a simple string.'
end end
it "ungets a single byte from a string in the middle of a multibyte characte" do it "ungets a single byte from a string in the middle of a multibyte character" do
str = "\u01a9" str = "\u01a9"
io = StringIO.new(str) io = StringIO.new(str)
b = io.getbyte b = io.getbyte

View File

@ -12,4 +12,10 @@ describe "StringScanner#check_until" do
@s.matched.should == "a" @s.matched.should == "a"
@s.check_until(/test/).should == "This is a test" @s.check_until(/test/).should == "This is a test"
end end
it "raises TypeError if given a String" do
-> {
@s.check_until('T')
}.should raise_error(TypeError, 'wrong argument type String (expected Regexp)')
end
end end

View File

@ -21,4 +21,10 @@ describe "StringScanner#exist?" do
@s.scan(/This is/) @s.scan(/This is/)
@s.exist?(/i/).should == nil @s.exist?(/i/).should == nil
end end
it "raises TypeError if given a String" do
-> {
@s.exist?('T')
}.should raise_error(TypeError, 'wrong argument type String (expected Regexp)')
end
end end

View File

@ -1,7 +1,24 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'shared/matched_size'
require 'strscan' require 'strscan'
describe "StringScanner#matched_size" do describe "StringScanner#matched_size" do
it_behaves_like :strscan_matched_size, :matched_size before :each do
@s = StringScanner.new("This is a test")
end
it "returns the size of the most recent match" do
@s.check(/This/)
@s.matched_size.should == 4
@s.matched_size.should == 4
@s.scan(//)
@s.matched_size.should == 0
end
it "returns nil if there was no recent match" do
@s.matched_size.should == nil
@s.check(/\d+/)
@s.matched_size.should == nil
@s.terminate
@s.matched_size.should == nil
end
end end

View File

@ -20,4 +20,10 @@ describe "StringScanner#scan_until" do
@s.scan(/T/) @s.scan(/T/)
@s.scan_until(/^h/).should == "h" @s.scan_until(/^h/).should == "h"
end end
it "raises TypeError if given a String" do
-> {
@s.scan_until('T')
}.should raise_error(TypeError, 'wrong argument type String (expected Regexp)')
end
end end

View File

@ -27,4 +27,10 @@ describe "StringScanner#search_full" do
@s.search_full(/This/, true, true).should == "This" @s.search_full(/This/, true, true).should == "This"
@s.pos.should == 4 @s.pos.should == 4
end end
it "raises TypeError if given a String" do
-> {
@s.search_full('T', true, true)
}.should raise_error(TypeError, 'wrong argument type String (expected Regexp)')
end
end end

View File

@ -1,21 +0,0 @@
describe :strscan_matched_size, shared: true do
before :each do
@s = StringScanner.new("This is a test")
end
it "returns the size of the most recent match" do
@s.check(/This/)
@s.send(@method).should == 4
@s.send(@method).should == 4
@s.scan(//)
@s.send(@method).should == 0
end
it "returns nil if there was no recent match" do
@s.send(@method).should == nil
@s.check(/\d+/)
@s.send(@method).should == nil
@s.terminate
@s.send(@method).should == nil
end
end

View File

@ -0,0 +1,17 @@
require_relative '../../spec_helper'
require 'strscan'
describe "StringScanner#size" do
before :each do
@s = StringScanner.new("This is a test")
end
it "returns the number of captures groups of the last match" do
@s.scan(/(.)(.)(.)/)
@s.size.should == 4
end
it "returns nil if there is no last match" do
@s.size.should == nil
end
end

View File

@ -15,4 +15,10 @@ describe "StringScanner#skip_until" do
it "returns nil if no match was found" do it "returns nil if no match was found" do
@s.skip_until(/d+/).should == nil @s.skip_until(/d+/).should == nil
end end
it "raises TypeError if given a String" do
-> {
@s.skip_until('T')
}.should raise_error(TypeError, 'wrong argument type String (expected Regexp)')
end
end end

View File

@ -12,6 +12,7 @@ autoload :ClassIdUnderAutoload, "#{object_path}/class_id_under_autoload_spec"
describe :rb_path_to_class, shared: true do describe :rb_path_to_class, shared: true do
it "returns a class or module from a scoped String" do it "returns a class or module from a scoped String" do
@s.send(@method, "CApiClassSpecs::A::B").should equal(CApiClassSpecs::A::B) @s.send(@method, "CApiClassSpecs::A::B").should equal(CApiClassSpecs::A::B)
@s.send(@method, "CApiClassSpecs::A::M").should equal(CApiClassSpecs::A::M)
end end
it "resolves autoload constants" do it "resolves autoload constants" do
@ -27,7 +28,9 @@ describe :rb_path_to_class, shared: true do
end end
it "raises a TypeError if the constant is not a class or module" do it "raises a TypeError if the constant is not a class or module" do
-> { @s.send(@method, "CApiClassSpecs::A::C") }.should raise_error(TypeError) -> {
@s.send(@method, "CApiClassSpecs::A::C")
}.should raise_error(TypeError, 'CApiClassSpecs::A::C does not refer to class/module')
end end
it "raises an ArgumentError even if a constant in the path exists on toplevel" do it "raises an ArgumentError even if a constant in the path exists on toplevel" do

View File

@ -577,6 +577,14 @@ static VALUE string_spec_rb_str_catf(VALUE self, VALUE mesg) {
return rb_str_catf(mesg, "fmt %d %d number", 41, 6); return rb_str_catf(mesg, "fmt %d %d number", 41, 6);
} }
static VALUE string_spec_rb_str_locktmp(VALUE self, VALUE str) {
return rb_str_locktmp(str);
}
static VALUE string_spec_rb_str_unlocktmp(VALUE self, VALUE str) {
return rb_str_unlocktmp(str);
}
void Init_string_spec(void) { void Init_string_spec(void) {
VALUE cls = rb_define_class("CApiStringSpecs", rb_cObject); VALUE cls = rb_define_class("CApiStringSpecs", rb_cObject);
rb_define_method(cls, "rb_cstr2inum", string_spec_rb_cstr2inum, 2); rb_define_method(cls, "rb_cstr2inum", string_spec_rb_cstr2inum, 2);
@ -672,6 +680,8 @@ void Init_string_spec(void) {
rb_define_method(cls, "rb_utf8_str_new_cstr", string_spec_rb_utf8_str_new_cstr, 0); rb_define_method(cls, "rb_utf8_str_new_cstr", string_spec_rb_utf8_str_new_cstr, 0);
rb_define_method(cls, "rb_str_vcatf", string_spec_rb_str_vcatf, 1); rb_define_method(cls, "rb_str_vcatf", string_spec_rb_str_vcatf, 1);
rb_define_method(cls, "rb_str_catf", string_spec_rb_str_catf, 1); rb_define_method(cls, "rb_str_catf", string_spec_rb_str_catf, 1);
rb_define_method(cls, "rb_str_locktmp", string_spec_rb_str_locktmp, 1);
rb_define_method(cls, "rb_str_unlocktmp", string_spec_rb_str_unlocktmp, 1);
} }
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -87,5 +87,8 @@ class CApiClassSpecs
class B class B
end end
module M
end
end end
end end

View File

@ -1209,4 +1209,31 @@ end
str.should == "test fmt 41 6 number" str.should == "test fmt 41 6 number"
end end
end end
describe "rb_str_locktmp" do
it "raises an error when trying to lock an already locked string" do
str = "test"
@s.rb_str_locktmp(str).should == str
-> { @s.rb_str_locktmp(str) }.should raise_error(RuntimeError, 'temporal locking already locked string')
end
it "locks a string so that modifications would raise an error" do
str = "test"
@s.rb_str_locktmp(str).should == str
-> { str.upcase! }.should raise_error(RuntimeError, 'can\'t modify string; temporarily locked')
end
end
describe "rb_str_unlocktmp" do
it "unlocks a locked string" do
str = "test"
@s.rb_str_locktmp(str)
@s.rb_str_unlocktmp(str).should == str
str.upcase!.should == "TEST"
end
it "raises an error when trying to unlock an already unlocked string" do
-> { @s.rb_str_unlocktmp("test") }.should raise_error(RuntimeError, 'temporal unlocking already unlocked string')
end
end
end end