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-06-26 14:50:14 +02:00
parent f616e81637
commit d3d5ef0cca
74 changed files with 1201 additions and 324 deletions

View file

@ -99,6 +99,9 @@ Lint/DuplicateElsifCondition:
Lint/OutOfRangeRegexpRef: Lint/OutOfRangeRegexpRef:
Enabled: false Enabled: false
Lint/InheritException:
Enabled: false
Lint/ElseLayout: Lint/ElseLayout:
Exclude: Exclude:
- 'language/if_spec.rb' - 'language/if_spec.rb'

View file

@ -50,17 +50,6 @@ Lint/IneffectiveAccessModifier:
- 'core/module/fixtures/classes.rb' - 'core/module/fixtures/classes.rb'
- 'language/fixtures/private.rb' - 'language/fixtures/private.rb'
# Offense count: 6
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: runtime_error, standard_error
Lint/InheritException:
Exclude:
- 'core/enumerator/lazy/fixtures/classes.rb'
- 'core/exception/fixtures/common.rb'
- 'core/module/fixtures/autoload_ex1.rb'
- 'shared/kernel/raise.rb'
# Offense count: 72 # Offense count: 72
# Cop supports --auto-correct. # Cop supports --auto-correct.
Lint/LiteralInInterpolation: Lint/LiteralInInterpolation:

View file

@ -205,6 +205,12 @@ describe "Array#fill with (filler, index, length)" do
-> { [].fill('a', obj) }.should raise_error(TypeError) -> { [].fill('a', obj) }.should raise_error(TypeError)
end end
it "raises a TypeError when the length is not numeric" do
-> { [1, 2, 3].fill("x", 1, "foo") }.should raise_error(TypeError, /no implicit conversion of String into Integer/)
-> { [1, 2, 3].fill("x", 1, :"foo") }.should raise_error(TypeError, /no implicit conversion of Symbol into Integer/)
-> { [1, 2, 3].fill("x", 1, Object.new) }.should raise_error(TypeError, /no implicit conversion of Object into Integer/)
end
not_supported_on :opal do not_supported_on :opal do
it "raises an ArgumentError or RangeError for too-large sizes" do it "raises an ArgumentError or RangeError for too-large sizes" do
error_types = [RangeError, ArgumentError] error_types = [RangeError, ArgumentError]

View file

@ -40,6 +40,68 @@ module ArraySpecs
a a
end end
# Chi squared critical values for tests with n degrees of freedom at 99% confidence.
# Values obtained from NIST Engineering Statistic Handbook at
# https://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm
CHI_SQUARED_CRITICAL_VALUES = [
0,
6.635, 9.210, 11.345, 13.277, 15.086, 16.812, 18.475, 20.090, 21.666, 23.209,
24.725, 26.217, 27.688, 29.141, 30.578, 32.000, 33.409, 34.805, 36.191, 37.566,
38.932, 40.289, 41.638, 42.980, 44.314, 45.642, 46.963, 48.278, 49.588, 50.892,
52.191, 53.486, 54.776, 56.061, 57.342, 58.619, 59.893, 61.162, 62.428, 63.691,
64.950, 66.206, 67.459, 68.710, 69.957, 71.201, 72.443, 73.683, 74.919, 76.154,
77.386, 78.616, 79.843, 81.069, 82.292, 83.513, 84.733, 85.950, 87.166, 88.379,
89.591, 90.802, 92.010, 93.217, 94.422, 95.626, 96.828, 98.028, 99.228, 100.425,
101.621, 102.816, 104.010, 105.202, 106.393, 107.583, 108.771, 109.958, 111.144, 112.329,
113.512, 114.695, 115.876, 117.057, 118.236, 119.414, 120.591, 121.767, 122.942, 124.116,
125.289, 126.462, 127.633, 128.803, 129.973, 131.141, 132.309, 133.476, 134.642, 135.807,
]
def self.measure_sample_fairness(size, samples, iters)
ary = Array.new(size) { |x| x }
(samples).times do |i|
chi_results = []
3.times do
counts = Array.new(size) { 0 }
expected = iters / size
iters.times do
x = ary.sample(samples)[i]
counts[x] += 1
end
chi_squared = 0.0
counts.each do |count|
chi_squared += (((count - expected) ** 2) * 1.0 / expected)
end
chi_results << chi_squared
break if chi_squared <= CHI_SQUARED_CRITICAL_VALUES[size]
end
chi_results.min.should <= CHI_SQUARED_CRITICAL_VALUES[size]
end
end
def self.measure_sample_fairness_large_sample_size(size, samples, iters)
ary = Array.new(size) { |x| x }
counts = Array.new(size) { 0 }
expected = iters * samples / size
iters.times do
ary.sample(samples).each do |sample|
counts[sample] += 1
end
end
chi_squared = 0.0
counts.each do |count|
chi_squared += (((count - expected) ** 2) * 1.0 / expected)
end
# Chi squared critical values for tests with 4 degrees of freedom
# Values obtained from NIST Engineering Statistic Handbook at
# https://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm
chi_squared.should <= CHI_SQUARED_CRITICAL_VALUES[size]
end
class MyArray < Array class MyArray < Array
# The #initialize method has a different signature than Array to help # The #initialize method has a different signature than Array to help
# catch places in the specs that do not assert the #initialize is not # catch places in the specs that do not assert the #initialize is not

View file

@ -3,16 +3,14 @@ require_relative 'fixtures/classes'
describe "Array#sample" do describe "Array#sample" do
it "samples evenly" do it "samples evenly" do
ary = [0, 1, 2, 3] ArraySpecs.measure_sample_fairness(4, 1, 400)
3.times do |i| ArraySpecs.measure_sample_fairness(4, 2, 400)
counts = [0, 0, 0, 0] ArraySpecs.measure_sample_fairness(4, 3, 400)
4000.times do ArraySpecs.measure_sample_fairness(40, 3, 400)
counts[ary.sample(3)[i]] += 1 ArraySpecs.measure_sample_fairness(40, 4, 400)
end ArraySpecs.measure_sample_fairness(40, 8, 400)
counts.each do |count| ArraySpecs.measure_sample_fairness(40, 16, 400)
(800..1200).should include(count) ArraySpecs.measure_sample_fairness_large_sample_size(100, 80, 4000)
end
end
end end
it "returns nil for an empty Array" do it "returns nil for an empty Array" do

View file

@ -41,13 +41,13 @@ describe "Dir.foreach" do
it "accepts an encoding keyword for the encoding of the entries" do it "accepts an encoding keyword for the encoding of the entries" do
dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").to_a.sort dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: "utf-8").to_a.sort
dirs.each {|dir| dir.encoding.should == Encoding::UTF_8} dirs.each { |dir| dir.encoding.should == Encoding::UTF_8 }
dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: Encoding::UTF_16LE).to_a.sort dirs = Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: Encoding::ISO_8859_1).to_a.sort
dirs.each {|dir| dir.encoding.should == Encoding::UTF_16LE} dirs.each { |dir| dir.encoding.should == Encoding::ISO_8859_1 }
Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: Encoding::UTF_16LE) do |f| Dir.foreach("#{DirSpecs.mock_dir}/deeply/nested", encoding: Encoding::ISO_8859_1) do |f|
f.encoding.should == Encoding::UTF_16LE f.encoding.should == Encoding::ISO_8859_1
end end
end end

View file

@ -93,7 +93,7 @@ describe "SignalException" do
platform_is_not :windows do platform_is_not :windows do
it "runs after at_exit" do it "runs after at_exit" do
output = ruby_exe(<<-RUBY, exit_status: nil) output = ruby_exe(<<-RUBY, exit_status: :SIGKILL)
at_exit do at_exit do
puts "hello" puts "hello"
$stdout.flush $stdout.flush
@ -107,7 +107,7 @@ describe "SignalException" do
end end
it "cannot be trapped with Signal.trap" do it "cannot be trapped with Signal.trap" do
ruby_exe(<<-RUBY, exit_status: nil) ruby_exe(<<-RUBY, exit_status: :SIGPROF)
Signal.trap("PROF") {} Signal.trap("PROF") {}
raise(SignalException, "PROF") raise(SignalException, "PROF")
RUBY RUBY
@ -116,7 +116,7 @@ describe "SignalException" do
end end
it "self-signals for USR1" do it "self-signals for USR1" do
ruby_exe("raise(SignalException, 'USR1')", exit_status: nil) ruby_exe("raise(SignalException, 'USR1')", exit_status: :SIGUSR1)
$?.termsig.should == Signal.list.fetch('USR1') $?.termsig.should == Signal.list.fetch('USR1')
end end
end end

View file

@ -494,6 +494,14 @@ describe "File.open" do
File.open(@file, "w") { |f| f.puts "testing" } File.open(@file, "w") { |f| f.puts "testing" }
File.size(@file).should > 0 File.size(@file).should > 0
File.open(@file, "rb+") do |f| File.open(@file, "rb+") do |f|
f.binmode?.should == true
f.external_encoding.should == Encoding::ASCII_8BIT
f.pos.should == 0
f.should_not.eof?
end
File.open(@file, "r+b") do |f|
f.binmode?.should == true
f.external_encoding.should == Encoding::ASCII_8BIT
f.pos.should == 0 f.pos.should == 0
f.should_not.eof? f.should_not.eof?
end end

View file

@ -36,4 +36,8 @@ describe "Float#/" do
-> { 13.0 / "10" }.should raise_error(TypeError) -> { 13.0 / "10" }.should raise_error(TypeError)
-> { 13.0 / :symbol }.should raise_error(TypeError) -> { 13.0 / :symbol }.should raise_error(TypeError)
end end
it "divides correctly by Rational numbers" do
(1.2345678901234567 / Rational(1, 10000000000000000000)).should == 1.2345678901234567e+19
end
end end

View file

