From 95d9fe9538441eb57ee6752aa1c5088fc6608e34 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Sat, 27 Mar 2021 13:02:41 +0100 Subject: Update to ruby/spec@fd6eddd --- spec/ruby/.rubocop.yml | 1 + spec/ruby/core/enumerable/chunk_spec.rb | 5 + spec/ruby/core/exception/interrupt_spec.rb | 18 +++- spec/ruby/core/exception/result_spec.rb | 8 +- spec/ruby/core/gc/stat_spec.rb | 38 +++++-- spec/ruby/core/kernel/raise_spec.rb | 21 ++++ spec/ruby/core/kernel/sleep_spec.rb | 6 ++ spec/ruby/core/kernel/trap_spec.rb | 5 +- spec/ruby/core/signal/trap_spec.rb | 113 ++++++++++++++++++--- spec/ruby/core/time/at_spec.rb | 14 +++ spec/ruby/language/constants_spec.rb | 46 --------- spec/ruby/language/ensure_spec.rb | 4 +- spec/ruby/language/hash_spec.rb | 39 +++++++ spec/ruby/language/method_spec.rb | 11 ++ spec/ruby/language/optional_assignments_spec.rb | 105 +++++++++++++++++++ spec/ruby/language/regexp/grouping_spec.rb | 7 ++ spec/ruby/language/rescue_spec.rb | 4 +- spec/ruby/language/string_spec.rb | 10 ++ spec/ruby/library/monitor/synchronize_spec.rb | 30 ++++++ .../library/socket/socket/udp_server_loop_spec.rb | 4 +- spec/ruby/library/weakref/fixtures/classes.rb | 6 +- spec/ruby/optional/capi/ext/kernel_spec.c | 9 ++ spec/ruby/optional/capi/ext/module_spec.c | 31 ++++++ spec/ruby/optional/capi/kernel_spec.rb | 8 ++ spec/ruby/optional/capi/module_spec.rb | 20 +++- spec/ruby/shared/kernel/raise.rb | 22 ++++ 26 files changed, 501 insertions(+), 84 deletions(-) create mode 100644 spec/ruby/library/monitor/synchronize_spec.rb (limited to 'spec') diff --git a/spec/ruby/.rubocop.yml b/spec/ruby/.rubocop.yml index a26b525b1c..60b42cff7f 100644 --- a/spec/ruby/.rubocop.yml +++ b/spec/ruby/.rubocop.yml @@ -95,6 +95,7 @@ Lint/UnreachableCode: - 'core/kernel/raise_spec.rb' - 'core/kernel/throw_spec.rb' - 'language/break_spec.rb' + - 'language/optional_assignments_spec.rb' - 'language/fixtures/break.rb' - 'language/fixtures/break_lambda_toplevel.rb' - 'language/fixtures/break_lambda_toplevel_block.rb' diff --git a/spec/ruby/core/enumerable/chunk_spec.rb b/spec/ruby/core/enumerable/chunk_spec.rb index 548544f4e8..c5579d67fa 100644 --- a/spec/ruby/core/enumerable/chunk_spec.rb +++ b/spec/ruby/core/enumerable/chunk_spec.rb @@ -35,6 +35,11 @@ describe "Enumerable#chunk" do result.should == [[:_alone, [1]], [false, [2, 3, 2]], [:_alone, [1]]] end + it "yields Arrays as a single argument to a rest argument" do + e = EnumerableSpecs::Numerous.new([1, 2]) + result = e.chunk { |*x| x.should == [[1,2]] }.to_a + end + it "does not return elements for which the block returns :_separator" do e = EnumerableSpecs::Numerous.new(1, 2, 3, 3, 2, 1) result = e.chunk { |x| x == 2 ? :_separator : 1 }.to_a diff --git a/spec/ruby/core/exception/interrupt_spec.rb b/spec/ruby/core/exception/interrupt_spec.rb index 14f294bec6..a7501efadc 100644 --- a/spec/ruby/core/exception/interrupt_spec.rb +++ b/spec/ruby/core/exception/interrupt_spec.rb @@ -29,7 +29,23 @@ describe "rescuing Interrupt" do sleep rescue Interrupt => e e.signo.should == Signal.list["INT"] - e.signm.should == "" + ["", "Interrupt"].should.include?(e.message) end end end + +describe "Interrupt" do + # This spec is basically the same as above, + # but it does not rely on Signal.trap(:INT, :SIG_DFL) which can be tricky + it "is raised on the main Thread by the default SIGINT handler" do + out = ruby_exe(<<-'RUBY', args: "2>&1") + begin + Process.kill :INT, Process.pid + sleep + rescue Interrupt => e + puts "Interrupt: #{e.signo}" + end + RUBY + out.should == "Interrupt: #{Signal.list["INT"]}\n" + end +end diff --git a/spec/ruby/core/exception/result_spec.rb b/spec/ruby/core/exception/result_spec.rb index 2f12673295..d42fcdffcb 100644 --- a/spec/ruby/core/exception/result_spec.rb +++ b/spec/ruby/core/exception/result_spec.rb @@ -14,10 +14,8 @@ describe "StopIteration#result" do it "returns the method-returned-object from an Enumerator" do @enum.next @enum.next - -> { @enum.next }.should( - raise_error(StopIteration) do |error| - error.result.should equal(:method_returned) - end - ) + -> { @enum.next }.should raise_error(StopIteration) { |error| + error.result.should equal(:method_returned) + } end end diff --git a/spec/ruby/core/gc/stat_spec.rb b/spec/ruby/core/gc/stat_spec.rb index 51bd00c7c7..34656c401c 100644 --- a/spec/ruby/core/gc/stat_spec.rb +++ b/spec/ruby/core/gc/stat_spec.rb @@ -1,16 +1,40 @@ require_relative '../../spec_helper' describe "GC.stat" do - it "supports access by key" do - keys = [:heap_free_slots, :total_allocated_objects, :count] - keys.each do |key| - GC.stat(key).should be_kind_of(Integer) - end - end - it "returns hash of values" do stat = GC.stat stat.should be_kind_of(Hash) stat.keys.should include(:count) end + + it "can return a single value" do + GC.stat(:count).should be_kind_of(Integer) + end + + it "increases count after GC is run" do + count = GC.stat(:count) + GC.start + GC.stat(:count).should > count + end + + it "increases major_gc_count after GC is run" do + count = GC.stat(:major_gc_count) + GC.start + GC.stat(:major_gc_count).should > count + end + + it "provides some number for count" do + GC.stat(:count).should be_kind_of(Integer) + GC.stat[:count].should be_kind_of(Integer) + end + + it "provides some number for heap_free_slots" do + GC.stat(:heap_free_slots).should be_kind_of(Integer) + GC.stat[:heap_free_slots].should be_kind_of(Integer) + end + + it "provides some number for total_allocated_objects" do + GC.stat(:total_allocated_objects).should be_kind_of(Integer) + GC.stat[:total_allocated_objects].should be_kind_of(Integer) + end end diff --git a/spec/ruby/core/kernel/raise_spec.rb b/spec/ruby/core/kernel/raise_spec.rb index 591daa03cf..6dac132498 100644 --- a/spec/ruby/core/kernel/raise_spec.rb +++ b/spec/ruby/core/kernel/raise_spec.rb @@ -27,6 +27,27 @@ describe "Kernel#raise" do ScratchPad.recorded.should be_nil end + + ruby_version_is "2.6" do + it "accepts a cause keyword argument that sets the cause" do + cause = StandardError.new + -> { raise("error", cause: cause) }.should raise_error(RuntimeError) { |e| e.cause.should == cause } + end + + it "accepts a cause keyword argument that overrides the last exception" do + begin + raise "first raise" + rescue => ignored + cause = StandardError.new + -> { raise("error", cause: cause) }.should raise_error(RuntimeError) { |e| e.cause.should == cause } + end + end + + it "raises an ArgumentError when only cause is given" do + cause = StandardError.new + -> { raise(cause: cause) }.should raise_error(ArgumentError) + end + end end describe "Kernel#raise" do diff --git a/spec/ruby/core/kernel/sleep_spec.rb b/spec/ruby/core/kernel/sleep_spec.rb index 71a3f24f13..32da6344c1 100644 --- a/spec/ruby/core/kernel/sleep_spec.rb +++ b/spec/ruby/core/kernel/sleep_spec.rb @@ -22,6 +22,12 @@ describe "Kernel#sleep" do sleep(Rational(1, 999)).should >= 0 end + it "accepts any Object that reponds to divmod" do + o = Object.new + def o.divmod(*); [0, 0.001]; end + sleep(o).should >= 0 + end + it "raises an ArgumentError when passed a negative duration" do -> { sleep(-0.1) }.should raise_error(ArgumentError) -> { sleep(-1) }.should raise_error(ArgumentError) diff --git a/spec/ruby/core/kernel/trap_spec.rb b/spec/ruby/core/kernel/trap_spec.rb index 465aacb0fb..4c801a7215 100644 --- a/spec/ruby/core/kernel/trap_spec.rb +++ b/spec/ruby/core/kernel/trap_spec.rb @@ -1,12 +1,9 @@ require_relative '../../spec_helper' -require_relative 'fixtures/classes' describe "Kernel#trap" do it "is a private method" do Kernel.should have_private_instance_method(:trap) end -end -describe "Kernel.trap" do - it "needs to be reviewed for spec completeness" + # Behaviour is specified for Signal.trap end diff --git a/spec/ruby/core/signal/trap_spec.rb b/spec/ruby/core/signal/trap_spec.rb index 45879b7147..61d39cfa93 100644 --- a/spec/ruby/core/signal/trap_spec.rb +++ b/spec/ruby/core/signal/trap_spec.rb @@ -1,11 +1,12 @@ require_relative '../../spec_helper' -platform_is_not :windows do - describe "Signal.trap" do +describe "Signal.trap" do + platform_is_not :windows do before :each do ScratchPad.clear @proc = -> {} @saved_trap = Signal.trap(:HUP, @proc) + @hup_number = Signal.list["HUP"] end after :each do @@ -16,10 +17,11 @@ platform_is_not :windows do Signal.trap(:HUP, @saved_trap).should equal(@proc) end - it "accepts a block in place of a proc/command argument" do + it "accepts a block" do done = false - Signal.trap(:HUP) do + Signal.trap(:HUP) do |signo| + signo.should == @hup_number ScratchPad.record :block_trap done = true end @@ -30,6 +32,94 @@ platform_is_not :windows do ScratchPad.recorded.should == :block_trap end + it "accepts a proc" do + done = false + + handler = -> signo { + signo.should == @hup_number + ScratchPad.record :proc_trap + done = true + } + + Signal.trap(:HUP, handler) + + Process.kill :HUP, Process.pid + Thread.pass until done + + ScratchPad.recorded.should == :proc_trap + end + + it "accepts a method" do + done = false + + handler_class = Class.new + hup_number = @hup_number + + handler_class.define_method :handler_method do |signo| + signo.should == hup_number + ScratchPad.record :method_trap + done = true + end + + handler_method = handler_class.new.method(:handler_method) + + Signal.trap(:HUP, handler_method) + + Process.kill :HUP, Process.pid + Thread.pass until done + + ScratchPad.recorded.should == :method_trap + end + + it "accepts anything you can call" do + done = false + + callable = Object.new + hup_number = @hup_number + + callable.singleton_class.define_method :call do |signo| + signo.should == hup_number + ScratchPad.record :callable_trap + done = true + end + + Signal.trap(:HUP, callable) + + Process.kill :HUP, Process.pid + Thread.pass until done + + ScratchPad.recorded.should == :callable_trap + end + + it "raises an exception for a non-callable at the point of use" do + not_callable = Object.new + Signal.trap(:HUP, not_callable) + -> { + Process.kill :HUP, Process.pid + loop { Thread.pass } + }.should raise_error(NoMethodError) + end + + it "accepts a non-callable that becomes callable when used" do + done = false + + late_callable = Object.new + hup_number = @hup_number + + Signal.trap(:HUP, late_callable) + + late_callable.singleton_class.define_method :call do |signo| + signo.should == hup_number + ScratchPad.record :late_callable_trap + done = true + end + + Process.kill :HUP, Process.pid + Thread.pass until done + + ScratchPad.recorded.should == :late_callable_trap + end + it "is possible to create a new Thread when the handler runs" do done = false @@ -130,14 +220,12 @@ platform_is_not :windows do Signal.trap :HUP, @proc Signal.trap(:HUP, @saved_trap).should equal(@proc) end - end - describe "Signal.trap" do # See man 2 signal %w[KILL STOP].each do |signal| it "raises ArgumentError or Errno::EINVAL for SIG#{signal}" do -> { - trap(signal, -> {}) + Signal.trap(signal, -> {}) }.should raise_error(StandardError) { |e| [ArgumentError, Errno::EINVAL].should include(e.class) e.message.should =~ /Invalid argument|Signal already used by VM or OS/ @@ -152,7 +240,7 @@ platform_is_not :windows do end it "returns 'DEFAULT' for the initial SIGINT handler" do - ruby_exe('print trap(:INT) { abort }').should == 'DEFAULT' + ruby_exe("print Signal.trap(:INT) { abort }").should == 'DEFAULT' end it "returns SYSTEM_DEFAULT if passed DEFAULT and no handler was ever set" do @@ -174,23 +262,22 @@ platform_is_not :windows do Signal.signame(status.termsig).should == "PIPE" end end -end -describe "Signal.trap" do describe "the special EXIT signal code" do it "accepts the EXIT code" do - code = "trap(:EXIT, proc { print 1 })" + code = "Signal.trap(:EXIT, proc { print 1 })" ruby_exe(code).should == "1" end it "runs the proc before at_exit handlers" do - code = "at_exit {print 1}; trap(:EXIT, proc {print 2}); at_exit {print 3}" + code = "at_exit {print 1}; Signal.trap(:EXIT, proc {print 2}); at_exit {print 3}" ruby_exe(code).should == "231" end it "can unset the handler" do - code = "trap(:EXIT, proc { print 1 }); trap(:EXIT, 'DEFAULT')" + code = "Signal.trap(:EXIT, proc { print 1 }); Signal.trap(:EXIT, 'DEFAULT')" ruby_exe(code).should == "" end end + end diff --git a/spec/ruby/core/time/at_spec.rb b/spec/ruby/core/time/at_spec.rb index 4ff38bbd21..ff43537dcc 100644 --- a/spec/ruby/core/time/at_spec.rb +++ b/spec/ruby/core/time/at_spec.rb @@ -38,6 +38,20 @@ describe "Time.at" do Time.at(BigDecimal('1.1')).to_f.should == 1.1 end end + + describe "passed Rational" do + it "returns Time with correct microseconds" do + t = Time.at(Rational(1_486_570_508_539_759, 1_000_000)) + t.usec.should == 539_759 + t.nsec.should == 539_759_000 + end + + it "returns Time with correct nanoseconds" do + t = Time.at(Rational(1_486_570_508_539_759_123, 1_000_000_000)) + t.usec.should == 539_759 + t.nsec.should == 539_759_123 + end + end end describe "passed Time" do diff --git a/spec/ruby/language/constants_spec.rb b/spec/ruby/language/constants_spec.rb index 6d3b1a5dd7..760b9d4a24 100644 --- a/spec/ruby/language/constants_spec.rb +++ b/spec/ruby/language/constants_spec.rb @@ -381,52 +381,6 @@ describe "Constant resolution within methods" do ConstantSpecs::ClassA.constx.should == :CS_CONSTX ConstantSpecs::ClassA.new.constx.should == :CS_CONSTX end - - describe "with ||=" do - it "assigns a scoped constant if previously undefined" do - ConstantSpecs.should_not have_constant(:OpAssignUndefined) - module ConstantSpecs - OpAssignUndefined ||= 42 - end - ConstantSpecs::OpAssignUndefined.should == 42 - ConstantSpecs::OpAssignUndefinedOutside ||= 42 - ConstantSpecs::OpAssignUndefinedOutside.should == 42 - ConstantSpecs.send(:remove_const, :OpAssignUndefined) - ConstantSpecs.send(:remove_const, :OpAssignUndefinedOutside) - end - - it "assigns a global constant if previously undefined" do - OpAssignGlobalUndefined ||= 42 - ::OpAssignGlobalUndefinedExplicitScope ||= 42 - OpAssignGlobalUndefined.should == 42 - ::OpAssignGlobalUndefinedExplicitScope.should == 42 - Object.send :remove_const, :OpAssignGlobalUndefined - Object.send :remove_const, :OpAssignGlobalUndefinedExplicitScope - end - - end - - describe "with &&=" do - it "re-assigns a scoped constant if already true" do - module ConstantSpecs - OpAssignTrue = true - end - suppress_warning do - ConstantSpecs::OpAssignTrue &&= 1 - end - ConstantSpecs::OpAssignTrue.should == 1 - ConstantSpecs.send :remove_const, :OpAssignTrue - end - - it "leaves scoped constant if not true" do - module ConstantSpecs - OpAssignFalse = false - end - ConstantSpecs::OpAssignFalse &&= 1 - ConstantSpecs::OpAssignFalse.should == false - ConstantSpecs.send :remove_const, :OpAssignFalse - end - end end describe "Constant resolution within a singleton class (class << obj)" do diff --git a/spec/ruby/language/ensure_spec.rb b/spec/ruby/language/ensure_spec.rb index e94c523e82..e893904bcb 100644 --- a/spec/ruby/language/ensure_spec.rb +++ b/spec/ruby/language/ensure_spec.rb @@ -74,9 +74,9 @@ describe "An ensure block inside a begin block" do ensure raise "from ensure" end - }.should raise_error(RuntimeError, "from ensure") do |e| + }.should raise_error(RuntimeError, "from ensure") { |e| e.cause.message.should == "from block" - end + } end end diff --git a/spec/ruby/language/hash_spec.rb b/spec/ruby/language/hash_spec.rb index d6600ddb4a..afc0df12a2 100644 --- a/spec/ruby/language/hash_spec.rb +++ b/spec/ruby/language/hash_spec.rb @@ -167,3 +167,42 @@ describe "Hash literal" do usascii_hash.keys.first.encoding.should == Encoding::US_ASCII end end + +describe "The ** operator" do + it "makes a copy when calling a method taking a keyword rest argument" do + def m(**h) + h.delete(:one); h + end + + h = { one: 1, two: 2 } + m(**h).should == { two: 2 } + m(**h).should_not.equal?(h) + h.should == { one: 1, two: 2 } + end + + ruby_version_is ""..."3.0" do + it "makes a caller-side copy when calling a method taking a positional Hash" do + def m(h) + h.delete(:one); h + end + + h = { one: 1, two: 2 } + m(**h).should == { two: 2 } + m(**h).should_not.equal?(h) + h.should == { one: 1, two: 2 } + end + end + + ruby_version_is "3.0" do + it "does not copy when calling a method taking a positional Hash" do + def m(h) + h.delete(:one); h + end + + h = { one: 1, two: 2 } + m(**h).should == { two: 2 } + m(**h).should.equal?(h) + h.should == { two: 2 } + end + end +end diff --git a/spec/ruby/language/method_spec.rb b/spec/ruby/language/method_spec.rb index 462a182b3d..449d5e6f1b 100644 --- a/spec/ruby/language/method_spec.rb +++ b/spec/ruby/language/method_spec.rb @@ -1758,6 +1758,17 @@ describe "A method" do end end end + + it "assigns the last Hash to the last optional argument if the Hash contains non-Symbol keys and is not passed as keywords" do + def m(a = nil, b = {}, v: false) + [a, b, v] + end + + h = { "key" => "value" } + m(:a, h).should == [:a, h, false] + m(:a, h, v: true).should == [:a, h, true] + m(v: true).should == [nil, {}, true] + end end describe "A method call with a space between method name and parentheses" do diff --git a/spec/ruby/language/optional_assignments_spec.rb b/spec/ruby/language/optional_assignments_spec.rb index 3d9e0dbb65..217dcab74b 100644 --- a/spec/ruby/language/optional_assignments_spec.rb +++ b/spec/ruby/language/optional_assignments_spec.rb @@ -1,4 +1,5 @@ require_relative '../spec_helper' +require_relative '../fixtures/constants' describe 'Optional variable assignments' do describe 'using ||=' do @@ -351,3 +352,107 @@ describe 'Optional variable assignments' do end end end + +describe 'Optional constant assignment' do + describe 'with ||=' do + it "assigns a scoped constant if previously undefined" do + ConstantSpecs.should_not have_constant(:OpAssignUndefined) + module ConstantSpecs + OpAssignUndefined ||= 42 + end + ConstantSpecs::OpAssignUndefined.should == 42 + ConstantSpecs::OpAssignUndefinedOutside ||= 42 + ConstantSpecs::OpAssignUndefinedOutside.should == 42 + ConstantSpecs.send(:remove_const, :OpAssignUndefined) + ConstantSpecs.send(:remove_const, :OpAssignUndefinedOutside) + end + + it "assigns a global constant if previously undefined" do + OpAssignGlobalUndefined ||= 42 + ::OpAssignGlobalUndefinedExplicitScope ||= 42 + OpAssignGlobalUndefined.should == 42 + ::OpAssignGlobalUndefinedExplicitScope.should == 42 + Object.send :remove_const, :OpAssignGlobalUndefined + Object.send :remove_const, :OpAssignGlobalUndefinedExplicitScope + end + + it 'correctly defines non-existing constants' do + ConstantSpecs::ClassA::OR_ASSIGNED_CONSTANT1 ||= :assigned + ConstantSpecs::ClassA::OR_ASSIGNED_CONSTANT1.should == :assigned + end + + it 'correctly overwrites nil constants' do + suppress_warning do # already initialized constant + ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT1 = nil + ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT1 ||= :assigned + ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT1.should == :assigned + end + end + + it 'causes side-effects of the module part to be applied only once (for undefined constant)' do + x = 0 + (x += 1; ConstantSpecs::ClassA)::OR_ASSIGNED_CONSTANT2 ||= :assigned + x.should == 1 + ConstantSpecs::ClassA::OR_ASSIGNED_CONSTANT2.should == :assigned + end + + it 'causes side-effects of the module part to be applied (for nil constant)' do + suppress_warning do # already initialized constant + ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT2 = nil + x = 0 + (x += 1; ConstantSpecs::ClassA)::NIL_OR_ASSIGNED_CONSTANT2 ||= :assigned + x.should == 1 + ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT2.should == :assigned + end + end + + it 'does not evaluate the right-hand side if the module part raises an exception (for undefined constant)' do + x = 0 + y = 0 + + -> { + (x += 1; raise Exception; ConstantSpecs::ClassA)::OR_ASSIGNED_CONSTANT3 ||= (y += 1; :assigned) + }.should raise_error(Exception) + + x.should == 1 + y.should == 0 + defined?(ConstantSpecs::ClassA::OR_ASSIGNED_CONSTANT3).should == nil + end + + it 'does not evaluate the right-hand side if the module part raises an exception (for nil constant)' do + ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT3 = nil + x = 0 + y = 0 + + -> { + (x += 1; raise Exception; ConstantSpecs::ClassA)::NIL_OR_ASSIGNED_CONSTANT3 ||= (y += 1; :assigned) + }.should raise_error(Exception) + + x.should == 1 + y.should == 0 + ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT3.should == nil + end + end + + describe "with &&=" do + it "re-assigns a scoped constant if already true" do + module ConstantSpecs + OpAssignTrue = true + end + suppress_warning do + ConstantSpecs::OpAssignTrue &&= 1 + end + ConstantSpecs::OpAssignTrue.should == 1 + ConstantSpecs.send :remove_const, :OpAssignTrue + end + + it "leaves scoped constant if not true" do + module ConstantSpecs + OpAssignFalse = false + end + ConstantSpecs::OpAssignFalse &&= 1 + ConstantSpecs::OpAssignFalse.should == false + ConstantSpecs.send :remove_const, :OpAssignFalse + end + end +end diff --git a/spec/ruby/language/regexp/grouping_spec.rb b/spec/ruby/language/regexp/grouping_spec.rb index 2fecf2d2cb..e2abe71d0d 100644 --- a/spec/ruby/language/regexp/grouping_spec.rb +++ b/spec/ruby/language/regexp/grouping_spec.rb @@ -25,4 +25,11 @@ describe "Regexps with grouping" do -> { Regexp.new("(?<1a>a)") }.should raise_error(RegexpError) -> { Regexp.new("(?<-a>a)") }.should raise_error(RegexpError) end + + it "ignore capture groups in line comments" do + /^ + (a) # there is a capture group on this line + b # there is no capture group on this line (not even here) + $/x.match("ab").to_a.should == [ "ab", "a" ] + end end diff --git a/spec/ruby/language/rescue_spec.rb b/spec/ruby/language/rescue_spec.rb index 54cbae1440..1c78f3935a 100644 --- a/spec/ruby/language/rescue_spec.rb +++ b/spec/ruby/language/rescue_spec.rb @@ -428,9 +428,9 @@ describe "The rescue keyword" do raise "from block" rescue (raise "from rescue expression") end - }.should raise_error(RuntimeError, "from rescue expression") do |e| + }.should raise_error(RuntimeError, "from rescue expression") { |e| e.cause.message.should == "from block" - end + } end it "should splat the handling Error classes" do diff --git a/spec/ruby/language/string_spec.rb b/spec/ruby/language/string_spec.rb index 0178083f58..ce4941569e 100644 --- a/spec/ruby/language/string_spec.rb +++ b/spec/ruby/language/string_spec.rb @@ -308,4 +308,14 @@ describe "Ruby String interpolation" do eval(code).should_not.frozen? end end + + ruby_version_is ""..."3.0" do + it "creates a frozen String when # frozen-string-literal: true is used" do + code = <<~'RUBY' + # frozen-string-literal: true + "a#{6*7}c" + RUBY + eval(code).should.frozen? + end + end end diff --git a/spec/ruby/library/monitor/synchronize_spec.rb b/spec/ruby/library/monitor/synchronize_spec.rb new file mode 100644 index 0000000000..d851b98d6c --- /dev/null +++ b/spec/ruby/library/monitor/synchronize_spec.rb @@ -0,0 +1,30 @@ +require_relative '../../spec_helper' +require 'monitor' + +describe "Monitor#synchronize" do + it "unlocks after return, even if it was interrupted by Thread#raise" do + exc_class = Class.new(RuntimeError) + + monitor = Monitor.new + 10.times do + locked = false + + thread = Thread.new do + begin + monitor.synchronize do + locked = true + # Do not wait here, we are trying to interrupt the ensure part of #synchronize + end + sleep # wait for exception if it did not happen yet + rescue exc_class + monitor.should_not.mon_locked? + :ok + end + end + + Thread.pass until locked + thread.raise exc_class, "interrupt" + thread.value.should == :ok + end + end +end diff --git a/spec/ruby/library/socket/socket/udp_server_loop_spec.rb b/spec/ruby/library/socket/socket/udp_server_loop_spec.rb index 66563bc742..fc030e75b9 100644 --- a/spec/ruby/library/socket/socket/udp_server_loop_spec.rb +++ b/spec/ruby/library/socket/socket/udp_server_loop_spec.rb @@ -21,7 +21,7 @@ describe 'Socket.udp_server_loop' do it 'yields the message and a Socket::UDPSource' do msg, src = nil - Thread.new do + thread = Thread.new do SocketSpecs::ServerLoopPortFinder.udp_server_loop('127.0.0.1', 0) do |message, source| msg = message src = source @@ -52,6 +52,8 @@ describe 'Socket.udp_server_loop' do msg.should == 'hello' src.should be_an_instance_of(Socket::UDPSource) + + thread.join end end end diff --git a/spec/ruby/library/weakref/fixtures/classes.rb b/spec/ruby/library/weakref/fixtures/classes.rb index 560c58b041..041afab14d 100644 --- a/spec/ruby/library/weakref/fixtures/classes.rb +++ b/spec/ruby/library/weakref/fixtures/classes.rb @@ -13,9 +13,11 @@ class WeakRefSpec def self.make_dead_weakref weaks = [] weak = nil - 10_000.times do + 1000.times do weaks << make_weakref - GC.start + end + + 1000.times do GC.start break if weak = weaks.find { |w| !w.weakref_alive? } end diff --git a/spec/ruby/optional/capi/ext/kernel_spec.c b/spec/ruby/optional/capi/ext/kernel_spec.c index a1960a2fe7..bbfeb198b7 100644 --- a/spec/ruby/optional/capi/ext/kernel_spec.c +++ b/spec/ruby/optional/capi/ext/kernel_spec.c @@ -191,6 +191,14 @@ static VALUE kernel_spec_rb_protect_yield(VALUE self, VALUE obj, VALUE ary) { return res; } +static VALUE kernel_spec_rb_protect_errinfo(VALUE self, VALUE obj, VALUE ary) { + int status = 0; + VALUE res = rb_protect(rb_yield, obj, &status); + rb_ary_store(ary, 0, INT2NUM(23)); + rb_ary_store(ary, 1, res); + return rb_errinfo(); +} + static VALUE kernel_spec_rb_protect_null_status(VALUE self, VALUE obj) { return rb_protect(rb_yield, obj, NULL); } @@ -345,6 +353,7 @@ void Init_kernel_spec(void) { rb_define_method(cls, "rb_rescue", kernel_spec_rb_rescue, 4); rb_define_method(cls, "rb_rescue2", kernel_spec_rb_rescue2, -1); rb_define_method(cls, "rb_protect_yield", kernel_spec_rb_protect_yield, 2); + rb_define_method(cls, "rb_protect_errinfo", kernel_spec_rb_protect_errinfo, 2); rb_define_method(cls, "rb_protect_null_status", kernel_spec_rb_protect_null_status, 1); rb_define_method(cls, "rb_eval_string_protect", kernel_spec_rb_eval_string_protect, 2); rb_define_method(cls, "rb_catch", kernel_spec_rb_catch, 2); diff --git a/spec/ruby/optional/capi/ext/module_spec.c b/spec/ruby/optional/capi/ext/module_spec.c index 3c8455a942..7475994fa1 100644 --- a/spec/ruby/optional/capi/ext/module_spec.c +++ b/spec/ruby/optional/capi/ext/module_spec.c @@ -9,6 +9,18 @@ static VALUE module_specs_test_method(VALUE self) { return ID2SYM(rb_intern("test_method")); } +static VALUE module_specs_test_method_2required(VALUE self, VALUE arg1, VALUE arg2) { + return ID2SYM(rb_intern("test_method_2required")); +} + +static VALUE module_specs_test_method_c_array(int argc, VALUE *argv, VALUE self) { + return ID2SYM(rb_intern("test_method_c_array")); +} + +static VALUE module_specs_test_method_ruby_array(VALUE self, VALUE args) { + return ID2SYM(rb_intern("test_method_ruby_array")); +} + static VALUE module_specs_const_defined(VALUE self, VALUE klass, VALUE id) { return rb_const_defined(klass, SYM2ID(id)) ? Qtrue : Qfalse; } @@ -76,6 +88,21 @@ static VALUE module_specs_rb_define_method(VALUE self, VALUE cls, VALUE str_name return Qnil; } +static VALUE module_specs_rb_define_method_2required(VALUE self, VALUE cls, VALUE str_name) { + rb_define_method(cls, RSTRING_PTR(str_name), module_specs_test_method_2required, 2); + return Qnil; +} + +static VALUE module_specs_rb_define_method_c_array(VALUE self, VALUE cls, VALUE str_name) { + rb_define_method(cls, RSTRING_PTR(str_name), module_specs_test_method_c_array, -1); + return Qnil; +} + +static VALUE module_specs_rb_define_method_ruby_array(VALUE self, VALUE cls, VALUE str_name) { + rb_define_method(cls, RSTRING_PTR(str_name), module_specs_test_method_ruby_array, -2); + return Qnil; +} + static VALUE module_specs_rb_define_module_function(VALUE self, VALUE cls, VALUE str_name) { rb_define_module_function(cls, RSTRING_PTR(str_name), module_specs_test_method, 0); return Qnil; @@ -132,6 +159,10 @@ void Init_module_spec(void) { module_specs_rb_define_global_function, 1); rb_define_method(cls, "rb_define_method", module_specs_rb_define_method, 2); + rb_define_method(cls, "rb_define_method_2required", module_specs_rb_define_method_2required, 2); + rb_define_method(cls, "rb_define_method_c_array", module_specs_rb_define_method_c_array, 2); + rb_define_method(cls, "rb_define_method_ruby_array", module_specs_rb_define_method_ruby_array, 2); + rb_define_method(cls, "rb_define_module_function", module_specs_rb_define_module_function, 2); diff --git a/spec/ruby/optional/capi/kernel_spec.rb b/spec/ruby/optional/capi/kernel_spec.rb index ab66f7fa9e..758d944da9 100644 --- a/spec/ruby/optional/capi/kernel_spec.rb +++ b/spec/ruby/optional/capi/kernel_spec.rb @@ -312,6 +312,14 @@ describe "C-API Kernel function" do @s.rb_protect_null_status(42) { |x| x + 1 }.should == 43 @s.rb_protect_null_status(42) { |x| raise }.should == nil end + + it "populates errinfo with the captured exception" do + proof = [] + @s.rb_protect_errinfo(77, proof) { |x| raise NameError }.class.should == NameError + proof[0].should == 23 + proof[1].should == nil + end + end describe "rb_eval_string_protect" do diff --git a/spec/ruby/optional/capi/module_spec.rb b/spec/ruby/optional/capi/module_spec.rb index 750d64367d..acf4d1fe48 100644 --- a/spec/ruby/optional/capi/module_spec.rb +++ b/spec/ruby/optional/capi/module_spec.rb @@ -246,12 +246,30 @@ describe "CApiModule" do cls.new.test_method.should == :test_method end - it "returns the correct arity of the method in class" do + it "returns the correct arity when argc of the method in class is 0" do cls = Class.new @m.rb_define_method(cls, "test_method") cls.new.method(:test_method).arity.should == 0 end + it "returns the correct arity when argc of the method in class is -1" do + cls = Class.new + @m.rb_define_method_c_array(cls, "test_method_c_array") + cls.new.method(:test_method_c_array).arity.should == -1 + end + + it "returns the correct arity when argc of the method in class is -2" do + cls = Class.new + @m.rb_define_method_ruby_array(cls, "test_method_ruby_array") + cls.new.method(:test_method_ruby_array).arity.should == -1 + end + + it "returns the correct arity when argc of the method in class is 2" do + cls = Class.new + @m.rb_define_method_2required(cls, "test_method_2required") + cls.new.method(:test_method_2required).arity.should == 2 + end + it "defines a method on a module" do mod = Module.new @m.rb_define_method(mod, "test_method") diff --git a/spec/ruby/shared/kernel/raise.rb b/spec/ruby/shared/kernel/raise.rb index f00a6ef294..765ba0f929 100644 --- a/spec/ruby/shared/kernel/raise.rb +++ b/spec/ruby/shared/kernel/raise.rb @@ -12,6 +12,28 @@ describe :kernel_raise, shared: true do ScratchPad.recorded.should be_nil end + it "accepts an exception that implements to_hash" do + custom_error = Class.new(StandardError) do + def to_hash + {} + end + end + error = custom_error.new + -> { @object.raise(error) }.should raise_error(custom_error) + end + + it "allows the message parameter to be a hash" do + data_error = Class.new(StandardError) do + attr_reader :data + def initialize(data) + @data = data + end + end + -> { @object.raise(data_error, {:data => 42}) }.should raise_error(data_error) do |ex| + ex.data.should == {:data => 42} + end + end + it "raises RuntimeError if no exception class is given" do -> { @object.raise }.should raise_error(RuntimeError, "") end -- cgit v1.2.3