diff options
author | eregon <eregon@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-09-20 20:18:52 +0000 |
---|---|---|
committer | eregon <eregon@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-09-20 20:18:52 +0000 |
commit | 1d15d5f08032acf1b7bceacbb450d617ff6e0931 (patch) | |
tree | a3785a79899302bc149e4a6e72f624ac27dc1f10 /spec/ruby/core/kernel | |
parent | 75bfc6440d595bf339007f4fb280fd4d743e89c1 (diff) | |
download | ruby-1d15d5f08032acf1b7bceacbb450d617ff6e0931.tar.gz |
Move spec/rubyspec to spec/ruby for consistency
* Other ruby implementations use the spec/ruby directory.
[Misc #13792] [ruby-core:82287]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59979 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'spec/ruby/core/kernel')
136 files changed, 9082 insertions, 0 deletions
diff --git a/spec/ruby/core/kernel/Array_spec.rb b/spec/ruby/core/kernel/Array_spec.rb new file mode 100644 index 0000000000..6031a828f6 --- /dev/null +++ b/spec/ruby/core/kernel/Array_spec.rb @@ -0,0 +1,97 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel" do + it "has private instance method Array()" do + Kernel.should have_private_instance_method(:Array) + end +end + +describe :kernel_Array, shared: true do + before :each do + @array = [1, 2, 3] + end + + it "does not call #to_ary on an Array" do + @array.should_not_receive(:to_ary) + @object.send(@method, @array).should == @array + end + + it "calls #to_ary to convert the argument to an Array" do + obj = mock("Array([1,2,3])") + obj.should_receive(:to_ary).and_return(@array) + obj.should_not_receive(:to_a) + + @object.send(@method, obj).should == @array + end + + it "does not call #to_a on an Array" do + @array.should_not_receive(:to_a) + @object.send(@method, @array).should == @array + end + + it "calls #to_a if the argument does not respond to #to_ary" do + obj = mock("Array([1,2,3])") + obj.should_receive(:to_a).and_return(@array) + + @object.send(@method, obj).should == @array + end + + it "calls #to_a if #to_ary returns nil" do + obj = mock("Array([1,2,3])") + obj.should_receive(:to_ary).and_return(nil) + obj.should_receive(:to_a).and_return(@array) + + @object.send(@method, obj).should == @array + end + + it "returns an Array containing the argument if #to_a returns nil" do + obj = mock("Array([1,2,3])") + obj.should_receive(:to_a).and_return(nil) + + @object.send(@method, obj).should == [obj] + end + + it "calls #to_ary first, even if it's private" do + obj = KernelSpecs::PrivateToAry.new + + @object.send(@method, obj).should == [1, 2] + end + + it "calls #to_a if #to_ary is not defined, even if it's private" do + obj = KernelSpecs::PrivateToA.new + + @object.send(@method, obj).should == [3, 4] + end + + it "returns an Array containing the argument if it responds to neither #to_ary nor #to_a" do + obj = mock("Array(x)") + @object.send(@method, obj).should == [obj] + end + + it "returns an empty Array when passed nil" do + @object.send(@method, nil).should == [] + end + + it "raises a TypeError if #to_ary does not return an Array" do + obj = mock("Array() string") + obj.should_receive(:to_ary).and_return("string") + + lambda { @object.send(@method, obj) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_a does not return an Array" do + obj = mock("Array() string") + obj.should_receive(:to_a).and_return("string") + + lambda { @object.send(@method, obj) }.should raise_error(TypeError) + end +end + +describe "Kernel.Array" do + it_behaves_like :kernel_Array, :Array_method, KernelSpecs +end + +describe "Kernel#Array" do + it_behaves_like :kernel_Array, :Array_function, KernelSpecs +end diff --git a/spec/ruby/core/kernel/Complex_spec.rb b/spec/ruby/core/kernel/Complex_spec.rb new file mode 100644 index 0000000000..b156cc4549 --- /dev/null +++ b/spec/ruby/core/kernel/Complex_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/complex/Complex', __FILE__) + +describe "Kernel.Complex()" do + it_behaves_like :kernel_Complex, :Complex +end diff --git a/spec/ruby/core/kernel/Float_spec.rb b/spec/ruby/core/kernel/Float_spec.rb new file mode 100644 index 0000000000..ee20190094 --- /dev/null +++ b/spec/ruby/core/kernel/Float_spec.rb @@ -0,0 +1,316 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_float, shared: true do + it "returns the identical Float for numeric Floats" do + float = 1.12 + float2 = @object.send(:Float, float) + float2.should == float + float2.object_id.should == float.object_id + end + + it "returns a Float for Fixnums" do + @object.send(:Float, 1).should == 1.0 + end + + it "returns a Float for Complex with only a real part" do + @object.send(:Float, Complex(1)).should == 1.0 + end + + it "returns a Float for Bignums" do + @object.send(:Float, 1000000000000).should == 1000000000000.0 + end + + it "raises an ArgumentError for nil" do + lambda { @object.send(:Float, nil) }.should raise_error(TypeError) + end + + it "returns the identical NaN for NaN" do + nan = nan_value + nan.nan?.should be_true + nan2 = @object.send(:Float, nan) + nan2.nan?.should be_true + nan2.should equal(nan) + end + + it "returns the same Infinity for Infinity" do + infinity = infinity_value + infinity2 = @object.send(:Float, infinity) + infinity2.should == infinity_value + infinity.should equal(infinity2) + end + + it "converts Strings to floats without calling #to_f" do + string = "10" + string.should_not_receive(:to_f) + @object.send(:Float, string).should == 10.0 + end + + it "converts Strings with decimal points into Floats" do + @object.send(:Float, "10.0").should == 10.0 + end + + it "raises an ArgumentError for a String of word characters" do + lambda { @object.send(:Float, "float") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are two decimal points in the String" do + lambda { @object.send(:Float, "10.0.0") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String of numbers followed by word characters" do + lambda { @object.send(:Float, "10D") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String of word characters followed by numbers" do + lambda { @object.send(:Float, "D10") }.should raise_error(ArgumentError) + end + + it "is strict about the string form even across newlines" do + lambda { @object.send(:Float, "not a number\n10") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "10\nnot a number") }.should raise_error(ArgumentError) + end + + it "converts String subclasses to floats without calling #to_f" do + my_string = Class.new(String) do + def to_f() 1.2 end + end + + @object.send(:Float, my_string.new("10")).should == 10.0 + end + + it "returns a positive Float if the string is prefixed with +" do + @object.send(:Float, "+10").should == 10.0 + @object.send(:Float, " +10").should == 10.0 + end + + it "returns a negative Float if the string is prefixed with +" do + @object.send(:Float, "-10").should == -10.0 + @object.send(:Float, " -10").should == -10.0 + end + + it "raises an ArgumentError if a + or - is embedded in a String" do + lambda { @object.send(:Float, "1+1") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "1-1") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if a String has a trailing + or -" do + lambda { @object.send(:Float, "11+") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "11-") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String with a leading _" do + lambda { @object.send(:Float, "_1") }.should raise_error(ArgumentError) + end + + it "returns a value for a String with an embedded _" do + @object.send(:Float, "1_000").should == 1000.0 + end + + it "raises an ArgumentError for a String with a trailing _" do + lambda { @object.send(:Float, "10_") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String of \\0" do + lambda { @object.send(:Float, "\0") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String with a leading \\0" do + lambda { @object.send(:Float, "\01") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String with an embedded \\0" do + lambda { @object.send(:Float, "1\01") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String with a trailing \\0" do + lambda { @object.send(:Float, "1\0") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String that is just an empty space" do + lambda { @object.send(:Float, " ") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a String that with an embedded space" do + lambda { @object.send(:Float, "1 2") }.should raise_error(ArgumentError) + end + + it "returns a value for a String with a leading space" do + @object.send(:Float, " 1").should == 1.0 + end + + it "returns a value for a String with a trailing space" do + @object.send(:Float, "1 ").should == 1.0 + end + + it "returns a value for a String with any leading whitespace" do + @object.send(:Float, "\t\n1").should == 1.0 + end + + it "returns a value for a String with any trailing whitespace" do + @object.send(:Float, "1\t\n").should == 1.0 + end + + %w(e E).each do |e| + it "raises an ArgumentError if #{e} is the trailing character" do + lambda { @object.send(:Float, "2#{e}") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if #{e} is the leading character" do + lambda { @object.send(:Float, "#{e}2") }.should raise_error(ArgumentError) + end + + it "returns Infinity for '2#{e}1000'" do + @object.send(:Float, "2#{e}1000").should == Float::INFINITY + end + + it "returns 0 for '2#{e}-1000'" do + @object.send(:Float, "2#{e}-1000").should == 0 + end + + it "allows embedded _ in a number on either side of the #{e}" do + @object.send(:Float, "2_0#{e}100").should == 20e100 + @object.send(:Float, "20#{e}1_00").should == 20e100 + @object.send(:Float, "2_0#{e}1_00").should == 20e100 + end + + it "raises an exception if a space is embedded on either side of the '#{e}'" do + lambda { @object.send(:Float, "2 0#{e}100") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "20#{e}1 00") }.should raise_error(ArgumentError) + end + + it "raises an exception if there's a leading _ on either side of the '#{e}'" do + lambda { @object.send(:Float, "_20#{e}100") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "20#{e}_100") }.should raise_error(ArgumentError) + end + + it "raises an exception if there's a trailing _ on either side of the '#{e}'" do + lambda { @object.send(:Float, "20_#{e}100") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "20#{e}100_") }.should raise_error(ArgumentError) + end + + it "allows decimal points on the left side of the '#{e}'" do + @object.send(:Float, "2.0#{e}2").should == 2e2 + end + + it "raises an ArgumentError if there's a decimal point on the right side of the '#{e}'" do + lambda { @object.send(:Float, "20#{e}2.0") }.should raise_error(ArgumentError) + end + end + + describe "for hexadecimal literals with binary exponent" do + %w(p P).each do |p| + it "interprets the fractional part (on the left side of '#{p}') in hexadecimal" do + @object.send(:Float, "0x10#{p}0").should == 16.0 + end + + it "interprets the exponent (on the right of '#{p}') in decimal" do + @object.send(:Float, "0x1#{p}10").should == 1024.0 + end + + it "raises an ArgumentError if #{p} is the trailing character" do + lambda { @object.send(:Float, "0x1#{p}") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if #{p} is the leading character" do + lambda { @object.send(:Float, "0x#{p}1") }.should raise_error(ArgumentError) + end + + it "returns Infinity for '0x1#{p}10000'" do + @object.send(:Float, "0x1#{p}10000").should == Float::INFINITY + end + + it "returns 0 for '0x1#{p}-10000'" do + @object.send(:Float, "0x1#{p}-10000").should == 0 + end + + it "allows embedded _ in a number on either side of the #{p}" do + @object.send(:Float, "0x1_0#{p}10").should == 16384.0 + @object.send(:Float, "0x10#{p}1_0").should == 16384.0 + @object.send(:Float, "0x1_0#{p}1_0").should == 16384.0 + end + + it "raises an exception if a space is embedded on either side of the '#{p}'" do + lambda { @object.send(:Float, "0x1 0#{p}10") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "0x10#{p}1 0") }.should raise_error(ArgumentError) + end + + it "raises an exception if there's a leading _ on either side of the '#{p}'" do + lambda { @object.send(:Float, "0x_10#{p}10") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "0x10#{p}_10") }.should raise_error(ArgumentError) + end + + it "raises an exception if there's a trailing _ on either side of the '#{p}'" do + lambda { @object.send(:Float, "0x10_#{p}10") }.should raise_error(ArgumentError) + lambda { @object.send(:Float, "0x10#{p}10_") }.should raise_error(ArgumentError) + end + + it "allows hexadecimal points on the left side of the '#{p}'" do + @object.send(:Float, "0x1.8#{p}0").should == 1.5 + end + + it "raises an ArgumentError if there's a decimal point on the right side of the '#{p}'" do + lambda { @object.send(:Float, "0x1#{p}1.0") }.should raise_error(ArgumentError) + end + end + end + + it "returns a Float that can be a parameter to #Float again" do + float = @object.send(:Float, "10") + @object.send(:Float, float).should == 10.0 + end + + it "otherwise, converts the given argument to a Float by calling #to_f" do + (obj = mock('1.2')).should_receive(:to_f).once.and_return(1.2) + obj.should_not_receive(:to_i) + @object.send(:Float, obj).should == 1.2 + end + + it "returns the identical NaN if to_f is called and it returns NaN" do + nan = nan_value + (nan_to_f = mock('NaN')).should_receive(:to_f).once.and_return(nan) + nan2 = @object.send(:Float, nan_to_f) + nan2.nan?.should be_true + nan2.should equal(nan) + end + + it "returns the identical Infinity if to_f is called and it returns Infinity" do + infinity = infinity_value + (infinity_to_f = mock('Infinity')).should_receive(:to_f).once.and_return(infinity) + infinity2 = @object.send(:Float, infinity_to_f) + infinity2.should equal(infinity) + end + + it "raises a TypeError if #to_f is not provided" do + lambda { @object.send(:Float, mock('x')) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_f returns a String" do + (obj = mock('ha!')).should_receive(:to_f).once.and_return('ha!') + lambda { @object.send(:Float, obj) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_f returns an Integer" do + (obj = mock('123')).should_receive(:to_f).once.and_return(123) + lambda { @object.send(:Float, obj) }.should raise_error(TypeError) + end + + it "raises a RangeError when passed a Complex argument" do + c = Complex(2, 3) + lambda { @object.send(:Float, c) }.should raise_error(RangeError) + end +end + +describe "Kernel.Float" do + it_behaves_like :kernel_float, :Float, Kernel +end + +describe "Kernel#Float" do + it_behaves_like :kernel_float, :Float, Object.new +end + +describe "Kernel#Float" do + it "is a private method" do + Kernel.should have_private_instance_method(:Float) + end +end diff --git a/spec/ruby/core/kernel/Hash_spec.rb b/spec/ruby/core/kernel/Hash_spec.rb new file mode 100644 index 0000000000..8d51316c75 --- /dev/null +++ b/spec/ruby/core/kernel/Hash_spec.rb @@ -0,0 +1,57 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#hash" do + it "needs to be reviewed for spec completeness" +end + +describe "Kernel" do + it "has private instance method Hash()" do + Kernel.should have_private_instance_method(:Hash) + end +end + +describe :kernel_Hash, shared: true do + before :each do + @hash = { a: 1} + end + + it "converts nil to a Hash" do + @object.send(@method, nil).should == {} + end + + it "converts an empty array to a Hash" do + @object.send(@method, []).should == {} + end + + it "does not call #to_hash on an Hash" do + @hash.should_not_receive(:to_hash) + @object.send(@method, @hash).should == @hash + end + + it "calls #to_hash to convert the argument to an Hash" do + obj = mock("Hash(a: 1)") + obj.should_receive(:to_hash).and_return(@hash) + + @object.send(@method, obj).should == @hash + end + + it "raises a TypeError if it doesn't respond to #to_hash" do + lambda { @object.send(@method, mock("")) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_hash does not return an Hash" do + obj = mock("Hash() string") + obj.should_receive(:to_hash).and_return("string") + + lambda { @object.send(@method, obj) }.should raise_error(TypeError) + end +end + +describe "Kernel.Hash" do + it_behaves_like :kernel_Hash, :Hash_method, KernelSpecs +end + +describe "Kernel#Hash" do + it_behaves_like :kernel_Hash, :Hash_function, KernelSpecs +end diff --git a/spec/ruby/core/kernel/Integer_spec.rb b/spec/ruby/core/kernel/Integer_spec.rb new file mode 100644 index 0000000000..1e95fc9151 --- /dev/null +++ b/spec/ruby/core/kernel/Integer_spec.rb @@ -0,0 +1,697 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_integer, shared: true do + it "returns a Bignum for a Bignum" do + Integer(2e100).should == 2e100 + end + + it "returns a Fixnum for a Fixnum" do + Integer(100).should == 100 + end + + it "uncritically return the value of to_int even if it is not an Integer" do + obj = mock("object") + obj.should_receive(:to_int).and_return("1") + obj.should_not_receive(:to_i) + Integer(obj).should == "1" + end + + it "raises a TypeError when passed nil" do + lambda { Integer(nil) }.should raise_error(TypeError) + end + + it "returns a Fixnum or Bignum object" do + Integer(2).should be_an_instance_of(Fixnum) + Integer(9**99).should be_an_instance_of(Bignum) + end + + it "truncates Floats" do + Integer(3.14).should == 3 + Integer(90.8).should == 90 + end + + it "calls to_i on Rationals" do + Integer(Rational(8,3)).should == 2 + Integer(3.quo(2)).should == 1 + end + + it "returns the value of to_int if the result is a Fixnum" do + obj = mock("object") + obj.should_receive(:to_int).and_return(1) + obj.should_not_receive(:to_i) + Integer(obj).should == 1 + end + + it "returns the value of to_int if the result is a Bignum" do + obj = mock("object") + obj.should_receive(:to_int).and_return(2e100) + obj.should_not_receive(:to_i) + Integer(obj).should == 2e100 + end + + it "calls to_i on an object whose to_int returns nil" do + obj = mock("object") + obj.should_receive(:to_int).and_return(nil) + obj.should_receive(:to_i).and_return(1) + Integer(obj).should == 1 + end + + it "raises a TypeError if to_i returns a value that is not an Integer" do + obj = mock("object") + obj.should_receive(:to_i).and_return("1") + lambda { Integer(obj) }.should raise_error(TypeError) + end + + it "raises a TypeError if no to_int or to_i methods exist" do + obj = mock("object") + lambda { Integer(obj) }.should raise_error(TypeError) + end + + it "raises a TypeError if to_int returns nil and no to_i exists" do + obj = mock("object") + obj.should_receive(:to_i).and_return(nil) + lambda { Integer(obj) }.should raise_error(TypeError) + end + + it "raises a FloatDomainError when passed NaN" do + lambda { Integer(nan_value) }.should raise_error(FloatDomainError) + end + + it "raises a FloatDomainError when passed Infinity" do + lambda { Integer(infinity_value) }.should raise_error(FloatDomainError) + end +end + +describe "Integer() given a String", shared: true do + it "raises an ArgumentError if the String is a null byte" do + lambda { Integer("\0") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the String starts with a null byte" do + lambda { Integer("\01") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the String ends with a null byte" do + lambda { Integer("1\0") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the String contains a null byte" do + lambda { Integer("1\01") }.should raise_error(ArgumentError) + end + + it "ignores leading whitespace" do + Integer(" 1").should == 1 + Integer(" 1").should == 1 + Integer("\t\n1").should == 1 + end + + it "ignores trailing whitespace" do + Integer("1 ").should == 1 + Integer("1 ").should == 1 + Integer("1\t\n").should == 1 + end + + it "raises an ArgumentError if there are leading _s" do + lambda { Integer("_1") }.should raise_error(ArgumentError) + lambda { Integer("___1") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are trailing _s" do + lambda { Integer("1_") }.should raise_error(ArgumentError) + lambda { Integer("1___") }.should raise_error(ArgumentError) + end + + it "ignores an embedded _" do + Integer("1_1").should == 11 + end + + it "raises an ArgumentError if there are multiple embedded _s" do + lambda { Integer("1__1") }.should raise_error(ArgumentError) + lambda { Integer("1___1") }.should raise_error(ArgumentError) + end + + it "ignores a single leading +" do + Integer("+1").should == 1 + end + + it "raises an ArgumentError if there is a space between the + and number" do + lambda { Integer("+ 1") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are multiple leading +s" do + lambda { Integer("++1") }.should raise_error(ArgumentError) + lambda { Integer("+++1") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are trailing +s" do + lambda { Integer("1+") }.should raise_error(ArgumentError) + lambda { Integer("1+++") }.should raise_error(ArgumentError) + end + + it "makes the number negative if there's a leading -" do + Integer("-1").should == -1 + end + + it "raises an ArgumentError if there are multiple leading -s" do + lambda { Integer("--1") }.should raise_error(ArgumentError) + lambda { Integer("---1") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are trailing -s" do + lambda { Integer("1-") }.should raise_error(ArgumentError) + lambda { Integer("1---") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there is a period" do + lambda { Integer("0.0") }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for an empty String" do + lambda { Integer("") }.should raise_error(ArgumentError) + end + + it "parses the value as 0 if the string consists of a single zero character" do + Integer("0").should == 0 + end + + %w(x X).each do |x| + it "parses the value as a hex number if there's a leading 0#{x}" do + Integer("0#{x}1").should == 0x1 + Integer("0#{x}dd").should == 0xdd + end + + it "is a positive hex number if there's a leading +0#{x}" do + Integer("+0#{x}1").should == 0x1 + Integer("+0#{x}dd").should == 0xdd + end + + it "is a negative hex number if there's a leading -0#{x}" do + Integer("-0#{x}1").should == -0x1 + Integer("-0#{x}dd").should == -0xdd + end + + it "raises an ArgumentError if the number cannot be parsed as hex" do + lambda { Integer("0#{x}g") }.should raise_error(ArgumentError) + end + end + + %w(b B).each do |b| + it "parses the value as a binary number if there's a leading 0#{b}" do + Integer("0#{b}1").should == 0b1 + Integer("0#{b}10").should == 0b10 + end + + it "is a positive binary number if there's a leading +0#{b}" do + Integer("+0#{b}1").should == 0b1 + Integer("+0#{b}10").should == 0b10 + end + + it "is a negative binary number if there's a leading -0#{b}" do + Integer("-0#{b}1").should == -0b1 + Integer("-0#{b}10").should == -0b10 + end + + it "raises an ArgumentError if the number cannot be parsed as binary" do + lambda { Integer("0#{b}2") }.should raise_error(ArgumentError) + end + end + + ["o", "O", ""].each do |o| + it "parses the value as an octal number if there's a leading 0#{o}" do + Integer("0#{o}1").should == 0O1 + Integer("0#{o}10").should == 0O10 + end + + it "is a positive octal number if there's a leading +0#{o}" do + Integer("+0#{o}1").should == 0O1 + Integer("+0#{o}10").should == 0O10 + end + + it "is a negative octal number if there's a leading -0#{o}" do + Integer("-0#{o}1").should == -0O1 + Integer("-0#{o}10").should == -0O10 + end + + it "raises an ArgumentError if the number cannot be parsed as octal" do + lambda { Integer("0#{o}9") }.should raise_error(ArgumentError) + end + end + + %w(D d).each do |d| + it "parses the value as a decimal number if there's a leading 0#{d}" do + Integer("0#{d}1").should == 1 + Integer("0#{d}10").should == 10 + end + + it "is a positive decimal number if there's a leading +0#{d}" do + Integer("+0#{d}1").should == 1 + Integer("+0#{d}10").should == 10 + end + + it "is a negative decimal number if there's a leading -0#{d}" do + Integer("-0#{d}1").should == -1 + Integer("-0#{d}10").should == -10 + end + + it "raises an ArgumentError if the number cannot be parsed as decimal" do + lambda { Integer("0#{d}a") }.should raise_error(ArgumentError) + end + end +end + +describe "Integer() given a String and base", shared: true do + it "raises an ArgumentError if the String is a null byte" do + lambda { Integer("\0", 2) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the String starts with a null byte" do + lambda { Integer("\01", 3) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the String ends with a null byte" do + lambda { Integer("1\0", 4) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if the String contains a null byte" do + lambda { Integer("1\01", 5) }.should raise_error(ArgumentError) + end + + it "ignores leading whitespace" do + Integer(" 16", 16).should == 22 + Integer(" 16", 16).should == 22 + Integer("\t\n16", 16).should == 22 + end + + it "ignores trailing whitespace" do + Integer("16 ", 16).should == 22 + Integer("16 ", 16).should == 22 + Integer("16\t\n", 16).should == 22 + end + + it "raises an ArgumentError if there are leading _s" do + lambda { Integer("_1", 7) }.should raise_error(ArgumentError) + lambda { Integer("___1", 7) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are trailing _s" do + lambda { Integer("1_", 12) }.should raise_error(ArgumentError) + lambda { Integer("1___", 12) }.should raise_error(ArgumentError) + end + + it "ignores an embedded _" do + Integer("1_1", 4).should == 5 + end + + it "raises an ArgumentError if there are multiple embedded _s" do + lambda { Integer("1__1", 4) }.should raise_error(ArgumentError) + lambda { Integer("1___1", 4) }.should raise_error(ArgumentError) + end + + it "ignores a single leading +" do + Integer("+10", 3).should == 3 + end + + it "raises an ArgumentError if there is a space between the + and number" do + lambda { Integer("+ 1", 3) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are multiple leading +s" do + lambda { Integer("++1", 3) }.should raise_error(ArgumentError) + lambda { Integer("+++1", 3) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are trailing +s" do + lambda { Integer("1+", 3) }.should raise_error(ArgumentError) + lambda { Integer("1+++", 12) }.should raise_error(ArgumentError) + end + + it "makes the number negative if there's a leading -" do + Integer("-19", 20).should == -29 + end + + it "raises an ArgumentError if there are multiple leading -s" do + lambda { Integer("--1", 9) }.should raise_error(ArgumentError) + lambda { Integer("---1", 9) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there are trailing -s" do + lambda { Integer("1-", 12) }.should raise_error(ArgumentError) + lambda { Integer("1---", 12) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if there is a period" do + lambda { Integer("0.0", 3) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for an empty String" do + lambda { Integer("", 12) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a base of 1" do + lambda { Integer("1", 1) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError for a base of 37" do + lambda { Integer("1", 37) }.should raise_error(ArgumentError) + end + + it "accepts wholly lowercase alphabetic strings for bases > 10" do + Integer('ab',12).should == 131 + Integer('af',20).should == 215 + Integer('ghj',30).should == 14929 + end + + it "accepts wholly uppercase alphabetic strings for bases > 10" do + Integer('AB',12).should == 131 + Integer('AF',20).should == 215 + Integer('GHJ',30).should == 14929 + end + + it "accepts mixed-case alphabetic strings for bases > 10" do + Integer('Ab',12).should == 131 + Integer('aF',20).should == 215 + Integer('GhJ',30).should == 14929 + end + + it "accepts alphanumeric strings for bases > 10" do + Integer('a3e',19).should == 3681 + Integer('12q',31).should == 1049 + Integer('c00o',29).should == 292692 + end + + it "raises an ArgumentError for letters invalid in the given base" do + lambda { Integer('z',19) }.should raise_error(ArgumentError) + lambda { Integer('c00o',2) }.should raise_error(ArgumentError) + end + + %w(x X).each do |x| + it "parses the value as a hex number if there's a leading 0#{x} and a base of 16" do + Integer("0#{x}10", 16).should == 16 + Integer("0#{x}dd", 16).should == 221 + end + + it "is a positive hex number if there's a leading +0#{x} and base of 16" do + Integer("+0#{x}1", 16).should == 0x1 + Integer("+0#{x}dd", 16).should == 0xdd + end + + it "is a negative hex number if there's a leading -0#{x} and a base of 16" do + Integer("-0#{x}1", 16).should == -0x1 + Integer("-0#{x}dd", 16).should == -0xdd + end + + 2.upto(15) do |base| + it "raises an ArgumentError if the number begins with 0#{x} and the base is #{base}" do + lambda { Integer("0#{x}1", base) }.should raise_error(ArgumentError) + end + end + + it "raises an ArgumentError if the number cannot be parsed as hex and the base is 16" do + lambda { Integer("0#{x}g", 16) }.should raise_error(ArgumentError) + end + end + + %w(b B).each do |b| + it "parses the value as a binary number if there's a leading 0#{b} and the base is 2" do + Integer("0#{b}1", 2).should == 0b1 + Integer("0#{b}10", 2).should == 0b10 + end + + it "is a positive binary number if there's a leading +0#{b} and a base of 2" do + Integer("+0#{b}1", 2).should == 0b1 + Integer("+0#{b}10", 2).should == 0b10 + end + + it "is a negative binary number if there's a leading -0#{b} and a base of 2" do + Integer("-0#{b}1", 2).should == -0b1 + Integer("-0#{b}10", 2).should == -0b10 + end + + it "raises an ArgumentError if the number cannot be parsed as binary and the base is 2" do + lambda { Integer("0#{b}2", 2) }.should raise_error(ArgumentError) + end + end + + ["o", "O"].each do |o| + it "parses the value as an octal number if there's a leading 0#{o} and a base of 8" do + Integer("0#{o}1", 8).should == 0O1 + Integer("0#{o}10", 8).should == 0O10 + end + + it "is a positive octal number if there's a leading +0#{o} and a base of 8" do + Integer("+0#{o}1", 8).should == 0O1 + Integer("+0#{o}10", 8).should == 0O10 + end + + it "is a negative octal number if there's a leading -0#{o} and a base of 8" do + Integer("-0#{o}1", 8).should == -0O1 + Integer("-0#{o}10", 8).should == -0O10 + end + + it "raises an ArgumentError if the number cannot be parsed as octal and the base is 8" do + lambda { Integer("0#{o}9", 8) }.should raise_error(ArgumentError) + end + + 2.upto(7) do |base| + it "raises an ArgumentError if the number begins with 0#{o} and the base is #{base}" do + lambda { Integer("0#{o}1", base) }.should raise_error(ArgumentError) + end + end + end + + %w(D d).each do |d| + it "parses the value as a decimal number if there's a leading 0#{d} and a base of 10" do + Integer("0#{d}1", 10).should == 1 + Integer("0#{d}10",10).should == 10 + end + + it "is a positive decimal number if there's a leading +0#{d} and a base of 10" do + Integer("+0#{d}1", 10).should == 1 + Integer("+0#{d}10", 10).should == 10 + end + + it "is a negative decimal number if there's a leading -0#{d} and a base of 10" do + Integer("-0#{d}1", 10).should == -1 + Integer("-0#{d}10", 10).should == -10 + end + + it "raises an ArgumentError if the number cannot be parsed as decimal and the base is 10" do + lambda { Integer("0#{d}a", 10) }.should raise_error(ArgumentError) + end + + 2.upto(9) do |base| + it "raises an ArgumentError if the number begins with 0#{d} and the base is #{base}" do + lambda { Integer("0#{d}1", base) }.should raise_error(ArgumentError) + end + end + + it "raises an ArgumentError if a base is given for a non-String value" do + lambda { Integer(98, 15) }.should raise_error(ArgumentError) + end + end +end + +describe :kernel_Integer, shared: true do + it "raises an ArgumentError when the String contains digits out of range of radix 2" do + str = "23456789abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 2) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 3" do + str = "3456789abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 3) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 4" do + str = "456789abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 4) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 5" do + str = "56789abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 5) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 6" do + str = "6789abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 6) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 7" do + str = "789abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 7) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 8" do + str = "89abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 8) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 9" do + str = "9abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 9) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 10" do + str = "abcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 10) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 11" do + str = "bcdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 11) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 12" do + str = "cdefghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 12) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 13" do + str = "defghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 13) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 14" do + str = "efghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 14) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 15" do + str = "fghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 15) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 16" do + str = "ghijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 16) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 17" do + str = "hijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 17) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 18" do + str = "ijklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 18) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 19" do + str = "jklmnopqrstuvwxyz" + lambda { @object.send(@method, str, 19) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 20" do + str = "klmnopqrstuvwxyz" + lambda { @object.send(@method, str, 20) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 21" do + str = "lmnopqrstuvwxyz" + lambda { @object.send(@method, str, 21) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 22" do + str = "mnopqrstuvwxyz" + lambda { @object.send(@method, str, 22) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 23" do + str = "nopqrstuvwxyz" + lambda { @object.send(@method, str, 23) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 24" do + str = "opqrstuvwxyz" + lambda { @object.send(@method, str, 24) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 25" do + str = "pqrstuvwxyz" + lambda { @object.send(@method, str, 25) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 26" do + str = "qrstuvwxyz" + lambda { @object.send(@method, str, 26) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 27" do + str = "rstuvwxyz" + lambda { @object.send(@method, str, 27) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 28" do + str = "stuvwxyz" + lambda { @object.send(@method, str, 28) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 29" do + str = "tuvwxyz" + lambda { @object.send(@method, str, 29) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 30" do + str = "uvwxyz" + lambda { @object.send(@method, str, 30) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 31" do + str = "vwxyz" + lambda { @object.send(@method, str, 31) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 32" do + str = "wxyz" + lambda { @object.send(@method, str, 32) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 33" do + str = "xyz" + lambda { @object.send(@method, str, 33) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 34" do + str = "yz" + lambda { @object.send(@method, str, 34) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 35" do + str = "z" + lambda { @object.send(@method, str, 35) }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError when the String contains digits out of range of radix 36" do + lambda { @object.send(@method, "{", 36) }.should raise_error(ArgumentError) + end +end + +describe "Kernel.Integer" do + it_behaves_like :kernel_Integer, :Integer_method, KernelSpecs + + # TODO: fix these specs + it_behaves_like :kernel_integer, :Integer, Kernel + it_behaves_like "Integer() given a String", :Integer + + it_behaves_like "Integer() given a String and base", :Integer + + it "is a public method" do + Kernel.Integer(10).should == 10 + end +end + +describe "Kernel#Integer" do + it_behaves_like :kernel_Integer, :Integer_function, KernelSpecs + + # TODO: fix these specs + it_behaves_like :kernel_integer, :Integer, Object.new + it_behaves_like "Integer() given a String", :Integer + + it_behaves_like "Integer() given a String and base", :Integer + + it "is a private method" do + Kernel.should have_private_instance_method(:Integer) + end +end diff --git a/spec/ruby/core/kernel/Rational_spec.rb b/spec/ruby/core/kernel/Rational_spec.rb new file mode 100644 index 0000000000..38f1da6333 --- /dev/null +++ b/spec/ruby/core/kernel/Rational_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/rational/Rational', __FILE__) + +describe "Kernel.Rational" do + it_behaves_like :kernel_Rational, :Rational +end diff --git a/spec/ruby/core/kernel/String_spec.rb b/spec/ruby/core/kernel/String_spec.rb new file mode 100644 index 0000000000..b24bc798e5 --- /dev/null +++ b/spec/ruby/core/kernel/String_spec.rb @@ -0,0 +1,106 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_String, shared: true do + it "converts nil to a String" do + @object.send(@method, nil).should == "" + end + + it "converts a Float to a String" do + @object.send(@method, 1.12).should == "1.12" + end + + it "converts a boolean to a String" do + @object.send(@method, false).should == "false" + @object.send(@method, true).should == "true" + end + + it "converts a constant to a String" do + @object.send(@method, Object).should == "Object" + end + + it "calls #to_s to convert an arbitrary object to a String" do + obj = mock('test') + obj.should_receive(:to_s).and_return("test") + + @object.send(@method, obj).should == "test" + end + + it "raises a TypeError if #to_s does not exist" do + obj = mock('to_s') + class << obj + undef_method :to_s + end + + lambda { @object.send(@method, obj) }.should raise_error(TypeError) + end + + # #5158 + it "raises a TypeError if respond_to? returns false for #to_s" do + obj = mock("to_s") + class << obj + def respond_to?(meth, include_private=false) + meth == :to_s ? false : super + end + end + + lambda { @object.send(@method, obj) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_s is not defined, even though #respond_to?(:to_s) returns true" do + # cannot use a mock because of how RSpec affects #method_missing + obj = Object.new + class << obj + undef_method :to_s + def respond_to?(meth, include_private=false) + meth == :to_s ? true : super + end + end + + lambda { @object.send(@method, obj) }.should raise_error(TypeError) + end + + it "calls #to_s if #respond_to?(:to_s) returns true" do + obj = mock('to_s') + class << obj + undef_method :to_s + def method_missing(meth, *args) + meth == :to_s ? "test" : super + end + end + + @object.send(@method, obj).should == "test" + end + + it "raises a TypeError if #to_s does not return a String" do + (obj = mock('123')).should_receive(:to_s).and_return(123) + lambda { @object.send(@method, obj) }.should raise_error(TypeError) + end + + it "returns the same object if it is already a String" do + string = "Hello" + string.should_not_receive(:to_s) + string2 = @object.send(@method, string) + string.should equal(string2) + end + + it "returns the same object if it is an instance of a String subclass" do + subklass = Class.new(String) + string = subklass.new("Hello") + string.should_not_receive(:to_s) + string2 = @object.send(@method, string) + string.should equal(string2) + end +end + +describe "Kernel.String" do + it_behaves_like :kernel_String, :String, Kernel +end + +describe "Kernel#String" do + it_behaves_like :kernel_String, :String, Object.new + + it "is a private method" do + Kernel.should have_private_instance_method(:String) + end +end diff --git a/spec/ruby/core/kernel/__callee___spec.rb b/spec/ruby/core/kernel/__callee___spec.rb new file mode 100644 index 0000000000..91cc4cdafa --- /dev/null +++ b/spec/ruby/core/kernel/__callee___spec.rb @@ -0,0 +1,48 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/__callee__', __FILE__) + +describe "Kernel.__callee__" do + it "returns the current method, even when aliased" do + KernelSpecs::CalleeTest.new.f.should == :f + end + + it "returns the aliased name when aliased method" do + KernelSpecs::CalleeTest.new.g.should == :g + end + + it "returns the caller from blocks too" do + KernelSpecs::CalleeTest.new.in_block.should == [:in_block, :in_block] + end + + it "returns the caller from define_method too" do + KernelSpecs::CalleeTest.new.dm.should == :dm + end + + it "returns the caller from block inside define_method too" do + KernelSpecs::CalleeTest.new.dm_block.should == [:dm_block, :dm_block] + end + + it "returns method name even from send" do + KernelSpecs::CalleeTest.new.from_send.should == :from_send + end + + it "returns method name even from eval" do + KernelSpecs::CalleeTest.new.from_eval.should == :from_eval + end + + it "returns nil from inside a class body" do + KernelSpecs::CalleeTest.new.from_class_body.should == nil + end + + it "returns nil when not called from a method" do + __callee__.should == nil + end + + it "returns the caller from a define_method called from the same class" do + c = Class.new do + define_method(:f) { 1.times{ break __callee__ } } + def g; f end + end + c.new.g.should == :f + end +end diff --git a/spec/ruby/core/kernel/__dir___spec.rb b/spec/ruby/core/kernel/__dir___spec.rb new file mode 100644 index 0000000000..395d30f494 --- /dev/null +++ b/spec/ruby/core/kernel/__dir___spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Kernel#__dir__" do + it "returns the real name of the directory containing the currently-executing file" do + __dir__.should == File.realpath(File.dirname(__FILE__)) + end + + context "when used in eval with top level binding" do + it "returns the real name of the directory containing the currently-executing file" do + eval("__dir__", binding).should == File.realpath(File.dirname(__FILE__)) + end + end +end diff --git a/spec/ruby/core/kernel/__method___spec.rb b/spec/ruby/core/kernel/__method___spec.rb new file mode 100644 index 0000000000..936a6b2f00 --- /dev/null +++ b/spec/ruby/core/kernel/__method___spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/__method__', __FILE__) + +describe "Kernel.__method__" do + it "returns the current method, even when aliased" do + KernelSpecs::MethodTest.new.f.should == :f + end + + it "returns the original name when aliased method" do + KernelSpecs::MethodTest.new.g.should == :f + end + + it "returns the caller from blocks too" do + KernelSpecs::MethodTest.new.in_block.should == [:in_block, :in_block] + end + + it "returns the caller from define_method too" do + KernelSpecs::MethodTest.new.dm.should == :dm + end + + it "returns the caller from block inside define_method too" do + KernelSpecs::MethodTest.new.dm_block.should == [:dm_block, :dm_block] + end + + it "returns method name even from send" do + KernelSpecs::MethodTest.new.from_send.should == :from_send + end + + it "returns method name even from eval" do + KernelSpecs::MethodTest.new.from_eval.should == :from_eval + end + + it "returns nil from inside a class body" do + KernelSpecs::MethodTest.new.from_class_body.should == nil + end + + it "returns nil when not called from a method" do + __method__.should == nil + end +end diff --git a/spec/ruby/core/kernel/abort_spec.rb b/spec/ruby/core/kernel/abort_spec.rb new file mode 100644 index 0000000000..eb9c1c30c7 --- /dev/null +++ b/spec/ruby/core/kernel/abort_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/process/abort', __FILE__) + +describe "Kernel#abort" do + it "is a private method" do + Kernel.should have_private_instance_method(:abort) + end + + it_behaves_like :process_abort, :abort, KernelSpecs::Method.new +end + +describe "Kernel.abort" do + it_behaves_like :process_abort, :abort, Kernel +end diff --git a/spec/ruby/core/kernel/at_exit_spec.rb b/spec/ruby/core/kernel/at_exit_spec.rb new file mode 100644 index 0000000000..9fcb99148c --- /dev/null +++ b/spec/ruby/core/kernel/at_exit_spec.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.at_exit" do + it "is a private method" do + Kernel.should have_private_instance_method(:at_exit) + end + + it "runs after all other code" do + ruby_exe("at_exit {print 5}; print 6").should == "65" + end + + it "runs in reverse order of registration" do + code = "at_exit {print 4};at_exit {print 5}; print 6; at_exit {print 7}" + ruby_exe(code).should == "6754" + end + + it "allows calling exit inside at_exit handler" do + code = "at_exit {print 3}; at_exit {print 4; exit; print 5}; at_exit {print 6}" + ruby_exe(code).should == "643" + end + + it "gives access to the last raised exception" do + code = <<-EOC + at_exit do + puts "The exception matches: \#{$! == $exception}" + end + + begin + raise "foo" + rescue => $exception + raise + end + EOC + + result = ruby_exe(code, args: "2>&1", escape: true) + result.should =~ /The exception matches: true/ + end + +end + +describe "Kernel#at_exit" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/autoload_spec.rb b/spec/ruby/core/kernel/autoload_spec.rb new file mode 100644 index 0000000000..e2741de74c --- /dev/null +++ b/spec/ruby/core/kernel/autoload_spec.rb @@ -0,0 +1,121 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# These specs only illustrate the basic autoload cases +# and where toplevel autoload behaves differently from +# Module#autoload. See those specs for more examples. + +autoload :KSAutoloadA, "autoload_a.rb" +autoload :KSAutoloadB, fixture(__FILE__, "autoload_b.rb") +autoload :KSAutoloadC, fixture(__FILE__, "autoload_c.rb") + +def check_autoload(const) + autoload? const +end + +describe "Kernel#autoload" do + before :each do + @loaded_features = $".dup + end + + after :each do + $".replace @loaded_features + end + + it "is a private method" do + Kernel.should have_private_instance_method(:autoload) + end + + it "registers a file to load the first time the named constant is accessed" do + Object.autoload?(:KSAutoloadA).should == "autoload_a.rb" + end + + it "registers a file to load the first time the named constant is accessed" do + check_autoload(:KSAutoloadA).should == "autoload_a.rb" + end + + it "sets the autoload constant in Object's constant table" do + Object.should have_constant(:KSAutoloadA) + end + + it "loads the file when the constant is accessed" do + KSAutoloadB.loaded.should == :ksautoload_b + end + + it "does not call Kernel.require or Kernel.load to load the file" do + Kernel.should_not_receive(:require) + Kernel.should_not_receive(:load) + KSAutoloadC.loaded.should == :ksautoload_c + end + + it "can autoload in instance_eval" do + Object.new.instance_eval do + autoload :KSAutoloadD, fixture(__FILE__, "autoload_d.rb") + KSAutoloadD.loaded.should == :ksautoload_d + end + end + + describe "when Object is frozen" do + it "raises a RuntimeError before defining the constant" do + ruby_exe(fixture(__FILE__, "autoload_frozen.rb")).should == "RuntimeError - nil" + end + end +end + +describe "Kernel#autoload?" do + it "is a private method" do + Kernel.should have_private_instance_method(:autoload?) + end + + it "returns the name of the file that will be autoloaded" do + check_autoload(:KSAutoloadA).should == "autoload_a.rb" + end + + it "returns nil if no file has been registered for a constant" do + check_autoload(:Manualload).should be_nil + end +end + +Kernel.autoload :KSAutoloadBB, "no_autoload.rb" + +describe "Kernel.autoload" do + before :all do + @non_existent = fixture __FILE__, "no_autoload.rb" + end + + before :each do + @loaded_features = $".dup + + ScratchPad.clear + end + + after :each do + $".replace @loaded_features + end + + it "registers a file to load the first time the toplevel constant is accessed" do + Kernel.autoload :KSAutoloadAA, @non_existent + Kernel.autoload?(:KSAutoloadAA).should == @non_existent + end + + it "sets the autoload constant in Object's constant table" do + Object.should have_constant(:KSAutoloadBB) + end + + it "calls #to_path on non-String filenames" do + p = mock('path') + p.should_receive(:to_path).and_return @non_existent + Kernel.autoload :KSAutoloadAA, p + end +end + +describe "Kernel.autoload?" do + it "returns the name of the file that will be autoloaded" do + Kernel.autoload :KSAutoload, "autoload.rb" + Kernel.autoload?(:KSAutoload).should == "autoload.rb" + end + + it "returns nil if no file has been registered for a constant" do + Kernel.autoload?(:Manualload).should be_nil + end +end diff --git a/spec/ruby/core/kernel/backtick_spec.rb b/spec/ruby/core/kernel/backtick_spec.rb new file mode 100644 index 0000000000..eb25750bc2 --- /dev/null +++ b/spec/ruby/core/kernel/backtick_spec.rb @@ -0,0 +1,80 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#`" do + before :each do + @original_external = Encoding.default_external + end + + after :each do + Encoding.default_external = @original_external + end + + it "is a private method" do + Kernel.should have_private_instance_method(:`) + end + + it "returns the standard output of the executed sub-process" do + ip = 'world' + `echo disc #{ip}`.should == "disc world\n" + end + + it "lets the standard error stream pass through to the inherited stderr" do + cmd = ruby_cmd('STDERR.print "error stream"') + lambda { + `#{cmd}`.should == "" + }.should output_to_fd("error stream", STDERR) + end + + it "produces a String in the default external encoding" do + Encoding.default_external = Encoding::SHIFT_JIS + `echo disc`.encoding.should equal(Encoding::SHIFT_JIS) + end + + it "raises an Errno::ENOENT if the command is not executable" do + lambda { `nonexistent_command` }.should raise_error(Errno::ENOENT) + end + + platform_is_not :windows do + it "sets $? to the exit status of the executed sub-process" do + ip = 'world' + `echo disc #{ip}` + $?.should be_kind_of(Process::Status) + $?.stopped?.should == false + $?.exited?.should == true + $?.exitstatus.should == 0 + $?.success?.should == true + `echo disc #{ip}; exit 99` + $?.should be_kind_of(Process::Status) + $?.stopped?.should == false + $?.exited?.should == true + $?.exitstatus.should == 99 + $?.success?.should == false + end + end + + platform_is :windows do + it "sets $? to the exit status of the executed sub-process" do + ip = 'world' + `echo disc #{ip}` + $?.should be_kind_of(Process::Status) + $?.stopped?.should == false + $?.exited?.should == true + $?.exitstatus.should == 0 + $?.success?.should == true + `echo disc #{ip}& exit 99` + $?.should be_kind_of(Process::Status) + $?.stopped?.should == false + $?.exited?.should == true + $?.exitstatus.should == 99 + $?.success?.should == false + end + end +end + +describe "Kernel.`" do + it "tries to convert the given argument to String using #to_str" do + (obj = mock('echo test')).should_receive(:to_str).and_return("echo test") + Kernel.`(obj).should == "test\n" #` fix vim syntax highlighting + end +end diff --git a/spec/ruby/core/kernel/binding_spec.rb b/spec/ruby/core/kernel/binding_spec.rb new file mode 100644 index 0000000000..0b33c86d3c --- /dev/null +++ b/spec/ruby/core/kernel/binding_spec.rb @@ -0,0 +1,51 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.binding" do + it "returns a binding for the caller" do + Kernel.binding.eval("self").should == self + end +end + +describe "Kernel#binding" do + it "is a private method" do + Kernel.should have_private_instance_method(:binding) + end + + before :each do + @b1 = KernelSpecs::Binding.new(99).get_binding + ScratchPad.clear + end + + it "returns a Binding object" do + @b1.kind_of?(Binding).should == true + end + + it "encapsulates the execution context properly" do + eval("@secret", @b1).should == 100 + eval("a", @b1).should == true + eval("b", @b1).should == true + eval("@@super_secret", @b1).should == "password" + + eval("square(2)", @b1).should == 4 + eval("self.square(2)", @b1).should == 4 + + eval("a = false", @b1) + eval("a", @b1).should == false + end + + it "raises a NameError on undefined variable" do + lambda { eval("a_fake_variable", @b1) }.should raise_error(NameError) + end + + it "uses the closure's self as self in the binding" do + m = mock(:whatever) + eval('self', m.send(:binding)).should == self + end + + it "uses the class as self in a Class.new block" do + m = mock(:whatever) + cls = Class.new { ScratchPad.record eval('self', m.send(:binding)) } + ScratchPad.recorded.should == cls + end +end diff --git a/spec/ruby/core/kernel/block_given_spec.rb b/spec/ruby/core/kernel/block_given_spec.rb new file mode 100644 index 0000000000..9454c938ae --- /dev/null +++ b/spec/ruby/core/kernel/block_given_spec.rb @@ -0,0 +1,38 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_block_given, shared: true do + it "returns true if and only if a block is supplied" do + @object.accept_block {}.should == true + @object.accept_block_as_argument {}.should == true + + @object.accept_block.should == false + @object.accept_block_as_argument.should == false + end + + # Clarify: Based on http://www.ruby-forum.com/topic/137822 it appears + # that Matz wanted this to be true in 1.9. + it "returns false when a method defined by define_method is called with a block" do + @object.defined_block {}.should == false + end +end + +describe "Kernel#block_given?" do + it_behaves_like :kernel_block_given, :block_given?, KernelSpecs::BlockGiven + + it "returns false outside of a method" do + block_given?.should == false + end + + it "is a private method" do + Kernel.should have_private_instance_method(:block_given?) + end +end + +describe "Kernel.block_given?" do + it_behaves_like :kernel_block_given, :block_given?, KernelSpecs::KernelBlockGiven +end + +describe "self.send(:block_given?)" do + it_behaves_like :kernel_block_given, :block_given?, KernelSpecs::SelfBlockGiven +end diff --git a/spec/ruby/core/kernel/caller_locations_spec.rb b/spec/ruby/core/kernel/caller_locations_spec.rb new file mode 100644 index 0000000000..69993c3ec0 --- /dev/null +++ b/spec/ruby/core/kernel/caller_locations_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/caller_locations', __FILE__) + +describe 'Kernel#caller_locations' do + it 'is a private method' do + Kernel.should have_private_instance_method(:caller_locations) + end + + it 'returns an Array of caller locations' do + KernelSpecs::CallerLocationsTest.locations.empty?.should == false + end + + it 'returns an Array of caller locations using a custom offset' do + locations = KernelSpecs::CallerLocationsTest.locations(2) + + locations[0].absolute_path.end_with?('mspec.rb').should == true + end + + it 'returns an Array of caller locations using a custom limit' do + locations = KernelSpecs::CallerLocationsTest.locations(1, 1) + + locations.length.should == 1 + end + + it 'returns the locations as Thread::Backtrace::Location instances' do + locations = KernelSpecs::CallerLocationsTest.locations + + locations.each do |location| + location.kind_of?(Thread::Backtrace::Location).should == true + end + end +end diff --git a/spec/ruby/core/kernel/caller_spec.rb b/spec/ruby/core/kernel/caller_spec.rb new file mode 100644 index 0000000000..d9be29a8db --- /dev/null +++ b/spec/ruby/core/kernel/caller_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/caller', __FILE__) + +describe 'Kernel#caller' do + it 'is a private method' do + Kernel.should have_private_instance_method(:caller) + end + + it 'returns an Array of caller locations' do + KernelSpecs::CallerTest.locations.empty?.should == false + end + + it 'returns an Array of caller locations using a custom offset' do + locations = KernelSpecs::CallerTest.locations(2) + + locations[0].should =~ %r{runner/mspec.rb} + end + + it 'returns an Array of caller locations using a custom limit' do + locations = KernelSpecs::CallerTest.locations(1, 1) + + locations.length.should == 1 + end + + it 'returns an Array of caller locations using a range' do + locations = KernelSpecs::CallerTest.locations(1..1) + + locations.length.should == 1 + end + + it 'returns the locations as String instances' do + locations = KernelSpecs::CallerTest.locations + line = __LINE__ - 1 + + locations[0].should include("#{__FILE__}:#{line}:in") + end +end diff --git a/spec/ruby/core/kernel/case_compare_spec.rb b/spec/ruby/core/kernel/case_compare_spec.rb new file mode 100644 index 0000000000..5332aa7647 --- /dev/null +++ b/spec/ruby/core/kernel/case_compare_spec.rb @@ -0,0 +1,135 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + + +module Specs + module Kernel + + class HasNone + end + + class HasOpEqual + def ==(other) + other.kind_of? HasOpEqual + end + end + + class HasEqual + def equal?(other) + false + end + end + + class HasOppoOpEqual + def ==(other) + false + end + + def equal?(other) + false + end + end + end +end + + +describe "Kernel#=== for a class with default #== and #equal?" do + before :each do + @o1 = Specs::Kernel::HasNone.new + @o2 = @o1.dup + end + + it "returns true if other object has same object id" do + @o1.object_id.should == @o1.object_id + (@o1 === @o1).should == true + end + + it "returns false if other object does not have same object id" do + @o1.object_id.should_not == @o2.object_id + (@o1 === @o2).should == false + end +end + +describe "Kernel#=== for a class with #== overridden to consider other object's class" do + before :each do + @o = Object.new + @o1 = Specs::Kernel::HasOpEqual.new + @o2 = @o1.dup + end + + it "returns true if #== returns true even if #equal? is false" do + @o1.should_not equal(@o2) + (@o1 == @o2).should == true + (@o1 === @o2).should == true + end + + it "returns true if #equal? returns true" do + @o1.should equal(@o1) + (@o1 === @o1).should == true + end + + it "returns false if neither #== nor #equal? returns true" do + @o1.should_not equal(@o) + (@o1 == @o).should == false + (@o1 === @o).should == false + end +end + +describe "Kernel#=== for a class with #equal? overridden to always be false" do + before :each do + @o = Object.new + @o1 = Specs::Kernel::HasEqual.new + @o2 = @o1.dup + end + + it "returns true if #== returns true even if #equal? is false" do + @o1.should_not equal(@o1) + (@o1 == @o1).should == true + (@o1 === @o1).should == true + end + + it "returns false if neither #== nor #equal? returns true" do + @o1.should_not equal(@o) + (@o1 == @o).should == false + (@o1 === @o).should == false + end +end + +describe "Kernel#=== for a class with #== and #equal? overridden to always be false" do + before :each do + @o = Object.new + @o1 = Specs::Kernel::HasOppoOpEqual.new + @o2 = @o1.dup + end + + it "returns true if the object id is the same even if both #== and #equal? return false" do + @o1.object_id.should == @o1.object_id + + @o1.should_not equal(@o1) + (@o1 == @o1).should == false + + (@o1 === @o1).should == true + end + + it "returns false if the object id is not the same and both #== and #equal? return false" do + @o1.object_id.should_not == @o2.object_id + + @o1.should_not equal(@o2) + (@o1 == @o2).should == false + + (@o1 === @o2).should == false + end +end + +describe "Kernel#=== does not call #object_id nor #equal?" do + before :each do + @o1 = Object.new + @o1.should_not_receive(:object_id) + @o1.should_not_receive(:equal?) + end + + it "but still returns true for #== or #=== on the same object" do + (@o1 == @o1).should == true + (@o1 === @o1).should == true + end +end diff --git a/spec/ruby/core/kernel/catch_spec.rb b/spec/ruby/core/kernel/catch_spec.rb new file mode 100644 index 0000000000..35a4860f38 --- /dev/null +++ b/spec/ruby/core/kernel/catch_spec.rb @@ -0,0 +1,127 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.catch" do + before :each do + ScratchPad.clear + end + + it "executes its block and catches a thrown value matching its argument" do + catch :thrown_key do + ScratchPad.record :catch_block + throw :thrown_key + ScratchPad.record :throw_failed + end + ScratchPad.recorded.should == :catch_block + end + + it "returns the second value passed to throw" do + catch(:thrown_key) { throw :thrown_key, :catch_value }.should == :catch_value + end + + it "returns the last expression evaluated if throw was not called" do + catch(:thrown_key) { 1; :catch_block }.should == :catch_block + end + + it "passes the given symbol to its block" do + catch :thrown_key do |tag| + ScratchPad.record tag + end + ScratchPad.recorded.should == :thrown_key + end + + it "raises an ArgumentError if a Symbol is thrown for a String catch value" do + lambda { catch("exit") { throw :exit } }.should raise_error(ArgumentError) + end + + it "raises an ArgumentError if a String with different identity is thrown" do + lambda { catch("exit") { throw "exit" } }.should raise_error(ArgumentError) + end + + it "catches a Symbol when thrown a matching Symbol" do + catch :thrown_key do + ScratchPad.record :catch_block + throw :thrown_key + end + ScratchPad.recorded.should == :catch_block + end + + it "catches a String when thrown a String with the same identity" do + key = "thrown_key" + catch key do + ScratchPad.record :catch_block + throw key + end + ScratchPad.recorded.should == :catch_block + end + + it "accepts an object as an argument" do + catch(Object.new) { :catch_block }.should == :catch_block + end + + it "yields an object when called without arguments" do + catch { |tag| tag }.should be_an_instance_of(Object) + end + + it "can be used even in a method different from where throw is called" do + class CatchSpecs + def self.throwing_method + throw :blah, :thrown_value + end + def self.catching_method + catch :blah do + throwing_method + end + end + end + CatchSpecs.catching_method.should == :thrown_value + end + + describe "when nested" do + before :each do + ScratchPad.record [] + end + + it "catches across invocation boundaries" do + catch :one do + ScratchPad << 1 + catch :two do + ScratchPad << 2 + catch :three do + ScratchPad << 3 + throw :one + ScratchPad << 4 + end + ScratchPad << 5 + end + ScratchPad << 6 + end + + ScratchPad.recorded.should == [1, 2, 3] + end + + it "catches in the nested invocation with the same key object" do + catch :thrown_key do + ScratchPad << 1 + catch :thrown_key do + ScratchPad << 2 + throw :thrown_key + ScratchPad << 3 + end + ScratchPad << 4 + end + + ScratchPad.recorded.should == [1, 2, 4] + end + end + + it "raises LocalJumpError if no block is given" do + lambda { catch :blah }.should raise_error(LocalJumpError) + end +end + +describe "Kernel#catch" do + it "is a private method" do + Kernel.should have_private_instance_method(:catch) + end +end diff --git a/spec/ruby/core/kernel/chomp_spec.rb b/spec/ruby/core/kernel/chomp_spec.rb new file mode 100644 index 0000000000..524a4c8b1d --- /dev/null +++ b/spec/ruby/core/kernel/chomp_spec.rb @@ -0,0 +1,67 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_chomp, shared: true do + it "removes the final newline of $_" do + KernelSpecs.chomp("abc\n", @method).should == "abc" + end + + it "removes the final carriage return of $_" do + KernelSpecs.chomp("abc\r", @method).should == "abc" + end + + it "removes the final carriage return, newline of $_" do + KernelSpecs.chomp("abc\r\n", @method).should == "abc" + end + + it "removes only the final newline of $_" do + KernelSpecs.chomp("abc\n\n", @method).should == "abc\n" + end + + it "removes the value of $/ from the end of $_" do + KernelSpecs.chomp("abcde", @method, "cde").should == "ab" + end +end + +describe :kernel_chomp_private, shared: true do + it "is a private method" do + KernelSpecs.has_private_method(@method).should be_true + end +end + +describe "Kernel.chomp" do + it_behaves_like :kernel_chomp, "Kernel.chomp" +end + +describe "Kernel#chomp" do + it_behaves_like :kernel_chomp, "chomp" + + it_behaves_like :kernel_chomp_private, :chomp +end + +with_feature :encoding do + describe :kernel_chomp_encoded, shared: true do + before :each do + @external = Encoding.default_external + Encoding.default_external = Encoding::UTF_8 + end + + after :each do + Encoding.default_external = @external + end + + it "removes the final carriage return, newline from a multi-byte $_" do + script = fixture __FILE__, "#{@method}.rb" + KernelSpecs.run_with_dash_n(script).should == "あれ" + end + end + + describe "Kernel.chomp" do + it_behaves_like :kernel_chomp_encoded, "chomp" + end + + describe "Kernel#chomp" do + it_behaves_like :kernel_chomp_encoded, "chomp_f" + end +end diff --git a/spec/ruby/core/kernel/chop_spec.rb b/spec/ruby/core/kernel/chop_spec.rb new file mode 100644 index 0000000000..5106fefee8 --- /dev/null +++ b/spec/ruby/core/kernel/chop_spec.rb @@ -0,0 +1,55 @@ +# -*- encoding: utf-8 -*- +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_chop, shared: true do + it "removes the final character of $_" do + KernelSpecs.chop("abc", @method).should == "ab" + end + + it "removes the final carriage return, newline of $_" do + KernelSpecs.chop("abc\r\n", @method).should == "abc" + end +end + +describe :kernel_chop_private, shared: true do + it "is a private method" do + KernelSpecs.has_private_method(@method).should be_true + end +end + +describe "Kernel.chop" do + it_behaves_like :kernel_chop, "Kernel.chop" +end + +describe "Kernel#chop" do + it_behaves_like :kernel_chop_private, :chop + + it_behaves_like :kernel_chop, "chop" +end + +with_feature :encoding do + describe :kernel_chop_encoded, shared: true do + before :each do + @external = Encoding.default_external + Encoding.default_external = Encoding::UTF_8 + end + + after :each do + Encoding.default_external = @external + end + + it "removes the final multi-byte character from $_" do + script = fixture __FILE__, "#{@method}.rb" + KernelSpecs.run_with_dash_n(script).should == "あ" + end + end + + describe "Kernel.chop" do + it_behaves_like :kernel_chop_encoded, "chop" + end + + describe "Kernel#chop" do + it_behaves_like :kernel_chop_encoded, "chop_f" + end +end diff --git a/spec/ruby/core/kernel/class_spec.rb b/spec/ruby/core/kernel/class_spec.rb new file mode 100644 index 0000000000..0d7b40c366 --- /dev/null +++ b/spec/ruby/core/kernel/class_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#class" do + it "returns the class of the object" do + Object.new.class.should equal(Object) + + 1.class.should equal(Fixnum) + 3.14.class.should equal(Float) + :hello.class.should equal(Symbol) + "hello".class.should equal(String) + [1, 2].class.should equal(Array) + { 1 => 2 }.class.should equal(Hash) + end + + it "returns Class for a class" do + BasicObject.class.should equal(Class) + String.class.should equal(Class) + end + + it "returns the first non-singleton class" do + a = "hello" + def a.my_singleton_method; end + a.class.should equal(String) + end +end diff --git a/spec/ruby/core/kernel/clone_spec.rb b/spec/ruby/core/kernel/clone_spec.rb new file mode 100644 index 0000000000..48b3c24c7f --- /dev/null +++ b/spec/ruby/core/kernel/clone_spec.rb @@ -0,0 +1,118 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/dup_clone', __FILE__) + +describe "Kernel#clone" do + it_behaves_like :kernel_dup_clone, :clone + + before :each do + ScratchPad.clear + @obj = KernelSpecs::Duplicate.new 1, :a + end + + it "calls #initialize_copy on the new instance" do + clone = @obj.clone + ScratchPad.recorded.should_not == @obj.object_id + ScratchPad.recorded.should == clone.object_id + end + + it "uses the internal allocator and does not call #allocate" do + klass = Class.new + instance = klass.new + + def klass.allocate + raise "allocate should not be called" + end + + clone = instance.clone + clone.class.should equal klass + end + + it "copies frozen state from the original" do + o2 = @obj.clone + @obj.freeze + o3 = @obj.clone + + o2.frozen?.should == false + o3.frozen?.should == true + end + + ruby_version_is '2.4' do + it 'takes an option to copy freeze state or not' do + @obj.clone(freeze: true).frozen?.should == false + @obj.clone(freeze: false).frozen?.should == false + @obj.freeze + @obj.clone(freeze: true).frozen?.should == true + @obj.clone(freeze: false).frozen?.should == false + end + end + + it "copies instance variables" do + clone = @obj.clone + clone.one.should == 1 + clone.two.should == :a + end + + it "copies singleton methods" do + def @obj.special() :the_one end + clone = @obj.clone + clone.special.should == :the_one + end + + it "copies modules included in the singleton class" do + class << @obj + include KernelSpecs::DuplicateM + end + + clone = @obj.clone + clone.repr.should == "KernelSpecs::Duplicate" + end + + it "copies constants defined in the singleton class" do + class << @obj + CLONE = :clone + end + + clone = @obj.clone + class << clone + CLONE.should == :clone + end + end + + it "replaces a singleton object's metaclass with a new copy with the same superclass" do + cls = Class.new do + def bar + ['a'] + end + end + + object = cls.new + object.define_singleton_method(:bar) do + ['b', *super()] + end + object.bar.should == ['b', 'a'] + + cloned = object.clone + + cloned.singleton_methods.should == [:bar] + + # bar should replace previous one + cloned.define_singleton_method(:bar) do + ['c', *super()] + end + cloned.bar.should == ['c', 'a'] + + # bar should be removed and call through to superclass + cloned.singleton_class.class_eval do + remove_method :bar + end + + cloned.bar.should == ['a'] + end + + it 'copies frozen? and tainted?' do + o = ''.taint.freeze.clone + o.frozen?.should be_true + o.tainted?.should be_true + end +end diff --git a/spec/ruby/core/kernel/comparison_spec.rb b/spec/ruby/core/kernel/comparison_spec.rb new file mode 100644 index 0000000000..2aa4358600 --- /dev/null +++ b/spec/ruby/core/kernel/comparison_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Kernel#<=>" do + it "returns 0 if self" do + obj = Object.new + obj.<=>(obj).should == 0 + end + + it "returns 0 if self is == to the argument" do + obj = mock('has ==') + obj.should_receive(:==).and_return(true) + obj.<=>(Object.new).should == 0 + end + + it "returns nil if self is eql? but not == to the argument" do + obj = mock('has eql?') + obj.should_not_receive(:eql?) + obj.<=>(Object.new).should be_nil + end + + it "returns nil if self.==(arg) returns nil" do + obj = mock('wrong ==') + obj.should_receive(:==).and_return(nil) + obj.<=>(Object.new).should be_nil + end + + it "returns nil if self is not == to the argument" do + obj = Object.new + obj.<=>(3.14).should be_nil + end +end diff --git a/spec/ruby/core/kernel/define_singleton_method_spec.rb b/spec/ruby/core/kernel/define_singleton_method_spec.rb new file mode 100644 index 0000000000..de6f7fc286 --- /dev/null +++ b/spec/ruby/core/kernel/define_singleton_method_spec.rb @@ -0,0 +1,101 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#define_singleton_method" do + describe "when given an UnboundMethod" do + class DefineSingletonMethodSpecClass + MY_CONST = 42 + define_singleton_method(:another_test_method, self.method(:constants)) + end + + it "correctly calls the new method" do + klass = DefineSingletonMethodSpecClass + klass.another_test_method.should == klass.constants + end + + it "adds the new method to the methods list" do + DefineSingletonMethodSpecClass.should have_method(:another_test_method) + end + + it "defines any Child class method from any Parent's class methods" do + um = KernelSpecs::Parent.method(:parent_class_method).unbind + KernelSpecs::Child.send :define_singleton_method, :child_class_method, um + KernelSpecs::Child.child_class_method.should == :foo + lambda{KernelSpecs::Parent.child_class_method}.should raise_error(NoMethodError) + end + + it "will raise when attempting to define an object's singleton method from another object's singleton method" do + other = KernelSpecs::Parent.new + p = KernelSpecs::Parent.new + class << p + def singleton_method + :single + end + end + um = p.method(:singleton_method).unbind + lambda{ other.send :define_singleton_method, :other_singleton_method, um }.should raise_error(TypeError) + end + + end + + it "defines a new method with the given name and the given block as body in self" do + class DefineSingletonMethodSpecClass + define_singleton_method(:block_test1) { self } + define_singleton_method(:block_test2, &lambda { self }) + end + + o = DefineSingletonMethodSpecClass + o.block_test1.should == o + o.block_test2.should == o + end + + it "raises a TypeError when the given method is no Method/Proc" do + lambda { + Class.new { define_singleton_method(:test, "self") } + }.should raise_error(TypeError) + + lambda { + Class.new { define_singleton_method(:test, 1234) } + }.should raise_error(TypeError) + end + + it "defines a new singleton method for objects" do + obj = Object.new + obj.define_singleton_method(:test) { "world!" } + obj.test.should == "world!" + lambda { + Object.new.test + }.should raise_error(NoMethodError) + end + + it "maintains the Proc's scope" do + class DefineMethodByProcClass + in_scope = true + method_proc = proc { in_scope } + + define_singleton_method(:proc_test, &method_proc) + end + + DefineMethodByProcClass.proc_test.should == true + end + + it "raises an ArgumentError when no block is given" do + obj = Object.new + lambda { + obj.define_singleton_method(:test) + }.should raise_error(ArgumentError) + end + + ruby_version_is "2.3" do + it "does not use the caller block when no block is given" do + o = Object.new + def o.define(name) + define_singleton_method(name) + end + + lambda { + o.define(:foo) { raise "not used" } + }.should raise_error(ArgumentError) + end + end +end diff --git a/spec/ruby/core/kernel/display_spec.rb b/spec/ruby/core/kernel/display_spec.rb new file mode 100644 index 0000000000..e771e14cdb --- /dev/null +++ b/spec/ruby/core/kernel/display_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#display" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/dup_spec.rb b/spec/ruby/core/kernel/dup_spec.rb new file mode 100644 index 0000000000..af7e924a66 --- /dev/null +++ b/spec/ruby/core/kernel/dup_spec.rb @@ -0,0 +1,67 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/dup_clone', __FILE__) + +describe "Kernel#dup" do + it_behaves_like :kernel_dup_clone, :dup + + before :each do + ScratchPad.clear + @obj = KernelSpecs::Duplicate.new 1, :a + end + + it "calls #initialize_copy on the new instance" do + dup = @obj.dup + ScratchPad.recorded.should_not == @obj.object_id + ScratchPad.recorded.should == dup.object_id + end + + it "uses the internal allocator and does not call #allocate" do + klass = Class.new + instance = klass.new + + def klass.allocate + raise "allocate should not be called" + end + + dup = instance.dup + dup.class.should equal klass + end + + it "does not copy frozen state from the original" do + @obj.freeze + dup = @obj.dup + + dup.frozen?.should == false + end + + it "copies instance variables" do + dup = @obj.dup + dup.one.should == 1 + dup.two.should == :a + end + + it "does not copy singleton methods" do + def @obj.special() :the_one end + dup = @obj.dup + lambda { dup.special }.should raise_error(NameError) + end + + it "does not copy modules included in the singleton class" do + class << @obj + include KernelSpecs::DuplicateM + end + + dup = @obj.dup + lambda { dup.repr }.should raise_error(NameError) + end + + it "does not copy constants defined in the singleton class" do + class << @obj + CLONE = :clone + end + + dup = @obj.dup + lambda { class << dup; CLONE; end }.should raise_error(NameError) + end +end diff --git a/spec/ruby/core/kernel/enum_for_spec.rb b/spec/ruby/core/kernel/enum_for_spec.rb new file mode 100644 index 0000000000..819140e0e4 --- /dev/null +++ b/spec/ruby/core/kernel/enum_for_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Kernel#enum_for" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/eql_spec.rb b/spec/ruby/core/kernel/eql_spec.rb new file mode 100644 index 0000000000..39c9fea7eb --- /dev/null +++ b/spec/ruby/core/kernel/eql_spec.rb @@ -0,0 +1,11 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/kernel/equal', __FILE__) + +describe "Kernel#eql?" do + it "is a public instance method" do + Kernel.should have_public_instance_method(:eql?) + end + + it_behaves_like :object_equal, :eql? +end + diff --git a/spec/ruby/core/kernel/equal_value_spec.rb b/spec/ruby/core/kernel/equal_value_spec.rb new file mode 100644 index 0000000000..670987ead2 --- /dev/null +++ b/spec/ruby/core/kernel/equal_value_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#==" do + it "returns true only if obj and other are the same object" do + o1 = mock('o1') + o2 = mock('o2') + (o1 == o1).should == true + (o2 == o2).should == true + (o1 == o2).should== false + (nil == nil).should == true + (o1 == nil).should== false + (nil == o2).should== false + end +end diff --git a/spec/ruby/core/kernel/eval_spec.rb b/spec/ruby/core/kernel/eval_spec.rb new file mode 100644 index 0000000000..96fc0f5b71 --- /dev/null +++ b/spec/ruby/core/kernel/eval_spec.rb @@ -0,0 +1,216 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +EvalSpecs::A.new.c + +describe "Kernel#eval" do + it "is a private method" do + Kernel.should have_private_instance_method(:eval) + end + + it "is a module function" do + Kernel.respond_to?(:eval).should == true + end + + it "evaluates the code within" do + eval("2 + 3").should == 5 + end + + it "coerces an object to string" do + eval(EvalSpecs::CoercedObject.new).should == 5 + end + + it "evaluates within the scope of the eval" do + EvalSpecs::A::B.name.should == "EvalSpecs::A::B" + end + + it "evaluates such that consts are scoped to the class of the eval" do + EvalSpecs::A::C.name.should == "EvalSpecs::A::C" + end + + it "finds a local in an enclosing scope" do + a = 1 + eval("a").should == 1 + end + + it "updates a local in an enclosing scope" do + a = 1 + eval("a = 2") + a.should == 2 + end + + it "updates a local in a surrounding block scope" do + EvalSpecs.new.f do + a = 1 + eval("a = 2") + a.should == 2 + end + end + + it "updates a local in a scope above a surrounding block scope" do + a = 1 + EvalSpecs.new.f do + eval("a = 2") + a.should == 2 + end + a.should == 2 + end + + it "updates a local in a scope above when modified in a nested block scope" do + a = 1 + es = EvalSpecs.new + eval("es.f { es.f { a = 2 } }") + a.should == 2 + end + + it "finds locals in a nested eval" do + eval('test = 10; eval("test")').should == 10 + end + + it "does not share locals across eval scopes" do + code = fixture __FILE__, "eval_locals.rb" + ruby_exe(code).chomp.should == "NameError" + end + + it "doesn't accept a Proc object as a binding" do + x = 1 + bind = proc {} + + lambda { eval("x", bind) }.should raise_error(TypeError) + end + + it "does not make Proc locals visible to evaluated code" do + bind = proc { inner = 4 } + lambda { eval("inner", bind.binding) }.should raise_error(NameError) + end + + # REWRITE ME: This obscures the real behavior of where locals are stored + # in eval bindings. + it "allows a binding to be captured inside an eval" do + outer_binding = binding + level1 = eval("binding", outer_binding) + level2 = eval("binding", level1) + + eval("x = 2", outer_binding) + eval("y = 3", level1) + + eval("w=1", outer_binding) + eval("w", outer_binding).should == 1 + eval("w=1", level1).should == 1 + eval("w", level1).should == 1 + eval("w=1", level2).should == 1 + eval("w", level2).should == 1 + + eval("x", outer_binding).should == 2 + eval("x=2", level1) + eval("x", level1).should == 2 + eval("x=2", level2) + eval("x", level2).should == 2 + + eval("y=3", outer_binding) + eval("y", outer_binding).should == 3 + eval("y=3", level1) + eval("y", level1).should == 3 + eval("y=3", level2) + eval("y", level2).should == 3 + end + + it "uses the same scope for local variables when given the same binding" do + outer_binding = binding + + eval("if false; a = 1; end", outer_binding) + eval("a", outer_binding).should be_nil + end + + it "allows creating a new class in a binding" do + bind = proc {} + eval("class EvalBindingProcA; end; EvalBindingProcA.name", bind.binding).should =~ /EvalBindingProcA$/ + end + + it "allows creating a new class in a binding created by #eval" do + bind = eval "binding" + eval("class EvalBindingA; end; EvalBindingA.name", bind).should =~ /EvalBindingA$/ + end + + it "includes file and line information in syntax error" do + expected = 'speccing.rb' + lambda { + eval('if true',TOPLEVEL_BINDING, expected) + }.should raise_error(SyntaxError) { |e| + e.message.should =~ /#{expected}:1:.+/ + } + end + + it "evaluates string with given filename and negative linenumber" do + expected_file = 'speccing.rb' + lambda { + eval('if true',TOPLEVEL_BINDING, expected_file, -100) + }.should raise_error(SyntaxError) { |e| + e.message.should =~ /#{expected_file}:-100:.+/ + } + end + + it "sets constants at the toplevel from inside a block" do + # The class Object bit is needed to workaround some mspec oddness + class Object + [1].each { eval "Const = 1"} + Const.should == 1 + remove_const :Const + end + end + + it "uses the filename of the binding if none is provided" do + eval("__FILE__").should == "(eval)" + eval("__FILE__", binding).should == __FILE__ + eval("__FILE__", binding, "success").should == "success" + eval("eval '__FILE__', binding").should == "(eval)" + eval("eval '__FILE__', binding", binding).should == __FILE__ + eval("eval '__FILE__', binding", binding, 'success').should == 'success' + end + + # Found via Rubinius bug github:#149 + it "does not alter the value of __FILE__ in the binding" do + first_time = EvalSpecs.call_eval + second_time = EvalSpecs.call_eval + + # This bug is seen by calling the method twice and comparing the values + # of __FILE__ each time. If the bug is present, calling eval will set the + # value of __FILE__ to the eval's "filename" argument. + + second_time.should_not == "(eval)" + first_time.should == second_time + end + + it "can be aliased" do + alias aliased_eval eval + x = 2 + aliased_eval('x += 40') + x.should == 42 + end + + # See http://jira.codehaus.org/browse/JRUBY-5163 + it "uses the receiver as self inside the eval" do + eval("self").should equal(self) + Kernel.eval("self").should equal(Kernel) + end + + it "does not pass the block to the method being eval'ed" do + lambda { + eval('KernelSpecs::EvalTest.call_yield') { "content" } + }.should raise_error(LocalJumpError) + end + + it "returns from the scope calling #eval when evaluating 'return'" do + lambda { eval("return :eval") }.call.should == :eval + end + + it "unwinds through a Proc-style closure and returns from a lambda-style closure in the closure chain" do + code = fixture __FILE__, "eval_return_with_lambda.rb" + ruby_exe(code).chomp.should == "a,b,c,eval,f" + end + + it "raises a LocalJumpError if there is no lambda-style closure in the chain" do + code = fixture __FILE__, "eval_return_without_lambda.rb" + ruby_exe(code).chomp.should == "a,b,c,e,LocalJumpError,f" + end +end diff --git a/spec/ruby/core/kernel/exec_spec.rb b/spec/ruby/core/kernel/exec_spec.rb new file mode 100644 index 0000000000..3a7b656914 --- /dev/null +++ b/spec/ruby/core/kernel/exec_spec.rb @@ -0,0 +1,18 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#exec" do + it "is a private method" do + Kernel.should have_private_instance_method(:exec) + end + + it "runs the specified command, replacing current process" do + ruby_exe('exec "echo hello"; puts "fail"', escape: true).should == "hello\n" + end +end + +describe "Kernel.exec" do + it "runs the specified command, replacing current process" do + ruby_exe('Kernel.exec "echo hello"; puts "fail"', escape: true).should == "hello\n" + end +end diff --git a/spec/ruby/core/kernel/exit_spec.rb b/spec/ruby/core/kernel/exit_spec.rb new file mode 100644 index 0000000000..61a6670cfd --- /dev/null +++ b/spec/ruby/core/kernel/exit_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/process/exit', __FILE__) + +describe "Kernel#exit" do + it "is a private method" do + Kernel.should have_private_instance_method(:exit) + end + + it_behaves_like :process_exit, :exit, KernelSpecs::Method.new +end + +describe "Kernel#exit!" do + it "is a private method" do + Kernel.should have_private_instance_method(:exit!) + end + + it_behaves_like :process_exit!, :exit!, "self" +end + +describe "Kernel.exit" do + it_behaves_like :process_exit, :exit, Kernel +end + +describe "Kernel.exit!" do + it_behaves_like :process_exit!, :exit!, Kernel +end diff --git a/spec/ruby/core/kernel/extend_spec.rb b/spec/ruby/core/kernel/extend_spec.rb new file mode 100644 index 0000000000..482eef32e9 --- /dev/null +++ b/spec/ruby/core/kernel/extend_spec.rb @@ -0,0 +1,79 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +module KernelSpecs::M + def self.extend_object(o) + ScratchPad << "extend_object" + super + end + + def self.extended(o) + ScratchPad << "extended" + super + end + + def self.append_features(o) + ScratchPad << "append_features" + super + end +end + +describe "Kernel#extend" do + before :each do + ScratchPad.record [] + end + + it "requires multiple arguments" do + Object.new.method(:extend).arity.should < 0 + end + + it "calls extend_object on argument" do + o = mock('o') + o.extend KernelSpecs::M + ScratchPad.recorded.include?("extend_object").should == true + end + + it "does not calls append_features on arguments metaclass" do + o = mock('o') + o.extend KernelSpecs::M + ScratchPad.recorded.include?("append_features").should == false + end + + it "calls extended on argument" do + o = mock('o') + o.extend KernelSpecs::M + ScratchPad.recorded.include?("extended").should == true + end + + it "makes the class a kind_of? the argument" do + class C + extend KernelSpecs::M + end + (C.kind_of? KernelSpecs::M).should == true + end + + it "raises an ArgumentError when no arguments given" do + lambda { Object.new.extend }.should raise_error(ArgumentError) + end + + it "raises a TypeError when the argument is not a Module" do + o = mock('o') + klass = Class.new + lambda { o.extend(klass) }.should raise_error(TypeError) + end + + describe "on frozen instance" do + before :each do + @frozen = Object.new.freeze + @module = KernelSpecs::M + end + + it "raises an ArgumentError when no arguments given" do + lambda { @frozen.extend }.should raise_error(ArgumentError) + end + + it "raises a RuntimeError" do + lambda { @frozen.extend @module }.should raise_error(RuntimeError) + end + end +end diff --git a/spec/ruby/core/kernel/fail_spec.rb b/spec/ruby/core/kernel/fail_spec.rb new file mode 100644 index 0000000000..9f3601c233 --- /dev/null +++ b/spec/ruby/core/kernel/fail_spec.rb @@ -0,0 +1,43 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.fail" do + it "is a private method" do + Kernel.should have_private_instance_method(:fail) + end + + it "raises a RuntimeError" do + lambda { fail }.should raise_error(RuntimeError) + end + + it "accepts an Object with an exception method returning an Exception" do + class Boring + def self.exception(msg) + StandardError.new msg + end + end + lambda { fail Boring, "..." }.should raise_error(StandardError) + end + + it "instantiates the specified exception class" do + class LittleBunnyFooFoo < RuntimeError; end + lambda { fail LittleBunnyFooFoo }.should raise_error(LittleBunnyFooFoo) + end + + it "uses the specified message" do + lambda { + begin + fail "the duck is not irish." + rescue => e + e.message.should == "the duck is not irish." + raise + else + raise Exception + end + }.should raise_error(RuntimeError) + end +end + +describe "Kernel#fail" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/fixtures/__callee__.rb b/spec/ruby/core/kernel/fixtures/__callee__.rb new file mode 100644 index 0000000000..7138dbc5aa --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/__callee__.rb @@ -0,0 +1,34 @@ +module KernelSpecs + class CalleeTest + def f + __callee__ + end + + alias_method :g, :f + + def in_block + (1..2).map { __callee__ } + end + + define_method(:dm) do + __callee__ + end + + define_method(:dm_block) do + (1..2).map { __callee__ } + end + + def from_send + send "__callee__" + end + + def from_eval + eval "__callee__" + end + + @@method = __callee__ + def from_class_body + @@method + end + end +end diff --git a/spec/ruby/core/kernel/fixtures/__method__.rb b/spec/ruby/core/kernel/fixtures/__method__.rb new file mode 100644 index 0000000000..9300366b37 --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/__method__.rb @@ -0,0 +1,34 @@ +module KernelSpecs + class MethodTest + def f + __method__ + end + + alias_method :g, :f + + def in_block + (1..2).map { __method__ } + end + + define_method(:dm) do + __method__ + end + + define_method(:dm_block) do + (1..2).map { __method__ } + end + + def from_send + send "__method__" + end + + def from_eval + eval "__method__" + end + + @@method = __method__ + def from_class_body + @@method + end + end +end diff --git a/spec/ruby/core/kernel/fixtures/autoload_b.rb b/spec/ruby/core/kernel/fixtures/autoload_b.rb new file mode 100644 index 0000000000..e8be221ec7 --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/autoload_b.rb @@ -0,0 +1,5 @@ +module KSAutoloadB + def self.loaded + :ksautoload_b + end +end diff --git a/spec/ruby/core/kernel/fixtures/autoload_c.rb b/spec/ruby/core/kernel/fixtures/autoload_c.rb new file mode 100644 index 0000000000..4569b23669 --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/autoload_c.rb @@ -0,0 +1,5 @@ +module KSAutoloadC + def self.loaded + :ksautoload_c + end +end diff --git a/spec/ruby/core/kernel/fixtures/autoload_d.rb b/spec/ruby/core/kernel/fixtures/autoload_d.rb new file mode 100644 index 0000000000..552cb5e82c --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/autoload_d.rb @@ -0,0 +1,5 @@ +module KSAutoloadD + def self.loaded + :ksautoload_d + end +end diff --git a/spec/ruby/core/kernel/fixtures/autoload_frozen.rb b/spec/ruby/core/kernel/fixtures/autoload_frozen.rb new file mode 100644 index 0000000000..e9dc42912b --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/autoload_frozen.rb @@ -0,0 +1,7 @@ +Object.freeze + +begin + autoload :ANY_CONSTANT, "no_autoload.rb" +rescue Exception => e + print e.class, " - ", defined?(ANY_CONSTANT).inspect +end diff --git a/spec/ruby/core/kernel/fixtures/caller.rb b/spec/ruby/core/kernel/fixtures/caller.rb new file mode 100644 index 0000000000..ae3e13e9c9 --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/caller.rb @@ -0,0 +1,7 @@ +module KernelSpecs + class CallerTest + def self.locations(*args) + caller(*args) + end + end +end diff --git a/spec/ruby/core/kernel/fixtures/caller_locations.rb b/spec/ruby/core/kernel/fixtures/caller_locations.rb new file mode 100644 index 0000000000..cc4e04d38f --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/caller_locations.rb @@ -0,0 +1,7 @@ +module KernelSpecs + class CallerLocationsTest + def self.locations(*args) + caller_locations(*args) + end + end +end diff --git a/spec/ruby/core/kernel/fixtures/chomp.rb b/spec/ruby/core/kernel/fixtures/chomp.rb new file mode 100644 index 0000000000..f08dbadce5 --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/chomp.rb @@ -0,0 +1,4 @@ +# -*- encoding: utf-8 -*- + +$_ = "あれ\r\n" +print Kernel.chomp diff --git a/spec/ruby/core/kernel/fixtures/chomp_f.rb b/spec/ruby/core/kernel/fixtures/chomp_f.rb new file mode 100644 index 0000000000..3c22cb21e7 --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/chomp_f.rb @@ -0,0 +1,4 @@ +# -*- encoding: utf-8 -*- + +$_ = "あれ\r\n" +print chomp diff --git a/spec/ruby/core/kernel/fixtures/chop.rb b/spec/ruby/core/kernel/fixtures/chop.rb new file mode 100644 index 0000000000..dfd0626723 --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/chop.rb @@ -0,0 +1,4 @@ +# -*- encoding: utf-8 -*- + +$_ = "あれ" +print Kernel.chop diff --git a/spec/ruby/core/kernel/fixtures/chop_f.rb b/spec/ruby/core/kernel/fixtures/chop_f.rb new file mode 100644 index 0000000000..4ec89eb9ec --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/chop_f.rb @@ -0,0 +1,4 @@ +# -*- encoding: utf-8 -*- + +$_ = "あれ" +print chop diff --git a/spec/ruby/core/kernel/fixtures/classes.rb b/spec/ruby/core/kernel/fixtures/classes.rb new file mode 100644 index 0000000000..afa2bec12f --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/classes.rb @@ -0,0 +1,419 @@ +module KernelSpecs + def self.Array_function(arg) + Array(arg) + end + + def self.Array_method(arg) + Kernel.Array(arg) + end + + def self.Hash_function(arg) + Hash(arg) + end + + def self.Hash_method(arg) + Kernel.Hash(arg) + end + + def self.Integer_function(arg) + Integer(arg) + end + + def self.Integer_method(arg) + Kernel.Integer(arg) + end + + def self.putc_function(arg) + putc arg + end + + def self.putc_method(arg) + Kernel.putc arg + end + + def self.has_private_method(name) + IO.popen([*ruby_exe, "-n", "-e", "print Kernel.private_method_defined?(#{name.inspect})"], "r+") do |io| + io.puts + io.close_write + io.read + end == "true" + end + + def self.chop(str, method) + IO.popen([*ruby_exe, "-n", "-e", "$_ = #{str.inspect}; #{method}; print $_"], "r+") do |io| + io.puts + io.close_write + io.read + end + end + + def self.chomp(str, method, sep="\n") + code = "$_ = #{str.inspect}; $/ = #{sep.inspect}; #{method}; print $_" + IO.popen([*ruby_exe, "-n", "-e", code], "r+") do |io| + io.puts + io.close_write + io.read + end + end + + def self.run_with_dash_n(file) + IO.popen([*ruby_exe, "-n", file], "r+") do |io| + io.puts + io.close_write + io.read + end + end + + # kind_of?, is_a?, instance_of? + module SomeOtherModule; end + module AncestorModule; end + module MyModule; end + module MyExtensionModule; end + + class AncestorClass < String + include AncestorModule + end + + class InstanceClass < AncestorClass + include MyModule + end + + class KindaClass < AncestorClass + include MyModule + def initialize + self.extend MyExtensionModule + end + end + + class Method + public :abort, :exit, :exit!, :fork, :system + end + + class Methods + + module MetaclassMethods + def peekaboo + end + + protected + + def nopeeking + end + + private + + def shoo + end + end + + def self.ichi; end + def ni; end + class << self + def san; end + end + + private + + def self.shi; end + def juu_shi; end + + class << self + def roku; end + + private + + def shichi; end + end + + protected + + def self.hachi; end + def ku; end + + class << self + def juu; end + + protected + + def juu_ichi; end + end + + public + + def self.juu_ni; end + def juu_san; end + end + + class PrivateSup + def public_in_sub + end + + private :public_in_sub + end + + class PublicSub < PrivateSup + def public_in_sub + end + end + + class A + # There is Kernel#public_method, so we don't want this one to clash + def pub_method; :public_method; end + + def undefed_method; :undefed_method; end + undef_method :undefed_method + + protected + def protected_method; :protected_method; end + + private + def private_method; :private_method; end + + public + define_method(:defined_method) { :defined } + end + + class B < A + alias aliased_pub_method pub_method + end + + class VisibilityChange + class << self + private :new + end + end + + class Binding + @@super_secret = "password" + + def initialize(n) + @secret = n + end + + def square(n) + n * n + end + + def get_binding + a = true + @bind = binding + + # Add/Change stuff + b = true + @secret += 1 + + @bind + end + end + + + module BlockGiven + def self.accept_block + block_given? + end + + def self.accept_block_as_argument(&block) + block_given? + end + + class << self + define_method(:defined_block) do + block_given? + end + end + end + + module SelfBlockGiven + def self.accept_block + self.send(:block_given?) + end + + def self.accept_block_as_argument(&block) + self.send(:block_given?) + end + + class << self + define_method(:defined_block) do + self.send(:block_given?) + end + end + end + + module KernelBlockGiven + def self.accept_block + Kernel.block_given? + end + + def self.accept_block_as_argument(&block) + Kernel.block_given? + end + + class << self + define_method(:defined_block) do + Kernel.block_given? + end + end + end + + class EvalTest + def self.eval_yield_with_binding + eval("yield", binding) + end + def self.call_yield + yield + end + end + + module DuplicateM + def repr + self.class.name.to_s + end + end + + class Duplicate + attr_accessor :one, :two + + def initialize(one, two) + @one = one + @two = two + end + + def initialize_copy(other) + ScratchPad.record object_id + end + end + + class Clone + def initialize_clone(other) + ScratchPad.record other.object_id + end + end + + class Dup + def initialize_dup(other) + ScratchPad.record other.object_id + end + end + + module ParentMixin + def parent_mixin_method; end + end + + class Parent + include ParentMixin + def parent_method; end + def another_parent_method; end + def self.parent_class_method; :foo; end + end + + class Child < Parent + undef_method :parent_method + end + + class Grandchild < Child + undef_method :parent_mixin_method + end + + # for testing lambda + class Lambda + def outer + inner + end + + def mp(&b); b; end + + def inner + b = mp { return :good } + + pr = lambda { |x| x.call } + + pr.call(b) + + # We shouldn't be here, b should have unwinded through + return :bad + end + end + + class RespondViaMissing + def respond_to_missing?(method, priv=false) + case method + when :handled_publicly + true + when :handled_privately + priv + when :not_handled + false + else + raise "Typo in method name" + end + end + + def method_missing(method, *args) + "Done #{method}(#{args})" + end + end + + class InstanceVariable + def initialize + @greeting = "hello" + end + end + + class PrivateToAry + private + + def to_ary + [1, 2] + end + + def to_a + [3, 4] + end + end + + class PrivateToA + private + + def to_a + [3, 4] + end + end +end + +class EvalSpecs + class A + eval "class B; end" + def c + eval "class C; end" + end + end + + class CoercedObject + def to_str + '2 + 3' + end + + def hash + nil + end + end + + def f + yield + end + + def self.call_eval + f = __FILE__ + eval "true", binding, "(eval)", 1 + return f + end +end + +# for Kernel#sleep to have Channel in it's specs +# TODO: switch directly to queue for both Kernel#sleep and Thread specs? +unless defined? Channel + require 'thread' + class Channel < Queue + alias receive shift + end +end diff --git a/spec/ruby/core/kernel/fixtures/eval_locals.rb b/spec/ruby/core/kernel/fixtures/eval_locals.rb new file mode 100644 index 0000000000..ca8b381806 --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/eval_locals.rb @@ -0,0 +1,6 @@ +begin + eval("a = 2") + eval("p a") +rescue Object => e + puts e.class +end diff --git a/spec/ruby/core/kernel/fixtures/eval_return_with_lambda.rb b/spec/ruby/core/kernel/fixtures/eval_return_with_lambda.rb new file mode 100644 index 0000000000..a48b5685f3 --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/eval_return_with_lambda.rb @@ -0,0 +1,12 @@ +print "a," +x = lambda do + print "b," + Proc.new do + print "c," + eval("return :eval") + print "d," + end.call + print "e," +end.call +print x, "," +print "f" diff --git a/spec/ruby/core/kernel/fixtures/eval_return_without_lambda.rb b/spec/ruby/core/kernel/fixtures/eval_return_without_lambda.rb new file mode 100644 index 0000000000..fc8b7f1d4a --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/eval_return_without_lambda.rb @@ -0,0 +1,14 @@ +print "a," +begin + print "b," + x = Proc.new do + print "c," + eval("return :eval") + print "d," + end.call + print x, "," +rescue LocalJumpError => e + print "e," + print e.class, "," +end +print "f" diff --git a/spec/ruby/core/kernel/fixtures/test.rb b/spec/ruby/core/kernel/fixtures/test.rb new file mode 100644 index 0000000000..949948606f --- /dev/null +++ b/spec/ruby/core/kernel/fixtures/test.rb @@ -0,0 +1,362 @@ +def foo1 +end + +def foo2 +end + +def foo3 +end + +def foo4 +end + +def foo5 +end + +def foo6 +end + +def foo7 +end + +def foo8 +end + +def foo9 +end + +def foo10 +end + +def foo11 +end + +def foo12 +end + +def foo13 +end + +def foo14 +end + +def foo15 +end + +def foo16 +end + +def foo17 +end + +def foo18 +end + +def foo19 +end + +def foo20 +end + +def foo21 +end + +def foo22 +end + +def foo23 +end + +def foo24 +end + +def foo25 +end + +def foo26 +end + +def foo27 +end + +def foo28 +end + +def foo29 +end + +def foo30 +end + +def foo31 +end + +def foo32 +end + +def foo33 +end + +def foo34 +end + +def foo35 +end + +def foo36 +end + +def foo37 +end + +def foo38 +end + +def foo39 +end + +def foo40 +end + +def foo41 +end + +def foo42 +end + +def foo43 +end + +def foo44 +end + +def foo45 +end + +def foo46 +end + +def foo47 +end + +def foo48 +end + +def foo49 +end + +def foo50 +end + +def foo51 +end + +def foo52 +end + +def foo53 +end + +def foo54 +end + +def foo55 +end + +def foo56 +end + +def foo57 +end + +def foo58 +end + +def foo59 +end + +def foo60 +end + +def foo61 +end + +def foo62 +end + +def foo63 +end + +def foo64 +end + +def foo65 +end + +def foo66 +end + +def foo67 +end + +def foo68 +end + +def foo69 +end + +def foo70 +end + +def foo71 +end + +def foo72 +end + +def foo73 +end + +def foo74 +end + +def foo75 +end + +def foo76 +end + +def foo77 +end + +def foo78 +end + +def foo79 +end + +def foo80 +end + +def foo81 +end + +def foo82 +end + +def foo83 +end + +def foo84 +end + +def foo85 +end + +def foo86 +end + +def foo87 +end + +def foo88 +end + +def foo89 +end + +def foo90 +end + +def foo91 +end + +def foo92 +end + +def foo93 +end + +def foo94 +end + +def foo95 +end + +def foo96 +end + +def foo97 +end + +def foo98 +end + +def foo99 +end + +def foo100 +end + +def foo101 +end + +def foo102 +end + +def foo103 +end + +def foo104 +end + +def foo105 +end + +def foo106 +end + +def foo107 +end + +def foo108 +end + +def foo109 +end + +def foo110 +end + +def foo111 +end + +def foo112 +end + +def foo113 +end + +def foo114 +end + +def foo115 +end + +def foo116 +end + +def foo117 +end + +def foo118 +end + +def foo119 +end + +def foo120 +end + +def foo121 +end diff --git a/spec/ruby/core/kernel/fork_spec.rb b/spec/ruby/core/kernel/fork_spec.rb new file mode 100644 index 0000000000..8919d0914d --- /dev/null +++ b/spec/ruby/core/kernel/fork_spec.rb @@ -0,0 +1,15 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/process/fork', __FILE__) + +describe "Kernel#fork" do + it "is a private method" do + Kernel.should have_private_instance_method(:fork) + end + + it_behaves_like :process_fork, :fork, KernelSpecs::Method.new +end + +describe "Kernel.fork" do + it_behaves_like :process_fork, :fork, Kernel +end diff --git a/spec/ruby/core/kernel/format_spec.rb b/spec/ruby/core/kernel/format_spec.rb new file mode 100644 index 0000000000..c73d5f0efb --- /dev/null +++ b/spec/ruby/core/kernel/format_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#format" do + it "is a private method" do + Kernel.should have_private_instance_method(:format) + end +end + +describe "Kernel.format" do + it "is accessible as a module function" do + Kernel.format("%s", "hello").should == "hello" + end +end diff --git a/spec/ruby/core/kernel/freeze_spec.rb b/spec/ruby/core/kernel/freeze_spec.rb new file mode 100644 index 0000000000..961c1bccf0 --- /dev/null +++ b/spec/ruby/core/kernel/freeze_spec.rb @@ -0,0 +1,67 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#freeze" do + it "prevents self from being further modified" do + o = mock('o') + o.frozen?.should be_false + o.freeze + o.frozen?.should be_true + end + + it "returns self" do + o = Object.new + o.freeze.should equal(o) + end + + describe "on integers" do + it "has no effect since they are already frozen" do + 1.frozen?.should be_true + 1.freeze + + bignum = bignum_value + bignum.frozen?.should be_true + bignum.freeze + end + end + + describe "on a Float" do + it "has no effect since it is already frozen" do + 1.2.frozen?.should be_true + 1.2.freeze + end + end + + describe "on a Symbol" do + it "has no effect since it is already frozen" do + :sym.frozen?.should be_true + :sym.freeze + end + end + + describe "on true, false and nil" do + it "has no effect since they are already frozen" do + nil.frozen?.should be_true + true.frozen?.should be_true + false.frozen?.should be_true + + nil.freeze + true.freeze + false.freeze + end + end + + it "causes mutative calls to raise RuntimeError" do + o = Class.new do + def mutate; @foo = 1; end + end.new + o.freeze + lambda {o.mutate}.should raise_error(RuntimeError) + end + + it "causes instance_variable_set to raise RuntimeError" do + o = Object.new + o.freeze + lambda {o.instance_variable_set(:@foo, 1)}.should raise_error(RuntimeError) + end +end diff --git a/spec/ruby/core/kernel/frozen_spec.rb b/spec/ruby/core/kernel/frozen_spec.rb new file mode 100644 index 0000000000..9444b62249 --- /dev/null +++ b/spec/ruby/core/kernel/frozen_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#frozen?" do + it "returns true if self is frozen" do + o = mock('o') + p = mock('p') + p.freeze + o.frozen?.should == false + p.frozen?.should == true + end + + describe "on true, false and nil" do + it "returns true" do + true.frozen?.should be_true + false.frozen?.should be_true + nil.frozen?.should be_true + end + end + + describe "on integers" do + before :each do + @fixnum = 1 + @bignum = bignum_value + end + + it "returns true" do + @fixnum.frozen?.should be_true + @bignum.frozen?.should be_true + end + end + + describe "on a Float" do + before :each do + @float = 0.1 + end + + it "returns true" do + @float.frozen?.should be_true + end + end + + describe "on a Symbol" do + before :each do + @symbol = :symbol + end + + it "returns true" do + @symbol.frozen?.should be_true + end + end +end diff --git a/spec/ruby/core/kernel/gets_spec.rb b/spec/ruby/core/kernel/gets_spec.rb new file mode 100644 index 0000000000..c775b2b7a4 --- /dev/null +++ b/spec/ruby/core/kernel/gets_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#gets" do + it "is a private method" do + Kernel.should have_private_instance_method(:gets) + end + + it "calls ARGF.gets" do + ARGF.should_receive(:gets).and_return("spec") + gets.should == "spec" + end +end + +describe "Kernel.gets" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/global_variables_spec.rb b/spec/ruby/core/kernel/global_variables_spec.rb new file mode 100644 index 0000000000..739b800938 --- /dev/null +++ b/spec/ruby/core/kernel/global_variables_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.global_variables" do + it "is a private method" do + Kernel.should have_private_instance_method(:global_variables) + end + + before :all do + @i = 0 + end + + it "finds subset starting with std" do + global_variables.grep(/std/).should include(:$stderr, :$stdin, :$stdout) + a = global_variables.size + gvar_name = "$foolish_global_var#{@i += 1}" + global_variables.include?(gvar_name.to_sym).should == false + eval("#{gvar_name} = 1") + global_variables.size.should == a+1 + global_variables.should include(gvar_name.to_sym) + end +end + +describe "Kernel#global_variables" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/gsub_spec.rb b/spec/ruby/core/kernel/gsub_spec.rb new file mode 100644 index 0000000000..005ed0063d --- /dev/null +++ b/spec/ruby/core/kernel/gsub_spec.rb @@ -0,0 +1,96 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# FIXME: These methods exist only when the -n or -p option is passed to +# ruby, but we currently don't have a way of specifying that. +ruby_version_is ""..."1.9" do + describe "Kernel#gsub" do + it "is a private method" do + Kernel.should have_private_instance_method(:gsub) + end + + it "raises a TypeError if $_ is not a String" do + lambda { + $_ = 123 + gsub(/./, "!") + }.should raise_error(TypeError) + end + + it "when matches sets $_ to a new string, leaving the former value unaltered" do + orig_value = $_ = "hello" + gsub("ello", "ola") + $_.should_not equal(orig_value) + $_.should == "hola" + orig_value.should == "hello" + end + + it "returns a string with the same contents as $_ after the operation" do + $_ = "bye" + gsub("non-match", "?").should == "bye" + + orig_value = $_ = "bye" + gsub(/$/, "!").should == "bye!" + orig_value.should == "bye" + end + + it "accepts Regexps as patterns" do + $_ = "food" + gsub(/.$/, "l") + $_.should == "fool" + end + + it "accepts Strings as patterns, treated literally" do + $_ = "hello, world." + gsub(".", "!") + $_.should == "hello, world!" + end + + it "accepts objects which respond to #to_str as patterns and treats them as strings" do + $_ = "hello, world." + stringlike = mock(".") + stringlike.should_receive(:to_str).and_return(".") + gsub(stringlike, "!") + $_.should == "hello, world!" + end + end + + describe "Kernel#gsub with a pattern and replacement" do + it "accepts strings for replacement" do + $_ = "hello" + gsub(/./, ".") + $_.should == "....." + end + + it "accepts objects which respond to #to_str for replacement" do + o = mock("o") + o.should_receive(:to_str).and_return("o") + $_ = "ping" + gsub("i", o) + $_.should == "pong" + end + + it "replaces \\1 sequences with the regexp's corresponding capture" do + $_ = "hello!" + gsub(/(.)(.)/, '\2\1') + $_.should == "ehll!o" + end + end + + describe "Kernel#gsub with pattern and block" do + it "acts similarly to using $_.gsub" do + $_ = "olleh dlrow" + gsub(/(\w+)/){ $1.reverse } + $_.should == "hello world" + end + end + + describe "Kernel#gsub!" do + it "is a private method" do + Kernel.should have_private_instance_method(:gsub!) + end + end + + describe "Kernel.gsub!" do + it "needs to be reviewed for spec completeness" + end +end diff --git a/spec/ruby/core/kernel/inspect_spec.rb b/spec/ruby/core/kernel/inspect_spec.rb new file mode 100644 index 0000000000..92129ebbc5 --- /dev/null +++ b/spec/ruby/core/kernel/inspect_spec.rb @@ -0,0 +1,31 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#inspect" do + it "returns a String" do + Object.new.inspect.should be_an_instance_of(String) + end + + it "returns a tainted string if self is tainted" do + Object.new.taint.inspect.tainted?.should be_true + end + + it "returns an untrusted string if self is untrusted" do + Object.new.untrust.inspect.untrusted?.should be_true + end + + it "does not call #to_s if it is defined" do + # We must use a bare Object here + obj = Object.new + inspected = obj.inspect + + obj.stub!(:to_s).and_return("to_s'd") + + obj.inspect.should == inspected + end + + it "returns a String with the object class and object_id encoded" do + obj = Object.new + obj.inspect.should =~ /^#<Object:0x[0-9a-f]+>$/ + end +end diff --git a/spec/ruby/core/kernel/instance_of_spec.rb b/spec/ruby/core/kernel/instance_of_spec.rb new file mode 100644 index 0000000000..4801a1ff96 --- /dev/null +++ b/spec/ruby/core/kernel/instance_of_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe Kernel, "#instance_of?" do + before :each do + @o = KernelSpecs::InstanceClass.new + end + + it "returns true if given class is object's class" do + @o.instance_of?(KernelSpecs::InstanceClass).should == true + [].instance_of?(Array).should == true + ''.instance_of?(String).should == true + end + + it "returns false if given class is object's ancestor class" do + @o.instance_of?(KernelSpecs::AncestorClass).should == false + end + + it "returns false if given class is not object's class nor object's ancestor class" do + @o.instance_of?(Array).should == false + end + + it "returns false if given a Module that is included in object's class" do + @o.instance_of?(KernelSpecs::MyModule).should == false + end + + it "returns false if given a Module that is included one of object's ancestors only" do + @o.instance_of?(KernelSpecs::AncestorModule).should == false + end + + it "returns false if given a Module that is not included in object's class" do + @o.instance_of?(KernelSpecs::SomeOtherModule).should == false + end + + it "raises a TypeError if given an object that is not a Class nor a Module" do + lambda { @o.instance_of?(Object.new) }.should raise_error(TypeError) + lambda { @o.instance_of?('KernelSpecs::InstanceClass') }.should raise_error(TypeError) + lambda { @o.instance_of?(1) }.should raise_error(TypeError) + end +end diff --git a/spec/ruby/core/kernel/instance_variable_defined_spec.rb b/spec/ruby/core/kernel/instance_variable_defined_spec.rb new file mode 100644 index 0000000000..969d36c731 --- /dev/null +++ b/spec/ruby/core/kernel/instance_variable_defined_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#instance_variable_defined?" do + before do + @instance = KernelSpecs::InstanceVariable.new + end + + describe "when passed a String" do + it "returns false if the instance variable is not defined" do + @instance.instance_variable_defined?("@goodbye").should be_false + end + + it "returns true if the instance variable is defined" do + @instance.instance_variable_defined?("@greeting").should be_true + end + end + + describe "when passed a Symbol" do + it "returns false if the instance variable is not defined" do + @instance.instance_variable_defined?(:@goodbye).should be_false + end + + it "returns true if the instance variable is defined" do + @instance.instance_variable_defined?(:@greeting).should be_true + end + end + + it "raises a TypeError if passed an Object not defining #to_str" do + lambda do + obj = mock("kernel instance_variable_defined?") + @instance.instance_variable_defined? obj + end.should raise_error(TypeError) + end + + it "returns false if the instance variable is not defined for different types" do + [nil, false, true, 1, 2.0, :test, "test"].each do |obj| + obj.instance_variable_defined?("@goodbye").should be_false + end + end +end diff --git a/spec/ruby/core/kernel/instance_variable_get_spec.rb b/spec/ruby/core/kernel/instance_variable_get_spec.rb new file mode 100644 index 0000000000..0c564f11b6 --- /dev/null +++ b/spec/ruby/core/kernel/instance_variable_get_spec.rb @@ -0,0 +1,105 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#instance_variable_get" do + before :each do + @obj = Object.new + @obj.instance_variable_set("@test", :test) + end + + it "tries to convert the passed argument to a String using #to_str" do + obj = mock("to_str") + obj.should_receive(:to_str).and_return("@test") + @obj.instance_variable_get(obj) + end + + it "returns the value of the passed instance variable that is referred to by the conversion result" do + obj = mock("to_str") + obj.stub!(:to_str).and_return("@test") + @obj.instance_variable_get(obj).should == :test + end + + it "returns nil when the referred instance variable does not exist" do + @obj.instance_variable_get(:@does_not_exist).should be_nil + end + + it "raises a TypeError when the passed argument does not respond to #to_str" do + lambda { @obj.instance_variable_get(Object.new) }.should raise_error(TypeError) + end + + it "raises a TypeError when the passed argument can't be converted to a String" do + obj = mock("to_str") + obj.stub!(:to_str).and_return(123) + lambda { @obj.instance_variable_get(obj) }.should raise_error(TypeError) + end + + it "raises a NameError when the conversion result does not start with an '@'" do + obj = mock("to_str") + obj.stub!(:to_str).and_return("test") + lambda { @obj.instance_variable_get(obj) }.should raise_error(NameError) + end + + it "raises a NameError when passed just '@'" do + obj = mock("to_str") + obj.stub!(:to_str).and_return('@') + lambda { @obj.instance_variable_get(obj) }.should raise_error(NameError) + end +end + +describe "Kernel#instance_variable_get when passed Symbol" do + before :each do + @obj = Object.new + @obj.instance_variable_set("@test", :test) + end + + it "returns the value of the instance variable that is referred to by the passed Symbol" do + @obj.instance_variable_get(:@test).should == :test + end + + it "raises a NameError when passed :@ as an instance variable name" do + lambda { @obj.instance_variable_get(:"@") }.should raise_error(NameError) + end + + it "raises a NameError when the passed Symbol does not start with an '@'" do + lambda { @obj.instance_variable_get(:test) }.should raise_error(NameError) + end + + it "raises a NameError when the passed Symbol is an invalid instance variable name" do + lambda { @obj.instance_variable_get(:"@0") }.should raise_error(NameError) + end +end + +describe "Kernel#instance_variable_get when passed String" do + before :each do + @obj = Object.new + @obj.instance_variable_set("@test", :test) + end + + it "returns the value of the instance variable that is referred to by the passed String" do + @obj.instance_variable_get("@test").should == :test + end + + it "raises a NameError when the passed String does not start with an '@'" do + lambda { @obj.instance_variable_get("test") }.should raise_error(NameError) + end + + it "raises a NameError when the passed String is an invalid instance variable name" do + lambda { @obj.instance_variable_get("@0") }.should raise_error(NameError) + end + + it "raises a NameError when passed '@' as an instance variable name" do + lambda { @obj.instance_variable_get("@") }.should raise_error(NameError) + end +end + +describe "Kernel#instance_variable_get when passed Fixnum" do + before :each do + @obj = Object.new + @obj.instance_variable_set("@test", :test) + end + + it "raises a TypeError" do + lambda { @obj.instance_variable_get(10) }.should raise_error(TypeError) + lambda { @obj.instance_variable_get(-10) }.should raise_error(TypeError) + end +end diff --git a/spec/ruby/core/kernel/instance_variable_set_spec.rb b/spec/ruby/core/kernel/instance_variable_set_spec.rb new file mode 100644 index 0000000000..bac1bb5f99 --- /dev/null +++ b/spec/ruby/core/kernel/instance_variable_set_spec.rb @@ -0,0 +1,93 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#instance_variable_set" do + it "sets the value of the specified instance variable" do + class Dog + def initialize(p1, p2) + @a, @b = p1, p2 + end + end + Dog.new('cat', 99).instance_variable_set(:@a, 'dog').should == "dog" + end + + it "sets the value of the instance variable when no instance variables exist yet" do + class NoVariables; end + NoVariables.new.instance_variable_set(:@a, "new").should == "new" + end + + it "raises a NameError exception if the argument is not of form '@x'" do + class NoDog; end + lambda { NoDog.new.instance_variable_set(:c, "cat") }.should raise_error(NameError) + end + + it "raises a NameError exception if the argument is an invalid instance variable name" do + class DigitDog; end + lambda { DigitDog.new.instance_variable_set(:"@0", "cat") }.should raise_error(NameError) + end + + it "raises a NameError when the argument is '@'" do + class DogAt; end + lambda { DogAt.new.instance_variable_set(:"@", "cat") }.should raise_error(NameError) + end + + it "raises a TypeError if the instance variable name is a Fixnum" do + lambda { "".instance_variable_set(1, 2) }.should raise_error(TypeError) + end + + it "raises a TypeError if the instance variable name is an object that does not respond to to_str" do + class KernelSpecs::A; end + lambda { "".instance_variable_set(KernelSpecs::A.new, 3) }.should raise_error(TypeError) + end + + it "raises a NameError if the passed object, when coerced with to_str, does not start with @" do + class KernelSpecs::B + def to_str + ":c" + end + end + lambda { "".instance_variable_set(KernelSpecs::B.new, 4) }.should raise_error(NameError) + end + + it "raises a NameError if pass an object that cannot be a symbol" do + lambda { "".instance_variable_set(:c, 1) }.should raise_error(NameError) + end + + it "accepts as instance variable name any instance of a class that responds to to_str" do + class KernelSpecs::C + def initialize + @a = 1 + end + def to_str + "@a" + end + end + KernelSpecs::C.new.instance_variable_set(KernelSpecs::C.new, 2).should == 2 + end + + describe "on frozen objects" do + before :each do + klass = Class.new do + attr_reader :ivar + def initialize + @ivar = :origin + end + end + + @frozen = klass.new.freeze + end + + it "keeps stored object after any exceptions" do + lambda { @frozen.instance_variable_set(:@ivar, :replacement) }.should raise_error(Exception) + @frozen.ivar.should equal(:origin) + end + + it "raises a RuntimeError when passed replacement is identical to stored object" do + lambda { @frozen.instance_variable_set(:@ivar, :origin) }.should raise_error(RuntimeError) + end + + it "raises a RuntimeError when passed replacement is different from stored object" do + lambda { @frozen.instance_variable_set(:@ivar, :replacement) }.should raise_error(RuntimeError) + end + end +end diff --git a/spec/ruby/core/kernel/instance_variables_spec.rb b/spec/ruby/core/kernel/instance_variables_spec.rb new file mode 100644 index 0000000000..f744ee3c7a --- /dev/null +++ b/spec/ruby/core/kernel/instance_variables_spec.rb @@ -0,0 +1,27 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#instance_variables" do + describe "immediate values" do + it "returns an empty array if no instance variables are defined" do + 0.instance_variables.should == [] + end + + it "returns the correct array if an instance variable is added" do + a = 0 + lambda{ a.instance_variable_set("@test", 1) }.should raise_error(RuntimeError) + end + end + + describe "regular objects" do + it "returns an empty array if no instance variables are defined" do + Object.new.instance_variables.should == [] + end + + it "returns the correct array if an instance variable is added" do + a = Object.new + a.instance_variable_set("@test", 1) + a.instance_variables.should == [:@test] + end + end +end diff --git a/spec/ruby/core/kernel/is_a_spec.rb b/spec/ruby/core/kernel/is_a_spec.rb new file mode 100644 index 0000000000..c67c6552a0 --- /dev/null +++ b/spec/ruby/core/kernel/is_a_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/kind_of', __FILE__) + +describe "Kernel#is_a?" do + it_behaves_like(:kernel_kind_of , :is_a?) +end diff --git a/spec/ruby/core/kernel/iterator_spec.rb b/spec/ruby/core/kernel/iterator_spec.rb new file mode 100644 index 0000000000..e85f0dc612 --- /dev/null +++ b/spec/ruby/core/kernel/iterator_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#iterator?" do + it "is a private method" do + Kernel.should have_private_instance_method(:iterator?) + end +end + +describe "Kernel.iterator?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/itself_spec.rb b/spec/ruby/core/kernel/itself_spec.rb new file mode 100644 index 0000000000..722d75d718 --- /dev/null +++ b/spec/ruby/core/kernel/itself_spec.rb @@ -0,0 +1,9 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#itself" do + it "returns the receiver itself" do + foo = Object.new + foo.itself.should equal foo + end +end diff --git a/spec/ruby/core/kernel/kind_of_spec.rb b/spec/ruby/core/kernel/kind_of_spec.rb new file mode 100644 index 0000000000..56a54ec859 --- /dev/null +++ b/spec/ruby/core/kernel/kind_of_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/kind_of', __FILE__) + +describe "Kernel#kind_of?" do + it_behaves_like(:kernel_kind_of , :kind_of?) +end diff --git a/spec/ruby/core/kernel/lambda_spec.rb b/spec/ruby/core/kernel/lambda_spec.rb new file mode 100644 index 0000000000..8fa0075675 --- /dev/null +++ b/spec/ruby/core/kernel/lambda_spec.rb @@ -0,0 +1,86 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/lambda', __FILE__) + +# The functionality of lambdas is specified in core/proc + +describe "Kernel.lambda" do + it_behaves_like(:kernel_lambda, :lambda) + + it "is a private method" do + Kernel.should have_private_instance_method(:lambda) + end + + it "creates a lambda-style Proc if given a literal block" do + l = lambda { 42 } + l.lambda?.should be_true + end + + it "returned the passed Proc if given an existing Proc" do + some_proc = proc {} + l = lambda(&some_proc) + l.should equal(some_proc) + l.lambda?.should be_false + end + + it "checks the arity of the call when no args are specified" do + l = lambda { :called } + l.call.should == :called + + lambda { l.call(1) }.should raise_error(ArgumentError) + lambda { l.call(1, 2) }.should raise_error(ArgumentError) + end + + it "checks the arity when 1 arg is specified" do + l = lambda { |a| :called } + l.call(1).should == :called + + lambda { l.call }.should raise_error(ArgumentError) + lambda { l.call(1, 2) }.should raise_error(ArgumentError) + end + + it "does not check the arity when passing a Proc with &" do + l = lambda { || :called } + p = proc { || :called } + + lambda { l.call(1) }.should raise_error(ArgumentError) + p.call(1).should == :called + end + + it "accepts 0 arguments when used with ||" do + lambda { + lambda { || }.call(1) + }.should raise_error(ArgumentError) + end + + it "strictly checks the arity when 0 or 2..inf args are specified" do + l = lambda { |a,b| } + + lambda { + l.call + }.should raise_error(ArgumentError) + + lambda { + l.call(1) + }.should raise_error(ArgumentError) + + lambda { + l.call(1,2) + }.should_not raise_error(ArgumentError) + end + + it "returns from the lambda itself, not the creation site of the lambda" do + @reached_end_of_method = nil + def test + send(:lambda) { return }.call + @reached_end_of_method = true + end + test + @reached_end_of_method.should be_true + end + + it "allows long returns to flow through it" do + KernelSpecs::Lambda.new.outer.should == :good + end +end + diff --git a/spec/ruby/core/kernel/load_spec.rb b/spec/ruby/core/kernel/load_spec.rb new file mode 100644 index 0000000000..36cc07e38a --- /dev/null +++ b/spec/ruby/core/kernel/load_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/code_loading', __FILE__) +require File.expand_path('../shared/load', __FILE__) +require File.expand_path('../shared/require', __FILE__) + +describe "Kernel#load" do + before :each do + CodeLoadingSpecs.spec_setup + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + it "is a private method" do + Kernel.should have_private_instance_method(:load) + end + + it_behaves_like :kernel_require_basic, :load, CodeLoadingSpecs::Method.new +end + +describe "Kernel#load" do + it_behaves_like :kernel_load, :load, CodeLoadingSpecs::Method.new +end + +describe "Kernel.load" do + before :each do + CodeLoadingSpecs.spec_setup + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + it_behaves_like :kernel_require_basic, :load, Kernel +end + +describe "Kernel.load" do + it_behaves_like :kernel_load, :load, Kernel +end diff --git a/spec/ruby/core/kernel/local_variables_spec.rb b/spec/ruby/core/kernel/local_variables_spec.rb new file mode 100644 index 0000000000..7e8b364b4f --- /dev/null +++ b/spec/ruby/core/kernel/local_variables_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#local_variables" do + after :each do + ScratchPad.clear + end + + it "is a private method" do + Kernel.should have_private_instance_method(:local_variables) + end + + it "contains locals as they are added" do + a = 1 + b = 2 + local_variables.should include(:a, :b) + local_variables.length.should == 2 + end + + it "is accessible from bindings" do + def local_var_foo + a = 1 + b = 2 + binding + end + foo_binding = local_var_foo() + res = eval("local_variables",foo_binding) + res.should include(:a, :b) + res.length.should == 2 + end + + it "is accessible in eval" do + eval "a=1; b=2; ScratchPad.record local_variables" + ScratchPad.recorded.should include(:a, :b) + ScratchPad.recorded.length.should == 2 + end +end diff --git a/spec/ruby/core/kernel/loop_spec.rb b/spec/ruby/core/kernel/loop_spec.rb new file mode 100644 index 0000000000..f23e3afb02 --- /dev/null +++ b/spec/ruby/core/kernel/loop_spec.rb @@ -0,0 +1,81 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.loop" do + it "is a private method" do + Kernel.should have_private_instance_method(:loop) + end + + it "calls block until it is terminated by a break" do + i = 0 + loop do + i += 1 + break if i == 10 + end + + i.should == 10 + end + + it "returns value passed to break" do + loop do + break 123 + end.should == 123 + end + + it "returns nil if no value passed to break" do + loop do + break + end.should == nil + end + + it "returns an enumerator if no block given" do + enum = loop + enum.instance_of?(Enumerator).should be_true + cnt = 0 + enum.each do |*args| + raise "Args should be empty #{args.inspect}" unless args.empty? + cnt += 1 + break cnt if cnt >= 42 + end.should == 42 + end + + it "rescues StopIteration" do + loop do + raise StopIteration + end + 42.should == 42 + end + + it "rescues StopIteration's subclasses" do + finish = Class.new StopIteration + loop do + raise finish + end + 42.should == 42 + end + + it "does not rescue other errors" do + lambda{ loop do raise StandardError end }.should raise_error( StandardError ) + end + + ruby_version_is "2.3" do + it "returns StopIteration#result, the result value of a finished iterator" do + e = Enumerator.new { |y| + y << 1 + y << 2 + :stopped + } + loop { e.next }.should == :stopped + end + end + + describe "when no block is given" do + describe "returned Enumerator" do + describe "size" do + it "returns Float::INFINITY" do + loop.size.should == Float::INFINITY + end + end + end + end +end diff --git a/spec/ruby/core/kernel/match_spec.rb b/spec/ruby/core/kernel/match_spec.rb new file mode 100644 index 0000000000..8a117ed497 --- /dev/null +++ b/spec/ruby/core/kernel/match_spec.rb @@ -0,0 +1,14 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Kernel#=~" do + it "returns nil matching any object" do + o = Object.new + + (o =~ /Object/).should be_nil + (o =~ 'Object').should be_nil + (o =~ Object).should be_nil + (o =~ Object.new).should be_nil + (o =~ nil).should be_nil + (o =~ true).should be_nil + end +end diff --git a/spec/ruby/core/kernel/method_spec.rb b/spec/ruby/core/kernel/method_spec.rb new file mode 100644 index 0000000000..09a3f940ca --- /dev/null +++ b/spec/ruby/core/kernel/method_spec.rb @@ -0,0 +1,37 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../shared/method', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#method" do + it_behaves_like(:kernel_method, :method) + + before :each do + @obj = KernelSpecs::A.new + end + + it "can be called on a private method" do + @obj.send(:private_method).should == :private_method + @obj.method(:private_method).should be_an_instance_of(Method) + end + + it "can be called on a protected method" do + @obj.send(:protected_method).should == :protected_method + @obj.method(:protected_method).should be_an_instance_of(Method) + end + + it "will see an alias of the original method as == when in a derived class" do + obj = KernelSpecs::B.new + obj.method(:aliased_pub_method).should == obj.method(:pub_method) + end + + it "can call methods created with define_method" do + m = @obj.method(:defined_method) + m.call.should == :defined + end + + it "can be called even if we only repond_to_missing? method, true" do + m = KernelSpecs::RespondViaMissing.new.method(:handled_privately) + m.should be_an_instance_of(Method) + m.call(1, 2, 3).should == "Done handled_privately([1, 2, 3])" + end +end diff --git a/spec/ruby/core/kernel/methods_spec.rb b/spec/ruby/core/kernel/methods_spec.rb new file mode 100644 index 0000000000..5dfb17d4cb --- /dev/null +++ b/spec/ruby/core/kernel/methods_spec.rb @@ -0,0 +1,101 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../fixtures/reflection', __FILE__) + +# TODO: rewrite +describe "Kernel#methods" do + it "returns singleton methods defined by obj.meth" do + KernelSpecs::Methods.methods(false).should include(:ichi) + end + + it "returns singleton methods defined in 'class << self'" do + KernelSpecs::Methods.methods(false).should include(:san) + end + + it "returns private singleton methods defined by obj.meth" do + KernelSpecs::Methods.methods(false).should include(:shi) + end + + it "returns singleton methods defined in 'class << self' when it follows 'private'" do + KernelSpecs::Methods.methods(false).should include(:roku) + end + + it "does not return private singleton methods defined in 'class << self'" do + KernelSpecs::Methods.methods(false).should_not include(:shichi) + end + + it "returns the publicly accessible methods of the object" do + meths = KernelSpecs::Methods.methods(false) + meths.should include(:hachi, :ichi, :juu, :juu_ichi, + :juu_ni, :roku, :san, :shi) + + KernelSpecs::Methods.new.methods(false).should == [] + end + + it "returns the publicly accessible methods in the object, its ancestors and mixed-in modules" do + meths = KernelSpecs::Methods.methods(false) & KernelSpecs::Methods.methods + meths.should include(:hachi, :ichi, :juu, :juu_ichi, + :juu_ni, :roku, :san, :shi) + + KernelSpecs::Methods.new.methods.should include(:ku, :ni, :juu_san) + end + + it "returns methods added to the metaclass through extend" do + meth = KernelSpecs::Methods.new + meth.methods.should_not include(:peekaboo) + meth.extend(KernelSpecs::Methods::MetaclassMethods) + meth.methods.should include(:peekaboo) + end + + it "does not return undefined singleton methods defined by obj.meth" do + o = KernelSpecs::Child.new + def o.single; end + o.methods.should include(:single) + + class << o; self; end.send :undef_method, :single + o.methods.should_not include(:single) + end + + it "does not return superclass methods undefined in the object's class" do + KernelSpecs::Child.new.methods.should_not include(:parent_method) + end + + it "does not return superclass methods undefined in a superclass" do + KernelSpecs::Grandchild.new.methods.should_not include(:parent_method) + end + + it "does not return included module methods undefined in the object's class" do + KernelSpecs::Grandchild.new.methods.should_not include(:parent_mixin_method) + end +end + +describe :kernel_methods_supers, shared: true do + before :all do + @ms = [:pro, :pub] + end + + it "returns a unique list for an object extended by a module" do + m = ReflectSpecs.oed.methods(*@object) + m.select { |x| @ms.include? x }.sort.should == @ms + end + + it "returns a unique list for a class including a module" do + m = ReflectSpecs::D.new.methods(*@object) + m.select { |x| @ms.include? x }.sort.should == @ms + end + + it "returns a unique list for a subclass of a class that includes a module" do + m = ReflectSpecs::E.new.methods(*@object) + m.select { |x| @ms.include? x }.sort.should == @ms + end +end + +describe "Kernel#methods" do + describe "when not passed an argument" do + it_behaves_like :kernel_methods_supers, nil, [] + end + + describe "when passed true" do + it_behaves_like :kernel_methods_supers, nil, true + end +end diff --git a/spec/ruby/core/kernel/nil_spec.rb b/spec/ruby/core/kernel/nil_spec.rb new file mode 100644 index 0000000000..0b5e34f7f1 --- /dev/null +++ b/spec/ruby/core/kernel/nil_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#nil?" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/not_match_spec.rb b/spec/ruby/core/kernel/not_match_spec.rb new file mode 100644 index 0000000000..42bd45c106 --- /dev/null +++ b/spec/ruby/core/kernel/not_match_spec.rb @@ -0,0 +1,21 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#!~" do + class KernelSpecs::NotMatch + def !~(obj) + :foo + end + end + + it 'calls =~ internally and negates the result' do + obj = Object.new + obj.should_receive(:=~).and_return(true) + (obj !~ :foo).should == false + end + + it 'can be overridden in subclasses' do + obj = KernelSpecs::NotMatch.new + (obj !~ :bar).should == :foo + end +end diff --git a/spec/ruby/core/kernel/object_id_spec.rb b/spec/ruby/core/kernel/object_id_spec.rb new file mode 100644 index 0000000000..0a12415a40 --- /dev/null +++ b/spec/ruby/core/kernel/object_id_spec.rb @@ -0,0 +1,6 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../shared/kernel/object_id', __FILE__) + +describe "Kernel#object_id" do + it_behaves_like :object_id, :object_id, Object +end diff --git a/spec/ruby/core/kernel/open_spec.rb b/spec/ruby/core/kernel/open_spec.rb new file mode 100644 index 0000000000..4e99061219 --- /dev/null +++ b/spec/ruby/core/kernel/open_spec.rb @@ -0,0 +1,141 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#open" do + + before :each do + @name = tmp("kernel_open.txt") + @content = "This is a test" + touch(@name) { |f| f.write @content } + @file = nil + end + + after :each do + @file.close if @file + rm_r @name + end + + it "is a private method" do + Kernel.should have_private_instance_method(:open) + end + + it "opens a file when given a valid filename" do + @file = open(@name) + @file.should be_kind_of(File) + end + + it "opens a file when called with a block" do + open(@name, "r") { |f| f.gets }.should == @content + end + + platform_is_not :windows do + it "opens an io when path starts with a pipe" do + @io = open("|date") + begin + @io.should be_kind_of(IO) + ensure + @io.close + end + end + + it "opens an io when called with a block" do + @output = open("|date") { |f| f.gets } + @output.should_not == '' + end + + it "opens an io for writing" do + bytes = open("|cat", "w") { |io| io.write(".") } + bytes.should == 1 + end + end + + platform_is :windows do + it "opens an io when path starts with a pipe" do + @io = open("|date /t") + begin + @io.should be_kind_of(IO) + @io.read + ensure + @io.close + end + end + + it "opens an io when called with a block" do + @output = open("|date /t") { |f| f.gets } + @output.should_not == '' + end + end + + it "raises an ArgumentError if not passed one argument" do + lambda { open }.should raise_error(ArgumentError) + end + + describe "when given an object that responds to to_open" do + before :each do + ScratchPad.clear + end + + it "calls #to_path to covert the argument to a String before calling #to_str" do + obj = mock("open to_path") + obj.should_receive(:to_path).at_least(1).times.and_return(@name) + obj.should_not_receive(:to_str) + + open(obj, "r") { |f| f.gets }.should == @content + end + + it "calls #to_str to convert the argument to a String" do + obj = mock("open to_str") + obj.should_receive(:to_str).at_least(1).times.and_return(@name) + + open(obj, "r") { |f| f.gets }.should == @content + end + + it "calls #to_open on argument" do + obj = mock('fileish') + @file = File.open(@name) + obj.should_receive(:to_open).and_return(@file) + @file = open(obj) + @file.should be_kind_of(File) + end + + it "returns the value from #to_open" do + obj = mock('to_open') + obj.should_receive(:to_open).and_return(:value) + + open(obj).should == :value + end + + it "passes its arguments onto #to_open" do + obj = mock('to_open') + obj.should_receive(:to_open).with(1,2,3) + + open(obj, 1, 2, 3) + end + + it "passes the return value from #to_open to a block" do + obj = mock('to_open') + obj.should_receive(:to_open).and_return(:value) + + open(obj) do |mock| + ScratchPad.record(mock) + end + + ScratchPad.recorded.should == :value + end + end + + it "raises a TypeError if passed a non-String that does not respond to #to_open" do + obj = mock('non-fileish') + lambda { open(obj) }.should raise_error(TypeError) + lambda { open(nil) }.should raise_error(TypeError) + lambda { open(7) }.should raise_error(TypeError) + end + + it "accepts nil for mode and permission" do + open(@name, nil, nil) { |f| f.gets }.should == @content + end +end + +describe "Kernel.open" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/p_spec.rb b/spec/ruby/core/kernel/p_spec.rb new file mode 100644 index 0000000000..c451f5952a --- /dev/null +++ b/spec/ruby/core/kernel/p_spec.rb @@ -0,0 +1,79 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#p" do + before :all do + @rs_f, @rs_b, @rs_c = $/, $\, $, + end + + after :each do + $/, $\, $, = @rs_f, @rs_b, @rs_c + end + + it "is a private method" do + Kernel.should have_private_instance_method(:p) + end + + # TODO: fix + it "flushes output if receiver is a File" do + filename = tmp("Kernel_p_flush") + $$.to_s + begin + File.open(filename, "w") do |f| + begin + old_stdout = $stdout + $stdout = f + p("abcde") + ensure + $stdout = old_stdout + end + + File.open(filename) do |f2| + f2.read(7).should == "\"abcde\"" + end + end + ensure + rm_r filename + end + end + + it "prints obj.inspect followed by system record separator for each argument given" do + o = mock("Inspector Gadget") + o.should_receive(:inspect).any_number_of_times.and_return "Next time, Gadget, NEXT TIME!" + + lambda { p(o) }.should output("Next time, Gadget, NEXT TIME!\n") + lambda { p(*[o]) }.should output("Next time, Gadget, NEXT TIME!\n") + lambda { p(*[o, o]) }.should output("Next time, Gadget, NEXT TIME!\nNext time, Gadget, NEXT TIME!\n") + lambda { p([o])}.should output("[#{o.inspect}]\n") + end + + it "is not affected by setting $\\, $/ or $," do + o = mock("Inspector Gadget") + o.should_receive(:inspect).any_number_of_times.and_return "Next time, Gadget, NEXT TIME!" + + $, = " *helicopter sound*\n" + lambda { p(o) }.should output_to_fd("Next time, Gadget, NEXT TIME!\n") + + $\ = " *helicopter sound*\n" + lambda { p(o) }.should output_to_fd("Next time, Gadget, NEXT TIME!\n") + + $/ = " *helicopter sound*\n" + lambda { p(o) }.should output_to_fd("Next time, Gadget, NEXT TIME!\n") + end + + it "prints nothing if no argument is given" do + lambda { p }.should output("") + end + + it "prints nothing if called splatting an empty Array" do + lambda { p(*[]) }.should output("") + end + +=begin Not sure how to spec this, but wanted to note the behavior here + it "does not flush if receiver is not a TTY or a File" do + end +=end +end + +describe "Kernel.p" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/print_spec.rb b/spec/ruby/core/kernel/print_spec.rb new file mode 100644 index 0000000000..3b642538cb --- /dev/null +++ b/spec/ruby/core/kernel/print_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#print" do + it "is a private method" do + Kernel.should have_private_instance_method(:print) + end +end + +describe "Kernel.print" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/printf_spec.rb b/spec/ruby/core/kernel/printf_spec.rb new file mode 100644 index 0000000000..b4c68fa449 --- /dev/null +++ b/spec/ruby/core/kernel/printf_spec.rb @@ -0,0 +1,34 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#printf" do + it "is a private method" do + Kernel.should have_private_instance_method(:printf) + end +end + +describe "Kernel.printf" do + + before :each do + @stdout = $stdout + @name = tmp("kernel_puts.txt") + $stdout = new_io @name + end + + after :each do + $stdout.close + $stdout = @stdout + rm_r @name + end + + it "writes to stdout when a string is the first argument" do + $stdout.should_receive(:write).with("string") + Kernel.printf("%s", "string") + end + + it "calls write on the first argument when it is not a string" do + object = mock('io') + object.should_receive(:write).with("string") + Kernel.printf(object, "%s", "string") + end +end diff --git a/spec/ruby/core/kernel/private_methods_spec.rb b/spec/ruby/core/kernel/private_methods_spec.rb new file mode 100644 index 0000000000..d0603c72b8 --- /dev/null +++ b/spec/ruby/core/kernel/private_methods_spec.rb @@ -0,0 +1,69 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../fixtures/reflection', __FILE__) + +# TODO: rewrite +describe "Kernel#private_methods" do + it "returns a list of the names of privately accessible methods in the object" do + m = KernelSpecs::Methods.private_methods(false) + m.should include(:shichi) + m = KernelSpecs::Methods.new.private_methods(false) + m.should include(:juu_shi) + end + + it "returns a list of the names of privately accessible methods in the object and its ancestors and mixed-in modules" do + m = (KernelSpecs::Methods.private_methods(false) & KernelSpecs::Methods.private_methods) + + m.should include(:shichi) + m = KernelSpecs::Methods.new.private_methods + m.should include(:juu_shi) + end + + it "returns private methods mixed in to the metaclass" do + m = KernelSpecs::Methods.new + m.extend(KernelSpecs::Methods::MetaclassMethods) + m.private_methods.should include(:shoo) + end +end + +describe :kernel_private_methods_supers, shared: true do + it "returns a unique list for an object extended by a module" do + m = ReflectSpecs.oed.private_methods(*@object) + m.select { |x| x == :pri }.sort.should == [:pri] + end + + it "returns a unique list for a class including a module" do + m = ReflectSpecs::D.new.private_methods(*@object) + m.select { |x| x == :pri }.sort.should == [:pri] + end + + it "returns a unique list for a subclass of a class that includes a module" do + m = ReflectSpecs::E.new.private_methods(*@object) + m.select { |x| x == :pri }.sort.should == [:pri] + end +end + +describe :kernel_private_methods_with_falsy, shared: true do + it "returns a list of private methods in without its ancestors" do + ReflectSpecs::F.private_methods(@object).select{|m|/_pri\z/ =~ m}.sort.should == [:ds_pri, :fs_pri] + ReflectSpecs::F.new.private_methods(@object).should == [:f_pri] + end +end + +describe "Kernel#private_methods" do + describe "when not passed an argument" do + it_behaves_like :kernel_private_methods_supers, nil, [] + end + + describe "when passed true" do + it_behaves_like :kernel_private_methods_supers, nil, true + end + + describe "when passed false" do + it_behaves_like :kernel_private_methods_with_falsy, nil, false + end + + describe "when passed nil" do + it_behaves_like :kernel_private_methods_with_falsy, nil, nil + end +end diff --git a/spec/ruby/core/kernel/proc_spec.rb b/spec/ruby/core/kernel/proc_spec.rb new file mode 100644 index 0000000000..4e4854f97d --- /dev/null +++ b/spec/ruby/core/kernel/proc_spec.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/lambda', __FILE__) + +# The functionality of Proc objects is specified in core/proc + +describe "Kernel.proc" do + it "is a private method" do + Kernel.should have_private_instance_method(:proc) + end + + it "creates a proc-style Proc if given a literal block" do + l = proc { 42 } + l.lambda?.should be_false + end + + it "returned the passed Proc if given an existing Proc" do + some_lambda = lambda {} + some_lambda.lambda?.should be_true + l = proc(&some_lambda) + l.should equal(some_lambda) + l.lambda?.should be_true + end + + it_behaves_like(:kernel_lambda, :proc) + + it "returns from the creation site of the proc, not just the proc itself" do + @reached_end_of_method = nil + def test + proc { return }.call + @reached_end_of_method = true + end + test + @reached_end_of_method.should be_nil + end +end + +describe "Kernel#proc" do + it "uses the implicit block from an enclosing method" do + def some_method + proc + end + + prc = some_method { "hello" } + + prc.call.should == "hello" + end + + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/protected_methods_spec.rb b/spec/ruby/core/kernel/protected_methods_spec.rb new file mode 100644 index 0000000000..2e09cead53 --- /dev/null +++ b/spec/ruby/core/kernel/protected_methods_spec.rb @@ -0,0 +1,69 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../fixtures/reflection', __FILE__) + +# TODO: rewrite + +# The reason why having include() is to show the specification explicitly. +# You should use have_protected_method() with the exception of this spec. +describe "Kernel#protected_methods" do + it "returns a list of the names of protected methods accessible in the object" do + KernelSpecs::Methods.protected_methods(false).sort.should include(:juu_ichi) + KernelSpecs::Methods.new.protected_methods(false).should include(:ku) + end + + it "returns a list of the names of protected methods accessible in the object and from its ancestors and mixed-in modules" do + l1 = KernelSpecs::Methods.protected_methods(false) + l2 = KernelSpecs::Methods.protected_methods + (l1 & l2).should include(:juu_ichi) + KernelSpecs::Methods.new.protected_methods.should include(:ku) + end + + it "returns methods mixed in to the metaclass" do + m = KernelSpecs::Methods.new + m.extend(KernelSpecs::Methods::MetaclassMethods) + m.protected_methods.should include(:nopeeking) + end +end + +describe :kernel_protected_methods_supers, shared: true do + it "returns a unique list for an object extended by a module" do + m = ReflectSpecs.oed.protected_methods(*@object) + m.select { |x| x == :pro }.sort.should == [:pro] + end + + it "returns a unique list for a class including a module" do + m = ReflectSpecs::D.new.protected_methods(*@object) + m.select { |x| x == :pro }.sort.should == [:pro] + end + + it "returns a unique list for a subclass of a class that includes a module" do + m = ReflectSpecs::E.new.protected_methods(*@object) + m.select { |x| x == :pro }.sort.should == [:pro] + end +end + +describe :kernel_protected_methods_with_falsy, shared: true do + it "returns a list of protected methods in without its ancestors" do + ReflectSpecs::F.protected_methods(@object).select{|m|/_pro\z/ =~ m}.sort.should == [:ds_pro, :fs_pro] + ReflectSpecs::F.new.protected_methods(@object).should == [:f_pro] + end +end + +describe "Kernel#protected_methods" do + describe "when not passed an argument" do + it_behaves_like :kernel_protected_methods_supers, nil, [] + end + + describe "when passed true" do + it_behaves_like :kernel_protected_methods_supers, nil, true + end + + describe "when passed false" do + it_behaves_like :kernel_protected_methods_with_falsy, nil, false + end + + describe "when passed nil" do + it_behaves_like :kernel_protected_methods_with_falsy, nil, nil + end +end diff --git a/spec/ruby/core/kernel/public_method_spec.rb b/spec/ruby/core/kernel/public_method_spec.rb new file mode 100644 index 0000000000..f1cc168420 --- /dev/null +++ b/spec/ruby/core/kernel/public_method_spec.rb @@ -0,0 +1,32 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../shared/method', __FILE__) + +describe "Kernel#public_method" do + it_behaves_like(:kernel_method, :public_method) + + before :each do + @obj = KernelSpecs::A.new + end + + it "raises a NameError when called on a private method" do + @obj.send(:private_method).should == :private_method + lambda do + @obj.public_method(:private_method) + end.should raise_error(NameError) + end + + it "raises a NameError when called on a protected method" do + @obj.send(:protected_method).should == :protected_method + lambda { + @obj.public_method(:protected_method) + }.should raise_error(NameError) + end + + it "raises a NameError if we only repond_to_missing? method, true" do + obj = KernelSpecs::RespondViaMissing.new + lambda do + obj.public_method(:handled_privately) + end.should raise_error(NameError) + end +end diff --git a/spec/ruby/core/kernel/public_methods_spec.rb b/spec/ruby/core/kernel/public_methods_spec.rb new file mode 100644 index 0000000000..b72775483c --- /dev/null +++ b/spec/ruby/core/kernel/public_methods_spec.rb @@ -0,0 +1,76 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../fixtures/reflection', __FILE__) + +# TODO: rewrite +describe "Kernel#public_methods" do + it "returns a list of the names of publicly accessible methods in the object" do + KernelSpecs::Methods.public_methods(false).sort.should include(:hachi, + :ichi, :juu, :juu_ni, :roku, :san, :shi) + KernelSpecs::Methods.new.public_methods(false).sort.should include(:juu_san, :ni) + end + + it "returns a list of names without protected accessible methods in the object" do + KernelSpecs::Methods.public_methods(false).sort.should_not include(:juu_ichi) + KernelSpecs::Methods.new.public_methods(false).sort.should_not include(:ku) + end + + it "returns a list of the names of publicly accessible methods in the object and its ancestors and mixed-in modules" do + (KernelSpecs::Methods.public_methods(false) & KernelSpecs::Methods.public_methods).sort.should include( + :hachi, :ichi, :juu, :juu_ni, :roku, :san, :shi) + m = KernelSpecs::Methods.new.public_methods + m.should include(:ni, :juu_san) + end + + it "returns methods mixed in to the metaclass" do + m = KernelSpecs::Methods.new + m.extend(KernelSpecs::Methods::MetaclassMethods) + m.public_methods.should include(:peekaboo) + end + + it "returns public methods for immediates" do + 10.public_methods.should include(:divmod) + end +end + +describe :kernel_public_methods_supers, shared: true do + it "returns a unique list for an object extended by a module" do + m = ReflectSpecs.oed.public_methods(*@object) + m.select { |x| x == :pub }.sort.should == [:pub] + end + + it "returns a unique list for a class including a module" do + m = ReflectSpecs::D.new.public_methods(*@object) + m.select { |x| x == :pub }.sort.should == [:pub] + end + + it "returns a unique list for a subclass of a class that includes a module" do + m = ReflectSpecs::E.new.public_methods(*@object) + m.select { |x| x == :pub }.sort.should == [:pub] + end +end + +describe :kernel_public_methods_with_falsy, shared: true do + it "returns a list of public methods in without its ancestors" do + ReflectSpecs::F.public_methods(@object).select{|m|/_pub\z/ =~ m}.sort.should == [:ds_pub, :fs_pub] + ReflectSpecs::F.new.public_methods(@object).should == [:f_pub] + end +end + +describe "Kernel#public_methods" do + describe "when not passed an argument" do + it_behaves_like :kernel_public_methods_supers, nil, [] + end + + describe "when passed true" do + it_behaves_like :kernel_public_methods_supers, nil, true + end + + describe "when passed false" do + it_behaves_like :kernel_public_methods_with_falsy, nil, false + end + + describe "when passed nil" do + it_behaves_like :kernel_public_methods_with_falsy, nil, nil + end +end diff --git a/spec/ruby/core/kernel/public_send_spec.rb b/spec/ruby/core/kernel/public_send_spec.rb new file mode 100644 index 0000000000..2eabbc7dc9 --- /dev/null +++ b/spec/ruby/core/kernel/public_send_spec.rb @@ -0,0 +1,108 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/basicobject/send', __FILE__) + +describe "Kernel#public_send" do + it "invokes the named public method" do + class KernelSpecs::Foo + def bar + 'done' + end + end + KernelSpecs::Foo.new.public_send(:bar).should == 'done' + end + + it "invokes the named alias of a public method" do + class KernelSpecs::Foo + def bar + 'done' + end + alias :aka :bar + end + KernelSpecs::Foo.new.public_send(:aka).should == 'done' + end + + it "raises a NoMethodError if the method is protected" do + class KernelSpecs::Foo + protected + def bar + 'done' + end + end + lambda { KernelSpecs::Foo.new.public_send(:bar)}.should raise_error(NoMethodError) + end + + it "raises a NoMethodError if the named method is private" do + class KernelSpecs::Foo + private + def bar + 'done2' + end + end + lambda { + KernelSpecs::Foo.new.public_send(:bar) + }.should raise_error(NoMethodError) + end + + context 'called from own public method' do + before do + class << @receiver = Object.new + def call_protected_method + public_send :protected_method + end + + def call_private_method + public_send :private_method + end + + protected + + def protected_method + raise 'Should not called' + end + + private + + def private_method + raise 'Should not called' + end + end + end + + it "raises a NoMethodError if the method is protected" do + lambda { @receiver.call_protected_method }.should raise_error(NoMethodError) + end + + it "raises a NoMethodError if the method is private" do + lambda { @receiver.call_private_method }.should raise_error(NoMethodError) + end + end + + it "raises a NoMethodError if the named method is an alias of a private method" do + class KernelSpecs::Foo + private + def bar + 'done2' + end + alias :aka :bar + end + lambda { + KernelSpecs::Foo.new.public_send(:aka) + }.should raise_error(NoMethodError) + end + + it "raises a NoMethodError if the named method is an alias of a protected method" do + class KernelSpecs::Foo + protected + def bar + 'done2' + end + alias :aka :bar + end + lambda { + KernelSpecs::Foo.new.public_send(:aka) + }.should raise_error(NoMethodError) + end + + it_behaves_like(:basicobject_send, :public_send) +end diff --git a/spec/ruby/core/kernel/putc_spec.rb b/spec/ruby/core/kernel/putc_spec.rb new file mode 100644 index 0000000000..1f5e9b6d63 --- /dev/null +++ b/spec/ruby/core/kernel/putc_spec.rb @@ -0,0 +1,39 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/io/putc', __FILE__) + +describe "Kernel#putc" do + it "is a private instance method" do + Kernel.should have_private_instance_method(:putc) + end +end + +describe "Kernel.putc" do + before :each do + @name = tmp("kernel_putc.txt") + @io = new_io @name + @io_object = @object + @stdout, $stdout = $stdout, @io + end + + after :each do + $stdout = @stdout + end + + it_behaves_like :io_putc, :putc_method, KernelSpecs +end + +describe "Kernel#putc" do + before :each do + @name = tmp("kernel_putc.txt") + @io = new_io @name + @io_object = @object + @stdout, $stdout = $stdout, @io + end + + after :each do + $stdout = @stdout + end + + it_behaves_like :io_putc, :putc_function, KernelSpecs +end diff --git a/spec/ruby/core/kernel/puts_spec.rb b/spec/ruby/core/kernel/puts_spec.rb new file mode 100644 index 0000000000..c5297f1cb2 --- /dev/null +++ b/spec/ruby/core/kernel/puts_spec.rb @@ -0,0 +1,29 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#puts" do + before :each do + @stdout = $stdout + @name = tmp("kernel_puts.txt") + $stdout = new_io @name + end + + after :each do + $stdout.close + $stdout = @stdout + rm_r @name + end + + it "is a private method" do + Kernel.should have_private_instance_method(:puts) + end + + it "delegates to $stdout.puts" do + $stdout.should_receive(:puts).with(:arg) + puts :arg + end +end + +describe "Kernel.puts" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/raise_spec.rb b/spec/ruby/core/kernel/raise_spec.rb new file mode 100644 index 0000000000..6efffd9366 --- /dev/null +++ b/spec/ruby/core/kernel/raise_spec.rb @@ -0,0 +1,17 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/kernel/raise', __FILE__) + +describe "Kernel#raise" do + it "is a private method" do + Kernel.should have_private_instance_method(:raise) + end +end + +describe "Kernel#raise" do + it_behaves_like :kernel_raise, :raise, Kernel +end + +describe "Kernel.raise" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/rand_spec.rb b/spec/ruby/core/kernel/rand_spec.rb new file mode 100644 index 0000000000..f52b5f75b5 --- /dev/null +++ b/spec/ruby/core/kernel/rand_spec.rb @@ -0,0 +1,139 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.rand" do + it "is a private method" do + Kernel.should have_private_instance_method(:rand) + end + + it "returns a float if no argument is passed" do + rand.should be_kind_of(Float) + end + + it "returns an integer for an integer argument" do + rand(77).should be_kind_of(Integer) + end + + it "returns an integer for a float argument greater than 1" do + rand(1.3).should be_kind_of(Integer) + end + + it "returns a float for an argument between -1 and 1" do + rand(-0.999).should be_kind_of(Float) + rand(-0.01).should be_kind_of(Float) + rand(0).should be_kind_of(Float) + rand(0.01).should be_kind_of(Float) + rand(0.999).should be_kind_of(Float) + end + + it "ignores the sign of the argument" do + [0, 1, 2, 3].should include(rand(-4)) + end + + it "never returns a value greater or equal to 1.0 with no arguments" do + 1000.times do + (0...1.0).should include(rand) + end + end + + it "never returns a value greater or equal to any passed in max argument" do + 1000.times do + (0...100).to_a.should include(rand(100)) + end + end + + it "calls to_int on its argument" do + l = mock('limit') + l.should_receive(:to_int).and_return 7 + + rand l + end + + context "given an exclusive range" do + it "returns an Integer between the two Integers" do + 1000.times do + x = rand(4...6) + x.should be_kind_of(Integer) + (4...6).should include(x) + end + end + + it "returns a Float between the given Integer and Float" do + 1000.times do + x = rand(4...6.5) + x.should be_kind_of(Float) + (4...6.5).should include(x) + end + end + + it "returns a Float between the given Float and Integer" do + 1000.times do + x = rand(3.5...6) + x.should be_kind_of(Float) + (3.5...6).should include(x) + end + end + + it "returns a Float between the two given Floats" do + 1000.times do + x = rand(3.5...6.5) + x.should be_kind_of(Float) + (3.5...6.5).should include(x) + end + end + end + + context "given an inclusive range" do + it "returns an Integer between the two Integers" do + 1000.times do + x = rand(4..6) + x.should be_kind_of(Integer) + (4..6).should include(x) + end + end + + it "returns a Float between the given Integer and Float" do + 1000.times do + x = rand(4..6.5) + x.should be_kind_of(Float) + (4..6.5).should include(x) + end + end + + it "returns a Float between the given Float and Integer" do + 1000.times do + x = rand(3.5..6) + x.should be_kind_of(Float) + (3.5..6).should include(x) + end + end + + it "returns a Float between the two given Floats" do + 1000.times do + x = rand(3.5..6.5) + x.should be_kind_of(Float) + (3.5..6.5).should include(x) + end + end + end + + it "returns a numeric for an range argument where max is < 1" do + rand(0.25..0.75).should be_kind_of(Numeric) + end + + it "returns nil when range is backwards" do + rand(1..0).should be_nil + end + + it "returns the range start/end when Float range is 0" do + rand(1.0..1.0).should eql(1.0) + end + + it "returns the range start/end when Integer range is 0" do + rand(42..42).should eql(42) + end +end + +describe "Kernel#rand" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/readline_spec.rb b/spec/ruby/core/kernel/readline_spec.rb new file mode 100644 index 0000000000..c69eee0726 --- /dev/null +++ b/spec/ruby/core/kernel/readline_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#readline" do + it "is a private method" do + Kernel.should have_private_instance_method(:readline) + end +end + +describe "Kernel.readline" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/readlines_spec.rb b/spec/ruby/core/kernel/readlines_spec.rb new file mode 100644 index 0000000000..d5e07f8f75 --- /dev/null +++ b/spec/ruby/core/kernel/readlines_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#readlines" do + it "is a private method" do + Kernel.should have_private_instance_method(:readlines) + end +end + +describe "Kernel.readlines" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/remove_instance_variable_spec.rb b/spec/ruby/core/kernel/remove_instance_variable_spec.rb new file mode 100644 index 0000000000..6a9f78b9bc --- /dev/null +++ b/spec/ruby/core/kernel/remove_instance_variable_spec.rb @@ -0,0 +1,59 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_remove_instance_variable, shared: true do + it "returns the instance variable's value" do + value = @instance.send :remove_instance_variable, @object + value.should == "hello" + end + + it "removes the instance variable" do + @instance.send :remove_instance_variable, @object + @instance.instance_variable_defined?(@object).should be_false + end +end + +describe "Kernel#remove_instance_variable" do + before do + @instance = KernelSpecs::InstanceVariable.new + end + + it "is a public method" do + Kernel.should have_public_instance_method(:remove_instance_variable, false) + end + + it "raises a NameError if the instance variable is not defined" do + lambda do + @instance.send :remove_instance_variable, :@unknown + end.should raise_error(NameError) + end + + it "raises a NameError if the argument is not a valid instance variable name" do + lambda do + @instance.send :remove_instance_variable, :"@0" + end.should raise_error(NameError) + end + + it "raises a TypeError if passed an Object not defining #to_str" do + lambda do + obj = mock("kernel remove_instance_variable") + @instance.send :remove_instance_variable, obj + end.should raise_error(TypeError) + end + + describe "when passed a String" do + it_behaves_like :kernel_remove_instance_variable, nil, "@greeting" + end + + describe "when passed a Symbol" do + it_behaves_like :kernel_remove_instance_variable, nil, :@greeting + end + + describe "when passed an Object" do + it "calls #to_str to convert the argument" do + name = mock("kernel remove_instance_variable") + name.should_receive(:to_str).and_return("@greeting") + @instance.send :remove_instance_variable, name + end + end +end diff --git a/spec/ruby/core/kernel/require_relative_spec.rb b/spec/ruby/core/kernel/require_relative_spec.rb new file mode 100644 index 0000000000..04cf5444d2 --- /dev/null +++ b/spec/ruby/core/kernel/require_relative_spec.rb @@ -0,0 +1,349 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/code_loading', __FILE__) + +describe "Kernel#require_relative with a relative path" do + it "needs to be reviewed for spec completeness" + + before :each do + CodeLoadingSpecs.spec_setup + @dir = "../../fixtures/code" + @abs_dir = File.realpath(@dir, File.dirname(__FILE__)) + @path = "#{@dir}/load_fixture.rb" + @abs_path = File.realpath(@path, File.dirname(__FILE__)) + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + platform_is_not :windows do + describe "when file is a symlink" do + before :each do + @link = tmp("symlink.rb", false) + @real_path = "#{@abs_dir}/symlink/symlink1.rb" + File.symlink(@real_path, @link) + end + + after :each do + rm_r @link + end + + it "loads a path relative to current file" do + require_relative(@link).should be_true + ScratchPad.recorded.should == [:loaded] + end + end + end + + it "loads a path relative to the current file" do + require_relative(@path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a file defining many methods" do + require_relative("#{@dir}/methods_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "raises a LoadError if the file does not exist" do + lambda { require_relative("#{@dir}/nonexistent.rb") }.should raise_error(LoadError) + ScratchPad.recorded.should == [] + end + + it "raises a LoadError if basepath does not exist" do + lambda { eval("require_relative('#{@dir}/nonexistent.rb')") }.should raise_error(LoadError) + end + + it "stores the missing path in a LoadError object" do + path = "#{@dir}/nonexistent.rb" + + lambda { + require_relative(path) + }.should(raise_error(LoadError) { |e| + e.path.should == File.expand_path(path, @abs_dir) + }) + end + + it "calls #to_str on non-String objects" do + name = mock("load_fixture.rb mock") + name.should_receive(:to_str).and_return(@path) + require_relative(name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "raises a TypeError if argument does not respond to #to_str" do + lambda { require_relative(nil) }.should raise_error(TypeError) + lambda { require_relative(42) }.should raise_error(TypeError) + lambda { + require_relative([@path,@path]) + }.should raise_error(TypeError) + end + + it "raises a TypeError if passed an object that has #to_s but not #to_str" do + name = mock("load_fixture.rb mock") + name.stub!(:to_s).and_return(@path) + lambda { require_relative(name) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_str does not return a String" do + name = mock("#to_str returns nil") + name.should_receive(:to_str).at_least(1).times.and_return(nil) + lambda { require_relative(name) }.should raise_error(TypeError) + end + + it "calls #to_path on non-String objects" do + name = mock("load_fixture.rb mock") + name.should_receive(:to_path).and_return(@path) + require_relative(name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "calls #to_str on non-String objects returned by #to_path" do + name = mock("load_fixture.rb mock") + to_path = mock("load_fixture_rb #to_path mock") + name.should_receive(:to_path).and_return(to_path) + to_path.should_receive(:to_str).and_return(@path) + require_relative(name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + describe "(file extensions)" do + it "loads a .rb extensioned file when passed a non-extensioned path" do + require_relative("#{@dir}/load_fixture").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a .rb extensioned file when a C-extension file of the same name is loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.bundle" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.dylib" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.so" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.dll" + require_relative(@path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "does not load a C-extension file if a .rb extensioned file is already loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.rb" + require_relative("#{@dir}/load_fixture").should be_false + ScratchPad.recorded.should == [] + end + + it "loads a .rb extensioned file when passed a non-.rb extensioned path" do + require_relative("#{@dir}/load_fixture.ext").should be_true + ScratchPad.recorded.should == [:loaded] + $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb" + end + + it "loads a .rb extensioned file when a complex-extensioned C-extension file of the same name is loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.bundle" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dylib" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.so" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dll" + require_relative("#{@dir}/load_fixture.ext").should be_true + ScratchPad.recorded.should == [:loaded] + $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb" + end + + it "does not load a C-extension file if a complex-extensioned .rb file is already loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.rb" + require_relative("#{@dir}/load_fixture.ext").should be_false + ScratchPad.recorded.should == [] + end + end + + describe "($LOADED_FEATURES)" do + it "stores an absolute path" do + require_relative(@path).should be_true + $LOADED_FEATURES.should include(@abs_path) + end + + it "does not store the path if the load fails" do + saved_loaded_features = $LOADED_FEATURES.dup + lambda { require_relative("#{@dir}/raise_fixture.rb") }.should raise_error(RuntimeError) + $LOADED_FEATURES.should == saved_loaded_features + end + + it "does not load an absolute path that is already stored" do + $LOADED_FEATURES << @abs_path + require_relative(@path).should be_false + ScratchPad.recorded.should == [] + end + + it "adds the suffix of the resolved filename" do + require_relative("#{@dir}/load_fixture").should be_true + $LOADED_FEATURES.should include("#{@abs_dir}/load_fixture.rb") + end + + it "loads a path for a file already loaded with a relative path" do + $LOAD_PATH << File.expand_path(@dir) + $LOADED_FEATURES << "load_fixture.rb" << "load_fixture" + require_relative(@path).should be_true + $LOADED_FEATURES.should include(@abs_path) + ScratchPad.recorded.should == [:loaded] + end + end +end + +describe "Kernel#require_relative with an absolute path" do + it "needs to be reviewed for spec completeness" + + before :each do + CodeLoadingSpecs.spec_setup + @dir = File.expand_path "../../fixtures/code", File.dirname(__FILE__) + @abs_dir = @dir + @path = File.join @dir, "load_fixture.rb" + @abs_path = @path + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + it "loads a path relative to the current file" do + require_relative(@path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a file defining many methods" do + require_relative("#{@dir}/methods_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "raises a LoadError if the file does not exist" do + lambda { require_relative("#{@dir}/nonexistent.rb") }.should raise_error(LoadError) + ScratchPad.recorded.should == [] + end + + it "raises a LoadError if basepath does not exist" do + lambda { eval("require_relative('#{@dir}/nonexistent.rb')") }.should raise_error(LoadError) + end + + it "stores the missing path in a LoadError object" do + path = "#{@dir}/nonexistent.rb" + + lambda { + require_relative(path) + }.should(raise_error(LoadError) { |e| + e.path.should == File.expand_path(path, @abs_dir) + }) + end + + it "calls #to_str on non-String objects" do + name = mock("load_fixture.rb mock") + name.should_receive(:to_str).and_return(@path) + require_relative(name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "raises a TypeError if argument does not respond to #to_str" do + lambda { require_relative(nil) }.should raise_error(TypeError) + lambda { require_relative(42) }.should raise_error(TypeError) + lambda { + require_relative([@path,@path]) + }.should raise_error(TypeError) + end + + it "raises a TypeError if passed an object that has #to_s but not #to_str" do + name = mock("load_fixture.rb mock") + name.stub!(:to_s).and_return(@path) + lambda { require_relative(name) }.should raise_error(TypeError) + end + + it "raises a TypeError if #to_str does not return a String" do + name = mock("#to_str returns nil") + name.should_receive(:to_str).at_least(1).times.and_return(nil) + lambda { require_relative(name) }.should raise_error(TypeError) + end + + it "calls #to_path on non-String objects" do + name = mock("load_fixture.rb mock") + name.should_receive(:to_path).and_return(@path) + require_relative(name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "calls #to_str on non-String objects returned by #to_path" do + name = mock("load_fixture.rb mock") + to_path = mock("load_fixture_rb #to_path mock") + name.should_receive(:to_path).and_return(to_path) + to_path.should_receive(:to_str).and_return(@path) + require_relative(name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + describe "(file extensions)" do + it "loads a .rb extensioned file when passed a non-extensioned path" do + require_relative("#{@dir}/load_fixture").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a .rb extensioned file when a C-extension file of the same name is loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.bundle" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.dylib" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.so" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.dll" + require_relative(@path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "does not load a C-extension file if a .rb extensioned file is already loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.rb" + require_relative("#{@dir}/load_fixture").should be_false + ScratchPad.recorded.should == [] + end + + it "loads a .rb extensioned file when passed a non-.rb extensioned path" do + require_relative("#{@dir}/load_fixture.ext").should be_true + ScratchPad.recorded.should == [:loaded] + $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb" + end + + it "loads a .rb extensioned file when a complex-extensioned C-extension file of the same name is loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.bundle" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dylib" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.so" + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.dll" + require_relative("#{@dir}/load_fixture.ext").should be_true + ScratchPad.recorded.should == [:loaded] + $LOADED_FEATURES.should include "#{@abs_dir}/load_fixture.ext.rb" + end + + it "does not load a C-extension file if a complex-extensioned .rb file is already loaded" do + $LOADED_FEATURES << "#{@abs_dir}/load_fixture.ext.rb" + require_relative("#{@dir}/load_fixture.ext").should be_false + ScratchPad.recorded.should == [] + end + end + + describe "($LOAD_FEATURES)" do + it "stores an absolute path" do + require_relative(@path).should be_true + $LOADED_FEATURES.should include(@abs_path) + end + + it "does not store the path if the load fails" do + saved_loaded_features = $LOADED_FEATURES.dup + lambda { require_relative("#{@dir}/raise_fixture.rb") }.should raise_error(RuntimeError) + $LOADED_FEATURES.should == saved_loaded_features + end + + it "does not load an absolute path that is already stored" do + $LOADED_FEATURES << @abs_path + require_relative(@path).should be_false + ScratchPad.recorded.should == [] + end + + it "adds the suffix of the resolved filename" do + require_relative("#{@dir}/load_fixture").should be_true + $LOADED_FEATURES.should include("#{@abs_dir}/load_fixture.rb") + end + + it "loads a path for a file already loaded with a relative path" do + $LOAD_PATH << File.expand_path(@dir) + $LOADED_FEATURES << "load_fixture.rb" << "load_fixture" + require_relative(@path).should be_true + $LOADED_FEATURES.should include(@abs_path) + ScratchPad.recorded.should == [:loaded] + end + end +end diff --git a/spec/ruby/core/kernel/require_spec.rb b/spec/ruby/core/kernel/require_spec.rb new file mode 100644 index 0000000000..75cea7565e --- /dev/null +++ b/spec/ruby/core/kernel/require_spec.rb @@ -0,0 +1,36 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/code_loading', __FILE__) +require File.expand_path('../shared/require', __FILE__) + +describe "Kernel#require" do + before :each do + CodeLoadingSpecs.spec_setup + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + # if this fails, update your rubygems + it "is a private method" do + Kernel.should have_private_instance_method(:require) + end + + it_behaves_like :kernel_require_basic, :require, CodeLoadingSpecs::Method.new + + it_behaves_like :kernel_require, :require, CodeLoadingSpecs::Method.new +end + +describe "Kernel.require" do + before :each do + CodeLoadingSpecs.spec_setup + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + it_behaves_like :kernel_require_basic, :require, Kernel + + it_behaves_like :kernel_require, :require, Kernel +end diff --git a/spec/ruby/core/kernel/respond_to_missing_spec.rb b/spec/ruby/core/kernel/respond_to_missing_spec.rb new file mode 100644 index 0000000000..f116f19dbd --- /dev/null +++ b/spec/ruby/core/kernel/respond_to_missing_spec.rb @@ -0,0 +1,100 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#respond_to_missing?" do + before :each do + @a = KernelSpecs::A.new + end + + it "is a private method" do + Kernel.should have_private_instance_method(:respond_to_missing?, false) + end + + it "is only an instance method" do + Kernel.method(:respond_to_missing?).owner.should == Kernel + end + + it "is not called when #respond_to? would return true" do + obj = mock('object') + obj.stub!(:glark) + obj.should_not_receive(:respond_to_missing?) + obj.respond_to?(:glark).should be_true + end + + it "is called with a 2nd argument of false when #respond_to? is" do + obj = mock('object') + obj.should_receive(:respond_to_missing?).with(:undefined_method, false) + obj.respond_to?(:undefined_method, false) + end + + it "is called a 2nd argument of false when #respond_to? is called with only 1 argument" do + obj = mock('object') + obj.should_receive(:respond_to_missing?).with(:undefined_method, false) + obj.respond_to?(:undefined_method) + end + + it "is called with true as the second argument when #respond_to? is" do + obj = mock('object') + obj.should_receive(:respond_to_missing?).with(:undefined_method, true) + obj.respond_to?(:undefined_method, true) + end + + it "is called when #respond_to? would return false" do + obj = mock('object') + obj.should_receive(:respond_to_missing?).with(:undefined_method, false) + obj.respond_to?(:undefined_method) + end + + it "causes #respond_to? to return true if called and not returning false" do + obj = mock('object') + obj.should_receive(:respond_to_missing?).with(:undefined_method, false).and_return(:glark) + obj.respond_to?(:undefined_method).should be_true + end + + it "causes #respond_to? to return false if called and returning false" do + obj = mock('object') + obj.should_receive(:respond_to_missing?).with(:undefined_method, false).and_return(false) + obj.respond_to?(:undefined_method).should be_false + end + + it "causes #respond_to? to return false if called and returning nil" do + obj = mock('object') + obj.should_receive(:respond_to_missing?).with(:undefined_method, false).and_return(nil) + obj.respond_to?(:undefined_method).should be_false + end + + it "isn't called when obj responds to the given public method" do + @a.should_not_receive(:respond_to_missing?) + @a.respond_to?(:pub_method).should be_true + end + + it "isn't called when obj responds to the given public method, include_private = true" do + @a.should_not_receive(:respond_to_missing?) + @a.respond_to?(:pub_method, true).should be_true + end + + it "is called when obj responds to the given protected method, include_private = false" do + @a.should_receive(:respond_to_missing?) + @a.respond_to?(:protected_method, false).should be_false + end + + it "isn't called when obj responds to the given protected method, include_private = true" do + @a.should_not_receive(:respond_to_missing?) + @a.respond_to?(:protected_method, true).should be_true + end + + it "is called when obj responds to the given private method, include_private = false" do + @a.should_receive(:respond_to_missing?).with(:private_method, false) + @a.respond_to?(:private_method) + end + + it "isn't called when obj responds to the given private method, include_private = true" do + @a.should_not_receive(:respond_to_missing?) + @a.respond_to?(:private_method, true).should be_true + end + + it "is called for missing class methods" do + @a.class.should_receive(:respond_to_missing?).with(:oOoOoO, false) + @a.class.respond_to?(:oOoOoO) + end +end diff --git a/spec/ruby/core/kernel/respond_to_spec.rb b/spec/ruby/core/kernel/respond_to_spec.rb new file mode 100644 index 0000000000..aa4379277b --- /dev/null +++ b/spec/ruby/core/kernel/respond_to_spec.rb @@ -0,0 +1,73 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#respond_to?" do + before :each do + @a = KernelSpecs::A.new + end + + it "is a public method" do + Kernel.should have_public_instance_method(:respond_to?, false) + end + + it "is only an instance method" do + Kernel.method(:respond_to?).owner.should == Kernel + end + + it "returns false if the given method was undefined" do + @a.respond_to?(:undefed_method).should == false + @a.respond_to?("undefed_method").should == false + end + + it "returns true if obj responds to the given public method" do + @a.respond_to?(:pub_method).should == true + @a.respond_to?("pub_method").should == true + end + + it "throws a type error if argument can't be coerced into a Symbol" do + lambda { @a.respond_to?(Object.new) }.should raise_error(TypeError) + end + + it "returns false if obj responds to the given protected method" do + @a.respond_to?(:protected_method).should == false + @a.respond_to?("protected_method").should == false + end + + it "returns false if obj responds to the given private method" do + @a.respond_to?(:private_method).should == false + @a.respond_to?("private_method").should == false + end + + it "returns true if obj responds to the given protected method (include_private = true)" do + @a.respond_to?(:protected_method, true).should == true + @a.respond_to?("protected_method", true).should == true + end + + it "returns false if obj responds to the given protected method (include_private = false)" do + @a.respond_to?(:protected_method, false).should == false + @a.respond_to?("protected_method", false).should == false + end + + it "returns false even if obj responds to the given private method (include_private = false)" do + @a.respond_to?(:private_method, false).should == false + @a.respond_to?("private_method", false).should == false + end + + it "returns true if obj responds to the given private method (include_private = true)" do + @a.respond_to?(:private_method, true).should == true + @a.respond_to?("private_method", true).should == true + end + + it "does not change method visibility when finding private method" do + KernelSpecs::VisibilityChange.respond_to?(:new, false).should == false + KernelSpecs::VisibilityChange.respond_to?(:new, true).should == true + lambda { KernelSpecs::VisibilityChange.new }.should raise_error(NoMethodError) + end + + it "indicates if an object responds to a particular message" do + class KernelSpecs::Foo; def bar; 'done'; end; end + KernelSpecs::Foo.new.respond_to?(:bar).should == true + KernelSpecs::Foo.new.respond_to?(:invalid_and_silly_method_name).should == false + end + +end diff --git a/spec/ruby/core/kernel/select_spec.rb b/spec/ruby/core/kernel/select_spec.rb new file mode 100644 index 0000000000..c37c621ae7 --- /dev/null +++ b/spec/ruby/core/kernel/select_spec.rb @@ -0,0 +1,20 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#select" do + it "is a private method" do + Kernel.should have_private_instance_method(:select) + end +end + +describe "Kernel.select" do + it "needs to be reviewed for spec completeness" + + it 'does not block when timeout is 0' do + IO.pipe do |read, write| + IO.select([read], [], [], 0).should == nil + write.write 'data' + IO.select([read], [], [], 0).should == [[read], [], []] + end + end +end diff --git a/spec/ruby/core/kernel/send_spec.rb b/spec/ruby/core/kernel/send_spec.rb new file mode 100644 index 0000000000..8afd16e97c --- /dev/null +++ b/spec/ruby/core/kernel/send_spec.rb @@ -0,0 +1,68 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) +require File.expand_path('../../../shared/basicobject/send', __FILE__) + +describe "Kernel#send" do + it "invokes the named public method" do + class KernelSpecs::Foo + def bar + 'done' + end + end + KernelSpecs::Foo.new.send(:bar).should == 'done' + end + + it "invokes the named alias of a public method" do + class KernelSpecs::Foo + def bar + 'done' + end + alias :aka :bar + end + KernelSpecs::Foo.new.send(:aka).should == 'done' + end + + it "invokes the named protected method" do + class KernelSpecs::Foo + protected + def bar + 'done' + end + end + KernelSpecs::Foo.new.send(:bar).should == 'done' + end + + it "invokes the named private method" do + class KernelSpecs::Foo + private + def bar + 'done2' + end + end + KernelSpecs::Foo.new.send(:bar).should == 'done2' + end + + it "invokes the named alias of a private method" do + class KernelSpecs::Foo + private + def bar + 'done2' + end + alias :aka :bar + end + KernelSpecs::Foo.new.send(:aka).should == 'done2' + end + + it "invokes the named alias of a protected method" do + class KernelSpecs::Foo + protected + def bar + 'done2' + end + alias :aka :bar + end + KernelSpecs::Foo.new.send(:aka).should == 'done2' + end + + it_behaves_like(:basicobject_send, :send) +end diff --git a/spec/ruby/core/kernel/set_trace_func_spec.rb b/spec/ruby/core/kernel/set_trace_func_spec.rb new file mode 100644 index 0000000000..5dafa8b5d3 --- /dev/null +++ b/spec/ruby/core/kernel/set_trace_func_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#set_trace_func" do + it "is a private method" do + Kernel.should have_private_instance_method(:set_trace_func) + end +end + +describe "Kernel.set_trace_func" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/shared/dup_clone.rb b/spec/ruby/core/kernel/shared/dup_clone.rb new file mode 100644 index 0000000000..320da8fc38 --- /dev/null +++ b/spec/ruby/core/kernel/shared/dup_clone.rb @@ -0,0 +1,125 @@ +class ObjectSpecDup + def initialize() + @obj = :original + end + + attr_accessor :obj +end + +class ObjectSpecDupInitCopy + def initialize() + @obj = :original + end + + attr_accessor :obj, :original + + def initialize_copy(original) + @obj = :init_copy + @original = original + end + + private :initialize_copy +end + +describe :kernel_dup_clone, shared: true do + it "returns a new object duplicated from the original" do + o = ObjectSpecDup.new + o2 = ObjectSpecDup.new + + o.obj = 10 + + o3 = o.send(@method) + + o3.obj.should == 10 + o2.obj.should == :original + end + + it "produces a shallow copy, contained objects are not recursively dupped" do + o = ObjectSpecDup.new + array = [1, 2] + o.obj = array + + o2 = o.send(@method) + o2.obj.should equal(o.obj) + end + + it "calls #initialize_copy on the NEW object if available, passing in original object" do + o = ObjectSpecDupInitCopy.new + o2 = o.send(@method) + + o.obj.should == :original + o2.obj.should == :init_copy + o2.original.should equal(o) + end + + it "preserves tainted state from the original" do + o = ObjectSpecDupInitCopy.new + o2 = o.send(@method) + o.taint + o3 = o.send(@method) + + o2.tainted?.should == false + o3.tainted?.should == true + end + + it "does not preserve the object_id" do + o1 = ObjectSpecDupInitCopy.new + old_object_id = o1.object_id + o2 = o1.send(@method) + o2.object_id.should_not == old_object_id + end + + it "preserves untrusted state from the original" do + o = ObjectSpecDupInitCopy.new + o2 = o.send(@method) + o.untrust + o3 = o.send(@method) + + o2.untrusted?.should == false + o3.untrusted?.should == true + end + + ruby_version_is ''...'2.4' do + it "raises a TypeError for NilClass" do + lambda { nil.send(@method) }.should raise_error(TypeError) + end + + it "raises a TypeError for TrueClass" do + lambda { true.send(@method) }.should raise_error(TypeError) + end + + it "raises a TypeError for FalseClass" do + lambda { false.send(@method) }.should raise_error(TypeError) + end + + it "raises a TypeError for Fixnum" do + lambda { 1.send(@method) }.should raise_error(TypeError) + end + + it "raises a TypeError for Symbol" do + lambda { :my_symbol.send(@method) }.should raise_error(TypeError) + end + end + + ruby_version_is '2.4' do + it "returns nil for NilClass" do + nil.send(@method).should == nil + end + + it "returns true for TrueClass" do + true.send(@method).should == true + end + + it "returns false for FalseClass" do + false.send(@method).should == false + end + + it "returns the same Integer for Integer" do + 1.send(@method).should == 1 + end + + it "returns the same Symbol for Symbol" do + :my_symbol.send(@method).should == :my_symbol + end + end +end diff --git a/spec/ruby/core/kernel/shared/kind_of.rb b/spec/ruby/core/kernel/shared/kind_of.rb new file mode 100644 index 0000000000..e99f46aa14 --- /dev/null +++ b/spec/ruby/core/kernel/shared/kind_of.rb @@ -0,0 +1,44 @@ +require File.expand_path('../../fixtures/classes', __FILE__) + +describe :kernel_kind_of, shared: true do + before :each do + @o = KernelSpecs::KindaClass.new + end + + it "returns true if given class is the object's class" do + @o.send(@method, KernelSpecs::KindaClass).should == true + end + + it "returns true if given class is an ancestor of the object's class" do + @o.send(@method, KernelSpecs::AncestorClass).should == true + @o.send(@method, String).should == true + @o.send(@method, Object).should == true + end + + it "returns false if the given class is not object's class nor an ancestor" do + @o.send(@method, Array).should == false + end + + it "returns true if given a Module that is included in object's class" do + @o.send(@method, KernelSpecs::MyModule).should == true + end + + it "returns true if given a Module that is included one of object's ancestors only" do + @o.send(@method, KernelSpecs::AncestorModule).should == true + end + + it "returns true if given a Module that object has been extended with" do + @o.send(@method, KernelSpecs::MyExtensionModule).should == true + end + + it "returns false if given a Module not included in object's class nor ancestors" do + @o.send(@method, KernelSpecs::SomeOtherModule).should == false + end + + it "raises a TypeError if given an object that is not a Class nor a Module" do + lambda { @o.send(@method, 1) }.should raise_error(TypeError) + lambda { @o.send(@method, 'KindaClass') }.should raise_error(TypeError) + lambda { @o.send(@method, :KindaClass) }.should raise_error(TypeError) + lambda { @o.send(@method, Object.new) }.should raise_error(TypeError) + end +end diff --git a/spec/ruby/core/kernel/shared/lambda.rb b/spec/ruby/core/kernel/shared/lambda.rb new file mode 100644 index 0000000000..bebb111c43 --- /dev/null +++ b/spec/ruby/core/kernel/shared/lambda.rb @@ -0,0 +1,9 @@ +describe :kernel_lambda, shared: true do + it "returns a Proc object" do + send(@method) { true }.kind_of?(Proc).should == true + end + + it "raises an ArgumentError when no block is given" do + lambda { send(@method) }.should raise_error(ArgumentError) + end +end diff --git a/spec/ruby/core/kernel/shared/load.rb b/spec/ruby/core/kernel/shared/load.rb new file mode 100644 index 0000000000..0ce7d58d2c --- /dev/null +++ b/spec/ruby/core/kernel/shared/load.rb @@ -0,0 +1,139 @@ +describe :kernel_load, shared: true do + before :each do + CodeLoadingSpecs.spec_setup + @path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR + end + + after :each do + CodeLoadingSpecs.spec_cleanup + end + + it "loads a non-extensioned file as a Ruby source file" do + path = File.expand_path "load_fixture", CODE_LOADING_DIR + @object.load(path).should be_true + ScratchPad.recorded.should == [:no_ext] + end + + it "loads a non .rb extensioned file as a Ruby source file" do + path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR + @object.load(path).should be_true + ScratchPad.recorded.should == [:no_rb_ext] + end + + it "loads from the current working directory" do + Dir.chdir CODE_LOADING_DIR do + @object.load("load_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded] + end + end + + it "loads a file that recursively requires itself" do + path = File.expand_path "recursive_require_fixture.rb", CODE_LOADING_DIR + -> { + $VERBOSE = true + @object.load(path).should be_true + }.should complain(/circular require considered harmful/) + ScratchPad.recorded.should == [:loaded, :loaded] + end + + it "loads a file that recursively loads itself" do + path = File.expand_path "recursive_load_fixture.rb", CODE_LOADING_DIR + @object.load(path).should be_true + ScratchPad.recorded.should == [:loaded, :loaded] + end + + it "loads a file each time the method is called" do + @object.load(@path).should be_true + @object.load(@path).should be_true + ScratchPad.recorded.should == [:loaded, :loaded] + end + + it "loads a file even when the name appears in $LOADED_FEATURES" do + $LOADED_FEATURES << @path + @object.load(@path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a file that has been loaded by #require" do + @object.require(@path).should be_true + @object.load(@path).should be_true + ScratchPad.recorded.should == [:loaded, :loaded] + end + + it "loads file even after $LOAD_PATH change" do + $LOAD_PATH << CODE_LOADING_DIR + @object.load("load_fixture.rb").should be_true + $LOAD_PATH.unshift CODE_LOADING_DIR + "/gem" + @object.load("load_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded, :loaded_gem] + end + + it "does not cause #require with the same path to fail" do + @object.load(@path).should be_true + @object.require(@path).should be_true + ScratchPad.recorded.should == [:loaded, :loaded] + end + + it "does not add the loaded path to $LOADED_FEATURES" do + saved_loaded_features = $LOADED_FEATURES.dup + @object.load(@path).should be_true + $LOADED_FEATURES.should == saved_loaded_features + end + + it "raises a LoadError if passed a non-extensioned path that does not exist but a .rb extensioned path does exist" do + path = File.expand_path "load_ext_fixture", CODE_LOADING_DIR + lambda { @object.load(path) }.should raise_error(LoadError) + end + + describe "when passed true for 'wrap'" do + it "loads from an existing path" do + path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR + @object.load(path, true).should be_true + end + + it "sets the enclosing scope to an anonymous module" do + path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR + @object.load(path, true) + + Object.const_defined?(:LoadSpecWrap).should be_false + end + + it "allows referencing outside namespaces" do + path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR + @object.load(path, true) + + ScratchPad.recorded.first.should be_an_instance_of(Class) + end + + describe "with top-level methods" do + before :each do + path = File.expand_path "load_wrap_method_fixture.rb", CODE_LOADING_DIR + @object.load(path, true) + end + + it "allows calling top-level methods" do + ScratchPad.recorded.last.should == :load_wrap_loaded + end + + it "does not pollute the receiver" do + lambda { @object.send(:top_level_method) }.should raise_error(NameError) + end + end + end + + describe "(shell expansion)" do + before :each do + @env_home = ENV["HOME"] + ENV["HOME"] = CODE_LOADING_DIR + end + + after :each do + ENV["HOME"] = @env_home + end + + it "expands a tilde to the HOME environment variable as the path to load" do + @object.require("~/load_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded] + end + end +end diff --git a/spec/ruby/core/kernel/shared/method.rb b/spec/ruby/core/kernel/shared/method.rb new file mode 100644 index 0000000000..1566c6ab09 --- /dev/null +++ b/spec/ruby/core/kernel/shared/method.rb @@ -0,0 +1,50 @@ +require File.expand_path('../../../../spec_helper', __FILE__) + +describe :kernel_method, shared: true do + it "returns a method object for a valid method" do + class KernelSpecs::Foo; def bar; 'done'; end; end + m = KernelSpecs::Foo.new.send(@method, :bar) + m.should be_an_instance_of Method + m.call.should == 'done' + end + + it "returns a method object for a valid singleton method" do + class KernelSpecs::Foo; def self.bar; 'class done'; end; end + m = KernelSpecs::Foo.send(@method, :bar) + m.should be_an_instance_of Method + m.call.should == 'class done' + end + + it "returns a method object if we repond_to_missing? method" do + m = KernelSpecs::RespondViaMissing.new.send(@method, :handled_publicly) + m.should be_an_instance_of Method + m.call(42).should == "Done handled_publicly([42])" + end + + it "raises a NameError for an invalid method name" do + class KernelSpecs::Foo; def bar; 'done'; end; end + lambda { + KernelSpecs::Foo.new.send(@method, :invalid_and_silly_method_name) + }.should raise_error(NameError) + end + + it "raises a NameError for an invalid singleton method name" do + class KernelSpecs::Foo; def self.bar; 'done'; end; end + lambda { KernelSpecs::Foo.send(@method, :baz) }.should raise_error(NameError) + end + + it "changes the method called for super on a target aliased method" do + c1 = Class.new do + def a; 'a'; end + def b; 'b'; end + end + c2 = Class.new(c1) do + def a; super; end + alias b a + end + + c2.new.a.should == 'a' + c2.new.b.should == 'a' + c2.new.send(@method, :b).call.should == 'a' + end +end diff --git a/spec/ruby/core/kernel/shared/require.rb b/spec/ruby/core/kernel/shared/require.rb new file mode 100644 index 0000000000..3296c7f42a --- /dev/null +++ b/spec/ruby/core/kernel/shared/require.rb @@ -0,0 +1,703 @@ +describe :kernel_require_basic, shared: true do + describe "(path resolution)" do + it "loads an absolute path" do + path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR + @object.send(@method, path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a non-canonical absolute path" do + path = File.join CODE_LOADING_DIR, "..", "code", "load_fixture.rb" + @object.send(@method, path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a file defining many methods" do + path = File.expand_path "methods_fixture.rb", CODE_LOADING_DIR + @object.send(@method, path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "raises a LoadError if the file does not exist" do + path = File.expand_path "nonexistent.rb", CODE_LOADING_DIR + File.exist?(path).should be_false + lambda { @object.send(@method, path) }.should raise_error(LoadError) + ScratchPad.recorded.should == [] + end + + # Can't make a file unreadable on these platforms + platform_is_not :windows, :cygwin do + describe "with an unreadable file" do + before :each do + @path = tmp("unreadable_file.rb") + touch @path + File.chmod 0000, @path + end + + after :each do + File.chmod 0666, @path + rm_r @path + end + + it "raises a LoadError" do + File.exist?(@path).should be_true + lambda { @object.send(@method, @path) }.should raise_error(LoadError) + end + end + end + + it "calls #to_str on non-String objects" do + path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR + name = mock("load_fixture.rb mock") + name.should_receive(:to_str).and_return(path) + @object.send(@method, name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "raises a TypeError if passed nil" do + lambda { @object.send(@method, nil) }.should raise_error(TypeError) + end + + it "raises a TypeError if passed a Fixnum" do + lambda { @object.send(@method, 42) }.should raise_error(TypeError) + end + + it "raises a TypeError if passed an Array" do + lambda { @object.send(@method, []) }.should raise_error(TypeError) + end + + it "raises a TypeError if passed an object that does not provide #to_str" do + lambda { @object.send(@method, mock("not a filename")) }.should raise_error(TypeError) + end + + it "raises a TypeError if passed an object that has #to_s but not #to_str" do + name = mock("load_fixture.rb mock") + name.stub!(:to_s).and_return("load_fixture.rb") + $LOAD_PATH << "." + Dir.chdir CODE_LOADING_DIR do + lambda { @object.send(@method, name) }.should raise_error(TypeError) + end + end + + it "raises a TypeError if #to_str does not return a String" do + name = mock("#to_str returns nil") + name.should_receive(:to_str).at_least(1).times.and_return(nil) + lambda { @object.send(@method, name) }.should raise_error(TypeError) + end + + it "calls #to_path on non-String objects" do + name = mock("load_fixture.rb mock") + name.stub!(:to_path).and_return("load_fixture.rb") + $LOAD_PATH << "." + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, name).should be_true + end + ScratchPad.recorded.should == [:loaded] + end + + it "calls #to_path on a String" do + path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR + str = mock("load_fixture.rb mock") + str.should_receive(:to_path).and_return(path) + @object.send(@method, str).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "calls #to_str on non-String objects returned by #to_path" do + path = File.expand_path "load_fixture.rb", CODE_LOADING_DIR + name = mock("load_fixture.rb mock") + to_path = mock("load_fixture_rb #to_path mock") + name.should_receive(:to_path).and_return(to_path) + to_path.should_receive(:to_str).and_return(path) + @object.send(@method, name).should be_true + ScratchPad.recorded.should == [:loaded] + end + + # "http://redmine.ruby-lang.org/issues/show/2578" + it "loads a ./ relative path from the current working directory with empty $LOAD_PATH" do + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, "./load_fixture.rb").should be_true + end + ScratchPad.recorded.should == [:loaded] + end + + it "loads a ../ relative path from the current working directory with empty $LOAD_PATH" do + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, "../code/load_fixture.rb").should be_true + end + ScratchPad.recorded.should == [:loaded] + end + + it "loads a ./ relative path from the current working directory with non-empty $LOAD_PATH" do + $LOAD_PATH << "an_irrelevant_dir" + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, "./load_fixture.rb").should be_true + end + ScratchPad.recorded.should == [:loaded] + end + + it "loads a ../ relative path from the current working directory with non-empty $LOAD_PATH" do + $LOAD_PATH << "an_irrelevant_dir" + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, "../code/load_fixture.rb").should be_true + end + ScratchPad.recorded.should == [:loaded] + end + + it "loads a non-canonical path from the current working directory with non-empty $LOAD_PATH" do + $LOAD_PATH << "an_irrelevant_dir" + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, "../code/../code/load_fixture.rb").should be_true + end + ScratchPad.recorded.should == [:loaded] + end + + it "resolves a filename against $LOAD_PATH entries" do + $LOAD_PATH << CODE_LOADING_DIR + @object.send(@method, "load_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "does not require file twice after $LOAD_PATH change" do + $LOAD_PATH << CODE_LOADING_DIR + @object.require("load_fixture.rb").should be_true + $LOAD_PATH.unshift CODE_LOADING_DIR + "/gem" + @object.require("load_fixture.rb").should be_false + ScratchPad.recorded.should == [:loaded] + end + + it "does not resolve a ./ relative path against $LOAD_PATH entries" do + $LOAD_PATH << CODE_LOADING_DIR + lambda do + @object.send(@method, "./load_fixture.rb") + end.should raise_error(LoadError) + ScratchPad.recorded.should == [] + end + + it "does not resolve a ../ relative path against $LOAD_PATH entries" do + $LOAD_PATH << CODE_LOADING_DIR + lambda do + @object.send(@method, "../code/load_fixture.rb") + end.should raise_error(LoadError) + ScratchPad.recorded.should == [] + end + + it "resolves a non-canonical path against $LOAD_PATH entries" do + $LOAD_PATH << File.dirname(CODE_LOADING_DIR) + @object.send(@method, "code/../code/load_fixture.rb").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a path with duplicate path separators" do + $LOAD_PATH << "." + sep = File::Separator + File::Separator + path = ["..", "code", "load_fixture.rb"].join(sep) + Dir.chdir CODE_LOADING_DIR do + @object.send(@method, path).should be_true + end + ScratchPad.recorded.should == [:loaded] + end + end +end + +describe :kernel_require, shared: true do + describe "(path resolution)" do + # For reference see [ruby-core:24155] in which matz confirms this feature is + # intentional for security reasons. + it "does not load a bare filename unless the current working directory is in $LOAD_PATH" do + Dir.chdir CODE_LOADING_DIR do + lambda { @object.require("load_fixture.rb") }.should raise_error(LoadError) + ScratchPad.recorded.should == [] + end + end + + it "does not load a relative path unless the current working directory is in $LOAD_PATH" do + Dir.chdir File.dirname(CODE_LOADING_DIR) do + lambda do + @object.require("code/load_fixture.rb") + end.should raise_error(LoadError) + ScratchPad.recorded.should == [] + end + end + + it "loads a file that recursively requires itself" do + path = File.expand_path "recursive_require_fixture.rb", CODE_LOADING_DIR + -> { + $VERBOSE = true + @object.require(path).should be_true + }.should complain(/circular require considered harmful/) + ScratchPad.recorded.should == [:loaded] + end + end + + describe "(non-extensioned path)" do + before :each do + a = File.expand_path "a", CODE_LOADING_DIR + b = File.expand_path "b", CODE_LOADING_DIR + $LOAD_PATH.replace [a, b] + end + + it "loads a .rb extensioned file when a C-extension file exists on an earlier load path" do + @object.require("load_fixture").should be_true + ScratchPad.recorded.should == [:loaded] + end + end + + describe "(file extensions)" do + it "loads a .rb extensioned file when passed a non-extensioned path" do + path = File.expand_path "load_fixture", CODE_LOADING_DIR + File.exist?(path).should be_true + @object.require(path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a .rb extensioned file when a C-extension file of the same name is loaded" do + $LOADED_FEATURES << File.expand_path("load_fixture.bundle", CODE_LOADING_DIR) + $LOADED_FEATURES << File.expand_path("load_fixture.dylib", CODE_LOADING_DIR) + $LOADED_FEATURES << File.expand_path("load_fixture.so", CODE_LOADING_DIR) + $LOADED_FEATURES << File.expand_path("load_fixture.dll", CODE_LOADING_DIR) + path = File.expand_path "load_fixture", CODE_LOADING_DIR + @object.require(path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "does not load a C-extension file if a .rb extensioned file is already loaded" do + $LOADED_FEATURES << File.expand_path("load_fixture.rb", CODE_LOADING_DIR) + path = File.expand_path "load_fixture", CODE_LOADING_DIR + @object.require(path).should be_false + ScratchPad.recorded.should == [] + end + + it "loads a .rb extensioned file when passed a non-.rb extensioned path" do + path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR + File.exist?(path).should be_true + @object.require(path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a .rb extensioned file when a complex-extensioned C-extension file of the same name is loaded" do + $LOADED_FEATURES << File.expand_path("load_fixture.ext.bundle", CODE_LOADING_DIR) + $LOADED_FEATURES << File.expand_path("load_fixture.ext.dylib", CODE_LOADING_DIR) + $LOADED_FEATURES << File.expand_path("load_fixture.ext.so", CODE_LOADING_DIR) + $LOADED_FEATURES << File.expand_path("load_fixture.ext.dll", CODE_LOADING_DIR) + path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR + @object.require(path).should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "does not load a C-extension file if a complex-extensioned .rb file is already loaded" do + $LOADED_FEATURES << File.expand_path("load_fixture.ext.rb", CODE_LOADING_DIR) + path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR + @object.require(path).should be_false + ScratchPad.recorded.should == [] + end + end + + describe "($LOADED_FEATURES)" do + before :each do + @path = File.expand_path("load_fixture.rb", CODE_LOADING_DIR) + end + + it "stores an absolute path" do + @object.require(@path).should be_true + $LOADED_FEATURES.should include(@path) + end + + it "does not store the path if the load fails" do + $LOAD_PATH << CODE_LOADING_DIR + saved_loaded_features = $LOADED_FEATURES.dup + lambda { @object.require("raise_fixture.rb") }.should raise_error(RuntimeError) + $LOADED_FEATURES.should == saved_loaded_features + end + + it "does not load an absolute path that is already stored" do + $LOADED_FEATURES << @path + @object.require(@path).should be_false + ScratchPad.recorded.should == [] + end + + it "does not load a ./ relative path that is already stored" do + $LOADED_FEATURES << "./load_fixture.rb" + Dir.chdir CODE_LOADING_DIR do + @object.require("./load_fixture.rb").should be_false + end + ScratchPad.recorded.should == [] + end + + it "does not load a ../ relative path that is already stored" do + $LOADED_FEATURES << "../load_fixture.rb" + Dir.chdir CODE_LOADING_DIR do + @object.require("../load_fixture.rb").should be_false + end + ScratchPad.recorded.should == [] + end + + it "does not load a non-canonical path that is already stored" do + $LOADED_FEATURES << "code/../code/load_fixture.rb" + $LOAD_PATH << File.dirname(CODE_LOADING_DIR) + @object.require("code/../code/load_fixture.rb").should be_false + ScratchPad.recorded.should == [] + end + + it "respects being replaced with a new array" do + prev = $LOADED_FEATURES.dup + + @object.require(@path).should be_true + $LOADED_FEATURES.should include(@path) + + $LOADED_FEATURES.replace(prev) + + $LOADED_FEATURES.should_not include(@path) + @object.require(@path).should be_true + $LOADED_FEATURES.should include(@path) + end + + it "does not load twice the same file with and without extension" do + $LOAD_PATH << CODE_LOADING_DIR + @object.require("load_fixture.rb").should be_true + @object.require("load_fixture").should be_false + end + + describe "when a non-extensioned file is in $LOADED_FEATURES" do + before :each do + $LOADED_FEATURES << "load_fixture" + end + + it "loads a .rb extensioned file when a non extensioned file is in $LOADED_FEATURES" do + $LOAD_PATH << CODE_LOADING_DIR + @object.require("load_fixture").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "loads a .rb extensioned file from a subdirectory" do + $LOAD_PATH << File.dirname(CODE_LOADING_DIR) + @object.require("code/load_fixture").should be_true + ScratchPad.recorded.should == [:loaded] + end + + it "returns false if the file is not found" do + Dir.chdir File.dirname(CODE_LOADING_DIR) do + @object.require("load_fixture").should be_false + ScratchPad.recorded.should == [] + end + end + + it "returns false when passed a path and the file is not found" do + $LOADED_FEATURES << "code/load_fixture" + Dir.chdir CODE_LOADING_DIR do + @object.require("code/load_fixture").should be_false + ScratchPad.recorded.should == [] + end + end + end + + it "stores ../ relative paths as absolute paths" do + Dir.chdir CODE_LOADING_DIR do + @object.require("../code/load_fixture.rb").should be_true + end + $LOADED_FEATURES.should include(@path) + end + + it "stores ./ relative paths as absolute paths" do + Dir.chdir CODE_LOADING_DIR do + @object.require("./load_fixture.rb").should be_true + end + $LOADED_FEATURES.should include(@path) + end + + it "collapses duplicate path separators" do + $LOAD_PATH << "." + sep = File::Separator + File::Separator + path = ["..", "code", "load_fixture.rb"].join(sep) + Dir.chdir CODE_LOADING_DIR do + @object.require(path).should be_true + end + $LOADED_FEATURES.should include(@path) + end + + it "canonicalizes non-unique absolute paths" do + path = File.join CODE_LOADING_DIR, "..", "code", "load_fixture.rb" + @object.require(path).should be_true + $LOADED_FEATURES.should include(@path) + end + + it "adds the suffix of the resolved filename" do + $LOAD_PATH << CODE_LOADING_DIR + @object.require("load_fixture").should be_true + $LOADED_FEATURES.should include(@path) + end + + it "does not load a non-canonical path for a file already loaded" do + $LOADED_FEATURES << @path + $LOAD_PATH << File.dirname(CODE_LOADING_DIR) + @object.require("code/../code/load_fixture.rb").should be_false + ScratchPad.recorded.should == [] + end + + it "does not load a ./ relative path for a file already loaded" do + $LOADED_FEATURES << @path + $LOAD_PATH << "an_irrelevant_dir" + Dir.chdir CODE_LOADING_DIR do + @object.require("./load_fixture.rb").should be_false + end + ScratchPad.recorded.should == [] + end + + it "does not load a ../ relative path for a file already loaded" do + $LOADED_FEATURES << @path + $LOAD_PATH << "an_irrelevant_dir" + Dir.chdir CODE_LOADING_DIR do + @object.require("../code/load_fixture.rb").should be_false + end + ScratchPad.recorded.should == [] + end + + ruby_version_is "2.2"..."2.3" do + it "complex, enumerator, rational and unicode_normalize are already required" do + provided = %w[complex enumerator rational unicode_normalize] + features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems') + provided.each { |feature| + features.should =~ /\b#{feature}\.(rb|so)$/ + } + + code = provided.map { |f| "puts require #{f.inspect}\n" }.join + required = ruby_exe(code, options: '--disable-gems') + required.should == "false\n" * provided.size + end + end + + ruby_version_is "2.3"..."2.5" do + it "complex, enumerator, rational, thread and unicode_normalize are already required" do + provided = %w[complex enumerator rational thread unicode_normalize] + features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems') + provided.each { |feature| + features.should =~ /\b#{feature}\.(rb|so|jar)$/ + } + + code = provided.map { |f| "puts require #{f.inspect}\n" }.join + required = ruby_exe(code, options: '--disable-gems') + required.should == "false\n" * provided.size + end + end + + ruby_version_is "2.5" do + it "complex, enumerator, rational and thread are already required" do + provided = %w[complex enumerator rational thread] + features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems') + provided.each { |feature| + features.should =~ /\b#{feature}\.(rb|so|jar)$/ + } + + code = provided.map { |f| "puts require #{f.inspect}\n" }.join + required = ruby_exe(code, options: '--disable-gems') + required.should == "false\n" * provided.size + end + end + end + + describe "(shell expansion)" do + before :each do + @path = File.expand_path("load_fixture.rb", CODE_LOADING_DIR) + @env_home = ENV["HOME"] + ENV["HOME"] = CODE_LOADING_DIR + end + + after :each do + ENV["HOME"] = @env_home + end + + # "#3171" + it "performs tilde expansion on a .rb file before storing paths in $LOADED_FEATURES" do + @object.require("~/load_fixture.rb").should be_true + $LOADED_FEATURES.should include(@path) + end + + it "performs tilde expansion on a non-extensioned file before storing paths in $LOADED_FEATURES" do + @object.require("~/load_fixture").should be_true + $LOADED_FEATURES.should include(@path) + end + end + + describe "(concurrently)" do + before :each do + ScratchPad.record [] + @path = File.expand_path "concurrent.rb", CODE_LOADING_DIR + @path2 = File.expand_path "concurrent2.rb", CODE_LOADING_DIR + @path3 = File.expand_path "concurrent3.rb", CODE_LOADING_DIR + end + + after :each do + ScratchPad.clear + $LOADED_FEATURES.delete @path + $LOADED_FEATURES.delete @path2 + $LOADED_FEATURES.delete @path3 + end + + # Quick note about these specs: + # + # The behavior we're spec'ing requires that t2 enter #require, see t1 is + # loading @path, grab a lock, and wait on it. + # + # We do make sure that t2 starts the require once t1 is in the middle + # of concurrent.rb, but we then need to get t2 to get far enough into #require + # to see t1's lock and try to lock it. + it "blocks a second thread from returning while the 1st is still requiring" do + fin = false + + t1_res = nil + t2_res = nil + + t2 = nil + t1 = Thread.new do + Thread.pass until t2 + Thread.current[:wait_for] = t2 + t1_res = @object.require(@path) + Thread.pass until fin + ScratchPad.recorded << :t1_post + end + + t2 = Thread.new do + Thread.pass until t1[:in_concurrent_rb] + $VERBOSE, @verbose = nil, $VERBOSE + begin + t2_res = @object.require(@path) + ScratchPad.recorded << :t2_post + ensure + $VERBOSE = @verbose + fin = true + end + end + + t1.join + t2.join + + t1_res.should be_true + t2_res.should be_false + + ScratchPad.recorded.should == [:con_pre, :con_post, :t2_post, :t1_post] + end + + it "blocks based on the path" do + t1_res = nil + t2_res = nil + + t2 = nil + t1 = Thread.new do + Thread.pass until t2 + Thread.current[:concurrent_require_thread] = t2 + t1_res = @object.require(@path2) + end + + t2 = Thread.new do + Thread.pass until t1[:in_concurrent_rb2] + t2_res = @object.require(@path3) + end + + t1.join + t2.join + + t1_res.should be_true + t2_res.should be_true + + ScratchPad.recorded.should == [:con2_pre, :con3, :con2_post] + end + + it "allows a 2nd require if the 1st raised an exception" do + fin = false + + t2_res = nil + + t2 = nil + t1 = Thread.new do + Thread.pass until t2 + Thread.current[:wait_for] = t2 + Thread.current[:con_raise] = true + + lambda { + @object.require(@path) + }.should raise_error(RuntimeError) + + Thread.pass until fin + ScratchPad.recorded << :t1_post + end + + t2 = Thread.new do + Thread.pass until t1[:in_concurrent_rb] + $VERBOSE, @verbose = nil, $VERBOSE + begin + t2_res = @object.require(@path) + ScratchPad.recorded << :t2_post + ensure + $VERBOSE = @verbose + fin = true + end + end + + t1.join + t2.join + + t2_res.should be_true + + ScratchPad.recorded.should == [:con_pre, :con_pre, :con_post, :t2_post, :t1_post] + end + + # "redmine #5754" + it "blocks a 3rd require if the 1st raises an exception and the 2nd is still running" do + fin = false + + t1_res = nil + t2_res = nil + + raised = false + + t2 = nil + t1 = Thread.new do + Thread.current[:con_raise] = true + + lambda { + @object.require(@path) + }.should raise_error(RuntimeError) + + raised = true + + # This hits the bug. Because MRI removes its internal lock from a table + # when the exception is raised, this #require doesn't see that t2 is in + # the middle of requiring the file, so this #require runs when it should not. + Thread.pass until t2 && t2[:in_concurrent_rb] + t1_res = @object.require(@path) + + Thread.pass until fin + ScratchPad.recorded << :t1_post + end + + t2 = Thread.new do + Thread.pass until raised + Thread.current[:wait_for] = t1 + begin + t2_res = @object.require(@path) + ScratchPad.recorded << :t2_post + ensure + fin = true + end + end + + t1.join + t2.join + + t1_res.should be_false + t2_res.should be_true + + ScratchPad.recorded.should == [:con_pre, :con_pre, :con_post, :t2_post, :t1_post] + end + end + + it "stores the missing path in a LoadError object" do + path = "abcd1234" + + lambda { + @object.send(@method, path) + }.should raise_error(LoadError) { |e| + e.path.should == path + } + end +end diff --git a/spec/ruby/core/kernel/singleton_class_spec.rb b/spec/ruby/core/kernel/singleton_class_spec.rb new file mode 100644 index 0000000000..b5e0703905 --- /dev/null +++ b/spec/ruby/core/kernel/singleton_class_spec.rb @@ -0,0 +1,27 @@ +describe "Kernel#singleton_class" do + it "returns class extended from an object" do + x = Object.new + xs = class << x; self; end + xs.should == x.singleton_class + end + + it "returns NilClass for nil" do + nil.singleton_class.should == NilClass + end + + it "returns TrueClass for true" do + true.singleton_class.should == TrueClass + end + + it "returns FalseClass for false" do + false.singleton_class.should == FalseClass + end + + it "raises TypeError for Fixnum" do + lambda { 123.singleton_class }.should raise_error(TypeError) + end + + it "raises TypeError for Symbol" do + lambda { :foo.singleton_class }.should raise_error(TypeError) + end +end diff --git a/spec/ruby/core/kernel/singleton_method_spec.rb b/spec/ruby/core/kernel/singleton_method_spec.rb new file mode 100644 index 0000000000..77022b40c2 --- /dev/null +++ b/spec/ruby/core/kernel/singleton_method_spec.rb @@ -0,0 +1,41 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Kernel#singleton_method" do + it "find a method defined on the singleton class" do + obj = Object.new + def obj.foo; end + obj.singleton_method(:foo).should be_an_instance_of(Method) + end + + it "returns a Method which can be called" do + obj = Object.new + def obj.foo; 42; end + obj.singleton_method(:foo).call.should == 42 + end + + it "only looks at singleton methods and not at methods in the class" do + klass = Class.new do + def foo + 42 + end + end + obj = klass.new + obj.foo.should == 42 + -> { + obj.singleton_method(:foo) + }.should raise_error(NameError) { |e| + # a NameError and not a NoMethodError + e.class.should == NameError + } + end + + it "raises a NameError if there is no such method" do + obj = Object.new + -> { + obj.singleton_method(:not_existing) + }.should raise_error(NameError) { |e| + # a NameError and not a NoMethodError + e.class.should == NameError + } + end +end diff --git a/spec/ruby/core/kernel/singleton_methods_spec.rb b/spec/ruby/core/kernel/singleton_methods_spec.rb new file mode 100644 index 0000000000..596e5ddad2 --- /dev/null +++ b/spec/ruby/core/kernel/singleton_methods_spec.rb @@ -0,0 +1,180 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../../../fixtures/reflection', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_singleton_methods, shared: true do + it "returns an empty Array for an object with no singleton methods" do + ReflectSpecs.o.singleton_methods(*@object).should == [] + end + + it "returns the names of module methods for a module" do + ReflectSpecs::M.singleton_methods(*@object).should include(:ms_pro, :ms_pub) + end + + it "does not return private module methods for a module" do + ReflectSpecs::M.singleton_methods(*@object).should_not include(:ms_pri) + end + + it "returns the names of class methods for a class" do + ReflectSpecs::A.singleton_methods(*@object).should include(:as_pro, :as_pub) + end + + it "does not return private class methods for a class" do + ReflectSpecs::A.singleton_methods(*@object).should_not include(:as_pri) + end + + it "returns the names of singleton methods for an object" do + ReflectSpecs.os.singleton_methods(*@object).should include(:os_pro, :os_pub) + end +end + +describe :kernel_singleton_methods_modules, shared: true do + it "does not return any included methods for a module including a module" do + ReflectSpecs::N.singleton_methods(*@object).should include(:ns_pro, :ns_pub) + end + + it "does not return any included methods for a class including a module" do + ReflectSpecs::D.singleton_methods(*@object).should include(:ds_pro, :ds_pub) + end +end + +describe :kernel_singleton_methods_supers, shared: true do + it "returns the names of singleton methods for an object extented with a module" do + ReflectSpecs.oe.singleton_methods(*@object).should include(:m_pro, :m_pub) + end + + it "returns a unique list for an object extended with a module" do + m = ReflectSpecs.oed.singleton_methods(*@object) + r = m.select { |x| x == :pub or x == :pro }.sort + r.should == [:pro, :pub] + end + + it "returns the names of singleton methods for an object extented with two modules" do + ReflectSpecs.oee.singleton_methods(*@object).should include(:m_pro, :m_pub, :n_pro, :n_pub) + end + + it "returns the names of singleton methods for an object extented with a module including a module" do + ReflectSpecs.oei.singleton_methods(*@object).should include(:n_pro, :n_pub, :m_pro, :m_pub) + end + + it "returns the names of inherited singleton methods for a subclass" do + ReflectSpecs::B.singleton_methods(*@object).should include(:as_pro, :as_pub, :bs_pro, :bs_pub) + end + + it "returns a unique list for a subclass" do + m = ReflectSpecs::B.singleton_methods(*@object) + r = m.select { |x| x == :pub or x == :pro }.sort + r.should == [:pro, :pub] + end + + it "returns the names of inherited singleton methods for a subclass including a module" do + ReflectSpecs::C.singleton_methods(*@object).should include(:as_pro, :as_pub, :cs_pro, :cs_pub) + end + + it "returns a unique list for a subclass including a module" do + m = ReflectSpecs::C.singleton_methods(*@object) + r = m.select { |x| x == :pub or x == :pro }.sort + r.should == [:pro, :pub] + end + + it "returns the names of inherited singleton methods for a subclass of a class including a module" do + ReflectSpecs::E.singleton_methods(*@object).should include(:ds_pro, :ds_pub, :es_pro, :es_pub) + end + + it "returns the names of inherited singleton methods for a subclass of a class that includes a module, where the subclass also includes a module" do + ReflectSpecs::F.singleton_methods(*@object).should include(:ds_pro, :ds_pub, :fs_pro, :fs_pub) + end + + it "returns the names of inherited singleton methods for a class extended with a module" do + ReflectSpecs::P.singleton_methods(*@object).should include(:m_pro, :m_pub) + end +end + +describe :kernel_singleton_methods_private_supers, shared: true do + it "does not return private singleton methods for an object extended with a module" do + ReflectSpecs.oe.singleton_methods(*@object).should_not include(:m_pri) + end + + it "does not return private singleton methods for an object extended with two modules" do + ReflectSpecs.oee.singleton_methods(*@object).should_not include(:m_pri) + end + + it "does not return private singleton methods for an object extented with a module including a module" do + ReflectSpecs.oei.singleton_methods(*@object).should_not include(:n_pri, :m_pri) + end + + it "does not return private singleton methods for a class extended with a module" do + ReflectSpecs::P.singleton_methods(*@object).should_not include(:m_pri) + end + + it "does not return private inherited singleton methods for a module including a module" do + ReflectSpecs::N.singleton_methods(*@object).should_not include(:ns_pri) + end + + it "does not return private inherited singleton methods for a class including a module" do + ReflectSpecs::D.singleton_methods(*@object).should_not include(:ds_pri) + end + + it "does not return private inherited singleton methods for a subclass" do + ReflectSpecs::B.singleton_methods(*@object).should_not include(:as_pri, :bs_pri) + end + + it "does not return private inherited singleton methods for a subclass including a module" do + ReflectSpecs::C.singleton_methods(*@object).should_not include(:as_pri, :cs_pri) + end + + it "does not return private inherited singleton methods for a subclass of a class including a module" do + ReflectSpecs::E.singleton_methods(*@object).should_not include(:ds_pri, :es_pri) + end + + it "does not return private inherited singleton methods for a subclass of a class that includes a module, where the subclass also includes a module" do + ReflectSpecs::F.singleton_methods(*@object).should_not include(:ds_pri, :fs_pri) + end +end + +describe "Kernel#singleton_methods" do + describe "when not passed an argument" do + it_behaves_like :kernel_singleton_methods, nil, [] + it_behaves_like :kernel_singleton_methods_supers, nil, [] + it_behaves_like :kernel_singleton_methods_modules, nil, [] + it_behaves_like :kernel_singleton_methods_private_supers, nil, [] + end + + describe "when passed true" do + it_behaves_like :kernel_singleton_methods, nil, true + it_behaves_like :kernel_singleton_methods_supers, nil, true + it_behaves_like :kernel_singleton_methods_modules, nil, true + it_behaves_like :kernel_singleton_methods_private_supers, nil, true + + end + + describe "when passed false" do + it_behaves_like :kernel_singleton_methods, nil, false + it_behaves_like :kernel_singleton_methods_modules, nil, false + it_behaves_like :kernel_singleton_methods_private_supers, nil, false + + it "returns an empty Array for an object extented with a module" do + ReflectSpecs.oe.singleton_methods(false).should == [] + end + + it "returns an empty Array for an object extented with two modules" do + ReflectSpecs.oee.singleton_methods(false).should == [] + end + + it "returns an empty Array for an object extended with a module including a module" do + ReflectSpecs.oei.singleton_methods(false).should == [] + end + + it "returns the names of singleton methods of the subclass" do + ReflectSpecs::B.singleton_methods(false).should include(:bs_pro, :bs_pub) + end + + it "does not return names of inherited singleton methods for a subclass" do + ReflectSpecs::B.singleton_methods(false).should_not include(:as_pro, :as_pub) + end + + it "does not return the names of inherited singleton methods for a class extended with a module" do + ReflectSpecs::P.singleton_methods(false).should_not include(:m_pro, :m_pub) + end + end +end diff --git a/spec/ruby/core/kernel/sleep_spec.rb b/spec/ruby/core/kernel/sleep_spec.rb new file mode 100644 index 0000000000..bcb0060aa3 --- /dev/null +++ b/spec/ruby/core/kernel/sleep_spec.rb @@ -0,0 +1,52 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#sleep" do + it "is a private method" do + Kernel.should have_private_instance_method(:sleep) + end + + it "accepts a Float" do + sleep(0.1).should be_close(0, 2) + end + + it "accepts a Fixnum" do + sleep(0).should be_close(0, 2) + end + + it "accepts a Rational" do + sleep(Rational(1, 9)).should be_close(0, 2) + end + + it "raises an ArgumentError when passed a negative duration" do + lambda { sleep(-0.1) }.should raise_error(ArgumentError) + lambda { sleep(-1) }.should raise_error(ArgumentError) + end + + it "raises a TypeError when passed nil" do + lambda { sleep(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + lambda { sleep('2') }.should raise_error(TypeError) + end + + it "pauses execution indefinitely if not given a duration" do + running = false + t = Thread.new do + running = true + sleep + 5 + end + + Thread.pass until running + Thread.pass while t.status and t.status != "sleep" + + t.wakeup + t.value.should == 5 + end +end + +describe "Kernel.sleep" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/spawn_spec.rb b/spec/ruby/core/kernel/spawn_spec.rb new file mode 100644 index 0000000000..ad937b17dd --- /dev/null +++ b/spec/ruby/core/kernel/spawn_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# These specs only run a basic usage of #spawn. +# Process.spawn has more complete specs and they are not +# run here as it is redundant and takes too long for little gain. +describe "Kernel#spawn" do + it "is a private method" do + Kernel.should have_private_instance_method(:spawn) + end + + it "executes the given command" do + lambda { + Process.wait spawn("echo spawn") + }.should output_to_fd("spawn\n") + end +end + +describe "Kernel.spawn" do + it "executes the given command" do + lambda { + Process.wait Kernel.spawn("echo spawn") + }.should output_to_fd("spawn\n") + end +end diff --git a/spec/ruby/core/kernel/sprintf_spec.rb b/spec/ruby/core/kernel/sprintf_spec.rb new file mode 100644 index 0000000000..984f31dc7f --- /dev/null +++ b/spec/ruby/core/kernel/sprintf_spec.rb @@ -0,0 +1,310 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#sprintf" do + it "is a private method" do + Kernel.should have_private_instance_method(:sprintf) + end + + it "treats nil arguments as zero-width strings in %s slots" do + sprintf("%s%d%s%s", nil, 4, 'a', 'b').should == '4ab' + end + + it "passes some tests for positive %x" do + sprintf("%x", 123).should == "7b" + sprintf("%0x", 123).should == "7b" + sprintf("% x", 123).should == " 7b" + sprintf("%+x", 123).should == "+7b" + sprintf("%+0x", 123).should == "+7b" + sprintf("%+ x", 123).should == "+7b" + sprintf("% 0x", 123).should == " 7b" + + sprintf("%#x", 123).should == "0x7b" + sprintf("%#0x", 123).should == "0x7b" + sprintf("%# x", 123).should == " 0x7b" + sprintf("%#+x", 123).should == "+0x7b" + sprintf("%#+0x", 123).should == "+0x7b" + sprintf("%#+ x", 123).should == "+0x7b" + sprintf("%# 0x", 123).should == " 0x7b" + + sprintf("%8x", 123).should == " 7b" + sprintf("%08x", 123).should == "0000007b" + sprintf("% 8x", 123).should == " 7b" + sprintf("%+8x", 123).should == " +7b" + sprintf("%+08x", 123).should == "+000007b" + sprintf("%+ 8x", 123).should == " +7b" + sprintf("% 08x", 123).should == " 000007b" + + sprintf("%#8x", 123).should == " 0x7b" + sprintf("%#08x", 123).should == "0x00007b" + sprintf("%# 8x", 123).should == " 0x7b" + sprintf("%#+8x", 123).should == " +0x7b" + sprintf("%#+08x", 123).should == "+0x0007b" + sprintf("%#+ 8x", 123).should == " +0x7b" + sprintf("%# 08x", 123).should == " 0x0007b" + + sprintf("%8.10x", 123).should == "000000007b" + sprintf("%08.10x", 123).should == "000000007b" + sprintf("% 8.10x", 123).should == " 000000007b" + sprintf("%+8.10x", 123).should == "+000000007b" + sprintf("%+08.10x", 123).should == "+000000007b" + sprintf("%+ 8.10x", 123).should == "+000000007b" + sprintf("% 08.10x", 123).should == " 000000007b" + + sprintf("%10.8x", 123).should == " 0000007b" + sprintf("%010.8x", 123).should == " 0000007b" + sprintf("% 10.8x", 123).should == " 0000007b" + sprintf("%+10.8x", 123).should == " +0000007b" + sprintf("%+010.8x", 123).should == " +0000007b" + sprintf("%+ 10.8x", 123).should == " +0000007b" + sprintf("% 010.8x", 123).should == " 0000007b" + end + + describe "with format string that contains %{} sections" do + it "substitutes values for named references" do + sprintf("%{foo}f", {foo: 1}).should == "1f" + end + + it "raises KeyError when no matching key is in second argument" do + lambda { sprintf("%{foo}f", {}) }.should raise_error(KeyError) + end + end + + describe "with format string that contains %<> sections" do + it "formats values for named references" do + sprintf("%<foo>f", {foo: 1}).should == "1.000000" + end + + it "raises KeyError when no matching key is in second argument" do + lambda { sprintf("%<foo>f", {}) }.should raise_error(KeyError) + end + + it "raises ArgumentError if missing second named argument" do + lambda { sprintf("%<key><foo>d", {key: 1}) }.should raise_error(ArgumentError) + end + end + + describe "with negative values" do + describe "with format %x" do + it "precedes the number with '..'" do + sprintf("%0x", -123).should == "..f85" + sprintf("%#0x", -123).should == "0x..f85" + sprintf("%08x", -123).should == "..ffff85" + sprintf("%#08x", -123).should == "0x..ff85" + sprintf("%8.10x", -123).should == "..ffffff85" + sprintf("%08.10x", -123).should == "..ffffff85" + sprintf("%10.8x", -123).should == " ..ffff85" + sprintf("%010.8x", -123).should == " ..ffff85" + end + end + + describe "with format %b or %B" do + it "precedes the number with '..'" do + sprintf("%.7b", -5).should == "..11011" + sprintf("%.7B", -5).should == "..11011" + sprintf("%0b", -5).should == "..1011" + end + end + end + + it "passes some tests for negative %x" do + sprintf("%x", -123).should == "..f85" + sprintf("% x", -123).should == "-7b" + sprintf("%+x", -123).should == "-7b" + sprintf("%+0x", -123).should == "-7b" + sprintf("%+ x", -123).should == "-7b" + sprintf("% 0x", -123).should == "-7b" + + sprintf("%#x", -123).should == "0x..f85" + sprintf("%# x", -123).should == "-0x7b" + sprintf("%#+x", -123).should == "-0x7b" + sprintf("%#+0x", -123).should == "-0x7b" + sprintf("%#+ x", -123).should == "-0x7b" + sprintf("%# 0x", -123).should == "-0x7b" + + sprintf("%8x", -123).should == " ..f85" + sprintf("% 8x", -123).should == " -7b" + sprintf("%+8x", -123).should == " -7b" + sprintf("%+08x", -123).should == "-000007b" + sprintf("%+ 8x", -123).should == " -7b" + sprintf("% 08x", -123).should == "-000007b" + + sprintf("%#8x", -123).should == " 0x..f85" + sprintf("%# 8x", -123).should == " -0x7b" + sprintf("%#+8x", -123).should == " -0x7b" + sprintf("%#+08x", -123).should == "-0x0007b" + sprintf("%#+ 8x", -123).should == " -0x7b" + sprintf("%# 08x", -123).should == "-0x0007b" + + sprintf("% 8.10x", -123).should == "-000000007b" + sprintf("%+8.10x", -123).should == "-000000007b" + sprintf("%+08.10x", -123).should == "-000000007b" + sprintf("%+ 8.10x", -123).should == "-000000007b" + sprintf("% 08.10x", -123).should == "-000000007b" + + sprintf("% 10.8x", -123).should == " -0000007b" + sprintf("%+10.8x", -123).should == " -0000007b" + sprintf("%+010.8x", -123).should == " -0000007b" + sprintf("%+ 10.8x", -123).should == " -0000007b" + sprintf("% 010.8x", -123).should == " -0000007b" + end + + it "passes some tests for negative %u" do + sprintf("%u", -123).should == "-123" + sprintf("%0u", -123).should == "-123" + sprintf("%#u", -123).should == "-123" + sprintf("%#0u", -123).should == "-123" + sprintf("%8u", -123).should == " -123" + sprintf("%08u", -123).should == "-0000123" + sprintf("%#8u", -123).should == " -123" + sprintf("%#08u", -123).should == "-0000123" + + sprintf("%30u", -123).should == " -123" + sprintf("%030u", -123).should == "-00000000000000000000000000123" + + sprintf("%#30u", -123).should == " -123" + sprintf("%#030u", -123).should == "-00000000000000000000000000123" + + sprintf("%24.30u", -123).should == "-000000000000000000000000000123" + sprintf("%024.30u", -123).should == "-000000000000000000000000000123" + + sprintf("%#24.30u", -123).should == "-000000000000000000000000000123" + sprintf("%#024.30u", -123).should == "-000000000000000000000000000123" + + + sprintf("%30.24u", -123).should == " -000000000000000000000123" + sprintf("%030.24u", -123).should == " -000000000000000000000123" + + sprintf("%#30.24u", -123).should == " -000000000000000000000123" + sprintf("%#030.24u", -123).should == " -000000000000000000000123" + end + + it "passes some tests for positive %u" do + sprintf("%30u", 123).should == " 123" + sprintf("%030u", 123).should == "000000000000000000000000000123" + + sprintf("%#30u", 123).should == " 123" + sprintf("%#030u", 123).should == "000000000000000000000000000123" + + sprintf("%24.30u", 123).should == "000000000000000000000000000123" + sprintf("%024.30u", 123).should == "000000000000000000000000000123" + + sprintf("%#24.30u", 123).should == "000000000000000000000000000123" + sprintf("%#024.30u", 123).should == "000000000000000000000000000123" + + sprintf("%30.24u", 123).should == " 000000000000000000000123" + sprintf("%030.24u", 123).should == " 000000000000000000000123" + + sprintf("%#30.24u", 123).should == " 000000000000000000000123" + sprintf("%#030.24u", 123).should == " 000000000000000000000123" + end + + it "passes some tests for positive %d" do + sprintf("%30d", 123).should == " 123" + sprintf("%030d", 123).should == "000000000000000000000000000123" + + sprintf("%#30d", 123).should == " 123" + sprintf("%#030d", 123).should == "000000000000000000000000000123" + + sprintf("%24.30d", 123).should == "000000000000000000000000000123" + sprintf("%024.30d", 123).should == "000000000000000000000000000123" + + sprintf("%#24.30d", 123).should == "000000000000000000000000000123" + sprintf("%#024.30d", 123).should == "000000000000000000000000000123" + + sprintf("%30.24d", 123).should == " 000000000000000000000123" + sprintf("%030.24d", 123).should == " 000000000000000000000123" + + sprintf("%#30.24d", 123).should == " 000000000000000000000123" + sprintf("%#030.24d", 123).should == " 000000000000000000000123" + end + + it "passes some tests for positive %f" do + sprintf("%30f", 123.1).should == " 123.100000" + sprintf("%030f", 123.1).should == "00000000000000000000123.100000" + + sprintf("%#30f", 123.1).should == " 123.100000" + sprintf("%#030f", 123.1).should == "00000000000000000000123.100000" + + sprintf("%10.4f", 123.1).should == " 123.1000" + sprintf("%010.4f", 123.1).should == "00123.1000" + + sprintf("%10.0f", 123.1).should == " 123" + sprintf("%010.0f", 123.1).should == "0000000123" + end + + it "passes some tests for negative %f" do + sprintf("%30f", -123.5).should == " -123.500000" + sprintf("%030f", -123.5).should == "-0000000000000000000123.500000" + + sprintf("%#30f", -123.5).should == " -123.500000" + sprintf("%#030f", -123.5).should == "-0000000000000000000123.500000" + + sprintf("%10.4f", -123.5).should == " -123.5000" + sprintf("%010.4f", -123.5).should == "-0123.5000" + + sprintf("%10.0f", -123.5).should == " -124" + sprintf("%010.0f", -123.5).should == "-000000124" + end + + it "passes some tests for infinite and nan" do + sprintf("%f", Float::INFINITY).should == "Inf" + sprintf("%f", -Float::INFINITY).should == "-Inf" + sprintf("%f", Float::NAN).should == "NaN" + + sprintf("%10f", Float::INFINITY).should == " Inf" + sprintf("%10f", -Float::INFINITY).should == " -Inf" + sprintf("%10f", Float::NAN).should == " NaN" + end + + it "passes kstephens's tests" do + sprintf("%*1$.*2$3$d", 10, 5, 1).should == " 00001" + sprintf("%b", 0).should == "0" + sprintf("%B", 0).should == "0" + sprintf("%b", -5).should == "..1011" + sprintf("%B", -5).should == "..1011" + sprintf("%+b", -5).should == "-101" + sprintf("%+b", 10).should == "+1010" + sprintf("%+b", 0).should == "+0" + sprintf("%+o", -5).should == "-5" + sprintf("%+o", 10).should == "+12" + sprintf("%+o", 0).should == "+0" + sprintf("%+d", -5).should == "-5" + sprintf("%+d", 10).should == "+10" + sprintf("%+d", 0).should == "+0" + sprintf("%+x", -15).should == "-f" + sprintf("%+x", 100).should == "+64" + sprintf("%+x", 0).should == "+0" + sprintf("%+X", -15).should == "-F" + sprintf("%+X", 100).should == "+64" + sprintf("%+X", 0).should == "+0" + sprintf("=%02X", 1).should == "=01" + sprintf("%+03d", 0).should == "+00" + sprintf("%+03d", 5).should == "+05" + sprintf("%+03d", -5).should == "-05" + sprintf("%+03d", 12).should == "+12" + sprintf("%+03d", -12).should == "-12" + sprintf("%+03d", 123).should == "+123" + sprintf("%+03d", -123).should == "-123" + end + + with_feature :encoding do + it "returns a String in the same encoding as the format String if compatible" do + format = "%.2f %4s".force_encoding(Encoding::KOI8_U) + result = sprintf(format, 1.2, "dogs") + result.encoding.should equal(Encoding::KOI8_U) + end + + it "returns a String in the argument encoding if format encoding is more restrictive" do + format = "foo %s".force_encoding(Encoding::US_ASCII) + arg = "b\303\274r".force_encoding(Encoding::UTF_8) + + result = sprintf(format, arg) + result.encoding.should equal(Encoding::UTF_8) + end + end +end + +describe "Kernel.sprintf" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/srand_spec.rb b/spec/ruby/core/kernel/srand_spec.rb new file mode 100644 index 0000000000..33f99f5ac4 --- /dev/null +++ b/spec/ruby/core/kernel/srand_spec.rb @@ -0,0 +1,61 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.srand" do + it "is a private method" do + Kernel.should have_private_instance_method(:srand) + end + + it "returns the previous seed value" do + srand(10) + srand(20).should == 10 + end + + it "seeds the RNG correctly and repeatably" do + srand(10) + x = rand + srand(10) + rand.should == x + end + + it "defaults number to a random value" do + lambda { srand }.should_not raise_error + srand.should_not == 0 + end + + it "accepts and uses a seed of 0" do + srand(0) + srand.should == 0 + end + + it "accepts a negative seed" do + srand(-17) + srand.should == -17 + end + + it "accepts a Bignum as a seed" do + srand(0x12345678901234567890) + srand.should == 0x12345678901234567890 + end + + it "calls #to_int on seed" do + srand(3.8) + srand.should == 3 + + s = mock('seed') + s.should_receive(:to_int).and_return 0 + srand(s) + end + + it "raises a TypeError when passed nil" do + lambda { srand(nil) }.should raise_error(TypeError) + end + + it "raises a TypeError when passed a String" do + lambda { srand("7") }.should raise_error(TypeError) + end +end + +describe "Kernel#srand" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/sub_spec.rb b/spec/ruby/core/kernel/sub_spec.rb new file mode 100644 index 0000000000..78e5eec4a8 --- /dev/null +++ b/spec/ruby/core/kernel/sub_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +# FIXME: These methods exist only when the -n or -p option is passed to +# ruby, but we currently don't have a way of specifying that. +ruby_version_is ""..."1.9" do + describe "Kernel#sub" do + it "is a private method" do + Kernel.should have_private_instance_method(:sub) + end + end + + describe "Kernel#sub!" do + it "is a private method" do + Kernel.should have_private_instance_method(:sub!) + end + end + + describe "Kernel.sub" do + it "needs to be reviewed for spec completeness" + end + + describe "Kernel.sub!" do + it "needs to be reviewed for spec completeness" + end +end diff --git a/spec/ruby/core/kernel/syscall_spec.rb b/spec/ruby/core/kernel/syscall_spec.rb new file mode 100644 index 0000000000..bcea833f1f --- /dev/null +++ b/spec/ruby/core/kernel/syscall_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#syscall" do + it "is a private method" do + Kernel.should have_private_instance_method(:syscall) + end +end + +describe "Kernel.syscall" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/system_spec.rb b/spec/ruby/core/kernel/system_spec.rb new file mode 100644 index 0000000000..aee75441a2 --- /dev/null +++ b/spec/ruby/core/kernel/system_spec.rb @@ -0,0 +1,107 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe :kernel_system, shared: true do + it "executes the specified command in a subprocess" do + lambda { @object.system("echo a") }.should output_to_fd("a\n") + + $?.should be_an_instance_of Process::Status + $?.success?.should == true + end + + it "returns true when the command exits with a zero exit status" do + @object.system(ruby_cmd('exit 0')).should == true + + $?.should be_an_instance_of Process::Status + $?.success?.should == true + $?.exitstatus.should == 0 + end + + it "returns false when the command exits with a non-zero exit status" do + @object.system(ruby_cmd('exit 1')).should == false + + $?.should be_an_instance_of Process::Status + $?.success?.should == false + $?.exitstatus.should == 1 + end + + it "returns nil when command execution fails" do + @object.system("sad").should be_nil + + $?.should be_an_instance_of Process::Status + $?.pid.should be_kind_of(Integer) + $?.exitstatus.should == 127 + end + + it "does not write to stderr when command execution fails" do + lambda { @object.system("sad") }.should output_to_fd("", STDERR) + end + + platform_is_not :windows do + before :each do + @shell = ENV['SHELL'] + end + + after :each do + ENV['SHELL'] = @shell + end + + it "executes with `sh` if the command contains shell characters" do + lambda { @object.system("echo $0") }.should output_to_fd("sh\n") + end + + it "ignores SHELL env var and always uses `sh`" do + ENV['SHELL'] = "/bin/fakeshell" + lambda { @object.system("echo $0") }.should output_to_fd("sh\n") + end + end + + before :each do + ENV['TEST_SH_EXPANSION'] = 'foo' + @shell_var = '$TEST_SH_EXPANSION' + platform_is :windows do + @shell_var = '%TEST_SH_EXPANSION%' + end + end + + after :each do + ENV.delete('TEST_SH_EXPANSION') + end + + it "expands shell variables when given a single string argument" do + lambda { @object.system("echo #{@shell_var}") }.should output_to_fd("foo\n") + end + + platform_is_not :windows do + it "does not expand shell variables when given multiples arguments" do + lambda { @object.system("echo", @shell_var) }.should output_to_fd("#{@shell_var}\n") + end + end + + platform_is :windows do + it "does expand shell variables when given multiples arguments" do + # See https://bugs.ruby-lang.org/issues/12231 + lambda { @object.system("echo", @shell_var) }.should output_to_fd("foo\n") + end + end + + platform_is :windows do + it "runs commands starting with any number of @ using shell" do + `#{ruby_cmd("p system 'does_not_exist'")} 2>NUL`.chomp.should == "nil" + @object.system('@does_not_exist 2>NUL').should == false + @object.system("@@@#{ruby_cmd('exit 0')}").should == true + end + end +end + +describe "Kernel#system" do + it "is a private method" do + Kernel.should have_private_instance_method(:system) + end + + it_behaves_like :kernel_system, :system, KernelSpecs::Method.new +end + +describe "Kernel.system" do + it_behaves_like :kernel_system, :system, Kernel +end diff --git a/spec/ruby/core/kernel/taint_spec.rb b/spec/ruby/core/kernel/taint_spec.rb new file mode 100644 index 0000000000..0c2fb3286b --- /dev/null +++ b/spec/ruby/core/kernel/taint_spec.rb @@ -0,0 +1,45 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#taint" do + it "returns self" do + o = Object.new + o.taint.should equal(o) + end + + it "sets the tainted bit" do + o = Object.new + o.taint + o.tainted?.should == true + end + + it "raises RuntimeError on an untainted, frozen object" do + o = Object.new.freeze + lambda { o.taint }.should raise_error(RuntimeError) + end + + it "does not raise an error on a tainted, frozen object" do + o = Object.new.taint.freeze + o.taint.should equal(o) + end + + it "has no effect on immediate values" do + [nil, true, false].each do |v| + v.taint + v.tainted?.should == false + end + end + + it "no raises a RuntimeError on symbols" do + v = :sym + lambda { v.taint }.should_not raise_error(RuntimeError) + v.tainted?.should == false + end + + it "no raises error on fixnum values" do + [1].each do |v| + lambda { v.taint }.should_not raise_error(RuntimeError) + v.tainted?.should == false + end + end +end diff --git a/spec/ruby/core/kernel/tainted_spec.rb b/spec/ruby/core/kernel/tainted_spec.rb new file mode 100644 index 0000000000..efb31be9d8 --- /dev/null +++ b/spec/ruby/core/kernel/tainted_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#tainted?" do + it "returns true if Object is tainted" do + o = mock('o') + p = mock('p') + p.taint + o.tainted?.should == false + p.tainted?.should == true + end +end diff --git a/spec/ruby/core/kernel/tap_spec.rb b/spec/ruby/core/kernel/tap_spec.rb new file mode 100644 index 0000000000..312a34426c --- /dev/null +++ b/spec/ruby/core/kernel/tap_spec.rb @@ -0,0 +1,13 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#tap" do + it "always yields self and returns self" do + a = KernelSpecs::A.new + a.tap{|o| o.should equal(a); 42}.should equal(a) + end + + it "raises a LocalJumpError when no block given" do + lambda { 3.tap }.should raise_error(LocalJumpError) + end +end diff --git a/spec/ruby/core/kernel/test_spec.rb b/spec/ruby/core/kernel/test_spec.rb new file mode 100644 index 0000000000..43e3a963e3 --- /dev/null +++ b/spec/ruby/core/kernel/test_spec.rb @@ -0,0 +1,109 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#test" do + before :all do + @file = File.dirname(__FILE__) + '/fixtures/classes.rb' + @dir = File.dirname(__FILE__) + '/fixtures' + end + + it "is a private method" do + Kernel.should have_private_instance_method(:test) + end + + it "returns true when passed ?f if the argument is a regular file" do + Kernel.test(?f, @file).should == true + end + + it "returns true when passed ?e if the argument is a file" do + Kernel.test(?e, @file).should == true + end + + it "returns true when passed ?d if the argument is a directory" do + Kernel.test(?d, @dir).should == true + end + + platform_is_not :windows do + it "returns true when passed ?l if the argument is a symlink" do + link = tmp("file_symlink.lnk") + File.symlink(@file, link) + begin + Kernel.test(?l, link).should be_true + ensure + rm_r link + end + end + end + + it "returns true when passed ?r if the argument is readable by the effective uid" do + Kernel.test(?r, @file).should be_true + end + + it "returns true when passed ?R if the argument is readable by the real uid" do + Kernel.test(?R, @file).should be_true + end + + context "writable test" do + before do + @tmp_file = tmp("file.kernel.test") + touch(@tmp_file) + end + + after do + rm_r @tmp_file + end + + it "returns true when passed ?w if the argument is readable by the effective uid" do + Kernel.test(?w, @tmp_file).should be_true + end + + it "returns true when passed ?W if the argument is readable by the real uid" do + Kernel.test(?W, @tmp_file).should be_true + end + end + + context "time commands" do + before :each do + @tmp_file = File.new(tmp("file.kernel.test"), "w") + end + + after :each do + @tmp_file.close + rm_r @tmp_file + end + + it "returns the last access time for the provided file when passed ?A" do + Kernel.test(?A, @tmp_file).should == @tmp_file.atime + end + + it "returns the time at which the file was created when passed ?C" do + Kernel.test(?C, @tmp_file).should == @tmp_file.ctime + end + + it "returns the time at which the file was modified when passed ?M" do + Kernel.test(?M, @tmp_file).should == @tmp_file.mtime + end + end + + it "calls #to_path on second argument when passed ?f and a filename" do + p = mock('path') + p.should_receive(:to_path).and_return @file + Kernel.test(?f, p) + end + + it "calls #to_path on second argument when passed ?e and a filename" do + p = mock('path') + p.should_receive(:to_path).and_return @file + Kernel.test(?e, p) + end + + it "calls #to_path on second argument when passed ?d and a directory" do + p = mock('path') + p.should_receive(:to_path).and_return @dir + Kernel.test(?d, p) + end +end + +describe "Kernel.test" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/throw_spec.rb b/spec/ruby/core/kernel/throw_spec.rb new file mode 100644 index 0000000000..3f8d272d6d --- /dev/null +++ b/spec/ruby/core/kernel/throw_spec.rb @@ -0,0 +1,80 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel.throw" do + it "transfers control to the end of the active catch block waiting for symbol" do + catch(:blah) do + :value + throw :blah + fail("throw didn't transfer the control") + end.should be_nil + end + + it "transfers control to the innermost catch block waiting for the same sympol" do + one = two = three = 0 + catch :duplicate do + catch :duplicate do + catch :duplicate do + one = 1 + throw :duplicate + end + two = 2 + throw :duplicate + end + three = 3 + throw :duplicate + end + [one, two, three].should == [1, 2, 3] + end + + it "sets the return value of the catch block to nil by default" do + res = catch :blah do + throw :blah + end + res.should == nil + end + + it "sets the return value of the catch block to a value specified as second parameter" do + res = catch :blah do + throw :blah, :return_value + end + res.should == :return_value + end + + it "raises an ArgumentError if there is no catch block for the symbol" do + lambda { throw :blah }.should raise_error(ArgumentError) + end + + it "raises an UncaughtThrowError if there is no catch block for the symbol" do + lambda { throw :blah }.should raise_error(UncaughtThrowError) + end + + it "raises ArgumentError if 3 or more arguments provided" do + lambda { + catch :blah do + throw :blah, :return_value, 2 + end + }.should raise_error(ArgumentError) + + lambda { + catch :blah do + throw :blah, :return_value, 2, 3, 4, 5 + end + }.should raise_error(ArgumentError) + end + + it "can throw an object" do + lambda { + obj = Object.new + catch obj do + throw obj + end + }.should_not raise_error(NameError) + end +end + +describe "Kernel#throw" do + it "is a private method" do + Kernel.should have_private_instance_method(:throw) + end +end diff --git a/spec/ruby/core/kernel/to_enum_spec.rb b/spec/ruby/core/kernel/to_enum_spec.rb new file mode 100644 index 0000000000..9fb228f318 --- /dev/null +++ b/spec/ruby/core/kernel/to_enum_spec.rb @@ -0,0 +1,5 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +describe "Kernel#to_enum" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/to_s_spec.rb b/spec/ruby/core/kernel/to_s_spec.rb new file mode 100644 index 0000000000..c6fcca54a2 --- /dev/null +++ b/spec/ruby/core/kernel/to_s_spec.rb @@ -0,0 +1,16 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#to_s" do + it "returns a String containing the name of self's class" do + Object.new.to_s.should =~ /Object/ + end + + it "returns a tainted result if self is tainted" do + Object.new.taint.to_s.tainted?.should be_true + end + + it "returns an untrusted result if self is untrusted" do + Object.new.untrust.to_s.untrusted?.should be_true + end +end diff --git a/spec/ruby/core/kernel/trace_var_spec.rb b/spec/ruby/core/kernel/trace_var_spec.rb new file mode 100644 index 0000000000..07e02feb72 --- /dev/null +++ b/spec/ruby/core/kernel/trace_var_spec.rb @@ -0,0 +1,54 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#trace_var" do + before :each do + $Kernel_trace_var_global = nil + end + + after :each do + untrace_var :$Kernel_trace_var_global + + $Kernel_trace_var_global = nil + $Kernel_trace_var_extra = nil + end + + it "is a private method" do + Kernel.should have_private_instance_method(:trace_var) + end + + it "hooks assignments to a global variable" do + captured = nil + + trace_var :$Kernel_trace_var_global do |value| + captured = value + end + + $Kernel_trace_var_global = 'foo' + captured.should == 'foo' + end + + it "accepts a proc argument instead of a block" do + captured = nil + + trace_var :$Kernel_trace_var_global, proc {|value| captured = value} + + $Kernel_trace_var_global = 'foo' + captured.should == 'foo' + end + + # String arguments should be evaluated in the context of the caller. + it "accepts a String argument instead of a Proc or block" do + trace_var :$Kernel_trace_var_global, '$Kernel_trace_var_extra = true' + + $Kernel_trace_var_global = 'foo' + + $Kernel_trace_var_extra.should == true + end + + it "raises ArgumentError if no block or proc is provided" do + lambda do + trace_var :$Kernel_trace_var_global + end.should raise_error(ArgumentError) + end +end diff --git a/spec/ruby/core/kernel/trap_spec.rb b/spec/ruby/core/kernel/trap_spec.rb new file mode 100644 index 0000000000..98f386dc85 --- /dev/null +++ b/spec/ruby/core/kernel/trap_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#trap" do + it "is a private method" do + Kernel.should have_private_instance_method(:trap) + end +end + +describe "Kernel.trap" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/trust_spec.rb b/spec/ruby/core/kernel/trust_spec.rb new file mode 100644 index 0000000000..a9fda5c5c6 --- /dev/null +++ b/spec/ruby/core/kernel/trust_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#trust" do + it "returns self" do + o = Object.new + o.trust.should equal(o) + end + + it "clears the untrusted bit" do + o = Object.new.untrust + o.trust + o.untrusted?.should == false + end + + it "raises RuntimeError on an untrusted, frozen object" do + o = Object.new.untrust.freeze + lambda { o.trust }.should raise_error(RuntimeError) + end + + it "does not raise an error on a trusted, frozen object" do + o = Object.new.freeze + o.trust.should equal(o) + end +end diff --git a/spec/ruby/core/kernel/untaint_spec.rb b/spec/ruby/core/kernel/untaint_spec.rb new file mode 100644 index 0000000000..5abe5d63fc --- /dev/null +++ b/spec/ruby/core/kernel/untaint_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#untaint" do + it "returns self" do + o = Object.new + o.untaint.should equal(o) + end + + it "clears the tainted bit" do + o = Object.new.taint + o.untaint + o.tainted?.should == false + end + + it "raises RuntimeError on a tainted, frozen object" do + o = Object.new.taint.freeze + lambda { o.untaint }.should raise_error(RuntimeError) + end + + it "does not raise an error on an untainted, frozen object" do + o = Object.new.freeze + o.untaint.should equal(o) + end +end diff --git a/spec/ruby/core/kernel/untrace_var_spec.rb b/spec/ruby/core/kernel/untrace_var_spec.rb new file mode 100644 index 0000000000..3af1348ffd --- /dev/null +++ b/spec/ruby/core/kernel/untrace_var_spec.rb @@ -0,0 +1,12 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#untrace_var" do + it "is a private method" do + Kernel.should have_private_instance_method(:untrace_var) + end +end + +describe "Kernel.untrace_var" do + it "needs to be reviewed for spec completeness" +end diff --git a/spec/ruby/core/kernel/untrust_spec.rb b/spec/ruby/core/kernel/untrust_spec.rb new file mode 100644 index 0000000000..280a465807 --- /dev/null +++ b/spec/ruby/core/kernel/untrust_spec.rb @@ -0,0 +1,25 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#untrust" do + it "returns self" do + o = Object.new + o.untrust.should equal(o) + end + + it "sets the untrusted bit" do + o = Object.new + o.untrust + o.untrusted?.should == true + end + + it "raises RuntimeError on a trusted, frozen object" do + o = Object.new.freeze + lambda { o.untrust }.should raise_error(RuntimeError) + end + + it "does not raise an error on an untrusted, frozen object" do + o = Object.new.untrust.freeze + o.untrust.should equal(o) + end +end diff --git a/spec/ruby/core/kernel/untrusted_spec.rb b/spec/ruby/core/kernel/untrusted_spec.rb new file mode 100644 index 0000000000..43c4c0aa18 --- /dev/null +++ b/spec/ruby/core/kernel/untrusted_spec.rb @@ -0,0 +1,28 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#untrusted?" do + it "returns the untrusted status of an object" do + o = mock('o') + o.untrusted?.should == false + o.untrust + o.untrusted?.should == true + end + + it "has no effect on immediate values" do + a = nil + b = true + c = false + a.untrust + b.untrust + c.untrust + a.untrusted?.should == false + b.untrusted?.should == false + c.untrusted?.should == false + end + + it "has effect on immediate values" do + d = 1 + lambda { d.untrust }.should_not raise_error(RuntimeError) + end +end diff --git a/spec/ruby/core/kernel/warn_spec.rb b/spec/ruby/core/kernel/warn_spec.rb new file mode 100644 index 0000000000..c44116dc21 --- /dev/null +++ b/spec/ruby/core/kernel/warn_spec.rb @@ -0,0 +1,79 @@ +require File.expand_path('../../../spec_helper', __FILE__) +require File.expand_path('../fixtures/classes', __FILE__) + +describe "Kernel#warn" do + before :each do + @before_verbose = $VERBOSE + @before_separator = $/ + end + + after :each do + $VERBOSE = @before_verbose + $/ = @before_separator + end + + it "is a private method" do + Kernel.should have_private_instance_method(:warn) + end + + it "requires multiple arguments" do + Kernel.method(:warn).arity.should < 0 + end + + it "does not append line-end if last character is line-end" do + lambda { + $VERBOSE = true + warn("this is some simple text with line-end\n") + }.should output(nil, "this is some simple text with line-end\n") + end + + it "calls #write on $stderr if $VERBOSE is true" do + lambda { + $VERBOSE = true + warn("this is some simple text") + }.should output(nil, "this is some simple text\n") + end + + it "calls #write on $stderr if $VERBOSE is false" do + lambda { + $VERBOSE = false + warn("this is some simple text") + }.should output(nil, "this is some simple text\n") + end + + it "does not call #write on $stderr if $VERBOSE is nil" do + lambda { + $VERBOSE = nil + warn("this is some simple text") + }.should output(nil, "") + end + + it "writes each argument on a line when passed multiple arguments" do + lambda { + $VERBOSE = true + warn("line 1", "line 2") + }.should output(nil, "line 1\nline 2\n") + end + + it "writes each array element on a line when passes an array" do + lambda { + $VERBOSE = true + warn(["line 1", "line 2"]) + }.should output(nil, "line 1\nline 2\n") + end + + it "does not write strings when passed no arguments" do + lambda { + $VERBOSE = true + warn + }.should output("", "") + end + + it "writes the default record separator and NOT $/ to $stderr after the warning message" do + lambda { + $VERBOSE = true + $/ = 'rs' + warn("") + }.should output(nil, /\n/) + end +end diff --git a/spec/ruby/core/kernel/yield_self_spec.rb b/spec/ruby/core/kernel/yield_self_spec.rb new file mode 100644 index 0000000000..817c01f288 --- /dev/null +++ b/spec/ruby/core/kernel/yield_self_spec.rb @@ -0,0 +1,26 @@ +require File.expand_path('../../../spec_helper', __FILE__) + +has_yield_self = VersionGuard.new("2.5").match? || PlatformGuard.implementation?(:truffleruby) + +if has_yield_self + describe "Kernel#yield_self" do + it "yields self" do + object = Object.new + object.yield_self { |o| o.should equal object } + end + + it "returns the block return value" do + object = Object.new + object.yield_self { 42 }.should equal 42 + end + + it "returns a sized Enumerator when no block given" do + object = Object.new + enum = object.yield_self + enum.should be_an_instance_of Enumerator + enum.size.should equal 1 + enum.peek.should equal object + enum.first.should equal object + end + end +end |