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 2020-05-31 18:22:49 +02:00
parent f4502b001a
commit 34776105c8
46 changed files with 629 additions and 94 deletions

View file

@ -59,8 +59,8 @@ There are a few extra specific matchers used in the couple specs that need it.
(1 + 2).should == 3 # Calls #== (1 + 2).should == 3 # Calls #==
(1 + 2).should_not == 5 (1 + 2).should_not == 5
File.should equal(File) # Calls #equal? (tests identity) File.should.equal?(File) # Calls #equal? (tests identity)
(1 + 2).should eql(3) # Calls #eql? (Hash equality) (1 + 2).should.eql?(3) # Calls #eql? (Hash equality)
1.should < 2 1.should < 2
2.should <= 2 2.should <= 2
@ -73,11 +73,14 @@ File.should equal(File) # Calls #equal? (tests identity)
#### Predicate matchers #### Predicate matchers
```ruby ```ruby
[].should be_empty # Calls #empty? [].should.empty?
[1,2,3].should include(2) # Calls #include? [1,2,3].should.include?(2)
"hello".should.start_with?("h")
"hello".should.end_with?("o")
(0.1 + 0.2).should be_close(0.3, TOLERANCE) # (0.2-0.1).abs < TOLERANCE (0.1 + 0.2).should be_close(0.3, TOLERANCE) # (0.2-0.1).abs < TOLERANCE
(0.0/0.0).should be_nan # Calls Float#nan? (0.0/0.0).should.nan?
(1.0/0.0).should be_positive_infinity (1.0/0.0).should be_positive_infinity
(-1.0/0.0).should be_negative_infinity (-1.0/0.0).should be_negative_infinity
@ -85,7 +88,7 @@ File.should equal(File) # Calls #equal? (tests identity)
3.14.should be_kind_of(Numeric) # Calls #is_a? 3.14.should be_kind_of(Numeric) # Calls #is_a?
Numeric.should be_ancestor_of(Float) # Float.ancestors.include?(Numeric) Numeric.should be_ancestor_of(Float) # Float.ancestors.include?(Numeric)
3.14.should respond_to(:to_i) # Calls #respond_to? 3.14.should.respond_to?(:to_i)
Fixnum.should have_instance_method(:+) Fixnum.should have_instance_method(:+)
Array.should have_method(:new) Array.should have_method(:new)
``` ```
@ -103,7 +106,7 @@ Also `have_constant`, `have_private_instance_method`, `have_singleton_method`, e
raise "oops" raise "oops"
}.should raise_error(RuntimeError) { |e| }.should raise_error(RuntimeError) { |e|
# Custom checks on the Exception object # Custom checks on the Exception object
e.message.should include("oops") e.message.should.include?("oops")
e.cause.should == nil e.cause.should == nil
} }
``` ```

View file

@ -144,7 +144,7 @@ module ArraySpecs
end end
def self.universal_pack_object def self.universal_pack_object
obj = mock("string float int") obj = mock("string float int".freeze)
obj.stub!(:to_int).and_return(1) obj.stub!(:to_int).and_return(1)
obj.stub!(:to_str).and_return("1") obj.stub!(:to_str).and_return("1")
obj.stub!(:to_f).and_return(1.0) obj.stub!(:to_f).and_return(1.0)

View file

@ -466,6 +466,12 @@ describe :array_slice, shared: true do
obj = 8e19 obj = 8e19
-> { array.send(@method, obj) }.should raise_error(RangeError) -> { array.send(@method, obj) }.should raise_error(RangeError)
# boundary value when longs are 64 bits
-> { array.send(@method, 2.0**63) }.should raise_error(RangeError)
# just under the boundary value when longs are 64 bits
array.send(@method, max_long.to_f.prev_float).should == nil
end end
it "raises a RangeError when the length is out of range of Fixnum" do it "raises a RangeError when the length is out of range of Fixnum" do

View file

@ -75,5 +75,36 @@ describe "Enumerator.new" do
enum.to_a.should == ["a\n", "b\n", "c"] enum.to_a.should == ["a\n", "b\n", "c"]
end end
end end
describe 'yielded values' do
it 'handles yield arguments properly' do
Enumerator.new { |y| y.yield(1) }.to_a.should == [1]
Enumerator.new { |y| y.yield(1) }.first.should == 1
Enumerator.new { |y| y.yield([1]) }.to_a.should == [[1]]
Enumerator.new { |y| y.yield([1]) }.first.should == [1]
Enumerator.new { |y| y.yield(1, 2) }.to_a.should == [[1, 2]]
Enumerator.new { |y| y.yield(1, 2) }.first.should == [1, 2]
Enumerator.new { |y| y.yield([1, 2]) }.to_a.should == [[1, 2]]
Enumerator.new { |y| y.yield([1, 2]) }.first.should == [1, 2]
end
it 'handles << arguments properly' do
Enumerator.new { |y| y.<<(1) }.to_a.should == [1]
Enumerator.new { |y| y.<<(1) }.first.should == 1
Enumerator.new { |y| y.<<([1]) }.to_a.should == [[1]]
Enumerator.new { |y| y.<<([1]) }.first.should == [1]
# << doesn't accept multiple arguments
# Enumerator.new { |y| y.<<(1, 2) }.to_a.should == [[1, 2]]
# Enumerator.new { |y| y.<<(1, 2) }.first.should == [1, 2]
Enumerator.new { |y| y.<<([1, 2]) }.to_a.should == [[1, 2]]
Enumerator.new { |y| y.<<([1, 2]) }.first.should == [1, 2]
end
end
end end
end end

View file

@ -21,4 +21,27 @@ describe "Enumerator::Yielder#<<" do
y = Enumerator::Yielder.new {|x| x + 1} y = Enumerator::Yielder.new {|x| x + 1}
(y << 1).should equal(y) (y << 1).should equal(y)
end end
context "when multiple arguments passed" do
ruby_version_is '' ... '2.6' do
it "yields the arguments list to the block" do
ary = []
y = Enumerator::Yielder.new { |*x| ary << x }
y.<<(1, 2)
ary.should == [[1, 2]]
end
end
ruby_version_is '2.6' do
it "raises an ArgumentError" do
ary = []
y = Enumerator::Yielder.new { |*x| ary << x }
-> {
y.<<(1, 2)
}.should raise_error(ArgumentError, /wrong number of arguments/)
end
end
end
end end

View file

@ -20,4 +20,14 @@ describe "Enumerator::Yielder#yield" do
y = Enumerator::Yielder.new {|x| x + 1} y = Enumerator::Yielder.new {|x| x + 1}
y.yield(1).should == 2 y.yield(1).should == 2
end end
context "when multiple arguments passed" do
it "yields the arguments list to the block" do
ary = []
y = Enumerator::Yielder.new { |*x| ary << x }
y.yield(1, 2)
ary.should == [[1, 2]]
end
end
end end

View file

@ -71,8 +71,11 @@ describe "Integer#<< (with n << m)" do
it "calls #to_int to convert the argument to an Integer" do it "calls #to_int to convert the argument to an Integer" do
obj = mock("4") obj = mock("4")
obj.should_receive(:to_int).and_return(4) obj.should_receive(:to_int).and_return(4)
(3 << obj).should == 48 (3 << obj).should == 48
obj = mock("to_int_neg_bignum")
obj.should_receive(:to_int).and_return(-bignum_value)
(3 << obj).should == 0
end end
it "raises a TypeError when #to_int does not return an Integer" do it "raises a TypeError when #to_int does not return an Integer" do

View file

@ -71,8 +71,11 @@ describe "Integer#>> (with n >> m)" do
it "calls #to_int to convert the argument to an Integer" do it "calls #to_int to convert the argument to an Integer" do
obj = mock("2") obj = mock("2")
obj.should_receive(:to_int).and_return(2) obj.should_receive(:to_int).and_return(2)
(8 >> obj).should == 2 (8 >> obj).should == 2
obj = mock("to_int_bignum")
obj.should_receive(:to_int).and_return(bignum_value)
(8 >> obj).should == 0
end end
it "raises a TypeError when #to_int does not return an Integer" do it "raises a TypeError when #to_int does not return an Integer" do

