1require 'test/unit'
2
3class TestSprintf < Test::Unit::TestCase
4  def test_positional
5    assert_equal("     00001", sprintf("%*1$.*2$3$d", 10, 5, 1))
6  end
7
8  def test_binary
9    assert_equal("0", sprintf("%b", 0))
10    assert_equal("1", sprintf("%b", 1))
11    assert_equal("10", sprintf("%b", 2))
12    assert_equal("..1", sprintf("%b", -1))
13
14    assert_equal("   0", sprintf("%4b", 0))
15    assert_equal("   1", sprintf("%4b", 1))
16    assert_equal("  10", sprintf("%4b", 2))
17    assert_equal(" ..1", sprintf("%4b", -1))
18
19    assert_equal("0000", sprintf("%04b", 0))
20    assert_equal("0001", sprintf("%04b", 1))
21    assert_equal("0010", sprintf("%04b", 2))
22    assert_equal("..11", sprintf("%04b", -1))
23
24    assert_equal("0000", sprintf("%.4b", 0))
25    assert_equal("0001", sprintf("%.4b", 1))
26    assert_equal("0010", sprintf("%.4b", 2))
27    assert_equal("..11", sprintf("%.4b", -1))
28
29    assert_equal("  0000", sprintf("%6.4b", 0))
30    assert_equal("  0001", sprintf("%6.4b", 1))
31    assert_equal("  0010", sprintf("%6.4b", 2))
32    assert_equal("  ..11", sprintf("%6.4b", -1))
33
34    assert_equal("   0", sprintf("%#4b", 0))
35    assert_equal(" 0b1", sprintf("%#4b", 1))
36    assert_equal("0b10", sprintf("%#4b", 2))
37    assert_equal("0b..1", sprintf("%#4b", -1))
38
39    assert_equal("0000", sprintf("%#04b", 0))
40    assert_equal("0b01", sprintf("%#04b", 1))
41    assert_equal("0b10", sprintf("%#04b", 2))
42    assert_equal("0b..1", sprintf("%#04b", -1))
43
44    assert_equal("0000", sprintf("%#.4b", 0))
45    assert_equal("0b0001", sprintf("%#.4b", 1))
46    assert_equal("0b0010", sprintf("%#.4b", 2))
47    assert_equal("0b..11", sprintf("%#.4b", -1))
48
49    assert_equal("  0000", sprintf("%#6.4b", 0))
50    assert_equal("0b0001", sprintf("%#6.4b", 1))
51    assert_equal("0b0010", sprintf("%#6.4b", 2))
52    assert_equal("0b..11", sprintf("%#6.4b", -1))
53
54    assert_equal("+0", sprintf("%+b", 0))
55    assert_equal("+1", sprintf("%+b", 1))
56    assert_equal("+10", sprintf("%+b", 2))
57    assert_equal("-1", sprintf("%+b", -1))
58
59    assert_equal("  +0", sprintf("%+4b", 0))
60    assert_equal("  +1", sprintf("%+4b", 1))
61    assert_equal(" +10", sprintf("%+4b", 2))
62    assert_equal("  -1", sprintf("%+4b", -1))
63
64    assert_equal("+000", sprintf("%+04b", 0))
65    assert_equal("+001", sprintf("%+04b", 1))
66    assert_equal("+010", sprintf("%+04b", 2))
67    assert_equal("-001", sprintf("%+04b", -1))
68
69    assert_equal("+0000", sprintf("%+.4b", 0))
70    assert_equal("+0001", sprintf("%+.4b", 1))
71    assert_equal("+0010", sprintf("%+.4b", 2))
72    assert_equal("-0001", sprintf("%+.4b", -1))
73
74    assert_equal(" +0000", sprintf("%+6.4b", 0))
75    assert_equal(" +0001", sprintf("%+6.4b", 1))
76    assert_equal(" +0010", sprintf("%+6.4b", 2))
77    assert_equal(" -0001", sprintf("%+6.4b", -1))
78  end
79
80  def test_nan
81    nan = 0.0 / 0.0
82    assert_equal("NaN", sprintf("%f", nan))
83    assert_equal("NaN", sprintf("%-f", nan))
84    assert_equal("+NaN", sprintf("%+f", nan))
85
86    assert_equal("     NaN", sprintf("%8f", nan))
87    assert_equal("NaN     ", sprintf("%-8f", nan))
88    assert_equal("    +NaN", sprintf("%+8f", nan))
89
90    assert_equal("     NaN", sprintf("%08f", nan))
91    assert_equal("NaN     ", sprintf("%-08f", nan))
92    assert_equal("    +NaN", sprintf("%+08f", nan))
93
94    assert_equal("     NaN", sprintf("% 8f", nan))
95    assert_equal(" NaN    ", sprintf("%- 8f", nan))
96    assert_equal("    +NaN", sprintf("%+ 8f", nan))
97
98    assert_equal("     NaN", sprintf("% 08f", nan))
99    assert_equal(" NaN    ", sprintf("%- 08f", nan))
100    assert_equal("    +NaN", sprintf("%+ 08f", nan))
101  end
102
103  def test_inf
104    inf = 1.0 / 0.0
105    assert_equal("Inf", sprintf("%f", inf))
106    assert_equal("Inf", sprintf("%-f", inf))
107    assert_equal("+Inf", sprintf("%+f", inf))
108
109    assert_equal("     Inf", sprintf("%8f", inf))
110    assert_equal("Inf     ", sprintf("%-8f", inf))
111    assert_equal("    +Inf", sprintf("%+8f", inf))
112
113    assert_equal("     Inf", sprintf("%08f", inf))
114    assert_equal("Inf     ", sprintf("%-08f", inf))
115    assert_equal("    +Inf", sprintf("%+08f", inf))
116
117    assert_equal("     Inf", sprintf("% 8f", inf))
118    assert_equal(" Inf    ", sprintf("%- 8f", inf))
119    assert_equal("    +Inf", sprintf("%+ 8f", inf))
120
121    assert_equal("     Inf", sprintf("% 08f", inf))
122    assert_equal(" Inf    ", sprintf("%- 08f", inf))
123    assert_equal("    +Inf", sprintf("%+ 08f", inf))
124
125    assert_equal("-Inf", sprintf("%f", -inf))
126    assert_equal("-Inf", sprintf("%-f", -inf))
127    assert_equal("-Inf", sprintf("%+f", -inf))
128
129    assert_equal("    -Inf", sprintf("%8f", -inf))
130    assert_equal("-Inf    ", sprintf("%-8f", -inf))
131    assert_equal("    -Inf", sprintf("%+8f", -inf))
132
133    assert_equal("    -Inf", sprintf("%08f", -inf))
134    assert_equal("-Inf    ", sprintf("%-08f", -inf))
135    assert_equal("    -Inf", sprintf("%+08f", -inf))
136
137    assert_equal("    -Inf", sprintf("% 8f", -inf))
138    assert_equal("-Inf    ", sprintf("%- 8f", -inf))
139    assert_equal("    -Inf", sprintf("%+ 8f", -inf))
140
141    assert_equal("    -Inf", sprintf("% 08f", -inf))
142    assert_equal("-Inf    ", sprintf("%- 08f", -inf))
143    assert_equal("    -Inf", sprintf("%+ 08f", -inf))
144    assert_equal('..f00000000',
145      sprintf("%x", -2**32), '[ruby-dev:32351]')
146    assert_equal("..101111111111111111111111111111111",
147      sprintf("%b", -2147483649), '[ruby-dev:32365]')
148    assert_equal(" Inf", sprintf("% e", inf), '[ruby-dev:34002]')
149  end
150
151  def test_invalid
152    # Star precision before star width:
153    assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%.**d", 5, 10, 1)}
154
155    # Precision before flags and width:
156    assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%.5+05d", 5)}
157    assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%.5 5d", 5)}
158
159    # Overriding a star width with a numeric one:
160    assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%*1s", 5, 1)}
161
162    # Width before flags:
163    assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%5+0d", 1)}
164    assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%5 0d", 1)}
165
166    # Specifying width multiple times:
167    assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%50+30+20+10+5d", 5)}
168    assert_raise(ArgumentError, "[ruby-core:11569]") {sprintf("%50 30 20 10 5d", 5)}
169
170    # Specifying the precision multiple times with negative star arguments:
171    assert_raise(ArgumentError, "[ruby-core:11570]") {sprintf("%.*.*.*.*f", -1, -1, -1, 5, 1)}
172
173    # Null bytes after percent signs are removed:
174    assert_equal("%\0x hello", sprintf("%\0x hello"), "[ruby-core:11571]")
175
176    assert_raise(ArgumentError, "[ruby-core:11573]") {sprintf("%.25555555555555555555555555555555555555s", "hello")}
177
178    assert_raise(ArgumentError) { sprintf("%\1", 1) }
179    assert_raise(ArgumentError) { sprintf("%!", 1) }
180    assert_raise(ArgumentError) { sprintf("%1$1$d", 1) }
181    assert_raise(ArgumentError) { sprintf("%0%") }
182    verbose, $VERBOSE = $VERBOSE, nil
183    assert_nothing_raised { sprintf("", 1) }
184  ensure
185    $VERBOSE = verbose
186  end
187
188  def test_float
189    assert_equal("36893488147419111424",
190                 sprintf("%20.0f", 36893488147419107329.0))
191    assert_equal(" Inf", sprintf("% 0e", 1.0/0.0), "moved from btest/knownbug")
192    assert_equal("       -0.", sprintf("%#10.0f", -0.5), "[ruby-dev:42552]")
193    assert_equal("0x1p+2",   sprintf('%.0a', Float('0x1.fp+1')),   "[ruby-dev:42551]")
194    assert_equal("-0x1.0p+2", sprintf('%.1a', Float('-0x1.ffp+1')), "[ruby-dev:42551]")
195  end
196
197  def test_float_hex
198    assert_equal("-0x0p+0", sprintf("%a", -0.0))
199    assert_equal("0x0p+0", sprintf("%a", 0.0))
200    assert_equal("0x1p-1", sprintf("%a", 0.5))
201    assert_equal("0x1p+0", sprintf("%a", 1.0))
202    assert_equal("0x1p+1", sprintf("%a", 2.0))
203    assert_equal("0x1p+10", sprintf("%a", 1024))
204    assert_equal("0x1.23456p+789", sprintf("%a", 3.704450999893983e+237))
205    assert_equal("0x1p-1074", sprintf("%a", 4.9e-324))
206    assert_equal("Inf", sprintf("%e", Float::INFINITY))
207    assert_equal("Inf", sprintf("%E", Float::INFINITY))
208    assert_equal("NaN", sprintf("%e", Float::NAN))
209    assert_equal("NaN", sprintf("%E", Float::NAN))
210
211    assert_equal("   -0x1p+0", sprintf("%10a", -1))
212    assert_equal(" -0x1.8p+0", sprintf("%10a", -1.5))
213    assert_equal(" -0x1.4p+0", sprintf("%10a", -1.25))
214    assert_equal(" -0x1.2p+0", sprintf("%10a", -1.125))
215    assert_equal(" -0x1.1p+0", sprintf("%10a", -1.0625))
216    assert_equal("-0x1.08p+0", sprintf("%10a", -1.03125))
217
218    bug3962 = '[ruby-core:32841]'
219    assert_equal("-0x0001p+0", sprintf("%010a", -1), bug3962)
220    assert_equal("-0x01.8p+0", sprintf("%010a", -1.5), bug3962)
221    assert_equal("-0x01.4p+0", sprintf("%010a", -1.25), bug3962)
222    assert_equal("-0x01.2p+0", sprintf("%010a", -1.125), bug3962)
223    assert_equal("-0x01.1p+0", sprintf("%010a", -1.0625), bug3962)
224    assert_equal("-0x1.08p+0", sprintf("%010a", -1.03125), bug3962)
225
226    bug3964 = '[ruby-core:32848]'
227    assert_equal("0x000000000000000p+0", sprintf("%020a",  0), bug3964)
228    assert_equal("0x000000000000001p+0", sprintf("%020a", 1), bug3964)
229    assert_equal("-0x00000000000001p+0", sprintf("%020a", -1), bug3964)
230    assert_equal("0x00000000000000.p+0", sprintf("%#020a",  0), bug3964)
231
232    bug3965 = '[ruby-dev:42431]'
233    assert_equal("0x1.p+0", sprintf("%#.0a",  1), bug3965)
234    assert_equal("0x00000000000000.p+0", sprintf("%#020a",  0), bug3965)
235    assert_equal("0x0000.0000000000p+0", sprintf("%#020.10a",  0), bug3965)
236
237    bug3979 = '[ruby-dev:42453]'
238    assert_equal("          0x0.000p+0", sprintf("%20.3a",  0), bug3979)
239    assert_equal("          0x1.000p+0", sprintf("%20.3a",  1), bug3979)
240  end
241
242  BSIZ = 120
243
244  def test_skip
245    assert_equal(" " * BSIZ + "1", sprintf(" " * BSIZ + "%d", 1))
246  end
247
248  def test_char
249    assert_equal("a", sprintf("%c", 97))
250    assert_equal("a", sprintf("%c", ?a))
251    assert_raise(ArgumentError) { sprintf("%c", sprintf("%c%c", ?a, ?a)) }
252    assert_equal(" " * (BSIZ - 1) + "a", sprintf(" " * (BSIZ - 1) + "%c", ?a))
253    assert_equal(" " * (BSIZ - 1) + "a", sprintf(" " * (BSIZ - 1) + "%-1c", ?a))
254    assert_equal(" " * BSIZ + "a", sprintf("%#{ BSIZ + 1 }c", ?a))
255    assert_equal("a" + " " * BSIZ, sprintf("%-#{ BSIZ + 1 }c", ?a))
256  end
257
258  def test_string
259    assert_equal("foo", sprintf("%s", "foo"))
260    assert_equal("fo", sprintf("%.2s", "foo"))
261    assert_equal(" " * BSIZ, sprintf("%s", " " * BSIZ))
262    assert_equal(" " * (BSIZ - 1) + "foo", sprintf("%#{ BSIZ - 1 + 3 }s", "foo"))
263    assert_equal(" " * BSIZ + "foo", sprintf("%#{ BSIZ + 3 }s", "foo"))
264    assert_equal("foo" + " " * BSIZ, sprintf("%-#{ BSIZ + 3 }s", "foo"))
265  end
266
267  def test_integer
268    assert_equal("01", sprintf("%#o", 1))
269    assert_equal("0x1", sprintf("%#x", 1))
270    assert_equal("0X1", sprintf("%#X", 1))
271    assert_equal("0b1", sprintf("%#b", 1))
272    assert_equal("0B1", sprintf("%#B", 1))
273    assert_equal("1", sprintf("%d", 1.0))
274    assert_equal("4294967296", sprintf("%d", (2**32).to_f))
275    assert_equal("-2147483648", sprintf("%d", -(2**31).to_f))
276    assert_equal("18446744073709551616", sprintf("%d", (2**64).to_f))
277    assert_equal("-9223372036854775808", sprintf("%d", -(2**63).to_f))
278    assert_equal("1", sprintf("%d", "1"))
279    o = Object.new; def o.to_int; 1; end
280    assert_equal("1", sprintf("%d", o))
281    assert_equal("+1", sprintf("%+d", 1))
282    assert_equal(" 1", sprintf("% d", 1))
283    assert_equal("..f", sprintf("%x", -1))
284    assert_equal("..7", sprintf("%o", -1))
285    one = (2**32).coerce(1).first
286    mone = (2**32).coerce(-1).first
287    assert_equal("+1", sprintf("%+d", one))
288    assert_equal(" 1", sprintf("% d", one))
289    assert_equal("..f", sprintf("%x", mone))
290    assert_equal("..7", sprintf("%o", mone))
291    assert_equal(" " * BSIZ + "1", sprintf("%#{ BSIZ + 1 }d", one))
292    assert_equal(" " * (BSIZ - 1) + "1", sprintf(" " * (BSIZ - 1) + "%d", 1))
293  end
294
295  def test_float2
296    inf = 1.0 / 0.0
297    assert_equal(" " * BSIZ + "Inf", sprintf("%#{ BSIZ + 3 }.1f", inf))
298    assert_equal("+Inf", sprintf("%+-f", inf))
299    assert_equal(" " * BSIZ + "1.0", sprintf("%#{ BSIZ + 3 }.1f", 1.0))
300  end
301
302  class T012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
303  end
304
305  def test_star
306    assert_equal("-1 ", sprintf("%*d", -3, -1))
307  end
308
309  def test_escape
310    assert_equal("%" * BSIZ, sprintf("%%" * BSIZ))
311  end
312
313  def test_rb_sprintf
314    assert_match(/^#<TestSprintf::T012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789:0x[0-9a-f]+>$/,
315                 T012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.new.inspect)
316  end
317
318  def test_negative_hex
319    s1 = sprintf("%0x", -0x40000000)
320    s2 = sprintf("%0x", -0x40000001)
321    b1 = (/\.\./ =~ s1) != nil
322    b2 = (/\.\./ =~ s2) != nil
323    assert(b1 == b2, "[ruby-dev:33224]")
324  end
325
326  def test_named_untyped
327    assert_equal("value", sprintf("%<key>s", :key => "value"))
328    e = assert_raise(ArgumentError) {sprintf("%1$<key2>s", :key => "value")}
329    assert_equal("named<key2> after numbered", e.message)
330    e = assert_raise(ArgumentError) {sprintf("%s%s%<key2>s", "foo", "bar", :key => "value")}
331    assert_equal("named<key2> after unnumbered(2)", e.message)
332    e = assert_raise(ArgumentError) {sprintf("%<key><key2>s", :key => "value")}
333    assert_equal("named<key2> after <key>", e.message)
334    e = assert_raise(KeyError) {sprintf("%<key>s", {})}
335    assert_equal("key<key> not found", e.message)
336  end
337
338  def test_named_untyped_enc
339    key = "\u{3012}"
340    [Encoding::UTF_8, Encoding::EUC_JP].each do |enc|
341      k = key.encode(enc)
342      e = assert_raise(ArgumentError) {sprintf("%1$<#{k}>s", key: "value")}
343      assert_equal(enc, e.message.encoding)
344      assert_equal("named<#{k}> after numbered", e.message)
345      e = assert_raise(ArgumentError) {sprintf("%s%s%<#{k}>s", "foo", "bar", key: "value")}
346      assert_equal(enc, e.message.encoding)
347      assert_equal("named<#{k}> after unnumbered(2)", e.message)
348      e = assert_raise(ArgumentError) {sprintf("%<key><#{k}>s", key: "value")}
349      assert_equal(enc, e.message.encoding)
350      assert_equal("named<#{k}> after <key>", e.message)
351      e = assert_raise(ArgumentError) {sprintf("%<#{k}><key>s", k.to_sym => "value")}
352      assert_equal(enc, e.message.encoding)
353      assert_equal("named<key> after <#{k}>", e.message)
354      e = assert_raise(KeyError) {sprintf("%<#{k}>s", {})}
355      assert_equal(enc, e.message.encoding)
356      assert_equal("key<#{k}> not found", e.message)
357    end
358  end
359
360  def test_named_typed
361    assert_equal("value", sprintf("%{key}", :key => "value"))
362    e = assert_raise(ArgumentError) {sprintf("%1${key2}", :key => "value")}
363    assert_equal("named{key2} after numbered", e.message)
364    e = assert_raise(ArgumentError) {sprintf("%s%s%{key2}", "foo", "bar", :key => "value")}
365    assert_equal("named{key2} after unnumbered(2)", e.message)
366    e = assert_raise(ArgumentError) {sprintf("%<key>{key2}", :key => "value")}
367    assert_equal("named{key2} after <key>", e.message)
368    assert_equal("value{key2}", sprintf("%{key}{key2}", :key => "value"))
369    e = assert_raise(KeyError) {sprintf("%{key}", {})}
370    assert_equal("key{key} not found", e.message)
371  end
372
373  def test_named_typed_enc
374    key = "\u{3012}"
375    [Encoding::UTF_8, Encoding::EUC_JP].each do |enc|
376      k = key.encode(enc)
377      e = assert_raise(ArgumentError) {sprintf("%1${#{k}}s", key: "value")}
378      assert_equal(enc, e.message.encoding)
379      assert_equal("named{#{k}} after numbered", e.message)
380      e = assert_raise(ArgumentError) {sprintf("%s%s%{#{k}}s", "foo", "bar", key: "value")}
381      assert_equal(enc, e.message.encoding)
382      assert_equal("named{#{k}} after unnumbered(2)", e.message)
383      e = assert_raise(ArgumentError) {sprintf("%<key>{#{k}}s", key: "value")}
384      assert_equal(enc, e.message.encoding)
385      assert_equal("named{#{k}} after <key>", e.message)
386      e = assert_raise(ArgumentError) {sprintf("%<#{k}>{key}s", k.to_sym => "value")}
387      assert_equal(enc, e.message.encoding)
388      assert_equal("named{key} after <#{k}>", e.message)
389      e = assert_raise(KeyError) {sprintf("%{#{k}}", {})}
390      assert_equal(enc, e.message.encoding)
391      assert_equal("key{#{k}} not found", e.message)
392    end
393  end
394end
395