1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/test/ruby/test_refinement.rb
shugo 75bed271f0 * class.c (rb_prepend_module): move refined methods from the origin
of a class to the class, because refinements should have priority
  over prepended modules.

* test/ruby/test_refinement.rb: related test.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38344 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-12-12 09:35:50 +00:00

808 lines
15 KiB
Ruby

require 'test/unit'
require_relative 'envutil'
EnvUtil.suppress_warning {require "refinement"}
class TestRefinement < Test::Unit::TestCase
class Foo
def x
return "Foo#x"
end
def y
return "Foo#y"
end
def a
return "Foo#a"
end
def call_x
return x
end
end
module FooExt
refine Foo do
def x
return "FooExt#x"
end
def y
return "FooExt#y " + super
end
def z
return "FooExt#z"
end
def a
return "FooExt#a"
end
end
end
module FooExt2
refine Foo do
def x
return "FooExt2#x"
end
def y
return "FooExt2#y " + super
end
def z
return "FooExt2#z"
end
end
end
class FooSub < Foo
def x
return "FooSub#x"
end
def y
return "FooSub#y " + super
end
end
eval <<-EOF, TOPLEVEL_BINDING
using TestRefinement::FooExt
class TestRefinement::FooExtClient
def self.invoke_x_on(foo)
return foo.x
end
def self.invoke_y_on(foo)
return foo.y
end
def self.invoke_z_on(foo)
return foo.z
end
def self.send_z_on(foo)
return foo.send(:z)
end
def self.method_z(foo)
return foo.method(:z)
end
def self.invoke_call_x_on(foo)
return foo.call_x
end
end
EOF
eval <<-EOF, TOPLEVEL_BINDING
using TestRefinement::FooExt
using TestRefinement::FooExt2
class TestRefinement::FooExtClient2
def self.invoke_y_on(foo)
return foo.y
end
def self.invoke_a_on(foo)
return foo.a
end
end
EOF
def test_override
foo = Foo.new
assert_equal("Foo#x", foo.x)
assert_equal("FooExt#x", FooExtClient.invoke_x_on(foo))
assert_equal("Foo#x", foo.x)
end
def test_super
foo = Foo.new
assert_equal("Foo#y", foo.y)
assert_equal("FooExt#y Foo#y", FooExtClient.invoke_y_on(foo))
assert_equal("Foo#y", foo.y)
end
def test_super_not_chained
foo = Foo.new
assert_equal("Foo#y", foo.y)
assert_equal("FooExt2#y Foo#y", FooExtClient2.invoke_y_on(foo))
assert_equal("Foo#y", foo.y)
end
def test_using_same_class_refinements
foo = Foo.new
assert_equal("Foo#a", foo.a)
assert_equal("FooExt#a", FooExtClient2.invoke_a_on(foo))
assert_equal("Foo#a", foo.a)
end
def test_new_method
foo = Foo.new
assert_raise(NoMethodError) { foo.z }
assert_equal("FooExt#z", FooExtClient.invoke_z_on(foo))
assert_raise(NoMethodError) { foo.z }
end
module RespondTo
class Super
def foo
end
end
class Sub < Super
end
module M
refine Sub do
def foo
end
end
end
end
def test_send_should_not_use_refinements
foo = Foo.new
assert_raise(NoMethodError) { foo.send(:z) }
assert_raise(NoMethodError) { FooExtClient.send_z_on(foo) }
assert_raise(NoMethodError) { foo.send(:z) }
assert_equal(true, RespondTo::Sub.new.respond_to?(:foo))
end
def test_method_should_not_use_refinements
foo = Foo.new
assert_raise(NameError) { foo.method(:z) }
assert_raise(NameError) { FooExtClient.method_z(foo) }
assert_raise(NameError) { foo.method(:z) }
end
def test_no_local_rebinding
foo = Foo.new
assert_equal("Foo#x", foo.call_x)
assert_equal("Foo#x", FooExtClient.invoke_call_x_on(foo))
assert_equal("Foo#x", foo.call_x)
end
def test_subclass_is_prior
sub = FooSub.new
assert_equal("FooSub#x", sub.x)
assert_equal("FooSub#x", FooExtClient.invoke_x_on(sub))
assert_equal("FooSub#x", sub.x)
end
def test_super_in_subclass
sub = FooSub.new
assert_equal("FooSub#y Foo#y", sub.y)
# not "FooSub#y FooExt#y Foo#y"
assert_equal("FooSub#y Foo#y", FooExtClient.invoke_y_on(sub))
assert_equal("FooSub#y Foo#y", sub.y)
end
def test_new_method_on_subclass
sub = FooSub.new
assert_raise(NoMethodError) { sub.z }
assert_equal("FooExt#z", FooExtClient.invoke_z_on(sub))
assert_raise(NoMethodError) { sub.z }
end
def test_module_eval
foo = Foo.new
assert_equal("Foo#x", foo.x)
assert_equal("Foo#x", FooExt.module_eval { foo.x })
assert_equal("Foo#x", FooExt.module_eval("foo.x"))
assert_equal("Foo#x", foo.x)
end
def test_instance_eval_without_refinement
foo = Foo.new
ext_client = FooExtClient.new
assert_equal("Foo#x", foo.x)
assert_equal("Foo#x", ext_client.instance_eval { foo.x })
assert_equal("Foo#x", foo.x)
end
module FixnumSlashExt
refine Fixnum do
def /(other) quo(other) end
end
end
def test_override_builtin_method
assert_equal(0, 1 / 2)
assert_equal(Rational(1, 2), eval_using(FixnumSlashExt, "1 / 2"))
assert_equal(0, 1 / 2)
end
module FixnumPlusExt
refine Fixnum do
def self.method_added(*args); end
def +(other) "overriden" end
end
end
def test_override_builtin_method_with_method_added
assert_equal(3, 1 + 2)
assert_equal("overriden", eval_using(FixnumPlusExt, "1 + 2"))
assert_equal(3, 1 + 2)
end
def test_return_value_of_refine
mod = nil
result = nil
m = Module.new {
result = refine(Object) {
mod = self
}
}
assert_equal mod, result
end
module RefineSameClass
REFINEMENT1 = refine(Fixnum) {
def foo; return "foo" end
}
REFINEMENT2 = refine(Fixnum) {
def bar; return "bar" end
}
REFINEMENT3 = refine(String) {
def baz; return "baz" end
}
end
def test_refine_same_class_twice
assert_equal("foo", eval_using(RefineSameClass, "1.foo"))
assert_equal("bar", eval_using(RefineSameClass, "1.bar"))
assert_equal(RefineSameClass::REFINEMENT1, RefineSameClass::REFINEMENT2)
assert_not_equal(RefineSameClass::REFINEMENT1, RefineSameClass::REFINEMENT3)
end
module FixnumFooExt
refine Fixnum do
def foo; "foo"; end
end
end
def test_respond_to_should_not_use_refinements
assert_equal(false, 1.respond_to?(:foo))
assert_equal(false, eval_using(FixnumFooExt, "1.respond_to?(:foo)"))
end
module StringCmpExt
refine String do
def <=>(other) return 0 end
end
end
module ArrayEachExt
refine Array do
def each
super do |i|
yield 2 * i
end
end
end
end
def test_builtin_method_no_local_rebinding
assert_equal(false, eval_using(StringCmpExt, '"1" >= "2"'))
assert_equal(1, eval_using(ArrayEachExt, "[1, 2, 3].min"))
end
module RefinePrependedClass
module M1
def foo
super << :m1
end
end
class C
prepend M1
def foo
[:c]
end
end
module M2
refine C do
def foo
super << :m2
end
end
end
end
def test_refine_prepended_class
x = eval_using(RefinePrependedClass::M2,
"TestRefinement::RefinePrependedClass::C.new.foo")
assert_equal([:c, :m1, :m2], x)
end
def test_refine_module
m1 = Module.new
assert_raise(TypeError) do
Module.new {
refine m1 do
def foo
:m2
end
end
}
end
end
def test_refine_neither_class_nor_module
assert_raise(TypeError) do
Module.new {
refine Object.new do
end
}
end
assert_raise(TypeError) do
Module.new {
refine 123 do
end
}
end
assert_raise(TypeError) do
Module.new {
refine "foo" do
end
}
end
end
def test_refine_in_class
assert_raise(NoMethodError) do
Class.new {
refine Fixnum do
def foo
"c"
end
end
}
end
end
def test_main_using
assert_in_out_err([], <<-INPUT, %w(:C :M), /Refinements are experimental/)
require "refinement"
class C
def foo
:C
end
end
module M
refine C do
def foo
:M
end
end
end
c = C.new
p c.foo
using M
p c.foo
INPUT
end
def test_main_using_is_private
assert_raise(NoMethodError) do
eval("self.using Module.new", TOPLEVEL_BINDING)
end
end
def test_no_kernel_using
assert_raise(NoMethodError) do
using Module.new
end
end
def test_no_module_using
assert_raise(NoMethodError) do
Module.new {
using Module.new
}
end
end
class UsingClass
end
def test_module_using_class
c = Class.new
assert_raise(TypeError) do
eval("using TestRefinement::UsingClass", TOPLEVEL_BINDING)
end
end
def test_refine_without_block
c1 = Class.new
e = assert_raise(ArgumentError) {
Module.new do
refine c1
end
}
assert_equal("no block given", e.message)
end
module Inspect
module M
Fixnum = refine(Fixnum) {}
end
end
def test_inspect
assert_equal("#<refinement:Fixnum@TestRefinement::Inspect::M>",
Inspect::M::Fixnum.inspect)
end
def test_using_method_cache
assert_in_out_err([], <<-INPUT, %w(:M1 :M2), /Refinements are experimental/)
require "refinement"
class C
def foo
"original"
end
end
module M1
refine C do
def foo
:M1
end
end
end
module M2
refine C do
def foo
:M2
end
end
end
c = C.new
using M1
p c.foo
using M2
p c.foo
INPUT
end
module RedefineRefinedMethod
class C
def foo
"original"
end
end
module M
refine C do
def foo
"refined"
end
end
end
class C
def foo
"redefined"
end
end
end
def test_redefine_refined_method
x = eval_using(RedefineRefinedMethod::M,
"TestRefinement::RedefineRefinedMethod::C.new.foo")
assert_equal("refined", x)
end
module StringExt
refine String do
def foo
"foo"
end
end
end
module RefineScoping
refine String do
def foo
"foo"
end
def RefineScoping.call_in_refine_block
"".foo
end
end
def self.call_outside_refine_block
"".foo
end
end
def test_refine_scoping
assert_equal("foo", RefineScoping.call_in_refine_block)
assert_raise(NoMethodError) do
RefineScoping.call_outside_refine_block
end
end
module StringRecursiveLength
refine String do
def recursive_length
if empty?
0
else
self[1..-1].recursive_length + 1
end
end
end
end
def test_refine_recursion
x = eval_using(StringRecursiveLength, "'foo'.recursive_length")
assert_equal(3, x)
end
module ToJSON
refine Integer do
def to_json; to_s; end
end
refine Array do
def to_json; "[" + map { |i| i.to_json }.join(",") + "]" end
end
refine Hash do
def to_json; "{" + map { |k, v| k.to_s.dump + ":" + v.to_json }.join(",") + "}" end
end
end
def test_refine_mutual_recursion
x = eval_using(ToJSON, "[{1=>2}, {3=>4}].to_json")
assert_equal('[{"1":2},{"3":4}]', x)
end
def test_refine_with_proc
assert_raise(ArgumentError) do
Module.new {
refine(String, &Proc.new {})
}
end
end
def test_using_in_module
assert_raise(RuntimeError) do
eval(<<-EOF, TOPLEVEL_BINDING)
$main = self
module M
end
module M2
$main.send(:using, M)
end
EOF
end
end
def test_using_in_method
assert_raise(RuntimeError) do
eval(<<-EOF, TOPLEVEL_BINDING)
$main = self
module M
end
def call_using_in_method
$main.send(:using, M)
end
call_using_in_method
EOF
end
end
module IncludeIntoRefinement
class C
def bar
return "C#bar"
end
def baz
return "C#baz"
end
end
module Mixin
def foo
return "Mixin#foo"
end
def bar
return super << " Mixin#bar"
end
def baz
return super << " Mixin#baz"
end
end
module M
refine C do
include Mixin
def baz
return super << " M#baz"
end
end
end
end
eval <<-EOF, TOPLEVEL_BINDING
using TestRefinement::IncludeIntoRefinement::M
module TestRefinement::IncludeIntoRefinement::User
def self.invoke_foo_on(x)
x.foo
end
def self.invoke_bar_on(x)
x.bar
end
def self.invoke_baz_on(x)
x.baz
end
end
EOF
def test_include_into_refinement
x = IncludeIntoRefinement::C.new
assert_equal("Mixin#foo", IncludeIntoRefinement::User.invoke_foo_on(x))
assert_equal("C#bar Mixin#bar",
IncludeIntoRefinement::User.invoke_bar_on(x))
assert_equal("C#baz Mixin#baz M#baz",
IncludeIntoRefinement::User.invoke_baz_on(x))
end
module PrependIntoRefinement
class C
def bar
return "C#bar"
end
def baz
return "C#baz"
end
end
module Mixin
def foo
return "Mixin#foo"
end
def bar
return super << " Mixin#bar"
end
def baz
return super << " Mixin#baz"
end
end
module M
refine C do
prepend Mixin
def baz
return super << " M#baz"
end
end
end
end
eval <<-EOF, TOPLEVEL_BINDING
using TestRefinement::PrependIntoRefinement::M
module TestRefinement::PrependIntoRefinement::User
def self.invoke_foo_on(x)
x.foo
end
def self.invoke_bar_on(x)
x.bar
end
def self.invoke_baz_on(x)
x.baz
end
end
EOF
def test_prepend_into_refinement
x = PrependIntoRefinement::C.new
assert_equal("Mixin#foo", PrependIntoRefinement::User.invoke_foo_on(x))
assert_equal("C#bar Mixin#bar",
PrependIntoRefinement::User.invoke_bar_on(x))
assert_equal("C#baz M#baz Mixin#baz",
PrependIntoRefinement::User.invoke_baz_on(x))
end
module PrependAfterRefine
class C
def foo
"original"
end
end
module M
refine C do
def foo
"refined"
end
def bar
"refined"
end
end
end
module Mixin
def foo
"mixin"
end
def bar
"mixin"
end
end
class C
prepend Mixin
end
end
def test_prepend_after_refine
x = eval_using(PrependAfterRefine::M,
"TestRefinement::PrependAfterRefine::C.new.foo")
assert_equal("refined", x)
assert_equal("mixin", TestRefinement::PrependAfterRefine::C.new.foo)
y = eval_using(PrependAfterRefine::M,
"TestRefinement::PrependAfterRefine::C.new.bar")
assert_equal("refined", y)
assert_equal("mixin", TestRefinement::PrependAfterRefine::C.new.bar)
end
private
def eval_using(mod, s)
eval("using #{mod}; #{s}", TOPLEVEL_BINDING)
end
end