This commit is contained in:
Andrew Konchin 2024-07-01 15:38:25 +03:00 committed by Benoit Daloze
parent d7af8afe1b
commit cee62c6738
44 changed files with 886 additions and 170 deletions

View File

@ -277,13 +277,13 @@ describe :kernel_sprintf, shared: true do
end
describe "Kernel#sprintf" do
it_behaves_like :kernel_sprintf, -> (format, *args) {
it_behaves_like :kernel_sprintf, -> format, *args {
sprintf(format, *args)
}
end
describe "Kernel.sprintf" do
it_behaves_like :kernel_sprintf, -> (format, *args) {
it_behaves_like :kernel_sprintf, -> format, *args {
Kernel.sprintf(format, *args)
}
end

View File

@ -35,7 +35,7 @@ More precisely, every latest stable MRI release should [pass](https://github.com
### Synchronization with Ruby Implementations
The specs are synchronized both ways around once a month by @eregon between ruby/spec, MRI, JRuby and TruffleRuby,
The specs are synchronized both ways around once a month by @andrykonchin between ruby/spec, MRI, JRuby and TruffleRuby,
using [this script](https://github.com/ruby/mspec/blob/master/tool/sync/sync-rubyspec.rb).
Each of these repositories has a full copy of the specs under `spec/ruby` to ease editing specs.
Any of these repositories can be used to add or edit specs, use what is most convenient for you.

View File

@ -7,7 +7,7 @@ require_relative '../enumerable/shared/enumeratorized'
# Mutating the array while it is being iterated is discouraged as it can result in confusing behavior.
# Yet a Ruby implementation must not crash in such a case, and following the simple CRuby behavior makes sense.
# CRuby simply reads the array storage and checks the size for every iteration;
# like `i = 0; while i < size; yield self[i]; end`
# like `i = 0; while i < size; yield self[i]; i += 1; end`
describe "Array#each" do
it "yields each element to the block" do

View File

@ -21,7 +21,7 @@ describe "Array#+" do
([1, 2, 3] + obj).should == [1, 2, 3, "x", "y"]
end
it "raises a Typeerror if the given argument can't be converted to an array" do
it "raises a TypeError if the given argument can't be converted to an array" do
-> { [1, 2, 3] + nil }.should raise_error(TypeError)
-> { [1, 2, 3] + "abc" }.should raise_error(TypeError)
end

View File

@ -45,6 +45,12 @@ describe "Array#to_h" do
[:a, :b].to_h { |k| [k, k.to_s] }.should == { a: 'a', b: 'b' }
end
it "passes to a block each element as a single argument" do
ScratchPad.record []
[[:a, 1], [:b, 2]].to_h { |*args| ScratchPad << args; [args[0], args[1]] }
ScratchPad.recorded.sort.should == [[[:a, 1]], [[:b, 2]]]
end
it "raises ArgumentError if block returns longer or shorter array" do
-> do
[:a, :b].to_h { |k| [k, k.to_s, 1] }

View File

@ -16,6 +16,23 @@ describe :enumerable_inject, shared: true do
it "can take two argument" do
EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, :-).should == 4
EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, "-").should == 4
[1, 2, 3].send(@method, 10, :-).should == 4
[1, 2, 3].send(@method, 10, "-").should == 4
end
it "converts non-Symbol method name argument to String with #to_str if two arguments" do
name = Object.new
def name.to_str; "-"; end
EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, name).should == 4
[1, 2, 3].send(@method, 10, name).should == 4
end
it "raises TypeError when the second argument is not Symbol or String and it cannot be converted to String if two arguments" do
-> { EnumerableSpecs::Numerous.new(1, 2, 3).send(@method, 10, Object.new) }.should raise_error(TypeError, /is not a symbol nor a string/)
-> { [1, 2, 3].send(@method, 10, Object.new) }.should raise_error(TypeError, /is not a symbol nor a string/)
end
it "ignores the block if two arguments" do
@ -39,6 +56,25 @@ describe :enumerable_inject, shared: true do
it "can take a symbol argument" do
EnumerableSpecs::Numerous.new(10, 1, 2, 3).send(@method, :-).should == 4
[10, 1, 2, 3].send(@method, :-).should == 4
end
it "can take a String argument" do
EnumerableSpecs::Numerous.new(10, 1, 2, 3).send(@method, "-").should == 4
[10, 1, 2, 3].send(@method, "-").should == 4
end
it "converts non-Symbol method name argument to String with #to_str" do
name = Object.new
def name.to_str; "-"; end
EnumerableSpecs::Numerous.new(10, 1, 2, 3).send(@method, name).should == 4
[10, 1, 2, 3].send(@method, name).should == 4
end
it "raises TypeError when passed not Symbol or String method name argument and it cannot be converted to String" do
-> { EnumerableSpecs::Numerous.new(10, 1, 2, 3).send(@method, Object.new) }.should raise_error(TypeError, /is not a symbol nor a string/)
-> { [10, 1, 2, 3].send(@method, Object.new) }.should raise_error(TypeError, /is not a symbol nor a string/)
end
it "without argument takes a block with an accumulator (with first element as initial value) and the current element. Value of block becomes new accumulator" do
@ -77,7 +113,6 @@ describe :enumerable_inject, shared: true do
EnumerableSpecs::EachDefiner.new('a','b','c').send(@method) {|result, i| i+result}.should == "cba"
EnumerableSpecs::EachDefiner.new(3, 4, 5).send(@method) {|result, i| result*i}.should == 60
EnumerableSpecs::EachDefiner.new([1], 2, 'a','b').send(@method){|r,i| r<<i}.should == [1, 2, 'a', 'b']
end
it "returns nil when fails(legacy rubycon)" do

View File

@ -53,6 +53,14 @@ describe "Enumerable#to_h" do
@enum.to_h { |k| [k, k.to_s] }.should == { a: 'a', b: 'b' }
end
it "passes to a block each element as a single argument" do
enum_of_arrays = EnumerableSpecs::EachDefiner.new([:a, 1], [:b, 2])
ScratchPad.record []
enum_of_arrays.to_h { |*args| ScratchPad << args; [args[0], args[1]] }
ScratchPad.recorded.sort.should == [[[:a, 1]], [[:b, 2]]]
end
it "raises ArgumentError if block returns longer or shorter array" do
-> do
@enum.to_h { |k| [k, k.to_s, 1] }

View File

@ -18,6 +18,18 @@ describe "ENV.to_h" do
ENV.to_h { |k, v| [k, v.upcase] }.should == { 'a' => "B", 'c' => "D" }
end
it "passes to a block each pair's key and value as separate arguments" do
ENV.replace("a" => "b", "c" => "d")
ScratchPad.record []
ENV.to_h { |k, v| ScratchPad << [k, v]; [k, v] }
ScratchPad.recorded.sort.should == [["a", "b"], ["c", "d"]]
ScratchPad.record []
ENV.to_h { |*args| ScratchPad << args; [args[0], args[1]] }
ScratchPad.recorded.sort.should == [["a", "b"], ["c", "d"]]
end
it "does not require the array elements to be strings" do
ENV.replace("a" => "b", "c" => "d")
ENV.to_h { |k, v| [k.to_sym, v.to_sym] }.should == { :a => :b, :c => :d }

View File

@ -15,14 +15,6 @@ describe "Exception#detailed_message" do
exception.full_message(highlight: false).should.include? "<prefix>new error<suffix>"
end
it "accepts highlight keyword argument and adds escape control sequences" do
RuntimeError.new("new error").detailed_message(highlight: true).should == "\e[1mnew error (\e[1;4mRuntimeError\e[m\e[1m)\e[m"
end
it "allows and ignores other keyword arguments" do
RuntimeError.new("new error").detailed_message(foo: true).should == "new error (RuntimeError)"
end
it "returns just a message if exception class is anonymous" do
Class.new(RuntimeError).new("message").detailed_message.should == "message"
end
@ -31,13 +23,30 @@ describe "Exception#detailed_message" do
RuntimeError.new("").detailed_message.should == "unhandled exception"
end
it "returns just class name for an instance of RuntimeError subclass with empty message" do
it "returns just class name for an instance other than RuntimeError with empty message" do
DetailedMessageSpec::C.new("").detailed_message.should == "DetailedMessageSpec::C"
StandardError.new("").detailed_message.should == "StandardError"
end
it "returns a generated class name for an instance of RuntimeError anonymous subclass with empty message" do
klass = Class.new(RuntimeError)
klass.new("").detailed_message.should =~ /\A#<Class:0x\h+>\z/
end
it "accepts highlight keyword argument and adds escape control sequences" do
RuntimeError.new("new error").detailed_message(highlight: true).should == "\e[1mnew error (\e[1;4mRuntimeError\e[m\e[1m)\e[m"
end
it "accepts highlight keyword argument and adds escape control sequences for an instance of RuntimeError with empty message" do
RuntimeError.new("").detailed_message(highlight: true).should == "\e[1;4munhandled exception\e[m"
end
it "accepts highlight keyword argument and adds escape control sequences for an instance other than RuntimeError with empty message" do
StandardError.new("").detailed_message(highlight: true).should == "\e[1;4mStandardError\e[m"
end
it "allows and ignores other keyword arguments" do
RuntimeError.new("new error").detailed_message(foo: true).should == "new error (RuntimeError)"
end
end
end

View File

@ -1,7 +1,70 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
require_relative 'shared/replace'
describe "Hash#replace" do
it_behaves_like :hash_replace, :replace
it "replaces the contents of self with other" do
h = { a: 1, b: 2 }
h.replace(c: -1, d: -2).should equal(h)
h.should == { c: -1, d: -2 }
end
it "tries to convert the passed argument to a hash using #to_hash" do
obj = mock('{1=>2,3=>4}')
obj.should_receive(:to_hash).and_return({ 1 => 2, 3 => 4 })
h = {}
h.replace(obj)
h.should == { 1 => 2, 3 => 4 }
end
it "calls to_hash on hash subclasses" do
h = {}
h.replace(HashSpecs::ToHashHash[1 => 2])
h.should == { 1 => 2 }
end
it "transfers the compare_by_identity flag" do
hash_a = { a: 1 }
hash_b = { b: 2 }
hash_b.compare_by_identity
hash_a.should_not.compare_by_identity?
hash_a.replace(hash_b)
hash_a.should.compare_by_identity?
hash_a = { a: 1 }
hash_b = { b: 2 }
hash_a.compare_by_identity
hash_a.should.compare_by_identity?
hash_a.replace(hash_b)
hash_a.should_not.compare_by_identity?
end
it "does not transfer default values" do
hash_a = {}
hash_b = Hash.new(5)
hash_a.replace(hash_b)
hash_a.default.should == 5
hash_a = {}
hash_b = Hash.new { |h, k| k * 2 }
hash_a.replace(hash_b)
hash_a.default(5).should == 10
hash_a = Hash.new { |h, k| k * 5 }
hash_b = Hash.new(-> { raise "Should not invoke lambda" })
hash_a.replace(hash_b)
hash_a.default.should == hash_b.default
end
it "raises a FrozenError if called on a frozen instance that would not be modified" do
-> do
HashSpecs.frozen_hash.replace(HashSpecs.frozen_hash)
end.should raise_error(FrozenError)
end
it "raises a FrozenError if called on a frozen instance that is modified" do
-> do
HashSpecs.frozen_hash.replace(HashSpecs.empty_frozen_hash)
end.should raise_error(FrozenError)
end
end

View File

@ -1,51 +0,0 @@
describe :hash_replace, shared: true do
it "replaces the contents of self with other" do
h = { a: 1, b: 2 }
h.send(@method, c: -1, d: -2).should equal(h)
h.should == { c: -1, d: -2 }
end
it "tries to convert the passed argument to a hash using #to_hash" do
obj = mock('{1=>2,3=>4}')
obj.should_receive(:to_hash).and_return({ 1 => 2, 3 => 4 })
h = {}
h.send(@method, obj)
h.should == { 1 => 2, 3 => 4 }
end
it "calls to_hash on hash subclasses" do
h = {}
h.send(@method, HashSpecs::ToHashHash[1 => 2])
h.should == { 1 => 2 }
end
it "does not transfer default values" do
hash_a = {}
hash_b = Hash.new(5)
hash_a.send(@method, hash_b)
hash_a.default.should == 5
hash_a = {}
hash_b = Hash.new { |h, k| k * 2 }
hash_a.send(@method, hash_b)
hash_a.default(5).should == 10
hash_a = Hash.new { |h, k| k * 5 }
hash_b = Hash.new(-> { raise "Should not invoke lambda" })
hash_a.send(@method, hash_b)
hash_a.default.should == hash_b.default
end
it "raises a FrozenError if called on a frozen instance that would not be modified" do
-> do
HashSpecs.frozen_hash.send(@method, HashSpecs.frozen_hash)
end.should raise_error(FrozenError)
end
it "raises a FrozenError if called on a frozen instance that is modified" do
-> do
HashSpecs.frozen_hash.send(@method, HashSpecs.empty_frozen_hash)
end.should raise_error(FrozenError)
end
end

View File

@ -37,6 +37,16 @@ describe "Hash#to_h" do
{ a: 1, b: 2 }.to_h { |k, v| [k.to_s, v*v]}.should == { "a" => 1, "b" => 4 }
end
it "passes to a block each pair's key and value as separate arguments" do
ScratchPad.record []
{ a: 1, b: 2 }.to_h { |k, v| ScratchPad << [k, v]; [k, v] }
ScratchPad.recorded.sort.should == [[:a, 1], [:b, 2]]
ScratchPad.record []
{ a: 1, b: 2 }.to_h { |*args| ScratchPad << args; [args[0], args[1]] }
ScratchPad.recorded.sort.should == [[:a, 1], [:b, 2]]
end
it "raises ArgumentError if block returns longer or shorter array" do
-> do
{ a: 1, b: 2 }.to_h { |k, v| [k.to_s, v*v, 1] }

View File

@ -10,12 +10,12 @@ describe "Integer#chr without argument" do
end
it "raises a RangeError is self is less than 0" do
-> { -1.chr }.should raise_error(RangeError)
-> { (-bignum_value).chr }.should raise_error(RangeError)
-> { -1.chr }.should raise_error(RangeError, /-1 out of char range/)
-> { (-bignum_value).chr }.should raise_error(RangeError, /bignum out of char range/)
end
it "raises a RangeError if self is too large" do
-> { 2206368128.chr(Encoding::UTF_8) }.should raise_error(RangeError)
-> { 2206368128.chr(Encoding::UTF_8) }.should raise_error(RangeError, /2206368128 out of char range/)
end
describe "when Encoding.default_internal is nil" do
@ -48,8 +48,8 @@ describe "Integer#chr without argument" do
end
it "raises a RangeError is self is greater than 255" do
-> { 256.chr }.should raise_error(RangeError)
-> { bignum_value.chr }.should raise_error(RangeError)
-> { 256.chr }.should raise_error(RangeError, /256 out of char range/)
-> { bignum_value.chr }.should raise_error(RangeError, /bignum out of char range/)
end
end
@ -137,7 +137,7 @@ describe "Integer#chr without argument" do
[620, "TIS-620"]
].each do |integer, encoding_name|
Encoding.default_internal = Encoding.find(encoding_name)
-> { integer.chr }.should raise_error(RangeError)
-> { integer.chr }.should raise_error(RangeError, /(invalid codepoint|out of char range)/)
end
end
end
@ -165,12 +165,12 @@ describe "Integer#chr with an encoding argument" do
# http://redmine.ruby-lang.org/issues/4869
it "raises a RangeError is self is less than 0" do
-> { -1.chr(Encoding::UTF_8) }.should raise_error(RangeError)
-> { (-bignum_value).chr(Encoding::EUC_JP) }.should raise_error(RangeError)
-> { -1.chr(Encoding::UTF_8) }.should raise_error(RangeError, /-1 out of char range/)
-> { (-bignum_value).chr(Encoding::EUC_JP) }.should raise_error(RangeError, /bignum out of char range/)
end
it "raises a RangeError if self is too large" do
-> { 2206368128.chr(Encoding::UTF_8) }.should raise_error(RangeError)
-> { 2206368128.chr(Encoding::UTF_8) }.should raise_error(RangeError, /2206368128 out of char range/)
end
it "returns a String with the specified encoding" do

