summaryrefslogtreecommitdiff
path: root/spec/ruby/core
diff options
context:
space:
mode:
authorBenoit Daloze <[email protected]>2021-01-28 17:08:57 +0100
committerBenoit Daloze <[email protected]>2021-01-28 17:08:57 +0100
commit2e32b919b4f2f5b7f2e1509d6fa985526ef1f61c (patch)
treeaedadac3c99ca0097c2bbeaa95830332d6fb9971 /spec/ruby/core
parent1b377b32c8616f85c0a97e68758c5c2db83f2169 (diff)
Update to ruby/spec@8cafaa5
Diffstat (limited to 'spec/ruby/core')
-rw-r--r--spec/ruby/core/integer/digits_spec.rb9
-rw-r--r--spec/ruby/core/kernel/caller_locations_spec.rb8
-rw-r--r--spec/ruby/core/kernel/caller_spec.rb8
-rw-r--r--spec/ruby/core/kernel/lambda_spec.rb12
-rw-r--r--spec/ruby/core/kernel/rand_spec.rb42
-rw-r--r--spec/ruby/core/module/ruby2_keywords_spec.rb112
-rw-r--r--spec/ruby/core/module/to_s_spec.rb23
-rw-r--r--spec/ruby/core/proc/ruby2_keywords_spec.rb64
-rw-r--r--spec/ruby/core/range/shared/cover.rb42
-rw-r--r--spec/ruby/core/range/shared/cover_and_include.rb7
-rw-r--r--spec/ruby/core/range/to_a_spec.rb5
-rw-r--r--spec/ruby/core/range/to_s_spec.rb7
-rw-r--r--spec/ruby/core/string/scrub_spec.rb7
-rw-r--r--spec/ruby/core/string/shared/slice.rb10
-rw-r--r--spec/ruby/core/string/split_spec.rb8
-rw-r--r--spec/ruby/core/struct/hash_spec.rb4
-rw-r--r--spec/ruby/core/symbol/name_spec.rb19
-rw-r--r--spec/ruby/core/thread/backtrace_locations_spec.rb8
-rw-r--r--spec/ruby/core/thread/handle_interrupt_spec.rb125
-rw-r--r--spec/ruby/core/thread/pending_interrupt_spec.rb32
-rw-r--r--spec/ruby/core/time/inspect_spec.rb26
21 files changed, 572 insertions, 6 deletions
diff --git a/spec/ruby/core/integer/digits_spec.rb b/spec/ruby/core/integer/digits_spec.rb
index 4a8e33980c..3d0a64c25f 100644
--- a/spec/ruby/core/integer/digits_spec.rb
+++ b/spec/ruby/core/integer/digits_spec.rb
@@ -29,4 +29,13 @@ describe "Integer#digits" do
it "raises Math::DomainError when calling digits on a negative number" do
-> { -12345.digits(7) }.should raise_error(Math::DomainError)
end
+
+ it "returns integer values > 9 when base is above 10" do
+ 1234.digits(16).should == [2, 13, 4]
+ end
+
+ it "can be used with base > 37" do
+ 1234.digits(100).should == [34, 12]
+ 980099.digits(100).should == [99, 0, 98]
+ end
end
diff --git a/spec/ruby/core/kernel/caller_locations_spec.rb b/spec/ruby/core/kernel/caller_locations_spec.rb
index 8050b5b3ab..bbb7e7b737 100644
--- a/spec/ruby/core/kernel/caller_locations_spec.rb
+++ b/spec/ruby/core/kernel/caller_locations_spec.rb
@@ -36,6 +36,14 @@ describe 'Kernel#caller_locations' do
end
end
+ ruby_version_is "2.7" do
+ it "works with beginless ranges" do
+ locations1 = caller_locations(0)
+ locations2 = caller_locations(eval("(...5)"))
+ locations2.map(&:to_s)[eval("(2..)")].should == locations1[eval("(...5)")].map(&:to_s)[eval("(2..)")]
+ end
+ end
+
it "can be called with a range whose end is negative" do
locations1 = caller_locations(0)
locations2 = caller_locations(2..-1)
diff --git a/spec/ruby/core/kernel/caller_spec.rb b/spec/ruby/core/kernel/caller_spec.rb
index 06e9ea6fc8..6c175868cb 100644
--- a/spec/ruby/core/kernel/caller_spec.rb
+++ b/spec/ruby/core/kernel/caller_spec.rb
@@ -52,6 +52,14 @@ describe 'Kernel#caller' do
end
end
+ ruby_version_is "2.7" do
+ it "works with beginless ranges" do
+ locations1 = KernelSpecs::CallerTest.locations(0)
+ locations2 = KernelSpecs::CallerTest.locations(eval("(..5)"))
+ locations2.map(&:to_s)[eval("(2..)")].should == locations1[eval("(..5)")].map(&:to_s)[eval("(2..)")]
+ end
+ end
+
guard -> { Kernel.instance_method(:tap).source_location } do
it "includes core library methods defined in Ruby" do
file, line = Kernel.instance_method(:tap).source_location
diff --git a/spec/ruby/core/kernel/lambda_spec.rb b/spec/ruby/core/kernel/lambda_spec.rb
index 4dd34c6ca9..9a960f3589 100644
--- a/spec/ruby/core/kernel/lambda_spec.rb
+++ b/spec/ruby/core/kernel/lambda_spec.rb
@@ -123,4 +123,16 @@ describe "Kernel.lambda" do
it "allows long returns to flow through it" do
KernelSpecs::Lambda.new.outer.should == :good
end
+
+ it "treats the block as a Proc when lambda is re-defined" do
+ klass = Class.new do
+ def lambda (&block); block; end
+ def ret
+ lambda { return 1 }.call
+ 2
+ end
+ end
+ klass.new.lambda { 42 }.should be_an_instance_of Proc
+ klass.new.ret.should == 1
+ end
end
diff --git a/spec/ruby/core/kernel/rand_spec.rb b/spec/ruby/core/kernel/rand_spec.rb
index a82b4fba74..481189d969 100644
--- a/spec/ruby/core/kernel/rand_spec.rb
+++ b/spec/ruby/core/kernel/rand_spec.rb
@@ -117,6 +117,48 @@ describe "Kernel.rand" do
end
end
+ context "given an inclusive range between 0 and 1" do
+ it "returns an Integer between the two Integers" do
+ x = rand(0..1)
+ x.should be_kind_of(Integer)
+ (0..1).should include(x)
+ end
+
+ it "returns a Float if at least one side is Float" do
+ seed = 42
+ x1 = Random.new(seed).rand(0..1.0)
+ x2 = Random.new(seed).rand(0.0..1.0)
+ x3 = Random.new(seed).rand(0.0..1)
+
+ x3.should be_kind_of(Float)
+ x1.should equal(x3)
+ x2.should equal(x3)
+
+ (0.0..1.0).should include(x3)
+ end
+ end
+
+ context "given an exclusive range between 0 and 1" do
+ it "returns zero as an Integer" do
+ x = rand(0...1)
+ x.should be_kind_of(Integer)
+ x.should eql(0)
+ end
+
+ it "returns a Float if at least one side is Float" do
+ seed = 42
+ x1 = Random.new(seed).rand(0...1.0)
+ x2 = Random.new(seed).rand(0.0...1.0)
+ x3 = Random.new(seed).rand(0.0...1)
+
+ x3.should be_kind_of(Float)
+ x1.should equal(x3)
+ x2.should equal(x3)
+
+ (0.0...1.0).should include(x3)
+ end
+ end
+
it "returns a numeric for an range argument where max is < 1" do
rand(0.25..0.75).should be_kind_of(Numeric)
end
diff --git a/spec/ruby/core/module/ruby2_keywords_spec.rb b/spec/ruby/core/module/ruby2_keywords_spec.rb
new file mode 100644
index 0000000000..34c45cb1bc
--- /dev/null
+++ b/spec/ruby/core/module/ruby2_keywords_spec.rb
@@ -0,0 +1,112 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is "2.7" do
+ describe "Module#ruby2_keywords" do
+ it "marks the final hash argument as keyword hash" do
+ obj = Object.new
+
+ obj.singleton_class.class_exec do
+ def foo(*a) a.last end
+ ruby2_keywords :foo
+ end
+
+ last = obj.foo(1, 2, a: "a")
+ Hash.ruby2_keywords_hash?(last).should == true
+ end
+
+ ruby_version_is "2.7" ... "3.0" do
+ it "fixes delegation warnings when calling a method accepting keywords" do
+ obj = Object.new
+
+ obj.singleton_class.class_exec do
+ def foo(*a) bar(*a) end
+ def bar(*a, **b) end
+ end
+
+ -> { obj.foo(1, 2, {a: "a"}) }.should complain(/Using the last argument as keyword parameters is deprecated/)
+
+ obj.singleton_class.class_exec do
+ ruby2_keywords :foo
+ end
+
+ -> { obj.foo(1, 2, {a: "a"}) }.should_not complain
+ end
+ end
+
+ it "returns nil" do
+ obj = Object.new
+
+ obj.singleton_class.class_exec do
+ def foo(*a) end
+
+ ruby2_keywords(:foo).should == nil
+ end
+ end
+
+ it "raises NameError when passed not existing method name" do
+ obj = Object.new
+
+ -> {
+ obj.singleton_class.class_exec do
+ ruby2_keywords :not_existing
+ end
+ }.should raise_error(NameError, /undefined method `not_existing'/)
+ end
+
+ it "acceps String as well" do
+ obj = Object.new
+
+ obj.singleton_class.class_exec do
+ def foo(*a) a.last end
+ ruby2_keywords "foo"
+ end
+
+ last = obj.foo(1, 2, a: "a")
+ Hash.ruby2_keywords_hash?(last).should == true
+ end
+
+ it "raises TypeError when passed not Symbol or String" do
+ obj = Object.new
+
+ -> {
+ obj.singleton_class.class_exec do
+ ruby2_keywords Object.new
+ end
+ }.should raise_error(TypeError, /is not a symbol nor a string/)
+ end
+
+ it "prints warning when a method does not accept argument splat" do
+ obj = Object.new
+ def obj.foo(a, b, c) end
+
+ -> {
+ obj.singleton_class.class_exec do
+ ruby2_keywords :foo
+ end
+ }.should complain(/Skipping set of ruby2_keywords flag for/)
+ end
+
+ it "prints warning when a method accepts keywords" do
+ obj = Object.new
+ def obj.foo(a:, b:) end
+
+ -> {
+ obj.singleton_class.class_exec do
+ ruby2_keywords :foo
+ end
+ }.should complain(/Skipping set of ruby2_keywords flag for/)
+ end
+
+ it "prints warning when a method accepts keyword splat" do
+ obj = Object.new
+ def obj.foo(**a) end
+
+ -> {
+ obj.singleton_class.class_exec do
+ ruby2_keywords :foo
+ end
+ }.should complain(/Skipping set of ruby2_keywords flag for/)
+ end
+ end
+end
diff --git a/spec/ruby/core/module/to_s_spec.rb b/spec/ruby/core/module/to_s_spec.rb
index 29f6ecf726..6b1a615ef9 100644
--- a/spec/ruby/core/module/to_s_spec.rb
+++ b/spec/ruby/core/module/to_s_spec.rb
@@ -42,4 +42,27 @@ describe "Module#to_s" do
obj = ModuleSpecs::NamedClass.new
obj.singleton_class.to_s.should =~ /\A#<Class:#<ModuleSpecs::NamedClass:0x\h+>>\z/
end
+
+ it "always show the refinement name, even if the module is named" do
+ module ModuleSpecs::RefinementInspect
+ R = refine String do
+ end
+ end
+
+ ModuleSpecs::RefinementInspect::R.name.should == 'ModuleSpecs::RefinementInspect::R'
+ ModuleSpecs::RefinementInspect::R.to_s.should == '#<refinement:String@ModuleSpecs::RefinementInspect>'
+ end
+
+ it 'does not call #inspect or #to_s for singleton classes' do
+ klass = Class.new
+ obj = klass.new
+ def obj.to_s
+ "to_s"
+ end
+ def obj.inspect
+ "inspect"
+ end
+ sclass = obj.singleton_class
+ sclass.to_s.should =~ /\A#<Class:#<#{Regexp.escape klass.to_s}:0x\h+>>\z/
+ end
end
diff --git a/spec/ruby/core/proc/ruby2_keywords_spec.rb b/spec/ruby/core/proc/ruby2_keywords_spec.rb
new file mode 100644
index 0000000000..4f6bc151b6
--- /dev/null
+++ b/spec/ruby/core/proc/ruby2_keywords_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "2.7" do
+ describe "Proc#ruby2_keywords" do
+ it "marks the final hash argument as keyword hash" do
+ f = -> *a { a.last }
+ f.ruby2_keywords
+
+ last = f.call(1, 2, a: "a")
+ Hash.ruby2_keywords_hash?(last).should == true
+ end
+
+ ruby_version_is "2.7" ... "3.0" do
+ it "fixes delegation warnings when calling a method accepting keywords" do
+ obj = Object.new
+ def obj.foo(*a, **b) end
+
+ f = -> *a { obj.foo(*a) }
+
+ -> { f.call(1, 2, {a: "a"}) }.should complain(/Using the last argument as keyword parameters is deprecated/)
+ f.ruby2_keywords
+ -> { f.call(1, 2, {a: "a"}) }.should_not complain
+ end
+
+ it "fixes delegation warnings when calling a proc accepting keywords" do
+ g = -> *a, **b { }
+ f = -> *a { g.call(*a) }
+
+ -> { f.call(1, 2, {a: "a"}) }.should complain(/Using the last argument as keyword parameters is deprecated/)
+ f.ruby2_keywords
+ -> { f.call(1, 2, {a: "a"}) }.should_not complain
+ end
+ end
+
+ it "returns self" do
+ f = -> *a { }
+ f.ruby2_keywords.should equal f
+ end
+
+ it "prints warning when a proc does not accept argument splat" do
+ f = -> a, b, c { }
+
+ -> {
+ f.ruby2_keywords
+ }.should complain(/Skipping set of ruby2_keywords flag for/)
+ end
+
+ it "prints warning when a proc accepts keywords" do
+ f = -> a:, b: { }
+
+ -> {
+ f.ruby2_keywords
+ }.should complain(/Skipping set of ruby2_keywords flag for/)
+ end
+
+ it "prints warning when a proc accepts keyword splat" do
+ f = -> **a { }
+
+ -> {
+ f.ruby2_keywords
+ }.should complain(/Skipping set of ruby2_keywords flag for/)
+ end
+ end
+end
diff --git a/spec/ruby/core/range/shared/cover.rb b/spec/ruby/core/range/shared/cover.rb
index 5b09cea4e0..e04682ba71 100644
--- a/spec/ruby/core/range/shared/cover.rb
+++ b/spec/ruby/core/range/shared/cover.rb
@@ -152,4 +152,46 @@ describe :range_cover_subrange, shared: true do
end
end
end
+
+ ruby_version_is "2.7" do
+ it "allows self to be a beginless range" do
+ eval("(...10)").send(@method, (3..7)).should be_true
+ eval("(...10)").send(@method, (3..15)).should be_false
+
+ eval("(..7.9)").send(@method, (2.5..6.5)).should be_true
+ eval("(..7.9)").send(@method, (2.5..8.5)).should be_false
+
+ eval("(..'i')").send(@method, ('d'..'f')).should be_true
+ eval("(..'i')").send(@method, ('d'..'z')).should be_false
+ end
+
+ it "allows self to be a endless range" do
+ eval("(0...)").send(@method, (3..7)).should be_true
+ eval("(5...)").send(@method, (3..15)).should be_false
+
+ eval("(1.1..)").send(@method, (2.5..6.5)).should be_true
+ eval("(3.3..)").send(@method, (2.5..8.5)).should be_false
+
+ eval("('a'..)").send(@method, ('d'..'f')).should be_true
+ eval("('p'..)").send(@method, ('d'..'z')).should be_false
+ end
+
+ it "accepts beginless range argument" do
+ eval("(..10)").send(@method, eval("(...10)")).should be_true
+ (0..10).send(@method, eval("(...10)")).should be_false
+
+ (1.1..7.9).send(@method, eval("(...10.5)")).should be_false
+
+ ('c'..'i').send(@method, eval("(..'i')")).should be_false
+ end
+
+ it "accepts endless range argument" do
+ eval("(0..)").send(@method, eval("(0...)")).should be_true
+ (0..10).send(@method, eval("(0...)")).should be_false
+
+ (1.1..7.9).send(@method, eval("(0.8...)")).should be_false
+
+ ('c'..'i').send(@method, eval("('a'..)")).should be_false
+ end
+ end
end
diff --git a/spec/ruby/core/range/shared/cover_and_include.rb b/spec/ruby/core/range/shared/cover_and_include.rb
index b308524310..0267e3ccb0 100644
--- a/spec/ruby/core/range/shared/cover_and_include.rb
+++ b/spec/ruby/core/range/shared/cover_and_include.rb
@@ -26,6 +26,13 @@ describe :range_cover_and_include, shared: true do
end
end
+ ruby_version_is "2.7" do
+ it "returns true if other is an element of self for beginless ranges" do
+ eval("(..10)").send(@method, 2.4).should == true
+ eval("(...10.5)").send(@method, 2.4).should == true
+ end
+ end
+
it "compares values using <=>" do
rng = (1..5)
m = mock("int")
diff --git a/spec/ruby/core/range/to_a_spec.rb b/spec/ruby/core/range/to_a_spec.rb
index 08f50e4d9e..9a1352b7b0 100644
--- a/spec/ruby/core/range/to_a_spec.rb
+++ b/spec/ruby/core/range/to_a_spec.rb
@@ -16,6 +16,11 @@ describe "Range#to_a" do
(0xffff...0xfffd).to_a.should == []
end
+ it "works with Ranges of 64-bit integers" do
+ large = 1 << 40
+ (large..large+1).to_a.should == [1099511627776, 1099511627777]
+ end
+
it "works with Ranges of Symbols" do
(:A..:z).to_a.size.should == 58
end
diff --git a/spec/ruby/core/range/to_s_spec.rb b/spec/ruby/core/range/to_s_spec.rb
index ccbc5d8e7e..59672da822 100644
--- a/spec/ruby/core/range/to_s_spec.rb
+++ b/spec/ruby/core/range/to_s_spec.rb
@@ -18,6 +18,13 @@ describe "Range#to_s" do
end
end
+ ruby_version_is "2.7" do
+ it "can show beginless ranges" do
+ eval("(..1)").to_s.should == "..1"
+ eval("(...1.0)").to_s.should == "...1.0"
+ end
+ end
+
ruby_version_is ''...'2.7' do
it "returns a tainted string if either end is tainted" do
(("a".taint)..."c").to_s.tainted?.should be_true
diff --git a/spec/ruby/core/string/scrub_spec.rb b/spec/ruby/core/string/scrub_spec.rb
index 390035ef30..86fd4e85ba 100644
--- a/spec/ruby/core/string/scrub_spec.rb
+++ b/spec/ruby/core/string/scrub_spec.rb
@@ -39,6 +39,13 @@ describe "String#scrub with a custom replacement" do
"abc\u3042#{x81}".scrub("*").should == "abc\u3042*"
end
+ it "replaces invalid byte sequences in frozen strings" do
+ x81 = [0x81].pack('C').force_encoding('utf-8')
+ (-"abc\u3042#{x81}").scrub("*").should == "abc\u3042*"
+ utf16_str = ("abc".encode('UTF-16LE').bytes + [0x81]).pack('c*').force_encoding('UTF-16LE')
+ (-(utf16_str)).scrub("*".encode('UTF-16LE')).should == "abc*".encode('UTF-16LE')
+ end
+
it "replaces an incomplete character at the end with a single replacement" do
xE3x80 = [0xE3, 0x80].pack('CC').force_encoding 'utf-8'
xE3x80.scrub("*").should == "*"
diff --git a/spec/ruby/core/string/shared/slice.rb b/spec/ruby/core/string/shared/slice.rb
index 69997b7c1d..a674e0b6ef 100644
--- a/spec/ruby/core/string/shared/slice.rb
+++ b/spec/ruby/core/string/shared/slice.rb
@@ -335,6 +335,16 @@ describe :string_slice_range, shared: true do
"hello there".send(@method, eval("(-4...)")).should == "here"
end
end
+
+ ruby_version_is "2.7" do
+ it "works with beginless ranges" do
+ "hello there".send(@method, eval("(..5)")).should == "hello "
+ "hello there".send(@method, eval("(...5)")).should == "hello"
+ "hello there".send(@method, eval("(..-4)")).should == "hello th"
+ "hello there".send(@method, eval("(...-4)")).should == "hello t"
+ "hello there".send(@method, eval("(...nil)")).should == "hello there"
+ end
+ end
end
describe :string_slice_regexp, shared: true do
diff --git a/spec/ruby/core/string/split_spec.rb b/spec/ruby/core/string/split_spec.rb
index c5441e3a49..2ebfe1e353 100644
--- a/spec/ruby/core/string/split_spec.rb
+++ b/spec/ruby/core/string/split_spec.rb
@@ -471,6 +471,14 @@ describe "String#split with Regexp" do
a.should == ["Chunky", "Bacon"]
end
+ it "yields each split substring with default pattern for a non-ASCII string" do
+ a = []
+ returned_object = "l'été arrive bientôt".split { |str| a << str }
+
+ returned_object.should == "l'été arrive bientôt"
+ a.should == ["l'été", "arrive", "bientôt"]
+ end
+
it "yields the string when limit is 1" do
a = []
returned_object = "chunky bacon".split("", 1) { |str| a << str.capitalize }
diff --git a/spec/ruby/core/struct/hash_spec.rb b/spec/ruby/core/struct/hash_spec.rb
index d3c95fbe56..53361eb7a9 100644
--- a/spec/ruby/core/struct/hash_spec.rb
+++ b/spec/ruby/core/struct/hash_spec.rb
@@ -56,5 +56,9 @@ describe "Struct#hash" do
# See the Struct#eql? specs
end
+ it "returns different hashes for different struct classes" do
+ Struct.new(:x).new(1).hash.should != Struct.new(:y).new(1).hash
+ end
+
it_behaves_like :struct_accessor, :hash
end
diff --git a/spec/ruby/core/symbol/name_spec.rb b/spec/ruby/core/symbol/name_spec.rb
new file mode 100644
index 0000000000..15b9aa75e9
--- /dev/null
+++ b/spec/ruby/core/symbol/name_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+
+ruby_version_is "3.0" do
+ describe "Symbol#name" do
+ it "returns string" do
+ :ruby.name.should == "ruby"
+ :ルビー.name.should == "ルビー"
+ end
+
+ it "returns same string instance" do
+ :"ruby_3".name.should.equal?(:ruby_3.name)
+ :"ruby_#{1+2}".name.should.equal?(:ruby_3.name)
+ end
+
+ it "returns frozen string" do
+ :symbol.name.should.frozen?
+ end
+ end
+end
diff --git a/spec/ruby/core/thread/backtrace_locations_spec.rb b/spec/ruby/core/thread/backtrace_locations_spec.rb
index ead4be2d8c..1f77e13378 100644
--- a/spec/ruby/core/thread/backtrace_locations_spec.rb
+++ b/spec/ruby/core/thread/backtrace_locations_spec.rb
@@ -51,6 +51,14 @@ describe "Thread#backtrace_locations" do
end
end
+ ruby_version_is "2.7" do
+ it "can be called with an beginless range" do
+ locations1 = Thread.current.backtrace_locations(0)
+ locations2 = Thread.current.backtrace_locations(eval("(..5)"))
+ locations2.map(&:to_s)[eval("(2..)")].should == locations1[eval("(..5)")].map(&:to_s)[eval("(2..)")]
+ end
+ end
+
it "returns nil if omitting more locations than available" do
Thread.current.backtrace_locations(100).should == nil
Thread.current.backtrace_locations(100..-1).should == nil
diff --git a/spec/ruby/core/thread/handle_interrupt_spec.rb b/spec/ruby/core/thread/handle_interrupt_spec.rb
new file mode 100644
index 0000000000..ea7e81cb98
--- /dev/null
+++ b/spec/ruby/core/thread/handle_interrupt_spec.rb
@@ -0,0 +1,125 @@
+require_relative '../../spec_helper'
+
+describe "Thread.handle_interrupt" do
+ def make_handle_interrupt_thread(interrupt_config, blocking = true)
+ interrupt_class = Class.new(RuntimeError)
+
+ ScratchPad.record []
+
+ in_handle_interrupt = Queue.new
+ can_continue = Queue.new
+
+ thread = Thread.new do
+ begin
+ Thread.handle_interrupt(interrupt_config) do
+ begin
+ in_handle_interrupt << true
+ if blocking
+ Thread.pass # Make it clearer the other thread needs to wait for this one to be in #pop
+ can_continue.pop
+ else
+ begin
+ can_continue.pop(true)
+ rescue ThreadError
+ Thread.pass
+ retry
+ end
+ end
+ rescue interrupt_class
+ ScratchPad << :interrupted
+ end
+ end
+ rescue interrupt_class
+ ScratchPad << :deferred
+ end
+ end
+
+ in_handle_interrupt.pop
+ if blocking
+ # Ensure the thread is inside Thread#pop, as if thread.raise is done before it would be deferred
+ Thread.pass until thread.stop?
+ end
+ thread.raise interrupt_class, "interrupt"
+ can_continue << true
+ thread.join
+
+ ScratchPad.recorded
+ end
+
+ before :each do
+ Thread.pending_interrupt?.should == false # sanity check
+ end
+
+ it "with :never defers interrupts until exiting the handle_interrupt block" do
+ make_handle_interrupt_thread(RuntimeError => :never).should == [:deferred]
+ end
+
+ it "with :on_blocking defers interrupts until the next blocking call" do
+ make_handle_interrupt_thread(RuntimeError => :on_blocking).should == [:interrupted]
+ make_handle_interrupt_thread({ RuntimeError => :on_blocking }, false).should == [:deferred]
+ end
+
+ it "with :immediate handles interrupts immediately" do
+ make_handle_interrupt_thread(RuntimeError => :immediate).should == [:interrupted]
+ end
+
+ it "with :immediate immediately runs pending interrupts, before the block" do
+ Thread.handle_interrupt(RuntimeError => :never) do
+ current = Thread.current
+ Thread.new {
+ current.raise "interrupt immediate"
+ }.join
+
+ Thread.pending_interrupt?.should == true
+ -> {
+ Thread.handle_interrupt(RuntimeError => :immediate) {
+ flunk "not reached"
+ }
+ }.should raise_error(RuntimeError, "interrupt immediate")
+ Thread.pending_interrupt?.should == false
+ end
+ end
+
+ it "also works with suspended Fibers and does not duplicate interrupts" do
+ fiber = Fiber.new { Fiber.yield }
+ fiber.resume
+
+ Thread.handle_interrupt(RuntimeError => :never) do
+ current = Thread.current
+ Thread.new {
+ current.raise "interrupt with fibers"
+ }.join
+
+ Thread.pending_interrupt?.should == true
+ -> {
+ Thread.handle_interrupt(RuntimeError => :immediate) {
+ flunk "not reached"
+ }
+ }.should raise_error(RuntimeError, "interrupt with fibers")
+ Thread.pending_interrupt?.should == false
+ end
+
+ fiber.resume
+ end
+
+ it "runs pending interrupts at the end of the block, even if there was an exception raised in the block" do
+ executed = false
+ -> {
+ Thread.handle_interrupt(RuntimeError => :never) do
+ current = Thread.current
+ Thread.new {
+ current.raise "interrupt exception"
+ }.join
+
+ Thread.pending_interrupt?.should == true
+ executed = true
+ raise "regular exception"
+ end
+ }.should raise_error(RuntimeError, "interrupt exception")
+ executed.should == true
+ end
+
+ it "supports multiple pairs in the Hash" do
+ make_handle_interrupt_thread(ArgumentError => :never, RuntimeError => :never).should == [:deferred]
+ end
+end
diff --git a/spec/ruby/core/thread/pending_interrupt_spec.rb b/spec/ruby/core/thread/pending_interrupt_spec.rb
new file mode 100644
index 0000000000..cd565d92a4
--- /dev/null
+++ b/spec/ruby/core/thread/pending_interrupt_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+
+describe "Thread.pending_interrupt?" do
+ it "returns false if there are no pending interrupts, e.g., outside any Thread.handle_interrupt block" do
+ Thread.pending_interrupt?.should == false
+ end
+
+ it "returns true if there are pending interrupts, e.g., Thread#raise inside Thread.handle_interrupt" do
+ executed = false
+ -> {
+ Thread.handle_interrupt(RuntimeError => :never) do
+ Thread.pending_interrupt?.should == false
+
+ current = Thread.current
+ Thread.new {
+ current.raise "interrupt"
+ }.join
+
+ Thread.pending_interrupt?.should == true
+ executed = true
+ end
+ }.should raise_error(RuntimeError, "interrupt")
+ executed.should == true
+ Thread.pending_interrupt?.should == false
+ end
+end
+
+describe "Thread#pending_interrupt?" do
+ it "returns whether the given threads has pending interrupts" do
+ Thread.current.pending_interrupt?.should == false
+ end
+end
diff --git a/spec/ruby/core/time/inspect_spec.rb b/spec/ruby/core/time/inspect_spec.rb
index 85133838e2..6f1b2e3ef1 100644
--- a/spec/ruby/core/time/inspect_spec.rb
+++ b/spec/ruby/core/time/inspect_spec.rb
@@ -5,17 +5,31 @@ describe "Time#inspect" do
it_behaves_like :inspect, :inspect
ruby_version_is "2.7" do
- it "preserves milliseconds" do
+ it "preserves microseconds" do
t = Time.utc(2007, 11, 1, 15, 25, 0, 123456)
t.inspect.should == "2007-11-01 15:25:00.123456 UTC"
end
- it "formats nanoseconds as a Rational" do
- t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789)
- t.nsec.should == 123456789
- t.strftime("%N").should == "123456789"
+ it "omits trailing zeros from microseconds" do
+ t = Time.utc(2007, 11, 1, 15, 25, 0, 100000)
+ t.inspect.should == "2007-11-01 15:25:00.1 UTC"
+ end
+
+ it "uses the correct time zone without microseconds" do
+ t = Time.utc(2000, 1, 1)
+ t = t.localtime(9*3600)
+ t.inspect.should == "2000-01-01 09:00:00 +0900"
+ end
+
+ it "uses the correct time zone with microseconds" do
+ t = Time.utc(2000, 1, 1, 0, 0, 0, 123456)
+ t = t.localtime(9*3600)
+ t.inspect.should == "2000-01-01 09:00:00.123456 +0900"
+ end
- t.inspect.should == "2007-11-01 15:25:00 8483885939586761/68719476736000000 UTC"
+ it "preserves nanoseconds" do
+ t = Time.utc(2007, 11, 1, 15, 25, 0, 123456.789r)
+ t.inspect.should == "2007-11-01 15:25:00.123456789 UTC"
end
end
end