summaryrefslogtreecommitdiff
path: root/spec/ruby/core/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/kernel')
-rw-r--r--spec/ruby/core/kernel/Array_spec.rb97
-rw-r--r--spec/ruby/core/kernel/Complex_spec.rb276
-rw-r--r--spec/ruby/core/kernel/Float_spec.rb413
-rw-r--r--spec/ruby/core/kernel/Hash_spec.rb63
-rw-r--r--spec/ruby/core/kernel/Integer_spec.rb823
-rw-r--r--spec/ruby/core/kernel/Rational_spec.rb236
-rw-r--r--spec/ruby/core/kernel/String_spec.rb106
-rw-r--r--spec/ruby/core/kernel/__callee___spec.rb48
-rw-r--r--spec/ruby/core/kernel/__dir___spec.rb27
-rw-r--r--spec/ruby/core/kernel/__method___spec.rb40
-rw-r--r--spec/ruby/core/kernel/abort_spec.rb15
-rw-r--r--spec/ruby/core/kernel/at_exit_spec.rb19
-rw-r--r--spec/ruby/core/kernel/autoload_spec.rb178
-rw-r--r--spec/ruby/core/kernel/backtick_spec.rb84
-rw-r--r--spec/ruby/core/kernel/binding_spec.rb51
-rw-r--r--spec/ruby/core/kernel/block_given_spec.rb43
-rw-r--r--spec/ruby/core/kernel/caller_locations_spec.rb110
-rw-r--r--spec/ruby/core/kernel/caller_spec.rb109
-rw-r--r--spec/ruby/core/kernel/case_compare_spec.rb135
-rw-r--r--spec/ruby/core/kernel/catch_spec.rb127
-rw-r--r--spec/ruby/core/kernel/chomp_spec.rb65
-rw-r--r--spec/ruby/core/kernel/chop_spec.rb53
-rw-r--r--spec/ruby/core/kernel/class_spec.rb26
-rw-r--r--spec/ruby/core/kernel/clone_spec.rb177
-rw-r--r--spec/ruby/core/kernel/comparison_spec.rb31
-rw-r--r--spec/ruby/core/kernel/define_singleton_method_spec.rb120
-rw-r--r--spec/ruby/core/kernel/display_spec.rb6
-rw-r--r--spec/ruby/core/kernel/dup_spec.rb67
-rw-r--r--spec/ruby/core/kernel/enum_for_spec.rb5
-rw-r--r--spec/ruby/core/kernel/eql_spec.rb10
-rw-r--r--spec/ruby/core/kernel/equal_value_spec.rb15
-rw-r--r--spec/ruby/core/kernel/eval_spec.rb570
-rw-r--r--spec/ruby/core/kernel/exec_spec.rb18
-rw-r--r--spec/ruby/core/kernel/exit_spec.rb27
-rw-r--r--spec/ruby/core/kernel/extend_spec.rb91
-rw-r--r--spec/ruby/core/kernel/fail_spec.rb42
-rw-r--r--spec/ruby/core/kernel/fixtures/Complex.rb5
-rw-r--r--spec/ruby/core/kernel/fixtures/__callee__.rb34
-rw-r--r--spec/ruby/core/kernel/fixtures/__dir__.rb2
-rw-r--r--spec/ruby/core/kernel/fixtures/__method__.rb34
-rw-r--r--spec/ruby/core/kernel/fixtures/autoload_b.rb5
-rw-r--r--spec/ruby/core/kernel/fixtures/autoload_d.rb5
-rw-r--r--spec/ruby/core/kernel/fixtures/autoload_from_included_module.rb9
-rw-r--r--spec/ruby/core/kernel/fixtures/autoload_from_included_module2.rb9
-rw-r--r--spec/ruby/core/kernel/fixtures/autoload_frozen.rb7
-rw-r--r--spec/ruby/core/kernel/fixtures/caller.rb7
-rw-r--r--spec/ruby/core/kernel/fixtures/caller_at_exit.rb7
-rw-r--r--spec/ruby/core/kernel/fixtures/caller_locations.rb7
-rw-r--r--spec/ruby/core/kernel/fixtures/chomp.rb4
-rw-r--r--spec/ruby/core/kernel/fixtures/chomp_f.rb4
-rw-r--r--spec/ruby/core/kernel/fixtures/chop.rb4
-rw-r--r--spec/ruby/core/kernel/fixtures/chop_f.rb4
-rw-r--r--spec/ruby/core/kernel/fixtures/classes.rb558
-rw-r--r--spec/ruby/core/kernel/fixtures/eval_locals.rb6
-rw-r--r--spec/ruby/core/kernel/fixtures/eval_return_with_lambda.rb12
-rw-r--r--spec/ruby/core/kernel/fixtures/eval_return_without_lambda.rb14
-rw-r--r--spec/ruby/core/kernel/fixtures/singleton_methods.rb13
-rw-r--r--spec/ruby/core/kernel/fixtures/test.rb362
-rw-r--r--spec/ruby/core/kernel/fixtures/warn_core_method.rb14
-rw-r--r--spec/ruby/core/kernel/fixtures/warn_require.rb1
-rw-r--r--spec/ruby/core/kernel/fixtures/warn_require_caller.rb2
-rw-r--r--spec/ruby/core/kernel/fork_spec.rb15
-rw-r--r--spec/ruby/core/kernel/format_spec.rb47
-rw-r--r--spec/ruby/core/kernel/freeze_spec.rb91
-rw-r--r--spec/ruby/core/kernel/frozen_spec.rb76
-rw-r--r--spec/ruby/core/kernel/gets_spec.rb17
-rw-r--r--spec/ruby/core/kernel/global_variables_spec.rb26
-rw-r--r--spec/ruby/core/kernel/gsub_spec.rb96
-rw-r--r--spec/ruby/core/kernel/initialize_clone_spec.rb26
-rw-r--r--spec/ruby/core/kernel/initialize_copy_spec.rb36
-rw-r--r--spec/ruby/core/kernel/initialize_dup_spec.rb20
-rw-r--r--spec/ruby/core/kernel/inspect_spec.rb90
-rw-r--r--spec/ruby/core/kernel/instance_of_spec.rb40
-rw-r--r--spec/ruby/core/kernel/instance_variable_defined_spec.rb41
-rw-r--r--spec/ruby/core/kernel/instance_variable_get_spec.rb111
-rw-r--r--spec/ruby/core/kernel/instance_variable_set_spec.rb105
-rw-r--r--spec/ruby/core/kernel/instance_variables_spec.rb40
-rw-r--r--spec/ruby/core/kernel/is_a_spec.rb6
-rw-r--r--spec/ruby/core/kernel/itself_spec.rb9
-rw-r--r--spec/ruby/core/kernel/kind_of_spec.rb6
-rw-r--r--spec/ruby/core/kernel/lambda_spec.rb158
-rw-r--r--spec/ruby/core/kernel/load_spec.rb40
-rw-r--r--spec/ruby/core/kernel/local_variables_spec.rb48
-rw-r--r--spec/ruby/core/kernel/loop_spec.rb79
-rw-r--r--spec/ruby/core/kernel/match_spec.rb7
-rw-r--r--spec/ruby/core/kernel/method_spec.rb80
-rw-r--r--spec/ruby/core/kernel/methods_spec.rb101
-rw-r--r--spec/ruby/core/kernel/nil_spec.rb12
-rw-r--r--spec/ruby/core/kernel/not_match_spec.rb25
-rw-r--r--spec/ruby/core/kernel/object_id_spec.rb6
-rw-r--r--spec/ruby/core/kernel/open_spec.rb194
-rw-r--r--spec/ruby/core/kernel/p_spec.rb85
-rw-r--r--spec/ruby/core/kernel/pp_spec.rb9
-rw-r--r--spec/ruby/core/kernel/print_spec.rb24
-rw-r--r--spec/ruby/core/kernel/printf_spec.rb70
-rw-r--r--spec/ruby/core/kernel/private_methods_spec.rb69
-rw-r--r--spec/ruby/core/kernel/proc_spec.rb48
-rw-r--r--spec/ruby/core/kernel/protected_methods_spec.rb69
-rw-r--r--spec/ruby/core/kernel/public_method_spec.rb32
-rw-r--r--spec/ruby/core/kernel/public_methods_spec.rb76
-rw-r--r--spec/ruby/core/kernel/public_send_spec.rb116
-rw-r--r--spec/ruby/core/kernel/putc_spec.rb39
-rw-r--r--spec/ruby/core/kernel/puts_spec.rb29
-rw-r--r--spec/ruby/core/kernel/raise_spec.rb292
-rw-r--r--spec/ruby/core/kernel/rand_spec.rb197
-rw-r--r--spec/ruby/core/kernel/readline_spec.rb12
-rw-r--r--spec/ruby/core/kernel/readlines_spec.rb12
-rw-r--r--spec/ruby/core/kernel/remove_instance_variable_spec.rb72
-rw-r--r--spec/ruby/core/kernel/require_relative_spec.rb437
-rw-r--r--spec/ruby/core/kernel/require_spec.rb60
-rw-r--r--spec/ruby/core/kernel/respond_to_missing_spec.rb100
-rw-r--r--spec/ruby/core/kernel/respond_to_spec.rb72
-rw-r--r--spec/ruby/core/kernel/select_spec.rb18
-rw-r--r--spec/ruby/core/kernel/send_spec.rb68
-rw-r--r--spec/ruby/core/kernel/set_trace_func_spec.rb12
-rw-r--r--spec/ruby/core/kernel/shared/dup_clone.rb91
-rw-r--r--spec/ruby/core/kernel/shared/kind_of.rb55
-rw-r--r--spec/ruby/core/kernel/shared/lambda.rb11
-rw-r--r--spec/ruby/core/kernel/shared/load.rb215
-rw-r--r--spec/ruby/core/kernel/shared/method.rb56
-rw-r--r--spec/ruby/core/kernel/shared/require.rb848
-rw-r--r--spec/ruby/core/kernel/shared/sprintf.rb987
-rw-r--r--spec/ruby/core/kernel/shared/sprintf_encoding.rb67
-rw-r--r--spec/ruby/core/kernel/shared/then.rb20
-rw-r--r--spec/ruby/core/kernel/singleton_class_spec.rb74
-rw-r--r--spec/ruby/core/kernel/singleton_method_spec.rb85
-rw-r--r--spec/ruby/core/kernel/singleton_methods_spec.rb192
-rw-r--r--spec/ruby/core/kernel/sleep_spec.rb126
-rw-r--r--spec/ruby/core/kernel/spawn_spec.rb25
-rw-r--r--spec/ruby/core/kernel/sprintf_spec.rb64
-rw-r--r--spec/ruby/core/kernel/srand_spec.rb73
-rw-r--r--spec/ruby/core/kernel/sub_spec.rb26
-rw-r--r--spec/ruby/core/kernel/syscall_spec.rb12
-rw-r--r--spec/ruby/core/kernel/system_spec.rb132
-rw-r--r--spec/ruby/core/kernel/taint_spec.rb8
-rw-r--r--spec/ruby/core/kernel/tainted_spec.rb8
-rw-r--r--spec/ruby/core/kernel/tap_spec.rb13
-rw-r--r--spec/ruby/core/kernel/test_spec.rb109
-rw-r--r--spec/ruby/core/kernel/then_spec.rb6
-rw-r--r--spec/ruby/core/kernel/throw_spec.rb80
-rw-r--r--spec/ruby/core/kernel/to_enum_spec.rb5
-rw-r--r--spec/ruby/core/kernel/to_s_spec.rb8
-rw-r--r--spec/ruby/core/kernel/trace_var_spec.rb54
-rw-r--r--spec/ruby/core/kernel/trap_spec.rb9
-rw-r--r--spec/ruby/core/kernel/trust_spec.rb8
-rw-r--r--spec/ruby/core/kernel/untaint_spec.rb8
-rw-r--r--spec/ruby/core/kernel/untrace_var_spec.rb12
-rw-r--r--spec/ruby/core/kernel/untrust_spec.rb8
-rw-r--r--spec/ruby/core/kernel/untrusted_spec.rb8
-rw-r--r--spec/ruby/core/kernel/warn_spec.rb298
-rw-r--r--spec/ruby/core/kernel/yield_self_spec.rb6
151 files changed, 12925 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..b4a8bb7599
--- /dev/null
+++ b/spec/ruby/core/kernel/Array_spec.rb
@@ -0,0 +1,97 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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")
+
+ -> { @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")
+
+ -> { @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..346d50ab5e
--- /dev/null
+++ b/spec/ruby/core/kernel/Complex_spec.rb
@@ -0,0 +1,276 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/kernel/complex'
+require_relative 'fixtures/Complex'
+
+describe "Kernel.Complex()" do
+ describe "when passed [Complex, Complex]" do
+ it "returns a new Complex number based on the two given numbers" do
+ Complex(Complex(3, 4), Complex(5, 6)).should == Complex(3 - 6, 4 + 5)
+ Complex(Complex(1.5, 2), Complex(-5, 6.3)).should == Complex(1.5 - 6.3, 2 - 5)
+ end
+ end
+
+ describe "when passed [Complex]" do
+ it "returns the passed Complex number" do
+ Complex(Complex(1, 2)).should == Complex(1, 2)
+ Complex(Complex(-3.4, bignum_value)).should == Complex(-3.4, bignum_value)
+ end
+ end
+
+ describe "when passed [Integer, Integer]" do
+ it "returns a new Complex number" do
+ Complex(1, 2).should be_an_instance_of(Complex)
+ Complex(1, 2).real.should == 1
+ Complex(1, 2).imag.should == 2
+
+ Complex(-3, -5).should be_an_instance_of(Complex)
+ Complex(-3, -5).real.should == -3
+ Complex(-3, -5).imag.should == -5
+
+ Complex(3.5, -4.5).should be_an_instance_of(Complex)
+ Complex(3.5, -4.5).real.should == 3.5
+ Complex(3.5, -4.5).imag.should == -4.5
+
+ Complex(bignum_value, 30).should be_an_instance_of(Complex)
+ Complex(bignum_value, 30).real.should == bignum_value
+ Complex(bignum_value, 30).imag.should == 30
+ end
+ end
+
+ describe "when passed [Integer/Float]" do
+ it "returns a new Complex number with 0 as the imaginary component" do
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ Complex(1).should be_an_instance_of(Complex)
+ Complex(1).imag.should == 0
+ Complex(1).real.should == 1
+
+ Complex(-3).should be_an_instance_of(Complex)
+ Complex(-3).imag.should == 0
+ Complex(-3).real.should == -3
+
+ Complex(-4.5).should be_an_instance_of(Complex)
+ Complex(-4.5).imag.should == 0
+ Complex(-4.5).real.should == -4.5
+
+ Complex(bignum_value).should be_an_instance_of(Complex)
+ Complex(bignum_value).imag.should == 0
+ Complex(bignum_value).real.should == bignum_value
+ end
+ end
+ end
+
+ describe "when passed [String]" do
+ it_behaves_like :kernel_complex, :Complex_method, KernelSpecs
+
+ context "invalid argument" do
+ it "raises Encoding::CompatibilityError if String is in not ASCII-compatible encoding" do
+ -> {
+ Complex("79+4i".encode("UTF-16"))
+ }.should raise_error(Encoding::CompatibilityError, "ASCII incompatible encoding: UTF-16")
+ end
+
+ it "raises ArgumentError for unrecognised Strings" do
+ -> {
+ Complex("ruby")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "ruby"')
+ end
+
+ it "raises ArgumentError for trailing garbage" do
+ -> {
+ Complex("79+4iruby")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "79+4iruby"')
+ end
+
+ it "does not understand Float::INFINITY" do
+ -> {
+ Complex("Infinity")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "Infinity"')
+
+ -> {
+ Complex("-Infinity")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "-Infinity"')
+ end
+
+ it "does not understand Float::NAN" do
+ -> {
+ Complex("NaN")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "NaN"')
+ end
+
+ it "does not understand a sequence of _" do
+ -> {
+ Complex("7__9+4__0i")
+ }.should raise_error(ArgumentError, 'invalid value for convert(): "7__9+4__0i"')
+ end
+
+ it "does not allow null-byte" do
+ -> {
+ Complex("1-2i\0")
+ }.should raise_error(ArgumentError, "string contains null byte")
+ end
+ end
+
+ context "invalid argument and exception: false passed" do
+ it "raises Encoding::CompatibilityError if String is in not ASCII-compatible encoding" do
+ -> {
+ Complex("79+4i".encode("UTF-16"), exception: false)
+ }.should raise_error(Encoding::CompatibilityError, "ASCII incompatible encoding: UTF-16")
+ end
+
+ it "returns nil for unrecognised Strings" do
+ Complex("ruby", exception: false).should == nil
+ end
+
+ it "returns nil when trailing garbage" do
+ Complex("79+4iruby", exception: false).should == nil
+ end
+
+ it "returns nil for Float::INFINITY" do
+ Complex("Infinity", exception: false).should == nil
+ Complex("-Infinity", exception: false).should == nil
+ end
+
+ it "returns nil for Float::NAN" do
+ Complex("NaN", exception: false).should == nil
+ end
+
+ it "returns nil when there is a sequence of _" do
+ Complex("7__9+4__0i", exception: false).should == nil
+ end
+
+ it "returns nil when String contains null-byte" do
+ Complex("1-2i\0", exception: false).should == nil
+ end
+ end
+ end
+
+ describe "when passes [String, String]" do
+ it "needs to be reviewed for spec completeness"
+ end
+
+ describe "when passed an Object which responds to #to_c" do
+ it "returns the passed argument" do
+ obj = Object.new; def obj.to_c; 1i end
+ Complex(obj).should == Complex(0, 1)
+ end
+ end
+
+ describe "when passed a Numeric which responds to #real? with false" do
+ it "returns the passed argument" do
+ n = mock_numeric("unreal")
+ n.should_receive(:real?).any_number_of_times.and_return(false)
+ Complex(n).should equal(n)
+ end
+ end
+
+ describe "when passed a Numeric which responds to #real? with true" do
+ it "returns a Complex with the passed argument as the real component and 0 as the imaginary component" do
+ n = mock_numeric("real")
+ n.should_receive(:real?).any_number_of_times.and_return(true)
+ result = Complex(n)
+ result.real.should equal(n)
+ result.imag.should equal(0)
+ end
+ end
+
+ describe "when passed Numerics n1 and n2 and at least one responds to #real? with false" do
+ [[false, false], [false, true], [true, false]].each do |r1, r2|
+ it "returns n1 + n2 * Complex(0, 1)" do
+ n1 = mock_numeric("n1")
+ n2 = mock_numeric("n2")
+ n3 = mock_numeric("n3")
+ n4 = mock_numeric("n4")
+ n1.should_receive(:real?).any_number_of_times.and_return(r1)
+ n2.should_receive(:real?).any_number_of_times.and_return(r2)
+ n2.should_receive(:*).with(Complex(0, 1)).and_return(n3)
+ n1.should_receive(:+).with(n3).and_return(n4)
+ Complex(n1, n2).should equal(n4)
+ end
+ end
+ end
+
+ describe "when passed two Numerics and both respond to #real? with true" do
+ it "returns a Complex with the passed arguments as real and imaginary components respectively" do
+ n1 = mock_numeric("n1")
+ n2 = mock_numeric("n2")
+ n1.should_receive(:real?).any_number_of_times.and_return(true)
+ n2.should_receive(:real?).any_number_of_times.and_return(true)
+ result = Complex(n1, n2)
+ result.real.should equal(n1)
+ result.imag.should equal(n2)
+ end
+ end
+
+ describe "when passed a single non-Numeric" do
+ it "coerces the passed argument using #to_c" do
+ n = mock("n")
+ c = Complex(0, 0)
+ n.should_receive(:to_c).and_return(c)
+ Complex(n).should equal(c)
+ end
+ end
+
+ describe "when passed a non-Numeric second argument" do
+ it "raises TypeError" do
+ -> { Complex(:sym, :sym) }.should raise_error(TypeError)
+ -> { Complex(0, :sym) }.should raise_error(TypeError)
+ end
+ end
+
+ describe "when passed nil" do
+ it "raises TypeError" do
+ -> { Complex(nil) }.should raise_error(TypeError, "can't convert nil into Complex")
+ -> { Complex(0, nil) }.should raise_error(TypeError, "can't convert nil into Complex")
+ -> { Complex(nil, 0) }.should raise_error(TypeError, "can't convert nil into Complex")
+ end
+ end
+
+ describe "when passed exception: false" do
+ describe "and [Numeric]" do
+ it "returns a complex number" do
+ Complex("123", exception: false).should == Complex(123)
+ end
+ end
+
+ describe "and [non-Numeric]" do
+ it "swallows an error" do
+ Complex(:sym, exception: false).should == nil
+ end
+ end
+
+ describe "and [non-Numeric, Numeric] argument" do
+ it "throws a TypeError" do
+ -> { Complex(:sym, 0, exception: false) }.should raise_error(TypeError, "not a real")
+ end
+ end
+
+ describe "and [anything, non-Numeric] argument" do
+ it "swallows an error" do
+ Complex("a", :sym, exception: false).should == nil
+ Complex(:sym, :sym, exception: false).should == nil
+ Complex(0, :sym, exception: false).should == nil
+ end
+ end
+
+ describe "and non-numeric String arguments" do
+ it "swallows an error" do
+ Complex("a", "b", exception: false).should == nil
+ Complex("a", 0, exception: false).should == nil
+ Complex(0, "b", exception: false).should == nil
+ end
+ end
+
+ describe "and nil arguments" do
+ it "swallows an error" do
+ Complex(nil, exception: false).should == nil
+ Complex(0, nil, exception: false).should == nil
+ Complex(nil, 0, exception: false).should == nil
+ end
+ end
+ end
+
+ it "freezes its result" do
+ Complex(1).frozen?.should == true
+ end
+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..9c436b05f7
--- /dev/null
+++ b/spec/ruby/core/kernel/Float_spec.rb
@@ -0,0 +1,413 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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.should equal float
+ 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
+ -> { @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
+ -> { @object.send(:Float, "float") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String with string in error message" do
+ -> { @object.send(:Float, "foo") }.should raise_error(ArgumentError) { |e|
+ e.message.should == 'invalid value for Float(): "foo"'
+ }
+ end
+
+ it "raises an ArgumentError if there are two decimal points in the String" do
+ -> { @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
+ -> { @object.send(:Float, "10D") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String of word characters followed by numbers" do
+ -> { @object.send(:Float, "D10") }.should raise_error(ArgumentError)
+ end
+
+ it "is strict about the string form even across newlines" do
+ -> { @object.send(:Float, "not a number\n10") }.should raise_error(ArgumentError)
+ -> { @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
+ -> { @object.send(:Float, "1+1") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "1-1") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if a String has a trailing + or -" do
+ -> { @object.send(:Float, "11+") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "11-") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String with a leading _" do
+ -> { @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
+ -> { @object.send(:Float, "10_") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String of \\0" do
+ -> { @object.send(:Float, "\0") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String with a leading \\0" do
+ -> { @object.send(:Float, "\01") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String with an embedded \\0" do
+ -> { @object.send(:Float, "1\01") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String with a trailing \\0" do
+ -> { @object.send(:Float, "1\0") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String that is just an empty space" do
+ -> { @object.send(:Float, " ") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a String that with an embedded space" do
+ -> { @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
+
+ ruby_version_is ""..."3.4" do
+ it "raises ArgumentError if a fractional part is missing" do
+ -> { @object.send(:Float, "1.") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "+1.") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "-1.") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "1.e+0") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "1.e-2") }.should raise_error(ArgumentError)
+ end
+ end
+
+ ruby_version_is "3.4" do
+ it "allows String representation without a fractional part" do
+ @object.send(:Float, "1.").should == 1.0
+ @object.send(:Float, "+1.").should == 1.0
+ @object.send(:Float, "-1.").should == -1.0
+ @object.send(:Float, "1.e+0").should == 1.0
+ @object.send(:Float, "1.e-2").should be_close(0.01, TOLERANCE)
+ end
+ end
+
+ %w(e E).each do |e|
+ it "raises an ArgumentError if #{e} is the trailing character" do
+ -> { @object.send(:Float, "2#{e}") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if #{e} is the leading character" do
+ -> { @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
+ -> { @object.send(:Float, "2 0#{e}100") }.should raise_error(ArgumentError)
+ -> { @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
+ -> { @object.send(:Float, "_20#{e}100") }.should raise_error(ArgumentError)
+ -> { @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
+ -> { @object.send(:Float, "20_#{e}100") }.should raise_error(ArgumentError)
+ -> { @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
+ -> { @object.send(:Float, "20#{e}2.0") }.should raise_error(ArgumentError)
+ end
+ end
+
+ context "for hexadecimal literals" do
+ it "interprets the 0x prefix as hexadecimal" do
+ @object.send(:Float, "0x10").should == 16.0
+ @object.send(:Float, "0x0F").should == 15.0
+ @object.send(:Float, "0x0f").should == 15.0
+ end
+
+ it "interprets negative hex value" do
+ @object.send(:Float, "-0x10").should == -16.0
+ end
+
+ it "accepts embedded _ if the number does not contain a-f" do
+ @object.send(:Float, "0x1_0").should == 16.0
+ end
+
+ ruby_version_is ""..."3.4.3" do
+ it "does not accept embedded _ if the number contains a-f" do
+ -> { @object.send(:Float, "0x1_0a") }.should raise_error(ArgumentError)
+ @object.send(:Float, "0x1_0a", exception: false).should be_nil
+ end
+ end
+
+ ruby_version_is "3.4.3" do
+ it "accepts embedded _ if the number contains a-f" do
+ @object.send(:Float, "0x1_0a").should == 0x10a.to_f
+ end
+ end
+
+ it "does not accept _ before, after or inside the 0x prefix" do
+ -> { @object.send(:Float, "_0x10") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "0_x10") }.should raise_error(ArgumentError)
+ -> { @object.send(:Float, "0x_10") }.should raise_error(ArgumentError)
+ @object.send(:Float, "_0x10", exception: false).should be_nil
+ @object.send(:Float, "0_x10", exception: false).should be_nil
+ @object.send(:Float, "0x_10", exception: false).should be_nil
+ end
+
+ it "parses negative hexadecimal string as negative float" do
+ @object.send(:Float, "-0x7b").should == -123.0
+ end
+
+ ruby_version_is "3.4" do
+ it "accepts a fractional part" do
+ @object.send(:Float, "0x0.8").should == 0.5
+ end
+ end
+
+ describe "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
+ -> { @object.send(:Float, "0x1#{p}") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if #{p} is the leading character" do
+ -> { @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
+ -> { @object.send(:Float, "0x1 0#{p}10") }.should raise_error(ArgumentError)
+ -> { @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
+ -> { @object.send(:Float, "0x_10#{p}10") }.should raise_error(ArgumentError)
+ -> { @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
+ -> { @object.send(:Float, "0x10_#{p}10") }.should raise_error(ArgumentError)
+ -> { @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
+ -> { @object.send(:Float, "0x1#{p}1.0") }.should raise_error(ArgumentError)
+ end
+ 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
+ -> { @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!')
+ -> { @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)
+ -> { @object.send(:Float, obj) }.should raise_error(TypeError)
+ end
+
+ it "raises a RangeError when passed a Complex argument" do
+ c = Complex(2, 3)
+ -> { @object.send(:Float, c) }.should raise_error(RangeError)
+ end
+
+ describe "when passed exception: false" do
+ describe "and valid input" do
+ it "returns a Float number" do
+ @object.send(:Float, 1, exception: false).should == 1.0
+ @object.send(:Float, "1", exception: false).should == 1.0
+ @object.send(:Float, "1.23", exception: false).should == 1.23
+ end
+ end
+
+ describe "and invalid input" do
+ it "swallows an error" do
+ @object.send(:Float, "abc", exception: false).should == nil
+ @object.send(:Float, :sym, exception: false).should == nil
+ end
+ end
+
+ describe "and nil" do
+ it "swallows it" do
+ @object.send(:Float, nil, exception: false).should == nil
+ end
+ end
+ 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..cbe098a8ac
--- /dev/null
+++ b/spec/ruby/core/kernel/Hash_spec.rb
@@ -0,0 +1,63 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#hash" do
+ it "is provided" do
+ 1.respond_to?(:hash).should == true
+ end
+
+ it "is stable" do
+ 1.hash.should == 1.hash
+ end
+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
+ -> { @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")
+
+ -> { @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..74dd3e0dd2
--- /dev/null
+++ b/spec/ruby/core/kernel/Integer_spec.rb
@@ -0,0 +1,823 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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 "raises a TypeError when to_int returns not-an-Integer object and to_i returns nil" do
+ obj = mock("object")
+ obj.should_receive(:to_int).and_return("1")
+ obj.should_receive(:to_i).and_return(nil)
+ -> { Integer(obj) }.should raise_error(TypeError)
+ end
+
+ it "return a result of to_i when to_int does not return an Integer" do
+ obj = mock("object")
+ obj.should_receive(:to_int).and_return("1")
+ obj.should_receive(:to_i).and_return(42)
+ Integer(obj).should == 42
+ end
+
+ it "raises a TypeError when passed nil" do
+ -> { Integer(nil) }.should raise_error(TypeError)
+ end
+
+ it "returns an Integer object" do
+ Integer(2).should be_an_instance_of(Integer)
+ Integer(9**99).should be_an_instance_of(Integer)
+ 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(2 * 10**100)
+ obj.should_not_receive(:to_i)
+ Integer(obj).should == 2 * 10**100
+ 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")
+ -> { Integer(obj) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if no to_int or to_i methods exist" do
+ obj = mock("object")
+ -> { 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)
+ -> { Integer(obj) }.should raise_error(TypeError)
+ end
+
+ it "raises a FloatDomainError when passed NaN" do
+ -> { Integer(nan_value) }.should raise_error(FloatDomainError)
+ end
+
+ it "raises a FloatDomainError when passed Infinity" do
+ -> { Integer(infinity_value) }.should raise_error(FloatDomainError)
+ end
+
+ describe "when passed exception: false" do
+ describe "and to_i returns a value that is not an Integer" do
+ it "swallows an error" do
+ obj = mock("object")
+ obj.should_receive(:to_i).and_return("1")
+ Integer(obj, exception: false).should == nil
+ end
+ end
+
+ describe "and no to_int or to_i methods exist" do
+ it "swallows an error" do
+ obj = mock("object")
+ Integer(obj, exception: false).should == nil
+ end
+ end
+
+ describe "and to_int returns nil and no to_i exists" do
+ it "swallows an error" do
+ obj = mock("object")
+ obj.should_receive(:to_i).and_return(nil)
+ Integer(obj, exception: false).should == nil
+ end
+ end
+
+ describe "and passed NaN" do
+ it "swallows an error" do
+ Integer(nan_value, exception: false).should == nil
+ end
+ end
+
+ describe "and passed Infinity" do
+ it "swallows an error" do
+ Integer(infinity_value, exception: false).should == nil
+ end
+ end
+
+ describe "and passed nil" do
+ it "swallows an error" do
+ Integer(nil, exception: false).should == nil
+ end
+ end
+
+ describe "and passed a String that contains numbers" do
+ it "normally parses it and returns an Integer" do
+ Integer("42", exception: false).should == 42
+ end
+ end
+
+ describe "and passed a String that can't be converted to an Integer" do
+ it "swallows an error" do
+ Integer("abc", exception: false).should == nil
+ end
+ end
+ end
+end
+
+describe :kernel_integer_string, shared: true do
+ it "raises an ArgumentError if the String is a null byte" do
+ -> { Integer("\0") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the String starts with a null byte" do
+ -> { Integer("\01") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the String ends with a null byte" do
+ -> { Integer("1\0") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the String contains a null byte" do
+ -> { 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
+ -> { Integer("_1") }.should raise_error(ArgumentError)
+ -> { Integer("___1") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are trailing _s" do
+ -> { Integer("1_") }.should raise_error(ArgumentError)
+ -> { 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
+ -> { Integer("1__1") }.should raise_error(ArgumentError)
+ -> { 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
+ -> { Integer("+ 1") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are multiple leading +s" do
+ -> { Integer("++1") }.should raise_error(ArgumentError)
+ -> { Integer("+++1") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are trailing +s" do
+ -> { Integer("1+") }.should raise_error(ArgumentError)
+ -> { 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
+ -> { Integer("--1") }.should raise_error(ArgumentError)
+ -> { Integer("---1") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are trailing -s" do
+ -> { Integer("1-") }.should raise_error(ArgumentError)
+ -> { Integer("1---") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there is a period" do
+ -> { Integer("0.0") }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for an empty String" do
+ -> { Integer("") }.should raise_error(ArgumentError)
+ end
+
+ describe "when passed exception: false" do
+ describe "and multiple leading -s" do
+ it "swallows an error" do
+ Integer("---1", exception: false).should == nil
+ end
+ end
+
+ describe "and multiple trailing -s" do
+ it "swallows an error" do
+ Integer("1---", exception: false).should == nil
+ end
+ end
+
+ describe "and an argument that contains a period" do
+ it "swallows an error" do
+ Integer("0.0", exception: false).should == nil
+ end
+ end
+
+ describe "and an empty string" do
+ it "swallows an error" do
+ Integer("", exception: false).should == nil
+ end
+ end
+ 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
+ -> { 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
+ -> { 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
+ -> { 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
+ -> { Integer("0#{d}a") }.should raise_error(ArgumentError)
+ end
+ end
+end
+
+describe :kernel_integer_string_base, shared: true do
+ it "raises an ArgumentError if the String is a null byte" do
+ -> { Integer("\0", 2) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the String starts with a null byte" do
+ -> { Integer("\01", 3) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the String ends with a null byte" do
+ -> { Integer("1\0", 4) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if the String contains a null byte" do
+ -> { 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
+ -> { Integer("_1", 7) }.should raise_error(ArgumentError)
+ -> { Integer("___1", 7) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are trailing _s" do
+ -> { Integer("1_", 12) }.should raise_error(ArgumentError)
+ -> { 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
+ -> { Integer("1__1", 4) }.should raise_error(ArgumentError)
+ -> { 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
+ -> { Integer("+ 1", 3) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are multiple leading +s" do
+ -> { Integer("++1", 3) }.should raise_error(ArgumentError)
+ -> { Integer("+++1", 3) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are trailing +s" do
+ -> { Integer("1+", 3) }.should raise_error(ArgumentError)
+ -> { 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
+ -> { Integer("--1", 9) }.should raise_error(ArgumentError)
+ -> { Integer("---1", 9) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there are trailing -s" do
+ -> { Integer("1-", 12) }.should raise_error(ArgumentError)
+ -> { Integer("1---", 12) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if there is a period" do
+ -> { Integer("0.0", 3) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for an empty String" do
+ -> { Integer("", 12) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a base of 1" do
+ -> { Integer("1", 1) }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError for a base of 37" do
+ -> { 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
+ -> { Integer('z',19) }.should raise_error(ArgumentError)
+ -> { 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
+ -> { 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
+ -> { 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
+ -> { 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
+ -> { 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
+ -> { 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
+ -> { 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
+ -> { Integer("0#{d}1", base) }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ it "raises an ArgumentError if a base is given for a non-String value" do
+ -> { Integer(98, 15) }.should raise_error(ArgumentError)
+ end
+
+ it "tries to convert the base to an integer using to_int" do
+ obj = mock('8')
+ obj.should_receive(:to_int).and_return(8)
+
+ Integer("777", obj).should == 0777
+ end
+
+ # https://bugs.ruby-lang.org/issues/19349
+ ruby_version_is ''...'3.3' do
+ it "ignores the base if it is not an integer and does not respond to #to_i" do
+ Integer("777", "8").should == 777
+ end
+ end
+
+ ruby_version_is '3.3' do
+ it "raises a TypeError if it is not an integer and does not respond to #to_i" do
+ -> {
+ Integer("777", "8")
+ }.should raise_error(TypeError, "no implicit conversion of String into Integer")
+ end
+ end
+
+ describe "when passed exception: false" do
+ describe "and valid argument" do
+ it "returns an Integer number" do
+ Integer("100", 10, exception: false).should == 100
+ Integer("100", 2, exception: false).should == 4
+ end
+ end
+
+ describe "and invalid argument" do
+ it "swallows an error" do
+ Integer("999", 2, exception: false).should == nil
+ Integer("abc", 10, exception: false).should == nil
+ end
+ 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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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"
+ -> { @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
+ -> { @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 :kernel_integer_string, :Integer
+
+ it_behaves_like :kernel_integer_string_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 :kernel_integer_string, :Integer
+
+ it_behaves_like :kernel_integer_string_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..cc11a35451
--- /dev/null
+++ b/spec/ruby/core/kernel/Rational_spec.rb
@@ -0,0 +1,236 @@
+require_relative '../../spec_helper'
+require_relative '../rational/fixtures/rational'
+
+describe "Kernel.Rational" do
+ describe "passed Integer" do
+ # Guard against the Mathn library
+ guard -> { !defined?(Math.rsqrt) } do
+ it "returns a new Rational number with 1 as the denominator" do
+ Rational(1).should eql(Rational(1, 1))
+ Rational(-3).should eql(Rational(-3, 1))
+ Rational(bignum_value).should eql(Rational(bignum_value, 1))
+ end
+ end
+ end
+
+ describe "passed two integers" do
+ it "returns a new Rational number" do
+ rat = Rational(1, 2)
+ rat.numerator.should == 1
+ rat.denominator.should == 2
+ rat.should be_an_instance_of(Rational)
+
+ rat = Rational(-3, -5)
+ rat.numerator.should == 3
+ rat.denominator.should == 5
+ rat.should be_an_instance_of(Rational)
+
+ rat = Rational(bignum_value, 3)
+ rat.numerator.should == bignum_value
+ rat.denominator.should == 3
+ rat.should be_an_instance_of(Rational)
+ end
+
+ it "reduces the Rational" do
+ rat = Rational(2, 4)
+ rat.numerator.should == 1
+ rat.denominator.should == 2
+
+ rat = Rational(3, 9)
+ rat.numerator.should == 1
+ rat.denominator.should == 3
+ end
+ end
+
+ describe "when passed a String" do
+ it "converts the String to a Rational using the same method as String#to_r" do
+ r = Rational(13, 25)
+ s_r = ".52".to_r
+ r_s = Rational(".52")
+
+ r_s.should == r
+ r_s.should == s_r
+ end
+
+ it "scales the Rational value of the first argument by the Rational value of the second" do
+ Rational(".52", ".6").should == Rational(13, 15)
+ Rational(".52", "1.6").should == Rational(13, 40)
+ end
+
+ it "does not use the same method as Float#to_r" do
+ r = Rational(3, 5)
+ f_r = 0.6.to_r
+ r_s = Rational("0.6")
+
+ r_s.should == r
+ r_s.should_not == f_r
+ end
+ end
+
+ describe "when passed a Numeric" do
+ it "calls #to_r to convert the first argument to a Rational" do
+ num = RationalSpecs::SubNumeric.new(2)
+
+ Rational(num).should == Rational(2)
+ end
+ end
+
+ describe "when passed a Complex" do
+ context "[Complex]" do
+ it "returns a Rational from the real part if the imaginary part is 0" do
+ Rational(Complex(1, 0)).should == Rational(1)
+ end
+
+ it "raises a RangeError if the imaginary part is not 0" do
+ -> { Rational(Complex(1, 2)) }.should raise_error(RangeError, "can't convert 1+2i into Rational")
+ end
+ end
+
+ context "[Numeric, Complex]" do
+ it "uses the real part if the imaginary part is 0" do
+ Rational(1, Complex(2, 0)).should == Rational(1, 2)
+ end
+
+ it "divides a numerator by the Complex denominator if the imaginary part is not 0" do
+ Rational(1, Complex(2, 1)).should == Complex(2/5r, -1/5r)
+ end
+ end
+ end
+
+ context "when passed neither a Numeric nor a String" do
+ it "converts to Rational with #to_r method" do
+ obj = Object.new
+ def obj.to_r; 1/2r; end
+
+ Rational(obj).should == 1/2r
+ end
+
+ it "tries to convert to Integer with #to_int method if it does not respond to #to_r" do
+ obj = Object.new
+ def obj.to_int; 1; end
+
+ Rational(obj).should == 1r
+ end
+
+ it "raises TypeError if it neither responds to #to_r nor #to_int method" do
+ -> { Rational([]) }.should raise_error(TypeError, "can't convert Array into Rational")
+ -> { Rational({}) }.should raise_error(TypeError, "can't convert Hash into Rational")
+ -> { Rational(nil) }.should raise_error(TypeError, "can't convert nil into Rational")
+ end
+
+ it "swallows exception raised in #to_int method" do
+ object = Object.new
+ def object.to_int() raise NoMethodError; end
+
+ -> { Rational(object) }.should raise_error(TypeError)
+ -> { Rational(object, 1) }.should raise_error(TypeError)
+ -> { Rational(1, object) }.should raise_error(TypeError)
+ end
+
+ it "raises TypeError if #to_r does not return Rational" do
+ obj = Object.new
+ def obj.to_r; []; end
+
+ -> { Rational(obj) }.should raise_error(TypeError, "can't convert Object to Rational (Object#to_r gives Array)")
+ end
+ end
+
+ it "raises a ZeroDivisionError if the second argument is 0" do
+ -> { Rational(1, 0) }.should raise_error(ZeroDivisionError, "divided by 0")
+ -> { Rational(1, 0.0) }.should raise_error(ZeroDivisionError, "divided by 0")
+ end
+
+ it "raises a TypeError if the first argument is nil" do
+ -> { Rational(nil) }.should raise_error(TypeError, "can't convert nil into Rational")
+ end
+
+ it "raises a TypeError if the second argument is nil" do
+ -> { Rational(1, nil) }.should raise_error(TypeError, "can't convert nil into Rational")
+ end
+
+ it "raises a TypeError if the first argument is a Symbol" do
+ -> { Rational(:sym) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if the second argument is a Symbol" do
+ -> { Rational(1, :sym) }.should raise_error(TypeError)
+ end
+
+ describe "when passed exception: false" do
+ describe "and [non-Numeric]" do
+ it "swallows an error" do
+ Rational(:sym, exception: false).should == nil
+ Rational("abc", exception: false).should == nil
+ end
+
+ it "swallows an exception raised in #to_r" do
+ obj = Object.new
+ def obj.to_r; raise; end
+ Rational(obj, exception: false).should == nil
+ end
+
+ it "swallows an exception raised in #to_int" do
+ obj = Object.new
+ def obj.to_int; raise; end
+ Rational(obj, exception: false).should == nil
+ end
+ end
+
+ describe "and [non-Numeric, Numeric]" do
+ it "swallows an error" do
+ Rational(:sym, 1, exception: false).should == nil
+ Rational("abc", 1, exception: false).should == nil
+ end
+
+ it "swallows an exception raised in #to_r" do
+ obj = Object.new
+ def obj.to_r; raise; end
+ Rational(obj, 1, exception: false).should == nil
+ end
+
+ it "swallows an exception raised in #to_int" do
+ obj = Object.new
+ def obj.to_int; raise; end
+ Rational(obj, 1, exception: false).should == nil
+ end
+ end
+
+ describe "and [anything, non-Numeric]" do
+ it "swallows an error" do
+ Rational(:sym, :sym, exception: false).should == nil
+ Rational("abc", :sym, exception: false).should == nil
+ end
+
+ it "swallows an exception raised in #to_r" do
+ obj = Object.new
+ def obj.to_r; raise; end
+ Rational(obj, obj, exception: false).should == nil
+ end
+
+ it "swallows an exception raised in #to_int" do
+ obj = Object.new
+ def obj.to_int; raise; end
+ Rational(obj, obj, exception: false).should == nil
+ end
+ end
+
+ describe "and non-Numeric String arguments" do
+ it "swallows an error" do
+ Rational("a", "b", exception: false).should == nil
+ Rational("a", 0, exception: false).should == nil
+ Rational(0, "b", exception: false).should == nil
+ end
+ end
+
+ describe "and nil arguments" do
+ it "swallows an error" do
+ Rational(nil, exception: false).should == nil
+ Rational(nil, nil, exception: false).should == nil
+ end
+ end
+ end
+
+ it "freezes its result" do
+ Rational(1).frozen?.should == true
+ end
+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..7caec6eda5
--- /dev/null
+++ b/spec/ruby/core/kernel/String_spec.rb
@@ -0,0 +1,106 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+
+ -> { @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
+
+ -> { @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
+
+ -> { @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)
+ -> { @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..3059ea8b57
--- /dev/null
+++ b/spec/ruby/core/kernel/__callee___spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/__callee__'
+
+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..242adbf48b
--- /dev/null
+++ b/spec/ruby/core/kernel/__dir___spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+
+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
+
+ it "returns the expanded path of the directory when used in the main script" do
+ fixtures_dir = File.dirname(fixture(__FILE__, '__dir__.rb'))
+ Dir.chdir(fixtures_dir) do
+ ruby_exe("__dir__.rb").should == "__dir__.rb\n#{fixtures_dir}\n"
+ end
+ end
+
+ context "when used in eval with a given filename" do
+ it "returns File.dirname(filename)" do
+ eval("__dir__", nil, "foo.rb").should == "."
+ eval("__dir__", nil, "foo/bar.rb").should == "foo"
+ end
+ end
+
+ context "when used in eval with top level binding" do
+ it "returns nil" do
+ eval("__dir__", binding).should == nil
+ 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..578d25640d
--- /dev/null
+++ b/spec/ruby/core/kernel/__method___spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/__method__'
+
+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..f8152718c5
--- /dev/null
+++ b/spec/ruby/core/kernel/abort_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/process/abort'
+
+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..ebd9a71d15
--- /dev/null
+++ b/spec/ruby/core/kernel/at_exit_spec.rb
@@ -0,0 +1,19 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/kernel/at_exit'
+
+describe "Kernel.at_exit" do
+ it_behaves_like :kernel_at_exit, :at_exit
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:at_exit)
+ end
+
+ it "raises ArgumentError if called without a block" do
+ -> { at_exit }.should raise_error(ArgumentError, "called without a block")
+ 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..5edb70541d
--- /dev/null
+++ b/spec/ruby/core/kernel/autoload_spec.rb
@@ -0,0 +1,178 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# 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")
+define_autoload_KSAutoloadCallsRequire = -> {
+ autoload :KSAutoloadCallsRequire, "main_autoload_not_exist.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 "calls main.require(path) to load the file" do
+ define_autoload_KSAutoloadCallsRequire.call
+ main = TOPLEVEL_BINDING.eval("self")
+ main.should_receive(:require).with("main_autoload_not_exist.rb")
+ # The constant won't be defined since require is mocked to do nothing
+ -> { KSAutoloadCallsRequire }.should raise_error(NameError)
+ 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 "inside a Class.new method body" do
+ # NOTE: this spec is being discussed in https://github.com/ruby/spec/pull/839
+ it "should define on the new anonymous class" do
+ cls = Class.new do
+ def go
+ autoload :Object, 'bogus'
+ autoload? :Object
+ end
+ end
+
+ cls.new.go.should == 'bogus'
+ cls.autoload?(:Object).should == 'bogus'
+ end
+ end
+
+ describe "when Object is frozen" do
+ it "raises a FrozenError before defining the constant" do
+ ruby_exe(fixture(__FILE__, "autoload_frozen.rb")).should == "FrozenError - nil"
+ end
+ end
+
+ describe "when called from included module's method" do
+ before :all do
+ @path = fixture(__FILE__, "autoload_from_included_module.rb")
+ KernelSpecs::AutoloadMethodIncluder.new.setup_autoload(@path)
+ end
+
+ it "setups the autoload on the included module" do
+ KernelSpecs::AutoloadMethod.autoload?(:AutoloadFromIncludedModule).should == @path
+ end
+
+ it "the autoload is reachable from the class too" do
+ KernelSpecs::AutoloadMethodIncluder.autoload?(:AutoloadFromIncludedModule).should == @path
+ end
+
+ it "the autoload relative to the included module works" do
+ KernelSpecs::AutoloadMethod::AutoloadFromIncludedModule.loaded.should == :autoload_from_included_module
+ 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
+
+ describe "when called from included module's method" do
+ before :all do
+ @path = fixture(__FILE__, "autoload_from_included_module2.rb")
+ KernelSpecs::AutoloadMethodIncluder2.new.setup_autoload(@path)
+ end
+
+ it "setups the autoload on the included module" do
+ KernelSpecs::AutoloadMethod2.autoload?(:AutoloadFromIncludedModule2).should == @path
+ end
+
+ it "the autoload is reachable from the class too" do
+ KernelSpecs::AutoloadMethodIncluder2.autoload?(:AutoloadFromIncludedModule2).should == @path
+ end
+
+ it "the autoload relative to the included module works" do
+ KernelSpecs::AutoloadMethod2::AutoloadFromIncludedModule2.loaded.should == :autoload_from_included_module2
+ end
+ 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..834d5636c1
--- /dev/null
+++ b/spec/ruby/core/kernel/backtick_spec.rb
@@ -0,0 +1,84 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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"')
+ -> {
+ `#{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
+ -> { `nonexistent_command` }.should raise_error(Errno::ENOENT)
+ end
+
+ platform_is_not :windows do
+ it "handles invalid UTF-8 bytes in command" do
+ `echo "testing\xC2 a non UTF-8 string"`.b.should == "testing\xC2 a non UTF-8 string\n".b
+ end
+
+ it "sets $? to the exit status of the executed sub-process" do
+ ip = 'world'
+ `echo disc #{ip}`
+ $?.should be_kind_of(Process::Status)
+ $?.should_not.stopped?
+ $?.should.exited?
+ $?.exitstatus.should == 0
+ $?.should.success?
+ `echo disc #{ip}; exit 99`
+ $?.should be_kind_of(Process::Status)
+ $?.should_not.stopped?
+ $?.should.exited?
+ $?.exitstatus.should == 99
+ $?.should_not.success?
+ 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)
+ $?.should_not.stopped?
+ $?.should.exited?
+ $?.exitstatus.should == 0
+ $?.should.success?
+ `echo disc #{ip}& exit 99`
+ $?.should be_kind_of(Process::Status)
+ $?.should_not.stopped?
+ $?.should.exited?
+ $?.exitstatus.should == 99
+ $?.should_not.success?
+ 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..f1c9c6ec9f
--- /dev/null
+++ b/spec/ruby/core/kernel/binding_spec.rb
@@ -0,0 +1,51 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+ -> { 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..aece4c821d
--- /dev/null
+++ b/spec/ruby/core/kernel/block_given_spec.rb
@@ -0,0 +1,43 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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_inside_block {}.should == true
+ @object.accept_block_as_argument_inside_block {}.should == true
+
+ @object.accept_block.should == false
+ @object.accept_block_as_argument.should == false
+ @object.accept_block_inside_block.should == false
+ @object.accept_block_as_argument_inside_block.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
+ @object.defined_block_inside_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..a917dba504
--- /dev/null
+++ b/spec/ruby/core/kernel/caller_locations_spec.rb
@@ -0,0 +1,110 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/caller_locations'
+
+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.should_not.empty?
+ end
+
+ it 'returns an Array of caller locations using a custom offset' do
+ locations = KernelSpecs::CallerLocationsTest.locations(2)
+
+ locations[0].absolute_path.should.end_with?('mspec.rb')
+ 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 "can be called with a range" do
+ locations1 = caller_locations(0)
+ locations2 = caller_locations(2..4)
+ locations1[2..4].map(&:to_s).should == locations2.map(&:to_s)
+ end
+
+ it "works with endless ranges" do
+ locations1 = caller_locations(0)
+ locations2 = caller_locations(eval("(2..)"))
+ locations2.map(&:to_s).should == locations1[2..-1].map(&:to_s)
+ end
+
+ it "works with beginless ranges" do
+ locations1 = caller_locations(0)
+ locations2 = caller_locations((...5))
+ locations2.map(&:to_s)[eval("(2..)")].should == locations1[(...5)].map(&:to_s)[eval("(2..)")]
+ end
+
+ it "can be called with a range whose end is negative" do
+ locations1 = caller_locations(0)
+ locations2 = caller_locations(2..-1)
+ locations3 = caller_locations(2..-2)
+ locations1[2..-1].map(&:to_s).should == locations2.map(&:to_s)
+ locations1[2..-2].map(&:to_s).should == locations3.map(&:to_s)
+ end
+
+ it "must return nil if omitting more locations than available" do
+ caller_locations(100).should == nil
+ caller_locations(100..-1).should == nil
+ end
+
+ it "must return [] if omitting exactly the number of locations available" do
+ omit = caller_locations(0).length
+ caller_locations(omit).should == []
+ 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
+
+ it "must return the same locations when called with 1..-1 and when called with no arguments" do
+ caller_locations.map(&:to_s).should == caller_locations(1..-1).map(&:to_s)
+ end
+
+ guard -> { Kernel.instance_method(:tap).source_location } do
+ ruby_version_is ""..."3.4" do
+ it "includes core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ loc = nil
+ tap { loc = caller_locations(1, 1)[0] }
+ loc.label.should == "tap"
+ loc.path.should.start_with? "<internal:"
+ end
+ end
+
+ ruby_version_is "3.4"..."4.0" do
+ it "includes core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ loc = nil
+ tap { loc = caller_locations(1, 1)[0] }
+ loc.label.should == "Kernel#tap"
+ loc.path.should.start_with? "<internal:"
+ end
+ end
+
+ ruby_version_is "4.0" do
+ it "does not include core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ loc = nil
+ tap { loc = caller_locations(1, 1)[0] }
+ loc.label.should == "Kernel#tap"
+ loc.path.should == __FILE__
+ end
+ 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..7cd703de5a
--- /dev/null
+++ b/spec/ruby/core/kernel/caller_spec.rb
@@ -0,0 +1,109 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/caller'
+
+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.should_not.empty?
+ 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
+
+ it "returns an Array with the block given to #at_exit at the base of the stack" do
+ path = fixture(__FILE__, "caller_at_exit.rb")
+ lines = ruby_exe(path).lines
+ lines.size.should == 2
+ lines[0].should =~ /\A#{path}:6:in [`'](?:Object#)?foo'\n\z/
+ lines[1].should =~ /\A#{path}:2:in [`']block in <main>'\n\z/
+ end
+
+ it "can be called with a range" do
+ locations1 = caller(0)
+ locations2 = caller(2..4)
+ locations1[2..4].should == locations2
+ end
+
+ it "works with endless ranges" do
+ locations1 = KernelSpecs::CallerTest.locations(0)
+ locations2 = KernelSpecs::CallerTest.locations(eval("(2..)"))
+ locations2.should == locations1[2..-1]
+ end
+
+ it "works with beginless ranges" do
+ locations1 = KernelSpecs::CallerTest.locations(0)
+ locations2 = KernelSpecs::CallerTest.locations((..5))
+ locations2[eval("(2..)")].should == locations1[(..5)][eval("(2..)")]
+ end
+
+ it "can be called with a range whose end is negative" do
+ locations1 = caller(0)
+ locations2 = caller(2..-1)
+ locations3 = caller(2..-2)
+ locations1[2..-1].should == locations2
+ locations1[2..-2].should == locations3
+ end
+
+ it "must return nil if omitting more locations than available" do
+ caller(100).should == nil
+ caller(100..-1).should == nil
+ end
+
+ it "must return [] if omitting exactly the number of locations available" do
+ omit = caller(0).length
+ caller(omit).should == []
+ end
+
+ it "must return the same locations when called with 1..-1 and when called with no arguments" do
+ caller.should == caller(1..-1)
+ end
+
+ guard -> { Kernel.instance_method(:tap).source_location } do
+ ruby_version_is ""..."4.0" do
+ it "includes core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ loc = nil
+ tap { loc = caller(1, 1)[0] }
+ loc.should =~ /\A<internal:.*in [`'](?:Kernel#)?tap'\z/
+ end
+ end
+
+ ruby_version_is "4.0" do
+ it "includes core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ loc = nil
+ tap { loc = caller(1, 1)[0] }
+ loc.should =~ /\A#{ __FILE__ }:.*in [`'](?:Kernel#)?tap'\z/
+ end
+ end
+ 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..b8d30960e8
--- /dev/null
+++ b/spec/ruby/core/kernel/case_compare_spec.rb
@@ -0,0 +1,135 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+
+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..9f59d3b384
--- /dev/null
+++ b/spec/ruby/core/kernel/catch_spec.rb
@@ -0,0 +1,127 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+ -> { catch("exit") { throw :exit } }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError if a String with different identity is thrown" do
+ -> { catch("exit".dup) { throw "exit".dup } }.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
+ -> { 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..d30e77c35a
--- /dev/null
+++ b/spec/ruby/core/kernel/chomp_spec.rb
@@ -0,0 +1,65 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+
+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
diff --git a/spec/ruby/core/kernel/chop_spec.rb b/spec/ruby/core/kernel/chop_spec.rb
new file mode 100644
index 0000000000..9b91c011bc
--- /dev/null
+++ b/spec/ruby/core/kernel/chop_spec.rb
@@ -0,0 +1,53 @@
+# -*- encoding: utf-8 -*-
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+
+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
diff --git a/spec/ruby/core/kernel/class_spec.rb b/spec/ruby/core/kernel/class_spec.rb
new file mode 100644
index 0000000000..b1d9df1671
--- /dev/null
+++ b/spec/ruby/core/kernel/class_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#class" do
+ it "returns the class of the object" do
+ Object.new.class.should equal(Object)
+
+ 1.class.should equal(Integer)
+ 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..5adcbbe603
--- /dev/null
+++ b/spec/ruby/core/kernel/clone_spec.rb
@@ -0,0 +1,177 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/dup_clone'
+
+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
+
+ describe "with no arguments" do
+ it "copies frozen state from the original" do
+ o2 = @obj.clone
+ o2.should_not.frozen?
+
+ @obj.freeze
+ o3 = @obj.clone
+ o3.should.frozen?
+ end
+
+ it 'copies frozen?' do
+ o = ''.freeze.clone
+ o.frozen?.should be_true
+ end
+ end
+
+ describe "with freeze: nil" do
+ it "copies frozen state from the original, like #clone without arguments" do
+ o2 = @obj.clone(freeze: nil)
+ o2.should_not.frozen?
+
+ @obj.freeze
+ o3 = @obj.clone(freeze: nil)
+ o3.should.frozen?
+ end
+
+ it "copies frozen?" do
+ o = "".freeze.clone(freeze: nil)
+ o.frozen?.should be_true
+ end
+ end
+
+ describe "with freeze: true" do
+ it 'makes a frozen copy if the original is frozen' do
+ @obj.freeze
+ @obj.clone(freeze: true).should.frozen?
+ end
+
+ it 'freezes the copy even if the original was not frozen' do
+ @obj.clone(freeze: true).should.frozen?
+ end
+
+ it "calls #initialize_clone with kwargs freeze: true" do
+ obj = KernelSpecs::CloneFreeze.new
+ obj.clone(freeze: true)
+ ScratchPad.recorded.should == [obj, { freeze: true }]
+ end
+
+ it "calls #initialize_clone with kwargs freeze: true even if #initialize_clone only takes a single argument" do
+ obj = KernelSpecs::Clone.new
+ -> { obj.clone(freeze: true) }.should raise_error(ArgumentError, 'wrong number of arguments (given 2, expected 1)')
+ end
+ end
+
+ describe "with freeze: false" do
+ it 'does not freeze the copy if the original is frozen' do
+ @obj.freeze
+ @obj.clone(freeze: false).should_not.frozen?
+ end
+
+ it 'does not freeze the copy if the original is not frozen' do
+ @obj.clone(freeze: false).should_not.frozen?
+ end
+
+ it "calls #initialize_clone with kwargs freeze: false" do
+ obj = KernelSpecs::CloneFreeze.new
+ obj.clone(freeze: false)
+ ScratchPad.recorded.should == [obj, { freeze: false }]
+ end
+
+ it "calls #initialize_clone with kwargs freeze: false even if #initialize_clone only takes a single argument" do
+ obj = KernelSpecs::Clone.new
+ -> { obj.clone(freeze: false) }.should raise_error(ArgumentError, 'wrong number of arguments (given 2, expected 1)')
+ end
+ end
+
+ describe "with freeze: anything else" do
+ it 'raises ArgumentError when passed not true/false/nil' do
+ -> { @obj.clone(freeze: 1) }.should raise_error(ArgumentError, /unexpected value for freeze: Integer/)
+ -> { @obj.clone(freeze: "") }.should raise_error(ArgumentError, /unexpected value for freeze: String/)
+ -> { @obj.clone(freeze: Object.new) }.should raise_error(ArgumentError, /unexpected value for freeze: Object/)
+ 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
+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..affdc5c00d
--- /dev/null
+++ b/spec/ruby/core/kernel/comparison_spec.rb
@@ -0,0 +1,31 @@
+require_relative '../../spec_helper'
+
+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..24acec84f5
--- /dev/null
+++ b/spec/ruby/core/kernel/define_singleton_method_spec.rb
@@ -0,0 +1,120 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+ ->{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
+ ->{ 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, &-> { 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
+ -> {
+ Class.new { define_singleton_method(:test, "self") }
+ }.should raise_error(TypeError)
+
+ -> {
+ 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!"
+ -> {
+ 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
+ -> {
+ obj.define_singleton_method(:test)
+ }.should raise_error(ArgumentError)
+ end
+
+ 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
+
+ -> {
+ o.define(:foo) { raise "not used" }
+ }.should raise_error(ArgumentError)
+ end
+
+ it "always defines the method with public visibility" do
+ cls = Class.new
+ def cls.define(name, &block)
+ private
+ define_singleton_method(name, &block)
+ end
+
+ -> {
+ suppress_warning do
+ cls.define(:foo) { :ok }
+ end
+ cls.foo.should == :ok
+ }.should_not raise_error(NoMethodError)
+ end
+
+ it "cannot define a singleton method with a frozen singleton class" do
+ o = Object.new
+ o.freeze
+ -> { o.define_singleton_method(:foo) { 1 } }.should raise_error(FrozenError)
+ 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..9d429a9fac
--- /dev/null
+++ b/spec/ruby/core/kernel/display_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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..70198abdb7
--- /dev/null
+++ b/spec/ruby/core/kernel/dup_spec.rb
@@ -0,0 +1,67 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/dup_clone'
+
+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.should_not.frozen?
+ 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
+ -> { 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
+ -> { 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
+ -> { 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..0092e20468
--- /dev/null
+++ b/spec/ruby/core/kernel/enum_for_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+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..e62a601a79
--- /dev/null
+++ b/spec/ruby/core/kernel/eql_spec.rb
@@ -0,0 +1,10 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/kernel/equal'
+
+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..2be151263d
--- /dev/null
+++ b/spec/ruby/core/kernel/equal_value_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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..e027294347
--- /dev/null
+++ b/spec/ruby/core/kernel/eval_spec.rb
@@ -0,0 +1,570 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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 constants 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 {}
+
+ -> { eval("x", bind) }.should raise_error(TypeError)
+ end
+
+ it "does not make Proc locals visible to evaluated code" do
+ bind = proc { inner = 4 }
+ -> { 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'
+ -> {
+ 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'
+ -> {
+ 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
+
+ ruby_version_is ""..."3.3" do
+ it "uses (eval) filename if none is provided" do
+ eval("__FILE__").should == "(eval)"
+ eval("__FILE__", binding).should == "(eval)"
+ eval("__FILE__", binding, "success").should == "success"
+ eval("eval '__FILE__', binding").should == "(eval)"
+ eval("eval '__FILE__', binding", binding).should == "(eval)"
+ eval("eval '__FILE__', binding", binding, 'success').should == '(eval)'
+ eval("eval '__FILE__', binding, 'success'", binding).should == 'success'
+ end
+
+ it 'uses (eval) for __FILE__ and 1 for __LINE__ with a binding argument' do
+ eval("[__FILE__, __LINE__]", binding).should == ["(eval)", 1]
+ end
+ end
+
+ context "parameter forwarding" do
+ it "allows anonymous rest parameter forwarding" do
+ object = Object.new
+ def object.foo(a, b, c)
+ [a, b, c]
+ end
+ def object.bar(*)
+ eval "foo(*)"
+ end
+
+ object.bar(1, 2, 3).should == [1, 2, 3]
+ end
+
+ it "allows anonymous keyword parameters forwarding" do
+ object = Object.new
+ def object.foo(a:, b:, c:)
+ [a, b, c]
+ end
+ def object.bar(**)
+ eval "foo(**)"
+ end
+
+ object.bar(a: 1, b: 2, c: 3).should == [1, 2, 3]
+ end
+
+ it "allows anonymous block parameter forwarding" do
+ object = Object.new
+ def object.foo(&block)
+ block.call
+ end
+ def object.bar(&)
+ eval "foo(&)"
+ end
+
+ object.bar { :foobar }.should == :foobar
+ end
+
+ it "allows ... forwarding" do
+ object = Object.new
+ def object.foo(a, b:, &block)
+ [a, b, block.call]
+ end
+ def object.bar(...)
+ eval "foo(...)"
+ end
+
+ object.bar(1, b: 2) { 3 }.should == [1, 2, 3]
+ end
+
+ it "allows parameter forwarding to super" do
+ m = Module.new do
+ def foo(a, b:, &block)
+ [a, b, block.call]
+ end
+ end
+
+ c = Class.new do
+ include m
+
+ def foo(a, b:, &block)
+ eval "super"
+ end
+ end
+
+ object = c.new
+ object.foo(1, b: 2) { 3 }.should == [1, 2, 3]
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "uses (eval at __FILE__:__LINE__) if none is provided" do
+ eval("__FILE__").should == "(eval at #{__FILE__}:#{__LINE__})"
+ eval("__FILE__", binding).should == "(eval at #{__FILE__}:#{__LINE__})"
+ eval("__FILE__", binding, "success").should == "success"
+ eval("eval '__FILE__', binding").should == "(eval at (eval at #{__FILE__}:#{__LINE__}):1)"
+ eval("eval '__FILE__', binding", binding).should == "(eval at (eval at #{__FILE__}:#{__LINE__}):1)"
+ eval("eval '__FILE__', binding", binding, 'success').should == "(eval at success:1)"
+ eval("eval '__FILE__', binding, 'success'", binding).should == 'success'
+ end
+
+ it 'uses (eval at __FILE__:__LINE__) for __FILE__ and 1 for __LINE__ with a binding argument' do
+ eval("[__FILE__, __LINE__]", binding).should == ["(eval at #{__FILE__}:#{__LINE__})", 1]
+ end
+ 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
+ -> {
+ eval('KernelSpecs::EvalTest.call_yield') { "content" }
+ }.should raise_error(LocalJumpError)
+ end
+
+ it "returns from the scope calling #eval when evaluating 'return'" do
+ -> { eval("return :eval") }.call.should == :eval
+ end
+
+ it "returns from the method calling #eval when evaluating 'return'" do
+ def eval_return(n)
+ eval("return n*2")
+ end
+ -> { eval_return(3) }.call.should == 6
+ end
+
+ it "returns from the method calling #eval when evaluating 'return' in BEGIN" do
+ def eval_return(n)
+ eval("BEGIN {return n*3}")
+ end
+ -> { eval_return(4) }.call.should == 12
+ 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
+
+ it "can be called with Method#call" do
+ method(:eval).call("2 * 3").should == 6
+ end
+
+ it "has the correct default definee when called through Method#call" do
+ class EvalSpecs
+ method(:eval).call("def eval_spec_method_call; end")
+ EvalSpecs.should have_instance_method(:eval_spec_method_call)
+ end
+ end
+
+ it "makes flip-flop operator work correctly" do
+ ScratchPad.record []
+
+ eval "10.times { |i| ScratchPad << i if (i == 4)...(i == 4) }"
+ ScratchPad.recorded.should == [4, 5, 6, 7, 8, 9]
+
+ ScratchPad.clear
+ end
+
+ it "returns nil if given an empty string" do
+ eval("").should == nil
+ end
+
+ context "with shebang" do
+ it "ignores shebang with ruby interpreter" do
+ pid = eval(<<~CODE.b)
+ #!/usr/bin/env ruby
+ Process.pid
+ CODE
+
+ pid.should == Process.pid
+ end
+
+ it "ignores shebang with non-ruby interpreter" do
+ pid = eval(<<~CODE.b)
+ #!/usr/bin/env puma
+ Process.pid
+ CODE
+
+ pid.should == Process.pid
+ end
+ end
+
+ # See language/magic_comment_spec.rb for more magic comments specs
+ describe "with a magic encoding comment" do
+ it "uses the magic comment encoding for the encoding of literal strings" do
+ code = "# encoding: UTF-8\n'é'.encoding".b
+ code.encoding.should == Encoding::BINARY
+ eval(code).should == Encoding::UTF_8
+ end
+
+ it "uses the magic comment encoding for parsing constants" do
+ code = <<CODE.b
+# encoding: UTF-8
+class EvalSpecs
+ Vπ = 3.14
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should include(:"Vπ")
+ EvalSpecs::Vπ.should == 3.14
+ ensure
+ EvalSpecs.send(:remove_const, :Vπ)
+ end
+
+ it "allows an emacs-style magic comment encoding" do
+ code = <<CODE.b
+# -*- encoding: UTF-8 -*-
+class EvalSpecs
+Vπemacs = 3.14
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should include(:"Vπemacs")
+ EvalSpecs::Vπemacs.should == 3.14
+ ensure
+ EvalSpecs.send(:remove_const, :Vπemacs)
+ end
+
+ it "allows spaces before the magic encoding comment" do
+ code = <<CODE.b
+\t \t # encoding: UTF-8
+class EvalSpecs
+ Vπspaces = 3.14
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should include(:"Vπspaces")
+ EvalSpecs::Vπspaces.should == 3.14
+ ensure
+ EvalSpecs.send(:remove_const, :Vπspaces)
+ end
+
+ it "allows a shebang line before the magic encoding comment" do
+ code = <<CODE.b
+#!/usr/bin/env ruby
+# encoding: UTF-8
+class EvalSpecs
+ Vπshebang = 3.14
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should include(:"Vπshebang")
+ EvalSpecs::Vπshebang.should == 3.14
+ ensure
+ EvalSpecs.send(:remove_const, :Vπshebang)
+ end
+
+ it "allows a shebang line and some spaces before the magic encoding comment" do
+ code = <<CODE.b
+#!/usr/bin/env ruby
+ # encoding: UTF-8
+class EvalSpecs
+ Vπshebang_spaces = 3.14
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should include(:"Vπshebang_spaces")
+ EvalSpecs::Vπshebang_spaces.should == 3.14
+ ensure
+ EvalSpecs.send(:remove_const, :Vπshebang_spaces)
+ end
+
+ it "allows a magic encoding comment and a subsequent frozen_string_literal magic comment" do
+ frozen_string_default = "test".frozen?
+
+ code = <<CODE.b
+# encoding: UTF-8
+# frozen_string_literal: #{!frozen_string_default}
+class EvalSpecs
+ Vπstring = "frozen"
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should include(:"Vπstring")
+ EvalSpecs::Vπstring.should == "frozen"
+ EvalSpecs::Vπstring.encoding.should == Encoding::UTF_8
+ EvalSpecs::Vπstring.frozen?.should == !frozen_string_default
+ ensure
+ EvalSpecs.send(:remove_const, :Vπstring)
+ end
+
+ it "allows a magic encoding comment and a frozen_string_literal magic comment on the same line in emacs style" do
+ code = <<CODE.b
+# -*- encoding: UTF-8; frozen_string_literal: true -*-
+class EvalSpecs
+Vπsame_line = "frozen"
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should include(:"Vπsame_line")
+ EvalSpecs::Vπsame_line.should == "frozen"
+ EvalSpecs::Vπsame_line.encoding.should == Encoding::UTF_8
+ EvalSpecs::Vπsame_line.frozen?.should be_true
+ ensure
+ EvalSpecs.send(:remove_const, :Vπsame_line)
+ end
+
+ it "ignores the magic encoding comment if it is after a frozen_string_literal magic comment" do
+ frozen_string_default = "test".frozen?
+ code = <<CODE.b
+# frozen_string_literal: #{!frozen_string_default}
+# encoding: UTF-8
+class EvalSpecs
+ Vπfrozen_first = "frozen"
+end
+CODE
+ code.encoding.should == Encoding::BINARY
+ eval(code)
+ EvalSpecs.constants(false).should_not include(:"Vπfrozen_first")
+ binary_constant = "Vπfrozen_first".b.to_sym
+ EvalSpecs.constants(false).should include(binary_constant)
+ value = EvalSpecs.const_get(binary_constant)
+ value.should == "frozen"
+ value.encoding.should == Encoding::BINARY
+ value.frozen?.should == !frozen_string_default
+ ensure
+ EvalSpecs.send(:remove_const, binary_constant)
+ end
+
+ it "ignores the frozen_string_literal magic comment if it appears after a token and warns if $VERBOSE is true" do
+ frozen_string_default = "test".frozen?
+ code = <<CODE
+some_token_before_magic_comment = :anything
+# frozen_string_literal: #{!frozen_string_default}
+class EvalSpecs
+ Vπstring_not_frozen = "not frozen"
+end
+CODE
+ -> { eval(code) }.should complain(/warning: [`']frozen_string_literal' is ignored after any tokens/, verbose: true)
+ EvalSpecs::Vπstring_not_frozen.frozen?.should == frozen_string_default
+ EvalSpecs.send :remove_const, :Vπstring_not_frozen
+
+ -> { eval(code) }.should_not complain(verbose: false)
+ EvalSpecs::Vπstring_not_frozen.frozen?.should == frozen_string_default
+ EvalSpecs.send :remove_const, :Vπstring_not_frozen
+ end
+ end
+
+ describe 'with refinements' do
+ it "activates refinements from the eval scope" do
+ refinery = Module.new do
+ refine EvalSpecs::A do
+ def foo
+ "bar"
+ end
+ end
+ end
+
+ result = nil
+
+ Module.new do
+ using refinery
+
+ result = eval "EvalSpecs::A.new.foo"
+ end
+
+ result.should == "bar"
+ end
+
+ it "activates refinements from the binding" do
+ refinery = Module.new do
+ refine EvalSpecs::A do
+ def foo
+ "bar"
+ end
+ end
+ end
+
+ b = nil
+ m = Module.new do
+ using refinery
+ b = binding
+ end
+
+ result = eval "EvalSpecs::A.new.foo", b
+
+ result.should == "bar"
+ end
+ 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..3d9520ad67
--- /dev/null
+++ b/spec/ruby/core/kernel/exec_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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"').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"').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..93cec3fee5
--- /dev/null
+++ b/spec/ruby/core/kernel/exit_spec.rb
@@ -0,0 +1,27 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/process/exit'
+
+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_behaves_like :process_exit, :exit, Kernel
+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
diff --git a/spec/ruby/core/kernel/extend_spec.rb b/spec/ruby/core/kernel/extend_spec.rb
new file mode 100644
index 0000000000..6342d8cae1
--- /dev/null
+++ b/spec/ruby/core/kernel/extend_spec.rb
@@ -0,0 +1,91 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+ c = Class.new do
+ extend KernelSpecs::M
+ end
+ (c.kind_of? KernelSpecs::M).should == true
+ end
+
+ it "raises an ArgumentError when no arguments given" do
+ -> { 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
+ -> { 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
+ -> { @frozen.extend }.should raise_error(ArgumentError)
+ end
+
+ it "raises a FrozenError" do
+ -> { @frozen.extend @module }.should raise_error(FrozenError)
+ end
+ end
+
+ it "updated class methods of a module when it extends self and includes another module" do
+ a = Module.new do
+ extend self
+ end
+ b = Module.new do
+ def foo; :foo; end
+ end
+
+ a.include b
+ a.foo.should == :foo
+ 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..fab622037e
--- /dev/null
+++ b/spec/ruby/core/kernel/fail_spec.rb
@@ -0,0 +1,42 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#fail" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:fail)
+ end
+
+ it "raises a RuntimeError" do
+ -> { fail }.should raise_error(RuntimeError)
+ end
+
+ it "accepts an Object with an exception method returning an Exception" do
+ obj = Object.new
+ def obj.exception(msg)
+ StandardError.new msg
+ end
+ -> { fail obj, "..." }.should raise_error(StandardError, "...")
+ end
+
+ it "instantiates the specified exception class" do
+ error_class = Class.new(RuntimeError)
+ -> { fail error_class }.should raise_error(error_class)
+ end
+
+ it "uses the specified message" do
+ -> {
+ 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/Complex.rb b/spec/ruby/core/kernel/fixtures/Complex.rb
new file mode 100644
index 0000000000..bf14d55ad5
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/Complex.rb
@@ -0,0 +1,5 @@
+module KernelSpecs
+ def self.Complex_method(string)
+ Complex(string)
+ end
+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/__dir__.rb b/spec/ruby/core/kernel/fixtures/__dir__.rb
new file mode 100644
index 0000000000..bf9a15e3c8
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/__dir__.rb
@@ -0,0 +1,2 @@
+puts __FILE__
+puts __dir__
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_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_from_included_module.rb b/spec/ruby/core/kernel/fixtures/autoload_from_included_module.rb
new file mode 100644
index 0000000000..f5bedc6992
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/autoload_from_included_module.rb
@@ -0,0 +1,9 @@
+module KernelSpecs
+ module AutoloadMethod
+ module AutoloadFromIncludedModule
+ def self.loaded
+ :autoload_from_included_module
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/fixtures/autoload_from_included_module2.rb b/spec/ruby/core/kernel/fixtures/autoload_from_included_module2.rb
new file mode 100644
index 0000000000..f4f1cfbf7c
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/autoload_from_included_module2.rb
@@ -0,0 +1,9 @@
+module KernelSpecs
+ module AutoloadMethod2
+ module AutoloadFromIncludedModule2
+ def self.loaded
+ :autoload_from_included_module2
+ end
+ end
+ 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_at_exit.rb b/spec/ruby/core/kernel/fixtures/caller_at_exit.rb
new file mode 100644
index 0000000000..ca9d808597
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/caller_at_exit.rb
@@ -0,0 +1,7 @@
+at_exit {
+ foo
+}
+
+def foo
+ puts caller(0)
+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..0e2b81988f
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/classes.rb
@@ -0,0 +1,558 @@
+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, "-W0", "-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 MyPrependedModule; end
+ module MyExtensionModule; end
+
+ class AncestorClass < String
+ include AncestorModule
+ end
+
+ class InstanceClass < AncestorClass
+ include MyModule
+ end
+
+ class KindaClass < AncestorClass
+ include MyModule
+ prepend MyPrependedModule
+
+ 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
+
+ def self.accept_block_inside_block()
+ yield_self {
+ block_given?
+ }
+ end
+
+ def self.accept_block_as_argument_inside_block(&block)
+ yield_self {
+ block_given?
+ }
+ end
+
+ class << self
+ define_method(:defined_block) do
+ block_given?
+ end
+
+ define_method(:defined_block_inside_block) do
+ yield_self {
+ 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
+
+ def self.accept_block_inside_block
+ yield_self {
+ self.send(:block_given?)
+ }
+ end
+
+ def self.accept_block_as_argument_inside_block(&block)
+ yield_self {
+ self.send(:block_given?)
+ }
+ end
+
+ class << self
+ define_method(:defined_block) do
+ self.send(:block_given?)
+ end
+
+ define_method(:defined_block_inside_block) do
+ yield_self {
+ 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
+
+ def self.accept_block_inside_block
+ yield_self {
+ Kernel.block_given?
+ }
+ end
+
+ def self.accept_block_as_argument_inside_block(&block)
+ yield_self {
+ Kernel.block_given?
+ }
+ end
+
+ class << self
+ define_method(:defined_block) do
+ Kernel.block_given?
+ end
+
+ define_method(:defined_block_inside_block) do
+ yield_self {
+ 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, **kw)
+ ScratchPad.record object_id
+ end
+
+ # define to support calling #clone with optional :freeze keyword argument
+ def initialize_clone(other, **kw)
+ super(other) # to call #initialize_copy
+ end
+ end
+
+ class Clone
+ def initialize_clone(other)
+ ScratchPad.record other
+ end
+ end
+
+ class CloneFreeze
+ def initialize_clone(other, **kwargs)
+ ScratchPad.record([other, kwargs])
+ 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 = -> x { x.call }
+
+ pr.call(b)
+
+ # We shouldn't be here, b should have unwinded through
+ return :bad
+ end
+ end
+
+ module LambdaSpecs
+ module ZSuper
+ def lambda
+ super
+ end
+ end
+
+ class ForwardBlockWithZSuper
+ prepend(ZSuper)
+ end
+
+ module Ampersand
+ def lambda(&block)
+ suppress_warning {super(&block)}
+ end
+ end
+
+ class SuperAmpersand
+ prepend(Ampersand)
+ 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: #{method.inspect}"
+ end
+ end
+
+ def method_missing(method, *args)
+ raise "the method name should be a Symbol" unless Symbol === method
+ "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
+
+ module AutoloadMethod
+ def setup_autoload(file)
+ autoload :AutoloadFromIncludedModule, file
+ end
+ end
+
+ class AutoloadMethodIncluder
+ include AutoloadMethod
+ end
+
+ module AutoloadMethod2
+ def setup_autoload(file)
+ Kernel.autoload :AutoloadFromIncludedModule2, file
+ end
+ end
+
+ class AutoloadMethodIncluder2
+ include AutoloadMethod2
+ end
+
+ class WarnInNestedCall
+ def f4(s = "", n)
+ f3(s, n)
+ end
+
+ def f3(s, n)
+ f2(s, n)
+ end
+
+ def f2(s, n)
+ f1(s, n)
+ end
+
+ def f1(s, n)
+ warn(s, uplevel: n)
+ end
+
+ def warn_call_lineno; method(:f1).source_location[1] + 1; end
+ def f1_call_lineno; method(:f2).source_location[1] + 1; end
+ def f2_call_lineno; method(:f3).source_location[1] + 1; end
+ def f3_call_lineno; method(:f4).source_location[1] + 1; end
+ end
+
+ CustomRangeInteger = Struct.new(:value) do
+ def to_int; value; end
+ def <=>(other); to_int <=> other.to_int; end
+ def -(other); self.class.new(to_int - other.to_int); end
+ def +(other); self.class.new(to_int + other.to_int); end
+ end
+
+ CustomRangeFloat = Struct.new(:value) do
+ def to_f; value; end
+ def <=>(other); to_f <=> other.to_f; end
+ def -(other); to_f - other.to_f; end
+ def +(other); self.class.new(to_f + other.to_f); 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
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..9e2d045bf3
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/eval_return_with_lambda.rb
@@ -0,0 +1,12 @@
+print "a,"
+x = -> 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/singleton_methods.rb b/spec/ruby/core/kernel/fixtures/singleton_methods.rb
new file mode 100644
index 0000000000..32ea0adf89
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/singleton_methods.rb
@@ -0,0 +1,13 @@
+module SingletonMethodsSpecs
+ module Prepended
+ def mspec_test_kernel_singleton_methods
+ end
+ public :mspec_test_kernel_singleton_methods
+ end
+
+ ::Module.prepend Prepended
+
+ module SelfExtending
+ extend self
+ end
+end
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/fixtures/warn_core_method.rb b/spec/ruby/core/kernel/fixtures/warn_core_method.rb
new file mode 100644
index 0000000000..fd82562404
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/warn_core_method.rb
@@ -0,0 +1,14 @@
+raise 'should be run without RubyGems' if defined?(Gem)
+
+public def deprecated(n=1)
+ # puts nil, caller(0), nil
+ warn "use X instead", uplevel: n
+end
+
+1.times do # to test with a non-empty stack above the reported locations
+ deprecated
+ tap(&:deprecated)
+ tap { deprecated(2) }
+ # eval sources with a <internal: file are also ignored
+ eval "tap(&:deprecated)", nil, "<internal:should-be-skipped-by-warn-uplevel>"
+end
diff --git a/spec/ruby/core/kernel/fixtures/warn_require.rb b/spec/ruby/core/kernel/fixtures/warn_require.rb
new file mode 100644
index 0000000000..c4b0733233
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/warn_require.rb
@@ -0,0 +1 @@
+warn 'warn-require-warning', uplevel: 1
diff --git a/spec/ruby/core/kernel/fixtures/warn_require_caller.rb b/spec/ruby/core/kernel/fixtures/warn_require_caller.rb
new file mode 100644
index 0000000000..35a0f969f9
--- /dev/null
+++ b/spec/ruby/core/kernel/fixtures/warn_require_caller.rb
@@ -0,0 +1,2 @@
+# Use a different line than just 1
+require "#{__dir__}/warn_require"
diff --git a/spec/ruby/core/kernel/fork_spec.rb b/spec/ruby/core/kernel/fork_spec.rb
new file mode 100644
index 0000000000..b37f9980e0
--- /dev/null
+++ b/spec/ruby/core/kernel/fork_spec.rb
@@ -0,0 +1,15 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/process/fork'
+
+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..1d0c000c15
--- /dev/null
+++ b/spec/ruby/core/kernel/format_spec.rb
@@ -0,0 +1,47 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# NOTE: most specs are in sprintf_spec.rb, this is just an alias
+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
+
+ describe "when $VERBOSE is true" do
+ it "warns if too many arguments are passed" do
+ code = <<~RUBY
+ $VERBOSE = true
+ format("test", 1)
+ RUBY
+
+ ruby_exe(code, args: "2>&1").should include("warning: too many arguments for format string")
+ end
+
+ it "does not warns if too many keyword arguments are passed" do
+ code = <<~RUBY
+ $VERBOSE = true
+ format("test %{test}", test: 1, unused: 2)
+ RUBY
+
+ ruby_exe(code, args: "2>&1").should_not include("warning")
+ end
+
+ ruby_bug "#20593", ""..."3.4" do
+ it "doesn't warns if keyword arguments are passed and none are used" do
+ code = <<~RUBY
+ $VERBOSE = true
+ format("test", test: 1)
+ format("test", {})
+ RUBY
+
+ ruby_exe(code, args: "2>&1").should_not include("warning")
+ end
+ end
+ 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..fa32d321cf
--- /dev/null
+++ b/spec/ruby/core/kernel/freeze_spec.rb
@@ -0,0 +1,91 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+
+ describe "on a Complex" do
+ it "has no effect since it is already frozen" do
+ c = Complex(1.3, 3.1)
+ c.frozen?.should be_true
+ c.freeze
+ end
+ end
+
+ describe "on a Rational" do
+ it "has no effect since it is already frozen" do
+ r = Rational(1, 3)
+ r.frozen?.should be_true
+ r.freeze
+ end
+ end
+
+ it "causes mutative calls to raise RuntimeError" do
+ o = Class.new do
+ def mutate; @foo = 1; end
+ end.new
+ o.freeze
+ -> {o.mutate}.should raise_error(RuntimeError)
+ end
+
+ it "causes instance_variable_set to raise RuntimeError" do
+ o = Object.new
+ o.freeze
+ -> {o.instance_variable_set(:@foo, 1)}.should raise_error(RuntimeError)
+ end
+
+ it "freezes an object's singleton class" do
+ o = Object.new
+ c = o.singleton_class
+ c.frozen?.should == false
+ o.freeze
+ c.frozen?.should == true
+ 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..a4cec4263d
--- /dev/null
+++ b/spec/ruby/core/kernel/frozen_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#frozen?" do
+ it "returns true if self is frozen" do
+ o = mock('o')
+ p = mock('p')
+ p.freeze
+ o.should_not.frozen?
+ p.should.frozen?
+ 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
+
+ describe "on a Complex" do
+ it "returns true" do
+ c = Complex(1.3, 3.1)
+ c.frozen?.should be_true
+ end
+
+ it "literal returns true" do
+ c = eval "1.3i"
+ c.frozen?.should be_true
+ end
+ end
+
+ describe "on a Rational" do
+ it "returns true" do
+ r = Rational(1, 3)
+ r.frozen?.should be_true
+ end
+
+ it "literal returns true" do
+ r = eval "1/3r"
+ r.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..104613dbfa
--- /dev/null
+++ b/spec/ruby/core/kernel/gets_spec.rb
@@ -0,0 +1,17 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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..8bce8e25b7
--- /dev/null
+++ b/spec/ruby/core/kernel/global_variables_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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..a0cb9f2a70
--- /dev/null
+++ b/spec/ruby/core/kernel/gsub_spec.rb
@@ -0,0 +1,96 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# 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
+ -> {
+ $_ = 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/initialize_clone_spec.rb b/spec/ruby/core/kernel/initialize_clone_spec.rb
new file mode 100644
index 0000000000..21a90c19f0
--- /dev/null
+++ b/spec/ruby/core/kernel/initialize_clone_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#initialize_clone" do
+ it "is a private instance method" do
+ Kernel.should have_private_instance_method(:initialize_clone)
+ end
+
+ it "returns the receiver" do
+ a = Object.new
+ b = Object.new
+ a.send(:initialize_clone, b).should == a
+ end
+
+ it "calls #initialize_copy" do
+ a = Object.new
+ b = Object.new
+ a.should_receive(:initialize_copy).with(b)
+ a.send(:initialize_clone, b)
+ end
+
+ it "accepts a :freeze keyword argument for obj.clone(freeze: value)" do
+ a = Object.new
+ b = Object.new
+ a.send(:initialize_clone, b, freeze: true).should == a
+ end
+end
diff --git a/spec/ruby/core/kernel/initialize_copy_spec.rb b/spec/ruby/core/kernel/initialize_copy_spec.rb
new file mode 100644
index 0000000000..d71ca9f60f
--- /dev/null
+++ b/spec/ruby/core/kernel/initialize_copy_spec.rb
@@ -0,0 +1,36 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#initialize_copy" do
+ it "returns self" do
+ obj = Object.new
+ obj.send(:initialize_copy, obj).should.equal?(obj)
+ end
+
+ it "does nothing if the argument is the same as the receiver" do
+ obj = Object.new
+ obj.send(:initialize_copy, obj).should.equal?(obj)
+
+ obj = Object.new.freeze
+ obj.send(:initialize_copy, obj).should.equal?(obj)
+
+ 1.send(:initialize_copy, 1).should.equal?(1)
+ end
+
+ it "raises FrozenError if the receiver is frozen" do
+ -> { Object.new.freeze.send(:initialize_copy, Object.new) }.should raise_error(FrozenError)
+ -> { 1.send(:initialize_copy, Object.new) }.should raise_error(FrozenError)
+ end
+
+ it "raises TypeError if the objects are of different class" do
+ klass = Class.new
+ sub = Class.new(klass)
+ a = klass.new
+ b = sub.new
+ message = 'initialize_copy should take same class object'
+ -> { a.send(:initialize_copy, b) }.should raise_error(TypeError, message)
+ -> { b.send(:initialize_copy, a) }.should raise_error(TypeError, message)
+
+ -> { a.send(:initialize_copy, 1) }.should raise_error(TypeError, message)
+ -> { a.send(:initialize_copy, 1.0) }.should raise_error(TypeError, message)
+ end
+end
diff --git a/spec/ruby/core/kernel/initialize_dup_spec.rb b/spec/ruby/core/kernel/initialize_dup_spec.rb
new file mode 100644
index 0000000000..6dff34b7ad
--- /dev/null
+++ b/spec/ruby/core/kernel/initialize_dup_spec.rb
@@ -0,0 +1,20 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#initialize_dup" do
+ it "is a private instance method" do
+ Kernel.should have_private_instance_method(:initialize_dup)
+ end
+
+ it "returns the receiver" do
+ a = Object.new
+ b = Object.new
+ a.send(:initialize_dup, b).should == a
+ end
+
+ it "calls #initialize_copy" do
+ a = Object.new
+ b = Object.new
+ a.should_receive(:initialize_copy).with(b)
+ a.send(:initialize_dup, b)
+ 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..1fa66cab98
--- /dev/null
+++ b/spec/ruby/core/kernel/inspect_spec.rb
@@ -0,0 +1,90 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#inspect" do
+ it "returns a String" do
+ Object.new.inspect.should be_an_instance_of(String)
+ 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
+
+ it "returns a String for an object without #class method" do
+ obj = Object.new
+ class << obj
+ undef_method :class
+ end
+ obj.inspect.should be_kind_of(String)
+ end
+
+ ruby_version_is "4.0" do
+ it "calls #instance_variables_to_inspect private method to know which variables to display" do
+ obj = Object.new
+ obj.instance_eval do
+ @host = "localhost"
+ @user = "root"
+ @password = "hunter2"
+ end
+ obj.singleton_class.class_eval do
+ private def instance_variables_to_inspect = %i[@host @user @does_not_exist]
+ end
+
+ inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00')
+ inspected.should == '#<Object:0x00 @host="localhost", @user="root">'
+
+ obj = Object.new
+ obj.instance_eval do
+ @host = "localhost"
+ @user = "root"
+ @password = "hunter2"
+ end
+ obj.singleton_class.class_eval do
+ private def instance_variables_to_inspect = []
+ end
+
+ inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00')
+ inspected.should == "#<Object:0x00>"
+ end
+
+ it "displays all instance variables if #instance_variables_to_inspect returns nil" do
+ obj = Object.new
+ obj.instance_eval do
+ @host = "localhost"
+ @user = "root"
+ @password = "hunter2"
+ end
+ obj.singleton_class.class_eval do
+ private def instance_variables_to_inspect = nil
+ end
+
+ inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00')
+ inspected.should == %{#<Object:0x00 @host="localhost", @user="root", @password="hunter2">}
+ end
+
+ it "raises an error if #instance_variables_to_inspect returns an invalid value" do
+ obj = Object.new
+ obj.instance_eval do
+ @host = "localhost"
+ @user = "root"
+ @password = "hunter2"
+ end
+ obj.singleton_class.class_eval do
+ private def instance_variables_to_inspect = {}
+ end
+
+ ->{ obj.inspect }.should raise_error(TypeError, "Expected #instance_variables_to_inspect to return an Array or nil, but it returned Hash")
+ end
+ 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..d1170d5047
--- /dev/null
+++ b/spec/ruby/core/kernel/instance_of_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+ -> { @o.instance_of?(Object.new) }.should raise_error(TypeError)
+ -> { @o.instance_of?('KernelSpecs::InstanceClass') }.should raise_error(TypeError)
+ -> { @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..2ebb582b43
--- /dev/null
+++ b/spec/ruby/core/kernel/instance_variable_defined_spec.rb
@@ -0,0 +1,41 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+ -> 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..f1d2a45df8
--- /dev/null
+++ b/spec/ruby/core/kernel/instance_variable_get_spec.rb
@@ -0,0 +1,111 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+ -> { @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)
+ -> { @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")
+ -> { @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('@')
+ -> { @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
+ -> { @obj.instance_variable_get(:"@") }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when the passed Symbol does not start with an '@'" do
+ -> { @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
+ -> { @obj.instance_variable_get(:"@0") }.should raise_error(NameError)
+ end
+
+ it "returns nil or raises for frozen objects" do
+ nil.instance_variable_get(:@foo).should == nil
+ -> { nil.instance_variable_get(:foo) }.should raise_error(NameError)
+ :foo.instance_variable_get(:@foo).should == nil
+ 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
+ -> { @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
+ -> { @obj.instance_variable_get("@0") }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when passed '@' as an instance variable name" do
+ -> { @obj.instance_variable_get("@") }.should raise_error(NameError)
+ end
+end
+
+describe "Kernel#instance_variable_get when passed Integer" do
+ before :each do
+ @obj = Object.new
+ @obj.instance_variable_set("@test", :test)
+ end
+
+ it "raises a TypeError" do
+ -> { @obj.instance_variable_get(10) }.should raise_error(TypeError)
+ -> { @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..2c25f4366f
--- /dev/null
+++ b/spec/ruby/core/kernel/instance_variable_set_spec.rb
@@ -0,0 +1,105 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#instance_variable_set" do
+ it "sets the value of the specified instance variable" do
+ dog = Class.new do
+ 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
+ no_variables = Class.new
+ no_variables.new.instance_variable_set(:@a, "new").should == "new"
+ end
+
+ it "raises a NameError exception if the argument is not of form '@x'" do
+ no_dog = Class.new
+ -> { no_dog.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
+ digit_dog = Class.new
+ -> { digit_dog.new.instance_variable_set(:"@0", "cat") }.should raise_error(NameError)
+ end
+
+ it "raises a NameError when the argument is '@'" do
+ dog_at = Class.new
+ -> { dog_at.new.instance_variable_set(:"@", "cat") }.should raise_error(NameError)
+ end
+
+ it "raises a TypeError if the instance variable name is an Integer" do
+ -> { "".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
+ -> { "".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
+ -> { "".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
+ -> { "".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
+ -> { @frozen.instance_variable_set(:@ivar, :replacement) }.should raise_error(Exception)
+ @frozen.ivar.should equal(:origin)
+ end
+
+ it "raises a FrozenError when passed replacement is identical to stored object" do
+ -> { @frozen.instance_variable_set(:@ivar, :origin) }.should raise_error(FrozenError)
+ end
+
+ it "raises a FrozenError when passed replacement is different from stored object" do
+ -> { @frozen.instance_variable_set(:@ivar, :replacement) }.should raise_error(FrozenError)
+ end
+
+ it "accepts unicode instance variable names" do
+ o = Object.new
+ o.instance_variable_set(:@💙, 42)
+ o.instance_variable_get(:@💙).should == 42
+ end
+
+ it "raises for frozen objects" do
+ -> { nil.instance_variable_set(:@foo, 42) }.should raise_error(FrozenError)
+ -> { nil.instance_variable_set(:foo, 42) }.should raise_error(NameError)
+ -> { :foo.instance_variable_set(:@foo, 42) }.should raise_error(FrozenError)
+ 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..677d8bb7b2
--- /dev/null
+++ b/spec/ruby/core/kernel/instance_variables_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#instance_variables" do
+ describe "immediate values" do
+ it "returns an empty array if no instance variables are defined" do
+ [0, 0.5, true, false, nil].each do |value|
+ value.instance_variables.should == []
+ end
+ end
+
+ it "returns the correct array if an instance variable is added" do
+ a = 0
+ ->{ 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
+
+ it "returns the instances variables in the order declared" do
+ c = Class.new do
+ def initialize
+ @c = 1
+ @a = 2
+ @b = 3
+ end
+ end
+ c.new.instance_variables.should == [:@c, :@a, :@b]
+ 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..bd8c96529a
--- /dev/null
+++ b/spec/ruby/core/kernel/is_a_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/kind_of'
+
+describe "Kernel#is_a?" do
+ it_behaves_like :kernel_kind_of, :is_a?
+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..c906d7c3e8
--- /dev/null
+++ b/spec/ruby/core/kernel/itself_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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..c988edccb5
--- /dev/null
+++ b/spec/ruby/core/kernel/kind_of_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/kind_of'
+
+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..565536ac0d
--- /dev/null
+++ b/spec/ruby/core/kernel/lambda_spec.rb
@@ -0,0 +1,158 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/lambda'
+
+# 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 "creates a lambda-style Proc if given a literal block via #send" do
+ l = send(:lambda) { 42 }
+ l.lambda?.should be_true
+ end
+
+ it "creates a lambda-style Proc if given a literal block via #__send__" do
+ l = __send__(:lambda) { 42 }
+ l.lambda?.should be_true
+ end
+
+ ruby_version_is ""..."3.3" do
+ it "creates a lambda-style Proc if given a literal block via Kernel.public_send" do
+ suppress_warning do
+ l = Kernel.public_send(:lambda) { 42 }
+ l.lambda?.should be_true
+ end
+ end
+
+ it "returns the passed Proc if given an existing Proc" do
+ some_proc = proc {}
+ l = suppress_warning {lambda(&some_proc)}
+ l.should equal(some_proc)
+ l.lambda?.should be_false
+ end
+
+ it "creates a lambda-style Proc when called with zsuper" do
+ suppress_warning do
+ l = KernelSpecs::LambdaSpecs::ForwardBlockWithZSuper.new.lambda { 42 }
+ l.lambda?.should be_true
+ l.call.should == 42
+
+ lambda { l.call(:extra) }.should raise_error(ArgumentError)
+ end
+ end
+
+ it "returns the passed Proc if given an existing Proc through super" do
+ some_proc = proc { }
+ l = KernelSpecs::LambdaSpecs::SuperAmpersand.new.lambda(&some_proc)
+ l.should equal(some_proc)
+ l.lambda?.should be_false
+ end
+
+ it "does not create lambda-style Procs when captured with #method" do
+ kernel_lambda = method(:lambda)
+ l = suppress_warning {kernel_lambda.call { 42 }}
+ l.lambda?.should be_false
+ l.call(:extra).should == 42
+ end
+ 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
+
+ it "treats the block as a Proc when lambda is re-defined" do
+ klass = Class.new do
+ def lambda (&block); block; end
+ def ret
+ lambda { return 1 }.call
+ 2
+ end
+ end
+ klass.new.lambda { 42 }.should be_an_instance_of Proc
+ klass.new.ret.should == 1
+ end
+
+ context "when called without a literal block" do
+ ruby_version_is ""..."3.3" do
+ it "warns when proc isn't a lambda" do
+ -> { lambda(&proc{}) }.should complain("#{__FILE__}:#{__LINE__}: warning: lambda without a literal block is deprecated; use the proc without lambda instead\n")
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "raises when proc isn't a lambda" do
+ -> { lambda(&proc{}) }.should raise_error(ArgumentError, /the lambda method requires a literal block/)
+ end
+ end
+
+ it "doesn't warn when proc is lambda" do
+ -> { lambda(&lambda{}) }.should_not complain(verbose: true)
+ end
+ 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..a165cc4acd
--- /dev/null
+++ b/spec/ruby/core/kernel/load_spec.rb
@@ -0,0 +1,40 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/code_loading'
+require_relative 'shared/load'
+require_relative 'shared/require'
+
+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..f6f1e15f52
--- /dev/null
+++ b/spec/ruby/core/kernel/local_variables_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+
+ it "includes only unique variable names" do
+ def local_var_method
+ a = 1
+ 1.times do |;a|
+ return local_variables
+ end
+ end
+
+ local_var_method.should == [:a]
+ 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..7c76c7d28e
--- /dev/null
+++ b/spec/ruby/core/kernel/loop_spec.rb
@@ -0,0 +1,79 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+ ->{ loop do raise StandardError end }.should raise_error( StandardError )
+ end
+
+ 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
+
+ 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..cd6330fe91
--- /dev/null
+++ b/spec/ruby/core/kernel/match_spec.rb
@@ -0,0 +1,7 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#=~" do
+ it "is no longer defined" do
+ Object.new.should_not.respond_to?(:=~)
+ 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..3fc566d6a6
--- /dev/null
+++ b/spec/ruby/core/kernel/method_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../../spec_helper'
+require_relative 'shared/method'
+require_relative 'fixtures/classes'
+
+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 respond_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
+
+ it "can call a #method_missing accepting zero or one arguments" do
+ cls = Class.new do
+ def respond_to_missing?(name, *)
+ name == :foo or super
+ end
+ def method_missing
+ :no_args
+ end
+ end
+ m = cls.new.method(:foo)
+ -> { m.call }.should raise_error(ArgumentError)
+
+ cls = Class.new do
+ def respond_to_missing?(name, *)
+ name == :bar or super
+ end
+ def method_missing(m)
+ m
+ end
+ end
+ m = cls.new.method(:bar)
+ m.call.should == :bar
+ end
+
+ describe "converts the given name to a String using #to_str" do
+ it "calls #to_str to convert the given name to a String" do
+ name = mock("method-name")
+ name.should_receive(:to_str).and_return("hash")
+ Object.method(name).should == Object.method(:hash)
+ end
+
+ it "raises a TypeError if the given name can't be converted to a String" do
+ -> { Object.method(nil) }.should raise_error(TypeError)
+ -> { Object.method([]) }.should raise_error(TypeError)
+ end
+
+ it "raises a NoMethodError if the given argument raises a NoMethodError during type coercion to a String" do
+ name = mock("method-name")
+ name.should_receive(:to_str).and_raise(NoMethodError)
+ -> { Object.method(name) }.should raise_error(NoMethodError)
+ end
+ 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..fb7a7e8be9
--- /dev/null
+++ b/spec/ruby/core/kernel/methods_spec.rb
@@ -0,0 +1,101 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../fixtures/reflection'
+
+# 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..7418245f26
--- /dev/null
+++ b/spec/ruby/core/kernel/nil_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe 'Kernel#nil?' do
+ it 'returns false' do
+ Object.should_not.nil?
+ Object.new.should_not.nil?
+ ''.should_not.nil?
+ [].should_not.nil?
+ {}.should_not.nil?
+ end
+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..082e56fed7
--- /dev/null
+++ b/spec/ruby/core/kernel/not_match_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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 "raises NoMethodError if self does not respond to #=~" do
+ -> { Object.new !~ :foo }.should raise_error(NoMethodError)
+ 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..ef9e80c831
--- /dev/null
+++ b/spec/ruby/core/kernel/object_id_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative '../../shared/kernel/object_id'
+
+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..b967d5044b
--- /dev/null
+++ b/spec/ruby/core/kernel/open_spec.rb
@@ -0,0 +1,194 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+
+ ruby_version_is ""..."4.0" do
+ platform_is_not :windows, :wasi do
+ it "opens an io when path starts with a pipe" do
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ @io = open("|date")
+ end
+ begin
+ @io.should be_kind_of(IO)
+ @io.read
+ ensure
+ @io.close
+ end
+ end
+
+ it "opens an io when called with a block" do
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ @output = open("|date") { |f| f.read }
+ end
+ @output.should_not == ''
+ end
+
+ it "opens an io for writing" do
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ -> {
+ bytes = open("|cat", "w") { |io| io.write(".") }
+ bytes.should == 1
+ }.should output_to_fd(".")
+ end
+ end
+ end
+
+ platform_is :windows do
+ it "opens an io when path starts with a pipe" do
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ @io = open("|date /t")
+ end
+ begin
+ @io.should be_kind_of(IO)
+ @io.read
+ ensure
+ @io.close
+ end
+ end
+
+ it "opens an io when called with a block" do
+ suppress_warning do # https://bugs.ruby-lang.org/issues/19630
+ @output = open("|date /t") { |f| f.read }
+ end
+ @output.should_not == ''
+ end
+ end
+
+ ruby_version_is "3.3" do
+ # https://bugs.ruby-lang.org/issues/19630
+ it "warns about deprecation given a path with a pipe" do
+ cmd = "|echo ok"
+ -> {
+ open(cmd) { |f| f.read }
+ }.should complain(/Kernel#open with a leading '\|'/)
+ end
+ end
+ end
+
+ it "raises an ArgumentError if not passed one argument" do
+ -> { open }.should raise_error(ArgumentError)
+ end
+
+ it "accepts options as keyword arguments" do
+ @file = open(@name, "r", 0666, flags: File::CREAT)
+ @file.should be_kind_of(File)
+
+ -> {
+ open(@name, "r", 0666, {flags: File::CREAT})
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
+ end
+
+ describe "when given an object that responds to to_open" do
+ before :each do
+ ScratchPad.clear
+ end
+
+ it "calls #to_path to convert 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 keyword arguments onto #to_open as keyword arguments if to_open accepts them" do
+ obj = Object.new
+ def obj.to_open(*args, **kw)
+ ScratchPad << {args: args, kw: kw}
+ end
+
+ ScratchPad.record []
+ open(obj, 1, 2, 3, a: "b")
+ ScratchPad.recorded.should == [args: [1, 2, 3], kw: {a: "b"}]
+ 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')
+ -> { open(obj) }.should raise_error(TypeError)
+ -> { open(nil) }.should raise_error(TypeError)
+ -> { 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
+
+ it "is not redefined by open-uri" do
+ code = <<~RUBY
+ before = Kernel.instance_method(:open)
+ require 'open-uri'
+ after = Kernel.instance_method(:open)
+ p before == after
+ RUBY
+ ruby_exe(code, args: "2>&1").should == "true\n"
+ 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..eae191aa54
--- /dev/null
+++ b/spec/ruby/core/kernel/p_spec.rb
@@ -0,0 +1,85 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#p" do
+ before :all do
+ @rs_f, @rs_b, @rs_c = $/, $\, $,
+ end
+
+ after :each do
+ suppress_warning {
+ $/, $\, $, = @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!"
+
+ -> { p(o) }.should output("Next time, Gadget, NEXT TIME!\n")
+ -> { p(*[o]) }.should output("Next time, Gadget, NEXT TIME!\n")
+ -> { p(*[o, o]) }.should output("Next time, Gadget, NEXT TIME!\nNext time, Gadget, NEXT TIME!\n")
+ -> { 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!"
+
+ suppress_warning {
+ $, = " *helicopter sound*\n"
+ }
+ -> { p(o) }.should output_to_fd("Next time, Gadget, NEXT TIME!\n")
+
+ suppress_warning {
+ $\ = " *helicopter sound*\n"
+ }
+ -> { p(o) }.should output_to_fd("Next time, Gadget, NEXT TIME!\n")
+
+ suppress_warning {
+ $/ = " *helicopter sound*\n"
+ }
+ -> { p(o) }.should output_to_fd("Next time, Gadget, NEXT TIME!\n")
+ end
+
+ it "prints nothing if no argument is given" do
+ -> { p }.should output("")
+ end
+
+ it "prints nothing if called splatting an empty Array" do
+ -> { p(*[]) }.should output("")
+ end
+
+ # 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"
+end
+
+describe "Kernel.p" do
+ it "needs to be reviewed for spec completeness"
+end
diff --git a/spec/ruby/core/kernel/pp_spec.rb b/spec/ruby/core/kernel/pp_spec.rb
new file mode 100644
index 0000000000..b5b1c98d38
--- /dev/null
+++ b/spec/ruby/core/kernel/pp_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#pp" do
+ it "lazily loads the 'pp' library and delegates the call to that library" do
+ # Run in child process to ensure 'pp' hasn't been loaded yet.
+ output = ruby_exe("pp [1, 2, 3]")
+ output.should == "[1, 2, 3]\n"
+ end
+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..7e7c9b822d
--- /dev/null
+++ b/spec/ruby/core/kernel/print_spec.rb
@@ -0,0 +1,24 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#print" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:print)
+ end
+
+ it "delegates to $stdout" do
+ -> { print :arg }.should output("arg")
+ end
+
+ it "prints $_ when no arguments are given" do
+ orig_value = $_
+ $_ = 'foo'
+ -> { print }.should output("foo")
+ ensure
+ $_ = orig_value
+ 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..61bf955c25
--- /dev/null
+++ b/spec/ruby/core/kernel/printf_spec.rb
@@ -0,0 +1,70 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/sprintf'
+
+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
+
+ it "calls #to_str to convert the format object to a String" do
+ object = mock('format string')
+ object.should_receive(:to_str).and_return("to_str: %i")
+ $stdout.should_receive(:write).with("to_str: 42")
+ Kernel.printf($stdout, object, 42)
+ end
+end
+
+describe "Kernel.printf" do
+ describe "formatting" do
+ before :each do
+ require "stringio"
+ end
+
+ context "io is specified" do
+ it_behaves_like :kernel_sprintf, -> format, *args {
+ io = StringIO.new(+"")
+ Kernel.printf(io, format, *args)
+ io.string
+ }
+ end
+
+ context "io is not specified" do
+ it_behaves_like :kernel_sprintf, -> format, *args {
+ stdout = $stdout
+ begin
+ $stdout = io = StringIO.new(+"")
+ Kernel.printf(format, *args)
+ io.string
+ ensure
+ $stdout = stdout
+ end
+ }
+ end
+ 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..041634d1e5
--- /dev/null
+++ b/spec/ruby/core/kernel/private_methods_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../fixtures/reflection'
+
+# 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..6553b8fd04
--- /dev/null
+++ b/spec/ruby/core/kernel/proc_spec.rb
@@ -0,0 +1,48 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/lambda'
+
+# 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 = -> {}
+ 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
+ def some_method
+ proc
+ end
+
+ it "raises an ArgumentError when passed no block" do
+ -> {
+ some_method { "hello" }
+ }.should raise_error(ArgumentError, 'tried to create Proc object without a block')
+ end
+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..d3334e886b
--- /dev/null
+++ b/spec/ruby/core/kernel/protected_methods_spec.rb
@@ -0,0 +1,69 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../fixtures/reflection'
+
+# 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..c5d54c777e
--- /dev/null
+++ b/spec/ruby/core/kernel/public_method_spec.rb
@@ -0,0 +1,32 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/method'
+
+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
+ -> 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
+ -> {
+ @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
+ -> 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..a5512784fb
--- /dev/null
+++ b/spec/ruby/core/kernel/public_methods_spec.rb
@@ -0,0 +1,76 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../fixtures/reflection'
+
+# 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..b684b1729c
--- /dev/null
+++ b/spec/ruby/core/kernel/public_send_spec.rb
@@ -0,0 +1,116 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/basicobject/send'
+
+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
+ -> { 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
+ -> {
+ 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
+ -> { @receiver.call_protected_method }.should raise_error(NoMethodError)
+ end
+
+ it "raises a NoMethodError if the method is private" do
+ -> { @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
+ -> {
+ 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
+ -> {
+ KernelSpecs::Foo.new.public_send(:aka)
+ }.should raise_error(NoMethodError)
+ end
+
+ it "includes `public_send` in the backtrace when passed not enough arguments" do
+ -> { public_send() }.should raise_error(ArgumentError) { |e| e.backtrace[0].should =~ /[`'](?:Kernel#)?public_send'/ }
+ end
+
+ it "includes `public_send` in the backtrace when passed a single incorrect argument" do
+ -> { public_send(Object.new) }.should raise_error(TypeError) { |e| e.backtrace[0].should =~ /[`'](?:Kernel#)?public_send'/ }
+ 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..74bd3765db
--- /dev/null
+++ b/spec/ruby/core/kernel/putc_spec.rb
@@ -0,0 +1,39 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/io/putc'
+
+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..6eb38e8fcf
--- /dev/null
+++ b/spec/ruby/core/kernel/puts_spec.rb
@@ -0,0 +1,29 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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..fcd011d4e6
--- /dev/null
+++ b/spec/ruby/core/kernel/raise_spec.rb
@@ -0,0 +1,292 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/kernel/raise'
+
+describe "Kernel#raise" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:raise)
+ end
+
+ it "re-raises the previously rescued exception if no exception is specified" do
+ ScratchPad.record nil
+
+ -> do
+ begin
+ raise Exception, "outer"
+ ScratchPad.record :no_abort
+ rescue Exception
+ begin
+ raise StandardError, "inner"
+ rescue StandardError
+ end
+
+ raise
+ ScratchPad.record :no_reraise
+ end
+ end.should raise_error(Exception, "outer")
+
+ ScratchPad.recorded.should be_nil
+ end
+
+ it "accepts a cause keyword argument that sets the cause" do
+ cause = StandardError.new
+ -> { raise("error", cause: cause) }.should raise_error(RuntimeError) { |e| e.cause.should == cause }
+ end
+
+ it "accepts a cause keyword argument that overrides the last exception" do
+ begin
+ raise "first raise"
+ rescue => ignored
+ cause = StandardError.new
+ -> { raise("error", cause: cause) }.should raise_error(RuntimeError) { |e| e.cause.should == cause }
+ end
+ end
+
+ it "raises an ArgumentError when only cause is given" do
+ cause = StandardError.new
+ -> { raise(cause: cause) }.should raise_error(ArgumentError, "only cause is given with no arguments")
+ end
+
+ it "raises an ArgumentError when only cause is given even if it has nil value" do
+ -> { raise(cause: nil) }.should raise_error(ArgumentError, "only cause is given with no arguments")
+ end
+
+ it "raises a TypeError when given cause is not an instance of Exception" do
+ -> { raise "message", cause: Object.new }.should raise_error(TypeError, "exception object expected")
+ end
+
+ it "doesn't raise a TypeError when given cause is nil" do
+ -> { raise "message", cause: nil }.should raise_error(RuntimeError, "message")
+ end
+
+ it "allows cause equal an exception" do
+ e = RuntimeError.new("message")
+ -> { raise e, cause: e }.should raise_error(e)
+ end
+
+ it "doesn't set given cause when it equals an exception" do
+ e = RuntimeError.new("message")
+
+ begin
+ raise e, cause: e
+ rescue
+ end
+
+ e.cause.should == nil
+ end
+
+ it "raises ArgumentError when exception is part of the cause chain" do
+ -> {
+ begin
+ raise "Error 1"
+ rescue => e1
+ begin
+ raise "Error 2"
+ rescue => e2
+ begin
+ raise "Error 3"
+ rescue => e3
+ raise e1, cause: e3
+ end
+ end
+ end
+ }.should raise_error(ArgumentError, "circular causes")
+ end
+
+ it "re-raises a rescued exception" do
+ -> do
+ begin
+ raise StandardError, "aaa"
+ rescue Exception
+ begin
+ raise ArgumentError
+ rescue ArgumentError
+ end
+
+ # should raise StandardError "aaa"
+ raise
+ end
+ end.should raise_error(StandardError, "aaa")
+ end
+
+ it "re-raises a previously rescued exception without overwriting the cause" do
+ begin
+ begin
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ raise "Error 2"
+ end
+ rescue => e2
+ raise "Error 3"
+ end
+ rescue
+ e2.cause.should == e1
+ raise e2
+ end
+ rescue => e
+ e.cause.should == e1
+ end
+ end
+
+ it "re-raises a previously rescued exception with overwriting the cause when it's explicitly specified with :cause option" do
+ e4 = RuntimeError.new("Error 4")
+
+ begin
+ begin
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ raise "Error 2"
+ end
+ rescue => e2
+ raise "Error 3"
+ end
+ rescue
+ e2.cause.should == e1
+ raise e2, cause: e4
+ end
+ rescue => e
+ e.cause.should == e4
+ end
+ end
+
+ it "re-raises a previously rescued exception without overwriting the cause when it's explicitly specified with :cause option and has nil value" do
+ begin
+ begin
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ raise "Error 2"
+ end
+ rescue => e2
+ raise "Error 3"
+ end
+ rescue
+ e2.cause.should == e1
+ raise e2, cause: nil
+ end
+ rescue => e
+ e.cause.should == e1
+ end
+ end
+
+ it "re-raises a previously rescued exception without setting a cause implicitly" do
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ raise
+ end
+ rescue => e
+ e.should == e1
+ e.cause.should == nil
+ end
+ end
+
+ it "re-raises a previously rescued exception that has a cause without setting a cause implicitly" do
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ begin
+ raise "Error 2"
+ rescue => e2
+ raise
+ end
+ end
+ rescue => e
+ e.should == e2
+ e.cause.should == e1
+ end
+ end
+
+ it "re-raises a previously rescued exception that doesn't have a cause and isn't a cause of any other exception with setting a cause implicitly" do
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ begin
+ raise "Error 2"
+ rescue => e2
+ raise "Error 3"
+ end
+ end
+ rescue => e
+ e.message.should == "Error 3"
+ e.cause.should == e2
+ end
+ end
+
+ it "re-raises a previously rescued exception that doesn't have a cause and is a cause of other exception without setting a cause implicitly" do
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ begin
+ raise "Error 2"
+ rescue => e2
+ e1.cause.should == nil
+ e2.cause.should == e1
+ raise e1
+ end
+ end
+ rescue => e
+ e.should == e1
+ e.cause.should == nil
+ end
+ end
+
+ it "re-raises a previously rescued exception that doesn't have a cause and is a cause of other exception (that wasn't raised explicitly) without setting a cause implicitly" do
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ begin
+ foo # raises NameError
+ rescue => e2
+ e1.cause.should == nil
+ e2.cause.should == e1
+ raise e1
+ end
+ end
+ rescue => e
+ e.should == e1
+ e.cause.should == nil
+ end
+ end
+
+ it "re-raises a previously rescued exception that has a cause but isn't a cause of any other exception without setting a cause implicitly" do
+ begin
+ begin
+ raise "Error 1"
+ rescue => e1
+ begin
+ raise "Error 2"
+ rescue => e2
+ begin
+ raise "Error 3", cause: RuntimeError.new("Error 4")
+ rescue => e3
+ e2.cause.should == e1
+ e3.cause.should_not == e2
+ raise e2
+ end
+ end
+ end
+ rescue => e
+ e.should == e2
+ e.cause.should == e1
+ end
+ 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..355e425792
--- /dev/null
+++ b/spec/ruby/core/kernel/rand_spec.rb
@@ -0,0 +1,197 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+
+ context "given an inclusive range between 0 and 1" do
+ it "returns an Integer between the two Integers" do
+ x = rand(0..1)
+ x.should be_kind_of(Integer)
+ (0..1).should include(x)
+ end
+
+ it "returns a Float if at least one side is Float" do
+ seed = 42
+ x1 = Random.new(seed).rand(0..1.0)
+ x2 = Random.new(seed).rand(0.0..1.0)
+ x3 = Random.new(seed).rand(0.0..1)
+
+ x3.should be_kind_of(Float)
+ x1.should eql(x3)
+ x2.should eql(x3)
+
+ (0.0..1.0).should include(x3)
+ end
+ end
+
+ context "given an exclusive range between 0 and 1" do
+ it "returns zero as an Integer" do
+ x = rand(0...1)
+ x.should be_kind_of(Integer)
+ x.should eql(0)
+ end
+
+ it "returns a Float if at least one side is Float" do
+ seed = 42
+ x1 = Random.new(seed).rand(0...1.0)
+ x2 = Random.new(seed).rand(0.0...1.0)
+ x3 = Random.new(seed).rand(0.0...1)
+
+ x3.should be_kind_of(Float)
+ x1.should eql(x3)
+ x2.should eql(x3)
+
+ (0.0...1.0).should include(x3)
+ 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
+
+ it "supports custom object types" do
+ rand(KernelSpecs::CustomRangeInteger.new(1)..KernelSpecs::CustomRangeInteger.new(42)).should be_an_instance_of(KernelSpecs::CustomRangeInteger)
+ rand(KernelSpecs::CustomRangeFloat.new(1.0)..KernelSpecs::CustomRangeFloat.new(42.0)).should be_an_instance_of(KernelSpecs::CustomRangeFloat)
+ rand(Time.now..Time.now).should be_an_instance_of(Time)
+ end
+
+ it "is random on boot" do
+ results = 2.times.map {
+ out = ruby_exe('p rand', options: '--disable-gems')
+ Float(out)
+ }
+ results.size.should == 2
+ # this is technically flaky, but very unlikely in a good distribution
+ results[0].should_not == results[1]
+ 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..dce7b03dc8
--- /dev/null
+++ b/spec/ruby/core/kernel/readline_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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..2b6d65fff2
--- /dev/null
+++ b/spec/ruby/core/kernel/readlines_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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..4e5ba5e018
--- /dev/null
+++ b/spec/ruby/core/kernel/remove_instance_variable_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+ -> 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
+ -> 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
+ -> do
+ obj = mock("kernel remove_instance_variable")
+ @instance.send :remove_instance_variable, obj
+ end.should raise_error(TypeError)
+ end
+
+ it "raises a FrozenError if self is frozen" do
+ o = Object.new
+ o.freeze
+ -> { o.remove_instance_variable(:@foo) }.should raise_error(FrozenError)
+ -> { o.remove_instance_variable(:foo) }.should raise_error(NameError)
+ end
+
+ it "raises for frozen objects" do
+ -> { nil.remove_instance_variable(:@foo) }.should raise_error(FrozenError)
+ -> { nil.remove_instance_variable(:foo) }.should raise_error(NameError)
+ -> { :foo.remove_instance_variable(:@foo) }.should raise_error(FrozenError)
+ 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..6188d13a4e
--- /dev/null
+++ b/spec/ruby/core/kernel/require_relative_spec.rb
@@ -0,0 +1,437 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/code_loading'
+
+describe "Kernel#require_relative with a relative path" do
+ before :each do
+ CodeLoadingSpecs.spec_setup
+ @dir = "../../fixtures/code"
+ @abs_dir = File.realpath(@dir, __dir__)
+ @path = "#{@dir}/load_fixture.rb"
+ @abs_path = File.realpath(@path, __dir__)
+ 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
+
+ describe "in an #instance_eval with a" do
+
+ it "synthetic file base name loads a file base name relative to the working directory" do
+ Dir.chdir @abs_dir do
+ Object.new.instance_eval("require_relative(#{File.basename(@path).inspect})", "foo.rb").should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "synthetic file path loads a relative path relative to the working directory plus the directory of the synthetic path" do
+ Dir.chdir @abs_dir do
+ Object.new.instance_eval("require_relative(File.join('..', #{File.basename(@path).inspect}))", "bar/foo.rb").should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ platform_is_not :windows do
+ it "synthetic relative file path with a Windows path separator specified loads a relative path relative to the working directory" do
+ Dir.chdir @abs_dir do
+ Object.new.instance_eval("require_relative(#{File.basename(@path).inspect})", "bar\\foo.rb").should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
+ end
+ end
+
+ it "absolute file path loads a path relative to the absolute path" do
+ Object.new.instance_eval("require_relative(#{@path.inspect})", __FILE__).should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ it "absolute file path loads a path relative to the root directory" do
+ root = @abs_path
+ until File.dirname(root) == root
+ root = File.dirname(root)
+ end
+ root_relative = @abs_path[root.size..-1]
+ Object.new.instance_eval("require_relative(#{root_relative.inspect})", "/").should be_true
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ 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
+ -> { require_relative("#{@dir}/nonexistent.rb") }.should raise_error(LoadError)
+ ScratchPad.recorded.should == []
+ end
+
+ it "raises a LoadError that includes the missing path" do
+ missing_path = "#{@dir}/nonexistent.rb"
+ expanded_missing_path = File.expand_path(missing_path, __dir__)
+ -> { require_relative(missing_path) }.should raise_error(LoadError) { |e|
+ e.message.should include(expanded_missing_path)
+ e.path.should == expanded_missing_path
+ }
+ ScratchPad.recorded.should == []
+ end
+
+ it "raises a LoadError if basepath does not exist" do
+ -> { 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"
+
+ -> {
+ 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
+ -> { require_relative(nil) }.should raise_error(TypeError)
+ -> { require_relative(42) }.should raise_error(TypeError)
+ -> {
+ 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)
+ -> { 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)
+ -> { 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
+
+ platform_is_not :windows, :wasi do
+ describe "with symlinks" do
+ before :each do
+ @symlink_to_code_dir = tmp("codesymlink")
+ File.symlink(CODE_LOADING_DIR, @symlink_to_code_dir)
+ @symlink_basename = File.basename(@symlink_to_code_dir)
+ @requiring_file = tmp("requiring")
+ end
+
+ after :each do
+ rm_r @symlink_to_code_dir, @requiring_file
+ end
+
+ it "does not canonicalize the path and stores a path with symlinks" do
+ symlink_path = "#{@symlink_basename}/load_fixture.rb"
+ absolute_path = "#{tmp("")}#{symlink_path}"
+ canonical_path = "#{CODE_LOADING_DIR}/load_fixture.rb"
+ touch(@requiring_file) { |f|
+ f.puts "require_relative #{symlink_path.inspect}"
+ }
+ load(@requiring_file)
+ ScratchPad.recorded.should == [:loaded]
+
+ features = $LOADED_FEATURES.select { |path| path.end_with?('load_fixture.rb') }
+ features.should include(absolute_path)
+ features.should_not include(canonical_path)
+ end
+
+ it "stores the same path that __FILE__ returns in the required file" do
+ symlink_path = "#{@symlink_basename}/load_fixture_and__FILE__.rb"
+ touch(@requiring_file) { |f|
+ f.puts "require_relative #{symlink_path.inspect}"
+ }
+ load(@requiring_file)
+ loaded_feature = $LOADED_FEATURES.last
+ ScratchPad.recorded.should == [loaded_feature]
+ end
+ end
+ end
+
+ it "does not store the path if the load fails" do
+ saved_loaded_features = $LOADED_FEATURES.dup
+ -> { 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
+ before :each do
+ CodeLoadingSpecs.spec_setup
+ @dir = File.expand_path "../../fixtures/code", __dir__
+ @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
+ -> { require_relative("#{@dir}/nonexistent.rb") }.should raise_error(LoadError)
+ ScratchPad.recorded.should == []
+ end
+
+ it "raises a LoadError if basepath does not exist" do
+ -> { 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"
+
+ -> {
+ 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
+ -> { require_relative(nil) }.should raise_error(TypeError)
+ -> { require_relative(42) }.should raise_error(TypeError)
+ -> {
+ 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)
+ -> { 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)
+ -> { 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
+ -> { 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..60d17242fe
--- /dev/null
+++ b/spec/ruby/core/kernel/require_spec.rb
@@ -0,0 +1,60 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/code_loading'
+require_relative 'shared/require'
+
+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
+
+ provided = %w[complex enumerator fiber rational thread ruby2_keywords]
+ ruby_version_is "4.0" do
+ provided << "set"
+ provided << "pathname"
+ end
+
+ it "#{provided.join(', ')} are already required" do
+ out = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems --disable-did-you-mean')
+ features = out.lines.map { |line| File.basename(line.chomp, '.*') }
+
+ # Ignore CRuby internals
+ features -= %w[encdb transdb windows_1252 windows_31j]
+ features.reject! { |feature| feature.end_with?('-fake') }
+
+ features.sort.should == provided.sort
+
+ requires = provided
+ ruby_version_is "4.0" do
+ requires = requires.map { |f| f == "pathname" ? "pathname.so" : f }
+ end
+
+ code = requires.map { |f| "puts require #{f.inspect}\n" }.join
+ required = ruby_exe(code, options: '--disable-gems')
+ required.should == "false\n" * requires.size
+ 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..cc82031e26
--- /dev/null
+++ b/spec/ruby/core/kernel/respond_to_missing_spec.rb
@@ -0,0 +1,100 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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..5b3ea3f651
--- /dev/null
+++ b/spec/ruby/core/kernel/respond_to_spec.rb
@@ -0,0 +1,72 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+ -> { @a.respond_to?(Object.new) }.should raise_error(TypeError, /is not a symbol nor a string/)
+ 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
+ -> { 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..df23414b28
--- /dev/null
+++ b/spec/ruby/core/kernel/select_spec.rb
@@ -0,0 +1,18 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#select" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:select)
+ end
+end
+
+describe "Kernel.select" do
+ it 'does not block when timeout is 0' do
+ IO.pipe do |read, write|
+ select([read], [], [], 0).should == nil
+ write.write 'data'
+ 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..9a4d261964
--- /dev/null
+++ b/spec/ruby/core/kernel/send_spec.rb
@@ -0,0 +1,68 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../shared/basicobject/send'
+
+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..1f43e7a009
--- /dev/null
+++ b/spec/ruby/core/kernel/set_trace_func_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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..4fac6006e1
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/dup_clone.rb
@@ -0,0 +1,91 @@
+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 "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 "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
+
+ it "returns self for Complex" do
+ c = Complex(1.3, 3.1)
+ c.send(@method).should equal c
+ end
+
+ it "returns self for Rational" do
+ r = Rational(1, 3)
+ r.send(@method).should equal r
+ 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..aef6f1c1d8
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/kind_of.rb
@@ -0,0 +1,55 @@
+require_relative '../fixtures/classes'
+
+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 true if given a Module that object has been prepended with" do
+ @o.send(@method, KernelSpecs::MyPrependedModule).should == true
+ end
+
+ it "returns false if given a Module not included nor prepended 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
+ -> { @o.send(@method, 1) }.should raise_error(TypeError)
+ -> { @o.send(@method, 'KindaClass') }.should raise_error(TypeError)
+ -> { @o.send(@method, :KindaClass) }.should raise_error(TypeError)
+ -> { @o.send(@method, Object.new) }.should raise_error(TypeError)
+ end
+
+ it "does not take into account `class` method overriding" do
+ def @o.class; Integer; end
+
+ @o.send(@method, Integer).should == false
+ @o.send(@method, KernelSpecs::KindaClass).should == true
+ 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..c70640082a
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/lambda.rb
@@ -0,0 +1,11 @@
+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
+ suppress_warning do
+ -> { send(@method) }.should raise_error(ArgumentError)
+ end
+ 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..62c5c7be9b
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/load.rb
@@ -0,0 +1,215 @@
+main = self
+
+# The big difference is Kernel#load does not attempt to add an extension to the passed path, unlike Kernel#require
+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
+
+ describe "(path resolution)" do
+ # This behavior is specific to Kernel#load, it differs for Kernel#require
+ 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
+
+ # This behavior is specific to Kernel#load, it differs for Kernel#require
+ it "does not look for a c-extension file when passed a path without extension (when no .rb is present)" do
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture"
+ -> { @object.send(@method, path) }.should raise_error(LoadError)
+ end
+ end
+
+ it "loads a file that recursively requires itself" do
+ path = File.expand_path "recursive_require_fixture.rb", CODE_LOADING_DIR
+ -> {
+ @object.load(path).should be_true
+ }.should complain(/circular require considered harmful/, verbose: true)
+ 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
+ -> { @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 "load_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 "load_wrap_fixture.rb", CODE_LOADING_DIR
+ @object.load(path, true)
+
+ Object.const_defined?(:LoadSpecWrap).should be_false
+
+ wrap_module = ScratchPad.recorded[1]
+ wrap_module.should be_an_instance_of(Module)
+ end
+
+ it "allows referencing outside namespaces" do
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ @object.load(path, true)
+
+ ScratchPad.recorded[0].should equal(String)
+ end
+
+ it "sets self as a copy of the top-level main" do
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ @object.load(path, true)
+
+ top_level = ScratchPad.recorded[2]
+ top_level.to_s.should == "main"
+ top_level.method(:to_s).owner.should == top_level.singleton_class
+ top_level.should_not equal(main)
+ top_level.should be_an_instance_of(Object)
+ end
+
+ it "includes modules included in main's singleton class in self's class" do
+ mod = Module.new
+ main.extend(mod)
+
+ main_ancestors = main.singleton_class.ancestors[1..-1]
+ main_ancestors.first.should == mod
+
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ @object.load(path, true)
+
+ top_level = ScratchPad.recorded[2]
+ top_level_ancestors = top_level.singleton_class.ancestors[-main_ancestors.size..-1]
+ top_level_ancestors.should == main_ancestors
+
+ wrap_module = ScratchPad.recorded[1]
+ top_level.singleton_class.ancestors.should == [top_level.singleton_class, wrap_module, *main_ancestors]
+ 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
+ -> { @object.send(:top_level_method) }.should raise_error(NameError)
+ end
+ end
+ end
+
+ describe "when passed a module for 'wrap'" do
+ it "sets the enclosing scope to the supplied module" do
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ mod = Module.new
+ @object.load(path, mod)
+
+ Object.const_defined?(:LoadSpecWrap).should be_false
+ mod.const_defined?(:LoadSpecWrap).should be_true
+
+ wrap_module = ScratchPad.recorded[1]
+ wrap_module.should == mod
+ end
+
+ it "makes constants and instance methods in the source file reachable with the supplied module" do
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ mod = Module.new
+ @object.load(path, mod)
+
+ mod::LOAD_WRAP_SPECS_TOP_LEVEL_CONSTANT.should == 1
+ obj = Object.new
+ obj.extend(mod)
+ obj.send(:load_wrap_specs_top_level_method).should == :load_wrap_specs_top_level_method
+ end
+
+ it "makes instance methods in the source file private" do
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
+ mod = Module.new
+ @object.load(path, mod)
+
+ mod.private_instance_methods.include?(:load_wrap_specs_top_level_method).should == true
+ 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..8b6ab23fd3
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/method.rb
@@ -0,0 +1,56 @@
+require_relative '../../../spec_helper'
+
+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 respond_to_missing?(method) is true" 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 "the returned method object if respond_to_missing?(method) calls #method_missing with a Symbol name" 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
+ -> {
+ 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
+ -> { 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..52f86f73e5
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/require.rb
@@ -0,0 +1,848 @@
+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.should_not.exist?(path)
+ -> { @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
+ as_user 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.should.exist?(@path)
+ -> { @object.send(@method, @path) }.should raise_error(LoadError)
+ end
+ 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
+ -> { @object.send(@method, nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an Integer" do
+ -> { @object.send(@method, 42) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an Array" do
+ -> { @object.send(@method, []) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError if passed an object that does not provide #to_str" do
+ -> { @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
+ -> { @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)
+ -> { @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 "accepts an Object with #to_path in $LOAD_PATH" do
+ obj = mock("to_path")
+ obj.should_receive(:to_path).at_least(:once).and_return(CODE_LOADING_DIR)
+ $LOAD_PATH << obj
+ @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.push 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
+ -> 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
+ -> 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
+ it "loads .rb file when passed absolute path without extension" do
+ path = File.expand_path "load_fixture", CODE_LOADING_DIR
+ @object.send(@method, path).should be_true
+ # This should _not_ be [:no_ext]
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ platform_is :linux, :darwin do
+ it "loads c-extension file when passed absolute path without extension when no .rb is present" do
+ # the error message is specific to what dlerror() returns
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture"
+ -> { @object.send(@method, path) }.should raise_error(LoadError)
+ end
+ end
+
+ platform_is :darwin do
+ it "loads .bundle file when passed absolute path with .so" do
+ # the error message is specific to what dlerror() returns
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture.so"
+ -> { @object.send(@method, path) }.should raise_error(LoadError)
+ end
+ end
+
+ it "does not try an extra .rb if the path already ends in .rb" do
+ path = File.join CODE_LOADING_DIR, "d", "load_fixture.rb"
+ -> { @object.send(@method, path) }.should raise_error(LoadError)
+ end
+
+ # 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
+ -> { @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
+ -> 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
+ -> {
+ @object.require(path).should be_true
+ }.should complain(/circular require considered harmful/, verbose: true)
+ ScratchPad.recorded.should == [:loaded]
+ end
+
+ ruby_bug "#17340", ''...'3.3' do
+ it "loads a file concurrently" do
+ path = File.expand_path "concurrent_require_fixture.rb", CODE_LOADING_DIR
+ ScratchPad.record(@object)
+ -> {
+ @object.require(path)
+ }.should_not complain(/circular require considered harmful/, verbose: true)
+ ScratchPad.recorded.join
+ end
+ 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
+
+ it "does not load a feature twice when $LOAD_PATH has been modified" do
+ $LOAD_PATH.replace [CODE_LOADING_DIR]
+ @object.require("load_fixture").should be_true
+ $LOAD_PATH.replace [File.expand_path("b", CODE_LOADING_DIR), CODE_LOADING_DIR]
+ @object.require("load_fixture").should be_false
+ end
+
+ it "stores the missing path in a LoadError object" do
+ path = "abcd1234"
+
+ -> {
+ @object.send(@method, path)
+ }.should raise_error(LoadError) { |e|
+ e.path.should == path
+ }
+ 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.should.exist?(path)
+ @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.should.exist?(path)
+ @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
+
+ platform_is_not :windows do
+ describe "with symlinks" do
+ before :each do
+ @symlink_to_code_dir = tmp("codesymlink")
+ File.symlink(CODE_LOADING_DIR, @symlink_to_code_dir)
+
+ $LOAD_PATH.delete(CODE_LOADING_DIR)
+ $LOAD_PATH.unshift(@symlink_to_code_dir)
+ end
+
+ after :each do
+ rm_r @symlink_to_code_dir
+ end
+
+ it "does not canonicalize the path and stores a path with symlinks" do
+ symlink_path = "#{@symlink_to_code_dir}/load_fixture.rb"
+ canonical_path = "#{CODE_LOADING_DIR}/load_fixture.rb"
+ @object.require(symlink_path).should be_true
+ ScratchPad.recorded.should == [:loaded]
+
+ features = $LOADED_FEATURES.select { |path| path.end_with?('load_fixture.rb') }
+ features.should include(symlink_path)
+ features.should_not include(canonical_path)
+ end
+
+ it "stores the same path that __FILE__ returns in the required file" do
+ symlink_path = "#{@symlink_to_code_dir}/load_fixture_and__FILE__.rb"
+ @object.require(symlink_path).should be_true
+ loaded_feature = $LOADED_FEATURES.last
+ ScratchPad.recorded.should == [loaded_feature]
+ end
+
+ it "requires only once when a new matching file added to path" do
+ @object.require('load_fixture').should be_true
+ ScratchPad.recorded.should == [:loaded]
+
+ symlink_to_code_dir_two = tmp("codesymlinktwo")
+ File.symlink("#{CODE_LOADING_DIR}/b", symlink_to_code_dir_two)
+ begin
+ $LOAD_PATH.unshift(symlink_to_code_dir_two)
+
+ @object.require('load_fixture').should be_false
+ ensure
+ rm_r symlink_to_code_dir_two
+ end
+ end
+ end
+
+ describe "with symlinks in the required feature and $LOAD_PATH" do
+ before :each do
+ @dir = tmp("realdir")
+ mkdir_p @dir
+ @file = "#{@dir}/realfile.rb"
+ touch(@file) { |f| f.puts 'ScratchPad << __FILE__' }
+
+ @symlink_to_dir = tmp("symdir").freeze
+ File.symlink(@dir, @symlink_to_dir)
+ @symlink_to_file = "#{@dir}/symfile.rb"
+ File.symlink("realfile.rb", @symlink_to_file)
+ end
+
+ after :each do
+ rm_r @dir, @symlink_to_dir
+ end
+
+ it "canonicalizes the entry in $LOAD_PATH but not the filename passed to #require" do
+ $LOAD_PATH.unshift(@symlink_to_dir)
+ @object.require("symfile").should be_true
+ loaded_feature = "#{@dir}/symfile.rb"
+ ScratchPad.recorded.should == [loaded_feature]
+ $".last.should == loaded_feature
+ $LOAD_PATH[0].should == @symlink_to_dir
+ end
+ end
+ end
+
+ it "does not store the path if the load fails" do
+ $LOAD_PATH << CODE_LOADING_DIR
+ saved_loaded_features = $LOADED_FEATURES.dup
+ -> { @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 "expands absolute paths containing .." 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
+
+ it "unicode_normalize is part of core and not $LOADED_FEATURES" do
+ features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems')
+ features.lines.each { |feature|
+ feature.should_not include("unicode_normalize")
+ }
+
+ -> { @object.require("unicode_normalize") }.should raise_error(LoadError)
+ end
+
+ it "does not load a file earlier on the $LOAD_PATH when other similar features were already loaded" do
+ Dir.chdir CODE_LOADING_DIR do
+ @object.send(@method, "../code/load_fixture").should be_true
+ end
+ ScratchPad.recorded.should == [:loaded]
+
+ $LOAD_PATH.unshift "#{CODE_LOADING_DIR}/b"
+ # This loads because the above load was not on the $LOAD_PATH
+ @object.send(@method, "load_fixture").should be_true
+ ScratchPad.recorded.should == [:loaded, :loaded]
+
+ $LOAD_PATH.unshift "#{CODE_LOADING_DIR}/c"
+ # This does not load because the above load was on the $LOAD_PATH
+ @object.send(@method, "load_fixture").should be_false
+ ScratchPad.recorded.should == [:loaded, :loaded]
+ 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
+
+ -> {
+ @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
+
+ -> {
+ @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"
+
+ -> {
+ @object.send(@method, path)
+ }.should raise_error(LoadError) { |e|
+ e.path.should == path
+ }
+ end
+
+ platform_is :linux, :darwin do
+ it "does not store the missing path in a LoadError object when c-extension file exists but loading fails and passed absolute path without extension" do
+ # the error message is specific to what dlerror() returns
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture"
+ -> { @object.send(@method, path) }.should raise_error(LoadError) { |e|
+ e.path.should == nil
+ }
+ end
+ end
+
+ platform_is :darwin do
+ it "does not store the missing path in a LoadError object when c-extension file exists but loading fails and passed absolute path with extension" do
+ # the error message is specific to what dlerror() returns
+ path = File.join CODE_LOADING_DIR, "a", "load_fixture.bundle"
+ -> { @object.send(@method, path) }.should raise_error(LoadError) { |e|
+ e.path.should == nil
+ }
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/shared/sprintf.rb b/spec/ruby/core/kernel/shared/sprintf.rb
new file mode 100644
index 0000000000..2b2c6c9b63
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/sprintf.rb
@@ -0,0 +1,987 @@
+describe :kernel_sprintf, shared: true do
+ describe "integer formats" do
+ it "converts argument into Integer with to_int" do
+ obj = Object.new
+ def obj.to_i; 10; end
+ def obj.to_int; 10; end
+
+ obj.should_receive(:to_int).and_return(10)
+ @method.call("%b", obj).should == "1010"
+ end
+
+ it "converts argument into Integer with to_i if to_int isn't available" do
+ obj = Object.new
+ def obj.to_i; 10; end
+
+ obj.should_receive(:to_i).and_return(10)
+ @method.call("%b", obj).should == "1010"
+ end
+
+ it "converts String argument with Kernel#Integer" do
+ @method.call("%d", "0b1010").should == "10"
+ @method.call("%d", "112").should == "112"
+ @method.call("%d", "0127").should == "87"
+ @method.call("%d", "0xc4").should == "196"
+ @method.call("%d", "0").should == "0"
+ end
+
+ it "raises TypeError exception if cannot convert to Integer" do
+ -> {
+ @method.call("%b", Object.new)
+ }.should raise_error(TypeError)
+ end
+
+ ["b", "B"].each do |f|
+ describe f do
+ it "converts argument as a binary number" do
+ @method.call("%#{f}", 10).should == "1010"
+ end
+
+ it "displays negative number as a two's complement prefixed with '..1'" do
+ @method.call("%#{f}", -10).should == "..1" + "0110"
+ end
+
+ it "collapse negative number representation if it equals 1" do
+ @method.call("%#{f}", -1).should_not == "..11"
+ @method.call("%#{f}", -1).should == "..1"
+ end
+ end
+ end
+
+ ["d", "i", "u"].each do |f|
+ describe f do
+ it "converts argument as a decimal number" do
+ @method.call("%#{f}", 112).should == "112"
+ @method.call("%#{f}", -112).should == "-112"
+ end
+
+ it "works well with large numbers" do
+ @method.call("%#{f}", 1234567890987654321).should == "1234567890987654321"
+ end
+
+ it "converts to the empty string if precision is 0 and value is 0" do
+ @method.call("%.#{f}", 0).should == ""
+ @method.call("%.0#{f}", 0).should == ""
+ end
+ end
+ end
+
+ describe "o" do
+ it "converts argument as an octal number" do
+ @method.call("%o", 87).should == "127"
+ end
+
+ it "displays negative number as a two's complement prefixed with '..7'" do
+ @method.call("%o", -87).should == "..7" + "651"
+ end
+
+ it "collapse negative number representation if it equals 7" do
+ @method.call("%o", -1).should_not == "..77"
+ @method.call("%o", -1).should == "..7"
+ end
+ end
+
+ describe "x" do
+ it "converts argument as a hexadecimal number" do
+ @method.call("%x", 196).should == "c4"
+ end
+
+ it "displays negative number as a two's complement prefixed with '..f'" do
+ @method.call("%x", -196).should == "..f" + "3c"
+ end
+
+ it "collapse negative number representation if it equals f" do
+ @method.call("%x", -1).should_not == "..ff"
+ @method.call("%x", -1).should == "..f"
+ end
+ end
+
+ describe "X" do
+ it "converts argument as a hexadecimal number with uppercase letters" do
+ @method.call("%X", 196).should == "C4"
+ end
+
+ it "displays negative number as a two's complement prefixed with '..f'" do
+ @method.call("%X", -196).should == "..F" + "3C"
+ end
+
+ it "collapse negative number representation if it equals F" do
+ @method.call("%X", -1).should_not == "..FF"
+ @method.call("%X", -1).should == "..F"
+ end
+ end
+ end
+
+ describe "float formats" do
+ it "converts argument into Float" do
+ obj = mock("float")
+ obj.should_receive(:to_f).and_return(9.6)
+ @method.call("%f", obj).should == "9.600000"
+ end
+
+ it "raises TypeError exception if cannot convert to Float" do
+ -> {
+ @method.call("%f", Object.new)
+ }.should raise_error(TypeError)
+ end
+
+ {"e" => "e", "E" => "E"}.each_pair do |f, exp|
+ describe f do
+ it "converts argument into exponential notation [-]d.dddddde[+-]dd" do
+ @method.call("%#{f}", 109.52).should == "1.095200#{exp}+02"
+ @method.call("%#{f}", -109.52).should == "-1.095200#{exp}+02"
+ @method.call("%#{f}", 0.10952).should == "1.095200#{exp}-01"
+ @method.call("%#{f}", -0.10952).should == "-1.095200#{exp}-01"
+ end
+
+ it "cuts excessive digits and keeps only 6 ones" do
+ @method.call("%#{f}", 1.123456789).should == "1.123457#{exp}+00"
+ end
+
+ it "rounds the last significant digit to the closest one" do
+ @method.call("%#{f}", 1.555555555).should == "1.555556#{exp}+00"
+ @method.call("%#{f}", -1.555555555).should == "-1.555556#{exp}+00"
+ @method.call("%#{f}", 1.444444444).should == "1.444444#{exp}+00"
+ end
+
+ it "displays Float::INFINITY as Inf" do
+ @method.call("%#{f}", Float::INFINITY).should == "Inf"
+ @method.call("%#{f}", -Float::INFINITY).should == "-Inf"
+ end
+
+ it "displays Float::NAN as NaN" do
+ @method.call("%#{f}", Float::NAN).should == "NaN"
+ @method.call("%#{f}", -Float::NAN).should == "NaN"
+ end
+ end
+ end
+
+ describe "f" do
+ it "converts floating point argument as [-]ddd.dddddd" do
+ @method.call("%f", 10.952).should == "10.952000"
+ @method.call("%f", -10.952).should == "-10.952000"
+ end
+
+ it "cuts excessive digits and keeps only 6 ones" do
+ @method.call("%f", 1.123456789).should == "1.123457"
+ end
+
+ it "rounds the last significant digit to the closest one" do
+ @method.call("%f", 1.555555555).should == "1.555556"
+ @method.call("%f", -1.555555555).should == "-1.555556"
+ @method.call("%f", 1.444444444).should == "1.444444"
+ end
+
+ it "displays Float::INFINITY as Inf" do
+ @method.call("%f", Float::INFINITY).should == "Inf"
+ @method.call("%f", -Float::INFINITY).should == "-Inf"
+ end
+
+ it "displays Float::NAN as NaN" do
+ @method.call("%f", Float::NAN).should == "NaN"
+ @method.call("%f", -Float::NAN).should == "NaN"
+ end
+ end
+
+ {"g" => "e", "G" => "E"}.each_pair do |f, exp|
+ describe f do
+ context "the exponent is less than -4" do
+ it "converts a floating point number using exponential form" do
+ @method.call("%#{f}", 0.0000123456).should == "1.23456#{exp}-05"
+ @method.call("%#{f}", -0.0000123456).should == "-1.23456#{exp}-05"
+
+ @method.call("%#{f}", 0.000000000123456).should == "1.23456#{exp}-10"
+ @method.call("%#{f}", -0.000000000123456).should == "-1.23456#{exp}-10"
+ end
+ end
+
+ context "the exponent is greater than or equal to the precision (6 by default)" do
+ it "converts a floating point number using exponential form" do
+ @method.call("%#{f}", 1234567).should == "1.23457#{exp}+06"
+ @method.call("%#{f}", 1234567890123).should == "1.23457#{exp}+12"
+ @method.call("%#{f}", -1234567).should == "-1.23457#{exp}+06"
+ end
+ end
+
+ context "otherwise" do
+ it "converts a floating point number in dd.dddd form" do
+ @method.call("%#{f}", 0.0001).should == "0.0001"
+ @method.call("%#{f}", -0.0001).should == "-0.0001"
+ @method.call("%#{f}", 123456).should == "123456"
+ @method.call("%#{f}", -123456).should == "-123456"
+ end
+
+ it "cuts excessive digits in fractional part and keeps only 4 ones" do
+ @method.call("%#{f}", 12.12341111).should == "12.1234"
+ @method.call("%#{f}", -12.12341111).should == "-12.1234"
+ end
+
+ it "rounds the last significant digit to the closest one in fractional part" do
+ @method.call("%#{f}", 1.555555555).should == "1.55556"
+ @method.call("%#{f}", -1.555555555).should == "-1.55556"
+ @method.call("%#{f}", 1.444444444).should == "1.44444"
+ end
+
+ it "cuts fraction part to have only 6 digits at all" do
+ @method.call("%#{f}", 1.1234567).should == "1.12346"
+ @method.call("%#{f}", 12.1234567).should == "12.1235"
+ @method.call("%#{f}", 123.1234567).should == "123.123"
+ @method.call("%#{f}", 1234.1234567).should == "1234.12"
+ @method.call("%#{f}", 12345.1234567).should == "12345.1"
+ @method.call("%#{f}", 123456.1234567).should == "123456"
+ end
+ end
+
+ it "displays Float::INFINITY as Inf" do
+ @method.call("%#{f}", Float::INFINITY).should == "Inf"
+ @method.call("%#{f}", -Float::INFINITY).should == "-Inf"
+ end
+
+ it "displays Float::NAN as NaN" do
+ @method.call("%#{f}", Float::NAN).should == "NaN"
+ @method.call("%#{f}", -Float::NAN).should == "NaN"
+ end
+ end
+ end
+
+ describe "a" do
+ it "converts floating point argument as [-]0xh.hhhhp[+-]dd" do
+ @method.call("%a", 196).should == "0x1.88p+7"
+ @method.call("%a", -196).should == "-0x1.88p+7"
+ @method.call("%a", 196.1).should == "0x1.8833333333333p+7"
+ @method.call("%a", 0.01).should == "0x1.47ae147ae147bp-7"
+ @method.call("%a", -0.01).should == "-0x1.47ae147ae147bp-7"
+ end
+
+ it "displays Float::INFINITY as Inf" do
+ @method.call("%a", Float::INFINITY).should == "Inf"
+ @method.call("%a", -Float::INFINITY).should == "-Inf"
+ end
+
+ it "displays Float::NAN as NaN" do
+ @method.call("%a", Float::NAN).should == "NaN"
+ @method.call("%a", -Float::NAN).should == "NaN"
+ end
+ end
+
+ describe "A" do
+ it "converts floating point argument as [-]0xh.hhhhp[+-]dd and use uppercase X and P" do
+ @method.call("%A", 196).should == "0X1.88P+7"
+ @method.call("%A", -196).should == "-0X1.88P+7"
+ @method.call("%A", 196.1).should == "0X1.8833333333333P+7"
+ @method.call("%A", 0.01).should == "0X1.47AE147AE147BP-7"
+ @method.call("%A", -0.01).should == "-0X1.47AE147AE147BP-7"
+ end
+
+ it "displays Float::INFINITY as Inf" do
+ @method.call("%A", Float::INFINITY).should == "Inf"
+ @method.call("%A", -Float::INFINITY).should == "-Inf"
+ end
+
+ it "displays Float::NAN as NaN" do
+ @method.call("%A", Float::NAN).should == "NaN"
+ @method.call("%A", -Float::NAN).should == "NaN"
+ end
+ end
+ end
+
+ describe "other formats" do
+ describe "c" do
+ it "displays character if argument is a numeric code of character" do
+ @method.call("%c", 97).should == "a"
+ end
+
+ it "displays character if argument is a single character string" do
+ @method.call("%c", "a").should == "a"
+ end
+
+ it "displays only the first character if argument is a string of several characters" do
+ @method.call("%c", "abc").should == "a"
+ end
+
+ it "displays no characters if argument is an empty string" do
+ @method.call("%c", "").should == ""
+ end
+
+ it "raises TypeError if argument is not String or Integer and cannot be converted to them" do
+ -> {
+ @method.call("%c", [])
+ }.should raise_error(TypeError, /no implicit conversion of Array into Integer/)
+ end
+
+ it "raises TypeError if argument is nil" do
+ -> {
+ @method.call("%c", nil)
+ }.should raise_error(TypeError, /no implicit conversion from nil to integer/)
+ end
+
+ it "tries to convert argument to String with to_str" do
+ obj = BasicObject.new
+ def obj.to_str
+ "a"
+ end
+
+ @method.call("%c", obj).should == "a"
+ end
+
+ it "tries to convert argument to Integer with to_int" do
+ obj = BasicObject.new
+ def obj.to_int
+ 90
+ end
+
+ @method.call("%c", obj).should == "Z"
+ end
+
+ it "raises TypeError if converting to String with to_str returns non-String" do
+ obj = BasicObject.new
+ def obj.to_str
+ :foo
+ end
+
+ -> {
+ @method.call("%c", obj)
+ }.should raise_error(TypeError, /can't convert BasicObject to String/)
+ end
+
+ it "raises TypeError if converting to Integer with to_int returns non-Integer" do
+ obj = BasicObject.new
+ def obj.to_int
+ :foo
+ end
+
+ -> {
+ @method.call("%c", obj)
+ }.should raise_error(TypeError, /can't convert BasicObject to Integer/)
+ end
+ end
+
+ describe "p" do
+ it "displays argument.inspect value" do
+ obj = mock("object")
+ obj.should_receive(:inspect).and_return("<inspect-result>")
+ @method.call("%p", obj).should == "<inspect-result>"
+ end
+
+ it "substitutes 'nil' for nil" do
+ @method.call("%p", nil).should == "nil"
+ end
+ end
+
+ describe "s" do
+ it "substitute argument passes as a string" do
+ @method.call("%s", "abc").should == "abc"
+ end
+
+ it "substitutes '' for nil" do
+ @method.call("%s", nil).should == ""
+ end
+
+ it "converts argument to string with to_s" do
+ obj = mock("string")
+ obj.should_receive(:to_s).and_return("abc")
+ @method.call("%s", obj).should == "abc"
+ end
+
+ it "does not try to convert with to_str" do
+ obj = BasicObject.new
+ def obj.to_str
+ "abc"
+ end
+
+ -> {
+ @method.call("%s", obj)
+ }.should raise_error(NoMethodError)
+ end
+
+ it "formats a partial substring without including omitted characters" do
+ long_string = "aabbccddhelloddccbbaa"
+ sub_string = long_string[8, 5]
+ sprintf("%.#{1 * 3}s", sub_string).should == "hel"
+ end
+
+ it "formats string with precision" do
+ Kernel.format("%.3s", "hello").should == "hel"
+ Kernel.format("%-3.3s", "hello").should == "hel"
+ end
+
+ it "formats string with width" do
+ @method.call("%6s", "abc").should == " abc"
+ @method.call("%6s", "abcdefg").should == "abcdefg"
+ end
+
+ it "formats string with width and precision" do
+ @method.call("%4.6s", "abc").should == " abc"
+ @method.call("%4.6s", "abcdefg").should == "abcdef"
+ end
+
+ it "formats nil with width" do
+ @method.call("%6s", nil).should == " "
+ end
+
+ it "formats nil with precision" do
+ @method.call("%.6s", nil).should == ""
+ end
+
+ it "formats nil with width and precision" do
+ @method.call("%4.6s", nil).should == " "
+ end
+
+ it "formats multibyte string with precision" do
+ Kernel.format("%.2s", "été").should == "ét"
+ end
+
+ it "preserves encoding of the format string" do
+ str = format('%s'.encode(Encoding::UTF_8), 'foobar')
+ str.encoding.should == Encoding::UTF_8
+
+ str = format('%s'.encode(Encoding::US_ASCII), 'foobar')
+ str.encoding.should == Encoding::US_ASCII
+ end
+ end
+
+ describe "%" do
+ it "alone raises an ArgumentError" do
+ -> {
+ @method.call("%")
+ }.should raise_error(ArgumentError)
+ end
+
+ it "is escaped by %" do
+ @method.call("%%").should == "%"
+ @method.call("%%d").should == "%d"
+ end
+ end
+ end
+
+ describe "flags" do
+ describe "space" do
+ context "applies to numeric formats bBdiouxXeEfgGaA" do
+ it "leaves a space at the start of non-negative numbers" do
+ @method.call("% b", 10).should == " 1010"
+ @method.call("% B", 10).should == " 1010"
+ @method.call("% d", 112).should == " 112"
+ @method.call("% i", 112).should == " 112"
+ @method.call("% o", 87).should == " 127"
+ @method.call("% u", 112).should == " 112"
+ @method.call("% x", 196).should == " c4"
+ @method.call("% X", 196).should == " C4"
+
+ @method.call("% e", 109.52).should == " 1.095200e+02"
+ @method.call("% E", 109.52).should == " 1.095200E+02"
+ @method.call("% f", 10.952).should == " 10.952000"
+ @method.call("% g", 12.1234).should == " 12.1234"
+ @method.call("% G", 12.1234).should == " 12.1234"
+ @method.call("% a", 196).should == " 0x1.88p+7"
+ @method.call("% A", 196).should == " 0X1.88P+7"
+ end
+
+ it "does not leave a space at the start of negative numbers" do
+ @method.call("% b", -10).should == "-1010"
+ @method.call("% B", -10).should == "-1010"
+ @method.call("% d", -112).should == "-112"
+ @method.call("% i", -112).should == "-112"
+ @method.call("% o", -87).should == "-127"
+ @method.call("% u", -112).should == "-112"
+ @method.call("% x", -196).should == "-c4"
+ @method.call("% X", -196).should == "-C4"
+
+ @method.call("% e", -109.52).should == "-1.095200e+02"
+ @method.call("% E", -109.52).should == "-1.095200E+02"
+ @method.call("% f", -10.952).should == "-10.952000"
+ @method.call("% g", -12.1234).should == "-12.1234"
+ @method.call("% G", -12.1234).should == "-12.1234"
+ @method.call("% a", -196).should == "-0x1.88p+7"
+ @method.call("% A", -196).should == "-0X1.88P+7"
+ end
+
+ it "prevents converting negative argument to two's complement form" do
+ @method.call("% b", -10).should == "-1010"
+ @method.call("% B", -10).should == "-1010"
+ @method.call("% o", -87).should == "-127"
+ @method.call("% x", -196).should == "-c4"
+ @method.call("% X", -196).should == "-C4"
+ end
+
+ it "treats several white spaces as one" do
+ @method.call("% b", 10).should == " 1010"
+ @method.call("% B", 10).should == " 1010"
+ @method.call("% d", 112).should == " 112"
+ @method.call("% i", 112).should == " 112"
+ @method.call("% o", 87).should == " 127"
+ @method.call("% u", 112).should == " 112"
+ @method.call("% x", 196).should == " c4"
+ @method.call("% X", 196).should == " C4"
+
+ @method.call("% e", 109.52).should == " 1.095200e+02"
+ @method.call("% E", 109.52).should == " 1.095200E+02"
+ @method.call("% f", 10.952).should == " 10.952000"
+ @method.call("% g", 12.1234).should == " 12.1234"
+ @method.call("% G", 12.1234).should == " 12.1234"
+ @method.call("% a", 196).should == " 0x1.88p+7"
+ @method.call("% A", 196).should == " 0X1.88P+7"
+ end
+ end
+ end
+
+ describe "(digit)$" do
+ it "specifies the absolute argument number for this field" do
+ @method.call("%2$b", 0, 10).should == "1010"
+ @method.call("%2$B", 0, 10).should == "1010"
+ @method.call("%2$d", 0, 112).should == "112"
+ @method.call("%2$i", 0, 112).should == "112"
+ @method.call("%2$o", 0, 87).should == "127"
+ @method.call("%2$u", 0, 112).should == "112"
+ @method.call("%2$x", 0, 196).should == "c4"
+ @method.call("%2$X", 0, 196).should == "C4"
+
+ @method.call("%2$e", 0, 109.52).should == "1.095200e+02"
+ @method.call("%2$E", 0, 109.52).should == "1.095200E+02"
+ @method.call("%2$f", 0, 10.952).should == "10.952000"
+ @method.call("%2$g", 0, 12.1234).should == "12.1234"
+ @method.call("%2$G", 0, 12.1234).should == "12.1234"
+ @method.call("%2$a", 0, 196).should == "0x1.88p+7"
+ @method.call("%2$A", 0, 196).should == "0X1.88P+7"
+
+ @method.call("%2$c", 1, 97).should == "a"
+ @method.call("%2$p", "a", []).should == "[]"
+ @method.call("%2$s", "-", "abc").should == "abc"
+ end
+
+ it "raises exception if argument number is bigger than actual arguments list" do
+ -> {
+ @method.call("%4$d", 1, 2, 3)
+ }.should raise_error(ArgumentError)
+ end
+
+ it "ignores '-' sign" do
+ @method.call("%2$d", 1, 2, 3).should == "2"
+ @method.call("%-2$d", 1, 2, 3).should == "2"
+ end
+
+ it "raises ArgumentError exception when absolute and relative argument numbers are mixed" do
+ -> {
+ @method.call("%1$d %d", 1, 2)
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "#" do
+ context "applies to format o" do
+ it "increases the precision until the first digit will be `0' if it is not formatted as complements" do
+ @method.call("%#o", 87).should == "0127"
+ end
+
+ it "does nothing for negative argument" do
+ @method.call("%#o", -87).should == "..7651"
+ end
+ end
+
+ context "applies to formats bBxX" do
+ it "prefixes the result with 0x, 0X, 0b and 0B respectively for non-zero argument" do
+ @method.call("%#b", 10).should == "0b1010"
+ @method.call("%#b", -10).should == "0b..10110"
+ @method.call("%#B", 10).should == "0B1010"
+ @method.call("%#B", -10).should == "0B..10110"
+
+ @method.call("%#x", 196).should == "0xc4"
+ @method.call("%#x", -196).should == "0x..f3c"
+ @method.call("%#X", 196).should == "0XC4"
+ @method.call("%#X", -196).should == "0X..F3C"
+ end
+
+ it "does nothing for zero argument" do
+ @method.call("%#b", 0).should == "0"
+ @method.call("%#B", 0).should == "0"
+
+ @method.call("%#o", 0).should == "0"
+
+ @method.call("%#x", 0).should == "0"
+ @method.call("%#X", 0).should == "0"
+ end
+ end
+
+ context "applies to formats aAeEfgG" do
+ it "forces a decimal point to be added, even if no digits follow" do
+ @method.call("%#.0a", 16.25).should == "0x1.p+4"
+ @method.call("%#.0A", 16.25).should == "0X1.P+4"
+
+ @method.call("%#.0e", 100).should == "1.e+02"
+ @method.call("%#.0E", 100).should == "1.E+02"
+
+ @method.call("%#.0f", 123.4).should == "123."
+
+ @method.call("%#g", 123456).should == "123456."
+ @method.call("%#G", 123456).should == "123456."
+ end
+
+ it "changes format from dd.dddd to exponential form for gG" do
+ @method.call("%#.0g", 123.4).should_not == "123."
+ @method.call("%#.0g", 123.4).should == "1.e+02"
+ end
+ end
+
+ context "applies to gG" do
+ it "does not remove trailing zeros" do
+ @method.call("%#g", 123.4).should == "123.400"
+ @method.call("%#g", 123.4).should == "123.400"
+ end
+ end
+ end
+
+ describe "+" do
+ context "applies to numeric formats bBdiouxXaAeEfgG" do
+ it "adds a leading plus sign to non-negative numbers" do
+ @method.call("%+b", 10).should == "+1010"
+ @method.call("%+B", 10).should == "+1010"
+ @method.call("%+d", 112).should == "+112"
+ @method.call("%+i", 112).should == "+112"
+ @method.call("%+o", 87).should == "+127"
+ @method.call("%+u", 112).should == "+112"
+ @method.call("%+x", 196).should == "+c4"
+ @method.call("%+X", 196).should == "+C4"
+
+ @method.call("%+e", 109.52).should == "+1.095200e+02"
+ @method.call("%+E", 109.52).should == "+1.095200E+02"
+ @method.call("%+f", 10.952).should == "+10.952000"
+ @method.call("%+g", 12.1234).should == "+12.1234"
+ @method.call("%+G", 12.1234).should == "+12.1234"
+ @method.call("%+a", 196).should == "+0x1.88p+7"
+ @method.call("%+A", 196).should == "+0X1.88P+7"
+ end
+
+ it "does not use two's complement form for negative numbers for formats bBoxX" do
+ @method.call("%+b", -10).should == "-1010"
+ @method.call("%+B", -10).should == "-1010"
+ @method.call("%+o", -87).should == "-127"
+ @method.call("%+x", -196).should == "-c4"
+ @method.call("%+X", -196).should == "-C4"
+ end
+ end
+ end
+
+ describe "-" do
+ it "left-justifies the result of conversion if width is specified" do
+ @method.call("%-10b", 10).should == "1010 "
+ @method.call("%-10B", 10).should == "1010 "
+ @method.call("%-10d", 112).should == "112 "
+ @method.call("%-10i", 112).should == "112 "
+ @method.call("%-10o", 87).should == "127 "
+ @method.call("%-10u", 112).should == "112 "
+ @method.call("%-10x", 196).should == "c4 "
+ @method.call("%-10X", 196).should == "C4 "
+
+ @method.call("%-20e", 109.52).should == "1.095200e+02 "
+ @method.call("%-20E", 109.52).should == "1.095200E+02 "
+ @method.call("%-20f", 10.952).should == "10.952000 "
+ @method.call("%-20g", 12.1234).should == "12.1234 "
+ @method.call("%-20G", 12.1234).should == "12.1234 "
+ @method.call("%-20a", 196).should == "0x1.88p+7 "
+ @method.call("%-20A", 196).should == "0X1.88P+7 "
+
+ @method.call("%-10c", 97).should == "a "
+ @method.call("%-10p", []).should == "[] "
+ @method.call("%-10s", "abc").should == "abc "
+ end
+ end
+
+ describe "0 (zero)" do
+ context "applies to numeric formats bBdiouxXaAeEfgG and width is specified" do
+ it "pads with zeros, not spaces" do
+ @method.call("%010b", 10).should == "0000001010"
+ @method.call("%010B", 10).should == "0000001010"
+ @method.call("%010d", 112).should == "0000000112"
+ @method.call("%010i", 112).should == "0000000112"
+ @method.call("%010o", 87).should == "0000000127"
+ @method.call("%010u", 112).should == "0000000112"
+ @method.call("%010x", 196).should == "00000000c4"
+ @method.call("%010X", 196).should == "00000000C4"
+
+ @method.call("%020e", 109.52).should == "000000001.095200e+02"
+ @method.call("%020E", 109.52).should == "000000001.095200E+02"
+ @method.call("%020f", 10.952).should == "0000000000010.952000"
+ @method.call("%020g", 12.1234).should == "000000000000012.1234"
+ @method.call("%020G", 12.1234).should == "000000000000012.1234"
+ @method.call("%020a", 196).should == "0x000000000001.88p+7"
+ @method.call("%020A", 196).should == "0X000000000001.88P+7"
+ end
+
+ it "uses radix-1 when displays negative argument as a two's complement" do
+ @method.call("%010b", -10).should == "..11110110"
+ @method.call("%010B", -10).should == "..11110110"
+ @method.call("%010o", -87).should == "..77777651"
+ @method.call("%010x", -196).should == "..ffffff3c"
+ @method.call("%010X", -196).should == "..FFFFFF3C"
+ end
+ end
+ end
+
+ describe "*" do
+ it "uses the previous argument as the field width" do
+ @method.call("%*b", 10, 10).should == " 1010"
+ @method.call("%*B", 10, 10).should == " 1010"
+ @method.call("%*d", 10, 112).should == " 112"
+ @method.call("%*i", 10, 112).should == " 112"
+ @method.call("%*o", 10, 87).should == " 127"
+ @method.call("%*u", 10, 112).should == " 112"
+ @method.call("%*x", 10, 196).should == " c4"
+ @method.call("%*X", 10, 196).should == " C4"
+
+ @method.call("%*e", 20, 109.52).should == " 1.095200e+02"
+ @method.call("%*E", 20, 109.52).should == " 1.095200E+02"
+ @method.call("%*f", 20, 10.952).should == " 10.952000"
+ @method.call("%*g", 20, 12.1234).should == " 12.1234"
+ @method.call("%*G", 20, 12.1234).should == " 12.1234"
+ @method.call("%*a", 20, 196).should == " 0x1.88p+7"
+ @method.call("%*A", 20, 196).should == " 0X1.88P+7"
+
+ @method.call("%*c", 10, 97).should == " a"
+ @method.call("%*p", 10, []).should == " []"
+ @method.call("%*s", 10, "abc").should == " abc"
+ end
+
+ it "left-justifies the result if width is negative" do
+ @method.call("%*b", -10, 10).should == "1010 "
+ @method.call("%*B", -10, 10).should == "1010 "
+ @method.call("%*d", -10, 112).should == "112 "
+ @method.call("%*i", -10, 112).should == "112 "
+ @method.call("%*o", -10, 87).should == "127 "
+ @method.call("%*u", -10, 112).should == "112 "
+ @method.call("%*x", -10, 196).should == "c4 "
+ @method.call("%*X", -10, 196).should == "C4 "
+
+ @method.call("%*e", -20, 109.52).should == "1.095200e+02 "
+ @method.call("%*E", -20, 109.52).should == "1.095200E+02 "
+ @method.call("%*f", -20, 10.952).should == "10.952000 "
+ @method.call("%*g", -20, 12.1234).should == "12.1234 "
+ @method.call("%*G", -20, 12.1234).should == "12.1234 "
+ @method.call("%*a", -20, 196).should == "0x1.88p+7 "
+ @method.call("%*A", -20, 196).should == "0X1.88P+7 "
+
+ @method.call("%*c", -10, 97).should == "a "
+ @method.call("%*p", -10, []).should == "[] "
+ @method.call("%*s", -10, "abc").should == "abc "
+ end
+
+ it "uses the specified argument as the width if * is followed by a number and $" do
+ @method.call("%1$*2$b", 10, 10).should == " 1010"
+ @method.call("%1$*2$B", 10, 10).should == " 1010"
+ @method.call("%1$*2$d", 112, 10).should == " 112"
+ @method.call("%1$*2$i", 112, 10).should == " 112"
+ @method.call("%1$*2$o", 87, 10).should == " 127"
+ @method.call("%1$*2$u", 112, 10).should == " 112"
+ @method.call("%1$*2$x", 196, 10).should == " c4"
+ @method.call("%1$*2$X", 196, 10).should == " C4"
+
+ @method.call("%1$*2$e", 109.52, 20).should == " 1.095200e+02"
+ @method.call("%1$*2$E", 109.52, 20).should == " 1.095200E+02"
+ @method.call("%1$*2$f", 10.952, 20).should == " 10.952000"
+ @method.call("%1$*2$g", 12.1234, 20).should == " 12.1234"
+ @method.call("%1$*2$G", 12.1234, 20).should == " 12.1234"
+ @method.call("%1$*2$a", 196, 20).should == " 0x1.88p+7"
+ @method.call("%1$*2$A", 196, 20).should == " 0X1.88P+7"
+
+ @method.call("%1$*2$c", 97, 10).should == " a"
+ @method.call("%1$*2$p", [], 10).should == " []"
+ @method.call("%1$*2$s", "abc", 10).should == " abc"
+ end
+
+ it "left-justifies the result if specified with $ argument is negative" do
+ @method.call("%1$*2$b", 10, -10).should == "1010 "
+ @method.call("%1$*2$B", 10, -10).should == "1010 "
+ @method.call("%1$*2$d", 112, -10).should == "112 "
+ @method.call("%1$*2$i", 112, -10).should == "112 "
+ @method.call("%1$*2$o", 87, -10).should == "127 "
+ @method.call("%1$*2$u", 112, -10).should == "112 "
+ @method.call("%1$*2$x", 196, -10).should == "c4 "
+ @method.call("%1$*2$X", 196, -10).should == "C4 "
+
+ @method.call("%1$*2$e", 109.52, -20).should == "1.095200e+02 "
+ @method.call("%1$*2$E", 109.52, -20).should == "1.095200E+02 "
+ @method.call("%1$*2$f", 10.952, -20).should == "10.952000 "
+ @method.call("%1$*2$g", 12.1234, -20).should == "12.1234 "
+ @method.call("%1$*2$G", 12.1234, -20).should == "12.1234 "
+ @method.call("%1$*2$a", 196, -20).should == "0x1.88p+7 "
+ @method.call("%1$*2$A", 196, -20).should == "0X1.88P+7 "
+
+ @method.call("%1$*2$c", 97, -10).should == "a "
+ @method.call("%1$*2$p", [], -10).should == "[] "
+ @method.call("%1$*2$s", "abc", -10).should == "abc "
+ end
+
+ it "raises ArgumentError when is mixed with width" do
+ -> {
+ @method.call("%*10d", 10, 112)
+ }.should raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe "width" do
+ it "specifies the minimum number of characters that will be written to the result" do
+ @method.call("%10b", 10).should == " 1010"
+ @method.call("%10B", 10).should == " 1010"
+ @method.call("%10d", 112).should == " 112"
+ @method.call("%10i", 112).should == " 112"
+ @method.call("%10o", 87).should == " 127"
+ @method.call("%10u", 112).should == " 112"
+ @method.call("%10x", 196).should == " c4"
+ @method.call("%10X", 196).should == " C4"
+
+ @method.call("%20e", 109.52).should == " 1.095200e+02"
+ @method.call("%20E", 109.52).should == " 1.095200E+02"
+ @method.call("%20f", 10.952).should == " 10.952000"
+ @method.call("%20g", 12.1234).should == " 12.1234"
+ @method.call("%20G", 12.1234).should == " 12.1234"
+ @method.call("%20a", 196).should == " 0x1.88p+7"
+ @method.call("%20A", 196).should == " 0X1.88P+7"
+
+ @method.call("%10c", 97).should == " a"
+ @method.call("%10p", []).should == " []"
+ @method.call("%10s", "abc").should == " abc"
+ end
+
+ it "is ignored if argument's actual length is greater" do
+ @method.call("%5d", 1234567890).should == "1234567890"
+ end
+ end
+
+ describe "precision" do
+ context "integer types" do
+ it "controls the number of decimal places displayed" do
+ @method.call("%.6b", 10).should == "001010"
+ @method.call("%.6B", 10).should == "001010"
+ @method.call("%.5d", 112).should == "00112"
+ @method.call("%.5i", 112).should == "00112"
+ @method.call("%.5o", 87).should == "00127"
+ @method.call("%.5u", 112).should == "00112"
+
+ @method.call("%.5x", 196).should == "000c4"
+ @method.call("%.5X", 196).should == "000C4"
+ end
+ end
+
+ context "float types" do
+ it "controls the number of decimal places displayed in fraction part" do
+ @method.call("%.10e", 109.52).should == "1.0952000000e+02"
+ @method.call("%.10E", 109.52).should == "1.0952000000E+02"
+ @method.call("%.10f", 10.952).should == "10.9520000000"
+ @method.call("%.10a", 196).should == "0x1.8800000000p+7"
+ @method.call("%.10A", 196).should == "0X1.8800000000P+7"
+ end
+
+ it "does not affect G format" do
+ @method.call("%.10g", 12.1234).should == "12.1234"
+ @method.call("%.10g", 123456789).should == "123456789"
+ end
+ end
+
+ context "string formats" do
+ it "determines the maximum number of characters to be copied from the string" do
+ @method.call("%.1p", [1]).should == "["
+ @method.call("%.2p", [1]).should == "[1"
+ @method.call("%.10p", [1]).should == "[1]"
+ @method.call("%.0p", [1]).should == ""
+
+ @method.call("%.1s", "abc").should == "a"
+ @method.call("%.2s", "abc").should == "ab"
+ @method.call("%.10s", "abc").should == "abc"
+ @method.call("%.0s", "abc").should == ""
+ end
+ end
+ end
+
+ describe "reference by name" do
+ describe "%<name>s style" do
+ it "uses value passed in a hash argument" do
+ @method.call("%<foo>d", foo: 123).should == "123"
+ end
+
+ it "supports flags, width, precision and type" do
+ @method.call("%+20.10<foo>f", foo: 10.952).should == " +10.9520000000"
+ end
+
+ it "allows to place name in any position" do
+ @method.call("%+15.5<foo>f", foo: 10.952).should == " +10.95200"
+ @method.call("%+15<foo>.5f", foo: 10.952).should == " +10.95200"
+ @method.call("%+<foo>15.5f", foo: 10.952).should == " +10.95200"
+ @method.call("%<foo>+15.5f", foo: 10.952).should == " +10.95200"
+ end
+
+ it "cannot be mixed with unnamed style" do
+ -> {
+ @method.call("%d %<foo>d", 1, foo: "123")
+ }.should raise_error(ArgumentError)
+ end
+ end
+
+ describe "%{name} style" do
+ it "uses value passed in a hash argument" do
+ @method.call("%{foo}", foo: 123).should == "123"
+ end
+
+ it "does not support type style" do
+ @method.call("%{foo}d", foo: 123).should == "123d"
+ end
+
+ it "supports flags, width and precision" do
+ @method.call("%-20.5{foo}", foo: "123456789").should == "12345 "
+ end
+
+ it "cannot be mixed with unnamed style" do
+ -> {
+ @method.call("%d %{foo}", 1, foo: "123")
+ }.should raise_error(ArgumentError)
+ end
+
+ it "raises KeyError when there is no matching key" do
+ -> {
+ @method.call("%{foo}", {})
+ }.should raise_error(KeyError)
+ end
+
+ it "converts value to String with to_s" do
+ obj = Object.new
+ def obj.to_s; end
+ def obj.to_str; end
+
+ obj.should_receive(:to_s).and_return("42")
+ obj.should_not_receive(:to_str)
+
+ @method.call("%{foo}", foo: obj).should == "42"
+ end
+ end
+ end
+
+ describe "faulty key" do
+ before :each do
+ @object = { foooo: 1 }
+ end
+
+ it "raises a KeyError" do
+ -> {
+ @method.call("%<foo>s", @object)
+ }.should raise_error(KeyError)
+ end
+
+ it "sets the Hash as the receiver of KeyError" do
+ -> {
+ @method.call("%<foo>s", @object)
+ }.should raise_error(KeyError) { |err|
+ err.receiver.should equal(@object)
+ }
+ end
+
+ it "sets the unmatched key as the key of KeyError" do
+ -> {
+ @method.call("%<foo>s", @object)
+ }.should raise_error(KeyError) { |err|
+ err.key.to_s.should == 'foo'
+ }
+ end
+ end
+
+ it "does not raise error when passed more arguments than needed" do
+ sprintf("%s %d %c", "string", 2, "c", []).should == "string 2 c"
+ end
+end
diff --git a/spec/ruby/core/kernel/shared/sprintf_encoding.rb b/spec/ruby/core/kernel/shared/sprintf_encoding.rb
new file mode 100644
index 0000000000..7ec0fe4c48
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/sprintf_encoding.rb
@@ -0,0 +1,67 @@
+# Keep encoding-related specs in a separate shared example to be able to skip them in IO/File/StringIO specs.
+# It's difficult to check result's encoding in the test after writing to a file/io buffer.
+describe :kernel_sprintf_encoding, shared: true do
+ it "can produce a string with valid encoding" do
+ string = @method.call("good day %{valid}", valid: "e")
+ string.encoding.should == Encoding::UTF_8
+ string.valid_encoding?.should be_true
+ end
+
+ it "can produce a string with invalid encoding" do
+ string = @method.call("good day %{invalid}", invalid: "\x80")
+ string.encoding.should == Encoding::UTF_8
+ string.valid_encoding?.should be_false
+ end
+
+ it "returns a String in the same encoding as the format String if compatible" do
+ string = "%s".dup.force_encoding(Encoding::KOI8_U)
+ result = @method.call(string, "dogs")
+ result.encoding.should equal(Encoding::KOI8_U)
+ end
+
+ it "returns a String in the argument's encoding if format encoding is more restrictive" do
+ string = "foo %s".dup.force_encoding(Encoding::US_ASCII)
+ argument = "b\303\274r".dup.force_encoding(Encoding::UTF_8)
+
+ result = @method.call(string, argument)
+ result.encoding.should equal(Encoding::UTF_8)
+ end
+
+ it "raises Encoding::CompatibilityError if both encodings are ASCII compatible and there are not ASCII characters" do
+ string = "Ä %s".encode('windows-1252')
+ argument = "Ђ".encode('windows-1251')
+
+ -> {
+ @method.call(string, argument)
+ }.should raise_error(Encoding::CompatibilityError)
+ end
+
+ describe "%c" do
+ it "supports Unicode characters" do
+ result = @method.call("%c", 1286)
+ result.should == "Ԇ"
+ result.bytes.should == [212, 134]
+
+ result = @method.call("%c", "ش")
+ result.should == "ش"
+ result.bytes.should == [216, 180]
+ end
+
+ it "raises error when a codepoint isn't representable in an encoding of a format string" do
+ format = "%c".encode("ASCII")
+
+ -> {
+ @method.call(format, 1286)
+ }.should raise_error(RangeError, /out of char range/)
+ end
+
+ it "uses the encoding of the format string to interpret codepoints" do
+ format = "%c".dup.force_encoding("euc-jp")
+ result = @method.call(format, 9415601)
+
+ result.encoding.should == Encoding::EUC_JP
+ result.should == "é".encode(Encoding::EUC_JP)
+ result.bytes.should == [143, 171, 177]
+ end
+ end
+end
diff --git a/spec/ruby/core/kernel/shared/then.rb b/spec/ruby/core/kernel/shared/then.rb
new file mode 100644
index 0000000000..b52075371f
--- /dev/null
+++ b/spec/ruby/core/kernel/shared/then.rb
@@ -0,0 +1,20 @@
+describe :kernel_then, shared: true do
+ it "yields self" do
+ object = Object.new
+ object.send(@method) { |o| o.should equal object }
+ end
+
+ it "returns the block return value" do
+ object = Object.new
+ object.send(@method) { 42 }.should equal 42
+ end
+
+ it "returns a sized Enumerator when no block given" do
+ object = Object.new
+ enum = object.send(@method)
+ enum.should be_an_instance_of Enumerator
+ enum.size.should equal 1
+ enum.peek.should equal object
+ enum.first.should equal object
+ 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..23c400f9bd
--- /dev/null
+++ b/spec/ruby/core/kernel/singleton_class_spec.rb
@@ -0,0 +1,74 @@
+# truffleruby_primitives: true
+require_relative '../../spec_helper'
+
+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 Integer" do
+ -> { 123.singleton_class }.should raise_error(TypeError, "can't define singleton")
+ end
+
+ it "raises TypeError for Float" do
+ -> { 3.14.singleton_class }.should raise_error(TypeError, "can't define singleton")
+ end
+
+ it "raises TypeError for Symbol" do
+ -> { :foo.singleton_class }.should raise_error(TypeError, "can't define singleton")
+ end
+
+ it "raises TypeError for a frozen deduplicated String" do
+ -> { (-"string").singleton_class }.should raise_error(TypeError, "can't define singleton")
+ -> { a = -"string"; a.singleton_class }.should raise_error(TypeError, "can't define singleton")
+ -> { a = "string"; (-a).singleton_class }.should raise_error(TypeError, "can't define singleton")
+ end
+
+ it "returns a frozen singleton class if object is frozen" do
+ obj = Object.new
+ obj.freeze
+ obj.singleton_class.frozen?.should be_true
+ end
+
+ context "for an IO object with a replaced singleton class" do
+ it "looks up singleton methods from the fresh singleton class after an object instance got a new one" do
+ proxy = -> io { io.foo }
+ if RUBY_ENGINE == 'truffleruby'
+ # We need an inline cache with only this object seen, the best way to do that is to use a Primitive
+ sclass = -> io { Primitive.singleton_class(io) }
+ else
+ sclass = -> io { io.singleton_class }
+ end
+
+ io = File.new(__FILE__)
+ io.define_singleton_method(:foo) { "old" }
+ sclass1 = sclass.call(io)
+ proxy.call(io).should == "old"
+
+ # IO#reopen is the only method which can replace an object's singleton class
+ io2 = File.new(__FILE__)
+ io.reopen(io2)
+ io.define_singleton_method(:foo) { "new" }
+ sclass2 = sclass.call(io)
+ sclass2.should_not.equal?(sclass1)
+ proxy.call(io).should == "new"
+ ensure
+ io2.close
+ io.close
+ end
+ 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..7d63fa7cc6
--- /dev/null
+++ b/spec/ruby/core/kernel/singleton_method_spec.rb
@@ -0,0 +1,85 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#singleton_method" do
+ it "finds 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
+
+ ruby_bug "#20620", ""..."3.4" do
+ it "finds a method defined in a module included in the singleton class" do
+ m = Module.new do
+ def foo
+ :foo
+ end
+ end
+
+ obj = Object.new
+ obj.singleton_class.include(m)
+
+ obj.singleton_method(:foo).should be_an_instance_of(Method)
+ obj.singleton_method(:foo).call.should == :foo
+ end
+
+ it "finds a method defined in a module prepended in the singleton class" do
+ m = Module.new do
+ def foo
+ :foo
+ end
+ end
+
+ obj = Object.new
+ obj.singleton_class.prepend(m)
+
+ obj.singleton_method(:foo).should be_an_instance_of(Method)
+ obj.singleton_method(:foo).call.should == :foo
+ end
+
+ it "finds a method defined in a module that an object is extended with" do
+ m = Module.new do
+ def foo
+ :foo
+ end
+ end
+
+ obj = Object.new
+ obj.extend(m)
+
+ obj.singleton_method(:foo).should be_an_instance_of(Method)
+ obj.singleton_method(:foo).call.should == :foo
+ end
+ 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..a127a439de
--- /dev/null
+++ b/spec/ruby/core/kernel/singleton_methods_spec.rb
@@ -0,0 +1,192 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/reflection'
+require_relative 'fixtures/classes'
+
+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
+
+ it "for a module does not return methods in a module prepended to Module itself" do
+ require_relative 'fixtures/singleton_methods'
+ mod = SingletonMethodsSpecs::SelfExtending
+ mod.method(:mspec_test_kernel_singleton_methods).owner.should == SingletonMethodsSpecs::Prepended
+
+ ancestors = mod.singleton_class.ancestors
+ ancestors[0...2].should == [ mod.singleton_class, mod ]
+ ancestors.should include(SingletonMethodsSpecs::Prepended)
+
+ # Do not search prepended modules of `Module`, as that's a non-singleton class
+ mod.singleton_methods.should == []
+ end
+end
+
+describe :kernel_singleton_methods_supers, shared: true do
+ it "returns the names of singleton methods for an object extended 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 extended 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 extended 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 extended 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 extended with a module" do
+ ReflectSpecs.oe.singleton_methods(false).should == []
+ end
+
+ it "returns an empty Array for an object extended 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..e9c600aac4
--- /dev/null
+++ b/spec/ruby/core/kernel/sleep_spec.rb
@@ -0,0 +1,126 @@
+require_relative '../../spec_helper'
+require_relative '../fiber/fixtures/scheduler'
+
+describe "Kernel#sleep" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:sleep)
+ end
+
+ it "returns an Integer" do
+ sleep(0.001).should be_kind_of(Integer)
+ end
+
+ it "accepts a Float" do
+ sleep(0.001).should >= 0
+ end
+
+ it "accepts an Integer" do
+ sleep(0).should >= 0
+ end
+
+ it "accepts a Rational" do
+ sleep(Rational(1, 999)).should >= 0
+ end
+
+ it "accepts any Object that responds to divmod" do
+ o = Object.new
+ def o.divmod(*); [0, 0.001]; end
+ sleep(o).should >= 0
+ end
+
+ it "raises an ArgumentError when passed a negative duration" do
+ -> { sleep(-0.1) }.should raise_error(ArgumentError)
+ -> { sleep(-1) }.should raise_error(ArgumentError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ -> { 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
+
+ it "sleeps with nanosecond precision" do
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ 100.times do
+ sleep(0.0001)
+ end
+ end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+
+ actual_duration = end_time - start_time
+ actual_duration.should > 0.01 # 100 * 0.0001 => 0.01
+ end
+
+ ruby_version_is ""..."3.3" do
+ it "raises a TypeError when passed nil" do
+ -> { sleep(nil) }.should raise_error(TypeError)
+ end
+ end
+
+ ruby_version_is "3.3" do
+ it "accepts a nil duration" do
+ running = false
+ t = Thread.new do
+ running = true
+ sleep(nil)
+ 5
+ end
+
+ Thread.pass until running
+ Thread.pass while t.status and t.status != "sleep"
+
+ t.wakeup
+ t.value.should == 5
+ end
+ end
+
+ context "Kernel.sleep with Fiber scheduler" do
+ before :each do
+ Fiber.set_scheduler(FiberSpecs::LoggingScheduler.new)
+ end
+
+ after :each do
+ Fiber.set_scheduler(nil)
+ end
+
+ it "calls the scheduler without arguments when no duration is given" do
+ sleeper = Fiber.new(blocking: false) do
+ sleep
+ end
+ sleeper.resume
+ Fiber.scheduler.events.should == [{ event: :kernel_sleep, fiber: sleeper, args: [] }]
+ end
+
+ it "calls the scheduler with the given duration" do
+ sleeper = Fiber.new(blocking: false) do
+ sleep(0.01)
+ end
+ sleeper.resume
+ Fiber.scheduler.events.should == [{ event: :kernel_sleep, fiber: sleeper, args: [0.01] }]
+ end
+
+ it "does not call the scheduler if the fiber is blocking" do
+ sleeper = Fiber.new(blocking: true) do
+ sleep(0.01)
+ end
+ sleeper.resume
+ Fiber.scheduler.events.should == []
+ end
+ 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..ba05b629d5
--- /dev/null
+++ b/spec/ruby/core/kernel/spawn_spec.rb
@@ -0,0 +1,25 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# 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
+ -> {
+ Process.wait spawn("echo spawn")
+ }.should output_to_fd("spawn\n")
+ end
+end
+
+describe "Kernel.spawn" do
+ it "executes the given command" do
+ -> {
+ 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..5a4a90ff7a
--- /dev/null
+++ b/spec/ruby/core/kernel/sprintf_spec.rb
@@ -0,0 +1,64 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/sprintf'
+require_relative 'shared/sprintf_encoding'
+
+describe :kernel_sprintf_to_str, shared: true do
+ it "calls #to_str to convert the format object to a String" do
+ obj = mock('format string')
+ obj.should_receive(:to_str).and_return("to_str: %i")
+ @method.call(obj, 42).should == "to_str: 42"
+ end
+end
+
+describe "Kernel#sprintf" do
+ it_behaves_like :kernel_sprintf, -> format, *args {
+ r = nil
+ -> {
+ r = sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
+ }
+
+ it_behaves_like :kernel_sprintf_encoding, -> format, *args {
+ r = nil
+ -> {
+ r = sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
+ }
+
+ it_behaves_like :kernel_sprintf_to_str, -> format, *args {
+ r = nil
+ -> {
+ r = sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
+ }
+end
+
+describe "Kernel.sprintf" do
+ it_behaves_like :kernel_sprintf, -> format, *args {
+ r = nil
+ -> {
+ r = Kernel.sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
+ }
+
+ it_behaves_like :kernel_sprintf_encoding, -> format, *args {
+ r = nil
+ -> {
+ r = Kernel.sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
+ }
+
+ it_behaves_like :kernel_sprintf_to_str, -> format, *args {
+ r = nil
+ -> {
+ r = Kernel.sprintf(format, *args)
+ }.should_not complain(verbose: true)
+ r
+ }
+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..95bb406f46
--- /dev/null
+++ b/spec/ruby/core/kernel/srand_spec.rb
@@ -0,0 +1,73 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#srand" do
+ before :each do
+ @seed = srand
+ end
+
+ after :each do
+ srand(@seed)
+ end
+
+ 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 "returns the system-initialized seed value on the first call" do
+ ruby_exe('print srand(10)', options: '--disable-gems').should =~ /\A\d+\z/
+ 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
+ -> { 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 an Integer 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
+ -> { srand(nil) }.should raise_error(TypeError)
+ end
+
+ it "raises a TypeError when passed a String" do
+ -> { 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..9130bd159c
--- /dev/null
+++ b/spec/ruby/core/kernel/sub_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+# 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..32d07b3ae2
--- /dev/null
+++ b/spec/ruby/core/kernel/syscall_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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..9bc03924dd
--- /dev/null
+++ b/spec/ruby/core/kernel/system_spec.rb
@@ -0,0 +1,132 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe :kernel_system, shared: true do
+ it "executes the specified command in a subprocess" do
+ -> { @object.system("echo a") }.should output_to_fd("a\n")
+
+ $?.should be_an_instance_of Process::Status
+ $?.should.success?
+ 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
+ $?.should.success?
+ $?.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
+ $?.should_not.success?
+ $?.exitstatus.should == 1
+ end
+
+ it "raises RuntimeError when `exception: true` is given and the command exits with a non-zero exit status" do
+ -> { @object.system(ruby_cmd('exit 1'), exception: true) }.should raise_error(RuntimeError)
+ end
+
+ it "raises Errno::ENOENT when `exception: true` is given and the specified command does not exist" do
+ -> { @object.system('feature_14386', exception: true) }.should raise_error(Errno::ENOENT)
+ 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)
+ $?.should_not.success?
+ end
+
+ it "does not write to stderr when command execution fails" do
+ -> { @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
+ -> { @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"
+ -> { @object.system("echo $0") }.should output_to_fd("sh\n")
+ end
+ end
+
+ platform_is_not :windows do
+ before :each do
+ require 'tmpdir'
+ @shell_command = File.join(Dir.mktmpdir, "noshebang.cmd")
+ File.write(@shell_command, %[echo "$PATH"\n], perm: 0o700)
+ end
+
+ after :each do
+ File.unlink(@shell_command)
+ Dir.rmdir(File.dirname(@shell_command))
+ end
+
+ it "executes with `sh` if the command is executable but not binary and there is no shebang" do
+ -> { @object.system(@shell_command) }.should output_to_fd(ENV['PATH'] + "\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
+ -> { @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
+ -> { @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
+ -> { @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..9a2efbaea0
--- /dev/null
+++ b/spec/ruby/core/kernel/taint_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#taint" do
+ it "has been removed" do
+ Object.new.should_not.respond_to?(:taint)
+ 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..837eb1dafb
--- /dev/null
+++ b/spec/ruby/core/kernel/tainted_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#tainted?" do
+ it "has been removed" do
+ Object.new.should_not.respond_to?(:tainted?)
+ 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..f7720a6dc7
--- /dev/null
+++ b/spec/ruby/core/kernel/tap_spec.rb
@@ -0,0 +1,13 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+ -> { 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..d26dc06361
--- /dev/null
+++ b/spec/ruby/core/kernel/test_spec.rb
@@ -0,0 +1,109 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#test" do
+ before :all do
+ @file = __dir__ + '/fixtures/classes.rb'
+ @dir = __dir__ + '/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/then_spec.rb b/spec/ruby/core/kernel/then_spec.rb
new file mode 100644
index 0000000000..8109a2960a
--- /dev/null
+++ b/spec/ruby/core/kernel/then_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/then'
+
+describe "Kernel#then" do
+ it_behaves_like :kernel_then, :then
+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..64bfccb413
--- /dev/null
+++ b/spec/ruby/core/kernel/throw_spec.rb
@@ -0,0 +1,80 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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 symbol" 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
+ -> { throw :blah }.should raise_error(ArgumentError)
+ end
+
+ it "raises an UncaughtThrowError if there is no catch block for the symbol" do
+ -> { throw :blah }.should raise_error(UncaughtThrowError)
+ end
+
+ it "raises ArgumentError if 3 or more arguments provided" do
+ -> {
+ catch :blah do
+ throw :blah, :return_value, 2
+ end
+ }.should raise_error(ArgumentError)
+
+ -> {
+ catch :blah do
+ throw :blah, :return_value, 2, 3, 4, 5
+ end
+ }.should raise_error(ArgumentError)
+ end
+
+ it "can throw an object" do
+ -> {
+ 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..9d9945450f
--- /dev/null
+++ b/spec/ruby/core/kernel/to_enum_spec.rb
@@ -0,0 +1,5 @@
+require_relative '../../spec_helper'
+
+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..ea4b00151e
--- /dev/null
+++ b/spec/ruby/core/kernel/to_s_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#to_s" do
+ it "returns a String containing the name of self's class" do
+ Object.new.to_s.should =~ /Object/
+ 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..3c84aa5e60
--- /dev/null
+++ b/spec/ruby/core/kernel/trace_var_spec.rb
@@ -0,0 +1,54 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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
+ -> 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..4c801a7215
--- /dev/null
+++ b/spec/ruby/core/kernel/trap_spec.rb
@@ -0,0 +1,9 @@
+require_relative '../../spec_helper'
+
+describe "Kernel#trap" do
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:trap)
+ end
+
+ # Behaviour is specified for Signal.trap
+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..ef3fa9a3e1
--- /dev/null
+++ b/spec/ruby/core/kernel/trust_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#trust" do
+ it "has been removed" do
+ Object.new.should_not.respond_to?(:trust)
+ 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..47e8544bd4
--- /dev/null
+++ b/spec/ruby/core/kernel/untaint_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#untaint" do
+ it "has been removed" do
+ Object.new.should_not.respond_to?(:untaint)
+ 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..1925a3a836
--- /dev/null
+++ b/spec/ruby/core/kernel/untrace_var_spec.rb
@@ -0,0 +1,12 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+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..8787ab3fc9
--- /dev/null
+++ b/spec/ruby/core/kernel/untrust_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#untrust" do
+ it "has been removed" do
+ Object.new.should_not.respond_to?(:untrust)
+ 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..29261be9c4
--- /dev/null
+++ b/spec/ruby/core/kernel/untrusted_spec.rb
@@ -0,0 +1,8 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#untrusted?" do
+ it "has been removed" do
+ Object.new.should_not.respond_to?(:untrusted?)
+ 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..e03498c6dc
--- /dev/null
+++ b/spec/ruby/core/kernel/warn_spec.rb
@@ -0,0 +1,298 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+describe "Kernel#warn" do
+ before :each do
+ @before_verbose = $VERBOSE
+ @before_separator = $/
+ end
+
+ after :each do
+ $VERBOSE = nil
+ $/ = @before_separator
+ $VERBOSE = @before_verbose
+ end
+
+ it "is a private method" do
+ Kernel.should have_private_instance_method(:warn)
+ end
+
+ it "accepts multiple arguments" do
+ Kernel.method(:warn).arity.should < 0
+ end
+
+ it "does not append line-end if last character is line-end" do
+ -> {
+ $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
+ -> {
+ $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
+ -> {
+ $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
+ -> {
+ $VERBOSE = nil
+ warn("this is some simple text")
+ }.should output(nil, "")
+ end
+
+ it "writes each argument on a line when passed multiple arguments" do
+ -> {
+ $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
+ -> {
+ $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
+ -> {
+ $VERBOSE = true
+ warn
+ }.should output("", "")
+ end
+
+ it "writes the default record separator and NOT $/ to $stderr after the warning message" do
+ -> {
+ $VERBOSE = true
+ $/ = 'rs'
+ warn("")
+ }.should output(nil, /\n/)
+ end
+
+ it "writes to_s representation if passed a non-string" do
+ obj = mock("obj")
+ obj.should_receive(:to_s).and_return("to_s called")
+ -> {
+ $VERBOSE = true
+ warn(obj)
+ }.should output(nil, "to_s called\n")
+ end
+
+ describe ":uplevel keyword argument" do
+ before :each do
+ $VERBOSE = true
+ end
+
+ it "prepends a message with specified line from the backtrace" do
+ w = KernelSpecs::WarnInNestedCall.new
+
+ -> { w.f4("foo", 0) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.warn_call_lineno}: warning: foo|)
+ -> { w.f4("foo", 1) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f1_call_lineno}: warning: foo|)
+ -> { w.f4("foo", 2) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f2_call_lineno}: warning: foo|)
+ -> { w.f4("foo", 3) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f3_call_lineno}: warning: foo|)
+ end
+
+ # Test both explicitly without and with RubyGems as RubyGems overrides Kernel#warn
+ it "shows the caller of #require and not #require itself without RubyGems" do
+ file = fixture(__FILE__ , "warn_require_caller.rb")
+ ruby_exe(file, options: "--disable-gems", args: "2>&1").should == "#{file}:2: warning: warn-require-warning\n"
+ end
+
+ it "shows the caller of #require and not #require itself with RubyGems loaded" do
+ file = fixture(__FILE__ , "warn_require_caller.rb")
+ ruby_exe(file, options: "-rrubygems", args: "2>&1").should == "#{file}:2: warning: warn-require-warning\n"
+ end
+
+ it "doesn't show the caller when the uplevel is `nil`" do
+ w = KernelSpecs::WarnInNestedCall.new
+
+ -> { w.f4("foo", nil) }.should output(nil, "foo\n")
+ end
+
+ guard -> { Kernel.instance_method(:tap).source_location } do
+ it "skips <internal: core library methods defined in Ruby" do
+ file, line = Kernel.instance_method(:tap).source_location
+ file.should.start_with?('<internal:')
+
+ file = fixture(__FILE__ , "warn_core_method.rb")
+ n = 9
+ ruby_exe(file, options: "--disable-gems", args: "2>&1").lines.should == [
+ "#{file}:#{n+0}: warning: use X instead\n",
+ "#{file}:#{n+1}: warning: use X instead\n",
+ "#{file}:#{n+2}: warning: use X instead\n",
+ "#{file}:#{n+4}: warning: use X instead\n",
+ ]
+ end
+ end
+
+ it "accepts :category keyword with a symbol" do
+ -> {
+ $VERBOSE = true
+ warn("message", category: :deprecated)
+ }.should output(nil, "message\n")
+ end
+
+ it "accepts :category keyword with nil" do
+ -> {
+ $VERBOSE = true
+ warn("message", category: nil)
+ }.should output(nil, "message\n")
+ end
+
+ it "accepts :category keyword with object convertible to symbol" do
+ o = Object.new
+ def o.to_sym; :deprecated; end
+ -> {
+ $VERBOSE = true
+ warn("message", category: o)
+ }.should output(nil, "message\n")
+ end
+
+ it "raises if :category keyword is not nil and not convertible to symbol" do
+ -> {
+ $VERBOSE = true
+ warn("message", category: Object.new)
+ }.should raise_error(TypeError)
+ end
+
+ it "converts first arg using to_s" do
+ w = KernelSpecs::WarnInNestedCall.new
+
+ -> { w.f4(false, 0) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.warn_call_lineno}: warning: false|)
+ -> { w.f4(nil, 1) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f1_call_lineno}: warning: |)
+ obj = mock("obj")
+ obj.should_receive(:to_s).and_return("to_s called")
+ -> { w.f4(obj, 2) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.f2_call_lineno}: warning: to_s called|)
+ end
+
+ it "does not prepend caller information if the uplevel argument is too large" do
+ w = KernelSpecs::WarnInNestedCall.new
+ -> { w.f4("foo", 100) }.should output(nil, "warning: foo\n")
+ end
+
+ it "prepends even if a message is empty or nil" do
+ w = KernelSpecs::WarnInNestedCall.new
+
+ -> { w.f4("", 0) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.warn_call_lineno}: warning: \n$|)
+ -> { w.f4(nil, 0) }.should output(nil, %r|core/kernel/fixtures/classes.rb:#{w.warn_call_lineno}: warning: \n$|)
+ end
+
+ it "converts value to Integer" do
+ w = KernelSpecs::WarnInNestedCall.new
+
+ -> { w.f4(0.1) }.should output(nil, %r|classes.rb:#{w.warn_call_lineno}:|)
+ -> { w.f4(Rational(1, 2)) }.should output(nil, %r|classes.rb:#{w.warn_call_lineno}:|)
+ end
+
+ it "raises ArgumentError if passed negative value" do
+ -> { warn "", uplevel: -2 }.should raise_error(ArgumentError)
+ -> { warn "", uplevel: -100 }.should raise_error(ArgumentError)
+ end
+
+ it "raises ArgumentError if passed -1" do
+ -> { warn "", uplevel: -1 }.should raise_error(ArgumentError)
+ end
+
+ it "raises TypeError if passed not Integer" do
+ -> { warn "", uplevel: "" }.should raise_error(TypeError)
+ -> { warn "", uplevel: [] }.should raise_error(TypeError)
+ -> { warn "", uplevel: {} }.should raise_error(TypeError)
+ -> { warn "", uplevel: Object.new }.should raise_error(TypeError)
+ end
+ end
+
+ it "treats empty hash as no keyword argument" do
+ h = {}
+ -> { warn(**h) }.should_not complain(verbose: true)
+ -> { warn('foo', **h) }.should complain("foo\n")
+ end
+
+ it "calls Warning.warn without keyword arguments if Warning.warn does not accept keyword arguments" do
+ verbose = $VERBOSE
+ $VERBOSE = false
+ class << Warning
+ alias_method :_warn, :warn
+ def warn(message)
+ ScratchPad.record(message)
+ end
+ end
+
+ begin
+ ScratchPad.clear
+ Kernel.warn("Chunky bacon!")
+ ScratchPad.recorded.should == "Chunky bacon!\n"
+
+ Kernel.warn("Deprecated bacon!", category: :deprecated)
+ ScratchPad.recorded.should == "Deprecated bacon!\n"
+ ensure
+ class << Warning
+ remove_method :warn
+ alias_method :warn, :_warn
+ remove_method :_warn
+ end
+ $VERBOSE = verbose
+ end
+ end
+
+ it "calls Warning.warn with category: nil if Warning.warn accepts keyword arguments" do
+ Warning.should_receive(:warn).with("Chunky bacon!\n", category: nil)
+ verbose = $VERBOSE
+ $VERBOSE = false
+ begin
+ Kernel.warn("Chunky bacon!")
+ ensure
+ $VERBOSE = verbose
+ end
+ end
+
+ it "calls Warning.warn with given category keyword converted to a symbol" do
+ Warning.should_receive(:warn).with("Chunky bacon!\n", category: :deprecated)
+ verbose = $VERBOSE
+ $VERBOSE = false
+ begin
+ Kernel.warn("Chunky bacon!", category: 'deprecated')
+ ensure
+ $VERBOSE = verbose
+ end
+ end
+
+ it "does not call Warning.warn if self is the Warning module" do
+ # RubyGems redefines Kernel#warn so we need to use a subprocess and disable RubyGems here
+ code = <<-RUBY
+ def Warning.warn(*args, **kwargs)
+ raise 'should not be called'
+ end
+ Kernel.instance_method(:warn).bind(Warning).call('Kernel#warn spec edge case')
+ RUBY
+ out = ruby_exe(code, args: "2>&1", options: "--disable-gems")
+ out.should == "Kernel#warn spec edge case\n"
+ $?.should.success?
+ end
+
+ it "avoids recursion if Warning#warn is redefined and calls super" do
+ # This works because of the spec above, which is the workaround for it.
+ # Note that redefining Warning#warn is a mistake which would naturally end in infinite recursion,
+ # Warning.extend Module.new { def warn } should be used instead.
+ # RubyGems redefines Kernel#warn so we need to use a subprocess and disable RubyGems here
+ code = <<-RUBY
+ module Warning
+ def warn(*args, **kwargs)
+ super
+ end
+ end
+ warn "avoid infinite recursion"
+ RUBY
+ out = ruby_exe(code, args: "2>&1", options: "--disable-gems")
+ out.should == "avoid infinite recursion\n"
+ $?.should.success?
+ 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..e311dcee47
--- /dev/null
+++ b/spec/ruby/core/kernel/yield_self_spec.rb
@@ -0,0 +1,6 @@
+require_relative '../../spec_helper'
+require_relative 'shared/then'
+
+describe "Kernel#yield_self" do
+ it_behaves_like :kernel_then, :yield_self
+end