View File

@ -0,0 +1,77 @@
require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "IO#autoclose?" do
before :each do
@io = IOSpecs.io_fixture "lines.txt"
end
after :each do
@io.autoclose = true unless @io.closed?
@io.close unless @io.closed?
end
it "is set to true by default" do
@io.should.autoclose?
end
it "cannot be queried on a closed IO object" do
@io.close
-> { @io.autoclose? }.should raise_error(IOError, /closed stream/)
end
end
describe "IO#autoclose=" do
before :each do
@io = IOSpecs.io_fixture "lines.txt"
end
after :each do
@io.autoclose = true unless @io.closed?
@io.close unless @io.closed?
end
it "can be set to true" do
@io.autoclose = false
@io.autoclose = true
@io.should.autoclose?
end
it "can be set to false" do
@io.autoclose = true
@io.autoclose = false
@io.should_not.autoclose?
end
it "can be set to any truthy value" do
@io.autoclose = false
@io.autoclose = 42
@io.should.autoclose?
@io.autoclose = false
@io.autoclose = Object.new
@io.should.autoclose?
end
it "can be set to any falsy value" do
@io.autoclose = true
@io.autoclose = nil
@io.should_not.autoclose?
end
it "can be set multiple times" do
@io.autoclose = true
@io.should.autoclose?
@io.autoclose = false
@io.should_not.autoclose?
@io.autoclose = true
@io.should.autoclose?
end
it "cannot be set on a closed IO object" do
@io.close
-> { @io.autoclose = false }.should raise_error(IOError, /closed stream/)
end
end

