diff options
Diffstat (limited to 'test/ruby/test_enum.rb')
| -rw-r--r-- | test/ruby/test_enum.rb | 356 |
1 files changed, 349 insertions, 7 deletions
diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb index 12fc2ee66a..237bdc8a4d 100644 --- a/test/ruby/test_enum.rb +++ b/test/ruby/test_enum.rb @@ -27,7 +27,6 @@ class TestEnumerable < Test::Unit::TestCase end end @verbose = $VERBOSE - $VERBOSE = nil end def teardown @@ -63,11 +62,32 @@ class TestEnumerable < Test::Unit::TestCase assert_equal([[2, 1], [2, 4]], a) end + def test_grep_optimization + bug17030 = '[ruby-core:99156]' + 'set last match' =~ /set last (.*)/ + assert_equal([:a, 'b', :c], [:a, 'b', 'z', :c, 42, nil].grep(/[a-d]/), bug17030) + assert_equal(['z', 42, nil], [:a, 'b', 'z', :c, 42, nil].grep_v(/[a-d]/), bug17030) + assert_equal('match', $1, bug17030) + + regexp = Regexp.new('x') + assert_equal([], @obj.grep(regexp), bug17030) # sanity check + def regexp.===(other) + true + end + assert_equal([1, 2, 3, 1, 2], @obj.grep(regexp), bug17030) + + o = Object.new + def o.to_str + 'hello' + end + assert_same(o, [o].grep(/ll/).first, bug17030) + end + def test_count assert_equal(5, @obj.count) assert_equal(2, @obj.count(1)) assert_equal(3, @obj.count {|x| x % 2 == 1 }) - assert_equal(2, @obj.count(1) {|x| x % 2 == 1 }) + assert_equal(2, assert_warning(/given block not used/) {@obj.count(1) {|x| x % 2 == 1 }}) assert_raise(ArgumentError) { @obj.count(0, 1) } if RUBY_ENGINE == "ruby" @@ -95,7 +115,7 @@ class TestEnumerable < Test::Unit::TestCase assert_equal(1, @obj.find_index {|x| x % 2 == 0 }) assert_equal(nil, @obj.find_index {|x| false }) assert_raise(ArgumentError) { @obj.find_index(0, 1) } - assert_equal(1, @obj.find_index(2) {|x| x == 1 }) + assert_equal(1, assert_warning(/given block not used/) {@obj.find_index(2) {|x| x == 1 }}) end def test_find_all @@ -114,6 +134,12 @@ class TestEnumerable < Test::Unit::TestCase assert_equal([1, 2, 3, 1, 2], @obj.to_a) end + def test_to_a_keywords + @obj.singleton_class.remove_method(:each) + def @obj.each(foo:) yield foo end + assert_equal([1], @obj.to_a(foo: 1)) + end + def test_to_a_size_symbol sym = Object.new class << sym @@ -144,8 +170,7 @@ class TestEnumerable < Test::Unit::TestCase assert_equal([], inf.to_a) end - def test_to_h - obj = Object.new + StubToH = Object.new.tap do |obj| def obj.each(*args) yield(*args) yield [:key, :value] @@ -157,6 +182,12 @@ class TestEnumerable < Test::Unit::TestCase yield kvp end obj.extend Enumerable + obj.freeze + end + + def test_to_h + obj = StubToH + assert_equal({ :hello => :world, :key => :value, @@ -175,13 +206,36 @@ class TestEnumerable < Test::Unit::TestCase assert_equal "element has wrong array length (expected 2, was 1)", e.message end + def test_to_h_block + obj = StubToH + + assert_equal({ + "hello" => "world", + "key" => "value", + "other_key" => "other_value", + "obtained" => "via_to_ary", + }, obj.to_h(:hello, :world) {|k, v| [k.to_s, v.to_s]}) + + e = assert_raise(TypeError) { + obj.to_h {:not_an_array} + } + assert_equal "wrong element type Symbol (expected array)", e.message + + e = assert_raise(ArgumentError) { + obj.to_h {[1]} + } + assert_equal "element has wrong array length (expected 2, was 1)", e.message + end + def test_inject assert_equal(12, @obj.inject {|z, x| z * x }) assert_equal(48, @obj.inject {|z, x| z * 2 + x }) assert_equal(12, @obj.inject(:*)) assert_equal(24, @obj.inject(2) {|z, x| z * x }) - assert_equal(24, @obj.inject(2, :*) {|z, x| z * x }) + assert_equal(24, assert_warning(/given block not used/) {@obj.inject(2, :*) {|z, x| z * x }}) assert_equal(nil, @empty.inject() {9}) + + assert_raise(ArgumentError) {@obj.inject} end FIXNUM_MIN = RbConfig::LIMITS['FIXNUM_MIN'] @@ -202,17 +256,75 @@ class TestEnumerable < Test::Unit::TestCase assert_equal(15, [3, 5, 7].inject(:+)) assert_float_equal(15.0, [3, 5, 7.0].inject(:+)) assert_equal(2*FIXNUM_MAX, Array.new(2, FIXNUM_MAX).inject(:+)) + assert_equal(3*FIXNUM_MAX, Array.new(3, FIXNUM_MAX).inject(:+)) assert_equal(2*(FIXNUM_MAX+1), Array.new(2, FIXNUM_MAX+1).inject(:+)) assert_equal(10*FIXNUM_MAX, Array.new(10, FIXNUM_MAX).inject(:+)) assert_equal(0, ([FIXNUM_MAX, 1, -FIXNUM_MAX, -1]*10).inject(:+)) assert_equal(FIXNUM_MAX*10, ([FIXNUM_MAX+1, -1]*10).inject(:+)) assert_equal(2*FIXNUM_MIN, Array.new(2, FIXNUM_MIN).inject(:+)) + assert_equal(3*FIXNUM_MIN, Array.new(3, FIXNUM_MIN).inject(:+)) assert_equal((FIXNUM_MAX+1).to_f, [FIXNUM_MAX, 1, 0.0].inject(:+)) assert_float_equal(10.0, [3.0, 5].inject(2.0, :+)) assert_float_equal((FIXNUM_MAX+1).to_f, [0.0, FIXNUM_MAX+1].inject(:+)) assert_equal(2.0+3.0i, [2.0, 3.0i].inject(:+)) end + def test_inject_op_redefined + assert_separately([], "#{<<~"end;"}\n""end") + k = Class.new do + include Enumerable + def each + yield 1 + yield 2 + yield 3 + end + end + all_assertions_foreach("", *%i[+ * / - %]) do |op| + bug = '[ruby-dev:49510] [Bug#12178] should respect redefinition' + begin + Integer.class_eval do + alias_method :orig, op + define_method(op) do |x| + 0 + end + end + assert_equal(0, k.new.inject(op), bug) + ensure + Integer.class_eval do + undef_method op + alias_method op, :orig + end + end + end; + end + + def test_inject_op_private + assert_separately([], "#{<<~"end;"}\n""end") + k = Class.new do + include Enumerable + def each + yield 1 + yield 2 + yield 3 + end + end + all_assertions_foreach("", *%i[+ * / - %]) do |op| + bug = '[ruby-core:81349] [Bug #13592] should respect visibility' + assert_raise_with_message(NoMethodError, /private method/, bug) do + begin + Integer.class_eval do + private op + end + k.new.inject(op) + ensure + Integer.class_eval do + public op + end + end + end + end; + end + def test_inject_array_op_redefined assert_separately([], "#{<<~"end;"}\n""end") all_assertions_foreach("", *%i[+ * / - %]) do |op| @@ -253,6 +365,25 @@ class TestEnumerable < Test::Unit::TestCase end; end + def test_refine_Enumerable_then_include + assert_separately([], "#{<<~"end;"}\n") + module RefinementBug + refine Enumerable do + def refined_method + :rm + end + end + end + using RefinementBug + + class A + include Enumerable + end + + assert_equal(:rm, [].refined_method) + end; + end + def test_partition assert_equal([[1, 3, 1], [2, 2]], @obj.partition {|x| x % 2 == 1 }) cond = ->(x, i) { x % 2 == 1 } @@ -268,6 +399,50 @@ class TestEnumerable < Test::Unit::TestCase assert_equal(h, @obj.each_with_index.group_by(&cond)) end + def test_tally + h = {1 => 2, 2 => 2, 3 => 1} + assert_equal(h, @obj.tally) + + h = {1 => 5, 2 => 2, 3 => 1, 4 => "x"} + assert_equal(h, @obj.tally({1 => 3, 4 => "x"})) + + assert_raise(TypeError) do + @obj.tally({1 => ""}) + end + + h = {1 => 2, 2 => 2, 3 => 1} + assert_same(h, @obj.tally(h)) + + h = {1 => 2, 2 => 2, 3 => 1}.freeze + assert_raise(FrozenError) do + @obj.tally(h) + end + assert_equal({1 => 2, 2 => 2, 3 => 1}, h) + + hash_convertible = Object.new + def hash_convertible.to_hash + {1 => 3, 4 => "x"} + end + assert_equal({1 => 5, 2 => 2, 3 => 1, 4 => "x"}, @obj.tally(hash_convertible)) + + hash_convertible = Object.new + def hash_convertible.to_hash + {1 => 3, 4 => "x"}.freeze + end + assert_raise(FrozenError) do + @obj.tally(hash_convertible) + end + assert_equal({1 => 3, 4 => "x"}, hash_convertible.to_hash) + + assert_raise(TypeError) do + @obj.tally(BasicObject.new) + end + + h = {1 => 2, 2 => 2, 3 => 1} + assert_equal(h, @obj.tally(Hash.new(100))) + assert_equal(h, @obj.tally(Hash.new {100})) + end + def test_first assert_equal(1, @obj.first) assert_equal([1, 2, 3], @obj.first(3)) @@ -288,6 +463,17 @@ class TestEnumerable < Test::Unit::TestCase empty.first empty.block.call end; + + bug18475 = '[ruby-dev:107059]' + assert_in_out_err([], <<-'end;', [], /unexpected break/, bug18475) + e = Enumerator.new do |g| + Thread.new do + g << 1 + end.join + end + + e.first + end; end def test_sort @@ -310,10 +496,25 @@ class TestEnumerable < Test::Unit::TestCase assert_equal(false, [true, true, false].all?) assert_equal(true, [].all?) assert_equal(true, @empty.all?) - assert_equal(true, @obj.all?(Fixnum)) + assert_equal(true, @obj.all?(Integer)) assert_equal(false, @obj.all?(1..2)) end + def test_all_with_unused_block + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + [1, 2].all?(1) {|x| x == 3 } + EOS + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + (1..2).all?(1) {|x| x == 3 } + EOS + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + 3.times.all?(1) {|x| x == 3 } + EOS + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + {a: 1, b: 2}.all?([:b, 2]) {|x| x == 4 } + EOS + end + def test_any assert_equal(true, @obj.any? {|x| x >= 3 }) assert_equal(false, @obj.any? {|x| x > 3 }) @@ -329,6 +530,21 @@ class TestEnumerable < Test::Unit::TestCase assert_equal(true, {a: 1, b: 2}.any?(->(kv) { kv == [:b, 2] })) end + def test_any_with_unused_block + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + [1, 23].any?(1) {|x| x == 1 } + EOS + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + (1..2).any?(34) {|x| x == 2 } + EOS + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + 3.times.any?(1) {|x| x == 3 } + EOS + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + {a: 1, b: 2}.any?([:b, 2]) {|x| x == 4 } + EOS + end + def test_one assert(@obj.one? {|x| x == 3 }) assert(!(@obj.one? {|x| x == 1 })) @@ -348,6 +564,21 @@ class TestEnumerable < Test::Unit::TestCase assert([ nil, true, 99 ].one?(Integer)) end + def test_one_with_unused_block + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + [1, 2].one?(1) {|x| x == 3 } + EOS + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + (1..2).one?(1) {|x| x == 3 } + EOS + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + 3.times.one?(1) {|x| x == 3 } + EOS + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + {a: 1, b: 2}.one?([:b, 2]) {|x| x == 4 } + EOS + end + def test_none assert(@obj.none? {|x| x == 4 }) assert(!(@obj.none? {|x| x == 1 })) @@ -365,6 +596,21 @@ class TestEnumerable < Test::Unit::TestCase assert(@empty.none?) end + def test_none_with_unused_block + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + [1, 2].none?(1) {|x| x == 3 } + EOS + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + (1..2).none?(1) {|x| x == 3 } + EOS + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + 3.times.none?(1) {|x| x == 3 } + EOS + assert_in_out_err [], <<-EOS, [], ["-:1: warning: given block not used"] + {a: 1, b: 2}.none?([:b, 2]) {|x| x == 4 } + EOS + end + def test_min assert_equal(1, @obj.min) assert_equal(3, @obj.min {|a,b| b <=> a }) @@ -504,6 +750,9 @@ class TestEnumerable < Test::Unit::TestCase ary.clear (1..10).each_slice(11) {|a| ary << a} assert_equal([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]], ary) + + assert_equal(1..10, (1..10).each_slice(3) { }) + assert_equal([], [].each_slice(3) { }) end def test_each_cons @@ -523,6 +772,9 @@ class TestEnumerable < Test::Unit::TestCase ary.clear (1..5).each_cons(6) {|a| ary << a} assert_empty(ary) + + assert_equal(1..5, (1..5).each_cons(3) { }) + assert_equal([], [].each_cons(3) { }) end def test_zip @@ -591,6 +843,8 @@ class TestEnumerable < Test::Unit::TestCase end def test_callcc + omit 'requires callcc support' unless respond_to?(:callcc) + assert_raise(RuntimeError) do c = nil @obj.sort_by {|x| callcc {|c2| c ||= c2 }; x } @@ -644,6 +898,19 @@ class TestEnumerable < Test::Unit::TestCase assert_equal([2,1,3,2,1], @obj.reverse_each.to_a) end + def test_reverse_each_memory_corruption + bug16354 = '[ruby-dev:50867]' + assert_normal_exit %q{ + size = 1000 + (0...size).reverse_each do |i| + i.inspect + ObjectSpace.each_object(Array) do |a| + a.clear if a.length == size + end + end + }, bug16354 + end + def test_chunk e = [].chunk {|elt| true } assert_equal([], e.to_a) @@ -966,6 +1233,21 @@ class TestEnumerable < Test::Unit::TestCase assert_float_equal(large_number+(small_number*11), [small_number, large_number/1r, *[small_number]*10].each.sum) assert_float_equal(small_number, [large_number, small_number, -large_number].each.sum) + k = Class.new do + include Enumerable + def initialize(*values) + @values = values + end + def each(&block) + @values.each(&block) + end + end + assert_equal(+Float::INFINITY, k.new(0.0, +Float::INFINITY).sum) + assert_equal(+Float::INFINITY, k.new(+Float::INFINITY, 0.0).sum) + assert_equal(-Float::INFINITY, k.new(0.0, -Float::INFINITY).sum) + assert_equal(-Float::INFINITY, k.new(-Float::INFINITY, 0.0).sum) + assert_predicate(k.new(-Float::INFINITY, Float::INFINITY).sum, :nan?) + assert_equal("abc", ["a", "b", "c"].each.sum("")) assert_equal([1, [2], 3], [[1], [[2]], [3]].each.sum([])) end @@ -1014,4 +1296,64 @@ class TestEnumerable < Test::Unit::TestCase assert_equal([1, 2, 3, 4, 5, 10], (1..100).uniq{|x| (x**2) % 10 }.first(6)) assert_equal([1, [1, 2]], Foo.new.to_enum.uniq) end + + def test_compact + class << (enum = Object.new) + include Enumerable + def each + yield 3 + yield nil + yield 7 + yield 9 + yield nil + end + end + + assert_equal([3, 7, 9], enum.compact) + end + + def test_transient_heap_sort_by + klass = Class.new do + include Comparable + attr_reader :i + def initialize e + @i = e + end + def <=> other + GC.start + i <=> other.i + end + end + assert_equal [1, 2, 3, 4, 5], (1..5).sort_by{|e| klass.new e} + end + + def test_filter_map + @obj = (1..8).to_a + assert_equal([4, 8, 12, 16], @obj.filter_map { |i| i * 2 if i.even? }) + assert_equal([2, 4, 6, 8, 10, 12, 14, 16], @obj.filter_map { |i| i * 2 }) + assert_equal([0, 0, 0, 0, 0, 0, 0, 0], @obj.filter_map { 0 }) + assert_equal([], @obj.filter_map { false }) + assert_equal([], @obj.filter_map { nil }) + assert_instance_of(Enumerator, @obj.filter_map) + end + + def test_ruby_svar + klass = Class.new do + include Enumerable + def each + %w(bar baz).each{|e| yield e} + end + end + svars = [] + klass.new.grep(/(b.)/) { svars << $1 } + assert_equal(["ba", "ba"], svars) + end + + def test_all_fast + data = { "key" => { "key2" => 1 } } + kk = vv = nil + data.all? { |(k, v)| kk, vv = k, v } + assert_equal(kk, "key") + assert_equal(vv, { "key2" => 1 }) + end end |
