diff options
Diffstat (limited to 'spec/ruby/core/basicobject')
| -rw-r--r-- | spec/ruby/core/basicobject/basicobject_spec.rb | 4 | ||||
| -rw-r--r-- | spec/ruby/core/basicobject/equal_spec.rb | 6 | ||||
| -rw-r--r-- | spec/ruby/core/basicobject/fixtures/classes.rb | 228 | ||||
| -rw-r--r-- | spec/ruby/core/basicobject/initialize_spec.rb | 2 | ||||
| -rw-r--r-- | spec/ruby/core/basicobject/instance_eval_spec.rb | 191 | ||||
| -rw-r--r-- | spec/ruby/core/basicobject/instance_exec_spec.rb | 32 | ||||
| -rw-r--r-- | spec/ruby/core/basicobject/method_missing_spec.rb | 1 | ||||
| -rw-r--r-- | spec/ruby/core/basicobject/singleton_method_added_spec.rb | 61 |
8 files changed, 476 insertions, 49 deletions
diff --git a/spec/ruby/core/basicobject/basicobject_spec.rb b/spec/ruby/core/basicobject/basicobject_spec.rb index ccaa9c8593..27a322e72c 100644 --- a/spec/ruby/core/basicobject/basicobject_spec.rb +++ b/spec/ruby/core/basicobject/basicobject_spec.rb @@ -8,7 +8,7 @@ describe "BasicObject" do end it "raises NameError when referencing built-in constants" do - lambda { class BasicObjectSpecs::BOSubclass; Kernel; end }.should raise_error(NameError) + -> { class BasicObjectSpecs::BOSubclass; Kernel; end }.should raise_error(NameError) end it "does not define built-in constants (according to const_defined?)" do @@ -85,7 +85,7 @@ describe "BasicObject subclass" do describe "BasicObject references" do it "can refer to BasicObject from within itself" do - lambda { BasicObject::BasicObject }.should_not raise_error + -> { BasicObject::BasicObject }.should_not raise_error end end end diff --git a/spec/ruby/core/basicobject/equal_spec.rb b/spec/ruby/core/basicobject/equal_spec.rb index 3c1ad56d4a..ce28eaed95 100644 --- a/spec/ruby/core/basicobject/equal_spec.rb +++ b/spec/ruby/core/basicobject/equal_spec.rb @@ -11,8 +11,10 @@ describe "BasicObject#equal?" do it "is unaffected by overriding __id__" do o1 = mock("object") o2 = mock("object") - def o1.__id__; 10; end - def o2.__id__; 10; end + suppress_warning { + def o1.__id__; 10; end + def o2.__id__; 10; end + } o1.equal?(o2).should be_false end diff --git a/spec/ruby/core/basicobject/fixtures/classes.rb b/spec/ruby/core/basicobject/fixtures/classes.rb index d1785afe31..ed5a2dda17 100644 --- a/spec/ruby/core/basicobject/fixtures/classes.rb +++ b/spec/ruby/core/basicobject/fixtures/classes.rb @@ -15,8 +15,231 @@ module BasicObjectSpecs include InstExec end - module InstEvalCVar - instance_eval { @@count = 2 } + module InstEval + module CVar + module Get + class ReceiverScope + @@cvar = :value_defined_in_receiver_scope + end + + class BlockDefinitionScope + @@cvar = :value_defined_in_block_definition_scope + + def block + -> * { @@cvar } + end + end + + class CallerScope + @@cvar = :value_defined_in_caller_scope + + def get_class_variable_with_string(obj) + obj.instance_eval("@@cvar") + end + + def get_class_variable_with_block(obj, block) + obj.instance_eval(&block) + end + end + + class CallerWithoutCVarScope + def get_class_variable_with_string(obj) + obj.instance_eval("@@cvar") + end + end + + ReceiverWithCVarDefinedInSingletonClass = Class.new.new.tap do |obj| + obj.singleton_class.class_variable_set(:@@cvar, :value_defined_in_receiver_singleton_class) + end + end + + module Set + class ReceiverScope + end + + class BlockDefinitionScope + def self.get_class_variable + @@cvar + end + + def block_to_assign(value) + -> * { @@cvar = value } + end + end + + class CallerScope + def self.get_class_variable + @@cvar + end + + def set_class_variable_with_string(obj, value) + obj.instance_eval("@@cvar=#{value.inspect}") + end + + def set_class_variable_with_block(obj, block) + obj.instance_eval(&block) + end + end + end + end + end + + module InstEval + module Constants + module ConstantInReceiverSingletonClass + module ReceiverScope + FOO = :ReceiverScope + + class ReceiverParent + FOO = :ReceiverParent + end + + class Receiver < ReceiverParent + FOO = :Receiver + + def initialize + self.singleton_class.const_set(:FOO, :singleton_class) + end + end + end + + module CallerScope + FOO = :CallerScope + + class CallerParent + FOO = :CallerParent + end + + class Caller < CallerParent + FOO = :Caller + + def get_constant_with_string(receiver) + receiver.instance_eval("FOO") + end + end + end + end + + module ConstantInReceiverClass + module ReceiverScope + FOO = :ReceiverScope + + class ReceiverParent + FOO = :ReceiverParent + end + + class Receiver < ReceiverParent + FOO = :Receiver + end + end + + module CallerScope + FOO = :CallerScope + + class CallerParent + FOO = :CallerParent + end + + class Caller < CallerParent + FOO = :Caller + + def get_constant_with_string(receiver) + receiver.instance_eval("FOO") + end + end + end + end + + module ConstantInCallerClass + module ReceiverScope + FOO = :ReceiverScope + + class ReceiverParent + FOO = :ReceiverParent + end + + class Receiver < ReceiverParent + # FOO is not declared in a receiver class + end + end + + module CallerScope + FOO = :CallerScope + + class CallerParent + FOO = :CallerParent + end + + class Caller < CallerParent + FOO = :Caller + + def get_constant_with_string(receiver) + receiver.instance_eval("FOO") + end + end + end + end + + module ConstantInCallerOuterScopes + module ReceiverScope + FOO = :ReceiverScope + + class ReceiverParent + FOO = :ReceiverParent + end + + class Receiver < ReceiverParent + # FOO is not declared in a receiver class + end + end + + module CallerScope + FOO = :CallerScope + + class CallerParent + FOO = :CallerParent + end + + class Caller < CallerParent + # FOO is not declared in a caller class + + def get_constant_with_string(receiver) + receiver.instance_eval("FOO") + end + end + end + end + + module ConstantInReceiverParentClass + module ReceiverScope + FOO = :ReceiverScope + + class ReceiverParent + FOO = :ReceiverParent + end + + class Receiver < ReceiverParent + # FOO is not declared in a receiver class + end + end + + module CallerScope + # FOO is not declared in a caller outer scopes + + class CallerParent + FOO = :CallerParent + end + + class Caller < CallerParent + # FOO is not declared in a caller class + + def get_constant_with_string(receiver) + receiver.instance_eval("FOO") + end + end + end + end + end end class InstEvalConst @@ -26,7 +249,6 @@ module BasicObjectSpecs module InstEvalOuter module Inner obj = InstEvalConst.new - X_BY_STR = obj.instance_eval("INST_EVAL_CONST_X") rescue nil X_BY_BLOCK = obj.instance_eval { INST_EVAL_CONST_X } rescue nil end end diff --git a/spec/ruby/core/basicobject/initialize_spec.rb b/spec/ruby/core/basicobject/initialize_spec.rb index 0266496856..b7ce73ffd5 100644 --- a/spec/ruby/core/basicobject/initialize_spec.rb +++ b/spec/ruby/core/basicobject/initialize_spec.rb @@ -6,7 +6,7 @@ describe "BasicObject#initialize" do end it "does not accept arguments" do - lambda { + -> { BasicObject.new("This", "makes it easier", "to call super", "from other constructors") }.should raise_error(ArgumentError) end diff --git a/spec/ruby/core/basicobject/instance_eval_spec.rb b/spec/ruby/core/basicobject/instance_eval_spec.rb index 24b4d6dc69..633b5c2cb1 100644 --- a/spec/ruby/core/basicobject/instance_eval_spec.rb +++ b/spec/ruby/core/basicobject/instance_eval_spec.rb @@ -20,12 +20,18 @@ describe "BasicObject#instance_eval" do a.instance_eval('self').equal?(a).should be_true end - it "expects a block with no arguments" do - lambda { "hola".instance_eval }.should raise_error(ArgumentError) + it "raises an ArgumentError when no arguments and no block are given" do + -> { "hola".instance_eval }.should raise_error(ArgumentError, "wrong number of arguments (given 0, expected 1..3)") end - it "takes no arguments with a block" do - lambda { "hola".instance_eval(4, 5) {|a,b| a + b } }.should raise_error(ArgumentError) + it "raises an ArgumentError when a block and normal arguments are given" do + -> { "hola".instance_eval(4, 5) {|a,b| a + b } }.should raise_error(ArgumentError, "wrong number of arguments (given 2, expected 0)") + end + + it "raises an ArgumentError when more than 3 arguments are given" do + -> { + "hola".instance_eval("1 + 1", "some file", 0, "bogus") + }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)") end it "yields the object to the block" do @@ -45,7 +51,7 @@ describe "BasicObject#instance_eval" do end end f.foo.should == 1 - lambda { Object.new.foo }.should raise_error(NoMethodError) + -> { Object.new.foo }.should raise_error(NoMethodError) end it "preserves self in the original block when passed a block argument" do @@ -78,11 +84,30 @@ describe "BasicObject#instance_eval" do end + ruby_version_is "3.3" do + it "uses the caller location as default location" do + f = Object.new + f.instance_eval("[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1] + end + end + it "has access to receiver's instance variables" do BasicObjectSpecs::IVars.new.instance_eval { @secret }.should == 99 BasicObjectSpecs::IVars.new.instance_eval("@secret").should == 99 end + it "raises TypeError for frozen objects when tries to set receiver's instance variables" do + -> { nil.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen NilClass: nil") + -> { true.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen TrueClass: true") + -> { false.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen FalseClass: false") + -> { 1.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen Integer: 1") + -> { :symbol.instance_eval { @foo = 42 } }.should raise_error(FrozenError, "can't modify frozen Symbol: :symbol") + + obj = Object.new + obj.freeze + -> { obj.instance_eval { @foo = 42 } }.should raise_error(FrozenError) + end + it "treats block-local variables as local to the block" do prc = instance_eval <<-CODE proc do |x, prc| @@ -99,11 +124,6 @@ describe "BasicObject#instance_eval" do prc.call(false, prc).should == 1 end - it "sets class variables in the receiver" do - BasicObjectSpecs::InstEvalCVar.class_variables.should include(:@@count) - BasicObjectSpecs::InstEvalCVar.send(:class_variable_get, :@@count).should == 2 - end - it "makes the receiver metaclass the scoped class when used with a string" do obj = Object.new obj.instance_eval %{ @@ -113,8 +133,41 @@ describe "BasicObject#instance_eval" do obj.singleton_class.const_get(:B).should be_an_instance_of(Class) end - it "gets constants in the receiver if a string given" do - BasicObjectSpecs::InstEvalOuter::Inner::X_BY_STR.should == 2 + describe "constants lookup when a String given" do + it "looks in the receiver singleton class first" do + receiver = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverSingletonClass::ReceiverScope::Receiver.new + caller = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverSingletonClass::CallerScope::Caller.new + + caller.get_constant_with_string(receiver).should == :singleton_class + end + + it "looks in the receiver class next" do + receiver = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverClass::ReceiverScope::Receiver.new + caller = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverClass::CallerScope::Caller.new + + caller.get_constant_with_string(receiver).should == :Receiver + end + + it "looks in the caller class next" do + receiver = BasicObjectSpecs::InstEval::Constants::ConstantInCallerClass::ReceiverScope::Receiver.new + caller = BasicObjectSpecs::InstEval::Constants::ConstantInCallerClass::CallerScope::Caller.new + + caller.get_constant_with_string(receiver).should == :Caller + end + + it "looks in the caller outer scopes next" do + receiver = BasicObjectSpecs::InstEval::Constants::ConstantInCallerOuterScopes::ReceiverScope::Receiver.new + caller = BasicObjectSpecs::InstEval::Constants::ConstantInCallerOuterScopes::CallerScope::Caller.new + + caller.get_constant_with_string(receiver).should == :CallerScope + end + + it "looks in the receiver class hierarchy next" do + receiver = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverParentClass::ReceiverScope::Receiver.new + caller = BasicObjectSpecs::InstEval::Constants::ConstantInReceiverParentClass::CallerScope::Caller.new + + caller.get_constant_with_string(receiver).should == :ReceiverParent + end end it "doesn't get constants in the receiver if a block given" do @@ -122,31 +175,65 @@ describe "BasicObject#instance_eval" do end it "raises a TypeError when defining methods on an immediate" do - lambda do + -> do 1.instance_eval { def foo; end } end.should raise_error(TypeError) - lambda do + -> do :foo.instance_eval { def foo; end } end.should raise_error(TypeError) end -quarantine! do # Not clean, leaves cvars lying around to break other specs - it "scopes class var accesses in the caller when called on a Fixnum" do - # Fixnum can take instance vars - Fixnum.class_eval "@@__tmp_instance_eval_spec = 1" - (defined? @@__tmp_instance_eval_spec).should be_nil + describe "class variables lookup" do + it "gets class variables in the caller class when called with a String" do + receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverScope.new + caller = BasicObjectSpecs::InstEval::CVar::Get::CallerScope.new + + caller.get_class_variable_with_string(receiver).should == :value_defined_in_caller_scope + end + + it "gets class variables in the block definition scope when called with a block" do + receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverScope.new + caller = BasicObjectSpecs::InstEval::CVar::Get::CallerScope.new + block = BasicObjectSpecs::InstEval::CVar::Get::BlockDefinitionScope.new.block + + caller.get_class_variable_with_block(receiver, block).should == :value_defined_in_block_definition_scope + end + + it "sets class variables in the caller class when called with a String" do + receiver = BasicObjectSpecs::InstEval::CVar::Set::ReceiverScope.new + caller = BasicObjectSpecs::InstEval::CVar::Set::CallerScope.new - @@__tmp_instance_eval_spec = 2 - 1.instance_eval { @@__tmp_instance_eval_spec }.should == 2 - Fixnum.__send__(:remove_class_variable, :@@__tmp_instance_eval_spec) + caller.set_class_variable_with_string(receiver, 1) + BasicObjectSpecs::InstEval::CVar::Set::CallerScope.get_class_variable.should == 1 + end + + it "sets class variables in the block definition scope when called with a block" do + receiver = BasicObjectSpecs::InstEval::CVar::Set::ReceiverScope.new + caller = BasicObjectSpecs::InstEval::CVar::Set::CallerScope.new + block = BasicObjectSpecs::InstEval::CVar::Set::BlockDefinitionScope.new.block_to_assign(1) + + caller.set_class_variable_with_block(receiver, block) + BasicObjectSpecs::InstEval::CVar::Set::BlockDefinitionScope.get_class_variable.should == 1 + end + + it "does not have access to class variables in the receiver class when called with a String" do + receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverScope.new + caller = BasicObjectSpecs::InstEval::CVar::Get::CallerWithoutCVarScope.new + -> { caller.get_class_variable_with_string(receiver) }.should raise_error(NameError, /uninitialized class variable @@cvar/) + end + + it "does not have access to class variables in the receiver's singleton class when called with a String" do + receiver = BasicObjectSpecs::InstEval::CVar::Get::ReceiverWithCVarDefinedInSingletonClass + caller = BasicObjectSpecs::InstEval::CVar::Get::CallerWithoutCVarScope.new + -> { caller.get_class_variable_with_string(receiver) }.should raise_error(NameError, /uninitialized class variable @@cvar/) + end end -end it "raises a TypeError when defining methods on numerics" do - lambda do + -> do (1.0).instance_eval { def foo; end } end.should raise_error(TypeError) - lambda do + -> do (1 << 64).instance_eval { def foo; end } end.should raise_error(TypeError) end @@ -185,4 +272,58 @@ end x.should == :value end + + it "converts string argument with #to_str method" do + source_code = Object.new + def source_code.to_str() "1" end + + a = BasicObject.new + a.instance_eval(source_code).should == 1 + end + + it "raises ArgumentError if returned value is not String" do + source_code = Object.new + def source_code.to_str() :symbol end + + a = BasicObject.new + -> { a.instance_eval(source_code) }.should raise_error(TypeError, /can't convert Object to String/) + end + + it "converts filename argument with #to_str method" do + filename = Object.new + def filename.to_str() "file.rb" end + + err = begin + Object.new.instance_eval("raise", filename) + rescue => e + e + end + err.backtrace.first.split(":")[0].should == "file.rb" + end + + it "raises ArgumentError if returned value is not String" do + filename = Object.new + def filename.to_str() :symbol end + + -> { Object.new.instance_eval("raise", filename) }.should raise_error(TypeError, /can't convert Object to String/) + end + + it "converts lineno argument with #to_int method" do + lineno = Object.new + def lineno.to_int() 15 end + + err = begin + Object.new.instance_eval("raise", "file.rb", lineno) + rescue => e + e + end + err.backtrace.first.split(":")[1].should == "15" + end + + it "raises ArgumentError if returned value is not Integer" do + lineno = Object.new + def lineno.to_int() :symbol end + + -> { Object.new.instance_eval("raise", "file.rb", lineno) }.should raise_error(TypeError, /can't convert Object to Integer/) + end end diff --git a/spec/ruby/core/basicobject/instance_exec_spec.rb b/spec/ruby/core/basicobject/instance_exec_spec.rb index 33c6b5a1d3..370f03d33c 100644 --- a/spec/ruby/core/basicobject/instance_exec_spec.rb +++ b/spec/ruby/core/basicobject/instance_exec_spec.rb @@ -17,7 +17,7 @@ describe "BasicObject#instance_exec" do end it "raises a LocalJumpError unless given a block" do - lambda { "hola".instance_exec }.should raise_error(LocalJumpError) + -> { "hola".instance_exec }.should raise_error(LocalJumpError) end it "has an arity of -1" do @@ -25,7 +25,7 @@ describe "BasicObject#instance_exec" do end it "accepts arguments with a block" do - lambda { "hola".instance_exec(4, 5) { |a,b| a + b } }.should_not raise_error + -> { "hola".instance_exec(4, 5) { |a,b| a + b } }.should_not raise_error end it "doesn't pass self to the block as an argument" do @@ -44,7 +44,7 @@ describe "BasicObject#instance_exec" do end end f.foo.should == 1 - lambda { Object.new.foo }.should raise_error(NoMethodError) + -> { Object.new.foo }.should raise_error(NoMethodError) end # TODO: This should probably be replaced with a "should behave like" that uses @@ -76,31 +76,31 @@ describe "BasicObject#instance_exec" do end it "raises a TypeError when defining methods on an immediate" do - lambda do + -> do 1.instance_exec { def foo; end } end.should raise_error(TypeError) - lambda do + -> do :foo.instance_exec { def foo; end } end.should raise_error(TypeError) end -quarantine! do # Not clean, leaves cvars lying around to break other specs - it "scopes class var accesses in the caller when called on a Fixnum" do - # Fixnum can take instance vars - Fixnum.class_eval "@@__tmp_instance_exec_spec = 1" - (defined? @@__tmp_instance_exec_spec).should == nil + quarantine! do # Not clean, leaves cvars lying around to break other specs + it "scopes class var accesses in the caller when called on an Integer" do + # Integer can take instance vars + Integer.class_eval "@@__tmp_instance_exec_spec = 1" + (defined? @@__tmp_instance_exec_spec).should == nil - @@__tmp_instance_exec_spec = 2 - 1.instance_exec { @@__tmp_instance_exec_spec }.should == 2 - Fixnum.__send__(:remove_class_variable, :@@__tmp_instance_exec_spec) + @@__tmp_instance_exec_spec = 2 + 1.instance_exec { @@__tmp_instance_exec_spec }.should == 2 + Integer.__send__(:remove_class_variable, :@@__tmp_instance_exec_spec) + end end -end it "raises a TypeError when defining methods on numerics" do - lambda do + -> do (1.0).instance_exec { def foo; end } end.should raise_error(TypeError) - lambda do + -> do (1 << 64).instance_exec { def foo; end } end.should raise_error(TypeError) end diff --git a/spec/ruby/core/basicobject/method_missing_spec.rb b/spec/ruby/core/basicobject/method_missing_spec.rb index b048780ee8..a29d4375bc 100644 --- a/spec/ruby/core/basicobject/method_missing_spec.rb +++ b/spec/ruby/core/basicobject/method_missing_spec.rb @@ -1,3 +1,4 @@ +require_relative "../../spec_helper" require_relative '../../shared/basicobject/method_missing' describe "BasicObject#method_missing" do diff --git a/spec/ruby/core/basicobject/singleton_method_added_spec.rb b/spec/ruby/core/basicobject/singleton_method_added_spec.rb index 8d256e22db..bd21458ea7 100644 --- a/spec/ruby/core/basicobject/singleton_method_added_spec.rb +++ b/spec/ruby/core/basicobject/singleton_method_added_spec.rb @@ -83,4 +83,65 @@ describe "BasicObject#singleton_method_added" do end ScratchPad.recorded.should == [:singleton_method_added, :new_method_with_define_method] end + + describe "when singleton_method_added is undefined" do + it "raises NoMethodError for a metaclass" do + class BasicObjectSpecs::NoSingletonMethodAdded + class << self + undef_method :singleton_method_added + end + + -> { + def self.foo + end + }.should raise_error(NoMethodError, /undefined method [`']singleton_method_added' for/) + end + ensure + BasicObjectSpecs.send(:remove_const, :NoSingletonMethodAdded) + end + + it "raises NoMethodError for a singleton instance" do + object = Object.new + class << object + undef_method :singleton_method_added + + -> { + def foo + end + }.should raise_error(NoMethodError, /undefined method [`']singleton_method_added' for #<Object:/) + + -> { + define_method(:bar) {} + }.should raise_error(NoMethodError, /undefined method [`']singleton_method_added' for #<Object:/) + end + + -> { + object.define_singleton_method(:baz) {} + }.should raise_error(NoMethodError, /undefined method [`']singleton_method_added' for #<Object:/) + end + + it "calls #method_missing" do + ScratchPad.record [] + object = Object.new + class << object + def method_missing(*args) + ScratchPad << args + end + + undef_method :singleton_method_added + + def foo + end + + define_method(:bar) {} + end + object.define_singleton_method(:baz) {} + + ScratchPad.recorded.should == [ + [:singleton_method_added, :foo], + [:singleton_method_added, :bar], + [:singleton_method_added, :baz], + ] + end + end end |