View File

@ -43,16 +43,44 @@ describe 'Kernel#caller' do
lines[1].should =~ /\A#{path}:2:in [`']block in <main>'\n\z/
end
it "can be called with a range" do
locations1 = caller(0)
locations2 = caller(2..4)
locations1[2..4].should == locations2
end
it "works with endless ranges" do
locations1 = KernelSpecs::CallerTest.locations(0)
locations2 = KernelSpecs::CallerTest.locations(eval("(2..)"))
locations2.map(&:to_s).should == locations1[2..-1].map(&:to_s)
locations2.should == locations1[2..-1]
end
it "works with beginless ranges" do
locations1 = KernelSpecs::CallerTest.locations(0)
locations2 = KernelSpecs::CallerTest.locations((..5))
locations2.map(&:to_s)[eval("(2..)")].should == locations1[(..5)].map(&:to_s)[eval("(2..)")]
locations2[eval("(2..)")].should == locations1[(..5)][eval("(2..)")]
end
it "can be called with a range whose end is negative" do
locations1 = caller(0)
locations2 = caller(2..-1)
locations3 = caller(2..-2)
locations1[2..-1].should == locations2
locations1[2..-2].should == locations3
end
it "must return nil if omitting more locations than available" do
caller(100).should == nil
caller(100..-1).should == nil
end
it "must return [] if omitting exactly the number of locations available" do
omit = caller(0).length
caller(omit).should == []
end
it "must return the same locations when called with 1..-1 and when called with no arguments" do
caller.should == caller(1..-1)
end
guard -> { Kernel.instance_method(:tap).source_location } do

