numfmt: support lowercase 'k' for Kilo and Kibi
[coreutils.git] / tests / misc / numfmt.pl
blob3047f806dbef4329eb37f0b8cccb114baf839f48
1 #!/usr/bin/perl
2 # Basic tests for "numfmt".
4 # Copyright (C) 2012-2023 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 <https://www.gnu.org/licenses/>.
19 use strict;
21 (my $program_name = $0) =~ s|.*/||;
22 my $prog = 'numfmt';
24 my $limits = getlimits ();
26 # TODO: add localization tests with "grouping"
27 # Turn off localization of executable's output.
28 @ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3;
30 my $locale = $ENV{LOCALE_FR_UTF8};
31 ! defined $locale || $locale eq 'none'
32 and $locale = 'C';
34 my $try = "Try '$prog --help' for more information.\n";
36 my @Tests =
38 ['1', '1234', {OUT => "1234"}],
39 ['2', '--from=si 1K', {OUT => "1000"}],
40 ['3', '--from=iec 1K', {OUT => "1024"}],
41 ['4', '--from=auto 1K', {OUT => "1000"}],
42 ['5', '--from=auto 1Ki', {OUT => "1024"}],
43 ['5.1', '--from=iec-i 1Ki', {OUT => "1024"}],
45 ['6', {IN_PIPE => "1234\n"}, {OUT => "1234"}],
46 ['7', '--from=si', {IN_PIPE => "2K\n"}, {OUT => "2000"}],
47 ['7a', '--invalid=fail', {IN_PIPE => "no_NL"}, {OUT => "no_NL"},
48 {ERR => "$prog: invalid number: 'no_NL'\n"},
49 {EXIT => '2'}],
51 ['8', '--to=si 2000', {OUT => "2.0k"}],
52 ['9', '--to=si 2001', {OUT => "2.1k"}],
53 ['10', '--to=si 1999', {OUT => "2.0k"}],
54 ['11', '--to=si --round=down 2001', {OUT => "2.0k"}],
55 ['12', '--to=si --round=down 1999', {OUT => "1.9k"}],
56 ['13', '--to=si --round=up 1901', {OUT => "2.0k"}],
57 ['14', '--to=si --round=down 1901', {OUT => "1.9k"}],
58 ['15', '--to=si --round=nearest 1901', {OUT => "1.9k"}],
59 ['16', '--to=si --round=nearest 1945', {OUT => "1.9k"}],
60 ['17', '--to=si --round=nearest 1955', {OUT => "2.0k"}],
62 ['18', '--to=iec 2048', {OUT => "2.0K"}],
63 ['19', '--to=iec 2049', {OUT => "2.1K"}],
64 ['20', '--to=iec 2047', {OUT => "2.0K"}],
65 ['21', '--to=iec --round=down 2049', {OUT => "2.0K"}],
66 ['22', '--to=iec --round=down 2047', {OUT => "1.9K"}],
67 ['23', '--to=iec --round=up 2040', {OUT => "2.0K"}],
68 ['24', '--to=iec --round=down 2040', {OUT => "1.9K"}],
69 ['25', '--to=iec --round=nearest 1996', {OUT => "1.9K"}],
70 ['26', '--to=iec --round=nearest 1997', {OUT => "2.0K"}],
71 ['27', '--to=iec-i 2048', {OUT => "2.0Ki"}],
73 ['neg-1', '-- -1234', {OUT => "-1234"}],
74 ['neg-2', '--padding=5 -- -1234', {OUT => "-1234"}],
75 ['neg-3', '--padding=6 -- -1234', {OUT => " -1234"}],
76 ['neg-4', '--to=iec -- 9100 -9100', {OUT => "8.9K\n-8.9K"}],
77 ['neg-5', '-- -0.1', {OUT => "-0.1"}],
78 ['neg-6', '-- -0', {OUT => "0"}],
79 ['neg-7', '-- -0.-1',
80 {ERR => "$prog: invalid number: '-0.-1'\n"},
81 {EXIT => '2'}],
83 ['float-1', '1.1', {OUT => "1.1"}],
84 ['float-2', '1.22', {OUT => "1.22"}],
85 ['float-3', '1.22.',
86 {ERR => "$prog: invalid suffix in input: '1.22.'\n"},
87 {EXIT => '2'}],
89 ['unit-1', '--from-unit=512 4', {OUT => "2048"}],
90 ['unit-2', '--to-unit=512 2048', {OUT => "4"}],
91 ['unit-3', '--from-unit=512 --from=si 4M', {OUT => "2048000000"}],
92 ['unit-4', '--from-unit=512 --from=iec --to=iec 4M', {OUT => "2.0G"}],
93 ['unit-5', '--from-unit=AA --from=iec --to=iec 4M',
94 {ERR => "$prog: invalid unit size: 'AA'\n"},
95 {EXIT => '1'}],
96 ['unit-6', '--from-unit=54W --from=iec --to=iec 4M',
97 {ERR => "$prog: invalid unit size: '54W'\n"},
98 {EXIT => '1'}],
99 ['unit-7', '--from-unit=K 30', {OUT=>"30000"}],
100 ['unit-7.1', '--from-unit=Ki 30', {OUT=>"30720"}],
101 ['unit-7.2', '--from-unit=i 0',
102 {ERR => "$prog: invalid unit size: 'i'\n"},
103 {EXIT => '1'}],
104 ['unit-7.3', '--from-unit=1i 0',
105 {ERR => "$prog: invalid unit size: '1i'\n"},
106 {EXIT => '1'}],
107 ['unit-8', '--from-unit='.$limits->{UINTMAX_OFLOW}.' --to=iec 30',
108 {ERR => "$prog: invalid unit size: '$limits->{UINTMAX_OFLOW}'\n"},
109 {EXIT => '1'}],
110 ['unit-9', '--from-unit=0 1',
111 {ERR => "$prog: invalid unit size: '0'\n"},
112 {EXIT => '1'}],
113 ['unit-10', '--to-unit=0 1',
114 {ERR => "$prog: invalid unit size: '0'\n"},
115 {EXIT => '1'}],
117 # Test Suffix logic
118 ['suf-1', '4000', {OUT=>'4000'}],
119 ['suf-2', '4J',
120 {ERR => "$prog: invalid suffix in input: '4J'\n"},
121 {EXIT => '2'}],
122 ['suf-2.1', '4M',
123 {ERR => "$prog: rejecting suffix " .
124 "in input: '4M' (consider using --from)\n"},
125 {EXIT => '2'}],
126 ['suf-3', '--from=si 4M', {OUT=>'4000000'}],
127 ['suf-4', '--from=si 4J',
128 {ERR => "$prog: invalid suffix in input: '4J'\n"},
129 {EXIT => '2'}],
130 ['suf-5', '--from=si 4MJ',
131 {ERR => "$prog: invalid suffix in input '4MJ': 'J'\n"},
132 {EXIT => '2'}],
134 ['suf-6', '--from=iec 4M', {OUT=>'4194304'}],
135 ['suf-7', '--from=auto 4M', {OUT=>'4000000'}],
136 ['suf-8', '--from=auto 4Mi', {OUT=>'4194304'}],
137 ['suf-9', '--from=auto 4MiJ',
138 {ERR => "$prog: invalid suffix in input '4MiJ': 'J'\n"},
139 {EXIT => '2'}],
140 ['suf-10', '--from=auto 4JiJ',
141 {ERR => "$prog: invalid suffix in input: '4JiJ'\n"},
142 {EXIT => '2'}],
144 # characters after a white space are OK - printed as-is
145 ['suf-11', '"4 M"', {OUT=>'4 M'}],
147 # Custom suffix
148 ['suf-12', '--suffix=Foo 70Foo', {OUT=>'70Foo'}],
149 ['suf-13', '--suffix=Foo 70', {OUT=>'70Foo'}],
150 ['suf-14', '--suffix=Foo --from=si 70K', {OUT=>'70000Foo'}],
151 ['suf-15', '--suffix=Foo --from=si 70KFoo', {OUT=>'70000Foo'}],
152 ['suf-16', '--suffix=Foo --to=si 7000Foo', {OUT=>'7.0kFoo'}],
153 ['suf-17', '--suffix=Foo --to=si 7000Bar',
154 {ERR => "$prog: invalid suffix in input: '7000Bar'\n"},
155 {EXIT => '2'}],
156 ['suf-18', '--suffix=Foo --to=si 7000FooF',
157 {ERR => "$prog: invalid suffix in input: '7000FooF'\n"},
158 {EXIT => '2'}],
159 # space(s) between number and suffix. Note only field 1 is used
160 # by default so specify the NUL delimiter to consider the whole "line".
161 ['suf-19', "-d '' --from=si '4.0 K'", {OUT => "4000"}],
162 ['suf-20',
163 '--suffix=Foo' . 'x' x 122 . 'y 0',
164 {OUT => '0Foo' . 'x' x 122 . 'y'}],
166 ## GROUPING
168 # "C" locale - no grouping (locale-specific tests, below)
169 ['grp-1', '--from=si --grouping 7M', {OUT=>'7000000'}],
170 ['grp-2', '--from=si --to=si --grouping 7M',
171 {ERR => "$prog: grouping cannot be combined with --to\n"},
172 {EXIT => '1'}],
175 ## Padding
176 ['pad-1', '--padding=10 5', {OUT=>' 5'}],
177 ['pad-2', '--padding=-10 5', {OUT=>'5 '}],
178 ['pad-3', '--padding=A 5',
179 {ERR => "$prog: invalid padding value 'A'\n"},
180 {EXIT => '1'}],
181 ['pad-3.1', '--padding=0 5',
182 {ERR => "$prog: invalid padding value '0'\n"},
183 {EXIT => '1'}],
184 ['pad-4', '--padding=10 --to=si 50000', {OUT=>' 50k'}],
185 ['pad-5', '--padding=-10 --to=si 50000', {OUT=>'50k '}],
187 # padding too narrow
188 ['pad-6', '--padding=2 --to=si 1000', {OUT=>'1.0k'}],
191 # Padding + suffix
192 ['pad-7', '--padding=10 --suffix=foo --to=si 50000',
193 {OUT=>' 50kfoo'}],
194 ['pad-8', '--padding=-10 --suffix=foo --to=si 50000',
195 {OUT=>'50kfoo '}],
198 # Delimiters
199 ['delim-1', '--delimiter=: --from=auto 40M:', {OUT=>'40000000:'}],
200 ['delim-2', '--delimiter="" --from=auto "40 M"',{OUT=>'40000000'}],
201 ['delim-3', '--delimiter=" " --from=auto "40M Foo"',{OUT=>'40000000 Foo'}],
202 ['delim-4', '--delimiter=: --from=auto 40M:60M', {OUT=>'40000000:60M'}],
203 ['delim-5', '-d: --field=2 --from=auto :40M:60M', {OUT=>':40000000:60M'}],
204 ['delim-6', '-d: --field 3 --from=auto 40M:60M', {OUT=>"40M:60M"}],
205 ['delim-err-1', '-d,, --to=si 1', {EXIT=>1},
206 {ERR => "$prog: the delimiter must be a single character\n"}],
208 #Fields
209 ['field-1', '--field A',
210 {ERR => "$prog: invalid field value 'A'\n$try"},
211 {EXIT => '1'}],
212 ['field-2', '--field 2 --from=auto "Hello 40M World 90G"',
213 {OUT=>'Hello 40000000 World 90G'}],
214 ['field-3', '--field 3 --from=auto "Hello 40M World 90G"',
215 {OUT=>"Hello 40M "},
216 {ERR=>"$prog: invalid number: 'World'\n"},
217 {EXIT => 2},],
218 # Last field - no text after number
219 ['field-4', '--field 4 --from=auto "Hello 40M World 90G"',
220 {OUT=>"Hello 40M World 90000000000"}],
221 # Last field - a delimiter after the number
222 ['field-5', '--field 4 --from=auto "Hello 40M World 90G "',
223 {OUT=>"Hello 40M World 90000000000 "}],
225 # Mix Fields + Delimiters
226 ['field-6', '--delimiter=: --field 2 --from=auto "Hello:40M:World:90G"',
227 {OUT=>"Hello:40000000:World:90G"}],
229 # not enough fields
230 ['field-8', '--field 3 --to=si "Hello World"', {OUT=>"Hello World"}],
232 # Multiple fields
233 ['field-range-1', '--field 2,4 --to=si "1000 2000 3000 4000 5000"',
234 {OUT=>"1000 2.0k 3000 4.0k 5000"}],
236 ['field-range-2', '--field 2-4 --to=si "1000 2000 3000 4000 5000"',
237 {OUT=>"1000 2.0k 3.0k 4.0k 5000"}],
239 ['field-range-3', '--field 1,2,3-5 --to=si "1000 2000 3000 4000 5000"',
240 {OUT=>"1.0k 2.0k 3.0k 4.0k 5.0k"}],
242 ['field-range-4', '--field 1-5 --to=si "1000 2000 3000 4000 5000"',
243 {OUT=>"1.0k 2.0k 3.0k 4.0k 5.0k"}],
245 ['field-range-5', '--field 1-3,5 --to=si "1000 2000 3000 4000 5000"',
246 {OUT=>"1.0k 2.0k 3.0k 4000 5.0k"}],
248 ['field-range-6', '--field 3- --to=si "1000 2000 3000 4000 5000"',
249 {OUT=>"1000 2000 3.0k 4.0k 5.0k"}],
251 ['field-range-7', '--field -3 --to=si "1000 2000 3000 4000 5000"',
252 {OUT=>"1.0k 2.0k 3.0k 4000 5000"}],
254 ['field-range-8', '--field 1-2,4-5 --to=si "1000 2000 3000 4000 5000"',
255 {OUT=>"1.0k 2.0k 3000 4.0k 5.0k"}],
256 ['field-range-9', '--field 4-5,1-2 --to=si "1000 2000 3000 4000 5000"',
257 {OUT=>"1.0k 2.0k 3000 4.0k 5.0k"}],
259 ['field-range-10','--field 1-3,2-4 --to=si "1000 2000 3000 4000 5000"',
260 {OUT=>"1.0k 2.0k 3.0k 4.0k 5000"}],
261 ['field-range-11','--field 2-4,1-3 --to=si "1000 2000 3000 4000 5000"',
262 {OUT=>"1.0k 2.0k 3.0k 4.0k 5000"}],
264 ['field-range-12','--field 1-1,3-3 --to=si "1000 2000 3000 4000 5000"',
265 {OUT=>"1.0k 2000 3.0k 4000 5000"}],
267 ['field-range-13', '--field 1,-2 --to=si "1000 2000 3000"',
268 {OUT=>"1.0k 2.0k 3000"}],
270 ['field-range-14', '--field -2,4- --to=si "1000 2000 3000 4000 5000"',
271 {OUT=>"1.0k 2.0k 3000 4.0k 5.0k"}],
272 ['field-range-15', '--field -2,-4 --to=si "1000 2000 3000 4000 5000"',
273 {OUT=>"1.0k 2.0k 3.0k 4.0k 5000"}],
274 ['field-range-16', '--field 2-,4- --to=si "1000 2000 3000 4000 5000"',
275 {OUT=>"1000 2.0k 3.0k 4.0k 5.0k"}],
276 ['field-range-17', '--field 4-,2- --to=si "1000 2000 3000 4000 5000"',
277 {OUT=>"1000 2.0k 3.0k 4.0k 5.0k"}],
279 # white space are valid field separators
280 # (undocumented? but works in cut as well).
281 ['field-range-18', '--field "1,2 4" --to=si "1000 2000 3000 4000 5000"',
282 {OUT=>"1.0k 2.0k 3000 4.0k 5000"}],
284 # Unlike 'cut', a lone '-' means 'all fields', even as part of a list
285 # of fields.
286 ['field-range-19','--field 3,- --to=si "1000 2000 3000 4000 5000"',
287 {OUT=>"1.0k 2.0k 3.0k 4.0k 5.0k"}],
289 ['all-fields-1', '--field=- --to=si "1000 2000 3000 4000 5000"',
290 {OUT=>"1.0k 2.0k 3.0k 4.0k 5.0k"}],
292 ['field-range-err-1', '--field -foo --to=si 10',
293 {EXIT=>1}, {ERR=>"$prog: invalid field value 'foo'\n$try"}],
294 ['field-range-err-2', '--field --3 --to=si 10',
295 {EXIT=>1}, {ERR=>"$prog: invalid field range\n$try"}],
296 ['field-range-err-3', '--field 0 --to=si 10',
297 {EXIT=>1}, {ERR=>"$prog: fields are numbered from 1\n$try"}],
298 ['field-range-err-4', '--field 3-2 --to=si 10',
299 {EXIT=>1}, {ERR=>"$prog: invalid decreasing range\n$try"}],
300 ['field-range-err-6', '--field - --field 1- --to=si 10',
301 {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}],
302 ['field-range-err-7', '--field -1 --field 1- --to=si 10',
303 {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}],
304 ['field-range-err-8', '--field -1 --field 1,2,3 --to=si 10',
305 {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}],
306 ['field-range-err-9', '--field 1- --field 1,2,3 --to=si 10',
307 {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}],
308 ['field-range-err-10','--field 1,2,3 --field 1- --to=si 10',
309 {EXIT=>1}, {ERR=>"$prog: multiple field specifications\n"}],
310 ['field-range-err-11','--field 1-2-3 --to=si 10',
311 {EXIT=>1}, {ERR=>"$prog: invalid field range\n$try"}],
312 ['field-range-err-12','--field 0-1 --to=si 10',
313 {EXIT=>1}, {ERR=>"$prog: fields are numbered from 1\n$try"}],
314 ['field-range-err-13','--field '.$limits->{UINTMAX_MAX}.',22 --to=si 10',
315 {EXIT=>1}, {ERR=>"$prog: field number " .
316 "'".$limits->{UINTMAX_MAX}."' is too large\n$try"}],
318 # Auto-consume white-space, setup auto-padding
319 ['whitespace-1', '--to=si --field 2 "A 500 B"', {OUT=>"A 500 B"}],
320 ['whitespace-2', '--to=si --field 2 "A 5000 B"', {OUT=>"A 5.0k B"}],
321 ['whitespace-3', '--to=si " 500"', {OUT=>" 500"}],
322 ['whitespace-4', '--to=si " 6500"', {OUT=>" 6.5k"}],
323 # NOTE: auto-padding is not enabled if the value is on the first
324 # field and there's no white-space before it.
325 ['whitespace-5', '--to=si "6000000"', {OUT=>"6.0M"}],
326 # but if there is whitespace, assume auto-padding is desired.
327 ['whitespace-6', '--to=si " 6000000"', {OUT=>" 6.0M"}],
329 # auto-padding - lines have same padding-width
330 # (padding_buffer will be alloc'd just once)
331 ['whitespace-7', '--to=si --field 2',
332 {IN_PIPE=>"rootfs 100000\n" .
333 "udevxx 2000000\n"},
334 {OUT =>"rootfs 100k\n" .
335 "udevxx 2.0M"}],
336 # auto-padding - second line requires a
337 # larger padding (padding-buffer needs to be realloc'd)
338 ['whitespace-8', '--to=si --field 2',
339 {IN_PIPE=>"rootfs 100000\n" .
340 "udev 20000000\n"},
341 {OUT =>"rootfs 100k\n" .
342 "udev 20M"}],
345 # Corner-cases:
346 # weird mix of identical suffix,delimiters
347 # The priority is:
348 # 1. delimiters (and fields) are parsed (in process_line()
349 # 2. optional custom suffix is removed (in process_suffixed_number())
350 # 3. Remaining suffixes must be valid SI/IEC (in human_xstrtol())
352 # custom suffix comes BEFORE SI/IEC suffix,
353 # so these are 40 of "M", not 40,000,000.
354 ['mix-1', '--suffix=M --from=si 40M', {OUT=>"40M"}],
356 # These are forty-million Ms .
357 ['mix-2', '--suffix=M --from=si 40MM', {OUT=>"40000000M"}],
359 ['mix-3', '--suffix=M --from=auto 40MM', {OUT=>"40000000M"}],
360 ['mix-4', '--suffix=M --from=auto 40MiM', {OUT=>"41943040M"}],
361 ['mix-5', '--suffix=M --to=si --from=si 4MM', {OUT=>"4.0MM"}],
363 # This might be confusing to the user, but it's legit:
364 # The M in the output is the custom suffix, not Mega.
365 ['mix-6', '--suffix=M 40', {OUT=>"40M"}],
366 ['mix-7', '--suffix=M 4000000', {OUT=>"4000000M"}],
367 ['mix-8', '--suffix=M --to=si 4000000', {OUT=>"4.0MM"}],
369 # The output 'M' is the custom suffix.
370 ['mix-10', '--delimiter=M --suffix=M 40', {OUT=>"40M"}],
372 # The INPUT 'M' is a delimiter (delimiters are top priority)
373 # The output contains one M for custom suffix, and one 'M' delimiter.
374 ['mix-11', '--delimiter=M --suffix=M 40M', {OUT=>"40MM"}],
376 # Same as above, the "M" is NOT treated as a mega SI prefix,
377 ['mix-12', '--delimiter=M --from=si --suffix=M 40M', {OUT=>"40MM"}],
379 # The 'M' is treated as a delimiter, and so the input value is '4000'
380 ['mix-13', '--delimiter=M --to=si --from=auto 4000M5000M9000',
381 {OUT=>"4.0kM5000M9000"}],
382 # 'M' is the delimiter, so the second input field is '5000'
383 ['mix-14', '--delimiter=M --field 2 --from=auto --to=si 4000M5000M9000',
384 {OUT=>"4000M5.0kM9000"}],
388 ## Header testing
390 # header - silently ignored with command line parameters
391 ['header-1', '--header --to=iec 4096', {OUT=>"4.0K"}],
393 # header warning with --debug
394 ['header-2', '--debug --header --to=iec 4096', {OUT=>"4.0K"},
395 {ERR=>"$prog: --header ignored with command-line input\n"}],
397 ['header-3', '--header=A',
398 {ERR=>"$prog: invalid header value 'A'\n"},
399 {EXIT => 1},],
400 ['header-4', '--header=0',
401 {ERR=>"$prog: invalid header value '0'\n"},
402 {EXIT => 1},],
403 ['header-5', '--header=-6',
404 {ERR=>"$prog: invalid header value '-6'\n"},
405 {EXIT => 1},],
406 ['header-6', '--debug --header --to=iec',
407 {IN_PIPE=>"size\n5000\n90000\n"},
408 {OUT=>"size\n4.9K\n88K"}],
409 ['header-7', '--debug --header=3 --to=iec',
410 {IN_PIPE=>"hello\nworld\nsize\n5000\n90000\n"},
411 {OUT=>"hello\nworld\nsize\n4.9K\n88K"}],
412 # header, but no actual content
413 ['header-8', '--header=2 --to=iec',
414 {IN_PIPE=>"hello\nworld\n"},
415 {OUT=>"hello\nworld"}],
416 # not enough header lines
417 ['header-9', '--header=3 --to=iec',
418 {IN_PIPE=>"hello\nworld\n"},
419 {OUT=>"hello\nworld"}],
422 ## human_strtod testing
424 # NO_DIGITS_FOUND
425 ['strtod-1', '--from=si "foo"',
426 {ERR=>"$prog: invalid number: 'foo'\n"},
427 {EXIT=> 2}],
428 ['strtod-2', '--from=si ""',
429 {ERR=>"$prog: invalid number: ''\n"},
430 {EXIT=> 2}],
432 # FRACTION_NO_DIGITS_FOUND
433 ['strtod-5', '--from=si 12.',
434 {ERR=>"$prog: invalid number: '12.'\n"},
435 {EXIT=>2}],
436 ['strtod-6', '--from=si 12.K',
437 {ERR=>"$prog: invalid number: '12.K'\n"},
438 {EXIT=>2}],
440 # whitespace is not allowed after decimal-point
441 ['strtod-6.1', '--from=si --delimiter=, "12. 2"',
442 {ERR=>"$prog: invalid number: '12. 2'\n"},
443 {EXIT=>2}],
445 # INVALID_SUFFIX
446 ['strtod-9', '--from=si 12.2J',
447 {ERR=>"$prog: invalid suffix in input: '12.2J'\n"},
448 {EXIT=>2}],
450 # VALID_BUT_FORBIDDEN_SUFFIX
451 ['strtod-10', '12M',
452 {ERR => "$prog: rejecting suffix " .
453 "in input: '12M' (consider using --from)\n"},
454 {EXIT=>2}],
456 # MISSING_I_SUFFIX
457 ['strtod-11', '--from=iec-i 12M',
458 {ERR => "$prog: missing 'i' suffix in input: " .
459 "'12M' (e.g Ki/Mi/Gi)\n"},
460 {EXIT=>2}],
463 # Test double_to_human()
466 # 1K and smaller
467 ['dbl-to-human-1','--to=si 800', {OUT=>"800"}],
468 ['dbl-to-human-2','--to=si 0', {OUT=>"0"}],
469 ['dbl-to-human-2.1','--to=si 999', {OUT=>"999"}],
470 ['dbl-to-human-2.2','--to=si 1000', {OUT=>"1.0k"}],
471 #NOTE: the following are consistent with "ls -lh" output
472 ['dbl-to-human-2.3','--to=iec 999', {OUT=>"999"}],
473 ['dbl-to-human-2.4','--to=iec 1023', {OUT=>"1023"}],
474 ['dbl-to-human-2.5','--to=iec 1024', {OUT=>"1.0K"}],
475 ['dbl-to-human-2.6','--to=iec 1025', {OUT=>"1.1K"}],
476 ['dbl-to-human-2.7','--to=iec 0', {OUT=>"0"}],
477 # no "i" suffix if output has no suffix
478 ['dbl-to-human-2.8','--to=iec-i 0', {OUT=>"0"}],
480 # values resulting in "N.Nx" output
481 ['dbl-to-human-3','--to=si 8000', {OUT=>"8.0k"}],
482 ['dbl-to-human-3.1','--to=si 8001', {OUT=>"8.1k"}],
483 ['dbl-to-human-4','--to=si --round=down 8001', {OUT=>"8.0k"}],
485 ['dbl-to-human-5','--to=si --round=down 3500', {OUT=>"3.5k"}],
486 ['dbl-to-human-6','--to=si --round=nearest 3500', {OUT=>"3.5k"}],
487 ['dbl-to-human-7','--to=si --round=up 3500', {OUT=>"3.5k"}],
489 ['dbl-to-human-8','--to=si --round=down 3501', {OUT=>"3.5k"}],
490 ['dbl-to-human-9','--to=si --round=nearest 3501', {OUT=>"3.5k"}],
491 ['dbl-to-human-10','--to=si --round=up 3501', {OUT=>"3.6k"}],
493 ['dbl-to-human-11','--to=si --round=nearest 3550', {OUT=>"3.6k"}],
494 ['dbl-to-human-12','--to=si --from=si 999.89K', {OUT=>"1.0M"}],
495 ['dbl-to-human-13','--to=si --from=si 9.9K', {OUT=>"9.9k"}],
496 ['dbl-to-human-14','--to=si 9900', {OUT=>"9.9k"}],
497 ['dbl-to-human-15','--to=iec --from=si 3.3K', {OUT=>"3.3K"}],
498 ['dbl-to-human-16','--to=iec --round=down --from=si 3.3K', {OUT=>"3.2K"}],
500 # values resulting in 'NNx' output
501 ['dbl-to-human-17','--to=si 9999', {OUT=>"10k"}],
502 ['dbl-to-human-18','--to=si --round=down 35000', {OUT=>"35k"}],
503 ['dbl-to-human-19','--to=iec 35000', {OUT=>"35K"}],
504 ['dbl-to-human-20','--to=iec --round=down 35000', {OUT=>"34K"}],
505 ['dbl-to-human-21','--to=iec 35000000', {OUT=>"34M"}],
506 ['dbl-to-human-22','--to=iec --round=down 35000000', {OUT=>"33M"}],
507 ['dbl-to-human-23','--to=si 35000001', {OUT=>"36M"}],
508 ['dbl-to-human-24','--to=si --from=si 9.99M', {OUT=>"10M"}],
509 ['dbl-to-human-25','--to=si --from=iec 9.99M', {OUT=>"11M"}],
510 ['dbl-to-human-25.1','--to=iec 99999', {OUT=>"98K"}],
512 # values resulting in 'NNNx' output
513 ['dbl-to-human-26','--to=si 999000000000', {OUT=>"999G"}],
514 ['dbl-to-human-27','--to=iec 999000000000', {OUT=>"931G"}],
515 ['dbl-to-human-28','--to=si 123600000000000', {OUT=>"124T"}],
516 ['dbl-to-human-29','--to=si 998123', {OUT=>"999k"}],
517 ['dbl-to-human-30','--to=si --round=nearest 998123', {OUT=>"998k"}],
518 ['dbl-to-human-31','--to=si 99999', {OUT=>"100k"}],
519 ['dbl-to-human-32','--to=iec 102399', {OUT=>"100K"}],
520 ['dbl-to-human-33','--to=iec-i 102399', {OUT=>"100Ki"}],
523 # Default --round=from-zero
524 ['round-1','--to-unit=1024 -- 6000 -6000',
525 {OUT=>"6\n-6"}],
526 ['round-2','--to-unit=1024 -- 6000.0 -6000.0',
527 {OUT=>"5.9\n-5.9"}],
528 ['round-3','--to-unit=1024 -- 6000.00 -6000.00',
529 {OUT=>"5.86\n-5.86"}],
530 ['round-4','--to-unit=1024 -- 6000.000 -6000.000',
531 {OUT=>"5.860\n-5.860"}],
532 ['round-5','--to-unit=1024 -- 6000.0000 -6000.0000',
533 {OUT=>"5.8594\n-5.8594"}],
534 # --round=up
535 ['round-1-up','--round=up --to-unit=1024 -- 6000 -6000',
536 {OUT=>"6\n-5"}],
537 ['round-2-up','--round=up --to-unit=1024 -- 6000.0 -6000.0',
538 {OUT=>"5.9\n-5.8"}],
539 ['round-3-up','--round=up --to-unit=1024 -- 6000.00 -6000.00',
540 {OUT=>"5.86\n-5.85"}],
541 ['round-4-up','--round=up --to-unit=1024 -- 6000.000 -6000.000',
542 {OUT=>"5.860\n-5.859"}],
543 ['round-5-up','--round=up --to-unit=1024 -- 6000.0000 -6000.0000',
544 {OUT=>"5.8594\n-5.8593"}],
545 # --round=down
546 ['round-1-down','--round=down --to-unit=1024 -- 6000 -6000',
547 {OUT=>"5\n-6"}],
548 ['round-2-down','--round=down --to-unit=1024 -- 6000.0 -6000.0',
549 {OUT=>"5.8\n-5.9"}],
550 ['round-3-down','--round=down --to-unit=1024 -- 6000.00 -6000.00',
551 {OUT=>"5.85\n-5.86"}],
552 ['round-4-down','--round=down --to-unit=1024 -- 6000.000 -6000.000',
553 {OUT=>"5.859\n-5.860"}],
554 ['round-5-down','--round=down --to-unit=1024 -- 6000.0000 -6000.0000',
555 {OUT=>"5.8593\n-5.8594"}],
556 # --round=towards-zero
557 ['round-1-to-zero','--ro=towards-zero --to-u=1024 -- 6000 -6000',
558 {OUT=>"5\n-5"}],
559 ['round-2-to-zero','--ro=towards-zero --to-u=1024 -- 6000.0 -6000.0',
560 {OUT=>"5.8\n-5.8"}],
561 ['round-3-to-zero','--ro=towards-zero --to-u=1024 -- 6000.00 -6000.00',
562 {OUT=>"5.85\n-5.85"}],
563 ['round-4-to-zero','--ro=towards-zero --to-u=1024 -- 6000.000 -6000.000',
564 {OUT=>"5.859\n-5.859"}],
565 ['round-5-to-zero','--ro=towards-zero --to-u=1024 -- 6000.0000 -6000.0000',
566 {OUT=>"5.8593\n-5.8593"}],
567 # --round=nearest
568 ['round-1-near','--ro=nearest --to-u=1024 -- 6000 -6000',
569 {OUT=>"6\n-6"}],
570 ['round-2-near','--ro=nearest --to-u=1024 -- 6000.0 -6000.0',
571 {OUT=>"5.9\n-5.9"}],
572 ['round-3-near','--ro=nearest --to-u=1024 -- 6000.00 -6000.00',
573 {OUT=>"5.86\n-5.86"}],
574 ['round-4-near','--ro=nearest --to-u=1024 -- 6000.000 -6000.000',
575 {OUT=>"5.859\n-5.859"}],
576 ['round-5-near','--ro=nearest --to-u=1024 -- 6000.0000 -6000.0000',
577 {OUT=>"5.8594\n-5.8594"}],
580 # Leading zeros weren't handled appropriately before 8.24
581 ['leading-1','0000000000000000000000000001', {OUT=>"1"}],
582 ['leading-2','.1', {OUT=>"0.1"}],
583 ['leading-3','bad.1',
584 {ERR => "$prog: invalid number: 'bad.1'\n"},
585 {EXIT => 2}],
586 ['leading-4','..1',
587 {ERR => "$prog: invalid suffix in input: '..1'\n"},
588 {EXIT => 2}],
589 ['leading-5','1.',
590 {ERR => "$prog: invalid number: '1.'\n"},
591 {EXIT => 2}],
593 # precision override
594 ['precision-1','--format=%.4f 9991239123 --to=si', {OUT=>"9.9913G"}],
595 ['precision-2','--format=%.1f 9991239123 --to=si', {OUT=>"10.0G"}],
596 ['precision-3','--format=%.1f 1', {OUT=>"1.0"}],
597 ['precision-4','--format=%.1f 1.12', {OUT=>"1.2"}],
598 ['precision-5','--format=%.1f 9991239123 --to-unit=G', {OUT=>"10.0"}],
599 ['precision-6','--format="% .1f" 9991239123 --to-unit=G', {OUT=>"10.0"}],
600 ['precision-7','--format=%.-1f 1.1',
601 {ERR => "$prog: invalid precision in format '%.-1f'\n"},
602 {EXIT => 1}],
603 ['precision-8','--format=%.+1f 1.1',
604 {ERR => "$prog: invalid precision in format '%.+1f'\n"},
605 {EXIT => 1}],
606 ['precision-9','--format="%. 1f" 1.1',
607 {ERR => "$prog: invalid precision in format '%. 1f'\n"},
608 {EXIT => 1}],
610 # debug warnings
611 ['debug-1', '--debug 4096', {OUT=>"4096"},
612 {ERR=>"$prog: no conversion option specified\n"}],
613 # '--padding' is a valid conversion option - no warning should be printed
614 ['debug-1.1', '--debug --padding 10 4096', {OUT=>" 4096"}],
615 ['debug-2', '--debug --grouping --from=si 4.0K', {OUT=>"4000"},
616 {ERR=>"$prog: grouping has no effect in this locale\n"}],
618 # dev-debug messages - the actual messages don't matter
619 # just ensure the program works, and for code coverage testing.
620 ['devdebug-1', '---debug --from=si 4.9K', {OUT=>"4900"},
621 {ERR=>""},
622 {ERR_SUBST=>"s/.*//msg"}],
623 ['devdebug-2', '---debug 4900', {OUT=>"4900"},
624 {ERR=>""},
625 {ERR_SUBST=>"s/.*//msg"}],
626 ['devdebug-3', '---debug --from=auto 4Mi', {OUT=>"4194304"},
627 {ERR=>""},
628 {ERR_SUBST=>"s/.*//msg"}],
629 ['devdebug-4', '---debug --to=si 4000000', {OUT=>"4.0M"},
630 {ERR=>""},
631 {ERR_SUBST=>"s/.*//msg"}],
632 ['devdebug-5', '---debug --to=si --padding=5 4000000', {OUT=>" 4.0M"},
633 {ERR=>""},
634 {ERR_SUBST=>"s/.*//msg"}],
635 ['devdebug-6', '---debug --suffix=Foo 1234Foo', {OUT=>"1234Foo"},
636 {ERR=>""},
637 {ERR_SUBST=>"s/.*//msg"}],
638 ['devdebug-7', '---debug --suffix=Foo 1234', {OUT=>"1234Foo"},
639 {ERR=>""},
640 {ERR_SUBST=>"s/.*//msg"}],
641 ['devdebug-9', '---debug --grouping 10000', {OUT=>"10000"},
642 {ERR=>""},
643 {ERR_SUBST=>"s/.*//msg"}],
644 ['devdebug-10', '---debug --format %f 10000', {OUT=>"10000"},
645 {ERR=>""},
646 {ERR_SUBST=>"s/.*//msg"}],
647 ['devdebug-11', '---debug --format "%\'-10f" 10000',{OUT=>"10000 "},
648 {ERR=>""},
649 {ERR_SUBST=>"s/.*//msg"}],
651 # Invalid parameters
652 ['help-1', '--foobar',
653 {ERR=>"$prog: unrecognized option\n$try"},
654 {ERR_SUBST=>"s/option.*/option/; s/unknown/unrecognized/"},
655 {EXIT=>1}],
657 ## Format string - check error detection
658 ['fmt-err-1', '--format ""',
659 {ERR=>"$prog: format '' has no % directive\n"},
660 {EXIT=>1}],
661 ['fmt-err-2', '--format "hello"',
662 {ERR=>"$prog: format 'hello' has no % directive\n"},
663 {EXIT=>1}],
664 ['fmt-err-3', '--format "hello%"',
665 {ERR=>"$prog: format 'hello%' ends in %\n"},
666 {EXIT=>1}],
667 ['fmt-err-4', '--format "%d"',
668 {ERR=>"$prog: invalid format '%d', " .
669 "directive must be %[0]['][-][N][.][N]f\n"},
670 {EXIT=>1}],
671 ['fmt-err-5', '--format "% -43 f"',
672 {ERR=>"$prog: invalid format '% -43 f', " .
673 "directive must be %[0]['][-][N][.][N]f\n"},
674 {EXIT=>1}],
675 ['fmt-err-6', '--format "%f %f"',
676 {ERR=>"$prog: format '%f %f' has too many % directives\n"},
677 {EXIT=>1}],
678 ['fmt-err-9', '--format "%f" --grouping',
679 {ERR=>"$prog: --grouping cannot be combined with --format\n"},
680 {EXIT=>1}],
681 ['fmt-err-10', '--format "%\'f" --to=si',
682 {ERR=>"$prog: grouping cannot be combined with --to\n"},
683 {EXIT=>1}],
684 ['fmt-err-11', '--debug --format "%\'f" 5000', {OUT=>"5000"},
685 {ERR=>"$prog: grouping has no effect in this locale\n"}],
687 ## Format string - check some corner cases
688 ['fmt-1', '--format "%% %f" 5000', {OUT=>"%%5000"}],
689 ['fmt-2', '--format "%f %%" 5000', {OUT=>"5000 %%"}],
691 ['fmt-3', '--format "--%f--" 5000000', {OUT=>"--5000000--"}],
692 ['fmt-4', '--format "--%f--" --to=si 5000000', {OUT=>"--5.0M--"}],
694 ['fmt-5', '--format "--%10f--" --to=si 5000000',{OUT=>"-- 5.0M--"}],
695 ['fmt-6', '--format "--%-10f--" --to=si 5000000',{OUT=>"--5.0M --"}],
696 ['fmt-7', '--format "--%10f--" 5000000',{OUT=>"-- 5000000--"}],
697 ['fmt-8', '--format "--%-10f--" 5000000',{OUT=>"--5000000 --"}],
699 # too-short width
700 ['fmt-9', '--format "--%5f--" 5000000',{OUT=>"--5000000--"}],
702 # Format + Suffix
703 ['fmt-10', '--format "--%10f--" --suffix Foo 50', {OUT=>"-- 50Foo--"}],
704 ['fmt-11', '--format "--%-10f--" --suffix Foo 50',{OUT=>"--50Foo --"}],
706 # Grouping in C locale - no grouping effect
707 ['fmt-12', '--format "%\'f" 50000',{OUT=>"50000"}],
708 ['fmt-13', '--format "%\'10f" 50000', {OUT=>" 50000"}],
709 ['fmt-14', '--format "%\'-10f" 50000',{OUT=>"50000 "}],
711 # Very large format strings
712 ['fmt-15', '--format "--%100000f--" --to=si 4200',
713 {OUT=>"--" . " " x 99996 . "4.2k--" }],
715 # --format padding overrides --padding
716 ['fmt-16', '--format="%6f" --padding=66 1234',{OUT=>" 1234"}],
718 # zero padding
719 ['fmt-17', '--format="%06f" 1234',{OUT=>"001234"}],
720 # also support spaces (which are ignored as spacing is handled separately)
721 ['fmt-18', '--format="%0 6f" 1234',{OUT=>"001234"}],
722 # handle generic padding in combination
723 ['fmt-22', '--format="%06f" --padding=7 1234',{OUT=>" 001234"}],
724 ['fmt-23', '--format="%06f" --padding=-7 1234',{OUT=>"001234 "}],
727 ## Check all errors again, this time with --invalid=fail
728 ## Input will be printed without conversion,
729 ## and exit code will be 2
730 ['ign-err-1', '--invalid=fail 4J',
731 {ERR => "$prog: invalid suffix in input: '4J'\n"},
732 {OUT => "4J\n"},
733 {EXIT => 2}],
734 ['ign-err-2', '--invalid=fail 4M',
735 {ERR => "$prog: rejecting suffix " .
736 "in input: '4M' (consider using --from)\n"},
737 {OUT => "4M\n"},
738 {EXIT => 2}],
739 ['ign-err-3', '--invalid=fail --from=si 4MJ',
740 {ERR => "$prog: invalid suffix in input '4MJ': 'J'\n"},
741 {OUT => "4MJ\n"},
742 {EXIT => 2}],
743 ['ign-err-4', '--invalid=fail --suffix=Foo --to=si 7000FooF',
744 {ERR => "$prog: invalid suffix in input: '7000FooF'\n"},
745 {OUT => "7000FooF\n"},
746 {EXIT => 2}],
747 ['ign-err-5','--invalid=fail --field 3 --from=auto "Hello 40M World 90G"',
748 {ERR => "$prog: invalid number: 'World'\n"},
749 {OUT => "Hello 40M World 90G\n"},
750 {EXIT => 2}],
751 ['ign-err-7', '--invalid=fail --from=si "foo"',
752 {ERR => "$prog: invalid number: 'foo'\n"},
753 {OUT => "foo\n"},
754 {EXIT=> 2}],
755 ['ign-err-8', '--invalid=fail 12M',
756 {ERR => "$prog: rejecting suffix " .
757 "in input: '12M' (consider using --from)\n"},
758 {OUT => "12M\n"},
759 {EXIT => 2}],
760 ['ign-err-9', '--invalid=fail --from=iec-i 12M',
761 {ERR => "$prog: missing 'i' suffix in input: " .
762 "'12M' (e.g Ki/Mi/Gi)\n"},
763 {OUT => "12M\n"},
764 {EXIT=>2}],
766 ## Ignore Errors with multiple conversions
767 ['ign-err-m1', '--invalid=ignore --to=si 1000 2000 bad 3000',
768 {OUT => "1.0k\n2.0k\nbad\n3.0k"},
769 {EXIT => 0}],
770 ['ign-err-m1.1', '--invalid=ignore --to=si',
771 {IN_PIPE => "1000\n2000\nbad\n3000\n"},
772 {OUT => "1.0k\n2.0k\nbad\n3.0k"},
773 {EXIT => 0}],
774 ['ign-err-m1.3', '--invalid=fail --debug --to=si 1000 2000 3000',
775 {OUT => "1.0k\n2.0k\n3.0k"},
776 {EXIT => 0}],
777 ['ign-err-m2', '--invalid=fail --to=si 1000 Foo 3000',
778 {OUT => "1.0k\nFoo\n3.0k\n"},
779 {ERR => "$prog: invalid number: 'Foo'\n"},
780 {EXIT => 2}],
781 ['ign-err-m2.1', '--invalid=warn --to=si',
782 {IN_PIPE => "1000\nFoo\n3000\n"},
783 {OUT => "1.0k\nFoo\n3.0k"},
784 {ERR => "$prog: invalid number: 'Foo'\n"},
785 {EXIT => 0}],
787 # --debug will trigger a final warning at EOF
788 ['ign-err-m2.2', '--invalid=fail --debug --to=si 1000 Foo 3000',
789 {OUT => "1.0k\nFoo\n3.0k\n"},
790 {ERR => "$prog: invalid number: 'Foo'\n" .
791 "$prog: failed to convert some of the input numbers\n"},
792 {EXIT => 2}],
794 ['ign-err-m3', '--invalid=fail --field 2 --from=si --to=iec',
795 {IN_PIPE => "A 1K x\nB 2M y\nC 3G z\n"},
796 {OUT => "A 1000 x\nB 2.0M y\nC 2.8G z"},
797 {EXIT => 0}],
798 # invalid input on one of the fields
799 ['ign-err-m3.1', '--invalid=fail --field 2 --from=si --to=iec',
800 {IN_PIPE => "A 1K x\nB Foo y\nC 3G z\n"},
801 {OUT => "A 1000 x\nB Foo y\nC 2.8G z\n"},
802 {ERR => "$prog: invalid number: 'Foo'\n"},
803 {EXIT => 2}],
806 # test null-terminated lines
807 my @NullDelim_Tests =
809 # Input from STDIN
810 ['z1', '-z --to=iec',
811 {IN_PIPE => "1025\x002048\x00"}, {OUT=>"1.1K\x002.0K\x00"}],
813 # Input from the commandline - terminated by NULL vs NL
814 ['z3', ' --to=iec 1024', {OUT=>"1.0K\n"}],
815 ['z2', '-z --to=iec 1024', {OUT=>"1.0K\x00"}],
817 # Input from STDIN, with fields
818 ['z4', '-z --field=3 --to=si',
819 {IN_PIPE => "A B 1001 C\x00" .
820 "D E 2002 F\x00"},
821 {OUT => "A B 1.1k C\x00" .
822 "D E 2.1k F\x00"}],
824 # Input from STDIN, with fields and embedded NL
825 ['z5', '-z --field=3 --to=si',
826 {IN_PIPE => "A\nB 1001 C\x00" .
827 "D E\n2002 F\x00"},
828 {OUT => "A B 1.1k C\x00" .
829 "D E 2.1k F\x00"}],
832 my @Limit_Tests =
834 # Large Values
835 ['large-1','1000000000000000', {OUT=>"1000000000000000"}],
836 # 18 digits is OK
837 ['large-2','1000000000000000000', {OUT=>"1000000000000000000"}],
838 # 19 digits is too much (without output scaling)
839 ['large-3','10000000000000000000',
840 {ERR => "$prog: value too large to be printed: '1e+19' " .
841 "(consider using --to)\n"},
842 {EXIT=>2}],
843 ['large-4','1000000000000000000.0',
844 {ERR => "$prog: value/precision too large to be printed: " .
845 "'1e+18/1' (consider using --to)\n"},
846 {EXIT=>2}],
849 # Test input:
850 # Up to 33 digits is OK.
851 ['large-3.1', '--to=si 1', {OUT=> "1"}],
852 ['large-3.2', '--to=si 10', {OUT=> "10"}],
853 ['large-3.3', '--to=si 100', {OUT=> "100"}],
854 ['large-3.4', '--to=si 1000', {OUT=>"1.0k"}],
855 ['large-3.5', '--to=si 10000', {OUT=> "10k"}],
856 ['large-3.6', '--to=si 100000', {OUT=>"100k"}],
857 ['large-3.7', '--to=si 1000000', {OUT=>"1.0M"}],
858 ['large-3.8', '--to=si 10000000', {OUT=> "10M"}],
859 ['large-3.9', '--to=si 100000000', {OUT=>"100M"}],
860 ['large-3.10','--to=si 1000000000', {OUT=>"1.0G"}],
861 ['large-3.11','--to=si 10000000000', {OUT=> "10G"}],
862 ['large-3.12','--to=si 100000000000', {OUT=>"100G"}],
863 ['large-3.13','--to=si 1000000000000', {OUT=>"1.0T"}],
864 ['large-3.14','--to=si 10000000000000', {OUT=> "10T"}],
865 ['large-3.15','--to=si 100000000000000', {OUT=>"100T"}],
866 ['large-3.16','--to=si 1000000000000000', {OUT=>"1.0P"}],
867 ['large-3.17','--to=si 10000000000000000', {OUT=> "10P"}],
868 ['large-3.18','--to=si 100000000000000000', {OUT=>"100P"}],
869 ['large-3.19','--to=si 1000000000000000000', {OUT=>"1.0E"}],
870 ['large-3.20','--to=si 10000000000000000000', {OUT=> "10E"}],
871 ['large-3.21','--to=si 210000000000000000000', {OUT=>"210E"}],
872 ['large-3.22','--to=si 3210000000000000000000', {OUT=>"3.3Z"}],
873 ['large-3.23','--to=si 43210000000000000000000', {OUT=> "44Z"}],
874 ['large-3.24','--to=si 543210000000000000000000', {OUT=>"544Z"}],
875 ['large-3.25','--to=si 6543210000000000000000000', {OUT=>"6.6Y"}],
876 ['large-3.26','--to=si 76543210000000000000000000', {OUT=> "77Y"}],
877 ['large-3.27','--to=si 876543210000000000000000000', {OUT=>"877Y"}],
878 ['large-3.28','--to=si 9876543210000000000000000000', {OUT=>"9.9R"}],
879 ['large-3.29','--to=si 19876543210000000000000000000', {OUT=> "20R"}],
880 ['large-3.30','--to=si 219876543210000000000000000000', {OUT=>"220R"}],
881 ['large-3.31','--to=si 3219876543210000000000000000000', {OUT=>"3.3Q"}],
882 ['large-3.32','--to=si 43219876543210000000000000000000', {OUT=> "44Q"}],
883 ['large-3.33','--to=si 543219876543210000000000000000000', {OUT=>"544Q"}],
885 # More than 33 digits is not OK
886 ['large-3.34','--to=si 6543219876543210000000000000000000',
887 {ERR => "$prog: value too large to be converted: " .
888 "'6543219876543210000000000000000000'\n"},
889 {EXIT => 2}],
891 # Test Output
892 ['large-4.1', '--from=si 9.7M', {OUT=>"9700000"}],
893 ['large-4.2', '--from=si 10M', {OUT =>"10000000"}],
894 ['large-4.3', '--from=si 200M', {OUT =>"200000000"}],
895 ['large-4.4', '--from=si 3G', {OUT =>"3000000000"}],
896 ['large-4.5', '--from=si 40G', {OUT =>"40000000000"}],
897 ['large-4.6', '--from=si 500G', {OUT =>"500000000000"}],
898 ['large-4.7', '--from=si 6T', {OUT =>"6000000000000"}],
899 ['large-4.8', '--from=si 70T', {OUT =>"70000000000000"}],
900 ['large-4.9', '--from=si 800T', {OUT =>"800000000000000"}],
901 ['large-4.10','--from=si 9P', {OUT =>"9000000000000000"}],
902 ['large-4.11','--from=si 10P', {OUT =>"10000000000000000"}],
903 ['large-4.12','--from=si 200P', {OUT =>"200000000000000000"}],
904 ['large-4.13','--from=si 3E', {OUT =>"3000000000000000000"}],
906 # More than 18 digits of output without scaling - no good.
907 ['large-4.14','--from=si 40E',
908 {ERR => "$prog: value too large to be printed: '4e+19' " .
909 "(consider using --to)\n"},
910 {EXIT => 2}],
911 ['large-4.15','--from=si 500E',
912 {ERR => "$prog: value too large to be printed: '5e+20' " .
913 "(consider using --to)\n"},
914 {EXIT => 2}],
915 ['large-4.16','--from=si 6Z',
916 {ERR => "$prog: value too large to be printed: '6e+21' " .
917 "(consider using --to)\n"},
918 {EXIT => 2}],
919 ['large-4.17','--from=si 70Z',
920 {ERR => "$prog: value too large to be printed: '7e+22' " .
921 "(consider using --to)\n"},
922 {EXIT => 2}],
923 ['large-4.18','--from=si 800Z',
924 {ERR => "$prog: value too large to be printed: '8e+23' " .
925 "(consider using --to)\n"},
926 {EXIT => 2}],
927 ['large-4.19','--from=si 9Y',
928 {ERR => "$prog: value too large to be printed: '9e+24' " .
929 "(consider using --to)\n"},
930 {EXIT => 2}],
931 ['large-4.20','--from=si 10Y',
932 {ERR => "$prog: value too large to be printed: '1e+25' " .
933 "(consider using --to)\n"},
934 {EXIT => 2}],
935 ['large-4.21','--from=si 200Y',
936 {ERR => "$prog: value too large to be printed: '2e+26' " .
937 "(consider using --to)\n"},
938 {EXIT => 2}],
940 ['large-5.1','--to=si 1000000000000000000', {OUT=>"1.0E"}],
941 ['large-5','--from=si --to=si 2E', {OUT=>"2.0E"}],
942 ['large-6','--from=si --to=si 3.4Z', {OUT=>"3.4Z"}],
943 ['large-7','--from=si --to=si 80Y', {OUT=>"80Y"}],
944 ['large-8','--from=si --to=si 9000Z', {OUT=>"9.0Y"}],
946 ['large-10','--from=si --to=si 999Q', {OUT=>"999Q"}],
947 ['large-11','--from=si --to=iec 999Q', {OUT=>"789Q"}],
948 ['large-12','--from=si --round=down --to=iec 999Q', {OUT=>"788Q"}],
950 # units can also affect the output
951 ['large-13','--from=si --from-unit=1000000 9P',
952 {ERR => "$prog: value too large to be printed: '9e+21' " .
953 "(consider using --to)\n"},
954 {EXIT => 2}],
955 ['large-13.1','--from=si --from-unit=1000000 --to=si 9P', {OUT=>"9.0Z"}],
957 # Numbers>999Q are never acceptable, regardless of scaling
958 ['large-14','--from=si --to=si 999Q', {OUT=>"999Q"}],
959 ['large-14.1','--from=si --to=si 1000Q',
960 {ERR => "$prog: value too large to be printed: '1e+33' " .
961 "(cannot handle values > 999Q)\n"},
962 {EXIT => 2}],
963 ['large-14.2','--from=si --to=si --from-unit=10000 1Q',
964 {ERR => "$prog: value too large to be printed: '1e+34' " .
965 "(cannot handle values > 999Q)\n"},
966 {EXIT => 2}],
968 # intmax_t overflow when rounding caused this to fail before 8.24
969 ['large-15',$limits->{INTMAX_OFLOW}, {OUT=>$limits->{INTMAX_OFLOW}}],
970 ['large-16','9.300000000000000000', {OUT=>'9.300000000000000000'}],
972 # INTEGRAL_OVERFLOW
973 ['strtod-3', '--from=si "1234567890123456789012345678901234567890'.
974 '1234567890123456789012345678901234567890"',
975 {ERR=>"$prog: value too large to be converted: '" .
976 "1234567890123456789012345678901234567890" .
977 "1234567890123456789012345678901234567890'\n",
979 {EXIT=> 2}],
981 # FRACTION_OVERFLOW
982 ['strtod-7', '--from=si "12.1234567890123456789012345678901234567890'.
983 '1234567890123456789012345678901234567890"',
984 {ERR=>"$prog: value too large to be converted: '" .
985 "12.1234567890123456789012345678901234567890" .
986 "1234567890123456789012345678901234567890'\n",
988 {EXIT=> 2}],
990 ['debug-4', '--to=si --debug 12345678901234567890',
991 {OUT=>"13E"},
992 {ERR=>"$prog: large input value '12345678901234567890':" .
993 " possible precision loss\n"}],
994 ['debug-5', '--to=si --from=si --debug 1.12345678901234567890Y',
995 {OUT=>"1.2Y"},
996 {ERR=>"$prog: large input value '1.12345678901234567890Y':" .
997 " possible precision loss\n"}],
999 ['ign-err-10','--invalid=fail 10000000000000000000',
1000 {ERR => "$prog: value too large to be printed: '1e+19' " .
1001 "(consider using --to)\n"},
1002 {OUT => "10000000000000000000\n"},
1003 {EXIT=>2}],
1004 ['ign-err-11','--invalid=fail --to=si 6543219876543210000000000000000000',
1005 {ERR => "$prog: value too large to be converted: " .
1006 "'6543219876543210000000000000000000'\n"},
1007 {OUT => "6543219876543210000000000000000000\n"},
1008 {EXIT => 2}],
1010 # Restrict these tests to systems with LDBL_DIG == 18
1011 (system "$prog ---debug 1 2>&1|grep 'MAX_UNSCALED_DIGITS: 18' > /dev/null") == 0
1012 and push @Tests, @Limit_Tests;
1014 my @Locale_Tests =
1016 # Locale that supports grouping, but without '--grouping' parameter
1017 ['lcl-grp-1', '--from=si 7M', {OUT=>"7000000"},
1018 {ENV=>"LC_ALL=$locale"}],
1020 # Locale with grouping
1021 ['lcl-grp-2', '--from=si --grouping 7M', {OUT=>"7 000 000"},
1022 {ENV=>"LC_ALL=$locale"}],
1024 # Locale with grouping and debug - no debug warning message
1025 ['lcl-grp-3', '--from=si --debug --grouping 7M', {OUT=>"7 000 000"},
1026 {ENV=>"LC_ALL=$locale"}],
1028 # Input with locale'd decimal-point
1029 ['lcl-stdtod-1', '--from=si 12,2K', {OUT=>"12200"},
1030 {ENV=>"LC_ALL=$locale"}],
1032 ['lcl-dbl-to-human-1', '--to=si 1100', {OUT=>"1,1K"},
1033 {ENV=>"LC_ALL=$locale"}],
1035 # Format + Grouping
1036 ['lcl-fmt-1', '--format "%\'f" 50000',{OUT=>"50 000"},
1037 {ENV=>"LC_ALL=$locale"}],
1038 ['lcl-fmt-2', '--format "--%\'10f--" 50000', {OUT=>"-- 50 000--"},
1039 {ENV=>"LC_ALL=$locale"}],
1040 ['lcl-fmt-3', '--format "--%\'-10f--" 50000',{OUT=>"--50 000 --"},
1041 {ENV=>"LC_ALL=$locale"}],
1042 ['lcl-fmt-4', '--format "--%-10f--" --to=si 5000000',
1043 {OUT=>"--5,0M --"},
1044 {ENV=>"LC_ALL=$locale"}],
1045 # handle zero/grouping in combination
1046 ['lcl-fmt-5', '--format="%\'06f" 1234',{OUT=>"01 234"},
1047 {ENV=>"LC_ALL=$locale"}],
1048 ['lcl-fmt-6', '--format="%0\'6f" 1234',{OUT=>"01 234"},
1049 {ENV=>"LC_ALL=$locale"}],
1050 ['lcl-fmt-7', '--format="%0\'\'6f" 1234',{OUT=>"01 234"},
1051 {ENV=>"LC_ALL=$locale"}],
1054 if ($locale ne 'C')
1056 # Reset locale to 'C' if LOCALE_FR_UTF8 doesn't output as expected
1057 # as determined by the separate printf program.
1058 open(LOC_NUM, "env LC_ALL=$locale printf \"%'d\" 1234|")
1059 or die "Can't fork command: $!";
1060 my $loc_num = <LOC_NUM>;
1061 close(LOC_NUM) || die "Failed to read grouped number from printf";
1062 if ($loc_num ne '1 234')
1064 warn "skipping locale grouping tests as 1234 groups like $loc_num\n";
1065 $locale = 'C';
1068 push @Tests, @Locale_Tests if $locale ne 'C';
1070 ## Check all valid/invalid suffixes
1071 foreach my $suf ( 'A' .. 'Z', 'a' .. 'z' ) {
1072 if ( $suf =~ /^[KkMGTPEZYRQ]$/ )
1074 my $si_suf = $suf;
1075 my $iec_suf = $suf;
1076 if ( $suf eq "k" )
1078 $iec_suf = "K";
1080 if ( $suf eq "K" )
1082 $si_suf = "k";
1084 push @Tests, ["auto-suf-si-$suf","--from=si --to=si 1$suf",
1085 {OUT=>"1.0$si_suf"}];
1086 push @Tests, ["auto-suf-iec-$suf","--from=iec --to=iec 1$suf",
1087 {OUT=>"1.0$iec_suf"}];
1088 push @Tests, ["auto-suf-auto-$suf","--from=auto --to=iec 1${suf}i",
1089 {OUT=>"1.0$iec_suf"}];
1090 push @Tests, ["auto-suf-iec-to-ieci-$suf","--from=iec --to=iec-i 1${suf}",
1091 {OUT=>"1.0${iec_suf}i"}];
1092 push @Tests, ["auto-suf-ieci-to-iec-$suf",
1093 "--from=iec-i --to=iec 1${suf}i",{OUT=>"1.0${iec_suf}"}];
1095 else
1097 push @Tests, ["auto-suf-si-$suf","--from=si --to=si 1$suf",
1098 {ERR=>"$prog: invalid suffix in input: '1${suf}'\n"},
1099 {EXIT=>2}];
1103 # Prepend the command line argument and append a newline to end
1104 # of each expected 'OUT' string.
1105 my $t;
1107 Test:
1108 foreach $t (@Tests)
1110 # Don't fiddle with expected OUT string if there's a nonzero exit status.
1111 foreach my $e (@$t)
1113 ref $e eq 'HASH' && exists $e->{EXIT} && $e->{EXIT}
1114 and next Test;
1117 foreach my $e (@$t)
1119 ref $e eq 'HASH' && exists $e->{OUT}
1120 and $e->{OUT} .= "\n"
1124 # Add test for null-terminated lines (after adjusting the OUT string, above).
1125 push @Tests, @NullDelim_Tests;
1127 my $save_temps = $ENV{SAVE_TEMPS};
1128 my $verbose = $ENV{VERBOSE};
1130 my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose);
1131 exit $fail;