@ -103,6 +103,70 @@ describe "Float#round" do
5.55.round(1, half: :up).should eql(5.6) 5.55.round(1, half: :up).should eql(5.6)
5.55.round(1, half: :down).should eql(5.5) 5.55.round(1, half: :down).should eql(5.5)
5.55.round(1, half: :even).should eql(5.6) 5.55.round(1, half: :even).should eql(5.6)
-5.55.round(1, half: nil).should eql(-5.6)
-5.55.round(1, half: :up).should eql(-5.6)
-5.55.round(1, half: :down).should eql(-5.5)
-5.55.round(1, half: :even).should eql(-5.6)
end
it "preserves cases where neighbouring floating pointer number increase the decimal places" do
4.8100000000000005.round(5, half: nil).should eql(4.81)
4.8100000000000005.round(5, half: :up).should eql(4.81)
4.8100000000000005.round(5, half: :down).should eql(4.81)
4.8100000000000005.round(5, half: :even).should eql(4.81)
-4.8100000000000005.round(5, half: nil).should eql(-4.81)
-4.8100000000000005.round(5, half: :up).should eql(-4.81)
-4.8100000000000005.round(5, half: :down).should eql(-4.81)
-4.8100000000000005.round(5, half: :even).should eql(-4.81)
4.81.round(5, half: nil).should eql(4.81)
4.81.round(5, half: :up).should eql(4.81)
4.81.round(5, half: :down).should eql(4.81)
4.81.round(5, half: :even).should eql(4.81)
-4.81.round(5, half: nil).should eql(-4.81)
-4.81.round(5, half: :up).should eql(-4.81)
-4.81.round(5, half: :down).should eql(-4.81)
-4.81.round(5, half: :even).should eql(-4.81)
4.809999999999999.round(5, half: nil).should eql(4.81)
4.809999999999999.round(5, half: :up).should eql(4.81)
4.809999999999999.round(5, half: :down).should eql(4.81)
4.809999999999999.round(5, half: :even).should eql(4.81)
-4.809999999999999.round(5, half: nil).should eql(-4.81)
-4.809999999999999.round(5, half: :up).should eql(-4.81)
-4.809999999999999.round(5, half: :down).should eql(-4.81)
-4.809999999999999.round(5, half: :even).should eql(-4.81)
end
ruby_bug "", ""..."3.3" do
# These numbers are neighbouring floating point numbers round a
# precise value. They test that the rounding modes work correctly
# round that value and precision is not lost which might cause
# incorrect results.
it "does not lose precision during the rounding process" do
767573.1875850001.round(5, half: nil).should eql(767573.18759)
767573.1875850001.round(5, half: :up).should eql(767573.18759)
767573.1875850001.round(5, half: :down).should eql(767573.18759)
767573.1875850001.round(5, half: :even).should eql(767573.18759)
-767573.1875850001.round(5, half: nil).should eql(-767573.18759)
-767573.1875850001.round(5, half: :up).should eql(-767573.18759)
-767573.1875850001.round(5, half: :down).should eql(-767573.18759)
-767573.1875850001.round(5, half: :even).should eql(-767573.18759)
767573.187585.round(5, half: nil).should eql(767573.18759)
767573.187585.round(5, half: :up).should eql(767573.18759)
767573.187585.round(5, half: :down).should eql(767573.18758)
767573.187585.round(5, half: :even).should eql(767573.18758)
-767573.187585.round(5, half: nil).should eql(-767573.18759)
-767573.187585.round(5, half: :up).should eql(-767573.18759)
-767573.187585.round(5, half: :down).should eql(-767573.18758)
-767573.187585.round(5, half: :even).should eql(-767573.18758)
767573.1875849998.round(5, half: nil).should eql(767573.18758)
767573.1875849998.round(5, half: :up).should eql(767573.18758)
767573.1875849998.round(5, half: :down).should eql(767573.18758)
767573.1875849998.round(5, half: :even).should eql(767573.18758)
-767573.1875849998.round(5, half: nil).should eql(-767573.18758)
-767573.1875849998.round(5, half: :up).should eql(-767573.18758)
-767573.1875849998.round(5, half: :down).should eql(-767573.18758)
-767573.1875849998.round(5, half: :even).should eql(-767573.18758)
end
end end
it "raises FloatDomainError for exceptional values with a half option" do it "raises FloatDomainError for exceptional values with a half option" do

View file

@ -223,26 +223,25 @@ describe "Integer#chr with an encoding argument" do
# #5864 # #5864
it "raises RangeError if self is invalid as a codepoint in the specified encoding" do it "raises RangeError if self is invalid as a codepoint in the specified encoding" do
[ [0x80, "US-ASCII"], -> { 0x80.chr("US-ASCII") }.should raise_error(RangeError)
[0x0100, "BINARY"], -> { 0x0100.chr("BINARY") }.should raise_error(RangeError)
[0x0100, "EUC-JP"], -> { 0x0100.chr("EUC-JP") }.should raise_error(RangeError)
[0xA1A0, "EUC-JP"], -> { 0xA1A0.chr("EUC-JP") }.should raise_error(RangeError)
[0xA1, "EUC-JP"], -> { 0xA1.chr("EUC-JP") }.should raise_error(RangeError)
[0x80, "SHIFT_JIS"], -> { 0x80.chr("SHIFT_JIS") }.should raise_error(RangeError)
[0xE0, "SHIFT_JIS"], -> { 0xE0.chr("SHIFT_JIS") }.should raise_error(RangeError)
[0x0100, "ISO-8859-9"], -> { 0x0100.chr("ISO-8859-9") }.should raise_error(RangeError)
[620, "TIS-620"], -> { 620.chr("TIS-620") }.should raise_error(RangeError)
[0xD800, "UTF-8"], # UTF-16 surrogate range
[0xDBFF, "UTF-8"], -> { 0xD800.chr("UTF-8") }.should raise_error(RangeError)
[0xDC00, "UTF-8"], -> { 0xDBFF.chr("UTF-8") }.should raise_error(RangeError)
[0xDFFF, "UTF-8"], -> { 0xDC00.chr("UTF-8") }.should raise_error(RangeError)
[0xD800, "UTF-16"], -> { 0xDFFF.chr("UTF-8") }.should raise_error(RangeError)
[0xDBFF, "UTF-16"], # UTF-16 surrogate range
[0xDC00, "UTF-16"], -> { 0xD800.chr("UTF-16") }.should raise_error(RangeError)
[0xDFFF, "UTF-16"], -> { 0xDBFF.chr("UTF-16") }.should raise_error(RangeError)
].each do |integer, encoding_name| -> { 0xDC00.chr("UTF-16") }.should raise_error(RangeError)
-> { integer.chr(encoding_name) }.should raise_error(RangeError) -> { 0xDFFF.chr("UTF-16") }.should raise_error(RangeError)
end
end end
it 'returns a String encoding self interpreted as a codepoint in the CESU-8 encoding' do it 'returns a String encoding self interpreted as a codepoint in the CESU-8 encoding' do

View file

@ -55,6 +55,11 @@ describe "Integer#fdiv" do
num.fdiv(den).should == -0.5555555555555556 num.fdiv(den).should == -0.5555555555555556
end end
it "rounds to the correct float for bignum denominators" do
1.fdiv(10**324).should == 0.0
1.fdiv(10**323).should == 1.0e-323
end
it "performs floating-point division between self and a Float" do it "performs floating-point division between self and a Float" do
8.fdiv(9.0).should be_close(0.888888888888889, TOLERANCE) 8.fdiv(9.0).should be_close(0.888888888888889, TOLERANCE)
end end

View file

@ -73,21 +73,11 @@ describe "IO#advise" do
end end
end end
platform_is :linux do guard -> { platform_is :linux and kernel_version_is '3.6' } do # [ruby-core:65355] tmpfs is not supported
it "supports the willneed advice type" do it "supports the willneed advice type" do
require 'etc'
uname = if Etc.respond_to?(:uname)
Etc.uname[:release]
else
`uname -r`.chomp
end
if (uname.split('.').map(&:to_i) <=> [3,6]) < 0
skip "[ruby-core:65355] tmpfs is not supported"
else
@io.advise(:willneed).should be_nil @io.advise(:willneed).should be_nil
end end
end end
end
it "raises an IOError if the stream is closed" do it "raises an IOError if the stream is closed" do
@io.close @io.close

View file

@ -108,6 +108,14 @@ module IOSpecs
"linha ", "cinco.\nHere ", "is ", "line ", "six.\n" ] "linha ", "cinco.\nHere ", "is ", "line ", "six.\n" ]
end end
def self.lines_space_separator_without_trailing_spaces
[ "Voici", "la", "ligne", "une.\nQui",
"\303\250", "la", "linea", "due.\n\n\nAqu\303\255",
"est\303\241", "la", "l\303\255nea", "tres.\nHier",
"ist", "Zeile", "vier.\n\nEst\303\241", "aqui", "a",
"linha", "cinco.\nHere", "is", "line", "six.\n" ]
end
def self.lines_arbitrary_separator def self.lines_arbitrary_separator
[ "Voici la ligne une.\nQui \303\250", [ "Voici la ligne une.\nQui \303\250",
" la linea due.\n\n\nAqu\303\255 est\303\241 la l\303\255nea tres.\nHier ist Zeile vier.\n\nEst\303\241 aqui a linha cinco.\nHere is line six.\n" ] " la linea due.\n\n\nAqu\303\255 est\303\241 la l\303\255nea tres.\nHier ist Zeile vier.\n\nEst\303\241 aqui a linha cinco.\nHere is line six.\n" ]
@ -119,6 +127,12 @@ module IOSpecs
"Est\303\241 aqui a linha cinco.\nHere is line six.\n" ] "Est\303\241 aqui a linha cinco.\nHere is line six.\n" ]
end end
def self.paragraphs_without_trailing_new_line_characters
[ "Voici la ligne une.\nQui \303\250 la linea due.",
"Aqu\303\255 est\303\241 la l\303\255nea tres.\nHier ist Zeile vier.",
"Est\303\241 aqui a linha cinco.\nHere is line six.\n" ]
end
# Creates an IO instance for an existing fixture file. The # Creates an IO instance for an existing fixture file. The
# file should obviously not be deleted. # file should obviously not be deleted.
def self.io_fixture(name, mode = "r:utf-8") def self.io_fixture(name, mode = "r:utf-8")

View file

@ -119,6 +119,16 @@ describe "IO#gets" do
it "returns the first line without a trailing newline character" do it "returns the first line without a trailing newline character" do
@io.gets(chomp: true).should == IOSpecs.lines_without_newline_characters[0] @io.gets(chomp: true).should == IOSpecs.lines_without_newline_characters[0]
end end
ruby_version_is "3.0" do
it "raises exception when options passed as Hash" do
-> { @io.gets({ chomp: true }) }.should raise_error(TypeError)
-> {
@io.gets("\n", 1, { chomp: true })
}.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
end
end
end end
end end
@ -200,6 +210,10 @@ describe "IO#gets" do
it "reads all bytes when pass a separator and reading more than all bytes" do it "reads all bytes when pass a separator and reading more than all bytes" do
@io.gets("\t", 100).should == "one\n\ntwo\n\nthree\nfour\n" @io.gets("\t", 100).should == "one\n\ntwo\n\nthree\nfour\n"
end end
it "returns empty string when 0 passed as a limit" do
@io.gets(0).should == ""
end
end end
describe "IO#gets" do describe "IO#gets" do

View file

@ -43,9 +43,40 @@ describe "IO#readline" do
end end
end end
describe "when passed limit" do
it "reads limit bytes" do
@io.readline(3).should == "Voi"
end
it "returns an empty string when passed 0 as a limit" do
@io.readline(0).should == ""
end
end
describe "when passed separator and limit" do
it "reads limit bytes till the separator" do
# Voici la ligne une.\
@io.readline(" ", 4).should == "Voic"
@io.readline(" ", 4).should == "i "
@io.readline(" ", 4).should == "la "
@io.readline(" ", 4).should == "lign"
@io.readline(" ", 4).should == "e "
end
end
describe "when passed chomp" do describe "when passed chomp" do
it "returns the first line without a trailing newline character" do it "returns the first line without a trailing newline character" do
@io.readline(chomp: true).should == IOSpecs.lines_without_newline_characters[0] @io.readline(chomp: true).should == IOSpecs.lines_without_newline_characters[0]
end end
ruby_version_is "3.0" do
it "raises exception when options passed as Hash" do
-> { @io.readline({ chomp: true }) }.should raise_error(TypeError)
-> {
@io.readline("\n", 1, { chomp: true })
}.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
end
end
end end
end end

