mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
820 lines
26 KiB
Ruby
820 lines
26 KiB
Ruby
require_relative '../../spec_helper'
|
|
require_relative '../kernel/shared/sprintf'
|
|
require_relative '../kernel/shared/sprintf_encoding'
|
|
require_relative 'fixtures/classes'
|
|
require_relative '../../shared/hash/key_error'
|
|
|
|
describe "String#%" do
|
|
it_behaves_like :kernel_sprintf, -> format, *args {
|
|
format % args
|
|
}
|
|
|
|
it_behaves_like :kernel_sprintf_encoding, -> format, *args {
|
|
format % args
|
|
}
|
|
end
|
|
|
|
# TODO: these specs are mostly redundant with kernel/shared/sprintf.rb specs.
|
|
# These specs should be moved there and deduplicated.
|
|
describe "String#%" do
|
|
context "when key is missing from passed-in hash" do
|
|
it_behaves_like :key_error, -> obj, key { "%{#{key}}" % obj }, { a: 5 }
|
|
end
|
|
|
|
it "formats multiple expressions" do
|
|
("%b %x %d %s" % [10, 10, 10, 10]).should == "1010 a 10 10"
|
|
end
|
|
|
|
it "formats expressions mid string" do
|
|
("hello %s!" % "world").should == "hello world!"
|
|
end
|
|
|
|
it "formats %% into %" do
|
|
("%d%% %s" % [10, "of chickens!"]).should == "10% of chickens!"
|
|
end
|
|
|
|
describe "output's encoding" do
|
|
it "is the same as the format string if passed value is encoding-compatible" do
|
|
[Encoding::BINARY, Encoding::US_ASCII, Encoding::UTF_8, Encoding::SHIFT_JIS].each do |encoding|
|
|
("hello %s!".encode(encoding) % "world").encoding.should == encoding
|
|
end
|
|
end
|
|
|
|
it "negotiates a compatible encoding if necessary" do
|
|
("hello %s" % 195.chr).encoding.should == Encoding::BINARY
|
|
("hello %s".encode("shift_jis") % "wörld").encoding.should == Encoding::UTF_8
|
|
end
|
|
|
|
it "raises if a compatible encoding can't be found" do
|
|
-> { "hello %s".encode("utf-8") % "world".encode("UTF-16LE") }.should raise_error(Encoding::CompatibilityError)
|
|
end
|
|
end
|
|
|
|
it "raises an error if single % appears at the end" do
|
|
-> { ("%" % []) }.should raise_error(ArgumentError)
|
|
-> { ("foo%" % [])}.should raise_error(ArgumentError)
|
|
end
|
|
|
|
it "formats single % character before a newline as literal %" do
|
|
("%\n" % []).should == "%\n"
|
|
("foo%\n" % []).should == "foo%\n"
|
|
("%\n.3f" % 1.2).should == "%\n.3f"
|
|
end
|
|
|
|
it "formats single % character before a NUL as literal %" do
|
|
("%\0" % []).should == "%\0"
|
|
("foo%\0" % []).should == "foo%\0"
|
|
("%\0.3f" % 1.2).should == "%\0.3f"
|
|
end
|
|
|
|
it "raises an error if single % appears anywhere else" do
|
|
-> { (" % " % []) }.should raise_error(ArgumentError)
|
|
-> { ("foo%quux" % []) }.should raise_error(ArgumentError)
|
|
end
|
|
|
|
it "raises an error if NULL or \\n appear anywhere else in the format string" do
|
|
begin
|
|
old_debug, $DEBUG = $DEBUG, false
|
|
|
|
-> { "%.\n3f" % 1.2 }.should raise_error(ArgumentError)
|
|
-> { "%.3\nf" % 1.2 }.should raise_error(ArgumentError)
|
|
-> { "%.\03f" % 1.2 }.should raise_error(ArgumentError)
|
|
-> { "%.3\0f" % 1.2 }.should raise_error(ArgumentError)
|
|
ensure
|
|
$DEBUG = old_debug
|
|
end
|
|
end
|
|
|
|
it "ignores unused arguments when $DEBUG is false" do
|
|
begin
|
|
old_debug = $DEBUG
|
|
$DEBUG = false
|
|
|
|
("" % [1, 2, 3]).should == ""
|
|
("%s" % [1, 2, 3]).should == "1"
|
|
ensure
|
|
$DEBUG = old_debug
|
|
end
|
|
end
|
|
|
|
it "raises an ArgumentError for unused arguments when $DEBUG is true" do
|
|
begin
|
|
old_debug = $DEBUG
|
|
$DEBUG = true
|
|
s = $stderr
|
|
$stderr = IOStub.new
|
|
|
|
-> { "" % [1, 2, 3] }.should raise_error(ArgumentError)
|
|
-> { "%s" % [1, 2, 3] }.should raise_error(ArgumentError)
|
|
ensure
|
|
$DEBUG = old_debug
|
|
$stderr = s
|
|
end
|
|
end
|
|
|
|
it "always allows unused arguments when positional argument style is used" do
|
|
begin
|
|
old_debug = $DEBUG
|
|
$DEBUG = false
|
|
|
|
("%2$s" % [1, 2, 3]).should == "2"
|
|
$DEBUG = true
|
|
("%2$s" % [1, 2, 3]).should == "2"
|
|
ensure
|
|
$DEBUG = old_debug
|
|
end
|
|
end
|
|
|
|
it "replaces trailing absolute argument specifier without type with percent sign" do
|
|
("hello %1$" % "foo").should == "hello %"
|
|
end
|
|
|
|
it "raises an ArgumentError when given invalid argument specifiers" do
|
|
-> { "%1" % [] }.should raise_error(ArgumentError)
|
|
-> { "%+" % [] }.should raise_error(ArgumentError)
|
|
-> { "%-" % [] }.should raise_error(ArgumentError)
|
|
-> { "%#" % [] }.should raise_error(ArgumentError)
|
|
-> { "%0" % [] }.should raise_error(ArgumentError)
|
|
-> { "%*" % [] }.should raise_error(ArgumentError)
|
|
-> { "%." % [] }.should raise_error(ArgumentError)
|
|
-> { "%_" % [] }.should raise_error(ArgumentError)
|
|
-> { "%0$s" % "x" }.should raise_error(ArgumentError)
|
|
-> { "%*0$s" % [5, "x"] }.should raise_error(ArgumentError)
|
|
-> { "%*1$.*0$1$s" % [1, 2, 3] }.should raise_error(ArgumentError)
|
|
end
|
|
|
|
it "raises an ArgumentError when multiple positional argument tokens are given for one format specifier" do
|
|
-> { "%1$1$s" % "foo" }.should raise_error(ArgumentError)
|
|
end
|
|
|
|
it "respects positional arguments and precision tokens given for one format specifier" do
|
|
("%2$1d" % [1, 0]).should == "0"
|
|
("%2$1d" % [0, 1]).should == "1"
|
|
|
|
("%2$.2f" % [1, 0]).should == "0.00"
|
|
("%2$.2f" % [0, 1]).should == "1.00"
|
|
end
|
|
|
|
it "allows more than one digit of position" do
|
|
("%50$d" % (0..100).to_a).should == "49"
|
|
end
|
|
|
|
it "raises an ArgumentError when multiple width star tokens are given for one format specifier" do
|
|
-> { "%**s" % [5, 5, 5] }.should raise_error(ArgumentError)
|
|
end
|
|
|
|
it "raises an ArgumentError when a width star token is seen after a width token" do
|
|
-> { "%5*s" % [5, 5] }.should raise_error(ArgumentError)
|
|
end
|
|
|
|
it "raises an ArgumentError when multiple precision tokens are given" do
|
|
-> { "%.5.5s" % 5 }.should raise_error(ArgumentError)
|
|
-> { "%.5.*s" % [5, 5] }.should raise_error(ArgumentError)
|
|
-> { "%.*.5s" % [5, 5] }.should raise_error(ArgumentError)
|
|
end
|
|
|
|
it "raises an ArgumentError when there are less arguments than format specifiers" do
|
|
("foo" % []).should == "foo"
|
|
-> { "%s" % [] }.should raise_error(ArgumentError)
|
|
-> { "%s %s" % [1] }.should raise_error(ArgumentError)
|
|
end
|
|
|
|
it "raises an ArgumentError when absolute and relative argument numbers are mixed" do
|
|
-> { "%s %1$s" % "foo" }.should raise_error(ArgumentError)
|
|
-> { "%1$s %s" % "foo" }.should raise_error(ArgumentError)
|
|
|
|
-> { "%s %2$s" % ["foo", "bar"] }.should raise_error(ArgumentError)
|
|
-> { "%2$s %s" % ["foo", "bar"] }.should raise_error(ArgumentError)
|
|
|
|
-> { "%*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
|
|
-> { "%*.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
|
|
-> { "%*2$.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
|
|
-> { "%*.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError)
|
|
end
|
|
|
|
it "allows reuse of the one argument multiple via absolute argument numbers" do
|
|
("%1$s %1$s" % "foo").should == "foo foo"
|
|
("%1$s %2$s %1$s %2$s" % ["foo", "bar"]).should == "foo bar foo bar"
|
|
end
|
|
|
|
it "always interprets an array argument as a list of argument parameters" do
|
|
-> { "%p" % [] }.should raise_error(ArgumentError)
|
|
("%p" % [1]).should == "1"
|
|
("%p %p" % [1, 2]).should == "1 2"
|
|
end
|
|
|
|
it "always interprets an array subclass argument as a list of argument parameters" do
|
|
-> { "%p" % StringSpecs::MyArray[] }.should raise_error(ArgumentError)
|
|
("%p" % StringSpecs::MyArray[1]).should == "1"
|
|
("%p %p" % StringSpecs::MyArray[1, 2]).should == "1 2"
|
|
end
|
|
|
|
it "allows positional arguments for width star and precision star arguments" do
|
|
("%*1$.*2$3$d" % [10, 5, 1]).should == " 00001"
|
|
end
|
|
|
|
it "allows negative width to imply '-' flag" do
|
|
("%*1$.*2$3$d" % [-10, 5, 1]).should == "00001 "
|
|
("%-*1$.*2$3$d" % [10, 5, 1]).should == "00001 "
|
|
("%-*1$.*2$3$d" % [-10, 5, 1]).should == "00001 "
|
|
end
|
|
|
|
it "ignores negative precision" do
|
|
("%*1$.*2$3$d" % [10, -5, 1]).should == " 1"
|
|
end
|
|
|
|
it "allows a star to take an argument number to use as the width" do
|
|
("%1$*2$s" % ["a", 8]).should == " a"
|
|
("%1$*10$s" % ["a",0,0,0,0,0,0,0,0,8]).should == " a"
|
|
end
|
|
|
|
it "calls to_int on width star and precision star tokens" do
|
|
w = mock('10')
|
|
w.should_receive(:to_int).and_return(10)
|
|
|
|
p = mock('5')
|
|
p.should_receive(:to_int).and_return(5)
|
|
|
|
("%*.*f" % [w, p, 1]).should == " 1.00000"
|
|
|
|
|
|
w = mock('10')
|
|
w.should_receive(:to_int).and_return(10)
|
|
|
|
p = mock('5')
|
|
p.should_receive(:to_int).and_return(5)
|
|
|
|
("%*.*d" % [w, p, 1]).should == " 00001"
|
|
end
|
|
|
|
it "does not call #to_a to convert the argument" do
|
|
x = mock("string modulo to_a")
|
|
x.should_not_receive(:to_a)
|
|
x.should_receive(:to_s).and_return("x")
|
|
|
|
("%s" % x).should == "x"
|
|
end
|
|
|
|
it "calls #to_ary to convert the argument" do
|
|
x = mock("string modulo to_ary")
|
|
x.should_not_receive(:to_s)
|
|
x.should_receive(:to_ary).and_return(["x"])
|
|
|
|
("%s" % x).should == "x"
|
|
end
|
|
|
|
it "wraps the object in an Array if #to_ary returns nil" do
|
|
x = mock("string modulo to_ary")
|
|
x.should_receive(:to_ary).and_return(nil)
|
|
x.should_receive(:to_s).and_return("x")
|
|
|
|
("%s" % x).should == "x"
|
|
end
|
|
|
|
it "raises a TypeError if #to_ary does not return an Array" do
|
|
x = mock("string modulo to_ary")
|
|
x.should_receive(:to_ary).and_return("x")
|
|
|
|
-> { "%s" % x }.should raise_error(TypeError)
|
|
end
|
|
|
|
it "tries to convert the argument to Array by calling #to_ary" do
|
|
obj = mock('[1,2]')
|
|
def obj.to_ary() [1, 2] end
|
|
def obj.to_s() "obj" end
|
|
("%s %s" % obj).should == "1 2"
|
|
("%s" % obj).should == "1"
|
|
end
|
|
|
|
it "doesn't return subclass instances when called on a subclass" do
|
|
universal = mock('0')
|
|
def universal.to_int() 0 end
|
|
def universal.to_str() "0" end
|
|
def universal.to_f() 0.0 end
|
|
|
|
[
|
|
"", "foo",
|
|
"%b", "%B", "%c", "%d", "%e", "%E",
|
|
"%f", "%g", "%G", "%i", "%o", "%p",
|
|
"%s", "%u", "%x", "%X"
|
|
].each do |format|
|
|
(StringSpecs::MyString.new(format) % universal).should be_an_instance_of(String)
|
|
end
|
|
end
|
|
|
|
ruby_version_is ''...'2.7' do
|
|
it "always taints the result when the format string is tainted" do
|
|
universal = mock('0')
|
|
def universal.to_int() 0 end
|
|
def universal.to_str() "0" end
|
|
def universal.to_f() 0.0 end
|
|
|
|
[
|
|
"", "foo",
|
|
"%b", "%B", "%c", "%d", "%e", "%E",
|
|
"%f", "%g", "%G", "%i", "%o", "%p",
|
|
"%s", "%u", "%x", "%X"
|
|
].each do |format|
|
|
subcls_format = StringSpecs::MyString.new(format)
|
|
subcls_format.taint
|
|
format.taint
|
|
|
|
(format % universal).should.tainted?
|
|
(subcls_format % universal).should.tainted?
|
|
end
|
|
end
|
|
end
|
|
|
|
it "supports binary formats using %b for positive numbers" do
|
|
("%b" % 10).should == "1010"
|
|
("% b" % 10).should == " 1010"
|
|
("%1$b" % [10, 20]).should == "1010"
|
|
("%#b" % 10).should == "0b1010"
|
|
("%+b" % 10).should == "+1010"
|
|
("%-9b" % 10).should == "1010 "
|
|
("%05b" % 10).should == "01010"
|
|
("%*b" % [10, 6]).should == " 110"
|
|
("%*b" % [-10, 6]).should == "110 "
|
|
("%.4b" % 2).should == "0010"
|
|
("%.32b" % 2147483648).should == "10000000000000000000000000000000"
|
|
end
|
|
|
|
it "supports binary formats using %b for negative numbers" do
|
|
("%b" % -5).should == "..1011"
|
|
("%0b" % -5).should == "..1011"
|
|
("%.1b" % -5).should == "..1011"
|
|
("%.7b" % -5).should == "..11011"
|
|
("%.10b" % -5).should == "..11111011"
|
|
("% b" % -5).should == "-101"
|
|
("%+b" % -5).should == "-101"
|
|
not_supported_on :opal do
|
|
("%b" % -(2 ** 64 + 5)).should ==
|
|
"..101111111111111111111111111111111111111111111111111111111111111011"
|
|
end
|
|
end
|
|
|
|
it "supports binary formats using %B with same behaviour as %b except for using 0B instead of 0b for #" do
|
|
("%B" % 10).should == ("%b" % 10)
|
|
("% B" % 10).should == ("% b" % 10)
|
|
("%1$B" % [10, 20]).should == ("%1$b" % [10, 20])
|
|
("%+B" % 10).should == ("%+b" % 10)
|
|
("%-9B" % 10).should == ("%-9b" % 10)
|
|
("%05B" % 10).should == ("%05b" % 10)
|
|
("%*B" % [10, 6]).should == ("%*b" % [10, 6])
|
|
("%*B" % [-10, 6]).should == ("%*b" % [-10, 6])
|
|
|
|
("%B" % -5).should == ("%b" % -5)
|
|
("%0B" % -5).should == ("%0b" % -5)
|
|
("%.1B" % -5).should == ("%.1b" % -5)
|
|
("%.7B" % -5).should == ("%.7b" % -5)
|
|
("%.10B" % -5).should == ("%.10b" % -5)
|
|
("% B" % -5).should == ("% b" % -5)
|
|
("%+B" % -5).should == ("%+b" % -5)
|
|
not_supported_on :opal do
|
|
("%B" % -(2 ** 64 + 5)).should == ("%b" % -(2 ** 64 + 5))
|
|
end
|
|
|
|
("%#B" % 10).should == "0B1010"
|
|
end
|
|
|
|
it "supports character formats using %c" do
|
|
("%c" % 10).should == "\n"
|
|
("%2$c" % [10, 11, 14]).should == "\v"
|
|
("%-4c" % 10).should == "\n "
|
|
("%*c" % [10, 3]).should == " \003"
|
|
("%c" % 42).should == "*"
|
|
|
|
-> { "%c" % Object }.should raise_error(TypeError)
|
|
end
|
|
|
|
it "supports single character strings as argument for %c" do
|
|
("%c" % 'A').should == "A"
|
|
end
|
|
|
|
it "raises an exception for multiple character strings as argument for %c" do
|
|
-> { "%c" % 'AA' }.should raise_error(ArgumentError)
|
|
end
|
|
|
|
it "calls to_str on argument for %c formats" do
|
|
obj = mock('A')
|
|
obj.should_receive(:to_str).and_return('A')
|
|
|
|
("%c" % obj).should == "A"
|
|
end
|
|
|
|
it "calls #to_ary on argument for %c formats" do
|
|
obj = mock('65')
|
|
obj.should_receive(:to_ary).and_return([65])
|
|
("%c" % obj).should == ("%c" % [65])
|
|
end
|
|
|
|
it "calls #to_int on argument for %c formats, if the argument does not respond to #to_ary" do
|
|
obj = mock('65')
|
|
obj.should_receive(:to_int).and_return(65)
|
|
|
|
("%c" % obj).should == ("%c" % 65)
|
|
end
|
|
|
|
%w(d i).each do |f|
|
|
format = "%" + f
|
|
|
|
it "supports integer formats using #{format}" do
|
|
("%#{f}" % 10).should == "10"
|
|
("% #{f}" % 10).should == " 10"
|
|
("%1$#{f}" % [10, 20]).should == "10"
|
|
("%+#{f}" % 10).should == "+10"
|
|
("%-7#{f}" % 10).should == "10 "
|
|
("%04#{f}" % 10).should == "0010"
|
|
("%*#{f}" % [10, 4]).should == " 4"
|
|
("%6.4#{f}" % 123).should == " 0123"
|
|
end
|
|
|
|
it "supports negative integers using #{format}" do
|
|
("%#{f}" % -5).should == "-5"
|
|
("%3#{f}" % -5).should == " -5"
|
|
("%03#{f}" % -5).should == "-05"
|
|
("%+03#{f}" % -5).should == "-05"
|
|
("%+.2#{f}" % -5).should == "-05"
|
|
("%-3#{f}" % -5).should == "-5 "
|
|
("%6.4#{f}" % -123).should == " -0123"
|
|
end
|
|
|
|
it "supports negative integers using #{format}, giving priority to `-`" do
|
|
("%-03#{f}" % -5).should == "-5 "
|
|
("%+-03#{f}" % -5).should == "-5 "
|
|
end
|
|
end
|
|
|
|
it "supports float formats using %e" do
|
|
("%e" % 10).should == "1.000000e+01"
|
|
("% e" % 10).should == " 1.000000e+01"
|
|
("%1$e" % 10).should == "1.000000e+01"
|
|
("%#e" % 10).should == "1.000000e+01"
|
|
("%+e" % 10).should == "+1.000000e+01"
|
|
("%-7e" % 10).should == "1.000000e+01"
|
|
("%05e" % 10).should == "1.000000e+01"
|
|
("%*e" % [10, 9]).should == "9.000000e+00"
|
|
end
|
|
|
|
it "supports float formats using %e, but Inf, -Inf, and NaN are not floats" do
|
|
("%e" % 1e1020).should == "Inf"
|
|
("%e" % -1e1020).should == "-Inf"
|
|
("%e" % -Float::NAN).should == "NaN"
|
|
("%e" % Float::NAN).should == "NaN"
|
|
end
|
|
|
|
it "supports float formats using %E, but Inf, -Inf, and NaN are not floats" do
|
|
("%E" % 1e1020).should == "Inf"
|
|
("%E" % -1e1020).should == "-Inf"
|
|
("%-10E" % 1e1020).should == "Inf "
|
|
("%10E" % 1e1020).should == " Inf"
|
|
("%+E" % 1e1020).should == "+Inf"
|
|
("% E" % 1e1020).should == " Inf"
|
|
("%E" % Float::NAN).should == "NaN"
|
|
("%E" % -Float::NAN).should == "NaN"
|
|
end
|
|
|
|
it "supports float formats using %E" do
|
|
("%E" % 10).should == "1.000000E+01"
|
|
("% E" % 10).should == " 1.000000E+01"
|
|
("%1$E" % 10).should == "1.000000E+01"
|
|
("%#E" % 10).should == "1.000000E+01"
|
|
("%+E" % 10).should == "+1.000000E+01"
|
|
("%-7E" % 10).should == "1.000000E+01"
|
|
("%05E" % 10).should == "1.000000E+01"
|
|
("%*E" % [10, 9]).should == "9.000000E+00"
|
|
end
|
|
|
|
it "pads with spaces for %E with Inf, -Inf, and NaN" do
|
|
("%010E" % -1e1020).should == " -Inf"
|
|
("%010E" % 1e1020).should == " Inf"
|
|
("%010E" % Float::NAN).should == " NaN"
|
|
end
|
|
|
|
it "supports float formats using %f" do
|
|
("%f" % 10).should == "10.000000"
|
|
("% f" % 10).should == " 10.000000"
|
|
("%1$f" % 10).should == "10.000000"
|
|
("%#f" % 10).should == "10.000000"
|
|
("%#0.3f" % 10).should == "10.000"
|
|
("%+f" % 10).should == "+10.000000"
|
|
("%-7f" % 10).should == "10.000000"
|
|
("%05f" % 10).should == "10.000000"
|
|
("%0.5f" % 10).should == "10.00000"
|
|
("%*f" % [10, 9]).should == " 9.000000"
|
|
end
|
|
|
|
it "supports float formats using %g" do
|
|
("%g" % 10).should == "10"
|
|
("% g" % 10).should == " 10"
|
|
("%1$g" % 10).should == "10"
|
|
("%#g" % 10).should == "10.0000"
|
|
("%#.3g" % 10).should == "10.0"
|
|
("%+g" % 10).should == "+10"
|
|
("%-7g" % 10).should == "10 "
|
|
("%05g" % 10).should == "00010"
|
|
("%g" % 10**10).should == "1e+10"
|
|
("%*g" % [10, 9]).should == " 9"
|
|
end
|
|
|
|
it "supports float formats using %G" do
|
|
("%G" % 10).should == "10"
|
|
("% G" % 10).should == " 10"
|
|
("%1$G" % 10).should == "10"
|
|
("%#G" % 10).should == "10.0000"
|
|
("%#.3G" % 10).should == "10.0"
|
|
("%+G" % 10).should == "+10"
|
|
("%-7G" % 10).should == "10 "
|
|
("%05G" % 10).should == "00010"
|
|
("%G" % 10**10).should == "1E+10"
|
|
("%*G" % [10, 9]).should == " 9"
|
|
end
|
|
|
|
it "supports octal formats using %o for positive numbers" do
|
|
("%o" % 10).should == "12"
|
|
("% o" % 10).should == " 12"
|
|
("%1$o" % [10, 20]).should == "12"
|
|
("%#o" % 10).should == "012"
|
|
("%+o" % 10).should == "+12"
|
|
("%-9o" % 10).should == "12 "
|
|
("%05o" % 10).should == "00012"
|
|
("%*o" % [10, 6]).should == " 6"
|
|
end
|
|
|
|
it "supports octal formats using %o for negative numbers" do
|
|
# These are incredibly wrong. -05 == -5, not 7177777...whatever
|
|
("%o" % -5).should == "..73"
|
|
("%0o" % -5).should == "..73"
|
|
("%.4o" % 20).should == "0024"
|
|
("%.1o" % -5).should == "..73"
|
|
("%.7o" % -5).should == "..77773"
|
|
("%.10o" % -5).should == "..77777773"
|
|
|
|
("% o" % -26).should == "-32"
|
|
("%+o" % -26).should == "-32"
|
|
not_supported_on :opal do
|
|
("%o" % -(2 ** 64 + 5)).should == "..75777777777777777777773"
|
|
end
|
|
end
|
|
|
|
it "supports inspect formats using %p" do
|
|
("%p" % 10).should == "10"
|
|
("%1$p" % [10, 5]).should == "10"
|
|
("%-22p" % 10).should == "10 "
|
|
("%*p" % [10, 10]).should == " 10"
|
|
("%p" % {capture: 1}).should == "{:capture=>1}"
|
|
("%p" % "str").should == "\"str\""
|
|
end
|
|
|
|
it "calls inspect on arguments for %p format" do
|
|
obj = mock('obj')
|
|
def obj.inspect() "obj" end
|
|
("%p" % obj).should == "obj"
|
|
|
|
# undef is not working
|
|
# obj = mock('obj')
|
|
# class << obj; undef :inspect; end
|
|
# def obj.method_missing(*args) "obj" end
|
|
# ("%p" % obj).should == "obj"
|
|
end
|
|
|
|
ruby_version_is ''...'2.7' do
|
|
it "taints result for %p when argument.inspect is tainted" do
|
|
obj = mock('x')
|
|
def obj.inspect() "x".taint end
|
|
|
|
("%p" % obj).should.tainted?
|
|
|
|
obj = mock('x'); obj.taint
|
|
def obj.inspect() "x" end
|
|
|
|
("%p" % obj).should_not.tainted?
|
|
end
|
|
end
|
|
|
|
it "supports string formats using %s" do
|
|
("%s" % "hello").should == "hello"
|
|
("%s" % "").should == ""
|
|
("%s" % 10).should == "10"
|
|
("%1$s" % [10, 8]).should == "10"
|
|
("%-5s" % 10).should == "10 "
|
|
("%*s" % [10, 9]).should == " 9"
|
|
end
|
|
|
|
it "respects a space padding request not as part of the width" do
|
|
x = "% -5s" % ["foo"]
|
|
x.should == "foo "
|
|
end
|
|
|
|
it "calls to_s on non-String arguments for %s format" do
|
|
obj = mock('obj')
|
|
def obj.to_s() "obj" end
|
|
|
|
("%s" % obj).should == "obj"
|
|
|
|
# undef doesn't work
|
|
# obj = mock('obj')
|
|
# class << obj; undef :to_s; end
|
|
# def obj.method_missing(*args) "obj" end
|
|
#
|
|
# ("%s" % obj).should == "obj"
|
|
end
|
|
|
|
ruby_version_is ''...'2.7' do
|
|
it "taints result for %s when argument is tainted" do
|
|
("%s" % "x".taint).should.tainted?
|
|
("%s" % mock('x').taint).should.tainted?
|
|
end
|
|
end
|
|
|
|
# MRI crashes on this one.
|
|
# See http://groups.google.com/group/ruby-core-google/t/c285c18cd94c216d
|
|
it "raises an ArgumentError for huge precisions for %s" do
|
|
block = -> { "%.25555555555555555555555555555555555555s" % "hello world" }
|
|
block.should raise_error(ArgumentError)
|
|
end
|
|
|
|
# Note: %u has been changed to an alias for %d in 1.9.
|
|
it "supports unsigned formats using %u" do
|
|
("%u" % 10).should == "10"
|
|
("% u" % 10).should == " 10"
|
|
("%1$u" % [10, 20]).should == "10"
|
|
("%+u" % 10).should == "+10"
|
|
("%-7u" % 10).should == "10 "
|
|
("%04u" % 10).should == "0010"
|
|
("%*u" % [10, 4]).should == " 4"
|
|
end
|
|
|
|
it "formats negative values with a leading sign using %u" do
|
|
("% u" % -26).should == "-26"
|
|
("%+u" % -26).should == "-26"
|
|
end
|
|
|
|
it "supports negative bignums with %u or %d" do
|
|
("%u" % -(2 ** 64 + 5)).should == "-18446744073709551621"
|
|
("%d" % -(2 ** 64 + 5)).should == "-18446744073709551621"
|
|
end
|
|
|
|
it "supports hex formats using %x for positive numbers" do
|
|
("%x" % 10).should == "a"
|
|
("% x" % 10).should == " a"
|
|
("%1$x" % [10, 20]).should == "a"
|
|
("%#x" % 10).should == "0xa"
|
|
("%+x" % 10).should == "+a"
|
|
("%-9x" % 10).should == "a "
|
|
("%05x" % 10).should == "0000a"
|
|
("%*x" % [10, 6]).should == " 6"
|
|
("%.4x" % 20).should == "0014"
|
|
("%x" % 0xFFFFFFFF).should == "ffffffff"
|
|
end
|
|
|
|
it "supports hex formats using %x for negative numbers" do
|
|
("%x" % -5).should == "..fb"
|
|
("%0x" % -5).should == "..fb"
|
|
("%.1x" % -5).should == "..fb"
|
|
("%.7x" % -5).should == "..ffffb"
|
|
("%.10x" % -5).should == "..fffffffb"
|
|
("% x" % -26).should == "-1a"
|
|
("%+x" % -26).should == "-1a"
|
|
not_supported_on :opal do
|
|
("%x" % -(2 ** 64 + 5)).should == "..fefffffffffffffffb"
|
|
end
|
|
end
|
|
|
|
it "supports hex formats using %X for positive numbers" do
|
|
("%X" % 10).should == "A"
|
|
("% X" % 10).should == " A"
|
|
("%1$X" % [10, 20]).should == "A"
|
|
("%#X" % 10).should == "0XA"
|
|
("%+X" % 10).should == "+A"
|
|
("%-9X" % 10).should == "A "
|
|
("%05X" % 10).should == "0000A"
|
|
("%*X" % [10, 6]).should == " 6"
|
|
("%X" % 0xFFFFFFFF).should == "FFFFFFFF"
|
|
end
|
|
|
|
it "supports hex formats using %X for negative numbers" do
|
|
("%X" % -5).should == "..FB"
|
|
("%0X" % -5).should == "..FB"
|
|
("%.1X" % -5).should == "..FB"
|
|
("%.7X" % -5).should == "..FFFFB"
|
|
("%.10X" % -5).should == "..FFFFFFFB"
|
|
("% X" % -26).should == "-1A"
|
|
("%+X" % -26).should == "-1A"
|
|
not_supported_on :opal do
|
|
("%X" % -(2 ** 64 + 5)).should == "..FEFFFFFFFFFFFFFFFB"
|
|
end
|
|
end
|
|
|
|
it "formats zero without prefix using %#x" do
|
|
("%#x" % 0).should == "0"
|
|
end
|
|
|
|
it "formats zero without prefix using %#X" do
|
|
("%#X" % 0).should == "0"
|
|
end
|
|
|
|
%w(b d i o u x X).each do |f|
|
|
format = "%" + f
|
|
|
|
it "behaves as if calling Kernel#Integer for #{format} argument, if it does not respond to #to_ary" do
|
|
(format % "10").should == (format % Kernel.Integer("10"))
|
|
(format % "0x42").should == (format % Kernel.Integer("0x42"))
|
|
(format % "0b1101").should == (format % Kernel.Integer("0b1101"))
|
|
(format % "0b1101_0000").should == (format % Kernel.Integer("0b1101_0000"))
|
|
(format % "0777").should == (format % Kernel.Integer("0777"))
|
|
-> {
|
|
# see [ruby-core:14139] for more details
|
|
(format % "0777").should == (format % Kernel.Integer("0777"))
|
|
}.should_not raise_error(ArgumentError)
|
|
|
|
-> { format % "0__7_7_7" }.should raise_error(ArgumentError)
|
|
|
|
-> { format % "" }.should raise_error(ArgumentError)
|
|
-> { format % "x" }.should raise_error(ArgumentError)
|
|
-> { format % "5x" }.should raise_error(ArgumentError)
|
|
-> { format % "08" }.should raise_error(ArgumentError)
|
|
-> { format % "0b2" }.should raise_error(ArgumentError)
|
|
-> { format % "123__456" }.should raise_error(ArgumentError)
|
|
|
|
obj = mock('5')
|
|
obj.should_receive(:to_i).and_return(5)
|
|
(format % obj).should == (format % 5)
|
|
|
|
obj = mock('6')
|
|
obj.stub!(:to_i).and_return(5)
|
|
obj.should_receive(:to_int).and_return(6)
|
|
(format % obj).should == (format % 6)
|
|
end
|
|
end
|
|
|
|
%w(e E f g G).each do |f|
|
|
format = "%" + f
|
|
|
|
it "tries to convert the passed argument to an Array using #to_ary" do
|
|
obj = mock('3.14')
|
|
obj.should_receive(:to_ary).and_return([3.14])
|
|
(format % obj).should == (format % [3.14])
|
|
end
|
|
|
|
it "behaves as if calling Kernel#Float for #{format} arguments, when the passed argument does not respond to #to_ary" do
|
|
(format % 10).should == (format % 10.0)
|
|
(format % "-10.4e-20").should == (format % -10.4e-20)
|
|
(format % ".5").should == (format % 0.5)
|
|
(format % "-.5").should == (format % -0.5)
|
|
# Something's strange with this spec:
|
|
# it works just fine in individual mode, but not when run as part of a group
|
|
(format % "10_1_0.5_5_5").should == (format % 1010.555)
|
|
|
|
(format % "0777").should == (format % 777)
|
|
|
|
-> { format % "" }.should raise_error(ArgumentError)
|
|
-> { format % "x" }.should raise_error(ArgumentError)
|
|
-> { format % "." }.should raise_error(ArgumentError)
|
|
-> { format % "10." }.should raise_error(ArgumentError)
|
|
-> { format % "5x" }.should raise_error(ArgumentError)
|
|
-> { format % "0b1" }.should raise_error(ArgumentError)
|
|
-> { format % "10e10.5" }.should raise_error(ArgumentError)
|
|
-> { format % "10__10" }.should raise_error(ArgumentError)
|
|
-> { format % "10.10__10" }.should raise_error(ArgumentError)
|
|
|
|
obj = mock('5.0')
|
|
obj.should_receive(:to_f).and_return(5.0)
|
|
(format % obj).should == (format % 5.0)
|
|
end
|
|
|
|
it "behaves as if calling Kernel#Float for #{format} arguments, when the passed argument is hexadecimal string" do
|
|
(format % "0xA").should == (format % 0xA)
|
|
end
|
|
|
|
ruby_version_is ''...'2.7' do
|
|
it "doesn't taint the result for #{format} when argument is tainted" do
|
|
(format % "5".taint).should_not.tainted?
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "when format string contains %{} sections" do
|
|
it "replaces %{} sections with values from passed-in hash" do
|
|
("%{foo}bar" % {foo: 'oof'}).should == "oofbar"
|
|
end
|
|
|
|
it "should raise ArgumentError if no hash given" do
|
|
-> {"%{foo}" % []}.should raise_error(ArgumentError)
|
|
end
|
|
end
|
|
|
|
describe "when format string contains %<> formats" do
|
|
it "uses the named argument for the format's value" do
|
|
("%<foo>d" % {foo: 1}).should == "1"
|
|
end
|
|
|
|
it "raises KeyError if key is missing from passed-in hash" do
|
|
-> {"%<foo>d" % {}}.should raise_error(KeyError)
|
|
end
|
|
|
|
it "should raise ArgumentError if no hash given" do
|
|
-> {"%<foo>" % []}.should raise_error(ArgumentError)
|
|
end
|
|
end
|
|
end
|