require File.dirname(File.join(__rhoGetCurrentDir(), __FILE__)) + '/../../spec_helper' require File.dirname(File.join(__rhoGetCurrentDir(), __FILE__)) + '/fixtures/classes.rb' describe "String#%" do 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 it "formats single % characters before a newline or NULL as literal %s" do ("%" % []).should == "%" ("foo%" % []).should == "foo%" ("%\n" % []).should == "%\n" ("foo%\n" % []).should == "foo%\n" ("%\0" % []).should == "%\0" ("foo%\0" % []).should == "foo%\0" ("%\n.3f" % 1.2).should == "%\n.3f" ("%\0.3f" % 1.2).should == "%\0.3f" end it "raises an error if single % appears anywhere else" do lambda { (" % " % []) }.should raise_error(ArgumentError) lambda { ("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 lambda { "%.\n3f" % 1.2 }.should raise_error(ArgumentError) lambda { "%.3\nf" % 1.2 }.should raise_error(ArgumentError) lambda { "%.\03f" % 1.2 }.should raise_error(ArgumentError) lambda { "%.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 lambda { "" % [1, 2, 3] }.should raise_error(ArgumentError) lambda { "%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 lambda { "%1" % [] }.should raise_error(ArgumentError) lambda { "%+" % [] }.should raise_error(ArgumentError) lambda { "%-" % [] }.should raise_error(ArgumentError) lambda { "%#" % [] }.should raise_error(ArgumentError) lambda { "%0" % [] }.should raise_error(ArgumentError) lambda { "%*" % [] }.should raise_error(ArgumentError) lambda { "%." % [] }.should raise_error(ArgumentError) lambda { "%_" % [] }.should raise_error(ArgumentError) lambda { "%0$s" % "x" }.should raise_error(ArgumentError) lambda { "%*0$s" % [5, "x"] }.should raise_error(ArgumentError) lambda { "%*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 lambda { "%1$1$s" % "foo" }.should raise_error(ArgumentError) end it "raises an ArgumentError when multiple width star tokens are given for one format specifier" do lambda { "%**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 lambda { "%5*s" % [5, 5] }.should raise_error(ArgumentError) end it "raises an ArgumentError when multiple precision tokens are given" do lambda { "%.5.5s" % 5 }.should raise_error(ArgumentError) lambda { "%.5.*s" % [5, 5] }.should raise_error(ArgumentError) lambda { "%.*.5s" % [5, 5] }.should raise_error(ArgumentError) end it "raises an ArgumentError when there are less arguments than format specifiers" do ("foo" % []).should == "foo" lambda { "%s" % [] }.should raise_error(ArgumentError) lambda { "%s %s" % [1] }.should raise_error(ArgumentError) end it "raises an ArgumentError when absolute and relative argument numbers are mixed" do lambda { "%s %1$s" % "foo" }.should raise_error(ArgumentError) lambda { "%1$s %s" % "foo" }.should raise_error(ArgumentError) lambda { "%s %2$s" % ["foo", "bar"] }.should raise_error(ArgumentError) lambda { "%2$s %s" % ["foo", "bar"] }.should raise_error(ArgumentError) lambda { "%*2$s" % [5, 5, 5] }.should raise_error(ArgumentError) lambda { "%*.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError) lambda { "%*2$.*2$s" % [5, 5, 5] }.should raise_error(ArgumentError) lambda { "%*.*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 lambda { "%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 lambda { "%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 "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" end ruby_bug "#", "1.8.6.228" do 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 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).class.should == String end end 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).tainted?.should == true (subcls_format % universal).tainted?.should == true 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" end ruby_version_is ""..."1.9" do it "supports binary formats using %b for negative numbers" do ("%b" % -5).should == "..1011" ("%0b" % -5).should == "1011" ("%.4b" % 2).should == "0010" ("%.1b" % -5).should == "1011" ("%.7b" % -5).should == "1111011" ("%.10b" % -5).should == "1111111011" ("% b" % -5).should == "-101" ("%+b" % -5).should == "-101" ("%b" % -(2 ** 64 + 5)).should == "..101111111111111111111111111111111111111111111111111111111111111011" end end ruby_version_is "1.9" do 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" ("%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) ("%B" % -(2 ** 64 + 5)).should == ("%b" % -(2 ** 64 + 5)) ("%#B" % 10).should == "0B1010" end ruby_version_is ""..."1.9" do 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" % (256 + 42)).should == "*" lambda { "%c" % Object }.should raise_error(TypeError) end it "uses argument % 256" do ("%c" % [256 * 3 + 64]).should == ("%c" % 64) ("%c" % -200).should == ("%c" % 56) end end ruby_version_is "1.9" do 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 == "*" lambda { "%c" % Object }.should raise_error(TypeError) end end ruby_version_is "1.8.6.278" do 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 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" 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 # TODO: If http://redmine.ruby-lang.org/issues/show/1566 is confirmed, we # can guard the behaviour of capitalising Inf and NaN as a bug, and # removed the compliance guards. ruby_version_is ""..."1.9" do not_compliant_on :rubinius, :jruby do it "supports float formats using %e, and downcases -Inf, Inf, and NaN" do ("%e" % 1e1020).should == "inf" ("%e" % -1e1020).should == "-inf" ("%e" % (0.0/0)).should == "nan" ("%e" % (-0e0/0)).should == "nan" end end end # Inf, -Inf, and NaN are identifiers for results of floating point operations # that cannot be expressed with any value in the set of real numbers. Upcasing # or downcasing these identifiers for %e or %E, which refers to the case of the # of the exponent identifier, is silly. # TODO: If http://redmine.ruby-lang.org/issues/show/1566 is confirmed, we # can guard the behaviour of capitalising Inf and NaN as a bug, and # removed the compliance guards. deviates_on :rubinius, :jruby do it "supports float formats using %e, but Inf, -Inf, and NaN are not floats" do ("%e" % 1e1020).should == "Inf" ("%e" % -1e1020).should == "-Inf" ("%e" % (-0e0/0)).should == "NaN" ("%e" % (0.0/0)).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" % (0.0/0)).should == "NaN" ("%E" % (-0e0/0)).should == "NaN" 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 # TODO: If http://redmine.ruby-lang.org/issues/show/1566 is confirmed, we # can guard the behaviour of capitalising Inf and NaN as a bug, and # removed the compliance guards. not_compliant_on :rubinius, :jruby do ruby_version_is ""..."1.9" do it "supports float formats using %E, and upcases Inf, -Inf, and NaN" do ("%E" % 1e1020).should == "INF" ("%E" % -1e1020).should == "-INF" ("%-10E" % 1e1020).should == "INF " ("%+E" % 1e1020).should == "+INF" ("% E" % 1e1020).should == " INF" ("%E" % (0.0/0)).should == "NAN" ("%E" % (-0e0/0)).should == "NAN" end end # TODO: If http://redmine.ruby-lang.org/issues/show/1566 is confirmed, we # can guard the behaviour of capitalising Inf and NaN as a bug, and # removed the compliance guards. ruby_version_is ""..."1.9" do platform_is :darwin do it "pads with zeros using %E with Inf, -Inf, and NaN" do ("%010E" % -1e1020).should == "-000000INF" ("%010E" % 1e1020).should == "0000000INF" ("%010E" % (0.0/0)).should == "0000000NAN" end end platform_is_not :darwin do it "pads with spaces for %E with Inf, -Inf, and NaN" do ("%010E" % -1e1020).should == " -INF" ("%010E" % 1e1020).should == " INF" ("%010E" % (0.0/0)).should == " NAN" end end end ruby_version_is "1.9" do platform_is :darwin do it "pads with zeros using %E with Inf, -Inf, and NaN" do ("%010E" % -1e1020).should == "-000000Inf" ("%010E" % 1e1020).should == "0000000Inf" ("%010E" % (0.0/0)).should == "0000000NaN" end end platform_is_not :darwin do it "pads with spaces for %E with Inf, -Inf, and NaN" do ("%010E" % -1e1020).should == " -Inf" ("%010E" % 1e1020).should == " Inf" ("%010E" % (0.0/0)).should == " NaN" end end end 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" ("%+f" % 10).should == "+10.000000" ("%-7f" % 10).should == "10.000000" ("%05f" % 10).should == "10.000000" ("%*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" ("%+g" % 10).should == "+10" ("%-7g" % 10).should == "10 " ("%05g" % 10).should == "00010" ("%*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" ("%+G" % 10).should == "+10" ("%-7G" % 10).should == "10 " ("%05G" % 10).should == "00010" ("%*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 ruby_version_is ""..."1.9" do 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 == "7777773" ("%.10o" % -5).should == "7777777773" ("% o" % -26).should == "-32" ("%+o" % -26).should == "-32" ("%o" % -(2 ** 64 + 5)).should == "..75777777777777777777773" end end ruby_version_is "1.9" do 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" ("%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" 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 it "taints result for %p when argument.inspect is tainted" do obj = mock('x') def obj.inspect() "x".taint end ("%p" % obj).tainted?.should == true obj = mock('x'); obj.taint def obj.inspect() "x" end ("%p" % obj).tainted?.should == false end it "supports string formats using %s" do ("%s" % 10).should == "10" ("%1$s" % [10, 8]).should == "10" ("%-5s" % 10).should == "10 " ("%*s" % [10, 9]).should == " 9" end it "calls to_s on 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 it "taints result for %s when argument is tainted" do ("%s" % "x".taint).tainted?.should == true ("%s" % mock('x').taint).tainted?.should == true ("%s" % 5.0.taint).tainted?.should == true 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 = lambda { "%.25555555555555555555555555555555555555s" % "hello world" } block.should raise_error(ArgumentError) end # Note: %u has been changed to an alias for %d in MRI 1.9 trunk. # Let's wait a bit for it to cool down and see if it will # be changed for 1.8 as well. 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 ruby_version_is "" ... "1.9" do platform_is :wordsize => 64 do it "supports unsigned formats using %u on 64-bit" do ("%u" % -5).should == "..#{2**64 - 5}" ("%0u" % -5).should == (2**64 - 5).to_s ("%.1u" % -5).should == (2**64 - 5).to_s ("%.7u" % -5).should == (2**64 - 5).to_s ("%.10u" % -5).should == (2**64 - 5).to_s end end platform_is :wordsize => 32 do it "supports unsigned formats using %u on 32-bit" do ("%u" % -5).should == "..#{2**32 - 5}" ("%0u" % -5).should == (2**32 - 5).to_s ("%.1u" % -5).should == (2**32 - 5).to_s ("%.7u" % -5).should == (2**32 - 5).to_s ("%.10u" % -5).should == (2**32 - 5).to_s end end end it "formats negative values with a leading sign using %u" do ("% u" % -26).should == "-26" ("%+u" % -26).should == "-26" end ruby_version_is ""..."1.9" do not_compliant_on :rubinius do # This is the proper, compliant behavior of both JRuby, and # MRI 1.8.6 with patchlevel greater than 114. ruby_bug "http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/8418", "1.8.6.114" do it "supports negative bignums by prefixing the value with dots" do ("%u" % -(2 ** 64 + 5)).should == "..79228162495817593519834398715" end end end deviates_on :rubinius do it "does not support negative bignums" do lambda { ("%u" % -(2 ** 64 + 5)) }.should raise_error(ArgumentError) end end end ruby_version_is "1.9" do it "supports negative bignums with %u or %d" do ("%u" % -(2 ** 64 + 5)).should == "-18446744073709551621" ("%d" % -(2 ** 64 + 5)).should == "-18446744073709551621" 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" ("%.4x" % 20).should == "0014" ("%x" % 0xFFFFFFFF).should == "ffffffff" end ruby_version_is ""..."1.9" do it "supports hex formats using %x for negative numbers" do ("%x" % -5).should == "..fb" ("%0x" % -5).should == "fb" ("%.4x" % 20).should == "0014" ("%.1x" % -5).should == "fb" ("%.7x" % -5).should == "ffffffb" ("%.10x" % -5).should == "fffffffffb" ("% x" % -26).should == "-1a" ("%+x" % -26).should == "-1a" ("%x" % 0xFFFFFFFF).should == "ffffffff" ("%x" % -(2 ** 64 + 5)).should == "..fefffffffffffffffb" end end ruby_version_is "1.9" do 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" ("%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 ruby_version_is "" ... "1.9" do 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 == "FFFFFFB" ("%.10X" % -5).should == "FFFFFFFFFB" ("% X" % -26).should == "-1A" ("%+X" % -26).should == "-1A" ("%X" % 0xFFFFFFFF).should == "FFFFFFFF" ("%X" % -(2 ** 64 + 5)).should == "..FEFFFFFFFFFFFFFFFB" end end ruby_version_is "1.9" do 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" ("%X" % -(2 ** 64 + 5)).should == "..FEFFFFFFFFFFFFFFFB" end end ruby_version_is "1.9" do 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 end ruby_version_is "" ... "1.9" do it 'formats zero with prefix using %#x' do ("%#x" % 0).should == "0x0" end it 'formats zero without prefix using %#X' do ("%#X" % 0).should == "0X0" end 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")) lambda { # see [ruby-core:14139] for more details (format % "0777").should == (format % Kernel.Integer("0777")) }.should_not raise_error(ArgumentError) lambda { format % "0__7_7_7" }.should raise_error(ArgumentError) lambda { format % "" }.should raise_error(ArgumentError) lambda { format % "x" }.should raise_error(ArgumentError) lambda { format % "5x" }.should raise_error(ArgumentError) lambda { format % "08" }.should raise_error(ArgumentError) lambda { format % "0b2" }.should raise_error(ArgumentError) lambda { 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 # 1.9 raises a TypeError for Kernel.Integer(nil), so we version guard this # case ruby_version_is ""..."1.9" do it "behaves as if calling Kernel#Integer(nil) for format argument, if it does not respond to #to_ary" do %w(b d i o u x X).each do |f| format = "%" + f (format % nil).should == (format % Kernel.Integer(nil)) end end end it "doesn't taint the result for #{format} when argument is tainted" do (format % "5".taint).tainted?.should == false end end %w(e E f g G).each do |f| format = "%" + f ruby_version_is "1.8.6.278" do 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 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) lambda { format % "" }.should raise_error(ArgumentError) lambda { format % "x" }.should raise_error(ArgumentError) lambda { format % "." }.should raise_error(ArgumentError) lambda { format % "10." }.should raise_error(ArgumentError) lambda { format % "5x" }.should raise_error(ArgumentError) lambda { format % "0xA" }.should raise_error(ArgumentError) lambda { format % "0b1" }.should raise_error(ArgumentError) lambda { format % "10e10.5" }.should raise_error(ArgumentError) lambda { format % "10__10" }.should raise_error(ArgumentError) lambda { 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 "doesn't taint the result for #{format} when argument is tainted" do (format % "5".taint).tainted?.should == false end end end