summaryrefslogtreecommitdiff
path: root/spec/ruby/core/class
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/class')
-rw-r--r--spec/ruby/core/class/allocate_spec.rb41
-rw-r--r--spec/ruby/core/class/dup_spec.rb64
-rw-r--r--spec/ruby/core/class/fixtures/classes.rb47
-rw-r--r--spec/ruby/core/class/inherited_spec.rb102
-rw-r--r--spec/ruby/core/class/initialize_spec.rb34
-rw-r--r--spec/ruby/core/class/new_spec.rb154
-rw-r--r--spec/ruby/core/class/superclass_spec.rb27
-rw-r--r--spec/ruby/core/class/to_s_spec.rb23
8 files changed, 492 insertions, 0 deletions
diff --git a/spec/ruby/core/class/allocate_spec.rb b/spec/ruby/core/class/allocate_spec.rb
new file mode 100644
index 0000000000..015db292eb
--- /dev/null
+++ b/spec/ruby/core/class/allocate_spec.rb
@@ -0,0 +1,41 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "Class#allocate" do
+ it "returns an instance of self" do
+ klass = Class.new
+ klass.allocate.should be_an_instance_of(klass)
+ end
+
+ it "returns a fully-formed instance of Module" do
+ klass = Class.allocate
+ klass.constants.should_not == nil
+ klass.methods.should_not == nil
+ end
+
+ it "throws an exception when calling a method on a new instance" do
+ klass = Class.allocate
+ lambda do
+ klass.new
+ end.should raise_error(Exception)
+ end
+
+ it "does not call initialize on the new instance" do
+ klass = Class.new do
+ def initialize(*args)
+ @initialized = true
+ end
+
+ def initialized?
+ @initialized || false
+ end
+ end
+
+ klass.allocate.initialized?.should == false
+ end
+
+ it "raises TypeError for #superclass" do
+ lambda do
+ Class.allocate.superclass
+ end.should raise_error(TypeError)
+ end
+end
diff --git a/spec/ruby/core/class/dup_spec.rb b/spec/ruby/core/class/dup_spec.rb
new file mode 100644
index 0000000000..0548536bd6
--- /dev/null
+++ b/spec/ruby/core/class/dup_spec.rb
@@ -0,0 +1,64 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+# NOTE: This is actually implemented by Module#initialize_copy
+describe "Class#dup" do
+ it "duplicates both the class and the singleton class" do
+ klass = Class.new do
+ def hello
+ "hello"
+ end
+
+ def self.message
+ "text"
+ end
+ end
+
+ klass_dup = klass.dup
+
+ klass_dup.new.hello.should == "hello"
+ klass_dup.message.should == "text"
+ end
+
+ it "retains an included module in the ancestor chain for the singleton class" do
+ klass = Class.new
+ mod = Module.new do
+ def hello
+ "hello"
+ end
+ end
+
+ klass.extend(mod)
+ klass_dup = klass.dup
+ klass_dup.hello.should == "hello"
+ end
+
+ it "retains the correct ancestor chain for the singleton class" do
+ super_klass = Class.new do
+ def hello
+ "hello"
+ end
+
+ def self.message
+ "text"
+ end
+ end
+
+ klass = Class.new(super_klass)
+ klass_dup = klass.dup
+
+ klass_dup.new.hello.should == "hello"
+ klass_dup.message.should == "text"
+ end
+
+ it "sets the name from the class to nil if not assigned to a constant" do
+ copy = CoreClassSpecs::Record.dup
+ copy.name.should be_nil
+ end
+
+ it "stores the new name if assigned to a constant" do
+ CoreClassSpecs::RecordCopy = CoreClassSpecs::Record.dup
+ CoreClassSpecs::RecordCopy.name.should == "CoreClassSpecs::RecordCopy"
+ end
+
+end
diff --git a/spec/ruby/core/class/fixtures/classes.rb b/spec/ruby/core/class/fixtures/classes.rb
new file mode 100644
index 0000000000..f96db90795
--- /dev/null
+++ b/spec/ruby/core/class/fixtures/classes.rb
@@ -0,0 +1,47 @@
+module CoreClassSpecs
+ class Record
+ end
+
+ module M
+ def inherited(klass)
+ ScratchPad.record klass
+ super
+ end
+ end
+
+ class F; end
+ class << F
+ include M
+ end
+
+ class A
+ def self.inherited(klass)
+ ScratchPad.record klass
+ end
+ end
+
+ class H < A
+ def self.inherited(klass)
+ super
+ end
+ end
+
+ module Inherited
+ class A
+ SUBCLASSES = []
+ def self.inherited(subclass)
+ SUBCLASSES << [self, subclass]
+ end
+ end
+
+ class B < A; end
+ class B < A; end # reopen
+ class C < B; end
+
+ class D
+ def self.inherited(subclass)
+ ScratchPad << self
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/class/inherited_spec.rb b/spec/ruby/core/class/inherited_spec.rb
new file mode 100644
index 0000000000..356e46be7c
--- /dev/null
+++ b/spec/ruby/core/class/inherited_spec.rb
@@ -0,0 +1,102 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "Class.inherited" do
+
+ before :each do
+ ScratchPad.record nil
+ end
+
+ it "is invoked with the child Class when self is subclassed" do
+ begin
+ top = Class.new do
+ def self.inherited(cls)
+ $child_class = cls
+ end
+ end
+
+ child = Class.new(top)
+ $child_class.should == child
+
+ other_child = Class.new(top)
+ $child_class.should == other_child
+ ensure
+ $child_class = nil
+ end
+ end
+
+ it "is invoked only once per subclass" do
+ expected = [
+ [CoreClassSpecs::Inherited::A, CoreClassSpecs::Inherited::B],
+ [CoreClassSpecs::Inherited::B, CoreClassSpecs::Inherited::C],
+ ]
+
+ CoreClassSpecs::Inherited::A::SUBCLASSES.should == expected
+ end
+
+ it "is called when marked as a private class method" do
+ a = Class.new do
+ def self.inherited(klass)
+ ScratchPad.record klass
+ end
+ end
+ a.private_class_method :inherited
+ ScratchPad.recorded.should == nil
+ b = Class.new(a)
+ ScratchPad.recorded.should == b
+ end
+
+ it "is called when marked as a protected class method" do
+ a = Class.new
+ class << a
+ def inherited(klass)
+ ScratchPad.record klass
+ end
+ protected :inherited
+ end
+ ScratchPad.recorded.should == nil
+ b = Class.new(a)
+ ScratchPad.recorded.should == b
+ end
+
+ it "is called when marked as a public class method" do
+ a = Class.new do
+ def self.inherited(klass)
+ ScratchPad.record klass
+ end
+ end
+ a.public_class_method :inherited
+ ScratchPad.recorded.should == nil
+ b = Class.new(a)
+ ScratchPad.recorded.should == b
+ end
+
+ it "is called by super from a method provided by an included module" do
+ ScratchPad.recorded.should == nil
+ e = Class.new(CoreClassSpecs::F)
+ ScratchPad.recorded.should == e
+ end
+
+ it "is called by super even when marked as a private class method" do
+ ScratchPad.recorded.should == nil
+ CoreClassSpecs::H.private_class_method :inherited
+ i = Class.new(CoreClassSpecs::H)
+ ScratchPad.recorded.should == i
+ end
+
+ it "will be invoked by child class regardless of visibility" do
+ top = Class.new do
+ class << self
+ def inherited(cls); end
+ end
+ end
+
+ class << top; private :inherited; end
+ lambda { Class.new(top) }.should_not raise_error
+
+ class << top; protected :inherited; end
+ lambda { Class.new(top) }.should_not raise_error
+ end
+
+end
+
diff --git a/spec/ruby/core/class/initialize_spec.rb b/spec/ruby/core/class/initialize_spec.rb
new file mode 100644
index 0000000000..d268596dfe
--- /dev/null
+++ b/spec/ruby/core/class/initialize_spec.rb
@@ -0,0 +1,34 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+
+describe "Class#initialize" do
+ it "is private" do
+ Class.should have_private_method(:initialize)
+ end
+
+ it "raises a TypeError when called on already initialized classes" do
+ lambda{
+ Fixnum.send :initialize
+ }.should raise_error(TypeError)
+
+ lambda{
+ Object.send :initialize
+ }.should raise_error(TypeError)
+ end
+
+ # See [redmine:2601]
+ it "raises a TypeError when called on BasicObject" do
+ lambda{
+ BasicObject.send :initialize
+ }.should raise_error(TypeError)
+ end
+
+ describe "when given the Class" do
+ before :each do
+ @uninitialized = Class.allocate
+ end
+
+ it "raises a TypeError" do
+ lambda{@uninitialized.send(:initialize, Class)}.should raise_error(TypeError)
+ end
+ end
+end
diff --git a/spec/ruby/core/class/new_spec.rb b/spec/ruby/core/class/new_spec.rb
new file mode 100644
index 0000000000..86323b1575
--- /dev/null
+++ b/spec/ruby/core/class/new_spec.rb
@@ -0,0 +1,154 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "Class.new with a block given" do
+ it "yields the new class as self in the block" do
+ self_in_block = nil
+ klass = Class.new do
+ self_in_block = self
+ end
+ self_in_block.should equal klass
+ end
+
+ it "uses the given block as the class' body" do
+ klass = Class.new do
+ def self.message
+ "text"
+ end
+
+ def hello
+ "hello again"
+ end
+ end
+
+ klass.message.should == "text"
+ klass.new.hello.should == "hello again"
+ end
+
+ it "creates a subclass of the given superclass" do
+ sc = Class.new do
+ def self.body
+ @body
+ end
+ @body = self
+ def message; "text"; end
+ end
+ klass = Class.new(sc) do
+ def self.body
+ @body
+ end
+ @body = self
+ def message2; "hello"; end
+ end
+
+ klass.body.should == klass
+ sc.body.should == sc
+ klass.superclass.should == sc
+ klass.new.message.should == "text"
+ klass.new.message2.should == "hello"
+ end
+
+ it "runs the inherited hook after yielding the block" do
+ ScratchPad.record []
+ klass = Class.new(CoreClassSpecs::Inherited::D) do
+ ScratchPad << self
+ end
+
+ ScratchPad.recorded.should == [CoreClassSpecs::Inherited::D, klass]
+ end
+end
+
+describe "Class.new" do
+ it "creates a new anonymous class" do
+ klass = Class.new
+ klass.is_a?(Class).should == true
+
+ klass_instance = klass.new
+ klass_instance.is_a?(klass).should == true
+ end
+
+ it "raises a TypeError if passed a metaclass" do
+ obj = mock("Class.new metaclass")
+ meta = obj.singleton_class
+ lambda { Class.new meta }.should raise_error(TypeError)
+ end
+
+ it "creates a class without a name" do
+ Class.new.name.should be_nil
+ end
+
+ it "creates a class that can be given a name by assigning it to a constant" do
+ ::MyClass = Class.new
+ ::MyClass.name.should == "MyClass"
+ a = Class.new
+ MyClass::NestedClass = a
+ MyClass::NestedClass.name.should == "MyClass::NestedClass"
+ end
+
+ it "sets the new class' superclass to the given class" do
+ top = Class.new
+ Class.new(top).superclass.should == top
+ end
+
+ it "sets the new class' superclass to Object when no class given" do
+ Class.new.superclass.should == Object
+ end
+
+ it "raises a TypeError when given a non-Class" do
+ error_msg = /superclass must be a Class/
+ lambda { Class.new("") }.should raise_error(TypeError, error_msg)
+ lambda { Class.new(1) }.should raise_error(TypeError, error_msg)
+ lambda { Class.new(:symbol) }.should raise_error(TypeError, error_msg)
+ lambda { Class.new(mock('o')) }.should raise_error(TypeError, error_msg)
+ lambda { Class.new(Module.new) }.should raise_error(TypeError, error_msg)
+ end
+end
+
+describe "Class#new" do
+ it "returns a new instance of self" do
+ klass = Class.new
+ klass.new.is_a?(klass).should == true
+ end
+
+ it "invokes #initialize on the new instance with the given args" do
+ klass = Class.new do
+ def initialize(*args)
+ @initialized = true
+ @args = args
+ end
+
+ def args
+ @args
+ end
+
+ def initialized?
+ @initialized || false
+ end
+ end
+
+ klass.new.initialized?.should == true
+ klass.new(1, 2, 3).args.should == [1, 2, 3]
+ end
+
+ it "uses the internal allocator and does not call #allocate" do
+ klass = Class.new do
+ def self.allocate
+ raise "allocate should not be called"
+ end
+ end
+
+ instance = klass.new
+ instance.should be_kind_of klass
+ instance.class.should equal klass
+ end
+
+ it "passes the block to #initialize" do
+ klass = Class.new do
+ def initialize
+ yield
+ end
+ end
+
+ klass.new { break 42 }.should == 42
+ end
+end
diff --git a/spec/ruby/core/class/superclass_spec.rb b/spec/ruby/core/class/superclass_spec.rb
new file mode 100644
index 0000000000..18b7ce5bde
--- /dev/null
+++ b/spec/ruby/core/class/superclass_spec.rb
@@ -0,0 +1,27 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "Class#superclass" do
+ it "returns the superclass of self" do
+ BasicObject.superclass.should be_nil
+ Object.superclass.should == BasicObject
+ Class.superclass.should == Module
+ Class.new.superclass.should == Object
+ Class.new(String).superclass.should == String
+ Class.new(Fixnum).superclass.should == Fixnum
+ end
+
+ # redmine:567
+ describe "for a singleton class" do
+ it "of an object returns the class of the object" do
+ a = CoreClassSpecs::A.new
+ sc = class << a; self; end
+ sc.superclass.should == CoreClassSpecs::A
+ end
+
+ it "of a class returns the singleton class of its superclass" do # sorry, can't find a simpler way to express this...
+ sc = class << CoreClassSpecs::H; self; end
+ sc.superclass.should == class << CoreClassSpecs::A; self; end
+ end
+ end
+end
diff --git a/spec/ruby/core/class/to_s_spec.rb b/spec/ruby/core/class/to_s_spec.rb
new file mode 100644
index 0000000000..693517f0da
--- /dev/null
+++ b/spec/ruby/core/class/to_s_spec.rb
@@ -0,0 +1,23 @@
+require File.expand_path('../../../spec_helper', __FILE__)
+require File.expand_path('../fixtures/classes', __FILE__)
+
+describe "Class#to_s" do
+ it 'regular class returns same name as Module#to_s' do
+ String.to_s.should == 'String'
+ end
+
+ describe 'singleton class' do
+ it 'for modules includes module name' do
+ CoreClassSpecs.singleton_class.to_s.should == '#<Class:CoreClassSpecs>'
+ end
+
+ it 'for classes includes class name' do
+ CoreClassSpecs::Record.singleton_class.to_s.should == '#<Class:CoreClassSpecs::Record>'
+ end
+
+ it 'for objects includes class name and object ID' do
+ obj = CoreClassSpecs::Record.new
+ obj.singleton_class.to_s.should =~ /#<Class:#<CoreClassSpecs::Record:0x[0-9a-f]+>>/
+ end
+ end
+end