View File

@ -21,7 +21,7 @@ describe "Kernel#sleep" do
sleep(Rational(1, 999)).should >= 0
end
it "accepts any Object that reponds to divmod" do
it "accepts any Object that responds to divmod" do
o = Object.new
def o.divmod(*); [0, 0.001]; end
sleep(o).should >= 0

View File

@ -20,6 +20,11 @@ describe "MatchData#[]" do
# negative index is larger than the number of match values
/(.)(.)(\d+)(\d)/.match("THX1138.")[-30, 2].should == nil
# positive index larger than number of match values
/(.)(.)(\d+)(\d)/.match("THX1138.")[5, 2].should == []
/(.)(.)(\d+)(\d)/.match("THX1138.")[6, 2].should == nil
/(.)(.)(\d+)(\d)/.match("THX1138.")[30, 2].should == nil
# length argument larger than number of match values is capped to match value length
/(.)(.)(\d+)(\d)/.match("THX1138.")[3, 10].should == %w|113 8|

View File

@ -56,7 +56,7 @@ describe :regexp_new_string, shared: true do
end
it "raises a RegexpError when passed an incorrect regexp" do
-> { Regexp.send(@method, "^[$", 0) }.should raise_error(RegexpError)
-> { Regexp.send(@method, "^[$", 0) }.should raise_error(RegexpError, Regexp.new(Regexp.escape("premature end of char-class: /^[$/")))
end
it "does not set Regexp options if only given one argument" do
@ -261,7 +261,7 @@ describe :regexp_new_string, shared: true do
describe "with escaped characters" do
it "raises a Regexp error if there is a trailing backslash" do
-> { Regexp.send(@method, "\\") }.should raise_error(RegexpError)
-> { Regexp.send(@method, "\\") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("too short escape sequence: /\\/")))
end
it "does not raise a Regexp error if there is an escaped trailing backslash" do
@ -293,7 +293,7 @@ describe :regexp_new_string, shared: true do
end
it "raises a RegexpError if \\x is not followed by any hexadecimal digits" do
-> { Regexp.send(@method, "\\" + "xn") }.should raise_error(RegexpError)
-> { Regexp.send(@method, "\\" + "xn") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("invalid hex escape: /\\xn/")))
end
it "accepts an escaped string interpolation" do
@ -453,15 +453,15 @@ describe :regexp_new_string, shared: true do
end
it "raises a RegexpError if less than four digits are given for \\uHHHH" do
-> { Regexp.send(@method, "\\" + "u304") }.should raise_error(RegexpError)
-> { Regexp.send(@method, "\\" + "u304") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("invalid Unicode escape: /\\u304/")))
end
it "raises a RegexpError if the \\u{} escape is empty" do
-> { Regexp.send(@method, "\\" + "u{}") }.should raise_error(RegexpError)
-> { Regexp.send(@method, "\\" + "u{}") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("invalid Unicode list: /\\u{}/")))
end
it "raises a RegexpError if more than six hexadecimal digits are given" do
-> { Regexp.send(@method, "\\" + "u{0ffffff}") }.should raise_error(RegexpError)
-> { Regexp.send(@method, "\\" + "u{0ffffff}") }.should raise_error(RegexpError, Regexp.new(Regexp.escape("invalid Unicode range: /\\u{0ffffff}/")))
end
it "returns a Regexp with US-ASCII encoding if only 7-bit ASCII characters are present regardless of the input String's encoding" do

View File

@ -61,10 +61,22 @@ describe "String#encode" do
str.encode(invalid: :replace).should_not equal(str)
end
it "normalizes newlines" do
"\r\nfoo".encode(universal_newline: true).should == "\nfoo"
it "normalizes newlines with cr_newline option" do
"\r\nfoo".encode(cr_newline: true).should == "\r\rfoo"
"\rfoo".encode(cr_newline: true).should == "\rfoo"
"\nfoo".encode(cr_newline: true).should == "\rfoo"
end
it "normalizes newlines with crlf_newline option" do
"\r\nfoo".encode(crlf_newline: true).should == "\r\r\nfoo"
"\rfoo".encode(crlf_newline: true).should == "\rfoo"
"\nfoo".encode(crlf_newline: true).should == "\r\nfoo"
end
it "normalizes newlines with universal_newline option" do
"\r\nfoo".encode(universal_newline: true).should == "\nfoo"
"\rfoo".encode(universal_newline: true).should == "\nfoo"
"\nfoo".encode(universal_newline: true).should == "\nfoo"
end
it "replaces invalid encoding in source with default replacement" do

View File

