This commit is contained in:
Benoit Daloze 2020-07-27 21:41:08 +02:00
Родитель 7429841ab6
Коммит 126fd5f15c
38 изменённых файлов: 733 добавлений и 44 удалений

Просмотреть файл

@ -152,6 +152,7 @@ RegexpSpecsSubclassTwo
Reline
RescueInClassExample
Resolv
Ripper
SHA1Constants
SHA256Constants
SHA384Constants

Просмотреть файл

@ -92,6 +92,7 @@ Lint/UnreachableCode:
Exclude:
- 'core/enumerator/lazy/fixtures/classes.rb'
- 'core/kernel/catch_spec.rb'
- 'core/kernel/raise_spec.rb'
- 'core/kernel/throw_spec.rb'
- 'language/break_spec.rb'
- 'language/fixtures/break.rb'

Просмотреть файл

@ -59,6 +59,7 @@ Lint/InheritException:
- 'core/enumerator/lazy/fixtures/classes.rb'
- 'core/exception/fixtures/common.rb'
- 'core/module/fixtures/autoload_ex1.rb'
- 'shared/kernel/raise.rb'
# Offense count: 72
# Cop supports --auto-correct.
@ -115,6 +116,7 @@ Lint/RescueException:
- 'core/exception/cause_spec.rb'
- 'core/exception/no_method_error_spec.rb'
- 'core/kernel/fixtures/autoload_frozen.rb'
- 'core/kernel/raise_spec.rb'
- 'core/module/autoload_spec.rb'
- 'core/mutex/sleep_spec.rb'
- 'core/thread/abort_on_exception_spec.rb'

Просмотреть файл

@ -136,11 +136,11 @@ Here is a list of the most commonly-used guards:
#### Version guards
```ruby
ruby_version_is ""..."2.6 do
ruby_version_is ""..."2.6" do
# Specs for RUBY_VERSION < 2.6
end
ruby_version_is "2.6 do
ruby_version_is "2.6" do
# Specs for RUBY_VERSION >= 2.6
end
```

Просмотреть файл

@ -7,7 +7,22 @@ describe "The -r command line option" do
end
it "requires the specified file" do
result = ruby_exe(@script, options: "-r #{@test_file}")
result.should include(@test_file + ".rb")
out = ruby_exe(@script, options: "-r #{@test_file}")
out.should include("REQUIRED")
out.should include(@test_file + ".rb")
end
it "requires the file before parsing the main script" do
out = ruby_exe(fixture(__FILE__, "bad_syntax.rb"), options: "-r #{@test_file}", args: "2>&1")
$?.should_not.success?
out.should include("REQUIRED")
out.should include("syntax error")
end
it "does not require the file if the main script file does not exist" do
out = `#{ruby_exe.to_a.join(' ')} -r #{@test_file} #{fixture(__FILE__, "does_not_exist.rb")} 2>&1`
$?.should_not.success?
out.should_not.include?("REQUIRED")
out.should.include?("No such file or directory")
end
end

Просмотреть файл

@ -1 +1 @@
"test file"
puts "REQUIRED"

Просмотреть файл

@ -207,8 +207,9 @@ describe "Array#fill with (filler, index, length)" do
not_supported_on :opal do
it "raises an ArgumentError or RangeError for too-large sizes" do
error_types = [RangeError, ArgumentError]
arr = [1, 2, 3]
-> { arr.fill(10, 1, fixnum_max) }.should raise_error(ArgumentError)
-> { arr.fill(10, 1, fixnum_max) }.should raise_error { |err| error_types.should include(err.class) }
-> { arr.fill(10, 1, bignum_value) }.should raise_error(RangeError)
end
end

Просмотреть файл

@ -43,7 +43,7 @@ describe "Exception#backtrace" do
# This regexp is deliberately imprecise to account for the need to abstract out
# the paths of the included mspec files and the desire to avoid specifying in any
# detail what the in `...' portion looks like.
line.should =~ /^[^ ]+\:\d+(:in `[^`]+')?$/
line.should =~ /^.+:\d+:in `[^`]+'$/
end
end

Просмотреть файл

@ -5,6 +5,13 @@ describe "Kernel#__dir__" do
__dir__.should == File.realpath(File.dirname(__FILE__))
end
it "returns the expanded path of the directory when used in the main script" do
fixtures_dir = File.dirname(fixture(__FILE__, '__dir__.rb'))
Dir.chdir(fixtures_dir) do
ruby_exe("__dir__.rb").should == "__dir__.rb\n#{fixtures_dir}\n"
end
end
context "when used in eval with a given filename" do
it "returns File.dirname(filename)" do
eval("__dir__", nil, "foo.rb").should == "."

