5 require './test.pl'; # for fresh_perl_is() etc
6 require './loc_tools.pl'; # to find locales
12 # These tests are here instead of lib/locale.t because
13 # some bugs depend on the internal state of the locale
14 # settings and pragma/locale messes up that state pretty badly.
15 # We need "fresh runs".
17 eval { require POSIX; POSIX->import("locale_h") };
19 skip_all("could not load the POSIX module"); # running minitest?
23 my $have_strtod = $Config{d_strtod} eq 'define';
24 my @locales = find_locales( [ 'LC_ALL', 'LC_CTYPE', 'LC_NUMERIC' ]);
25 skip_all("no locales available") unless @locales;
26 note("locales available: @locales");
30 if (defined $ARGV[0] && $ARGV[0] ne "") {
31 if ($ARGV[0] ne 'debug') {
32 print STDERR "Usage: $0 [ debug ]\n";
36 $switches = "switches => [ '-DLv' ]";
39 # reset the locale environment
40 delete local @ENV{'LANG', (grep /^LC_[A-Z]+$/, keys %ENV)};
42 # If user wants this to happen, they set the environment variable AND use
44 delete local $ENV{'PERL_DEBUG_LOCALE_INIT'} unless $debug;
47 fresh_perl_is(<<"EOF",
50 POSIX::setlocale(POSIX::LC_CTYPE(),"C");
51 print "h" =~ /[g\\w]/i || 0;
54 1, { stderr => 'devnull' }, "/il matching of [bracketed] doesn't skip POSIX class if fails individ char");
58 fresh_perl_is(<<"EOF",
61 POSIX::setlocale(POSIX::LC_CTYPE(),"C");
62 print "0" =~ /[\\d[:punct:]]/l || 0;
65 1, { stderr => 'devnull' }, "/l matching of [bracketed] doesn't skip non-first POSIX class");
70 foreach my $locale (@locales) {
71 next if $locale eq "C" || $locale eq 'POSIX';
72 $non_C_locale = $locale;
77 note("using non-C locale '$non_C_locale'");
78 setlocale(LC_NUMERIC, $non_C_locale);
79 isnt(setlocale(LC_NUMERIC), "C", "retrieving current non-C LC_NUMERIC doesn't give 'C'");
80 setlocale(LC_ALL, $non_C_locale);
81 isnt(setlocale(LC_ALL), "C", "retrieving current non-C LC_ALL doesn't give 'C'");
83 my @test_numeric_locales = @locales;
85 # Skip this locale on these cygwin versions as the returned radix character
88 && version->new(($Config{'osvers'} =~ /^(\d+(?:\.\d+)+)/)[0]) le v2.4.1)
90 @test_numeric_locales = grep { $_ !~ m/ps_AF/i } @test_numeric_locales;
93 # Similarly the arabic locales on solaris don't work right on the
94 # multi-byte radix character, generating malformed UTF-8.
95 if ($^O eq 'solaris') {
96 @test_numeric_locales = grep { $_ !~ m/ ^ ( ar_ | pa_ ) /x }
97 @test_numeric_locales;
100 fresh_perl_is("for (qw(@test_numeric_locales)) {\n" . <<'EOF',
101 use POSIX qw(locale_h);
103 setlocale(LC_NUMERIC, "$_") or next;
104 my $s = sprintf "%g %g", 3.1, 3.1;
105 next if $s eq '3.1 3.1' || $s =~ /^(3.+1) \1$/;
110 "", { eval $switches }, "no locales where LC_NUMERIC breaks");
113 skip("Windows stores locale defaults in the registry", 1 )
115 fresh_perl_is("for (qw(@locales)) {\n" . <<'EOF',
116 use POSIX qw(locale_h);
119 my $s = sprintf "%g", $in; # avoid any constant folding bugs
125 "", { eval $switches }, "LC_NUMERIC without environment nor setlocale() has no effect in any locale");
128 # try to find out a locale where LC_NUMERIC makes a difference
129 my $original_locale = setlocale(LC_NUMERIC);
131 my ($base, $different, $comma, $difference, $utf8_radix);
132 my $radix_encoded_as_utf8;
133 for ("C", @locales) { # prefer C for the base if available
135 setlocale(LC_NUMERIC, $_) or next;
136 my $in = 4.2; # avoid any constant folding bugs
137 if ((my $s = sprintf("%g", $in)) eq "4.2") {
142 my $radix = localeconv()->{decimal_point};
144 # For utf8 locales with a non-ascii radix, it should be encoded as
145 # UTF-8 with the internal flag so set.
146 if (! defined $utf8_radix
147 && $radix =~ /[[:^ascii:]]/u # /u because /l can raise warnings
148 && is_locale_utf8($_))
151 $radix_encoded_as_utf8 = utf8::is_utf8($radix);
154 $comma ||= $_ if $radix eq ',';
158 last if $base && $different && $comma && $utf8_radix;
160 setlocale(LC_NUMERIC, $original_locale);
163 skip("no UTF-8 locale available where LC_NUMERIC radix isn't ASCII", 1 )
165 ok($radix_encoded_as_utf8 == 1, "UTF-8 locale '$utf8_radix' with non-ASCII"
166 . " radix is marked UTF-8");
170 note("using the '$different' locale for LC_NUMERIC tests");
172 local $ENV{LC_NUMERIC} = $different;
174 fresh_perl_is(<<'EOF', "4.2", { eval $switches },
181 "format() does not look at LC_NUMERIC without 'use locale'");
184 fresh_perl_is(<<'EOF', "$difference\n", { eval $switches },
193 "format() looks at LC_NUMERIC with 'use locale'");
197 fresh_perl_is(<<'EOF', ",,", { eval $switches },
200 print localeconv()->{decimal_point};
202 print localeconv()->{decimal_point};
204 "localeconv() looks at LC_NUMERIC with and without 'use locale'");
208 my $categories = ":collate :characters :collate :ctype :monetary :time";
209 fresh_perl_is(<<"EOF", "4.2", { eval $switches },
210 use locale qw($categories);
217 "format() does not look at LC_NUMERIC with 'use locale qw($categories)'");
221 fresh_perl_is(<<'EOF', $difference, { eval $switches },
229 "format() looks at LC_NUMERIC with 'use locale'");
232 for my $category (qw(collate characters collate ctype monetary time)) {
233 for my $negation ("!", "not_") {
234 fresh_perl_is(<<"EOF", $difference, { eval $switches },
235 use locale ":$negation$category";
242 "format() looks at LC_NUMERIC with 'use locale \":"
243 . "$negation$category\"'");
248 fresh_perl_is(<<'EOF', $difference, { eval $switches },
249 use locale ":numeric";
256 "format() looks at LC_NUMERIC with 'use locale \":numeric\"'");
260 fresh_perl_is(<<'EOF', "4.2", { eval $switches },
265 { use locale; write; }
267 "too late to look at the locale at write() time");
271 fresh_perl_is(<<'EOF', $difference, { eval $switches },
277 { no locale; write; }
279 "too late to ignore the locale at write() time");
284 # do not let "use 5.000" affect the locale!
285 # this test is to prevent regression of [rt.perl.org #105784]
286 fresh_perl_is(<<"EOF",
290 POSIX::setlocale(POSIX::LC_NUMERIC(),"$different");
291 \$a = sprintf("%.2f", \$i);
293 \$b = sprintf("%.2f", \$i);
295 print ".\$a \$b" unless \$a eq \$b
297 "", { eval $switches }, "version does not clobber version");
299 fresh_perl_is(<<"EOF",
303 POSIX::setlocale(POSIX::LC_NUMERIC(),"$different");
304 \$a = sprintf("%.2f", \$i);
306 \$b = sprintf("%.2f", \$i);
308 print "\$a \$b" unless \$a eq \$b
310 "", { eval $switches }, "version does not clobber version (via eval)");
314 local $ENV{LC_NUMERIC} = $different;
315 fresh_perl_is(<<'EOF', "$difference "x4, { eval $switches },
317 use POSIX qw(locale_h);
319 printf("%g %g %s %s ", $in, 4.2, sprintf("%g", $in), sprintf("%g", 4.2));
321 "sprintf() and printf() look at LC_NUMERIC regardless of constant folding");
325 local $ENV{LC_NUMERIC} = $different;
326 fresh_perl_is(<<'EOF', "$difference "x4, { eval $switches },
328 use POSIX qw(locale_h);
330 printf("%g %g %s %s ", $in, 4.2, sprintf("%g", $in), sprintf("%g", 4.2));
332 "Uses the above test to verify that on Windows the system default locale has lower priority than LC_NUMERIC");
336 # within this block, STDERR is closed. This is because fresh_perl_is()
337 # forks a shell, and some shells (like bash) can complain noisily when
338 # LC_ALL or similar is set to an invalid value
341 open my $saved_stderr, ">&STDERR" or die "Can't dup STDERR: $!";
345 local $ENV{LC_ALL} = "invalid";
346 local $ENV{LC_NUMERIC} = "invalid";
347 local $ENV{LANG} = $different;
348 local $ENV{PERL_BADLANG} = 0;
350 if (! fresh_perl_is(<<"EOF", "$difference", { eval $switches },
351 if (\$ENV{LC_ALL} ne "invalid") {
352 # Make the test pass if the sh didn't accept the ENV set
354 print "$difference\n";
358 use POSIX qw(locale_h);
362 "LANG is used if LC_ALL, LC_NUMERIC are invalid"))
364 note "To see details change this .t, do not close STDERR";
369 if ($^O eq 'MSWin32') {
370 skip("Win32 uses system default locale in preference to \"C\"",
374 local $ENV{LC_ALL} = "invalid";
375 local $ENV{LC_NUMERIC} = "invalid";
376 local $ENV{LANG} = "invalid";
377 local $ENV{PERL_BADLANG} = 0;
379 if (! fresh_perl_is(<<"EOF", 4.2, { eval $switches },
380 if (\$ENV{LC_ALL} ne "invalid") {
382 print "$difference\n";
386 use POSIX qw(locale_h);
390 'C locale is used if LC_ALL, LC_NUMERIC, LANG are invalid'))
392 note "To see details change this .t, do not close STDERR";
397 open STDERR, ">&", $saved_stderr or die "Can't dup \$saved_stderr: $!";
401 local $ENV{LC_NUMERIC} = $different;
402 fresh_perl_is(<<"EOF",
403 use POSIX qw(locale_h);
405 BEGIN { setlocale(LC_NUMERIC, \"$different\"); };
406 setlocale(LC_ALL, "C");
408 print setlocale(LC_NUMERIC);
410 "C", { stderr => 'devnull' },
411 "No compile error on v-strings when setting the locale to non-dot radix at compile time when default environment has non-dot radix");
415 skip("no locale available where LC_NUMERIC is a comma", 3);
419 fresh_perl_is(<<"EOF",
424 POSIX::setlocale(POSIX::LC_NUMERIC(),"$comma");
429 "1,5\n1.5", { stderr => 'devnull' }, "Radix print properly in locale scope, and without");
431 fresh_perl_is(<<"EOF",
432 my \$i = 1.5; # Should be exactly representable as a base 2
433 # fraction, so can use 'eq' below
436 POSIX::setlocale(POSIX::LC_NUMERIC(),"$comma");
441 "1,5\n2,5", { stderr => 'devnull' }, "Can do math when radix is a comma"); # [perl 115800]
444 unless ($have_strtod) {
445 skip("no strtod()", 1);
448 fresh_perl_is(<<"EOF",
450 POSIX::setlocale(POSIX::LC_NUMERIC(),"$comma");
451 my \$one_point_5 = POSIX::strtod("1,5");
452 \$one_point_5 =~ s/0+\$//; # Remove any trailing zeros
453 print \$one_point_5, "\n";
455 "1.5", { stderr => 'devnull' }, "POSIX::strtod() uses underlying locale");
462 my @valid_categories = valid_locale_categories();
464 my $valid_string = "";
465 my $invalid_string = "";
467 # Deliberately don't include all categories, so as to test this situation
468 for my $i (0 .. @valid_categories - 2) {
469 my $category = $valid_categories[$i];
470 if ($category ne "LC_ALL") {
471 $invalid_string .= ";" if $invalid_string ne "";
472 $invalid_string .= "$category=foo_BAR";
474 next unless $non_C_locale;
475 $valid_string .= ";" if $valid_string ne "";
476 $valid_string .= "$category=$non_C_locale";
483 POSIX::setlocale(LC_ALL, "$invalid_string");
486 is ($?, 0, "In setting complicated invalid LC_ALL, final individ category doesn't need a \';'");
488 skip("no non-C locale available", 1 ) unless $non_C_locale;
492 POSIX::setlocale(LC_ALL, "$valid_string");
495 is ($?, 0, "In setting complicated valid LC_ALL, final individ category doesn't need a \';'");