@ -194,6 +194,190 @@ describe :string_encode, shared: true do
end
end
describe "given the fallback option" do
context "given a hash" do
it "looks up the replacement value from the hash" do
encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: { "\ufffd" => "bar" })
encoded.should == "Bbar"
end
it "calls to_str on the returned value" do
obj = Object.new
obj.should_receive(:to_str).and_return("bar")
encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: { "\ufffd" => obj })
encoded.should == "Bbar"
end
it "does not call to_s on the returned value" do
obj = Object.new
obj.should_not_receive(:to_s)
-> {
"B\ufffd".encode(Encoding::US_ASCII, fallback: { "\ufffd" => obj })
}.should raise_error(TypeError, "no implicit conversion of Object into String")
end
it "raises an error if the key is not present in the hash" do
-> {
"B\ufffd".encode(Encoding::US_ASCII, fallback: { "foo" => "bar" })
}.should raise_error(Encoding::UndefinedConversionError, "U+FFFD from UTF-8 to US-ASCII")
end
it "raises an error if the value is itself invalid" do
-> {
"B\ufffd".encode(Encoding::US_ASCII, fallback: { "\ufffd" => "\uffee" })
}.should raise_error(ArgumentError, "too big fallback string")
end
it "uses the hash's default value if set" do
hash = {}
hash.default = "bar"
encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: hash)
encoded.should == "Bbar"
end
it "uses the result of calling default_proc if set" do
hash = {}
hash.default_proc = -> _, _ { "bar" }
encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: hash)
encoded.should == "Bbar"
end
end
context "given an object inheriting from Hash" do
before do
klass = Class.new(Hash)
@hash_like = klass.new
@hash_like["\ufffd"] = "bar"
end
it "looks up the replacement value from the object" do
encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: @hash_like)
encoded.should == "Bbar"
end
end
context "given an object responding to []" do
before do
klass = Class.new do
def [](c) = c.bytes.inspect
end
@hash_like = klass.new
end
it "calls [] on the object, passing the invalid character" do
encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: @hash_like)
encoded.should == "B[239, 191, 189]"
end
end
context "given an object not responding to []" do
before do
@non_hash_like = Object.new
end
it "raises an error" do
-> {
"B\ufffd".encode(Encoding::US_ASCII, fallback: @non_hash_like)
}.should raise_error(Encoding::UndefinedConversionError, "U+FFFD from UTF-8 to US-ASCII")
end
end
context "given a proc" do
it "calls the proc to get the replacement value, passing in the invalid character" do
encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: proc { |c| c.bytes.inspect })
encoded.should == "B[239, 191, 189]"
end
it "calls to_str on the returned value" do
obj = Object.new
obj.should_receive(:to_str).and_return("bar")
encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: proc { |c| obj })
encoded.should == "Bbar"
end
it "does not call to_s on the returned value" do
obj = Object.new
obj.should_not_receive(:to_s)
-> {
"B\ufffd".encode(Encoding::US_ASCII, fallback: proc { |c| obj })
}.should raise_error(TypeError, "no implicit conversion of Object into String")
end
it "raises an error if the returned value is itself invalid" do
-> {
"B\ufffd".encode(Encoding::US_ASCII, fallback: -> c { "\uffee" })
}.should raise_error(ArgumentError, "too big fallback string")
end
end
context "given a lambda" do
it "calls the lambda to get the replacement value, passing in the invalid character" do
encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: -> c { c.bytes.inspect })
encoded.should == "B[239, 191, 189]"
end
it "calls to_str on the returned value" do
obj = Object.new
obj.should_receive(:to_str).and_return("bar")
encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: -> c { obj })
encoded.should == "Bbar"
end
it "does not call to_s on the returned value" do
obj = Object.new
obj.should_not_receive(:to_s)
-> {
"B\ufffd".encode(Encoding::US_ASCII, fallback: -> c { obj })
}.should raise_error(TypeError, "no implicit conversion of Object into String")
end
it "raises an error if the returned value is itself invalid" do
-> {
"B\ufffd".encode(Encoding::US_ASCII, fallback: -> c { "\uffee" })
}.should raise_error(ArgumentError, "too big fallback string")
end
end
context "given a method" do
def replace(c) = c.bytes.inspect
def replace_bad(c) = "\uffee"
def replace_to_str(c)
obj = Object.new
obj.should_receive(:to_str).and_return("bar")
obj
end
def replace_to_s(c)
obj = Object.new
obj.should_not_receive(:to_s)
obj
end
it "calls the method to get the replacement value, passing in the invalid character" do
encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: method(:replace))
encoded.should == "B[239, 191, 189]"
end
it "calls to_str on the returned value" do
encoded = "B\ufffd".encode(Encoding::US_ASCII, fallback: method(:replace_to_str))
encoded.should == "Bbar"
end
it "does not call to_s on the returned value" do
-> {
"B\ufffd".encode(Encoding::US_ASCII, fallback: method(:replace_to_s))
}.should raise_error(TypeError, "no implicit conversion of Object into String")
end
it "raises an error if the returned value is itself invalid" do
-> {
"B\ufffd".encode(Encoding::US_ASCII, fallback: method(:replace_bad))
}.should raise_error(ArgumentError, "too big fallback string")
end
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;'

View File

@ -21,6 +21,18 @@ describe "Struct#to_h" do
h.should == { "make" => "ford", "model" => "ranger", "year" => "" }
end
it "passes to a block each pair's key and value as separate arguments" do
s = StructClasses::Ruby.new('3.2.4', 'macos')
ScratchPad.record []
s.to_h { |k, v| ScratchPad << [k, v]; [k, v] }
ScratchPad.recorded.sort.should == [[:platform, 'macos'], [:version, '3.2.4']]
ScratchPad.record []
s.to_h { |*args| ScratchPad << args; [args[0], args[1]] }
ScratchPad.recorded.sort.should == [[:platform, 'macos'], [:version, '3.2.4']]
end
it "raises ArgumentError if block returns longer or shorter array" do
-> do
StructClasses::Car.new.to_h { |k, v| [k.to_s, "#{v}".downcase, 1] }

View File

@ -14,12 +14,40 @@ describe "Thread#thread_variable_get" do
end
it "returns the value previously set by #thread_variable_set" do
@t.thread_variable_set :a, 49
@t.thread_variable_set(:a, 49)
@t.thread_variable_get(:a).should == 49
end
it "returns a value private to self" do
@t.thread_variable_set :thread_variable_get_spec, 82
@t.thread_variable_set(:thread_variable_get_spec, 82)
Thread.current.thread_variable_get(:thread_variable_get_spec).should be_nil
end
it "accepts String and Symbol keys interchangeably" do
@t.thread_variable_set("a", 49)
@t.thread_variable_get("a").should == 49
@t.thread_variable_get(:a).should == 49
end
it "converts a key that is neither String nor Symbol with #to_str" do
key = mock('key')
key.should_receive(:to_str).and_return('a')
@t.thread_variable_set(:a, 49)
@t.thread_variable_get(key).should == 49
end
it "does not raise FrozenError if the thread is frozen" do
@t.freeze
@t.thread_variable_get(:a).should be_nil
end
it "does not raise a TypeError if the key is neither Symbol nor String, nor responds to #to_str" do
@t.thread_variable_get(123).should be_nil
end
it "does not try to convert the key with #to_sym" do
key = mock('key')
key.should_not_receive(:to_sym)
@t.thread_variable_get(key).should be_nil
end
end