Просмотреть файл

@ -23,7 +23,7 @@ describe "Kernel.at_exit" do
it "gives access to the last raised exception" do
code = <<-EOC
at_exit do
puts "The exception matches: \#{$! == $exception}"
puts "The exception matches: \#{$! == $exception} (message=\#{$!.message})"
end
begin
@ -33,10 +33,35 @@ describe "Kernel.at_exit" do
end
EOC
result = ruby_exe(code, args: "2>&1", escape: true)
result.should =~ /The exception matches: true/
result = ruby_exe(code, args: "2>&1")
result.lines.should.include?("The exception matches: true (message=foo)\n")
end
it "both exceptions in at_exit and in the main script are printed" do
result = ruby_exe('at_exit { raise "at_exit_error" }; raise "main_script_error"', args: "2>&1")
result.should.include?('at_exit_error (RuntimeError)')
result.should.include?('main_script_error (RuntimeError)')
end
it "decides the exit status if both at_exit and the main script raise SystemExit" do
ruby_exe('at_exit { exit 43 }; exit 42', args: "2>&1")
$?.exitstatus.should == 43
end
it "runs all at_exit even if some raise exceptions" do
code = 'at_exit { STDERR.puts "last" }; at_exit { exit 43 }; at_exit { STDERR.puts "first" }; exit 42'
result = ruby_exe(code, args: "2>&1")
result.should == "first\nlast\n"
$?.exitstatus.should == 43
end
it "runs at_exit handlers even if the main script fails to parse" do
script = fixture(__FILE__, "at_exit.rb")
result = ruby_exe('{', options: "-r#{script}", args: "2>&1")
$?.should_not.success?
result.should.include?("at_exit ran\n")
result.should.include?("syntax error")
end
end
describe "Kernel#at_exit" do

Просмотреть файл

@ -0,0 +1,2 @@
puts __FILE__
puts __dir__

Просмотреть файл

@ -0,0 +1,3 @@
at_exit do
STDERR.puts "at_exit ran"
end

Просмотреть файл

@ -0,0 +1 @@
warn 'warn-require-warning', uplevel: 1

Просмотреть файл

@ -0,0 +1,2 @@
# Use a different line than just 1
require "#{__dir__}/warn_require"

Просмотреть файл

@ -6,6 +6,27 @@ describe "Kernel#raise" do
it "is a private method" do
Kernel.should have_private_instance_method(:raise)
end
it "re-raises the previously rescued exception if no exception is specified" do
ScratchPad.record nil
-> do
begin
raise Exception, "outer"
ScratchPad.record :no_abort
rescue Exception
begin
raise StandardError, "inner"
rescue StandardError
end
raise
ScratchPad.record :no_reraise
end
end.should raise_error(Exception, "outer")
ScratchPad.recorded.should be_nil
end
end
describe "Kernel#raise" do

Просмотреть файл

@ -101,6 +101,19 @@ describe "Kernel#warn" do
-> { w.f4("foo", 3) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f3_call_lineno}: warning: foo|)
end
# Test both explicitly without and with RubyGems as RubyGems overrides Kernel#warn
it "shows the caller of #require and not #require itself without RubyGems" do
file = fixture(__FILE__ , "warn_require_caller.rb")
ruby_exe(file, options: "--disable-gems", args: "2>&1").should == "#{file}:2: warning: warn-require-warning\n"
end
ruby_version_is "2.6" do
it "shows the caller of #require and not #require itself with RubyGems loaded" do
file = fixture(__FILE__ , "warn_require_caller.rb")
ruby_exe(file, options: "-rrubygems", args: "2>&1").should == "#{file}:2: warning: warn-require-warning\n"
end
end
it "converts first arg using to_s" do
w = KernelSpecs::WarnInNestedCall.new

Просмотреть файл

@ -3,6 +3,10 @@ module ModuleSpecs
def foo; "foo" end
end
class ClassWithSuperFoo
def foo; [:C] end
end
module PrependedModule
def foo; "foo from prepended module"; end
end
@ -11,7 +15,11 @@ module ModuleSpecs
def foo; "foo from included module"; end
end
def self.build_refined_class
def self.build_refined_class(for_super: false)
if for_super
Class.new(ClassWithSuperFoo)
else
Class.new(ClassWithFoo)
end
end
end