View file

@ -242,6 +242,15 @@ describe :kernel_require, shared: true do
@object.require("load_fixture").should be_true @object.require("load_fixture").should be_true
ScratchPad.recorded.should == [:loaded] ScratchPad.recorded.should == [:loaded]
end end
ruby_bug "#16926", "2.7"..."2.8" do
it "does not load a feature twice when $LOAD_PATH has been modified" do
$LOAD_PATH.replace [CODE_LOADING_DIR]
@object.require("load_fixture").should be_true
$LOAD_PATH.replace [File.expand_path("b", CODE_LOADING_DIR), CODE_LOADING_DIR]
@object.require("load_fixture").should be_false
end
end
end end
describe "(file extensions)" do describe "(file extensions)" do

View file

@ -294,6 +294,32 @@ describe "Module#autoload" do
ScratchPad.recorded.should == [nil, nil] ScratchPad.recorded.should == [nil, nil]
@check.call.should == ["constant", nil] @check.call.should == ["constant", nil]
end end
it "does not raise an error if the autoload constant was not defined" do
module ModuleSpecs::Autoload
autoload :RequiredDirectlyNoConstant, fixture(__FILE__, "autoload_required_directly_no_constant.rb")
end
@path = fixture(__FILE__, "autoload_required_directly_no_constant.rb")
@remove << :RequiredDirectlyNoConstant
@check = -> {
[
defined?(ModuleSpecs::Autoload::RequiredDirectlyNoConstant),
ModuleSpecs::Autoload.constants(false).include?(:RequiredDirectlyNoConstant),
ModuleSpecs::Autoload.const_defined?(:RequiredDirectlyNoConstant),
ModuleSpecs::Autoload.autoload?(:RequiredDirectlyNoConstant)
]
}
ScratchPad.record @check
@check.call.should == ["constant", true, true, @path]
$:.push File.dirname(@path)
begin
require "autoload_required_directly_no_constant.rb"
ensure
$:.pop
end
ScratchPad.recorded.should == [nil, true, false, nil]
@check.call.should == [nil, true, false, nil]
end
end end
describe "after the autoload is triggered by require" do describe "after the autoload is triggered by require" do

View file

@ -0,0 +1,2 @@
block = ScratchPad.recorded
ScratchPad.record(block.call)

View file

@ -29,33 +29,35 @@ describe 'Thread::Backtrace::Location#absolute_path' do
end end
end end
platform_is_not :windows do context "canonicalization" do
before :each do platform_is_not :windows do
@file = fixture(__FILE__, "absolute_path.rb") before :each do
@symlink = tmp("symlink.rb") @file = fixture(__FILE__, "absolute_path.rb")
File.symlink(@file, @symlink) @symlink = tmp("symlink.rb")
ScratchPad.record [] File.symlink(@file, @symlink)
end ScratchPad.record []
end
after :each do after :each do
rm_r @symlink rm_r @symlink
end end
it "returns a canonical path without symlinks, even when __FILE__ does not" do it "returns a canonical path without symlinks, even when __FILE__ does not" do
realpath = File.realpath(@symlink) realpath = File.realpath(@symlink)
realpath.should_not == @symlink realpath.should_not == @symlink
load @symlink load @symlink
ScratchPad.recorded.should == [@symlink, realpath] ScratchPad.recorded.should == [@symlink, realpath]
end end
it "returns a canonical path without symlinks, even when __FILE__ is removed" do it "returns a canonical path without symlinks, even when __FILE__ is removed" do
realpath = File.realpath(@symlink) realpath = File.realpath(@symlink)
realpath.should_not == @symlink realpath.should_not == @symlink
ScratchPad << -> { rm_r(@symlink) } ScratchPad << -> { rm_r(@symlink) }
load @symlink load @symlink
ScratchPad.recorded.should == [@symlink, realpath] ScratchPad.recorded.should == [@symlink, realpath]
end
end end
end end
end end

View file

@ -0,0 +1,2 @@
ScratchPad << __FILE__
ScratchPad << caller_locations(0)[0].path

View file

@ -86,4 +86,27 @@ describe 'Thread::Backtrace::Location#path' do
end end
end end
end end
context "canonicalization" do
platform_is_not :windows do
before :each do
@file = fixture(__FILE__, "path.rb")
@symlink = tmp("symlink.rb")
File.symlink(@file, @symlink)
ScratchPad.record []
end
after :each do
rm_r @symlink
end
it "returns a non-canonical path with symlinks, the same as __FILE__" do
realpath = File.realpath(@symlink)
realpath.should_not == @symlink
load @symlink
ScratchPad.recorded.should == [@symlink, @symlink]
end
end
end
end end

View file