View File

@ -10,17 +10,53 @@ describe "Thread#thread_variable_set" do
end
it "returns the value set" do
(@t.thread_variable_set :a, 2).should == 2
@t.thread_variable_set(:a, 2).should == 2
end
it "sets a value that will be returned by #thread_variable_get" do
@t.thread_variable_set :a, 49
@t.thread_variable_set(:a, 49)
@t.thread_variable_get(:a).should == 49
end
it "sets a value private to self" do
@t.thread_variable_set :thread_variable_get_spec, 82
@t.thread_variable_set(:thread_variable_get_spec, 82)
@t.thread_variable_get(:thread_variable_get_spec).should == 82
Thread.current.thread_variable_get(:thread_variable_get_spec).should be_nil
end
it "accepts String and Symbol keys interchangeably" do
@t.thread_variable_set('a', 49)
@t.thread_variable_get('a').should == 49
@t.thread_variable_set(:a, 50)
@t.thread_variable_get('a').should == 50
end
it "converts a key that is neither String nor Symbol with #to_str" do
key = mock('key')
key.should_receive(:to_str).and_return('a')
@t.thread_variable_set(key, 49)
@t.thread_variable_get(:a).should == 49
end
it "removes a key if the value is nil" do
@t.thread_variable_set(:a, 52)
@t.thread_variable_set(:a, nil)
@t.thread_variable?(:a).should be_false
end
it "raises a FrozenError if the thread is frozen" do
@t.freeze
-> { @t.thread_variable_set(:a, 1) }.should raise_error(FrozenError, "can't modify frozen thread locals")
end
it "raises a TypeError if the key is neither Symbol nor String, nor responds to #to_str" do
-> { @t.thread_variable_set(123, 1) }.should raise_error(TypeError, '123 is not a symbol')
end
it "does not try to convert the key with #to_sym" do
key = mock('key')
key.should_not_receive(:to_sym)
-> { @t.thread_variable_set(key, 42) }.should raise_error(TypeError, "#{key.inspect} is not a symbol")
end
end

View File

@ -10,12 +10,44 @@ describe "Thread#thread_variable?" do
end
it "returns false if the thread variables do not contain 'key'" do
@t.thread_variable_set :a, 2
@t.thread_variable_set(:a, 2)
@t.thread_variable?(:b).should be_false
end
it "returns true if the thread variables contain 'key'" do
@t.thread_variable_set :a, 2
@t.thread_variable_set(:a, 2)
@t.thread_variable?(:a).should be_true
end
it "accepts String and Symbol keys interchangeably" do
@t.thread_variable?('a').should be_false
@t.thread_variable?(:a).should be_false
@t.thread_variable_set(:a, 49)
@t.thread_variable?('a').should be_true
@t.thread_variable?(:a).should be_true
end
it "converts a key that is neither String nor Symbol with #to_str" do
key = mock('key')
key.should_receive(:to_str).and_return('a')
@t.thread_variable_set(:a, 49)
@t.thread_variable?(key).should be_true
end
it "does not raise FrozenError if the thread is frozen" do
@t.freeze
@t.thread_variable?(:a).should be_false
end
it "does not raise a TypeError if the key is neither Symbol nor String, nor responds to #to_str" do
@t.thread_variable?(123).should be_false
end
it "does not try to convert the key with #to_sym" do
key = mock('key')
key.should_not_receive(:to_sym)
@t.thread_variable?(key).should be_false
end
end

View File

@ -10,15 +10,15 @@ describe "Thread#thread_variables" do
end
it "returns the keys of all the values set" do
@t.thread_variable_set :a, 2
@t.thread_variable_set :b, 4
@t.thread_variable_set :c, 6
@t.thread_variable_set(:a, 2)
@t.thread_variable_set(:b, 4)
@t.thread_variable_set(:c, 6)
@t.thread_variables.sort.should == [:a, :b, :c]
end
it "sets a value private to self" do
@t.thread_variable_set :a, 82
@t.thread_variable_set :b, 82
it "returns the keys private to self" do
@t.thread_variable_set(:a, 82)
@t.thread_variable_set(:b, 82)
Thread.current.thread_variables.should_not include(:a, :b)
end
@ -26,4 +26,14 @@ describe "Thread#thread_variables" do
Thread.current.thread_variables.should == []
@t.thread_variables.should == []
end
it "returns keys as Symbols" do
key = mock('key')
key.should_receive(:to_str).and_return('a')
@t.thread_variable_set(key, 49)
@t.thread_variable_set('b', 50)
@t.thread_variable_set(:c, 51)
@t.thread_variables.sort.should == [:a, :b, :c]
end
end

View File

@ -1,4 +1,5 @@
require_relative '../../spec_helper'
require_relative '../../shared/time/yday'
describe "Time#yday" do
it "returns an integer representing the day of the year, 1..366" do
@ -7,15 +8,5 @@ describe "Time#yday" do
end
end
it 'returns the correct value for each day of each month' do
mdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
yday = 1
mdays.each_with_index do |days, month|
days.times do |day|
Time.new(2014, month+1, day+1).yday.should == yday
yday += 1
end
end
end
it_behaves_like :time_yday, -> year, month, day { Time.new(year, month, day).yday }
end

View File

