summaryrefslogtreecommitdiff
path: root/spec/ruby/core/kernel/shared
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/kernel/shared')
-rw-r--r--spec/ruby/core/kernel/shared/dup_clone.rb94
-rw-r--r--spec/ruby/core/kernel/shared/kind_of.rb23
-rw-r--r--spec/ruby/core/kernel/shared/lambda.rb4
-rw-r--r--spec/ruby/core/kernel/shared/load.rb120
-rw-r--r--spec/ruby/core/kernel/shared/method.rb14
-rw-r--r--spec/ruby/core/kernel/shared/require.rb289
-rw-r--r--spec/ruby/core/kernel/shared/sprintf.rb1004
-rw-r--r--spec/ruby/core/kernel/shared/sprintf_encoding.rb59
-rw-r--r--spec/ruby/core/kernel/shared/then.rb20
9 files changed, 992 insertions, 635 deletions
diff --git a/spec/ruby/core/kernel/shared/dup_clone.rb b/spec/ruby/core/kernel/shared/dup_clone.rb
index 116989958b..4fac6006e1 100644
--- a/spec/ruby/core/kernel/shared/dup_clone.rb
+++ b/spec/ruby/core/kernel/shared/dup_clone.rb
@@ -52,16 +52,6 @@ describe :kernel_dup_clone, shared: true do
o2.original.should equal(o)
end
- it "preserves tainted state from the original" do
- o = ObjectSpecDupInitCopy.new
- o2 = o.send(@method)
- o.taint
- o3 = o.send(@method)
-
- o2.tainted?.should == false
- o3.tainted?.should == true
- end
-
it "does not preserve the object_id" do
o1 = ObjectSpecDupInitCopy.new
old_object_id = o1.object_id
@@ -69,81 +59,33 @@ describe :kernel_dup_clone, shared: true do
o2.object_id.should_not == old_object_id
end
- it "preserves untrusted state from the original" do
- o = ObjectSpecDupInitCopy.new
- o2 = o.send(@method)
- o.untrust
- o3 = o.send(@method)
-
- o2.untrusted?.should == false
- o3.untrusted?.should == true
+ it "returns nil for NilClass" do
+ nil.send(@method).should == nil
end
- ruby_version_is ''...'2.4' do
- it "raises a TypeError for NilClass" do
- lambda { nil.send(@method) }.should raise_error(TypeError)
- end
-
- it "raises a TypeError for TrueClass" do
- lambda { true.send(@method) }.should raise_error(TypeError)
- end
-
- it "raises a TypeError for FalseClass" do
- lambda { false.send(@method) }.should raise_error(TypeError)
- end
-
- it "raises a TypeError for Fixnum" do
- lambda { 1.send(@method) }.should raise_error(TypeError)
- end
-
- it "raises a TypeError for Symbol" do
- lambda { :my_symbol.send(@method) }.should raise_error(TypeError)
- end
+ it "returns true for TrueClass" do
+ true.send(@method).should == true
end
- ruby_version_is '2.4' do
- it "returns nil for NilClass" do
- nil.send(@method).should == nil
- end
-
- it "returns true for TrueClass" do
- true.send(@method).should == true
- end
-
- it "returns false for FalseClass" do
- false.send(@method).should == false
- end
-
- it "returns the same Integer for Integer" do
- 1.send(@method).should == 1
- end
-
- it "returns the same Symbol for Symbol" do
- :my_symbol.send(@method).should == :my_symbol
- end
+ it "returns false for FalseClass" do
+ false.send(@method).should == false
end
- ruby_version_is ''...'2.5' do
- it "raises a TypeError for Complex" do
- c = Complex(1.3, 3.1)
- lambda { c.send(@method) }.should raise_error(TypeError)
- end
+ it "returns the same Integer for Integer" do
+ 1.send(@method).should == 1
+ end
- it "raises a TypeError for Rational" do
- r = Rational(1, 3)
- lambda { r.send(@method) }.should raise_error(TypeError)
- end
+ it "returns the same Symbol for Symbol" do
+ :my_symbol.send(@method).should == :my_symbol
end
- ruby_version_is '2.5' do
- it "returns self for Complex" do
- c = Complex(1.3, 3.1)
- c.send(@method).should equal c
- 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
+ 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
index e99f46aa14..aef6f1c1d8 100644
--- a/spec/ruby/core/kernel/shared/kind_of.rb
+++ b/spec/ruby/core/kernel/shared/kind_of.rb
@@ -1,4 +1,4 @@
-require File.expand_path('../../fixtures/classes', __FILE__)
+require_relative '../fixtures/classes'
describe :kernel_kind_of, shared: true do
before :each do
@@ -31,14 +31,25 @@ describe :kernel_kind_of, shared: true do
@o.send(@method, KernelSpecs::MyExtensionModule).should == true
end
- it "returns false if given a Module not included in object's class nor ancestors" do
+ 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
- lambda { @o.send(@method, 1) }.should raise_error(TypeError)
- lambda { @o.send(@method, 'KindaClass') }.should raise_error(TypeError)
- lambda { @o.send(@method, :KindaClass) }.should raise_error(TypeError)
- lambda { @o.send(@method, Object.new) }.should raise_error(TypeError)
+ -> { @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
index bebb111c43..c70640082a 100644
--- a/spec/ruby/core/kernel/shared/lambda.rb
+++ b/spec/ruby/core/kernel/shared/lambda.rb
@@ -4,6 +4,8 @@ describe :kernel_lambda, shared: true do
end
it "raises an ArgumentError when no block is given" do
- lambda { send(@method) }.should raise_error(ArgumentError)
+ 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
index 0ce7d58d2c..62c5c7be9b 100644
--- a/spec/ruby/core/kernel/shared/load.rb
+++ b/spec/ruby/core/kernel/shared/load.rb
@@ -1,3 +1,6 @@
+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
@@ -8,31 +11,39 @@ describe :kernel_load, shared: true do
CodeLoadingSpecs.spec_cleanup
end
- it "loads a non-extensioned file as a Ruby source file" do
- path = File.expand_path "load_fixture", CODE_LOADING_DIR
- @object.load(path).should be_true
- ScratchPad.recorded.should == [:no_ext]
- end
+ 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 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]
+ 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
-> {
- $VERBOSE = true
@object.load(path).should be_true
- }.should complain(/circular require considered harmful/)
+ }.should complain(/circular require considered harmful/, verbose: true)
ScratchPad.recorded.should == [:loaded, :loaded]
end
@@ -82,27 +93,59 @@ describe :kernel_load, shared: true do
it "raises a LoadError if passed a non-extensioned path that does not exist but a .rb extensioned path does exist" do
path = File.expand_path "load_ext_fixture", CODE_LOADING_DIR
- lambda { @object.load(path) }.should raise_error(LoadError)
+ -> { @object.load(path) }.should raise_error(LoadError)
end
describe "when passed true for 'wrap'" do
it "loads from an existing path" do
- path = File.expand_path "wrap_fixture.rb", CODE_LOADING_DIR
+ 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 "wrap_fixture.rb", CODE_LOADING_DIR
+ 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 "wrap_fixture.rb", CODE_LOADING_DIR
+ path = File.expand_path "load_wrap_fixture.rb", CODE_LOADING_DIR
@object.load(path, true)
- ScratchPad.recorded.first.should be_an_instance_of(Class)
+ 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
@@ -116,11 +159,44 @@ describe :kernel_load, shared: true do
end
it "does not pollute the receiver" do
- lambda { @object.send(:top_level_method) }.should raise_error(NameError)
+ -> { @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"]
diff --git a/spec/ruby/core/kernel/shared/method.rb b/spec/ruby/core/kernel/shared/method.rb
index 1566c6ab09..8b6ab23fd3 100644
--- a/spec/ruby/core/kernel/shared/method.rb
+++ b/spec/ruby/core/kernel/shared/method.rb
@@ -1,4 +1,4 @@
-require File.expand_path('../../../../spec_helper', __FILE__)
+require_relative '../../../spec_helper'
describe :kernel_method, shared: true do
it "returns a method object for a valid method" do
@@ -15,22 +15,28 @@ describe :kernel_method, shared: true do
m.call.should == 'class done'
end
- it "returns a method object if we repond_to_missing? method" do
+ 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
- lambda {
+ -> {
KernelSpecs::Foo.new.send(@method, :invalid_and_silly_method_name)
}.should raise_error(NameError)
end
it "raises a NameError for an invalid singleton method name" do
class KernelSpecs::Foo; def self.bar; 'done'; end; end
- lambda { KernelSpecs::Foo.send(@method, :baz) }.should raise_error(NameError)
+ -> { KernelSpecs::Foo.send(@method, :baz) }.should raise_error(NameError)
end
it "changes the method called for super on a target aliased method" do
diff --git a/spec/ruby/core/kernel/shared/require.rb b/spec/ruby/core/kernel/shared/require.rb
index 3296c7f42a..52f86f73e5 100644
--- a/spec/ruby/core/kernel/shared/require.rb
+++ b/spec/ruby/core/kernel/shared/require.rb
@@ -20,28 +20,30 @@ describe :kernel_require_basic, shared: true do
it "raises a LoadError if the file does not exist" do
path = File.expand_path "nonexistent.rb", CODE_LOADING_DIR
- File.exist?(path).should be_false
- lambda { @object.send(@method, path) }.should raise_error(LoadError)
+ 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
- describe "with an unreadable file" do
- before :each do
- @path = tmp("unreadable_file.rb")
- touch @path
- File.chmod 0000, @path
- end
-
- after :each do
- File.chmod 0666, @path
- rm_r @path
- end
-
- it "raises a LoadError" do
- File.exist?(@path).should be_true
- lambda { @object.send(@method, @path) }.should raise_error(LoadError)
+ 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
@@ -55,19 +57,19 @@ describe :kernel_require_basic, shared: true do
end
it "raises a TypeError if passed nil" do
- lambda { @object.send(@method, nil) }.should raise_error(TypeError)
+ -> { @object.send(@method, nil) }.should raise_error(TypeError)
end
- it "raises a TypeError if passed a Fixnum" do
- lambda { @object.send(@method, 42) }.should raise_error(TypeError)
+ 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
- lambda { @object.send(@method, []) }.should raise_error(TypeError)
+ -> { @object.send(@method, []) }.should raise_error(TypeError)
end
it "raises a TypeError if passed an object that does not provide #to_str" do
- lambda { @object.send(@method, mock("not a filename")) }.should raise_error(TypeError)
+ -> { @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
@@ -75,14 +77,14 @@ describe :kernel_require_basic, shared: true do
name.stub!(:to_s).and_return("load_fixture.rb")
$LOAD_PATH << "."
Dir.chdir CODE_LOADING_DIR do
- lambda { @object.send(@method, name) }.should raise_error(TypeError)
+ -> { @object.send(@method, name) }.should raise_error(TypeError)
end
end
it "raises a TypeError if #to_str does not return a String" do
name = mock("#to_str returns nil")
name.should_receive(:to_str).at_least(1).times.and_return(nil)
- lambda { @object.send(@method, name) }.should raise_error(TypeError)
+ -> { @object.send(@method, name) }.should raise_error(TypeError)
end
it "calls #to_path on non-String objects" do
@@ -158,17 +160,25 @@ describe :kernel_require_basic, shared: true do
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.unshift CODE_LOADING_DIR + "/gem"
+ $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
- lambda do
+ -> do
@object.send(@method, "./load_fixture.rb")
end.should raise_error(LoadError)
ScratchPad.recorded.should == []
@@ -176,7 +186,7 @@ describe :kernel_require_basic, shared: true do
it "does not resolve a ../ relative path against $LOAD_PATH entries" do
$LOAD_PATH << CODE_LOADING_DIR
- lambda do
+ -> do
@object.send(@method, "../code/load_fixture.rb")
end.should raise_error(LoadError)
ScratchPad.recorded.should == []
@@ -202,18 +212,46 @@ 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
- lambda { @object.require("load_fixture.rb") }.should raise_error(LoadError)
+ -> { @object.require("load_fixture.rb") }.should raise_error(LoadError)
ScratchPad.recorded.should == []
end
end
it "does not load a relative path unless the current working directory is in $LOAD_PATH" do
Dir.chdir File.dirname(CODE_LOADING_DIR) do
- lambda do
+ -> do
@object.require("code/load_fixture.rb")
end.should raise_error(LoadError)
ScratchPad.recorded.should == []
@@ -223,11 +261,21 @@ describe :kernel_require, shared: true do
it "loads a file that recursively requires itself" do
path = File.expand_path "recursive_require_fixture.rb", CODE_LOADING_DIR
-> {
- $VERBOSE = true
@object.require(path).should be_true
- }.should complain(/circular require considered harmful/)
+ }.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
@@ -241,12 +289,29 @@ describe :kernel_require, shared: true 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.exist?(path).should be_true
+ File.should.exist?(path)
@object.require(path).should be_true
ScratchPad.recorded.should == [:loaded]
end
@@ -270,7 +335,7 @@ describe :kernel_require, shared: true do
it "loads a .rb extensioned file when passed a non-.rb extensioned path" do
path = File.expand_path "load_fixture.ext", CODE_LOADING_DIR
- File.exist?(path).should be_true
+ File.should.exist?(path)
@object.require(path).should be_true
ScratchPad.recorded.should == [:loaded]
end
@@ -303,10 +368,86 @@ describe :kernel_require, shared: true do
$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
- lambda { @object.require("raise_fixture.rb") }.should raise_error(RuntimeError)
+ -> { @object.require("raise_fixture.rb") }.should raise_error(RuntimeError)
$LOADED_FEATURES.should == saved_loaded_features
end
@@ -415,7 +556,7 @@ describe :kernel_require, shared: true do
$LOADED_FEATURES.should include(@path)
end
- it "canonicalizes non-unique absolute paths" do
+ 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)
@@ -452,46 +593,30 @@ describe :kernel_require, shared: true do
ScratchPad.recorded.should == []
end
- ruby_version_is "2.2"..."2.3" do
- it "complex, enumerator, rational and unicode_normalize are already required" do
- provided = %w[complex enumerator rational unicode_normalize]
- features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems')
- provided.each { |feature|
- features.should =~ /\b#{feature}\.(rb|so)$/
- }
+ 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")
+ }
- code = provided.map { |f| "puts require #{f.inspect}\n" }.join
- required = ruby_exe(code, options: '--disable-gems')
- required.should == "false\n" * provided.size
- end
+ -> { @object.require("unicode_normalize") }.should raise_error(LoadError)
end
- ruby_version_is "2.3"..."2.5" do
- it "complex, enumerator, rational, thread and unicode_normalize are already required" do
- provided = %w[complex enumerator rational thread unicode_normalize]
- features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems')
- provided.each { |feature|
- features.should =~ /\b#{feature}\.(rb|so|jar)$/
- }
-
- code = provided.map { |f| "puts require #{f.inspect}\n" }.join
- required = ruby_exe(code, options: '--disable-gems')
- required.should == "false\n" * provided.size
+ 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
- end
+ ScratchPad.recorded.should == [:loaded]
- ruby_version_is "2.5" do
- it "complex, enumerator, rational and thread are already required" do
- provided = %w[complex enumerator rational thread]
- features = ruby_exe("puts $LOADED_FEATURES", options: '--disable-gems')
- provided.each { |feature|
- features.should =~ /\b#{feature}\.(rb|so|jar)$/
- }
+ $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]
- code = provided.map { |f| "puts require #{f.inspect}\n" }.join
- required = ruby_exe(code, options: '--disable-gems')
- required.should == "false\n" * provided.size
- end
+ $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
@@ -613,7 +738,7 @@ describe :kernel_require, shared: true do
Thread.current[:wait_for] = t2
Thread.current[:con_raise] = true
- lambda {
+ -> {
@object.require(@path)
}.should raise_error(RuntimeError)
@@ -654,7 +779,7 @@ describe :kernel_require, shared: true do
t1 = Thread.new do
Thread.current[:con_raise] = true
- lambda {
+ -> {
@object.require(@path)
}.should raise_error(RuntimeError)
@@ -694,10 +819,30 @@ describe :kernel_require, shared: true do
it "stores the missing path in a LoadError object" do
path = "abcd1234"
- lambda {
+ -> {
@object.send(@method, path)
}.should raise_error(LoadError) { |e|
e.path.should == path
}
end
+
+ 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
index e595a06640..2b2c6c9b63 100644
--- a/spec/ruby/core/kernel/shared/sprintf.rb
+++ b/spec/ruby/core/kernel/shared/sprintf.rb
@@ -1,8 +1,4 @@
describe :kernel_sprintf, shared: true do
- def format(*args)
- @method.call(*args)
- end
-
describe "integer formats" do
it "converts argument into Integer with to_int" do
obj = Object.new
@@ -10,7 +6,7 @@ describe :kernel_sprintf, shared: true do
def obj.to_int; 10; end
obj.should_receive(:to_int).and_return(10)
- format("%b", obj).should == "1010"
+ @method.call("%b", obj).should == "1010"
end
it "converts argument into Integer with to_i if to_int isn't available" do
@@ -18,35 +14,36 @@ describe :kernel_sprintf, shared: true do
def obj.to_i; 10; end
obj.should_receive(:to_i).and_return(10)
- format("%b", obj).should == "1010"
+ @method.call("%b", obj).should == "1010"
end
it "converts String argument with Kernel#Integer" do
- format("%d", "0b1010").should == "10"
- format("%d", "112").should == "112"
- format("%d", "0127").should == "87"
- format("%d", "0xc4").should == "196"
+ @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
- -> () {
- format("%b", Object.new)
+ -> {
+ @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
- format("%#{f}", 10).should == "1010"
+ @method.call("%#{f}", 10).should == "1010"
end
it "displays negative number as a two's complement prefixed with '..1'" do
- format("%#{f}", -10).should == "..1" + "0110"
+ @method.call("%#{f}", -10).should == "..1" + "0110"
end
it "collapse negative number representation if it equals 1" do
- format("%#{f}", -1).should_not == "..11"
- format("%#{f}", -1).should == "..1"
+ @method.call("%#{f}", -1).should_not == "..11"
+ @method.call("%#{f}", -1).should == "..1"
end
end
end
@@ -54,58 +51,63 @@ describe :kernel_sprintf, shared: true do
["d", "i", "u"].each do |f|
describe f do
it "converts argument as a decimal number" do
- format("%#{f}", 112).should == "112"
- format("%#{f}", -112).should == "-112"
+ @method.call("%#{f}", 112).should == "112"
+ @method.call("%#{f}", -112).should == "-112"
end
it "works well with large numbers" do
- format("%#{f}", 1234567890987654321).should == "1234567890987654321"
+ @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
- format("%o", 87).should == "127"
+ @method.call("%o", 87).should == "127"
end
it "displays negative number as a two's complement prefixed with '..7'" do
- format("%o", -87).should == "..7" + "651"
+ @method.call("%o", -87).should == "..7" + "651"
end
it "collapse negative number representation if it equals 7" do
- format("%o", -1).should_not == "..77"
- format("%o", -1).should == "..7"
+ @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
- format("%x", 196).should == "c4"
+ @method.call("%x", 196).should == "c4"
end
it "displays negative number as a two's complement prefixed with '..f'" do
- format("%x", -196).should == "..f" + "3c"
+ @method.call("%x", -196).should == "..f" + "3c"
end
it "collapse negative number representation if it equals f" do
- format("%x", -1).should_not == "..ff"
- format("%x", -1).should == "..f"
+ @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
- format("%X", 196).should == "C4"
+ @method.call("%X", 196).should == "C4"
end
it "displays negative number as a two's complement prefixed with '..f'" do
- format("%X", -196).should == "..F" + "3C"
+ @method.call("%X", -196).should == "..F" + "3C"
end
it "collapse negative number representation if it equals F" do
- format("%X", -1).should_not == "..FF"
- format("%X", -1).should == "..F"
+ @method.call("%X", -1).should_not == "..FF"
+ @method.call("%X", -1).should == "..F"
end
end
end
@@ -114,70 +116,70 @@ describe :kernel_sprintf, shared: true do
it "converts argument into Float" do
obj = mock("float")
obj.should_receive(:to_f).and_return(9.6)
- format("%f", obj).should == "9.600000"
+ @method.call("%f", obj).should == "9.600000"
end
it "raises TypeError exception if cannot convert to Float" do
- -> () {
- format("%f", Object.new)
+ -> {
+ @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
- format("%#{f}", 109.52).should == "1.095200#{exp}+02"
- format("%#{f}", -109.52).should == "-1.095200#{exp}+02"
- format("%#{f}", 0.10952).should == "1.095200#{exp}-01"
- format("%#{f}", -0.10952).should == "-1.095200#{exp}-01"
+ @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
- format("%#{f}", 1.123456789).should == "1.123457#{exp}+00"
+ @method.call("%#{f}", 1.123456789).should == "1.123457#{exp}+00"
end
it "rounds the last significant digit to the closest one" do
- format("%#{f}", 1.555555555).should == "1.555556#{exp}+00"
- format("%#{f}", -1.555555555).should == "-1.555556#{exp}+00"
- format("%#{f}", 1.444444444).should == "1.444444#{exp}+00"
+ @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
- format("%#{f}", Float::INFINITY).should == "Inf"
- format("%#{f}", -Float::INFINITY).should == "-Inf"
+ @method.call("%#{f}", Float::INFINITY).should == "Inf"
+ @method.call("%#{f}", -Float::INFINITY).should == "-Inf"
end
it "displays Float::NAN as NaN" do
- format("%#{f}", Float::NAN).should == "NaN"
- format("%#{f}", -Float::NAN).should == "NaN"
+ @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
- format("%f", 10.952).should == "10.952000"
- format("%f", -10.952).should == "-10.952000"
+ @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
- format("%f", 1.123456789).should == "1.123457"
+ @method.call("%f", 1.123456789).should == "1.123457"
end
it "rounds the last significant digit to the closest one" do
- format("%f", 1.555555555).should == "1.555556"
- format("%f", -1.555555555).should == "-1.555556"
- format("%f", 1.444444444).should == "1.444444"
+ @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
- format("%f", Float::INFINITY).should == "Inf"
- format("%f", -Float::INFINITY).should == "-Inf"
+ @method.call("%f", Float::INFINITY).should == "Inf"
+ @method.call("%f", -Float::INFINITY).should == "-Inf"
end
it "displays Float::NAN as NaN" do
- format("%f", Float::NAN).should == "NaN"
- format("%f", -Float::NAN).should == "NaN"
+ @method.call("%f", Float::NAN).should == "NaN"
+ @method.call("%f", -Float::NAN).should == "NaN"
end
end
@@ -185,100 +187,100 @@ describe :kernel_sprintf, shared: true do
describe f do
context "the exponent is less than -4" do
it "converts a floating point number using exponential form" do
- format("%#{f}", 0.0000123456).should == "1.23456#{exp}-05"
- format("%#{f}", -0.0000123456).should == "-1.23456#{exp}-05"
+ @method.call("%#{f}", 0.0000123456).should == "1.23456#{exp}-05"
+ @method.call("%#{f}", -0.0000123456).should == "-1.23456#{exp}-05"
- format("%#{f}", 0.000000000123456).should == "1.23456#{exp}-10"
- format("%#{f}", -0.000000000123456).should == "-1.23456#{exp}-10"
+ @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
- format("%#{f}", 1234567).should == "1.23457#{exp}+06"
- format("%#{f}", 1234567890123).should == "1.23457#{exp}+12"
- format("%#{f}", -1234567).should == "-1.23457#{exp}+06"
+ @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
- format("%#{f}", 0.0001).should == "0.0001"
- format("%#{f}", -0.0001).should == "-0.0001"
- format("%#{f}", 123456).should == "123456"
- format("%#{f}", -123456).should == "-123456"
+ @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
- format("%#{f}", 12.12341111).should == "12.1234"
- format("%#{f}", -12.12341111).should == "-12.1234"
+ @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
- format("%#{f}", 1.555555555).should == "1.55556"
- format("%#{f}", -1.555555555).should == "-1.55556"
- format("%#{f}", 1.444444444).should == "1.44444"
+ @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
- format("%#{f}", 1.1234567).should == "1.12346"
- format("%#{f}", 12.1234567).should == "12.1235"
- format("%#{f}", 123.1234567).should == "123.123"
- format("%#{f}", 1234.1234567).should == "1234.12"
- format("%#{f}", 12345.1234567).should == "12345.1"
- format("%#{f}", 123456.1234567).should == "123456"
+ @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
- format("%#{f}", Float::INFINITY).should == "Inf"
- format("%#{f}", -Float::INFINITY).should == "-Inf"
+ @method.call("%#{f}", Float::INFINITY).should == "Inf"
+ @method.call("%#{f}", -Float::INFINITY).should == "-Inf"
end
it "displays Float::NAN as NaN" do
- format("%#{f}", Float::NAN).should == "NaN"
- format("%#{f}", -Float::NAN).should == "NaN"
+ @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
- format("%a", 196).should == "0x1.88p+7"
- format("%a", -196).should == "-0x1.88p+7"
- format("%a", 196.1).should == "0x1.8833333333333p+7"
- format("%a", 0.01).should == "0x1.47ae147ae147bp-7"
- format("%a", -0.01).should == "-0x1.47ae147ae147bp-7"
+ @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
- format("%a", Float::INFINITY).should == "Inf"
- format("%a", -Float::INFINITY).should == "-Inf"
+ @method.call("%a", Float::INFINITY).should == "Inf"
+ @method.call("%a", -Float::INFINITY).should == "-Inf"
end
it "displays Float::NAN as NaN" do
- format("%a", Float::NAN).should == "NaN"
- format("%a", -Float::NAN).should == "NaN"
+ @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
- format("%A", 196).should == "0X1.88P+7"
- format("%A", -196).should == "-0X1.88P+7"
- format("%A", 196.1).should == "0X1.8833333333333P+7"
- format("%A", 0.01).should == "0X1.47AE147AE147BP-7"
- format("%A", -0.01).should == "-0X1.47AE147AE147BP-7"
+ @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
- format("%A", Float::INFINITY).should == "Inf"
- format("%A", -Float::INFINITY).should == "-Inf"
+ @method.call("%A", Float::INFINITY).should == "Inf"
+ @method.call("%A", -Float::INFINITY).should == "-Inf"
end
it "displays Float::NAN as NaN" do
- format("%A", Float::NAN).should == "NaN"
- format("%A", -Float::NAN).should == "NaN"
+ @method.call("%A", Float::NAN).should == "NaN"
+ @method.call("%A", -Float::NAN).should == "NaN"
end
end
end
@@ -286,28 +288,71 @@ describe :kernel_sprintf, shared: true do
describe "other formats" do
describe "c" do
it "displays character if argument is a numeric code of character" do
- format("%c", 97).should == "a"
+ @method.call("%c", 97).should == "a"
end
it "displays character if argument is a single character string" do
- format("%c", "a").should == "a"
+ @method.call("%c", "a").should == "a"
end
- it "raises ArgumentError if argument is a string of several characters" do
- -> () {
- format("%c", "abc")
- }.should raise_error(ArgumentError)
+ it "displays only the first character if argument is a string of several characters" do
+ @method.call("%c", "abc").should == "a"
end
- it "raises ArgumentError if argument is an empty string" do
- -> () {
- format("%c", "")
- }.should raise_error(ArgumentError)
+ 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 "supports Unicode characters" do
- format("%c", 1286).should == "Ԇ"
- format("%c", "ش").should == "ش"
+ 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
@@ -315,19 +360,27 @@ describe :kernel_sprintf, shared: true do
it "displays argument.inspect value" do
obj = mock("object")
obj.should_receive(:inspect).and_return("<inspect-result>")
- format("%p", obj).should == "<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
- format("%s", "abc").should == "abc"
+ @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")
- format("%s", obj).should == "abc"
+ @method.call("%s", obj).should == "abc"
end
it "does not try to convert with to_str" do
@@ -336,30 +389,67 @@ describe :kernel_sprintf, shared: true do
"abc"
end
- -> () {
- format("%s", obj)
+ -> {
+ @method.call("%s", obj)
}.should raise_error(NoMethodError)
end
- end
- describe "%" do
- ruby_version_is ""..."2.5" do
- it "alone displays the percent sign" do
- format("%").should == "%"
- 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
- ruby_version_is "2.5" do
- it "alone raises an ArgumentError" do
- -> {
- format("%")
- }.should raise_error(ArgumentError)
- 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
- format("%%").should == "%"
- format("%%d", 10).should == "%d"
+ @method.call("%%").should == "%"
+ @method.call("%%d").should == "%d"
end
end
end
@@ -368,110 +458,110 @@ describe :kernel_sprintf, shared: true do
describe "space" do
context "applies to numeric formats bBdiouxXeEfgGaA" do
it "leaves a space at the start of non-negative numbers" do
- format("% b", 10).should == " 1010"
- format("% B", 10).should == " 1010"
- format("% d", 112).should == " 112"
- format("% i", 112).should == " 112"
- format("% o", 87).should == " 127"
- format("% u", 112).should == " 112"
- format("% x", 196).should == " c4"
- format("% X", 196).should == " C4"
-
- format("% e", 109.52).should == " 1.095200e+02"
- format("% E", 109.52).should == " 1.095200E+02"
- format("% f", 10.952).should == " 10.952000"
- format("% g", 12.1234).should == " 12.1234"
- format("% G", 12.1234).should == " 12.1234"
- format("% a", 196).should == " 0x1.88p+7"
- format("% A", 196).should == " 0X1.88P+7"
+ @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
- format("% b", -10).should == "-1010"
- format("% B", -10).should == "-1010"
- format("% d", -112).should == "-112"
- format("% i", -112).should == "-112"
- format("% o", -87).should == "-127"
- format("% u", -112).should == "-112"
- format("% x", -196).should == "-c4"
- format("% X", -196).should == "-C4"
-
- format("% e", -109.52).should == "-1.095200e+02"
- format("% E", -109.52).should == "-1.095200E+02"
- format("% f", -10.952).should == "-10.952000"
- format("% g", -12.1234).should == "-12.1234"
- format("% G", -12.1234).should == "-12.1234"
- format("% a", -196).should == "-0x1.88p+7"
- format("% A", -196).should == "-0X1.88P+7"
+ @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
- format("% b", -10).should == "-1010"
- format("% B", -10).should == "-1010"
- format("% o", -87).should == "-127"
- format("% x", -196).should == "-c4"
- format("% X", -196).should == "-C4"
+ @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
- format("% b", 10).should == " 1010"
- format("% B", 10).should == " 1010"
- format("% d", 112).should == " 112"
- format("% i", 112).should == " 112"
- format("% o", 87).should == " 127"
- format("% u", 112).should == " 112"
- format("% x", 196).should == " c4"
- format("% X", 196).should == " C4"
+ @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"
- format("% e", 109.52).should == " 1.095200e+02"
- format("% E", 109.52).should == " 1.095200E+02"
- format("% f", 10.952).should == " 10.952000"
- format("% g", 12.1234).should == " 12.1234"
- format("% G", 12.1234).should == " 12.1234"
- format("% a", 196).should == " 0x1.88p+7"
- format("% A", 196).should == " 0X1.88P+7"
+ @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
- format("%2$b", 0, 10).should == "1010"
- format("%2$B", 0, 10).should == "1010"
- format("%2$d", 0, 112).should == "112"
- format("%2$i", 0, 112).should == "112"
- format("%2$o", 0, 87).should == "127"
- format("%2$u", 0, 112).should == "112"
- format("%2$x", 0, 196).should == "c4"
- format("%2$X", 0, 196).should == "C4"
-
- format("%2$e", 0, 109.52).should == "1.095200e+02"
- format("%2$E", 0, 109.52).should == "1.095200E+02"
- format("%2$f", 0, 10.952).should == "10.952000"
- format("%2$g", 0, 12.1234).should == "12.1234"
- format("%2$G", 0, 12.1234).should == "12.1234"
- format("%2$a", 0, 196).should == "0x1.88p+7"
- format("%2$A", 0, 196).should == "0X1.88P+7"
-
- format("%2$c", 1, 97).should == "a"
- format("%2$p", "a", []).should == "[]"
- format("%2$s", "-", "abc").should == "abc"
+ @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
- -> () {
- format("%4$d", 1, 2, 3)
+ -> {
+ @method.call("%4$d", 1, 2, 3)
}.should raise_error(ArgumentError)
end
it "ignores '-' sign" do
- format("%2$d", 1, 2, 3).should == "2"
- format("%-2$d", 1, 2, 3).should == "2"
+ @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
- -> () {
- format("%1$d %d", 1, 2)
+ -> {
+ @method.call("%1$d %d", 1, 2)
}.should raise_error(ArgumentError)
end
end
@@ -479,62 +569,62 @@ describe :kernel_sprintf, shared: true do
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
- format("%#o", 87).should == "0127"
+ @method.call("%#o", 87).should == "0127"
end
it "does nothing for negative argument" do
- format("%#o", -87).should == "..7651"
+ @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
- format("%#b", 10).should == "0b1010"
- format("%#b", -10).should == "0b..10110"
- format("%#B", 10).should == "0B1010"
- format("%#B", -10).should == "0B..10110"
+ @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"
- format("%#x", 196).should == "0xc4"
- format("%#x", -196).should == "0x..f3c"
- format("%#X", 196).should == "0XC4"
- format("%#X", -196).should == "0X..F3C"
+ @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
- format("%#b", 0).should == "0"
- format("%#B", 0).should == "0"
+ @method.call("%#b", 0).should == "0"
+ @method.call("%#B", 0).should == "0"
- format("%#o", 0).should == "0"
+ @method.call("%#o", 0).should == "0"
- format("%#x", 0).should == "0"
- format("%#X", 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
- format("%#.0a", 16.25).should == "0x1.p+4"
- format("%#.0A", 16.25).should == "0X1.P+4"
+ @method.call("%#.0a", 16.25).should == "0x1.p+4"
+ @method.call("%#.0A", 16.25).should == "0X1.P+4"
- format("%#.0e", 100).should == "1.e+02"
- format("%#.0E", 100).should == "1.E+02"
+ @method.call("%#.0e", 100).should == "1.e+02"
+ @method.call("%#.0E", 100).should == "1.E+02"
- format("%#.0f", 123.4).should == "123."
+ @method.call("%#.0f", 123.4).should == "123."
- format("%#g", 123456).should == "123456."
- format("%#G", 123456).should == "123456."
+ @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
- format("%#.0g", 123.4).should_not == "123."
- format("%#.0g", 123.4).should == "1.e+02"
+ @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
- format("%#g", 123.4).should == "123.400"
- format("%#g", 123.4).should == "123.400"
+ @method.call("%#g", 123.4).should == "123.400"
+ @method.call("%#g", 123.4).should == "123.400"
end
end
end
@@ -542,186 +632,186 @@ describe :kernel_sprintf, shared: true do
describe "+" do
context "applies to numeric formats bBdiouxXaAeEfgG" do
it "adds a leading plus sign to non-negative numbers" do
- format("%+b", 10).should == "+1010"
- format("%+B", 10).should == "+1010"
- format("%+d", 112).should == "+112"
- format("%+i", 112).should == "+112"
- format("%+o", 87).should == "+127"
- format("%+u", 112).should == "+112"
- format("%+x", 196).should == "+c4"
- format("%+X", 196).should == "+C4"
-
- format("%+e", 109.52).should == "+1.095200e+02"
- format("%+E", 109.52).should == "+1.095200E+02"
- format("%+f", 10.952).should == "+10.952000"
- format("%+g", 12.1234).should == "+12.1234"
- format("%+G", 12.1234).should == "+12.1234"
- format("%+a", 196).should == "+0x1.88p+7"
- format("%+A", 196).should == "+0X1.88P+7"
+ @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
- format("%+b", -10).should == "-1010"
- format("%+B", -10).should == "-1010"
- format("%+o", -87).should == "-127"
- format("%+x", -196).should == "-c4"
- format("%+X", -196).should == "-C4"
+ @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
- format("%-10b", 10).should == "1010 "
- format("%-10B", 10).should == "1010 "
- format("%-10d", 112).should == "112 "
- format("%-10i", 112).should == "112 "
- format("%-10o", 87).should == "127 "
- format("%-10u", 112).should == "112 "
- format("%-10x", 196).should == "c4 "
- format("%-10X", 196).should == "C4 "
+ @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 "
- format("%-20e", 109.52).should == "1.095200e+02 "
- format("%-20E", 109.52).should == "1.095200E+02 "
- format("%-20f", 10.952).should == "10.952000 "
- format("%-20g", 12.1234).should == "12.1234 "
- format("%-20G", 12.1234).should == "12.1234 "
- format("%-20a", 196).should == "0x1.88p+7 "
- format("%-20A", 196).should == "0X1.88P+7 "
+ @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 "
- format("%-10c", 97).should == "a "
- format("%-10p", []).should == "[] "
- format("%-10s", "abc").should == "abc "
+ @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
- format("%010b", 10).should == "0000001010"
- format("%010B", 10).should == "0000001010"
- format("%010d", 112).should == "0000000112"
- format("%010i", 112).should == "0000000112"
- format("%010o", 87).should == "0000000127"
- format("%010u", 112).should == "0000000112"
- format("%010x", 196).should == "00000000c4"
- format("%010X", 196).should == "00000000C4"
-
- format("%020e", 109.52).should == "000000001.095200e+02"
- format("%020E", 109.52).should == "000000001.095200E+02"
- format("%020f", 10.952).should == "0000000000010.952000"
- format("%020g", 12.1234).should == "000000000000012.1234"
- format("%020G", 12.1234).should == "000000000000012.1234"
- format("%020a", 196).should == "0x000000000001.88p+7"
- format("%020A", 196).should == "0X000000000001.88P+7"
+ @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
- format("%010b", -10).should == "..11110110"
- format("%010B", -10).should == "..11110110"
- format("%010o", -87).should == "..77777651"
- format("%010x", -196).should == "..ffffff3c"
- format("%010X", -196).should == "..FFFFFF3C"
+ @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
- format("%*b", 10, 10).should == " 1010"
- format("%*B", 10, 10).should == " 1010"
- format("%*d", 10, 112).should == " 112"
- format("%*i", 10, 112).should == " 112"
- format("%*o", 10, 87).should == " 127"
- format("%*u", 10, 112).should == " 112"
- format("%*x", 10, 196).should == " c4"
- format("%*X", 10, 196).should == " C4"
-
- format("%*e", 20, 109.52).should == " 1.095200e+02"
- format("%*E", 20, 109.52).should == " 1.095200E+02"
- format("%*f", 20, 10.952).should == " 10.952000"
- format("%*g", 20, 12.1234).should == " 12.1234"
- format("%*G", 20, 12.1234).should == " 12.1234"
- format("%*a", 20, 196).should == " 0x1.88p+7"
- format("%*A", 20, 196).should == " 0X1.88P+7"
-
- format("%*c", 10, 97).should == " a"
- format("%*p", 10, []).should == " []"
- format("%*s", 10, "abc").should == " abc"
+ @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
- format("%*b", -10, 10).should == "1010 "
- format("%*B", -10, 10).should == "1010 "
- format("%*d", -10, 112).should == "112 "
- format("%*i", -10, 112).should == "112 "
- format("%*o", -10, 87).should == "127 "
- format("%*u", -10, 112).should == "112 "
- format("%*x", -10, 196).should == "c4 "
- format("%*X", -10, 196).should == "C4 "
-
- format("%*e", -20, 109.52).should == "1.095200e+02 "
- format("%*E", -20, 109.52).should == "1.095200E+02 "
- format("%*f", -20, 10.952).should == "10.952000 "
- format("%*g", -20, 12.1234).should == "12.1234 "
- format("%*G", -20, 12.1234).should == "12.1234 "
- format("%*a", -20, 196).should == "0x1.88p+7 "
- format("%*A", -20, 196).should == "0X1.88P+7 "
-
- format("%*c", -10, 97).should == "a "
- format("%*p", -10, []).should == "[] "
- format("%*s", -10, "abc").should == "abc "
+ @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
- format("%1$*2$b", 10, 10).should == " 1010"
- format("%1$*2$B", 10, 10).should == " 1010"
- format("%1$*2$d", 112, 10).should == " 112"
- format("%1$*2$i", 112, 10).should == " 112"
- format("%1$*2$o", 87, 10).should == " 127"
- format("%1$*2$u", 112, 10).should == " 112"
- format("%1$*2$x", 196, 10).should == " c4"
- format("%1$*2$X", 196, 10).should == " C4"
-
- format("%1$*2$e", 109.52, 20).should == " 1.095200e+02"
- format("%1$*2$E", 109.52, 20).should == " 1.095200E+02"
- format("%1$*2$f", 10.952, 20).should == " 10.952000"
- format("%1$*2$g", 12.1234, 20).should == " 12.1234"
- format("%1$*2$G", 12.1234, 20).should == " 12.1234"
- format("%1$*2$a", 196, 20).should == " 0x1.88p+7"
- format("%1$*2$A", 196, 20).should == " 0X1.88P+7"
-
- format("%1$*2$c", 97, 10).should == " a"
- format("%1$*2$p", [], 10).should == " []"
- format("%1$*2$s", "abc", 10).should == " abc"
+ @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
- format("%1$*2$b", 10, -10).should == "1010 "
- format("%1$*2$B", 10, -10).should == "1010 "
- format("%1$*2$d", 112, -10).should == "112 "
- format("%1$*2$i", 112, -10).should == "112 "
- format("%1$*2$o", 87, -10).should == "127 "
- format("%1$*2$u", 112, -10).should == "112 "
- format("%1$*2$x", 196, -10).should == "c4 "
- format("%1$*2$X", 196, -10).should == "C4 "
-
- format("%1$*2$e", 109.52, -20).should == "1.095200e+02 "
- format("%1$*2$E", 109.52, -20).should == "1.095200E+02 "
- format("%1$*2$f", 10.952, -20).should == "10.952000 "
- format("%1$*2$g", 12.1234, -20).should == "12.1234 "
- format("%1$*2$G", 12.1234, -20).should == "12.1234 "
- format("%1$*2$a", 196, -20).should == "0x1.88p+7 "
- format("%1$*2$A", 196, -20).should == "0X1.88P+7 "
-
- format("%1$*2$c", 97, -10).should == "a "
- format("%1$*2$p", [], -10).should == "[] "
- format("%1$*2$s", "abc", -10).should == "abc "
+ @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
- -> () {
- format("%*10d", 10, 112)
+ -> {
+ @method.call("%*10d", 10, 112)
}.should raise_error(ArgumentError)
end
end
@@ -729,74 +819,74 @@ describe :kernel_sprintf, shared: true do
describe "width" do
it "specifies the minimum number of characters that will be written to the result" do
- format("%10b", 10).should == " 1010"
- format("%10B", 10).should == " 1010"
- format("%10d", 112).should == " 112"
- format("%10i", 112).should == " 112"
- format("%10o", 87).should == " 127"
- format("%10u", 112).should == " 112"
- format("%10x", 196).should == " c4"
- format("%10X", 196).should == " C4"
-
- format("%20e", 109.52).should == " 1.095200e+02"
- format("%20E", 109.52).should == " 1.095200E+02"
- format("%20f", 10.952).should == " 10.952000"
- format("%20g", 12.1234).should == " 12.1234"
- format("%20G", 12.1234).should == " 12.1234"
- format("%20a", 196).should == " 0x1.88p+7"
- format("%20A", 196).should == " 0X1.88P+7"
-
- format("%10c", 97).should == " a"
- format("%10p", []).should == " []"
- format("%10s", "abc").should == " abc"
+ @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
- format("%5d", 1234567890).should == "1234567890"
+ @method.call("%5d", 1234567890).should == "1234567890"
end
end
describe "precision" do
context "integer types" do
it "controls the number of decimal places displayed" do
- format("%.6b", 10).should == "001010"
- format("%.6B", 10).should == "001010"
- format("%.5d", 112).should == "00112"
- format("%.5i", 112).should == "00112"
- format("%.5o", 87).should == "00127"
- format("%.5u", 112).should == "00112"
+ @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"
- format("%.5x", 196).should == "000c4"
- format("%.5X", 196).should == "000C4"
+ @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
- format("%.10e", 109.52).should == "1.0952000000e+02"
- format("%.10E", 109.52).should == "1.0952000000E+02"
- format("%.10f", 10.952).should == "10.9520000000"
- format("%.10a", 196).should == "0x1.8800000000p+7"
- format("%.10A", 196).should == "0X1.8800000000P+7"
+ @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
- format("%.10g", 12.1234).should == "12.1234"
- format("%.10g", 123456789).should == "123456789"
+ @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
- format("%.1p", [1]).should == "["
- format("%.2p", [1]).should == "[1"
- format("%.10p", [1]).should == "[1]"
- format("%.0p", [1]).should == ""
+ @method.call("%.1p", [1]).should == "["
+ @method.call("%.2p", [1]).should == "[1"
+ @method.call("%.10p", [1]).should == "[1]"
+ @method.call("%.0p", [1]).should == ""
- format("%.1s", "abc").should == "a"
- format("%.2s", "abc").should == "ab"
- format("%.10s", "abc").should == "abc"
- format("%.0s", "abc").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
@@ -804,55 +894,49 @@ describe :kernel_sprintf, shared: true do
describe "reference by name" do
describe "%<name>s style" do
it "uses value passed in a hash argument" do
- format("%<foo>d", foo: 123).should == "123"
+ @method.call("%<foo>d", foo: 123).should == "123"
end
it "supports flags, width, precision and type" do
- format("%+20.10<foo>f", foo: 10.952).should == " +10.9520000000"
+ @method.call("%+20.10<foo>f", foo: 10.952).should == " +10.9520000000"
end
it "allows to place name in any position" do
- format("%+15.5<foo>f", foo: 10.952).should == " +10.95200"
- format("%+15<foo>.5f", foo: 10.952).should == " +10.95200"
- format("%+<foo>15.5f", foo: 10.952).should == " +10.95200"
- format("%<foo>+15.5f", foo: 10.952).should == " +10.95200"
+ @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
- -> () {
- format("%d %<foo>d", 1, foo: "123")
+ -> {
+ @method.call("%d %<foo>d", 1, foo: "123")
}.should raise_error(ArgumentError)
end
-
- it "raises KeyError when there is no matching key" do
- -> () {
- format("%<foo>s", {})
- }.should raise_error(KeyError)
- end
end
describe "%{name} style" do
it "uses value passed in a hash argument" do
- format("%{foo}", foo: 123).should == "123"
+ @method.call("%{foo}", foo: 123).should == "123"
end
it "does not support type style" do
- format("%{foo}d", foo: 123).should == "123d"
+ @method.call("%{foo}d", foo: 123).should == "123d"
end
it "supports flags, width and precision" do
- format("%-20.5{foo}", foo: "123456789").should == "12345 "
+ @method.call("%-20.5{foo}", foo: "123456789").should == "12345 "
end
it "cannot be mixed with unnamed style" do
- -> () {
- format("%d %{foo}", 1, foo: "123")
+ -> {
+ @method.call("%d %{foo}", 1, foo: "123")
}.should raise_error(ArgumentError)
end
it "raises KeyError when there is no matching key" do
- -> () {
- format("%{foo}", {})
+ -> {
+ @method.call("%{foo}", {})
}.should raise_error(KeyError)
end
@@ -864,8 +948,40 @@ describe :kernel_sprintf, shared: true do
obj.should_receive(:to_s).and_return("42")
obj.should_not_receive(:to_str)
- format("%{foo}", foo: obj).should == "42"
+ @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
index a92f3c10cd..7ec0fe4c48 100644
--- a/spec/ruby/core/kernel/shared/sprintf_encoding.rb
+++ b/spec/ruby/core/kernel/shared/sprintf_encoding.rb
@@ -1,28 +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
- def format(*args)
- @method.call(*args)
+ 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".force_encoding(Encoding::KOI8_U)
- result = format(string, "dogs")
+ 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".force_encoding(Encoding::US_ASCII)
- argument = "b\303\274r".force_encoding(Encoding::UTF_8)
+ string = "foo %s".dup.force_encoding(Encoding::US_ASCII)
+ argument = "b\303\274r".dup.force_encoding(Encoding::UTF_8)
- result = format(string, argument)
+ 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 ano not ASCII characters" do
+ 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')
- -> () {
- format(string, argument)
+ -> {
+ @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