2 # Basic tests for "numfmt".
4 # Copyright (C) 2012 Free Software Foundation, Inc.
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 (my $program_name = $0) =~ s|.*/||;
24 # TODO: add localization tests with "grouping"
25 # Turn off localization of executable's output.
26 @ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3;
28 my $locale = $ENV{LOCALE_FR_UTF8};
29 ! defined $locale || $locale eq 'none'
34 ['1', '1234', {OUT => "1234"}],
35 ['2', '--from=si 1K', {OUT => "1000"}],
36 ['3', '--from=iec 1K', {OUT => "1024"}],
37 ['4', '--from=auto 1K', {OUT => "1000"}],
38 ['5', '--from=auto 1Ki', {OUT => "1024"}],
39 ['5.1', '--from=iec-i 1Ki', {OUT => "1024"}],
41 ['6', {IN_PIPE => "1234\n"}, {OUT => "1234"}],
42 ['7', '--from=si', {IN_PIPE => "2K\n"}, {OUT => "2000"}],
43 ['7a', '--invalid=fail', {IN_PIPE => "no_NL"}, {OUT => "no_NL"},
44 {ERR => "$prog: invalid number: 'no_NL'\n"},
47 ['8', '--to=si 2000', {OUT => "2.0K"}],
48 ['9', '--to=si 2001', {OUT => "2.1K"}],
49 ['10', '--to=si 1999', {OUT => "2.0K"}],
50 ['11', '--to=si --round=down 2001', {OUT => "2.0K"}],
51 ['12', '--to=si --round=down 1999', {OUT => "1.9K"}],
52 ['13', '--to=si --round=up 1901', {OUT => "2.0K"}],
53 ['14', '--to=si --round=down 1901', {OUT => "1.9K"}],
54 ['15', '--to=si --round=nearest 1901', {OUT => "1.9K"}],
55 ['16', '--to=si --round=nearest 1945', {OUT => "1.9K"}],
56 ['17', '--to=si --round=nearest 1955', {OUT => "2.0K"}],
58 ['18', '--to=iec 2048', {OUT => "2.0K"}],
59 ['19', '--to=iec 2049', {OUT => "2.1K"}],
60 ['20', '--to=iec 2047', {OUT => "2.0K"}],
61 ['21', '--to=iec --round=down 2049', {OUT => "2.0K"}],
62 ['22', '--to=iec --round=down 2047', {OUT => "1.9K"}],
63 ['23', '--to=iec --round=up 2040', {OUT => "2.0K"}],
64 ['24', '--to=iec --round=down 2040', {OUT => "1.9K"}],
65 ['25', '--to=iec --round=nearest 1996', {OUT => "1.9K"}],
66 ['26', '--to=iec --round=nearest 1997', {OUT => "2.0K"}],
67 ['27', '--to=iec-i 2048', {OUT => "2.0Ki"}],
69 ['neg-1', '-- -1234', {OUT => "-1234"}],
70 ['neg-2', '--padding=5 -- -1234', {OUT => "-1234"}],
71 ['neg-3', '--padding=6 -- -1234', {OUT => " -1234"}],
72 ['neg-4', '--to=iec -- 9100 -9100', {OUT => "8.9K\n-8.9K"}],
73 ['neg-5', '-- -0.1', {OUT => "-0.1"}],
74 ['neg-6', '-- -0', {OUT => "0"}],
76 {ERR => "$prog: invalid number: '-0.-1'\n"},
79 ['float-1', '1.1', {OUT => "1.1"}],
80 ['float-2', '1.22', {OUT => "1.22"}],
82 {ERR => "$prog: invalid suffix in input: '1.22.'\n"},
85 ['unit-1', '--from-unit=512 4', {OUT => "2048"}],
86 ['unit-2', '--to-unit=512 2048', {OUT => "4"}],
87 ['unit-3', '--from-unit=512 --from=si 4M', {OUT => "2048000000"}],
88 ['unit-4', '--from-unit=512 --from=iec --to=iec 4M', {OUT => "2.0G"}],
89 ['unit-5', '--from-unit=AA --from=iec --to=iec 4M',
90 {ERR => "$prog: invalid unit size: 'AA'\n"},
92 ['unit-6', '--from-unit=54W --from=iec --to=iec 4M',
93 {ERR => "$prog: invalid unit size: '54W'\n"},
95 # Not fully documented.. "--{from,to}-unit" can accept IEC suffixes
96 ['unit-7', '--from-unit=2K --to=iec 30', {OUT=>"60K"}],
97 ['unit-8', '--from-unit=1234567890123456789012345 --to=iec 30',
98 {ERR => "$prog: invalid unit size: '1234567890123456789012345'\n"},
100 ['unit-9', '--from-unit=0 1',
101 {ERR => "$prog: invalid unit size: '0'\n"},
103 ['unit-10', '--to-unit=0 1',
104 {ERR => "$prog: invalid unit size: '0'\n"},
108 ['suf-1', '4000', {OUT=>'4000'}],
110 {ERR => "$prog: invalid suffix in input: '4Q'\n"},
113 {ERR => "$prog: rejecting suffix " .
114 "in input: '4M' (consider using --from)\n"},
116 ['suf-3', '--from=si 4M', {OUT=>'4000000'}],
117 ['suf-4', '--from=si 4Q',
118 {ERR => "$prog: invalid suffix in input: '4Q'\n"},
120 ['suf-5', '--from=si 4MQ',
121 {ERR => "$prog: invalid suffix in input '4MQ': 'Q'\n"},
124 ['suf-6', '--from=iec 4M', {OUT=>'4194304'}],
125 ['suf-7', '--from=auto 4M', {OUT=>'4000000'}],
126 ['suf-8', '--from=auto 4Mi', {OUT=>'4194304'}],
127 ['suf-9', '--from=auto 4MiQ',
128 {ERR => "$prog: invalid suffix in input '4MiQ': 'Q'\n"},
130 ['suf-10', '--from=auto 4QiQ',
131 {ERR => "$prog: invalid suffix in input: '4QiQ'\n"},
134 # characters after a white space are OK - printed as-is
135 ['suf-11', '"4 M"', {OUT=>'4 M'}],
138 ['suf-12', '--suffix=Foo 70Foo', {OUT=>'70Foo'}],
139 ['suf-13', '--suffix=Foo 70', {OUT=>'70Foo'}],
140 ['suf-14', '--suffix=Foo --from=si 70K', {OUT=>'70000Foo'}],
141 ['suf-15', '--suffix=Foo --from=si 70KFoo', {OUT=>'70000Foo'}],
142 ['suf-16', '--suffix=Foo --to=si 7000Foo', {OUT=>'7.0KFoo'}],
143 ['suf-17', '--suffix=Foo --to=si 7000Bar',
144 {ERR => "$prog: invalid suffix in input: '7000Bar'\n"},
146 ['suf-18', '--suffix=Foo --to=si 7000FooF',
147 {ERR => "$prog: invalid suffix in input: '7000FooF'\n"},
149 # space(s) between number and suffix. Note only field 1 is used
150 # by default so specify the NUL delimiter to consider the whole "line".
151 ['suf-19', "-d '' --from=si '4.0 K'", {OUT => "4000"}],
155 # "C" locale - no grouping (locale-specific tests, below)
156 ['grp-1', '--from=si --grouping 7M', {OUT=>'7000000'}],
157 ['grp-2', '--from=si --to=si --grouping 7M',
158 {ERR => "$prog: grouping cannot be combined with --to\n"},
163 ['pad-1', '--padding=10 5', {OUT=>' 5'}],
164 ['pad-2', '--padding=-10 5', {OUT=>'5 '}],
165 ['pad-3', '--padding=A 5',
166 {ERR => "$prog: invalid padding value 'A'\n"},
168 ['pad-3.1', '--padding=0 5',
169 {ERR => "$prog: invalid padding value '0'\n"},
171 ['pad-4', '--padding=10 --to=si 50000', {OUT=>' 50K'}],
172 ['pad-5', '--padding=-10 --to=si 50000', {OUT=>'50K '}],
175 ['pad-6', '--padding=2 --to=si 1000', {OUT=>'1.0K'}],
179 ['pad-7', '--padding=10 --suffix=foo --to=si 50000',
181 ['pad-8', '--padding=-10 --suffix=foo --to=si 50000',
186 ['delim-1', '--delimiter=: --from=auto 40M:', {OUT=>'40000000:'}],
187 ['delim-2', '--delimiter="" --from=auto "40 M"',{OUT=>'40000000'}],
188 ['delim-3', '--delimiter=" " --from=auto "40M Foo"',{OUT=>'40000000 Foo'}],
189 ['delim-4', '--delimiter=: --from=auto 40M:60M', {OUT=>'40000000:60M'}],
190 ['delim-5', '-d: --field=2 --from=auto :40M:60M', {OUT=>':40000000:60M'}],
191 ['delim-6', '--delimiter=: --field 3 --from=auto 40M:60M',
193 {ERR=>"$prog: input line is too short, no numbers found " .
194 "to convert in field 3\n"}],
197 ['field-1', '--field A',
198 {ERR => "$prog: invalid field value 'A'\n"},
200 ['field-1.1', '--field -5',
201 {ERR => "$prog: invalid field value '-5'\n"},
203 ['field-2', '--field 2 --from=auto "Hello 40M World 90G"',
204 {OUT=>'Hello 40000000 World 90G'}],
205 ['field-3', '--field 3 --from=auto "Hello 40M World 90G"',
206 {ERR=>"$prog: invalid number: 'World'\n"},
208 # Last field - no text after number
209 ['field-4', '--field 4 --from=auto "Hello 40M World 90G"',
210 {OUT=>"Hello 40M World 90000000000"}],
211 # Last field - a delimiter after the number
212 ['field-5', '--field 4 --from=auto "Hello 40M World 90G "',
213 {OUT=>"Hello 40M World 90000000000 "}],
215 # Mix Fields + Delimiters
216 ['field-6', '--delimiter=: --field 2 --from=auto "Hello:40M:World:90G"',
217 {OUT=>"Hello:40000000:World:90G"}],
220 ['field-8', '--field 3 --to=si "Hello World"',
222 {ERR=>"$prog: input line is too short, no numbers found " .
223 "to convert in field 3\n"}],
225 # Auto-consume white-space, setup auto-padding
226 ['whitespace-1', '--to=si --field 2 "A 500 B"', {OUT=>"A 500 B"}],
227 ['whitespace-2', '--to=si --field 2 "A 5000 B"', {OUT=>"A 5.0K B"}],
228 ['whitespace-3', '--to=si " 500"', {OUT=>" 500"}],
229 ['whitespace-4', '--to=si " 6500"', {OUT=>" 6.5K"}],
230 # NOTE: auto-padding is not enabled if the value is on the first
231 # field and there's no white-space before it.
232 ['whitespace-5', '--to=si "6000000"', {OUT=>"6.0M"}],
233 # but if there is whitespace, assume auto-padding is desired.
234 ['whitespace-6', '--to=si " 6000000"', {OUT=>" 6.0M"}],
236 # auto-padding - lines have same padding-width
237 # (padding_buffer will be alloc'd just once)
238 ['whitespace-7', '--to=si --field 2',
239 {IN_PIPE=>"rootfs 100000\n" .
241 {OUT =>"rootfs 100K\n" .
243 # auto-padding - second line requires a
244 # larger padding (padding-buffer needs to be realloc'd)
245 ['whitespace-8', '--to=si --field 2',
246 {IN_PIPE=>"rootfs 100000\n" .
248 {OUT =>"rootfs 100K\n" .
253 # weird mix of identical suffix,delimiters
255 # 1. delimiters (and fields) are parsed (in process_line()
256 # 2. optional custom suffix is removed (in process_suffixed_number())
257 # 3. Remaining suffixes must be valid SI/IEC (in human_xstrtol())
259 # custom suffix comes BEFORE SI/IEC suffix,
260 # so these are 40 of "M", not 40,000,000.
261 ['mix-1', '--suffix=M --from=si 40M', {OUT=>"40M"}],
263 # These are fourty-million Ms .
264 ['mix-2', '--suffix=M --from=si 40MM', {OUT=>"40000000M"}],
266 ['mix-3', '--suffix=M --from=auto 40MM', {OUT=>"40000000M"}],
267 ['mix-4', '--suffix=M --from=auto 40MiM', {OUT=>"41943040M"}],
268 ['mix-5', '--suffix=M --to=si --from=si 4MM', {OUT=>"4.0MM"}],
270 # This might be confusing to the user, but it's legit:
271 # The M in the output is the custom suffix, not Mega.
272 ['mix-6', '--suffix=M 40', {OUT=>"40M"}],
273 ['mix-7', '--suffix=M 4000000', {OUT=>"4000000M"}],
274 ['mix-8', '--suffix=M --to=si 4000000', {OUT=>"4.0MM"}],
276 # The output 'M' is the custom suffix.
277 ['mix-10', '--delimiter=M --suffix=M 40', {OUT=>"40M"}],
279 # The INPUT 'M' is a delimiter (delimiters are top priority)
280 # The output contains one M for custom suffix, and one 'M' delimiter.
281 ['mix-11', '--delimiter=M --suffix=M 40M', {OUT=>"40MM"}],
283 # Same as above, the "M" is NOT treated as a mega SI prefix,
284 ['mix-12', '--delimiter=M --from=si --suffix=M 40M', {OUT=>"40MM"}],
286 # The 'M' is treated as a delimiter, and so the input value is '4000'
287 ['mix-13', '--delimiter=M --to=si --from=auto 4000M5000M9000',
288 {OUT=>"4.0KM5000M9000"}],
289 # 'M' is the delimiter, so the second input field is '5000'
290 ['mix-14', '--delimiter=M --field 2 --from=auto --to=si 4000M5000M9000',
291 {OUT=>"4000M5.0KM9000"}],
297 # header - silently ignored with command line parameters
298 ['header-1', '--header --to=iec 4096', {OUT=>"4.0K"}],
300 # header warning with --debug
301 ['header-2', '--debug --header --to=iec 4096', {OUT=>"4.0K"},
302 {ERR=>"$prog: --header ignored with command-line input\n"}],
304 ['header-3', '--header=A',
305 {ERR=>"$prog: invalid header value 'A'\n"},
307 ['header-4', '--header=0',
308 {ERR=>"$prog: invalid header value '0'\n"},
310 ['header-5', '--header=-6',
311 {ERR=>"$prog: invalid header value '-6'\n"},
313 ['header-6', '--debug --header --to=iec',
314 {IN_PIPE=>"size\n5000\n90000\n"},
315 {OUT=>"size\n4.9K\n88K"}],
316 ['header-7', '--debug --header=3 --to=iec',
317 {IN_PIPE=>"hello\nworld\nsize\n5000\n90000\n"},
318 {OUT=>"hello\nworld\nsize\n4.9K\n88K"}],
319 # header, but no actual content
320 ['header-8', '--header=2 --to=iec',
321 {IN_PIPE=>"hello\nworld\n"},
322 {OUT=>"hello\nworld"}],
323 # not enough header lines
324 ['header-9', '--header=3 --to=iec',
325 {IN_PIPE=>"hello\nworld\n"},
326 {OUT=>"hello\nworld"}],
329 ## human_strtod testing
332 ['strtod-1', '--from=si "foo"',
333 {ERR=>"$prog: invalid number: 'foo'\n"},
335 ['strtod-2', '--from=si ""',
336 {ERR=>"$prog: invalid number: ''\n"},
340 ['strtod-3', '--from=si "1234567890123456789012345678901234567890'.
341 '1234567890123456789012345678901234567890"',
342 {ERR=>"$prog: value too large to be converted: '" .
343 "1234567890123456789012345678901234567890" .
344 "1234567890123456789012345678901234567890'\n",
348 # FRACTION_NO_DIGITS_FOUND
349 ['strtod-5', '--from=si 12.',
350 {ERR=>"$prog: invalid number: '12.'\n"},
352 ['strtod-6', '--from=si 12.K',
353 {ERR=>"$prog: invalid number: '12.K'\n"},
356 # whitespace is not allowed after decimal-point
357 ['strtod-6.1', '--from=si --delimiter=, "12. 2"',
358 {ERR=>"$prog: invalid number: '12. 2'\n"},
362 ['strtod-7', '--from=si "12.1234567890123456789012345678901234567890'.
363 '1234567890123456789012345678901234567890"',
364 {ERR=>"$prog: value too large to be converted: '" .
365 "12.1234567890123456789012345678901234567890" .
366 "1234567890123456789012345678901234567890'\n",
371 ['strtod-9', '--from=si 12.2Q',
372 {ERR=>"$prog: invalid suffix in input: '12.2Q'\n"},
375 # VALID_BUT_FORBIDDEN_SUFFIX
377 {ERR => "$prog: rejecting suffix " .
378 "in input: '12M' (consider using --from)\n"},
382 ['strtod-11', '--from=iec-i 12M',
383 {ERR => "$prog: missing 'i' suffix in input: " .
384 "'12M' (e.g Ki/Mi/Gi)\n"},
388 # Test double_to_human()
392 ['dbl-to-human-1','--to=si 800', {OUT=>"800"}],
393 ['dbl-to-human-2','--to=si 0', {OUT=>"0"}],
394 ['dbl-to-human-2.1','--to=si 999', {OUT=>"999"}],
395 ['dbl-to-human-2.2','--to=si 1000', {OUT=>"1.0K"}],
396 #NOTE: the following are consistent with "ls -lh" output
397 ['dbl-to-human-2.3','--to=iec 999', {OUT=>"999"}],
398 ['dbl-to-human-2.4','--to=iec 1023', {OUT=>"1023"}],
399 ['dbl-to-human-2.5','--to=iec 1024', {OUT=>"1.0K"}],
400 ['dbl-to-human-2.6','--to=iec 1025', {OUT=>"1.1K"}],
401 ['dbl-to-human-2.7','--to=iec 0', {OUT=>"0"}],
402 # no "i" suffix if output has no suffix
403 ['dbl-to-human-2.8','--to=iec-i 0', {OUT=>"0"}],
405 # values resulting in "N.Nx" output
406 ['dbl-to-human-3','--to=si 8000', {OUT=>"8.0K"}],
407 ['dbl-to-human-3.1','--to=si 8001', {OUT=>"8.1K"}],
408 ['dbl-to-human-4','--to=si --round=down 8001', {OUT=>"8.0K"}],
410 ['dbl-to-human-5','--to=si --round=down 3500', {OUT=>"3.5K"}],
411 ['dbl-to-human-6','--to=si --round=nearest 3500', {OUT=>"3.5K"}],
412 ['dbl-to-human-7','--to=si --round=up 3500', {OUT=>"3.5K"}],
414 ['dbl-to-human-8','--to=si --round=down 3501', {OUT=>"3.5K"}],
415 ['dbl-to-human-9','--to=si --round=nearest 3501', {OUT=>"3.5K"}],
416 ['dbl-to-human-10','--to=si --round=up 3501', {OUT=>"3.6K"}],
418 ['dbl-to-human-11','--to=si --round=nearest 3550', {OUT=>"3.6K"}],
419 ['dbl-to-human-12','--to=si --from=si 999.89K', {OUT=>"1.0M"}],
420 ['dbl-to-human-13','--to=si --from=si 9.9K', {OUT=>"9.9K"}],
421 ['dbl-to-human-14','--to=si 9900', {OUT=>"9.9K"}],
422 ['dbl-to-human-15','--to=iec --from=si 3.3K', {OUT=>"3.3K"}],
423 ['dbl-to-human-16','--to=iec --round=down --from=si 3.3K', {OUT=>"3.2K"}],
425 # values resulting in 'NNx' output
426 ['dbl-to-human-17','--to=si 9999', {OUT=>"10K"}],
427 ['dbl-to-human-18','--to=si --round=down 35000', {OUT=>"35K"}],
428 ['dbl-to-human-19','--to=iec 35000', {OUT=>"35K"}],
429 ['dbl-to-human-20','--to=iec --round=down 35000', {OUT=>"34K"}],
430 ['dbl-to-human-21','--to=iec 35000000', {OUT=>"34M"}],
431 ['dbl-to-human-22','--to=iec --round=down 35000000', {OUT=>"33M"}],
432 ['dbl-to-human-23','--to=si 35000001', {OUT=>"36M"}],
433 ['dbl-to-human-24','--to=si --from=si 9.99M', {OUT=>"10M"}],
434 ['dbl-to-human-25','--to=si --from=iec 9.99M', {OUT=>"11M"}],
435 ['dbl-to-human-25.1','--to=iec 99999', {OUT=>"98K"}],
437 # values resulting in 'NNNx' output
438 ['dbl-to-human-26','--to=si 999000000000', {OUT=>"999G"}],
439 ['dbl-to-human-27','--to=iec 999000000000', {OUT=>"931G"}],
440 ['dbl-to-human-28','--to=si 123600000000000', {OUT=>"124T"}],
441 ['dbl-to-human-29','--to=si 998123', {OUT=>"999K"}],
442 ['dbl-to-human-30','--to=si --round=nearest 998123', {OUT=>"998K"}],
443 ['dbl-to-human-31','--to=si 99999', {OUT=>"100K"}],
444 ['dbl-to-human-32','--to=iec 102399', {OUT=>"100K"}],
445 ['dbl-to-human-33','--to=iec-i 102399', {OUT=>"100Ki"}],
448 # Default --round=from-zero
449 ['round-1','--to-unit=1024 -- 6000 -6000',
451 ['round-2','--to-unit=1024 -- 6000.0 -6000.0',
453 ['round-3','--to-unit=1024 -- 6000.00 -6000.00',
454 {OUT=>"5.86\n-5.86"}],
455 ['round-4','--to-unit=1024 -- 6000.000 -6000.000',
456 {OUT=>"5.860\n-5.860"}],
457 ['round-5','--to-unit=1024 -- 6000.0000 -6000.0000',
458 {OUT=>"5.8594\n-5.8594"}],
460 ['round-1-up','--round=up --to-unit=1024 -- 6000 -6000',
462 ['round-2-up','--round=up --to-unit=1024 -- 6000.0 -6000.0',
464 ['round-3-up','--round=up --to-unit=1024 -- 6000.00 -6000.00',
465 {OUT=>"5.86\n-5.85"}],
466 ['round-4-up','--round=up --to-unit=1024 -- 6000.000 -6000.000',
467 {OUT=>"5.860\n-5.859"}],
468 ['round-5-up','--round=up --to-unit=1024 -- 6000.0000 -6000.0000',
469 {OUT=>"5.8594\n-5.8593"}],
471 ['round-1-down','--round=down --to-unit=1024 -- 6000 -6000',
473 ['round-2-down','--round=down --to-unit=1024 -- 6000.0 -6000.0',
475 ['round-3-down','--round=down --to-unit=1024 -- 6000.00 -6000.00',
476 {OUT=>"5.85\n-5.86"}],
477 ['round-4-down','--round=down --to-unit=1024 -- 6000.000 -6000.000',
478 {OUT=>"5.859\n-5.860"}],
479 ['round-5-down','--round=down --to-unit=1024 -- 6000.0000 -6000.0000',
480 {OUT=>"5.8593\n-5.8594"}],
481 # --round=towards-zero
482 ['round-1-to-zero','--ro=towards-zero --to-u=1024 -- 6000 -6000',
484 ['round-2-to-zero','--ro=towards-zero --to-u=1024 -- 6000.0 -6000.0',
486 ['round-3-to-zero','--ro=towards-zero --to-u=1024 -- 6000.00 -6000.00',
487 {OUT=>"5.85\n-5.85"}],
488 ['round-4-to-zero','--ro=towards-zero --to-u=1024 -- 6000.000 -6000.000',
489 {OUT=>"5.859\n-5.859"}],
490 ['round-5-to-zero','--ro=towards-zero --to-u=1024 -- 6000.0000 -6000.0000',
491 {OUT=>"5.8593\n-5.8593"}],
493 ['round-1-near','--ro=nearest --to-u=1024 -- 6000 -6000',
495 ['round-2-near','--ro=nearest --to-u=1024 -- 6000.0 -6000.0',
497 ['round-3-near','--ro=nearest --to-u=1024 -- 6000.00 -6000.00',
498 {OUT=>"5.86\n-5.86"}],
499 ['round-4-near','--ro=nearest --to-u=1024 -- 6000.000 -6000.000',
500 {OUT=>"5.859\n-5.859"}],
501 ['round-5-near','--ro=nearest --to-u=1024 -- 6000.0000 -6000.0000',
502 {OUT=>"5.8594\n-5.8594"}],
506 ['large-1','1000000000000000', {OUT=>"1000000000000000"}],
508 ['large-2','1000000000000000000', {OUT=>"1000000000000000000"}],
509 # 19 digits is too much (without output scaling)
510 ['large-3','10000000000000000000',
511 {ERR => "$prog: value too large to be printed: '1e+19' " .
512 "(consider using --to)\n"},
516 # Up to 27 digits is OK.
517 ['large-3.1', '--to=si 1', {OUT=> "1"}],
518 ['large-3.2', '--to=si 10', {OUT=> "10"}],
519 ['large-3.3', '--to=si 100', {OUT=> "100"}],
520 ['large-3.4', '--to=si 1000', {OUT=>"1.0K"}],
521 ['large-3.5', '--to=si 10000', {OUT=> "10K"}],
522 ['large-3.6', '--to=si 100000', {OUT=>"100K"}],
523 ['large-3.7', '--to=si 1000000', {OUT=>"1.0M"}],
524 ['large-3.8', '--to=si 10000000', {OUT=> "10M"}],
525 ['large-3.9', '--to=si 100000000', {OUT=>"100M"}],
526 ['large-3.10','--to=si 1000000000', {OUT=>"1.0G"}],
527 ['large-3.11','--to=si 10000000000', {OUT=> "10G"}],
528 ['large-3.12','--to=si 100000000000', {OUT=>"100G"}],
529 ['large-3.13','--to=si 1000000000000', {OUT=>"1.0T"}],
530 ['large-3.14','--to=si 10000000000000', {OUT=> "10T"}],
531 ['large-3.15','--to=si 100000000000000', {OUT=>"100T"}],
532 ['large-3.16','--to=si 1000000000000000', {OUT=>"1.0P"}],
533 ['large-3.17','--to=si 10000000000000000', {OUT=> "10P"}],
534 ['large-3.18','--to=si 100000000000000000', {OUT=>"100P"}],
535 ['large-3.19','--to=si 1000000000000000000', {OUT=>"1.0E"}],
536 ['large-3.20','--to=si 10000000000000000000', {OUT=> "10E"}],
537 ['large-3.21','--to=si 210000000000000000000', {OUT=>"210E"}],
538 ['large-3.22','--to=si 3210000000000000000000', {OUT=>"3.3Z"}],
539 ['large-3.23','--to=si 43210000000000000000000', {OUT=> "44Z"}],
540 ['large-3.24','--to=si 543210000000000000000000', {OUT=>"544Z"}],
541 ['large-3.25','--to=si 6543210000000000000000000', {OUT=>"6.6Y"}],
542 ['large-3.26','--to=si 76543210000000000000000000', {OUT=> "77Y"}],
543 ['large-3.27','--to=si 876543210000000000000000000', {OUT=>"877Y"}],
545 # More than 27 digits is not OK
546 ['large-3.28','--to=si 9876543210000000000000000000',
547 {ERR => "$prog: value too large to be converted: " .
548 "'9876543210000000000000000000'\n"},
552 ['large-4.1', '--from=si 9.7M', {OUT=>"9700000"}],
553 ['large-4.2', '--from=si 10M', {OUT =>"10000000"}],
554 ['large-4.3', '--from=si 200M', {OUT =>"200000000"}],
555 ['large-4.4', '--from=si 3G', {OUT =>"3000000000"}],
556 ['large-4.5', '--from=si 40G', {OUT =>"40000000000"}],
557 ['large-4.6', '--from=si 500G', {OUT =>"500000000000"}],
558 ['large-4.7', '--from=si 6T', {OUT =>"6000000000000"}],
559 ['large-4.8', '--from=si 70T', {OUT =>"70000000000000"}],
560 ['large-4.9', '--from=si 800T', {OUT =>"800000000000000"}],
561 ['large-4.10','--from=si 9P', {OUT =>"9000000000000000"}],
562 ['large-4.11','--from=si 10P', {OUT =>"10000000000000000"}],
563 ['large-4.12','--from=si 200P', {OUT =>"200000000000000000"}],
564 ['large-4.13','--from=si 3E', {OUT =>"3000000000000000000"}],
566 # More than 18 digits of output without scaling - no good.
567 ['large-4.14','--from=si 40E',
568 {ERR => "$prog: value too large to be printed: '4e+19' " .
569 "(consider using --to)\n"},
571 ['large-4.15','--from=si 500E',
572 {ERR => "$prog: value too large to be printed: '5e+20' " .
573 "(consider using --to)\n"},
575 ['large-4.16','--from=si 6Z',
576 {ERR => "$prog: value too large to be printed: '6e+21' " .
577 "(consider using --to)\n"},
579 ['large-4.17','--from=si 70Z',
580 {ERR => "$prog: value too large to be printed: '7e+22' " .
581 "(consider using --to)\n"},
583 ['large-4.18','--from=si 800Z',
584 {ERR => "$prog: value too large to be printed: '8e+23' " .
585 "(consider using --to)\n"},
587 ['large-4.19','--from=si 9Y',
588 {ERR => "$prog: value too large to be printed: '9e+24' " .
589 "(consider using --to)\n"},
591 ['large-4.20','--from=si 10Y',
592 {ERR => "$prog: value too large to be printed: '1e+25' " .
593 "(consider using --to)\n"},
595 ['large-4.21','--from=si 200Y',
596 {ERR => "$prog: value too large to be printed: '2e+26' " .
597 "(consider using --to)\n"},
600 ['large-5.1','--to=si 1000000000000000000', {OUT=>"1.0E"}],
601 ['large-5','--from=si --to=si 2E', {OUT=>"2.0E"}],
602 ['large-6','--from=si --to=si 3.4Z', {OUT=>"3.4Z"}],
603 ['large-7','--from=si --to=si 80Y', {OUT=>"80Y"}],
604 ['large-8','--from=si --to=si 9000Z', {OUT=>"9.0Y"}],
606 ['large-10','--from=si --to=si 999Y', {OUT=>"999Y"}],
607 ['large-11','--from=si --to=iec 999Y', {OUT=>"827Y"}],
608 ['large-12','--from=si --round=down --to=iec 999Y', {OUT=>"826Y"}],
610 # units can also affect the output
611 ['large-13','--from=si --from-unit=1000000 9P',
612 {ERR => "$prog: value too large to be printed: '9e+21' " .
613 "(consider using --to)\n"},
615 ['large-13.1','--from=si --from-unit=1000000 --to=si 9P', {OUT=>"9.0Z"}],
617 # Numbers>999Y are never acceptable, regardless of scaling
618 ['large-14','--from=si --to=si 999Y', {OUT=>"999Y"}],
619 ['large-14.1','--from=si --to=si 1000Y',
620 {ERR => "$prog: value too large to be printed: '1e+27' " .
621 "(cannot handle values > 999Y)\n"},
623 ['large-14.2','--from=si --to=si --from-unit=10000 1Y',
624 {ERR => "$prog: value too large to be printed: '1e+28' " .
625 "(cannot handle values > 999Y)\n"},
629 ['debug-1', '--debug 4096', {OUT=>"4096"},
630 {ERR=>"$prog: no conversion option specified\n"}],
631 # '--padding' is a valid conversion option - no warning should be printed
632 ['debug-1.1', '--debug --padding 10 4096', {OUT=>" 4096"}],
633 ['debug-2', '--debug --grouping --from=si 4.0K', {OUT=>"4000"},
634 {ERR=>"$prog: grouping has no effect in this locale\n"}],
635 ['debug-4', '--to=si --debug 12345678901234567890',
637 {ERR=>"$prog: large input value '12345678901234567890':" .
638 " possible precision loss\n"}],
639 ['debug-5', '--to=si --from=si --debug 1.12345678901234567890Y',
641 {ERR=>"$prog: large input value '1.12345678901234567890Y':" .
642 " possible precision loss\n"}],
644 # dev-debug messages - the actual messages don't matter
645 # just ensure the program works, and for code coverage testing.
646 ['devdebug-1', '---devdebug --from=si 4.9K', {OUT=>"4900"},
648 {ERR_SUBST=>"s/.*//msg"}],
649 ['devdebug-2', '---devdebug 4900', {OUT=>"4900"},
651 {ERR_SUBST=>"s/.*//msg"}],
652 ['devdebug-3', '---devdebug --from=auto 4Mi', {OUT=>"4194304"},
654 {ERR_SUBST=>"s/.*//msg"}],
655 ['devdebug-4', '---devdebug --to=si 4000000', {OUT=>"4.0M"},
657 {ERR_SUBST=>"s/.*//msg"}],
658 ['devdebug-5', '---devdebug --to=si --padding=5 4000000', {OUT=>" 4.0M"},
660 {ERR_SUBST=>"s/.*//msg"}],
661 ['devdebug-6', '---devdebug --suffix=Foo 1234Foo', {OUT=>"1234Foo"},
663 {ERR_SUBST=>"s/.*//msg"}],
664 ['devdebug-7', '---devdebug --suffix=Foo 1234', {OUT=>"1234Foo"},
666 {ERR_SUBST=>"s/.*//msg"}],
667 ['devdebug-9', '---devdebug --grouping 10000', {OUT=>"10000"},
669 {ERR_SUBST=>"s/.*//msg"}],
670 ['devdebug-10', '---devdebug --format %f 10000', {OUT=>"10000"},
672 {ERR_SUBST=>"s/.*//msg"}],
673 ['devdebug-11', '---devdebug --format "%\'-10f" 10000',{OUT=>"10000 "},
675 {ERR_SUBST=>"s/.*//msg"}],
676 ['devdebug-12', '---devdebug --field 2 A',{OUT=>""},
677 {ERR=>""}, {EXIT=>2},
678 {ERR_SUBST=>"s/.*//msg"}],
681 ['help-1', '--foobar',
682 {ERR=>"$prog: unrecognized option '--foobar'\n" .
683 "Try '$prog --help' for more information.\n"},
686 ## Format string - check error detection
687 ['fmt-err-1', '--format ""',
688 {ERR=>"$prog: format '' has no % directive\n"},
690 ['fmt-err-2', '--format "hello"',
691 {ERR=>"$prog: format 'hello' has no % directive\n"},
693 ['fmt-err-3', '--format "hello%"',
694 {ERR=>"$prog: format 'hello%' ends in %\n"},
696 ['fmt-err-4', '--format "%d"',
697 {ERR=>"$prog: invalid format '%d', " .
698 "directive must be %['][-][N]f\n"},
700 ['fmt-err-5', '--format "% -43 f"',
701 {ERR=>"$prog: invalid format '% -43 f', " .
702 "directive must be %['][-][N]f\n"},
704 ['fmt-err-6', '--format "%f %f"',
705 {ERR=>"$prog: format '%f %f' has too many % directives\n"},
707 ['fmt-err-7', '--format "%123456789012345678901234567890f"',
708 {ERR=>"$prog: invalid format '%123456789012345678901234567890f'".
709 " (width overflow)\n"},
711 ['fmt-err-8', '--format "%f" --padding 20',
712 {ERR=>"$prog: --padding cannot be combined with --format\n"},
714 ['fmt-err-9', '--format "%f" --grouping',
715 {ERR=>"$prog: --grouping cannot be combined with --format\n"},
717 ['fmt-err-10', '--format "%\'f" --to=si',
718 {ERR=>"$prog: grouping cannot be combined with --to\n"},
720 ['fmt-err-11', '--debug --format "%\'f" 5000', {OUT=>"5000"},
721 {ERR=>"$prog: grouping has no effect in this locale\n"}],
723 ## Format string - check some corner cases
724 ['fmt-1', '--format "%% %f" 5000', {OUT=>"%%5000"}],
725 ['fmt-2', '--format "%f %%" 5000', {OUT=>"5000 %%"}],
727 ['fmt-3', '--format "--%f--" 5000000', {OUT=>"--5000000--"}],
728 ['fmt-4', '--format "--%f--" --to=si 5000000', {OUT=>"--5.0M--"}],
730 ['fmt-5', '--format "--%10f--" --to=si 5000000',{OUT=>"-- 5.0M--"}],
731 ['fmt-6', '--format "--%-10f--" --to=si 5000000',{OUT=>"--5.0M --"}],
732 ['fmt-7', '--format "--%10f--" 5000000',{OUT=>"-- 5000000--"}],
733 ['fmt-8', '--format "--%-10f--" 5000000',{OUT=>"--5000000 --"}],
736 ['fmt-9', '--format "--%5f--" 5000000',{OUT=>"--5000000--"}],
739 ['fmt-10', '--format "--%10f--" --suffix Foo 50', {OUT=>"-- 50Foo--"}],
740 ['fmt-11', '--format "--%-10f--" --suffix Foo 50',{OUT=>"--50Foo --"}],
742 # Grouping in C locale - no grouping effect
743 ['fmt-12', '--format "%\'f" 50000',{OUT=>"50000"}],
744 ['fmt-13', '--format "%\'10f" 50000', {OUT=>" 50000"}],
745 ['fmt-14', '--format "%\'-10f" 50000',{OUT=>"50000 "}],
747 # Very large format strings
748 ['fmt-15', '--format "--%100000f--" --to=si 4200',
749 {OUT=>"--" . " " x 99996 . "4.2K--" }],
752 ## Check all errors again, this time with --invalid=fail
753 ## Input will be printed without conversion,
754 ## and exit code will be 2
755 ['ign-err-1', '--invalid=fail 4Q',
756 {ERR => "$prog: invalid suffix in input: '4Q'\n"},
759 ['ign-err-2', '--invalid=fail 4M',
760 {ERR => "$prog: rejecting suffix " .
761 "in input: '4M' (consider using --from)\n"},
764 ['ign-err-3', '--invalid=fail --from=si 4MQ',
765 {ERR => "$prog: invalid suffix in input '4MQ': 'Q'\n"},
768 ['ign-err-4', '--invalid=fail --suffix=Foo --to=si 7000FooF',
769 {ERR => "$prog: invalid suffix in input: '7000FooF'\n"},
770 {OUT => "7000FooF\n"},
772 ['ign-err-5','--invalid=fail --field 3 --from=auto "Hello 40M World 90G"',
773 {ERR => "$prog: invalid number: 'World'\n"},
774 {OUT => "Hello 40M World 90G\n"},
776 ['ign-err-6', '--invalid=fail --field 3 --to=si "Hello World"',
777 {ERR => "$prog: input line is too short, no numbers found " .
778 "to convert in field 3\n"},
779 {OUT => "Hello World\n"},
781 ['ign-err-7', '--invalid=fail --from=si "foo"',
782 {ERR => "$prog: invalid number: 'foo'\n"},
785 ['ign-err-8', '--invalid=fail 12M',
786 {ERR => "$prog: rejecting suffix " .
787 "in input: '12M' (consider using --from)\n"},
790 ['ign-err-9', '--invalid=fail --from=iec-i 12M',
791 {ERR => "$prog: missing 'i' suffix in input: " .
792 "'12M' (e.g Ki/Mi/Gi)\n"},
795 ['ign-err-10','--invalid=fail 10000000000000000000',
796 {ERR => "$prog: value too large to be printed: '1e+19' " .
797 "(consider using --to)\n"},
798 {OUT => "10000000000000000000\n"},
800 ['ign-err-11','--invalid=fail --to=si 9876543210000000000000000000',
801 {ERR => "$prog: value too large to be converted: " .
802 "'9876543210000000000000000000'\n"},
803 {OUT => "9876543210000000000000000000\n"},
806 ## Ignore Errors with multiple conversions
807 ['ign-err-m1', '--invalid=ignore --to=si 1000 2000 bad 3000',
808 {OUT => "1.0K\n2.0K\nbad\n3.0K"},
810 ['ign-err-m1.1', '--invalid=ignore --to=si',
811 {IN_PIPE => "1000\n2000\nbad\n3000\n"},
812 {OUT => "1.0K\n2.0K\nbad\n3.0K"},
814 ['ign-err-m1.3', '--invalid=fail --debug --to=si 1000 2000 3000',
815 {OUT => "1.0K\n2.0K\n3.0K"},
817 ['ign-err-m2', '--invalid=fail --to=si 1000 Foo 3000',
818 {OUT => "1.0K\nFoo\n3.0K\n"},
819 {ERR => "$prog: invalid number: 'Foo'\n"},
821 ['ign-err-m2.1', '--invalid=warn --to=si',
822 {IN_PIPE => "1000\nFoo\n3000\n"},
823 {OUT => "1.0K\nFoo\n3.0K"},
824 {ERR => "$prog: invalid number: 'Foo'\n"},
827 # --debug will trigger a final warning at EOF
828 ['ign-err-m2.2', '--invalid=fail --debug --to=si 1000 Foo 3000',
829 {OUT => "1.0K\nFoo\n3.0K\n"},
830 {ERR => "$prog: invalid number: 'Foo'\n" .
831 "$prog: failed to convert some of the input numbers\n"},
834 ['ign-err-m3', '--invalid=fail --field 2 --from=si --to=iec',
835 {IN_PIPE => "A 1K x\nB 2M y\nC 3G z\n"},
836 {OUT => "A 1000 x\nB 2.0M y\nC 2.8G z"},
838 # invalid input on one of the fields
839 ['ign-err-m3.1', '--invalid=fail --field 2 --from=si --to=iec',
840 {IN_PIPE => "A 1K x\nB Foo y\nC 3G z\n"},
841 {OUT => "A 1000 x\nB Foo y\nC 2.8G z\n"},
842 {ERR => "$prog: invalid number: 'Foo'\n"},
844 # one of the lines is too short
845 ['ign-err-m3.2', '--invalid=fail --field 2 --from=si --to=iec',
846 {IN_PIPE => "A 1K x\nB\nC 3G z\n"},
847 {OUT => "A 1000 x\nB\nC 2.8G z\n"},
848 {ERR => "$prog: input line is too short, no numbers found " .
849 "to convert in field 2\n"},
855 # Locale that supports grouping, but without '--grouping' parameter
856 ['lcl-grp-1', '--from=si 7M', {OUT=>"7000000"},
857 {ENV=>"LC_ALL=$locale"}],
859 # Locale with grouping
860 ['lcl-grp-2', '--from=si --grouping 7M', {OUT=>"7 000 000"},
861 {ENV=>"LC_ALL=$locale"}],
863 # Locale with grouping and debug - no debug warning message
864 ['lcl-grp-3', '--from=si --debug --grouping 7M', {OUT=>"7 000 000"},
865 {ENV=>"LC_ALL=$locale"}],
867 # Input with locale'd decimal-point
868 ['lcl-stdtod-1', '--from=si 12,2K', {OUT=>"12200"},
869 {ENV=>"LC_ALL=$locale"}],
871 ['lcl-dbl-to-human-1', '--to=si 1100', {OUT=>"1,1K"},
872 {ENV=>"LC_ALL=$locale"}],
875 ['lcl-fmt-1', '--format "%\'f" 50000',{OUT=>"50 000"},
876 {ENV=>"LC_ALL=$locale"}],
877 ['lcl-fmt-2', '--format "--%\'10f--" 50000', {OUT=>"-- 50 000--"},
878 {ENV=>"LC_ALL=$locale"}],
879 ['lcl-fmt-3', '--format "--%\'-10f--" 50000',{OUT=>"--50 000 --"},
880 {ENV=>"LC_ALL=$locale"}],
881 ['lcl-fmt-4', '--format "--%-10f--" --to=si 5000000',
883 {ENV=>"LC_ALL=$locale"}],
886 push @Tests, @Locale_Tests if $locale ne "C";
888 ## Check all valid/invalid suffixes
889 foreach my $suf ( 'A' .. 'Z', 'a' .. 'z' ) {
890 if ( $suf =~ /^[KMGTPEZY]$/ )
892 push @Tests, ["auto-suf-si-$suf","--from=si --to=si 1$suf",
894 push @Tests, ["auto-suf-iec-$suf","--from=iec --to=iec 1$suf",
896 push @Tests, ["auto-suf-auto-$suf","--from=auto --to=iec 1${suf}i",
898 push @Tests, ["auto-suf-iec-to-ieci-$suf","--from=iec --to=iec-i 1${suf}",
899 {OUT=>"1.0${suf}i"}];
900 push @Tests, ["auto-suf-ieci-to-iec-$suf",
901 "--from=iec-i --to=iec 1${suf}i",{OUT=>"1.0${suf}"}];
905 push @Tests, ["auto-suf-si-$suf","--from=si --to=si 1$suf",
906 {ERR=>"$prog: invalid suffix in input: '1${suf}'\n"},
911 # Prepend the command line argument and append a newline to end
912 # of each expected 'OUT' string.
918 # Don't fiddle with expected OUT string if there's a nonzero exit status.
921 ref $e eq 'HASH' && exists $e->{EXIT} && $e->{EXIT}
927 ref $e eq 'HASH' && exists $e->{OUT}
928 and $e->{OUT} .= "\n"
932 my $save_temps = $ENV{SAVE_TEMPS};
933 my $verbose = $ENV{VERBOSE};
935 my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose);