diff options
Diffstat (limited to 'test/ruby/test_proc.rb')
| -rw-r--r-- | test/ruby/test_proc.rb | 1138 |
1 files changed, 1072 insertions, 66 deletions
diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb index da7f07ef6c..959ea87f25 100644 --- a/test/ruby/test_proc.rb +++ b/test/ruby/test_proc.rb @@ -1,10 +1,9 @@ +# frozen_string_literal: false require 'test/unit' -require_relative 'envutil' class TestProc < Test::Unit::TestCase def setup @verbose = $VERBOSE - $VERBOSE = nil end def teardown @@ -53,10 +52,14 @@ class TestProc < Test::Unit::TestCase assert_equal(5, x) end - def assert_arity(n) + def assert_arity(n, &block) meta = class << self; self; end - meta.class_eval {define_method(:foo, Proc.new)} - assert_equal(n, method(:foo).arity) + b = Proc.new(&block) + meta.class_eval { + remove_method(:foo_arity) if method_defined?(:foo_arity) + define_method(:foo_arity, b) + } + assert_equal(n, method(:foo_arity).arity) end def test_arity @@ -77,6 +80,18 @@ class TestProc < Test::Unit::TestCase assert_equal(2, proc{|(x, y), z|[x,y]}.arity) assert_equal(1, proc{|(x, y), z=0|[x,y]}.arity) assert_equal(-4, proc{|x, *y, z, a|}.arity) + assert_equal(0, proc{|**|}.arity) + assert_equal(0, proc{|**o|}.arity) + assert_equal(1, proc{|x, **o|}.arity) + assert_equal(0, proc{|x=0, **o|}.arity) + assert_equal(1, proc{|x, y=0, **o|}.arity) + assert_equal(2, proc{|x, y=0, z, **o|}.arity) + assert_equal(-3, proc{|x, y=0, *z, w, **o|}.arity) + + assert_equal(2, proc{|x, y=0, z, a:1|}.arity) + assert_equal(3, proc{|x, y=0, z, a:|}.arity) + assert_equal(-4, proc{|x, y, *rest, a:, b:, c:|}.arity) + assert_equal(3, proc{|x, y=0, z, a:, **o|}.arity) assert_equal(0, lambda{}.arity) assert_equal(0, lambda{||}.arity) @@ -95,6 +110,13 @@ class TestProc < Test::Unit::TestCase assert_equal(2, lambda{|(x, y), z|[x,y]}.arity) assert_equal(-2, lambda{|(x, y), z=0|[x,y]}.arity) assert_equal(-4, lambda{|x, *y, z, a|}.arity) + assert_equal(-1, lambda{|**|}.arity) + assert_equal(-1, lambda{|**o|}.arity) + assert_equal(-2, lambda{|x, **o|}.arity) + assert_equal(-1, lambda{|x=0, **o|}.arity) + assert_equal(-2, lambda{|x, y=0, **o|}.arity) + assert_equal(-3, lambda{|x, y=0, z, **o|}.arity) + assert_equal(-3, lambda{|x, y=0, *z, w, **o|}.arity) assert_arity(0) {} assert_arity(0) {||} @@ -104,12 +126,24 @@ class TestProc < Test::Unit::TestCase assert_arity(-3) {|x, *y, z|} assert_arity(-1) {|*x|} assert_arity(-1) {|*|} + assert_arity(-1) {|**o|} + assert_arity(-1) {|**|} + assert_arity(-2) {|x, *y, **|} + assert_arity(-3) {|x, *y, z, **|} end def m(x) lambda { x } end + def m_nest0(&block) + block + end + + def m_nest(&block) + [m_nest0(&block), m_nest0(&block)] + end + def test_eq a = m(1) b = m(2) @@ -121,42 +155,74 @@ class TestProc < Test::Unit::TestCase a = lambda {|x| lambda {} }.call(1) b = lambda {} assert_not_equal(a, b, "[ruby-dev:22601]") + + assert_equal(*m_nest{}, "[ruby-core:84583] Feature #14627") end - def test_block_par - assert_equal(10, Proc.new{|&b| b.call(10)}.call {|x| x}) - assert_equal(12, Proc.new{|a,&b| b.call(a)}.call(12) {|x| x}) + def test_hash_equal + # iseq backed proc + p1 = proc {} + p2 = p1.dup + + assert_equal p1.hash, p2.hash + + # ifunc backed proc + p1 = {}.to_proc + p2 = p1.dup + + assert_equal p1.hash, p2.hash + + # symbol backed proc + p1 = :hello.to_proc + p2 = :hello.to_proc + + assert_equal p1.hash, p2.hash end - def test_safe - safe = $SAFE - c = Class.new - x = c.new + def test_hash_uniqueness + def self.capture(&block) + block + end - p = proc { - $SAFE += 1 - proc {$SAFE} - }.call - assert_equal(safe, $SAFE) - assert_equal(safe + 1, p.call) - assert_equal(safe, $SAFE) + procs = Array.new(1000){capture{:foo }} + assert_operator(procs.map(&:hash).uniq.size, :>=, 500) - c.class_eval {define_method(:safe, p)} - assert_equal(safe, x.safe) - assert_equal(safe, x.method(:safe).call) - assert_equal(safe, x.method(:safe).to_proc.call) + # iseq backed proc + unique_hashes = 1000.times.map { proc {}.hash }.uniq + assert_operator(unique_hashes.size, :>=, 500) - p = proc {$SAFE += 1} - assert_equal(safe + 1, p.call) - assert_equal(safe, $SAFE) + # ifunc backed proc + unique_hashes = 1000.times.map { {}.to_proc.hash }.uniq + assert_operator(unique_hashes.size, :>=, 500) - c.class_eval {define_method(:inc, p)} - assert_equal(safe + 1, proc {x.inc; $SAFE}.call) - assert_equal(safe, $SAFE) - assert_equal(safe + 1, proc {x.method(:inc).call; $SAFE}.call) - assert_equal(safe, $SAFE) - assert_equal(safe + 1, proc {x.method(:inc).to_proc.call; $SAFE}.call) - assert_equal(safe, $SAFE) + # symbol backed proc + unique_hashes = 1000.times.map { |i| :"test#{i}".to_proc.hash }.uniq + assert_operator(unique_hashes.size, :>=, 500) + end + + def test_hash_does_not_change_after_compaction + omit "compaction is not supported on this platform" unless GC.respond_to?(:compact) + + # [Bug #20853] + [ + "proc {}", # iseq backed proc + "{}.to_proc", # ifunc backed proc + ":hello.to_proc", # symbol backed proc + ].each do |proc| + assert_separately([], <<~RUBY) + p1 = #{proc} + hash = p1.hash + + GC.verify_compaction_references(expand_heap: true, toward: :empty) + + assert_equal(hash, p1.hash, "proc is `#{proc}`") + RUBY + end + end + + def test_block_par + assert_equal(10, Proc.new{|&b| b.call(10)}.call {|x| x}) + assert_equal(12, Proc.new{|a,&b| b.call(a)}.call(12) {|x| x}) end def m2 @@ -182,22 +248,35 @@ class TestProc < Test::Unit::TestCase b = b.binding assert_instance_of(Binding, b, '[ruby-core:25589]') bug10432 = '[ruby-core:65919] [Bug #10432]' - assert_same(self, b.eval("self"), bug10432) + assert_same(self, b.receiver, bug10432) + assert_not_send [b, :local_variable_defined?, :value] + assert_raise(NameError) { + b.local_variable_get(:value) + } + assert_equal 42, b.local_variable_set(:value, 42) + assert_send [b, :local_variable_defined?, :value] + assert_equal 42, b.local_variable_get(:value) end def test_block_given_method + verbose_bak, $VERBOSE = $VERBOSE, nil m = method(:m_block_given?) assert(!m.call, "without block") assert(m.call {}, "with block") assert(!m.call, "without block second") + ensure + $VERBOSE = verbose_bak end def test_block_given_method_to_proc + verbose_bak, $VERBOSE = $VERBOSE, nil bug8341 = '[Bug #8341]' m = method(:m_block_given?).to_proc assert(!m.call, "#{bug8341} without block") assert(m.call {}, "#{bug8341} with block") assert(!m.call, "#{bug8341} without block second") + ensure + $VERBOSE = verbose_bak end def test_block_persist_between_calls @@ -216,48 +295,82 @@ class TestProc < Test::Unit::TestCase assert_equal([false, false], m.call, "#{bug8341} nested without block") end - def test_curry + def test_curry_proc b = proc {|x, y, z| (x||0) + (y||0) + (z||0) } assert_equal(6, b.curry[1][2][3]) assert_equal(6, b.curry[1, 2][3, 4]) assert_equal(6, b.curry(5)[1][2][3][4][5]) assert_equal(6, b.curry(5)[1, 2][3, 4][5]) assert_equal(1, b.curry(1)[1]) + end + def test_curry_proc_splat b = proc {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) } assert_equal(6, b.curry[1][2][3]) assert_equal(10, b.curry[1, 2][3, 4]) assert_equal(15, b.curry(5)[1][2][3][4][5]) assert_equal(15, b.curry(5)[1, 2][3, 4][5]) assert_equal(1, b.curry(1)[1]) + end + def test_curry_lambda b = lambda {|x, y, z| (x||0) + (y||0) + (z||0) } assert_equal(6, b.curry[1][2][3]) assert_raise(ArgumentError) { b.curry[1, 2][3, 4] } assert_raise(ArgumentError) { b.curry(5) } assert_raise(ArgumentError) { b.curry(1) } + end + def test_curry_lambda_splat b = lambda {|x, y, z, *w| (x||0) + (y||0) + (z||0) + w.inject(0, &:+) } assert_equal(6, b.curry[1][2][3]) assert_equal(10, b.curry[1, 2][3, 4]) assert_equal(15, b.curry(5)[1][2][3][4][5]) assert_equal(15, b.curry(5)[1, 2][3, 4][5]) assert_raise(ArgumentError) { b.curry(1) } + end + def test_curry_no_arguments b = proc { :foo } assert_equal(:foo, b.curry[]) + end + def test_curry_given_blocks b = lambda {|x, y, &blk| blk.call(x + y) }.curry - b = b.call(2) { raise } + b = assert_warning(/given block not used/) {b.call(2) { raise }} b = b.call(3) {|x| x + 4 } assert_equal(9, b) + end + def test_lambda? l = proc {} assert_equal(false, l.lambda?) assert_equal(false, l.curry.lambda?, '[ruby-core:24127]') + assert_equal(false, proc(&l).lambda?) + assert_equal(false, Proc.new(&l).lambda?) l = lambda {} assert_equal(true, l.lambda?) assert_equal(true, l.curry.lambda?, '[ruby-core:24127]') + assert_equal(true, proc(&l).lambda?) + assert_equal(true, lambda(&l).lambda?) + assert_equal(true, Proc.new(&l).lambda?) + end + + def helper_test_warn_lambda_with_passed_block &b + lambda(&b) + end + + def test_lambda_warning_pass_proc + assert_raise(ArgumentError) do + b = proc{} + lambda(&b) + end + end + + def test_lambda_warning_pass_block + assert_raise(ArgumentError) do + helper_test_warn_lambda_with_passed_block{} + end end def test_curry_ski_fib @@ -292,16 +405,11 @@ class TestProc < Test::Unit::TestCase assert_equal(fib, [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]) end - def test_curry_from_knownbug + def test_curry_passed_block a = lambda {|x, y, &b| b } b = a.curry[1] - assert_equal(:ok, - if b.call(2){} == nil - :ng - else - :ok - end, 'moved from btest/knownbug, [ruby-core:15551]') + assert_not_nil(b.call(2){}, '[ruby-core:15551]: passed block to curried block') end def test_curry_instance_exec @@ -321,6 +429,7 @@ class TestProc < Test::Unit::TestCase end def test_dup_clone + # iseq backed proc b = proc {|x| x + "bar" } class << b; attr_accessor :foo; end @@ -333,6 +442,50 @@ class TestProc < Test::Unit::TestCase assert_equal("foobar", bc.call("foo")) bc.foo = :foo assert_equal(:foo, bc.foo) + + # ifunc backed proc + b = {foo: "bar"}.to_proc + + bd = b.dup + assert_equal("bar", bd.call(:foo)) + + bc = b.clone + assert_equal("bar", bc.call(:foo)) + + # symbol backed proc + b = :to_s.to_proc + + bd = b.dup + assert_equal("testing", bd.call(:testing)) + + bc = b.clone + assert_equal("testing", bc.call(:testing)) + end + + def test_dup_subclass + c1 = Class.new(Proc) + assert_equal c1, c1.new{}.dup.class, '[Bug #17545]' + c1 = Class.new(Proc) {def initialize_dup(*) throw :initialize_dup; end} + assert_throw(:initialize_dup) {c1.new{}.dup} + end + + def test_dup_ifunc_proc_bug_20950 + assert_normal_exit(<<~RUBY, "[Bug #20950]") + p = { a: 1 }.to_proc + 100.times do + p = p.dup + GC.start + p.call + rescue ArgumentError + end + RUBY + end + + def test_clone_subclass + c1 = Class.new(Proc) + assert_equal c1, c1.new{}.clone.class, '[Bug #17545]' + c1 = Class.new(Proc) {def initialize_clone(*) throw :initialize_clone; end} + assert_throw(:initialize_clone) {c1.new{}.clone} end def test_binding @@ -354,9 +507,23 @@ class TestProc < Test::Unit::TestCase assert_equal([1, 2, 3], b.eval("[x, y, z]")) end + def test_binding_source_location + b, expected_location = binding, [__FILE__, __LINE__] + assert_equal(expected_location, b.source_location) + + file, lineno = method(:source_location_test).to_proc.binding.source_location + assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) + assert_equal(@@line_of_source_location_test[0], lineno, 'Bug #2427') + end + + def test_binding_error_unless_ruby_frame + define_singleton_method :binding_from_c!, method(:binding).to_proc >> ->(bndg) {bndg} + assert_raise(RuntimeError) { binding_from_c! } + end + def test_proc_lambda assert_raise(ArgumentError) { proc } - assert_raise(ArgumentError) { lambda } + assert_raise(ArgumentError) { assert_warn(/deprecated/) {lambda} } o = Object.new def o.foo @@ -364,14 +531,18 @@ class TestProc < Test::Unit::TestCase 1.times { b = lambda } b end - assert_equal(:foo, o.foo { :foo }.call) + assert_raise(ArgumentError) do + assert_deprecated_warning {o.foo { :foo }}.call + end - def o.foo(&b) + def o.bar(&b) b = nil 1.times { b = lambda } b end - assert_equal(:foo, o.foo { :foo }.call) + assert_raise(ArgumentError) do + assert_deprecated_warning {o.bar { :foo }}.call + end end def test_arity2 @@ -387,6 +558,7 @@ class TestProc < Test::Unit::TestCase t = Thread.new { sleep } assert_raise(ThreadError) { t.instance_eval { initialize { } } } t.kill + t.join end def test_to_proc @@ -402,7 +574,7 @@ class TestProc < Test::Unit::TestCase assert_equal(:noreason, exc.reason) end - def test_binding2 + def test_curry_binding assert_raise(ArgumentError) { proc {}.curry.binding } end @@ -549,7 +721,7 @@ class TestProc < Test::Unit::TestCase assert_equal [1, 2, 3], pr.call([1,2,3,4,5,6]) end - def test_proc_args_opt_signle + def test_proc_args_opt_single bug7621 = '[ruby-dev:46801]' pr = proc {|a=:a| a @@ -756,6 +928,115 @@ class TestProc < Test::Unit::TestCase assert_equal [[1, 2], Proc, :x], (pr.call(1, 2){|x| x}) end + def test_proc_args_single_kw_no_autosplat + pr = proc {|c, a: 1| [c, a] } + assert_equal [nil, 1], pr.call() + assert_equal [1, 1], pr.call(1) + assert_equal [[1], 1], pr.call([1]) + assert_equal [1, 1], pr.call(1,2) + assert_equal [[1, 2], 1], pr.call([1,2]) + + assert_equal [nil, 3], pr.call(a: 3) + assert_equal [1, 3], pr.call(1, a: 3) + assert_equal [[1], 3], pr.call([1], a: 3) + assert_equal [1, 3], pr.call(1,2, a: 3) + assert_equal [[1, 2], 3], pr.call([1,2], a: 3) + end + + def test_proc_args_single_kwsplat_no_autosplat + pr = proc {|c, **kw| [c, kw] } + assert_equal [nil, {}], pr.call() + assert_equal [1, {}], pr.call(1) + assert_equal [[1], {}], pr.call([1]) + assert_equal [1, {}], pr.call(1,2) + assert_equal [[1, 2], {}], pr.call([1,2]) + + assert_equal [nil, {a: 3}], pr.call(a: 3) + assert_equal [1, {a: 3}], pr.call(1, a: 3) + assert_equal [[1], {a: 3}], pr.call([1], a: 3) + assert_equal [1, {a: 3}], pr.call(1,2, a: 3) + assert_equal [[1, 2], {a: 3}], pr.call([1,2], a: 3) + end + + def test_proc_args_multiple_kw_autosplat + pr = proc {|c, b, a: 1| [c, b, a] } + assert_equal [1, 2, 1], pr.call([1,2]) + + pr = proc {|c=nil, b=nil, a: 1| [c, b, a] } + assert_equal [nil, nil, 1], pr.call([]) + assert_equal [1, nil, 1], pr.call([1]) + assert_equal [1, 2, 1], pr.call([1,2]) + + pr = proc {|c, b=nil, a: 1| [c, b, a] } + assert_equal [1, nil, 1], pr.call([1]) + assert_equal [1, 2, 1], pr.call([1,2]) + + pr = proc {|c=nil, b, a: 1| [c, b, a] } + assert_equal [nil, 1, 1], pr.call([1]) + assert_equal [1, 2, 1], pr.call([1,2]) + + pr = proc {|c, *b, a: 1| [c, b, a] } + assert_equal [1, [], 1], pr.call([1]) + assert_equal [1, [2], 1], pr.call([1,2]) + + pr = proc {|*c, b, a: 1| [c, b, a] } + assert_equal [[], 1, 1], pr.call([1]) + assert_equal [[1], 2, 1], pr.call([1,2]) + end + + def test_proc_args_multiple_kwsplat_autosplat + pr = proc {|c, b, **kw| [c, b, kw] } + assert_equal [1, 2, {}], pr.call([1,2]) + + pr = proc {|c=nil, b=nil, **kw| [c, b, kw] } + assert_equal [nil, nil, {}], pr.call([]) + assert_equal [1, nil, {}], pr.call([1]) + assert_equal [1, 2, {}], pr.call([1,2]) + + pr = proc {|c, b=nil, **kw| [c, b, kw] } + assert_equal [1, nil, {}], pr.call([1]) + assert_equal [1, 2, {}], pr.call([1,2]) + + pr = proc {|c=nil, b, **kw| [c, b, kw] } + assert_equal [nil, 1, {}], pr.call([1]) + assert_equal [1, 2, {}], pr.call([1,2]) + + pr = proc {|c, *b, **kw| [c, b, kw] } + assert_equal [1, [], {}], pr.call([1]) + assert_equal [1, [2], {}], pr.call([1,2]) + + pr = proc {|*c, b, **kw| [c, b, kw] } + assert_equal [[], 1, {}], pr.call([1]) + assert_equal [[1], 2, {}], pr.call([1,2]) + end + + def test_proc_args_only_rest + pr = proc {|*c| c } + assert_equal [], pr.call() + assert_equal [1], pr.call(1) + assert_equal [[1]], pr.call([1]) + assert_equal [1, 2], pr.call(1,2) + assert_equal [[1, 2]], pr.call([1,2]) + end + + def test_proc_args_rest_kw + pr = proc {|*c, a: 1| [c, a] } + assert_equal [[], 1], pr.call() + assert_equal [[1], 1], pr.call(1) + assert_equal [[[1]], 1], pr.call([1]) + assert_equal [[1, 2], 1], pr.call(1,2) + assert_equal [[[1, 2]], 1], pr.call([1,2]) + end + + def test_proc_args_rest_kwsplat + pr = proc {|*c, **kw| [c, kw] } + assert_equal [[], {}], pr.call() + assert_equal [[1], {}], pr.call(1) + assert_equal [[[1]], {}], pr.call([1]) + assert_equal [[1, 2], {}], pr.call(1,2) + assert_equal [[[1, 2]], {}], pr.call([1,2]) + end + def test_proc_args_pos_rest_post_block pr = proc {|a,b,*c,d,e,&f| [a, b, c, d, e, f.class, f&&f.call(:x)] @@ -1059,6 +1340,36 @@ class TestProc < Test::Unit::TestCase assert_equal([1,2,[3],4,5], r, "[ruby-core:19485]") end + def test_proc_autosplat + def self.a(arg, kw) + yield arg + yield arg, **kw + yield arg, kw + end + + arr = [] + a([1,2,3], {}) do |arg1, arg2=0| + arr << [arg1, arg2] + end + assert_equal([[1, 2], [[1, 2, 3], 0], [[1, 2, 3], {}]], arr) + + arr = [] + a([1,2,3], a: 1) do |arg1, arg2=0| + arr << [arg1, arg2] + end + assert_equal([[1, 2], [[1, 2, 3], {a: 1}], [[1, 2, 3], {a: 1}]], arr) + end + + def test_proc_single_arg_with_keywords_accepted_and_yielded + def self.a + yield [], **{a: 1} + end + res = a do |arg, **opts| + [arg, opts] + end + assert_equal([[], {a: 1}], res) + end + def test_parameters assert_equal([], proc {}.parameters) assert_equal([], proc {||}.parameters) @@ -1071,11 +1382,68 @@ class TestProc < Test::Unit::TestCase assert_equal([[:opt, :a], [:rest, :b], [:opt, :c]], proc {|a, *b, c|}.parameters) assert_equal([[:opt, :a], [:rest, :b], [:opt, :c], [:block, :d]], proc {|a, *b, c, &d|}.parameters) assert_equal([[:opt, :a], [:opt, :b], [:rest, :c], [:opt, :d], [:block, :e]], proc {|a, b=:b, *c, d, &e|}.parameters) - assert_equal([[:opt, nil], [:block, :b]], proc {|(a), &b|a}.parameters) + assert_equal([[:opt], [:block, :b]], proc {|(a), &b|a}.parameters) + assert_equal([[:opt], [:rest, :_], [:opt]], proc {|(a_), *_, (b_)|}.parameters) assert_equal([[:opt, :a], [:opt, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:opt, :f], [:opt, :g], [:block, :h]], proc {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters) assert_equal([[:req]], method(:putc).parameters) assert_equal([[:rest]], method(:p).parameters) + + pr = eval("proc{|"+"(_),"*30+"|}") + assert_empty(pr.parameters.map{|_,n|n}.compact) + + assert_equal([[:opt]], proc { it }.parameters) + end + + def test_proc_autosplat_with_multiple_args_with_ruby2_keywords_splat_bug_19759 + def self.yielder_ab(splat) + yield([:a, :b], *splat) + end + + res = yielder_ab([[:aa, :bb], Hash.ruby2_keywords_hash({k: :k})]) do |a, b, k:| + [a, b, k] + end + assert_equal([[:a, :b], [:aa, :bb], :k], res) + + def self.yielder(splat) + yield(*splat) + end + res = yielder([ [:a, :b] ]){|a, b, **| [a, b]} + assert_equal([:a, :b], res) + + res = yielder([ [:a, :b], Hash.ruby2_keywords_hash({}) ]){|a, b, **| [a, b]} + assert_equal([[:a, :b], nil], res) + + res = yielder([ [:a, :b], Hash.ruby2_keywords_hash({c: 1}) ]){|a, b, **| [a, b]} + assert_equal([[:a, :b], nil], res) + + res = yielder([ [:a, :b], Hash.ruby2_keywords_hash({}) ]){|a, b, **nil| [a, b]} + assert_equal([[:a, :b], nil], res) + end + + def test_parameters_lambda + assert_equal([], proc {}.parameters(lambda: true)) + assert_equal([], proc {||}.parameters(lambda: true)) + assert_equal([[:req, :a]], proc {|a|}.parameters(lambda: true)) + assert_equal([[:req, :a], [:req, :b]], proc {|a, b|}.parameters(lambda: true)) + assert_equal([[:opt, :a], [:block, :b]], proc {|a=:a, &b|}.parameters(lambda: true)) + assert_equal([[:req, :a], [:opt, :b]], proc {|a, b=:b|}.parameters(lambda: true)) + assert_equal([[:rest, :a]], proc {|*a|}.parameters(lambda: true)) + assert_equal([[:req, :a], [:rest, :b], [:block, :c]], proc {|a, *b, &c|}.parameters(lambda: true)) + assert_equal([[:req, :a], [:rest, :b], [:req, :c]], proc {|a, *b, c|}.parameters(lambda: true)) + assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], proc {|a, *b, c, &d|}.parameters(lambda: true)) + assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], proc {|a, b=:b, *c, d, &e|}.parameters(lambda: true)) + assert_equal([[:req], [:block, :b]], proc {|(a), &b|a}.parameters(lambda: true)) + assert_equal([[:req, :a], [:req, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:req, :f], [:req, :g], [:block, :h]], proc {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters(lambda: true)) + + pr = eval("proc{|"+"(_),"*30+"|}") + assert_empty(pr.parameters(lambda: true).map{|_,n|n}.compact) + + assert_equal([[:opt, :a]], lambda {|a|}.parameters(lambda: false)) + assert_equal([[:opt, :a], [:opt, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:opt, :f], [:opt, :g], [:block, :h]], lambda {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters(lambda: false)) + + assert_equal([[:req]], proc { it }.parameters(lambda: true)) + assert_equal([[:opt]], lambda { it }.parameters(lambda: false)) end def pm0() end @@ -1089,6 +1457,13 @@ class TestProc < Test::Unit::TestCase def pmo6(a, *b, c, &d) end def pmo7(a, b = :b, *c, d, &e) end def pma1((a), &b) a; end + def pmk1(**) end + def pmk2(**o) nil && o end + def pmk3(a, **o) nil && o end + def pmk4(a = nil, **o) nil && o end + def pmk5(a, b = nil, **o) nil && o end + def pmk6(a, b = nil, c, **o) nil && o end + def pmk7(a, b = nil, *c, d, **o) nil && o end def test_bound_parameters @@ -1103,30 +1478,40 @@ class TestProc < Test::Unit::TestCase assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:pmo6).to_proc.parameters) assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], method(:pmo7).to_proc.parameters) assert_equal([[:req], [:block, :b]], method(:pma1).to_proc.parameters) - - assert_equal([], "".method(:upcase).to_proc.parameters) + assert_equal([[:keyrest, :**]], method(:pmk1).to_proc.parameters) + assert_equal([[:keyrest, :o]], method(:pmk2).to_proc.parameters) + assert_equal([[:req, :a], [:keyrest, :o]], method(:pmk3).to_proc.parameters) + assert_equal([[:opt, :a], [:keyrest, :o]], method(:pmk4).to_proc.parameters) + assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], method(:pmk5).to_proc.parameters) + assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], method(:pmk6).to_proc.parameters) + assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], method(:pmk7).to_proc.parameters) + + assert_equal([], "".method(:empty?).to_proc.parameters) assert_equal([[:rest]], "".method(:gsub).to_proc.parameters) assert_equal([[:rest]], proc {}.curry.parameters) end def test_to_s - assert_match(/^#<Proc:0x\h+@#{ Regexp.quote(__FILE__) }:\d+>$/, proc {}.to_s) - assert_match(/^#<Proc:0x\h+@#{ Regexp.quote(__FILE__) }:\d+ \(lambda\)>$/, lambda {}.to_s) + assert_match(/^#<Proc:0x\h+ #{ Regexp.quote(__FILE__) }:\d+>$/, proc {}.to_s) + assert_match(/^#<Proc:0x\h+ #{ Regexp.quote(__FILE__) }:\d+ \(lambda\)>$/, lambda {}.to_s) assert_match(/^#<Proc:0x\h+ \(lambda\)>$/, method(:p).to_proc.to_s) - x = proc {} - x.taint - assert(x.to_s.tainted?) + name = "Proc\u{1f37b}" + assert_include(EnvUtil.labeled_class(name, Proc).new {}.to_s, name) end - @@line_of_source_location_test = __LINE__ + 1 + @@line_of_source_location_test = [__LINE__ + 1, 2, __LINE__ + 3, 5] def source_location_test a=1, b=2 end def test_source_location - file, lineno = method(:source_location_test).source_location + file, *loc = method(:source_location_test).source_location assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(@@line_of_source_location_test, lineno, 'Bug #2427') + assert_equal(@@line_of_source_location_test, loc, 'Bug #2427') + + file, *loc = self.class.instance_method(:source_location_test).source_location + assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) + assert_equal(@@line_of_source_location_test, loc, 'Bug #2427') end @@line_of_attr_reader_source_location_test = __LINE__ + 3 @@ -1159,13 +1544,13 @@ class TestProc < Test::Unit::TestCase end def test_block_source_location - exp_lineno = __LINE__ + 3 - file, lineno = block_source_location_test(1, + exp_loc = [__LINE__ + 3, 49, __LINE__ + 4, 49] + file, *loc = block_source_location_test(1, 2, 3) do end assert_match(/^#{ Regexp.quote(__FILE__) }$/, file) - assert_equal(exp_lineno, lineno) + assert_equal(exp_loc, loc) end def test_splat_without_respond_to @@ -1178,7 +1563,10 @@ class TestProc < Test::Unit::TestCase def test_curry_with_trace # bug3751 = '[ruby-core:31871]' set_trace_func(proc {}) - test_curry + methods.grep(/\Atest_curry/) do |test| + next if test == __method__ + __send__(test) + end ensure set_trace_func(nil) end @@ -1201,13 +1589,631 @@ class TestProc < Test::Unit::TestCase } end - def test_overriden_lambda + def test_overridden_lambda bug8345 = '[ruby-core:54687] [Bug #8345]' assert_normal_exit('def lambda; end; method(:puts).to_proc', bug8345) end - def test_overriden_proc + def test_overridden_proc bug8345 = '[ruby-core:54688] [Bug #8345]' assert_normal_exit('def proc; end; ->{}.curry', bug8345) end + + def get_binding if: 1, case: 2, when: 3, begin: 4, end: 5 + a ||= 0 + binding + end + + def test_local_variables + b = get_binding + assert_equal(%i'if case when begin end a', b.local_variables) + a = tap {|;x, y| x = y = x; break binding.local_variables} + assert_equal(%i[a b x y], a.sort) + end + + def test_local_variables_nested + b = tap {break binding} + assert_equal(%i[b], b.local_variables, '[ruby-dev:48351] [Bug #10001]') + end + + def local_variables_of(bind) + this_should_not_be_in_bind = this_should_not_be_in_bind = 2 + bind.local_variables + end + + def test_local_variables_in_other_context + feature8773 = '[Feature #8773]' + assert_equal([:feature8773], local_variables_of(binding), feature8773) + end + + def test_local_variable_get + b = get_binding + assert_equal(0, b.local_variable_get(:a)) + assert_raise(NameError){ b.local_variable_get(:b) } + + # access keyword named local variables + assert_equal(1, b.local_variable_get(:if)) + assert_equal(2, b.local_variable_get(:case)) + assert_equal(3, b.local_variable_get(:when)) + assert_equal(4, b.local_variable_get(:begin)) + assert_equal(5, b.local_variable_get(:end)) + + assert_raise_with_message(NameError, /local variable \Wdefault\W/) { + binding.local_variable_get(:default) + } + end + + def test_local_variable_set + b = get_binding + b.local_variable_set(:a, 10) + b.local_variable_set(:b, 20) + assert_equal(10, b.local_variable_get(:a)) + assert_equal(20, b.local_variable_get(:b)) + assert_equal(10, b.eval("a")) + assert_equal(20, b.eval("b")) + end + + def test_numparam_is_not_local_variables + "foo".tap do + _9 and flunk + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:_9) } + assert_raise(NameError) { binding.local_variable_set(:_9, 1) } + assert_raise(NameError) { binding.local_variable_defined?(:_9) } + "bar".tap do + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:_9) } + assert_raise(NameError) { binding.local_variable_set(:_9, 1) } + assert_raise(NameError) { binding.local_variable_defined?(:_9) } + end + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:_9) } + assert_raise(NameError) { binding.local_variable_set(:_9, 1) } + assert_raise(NameError) { binding.local_variable_defined?(:_9) } + end + + "foo".tap do + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:_9) } + assert_raise(NameError) { binding.local_variable_set(:_9, 1) } + assert_raise(NameError) { binding.local_variable_defined?(:_9) } + "bar".tap do + _9 and flunk + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:_9) } + assert_raise(NameError) { binding.local_variable_set(:_9, 1) } + assert_raise(NameError) { binding.local_variable_defined?(:_9) } + end + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:_9) } + assert_raise(NameError) { binding.local_variable_set(:_9, 1) } + assert_raise(NameError) { binding.local_variable_defined?(:_9) } + end + end + + def test_implicit_parameters_for_numparams + x = x = 1 + assert_raise(NameError) { binding.implicit_parameter_get(:x) } + assert_raise(NameError) { binding.implicit_parameter_defined?(:x) } + + "foo".tap do + _5 and flunk + assert_equal([:_1, :_2, :_3, :_4, :_5], binding.implicit_parameters) + assert_equal("foo", binding.implicit_parameter_get(:_1)) + assert_equal(nil, binding.implicit_parameter_get(:_5)) + assert_raise(NameError) { binding.implicit_parameter_get(:_6) } + assert_raise(NameError) { binding.implicit_parameter_get(:it) } + assert_equal(true, binding.implicit_parameter_defined?(:_1)) + assert_equal(true, binding.implicit_parameter_defined?(:_5)) + assert_equal(false, binding.implicit_parameter_defined?(:_6)) + assert_equal(false, binding.implicit_parameter_defined?(:it)) + "bar".tap do + assert_equal([], binding.implicit_parameters) + assert_raise(NameError) { binding.implicit_parameter_get(:_1) } + assert_raise(NameError) { binding.implicit_parameter_get(:_6) } + assert_raise(NameError) { binding.implicit_parameter_get(:it) } + assert_equal(false, binding.implicit_parameter_defined?(:_1)) + assert_equal(false, binding.implicit_parameter_defined?(:_6)) + assert_equal(false, binding.implicit_parameter_defined?(:it)) + end + assert_equal([:_1, :_2, :_3, :_4, :_5], binding.implicit_parameters) + assert_equal("foo", binding.implicit_parameter_get(:_1)) + assert_equal(nil, binding.implicit_parameter_get(:_5)) + assert_raise(NameError) { binding.implicit_parameter_get(:_6) } + assert_raise(NameError) { binding.implicit_parameter_get(:it) } + assert_equal(true, binding.implicit_parameter_defined?(:_1)) + assert_equal(true, binding.implicit_parameter_defined?(:_5)) + assert_equal(false, binding.implicit_parameter_defined?(:_6)) + assert_equal(false, binding.implicit_parameter_defined?(:it)) + end + + "foo".tap do + assert_equal([], binding.implicit_parameters) + assert_raise(NameError) { binding.implicit_parameter_get(:_1) } + assert_raise(NameError) { binding.implicit_parameter_get(:_6) } + assert_equal(false, binding.implicit_parameter_defined?(:_1)) + assert_equal(false, binding.implicit_parameter_defined?(:_6)) + assert_equal(false, binding.implicit_parameter_defined?(:it)) + "bar".tap do + _5 and flunk + assert_equal([:_1, :_2, :_3, :_4, :_5], binding.implicit_parameters) + assert_equal("bar", binding.implicit_parameter_get(:_1)) + assert_equal(nil, binding.implicit_parameter_get(:_5)) + assert_raise(NameError) { binding.implicit_parameter_get(:_6) } + assert_raise(NameError) { binding.implicit_parameter_get(:it) } + assert_equal(true, binding.implicit_parameter_defined?(:_1)) + assert_equal(true, binding.implicit_parameter_defined?(:_5)) + assert_equal(false, binding.implicit_parameter_defined?(:_6)) + assert_equal(false, binding.implicit_parameter_defined?(:it)) + end + assert_equal([], binding.implicit_parameters) + assert_raise(NameError) { binding.implicit_parameter_get(:_1) } + assert_raise(NameError) { binding.implicit_parameter_get(:_6) } + assert_equal(false, binding.implicit_parameter_defined?(:_1)) + assert_equal(false, binding.implicit_parameter_defined?(:_6)) + assert_equal(false, binding.implicit_parameter_defined?(:it)) + end + end + + def test_it_is_not_local_variable + "foo".tap do + it + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:it) } + assert_equal(false, binding.local_variable_defined?(:it)) + "bar".tap do + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:it) } + assert_equal(false, binding.local_variable_defined?(:it)) + end + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:it) } + assert_equal(false, binding.local_variable_defined?(:it)) + "bar".tap do + it + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:it) } + assert_equal(false, binding.local_variable_defined?(:it)) + end + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:it) } + assert_equal(false, binding.local_variable_defined?(:it)) + end + + "foo".tap do + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:it) } + assert_equal(false, binding.local_variable_defined?(:it)) + "bar".tap do + it + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:it) } + assert_equal(false, binding.local_variable_defined?(:it)) + end + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:it) } + assert_equal(false, binding.local_variable_defined?(:it)) + end + end + + def test_implicit_parameters_for_it + "foo".tap do + it or flunk + assert_equal([:it], binding.implicit_parameters) + assert_equal("foo", binding.implicit_parameter_get(:it)) + assert_raise(NameError) { binding.implicit_parameter_get(:_1) } + assert_equal(true, binding.implicit_parameter_defined?(:it)) + assert_equal(false, binding.implicit_parameter_defined?(:_1)) + "bar".tap do + assert_equal([], binding.implicit_parameters) + assert_raise(NameError) { binding.implicit_parameter_get(:it) } + assert_raise(NameError) { binding.implicit_parameter_get(:_1) } + assert_equal(false, binding.implicit_parameter_defined?(:it)) + assert_equal(false, binding.implicit_parameter_defined?(:_1)) + end + assert_equal([:it], binding.implicit_parameters) + assert_equal("foo", binding.implicit_parameter_get(:it)) + assert_raise(NameError) { binding.implicit_parameter_get(:_1) } + assert_equal(true, binding.implicit_parameter_defined?(:it)) + assert_equal(false, binding.implicit_parameter_defined?(:_1)) + end + + "foo".tap do + assert_equal([], binding.implicit_parameters) + assert_raise(NameError) { binding.implicit_parameter_get(:it) } + assert_raise(NameError) { binding.implicit_parameter_get(:_1) } + assert_equal(false, binding.implicit_parameter_defined?(:it)) + assert_equal(false, binding.implicit_parameter_defined?(:_1)) + "bar".tap do + it or flunk + assert_equal([:it], binding.implicit_parameters) + assert_equal("bar", binding.implicit_parameter_get(:it)) + assert_raise(NameError) { binding.implicit_parameter_get(:_1) } + assert_equal(true, binding.implicit_parameter_defined?(:it)) + assert_equal(false, binding.implicit_parameter_defined?(:_1)) + end + assert_equal([], binding.implicit_parameters) + assert_raise(NameError) { binding.implicit_parameter_get(:it) } + assert_raise(NameError) { binding.implicit_parameter_get(:_1) } + assert_equal(false, binding.implicit_parameter_defined?(:it)) + assert_equal(false, binding.implicit_parameter_defined?(:_1)) + end + end + + def test_implicit_parameters_for_it_complex + "foo".tap do + it = it = "bar" + + assert_equal([], binding.implicit_parameters) + assert_raise(NameError) { binding.implicit_parameter_get(:it) } + assert_equal(false, binding.implicit_parameter_defined?(:it)) + + assert_equal([:it], binding.local_variables) + assert_equal("bar", binding.local_variable_get(:it)) + assert_equal(true, binding.local_variable_defined?(:it)) + end + + "foo".tap do + it or flunk + + assert_equal([:it], binding.implicit_parameters) + assert_equal("foo", binding.implicit_parameter_get(:it)) + assert_equal(true, binding.implicit_parameter_defined?(:it)) + + assert_equal([], binding.local_variables) + assert_raise(NameError) { binding.local_variable_get(:it) } + assert_equal(false, binding.local_variable_defined?(:it)) + end + + "foo".tap do + it or flunk + it = it = "bar" + + assert_equal([:it], binding.implicit_parameters) + assert_equal("foo", binding.implicit_parameter_get(:it)) + assert_equal(true, binding.implicit_parameter_defined?(:it)) + + assert_equal([:it], binding.local_variables) + assert_equal("bar", binding.local_variable_get(:it)) + assert_equal(true, binding.local_variable_defined?(:it)) + end + end + + def test_implicit_parameters_for_it_and_numparams + "foo".tap do + it or flunk + "bar".tap do + _5 and flunk + assert_equal([:_1, :_2, :_3, :_4, :_5], binding.implicit_parameters) + assert_raise(NameError) { binding.implicit_parameter_get(:it) } + assert_equal("bar", binding.implicit_parameter_get(:_1)) + assert_equal(nil, binding.implicit_parameter_get(:_5)) + assert_raise(NameError) { binding.implicit_parameter_get(:_6) } + assert_equal(false, binding.implicit_parameter_defined?(:it)) + assert_equal(true, binding.implicit_parameter_defined?(:_1)) + assert_equal(true, binding.implicit_parameter_defined?(:_5)) + assert_equal(false, binding.implicit_parameter_defined?(:_6)) + end + end + + "foo".tap do + _5 and flunk + "bar".tap do + it or flunk + assert_equal([:it], binding.implicit_parameters) + assert_equal("bar", binding.implicit_parameter_get(:it)) + assert_raise(NameError) { binding.implicit_parameter_get(:_1) } + assert_raise(NameError) { binding.implicit_parameter_get(:_5) } + assert_raise(NameError) { binding.implicit_parameter_get(:_6) } + assert_equal(true, binding.implicit_parameter_defined?(:it)) + assert_equal(false, binding.implicit_parameter_defined?(:_1)) + assert_equal(false, binding.implicit_parameter_defined?(:_5)) + assert_equal(false, binding.implicit_parameter_defined?(:_6)) + end + end + end + + def test_implicit_parameter_invalid_name + message_pattern = /is not an implicit parameter/ + assert_raise_with_message(NameError, message_pattern) { binding.implicit_parameter_defined?(:foo) } + assert_raise_with_message(NameError, message_pattern) { binding.implicit_parameter_get(:foo) } + assert_raise_with_message(NameError, message_pattern) { binding.implicit_parameter_defined?("wrong_implicit_parameter_name_#{rand(10000)}") } + assert_raise_with_message(NameError, message_pattern) { binding.implicit_parameter_get("wrong_implicit_parameter_name_#{rand(10000)}") } + end + + def test_local_variable_set_wb + assert_ruby_status([], <<-'end;', '[Bug #13605]', timeout: 30) + b = binding + n = 20_000 + + n.times do |i| + v = rand(2_000) + name = "n#{v}" + value = Object.new + b.local_variable_set name, value + end + end; + end + + def test_local_variable_defined? + b = get_binding + assert_equal(true, b.local_variable_defined?(:a)) + assert_equal(false, b.local_variable_defined?(:b)) + end + + def test_binding_receiver + feature8779 = '[ruby-dev:47613] [Feature #8779]' + + assert_same(self, binding.receiver, feature8779) + + obj = Object.new + def obj.b; binding; end + assert_same(obj, obj.b.receiver, feature8779) + end + + def test_proc_mark + assert_normal_exit(<<-'EOS') + def f + Enumerator.new{ + 100000.times {|i| + yield + s = "#{i}" + } + } + end + + def g + x = proc{} + f(&x) + end + e = g + e.each {} + EOS + end + + def test_prepended_call + assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", ["call"]) + begin; + Proc.prepend Module.new {def call() puts "call"; super; end} + def m(&blk) blk.call; end + m {} + end; + end + + def test_refined_call + assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", ["call"]) + begin; + using Module.new {refine(Proc) {def call() puts "call"; super; end}} + def m(&blk) blk.call; end + m {} + end; + end + + def test_compose + f = proc {|x| x * 2} + g = proc {|x| x + 1} + + assert_equal(6, (f << g).call(2)) + assert_equal(6, (g >> f).call(2)) + end + + def test_compose_with_multiple_args + f = proc {|x| x * 2} + g = proc {|x, y| x + y} + + assert_equal(6, (f << g).call(1, 2)) + assert_equal(6, (g >> f).call(1, 2)) + end + + def test_compose_with_block + f = proc {|x| x * 2} + g = proc {|&blk| blk.call(1) } + + assert_equal(8, (f << g).call { |x| x + 3 }) + assert_equal(8, (g >> f).call { |x| x + 3 }) + end + + def test_compose_with_lambda + f = lambda {|x| x * 2} + g = lambda {|x| x} + not_lambda = proc {|x| x} + + assert_predicate((f << g), :lambda?) + assert_predicate((g >> f), :lambda?) + assert_predicate((not_lambda << f), :lambda?) + assert_not_predicate((f << not_lambda), :lambda?) + assert_not_predicate((not_lambda >> f), :lambda?) + end + + def test_compose_with_method + f = proc {|x| x * 2} + c = Class.new { + def g(x) x + 1 end + } + g = c.new.method(:g) + + assert_equal(6, (f << g).call(2)) + assert_equal(5, (f >> g).call(2)) + assert_predicate((f << g), :lambda?) + end + + def test_compose_with_callable + f = proc {|x| x * 2} + c = Class.new { + def call(x) x + 1 end + } + g = c.new + + assert_equal(6, (f << g).call(2)) + assert_equal(5, (f >> g).call(2)) + assert_predicate((f << g), :lambda?) + end + + def test_compose_with_noncallable + f = proc {|x| x * 2} + + assert_raise(TypeError) { + (f << 5).call(2) + } + assert_raise(TypeError) { + (f >> 5).call(2) + } + end + + def test_orphan_return + assert_equal(42, Module.new { extend self + def m1(&b) b.call end; def m2(); m1 { return 42 } end }.m2) + assert_equal(42, Module.new { extend self + def m1(&b) b end; def m2(); m1 { return 42 }.call end }.m2) + assert_raise(LocalJumpError) { Module.new { extend self + def m1(&b) b end; def m2(); m1 { return 42 } end }.m2.call } + end + + def test_orphan_break + assert_equal(42, Module.new { extend self + def m1(&b) b.call end; def m2(); m1 { break 42 } end }.m2 ) + assert_raise(LocalJumpError) { Module.new { extend self + def m1(&b) b end; def m2(); m1 { break 42 }.call end }.m2 } + assert_raise(LocalJumpError) { Module.new { extend self + def m1(&b) b end; def m2(); m1 { break 42 } end }.m2.call } + end + + def test_not_orphan_next + assert_equal(42, Module.new { extend self + def m1(&b) b.call end; def m2(); m1 { next 42 } end }.m2) + assert_equal(42, Module.new { extend self + def m1(&b) b end; def m2(); m1 { next 42 }.call end }.m2) + assert_equal(42, Module.new { extend self + def m1(&b) b end; def m2(); m1 { next 42 } end }.m2.call) + end + + def test_isolate + assert_raise_with_message ArgumentError, /\(a\)/ do + a = :a + Proc.new{p a}.isolate + end + + assert_raise_with_message ArgumentError, /\(a\)/ do + a = :a + 1.times{ + Proc.new{p a}.isolate + } + end + + assert_raise_with_message ArgumentError, /yield/ do + Proc.new{yield}.isolate + end + + + name = "\u{2603 26a1}" + assert_raise_with_message(ArgumentError, /\(#{name}\)/) do + eval("#{name} = :#{name}; Proc.new {p #{name}}").isolate + end + + # binding + + :a.tap{|a| + :b.tap{|b| + Proc.new{ + :c.tap{|c| + assert_equal :c, eval('c') + + assert_raise_with_message SyntaxError, /\`a\'/ do + eval('p a') + end + + assert_raise_with_message SyntaxError, /\`b\'/ do + eval('p b') + end + + assert_raise_with_message SyntaxError, /can not yield from isolated Proc/ do + eval('p yield') + end + + assert_equal :c, binding.local_variable_get(:c) + + assert_raise_with_message NameError, /local variable \`a\' is not defined/ do + binding.local_variable_get(:a) + end + + assert_equal [:c], local_variables + assert_equal [:c], binding.local_variables + } + }.isolate.call + } + } + end if proc{}.respond_to? :isolate +end + +class TestProcKeywords < Test::Unit::TestCase + def test_compose_keywords + f = ->(**kw) { kw.merge(:a=>1) } + g = ->(kw) { kw.merge(:a=>2) } + + assert_equal(2, (f >> g).call(a: 3)[:a]) + assert_raise(ArgumentError) { (f << g).call(a: 3)[:a] } + assert_equal(2, (f >> g).call(a: 3)[:a]) + assert_raise(ArgumentError) { (f << g).call({a: 3})[:a] } + assert_raise(ArgumentError) { (f >> g).call({a: 3})[:a] } + assert_equal(2, (g << f).call(a: 3)[:a]) + assert_raise(ArgumentError) { (g >> f).call(a: 3)[:a] } + assert_raise(ArgumentError) { (g << f).call({a: 3})[:a] } + assert_raise(ArgumentError) { (g >> f).call({a: 3})[:a] } + assert_raise(ArgumentError) { (f << g).call(**{})[:a] } + assert_equal(2, (f >> g).call(**{})[:a]) + end + + def test_compose_keywords_method + f = ->(**kw) { kw.merge(:a=>1) }.method(:call) + g = ->(kw) { kw.merge(:a=>2) }.method(:call) + + assert_raise(ArgumentError) { (f << g).call(a: 3)[:a] } + assert_equal(2, (f >> g).call(a: 3)[:a]) + assert_raise(ArgumentError) { (f << g).call({a: 3})[:a] } + assert_raise(ArgumentError) { (f >> g).call({a: 3})[:a] } + assert_equal(2, (g << f).call(a: 3)[:a]) + assert_raise(ArgumentError) { (g >> f).call(a: 3)[:a] } + assert_raise(ArgumentError) { (g << f).call({a: 3})[:a] } + assert_raise(ArgumentError) { (g >> f).call({a: 3})[:a] } + assert_raise(ArgumentError) { (f << g).call(**{})[:a] } + assert_equal(2, (f >> g).call(**{})[:a]) + end + + def test_compose_keywords_non_proc + f = ->(**kw) { kw.merge(:a=>1) } + g = Object.new + def g.call(kw) kw.merge(:a=>2) end + def g.to_proc; method(:call).to_proc; end + def g.<<(f) to_proc << f end + def g.>>(f) to_proc >> f end + + assert_raise(ArgumentError) { (f << g).call(a: 3)[:a] } + assert_equal(2, (f >> g).call(a: 3)[:a]) + assert_raise(ArgumentError) { (f << g).call({a: 3})[:a] } + assert_raise(ArgumentError) { (f >> g).call({a: 3})[:a] } + assert_equal(2, (g << f).call(a: 3)[:a]) + assert_raise(ArgumentError) { (g >> f).call(a: 3)[:a] } + assert_raise(ArgumentError) { (g << f).call({a: 3})[:a] } + assert_raise(ArgumentError) { (g >> f).call({a: 3})[:a] } + assert_raise(ArgumentError) { (f << g).call(**{})[:a] } + assert_equal(2, (f >> g).call(**{})[:a]) + + f = ->(kw) { kw.merge(:a=>1) } + g = Object.new + def g.call(**kw) kw.merge(:a=>2) end + def g.to_proc; method(:call).to_proc; end + def g.<<(f) to_proc << f end + def g.>>(f) to_proc >> f end + + assert_equal(1, (f << g).call(a: 3)[:a]) + assert_raise(ArgumentError) { (f >> g).call(a: 3)[:a] } + assert_raise(ArgumentError) { (f << g).call({a: 3})[:a] } + assert_raise(ArgumentError) { (f >> g).call({a: 3})[:a] } + assert_raise(ArgumentError) { (g << f).call(a: 3)[:a] } + assert_equal(1, (g >> f).call(a: 3)[:a]) + assert_raise(ArgumentError) { (g << f).call({a: 3})[:a] } + assert_raise(ArgumentError) { (g >> f).call({a: 3})[:a] } + assert_equal(1, (f << g).call(**{})[:a]) + assert_raise(ArgumentError) { (f >> g).call(**{})[:a] } + end end |