View file

@ -101,6 +101,28 @@ describe "IO#readlines" do
@io.readlines(obj).should == IOSpecs.lines_r_separator @io.readlines(obj).should == IOSpecs.lines_r_separator
end end
end end
describe "when passed limit" do
it "raises ArgumentError when passed 0 as a limit" do
-> { @io.readlines(0) }.should raise_error(ArgumentError)
end
end
describe "when passed chomp" do
it "returns the first line without a trailing newline character" do
@io.readlines(chomp: true).should == IOSpecs.lines_without_newline_characters
end
ruby_version_is "3.0" do
it "raises exception when options passed as Hash" do
-> { @io.readlines({ chomp: true }) }.should raise_error(TypeError)
-> {
@io.readlines("\n", 1, { chomp: true })
}.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
end
end
end
end end
describe "IO#readlines" do describe "IO#readlines" do

View file

@ -161,6 +161,54 @@ describe :io_each, shared: true do
@io.send(@method, chomp: true) { |s| ScratchPad << s } @io.send(@method, chomp: true) { |s| ScratchPad << s }
ScratchPad.recorded.should == IOSpecs.lines_without_newline_characters ScratchPad.recorded.should == IOSpecs.lines_without_newline_characters
end end
ruby_version_is "3.0" do
it "raises exception when options passed as Hash" do
-> {
@io.send(@method, { chomp: true }) { |s| }
}.should raise_error(TypeError)
-> {
@io.send(@method, "\n", 1, { chomp: true }) { |s| }
}.should raise_error(ArgumentError, "wrong number of arguments (given 3, expected 0..2)")
end
end
end
describe "when passed chomp and a separator" do
it "yields each line without separator to the passed block" do
@io.send(@method, " ", chomp: true) { |s| ScratchPad << s }
ScratchPad.recorded.should == IOSpecs.lines_space_separator_without_trailing_spaces
end
end
describe "when passed chomp and empty line as a separator" do
it "yields each paragraph without trailing new line characters" do
@io.send(@method, "", 1024, chomp: true) { |s| ScratchPad << s }
ScratchPad.recorded.should == IOSpecs.paragraphs_without_trailing_new_line_characters
end
end
describe "when passed chomp and nil as a separator" do
it "yields self's content without trailing new line character" do
@io.pos = 100
@io.send(@method, nil, chomp: true) { |s| ScratchPad << s }
ScratchPad.recorded.should == ["qui a linha cinco.\nHere is line six."]
end
end
describe "when passed chomp, nil as a separator, and a limit" do
it "yields each line of limit size without truncating trailing new line character" do
# 43 - is a size of the 1st paragraph in the file
@io.send(@method, nil, 43, chomp: true) { |s| ScratchPad << s }
ScratchPad.recorded.should == [
"Voici la ligne une.\nQui è la linea due.\n\n\n",
"Aquí está la línea tres.\n" + "Hier ist Zeile ",
"vier.\n\nEstá aqui a linha cinco.\nHere is li",
"ne six.\n"
]
end
end end
end end

View file

