This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
lib/locale.t: Improve debug info
[perl5.git] / lib / locale.t
index 5e839bc..c8741c3 100644 (file)
@@ -31,12 +31,9 @@ my $debug = $ENV{PERL_DEBUG_FULL_TEST} // 0;
 
 # Certain tests have been shown to be problematical for a few locales.  Don't
 # fail them unless at least this percentage of the tested locales fail.
-# Some Windows machines are defective in every locale but the C, calling \t
-# printable; superscripts to be digits, etc.  See
-# http://markmail.org/message/5jwam4xsx4amsdnv.  Also on AIX machines, many
-# locales call a no-break space a graphic.
+# On AIX machines, many locales call a no-break space a graphic.
 # (There aren't 1000 locales currently in existence, so 99.9 works)
-my $acceptable_failure_percentage = ($^O =~ / ^ ( MSWin32 | AIX ) $ /ix)
+my $acceptable_failure_percentage = ($^O =~ / ^ ( AIX ) $ /ix)
                                      ? 99.9
                                      : 5;
 
@@ -53,7 +50,7 @@ my $dumper = Dumpvalue->new(
                            );
 sub debug {
   return unless $debug;
-  my($mess) = join "", @_;
+  my($mess) = join "", '# ', @_;
   chop $mess;
   print $dumper->stringify($mess,1), "\n";
 }
@@ -702,15 +699,15 @@ my $final_without_setlocale = $test_num;
 
 # Find locales.
 
-debug "Scanning for locales...\n";
+debug "Scanning for locales...\n";
 
 require POSIX; import POSIX ':locale_h';
 
-my @Locale = find_locales([ &POSIX::LC_CTYPE, &POSIX::LC_ALL ]);
+my @Locale = find_locales([ &POSIX::LC_CTYPE, &POSIX::LC_NUMERIC, &POSIX::LC_ALL ]);
 