@ -19,6 +19,27 @@ describe "The for expression" do
end
end
it "iterates over a list of arrays and destructures with an empty splat" do
for i, * in [[1,2]]
i.should == 1
end
end
it "iterates over a list of arrays and destructures with a splat" do
for i, *j in [[1,2]]
i.should == 1
j.should == [2]
end
end
it "iterates over a list of arrays and destructures with a splat and additional targets" do
for i, *j, k in [[1,2,3,4]]
i.should == 1
j.should == [2,3]
k.should == 4
end
end
it "iterates over an Hash passing each key-value pair to the block" do
k = 0
l = 0
@ -81,6 +102,88 @@ describe "The for expression" do
end
end
it "allows a global variable as an iterator name" do
old_global_var = $var
m = [1,2,3]
n = 0
for $var in m
n += 1
end
$var.should == 3
n.should == 3
$var = old_global_var
end
it "allows an attribute as an iterator name" do
class OFor
attr_accessor :target
end
ofor = OFor.new
m = [1,2,3]
n = 0
for ofor.target in m
n += 1
end
ofor.target.should == 3
n.should == 3
end
# Segfault in MRI 3.3 and lower: https://bugs.ruby-lang.org/issues/20468
ruby_bug "#20468", ""..."3.4" do
it "allows an attribute with safe navigation as an iterator name" do
class OFor
attr_accessor :target
end
ofor = OFor.new
m = [1,2,3]
n = 0
eval <<~RUBY
for ofor&.target in m
n += 1
end
RUBY
ofor.target.should == 3
n.should == 3
end
it "allows an attribute with safe navigation on a nil base as an iterator name" do
ofor = nil
m = [1,2,3]
n = 0
eval <<~RUBY
for ofor&.target in m
n += 1
end
RUBY
ofor.should be_nil
n.should == 3
end
end
it "allows an array index writer as an iterator name" do
arr = [:a, :b, :c]
m = [1,2,3]
n = 0
for arr[1] in m
n += 1
end
arr.should == [:a, 3, :c]
n.should == 3
end
it "allows a hash index writer as an iterator name" do
hash = { a: 10, b: 20, c: 30 }
m = [1,2,3]
n = 0
for hash[:b] in m
n += 1
end
hash.should == { a: 10, b: 3, c: 30 }
n.should == 3
end
# 1.9 behaviour verified by nobu in
# http://redmine.ruby-lang.org/issues/show/2053
it "yields only as many values as there are arguments" do

View File

@ -106,4 +106,14 @@ HERE
SquigglyHeredocSpecs.least_indented_on_the_first_line_single.should == "a\n b\n c\n"
SquigglyHeredocSpecs.least_indented_on_the_last_line_single.should == " a\n b\nc\n"
end
it "reports line numbers inside HEREDOC with method call" do
-> {
<<-HERE.chomp
a
b
#{c}
HERE
}.should raise_error(NameError) { |e| e.backtrace[0].should.start_with?("#{__FILE__}:#{__LINE__ - 2}") }
end
end

View File

@ -5,7 +5,7 @@ describe "Pattern matching" do
ScratchPad.record []
end
describe "can be standalone assoc operator that" do
describe "Rightward assignment (`=>`) that can be standalone assoc operator that" do
it "deconstructs value" do
suppress_warning do
[0, 1] => [a, b]
@ -22,6 +22,23 @@ describe "Pattern matching" do
[a, defined?(b)].should == [0, nil]
end
end
it "can work with keywords" do
{ a: 0, b: 1 } => { a:, b: }
[a, b].should == [0, 1]
end
end
describe "One-line pattern matching" do
it "can be used to check if a pattern matches for Array-like entities" do
([0, 1] in [a, b]).should == true
([0, 1] in [a, b, c]).should == false
end
it "can be used to check if a pattern matches for Hash-like entities" do
({ a: 0, b: 1 } in { a:, b: }).should == true
({ a: 0, b: 1 } in { a:, b:, c: }).should == false
end
end
describe "find pattern" do

View File

@ -52,6 +52,16 @@ describe "The rescue keyword" do
RescueSpecs::SafeNavigationSetterCaptor.should_capture_exception
end
it 'using a safely navigated setter method on a nil target' do
target = nil
begin
raise SpecificExampleException, "Raising this to be handled below"
rescue SpecificExampleException => target&.captured_error
:caught
end.should == :caught
target.should be_nil
end
it 'using a setter method' do
RescueSpecs::SetterCaptor.should_capture_exception
end

View File

@ -0,0 +1,3 @@
begin
'coverage with begin'
end

View File

@ -6,6 +6,7 @@ describe 'Coverage.result' do
@class_file = fixture __FILE__, 'some_class.rb'
@config_file = fixture __FILE__, 'start_coverage.rb'
@eval_code_file = fixture __FILE__, 'eval_code.rb'
@with_begin_file = fixture __FILE__, 'code_with_begin.rb'
end
before :each do
@ -16,6 +17,7 @@ describe 'Coverage.result' do
$LOADED_FEATURES.delete(@class_file)
$LOADED_FEATURES.delete(@config_file)
$LOADED_FEATURES.delete(@eval_code_file)
$LOADED_FEATURES.delete(@with_begin_file)
Coverage.result if Coverage.running?
end
@ -354,4 +356,16 @@ describe 'Coverage.result' do
Coverage.peek_result.should == result
end
it 'covers 100% lines with begin' do
Coverage.start
require @with_begin_file.chomp('.rb')
result = Coverage.result
result.should == {
@with_begin_file => [
nil, 1, nil
]
}
end
end

View File

@ -38,7 +38,7 @@ describe "Date#year" do
end
describe "Date#yday" do
it "determines the year" do
it "determines the day of the year" do
Date.civil(2007, 1, 17).yday.should == 17
Date.civil(2008, 10, 28).yday.should == 302
end

View File

@ -1,6 +1,7 @@
require_relative '../../spec_helper'
require_relative '../../shared/time/yday'
require 'date'
describe "Date#yday" do
it "needs to be reviewed for spec completeness"
it_behaves_like :time_yday, -> year, month, day { Date.new(year, month, day).yday }
end

View File

@ -0,0 +1,7 @@
require_relative '../../spec_helper'
require_relative '../../shared/time/yday'
require 'date'
describe "DateTime#yday" do
it_behaves_like :time_yday, -> year, month, day { DateTime.new(year, month, day).yday }
end

View File

@ -34,6 +34,18 @@ describe :net_ftp_puttextfile, shared: true do
remote_lines.should == local_lines.gsub("\n", "\r\n")
end
guard -> { Net::FTP::VERSION < '0.3.6' } do
it "returns nil" do
@ftp.send(@method, @local_fixture_file, "text").should be_nil
end
end
guard -> { Net::FTP::VERSION >= '0.3.6' } do
it "returns the response" do
@ftp.send(@method, @local_fixture_file, "text").should == @ftp.last_response
end
end
describe "when passed a block" do
it "yields each transmitted line" do
res = []

View File