Просмотреть файл

@ -731,16 +731,67 @@ describe "Module#refine" do
result.should == "foo"
end
it "looks in the refined class from included module" do
refined_class = ModuleSpecs.build_refined_class(for_super: true)
a = Module.new do
def foo
[:A] + super
end
end
refinement = Module.new do
refine refined_class do
include a
end
end
result = nil
Module.new do
using refinement
result = refined_class.new.foo
end
result.should == [:A, :C]
end
it "looks in the refined ancestors from included module" do
refined_class = ModuleSpecs.build_refined_class(for_super: true)
subclass = Class.new(refined_class)
a = Module.new do
def foo
[:A] + super
end
end
refinement = Module.new do
refine refined_class do
include a
end
end
result = nil
Module.new do
using refinement
result = subclass.new.foo
end
result.should == [:A, :C]
end
# super in a method of a refinement invokes the method in the refined
# class even if there is another refinement which has been activated
# in the same context.
it "looks in the refined class even if there is another active refinement" do
refined_class = ModuleSpecs.build_refined_class
it "looks in the refined class first if called from refined method" do
refined_class = ModuleSpecs.build_refined_class(for_super: true)
refinement = Module.new do
refine refined_class do
def foo
"foo from refinement"
[:R1]
end
end
end
@ -748,7 +799,7 @@ describe "Module#refine" do
refinement_with_super = Module.new do
refine refined_class do
def foo
super
[:R2] + super
end
end
end
@ -760,7 +811,246 @@ describe "Module#refine" do
result = refined_class.new.foo
end
result.should == "foo"
result.should == [:R2, :C]
end
it "looks only in the refined class even if there is another active refinement" do
refined_class = ModuleSpecs.build_refined_class(for_super: true)
refinement = Module.new do
refine refined_class do
def bar
"you cannot see me from super because I belong to another active R"
end
end
end
refinement_with_super = Module.new do
refine refined_class do
def bar
super
end
end
end
Module.new do
using refinement
using refinement_with_super
-> {
refined_class.new.bar
}.should raise_error(NoMethodError)
end
end
it "does't have access to active refinements for C from included module" do
refined_class = ModuleSpecs.build_refined_class
a = Module.new do
def foo
super + bar
end
end
refinement = Module.new do
refine refined_class do
include a
def bar
"bar is not seen from A methods"
end
end
end
Module.new do
using refinement
-> {
refined_class.new.foo
}.should raise_error(NameError) { |e| e.name.should == :bar }
end
end
it "does't have access to other active refinements from included module" do
refined_class = ModuleSpecs.build_refined_class
refinement_integer = Module.new do
refine Integer do
def bar
"bar is not seen from A methods"
end
end
end
a = Module.new do
def foo
super + 1.bar
end
end
refinement = Module.new do
refine refined_class do
include a
end
end
Module.new do
using refinement
using refinement_integer
-> {
refined_class.new.foo
}.should raise_error(NameError) { |e| e.name.should == :bar }
end
end
# https://bugs.ruby-lang.org/issues/16977
it "looks in the another active refinement if super called from included modules" do
refined_class = ModuleSpecs.build_refined_class(for_super: true)
a = Module.new do
def foo
[:A] + super
end
end
b = Module.new do
def foo
[:B] + super
end
end
refinement_a = Module.new do
refine refined_class do
include a
end
end
refinement_b = Module.new do
refine refined_class do
include b
end
end
result = nil
Module.new do
using refinement_a
using refinement_b
result = refined_class.new.foo
end
result.should == [:B, :A, :C]
end
it "looks in the current active refinement from included modules" do
refined_class = ModuleSpecs.build_refined_class(for_super: true)
a = Module.new do
def foo
[:A] + super
end
end
b = Module.new do
def foo
[:B] + super
end
end
refinement = Module.new do
refine refined_class do
def foo
[:LAST] + super
end
end
end
refinement_a_b = Module.new do
refine refined_class do
include a
include b
end
end
result = nil
Module.new do
using refinement
using refinement_a_b
result = refined_class.new.foo
end
result.should == [:B, :A, :LAST, :C]
end
ruby_version_is ""..."2.8" do
it "looks in the lexical scope refinements before other active refinements" do
refined_class = ModuleSpecs.build_refined_class(for_super: true)
refinement_local = Module.new do
refine refined_class do
def foo
[:LOCAL] + super
end
end
end
a = Module.new do
using refinement_local
def foo
[:A] + super
end
end
refinement = Module.new do
refine refined_class do
include a
end
end
result = nil
Module.new do
using refinement
result = refined_class.new.foo
end
result.should == [:A, :LOCAL, :C]
end
end
ruby_version_is "2.8" do
# https://bugs.ruby-lang.org/issues/17007
it "does not look in the lexical scope refinements before other active refinements" do
refined_class = ModuleSpecs.build_refined_class(for_super: true)
refinement_local = Module.new do
refine refined_class do
def foo
[:LOCAL] + super
end
end
end
a = Module.new do
using refinement_local
def foo
[:A] + super
end
end
refinement = Module.new do
refine refined_class do
include a
end
end
result = nil
Module.new do
using refinement
result = refined_class.new.foo
end
result.should == [:A, :C]
end
end
end