@ -78,6 +78,14 @@ describe :io_readlines_options_19, shared: true do
result = IO.send(@method, @name, -2, &@object) result = IO.send(@method, @name, -2, &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines (result ? result : ScratchPad.recorded).should == IOSpecs.lines
end end
ruby_bug "#18767", ""..."3.3" do
describe "when passed limit" do
it "raises ArgumentError when passed 0 as a limit" do
-> { IO.send(@method, @name, 0, &@object) }.should raise_error(ArgumentError)
end
end
end
end end
describe "when the object is a String" do describe "when the object is a String" do
@ -92,31 +100,35 @@ describe :io_readlines_options_19, shared: true do
end end
end end
describe "when the object is a Hash" do describe "when the object is an options Hash" do
it "uses the value as the options hash" do ruby_version_is "3.0" do
it "raises TypeError exception" do
-> {
IO.send(@method, @name, { chomp: true }, &@object)
}.should raise_error(TypeError)
end
end
end
describe "when the object is neither Integer nor String" do
it "raises TypeError exception" do
obj = mock("not io readlines limit")
-> {
IO.send(@method, @name, obj, &@object)
}.should raise_error(TypeError)
end
end
end
describe "when passed name, keyword arguments" do
it "uses the keyword arguments as options" do
result = IO.send(@method, @name, mode: "r", &@object) result = IO.send(@method, @name, mode: "r", &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines (result ? result : ScratchPad.recorded).should == IOSpecs.lines
end end
end end
end
describe "when passed name, object, object" do describe "when passed name, object, object" do
describe "when the first object is an Integer" do
it "uses the second object as an options Hash" do
-> do
IO.send(@method, @filename, 10, mode: "w", &@object)
end.should raise_error(IOError)
end
it "calls #to_hash to convert the second object to a Hash" do
options = mock("io readlines options Hash")
options.should_receive(:to_hash).and_return({ mode: "w" })
-> do
IO.send(@method, @filename, 10, **options, &@object)
end.should raise_error(IOError)
end
end
describe "when the first object is a String" do describe "when the first object is a String" do
it "uses the second object as a limit if it is an Integer" do it "uses the second object as a limit if it is an Integer" do
result = IO.send(@method, @name, " ", 10, &@object) result = IO.send(@method, @name, " ", 10, &@object)
@ -129,32 +141,18 @@ describe :io_readlines_options_19, shared: true do
result = IO.send(@method, @name, " ", limit, &@object) result = IO.send(@method, @name, " ", limit, &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
end end
it "uses the second object as an options Hash" do
-> do
IO.send(@method, @filename, " ", mode: "w", &@object)
end.should raise_error(IOError)
end
it "calls #to_hash to convert the second object to a Hash" do
options = mock("io readlines options Hash")
options.should_receive(:to_hash).and_return({ mode: "w" })
-> do
IO.send(@method, @filename, " ", **options, &@object)
end.should raise_error(IOError)
end
end end
describe "when the first object is not a String or Integer" do describe "when the first object is not a String or Integer" do
it "calls #to_str to convert the object to a String" do it "calls #to_str to convert the object to a String" do
sep = mock("io readlines separator") sep = mock("io readlines separator")
sep.should_receive(:to_str).at_least(1).and_return(" ") sep.should_receive(:to_str).at_least(1).and_return(" ")
result = IO.send(@method, @name, sep, 10, mode: "r", &@object) result = IO.send(@method, @name, sep, 10, &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
end end
it "uses the second object as a limit if it is an Integer" do it "uses the second object as a limit if it is an Integer" do
result = IO.send(@method, @name, " ", 10, mode: "r", &@object) result = IO.send(@method, @name, " ", 10, &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
end end
@ -164,24 +162,59 @@ describe :io_readlines_options_19, shared: true do
result = IO.send(@method, @name, " ", limit, &@object) result = IO.send(@method, @name, " ", limit, &@object)
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
end end
end
it "uses the second object as an options Hash" do describe "when the second object is neither Integer nor String" do
it "raises TypeError exception" do
obj = mock("not io readlines limit")
-> {
IO.send(@method, @name, " ", obj, &@object)
}.should raise_error(TypeError)
end
end
describe "when the second object is an options Hash" do
ruby_version_is "3.0" do
it "raises TypeError exception" do
-> {
IO.send(@method, @name, "", { chomp: true }, &@object)
}.should raise_error(TypeError)
end
end
end
end
describe "when passed name, object, keyword arguments" do
describe "when the first object is an Integer" do
it "uses the keyword arguments as options" do
-> do
IO.send(@method, @filename, 10, mode: "w", &@object)
end.should raise_error(IOError)
end
end
describe "when the first object is a String" do
it "uses the keyword arguments as options" do
-> do -> do
IO.send(@method, @filename, " ", mode: "w", &@object) IO.send(@method, @filename, " ", mode: "w", &@object)
end.should raise_error(IOError) end.should raise_error(IOError)
end end
end
describe "when the first object is not a String or Integer" do
it "uses the keyword arguments as options" do
sep = mock("io readlines separator")
sep.should_receive(:to_str).at_least(1).and_return(" ")
it "calls #to_hash to convert the second object to a Hash" do
options = mock("io readlines options Hash")
options.should_receive(:to_hash).and_return({ mode: "w" })
-> do -> do
IO.send(@method, @filename, " ", **options, &@object) IO.send(@method, @filename, sep, mode: "w", &@object)
end.should raise_error(IOError) end.should raise_error(IOError)
end end
end end
end end
describe "when passed name, separator, limit, options" do describe "when passed name, separator, limit, keyword arguments" do
it "calls #to_path to convert the name object" do it "calls #to_path to convert the name object" do
name = mock("io name to_path") name = mock("io name to_path")
name.should_receive(:to_path).and_return(@name) name.should_receive(:to_path).and_return(@name)
@ -203,12 +236,24 @@ describe :io_readlines_options_19, shared: true do
(result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit (result ? result : ScratchPad.recorded).should == IOSpecs.lines_space_separator_limit
end end
it "calls #to_hash to convert the options object" do it "uses the keyword arguments as options" do
options = mock("io readlines options Hash")
options.should_receive(:to_hash).and_return({ mode: "w" })
-> do -> do
IO.send(@method, @filename, " ", 10, **options, &@object) IO.send(@method, @filename, " ", 10, mode: "w", &@object)
end.should raise_error(IOError) end.should raise_error(IOError)
end end
describe "when passed chomp, nil as a separator, and a limit" do
it "yields each line of limit size without truncating trailing new line character" do
# 43 - is a size of the 1st paragraph in the file
result = IO.send(@method, @name, nil, 43, chomp: true, &@object)
(result ? result : ScratchPad.recorded).should == [
"Voici la ligne une.\nQui è la linea due.\n\n\n",
"Aquí está la línea tres.\n" + "Hier ist Zeile ",
"vier.\n\nEstá aqui a linha cinco.\nHere is li",
"ne six.\n"
]
end
end
end end
end end

View file

@ -67,6 +67,12 @@ describe "Kernel#instance_variable_get when passed Symbol" do
it "raises a NameError when the passed Symbol is an invalid instance variable name" do it "raises a NameError when the passed Symbol is an invalid instance variable name" do
-> { @obj.instance_variable_get(:"@0") }.should raise_error(NameError) -> { @obj.instance_variable_get(:"@0") }.should raise_error(NameError)
end end
it "returns nil or raises for frozen objects" do
nil.instance_variable_get(:@foo).should == nil
-> { nil.instance_variable_get(:foo) }.should raise_error(NameError)
:foo.instance_variable_get(:@foo).should == nil
end
end end
describe "Kernel#instance_variable_get when passed String" do describe "Kernel#instance_variable_get when passed String" do

View file

@ -95,5 +95,11 @@ describe "Kernel#instance_variable_set" do
o.instance_variable_set(:@💙, 42) o.instance_variable_set(:@💙, 42)
o.instance_variable_get(:@💙).should == 42 o.instance_variable_get(:@💙).should == 42
end end
it "raises for frozen objects" do
-> { nil.instance_variable_set(:@foo, 42) }.should raise_error(FrozenError)
-> { nil.instance_variable_set(:foo, 42) }.should raise_error(NameError)
-> { :foo.instance_variable_set(:@foo, 42) }.should raise_error(FrozenError)
end
end end
end end

View file

@ -41,6 +41,19 @@ describe "Kernel#remove_instance_variable" do
end.should raise_error(TypeError) end.should raise_error(TypeError)
end end
it "raises a FrozenError if self is frozen" do
o = Object.new
o.freeze
-> { o.remove_instance_variable(:@foo) }.should raise_error(FrozenError)
-> { o.remove_instance_variable(:foo) }.should raise_error(NameError)
end
it "raises for frozen objects" do
-> { nil.remove_instance_variable(:@foo) }.should raise_error(FrozenError)
-> { nil.remove_instance_variable(:foo) }.should raise_error(NameError)
-> { :foo.remove_instance_variable(:@foo) }.should raise_error(FrozenError)
end
describe "when passed a String" do describe "when passed a String" do
it_behaves_like :kernel_remove_instance_variable, nil, "@greeting" it_behaves_like :kernel_remove_instance_variable, nil, "@greeting"
end end

View file

@ -568,6 +568,25 @@ describe :kernel_require, shared: true do
-> { @object.require("unicode_normalize") }.should raise_error(LoadError) -> { @object.require("unicode_normalize") }.should raise_error(LoadError)
end end
ruby_version_is "3.0" do
it "does not load a file earlier on the $LOAD_PATH when other similar features were already loaded" do
Dir.chdir CODE_LOADING_DIR do
@object.send(@method, "../code/load_fixture").should be_true
end
ScratchPad.recorded.should == [:loaded]
$LOAD_PATH.unshift "#{CODE_LOADING_DIR}/b"
# This loads because the above load was not on the $LOAD_PATH
@object.send(@method, "load_fixture").should be_true
ScratchPad.recorded.should == [:loaded, :loaded]
$LOAD_PATH.unshift "#{CODE_LOADING_DIR}/c"
# This does not load because the above load was on the $LOAD_PATH
@object.send(@method, "load_fixture").should be_false
ScratchPad.recorded.should == [:loaded, :loaded]
end
end
end end
describe "(shell expansion)" do describe "(shell expansion)" do

View file

@ -45,6 +45,12 @@ describe "Math.ldexp" do
it "accepts any second argument that can be coerced with Integer()" do it "accepts any second argument that can be coerced with Integer()" do
Math.ldexp(3.23, MathSpecs::Integer.new).should be_close(12.92, TOLERANCE) Math.ldexp(3.23, MathSpecs::Integer.new).should be_close(12.92, TOLERANCE)
end end
it "returns correct value that closes to the max value of double type" do
Math.ldexp(0.5122058490966879, 1024).should == 9.207889385574391e+307
Math.ldexp(0.9999999999999999, 1024).should == 1.7976931348623157e+308
Math.ldexp(0.99999999999999999, 1024).should == Float::INFINITY
end
end end
describe "Math#ldexp" do describe "Math#ldexp" do

View file

@ -23,4 +23,12 @@ describe "Module#class_variables" do
c.extend ModuleSpecs::MVars c.extend ModuleSpecs::MVars
c.class_variables.should_not include(:@@mvar) c.class_variables.should_not include(:@@mvar)
end end
it "returns the correct class variables when inherit is given" do
ModuleSpecs::SubCVars.class_variables(false).should == [:@@sub]
ModuleSpecs::SubCVars.new.singleton_class.class_variables(false).should == []
ModuleSpecs::SubCVars.class_variables(true).should == [:@@sub, :@@cls, :@@meta]
ModuleSpecs::SubCVars.new.singleton_class.class_variables(true).should == [:@@sub, :@@cls, :@@meta]
end
end end

View file

@ -352,6 +352,10 @@ module ModuleSpecs
end end
end end
class SubCVars < CVars
@@sub = :sub
end
module MVars module MVars
@@mvar = :mvar @@mvar = :mvar
end end

View file

@ -52,7 +52,7 @@ describe "Process.clock_gettime" do
end end
# These specs need macOS 10.12+ / darwin 16+ # These specs need macOS 10.12+ / darwin 16+
guard_not -> { platform_is_not(:darwin) or RUBY_PLATFORM[/darwin\d+/].to_i >= 16 } do guard -> { platform_is_not(:darwin) or kernel_version_is '16' } do
platform_is :linux, :openbsd, :darwin do platform_is :linux, :openbsd, :darwin do
it "CLOCK_PROCESS_CPUTIME_ID" do it "CLOCK_PROCESS_CPUTIME_ID" do
Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID).should be_an_instance_of(Float) Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID).should be_an_instance_of(Float)
@ -65,20 +65,6 @@ describe "Process.clock_gettime" do
end end
end end
platform_is :freebsd, :openbsd do
it "CLOCK_VIRTUAL" do
Process.clock_gettime(Process::CLOCK_VIRTUAL).should be_an_instance_of(Float)
end
it "CLOCK_PROF" do
Process.clock_gettime(Process::CLOCK_PROF).should be_an_instance_of(Float)
end
it "CLOCK_UPTIME" do
Process.clock_gettime(Process::CLOCK_UPTIME).should be_an_instance_of(Float)
end
end
platform_is :linux, :darwin do platform_is :linux, :darwin do
it "CLOCK_MONOTONIC_RAW" do it "CLOCK_MONOTONIC_RAW" do
Process.clock_gettime(Process::CLOCK_MONOTONIC_RAW).should be_an_instance_of(Float) Process.clock_gettime(Process::CLOCK_MONOTONIC_RAW).should be_an_instance_of(Float)
@ -95,6 +81,21 @@ describe "Process.clock_gettime" do
Process.clock_gettime(Process::CLOCK_UPTIME_RAW_APPROX).should be_an_instance_of(Float) Process.clock_gettime(Process::CLOCK_UPTIME_RAW_APPROX).should be_an_instance_of(Float)
end end
end end
end
platform_is :freebsd, :openbsd do
it "CLOCK_VIRTUAL" do
Process.clock_gettime(Process::CLOCK_VIRTUAL).should be_an_instance_of(Float)
end
it "CLOCK_PROF" do
Process.clock_gettime(Process::CLOCK_PROF).should be_an_instance_of(Float)
end
it "CLOCK_UPTIME" do
Process.clock_gettime(Process::CLOCK_UPTIME).should be_an_instance_of(Float)
end
end
platform_is :freebsd do platform_is :freebsd do
it "CLOCK_REALTIME_FAST and CLOCK_REALTIME_PRECISE" do it "CLOCK_REALTIME_FAST and CLOCK_REALTIME_PRECISE" do
@ -117,21 +118,33 @@ describe "Process.clock_gettime" do
end end
end end
platform_is :linux do guard -> { platform_is :linux and kernel_version_is '2.6.32' } do
it "CLOCK_REALTIME_COARSE and CLOCK_REALTIME_ALARM" do it "CLOCK_REALTIME_COARSE" do
Process.clock_gettime(Process::CLOCK_REALTIME_COARSE).should be_an_instance_of(Float) Process.clock_gettime(Process::CLOCK_REALTIME_COARSE).should be_an_instance_of(Float)
Process.clock_gettime(Process::CLOCK_REALTIME_ALARM).should be_an_instance_of(Float)
end end
it "CLOCK_MONOTONIC_COARSE" do it "CLOCK_MONOTONIC_COARSE" do
Process.clock_gettime(Process::CLOCK_MONOTONIC_COARSE).should be_an_instance_of(Float) Process.clock_gettime(Process::CLOCK_MONOTONIC_COARSE).should be_an_instance_of(Float)
end end
end
it "CLOCK_BOOTTIME and CLOCK_BOOTTIME_ALARM" do guard -> { platform_is :linux and kernel_version_is '2.6.39' } do
it "CLOCK_BOOTTIME" do
skip "No Process::CLOCK_BOOTTIME" unless defined?(Process::CLOCK_BOOTTIME)
Process.clock_gettime(Process::CLOCK_BOOTTIME).should be_an_instance_of(Float) Process.clock_gettime(Process::CLOCK_BOOTTIME).should be_an_instance_of(Float)
end
end
guard -> { platform_is "x86_64-linux" and kernel_version_is '3.0' } do
it "CLOCK_REALTIME_ALARM" do
skip "No Process::CLOCK_REALTIME_ALARM" unless defined?(Process::CLOCK_REALTIME_ALARM)
Process.clock_gettime(Process::CLOCK_REALTIME_ALARM).should be_an_instance_of(Float)
end
it "CLOCK_BOOTTIME_ALARM" do
skip "No Process::CLOCK_BOOTTIME_ALARM" unless defined?(Process::CLOCK_BOOTTIME_ALARM)
Process.clock_gettime(Process::CLOCK_BOOTTIME_ALARM).should be_an_instance_of(Float) Process.clock_gettime(Process::CLOCK_BOOTTIME_ALARM).should be_an_instance_of(Float)
end end
end end
end end
end
end end

View file

@ -15,5 +15,44 @@ describe "Process.egid" do
end end
describe "Process.egid=" do describe "Process.egid=" do
it "needs to be reviewed for spec completeness"
platform_is_not :windows do
it "raises TypeError if not passed an Integer or String" do
-> { Process.egid = Object.new }.should raise_error(TypeError)
end
it "sets the effective group id to its own gid if given the username corresponding to its own gid" do
raise unless Process.gid == Process.egid
require "etc"
group = Etc.getgrgid(Process.gid).name
Process.egid = group
Process.egid.should == Process.gid
end
as_user do
it "raises Errno::ERPERM if run by a non superuser trying to set the root group id" do
-> { Process.egid = 0 }.should raise_error(Errno::EPERM)
end
platform_is :linux do
it "raises Errno::ERPERM if run by a non superuser trying to set the group id from group name" do
-> { Process.egid = "root" }.should raise_error(Errno::EPERM)
end
end
end
as_superuser do
context "when ran by a superuser" do
it "sets the effective group id for the current process if run by a superuser" do
code = <<-RUBY
Process.egid = 1
puts Process.egid
RUBY
ruby_exe(code).should == "1\n"
end
end
end
end
end end

View file

@ -21,9 +21,19 @@ describe "Process.euid=" do
-> { Process.euid = Object.new }.should raise_error(TypeError) -> { Process.euid = Object.new }.should raise_error(TypeError)
end end
it "sets the effective user id to its own uid if given the username corresponding to its own uid" do
raise unless Process.uid == Process.euid
require "etc"
user = Etc.getpwuid(Process.uid).name
Process.euid = user
Process.euid.should == Process.uid
end
as_user do as_user do
it "raises Errno::ERPERM if run by a non superuser trying to set the superuser id" do it "raises Errno::ERPERM if run by a non superuser trying to set the superuser id" do
-> { (Process.euid = 0)}.should raise_error(Errno::EPERM) -> { Process.euid = 0 }.should raise_error(Errno::EPERM)
end end
it "raises Errno::ERPERM if run by a non superuser trying to set the superuser id from username" do it "raises Errno::ERPERM if run by a non superuser trying to set the superuser id from username" do

View file

@ -349,7 +349,7 @@ describe "Process.spawn" do
pgid = Process.getpgid(Process.pid) pgid = Process.getpgid(Process.pid)
# The process group is not available on all platforms. # The process group is not available on all platforms.
# See "man proc" - /proc/[pid]/stat - (5) pgrp # See "man proc" - /proc/[pid]/stat - (5) pgrp
# In Travis arm64 environment, the value is 0. # In Travis aarch64 environment, the value is 0.
# #
# $ cat /proc/[pid]/stat # $ cat /proc/[pid]/stat
# 19179 (ruby) S 19160 0 0 ... # 19179 (ruby) S 19160 0 0 ...

View file

@ -8,7 +8,7 @@ describe "Process::Status#==" do
end end
it "returns true when compared to the integer status of a terminated child" do it "returns true when compared to the integer status of a terminated child" do
ruby_exe("Process.kill(:KILL, $$); exit(29)", exit_status: platform_is(:windows) ? 0 : nil) ruby_exe("Process.kill(:KILL, $$); exit(29)", exit_status: platform_is(:windows) ? 0 : :SIGKILL)
$?.to_i.should == $? $?.to_i.should == $?
$?.should == $?.to_i $?.should == $?.to_i
end end

View file

@ -14,7 +14,7 @@ describe "Process::Status#exited?" do
describe "for a terminated child" do describe "for a terminated child" do
before :each do before :each do
ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : nil) ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : :SIGKILL)
end end
platform_is_not :windows do platform_is_not :windows do

View file

@ -11,7 +11,7 @@ describe "Process::Status#exitstatus" do
describe "for a child that raised SignalException" do describe "for a child that raised SignalException" do
before :each do before :each do
ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : nil) ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : :SIGKILL)
end end
platform_is_not :windows do platform_is_not :windows do