@ -2,7 +2,6 @@ require_relative '../spec_helper'
require_relative '../fixtures/classes'
describe "Socket::IPSocket#getaddress" do
it "returns the IP address of hostname" do
addr_local = IPSocket.getaddress(SocketSpecs.hostname)
["127.0.0.1", "::1"].include?(addr_local).should == true
@ -14,6 +13,10 @@ describe "Socket::IPSocket#getaddress" do
IPSocket.getaddress('::1').should == '::1'
end
it 'returns IPv4 compatible IPv6 addresses' do
IPSocket.getaddress('::ffff:192.168.1.1').should == '::ffff:192.168.1.1'
end
# There is no way to make this fail-proof on all machines, because
# DNS servers like opendns return A records for ANY host, including
# traditionally invalidly named ones.

View File

@ -1,9 +1,11 @@
require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/partially_closable_sockets'
require_relative 'shared/pair'
with_feature :unix_socket do
describe "UNIXSocket.pair" do
it_should_behave_like :unixsocket_pair
it_should_behave_like :partially_closable_sockets
before :each do
@ -14,25 +16,5 @@ with_feature :unix_socket do
@s1.close
@s2.close
end
it "returns a pair of connected sockets" do
@s1.puts "foo"
@s2.gets.should == "foo\n"
end
it "returns sockets with no name" do
@s1.path.should == @s2.path
@s1.path.should == ""
end
it "returns sockets with no address" do
@s1.addr.should == ["AF_UNIX", ""]
@s2.addr.should == ["AF_UNIX", ""]
end
it "returns sockets with no peeraddr" do
@s1.peeraddr.should == ["AF_UNIX", ""]
@s2.peeraddr.should == ["AF_UNIX", ""]
end
end
end

View File

@ -0,0 +1,29 @@
require_relative '../../spec_helper'
require_relative '../../fixtures/classes'
describe :unixsocket_pair, shared: true do
it "returns two UNIXSockets" do
@s1.should be_an_instance_of(UNIXSocket)
@s2.should be_an_instance_of(UNIXSocket)
end
it "returns a pair of connected sockets" do
@s1.puts "foo"
@s2.gets.should == "foo\n"
end
it "sets the socket paths to empty Strings" do
@s1.path.should == ""
@s2.path.should == ""
end
it "sets the socket addresses to empty Strings" do
@s1.addr.should == ["AF_UNIX", ""]
@s2.addr.should == ["AF_UNIX", ""]
end
it "sets the socket peer addresses to empty Strings" do
@s1.peeraddr.should == ["AF_UNIX", ""]
@s2.peeraddr.should == ["AF_UNIX", ""]
end
end

View File

@ -1,40 +1,20 @@
require_relative '../spec_helper'
require_relative '../fixtures/classes'
require_relative '../shared/partially_closable_sockets'
require_relative 'shared/pair'
with_feature :unix_socket do
describe 'UNIXSocket.socketpair' do
before do
describe "UNIXSocket.socketpair" do
it_should_behave_like :unixsocket_pair
it_should_behave_like :partially_closable_sockets
before :each do
@s1, @s2 = UNIXSocket.socketpair
end
after do
after :each do
@s1.close
@s2.close
end
it 'returns two UNIXSockets' do
@s1.should be_an_instance_of(UNIXSocket)
@s2.should be_an_instance_of(UNIXSocket)
end
it 'connects the sockets to each other' do
@s1.write('hello')
@s2.recv(5).should == 'hello'
end
it 'sets the socket paths to empty Strings' do
@s1.path.should == ''
@s2.path.should == ''
end
it 'sets the socket addresses to empty Strings' do
@s1.addr.should == ['AF_UNIX', '']
@s2.addr.should == ['AF_UNIX', '']
end
it 'sets the socket peer addresses to empty Strings' do
@s1.peeraddr.should == ['AF_UNIX', '']
@s2.peeraddr.should == ['AF_UNIX', '']
end
end
end

View File

@ -130,6 +130,26 @@ describe "StringIO#initialize when passed [Object, mode]" do
-> { @io.send(:initialize, str, "w") }.should raise_error(Errno::EACCES)
-> { @io.send(:initialize, str, "a") }.should raise_error(Errno::EACCES)
end
it "truncates all the content if passed w mode" do
io = StringIO.allocate
source = +"example".encode(Encoding::ISO_8859_1);
io.send(:initialize, source, "w")
io.string.should.empty?
io.string.encoding.should == Encoding::ISO_8859_1
end
it "truncates all the content if passed IO::TRUNC mode" do
io = StringIO.allocate
source = +"example".encode(Encoding::ISO_8859_1);
io.send(:initialize, source, IO::TRUNC)
io.string.should.empty?
io.string.encoding.should == Encoding::ISO_8859_1
end
end
describe "StringIO#initialize when passed [Object]" do
@ -172,7 +192,7 @@ end
# NOTE: Synchronise with core/io/new_spec.rb (core/io/shared/new.rb)
describe "StringIO#initialize when passed keyword arguments" do
it "sets the mode based on the passed :mode option" do
io = StringIO.new("example", "r")
io = StringIO.new("example", mode: "r")
io.closed_read?.should be_false
io.closed_write?.should be_true
end

View File

@ -7,9 +7,9 @@ describe "StringScanner#scan_until" do
end
it "returns the substring up to and including the end of the match" do
@s.scan_until(/a/).should == "This is a"
@s.pre_match.should == "This is "
@s.post_match.should == " test"
@s.scan_until(/a/).should == "This is a"
@s.pre_match.should == "This is "
@s.post_match.should == " test"
end
it "returns nil if there's no match" do

View File

@ -221,7 +221,7 @@ static VALUE kernel_spec_rb_eval_string_protect(VALUE self, VALUE str, VALUE ary
VALUE kernel_spec_rb_sys_fail(VALUE self, VALUE msg) {
errno = 1;
if (msg == Qnil) {
rb_sys_fail(0);
rb_sys_fail(NULL);
} else if (self != Qundef) {
rb_sys_fail(StringValuePtr(msg));
}

View File

@ -0,0 +1,18 @@
describe :time_yday, shared: true do
it 'returns the correct value for each day of each month' do
mdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
yday = 1
mdays.each_with_index do |days, month|
days.times do |day|
@method.call(2014, month+1, day+1).should == yday
yday += 1
end
end
end
it 'supports leap years' do
@method.call(2016, 2, 29).should == 31 + 29
@method.call(2016, 3, 1).should == 31 + 29 + 1
end
end