@ -1,4 +1,5 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe 'TracePoint#binding' do describe 'TracePoint#binding' do
def test def test
@ -8,6 +9,7 @@ describe 'TracePoint#binding' do
it 'return the generated binding object from event' do it 'return the generated binding object from event' do
bindings = [] bindings = []
TracePoint.new(:return) { |tp| TracePoint.new(:return) { |tp|
next unless TracePointSpec.target_thread?
bindings << tp.binding bindings << tp.binding
}.enable { }.enable {
test test

View file

@ -7,6 +7,7 @@ describe "TracePoint#callee_id" do
obj = TracePointSpec::ClassWithMethodAlias.new obj = TracePointSpec::ClassWithMethodAlias.new
TracePoint.new(:call) do |tp| TracePoint.new(:call) do |tp|
next unless TracePointSpec.target_thread?
a << tp.callee_id a << tp.callee_id
end.enable do end.enable do
obj.m_alias obj.m_alias

View file

@ -5,6 +5,7 @@ describe 'TracePoint#defined_class' do
it 'returns class or module of the method being called' do it 'returns class or module of the method being called' do
last_class_name = nil last_class_name = nil
TracePoint.new(:call) do |tp| TracePoint.new(:call) do |tp|
next unless TracePointSpec.target_thread?
last_class_name = tp.defined_class last_class_name = tp.defined_class
end.enable do end.enable do
TracePointSpec::B.new.foo TracePointSpec::B.new.foo

View file

@ -1,9 +1,11 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe 'TracePoint#disable' do describe 'TracePoint#disable' do
it 'returns true if trace was enabled' do it 'returns true if trace was enabled' do
called = false called = false
trace = TracePoint.new(:line) do |tp| trace = TracePoint.new(:line) do |tp|
next unless TracePointSpec.target_thread?
called = true called = true
end end
@ -25,6 +27,7 @@ describe 'TracePoint#disable' do
it 'returns false if trace was disabled' do it 'returns false if trace was disabled' do
called = false called = false
trace = TracePoint.new(:line) do |tp| trace = TracePoint.new(:line) do |tp|
next unless TracePointSpec.target_thread?
called = true called = true
end end

View file

@ -1,20 +1,21 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe 'TracePoint#enable' do describe 'TracePoint#enable' do
# def test; end
describe 'without a block' do describe 'without a block' do
it 'returns true if trace was enabled' do it 'returns false if trace was disabled' do
called = false called = false
trace = TracePoint.new(:line) do |tp| trace = TracePoint.new(:line) do |tp|
next unless TracePointSpec.target_thread?
called = true called = true
end end
line_event = true line_event = true
called.should == false called.should == false
trace.enable ret = trace.enable
begin begin
ret.should == false
line_event = true line_event = true
called.should == true called.should == true
ensure ensure
@ -22,30 +23,27 @@ describe 'TracePoint#enable' do
end end
end end
it 'returns false if trace was disabled' do it 'returns true if trace was already enabled' do
called = false called = false
trace = TracePoint.new(:line) do |tp| trace = TracePoint.new(:line) do |tp|
next unless TracePointSpec.target_thread?
called = true called = true
end end
trace.enable.should == false
begin
line_event = true
called.should == true
ensure
trace.disable
end
called = false
line_event = true line_event = true
called.should == false called.should == false
trace.enable.should == false ret = trace.enable
begin begin
ret.should == false
trace.enable.should == true
line_event = true line_event = true
called.should == true called.should == true
ensure ensure
trace.disable trace.disable
trace.should_not.enabled?
end end
end end
end end
@ -54,13 +52,38 @@ describe 'TracePoint#enable' do
it 'enables the trace object within a block' do it 'enables the trace object within a block' do
event_name = nil event_name = nil
TracePoint.new(:line) do |tp| TracePoint.new(:line) do |tp|
next unless TracePointSpec.target_thread?
event_name = tp.event event_name = tp.event
end.enable { event_name.should equal(:line) } end.enable { event_name.should equal(:line) }
end end
it 'enables the trace object for any thread' do
threads = []
trace = TracePoint.new(:line) do |tp|
# Runs on purpose on any Thread
threads << Thread.current
end
thread = nil
trace.enable do
line_event = true
thread = Thread.new do
event_in_other_thread = true
end
thread.join
end
threads = threads.uniq
threads.should.include?(Thread.current)
threads.should.include?(thread)
end
it 'can accept arguments within a block but it should not yield arguments' do it 'can accept arguments within a block but it should not yield arguments' do
event_name = nil event_name = nil
trace = TracePoint.new(:line) { |tp| event_name = tp.event } trace = TracePoint.new(:line) do |tp|
next unless TracePointSpec.target_thread?
event_name = tp.event
end
trace.enable do |*args| trace.enable do |*args|
event_name.should equal(:line) event_name.should equal(:line)
args.should == [] args.should == []
@ -87,7 +110,10 @@ describe 'TracePoint#enable' do
it 'disables the trace object outside the block' do it 'disables the trace object outside the block' do
called = false called = false
trace = TracePoint.new(:line) { called = true } trace = TracePoint.new(:line) do
next unless TracePointSpec.target_thread?
called = true
end
trace.enable { trace.enable {
line_event = true line_event = true
} }
@ -96,6 +122,35 @@ describe 'TracePoint#enable' do
end end
end end
describe "when nested" do
it "enables both TracePoints but only calls the respective callbacks" do
called = false
first = TracePoint.new(:line) do |tp|
next unless TracePointSpec.target_thread?
called = true
end
all = []
inspects = []
second = TracePoint.new(:line) { |tp|
next unless TracePointSpec.target_thread?
all << tp
inspects << tp.inspect
}
line = nil
first.enable do
second.enable do
line = __LINE__
end
end
all.uniq.should == [second]
inspects.uniq.should == ["#<TracePoint:line@#{__FILE__}:#{line}>"]
called.should == true
end
end
ruby_version_is "2.6" do ruby_version_is "2.6" do
describe 'target: option' do describe 'target: option' do
before :each do before :each do
@ -104,6 +159,7 @@ describe 'TracePoint#enable' do
it 'enables trace point for specific location' do it 'enables trace point for specific location' do
trace = TracePoint.new(:call) do |tp| trace = TracePoint.new(:call) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << tp.method_id ScratchPad << tp.method_id
end end
@ -121,6 +177,7 @@ describe 'TracePoint#enable' do
it 'traces all the events triggered in specified location' do it 'traces all the events triggered in specified location' do
trace = TracePoint.new(:line, :call, :return, :b_call, :b_return) do |tp| trace = TracePoint.new(:line, :call, :return, :b_call, :b_return) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << tp.event ScratchPad << tp.event
end end
@ -140,6 +197,7 @@ describe 'TracePoint#enable' do
it 'does not trace events in nested locations' do it 'does not trace events in nested locations' do
trace = TracePoint.new(:call) do |tp| trace = TracePoint.new(:call) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << tp.method_id ScratchPad << tp.method_id
end end
@ -177,6 +235,7 @@ describe 'TracePoint#enable' do
end end
trace = TracePoint.new(:b_call) do |tp| trace = TracePoint.new(:b_call) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << tp.lineno ScratchPad << tp.lineno
end end
@ -193,6 +252,7 @@ describe 'TracePoint#enable' do
describe 'option value' do describe 'option value' do
it 'accepts Method' do it 'accepts Method' do
trace = TracePoint.new(:call) do |tp| trace = TracePoint.new(:call) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << tp.method_id ScratchPad << tp.method_id
end end
@ -208,6 +268,7 @@ describe 'TracePoint#enable' do
it 'accepts UnboundMethod' do it 'accepts UnboundMethod' do
trace = TracePoint.new(:call) do |tp| trace = TracePoint.new(:call) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << tp.method_id ScratchPad << tp.method_id
end end
@ -225,6 +286,7 @@ describe 'TracePoint#enable' do
it 'accepts Proc' do it 'accepts Proc' do
trace = TracePoint.new(:b_call) do |tp| trace = TracePoint.new(:b_call) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << tp.lineno ScratchPad << tp.lineno
end end
@ -242,6 +304,7 @@ describe 'TracePoint#enable' do
it "raises ArgumentError if target object cannot trigger specified event" do it "raises ArgumentError if target object cannot trigger specified event" do
trace = TracePoint.new(:call) do |tp| trace = TracePoint.new(:call) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << tp.method_id ScratchPad << tp.method_id
end end
@ -255,8 +318,7 @@ describe 'TracePoint#enable' do
end end
it "raises ArgumentError if passed not Method/UnboundMethod/Proc" do it "raises ArgumentError if passed not Method/UnboundMethod/Proc" do
trace = TracePoint.new(:call) do |tp| trace = TracePoint.new(:call) {}
end
-> { -> {
trace.enable(target: Object.new) do trace.enable(target: Object.new) do
@ -266,8 +328,7 @@ describe 'TracePoint#enable' do
context "nested enabling and disabling" do context "nested enabling and disabling" do
it "raises ArgumentError if trace point already enabled with target is re-enabled with target" do it "raises ArgumentError if trace point already enabled with target is re-enabled with target" do
trace = TracePoint.new(:b_call) do trace = TracePoint.new(:b_call) {}
end
-> { -> {
trace.enable(target: -> {}) do trace.enable(target: -> {}) do
@ -278,8 +339,7 @@ describe 'TracePoint#enable' do
end end
it "raises ArgumentError if trace point already enabled without target is re-enabled with target" do it "raises ArgumentError if trace point already enabled without target is re-enabled with target" do
trace = TracePoint.new(:b_call) do trace = TracePoint.new(:b_call) {}
end
-> { -> {
trace.enable do trace.enable do
@ -290,8 +350,7 @@ describe 'TracePoint#enable' do
end end
it "raises ArgumentError if trace point already enabled with target is re-enabled without target" do it "raises ArgumentError if trace point already enabled with target is re-enabled without target" do
trace = TracePoint.new(:b_call) do trace = TracePoint.new(:b_call) {}
end
-> { -> {
trace.enable(target: -> {}) do trace.enable(target: -> {}) do
@ -302,8 +361,7 @@ describe 'TracePoint#enable' do
end end
it "raises ArgumentError if trace point already enabled with target is disabled with block" do it "raises ArgumentError if trace point already enabled with target is disabled with block" do
trace = TracePoint.new(:b_call) do trace = TracePoint.new(:b_call) {}
end
-> { -> {
trace.enable(target: -> {}) do trace.enable(target: -> {}) do
@ -315,10 +373,12 @@ describe 'TracePoint#enable' do
it "traces events when trace point with target is enabled in another trace point enabled without target" do it "traces events when trace point with target is enabled in another trace point enabled without target" do
trace_outer = TracePoint.new(:b_call) do |tp| trace_outer = TracePoint.new(:b_call) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << :outer ScratchPad << :outer
end end
trace_inner = TracePoint.new(:b_call) do |tp| trace_inner = TracePoint.new(:b_call) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << :inner ScratchPad << :inner
end end
@ -335,10 +395,12 @@ describe 'TracePoint#enable' do
it "traces events when trace point with target is enabled in another trace point enabled with target" do it "traces events when trace point with target is enabled in another trace point enabled with target" do
trace_outer = TracePoint.new(:b_call) do |tp| trace_outer = TracePoint.new(:b_call) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << :outer ScratchPad << :outer
end end
trace_inner = TracePoint.new(:b_call) do |tp| trace_inner = TracePoint.new(:b_call) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << :inner ScratchPad << :inner
end end
@ -355,10 +417,12 @@ describe 'TracePoint#enable' do
it "traces events when trace point without target is enabled in another trace point enabled with target" do it "traces events when trace point without target is enabled in another trace point enabled with target" do
trace_outer = TracePoint.new(:b_call) do |tp| trace_outer = TracePoint.new(:b_call) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << :outer ScratchPad << :outer
end end
trace_inner = TracePoint.new(:b_call) do |tp| trace_inner = TracePoint.new(:b_call) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << :inner ScratchPad << :inner
end end
@ -382,6 +446,7 @@ describe 'TracePoint#enable' do
it "traces :line events only on specified line of code" do it "traces :line events only on specified line of code" do
trace = TracePoint.new(:line) do |tp| trace = TracePoint.new(:line) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << tp.lineno ScratchPad << tp.lineno
end end
@ -401,8 +466,7 @@ describe 'TracePoint#enable' do
end end
it "raises ArgumentError if :target option isn't specified" do it "raises ArgumentError if :target option isn't specified" do
trace = TracePoint.new(:line) do |tp| trace = TracePoint.new(:line) {}
end
-> { -> {
trace.enable(target_line: 67) do trace.enable(target_line: 67) do
@ -411,8 +475,7 @@ describe 'TracePoint#enable' do
end end
it "raises ArgumentError if :line event isn't registered" do it "raises ArgumentError if :line event isn't registered" do
trace = TracePoint.new(:call) do |tp| trace = TracePoint.new(:call) {}
end
target = -> { target = -> {
x = 1 x = 1
@ -429,8 +492,7 @@ describe 'TracePoint#enable' do
end end
it "raises ArgumentError if :target_line value is out of target code lines range" do it "raises ArgumentError if :target_line value is out of target code lines range" do
trace = TracePoint.new(:line) do |tp| trace = TracePoint.new(:line) {}
end
-> { -> {
trace.enable(target_line: 1, target: -> { }) do trace.enable(target_line: 1, target: -> { }) do
@ -439,8 +501,7 @@ describe 'TracePoint#enable' do
end end
it "raises TypeError if :target_line value couldn't be coerced to Integer" do it "raises TypeError if :target_line value couldn't be coerced to Integer" do
trace = TracePoint.new(:line) do |tp| trace = TracePoint.new(:line) {}
end
-> { -> {
trace.enable(target_line: Object.new, target: -> { }) do trace.enable(target_line: Object.new, target: -> { }) do
@ -449,8 +510,7 @@ describe 'TracePoint#enable' do
end end
it "raises ArgumentError if :target_line value is negative" do it "raises ArgumentError if :target_line value is negative" do
trace = TracePoint.new(:line) do |tp| trace = TracePoint.new(:line) {}
end
-> { -> {
trace.enable(target_line: -2, target: -> { }) do trace.enable(target_line: -2, target: -> { }) do
@ -460,6 +520,7 @@ describe 'TracePoint#enable' do
it "accepts value that could be coerced to Integer" do it "accepts value that could be coerced to Integer" do
trace = TracePoint.new(:line) do |tp| trace = TracePoint.new(:line) do |tp|
next unless TracePointSpec.target_thread?
ScratchPad << tp.lineno ScratchPad << tp.lineno
end end

View file

@ -1,4 +1,5 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe 'TracePoint#enabled?' do describe 'TracePoint#enabled?' do
it 'returns true when current status of the trace is enable' do it 'returns true when current status of the trace is enable' do

View file

@ -13,6 +13,7 @@ ruby_version_is "2.6" do
CODE CODE
TracePoint.new(:script_compiled) do |e| TracePoint.new(:script_compiled) do |e|
next unless TracePointSpec.target_thread?
ScratchPad << e.eval_script ScratchPad << e.eval_script
end.enable do end.enable do
eval script eval script

View file

@ -5,6 +5,7 @@ describe 'TracePoint#event' do
it 'returns the type of event' do it 'returns the type of event' do
event_name = nil event_name = nil
TracePoint.new(:end, :call) do |tp| TracePoint.new(:end, :call) do |tp|
next unless TracePointSpec.target_thread?
event_name = tp.event event_name = tp.event
end.enable do end.enable do
TracePointSpec.test TracePointSpec.test

View file

@ -1,4 +1,10 @@
module TracePointSpec module TracePointSpec
@thread = Thread.current
def self.target_thread?
Thread.current == @thread
end
class ClassWithMethodAlias class ClassWithMethodAlias
def m def m
end end

View file

@ -10,6 +10,7 @@ describe 'TracePoint#inspect' do
inspect = nil inspect = nil
line = nil line = nil
TracePoint.new(:line) { |tp| TracePoint.new(:line) { |tp|
next unless TracePointSpec.target_thread?
inspect ||= tp.inspect inspect ||= tp.inspect
}.enable do }.enable do
line = __LINE__ line = __LINE__
@ -22,6 +23,7 @@ describe 'TracePoint#inspect' do
inspect = nil inspect = nil
line = nil line = nil
TracePoint.new(:class) { |tp| TracePoint.new(:class) { |tp|
next unless TracePointSpec.target_thread?
inspect ||= tp.inspect inspect ||= tp.inspect
}.enable do }.enable do
line = __LINE__ + 1 line = __LINE__ + 1

View file

@ -1,10 +1,20 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe 'TracePoint#lineno' do describe 'TracePoint#lineno' do
it 'returns the line number of the event' do it 'returns the line number of the event' do
lineno = nil lineno = nil
TracePoint.new(:line) { |tp| lineno = tp.lineno }.enable do TracePoint.new(:line) { |tp|
lineno.should == 7 next unless TracePointSpec.target_thread?
lineno = tp.lineno
}.enable do
line_event = true
end end
lineno.should == __LINE__ - 2
end
it 'raises RuntimeError if accessed from outside' do
tp = TracePoint.new(:line) {}
-> { tp.lineno }.should raise_error(RuntimeError, 'access from outside')
end end
end end

View file

@ -1,12 +1,14 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe 'TracePoint#method_id' do describe 'TracePoint#method_id' do
def test; end
it 'returns the name at the definition of the method being called' do it 'returns the name at the definition of the method being called' do
method_name = nil method_name = nil
TracePoint.new(:call) { |tp| method_name = tp.method_id}.enable do TracePoint.new(:call) { |tp|
test next unless TracePointSpec.target_thread?
method_name = tp.method_id
}.enable do
TracePointSpec.test
method_name.should equal(:test) method_name.should equal(:test)
end end
end end

View file

@ -8,7 +8,10 @@ describe 'TracePoint.new' do
it 'includes :line event when event is not specified' do it 'includes :line event when event is not specified' do
event_name = nil event_name = nil
TracePoint.new() { |tp| event_name = tp.event }.enable do TracePoint.new { |tp|
next unless TracePointSpec.target_thread?
event_name = tp.event
}.enable do
event_name.should equal(:line) event_name.should equal(:line)
event_name = nil event_name = nil
@ -25,7 +28,10 @@ describe 'TracePoint.new' do
event_name = nil event_name = nil
(o = mock('line')).should_receive(:to_sym).and_return(:line) (o = mock('line')).should_receive(:to_sym).and_return(:line)
TracePoint.new(o) { |tp| event_name = tp.event }.enable do TracePoint.new(o) { |tp|
next unless TracePointSpec.target_thread?
event_name = tp.event
}.enable do
line_event = true line_event = true
event_name.should == :line event_name.should == :line
end end
@ -34,6 +40,7 @@ describe 'TracePoint.new' do
it 'includes multiple events when multiple event names are passed as params' do it 'includes multiple events when multiple event names are passed as params' do
event_name = nil event_name = nil
TracePoint.new(:end, :call) do |tp| TracePoint.new(:end, :call) do |tp|
next unless TracePointSpec.target_thread?
event_name = tp.event event_name = tp.event
end.enable do end.enable do
TracePointSpec.test TracePointSpec.test
@ -49,10 +56,10 @@ describe 'TracePoint.new' do
it 'raises a TypeError when the given object is not a string/symbol' do it 'raises a TypeError when the given object is not a string/symbol' do
o = mock('123') o = mock('123')
-> { TracePoint.new(o) {}}.should raise_error(TypeError) -> { TracePoint.new(o) {} }.should raise_error(TypeError)
o.should_receive(:to_sym).and_return(123) o.should_receive(:to_sym).and_return(123)
-> { TracePoint.new(o) {}}.should raise_error(TypeError) -> { TracePoint.new(o) {} }.should raise_error(TypeError)
end end
it 'expects to be called with a block' do it 'expects to be called with a block' do

View file

@ -1,11 +1,15 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
ruby_version_is "2.6" do ruby_version_is "2.6" do
describe 'TracePoint#parameters' do describe 'TracePoint#parameters' do
it 'returns the parameters of block' do it 'returns the parameters of block' do
f = proc {|x, y, z| } f = proc {|x, y, z| }
parameters = nil parameters = nil
TracePoint.new(:b_call) {|tp| parameters = tp.parameters }.enable do TracePoint.new(:b_call) { |tp|
next unless TracePointSpec.target_thread?
parameters = tp.parameters
}.enable do
f.call f.call
parameters.should == [[:opt, :x], [:opt, :y], [:opt, :z]] parameters.should == [[:opt, :x], [:opt, :y], [:opt, :z]]
end end
@ -14,7 +18,10 @@ ruby_version_is "2.6" do
it 'returns the parameters of lambda block' do it 'returns the parameters of lambda block' do
f = -> x, y, z { } f = -> x, y, z { }
parameters = nil parameters = nil
TracePoint.new(:b_call) {|tp| parameters = tp.parameters }.enable do TracePoint.new(:b_call) { |tp|
next unless TracePointSpec.target_thread?
parameters = tp.parameters
}.enable do
f.call(1, 2, 3) f.call(1, 2, 3)
parameters.should == [[:req, :x], [:req, :y], [:req, :z]] parameters.should == [[:req, :x], [:req, :y], [:req, :z]]
end end

View file

@ -1,18 +1,26 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe 'TracePoint#path' do describe 'TracePoint#path' do
it 'returns the path of the file being run' do it 'returns the path of the file being run' do
path = nil path = nil
TracePoint.new(:line) { |tp| path = tp.path }.enable do TracePoint.new(:line) { |tp|
path.should == "#{__FILE__}" next unless TracePointSpec.target_thread?
path = tp.path
}.enable do
line_event = true
end end
path.should == "#{__FILE__}"
end end
it 'equals (eval) inside an eval for :end event' do it 'equals (eval) inside an eval for :end event' do
path = nil path = nil
TracePoint.new(:end) { |tp| path = tp.path }.enable do TracePoint.new(:end) { |tp|
next unless TracePointSpec.target_thread?
path = tp.path
}.enable do
eval("module TracePointSpec; end") eval("module TracePointSpec; end")
path.should == '(eval)'
end end
path.should == '(eval)'
end end
end end

View file

@ -1,9 +1,13 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe 'TracePoint#raised_exception' do describe 'TracePoint#raised_exception' do
it 'returns value from exception raised on the :raise event' do it 'returns value from exception raised on the :raise event' do
raised_exception, error_result = nil raised_exception, error_result = nil
trace = TracePoint.new(:raise) { |tp| raised_exception = tp.raised_exception } trace = TracePoint.new(:raise) { |tp|
next unless TracePointSpec.target_thread?
raised_exception = tp.raised_exception
}
trace.enable do trace.enable do
begin begin
raise StandardError raise StandardError

View file

@ -1,11 +1,15 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe 'TracePoint#return_value' do describe 'TracePoint#return_value' do
def test; 'test' end def test; 'test' end
it 'returns value from :return event' do it 'returns value from :return event' do
trace_value = nil trace_value = nil
TracePoint.new(:return) { |tp| trace_value = tp.return_value}.enable do TracePoint.new(:return) { |tp|
next unless TracePointSpec.target_thread?
trace_value = tp.return_value
}.enable do
test test
trace_value.should == 'test' trace_value.should == 'test'
end end

View file

@ -4,14 +4,20 @@ require_relative 'fixtures/classes'
describe 'TracePoint#self' do describe 'TracePoint#self' do
it 'return the trace object from event' do it 'return the trace object from event' do
trace = nil trace = nil
TracePoint.new(:line) { |tp| trace = tp.self }.enable do TracePoint.new(:line) { |tp|
next unless TracePointSpec.target_thread?
trace = tp.self
}.enable do
trace.equal?(self).should be_true trace.equal?(self).should be_true
end end
end end
it 'return the class object from a class event' do it 'return the class object from a class event' do
trace = nil trace = nil
TracePoint.new(:class) { |tp| trace = tp.self }.enable do TracePoint.new(:class) { |tp|
next unless TracePointSpec.target_thread?
trace = tp.self
}.enable do
class TracePointSpec::C class TracePointSpec::C
end end
end end

View file

@ -1,4 +1,5 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe 'TracePoint.trace' do describe 'TracePoint.trace' do
it 'activates the trace automatically' do it 'activates the trace automatically' do

View file

@ -29,6 +29,14 @@ module SquigglyHeredocSpecs
HERE HERE
end end
def self.least_indented_on_the_first_line
<<~HERE
a
b
c
HERE
end
def self.least_indented_on_the_last_line def self.least_indented_on_the_last_line
<<~HERE <<~HERE
a a
@ -36,4 +44,20 @@ module SquigglyHeredocSpecs
c c
HERE HERE
end end
def self.least_indented_on_the_first_line_single
<<~'HERE'
a
b
c
HERE
end
def self.least_indented_on_the_last_line_single
<<~'HERE'
a
b
c
HERE
end
end end

View file

@ -86,6 +86,13 @@ HERE
it "selects the least-indented line and removes its indentation from all the lines" do it "selects the least-indented line and removes its indentation from all the lines" do
require_relative 'fixtures/squiggly_heredoc' require_relative 'fixtures/squiggly_heredoc'
SquigglyHeredocSpecs.least_indented_on_the_first_line.should == "a\n b\n c\n"
SquigglyHeredocSpecs.least_indented_on_the_last_line.should == " a\n b\nc\n" SquigglyHeredocSpecs.least_indented_on_the_last_line.should == " a\n b\nc\n"
end end
it "selects the least-indented line and removes its indentation from all the lines for <<~'identifier'" do
require_relative 'fixtures/squiggly_heredoc'
SquigglyHeredocSpecs.least_indented_on_the_first_line_single.should == "a\n b\n c\n"
SquigglyHeredocSpecs.least_indented_on_the_last_line_single.should == " a\n b\nc\n"
end
end end

View file

@ -112,6 +112,17 @@ describe "Kernel#BigDecimal" do
neg_inf.should < 0 neg_inf.should < 0
end end
ruby_version_is "2.6" do
describe "with exception: false" do
it "returns nil for invalid strings" do
BigDecimal("invalid", exception: false).should be_nil
BigDecimal("0invalid", exception: false).should be_nil
BigDecimal("invalid0", exception: false).should be_nil
BigDecimal("0.", exception: false).should be_nil
end
end
end
describe "accepts NaN and [+-]Infinity as Float values" do describe "accepts NaN and [+-]Infinity as Float values" do
it "works without an explicit precision" do it "works without an explicit precision" do
BigDecimal(Float::NAN).should.nan? BigDecimal(Float::NAN).should.nan?

View file

@ -60,15 +60,16 @@ describe "Net::HTTP.get" do
Thread.current.report_on_exception = false Thread.current.report_on_exception = false
Net::HTTP.get("127.0.0.1", '/', server.connect_address.ip_port) Net::HTTP.get("127.0.0.1", '/', server.connect_address.ip_port)
end end
socket = server_thread.value
Thread.pass until client_thread.stop? Thread.pass until client_thread.stop?
[server_thread, client_thread] [socket, client_thread]
end end
it "propagates exceptions interrupting the thread and does not replace it with Zlib::BufError" do it "propagates exceptions interrupting the thread and does not replace it with Zlib::BufError" do
my_exception = Class.new(RuntimeError) my_exception = Class.new(RuntimeError)
server_thread, client_thread = start_threads socket, client_thread = start_threads
socket = server_thread.value
begin begin
client_thread.raise my_exception, "my exception" client_thread.raise my_exception, "my exception"
-> { client_thread.value }.should raise_error(my_exception) -> { client_thread.value }.should raise_error(my_exception)
@ -79,8 +80,7 @@ describe "Net::HTTP.get" do
ruby_version_is "2.8" do # https://bugs.ruby-lang.org/issues/13882#note-6 ruby_version_is "2.8" do # https://bugs.ruby-lang.org/issues/13882#note-6
it "lets the kill Thread exception goes through and does not replace it with Zlib::BufError" do it "lets the kill Thread exception goes through and does not replace it with Zlib::BufError" do
server_thread, client_thread = start_threads socket, client_thread = start_threads
socket = server_thread.value
begin begin
client_thread.kill client_thread.kill
client_thread.value.should == nil client_thread.value.should == nil

View file

@ -12,6 +12,10 @@ describe "ObjectSpace.memsize_of" do
ObjectSpace.memsize_of(42).should == 0 ObjectSpace.memsize_of(42).should == 0
end end
it "returns 0 for literal Symbols" do
ObjectSpace.memsize_of(:abc).should == 0
end
it "returns an Integer for an Object" do it "returns an Integer for an Object" do
obj = Object.new obj = Object.new
ObjectSpace.memsize_of(obj).should be_kind_of(Integer) ObjectSpace.memsize_of(obj).should be_kind_of(Integer)

View file

@ -58,6 +58,26 @@ describe "TCPServer#accept" do
t.join t.join
end end
it "is automatically retried when interrupted by SIGVTALRM" do
t = Thread.new do
client = @server.accept
value = client.read(2)
client.close
value
end
Thread.pass while t.status and t.status != "sleep"
# Thread#backtrace uses SIGVTALRM on TruffleRuby and potentially other implementations.
# Sending a signal to a thread is not possible with Ruby APIs.
t.backtrace.join("\n").should.include?("in `accept'")
socket = TCPSocket.new('127.0.0.1', @port)
socket.write("OK")
socket.close
t.value.should == "OK"
end
it "raises an IOError if the socket is closed" do it "raises an IOError if the socket is closed" do
@server.close @server.close
-> { @server.accept }.should raise_error(IOError) -> { @server.accept }.should raise_error(IOError)

View file

@ -131,6 +131,29 @@ describe "C-API Encoding function" do
end end
end end
describe "rb_enc_precise_mbclen" do
it "returns the correct length for single byte characters" do
@s.rb_enc_precise_mbclen("hello", 7).should == 1
@s.rb_enc_precise_mbclen("hello", 5).should == 1
@s.rb_enc_precise_mbclen("hello", 1).should == 1
@s.rb_enc_precise_mbclen("hello", 0).should == -2
@s.rb_enc_precise_mbclen("hello", -1).should == -2
@s.rb_enc_precise_mbclen("hello", -5).should == -2
end
it "returns the correct length for multi-byte characters" do
@s.rb_enc_precise_mbclen("ésumé", 2).should == 2
@s.rb_enc_precise_mbclen("ésumé", 3).should == 2
@s.rb_enc_precise_mbclen("ésumé", 0).should == -2
@s.rb_enc_precise_mbclen("ésumé", 1).should == -2
@s.rb_enc_precise_mbclen("", 20).should == 3
@s.rb_enc_precise_mbclen("", 3).should == 3
@s.rb_enc_precise_mbclen("", 2).should == -2
@s.rb_enc_precise_mbclen("", 0).should == -2
@s.rb_enc_precise_mbclen("", -2).should == -2
end
end
describe "rb_obj_encoding" do describe "rb_obj_encoding" do
it "returns the encoding associated with an object" do it "returns the encoding associated with an object" do
str = "abc".encode Encoding::BINARY str = "abc".encode Encoding::BINARY
@ -173,6 +196,26 @@ describe "C-API Encoding function" do
end end
end end
describe "rb_enc_str_new_cstr" do
it "creates a new ruby string from a c string literal" do
result = @s.rb_enc_str_new_cstr_constant(Encoding::US_ASCII)
result.should == "test string literal"
result.encoding.should == Encoding::US_ASCII
end
it "creates a new ruby string from a c string variable" do
result = @s.rb_enc_str_new_cstr("test string", Encoding::US_ASCII)
result.should == "test string"
result.encoding.should == Encoding::US_ASCII
end
it "when null encoding is given with a c string literal, it creates a new ruby string with ASCII_8BIT encoding" do
result = @s.rb_enc_str_new_cstr_constant(nil)
result.should == "test string literal"
result.encoding.should == Encoding::ASCII_8BIT
end
end
describe "rb_enc_str_coderange" do describe "rb_enc_str_coderange" do
describe "when the encoding is BINARY" do describe "when the encoding is BINARY" do
it "returns ENC_CODERANGE_7BIT if there are no high bits set" do it "returns ENC_CODERANGE_7BIT if there are no high bits set" do
@ -217,6 +260,17 @@ describe "C-API Encoding function" do
end end
end end
describe "MBCLEN_CHARFOUND_P" do
it "returns non-zero for valid character" do
@s.MBCLEN_CHARFOUND_P("a".ord).should == 1
end
it "returns zero for invalid characters" do
@s.MBCLEN_CHARFOUND_P(0).should == 0
@s.MBCLEN_CHARFOUND_P(-1).should == 0
end
end
describe "ENCODING_GET" do describe "ENCODING_GET" do
it_behaves_like :rb_enc_get_index, :ENCODING_GET it_behaves_like :rb_enc_get_index, :ENCODING_GET
end end
@ -497,4 +551,21 @@ describe "C-API Encoding function" do
@s.rb_enc_str_asciionly_p("hüllo").should be_false @s.rb_enc_str_asciionly_p("hüllo").should be_false
end end
end end
describe "rb_uv_to_utf8" do
it 'converts a Unicode codepoint to a UTF-8 C string' do
str = ' ' * 6
{
0 => "\x01",
0x7f => "\xC2\x80",
0x7ff => "\xE0\xA0\x80",
0xffff => "\xF0\x90\x80\x80",
0x1fffff => "\xF8\x88\x80\x80\x80",
0x3ffffff => "\xFC\x84\x80\x80\x80\x80",
}.each do |num, result|
len = @s.rb_uv_to_utf8(str, num + 1)
str[0..len-1].should == result
end
end
end
end end

View file

@ -7,6 +7,10 @@
extern "C" { extern "C" {
#endif #endif
static VALUE encoding_spec_MBCLEN_CHARFOUND_P(VALUE self, VALUE obj) {
return INT2FIX(MBCLEN_CHARFOUND_P(FIX2INT(obj)));
}
static VALUE encoding_spec_ENC_CODERANGE_ASCIIONLY(VALUE self, VALUE obj) { static VALUE encoding_spec_ENC_CODERANGE_ASCIIONLY(VALUE self, VALUE obj) {
if(ENC_CODERANGE_ASCIIONLY(obj)) { if(ENC_CODERANGE_ASCIIONLY(obj)) {
return Qtrue; return Qtrue;
@ -114,6 +118,13 @@ static VALUE encoding_spec_rb_enc_get(VALUE self, VALUE obj) {
return rb_str_new2(rb_enc_get(obj)->name); return rb_str_new2(rb_enc_get(obj)->name);
} }
static VALUE encoding_spec_rb_enc_precise_mbclen(VALUE self, VALUE str, VALUE offset) {
int o = FIX2INT(offset);
char *p = RSTRING_PTR(str);
char *e = p + o;
return INT2FIX(rb_enc_precise_mbclen(p, e, rb_enc_get(str)));
}
static VALUE encoding_spec_rb_obj_encoding(VALUE self, VALUE obj) { static VALUE encoding_spec_rb_obj_encoding(VALUE self, VALUE obj) {
return rb_obj_encoding(obj); return rb_obj_encoding(obj);
} }
@ -149,6 +160,16 @@ static VALUE encoding_spec_rb_enc_str_coderange(VALUE self, VALUE str) {
} }
} }
static VALUE encoding_spec_rb_enc_str_new_cstr(VALUE self, VALUE str, VALUE enc) {
rb_encoding *e = rb_to_encoding(enc);
return rb_enc_str_new_cstr(StringValueCStr(str), e);
}
static VALUE encoding_spec_rb_enc_str_new_cstr_constant(VALUE self, VALUE enc) {
rb_encoding *e = NIL_P(enc) ? NULL : rb_to_encoding(enc);
return rb_enc_str_new_cstr("test string literal", e);
}
static VALUE encoding_spec_rb_enc_str_new(VALUE self, VALUE str, VALUE len, VALUE enc) { static VALUE encoding_spec_rb_enc_str_new(VALUE self, VALUE str, VALUE len, VALUE enc) {
return rb_enc_str_new(RSTRING_PTR(str), FIX2INT(len), rb_to_encoding(enc)); return rb_enc_str_new(RSTRING_PTR(str), FIX2INT(len), rb_to_encoding(enc));
} }
@ -219,6 +240,10 @@ 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) {
return INT2NUM(rb_uv_to_utf8(RSTRING_PTR(buf), NUM2INT(num)));
}
void Init_encoding_spec(void) { void Init_encoding_spec(void) {
VALUE cls; VALUE cls;
native_rb_encoding_pointer = (rb_encoding**) malloc(sizeof(rb_encoding*)); native_rb_encoding_pointer = (rb_encoding**) malloc(sizeof(rb_encoding*));
@ -247,6 +272,7 @@ void Init_encoding_spec(void) {
rb_define_method(cls, "rb_enc_alias", encoding_spec_rb_enc_alias, 2); rb_define_method(cls, "rb_enc_alias", encoding_spec_rb_enc_alias, 2);
#endif #endif
rb_define_method(cls, "MBCLEN_CHARFOUND_P", encoding_spec_MBCLEN_CHARFOUND_P, 1);
rb_define_method(cls, "rb_enc_associate", encoding_spec_rb_enc_associate, 2); rb_define_method(cls, "rb_enc_associate", encoding_spec_rb_enc_associate, 2);
rb_define_method(cls, "rb_enc_associate_index", encoding_spec_rb_enc_associate_index, 2); 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_compatible", encoding_spec_rb_enc_compatible, 2);
@ -256,10 +282,13 @@ void Init_encoding_spec(void) {
rb_define_method(cls, "rb_enc_from_index", encoding_spec_rb_enc_from_index, 1); rb_define_method(cls, "rb_enc_from_index", encoding_spec_rb_enc_from_index, 1);
rb_define_method(cls, "rb_enc_from_encoding", encoding_spec_rb_enc_from_encoding, 1); 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_get", encoding_spec_rb_enc_get, 1);
rb_define_method(cls, "rb_enc_precise_mbclen", encoding_spec_rb_enc_precise_mbclen, 2);
rb_define_method(cls, "rb_obj_encoding", encoding_spec_rb_obj_encoding, 1); rb_define_method(cls, "rb_obj_encoding", encoding_spec_rb_obj_encoding, 1);
rb_define_method(cls, "rb_enc_get_index", encoding_spec_rb_enc_get_index, 1); rb_define_method(cls, "rb_enc_get_index", encoding_spec_rb_enc_get_index, 1);
rb_define_method(cls, "rb_enc_set_index", encoding_spec_rb_enc_set_index, 2); rb_define_method(cls, "rb_enc_set_index", encoding_spec_rb_enc_set_index, 2);
rb_define_method(cls, "rb_enc_str_coderange", encoding_spec_rb_enc_str_coderange, 1); rb_define_method(cls, "rb_enc_str_coderange", encoding_spec_rb_enc_str_coderange, 1);
rb_define_method(cls, "rb_enc_str_new_cstr", encoding_spec_rb_enc_str_new_cstr, 2);
rb_define_method(cls, "rb_enc_str_new_cstr_constant", encoding_spec_rb_enc_str_new_cstr_constant, 1);
rb_define_method(cls, "rb_enc_str_new", encoding_spec_rb_enc_str_new, 3); rb_define_method(cls, "rb_enc_str_new", encoding_spec_rb_enc_str_new, 3);
rb_define_method(cls, "ENCODING_GET", encoding_spec_ENCODING_GET, 1); rb_define_method(cls, "ENCODING_GET", encoding_spec_ENCODING_GET, 1);
rb_define_method(cls, "ENCODING_SET", encoding_spec_ENCODING_SET, 2); rb_define_method(cls, "ENCODING_SET", encoding_spec_ENCODING_SET, 2);
@ -271,6 +300,7 @@ void Init_encoding_spec(void) {
rb_define_method(cls, "rb_enc_nth", encoding_spec_rb_enc_nth, 2); rb_define_method(cls, "rb_enc_nth", encoding_spec_rb_enc_nth, 2);
rb_define_method(cls, "rb_enc_codepoint_len", encoding_spec_rb_enc_codepoint_len, 1); 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_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);
} }
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -49,6 +49,12 @@ VALUE string_spec_rb_str_set_len_RSTRING_LEN(VALUE self, VALUE str, VALUE len) {
return INT2FIX(RSTRING_LEN(str)); return INT2FIX(RSTRING_LEN(str));
} }
VALUE rb_fstring(VALUE str); /* internal.h, used in ripper */
VALUE string_spec_rb_str_fstring(VALUE self, VALUE str) {
return rb_fstring(str);
}
VALUE string_spec_rb_str_buf_new(VALUE self, VALUE len, VALUE str) { VALUE string_spec_rb_str_buf_new(VALUE self, VALUE len, VALUE str) {
VALUE buf; VALUE buf;
@ -93,6 +99,14 @@ VALUE string_spec_rb_str_cat2(VALUE self, VALUE str) {
return rb_str_cat2(str, "?"); return rb_str_cat2(str, "?");
} }
VALUE string_spec_rb_str_cat_cstr(VALUE self, VALUE str, VALUE other) {
return rb_str_cat_cstr(str, StringValueCStr(other));
}
VALUE string_spec_rb_str_cat_cstr_constant(VALUE self, VALUE str) {
return rb_str_cat_cstr(str, "?");
}
VALUE string_spec_rb_str_cmp(VALUE self, VALUE str1, VALUE str2) { VALUE string_spec_rb_str_cmp(VALUE self, VALUE str1, VALUE str2) {
return INT2NUM(rb_str_cmp(str1, str2)); return INT2NUM(rb_str_cmp(str1, str2));
} }
@ -452,6 +466,7 @@ void Init_string_spec(void) {
VALUE cls = rb_define_class("CApiStringSpecs", rb_cObject); VALUE cls = rb_define_class("CApiStringSpecs", rb_cObject);
rb_define_method(cls, "rb_cstr2inum", string_spec_rb_cstr2inum, 2); rb_define_method(cls, "rb_cstr2inum", string_spec_rb_cstr2inum, 2);
rb_define_method(cls, "rb_cstr_to_inum", string_spec_rb_cstr_to_inum, 3); rb_define_method(cls, "rb_cstr_to_inum", string_spec_rb_cstr_to_inum, 3);
rb_define_method(cls, "rb_fstring", string_spec_rb_str_fstring, 1);
rb_define_method(cls, "rb_str2inum", string_spec_rb_str2inum, 2); rb_define_method(cls, "rb_str2inum", string_spec_rb_str2inum, 2);
rb_define_method(cls, "rb_str_append", string_spec_rb_str_append, 2); rb_define_method(cls, "rb_str_append", string_spec_rb_str_append, 2);
rb_define_method(cls, "rb_str_buf_new", string_spec_rb_str_buf_new, 2); rb_define_method(cls, "rb_str_buf_new", string_spec_rb_str_buf_new, 2);
@ -462,6 +477,8 @@ void Init_string_spec(void) {
rb_define_method(cls, "rb_str_buf_cat", string_spec_rb_str_buf_cat, 1); rb_define_method(cls, "rb_str_buf_cat", string_spec_rb_str_buf_cat, 1);
rb_define_method(cls, "rb_str_cat", string_spec_rb_str_cat, 1); rb_define_method(cls, "rb_str_cat", string_spec_rb_str_cat, 1);
rb_define_method(cls, "rb_str_cat2", string_spec_rb_str_cat2, 1); rb_define_method(cls, "rb_str_cat2", string_spec_rb_str_cat2, 1);
rb_define_method(cls, "rb_str_cat_cstr", string_spec_rb_str_cat_cstr, 2);
rb_define_method(cls, "rb_str_cat_cstr_constant", string_spec_rb_str_cat_cstr_constant, 1);
rb_define_method(cls, "rb_str_cmp", string_spec_rb_str_cmp, 2); rb_define_method(cls, "rb_str_cmp", string_spec_rb_str_cmp, 2);
rb_define_method(cls, "rb_str_conv_enc", string_spec_rb_str_conv_enc, 3); rb_define_method(cls, "rb_str_conv_enc", string_spec_rb_str_conv_enc, 3);
rb_define_method(cls, "rb_str_conv_enc_opts", string_spec_rb_str_conv_enc_opts, 5); rb_define_method(cls, "rb_str_conv_enc_opts", string_spec_rb_str_conv_enc_opts, 5);

View file

@ -1,4 +1,5 @@
#include "ruby.h" #include "ruby.h"
#include "ruby/util.h"
#include "rubyspec.h" #include "rubyspec.h"
#ifdef __cplusplus #ifdef __cplusplus
@ -93,6 +94,18 @@ static VALUE util_spec_rb_sourceline(VALUE self) {
return INT2NUM(rb_sourceline()); return INT2NUM(rb_sourceline());
} }
static VALUE util_spec_strtod(VALUE self, VALUE string) {
char *endptr = NULL;
double value = strtod(RSTRING_PTR(string), &endptr);
return rb_ary_new_from_args(2, rb_float_new(value), endptr ? rb_str_new2(endptr) : Qnil);
}
static VALUE util_spec_ruby_strtod(VALUE self, VALUE string) {
char *endptr = NULL;
double value = ruby_strtod(RSTRING_PTR(string), &endptr);
return rb_ary_new_from_args(2, rb_float_new(value), endptr ? rb_str_new2(endptr) : Qnil);
}
void Init_util_spec(void) { void Init_util_spec(void) {
VALUE cls = rb_define_class("CApiUtilSpecs", rb_cObject); VALUE cls = rb_define_class("CApiUtilSpecs", rb_cObject);
rb_define_method(cls, "rb_scan_args", util_spec_rb_scan_args, 4); rb_define_method(cls, "rb_scan_args", util_spec_rb_scan_args, 4);
@ -101,6 +114,8 @@ void Init_util_spec(void) {
rb_define_method(cls, "rb_iter_break", util_spec_rb_iter_break, 0); rb_define_method(cls, "rb_iter_break", util_spec_rb_iter_break, 0);
rb_define_method(cls, "rb_sourcefile", util_spec_rb_sourcefile, 0); rb_define_method(cls, "rb_sourcefile", util_spec_rb_sourcefile, 0);
rb_define_method(cls, "rb_sourceline", util_spec_rb_sourceline, 0); rb_define_method(cls, "rb_sourceline", util_spec_rb_sourceline, 0);
rb_define_method(cls, "strtod", util_spec_strtod, 1);
rb_define_method(cls, "ruby_strtod", util_spec_ruby_strtod, 1);
} }
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -382,6 +382,16 @@ describe "C-API String function" do
end end
end end
describe "rb_str_cat_cstr" do
it "concatenates a C string literal to a ruby string" do
@s.rb_str_cat_cstr_constant("Your house is on fire").should == "Your house is on fire?"
end
it "concatenates a variable C string to a ruby string" do
@s.rb_str_cat_cstr("Your house is on fire", "?").should == "Your house is on fire?"
end
end
describe "rb_str_cmp" do describe "rb_str_cmp" do
it "returns 0 if two strings are identical" do it "returns 0 if two strings are identical" do
@s.rb_str_cmp("ppp", "ppp").should == 0 @s.rb_str_cmp("ppp", "ppp").should == 0
@ -454,6 +464,25 @@ describe "C-API String function" do
end end
end end
describe "rb_fstring" do
it 'returns self if the String is frozen' do
input = 'foo'.freeze
output = @s.rb_fstring(input)
output.should equal(input)
output.should.frozen?
end
it 'returns a frozen copy if the String is not frozen' do
input = 'foo'
output = @s.rb_fstring(input)
output.should.frozen?
output.should_not equal(input)
output.should == 'foo'
end
end
describe "rb_str_subseq" do describe "rb_str_subseq" do
it "returns a byte-indexed substring" do it "returns a byte-indexed substring" do
str = "\x00\x01\x02\x03\x04".force_encoding("binary") str = "\x00\x01\x02\x03\x04".force_encoding("binary")

View file

@ -293,4 +293,34 @@ describe "C-API Util function" do
end end
end end
# ruby/util.h redefines strtod as a macro calling ruby_strtod
describe "strtod" do
it "converts a string to a double and returns the remaining string" do
d, s = @o.strtod("14.25test")
d.should == 14.25
s.should == "test"
end
it "returns 0 and the full string if there's no numerical value" do
d, s = @o.strtod("test")
d.should == 0
s.should == "test"
end
end
describe "ruby_strtod" do
it "converts a string to a double and returns the remaining string" do
d, s = @o.ruby_strtod("14.25test")
d.should == 14.25
s.should == "test"
end
it "returns 0 and the full string if there's no numerical value" do
d, s = @o.ruby_strtod("test")
d.should == 0
s.should == "test"
end
end
end end

View file

@ -20,10 +20,16 @@ describe :string_times, shared: true do
it "raises an ArgumentError when given integer is negative" do it "raises an ArgumentError when given integer is negative" do
-> { @object.call("cool", -3) }.should raise_error(ArgumentError) -> { @object.call("cool", -3) }.should raise_error(ArgumentError)
-> { @object.call("cool", -3.14) }.should raise_error(ArgumentError) -> { @object.call("cool", -3.14) }.should raise_error(ArgumentError)
-> { @object.call("cool", min_long) }.should raise_error(ArgumentError)
end end
it "raises a RangeError when given integer is a Bignum" do it "raises a RangeError when given integer is a Bignum" do
-> { @object.call("cool", 999999999999999999999) }.should raise_error(RangeError) -> { @object.call("cool", 999999999999999999999) }.should raise_error(RangeError)
-> { @object.call("", 999999999999999999999) }.should raise_error(RangeError)
end
it "works with huge long values when string is empty" do
@object.call("", max_long).should == ""
end end
it "returns subclass instances" do it "returns subclass instances" do