View file

@ -13,7 +13,7 @@ describe "Process::Status#signaled?" do
describe "for a terminated child" do describe "for a terminated child" do
before :each do before :each do
ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : nil) ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : :SIGKILL)
end end
platform_is_not :windows do platform_is_not :windows do

View file

@ -23,7 +23,7 @@ describe "Process::Status#success?" do
describe "for a child that was terminated" do describe "for a child that was terminated" do
before :each do before :each do
ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : nil) ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : :SIGKILL)
end end
platform_is_not :windows do platform_is_not :windows do

View file

@ -13,7 +13,7 @@ describe "Process::Status#termsig" do
describe "for a child that raised SignalException" do describe "for a child that raised SignalException" do
before :each do before :each do
ruby_exe("raise SignalException, 'SIGTERM'", exit_status: nil) ruby_exe("raise SignalException, 'SIGTERM'", exit_status: :SIGTERM)
end end
platform_is_not :windows do platform_is_not :windows do
@ -25,7 +25,7 @@ describe "Process::Status#termsig" do
describe "for a child that was sent a signal" do describe "for a child that was sent a signal" do
before :each do before :each do
ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : nil) ruby_exe("Process.kill(:KILL, $$); exit(42)", exit_status: platform_is(:windows) ? 0 : :SIGKILL)
end end
platform_is_not :windows do platform_is_not :windows do

View file

@ -7,7 +7,7 @@ describe "Process::Status#to_i" do
end end
it "returns an integer when the child is signaled" do it "returns an integer when the child is signaled" do
ruby_exe('raise SignalException, "TERM"', exit_status: platform_is(:windows) ? 3 : nil) ruby_exe('raise SignalException, "TERM"', exit_status: platform_is(:windows) ? 3 : :SIGTERM)
$?.to_i.should be_an_instance_of(Integer) $?.to_i.should be_an_instance_of(Integer)
end end
end end

View file

@ -12,6 +12,11 @@ describe :regexp_quote, shared: true do
Regexp.send(@method, :symbol).should == 'symbol' Regexp.send(@method, :symbol).should == 'symbol'
end end
it "works with substrings" do
str = ".+[]()"[1...-1]
Regexp.send(@method, str).should == '\+\[\]\('
end
it "sets the encoding of the result to US-ASCII if there are only US-ASCII characters present in the input String" do it "sets the encoding of the result to US-ASCII if there are only US-ASCII characters present in the input String" do
str = "abc".force_encoding("euc-jp") str = "abc".force_encoding("euc-jp")
Regexp.send(@method, str).encoding.should == Encoding::US_ASCII Regexp.send(@method, str).encoding.should == Encoding::US_ASCII

View file

@ -254,12 +254,10 @@ describe "Signal.trap" do
r.close r.close
loop { w.write("a"*1024) } loop { w.write("a"*1024) }
RUBY RUBY
out = ruby_exe(code, exit_status: nil) out = ruby_exe(code, exit_status: :SIGPIPE)
status = $? status = $?
out.should == "nil\n" out.should == "nil\n"
status.should.signaled? status.should.signaled?
status.termsig.should be_kind_of(Integer)
Signal.signame(status.termsig).should == "PIPE"
end end
end end

View file

@ -35,6 +35,10 @@ describe "String#capitalize" do
it "does not capitalize non-ASCII characters" do it "does not capitalize non-ASCII characters" do
"ßet".capitalize(:ascii).should == "ßet" "ßet".capitalize(:ascii).should == "ßet"
end end
it "handles non-ASCII substrings properly" do
"garçon"[1..-1].capitalize(:ascii).should == "Arçon"
end
end end
describe "full Unicode case mapping adapted for Turkic languages" do describe "full Unicode case mapping adapted for Turkic languages" do

View file

@ -49,4 +49,13 @@ describe "String#dup" do
orig.should == "xtring" orig.should == "xtring"
dup.should == "string" dup.should == "string"
end end
it "does not modify the original setbyte-mutated string when changing dupped string" do
orig = "a"
orig.setbyte 0, "b".ord
copy = orig.dup
orig.setbyte 0, "c".ord
orig.should == "c"
copy.should == "b"
end
end end

View file

@ -69,4 +69,13 @@ describe "String#insert with index, other" do
"あれ".insert 0, pat "あれ".insert 0, pat
end.should raise_error(Encoding::CompatibilityError) end.should raise_error(Encoding::CompatibilityError)
end end
it "should not call subclassed string methods" do
cls = Class.new(String) do
def replace(arg)
raise "should not call replace"
end
end
cls.new("abcd").insert(0, 'X').should == "Xabcd"
end
end end

View file

@ -50,4 +50,10 @@ describe "String#lstrip!" do
-> { "hello".freeze.lstrip! }.should raise_error(FrozenError) -> { "hello".freeze.lstrip! }.should raise_error(FrozenError)
-> { "".freeze.lstrip! }.should raise_error(FrozenError) -> { "".freeze.lstrip! }.should raise_error(FrozenError)
end end
it "raises an ArgumentError if the first codepoint is invalid" do
s = "\xDFabc".force_encoding(Encoding::UTF_8)
s.valid_encoding?.should be_false
-> { s.lstrip! }.should raise_error(ArgumentError)
end
end end

View file

@ -46,4 +46,10 @@ describe "String#rstrip!" do
-> { "hello".freeze.rstrip! }.should raise_error(FrozenError) -> { "hello".freeze.rstrip! }.should raise_error(FrozenError)
-> { "".freeze.rstrip! }.should raise_error(FrozenError) -> { "".freeze.rstrip! }.should raise_error(FrozenError)
end end
it "raises an ArgumentError if the last codepoint is invalid" do
s = "abc\xDF".force_encoding(Encoding::UTF_8)
s.valid_encoding?.should be_false
-> { s.rstrip! }.should raise_error(ArgumentError)
end
end end

View file

@ -14,6 +14,11 @@ describe "String#scrub with a default replacement" do
"abc\u3042#{x81}".scrub.should == "abc\u3042\uFFFD" "abc\u3042#{x81}".scrub.should == "abc\u3042\uFFFD"
end end
it "replaces invalid byte sequences in lazy substrings" do
x81 = [0x81].pack('C').force_encoding('utf-8')
"abc\u3042#{x81}def"[1...-1].scrub.should == "bc\u3042\uFFFDde"
end
it "returns a copy of self when the input encoding is BINARY" do it "returns a copy of self when the input encoding is BINARY" do
input = "foo".encode('BINARY') input = "foo".encode('BINARY')

View file

@ -375,10 +375,20 @@ describe :string_slice_regexp_index, shared: true do
"hello there".send(@method, /(what?)/, 1).should == nil "hello there".send(@method, /(what?)/, 1).should == nil
end end
it "returns nil if the index is larger than the number of captures" do
"hello there".send(@method, /hello (.)/, 2).should == nil
# You can't refer to 0 using negative indices
"hello there".send(@method, /hello (.)/, -2).should == nil
end
it "returns nil if there is no capture for the given index" do it "returns nil if there is no capture for the given index" do
"hello there".send(@method, /[aeiou](.)\1/, 2).should == nil "hello there".send(@method, /[aeiou](.)\1/, 2).should == nil
# You can't refer to 0 using negative indices end
"hello there".send(@method, /[aeiou](.)\1/, -2).should == nil
it "returns nil if the given capture group was not matched but still sets $~" do
"test".send(@method, /te(z)?/, 1).should == nil
$~[0].should == "te"
$~[1].should == nil
end end
it "calls to_int on the given index" do it "calls to_int on the given index" do

View file

@ -3,12 +3,17 @@ require_relative '../../spec_helper'
require_relative 'fixtures/classes' require_relative 'fixtures/classes'
describe "String#split with String" do describe "String#split with String" do
it "throws an ArgumentError if the string is not a valid" do
s = "\xDF".force_encoding(Encoding::UTF_8)
-> { s.split }.should raise_error(ArgumentError)
-> { s.split(':') }.should raise_error(ArgumentError)
end
it "throws an ArgumentError if the pattern is not a valid string" do it "throws an ArgumentError if the pattern is not a valid string" do
str = 'проверка' str = 'проверка'
broken_str = 'проверка' broken_str = "\xDF".force_encoding(Encoding::UTF_8)
broken_str.force_encoding('binary')
broken_str.chop!
broken_str.force_encoding('utf-8')
-> { str.split(broken_str) }.should raise_error(ArgumentError) -> { str.split(broken_str) }.should raise_error(ArgumentError)
end end
@ -218,6 +223,12 @@ describe "String#split with String" do
end end
describe "String#split with Regexp" do describe "String#split with Regexp" do
it "throws an ArgumentError if the string is not a valid" do
s = "\xDF".force_encoding(Encoding::UTF_8)
-> { s.split(/./) }.should raise_error(ArgumentError)
end
it "divides self on regexp matches" do it "divides self on regexp matches" do
" now's the time".split(/ /).should == ["", "now's", "", "the", "time"] " now's the time".split(/ /).should == ["", "now's", "", "the", "time"]
" x\ny ".split(/ /).should == ["", "x\ny"] " x\ny ".split(/ /).should == ["", "x\ny"]

View file