-debug "Locales =\n";
+debug "Locales =\n";
 for ( @Locale ) {
-    debug "$_\n";
+    debug "$_\n";
 }
 
 unless (@Locale) {
@@ -808,13 +805,71 @@ sub disp_chars {
     return $output;
 }
 
+sub disp_str ($) {
+    my $string = shift;
+
+    # Displays the string unambiguously.  ASCII printables are always output
+    # as-is, though perhaps separated by blanks from other characters.  If
+    # entirely printable ASCII, just returns the string.  Otherwise if valid
+    # UTF-8 it uses the character names for non-printable-ASCII.  Otherwise it
+    # outputs hex for each non-ASCII-printable byte.
+
+    return $string if $string =~ / ^ [[:print:]]* $/xa;
+
+    my $result = "";
+    my $prev_was_punct = 1; # Beginning is considered punct
+    if (utf8::valid($string) && utf8::is_utf8($string)) {
+        use charnames ();
+        foreach my $char (split "", $string) {
+
+            # Keep punctuation adjacent to other characters; otherwise
+            # separate them with a blank
+            if ($char =~ /[[:punct:]]/a) {
+                $result .= $char;
+                $prev_was_punct = 1;
+            }
+            elsif ($char =~ /[[:print:]]/a) {
+                $result .= "  " unless $prev_was_punct;
+                $result .= $char;
+                $prev_was_punct = 0;
+            }
+            else {
+                $result .= "  " unless $prev_was_punct;
+                $result .= charnames::viacode(ord $char);
+                $prev_was_punct = 0;
+            }
+        }
+    }
+    else {
+        use bytes;
+        foreach my $char (split "", $string) {
+            if ($char =~ /[[:punct:]]/a) {
+                $result .= $char;
+                $prev_was_punct = 1;
+            }
+            elsif ($char =~ /[[:print:]]/a) {
+                $result .= " " unless $prev_was_punct;
+                $result .= $char;
+                $prev_was_punct = 0;
+            }
+            else {
+                $result .= " " unless $prev_was_punct;
+                $result .= sprintf("%02X", ord $char);
+                $prev_was_punct = 0;
+            }
+        }
+    }
+
+    return $result;
+}
+
 sub report_result {
     my ($Locale, $i, $pass_fail, $message) = @_;
     $message //= "";
     $message = "  ($message)" if $message;
     unless ($pass_fail) {
        $Problem{$i}{$Locale} = 1;
-       debug "failed $i ($test_names{$i}) with locale '$Locale'$message\n";
+       debug "failed $i ($test_names{$i}) with locale '$Locale'$message\n";
     } else {
        push @{$Okay{$i}}, $Locale;
     }
@@ -841,8 +896,8 @@ my %setlocale_failed;   # List of locales that setlocale() didn't work on
 
 foreach my $Locale (@Locale) {
     $locales_test_number = $first_locales_test_number - 1;
-    debug "#\n";
-    debug "Locale = $Locale\n";
+    debug "\n";
+    debug "Locale = $Locale\n";
 
     unless (setlocale(&POSIX::LC_ALL, $Locale)) {
         $setlocale_failed{$Locale} = $Locale;
@@ -860,14 +915,14 @@ foreach my $Locale (@Locale) {
 
     my $is_utf8_locale = is_locale_utf8($Locale);
 
-    debug "is utf8 locale? = $is_utf8_locale\n";
+    debug "is utf8 locale? = $is_utf8_locale\n";
 
     my $radix = localeconv()->{decimal_point};
     if ($radix !~ / ^ [[:ascii:]] + $/x) {
         use bytes;
         $radix = disp_chars(split "", $radix);
     }
-    debug "radix = $radix\n";
+    debug "radix = $radix\n";
 
     if (! $is_utf8_locale) {
         use locale;
@@ -931,21 +986,21 @@ foreach my $Locale (@Locale) {
 
     # Ordered, where possible,  in groups of "this is a subset of the next
     # one"
-    debug ":upper:  = ", disp_chars(@{$posixes{'upper'}}), "\n";
-    debug ":lower:  = ", disp_chars(@{$posixes{'lower'}}), "\n";
-    debug ":cased:  = ", disp_chars(@{$posixes{'cased'}}), "\n";
-    debug ":alpha:  = ", disp_chars(@{$posixes{'alpha'}}), "\n";
-    debug ":alnum:  = ", disp_chars(@{$posixes{'alnum'}}), "\n";
-    debug " w       = ", disp_chars(@{$posixes{'word'}}), "\n";
-    debug ":graph:  = ", disp_chars(@{$posixes{'graph'}}), "\n";
-    debug ":print:  = ", disp_chars(@{$posixes{'print'}}), "\n";
-    debug " d       = ", disp_chars(@{$posixes{'digit'}}), "\n";
-    debug ":xdigit: = ", disp_chars(@{$posixes{'xdigit'}}), "\n";
-    debug ":blank:  = ", disp_chars(@{$posixes{'blank'}}), "\n";
-    debug " s       = ", disp_chars(@{$posixes{'space'}}), "\n";
-    debug ":punct:  = ", disp_chars(@{$posixes{'punct'}}), "\n";
-    debug ":cntrl:  = ", disp_chars(@{$posixes{'cntrl'}}), "\n";
-    debug ":ascii:  = ", disp_chars(@{$posixes{'ascii'}}), "\n";
+    debug ":upper:  = ", disp_chars(@{$posixes{'upper'}}), "\n";
+    debug ":lower:  = ", disp_chars(@{$posixes{'lower'}}), "\n";
+    debug ":cased:  = ", disp_chars(@{$posixes{'cased'}}), "\n";
+    debug ":alpha:  = ", disp_chars(@{$posixes{'alpha'}}), "\n";
+    debug ":alnum:  = ", disp_chars(@{$posixes{'alnum'}}), "\n";
+    debug " w       = ", disp_chars(@{$posixes{'word'}}), "\n";
+    debug ":graph:  = ", disp_chars(@{$posixes{'graph'}}), "\n";
+    debug ":print:  = ", disp_chars(@{$posixes{'print'}}), "\n";
+    debug " d       = ", disp_chars(@{$posixes{'digit'}}), "\n";
+    debug ":xdigit: = ", disp_chars(@{$posixes{'xdigit'}}), "\n";
+    debug ":blank:  = ", disp_chars(@{$posixes{'blank'}}), "\n";
+    debug " s       = ", disp_chars(@{$posixes{'space'}}), "\n";
+    debug ":punct:  = ", disp_chars(@{$posixes{'punct'}}), "\n";
+    debug ":cntrl:  = ", disp_chars(@{$posixes{'cntrl'}}), "\n";
+    debug ":ascii:  = ", disp_chars(@{$posixes{'ascii'}}), "\n";
 
     foreach (keys %UPPER) {
 
@@ -969,10 +1024,10 @@ foreach my $Locale (@Locale) {
         }
     }
 
-    debug "UPPER    = ", disp_chars(sort { ord $a <=> ord $b } keys %UPPER), "\n";
-    debug "lower    = ", disp_chars(sort { ord $a <=> ord $b } keys %lower), "\n";
-    debug "BoThCaSe = ", disp_chars(sort { ord $a <=> ord $b } keys %BoThCaSe), "\n";
-    debug "Unassigned = ", disp_chars(sort { ord $a <=> ord $b } keys %Unassigned), "\n";
+    debug "UPPER    = ", disp_chars(sort { ord $a <=> ord $b } keys %UPPER), "\n";
+    debug "lower    = ", disp_chars(sort { ord $a <=> ord $b } keys %lower), "\n";
+    debug "BoThCaSe = ", disp_chars(sort { ord $a <=> ord $b } keys %BoThCaSe), "\n";
+    debug "Unassigned = ", disp_chars(sort { ord $a <=> ord $b } keys %Unassigned), "\n";
 
     my @failures;
     my @fold_failures;
@@ -1043,7 +1098,7 @@ foreach my $Locale (@Locale) {
 
     @Added_alpha = sort { ord $a <=> ord $b } @Added_alpha;
 
-    debug "Added_alpha = ", disp_chars(@Added_alpha), "\n";
+    debug "Added_alpha = ", disp_chars(@Added_alpha), "\n";
 
     # Cross-check the whole 8-bit character set.
 
@@ -1628,20 +1683,20 @@ foreach my $Locale (@Locale) {
             }
             report_result($Locale, $locales_test_number, $test == 0);
             if ($test) {
-                debug "lesser  = '$lesser'\n";
-                debug "greater = '$greater'\n";
-                debug "lesser cmp greater = ",
+                debug "lesser  = '$lesser'\n";
+                debug "greater = '$greater'\n";
+                debug "lesser cmp greater = ",
                         $lesser cmp $greater, "\n";
-                debug "greater cmp lesser = ",
+                debug "greater cmp lesser = ",
                         $greater cmp $lesser, "\n";
-                debug "(greater) from = $from, to = $to\n";
+                debug "(greater) from = $from, to = $to\n";
                 for my $ti (@test) {
                     debugf("# %-40s %-4s", $ti,
                             $test{$ti} ? 'FAIL' : 'ok');
                     if ($ti =~ /\(\.*(\$.+ +cmp +\$[^\)]+)\.*\)/) {
                         debugf("(%s == %4d)", $1, eval $1);
                     }
-                    debug "\n#";
+                    debugf("\n#");
                 }
 
                 last;
@@ -1663,10 +1718,14 @@ foreach my $Locale (@Locale) {
     my $ok12;
     my $ok13;
     my $ok14;
+    my $ok14_5;
     my $ok15;
     my $ok16;
     my $ok17;
     my $ok18;
+    my $ok19;
+    my $ok20;
+    my $ok21;
 
     my $c;
     my $d;
@@ -1728,7 +1787,7 @@ foreach my $Locale (@Locale) {
             $ok11 = $f == $c;
             $ok12 = abs(($f + $g) - 3.57) < 0.01;
             $ok13 = $w == 0;
-            $ok14 = $ok15 = $ok16 = 1;  # Skip for non-utf8 locales
+            $ok14 = $ok14_5 = $ok15 = $ok16 = 1;  # Skip for non-utf8 locales
         }
         {
             no locale;
@@ -1782,16 +1841,21 @@ foreach my $Locale (@Locale) {
             $ok13 = $w == 0;
 
             # Look for non-ASCII error messages, and verify that the first
-            # such is NOT in UTF-8 (the others almost certainly will be like
-            # the first)  See [perl #119499].
+            # such is in UTF-8 (the others almost certainly will be like the
+            # first).  This is only done if the current locale has LC_MESSAGES
             $ok14 = 1;
-            foreach my $err (keys %!) {
-                use Errno;
-                $! = eval "&Errno::$err";   # Convert to strerror() output
-                my $strerror = "$!";
-                if ("$strerror" =~ /\P{ASCII}/) {
-                    $ok14 = ! utf8::is_utf8($strerror);
-                    last;
+            $ok14_5 = 1;
+            if (setlocale(&POSIX::LC_MESSAGES, $Locale)) {
+                foreach my $err (keys %!) {
+                    use Errno;
+                    $! = eval "&Errno::$err";   # Convert to strerror() output
+                    my $strerror = "$!";
+                    if ("$strerror" =~ /\P{ASCII}/) {
+                        $ok14 = utf8::is_utf8($strerror);
+                        no locale;
+                        $ok14_5 = "$!" !~ /\P{ASCII}/;
+                        last;
+                    }
                 }
             }
 
@@ -1812,11 +1876,39 @@ foreach my $Locale (@Locale) {
         $ok18 = $j eq sprintf("%g:%g", $h, $i);
     }
 
+    $ok19 = $ok20 = 1;
+    if (setlocale(&POSIX::LC_TIME, $Locale)) { # These tests aren't affected by
+                                               # :not_characters
+        my @times = CORE::localtime();
+
+        use locale;
+        $ok19 = POSIX::strftime("%p", @times) ne "%p"; # [perl #119425]
+        my $date = POSIX::strftime("'%A'  '%B'  '%Z'  '%p'", @times);
+        debug("'Day' 'Month' 'TZ' 'am/pm' = ", disp_str($date));
+
+        # If there is any non-ascii, it better be UTF-8 in a UTF-8 locale, and
+        # not UTF-8 if the locale isn't UTF-8.
+        $ok20 = $date =~ / ^ \p{ASCII}+ $ /x
+                || $is_utf8_locale == utf8::is_utf8($date);
+    }
+
+    $ok21 = 1;
+    foreach my $err (keys %!) {
+        no locale;
+        use Errno;
+        $! = eval "&Errno::$err";   # Convert to strerror() output
+        my $strerror = "$!";
+        if ("$strerror" =~ /\P{ASCII}/) {
+            $ok21 = 0;
+            last;
+        }
+    }
+
     report_result($Locale, ++$locales_test_number, $ok1);
     $test_names{$locales_test_number} = 'Verify that an intervening printf doesn\'t change assignment results';
     my $first_a_test = $locales_test_number;
 
-    debug "$first_a_test..$locales_test_number: \$a = $a, \$b = $b, Locale = $Locale\n";
+    debug "$first_a_test..$locales_test_number: \$a = $a, \$b = $b, Locale = $Locale\n";
 
     report_result($Locale, ++$locales_test_number, $ok2);
     $test_names{$locales_test_number} = 'Verify that an intervening sprintf doesn\'t change assignment results';
@@ -1835,7 +1927,7 @@ foreach my $Locale (@Locale) {
     $test_names{$locales_test_number} = 'Verify that a different locale radix works when doing "==" with a scalar and an intervening sprintf';
     $problematical_tests{$locales_test_number} = 1;
 
-    debug "$first_c_test..$locales_test_number: \$c = $c, \$d = $d, Locale = $Locale\n";
+    debug "$first_c_test..$locales_test_number: \$c = $c, \$d = $d, Locale = $Locale\n";
 
     report_result($Locale, ++$locales_test_number, $ok6);
     $test_names{$locales_test_number} = 'Verify that can assign stringified under inner no-locale block';
@@ -1848,7 +1940,7 @@ foreach my $Locale (@Locale) {
     $test_names{$locales_test_number} = 'Verify that "==" with a scalar and an intervening sprintf still works in inner no locale';
     $problematical_tests{$locales_test_number} = 1;
 
-    debug "$first_e_test..$locales_test_number: \$e = $e, no locale\n";
+    debug "$first_e_test..$locales_test_number: \$e = $e, no locale\n";
 
     report_result($Locale, ++$locales_test_number, $ok9);
     $test_names{$locales_test_number} = 'Verify that after a no-locale block, a different locale radix still works when doing "==" with a constant';
@@ -1872,7 +1964,10 @@ foreach my $Locale (@Locale) {
     $problematical_tests{$locales_test_number} = 1;
 
     report_result($Locale, ++$locales_test_number, $ok14);
-    $test_names{$locales_test_number} = 'Verify that non-ASCII UTF-8 error messages are NOT in UTF-8';
+    $test_names{$locales_test_number} = 'Verify that non-ASCII UTF-8 error messages are in UTF-8';
+
+    report_result($Locale, ++$locales_test_number, $ok14_5);
+    $test_names{$locales_test_number} = '... and are ASCII outside "use locale"';
 
     report_result($Locale, ++$locales_test_number, $ok15);
     $test_names{$locales_test_number} = 'Verify that a number with a UTF-8 radix has a UTF-8 stringification';
@@ -1886,7 +1981,18 @@ foreach my $Locale (@Locale) {
     report_result($Locale, ++$locales_test_number, $ok18);
     $test_names{$locales_test_number} = 'Verify that a sprintf of a number back within locale scope uses locale radix';
 
-    debug "# $first_f_test..$locales_test_number: \$f = $f, \$g = $g, back to locale = $Locale\n";
+    report_result($Locale, ++$locales_test_number, $ok19);
+    $test_names{$locales_test_number} = 'Verify that strftime doesn\'t return "%p" in locales where %p is empty';
+
+    report_result($Locale, ++$locales_test_number, $ok20);
+    $test_names{$locales_test_number} = 'Verify that strftime returns date with UTF-8 flag appropriately set';
+    $problematical_tests{$locales_test_number} = 1;   # This is broken in
+                                                      # OS X 10.9.3
+
+    report_result($Locale, ++$locales_test_number, $ok21);
+    $test_names{$locales_test_number} = '"$!" is ASCII only outside of locale scope';
+
+    debug "$first_f_test..$locales_test_number: \$f = $f, \$g = $g, back to locale = $Locale\n";
 
     # Does taking lc separately differ from taking
     # the lc "in-line"?  (This was the bug 19990704.002, change #3568.)
@@ -1950,7 +2056,7 @@ foreach my $Locale (@Locale) {
             if (! $is_utf8_locale) {
                 my $y = lc $x;
                 next unless uc $y eq $x;
-                debug_more( "UPPER=", disp_chars(($x)),
+                debug_more( "UPPER=", disp_chars(($x)),
                             "; lc=", disp_chars(($y)), "; ",
                             "; fc=", disp_chars((fc $x)), "; ",
                             disp_chars(($x)), "=~/", disp_chars(($y)), "/i=",
@@ -1996,7 +2102,7 @@ foreach my $Locale (@Locale) {
                 use locale ':not_characters';
                 my $y = lc $x;
                 next unless uc $y eq $x;
-                debug_more( "UPPER=", disp_chars(($x)),
+                debug_more( "UPPER=", disp_chars(($x)),
                             "; lc=", disp_chars(($y)), "; ",
                             "; fc=", disp_chars((fc $x)), "; ",
                             disp_chars(($x)), "=~/", disp_chars(($y)), "/i=",
@@ -2018,7 +2124,7 @@ foreach my $Locale (@Locale) {
             if (! $is_utf8_locale) {
                 my $y = uc $x;
                 next unless lc $y eq $x;
-                debug_more( "lower=", disp_chars(($x)),
+                debug_more( "lower=", disp_chars(($x)),
                             "; uc=", disp_chars(($y)), "; ",
                             "; fc=", disp_chars((fc $x)), "; ",
                             disp_chars(($x)), "=~/", disp_chars(($y)), "/i=",
@@ -2039,7 +2145,7 @@ foreach my $Locale (@Locale) {
                 use locale ':not_characters';
                 my $y = uc $x;
                 next unless lc $y eq $x;
-                debug_more( "lower=", disp_chars(($x)),
+                debug_more( "lower=", disp_chars(($x)),
                             "; uc=", disp_chars(($y)), "; ",
                             "; fc=", disp_chars((fc $x)), "; ",
                             disp_chars(($x)), "=~/", disp_chars(($y)), "/i=",
@@ -2114,12 +2220,22 @@ foreach $test_num ($first_locales_test_number..$final_locales_test_number) {
             my $percent_fail = (int(.5 + (1000 * scalar(keys $Problem{$test_num})
                                           / scalar(@Locale))))
                                / 10;
-            if (! $debug && $percent_fail < $acceptable_failure_percentage)
-            {
-                $test_names{$test_num} .= 'TODO';
-                print "# ", 100 - $percent_fail, "% of locales pass the following test, so it is likely that the failures\n";
-                print "# are errors in the locale definitions.  The test is marked TODO, as the\n";
-                print "# problem is not likely to be Perl's\n";
+            if ($percent_fail < $acceptable_failure_percentage) {
+                if (! $debug) {
+                    $test_names{$test_num} .= 'TODO';
+                    print "# ", 100 - $percent_fail, "% of locales pass the following test, so it is likely that the failures\n";
+                    print "# are errors in the locale definitions.  The test is marked TODO, as the\n";
+                    print "# problem is not likely to be Perl's\n";
+                }
+            }
+            if ($debug) {
+                print "# $percent_fail% of locales (",
+                      scalar(keys $Problem{$test_num}),
+                      " of ",
+                      scalar(@Locale),
+                      ") fail the above test (TODO cut-off is ",
+                      $acceptable_failure_percentage,
+                      "%)\n";
             }
         }
         print "#\n";