mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Update to ruby/spec@d01709f
This commit is contained in:
parent
a319d3cfdc
commit
4ee1a68776
32 changed files with 471 additions and 50 deletions
|
@ -175,9 +175,10 @@ end
|
|||
|
||||
#### Guard for bug
|
||||
|
||||
In case there is a bug in MRI but the expected behavior is obvious.
|
||||
In case there is a bug in MRI and the fix will be backported to previous versions.
|
||||
If it is not backported or not likely, use `ruby_version_is` instead.
|
||||
First, file a bug at https://bugs.ruby-lang.org/.
|
||||
It is better to use a `ruby_version_is` guard if there was a release with the fix.
|
||||
The problem is `ruby_bug` would make non-MRI implementations fail this spec while MRI itself does not pass it, so it should only be used if the bug is/will be fixed and backported.
|
||||
|
||||
```ruby
|
||||
ruby_bug '#13669', ''...'3.2' do
|
||||
|
|
|
@ -144,10 +144,9 @@ The file `/etc/services` is required for socket specs (package `netbase` on Debi
|
|||
|
||||
### Socket specs from rubysl-socket
|
||||
|
||||
Most specs under `library/socket` were imported from [the rubysl-socket project](https://github.com/rubysl/rubysl-socket).
|
||||
Most specs under `library/socket` were imported from the rubysl-socket project (which is no longer on GitHub).
|
||||
The 3 copyright holders of rubysl-socket, Yorick Peterse, Chuck Remes and
|
||||
Brian Shirai, [agreed to relicense those specs](https://github.com/rubysl/rubysl-socket/issues/15)
|
||||
under the MIT license in ruby/spec.
|
||||
Brian Shirai, agreed to relicense those specs under the MIT license in ruby/spec.
|
||||
|
||||
### History and RubySpec
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ module DirSpecs
|
|||
|
||||
special/test{1}/file[1]
|
||||
special/{}/special
|
||||
special/test\ +()[]{}/hello_world.erb
|
||||
]
|
||||
|
||||
platform_is_not :windows do
|
||||
|
|
|
@ -79,6 +79,7 @@ describe "Dir.glob" do
|
|||
nested/
|
||||
nested/.dotsubir/
|
||||
special/
|
||||
special/test\ +()[]{}/
|
||||
special/test{1}/
|
||||
special/{}/
|
||||
subdir_one/
|
||||
|
@ -130,6 +131,7 @@ describe "Dir.glob" do
|
|||
./nested/
|
||||
./nested/.dotsubir/
|
||||
./special/
|
||||
./special/test\ +()[]{}/
|
||||
./special/test{1}/
|
||||
./special/{}/
|
||||
./subdir_one/
|
||||
|
|
|
@ -111,6 +111,10 @@ describe :dir_glob, shared: true do
|
|||
it "matches files with backslashes in their name" do
|
||||
Dir.glob('special/\\\\{a,b}').should == ['special/\a']
|
||||
end
|
||||
|
||||
it "matches directory with special characters in their name in complex patterns" do
|
||||
Dir.glob("special/test +()\\[\\]\\{\\}/hello_world{.{en},}{.{html},}{+{phone},}{.{erb},}").should == ['special/test +()[]{}/hello_world.erb']
|
||||
end
|
||||
end
|
||||
|
||||
it "matches regexp special ^" do
|
||||
|
@ -225,6 +229,7 @@ describe :dir_glob, shared: true do
|
|||
dir/
|
||||
nested/
|
||||
special/
|
||||
special/test\ +()[]{}/
|
||||
special/test{1}/
|
||||
special/{}/
|
||||
subdir_one/
|
||||
|
|
|
@ -7,6 +7,18 @@ module IOSpecs
|
|||
class SubIO < IO
|
||||
end
|
||||
|
||||
class SubIOWithRedefinedNew < IO
|
||||
def self.new(...)
|
||||
ScratchPad << :redefined_new_called
|
||||
super
|
||||
end
|
||||
|
||||
def initialize(...)
|
||||
ScratchPad << :call_original_initialize
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def self.collector
|
||||
Proc.new { |x| ScratchPad << x }
|
||||
end
|
||||
|
|
|
@ -25,6 +25,17 @@ describe "IO.pipe" do
|
|||
@r.should be_an_instance_of(IOSpecs::SubIO)
|
||||
@w.should be_an_instance_of(IOSpecs::SubIO)
|
||||
end
|
||||
|
||||
it "does not use IO.new method to create pipes and allows its overriding" do
|
||||
ScratchPad.record []
|
||||
|
||||
# so redefined .new is not called, but original #initialize is
|
||||
@r, @w = IOSpecs::SubIOWithRedefinedNew.pipe
|
||||
ScratchPad.recorded.should == [:call_original_initialize, :call_original_initialize] # called 2 times - for each pipe (r and w)
|
||||
|
||||
@r.should be_an_instance_of(IOSpecs::SubIOWithRedefinedNew)
|
||||
@w.should be_an_instance_of(IOSpecs::SubIOWithRedefinedNew)
|
||||
end
|
||||
end
|
||||
|
||||
describe "IO.pipe" do
|
||||
|
|
|
@ -293,13 +293,13 @@ describe :kernel_sprintf, shared: true do
|
|||
it "raises ArgumentError if argument is a string of several characters" do
|
||||
-> {
|
||||
@method.call("%c", "abc")
|
||||
}.should raise_error(ArgumentError)
|
||||
}.should raise_error(ArgumentError, /%c requires a character/)
|
||||
end
|
||||
|
||||
it "raises ArgumentError if argument is an empty string" do
|
||||
-> {
|
||||
@method.call("%c", "")
|
||||
}.should raise_error(ArgumentError)
|
||||
}.should raise_error(ArgumentError, /%c requires a character/)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -313,9 +313,56 @@ describe :kernel_sprintf, shared: true do
|
|||
end
|
||||
end
|
||||
|
||||
it "supports Unicode characters" do
|
||||
@method.call("%c", 1286).should == "Ԇ"
|
||||
@method.call("%c", "ش").should == "ش"
|
||||
it "raises TypeError if argument is not String or Integer and cannot be converted to them" do
|
||||
-> {
|
||||
@method.call("%c", [])
|
||||
}.should raise_error(TypeError, /no implicit conversion of Array into Integer/)
|
||||
end
|
||||
|
||||
it "raises TypeError if argument is nil" do
|
||||
-> {
|
||||
@method.call("%c", nil)
|
||||
}.should raise_error(TypeError, /no implicit conversion from nil to integer/)
|
||||
end
|
||||
|
||||
it "tries to convert argument to String with to_str" do
|
||||
obj = BasicObject.new
|
||||
def obj.to_str
|
||||
"a"
|
||||
end
|
||||
|
||||
@method.call("%c", obj).should == "a"
|
||||
end
|
||||
|
||||
it "tries to convert argument to Integer with to_int" do
|
||||
obj = BasicObject.new
|
||||
def obj.to_int
|
||||
90
|
||||
end
|
||||
|
||||
@method.call("%c", obj).should == "Z"
|
||||
end
|
||||
|
||||
it "raises TypeError if converting to String with to_str returns non-String" do
|
||||
obj = BasicObject.new
|
||||
def obj.to_str
|
||||
:foo
|
||||
end
|
||||
|
||||
-> {
|
||||
@method.call("%c", obj)
|
||||
}.should raise_error(TypeError, /can't convert BasicObject to String/)
|
||||
end
|
||||
|
||||
it "raises TypeError if converting to Integer with to_int returns non-Integer" do
|
||||
obj = BasicObject.new
|
||||
def obj.to_str
|
||||
:foo
|
||||
end
|
||||
|
||||
-> {
|
||||
@method.call("%c", obj)
|
||||
}.should raise_error(TypeError, /can't convert BasicObject to String/)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -374,11 +421,11 @@ describe :kernel_sprintf, shared: true do
|
|||
@method.call("%4.6s", "abcdefg").should == "abcdef"
|
||||
end
|
||||
|
||||
it "formats nli with width" do
|
||||
it "formats nil with width" do
|
||||
@method.call("%6s", nil).should == " "
|
||||
end
|
||||
|
||||
it "formats nli with precision" do
|
||||
it "formats nil with precision" do
|
||||
@method.call("%.6s", nil).should == ""
|
||||
end
|
||||
|
||||
|
@ -939,4 +986,8 @@ describe :kernel_sprintf, shared: true do
|
|||
}
|
||||
end
|
||||
end
|
||||
|
||||
it "does not raise error when passed more arguments than needed" do
|
||||
sprintf("%s %d %c", "string", 2, "c", []).should == "string 2 c"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# Keep encoding-related specs in a separate shared example to be able to skip them in IO/File/StringIO specs.
|
||||
# It's difficult to check result's encoding in the test after writing to a file/io buffer.
|
||||
describe :kernel_sprintf_encoding, shared: true do
|
||||
it "can produce a string with valid encoding" do
|
||||
string = @method.call("good day %{valid}", valid: "e")
|
||||
|
@ -25,7 +27,7 @@ describe :kernel_sprintf_encoding, shared: true do
|
|||
result.encoding.should equal(Encoding::UTF_8)
|
||||
end
|
||||
|
||||
it "raises Encoding::CompatibilityError if both encodings are ASCII compatible and there ano not ASCII characters" do
|
||||
it "raises Encoding::CompatibilityError if both encodings are ASCII compatible and there are not ASCII characters" do
|
||||
string = "Ä %s".encode('windows-1252')
|
||||
argument = "Ђ".encode('windows-1251')
|
||||
|
||||
|
@ -33,4 +35,33 @@ describe :kernel_sprintf_encoding, shared: true do
|
|||
@method.call(string, argument)
|
||||
}.should raise_error(Encoding::CompatibilityError)
|
||||
end
|
||||
|
||||
describe "%c" do
|
||||
it "supports Unicode characters" do
|
||||
result = @method.call("%c", 1286)
|
||||
result.should == "Ԇ"
|
||||
result.bytes.should == [212, 134]
|
||||
|
||||
result = @method.call("%c", "ش")
|
||||
result.should == "ش"
|
||||
result.bytes.should == [216, 180]
|
||||
end
|
||||
|
||||
it "raises error when a codepoint isn't representable in an encoding of a format string" do
|
||||
format = "%c".encode("ASCII")
|
||||
|
||||
-> {
|
||||
@method.call(format, 1286)
|
||||
}.should raise_error(RangeError, /out of char range/)
|
||||
end
|
||||
|
||||
it "uses the encoding of the format string to interpret codepoints" do
|
||||
format = "%c".force_encoding("euc-jp")
|
||||
result = @method.call(format, 9415601)
|
||||
|
||||
result.encoding.should == Encoding::EUC_JP
|
||||
result.should == "é".encode(Encoding::EUC_JP)
|
||||
result.bytes.should == [143, 171, 177]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,3 +13,7 @@ end
|
|||
describe "Regexp.compile given a Regexp" do
|
||||
it_behaves_like :regexp_new_regexp, :compile
|
||||
end
|
||||
|
||||
describe "Regexp.new given a non-String/Regexp" do
|
||||
it_behaves_like :regexp_new_non_string_or_regexp, :compile
|
||||
end
|
||||
|
|
|
@ -11,17 +11,9 @@ end
|
|||
|
||||
describe "Regexp.new given a Regexp" do
|
||||
it_behaves_like :regexp_new_regexp, :new
|
||||
it_behaves_like :regexp_new_string_binary, :compile
|
||||
it_behaves_like :regexp_new_string_binary, :new
|
||||
end
|
||||
|
||||
describe "Regexp.new given an Integer" do
|
||||
it "raises a TypeError" do
|
||||
-> { Regexp.new(1) }.should raise_error(TypeError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Regexp.new given a Float" do
|
||||
it "raises a TypeError" do
|
||||
-> { Regexp.new(1.0) }.should raise_error(TypeError)
|
||||
end
|
||||
describe "Regexp.new given a non-String/Regexp" do
|
||||
it_behaves_like :regexp_new_non_string_or_regexp, :new
|
||||
end
|
||||
|
|
|
@ -24,6 +24,32 @@ describe :regexp_new, shared: true do
|
|||
end
|
||||
end
|
||||
|
||||
describe :regexp_new_non_string_or_regexp, shared: true do
|
||||
it "calls #to_str method for non-String/Regexp argument" do
|
||||
obj = Object.new
|
||||
def obj.to_str() "a" end
|
||||
|
||||
Regexp.send(@method, obj).should == /a/
|
||||
end
|
||||
|
||||
it "raises TypeError if there is no #to_str method for non-String/Regexp argument" do
|
||||
obj = Object.new
|
||||
-> { Regexp.send(@method, obj) }.should raise_error(TypeError, "no implicit conversion of Object into String")
|
||||
|
||||
-> { Regexp.send(@method, 1) }.should raise_error(TypeError, "no implicit conversion of Integer into String")
|
||||
-> { Regexp.send(@method, 1.0) }.should raise_error(TypeError, "no implicit conversion of Float into String")
|
||||
-> { Regexp.send(@method, :symbol) }.should raise_error(TypeError, "no implicit conversion of Symbol into String")
|
||||
-> { Regexp.send(@method, []) }.should raise_error(TypeError, "no implicit conversion of Array into String")
|
||||
end
|
||||
|
||||
it "raises TypeError if #to_str returns non-String value" do
|
||||
obj = Object.new
|
||||
def obj.to_str() [] end
|
||||
|
||||
-> { Regexp.send(@method, obj) }.should raise_error(TypeError, /can't convert Object to String/)
|
||||
end
|
||||
end
|
||||
|
||||
describe :regexp_new_string, shared: true do
|
||||
it "uses the String argument as an unescaped literal to construct a Regexp object" do
|
||||
Regexp.send(@method, "^hi{2,3}fo.o$").should == /^hi{2,3}fo.o$/
|
||||
|
@ -97,6 +123,16 @@ describe :regexp_new_string, shared: true do
|
|||
(r.options & Regexp::EXTENDED).should_not == 0
|
||||
end
|
||||
|
||||
it "does not try to convert the second argument to Integer with #to_int method call" do
|
||||
ScratchPad.clear
|
||||
obj = Object.new
|
||||
def obj.to_int() ScratchPad.record(:called) end
|
||||
|
||||
Regexp.send(@method, "Hi", obj)
|
||||
|
||||
ScratchPad.recorded.should == nil
|
||||
end
|
||||
|
||||
ruby_version_is ""..."3.2" do
|
||||
it "treats any non-Integer, non-nil, non-false second argument as IGNORECASE" do
|
||||
r = Regexp.send(@method, 'Hi', Object.new)
|
||||
|
|
|
@ -9,10 +9,10 @@ describe :time_local, shared: true do
|
|||
=begin
|
||||
platform_is_not :windows do
|
||||
describe "timezone changes" do
|
||||
it "correctly adjusts the timezone change to 'CEST' on 'Europe/Amsterdam'" do
|
||||
it "correctly adjusts the timezone change to 'CET' on 'Europe/Amsterdam'" do
|
||||
with_timezone("Europe/Amsterdam") do
|
||||
Time.send(@method, 1940, 5, 16).to_a.should ==
|
||||
[0, 40, 1, 16, 5, 1940, 4, 137, true, "CEST"]
|
||||
Time.send(@method, 1970, 5, 16).to_a.should ==
|
||||
[0, 0, 0, 16, 5, 1970, 6, 136, false, "CET"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -983,3 +983,77 @@ describe "Post-args" do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Anonymous block forwarding" do
|
||||
ruby_version_is "3.1" do
|
||||
it "forwards blocks to other functions that formally declare anonymous blocks" do
|
||||
eval <<-EOF
|
||||
def b(&); c(&) end
|
||||
def c(&); yield :non_null end
|
||||
EOF
|
||||
|
||||
b { |c| c }.should == :non_null
|
||||
end
|
||||
|
||||
it "requires the anonymous block parameter to be declared if directly passing a block" do
|
||||
-> { eval "def a; b(&); end; def b; end" }.should raise_error(SyntaxError)
|
||||
end
|
||||
|
||||
it "works when it's the only declared parameter" do
|
||||
eval <<-EOF
|
||||
def inner; yield end
|
||||
def block_only(&); inner(&) end
|
||||
EOF
|
||||
|
||||
block_only { 1 }.should == 1
|
||||
end
|
||||
|
||||
it "works alongside positional parameters" do
|
||||
eval <<-EOF
|
||||
def inner; yield end
|
||||
def pos(arg1, &); inner(&) end
|
||||
EOF
|
||||
|
||||
pos(:a) { 1 }.should == 1
|
||||
end
|
||||
|
||||
it "works alongside positional arguments and splatted keyword arguments" do
|
||||
eval <<-EOF
|
||||
def inner; yield end
|
||||
def pos_kwrest(arg1, **kw, &); inner(&) end
|
||||
EOF
|
||||
|
||||
pos_kwrest(:a, arg: 3) { 1 }.should == 1
|
||||
end
|
||||
|
||||
it "works alongside positional arguments and disallowed keyword arguments" do
|
||||
eval <<-EOF
|
||||
def inner; yield end
|
||||
def no_kw(arg1, **nil, &); inner(&) end
|
||||
EOF
|
||||
|
||||
no_kw(:a) { 1 }.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.2" do
|
||||
it "works alongside explicit keyword arguments" do
|
||||
eval <<-EOF
|
||||
def inner; yield end
|
||||
def rest_kw(*a, kwarg: 1, &); inner(&) end
|
||||
def kw(kwarg: 1, &); inner(&) end
|
||||
def pos_kw_kwrest(arg1, kwarg: 1, **kw, &); inner(&) end
|
||||
def pos_rkw(arg1, kwarg1:, &); inner(&) end
|
||||
def all(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, &); inner(&) end
|
||||
def all_kwrest(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, **kw, &); inner(&) end
|
||||
EOF
|
||||
|
||||
rest_kw { 1 }.should == 1
|
||||
kw { 1 }.should == 1
|
||||
pos_kw_kwrest(:a) { 1 }.should == 1
|
||||
pos_rkw(:a, kwarg1: 3) { 1 }.should == 1
|
||||
all(:a, :b, :c, :d, :e, okw1: 'x', okw2: 'y') { 1 }.should == 1
|
||||
all_kwrest(:a, :b, :c, :d, :e, okw1: 'x', okw2: 'y') { 1 }.should == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -58,6 +58,24 @@ ruby_version_is "3.0" do
|
|||
m(kw: 1).should == []
|
||||
-> { m(kw: 1, kw2: 2) }.should raise_error(ArgumentError, 'unknown keyword: :kw2')
|
||||
-> { m(kw: 1, true => false) }.should raise_error(ArgumentError, 'unknown keyword: true')
|
||||
-> { m(kw: 1, a: 1, b: 2, c: 3) }.should raise_error(ArgumentError, 'unknown keywords: :a, :b, :c')
|
||||
end
|
||||
|
||||
it "raises ArgumentError exception when required keyword argument is not passed" do
|
||||
def m(a:, b:, c:)
|
||||
[a, b, c]
|
||||
end
|
||||
|
||||
-> { m(a: 1, b: 2) }.should raise_error(ArgumentError, /missing keyword: :c/)
|
||||
-> { m() }.should raise_error(ArgumentError, /missing keywords: :a, :b, :c/)
|
||||
end
|
||||
|
||||
it "raises ArgumentError for missing keyword arguments even if there are extra ones" do
|
||||
def m(a:)
|
||||
a
|
||||
end
|
||||
|
||||
-> { m(b: 1) }.should raise_error(ArgumentError, /missing keyword: :a/)
|
||||
end
|
||||
|
||||
it "handle * and ** at the same call site" do
|
||||
|
|
|
@ -571,6 +571,13 @@ describe "A method" do
|
|||
end
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
def m(a:, **kw) [a, kw] end
|
||||
ruby
|
||||
|
||||
-> { m(b: 1) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
evaluate <<-ruby do
|
||||
def m(a: 1) a end
|
||||
ruby
|
||||
|
|
|
@ -237,4 +237,11 @@ describe "A Proc" do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "taking |required keyword arguments, **kw| arguments" do
|
||||
it "raises ArgumentError for missing required argument" do
|
||||
p = proc { |a:, **kw| [a, kw] }
|
||||
-> { p.call() }.should raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,4 +13,16 @@ describe "BigDecimal#to_r" do
|
|||
r.denominator.should eql(1000000000000000000000000)
|
||||
end
|
||||
|
||||
it "returns a Rational from a BigDecimal with an exponent" do
|
||||
r = BigDecimal("1E2").to_r
|
||||
r.numerator.should eql(100)
|
||||
r.denominator.should eql(1)
|
||||
end
|
||||
|
||||
it "returns a Rational from a negative BigDecimal with an exponent" do
|
||||
r = BigDecimal("-1E2").to_r
|
||||
r.numerator.should eql(-100)
|
||||
r.denominator.should eql(1)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -167,10 +167,14 @@ describe "StringIO.open when passed [Object]" do
|
|||
io.should equal(ret)
|
||||
end
|
||||
|
||||
it "sets the mode to read-write" do
|
||||
it "sets the mode to read-write (r+)" do
|
||||
io = StringIO.open("example")
|
||||
io.closed_read?.should be_false
|
||||
io.closed_write?.should be_false
|
||||
|
||||
io = StringIO.new("example")
|
||||
io.printf("%d", 123)
|
||||
io.string.should == "123mple"
|
||||
end
|
||||
|
||||
it "tries to convert the passed Object to a String using #to_str" do
|
||||
|
@ -195,10 +199,14 @@ describe "StringIO.open when passed no arguments" do
|
|||
io.should equal(ret)
|
||||
end
|
||||
|
||||
it "sets the mode to read-write" do
|
||||
it "sets the mode to read-write (r+)" do
|
||||
io = StringIO.open
|
||||
io.closed_read?.should be_false
|
||||
io.closed_write?.should be_false
|
||||
|
||||
io = StringIO.new("example")
|
||||
io.printf("%d", 123)
|
||||
io.string.should == "123mple"
|
||||
end
|
||||
|
||||
it "uses an empty String as the StringIO backend" do
|
||||
|
|
|
@ -4,7 +4,7 @@ require_relative '../../core/kernel/shared/sprintf'
|
|||
|
||||
describe "StringIO#printf" do
|
||||
before :each do
|
||||
@io = StringIO.new('example')
|
||||
@io = StringIO.new()
|
||||
end
|
||||
|
||||
it "returns nil" do
|
||||
|
@ -12,9 +12,9 @@ describe "StringIO#printf" do
|
|||
end
|
||||
|
||||
it "pads self with \\000 when the current position is after the end" do
|
||||
@io.pos = 10
|
||||
@io.pos = 3
|
||||
@io.printf("%d", 123)
|
||||
@io.string.should == "example\000\000\000123"
|
||||
@io.string.should == "\000\000\000123"
|
||||
end
|
||||
|
||||
it "performs format conversion" do
|
||||
|
@ -39,6 +39,27 @@ describe "StringIO#printf" do
|
|||
end
|
||||
end
|
||||
|
||||
describe "StringIO#printf when in read-write mode" do
|
||||
before :each do
|
||||
@io = StringIO.new("example", "r+")
|
||||
end
|
||||
|
||||
it "starts from the beginning" do
|
||||
@io.printf("%s", "abcdefghijk")
|
||||
@io.string.should == "abcdefghijk"
|
||||
end
|
||||
|
||||
it "does not truncate existing string" do
|
||||
@io.printf("%s", "abc")
|
||||
@io.string.should == "abcmple"
|
||||
end
|
||||
|
||||
it "correctly updates self's position" do
|
||||
@io.printf("%s", "abc")
|
||||
@io.pos.should eql(3)
|
||||
end
|
||||
end
|
||||
|
||||
describe "StringIO#printf when in append mode" do
|
||||
before :each do
|
||||
@io = StringIO.new("example", "a")
|
||||
|
|
|
@ -5,10 +5,21 @@ require_relative 'shared/sysread'
|
|||
|
||||
describe "StringIO#read_nonblock when passed length, buffer" do
|
||||
it_behaves_like :stringio_read, :read_nonblock
|
||||
|
||||
it "accepts :exception option" do
|
||||
io = StringIO.new("example")
|
||||
io.read_nonblock(3, buffer = "", exception: true)
|
||||
buffer.should == "exa"
|
||||
end
|
||||
end
|
||||
|
||||
describe "StringIO#read_nonblock when passed length" do
|
||||
it_behaves_like :stringio_read_length, :read_nonblock
|
||||
|
||||
it "accepts :exception option" do
|
||||
io = StringIO.new("example")
|
||||
io.read_nonblock(3, exception: true).should == "exa"
|
||||
end
|
||||
end
|
||||
|
||||
describe "StringIO#read_nonblock when passed nil" do
|
||||
|
|
|
@ -8,6 +8,12 @@ end
|
|||
|
||||
describe "StringIO#write_nonblock when passed [String]" do
|
||||
it_behaves_like :stringio_write_string, :write_nonblock
|
||||
|
||||
it "accepts :exception option" do
|
||||
io = StringIO.new("12345", "a")
|
||||
io.write_nonblock("67890", exception: true)
|
||||
io.string.should == "1234567890"
|
||||
end
|
||||
end
|
||||
|
||||
describe "StringIO#write_nonblock when self is not writable" do
|
||||
|
|
|
@ -58,6 +58,11 @@ describe "Zlib::Deflate#deflate" do
|
|||
Array.new(31, 0) +
|
||||
[24, 128, 0, 0, 1]).pack('C*')
|
||||
end
|
||||
|
||||
it "has a binary encoding" do
|
||||
@deflator.deflate("").encoding.should == Encoding::BINARY
|
||||
@deflator.finish.encoding.should == Encoding::BINARY
|
||||
end
|
||||
end
|
||||
|
||||
describe "Zlib::Deflate#deflate" do
|
||||
|
|
|
@ -39,6 +39,13 @@ describe "Zlib::Inflate#inflate" do
|
|||
@inflator.finish.should == 'uncompressed_data'
|
||||
end
|
||||
|
||||
it "has a binary encoding" do
|
||||
data = [120, 156, 99, 96, 128, 1, 0, 0, 10, 0, 1].pack('C*')
|
||||
unzipped = @inflator.inflate data
|
||||
@inflator.finish.encoding.should == Encoding::BINARY
|
||||
unzipped.encoding.should == Encoding::BINARY
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "Zlib::Inflate.inflate" do
|
||||
|
|
|
@ -63,6 +63,48 @@ describe "C-API Encoding function" do
|
|||
end
|
||||
end
|
||||
|
||||
describe "rb_enc_strlen" do
|
||||
before :each do
|
||||
@str = 'こにちわ' # Each codepoint in this string is 3 bytes in UTF-8
|
||||
end
|
||||
|
||||
it "returns the correct string length for the encoding" do
|
||||
@s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_8).should == 4
|
||||
@s.rb_enc_strlen(@str, @str.bytesize, Encoding::BINARY).should == 12
|
||||
end
|
||||
|
||||
it "returns the string length based on a fixed-width encoding's character length, even if the encoding is incompatible" do
|
||||
@s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_16BE).should == 6
|
||||
@s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_16LE).should == 6
|
||||
@s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_32BE).should == 3
|
||||
@s.rb_enc_strlen(@str, @str.bytesize, Encoding::UTF_32LE).should == 3
|
||||
end
|
||||
|
||||
it "does not consider strings to be NUL-terminated" do
|
||||
s = "abc\0def"
|
||||
@s.rb_enc_strlen(s, s.bytesize, Encoding::US_ASCII).should == 7
|
||||
@s.rb_enc_strlen(s, s.bytesize, Encoding::UTF_8).should == 7
|
||||
end
|
||||
|
||||
describe "handles broken strings" do
|
||||
it "combines valid character and invalid character counts in UTF-8" do
|
||||
# The result is 3 because `rb_enc_strlen` counts the first valid character and then adds
|
||||
# the byte count for the invalid character that follows for 1 + 2.
|
||||
@s.rb_enc_strlen(@str, 5, Encoding::UTF_8).should == 3
|
||||
end
|
||||
|
||||
it "combines valid character and invalid character counts in UTF-16" do
|
||||
@s.rb_enc_strlen(@str, 5, Encoding::UTF_16BE).should == 3
|
||||
end
|
||||
|
||||
it "rounds up for fixed-width encodings" do
|
||||
@s.rb_enc_strlen(@str, 7, Encoding::UTF_32BE).should == 2
|
||||
@s.rb_enc_strlen(@str, 7, Encoding::UTF_32LE).should == 2
|
||||
@s.rb_enc_strlen(@str, 5, Encoding::BINARY).should == 5
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "rb_enc_find" do
|
||||
it "returns the encoding of an Encoding" do
|
||||
@s.rb_enc_find("UTF-8").should == "UTF-8"
|
||||
|
|
|
@ -301,6 +301,14 @@ static VALUE encoding_spec_rb_enc_codelen(VALUE self, VALUE code, VALUE encoding
|
|||
return INT2FIX(rb_enc_codelen(c, enc));
|
||||
}
|
||||
|
||||
static VALUE encoding_spec_rb_enc_strlen(VALUE self, VALUE str, VALUE length, VALUE encoding) {
|
||||
int l = FIX2INT(length);
|
||||
char *p = RSTRING_PTR(str);
|
||||
char *e = p + l;
|
||||
|
||||
return LONG2FIX(rb_enc_strlen(p, e, rb_to_encoding(encoding)));
|
||||
}
|
||||
|
||||
void Init_encoding_spec(void) {
|
||||
VALUE cls;
|
||||
native_rb_encoding_pointer = (rb_encoding**) malloc(sizeof(rb_encoding*));
|
||||
|
@ -335,6 +343,7 @@ void Init_encoding_spec(void) {
|
|||
rb_define_method(cls, "rb_enc_compatible", encoding_spec_rb_enc_compatible, 2);
|
||||
rb_define_method(cls, "rb_enc_copy", encoding_spec_rb_enc_copy, 2);
|
||||
rb_define_method(cls, "rb_enc_codelen", encoding_spec_rb_enc_codelen, 2);
|
||||
rb_define_method(cls, "rb_enc_strlen", encoding_spec_rb_enc_strlen, 3);
|
||||
rb_define_method(cls, "rb_enc_find", encoding_spec_rb_enc_find, 1);
|
||||
rb_define_method(cls, "rb_enc_find_index", encoding_spec_rb_enc_find_index, 1);
|
||||
rb_define_method(cls, "rb_enc_isalnum", encoding_spec_rb_enc_isalnum, 2);
|
||||
|
|
|
@ -437,6 +437,12 @@ VALUE string_spec_RSTRING_PTR_read(VALUE self, VALUE str, VALUE path) {
|
|||
return capacities;
|
||||
}
|
||||
|
||||
VALUE string_spec_RSTRING_PTR_null_terminate(VALUE self, VALUE str, VALUE min_length) {
|
||||
char* ptr = RSTRING_PTR(str);
|
||||
char* end = ptr + RSTRING_LEN(str);
|
||||
return rb_str_new(end, FIX2LONG(min_length));
|
||||
}
|
||||
|
||||
VALUE string_spec_StringValue(VALUE self, VALUE str) {
|
||||
return StringValue(str);
|
||||
}
|
||||
|
@ -662,6 +668,7 @@ void Init_string_spec(void) {
|
|||
rb_define_method(cls, "RSTRING_PTR_after_funcall", string_spec_RSTRING_PTR_after_funcall, 2);
|
||||
rb_define_method(cls, "RSTRING_PTR_after_yield", string_spec_RSTRING_PTR_after_yield, 1);
|
||||
rb_define_method(cls, "RSTRING_PTR_read", string_spec_RSTRING_PTR_read, 2);
|
||||
rb_define_method(cls, "RSTRING_PTR_null_terminate", string_spec_RSTRING_PTR_null_terminate, 2);
|
||||
rb_define_method(cls, "StringValue", string_spec_StringValue, 1);
|
||||
rb_define_method(cls, "SafeStringValue", string_spec_SafeStringValue, 1);
|
||||
rb_define_method(cls, "rb_str_hash", string_spec_rb_str_hash, 1);
|
||||
|
|
|
@ -7,15 +7,18 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
VALUE util_spec_rb_scan_args(VALUE self, VALUE argv, VALUE fmt, VALUE expected, VALUE acc) {
|
||||
int i, result, argc = (int)RARRAY_LEN(argv);
|
||||
VALUE args[6], failed, a1, a2, a3, a4, a5, a6;
|
||||
int result, argc;
|
||||
VALUE a1, a2, a3, a4, a5, a6;
|
||||
|
||||
failed = rb_intern("failed");
|
||||
a1 = a2 = a3 = a4 = a5 = a6 = failed;
|
||||
argc = (int) RARRAY_LEN(argv);
|
||||
VALUE* args = RARRAY_PTR(argv);
|
||||
/* the line above can be replaced with this for Ruby implementations which do not support RARRAY_PTR() yet
|
||||
VALUE args[6];
|
||||
for(int i = 0; i < argc; i++) {
|
||||
args[i] = rb_ary_entry(argv, i);
|
||||
} */
|
||||
|
||||
for(i = 0; i < argc; i++) {
|
||||
args[i] = rb_ary_entry(argv, i);
|
||||
}
|
||||
a1 = a2 = a3 = a4 = a5 = a6 = INT2FIX(-1);
|
||||
|
||||
#ifdef RB_SCAN_ARGS_KEYWORDS
|
||||
if (*RSTRING_PTR(fmt) == 'k') {
|
||||
|
|
|
@ -97,6 +97,32 @@ describe "C-API String function" do
|
|||
end
|
||||
end
|
||||
|
||||
describe "rb_str_set_len on a UTF-16 String" do
|
||||
before :each do
|
||||
@str = "abcdefghij".force_encoding(Encoding::UTF_16BE)
|
||||
# Make sure to unshare the string
|
||||
@s.rb_str_modify(@str)
|
||||
end
|
||||
|
||||
it "inserts two NULL bytes at the length" do
|
||||
@s.rb_str_set_len(@str, 4).b.should == "abcd".b
|
||||
@s.rb_str_set_len(@str, 8).b.should == "abcd\x00\x00gh".b
|
||||
end
|
||||
end
|
||||
|
||||
describe "rb_str_set_len on a UTF-32 String" do
|
||||
before :each do
|
||||
@str = "abcdefghijkl".force_encoding(Encoding::UTF_32BE)
|
||||
# Make sure to unshare the string
|
||||
@s.rb_str_modify(@str)
|
||||
end
|
||||
|
||||
it "inserts four NULL bytes at the length" do
|
||||
@s.rb_str_set_len(@str, 4).b.should == "abcd".b
|
||||
@s.rb_str_set_len(@str, 12).b.should == "abcd\x00\x00\x00\x00ijkl".b
|
||||
end
|
||||
end
|
||||
|
||||
describe "rb_str_buf_new" do
|
||||
it "returns the equivalent of an empty string" do
|
||||
buf = @s.rb_str_buf_new(10, nil)
|
||||
|
@ -592,6 +618,12 @@ describe "C-API String function" do
|
|||
capacities[0].should < capacities[1]
|
||||
str.should == "fixture file contents to test read() with RSTRING_PTR"
|
||||
end
|
||||
|
||||
it "terminates the string with at least (encoding min length) \\0 bytes" do
|
||||
@s.RSTRING_PTR_null_terminate("abc", 1).should == "\x00"
|
||||
@s.RSTRING_PTR_null_terminate("abc".encode("UTF-16BE"), 2).should == "\x00\x00"
|
||||
@s.RSTRING_PTR_null_terminate("abc".encode("UTF-32BE"), 4).should == "\x00\x00\x00\x00"
|
||||
end
|
||||
end
|
||||
|
||||
describe "RSTRING_LEN" do
|
||||
|
|
|
@ -15,8 +15,9 @@ describe "C-API Util function" do
|
|||
end
|
||||
|
||||
it "assigns the required arguments scanned" do
|
||||
@o.rb_scan_args([1, 2], "2", 2, @acc).should == 2
|
||||
ScratchPad.recorded.should == [1, 2]
|
||||
obj = Object.new
|
||||
@o.rb_scan_args([obj, 2], "2", 2, @acc).should == 2
|
||||
ScratchPad.recorded.should == [obj, 2]
|
||||
end
|
||||
|
||||
it "raises an ArgumentError if there are insufficient arguments" do
|
||||
|
|
|
@ -5,16 +5,17 @@ require 'rubygems/command_manager'
|
|||
|
||||
describe "CVE-2019-8325 is resisted by" do
|
||||
describe "sanitising error message components" do
|
||||
silent_ui = Module.new do
|
||||
attr_accessor :ui
|
||||
def self.extended(obj)
|
||||
obj.ui = Gem::SilentUI.new
|
||||
end
|
||||
before :each do
|
||||
@ui = Gem::SilentUI.new
|
||||
end
|
||||
|
||||
after :each do
|
||||
@ui.close
|
||||
end
|
||||
|
||||
it "for the 'while executing' message" do
|
||||
manager = Gem::CommandManager.new
|
||||
manager.extend(silent_ui)
|
||||
manager.ui = @ui
|
||||
def manager.process_args(args, build_args)
|
||||
raise StandardError, "\e]2;nyan\a"
|
||||
end
|
||||
|
@ -34,7 +35,7 @@ describe "CVE-2019-8325 is resisted by" do
|
|||
|
||||
it "for the 'loading command' message" do
|
||||
manager = Gem::CommandManager.new
|
||||
manager.extend(silent_ui)
|
||||
manager.ui = @ui
|
||||
def manager.require(x)
|
||||
raise 'foo'
|
||||
end
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
describe :sizedqueue_new, shared: true do
|
||||
it "raises a TypeError when the given argument is not Numeric" do
|
||||
-> { @object.call("foo") }.should raise_error(TypeError)
|
||||
it "raises a TypeError when the given argument doesn't respond to #to_int" do
|
||||
-> { @object.call("12") }.should raise_error(TypeError)
|
||||
-> { @object.call(Object.new) }.should raise_error(TypeError)
|
||||
|
||||
@object.call(12.9).max.should == 12
|
||||
object = Object.new
|
||||
object.define_singleton_method(:to_int) { 42 }
|
||||
@object.call(object).max.should == 42
|
||||
end
|
||||
|
||||
it "raises an argument error when no argument is given" do
|
||||
|
|
Loading…
Reference in a new issue