@ -20,4 +20,9 @@ describe "String#unpack with format 'Z'" do
["\x00a\x00 bc \x00", ["", "c"]] ["\x00a\x00 bc \x00", ["", "c"]]
].should be_computed_by(:unpack, "Z5Z") ].should be_computed_by(:unpack, "Z5Z")
end end
it "does not advance past the null byte when given a 'Z' format specifier" do
"a\x00\x0f".unpack('Zxc').should == ['a', 15]
"a\x00\x0f".unpack('Zcc').should == ['a', 0, 15]
end
end end

View file

@ -102,6 +102,30 @@ describe "Thread#raise on a sleeping thread" do
raised_again.backtrace.first.should_not include("#{__FILE__}:#{raise_again_line}:") raised_again.backtrace.first.should_not include("#{__FILE__}:#{raise_again_line}:")
end end
end end
it "calls #exception in both the caller and in the target thread" do
cls = Class.new(Exception) do
attr_accessor :log
def initialize(*args)
@log = [] # This is shared because the super #exception uses a shallow clone
super
end
def exception(*args)
@log << [self, Thread.current, args]
super
end
end
exc = cls.new
@thr.raise exc, "Thread#raise #exception spec"
@thr.join
ScratchPad.recorded.should.is_a?(cls)
exc.log.should == [
[exc, Thread.current, ["Thread#raise #exception spec"]],
[ScratchPad.recorded, @thr, []]
]
end
end end
describe "Thread#raise on a running thread" do describe "Thread#raise on a running thread" do

View file

@ -0,0 +1 @@
ScratchPad << :loaded

View file

@ -300,6 +300,44 @@ describe 'Optional variable assignments' do
(@b[:k] ||= 12).should == 12 (@b[:k] ||= 12).should == 12
end end
it 'correctly handles a splatted argument for the index' do
(@b[*[:k]] ||= 12).should == 12
end
it "evaluates the index precisely once" do
ary = [:x, :y]
@a[:x] = 15
@a[ary.pop] ||= 25
ary.should == [:x]
@a.should == { x: 15, y: 25 }
end
it "evaluates the index arguments in the correct order" do
ary = Class.new(Array) do
def [](x, y)
super(x + 3 * y)
end
def []=(x, y, value)
super(x + 3 * y, value)
end
end.new
ary[0, 0] = 1
ary[1, 0] = 1
ary[2, 0] = nil
ary[3, 0] = 1
ary[4, 0] = 1
ary[5, 0] = 1
ary[6, 0] = nil
foo = [0, 2]
ary[foo.pop, foo.pop] ||= 2
ary[2, 0].should == 2
ary[6, 0].should == nil
end
it 'returns the assigned value, not the result of the []= method with +=' do it 'returns the assigned value, not the result of the []= method with +=' do
@b[:k] = 17 @b[:k] = 17
(@b[:k] += 12).should == 29 (@b[:k] += 12).should == 29

View file

@ -570,7 +570,6 @@ describe "Predefined global $/" do
($/ = "xyz").should == "xyz" ($/ = "xyz").should == "xyz"
end end
it "changes $-0" do it "changes $-0" do
$/ = "xyz" $/ = "xyz"
$-0.should equal($/) $-0.should equal($/)
@ -641,6 +640,45 @@ describe "Predefined global $-0" do
end end
end end
describe "Predefined global $\\" do
before :each do
@verbose, $VERBOSE = $VERBOSE, nil
@dollar_backslash = $\
end
after :each do
$\ = @dollar_backslash
$VERBOSE = @verbose
end
it "can be assigned a String" do
str = "abc"
$\ = str
$\.should equal(str)
end
it "can be assigned nil" do
$\ = nil
$\.should be_nil
end
it "returns the value assigned" do
($\ = "xyz").should == "xyz"
end
it "does not call #to_str to convert the object to a String" do
obj = mock("$\\ value")
obj.should_not_receive(:to_str)
-> { $\ = obj }.should raise_error(TypeError)
end
it "raises a TypeError if assigned not String" do
-> { $\ = 1 }.should raise_error(TypeError)
-> { $\ = true }.should raise_error(TypeError)
end
end
describe "Predefined global $," do describe "Predefined global $," do
after :each do after :each do
$, = nil $, = nil
@ -1340,3 +1378,29 @@ describe "$LOAD_PATH.resolve_feature_path" do
end end
end end
end end
# Some other pre-defined global variables
describe "Predefined global $=" do
before :each do
@verbose, $VERBOSE = $VERBOSE, nil
@dollar_assign = $=
end
after :each do
$= = @dollar_assign
$VERBOSE = @verbose
end
it "warns when accessed" do
-> { a = $= }.should complain(/is no longer effective/)
end
it "warns when assigned" do
-> { $= = "_" }.should complain(/is no longer effective/)
end
it "returns the value assigned" do
($= = "xyz").should == "xyz"
end
end

View file

