1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
This commit is contained in:
Benoit Daloze 2022-01-10 16:29:54 +01:00
parent 8abfc10605
commit 4053e8ba0d
42 changed files with 470 additions and 84 deletions

View file

@ -12,6 +12,17 @@ describe "Array#pack with format 'A'" do
it_behaves_like :array_pack_string, 'A' it_behaves_like :array_pack_string, 'A'
it_behaves_like :array_pack_taint, 'A' it_behaves_like :array_pack_taint, 'A'
it "calls #to_str to convert an Object to a String" do
obj = mock("pack A string")
obj.should_receive(:to_str).and_return("``abcdef")
[obj].pack("A*").should == "``abcdef"
end
it "will not implicitly convert a number to a string" do
-> { [0].pack('A') }.should raise_error(TypeError)
-> { [0].pack('a') }.should raise_error(TypeError)
end
it "adds all the bytes to the output when passed the '*' modifier" do it "adds all the bytes to the output when passed the '*' modifier" do
["abc"].pack("A*").should == "abc" ["abc"].pack("A*").should == "abc"
end end

View file

@ -13,11 +13,16 @@ describe "Array#pack with format 'B'" do
it_behaves_like :array_pack_taint, 'B' it_behaves_like :array_pack_taint, 'B'
it "calls #to_str to convert an Object to a String" do it "calls #to_str to convert an Object to a String" do
obj = mock("pack H string") obj = mock("pack B string")
obj.should_receive(:to_str).and_return("``abcdef") obj.should_receive(:to_str).and_return("``abcdef")
[obj].pack("B*").should == "\x2a" [obj].pack("B*").should == "\x2a"
end end
it "will not implicitly convert a number to a string" do
-> { [0].pack('B') }.should raise_error(TypeError)
-> { [0].pack('b') }.should raise_error(TypeError)
end
it "encodes one bit for each character starting with the most significant bit" do it "encodes one bit for each character starting with the most significant bit" do
[ [["0"], "\x00"], [ [["0"], "\x00"],
[["1"], "\x80"] [["1"], "\x80"]

View file

@ -18,6 +18,11 @@ describe "Array#pack with format 'H'" do
[obj].pack("H").should == "\xa0" [obj].pack("H").should == "\xa0"
end end
it "will not implicitly convert a number to a string" do
-> { [0].pack('H') }.should raise_error(TypeError)
-> { [0].pack('h') }.should raise_error(TypeError)
end
it "encodes the first character as the most significant nibble when passed no count modifier" do it "encodes the first character as the most significant nibble when passed no count modifier" do
["ab"].pack("H").should == "\xa0" ["ab"].pack("H").should == "\xa0"
end end

View file

@ -53,6 +53,14 @@ describe :array_pack_float_le, shared: true do
it "encodes a negative Float outside the range of a single precision float" do it "encodes a negative Float outside the range of a single precision float" do
[-1e150].pack(pack_format).should == "\x00\x00\x80\xff" [-1e150].pack(pack_format).should == "\x00\x00\x80\xff"
end end
it "encodes a bignum as a float" do
[2 ** 65].pack(pack_format).should == [(2 ** 65).to_f].pack(pack_format)
end
it "encodes a rational as a float" do
[Rational(3, 4)].pack(pack_format).should == [Rational(3, 4).to_f].pack(pack_format)
end
end end
describe :array_pack_float_be, shared: true do describe :array_pack_float_be, shared: true do

View file

@ -18,6 +18,16 @@ describe "Array#pack with format 'u'" do
it_behaves_like :array_pack_arguments, 'u' it_behaves_like :array_pack_arguments, 'u'
it_behaves_like :array_pack_taint, 'u' it_behaves_like :array_pack_taint, 'u'
it "calls #to_str to convert an Object to a String" do
obj = mock("pack u string")
obj.should_receive(:to_str).and_return("``abcdef")
[obj].pack("u*").should == "(8&!A8F-D968`\n"
end
it "will not implicitly convert a number to a string" do
-> { [0].pack('u') }.should raise_error(TypeError)
end
it "encodes an empty string as an empty string" do it "encodes an empty string as an empty string" do
[""].pack("u").should == "" [""].pack("u").should == ""
end end

View file

@ -12,6 +12,16 @@ describe "Array#pack with format 'Z'" do
it_behaves_like :array_pack_string, 'Z' it_behaves_like :array_pack_string, 'Z'
it_behaves_like :array_pack_taint, 'Z' it_behaves_like :array_pack_taint, 'Z'
it "calls #to_str to convert an Object to a String" do
obj = mock("pack Z string")
obj.should_receive(:to_str).and_return("``abcdef")
[obj].pack("Z*").should == "``abcdef\x00"
end
it "will not implicitly convert a number to a string" do
-> { [0].pack('Z') }.should raise_error(TypeError)
end
it "adds all the bytes and appends a NULL byte when passed the '*' modifier" do it "adds all the bytes and appends a NULL byte when passed the '*' modifier" do
["abc"].pack("Z*").should == "abc\x00" ["abc"].pack("Z*").should == "abc\x00"
end end

View file

@ -75,6 +75,14 @@ describe :file_fnmatch, shared: true do
File.send(@method, 'c*t', 'c/a/b/t').should == true File.send(@method, 'c*t', 'c/a/b/t').should == true
end end
it "does not match unterminated range of characters" do
File.send(@method, 'abc[de', 'abcd').should == false
end
it "does not match unterminated range of characters as a literal" do
File.send(@method, 'abc[de', 'abc[de').should == false
end
it "matches ranges of characters using bracket expression (e.g. [a-z])" do it "matches ranges of characters using bracket expression (e.g. [a-z])" do
File.send(@method, 'ca[a-z]', 'cat').should == true File.send(@method, 'ca[a-z]', 'cat').should == true
end end

View file

@ -36,4 +36,14 @@ describe "Hash#to_a" do
{}.untrust.to_a.untrusted?.should be_true {}.untrust.to_a.untrusted?.should be_true
end end
end end
ruby_version_is '2.7'...'3.0' do
it "returns a not tainted array if self is tainted" do
{}.taint.to_a.tainted?.should be_false
end
it "returns a trusted array if self is untrusted" do
{}.untrust.to_a.untrusted?.should be_false
end
end
end end

View file

@ -80,7 +80,7 @@ describe "Hash#transform_keys!" do
end end
ruby_version_is ""..."3.0.2" do # https://bugs.ruby-lang.org/issues/17735 ruby_version_is ""..."3.0.2" do # https://bugs.ruby-lang.org/issues/17735
it "returns the processed keys if we broke from the block" do it "returns the processed keys if we break from the block" do
@hash.transform_keys! do |v| @hash.transform_keys! do |v|
break if v == :c break if v == :c
v.succ v.succ
@ -90,7 +90,7 @@ describe "Hash#transform_keys!" do
end end
ruby_version_is "3.0.2" do ruby_version_is "3.0.2" do
it "returns the processed keys and non evaluated keys if we broke from the block" do it "returns the processed keys and non evaluated keys if we break from the block" do
@hash.transform_keys! do |v| @hash.transform_keys! do |v|
break if v == :c break if v == :c
v.succ v.succ

View file

@ -1,7 +1,7 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
ruby_version_is ""..."3.1" do describe "Fixnum" do
describe "Fixnum" do ruby_version_is ""..."3.2" do
it "is unified into Integer" do it "is unified into Integer" do
suppress_warning do suppress_warning do
Fixnum.should equal(Integer) Fixnum.should equal(Integer)
@ -13,7 +13,15 @@ ruby_version_is ""..."3.1" do
end end
end end
describe "Bignum" do ruby_version_is "3.2" do
it "is no longer defined" do
Object.should_not.const_defined?(:Fixnum)
end
end
end
describe "Bignum" do
ruby_version_is ""..."3.2" do
it "is unified into Integer" do it "is unified into Integer" do
suppress_warning do suppress_warning do
Bignum.should equal(Integer) Bignum.should equal(Integer)
@ -24,4 +32,10 @@ ruby_version_is ""..."3.1" do
-> { Bignum }.should complain(/constant ::Bignum is deprecated/) -> { Bignum }.should complain(/constant ::Bignum is deprecated/)
end end
end end
ruby_version_is "3.2" do
it "is no longer defined" do
Object.should_not.const_defined?(:Bignum)
end
end
end end

View file

@ -103,7 +103,7 @@ describe "IO#ungetc" do
-> { @io.sysread(1) }.should raise_error(IOError) -> { @io.sysread(1) }.should raise_error(IOError)
end end
ruby_version_is "0"..."3.0" do ruby_version_is ""..."3.0" do
it "does not affect the stream and returns nil when passed nil" do it "does not affect the stream and returns nil when passed nil" do
@io.getc.should == ?V @io.getc.should == ?V
@io.ungetc(nil) @io.ungetc(nil)

View file

@ -1,7 +1,7 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
ruby_version_is ''...'3.2' do describe "Kernel#=~" do
describe "Kernel#=~" do ruby_version_is ''...'3.2' do
it "returns nil matching any object" do it "returns nil matching any object" do
o = Object.new o = Object.new
@ -21,4 +21,10 @@ ruby_version_is ''...'3.2' do
end.should complain(/deprecated Object#=~ is called on Object/, verbose: true) end.should complain(/deprecated Object#=~ is called on Object/, verbose: true)
end end
end end
ruby_version_is '3.2' do
it "is no longer defined" do
Object.new.should_not.respond_to?(:=~)
end
end
end end

View file

@ -15,7 +15,7 @@ describe "Math.log2" do
Math.log2((2**301+45677544234809571)).should == 301.0 Math.log2((2**301+45677544234809571)).should == 301.0
end end
it "raises an Errno::EDOM if the argument is less than 0" do it "raises Math::DomainError if the argument is less than 0" do
-> { Math.log2(-1e-15) }.should raise_error( Math::DomainError) -> { Math.log2(-1e-15) }.should raise_error( Math::DomainError)
end end

View file

@ -532,6 +532,27 @@ describe "Module#include" do
B.foo.should == 'n' B.foo.should == 'n'
end end
end end
it "overrides a previous super method call" do
c1 = Class.new do
def foo
[:c1]
end
end
c2 = Class.new(c1) do
def foo
[:c2] + super
end
end
c2.new.foo.should == [:c2, :c1]
m = Module.new do
def foo
[:m1]
end
end
c2.include(m)
c2.new.foo.should == [:c2, :m1]
end
end end
describe "Module#include?" do describe "Module#include?" do

View file

@ -499,7 +499,7 @@ describe "Module#prepend" do
c.dup.new.should be_kind_of(m) c.dup.new.should be_kind_of(m)
end end
ruby_version_is '0'...'3.0' do ruby_version_is ''...'3.0' do
it "keeps the module in the chain when dupping an intermediate module" do it "keeps the module in the chain when dupping an intermediate module" do
m1 = Module.new { def calc(x) x end } m1 = Module.new { def calc(x) x end }
m2 = Module.new { prepend(m1) } m2 = Module.new { prepend(m1) }

View file

@ -256,7 +256,6 @@ describe :numeric_step, :shared => true do
end end
describe "when no block is given" do describe "when no block is given" do
step_enum_class = Enumerator
step_enum_class = Enumerator::ArithmeticSequence step_enum_class = Enumerator::ArithmeticSequence
ruby_version_is ""..."3.0" do ruby_version_is ""..."3.0" do

View file

@ -21,7 +21,6 @@ describe "Numeric#step" do
it_behaves_like :numeric_step, :step it_behaves_like :numeric_step, :step
describe "when no block is given" do describe "when no block is given" do
step_enum_class = Enumerator
step_enum_class = Enumerator::ArithmeticSequence step_enum_class = Enumerator::ArithmeticSequence
ruby_version_is ""..."3.0" do ruby_version_is ""..."3.0" do
@ -61,7 +60,6 @@ describe "Numeric#step" do
end end
end end
end end
end end
describe 'with keyword arguments' do describe 'with keyword arguments' do

View file

@ -61,9 +61,17 @@ describe "Proc#<<" do
g = proc { |x| x + x } g = proc { |x| x + x }
lambda_proc = -> x { x } lambda_proc = -> x { x }
# lambda << proc
(f << g).is_a?(Proc).should == true (f << g).is_a?(Proc).should == true
(f << g).should_not.lambda? (f << g).should_not.lambda?
# lambda << lambda
(f << lambda_proc).is_a?(Proc).should == true
(f << lambda_proc).should.lambda? (f << lambda_proc).should.lambda?
# proc << lambda
(g << f).is_a?(Proc).should == true
(g << f).should.lambda?
end end
end end

View file

@ -2,7 +2,7 @@ require_relative '../../spec_helper'
require_relative 'shared/equal' require_relative 'shared/equal'
describe "Proc#eql?" do describe "Proc#eql?" do
ruby_version_is "0"..."3.0" do ruby_version_is ""..."3.0" do
it_behaves_like :proc_equal_undefined, :eql? it_behaves_like :proc_equal_undefined, :eql?
end end

View file

@ -2,7 +2,7 @@ require_relative '../../spec_helper'
require_relative 'shared/equal' require_relative 'shared/equal'
describe "Proc#==" do describe "Proc#==" do
ruby_version_is "0"..."3.0" do ruby_version_is ""..."3.0" do
it_behaves_like :proc_equal_undefined, :== it_behaves_like :proc_equal_undefined, :==
end end

View file

@ -1,7 +1,7 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
ruby_version_is ''...'3.2' do describe "Random::DEFAULT" do
describe "Random::DEFAULT" do ruby_version_is ''...'3.2' do
it "returns a random number generator" do it "returns a random number generator" do
suppress_warning do suppress_warning do
Random::DEFAULT.should respond_to(:rand) Random::DEFAULT.should respond_to(:rand)
@ -13,5 +13,33 @@ ruby_version_is ''...'3.2' do
seed2 = ruby_exe('p Random::DEFAULT.seed', options: '--disable-gems') seed2 = ruby_exe('p Random::DEFAULT.seed', options: '--disable-gems')
seed1.should != seed2 seed1.should != seed2
end end
ruby_version_is ''...'3.0' do
it "returns a Random instance" do
suppress_warning do
Random::DEFAULT.should be_an_instance_of(Random)
end
end
end
ruby_version_is '3.0' do
it "refers to the Random class" do
suppress_warning do
Random::DEFAULT.should.equal?(Random)
end
end
it "is deprecated" do
-> {
Random::DEFAULT.should.equal?(Random)
}.should complain(/constant Random::DEFAULT is deprecated/)
end
end
end
ruby_version_is '3.2' do
it "is no longer defined" do
Random.should_not.const_defined?(:DEFAULT)
end
end end
end end

View file

@ -1,6 +0,0 @@
require_relative '../../spec_helper'
require_relative 'shared/urandom'
describe "Random.urandom" do
it_behaves_like :random_urandom, :urandom
end

View file

@ -1,23 +0,0 @@
describe :random_urandom, shared: true do
it "returns a String" do
Random.send(@method, 1).should be_an_instance_of(String)
end
it "returns a String of the length given as argument" do
Random.send(@method, 15).length.should == 15
end
it "raises an ArgumentError on a negative size" do
-> {
Random.send(@method, -1)
}.should raise_error(ArgumentError)
end
it "returns a binary String" do
Random.send(@method, 15).encoding.should == Encoding::BINARY
end
it "returns a random binary String" do
Random.send(@method, 12).should_not == Random.send(@method, 12)
end
end

View file

@ -0,0 +1,25 @@
require_relative '../../spec_helper'
describe "Random.urandom" do
it "returns a String" do
Random.urandom(1).should be_an_instance_of(String)
end
it "returns a String of the length given as argument" do
Random.urandom(15).length.should == 15
end
it "raises an ArgumentError on a negative size" do
-> {
Random.urandom(-1)
}.should raise_error(ArgumentError)
end
it "returns a binary String" do
Random.urandom(15).encoding.should == Encoding::BINARY
end
it "returns a random binary String" do
Random.urandom(12).should_not == Random.urandom(12)
end
end

View file

@ -14,6 +14,6 @@ describe "String.allocate" do
end end
it "returns a binary String" do it "returns a binary String" do
String.new.encoding.should == Encoding::BINARY String.allocate.encoding.should == Encoding::BINARY
end end
end end

View file

@ -2,7 +2,7 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
describe "#String#bytesize" do describe "String#bytesize" do
it "returns the length of self in bytes" do it "returns the length of self in bytes" do
"hello".bytesize.should == 5 "hello".bytesize.should == 5
" ".bytesize.should == 1 " ".bytesize.should == 1

View file

@ -141,6 +141,12 @@ describe "String#[]= with Integer index" do
str.encoding.should equal(Encoding::BINARY) str.encoding.should equal(Encoding::BINARY)
end end
it "updates the string to a compatible encoding" do
str = " "
str[1] = [0xB9].pack("C*")
str.encoding.should == Encoding::ASCII_8BIT
end
it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do it "raises an Encoding::CompatibilityError if the replacement encoding is incompatible" do
str = "あれ" str = "あれ"
rep = "".encode Encoding::EUC_JP rep = "".encode Encoding::EUC_JP

View file

@ -12,9 +12,9 @@ describe :string_length, shared: true do
it "returns the length of a string in different encodings" do it "returns the length of a string in different encodings" do
utf8_str = 'こにちわ' * 100 utf8_str = 'こにちわ' * 100
utf8_str.size.should == 400 utf8_str.send(@method).should == 400
utf8_str.encode(Encoding::UTF_32BE).size.should == 400 utf8_str.encode(Encoding::UTF_32BE).send(@method).should == 400
utf8_str.encode(Encoding::SHIFT_JIS).size.should == 400 utf8_str.encode(Encoding::SHIFT_JIS).send(@method).should == 400
end end
it "returns the length of the new self after encoding is changed" do it "returns the length of the new self after encoding is changed" do
@ -32,24 +32,24 @@ describe :string_length, shared: true do
concat.encoding.should == Encoding::UTF_8 concat.encoding.should == Encoding::UTF_8
concat.bytesize.should == 4 concat.bytesize.should == 4
concat.size.should == 2 concat.send(@method).should == 2
concat.force_encoding(Encoding::ASCII_8BIT) concat.force_encoding(Encoding::ASCII_8BIT)
concat.size.should == 4 concat.send(@method).should == 4
end end
it "adds 1 for every invalid byte in UTF-8" do it "adds 1 for every invalid byte in UTF-8" do
"\xF4\x90\x80\x80".size.should == 4 "\xF4\x90\x80\x80".send(@method).should == 4
"a\xF4\x90\x80\x80b".size.should == 6 "a\xF4\x90\x80\x80b".send(@method).should == 6
"é\xF4\x90\x80\x80è".size.should == 6 "é\xF4\x90\x80\x80è".send(@method).should == 6
end end
it "adds 1 (and not 2) for a incomplete surrogate in UTF-16" do it "adds 1 (and not 2) for a incomplete surrogate in UTF-16" do
"\x00\xd8".force_encoding("UTF-16LE").size.should == 1 "\x00\xd8".force_encoding("UTF-16LE").send(@method).should == 1
"\xd8\x00".force_encoding("UTF-16BE").size.should == 1 "\xd8\x00".force_encoding("UTF-16BE").send(@method).should == 1
end end
it "adds 1 for a broken sequence in UTF-32" do it "adds 1 for a broken sequence in UTF-32" do
"\x04\x03\x02\x01".force_encoding("UTF-32LE").size.should == 1 "\x04\x03\x02\x01".force_encoding("UTF-32LE").send(@method).should == 1
"\x01\x02\x03\x04".force_encoding("UTF-32BE").size.should == 1 "\x01\x02\x03\x04".force_encoding("UTF-32BE").send(@method).should == 1
end end
end end

View file

@ -62,6 +62,10 @@ describe "String#split with String" do
",".split(",", -1).should == ["", ""] ",".split(",", -1).should == ["", ""]
end end
it "raises a RangeError when the limit is larger than int" do
-> { "a,b".split(" ", 2147483649) }.should raise_error(RangeError)
end
it "defaults to $; when string isn't given or nil" do it "defaults to $; when string isn't given or nil" do
suppress_warning do suppress_warning do
old_fs = $; old_fs = $;

View file

@ -122,6 +122,10 @@ module ClassSpecs
end end
end end
end end
DEFINE_CLASS = -> do
class ::A; end
end
end end
class Class class Class

View file

@ -156,6 +156,15 @@ describe "The 'case'-construct" do
end.should == "foo" end.should == "foo"
end end
it "tests an empty array" do
case []
when []
'foo'
else
'bar'
end.should == 'foo'
end
it "expands arrays to lists of values" do it "expands arrays to lists of values" do
case 'z' case 'z'
when *['a', 'b', 'c', 'd'] when *['a', 'b', 'c', 'd']

View file

@ -17,6 +17,19 @@ describe "The class keyword" do
eval "class ClassSpecsKeywordWithoutSemicolon end" eval "class ClassSpecsKeywordWithoutSemicolon end"
ClassSpecsKeywordWithoutSemicolon.should be_an_instance_of(Class) ClassSpecsKeywordWithoutSemicolon.should be_an_instance_of(Class)
end end
it "can redefine a class when called from a block" do
ClassSpecs::DEFINE_CLASS.call
A.should be_an_instance_of(Class)
Object.send(:remove_const, :A)
defined?(A).should be_nil
ClassSpecs::DEFINE_CLASS.call
A.should be_an_instance_of(Class)
ensure
Object.send(:remove_const, :A) if defined?(::A)
end
end end
describe "A class definition" do describe "A class definition" do

View file

@ -718,3 +718,17 @@ describe 'Allowed characters' do
eval("mod::ἍBB").should == 1 eval("mod::ἍBB").should == 1
end end
end end
describe 'Assignment' do
context 'dynamic assignment' do
it 'raises SyntaxError' do
-> do
eval <<-CODE
def test
B = 1
end
CODE
end.should raise_error(SyntaxError, /dynamic constant assignment/)
end
end
end

View file

@ -213,7 +213,7 @@ describe "An instance method with a default argument" do
end end
ruby_version_is '2.7' do ruby_version_is '2.7' do
it "raises a syntaxError an existing method with the same name as the local variable" do it "raises a SyntaxError when there is an existing method with the same name as the local variable" do
def bar def bar
1 1
end end

View file

@ -1297,6 +1297,65 @@ ruby_version_is "2.7" do
a a
RUBY RUBY
end end
it "supports pinning instance variables" do
eval(<<~RUBY).should == true
@a = /a/
case 'abc'
in ^@a
true
end
RUBY
end
it "supports pinning class variables" do
result = nil
Module.new do
result = module_eval(<<~RUBY)
@@a = 0..10
case 2
in ^@@a
true
end
RUBY
end
result.should == true
end
it "supports pinning global variables" do
eval(<<~RUBY).should == true
$a = /a/
case 'abc'
in ^$a
true
end
RUBY
end
it "supports pinning expressions" do
eval(<<~RUBY).should == true
case 'abc'
in ^(/a/)
true
end
RUBY
eval(<<~RUBY).should == true
case {name: '2.6', released_at: Time.new(2018, 12, 25)}
in {released_at: ^(Time.new(2010)..Time.new(2020))}
true
end
RUBY
eval(<<~RUBY).should == true
case 0
in ^(0+0)
true
end
RUBY
end
end end
end end
end end

View file

@ -797,17 +797,6 @@ describe 'Local variable shadowing' do
end end
describe 'Allowed characters' do describe 'Allowed characters' do
# new feature in 2.6 -- https://bugs.ruby-lang.org/issues/13770
it 'does not allow non-ASCII upcased characters at the beginning' do
-> do
eval <<-CODE
def test
BB = 1
end
CODE
end.should raise_error(SyntaxError, /dynamic constant assignment/)
end
it 'allows non-ASCII lowercased characters at the beginning' do it 'allows non-ASCII lowercased characters at the beginning' do
result = nil result = nil
@ -821,6 +810,16 @@ describe 'Allowed characters' do
result.should == 1 result.should == 1
end end
it 'parses a non-ASCII upcased character as a constant identifier' do
-> do
eval <<-CODE
def test
BB = 1
end
CODE
end.should raise_error(SyntaxError, /dynamic constant assignment/)
end
end end
describe "Instance variables" do describe "Instance variables" do

View file

@ -20,4 +20,11 @@ describe "PP.pp" do
other_out.to_s.should == "[1, 2, 3]\n" other_out.to_s.should == "[1, 2, 3]\n"
end end
it 'correctly prints a Hash' do
hash = { 'key' => 42 }
-> {
PP.pp hash
}.should output('{"key"=>42}' + "\n")
end
end end

View file

@ -11,10 +11,63 @@ VALUE proc_spec_rb_proc_new_function(RB_BLOCK_CALL_FUNC_ARGLIST(args, dummy)) {
return rb_funcall(args, rb_intern("inspect"), 0); return rb_funcall(args, rb_intern("inspect"), 0);
} }
VALUE proc_spec_rb_proc_new_function_arg(VALUE arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg) {
return arg;
}
VALUE proc_spec_rb_proc_new_function_argc(VALUE arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg) {
return INT2FIX(argc);
}
VALUE proc_spec_rb_proc_new_function_argv_n(VALUE arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg) {
int n = FIX2INT(arg);
if (n < argc) {
return argv[n];
} else {
rb_exc_raise(rb_exc_new2(rb_eArgError, "Arg index out of bounds."));
}
}
VALUE proc_spec_rb_proc_new_function_callback_arg(VALUE arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg) {
return callback_arg;
}
VALUE proc_spec_rb_proc_new_function_blockarg(VALUE arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg) {
return blockarg;
}
VALUE proc_spec_rb_proc_new_function_block_given_p(VALUE arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg) {
return rb_block_given_p() ? Qtrue : Qfalse;
}
VALUE proc_spec_rb_proc_new(VALUE self) { VALUE proc_spec_rb_proc_new(VALUE self) {
return rb_proc_new(proc_spec_rb_proc_new_function, Qnil); return rb_proc_new(proc_spec_rb_proc_new_function, Qnil);
} }
VALUE proc_spec_rb_proc_new_arg(VALUE self) {
return rb_proc_new(proc_spec_rb_proc_new_function_arg, Qnil);
}
VALUE proc_spec_rb_proc_new_argc(VALUE self) {
return rb_proc_new(proc_spec_rb_proc_new_function_argc, Qnil);
}
VALUE proc_spec_rb_proc_new_argv_n(VALUE self) {
return rb_proc_new(proc_spec_rb_proc_new_function_argv_n, Qnil);
}
VALUE proc_spec_rb_proc_new_callback_arg(VALUE self, VALUE arg) {
return rb_proc_new(proc_spec_rb_proc_new_function_callback_arg, arg);
}
VALUE proc_spec_rb_proc_new_blockarg(VALUE self) {
return rb_proc_new(proc_spec_rb_proc_new_function_blockarg, Qnil);
}
VALUE proc_spec_rb_proc_new_block_given_p(VALUE self) {
return rb_proc_new(proc_spec_rb_proc_new_function_block_given_p, Qnil);
}
VALUE proc_spec_rb_proc_arity(VALUE self, VALUE prc) { VALUE proc_spec_rb_proc_arity(VALUE self, VALUE prc) {
return INT2FIX(rb_proc_arity(prc)); return INT2FIX(rb_proc_arity(prc));
} }
@ -62,6 +115,12 @@ VALUE proc_spec_rb_Proc_new(VALUE self, VALUE scenario) {
void Init_proc_spec(void) { void Init_proc_spec(void) {
VALUE cls = rb_define_class("CApiProcSpecs", rb_cObject); VALUE cls = rb_define_class("CApiProcSpecs", rb_cObject);
rb_define_method(cls, "rb_proc_new", proc_spec_rb_proc_new, 0); rb_define_method(cls, "rb_proc_new", proc_spec_rb_proc_new, 0);
rb_define_method(cls, "rb_proc_new_arg", proc_spec_rb_proc_new_arg, 0);
rb_define_method(cls, "rb_proc_new_argc", proc_spec_rb_proc_new_argc, 0);
rb_define_method(cls, "rb_proc_new_argv_n", proc_spec_rb_proc_new_argv_n, 0);
rb_define_method(cls, "rb_proc_new_callback_arg", proc_spec_rb_proc_new_callback_arg, 1);
rb_define_method(cls, "rb_proc_new_blockarg", proc_spec_rb_proc_new_blockarg, 0);
rb_define_method(cls, "rb_proc_new_block_given_p", proc_spec_rb_proc_new_block_given_p, 0);
rb_define_method(cls, "rb_proc_arity", proc_spec_rb_proc_arity, 1); rb_define_method(cls, "rb_proc_arity", proc_spec_rb_proc_arity, 1);
rb_define_method(cls, "rb_proc_call", proc_spec_rb_proc_call, 2); rb_define_method(cls, "rb_proc_call", proc_spec_rb_proc_call, 2);
rb_define_method(cls, "rb_Proc_new", proc_spec_rb_Proc_new, 1); rb_define_method(cls, "rb_Proc_new", proc_spec_rb_Proc_new, 1);

View file

@ -11,6 +11,10 @@
#define pipe(p) rb_w32_pipe(p) #define pipe(p) rb_w32_pipe(p)
#endif #endif
#ifndef _WIN32
#include <pthread.h>
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -131,6 +135,36 @@ static VALUE thread_spec_rb_thread_create(VALUE self, VALUE proc, VALUE arg) {
return rb_thread_create(thread_spec_call_proc, (void*)args); return rb_thread_create(thread_spec_call_proc, (void*)args);
} }
static VALUE thread_spec_ruby_native_thread_p(VALUE self) {
if (ruby_native_thread_p()) {
return Qtrue;
} else {
return Qfalse;
}
}
static VALUE false_result = Qfalse;
static VALUE true_result = Qtrue;
static void *new_thread_check(void *args) {
if (ruby_native_thread_p()) {
return &true_result;
} else {
return &false_result;
}
}
static VALUE thread_spec_ruby_native_thread_p_new_thread(VALUE self) {
#ifndef _WIN32
pthread_t t;
VALUE *result = &true_result;
pthread_create(&t, NULL, new_thread_check, NULL);
pthread_join(t, (void **)&result);
return *result;
#else
return Qfalse;
#endif
}
void Init_thread_spec(void) { void Init_thread_spec(void) {
VALUE cls = rb_define_class("CApiThreadSpecs", rb_cObject); VALUE cls = rb_define_class("CApiThreadSpecs", rb_cObject);
@ -143,6 +177,8 @@ void Init_thread_spec(void) {
rb_define_method(cls, "rb_thread_wakeup", thread_spec_rb_thread_wakeup, 1); rb_define_method(cls, "rb_thread_wakeup", thread_spec_rb_thread_wakeup, 1);
rb_define_method(cls, "rb_thread_wait_for", thread_spec_rb_thread_wait_for, 2); rb_define_method(cls, "rb_thread_wait_for", thread_spec_rb_thread_wait_for, 2);
rb_define_method(cls, "rb_thread_create", thread_spec_rb_thread_create, 2); rb_define_method(cls, "rb_thread_create", thread_spec_rb_thread_create, 2);
rb_define_method(cls, "ruby_native_thread_p", thread_spec_ruby_native_thread_p, 0);
rb_define_method(cls, "ruby_native_thread_p_new_thread", thread_spec_ruby_native_thread_p_new_thread, 0);
} }
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -7,6 +7,8 @@ describe "C-API Proc function" do
before :each do before :each do
@p = CApiProcSpecs.new @p = CApiProcSpecs.new
@prc = @p.rb_proc_new @prc = @p.rb_proc_new
@prc2 = @p.rb_proc_new_argv_n
@prc3 = @p.rb_proc_new_argc
end end
describe "rb_proc_new" do describe "rb_proc_new" do
@ -15,6 +17,7 @@ describe "C-API Proc function" do
end end
it "calls the C function wrapped by the Proc instance when sent #call" do it "calls the C function wrapped by the Proc instance when sent #call" do
@p.rb_proc_new_arg.call().should == nil
@prc.call(:foo_bar).should == ":foo_bar" @prc.call(:foo_bar).should == ":foo_bar"
@prc.call([:foo, :bar]).should == "[:foo, :bar]" @prc.call([:foo, :bar]).should == "[:foo, :bar]"
end end
@ -24,6 +27,30 @@ describe "C-API Proc function" do
@prc[[:foo, :bar]].should == "[:foo, :bar]" @prc[[:foo, :bar]].should == "[:foo, :bar]"
end end
it "calls the C function with the arg count in argc" do
@prc3.call().should == 0
@prc3.call(:foo).should == 1
@prc3.call(:foo, :bar).should == 2
end
it "calls the C function with arguments in argv" do
@prc2.call(1, :foo).should == :foo
@prc2.call(2, :foo, :bar).should == :bar
-> { @prc2.call(3, :foo, :bar) }.should raise_error(ArgumentError)
end
it "calls the C function with the block passed in blockarg" do
a_block = :foo.to_proc
@p.rb_proc_new_blockarg.call(&a_block).should == a_block
@p.rb_proc_new_blockarg.call().should == nil
end
it "calls the C function and yields to the block passed in blockarg" do
@p.rb_proc_new_block_given_p.call() do
end.should == false
@p.rb_proc_new_block_given_p.call().should == false
end
it "returns a Proc instance correctly described in #inspect without source location" do it "returns a Proc instance correctly described in #inspect without source location" do
@prc.inspect.should =~ /^#<Proc:([^ :@]*?)>$/ @prc.inspect.should =~ /^#<Proc:([^ :@]*?)>$/
end end

View file

@ -101,6 +101,16 @@ describe "C-API Thread function" do
end end
end end
describe "ruby_native_thread_p" do
it "returns non-zero for a ruby thread" do
@t.ruby_native_thread_p.should be_true
end
it "returns zero for a non ruby thread" do
@t.ruby_native_thread_p_new_thread.should be_false
end
end
describe "rb_thread_call_without_gvl" do describe "rb_thread_call_without_gvl" do
it "runs a C function with the global lock unlocked and can be woken by Thread#wakeup" do it "runs a C function with the global lock unlocked and can be woken by Thread#wakeup" do
thr = Thread.new do thr = Thread.new do

View file

@ -5,17 +5,19 @@ require 'rubygems'
require 'rubygems/safe_yaml' require 'rubygems/safe_yaml'
require 'rubygems/commands/owner_command' require 'rubygems/commands/owner_command'
describe "CVE-2019-8322 is resisted by" do platform_is_not :darwin do # frequent timeout/hang on macOS
it "sanitising owner names" do describe "CVE-2019-8322 is resisted by" do
command = Gem::Commands::OwnerCommand.new it "sanitising owner names" do
def command.rubygems_api_request(*args) command = Gem::Commands::OwnerCommand.new
Struct.new(:body).new("---\n- email: \"\e]2;nyan\a\"\n handle: handle\n id: id\n") def command.rubygems_api_request(*args)
Struct.new(:body).new("---\n- email: \"\e]2;nyan\a\"\n handle: handle\n id: id\n")
end
def command.with_response(response)
yield response
end
command.should_receive(:say).with("Owners for gem: name")
command.should_receive(:say).with("- .]2;nyan.")
command.show_owners "name"
end end
def command.with_response(response)
yield response
end
command.should_receive(:say).with("Owners for gem: name")
command.should_receive(:say).with("- .]2;nyan.")
command.show_owners "name"
end end
end end