Просмотреть файл

@ -426,14 +426,72 @@ describe "String#split with Regexp" do
end
ruby_version_is "2.6" do
it "yields each split substrings if a block is given" do
context "when a block is given" do
it "yields each split substring with default pattern" do
a = []
returned_object = "chunky bacon".split(" ") { |str| a << str.capitalize }
returned_object = "chunky bacon".split { |str| a << str.capitalize }
returned_object.should == "chunky bacon"
a.should == ["Chunky", "Bacon"]
end
it "yields the string when limit is 1" do
a = []
returned_object = "chunky bacon".split("", 1) { |str| a << str.capitalize }
returned_object.should == "chunky bacon"
a.should == ["Chunky bacon"]
end
it "yields each split letter" do
a = []
returned_object = "chunky".split("", 0) { |str| a << str.capitalize }
returned_object.should == "chunky"
a.should == %w(C H U N K Y)
end
it "yields each split substring with a pattern" do
a = []
returned_object = "chunky-bacon".split("-", 0) { |str| a << str.capitalize }
returned_object.should == "chunky-bacon"
a.should == ["Chunky", "Bacon"]
end
it "yields each split substring with empty regexp pattern" do
a = []
returned_object = "chunky".split(//) { |str| a << str.capitalize }
returned_object.should == "chunky"
a.should == %w(C H U N K Y)
end
it "yields each split substring with empty regexp pattern and limit" do
a = []
returned_object = "chunky".split(//, 3) { |str| a << str.capitalize }
returned_object.should == "chunky"
a.should == %w(C H Unky)
end
it "yields each split substring with a regexp pattern" do
a = []
returned_object = "chunky:bacon".split(/:/) { |str| a << str.capitalize }
returned_object.should == "chunky:bacon"
a.should == ["Chunky", "Bacon"]
end
it "returns a string as is (and doesn't call block) if it is empty" do
a = []
returned_object = "".split { |str| a << str.capitalize }
returned_object.should == ""
a.should == []
end
end
describe "for a String subclass" do
it "yields instances of the same subclass" do
a = []

Просмотреть файл

@ -10,6 +10,13 @@ describe 'Thread::Backtrace::Location#absolute_path' do
@frame.absolute_path.should == File.realpath(__FILE__)
end
it 'returns an absolute path when using a relative main script path' do
script = fixture(__FILE__, 'absolute_path_main.rb')
Dir.chdir(File.dirname(script)) do
ruby_exe('absolute_path_main.rb').should == "absolute_path_main.rb\n#{script}\n"
end
end
context "when used in eval with a given filename" do
it "returns filename" do
code = "caller_locations(0)[0].absolute_path"

Просмотреть файл

@ -0,0 +1,2 @@
puts __FILE__
puts caller_locations(0)[0].absolute_path

Просмотреть файл

@ -10,4 +10,14 @@ describe 'Thread::Backtrace::Location#lineno' do
it 'returns the absolute path of the call frame' do
@frame.lineno.should == @line
end
it 'should be the same line number as in #to_s, including for core methods' do
# Get the caller_locations from a call made into a core library method
locations = [:non_empty].map { caller_locations }[0]
locations.each do |location|
line_number = location.to_s[/:(\d+):/, 1]
location.lineno.should == Integer(line_number)
end
end
end

Просмотреть файл

@ -87,6 +87,18 @@ describe 'Thread::Backtrace::Location#path' do
end
end
it 'should be the same path as in #to_s, including for core methods' do
# Get the caller_locations from a call made into a core library method
locations = [:non_empty].map { caller_locations }[0]
locations.each do |location|
filename = location.to_s[/^(.+):\d+:/, 1]
path = location.path
path.should == filename
end
end
context "canonicalization" do
platform_is_not :windows do
before :each do

Просмотреть файл

@ -203,6 +203,6 @@ describe "Thread#raise on same thread" do
Thread.current.raise
end
end
-> { t.value }.should raise_error(RuntimeError)
-> { t.value }.should raise_error(RuntimeError, '')
end
end

Просмотреть файл

@ -362,4 +362,22 @@ describe "Executing break from within a block" do
bt2.three
ScratchPad.recorded.should == [:two_ensure, :three_post, :three_ensure]
end
it "works when passing through a super call" do
cls1 = Class.new { def foo; yield; end }
cls2 = Class.new(cls1) { def foo; super { break 1 }; end }
-> do
cls2.new.foo.should == 1
end.should_not raise_error
end
it "raises LocalJumpError when converted into a proc during a a super call" do
cls1 = Class.new { def foo(&b); b; end }
cls2 = Class.new(cls1) { def foo; super { break 1 }.call; end }
-> do
cls2.new.foo
end.should raise_error(LocalJumpError)
end
end

Просмотреть файл

@ -42,7 +42,7 @@ ruby_version_is "2.7" do
end
end
it "warns when numbered parameter is overriten with local variable" do
it "warns when numbered parameter is overwritten with local variable" do
-> {
eval("_1 = 0")
}.should complain(/warning: `_1' is reserved for numbered parameter; consider another name/)
@ -59,7 +59,7 @@ ruby_version_is "2.7" do
}.should raise_error(SyntaxError, /_1 is reserved for numbered parameter/)
end
it "errors when numbered parameter is overriten with local variable" do
it "errors when numbered parameter is overwritten with local variable" do
-> {
eval("_1 = 0")
}.should raise_error(SyntaxError, /_1 is reserved for numbered parameter/)

Просмотреть файл

@ -8,6 +8,11 @@ describe "BigDecimal#to_s" do
@bigneg_str = "-3.1415926535897932384626433832795028841971693993"
@bigdec = BigDecimal(@bigdec_str)
@bigneg = BigDecimal(@bigneg_str)
@internal = Encoding.default_internal
end
after :each do
Encoding.default_internal = @internal
end
it "return type is of class String" do
@ -78,4 +83,15 @@ describe "BigDecimal#to_s" do
end
end
ruby_version_is "2.8" do
it "returns a String in US-ASCII encoding when Encoding.default_internal is nil" do
Encoding.default_internal = nil
BigDecimal('1.23').to_s.encoding.should equal(Encoding::US_ASCII)
end
it "returns a String in US-ASCII encoding when Encoding.default_internal is not nil" do
Encoding.default_internal = Encoding::IBM437
BigDecimal('1.23').to_s.encoding.should equal(Encoding::US_ASCII)
end
end
end

Просмотреть файл

@ -0,0 +1,19 @@
require_relative '../../../spec_helper'
require 'digest'
require_relative '../md5/shared/constants'
describe "Digest::Instance#new" do
it "returns a copy of the digest instance" do
digest = Digest::MD5.new
copy = digest.new
copy.should_not.equal?(digest)
end
it "calls reset" do
digest = Digest::MD5.new
digest << "test"
digest.hexdigest.should != MD5Constants::BlankHexdigest
copy = digest.new
copy.hexdigest.should == MD5Constants::BlankHexdigest
end
end

Просмотреть файл

@ -0,0 +1,23 @@
require_relative '../../spec_helper'
require 'ripper'
describe "Ripper.lex" do
it "lexes a simple method declaration" do
expected = [
[[1, 0], :on_kw, "def", 'FNAME'],
[[1, 3], :on_sp, " ", 'FNAME'],
[[1, 4], :on_ident, "m", 'ENDFN'],
[[1, 5], :on_lparen, "(", 'BEG|LABEL'],
[[1, 6], :on_ident, "a", 'ARG'],
[[1, 7], :on_rparen, ")", 'ENDFN'],
[[1, 8], :on_sp, " ", 'BEG'],
[[1, 9], :on_kw, "nil", 'END'],
[[1, 12], :on_sp, " ", 'END'],
[[1, 13], :on_kw, "end", 'END']
]
lexed = Ripper.lex("def m(a) nil end")
lexed.map { |e|
e[0...-1] + [e[-1].to_s.split('|').map { |s| s.sub(/^EXPR_/, '') }.join('|')]
}.should == expected
end
end

Просмотреть файл

@ -0,0 +1,13 @@
require_relative '../../spec_helper'
require 'ripper'
describe "Ripper.sexp" do
it "returns an s-expression for a method declaration" do
expected = [:program,
[[:def,
[:@ident, "hello", [1, 4]],
[:params, nil, nil, nil, nil, nil, nil, nil],
[:bodystmt, [[:@int, "42", [1, 11]]], nil, nil, nil]]]]
Ripper.sexp("def hello; 42; end").should == expected
end
end

Просмотреть файл

@ -58,6 +58,15 @@ describe "C-API Encoding function" do
end
end
describe "rb_enc_codelen" do
it "returns the correct length for the given codepoint" do
@s.rb_enc_codelen(0x24, Encoding::UTF_8).should == 1
@s.rb_enc_codelen(0xA2, Encoding::UTF_8).should == 2
@s.rb_enc_codelen(0x20AC, Encoding::UTF_8).should == 3
@s.rb_enc_codelen(0x24B62, Encoding::UTF_8).should == 4
end
end
describe "rb_enc_find" do
it "returns the encoding of an Encoding" do
@s.rb_enc_find("UTF-8").should == "UTF-8"
@ -130,6 +139,18 @@ describe "C-API Encoding function" do
end
end
describe "rb_enc_mbcput" do
it "writes the correct bytes to the buffer" do
@s.rb_enc_mbcput(0x24, Encoding::UTF_8).should == "$"
@s.rb_enc_mbcput(0xA2, Encoding::UTF_8).should == "¢"
@s.rb_enc_mbcput(0x20AC, Encoding::UTF_8).should == ""
@s.rb_enc_mbcput(0x24B62, Encoding::UTF_8).should == "𤭢"
@s.rb_enc_mbcput(0x24, Encoding::UTF_16BE).bytes.should == [0, 0x24]
@s.rb_enc_mbcput(0x24B62, Encoding::UTF_16LE).bytes.should == [82, 216, 98, 223]
end
end
describe "rb_usascii_encoding" do
it "returns the encoding for Encoding::US_ASCII" do
@s.rb_usascii_encoding.should == "US-ASCII"
@ -610,4 +631,21 @@ describe "C-API Encoding function" do
end
end
end
describe "ONIGENC_MBC_CASE_FOLD" do
it "returns the correct case fold for the given string" do
@s.ONIGENC_MBC_CASE_FOLD("lower").should == ["l", 1]
@s.ONIGENC_MBC_CASE_FOLD("Upper").should == ["u", 1]
end
it "works with other encodings" do
@s.ONIGENC_MBC_CASE_FOLD("lower".force_encoding("binary")).should == ["l", 1]
@s.ONIGENC_MBC_CASE_FOLD("Upper".force_encoding("binary")).should == ["u", 1]
@s.ONIGENC_MBC_CASE_FOLD("É").should == ["é", 2]
str, length = @s.ONIGENC_MBC_CASE_FOLD('$'.encode(Encoding::UTF_16BE))
length.should == 2
str.bytes.should == [0, 0x24]
end
end
end

Просмотреть файл

@ -127,6 +127,18 @@ static VALUE encoding_spec_rb_enc_mbc_to_codepoint(VALUE self, VALUE str, VALUE
return INT2FIX(rb_enc_mbc_to_codepoint(p, e, rb_enc_get(str)));
}
static VALUE encoding_spec_rb_enc_mbcput(VALUE self, VALUE code, VALUE encoding) {
unsigned int c = FIX2UINT(code);
rb_encoding *enc = rb_to_encoding(encoding);
char buf[ONIGENC_CODE_TO_MBC_MAXLEN];
memset(buf, '\1', sizeof(buf));
int len = rb_enc_mbcput(c, buf, enc);
if (buf[len] != '\1') {
rb_raise(rb_eRuntimeError, "should not change bytes after len");
}
return rb_enc_str_new(buf, len, enc);
}
static VALUE encoding_spec_rb_enc_from_encoding(VALUE self, VALUE name) {
return rb_enc_from_encoding(rb_enc_find(RSTRING_PTR(name)));
}
@ -266,6 +278,28 @@ 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)));
}
static VALUE encoding_spec_ONIGENC_MBC_CASE_FOLD(VALUE self, VALUE str) {
char *beg = RSTRING_PTR(str);
char *beg_initial = beg;
char *end = beg + 2;
OnigUChar fold[ONIGENC_GET_CASE_FOLD_CODES_MAX_NUM];
memset(fold, '\1', sizeof(fold));
rb_encoding *enc = rb_enc_get(str);
int r = ONIGENC_MBC_CASE_FOLD(enc, ONIGENC_CASE_FOLD, &beg, (const OnigUChar *)end, fold);
if (r > 0 && fold[r] != '\1') {
rb_raise(rb_eRuntimeError, "should not change bytes after len");
}
VALUE str_result = r <= 0 ? Qnil : rb_enc_str_new((char *)fold, r, enc);
long bytes_used = beg - beg_initial;
return rb_ary_new3(2, str_result, INT2FIX(bytes_used));
}
static VALUE encoding_spec_rb_enc_codelen(VALUE self, VALUE code, VALUE encoding) {
unsigned int c = FIX2UINT(code);
rb_encoding *enc = rb_to_encoding(encoding);
return INT2FIX(rb_enc_codelen(c, enc));
}
void Init_encoding_spec(void) {
VALUE cls;
native_rb_encoding_pointer = (rb_encoding**) malloc(sizeof(rb_encoding*));
@ -299,12 +333,14 @@ void Init_encoding_spec(void) {
rb_define_method(cls, "rb_enc_associate_index", encoding_spec_rb_enc_associate_index, 2);
rb_define_method(cls, "rb_enc_compatible", encoding_spec_rb_enc_compatible, 2);
rb_define_method(cls, "rb_enc_copy", encoding_spec_rb_enc_copy, 2);
rb_define_method(cls, "rb_enc_codelen", encoding_spec_rb_enc_codelen, 2);
rb_define_method(cls, "rb_enc_find", encoding_spec_rb_enc_find, 1);
rb_define_method(cls, "rb_enc_find_index", encoding_spec_rb_enc_find_index, 1);
rb_define_method(cls, "rb_enc_isalnum", encoding_spec_rb_enc_isalnum, 2);
rb_define_method(cls, "rb_enc_isspace", encoding_spec_rb_enc_isspace, 2);
rb_define_method(cls, "rb_enc_from_index", encoding_spec_rb_enc_from_index, 1);
rb_define_method(cls, "rb_enc_mbc_to_codepoint", encoding_spec_rb_enc_mbc_to_codepoint, 2);
rb_define_method(cls, "rb_enc_mbcput", encoding_spec_rb_enc_mbcput, 2);
rb_define_method(cls, "rb_enc_from_encoding", encoding_spec_rb_enc_from_encoding, 1);
rb_define_method(cls, "rb_enc_get", encoding_spec_rb_enc_get, 1);
rb_define_method(cls, "rb_enc_precise_mbclen", encoding_spec_rb_enc_precise_mbclen, 2);
@ -326,6 +362,7 @@ void Init_encoding_spec(void) {
rb_define_method(cls, "rb_enc_codepoint_len", encoding_spec_rb_enc_codepoint_len, 1);
rb_define_method(cls, "rb_enc_str_asciionly_p", encoding_spec_rb_enc_str_asciionly_p, 1);
rb_define_method(cls, "rb_uv_to_utf8", encoding_spec_rb_uv_to_utf8, 2);
rb_define_method(cls, "ONIGENC_MBC_CASE_FOLD", encoding_spec_ONIGENC_MBC_CASE_FOLD, 1);
}
#ifdef __cplusplus

Просмотреть файл

@ -148,6 +148,18 @@ VALUE sws_typed_change_struct(VALUE self, VALUE obj, VALUE new_val) {
return Qnil;
}
VALUE sws_typed_rb_check_typeddata_same_type(VALUE self, VALUE obj) {
return rb_check_typeddata(obj, &sample_typed_wrapped_struct_data_type) == DATA_PTR(obj) ? Qtrue : Qfalse;
}
VALUE sws_typed_rb_check_typeddata_same_type_parent(VALUE self, VALUE obj) {
return rb_check_typeddata(obj, &sample_typed_wrapped_struct_parent_data_type) == DATA_PTR(obj) ? Qtrue : Qfalse;
}
VALUE sws_typed_rb_check_typeddata_different_type(VALUE self, VALUE obj) {
return rb_check_typeddata(obj, &sample_typed_wrapped_struct_other_data_type) == DATA_PTR(obj) ? Qtrue : Qfalse;
}
void Init_typed_data_spec(void) {
VALUE cls = rb_define_class("CApiAllocTypedSpecs", rb_cObject);
rb_define_alloc_func(cls, sdaf_alloc_typed_func);
@ -160,6 +172,9 @@ void Init_typed_data_spec(void) {
rb_define_method(cls, "typed_get_struct_rdata", sws_typed_get_struct_rdata, 1);
rb_define_method(cls, "typed_get_struct_data_ptr", sws_typed_get_struct_data_ptr, 1);
rb_define_method(cls, "typed_change_struct", sws_typed_change_struct, 2);
rb_define_method(cls, "rb_check_typeddata_same_type", sws_typed_rb_check_typeddata_same_type, 1);
rb_define_method(cls, "rb_check_typeddata_same_type_parent", sws_typed_rb_check_typeddata_same_type_parent, 1);
rb_define_method(cls, "rb_check_typeddata_different_type", sws_typed_rb_check_typeddata_different_type, 1);
}
#ifdef __cplusplus

Просмотреть файл

@ -831,6 +831,7 @@ describe "CApiObject" do
it "returns nil if the instance variable has not been initialized and is not a valid Ruby name" do
@o.rb_ivar_get(@test, :bar).should == nil
@o.rb_ivar_get(@test, :mesg).should == nil
end
it 'returns the instance variable when it is not a valid Ruby name' do
@ -866,6 +867,7 @@ describe "CApiObject" do
it "does not throw an error if the instance variable is not a valid Ruby name" do
@o.rb_ivar_defined(@test, :bar).should == false
@o.rb_ivar_defined(@test, :mesg).should == false
end
end

Просмотреть файл

@ -76,6 +76,7 @@ def compile_extension(name)
$ruby = ENV.values_at('RUBY_EXE', 'RUBY_FLAGS').join(' ')
# MRI magic to consider building non-bundled extensions
$extout = nil
$warnflags << ' -Wno-declaration-after-statement'
create_makefile(#{ext.inspect})
RUBY
output = ruby_exe("extconf.rb")

Просмотреть файл

@ -57,4 +57,21 @@ describe "CApiWrappedTypedStruct" do
@s.typed_get_struct_data_ptr(a).should == 1024
end
end
describe "rb_check_typeddata" do
it "returns data pointer when the struct has the given type" do
a = @s.typed_wrap_struct(1024)
@s.rb_check_typeddata_same_type(a).should == true
end
it "returns data pointer when the parent struct has the given type" do
a = @s.typed_wrap_struct(1024)
@s.rb_check_typeddata_same_type_parent(a).should == true
end
it "raises an error for different types" do
a = @s.typed_wrap_struct(1024)
-> { @s.rb_check_typeddata_different_type(a) }.should raise_error(TypeError)
end
end
end

Просмотреть файл

@ -25,6 +25,14 @@ describe :kernel_raise, shared: true do
-> { @object.raise("a bad thing") }.should raise_error(RuntimeError)
end
it "passes no arguments to the constructor when given only an exception class" do
klass = Class.new(Exception) do
def initialize
end
end
-> { @object.raise(klass) }.should raise_error(klass) { |e| e.message.should == klass.to_s }
end
it "raises a TypeError when passed a non-Exception object" do
-> { @object.raise(Object.new) }.should raise_error(TypeError)
end
@ -41,25 +49,6 @@ describe :kernel_raise, shared: true do
-> { @object.raise(nil) }.should raise_error(TypeError)
end
it "re-raises the previously rescued exception if no exception is specified" do
-> do
begin
@object.raise Exception, "outer"
ScratchPad.record :no_abort
rescue
begin
@object.raise StandardError, "inner"
rescue
end
@object.raise
ScratchPad.record :no_reraise
end
end.should raise_error(Exception, "outer")
ScratchPad.recorded.should be_nil
end
it "re-raises a previously rescued exception without overwriting the backtrace" do
begin
initial_raise_line = __LINE__; @object.raise 'raised'

Просмотреть файл

@ -91,4 +91,24 @@ describe :process_exit!, shared: true do
out.should == ""
$?.exitstatus.should == 21
end
it "skips at_exit handlers" do
out = ruby_exe("at_exit { STDERR.puts 'at_exit' }; #{@object}.send(:exit!, 21)", args: '2>&1')
out.should == ""
$?.exitstatus.should == 21
end
it "overrides the original exception and exit status when called from #at_exit" do
code = <<-RUBY
at_exit do
STDERR.puts 'in at_exit'
STDERR.puts "$! is \#{$!.class}:\#{$!.message}"
#{@object}.send(:exit!, 21)
end
raise 'original error'
RUBY
out = ruby_exe(code, args: '2>&1')
out.should == "in at_exit\n$! is RuntimeError:original error\n"
$?.exitstatus.should == 21
end
end