@ -849,4 +849,22 @@ describe "Instance variables" do
-> { obj.foobar }.should_not complain(verbose: true) -> { obj.foobar }.should_not complain(verbose: true)
end end
end end
describe "global variable" do
context "when global variable is uninitialized" do
it "warns about accessing uninitialized global variable in verbose mode" do
obj = Object.new
def obj.foobar; a = $specs_uninitialized_global_variable; end
-> { obj.foobar }.should complain(/warning: global variable `\$specs_uninitialized_global_variable' not initialized/, verbose: true)
end
it "doesn't warn at lazy initialization" do
obj = Object.new
def obj.foobar; $specs_uninitialized_global_variable_lazy ||= 42; end
-> { obj.foobar }.should_not complain(verbose: true)
end
end
end
end end

View file

@ -213,3 +213,13 @@ describe "Using yield in a singleton class literal" do
end end
end end
end end
describe "Using yield in non-lambda block" do
it 'raises a SyntaxError' do
code = <<~RUBY
1.times { yield }
RUBY
-> { eval(code) }.should raise_error(SyntaxError, /Invalid yield/)
end
end

View file

@ -128,4 +128,22 @@ describe "ObjectSpace.trace_object_allocations" do
ObjectSpace.trace_object_allocations_stop ObjectSpace.trace_object_allocations_stop
end end
end end
it "returns nil for class_path, generation, method_id, sourcefile, and sourceline for immutable objects" do
ObjectSpace.trace_object_allocations_start
begin
one = nil
two = 42
three = :foo
[one, two, three].each do |i|
ObjectSpace.allocation_class_path(i).should == nil
ObjectSpace.allocation_generation(i).should == nil
ObjectSpace.allocation_method_id(i).should == nil
ObjectSpace.allocation_sourcefile(i).should == nil
ObjectSpace.allocation_sourceline(i).should == nil
end
ensure
ObjectSpace.trace_object_allocations_stop
end
end
end end

View file

@ -0,0 +1,78 @@
require_relative '../../../../spec_helper'
require 'openssl'
describe "OpenSSL::X509::Name.verify" do
it "returns true for valid certificate" do
key = OpenSSL::PKey::RSA.new 2048
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 1
cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=truffleruby/CN=TruffleRuby CA"
cert.issuer = cert.subject
cert.public_key = key.public_key
cert.not_before = Time.now
cert.not_after = cert.not_before + 365 * 24 * 60 * 60
cert.sign key, OpenSSL::Digest.new('SHA1')
store = OpenSSL::X509::Store.new
store.add_cert(cert)
store.verify(cert).should == true
end
it "returns false for an expired certificate" do
key = OpenSSL::PKey::RSA.new 2048
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 1
cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=truffleruby/CN=TruffleRuby CA"
cert.issuer = cert.subject
cert.public_key = key.public_key
cert.not_before = Time.now - 10
cert.not_after = Time.now - 5
cert.sign key, OpenSSL::Digest.new('SHA1')
store = OpenSSL::X509::Store.new
store.add_cert(cert)
store.verify(cert).should == false
end
it "returns false for an expired root certificate" do
root_key = OpenSSL::PKey::RSA.new 2048
root_cert = OpenSSL::X509::Certificate.new
root_cert.version = 2
root_cert.serial = 1
root_cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=truffleruby/CN=TruffleRuby CA"
root_cert.issuer = root_cert.subject
root_cert.public_key = root_key.public_key
root_cert.not_before = Time.now - 10
root_cert.not_after = Time.now - 5
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = root_cert
ef.issuer_certificate = root_cert
root_cert.add_extension(ef.create_extension("basicConstraints","CA:TRUE",true))
root_cert.add_extension(ef.create_extension("keyUsage","keyCertSign, cRLSign", true))
root_cert.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
root_cert.add_extension(ef.create_extension("authorityKeyIdentifier","keyid:always",false))
root_cert.sign(root_key, OpenSSL::Digest.new('SHA256'))
key = OpenSSL::PKey::RSA.new 2048
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 2
cert.subject = OpenSSL::X509::Name.parse "/DC=org/DC=truffleruby/CN=TruffleRuby certificate"
cert.issuer = root_cert.subject
cert.public_key = key.public_key
cert.not_before = Time.now
cert.not_after = cert.not_before + 1 * 365 * 24 * 60 * 60
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = root_cert
cert.add_extension(ef.create_extension("keyUsage","digitalSignature", true))
cert.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
cert.sign(root_key, OpenSSL::Digest.new('SHA256'))
store = OpenSSL::X509::Store.new
store.add_cert(root_cert)
store.add_cert(cert)
store.verify(cert).should == false
end
end

View file

@ -17,3 +17,7 @@ end
describe "StringIO#each_line when passed chomp" do describe "StringIO#each_line when passed chomp" do
it_behaves_like :stringio_each_chomp, :each_line it_behaves_like :stringio_each_chomp, :each_line
end end
describe "StringIO#each_line when passed limit" do
it_behaves_like :stringio_each_limit, :each_line
end

View file

@ -17,3 +17,11 @@ end
describe "StringIO#each when passed chomp" do describe "StringIO#each when passed chomp" do
it_behaves_like :stringio_each_chomp, :each it_behaves_like :stringio_each_chomp, :each
end end
describe "StringIO#each when passed chomp" do
it_behaves_like :stringio_each_separator_and_chomp, :each
end
describe "StringIO#each when passed limit" do
it_behaves_like :stringio_each_limit, :each
end

View file

@ -171,6 +171,10 @@ describe "StringIO#gets when passed [limit]" do
it "returns a blank string when passed a limit of 0" do it "returns a blank string when passed a limit of 0" do
@io.gets(0).should == "" @io.gets(0).should == ""
end end
it "ignores it when passed a negative limit" do
@io.gets(-4).should == "this>is>an>example"
end
end end
describe "StringIO#gets when passed [separator] and [limit]" do describe "StringIO#gets when passed [separator] and [limit]" do

View file

@ -128,3 +128,23 @@ describe "StringIO#readline when passed [chomp]" do
io.readline(chomp: true).should == "this>is>an>example" io.readline(chomp: true).should == "this>is>an>example"
end end
end end
describe "StringIO#readline when passed [limit]" do
before :each do
@io = StringIO.new("this>is>an>example")
end
it "returns the data read until the limit is met" do
io = StringIO.new("this>is>an>example\n")
io.readline(3).should == "thi"
end
it "returns a blank string when passed a limit of 0" do
@io.readline(0).should == ""
end
it "ignores it when the limit is negative" do
seen = []
@io.readline(-4).should == "this>is>an>example"
end
end

View file

@ -98,3 +98,21 @@ describe "StringIO#readlines when passed [chomp]" do
io.readlines(chomp: true).should == ["this>is", "an>example"] io.readlines(chomp: true).should == ["this>is", "an>example"]
end end
end end
describe "StringIO#readlines when passed [limit]" do
before :each do
@io = StringIO.new("a b c d e\n1 2 3 4 5")
end
it "returns the data read until the limit is met" do
@io.readlines(4).should == ["a b ", "c d ", "e\n", "1 2 ", "3 4 ", "5"]
end
it "raises ArgumentError when limit is 0" do
-> { @io.readlines(0) }.should raise_error(ArgumentError)
end
it "ignores it when the limit is negative" do
@io.readlines(-4).should == ["a b c d e\n", "1 2 3 4 5"]
end
end

View file

@ -123,4 +123,41 @@ describe :stringio_each_chomp, shared: true do
io.send(@method, chomp: true) {|s| seen << s } io.send(@method, chomp: true) {|s| seen << s }
seen.should == ["a b \rc d e", "1 2 3 4 5", "the end"] seen.should == ["a b \rc d e", "1 2 3 4 5", "the end"]
end end
it "returns each line with removed newline characters when called without block" do
seen = []
io = StringIO.new("a b \rc d e\n1 2 3 4 5\r\nthe end")
enum = io.send(@method, chomp: true)
enum.each {|s| seen << s }
seen.should == ["a b \rc d e", "1 2 3 4 5", "the end"]
end
end
describe :stringio_each_separator_and_chomp, shared: true do
it "yields each line with removed separator to the passed block" do
seen = []
io = StringIO.new("a b \nc d e|1 2 3 4 5\n|the end")
io.send(@method, "|", chomp: true) {|s| seen << s }
seen.should == ["a b \nc d e", "1 2 3 4 5\n", "the end"]
end
it "returns each line with removed separator when called without block" do
seen = []
io = StringIO.new("a b \nc d e|1 2 3 4 5\n|the end")
enum = io.send(@method, "|", chomp: true)
enum.each {|s| seen << s }
seen.should == ["a b \nc d e", "1 2 3 4 5\n", "the end"]
end
end
describe :stringio_each_limit, shared: true do
before :each do
@io = StringIO.new("a b c d e\n1 2 3 4 5")
end
it "returns the data read until the limit is met" do
seen = []
@io.send(@method, 4) { |s| seen << s }
seen.should == ["a b ", "c d ", "e\n", "1 2 ", "3 4 ", "5"]
end
end end

View file

@ -2,7 +2,12 @@ require_relative '../../spec_helper'
require "zlib" require "zlib"
describe "Zlib.crc_table" do describe "Zlib.crc_table" do
# This spec fails when zlib.h and libz.so are not from the same version.
# In older zlib (< 1.2.7 it seems), get_crc_table() is stored as u64[],
# but in newer zlib, get_crc_table() is stored as u32[].
# Technically, there is ABI breakage between those zlib versions,
# but get_crc_table() is an "undocumented function" according to zlib.h.
guard -> { ENV["RUBY_SPEC_TEST_ZLIB_CRC_TABLE"] != "false" } do
it "returns the same value as zlib's get_crc_table()" do it "returns the same value as zlib's get_crc_table()" do
Zlib.crc_table.should == [ Zlib.crc_table.should == [
0, 1996959894, 3993919788, 2567524794, 0, 1996959894, 3993919788, 2567524794,
@ -71,5 +76,5 @@ describe "Zlib.crc_table" do
3020668471, 3272380065, 1510334235, 755167117, 3020668471, 3272380065, 1510334235, 755167117,
] ]
end end
end
end end

View file

@ -196,16 +196,6 @@ describe "C-API Class function" do
end end
end end
describe "rb_define_method" do
it "defines a method taking variable arguments as a C array if the argument count is -1" do
@s.rb_method_varargs_1(1, 3, 7, 4).should == [1, 3, 7, 4]
end
it "defines a method taking variable arguments as a Ruby array if the argument count is -2" do
@s.rb_method_varargs_2(1, 3, 7, 4).should == [1, 3, 7, 4]
end
end
describe "rb_class2name" do describe "rb_class2name" do
it "returns the class name" do it "returns the class name" do
@s.rb_class2name(CApiClassSpecs).should == "CApiClassSpecs" @s.rb_class2name(CApiClassSpecs).should == "CApiClassSpecs"

View file

@ -613,15 +613,15 @@ describe "C-API Encoding function" do
it 'converts a Unicode codepoint to a UTF-8 C string' do it 'converts a Unicode codepoint to a UTF-8 C string' do
str = ' ' * 6 str = ' ' * 6
{ {
0 => "\x01", 1 => "\x01",
0x7f => "\xC2\x80", 0x80 => "\xC2\x80",
0x7ff => "\xE0\xA0\x80", 0x800 => "\xE0\xA0\x80",
0xffff => "\xF0\x90\x80\x80", 0x10000 => "\xF0\x90\x80\x80",
0x1fffff => "\xF8\x88\x80\x80\x80", 0x200000 => "\xF8\x88\x80\x80\x80",
0x3ffffff => "\xFC\x84\x80\x80\x80\x80", 0x4000000 => "\xFC\x84\x80\x80\x80\x80",
}.each do |num, result| }.each do |num, result|
len = @s.rb_uv_to_utf8(str, num + 1) len = @s.rb_uv_to_utf8(str, num)
str[0..len-1].should == result str.byteslice(0, len).should == result
end end
end end
end end

View file

@ -142,19 +142,6 @@ static VALUE class_spec_include_module(VALUE self, VALUE klass, VALUE module) {
return klass; return klass;
} }
static VALUE class_spec_method_var_args_1(int argc, VALUE *argv, VALUE self) {
VALUE ary = rb_ary_new();
int i;
for (i = 0; i < argc; i++) {
rb_ary_push(ary, argv[i]);
}
return ary;
}
static VALUE class_spec_method_var_args_2(VALUE self, VALUE argv) {
return argv;
}
void Init_class_spec(void) { void Init_class_spec(void) {
VALUE cls = rb_define_class("CApiClassSpecs", rb_cObject); VALUE cls = rb_define_class("CApiClassSpecs", rb_cObject);
rb_define_method(cls, "define_call_super_method", class_spec_define_call_super_method, 2); rb_define_method(cls, "define_call_super_method", class_spec_define_call_super_method, 2);
@ -185,8 +172,6 @@ void Init_class_spec(void) {
rb_define_method(cls, "rb_define_class_id_under", class_spec_rb_define_class_id_under, 3); rb_define_method(cls, "rb_define_class_id_under", class_spec_rb_define_class_id_under, 3);
rb_define_method(cls, "rb_define_class_variable", class_spec_define_class_variable, 3); rb_define_method(cls, "rb_define_class_variable", class_spec_define_class_variable, 3);
rb_define_method(cls, "rb_include_module", class_spec_include_module, 2); rb_define_method(cls, "rb_include_module", class_spec_include_module, 2);
rb_define_method(cls, "rb_method_varargs_1", class_spec_method_var_args_1, -1);
rb_define_method(cls, "rb_method_varargs_2", class_spec_method_var_args_2, -2);
} }
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -275,7 +275,9 @@ static VALUE encoding_spec_rb_enc_str_asciionly_p(VALUE self, VALUE str) {
} }
static VALUE encoding_spec_rb_uv_to_utf8(VALUE self, VALUE buf, VALUE num) { static VALUE encoding_spec_rb_uv_to_utf8(VALUE self, VALUE buf, VALUE num) {
return INT2NUM(rb_uv_to_utf8(RSTRING_PTR(buf), NUM2INT(num))); int len = rb_uv_to_utf8(RSTRING_PTR(buf), NUM2INT(num));
RB_ENC_CODERANGE_CLEAR(buf);
return INT2NUM(len);
} }
static VALUE encoding_spec_ONIGENC_MBC_CASE_FOLD(VALUE self, VALUE str) { static VALUE encoding_spec_ONIGENC_MBC_CASE_FOLD(VALUE self, VALUE str) {

View file

@ -9,18 +9,6 @@ static VALUE module_specs_test_method(VALUE self) {
return ID2SYM(rb_intern("test_method")); return ID2SYM(rb_intern("test_method"));
} }
static VALUE module_specs_test_method_2required(VALUE self, VALUE arg1, VALUE arg2) {
return ID2SYM(rb_intern("test_method_2required"));
}
static VALUE module_specs_test_method_c_array(int argc, VALUE *argv, VALUE self) {
return ID2SYM(rb_intern("test_method_c_array"));
}
static VALUE module_specs_test_method_ruby_array(VALUE self, VALUE args) {
return ID2SYM(rb_intern("test_method_ruby_array"));
}
static VALUE module_specs_const_defined(VALUE self, VALUE klass, VALUE id) { static VALUE module_specs_const_defined(VALUE self, VALUE klass, VALUE id) {
return rb_const_defined(klass, SYM2ID(id)) ? Qtrue : Qfalse; return rb_const_defined(klass, SYM2ID(id)) ? Qtrue : Qfalse;
} }
@ -88,19 +76,25 @@ static VALUE module_specs_rb_define_method(VALUE self, VALUE cls, VALUE str_name
return Qnil; return Qnil;
} }
static VALUE module_specs_rb_define_method_2required(VALUE self, VALUE cls, VALUE str_name) { static VALUE module_specs_method_var_args_1(int argc, VALUE *argv, VALUE self) {
rb_define_method(cls, RSTRING_PTR(str_name), module_specs_test_method_2required, 2); VALUE ary = rb_ary_new();
return Qnil; int i;
for (i = 0; i < argc; i++) {
rb_ary_push(ary, argv[i]);
}
return ary;
} }
static VALUE module_specs_rb_define_method_c_array(VALUE self, VALUE cls, VALUE str_name) { static VALUE module_specs_method_var_args_2(VALUE self, VALUE argv) {
rb_define_method(cls, RSTRING_PTR(str_name), module_specs_test_method_c_array, -1); return argv;
return Qnil;
} }
static VALUE module_specs_rb_define_method_ruby_array(VALUE self, VALUE cls, VALUE str_name) { static VALUE module_specs_rb_define_method_1required(VALUE self, VALUE arg1) {
rb_define_method(cls, RSTRING_PTR(str_name), module_specs_test_method_ruby_array, -2); return arg1;
return Qnil; }
static VALUE module_specs_rb_define_method_2required(VALUE self, VALUE arg1, VALUE arg2) {
return arg2;
} }
static VALUE module_specs_rb_define_module_function(VALUE self, VALUE cls, VALUE str_name) { static VALUE module_specs_rb_define_module_function(VALUE self, VALUE cls, VALUE str_name) {
@ -155,25 +149,21 @@ void Init_module_spec(void) {
rb_define_method(cls, "rb_define_module_under", module_specs_rb_define_module_under, 2); rb_define_method(cls, "rb_define_module_under", module_specs_rb_define_module_under, 2);
rb_define_method(cls, "rb_define_const", module_specs_define_const, 3); rb_define_method(cls, "rb_define_const", module_specs_define_const, 3);
rb_define_method(cls, "rb_define_global_const", module_specs_define_global_const, 2); rb_define_method(cls, "rb_define_global_const", module_specs_define_global_const, 2);
rb_define_method(cls, "rb_define_global_function", rb_define_method(cls, "rb_define_global_function", module_specs_rb_define_global_function, 1);
module_specs_rb_define_global_function, 1);
rb_define_method(cls, "rb_define_method", module_specs_rb_define_method, 2); rb_define_method(cls, "rb_define_method", module_specs_rb_define_method, 2);
rb_define_method(cls, "rb_define_method_varargs_1", module_specs_method_var_args_1, -1);
rb_define_method(cls, "rb_define_method_varargs_2", module_specs_method_var_args_2, -2);
rb_define_method(cls, "rb_define_method_1required", module_specs_rb_define_method_1required, 1);
rb_define_method(cls, "rb_define_method_2required", module_specs_rb_define_method_2required, 2); rb_define_method(cls, "rb_define_method_2required", module_specs_rb_define_method_2required, 2);
rb_define_method(cls, "rb_define_method_c_array", module_specs_rb_define_method_c_array, 2);
rb_define_method(cls, "rb_define_method_ruby_array", module_specs_rb_define_method_ruby_array, 2);
rb_define_method(cls, "rb_define_module_function", rb_define_method(cls, "rb_define_module_function", module_specs_rb_define_module_function, 2);
module_specs_rb_define_module_function, 2);
rb_define_method(cls, "rb_define_private_method", rb_define_method(cls, "rb_define_private_method", module_specs_rb_define_private_method, 2);
module_specs_rb_define_private_method, 2);
rb_define_method(cls, "rb_define_protected_method", rb_define_method(cls, "rb_define_protected_method", module_specs_rb_define_protected_method, 2);
module_specs_rb_define_protected_method, 2);
rb_define_method(cls, "rb_define_singleton_method", rb_define_method(cls, "rb_define_singleton_method", module_specs_rb_define_singleton_method, 2);
module_specs_rb_define_singleton_method, 2);
rb_define_method(cls, "rb_undef_method", module_specs_rb_undef_method, 2); rb_define_method(cls, "rb_undef_method", module_specs_rb_undef_method, 2);
rb_define_method(cls, "rb_undef", module_specs_rb_undef, 2); rb_define_method(cls, "rb_undef", module_specs_rb_undef, 2);

View file

@ -301,6 +301,13 @@ static VALUE so_is_rb_type_p_data(VALUE self, VALUE obj) {
return Qfalse; return Qfalse;
} }
static VALUE so_is_rb_type_p_file(VALUE self, VALUE obj) {
if(rb_type_p(obj, T_FILE)) {
return Qtrue;
}
return Qfalse;
}
static VALUE so_is_builtin_type_object(VALUE self, VALUE obj) { static VALUE so_is_builtin_type_object(VALUE self, VALUE obj) {
if(BUILTIN_TYPE(obj) == T_OBJECT) { if(BUILTIN_TYPE(obj) == T_OBJECT) {
return Qtrue; return Qtrue;
@ -478,6 +485,7 @@ void Init_object_spec(void) {
rb_define_method(cls, "rb_is_rb_type_p_module", so_is_rb_type_p_module, 1); rb_define_method(cls, "rb_is_rb_type_p_module", so_is_rb_type_p_module, 1);
rb_define_method(cls, "rb_is_rb_type_p_class", so_is_rb_type_p_class, 1); rb_define_method(cls, "rb_is_rb_type_p_class", so_is_rb_type_p_class, 1);
rb_define_method(cls, "rb_is_rb_type_p_data", so_is_rb_type_p_data, 1); rb_define_method(cls, "rb_is_rb_type_p_data", so_is_rb_type_p_data, 1);
rb_define_method(cls, "rb_is_rb_type_p_file", so_is_rb_type_p_file, 1);
rb_define_method(cls, "rb_is_builtin_type_object", so_is_builtin_type_object, 1); rb_define_method(cls, "rb_is_builtin_type_object", so_is_builtin_type_object, 1);
rb_define_method(cls, "rb_is_builtin_type_array", so_is_builtin_type_array, 1); rb_define_method(cls, "rb_is_builtin_type_array", so_is_builtin_type_array, 1);
rb_define_method(cls, "rb_is_builtin_type_module", so_is_builtin_type_module, 1); rb_define_method(cls, "rb_is_builtin_type_module", so_is_builtin_type_module, 1);

View file

@ -49,6 +49,12 @@ VALUE regexp_spec_match(VALUE self, VALUE regexp, VALUE str) {
return rb_funcall(regexp, rb_intern("match"), 1, str); return rb_funcall(regexp, rb_intern("match"), 1, str);
} }
VALUE regexp_spec_memcicmp(VALUE self, VALUE str1, VALUE str2) {
long l1 = RSTRING_LEN(str1);
long l2 = RSTRING_LEN(str2);
return INT2FIX(rb_memcicmp(RSTRING_PTR(str1), RSTRING_PTR(str2), l1 < l2 ? l1 : l2));
}
void Init_regexp_spec(void) { void Init_regexp_spec(void) {
VALUE cls = rb_define_class("CApiRegexpSpecs", rb_cObject); VALUE cls = rb_define_class("CApiRegexpSpecs", rb_cObject);
rb_define_method(cls, "match", regexp_spec_match, 2); rb_define_method(cls, "match", regexp_spec_match, 2);
@ -60,6 +66,7 @@ void Init_regexp_spec(void) {
rb_define_method(cls, "rb_reg_match_backref_get", regexp_spec_reg_match_backref_get, 2); rb_define_method(cls, "rb_reg_match_backref_get", regexp_spec_reg_match_backref_get, 2);
rb_define_method(cls, "rb_reg_options", regexp_spec_rb_reg_options, 1); rb_define_method(cls, "rb_reg_options", regexp_spec_rb_reg_options, 1);
rb_define_method(cls, "rb_reg_regcomp", regexp_spec_rb_reg_regcomp, 1); rb_define_method(cls, "rb_reg_regcomp", regexp_spec_rb_reg_regcomp, 1);
rb_define_method(cls, "rb_memcicmp", regexp_spec_memcicmp, 2);
} }
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -252,22 +252,30 @@ describe "CApiModule" do
cls.new.method(:test_method).arity.should == 0 cls.new.method(:test_method).arity.should == 0
end end
it "returns the correct arity when argc of the method in class is -1" do it "returns the correct arity when argc of the method in class is 1" do
cls = Class.new @m.rb_define_method_1required(42).should == 42
@m.rb_define_method_c_array(cls, "test_method_c_array") @m.method(:rb_define_method_1required).arity.should == 1
cls.new.method(:test_method_c_array).arity.should == -1
end
it "returns the correct arity when argc of the method in class is -2" do
cls = Class.new
@m.rb_define_method_ruby_array(cls, "test_method_ruby_array")
cls.new.method(:test_method_ruby_array).arity.should == -1
end end
it "returns the correct arity when argc of the method in class is 2" do it "returns the correct arity when argc of the method in class is 2" do
cls = Class.new @m.rb_define_method_2required(1, 2).should == 2
@m.rb_define_method_2required(cls, "test_method_2required") @m.method(:rb_define_method_2required).arity.should == 2
cls.new.method(:test_method_2required).arity.should == 2 end
it "defines a method taking variable arguments as a C array if the argument count is -1" do
@m.rb_define_method_varargs_1(1, 3, 7, 4).should == [1, 3, 7, 4]
end
it "returns the correct arity when argc of the method in class is -1" do
@m.method(:rb_define_method_varargs_1).arity.should == -1
end
it "defines a method taking variable arguments as a Ruby array if the argument count is -2" do
@m.rb_define_method_varargs_2(1, 3, 7, 4).should == [1, 3, 7, 4]
end
it "returns the correct arity when argc of the method in class is -2" do
@m.method(:rb_define_method_varargs_2).arity.should == -1
end end
it "defines a method on a module" do it "defines a method on a module" do

View file

@ -30,6 +30,7 @@ describe "CApiObject" do
class ObjectTest class ObjectTest
def initialize def initialize
@foo = 7 @foo = 7
yield if block_given?
end end
def foo def foo
@ -88,6 +89,15 @@ describe "CApiObject" do
o.initialized.should be_true o.initialized.should be_true
o.arguments.should == [:one, :two] o.arguments.should == [:one, :two]
end end
it "passes the block to #initialize" do
v = nil
o = @o.rb_obj_alloc(ObjectTest)
@o.rb_obj_call_init(o, 0, []) do
v = :foo
end
v.should == :foo
end
end end
describe "rb_is_instance_of" do describe "rb_is_instance_of" do
@ -513,6 +523,21 @@ describe "CApiObject" do
@o.rb_is_type_class(ObjectTest).should == true @o.rb_is_type_class(ObjectTest).should == true
@o.rb_is_type_data(Time.now).should == true @o.rb_is_type_data(Time.now).should == true
end end
it "returns T_FILE for instances of IO and subclasses" do
STDERR.class.should == IO
@o.rb_is_rb_type_p_file(STDERR).should == true
File.open(__FILE__) do |f|
f.class.should == File
@o.rb_is_rb_type_p_file(f).should == true
end
require 'socket'
TCPServer.open(0) do |s|
@o.rb_is_rb_type_p_file(s).should == true
end
end
end end
describe "rb_check_type" do describe "rb_check_type" do

View file

@ -109,4 +109,20 @@ describe "C-API Regexp function" do
thr.join thr.join
end end
end end
describe "rb_memicmp" do
it "returns 0 for identical strings" do
@p.rb_memcicmp('Hello', 'Hello').should == 0
end
it "returns 0 for strings which only differ in case" do
@p.rb_memcicmp('Hello', 'HELLO').should == 0
@p.rb_memcicmp('HELLO', 'Hello').should == 0
end
it "returns the difference between the first non matching characters" do
@p.rb_memcicmp('Hello', 'HELLP').should == -1
@p.rb_memcicmp('HELLp', 'Hello').should == 1
end
end
end end

View file

@ -14,8 +14,7 @@ else
end end
end end
# Running directly with ruby some_spec.rb unless ENV['MSPEC_RUNNER'] # Running directly with ruby some_spec.rb
unless ENV['MSPEC_RUNNER']
mspec_lib = File.expand_path("../../mspec/lib", __FILE__) mspec_lib = File.expand_path("../../mspec/lib", __FILE__)
$LOAD_PATH << mspec_lib if File.directory?(mspec_lib) $LOAD_PATH << mspec_lib if File.directory?(mspec_lib)
@ -26,7 +25,13 @@ unless ENV['MSPEC_RUNNER']
puts "Please add -Ipath/to/mspec/lib or clone mspec as a sibling to run the specs." puts "Please add -Ipath/to/mspec/lib or clone mspec as a sibling to run the specs."
exit 1 exit 1
end end
end
ruby_version_is ""..."2.7" do
abort "This version of ruby/spec requires Ruby 2.7+"
end
unless ENV['MSPEC_RUNNER'] # Running directly with ruby some_spec.rb
ARGV.unshift $0 ARGV.unshift $0
MSpecRun.main MSpecRun.main
end end