libsub-quote-perl/t/quotify.t

257 lines
5.5 KiB
Perl

use strict;
use warnings;
no warnings 'once';
use Test::More;
use Data::Dumper;
use B;
BEGIN {
$ENV{SUB_QUOTE_NO_HEX_FLOAT} = $::SUB_QUOTE_NO_HEX_FLOAT ? 1 : 0;
}
use Sub::Quote qw(
quotify
);
use constant HAVE_UTF8 => Sub::Quote::_HAVE_IS_UTF8;
use constant FLOAT_PRECISION => Sub::Quote::_FLOAT_PRECISION;
use constant HAVE_HEX_FLOAT => Sub::Quote::_HAVE_HEX_FLOAT;
use constant INF => 9**9**9**9;
use constant NAN => INF * 0;
use constant MAXUINT => ~0;
use constant MAXINT => ~0 >> 1;
use constant MININT => -(~0 >> 1) - 1;
use constant INF_NAN_SUPPORT => (
INF == 10 * INF
and !(NAN == 0 || NAN == 0.1 || NAN + 0 == 0)
);
sub _dump {
my $value = shift;
if (!defined $value) {
return 'undef';
}
elsif (is_strict_numeric($value)) {
return "$value";
}
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Useqq = 1;
my $d = Data::Dumper::Dumper("$value");
$d =~ s/\s+$//;
$d;
}
sub is_numeric {
my $flags = B::svref_2object(\($_[0]))->FLAGS;
!!( $flags & ( B::SVp_IOK | B::SVp_NOK ) )
}
sub is_float {
my $num = shift;
$num != int($num)
|| $num > ~0
|| $num < -(~0>>1)-1;
}
sub is_strict_numeric {
my $flags = B::svref_2object(\($_[0]))->FLAGS;
!!( $flags & ( B::SVp_IOK | B::SVp_NOK ) && !( $flags & B::SVp_POK ) )
}
my %flags;
{
no strict 'refs';
for my $flag (qw(
SVs_TEMP
SVs_OBJECT
SVs_GMG
SVs_SMG
SVs_RMG
SVf_IOK
SVf_NOK
SVf_POK
SVf_OOK
SVf_FAKE
SVf_READONLY
SVf_PROTECT
SVf_BREAK
SVp_IOK
SVp_NOK
SVp_POK
)) {
if (defined &{'B::'.$flag}) {
$flags{$flag} = &{'B::'.$flag};
}
}
}
sub flags {
my $flags = B::svref_2object(\($_[0]))->FLAGS;
join ' ', sort grep $flags & $flags{$_}, keys %flags;
}
# unique values taking flags into account
sub _uniq {
my %s;
grep {
my $copy = $_;
my $key = defined $_ ? flags($_).'|'.(HAVE_UTF8 && utf8::is_utf8($_) ? 1 : 0)."|$copy" : '';
!$s{$key}++;
} @_;
}
sub eval_utf8 {
my $value = shift;
my $output;
eval "use utf8; \$output = $value; 1;" or die $@;
$output;
}
my @numbers = (
-20 .. 20,
-0.0,
qw(00 01 .0 .1 0.0 0.00 00.00 0.10 0.101),
'0 but true',
'0e0',
(map +("1e$_", "-1e$_"), -50, -5, 0, 1, 5, 50),
(map 1 / $_, -10 .. -2, 2 .. 10),
(map +(1 / 9) * $_, -9 .. -1, 1 .. 9),
(map $_ x 100, 1 .. 9),
3.14159265358979323846264338327950288419716939937510,
2.71828182845904523536028747135266249775724709369995,
sqrt(2),
1.4142135623730951,
1.4142135623730954,
sqrt(3),
1.7320508075688772935274463415058722,
1.73205080756887729352744634150587224,
sqrt(5),
2.2360679774997896963,
2.23606797749978969634,
MAXUINT,
MAXUINT-1,
MAXINT,
MAXINT+1,
MININT,
(INF_NAN_SUPPORT ? (
INF, -(INF),
NAN, -(NAN),
) : ()),
);
my @strings = (
"",
(map +chr($_), 0 .. 0xff),
"\\a\"",
"\xC3\x84",
"\x{ABCD}",
"\x{1F4A9}",
);
if (HAVE_UTF8) {
utf8::downgrade($_, 1)
for @strings;
}
my @utf8_strings;
if (HAVE_UTF8) {
@utf8_strings = @strings;
utf8::upgrade($_)
for @utf8_strings;
}
my @booleans = (!1, !0);
my @quotify = (
undef,
@booleans,
(map {
my $indeterminate = $_;
my $number = $indeterminate + 0;
my $string = $indeterminate . "";
($number, $indeterminate, $string);
} @numbers),
@strings,
@utf8_strings,
);
# HAVE_UTF8 will be artificially false under quotify-5.6.t. skip utf8 strings
# in this case as they will produce warnings or errors in newer perls.
@quotify = grep !utf8::is_utf8($_), @quotify
if !HAVE_UTF8 and "$]" >= 5.025;
my $eval_utf8;
for my $value (_uniq @quotify) {
my $value_name
= _dump($value)
. (HAVE_UTF8 && utf8::is_utf8($value) ? ' utf8' : '')
. (is_strict_numeric($value) ? ' pure' : '')
. (is_numeric($value) ? ' num' : '');
my $quoted = quotify(my $copy = $value);
utf8::downgrade($quoted, 1)
if HAVE_UTF8;
my $note = "quotified as $quoted";
utf8::encode($note)
if defined &utf8::encode;
note $note;
is flags($copy), flags($value),
"$value_name: quotify doesn't modify input";
my $evaled;
eval "\$evaled = $quoted; 1" or die $@;
for my $check (
[ $evaled ],
( HAVE_UTF8 ? [ eval_utf8($quoted), ' under utf8' ] : ()),
) {
my ($check_value, $suffix) = @$check;
$suffix ||= '';
if (is_strict_numeric($value)) {
ok is_strict_numeric($check_value),
"$value_name: numeric status maintained$suffix";
}
if (is_numeric($value)) {
if ($value == $value) {
my $todo;
if (!HAVE_HEX_FLOAT && $check_value != $value && is_float($value)) {
my $diff = abs($check_value - $value);
my $accuracy = abs($value)/$diff;
my $precision = FLOAT_PRECISION + 1;
$todo = "not always accurate beyond $precision digits"
if $accuracy <= 10**$precision;
}
local $TODO = $todo
if $todo;
cmp_ok $check_value, '==', $value,
"$value_name: numeric value maintained$suffix"
or do {
diag "quotified as $quoted";
diag "got float : ".uc unpack("h*", pack("F", $check_value));
diag "expected float : ".uc unpack("h*", pack("F", $value));
};
}
else {
cmp_ok $check_value, '!=', $check_value,
"$value_name: numeric value maintained$suffix";
}
}
if (defined $value) {
cmp_ok $check_value, 'eq', $value,
"$value_name: string value maintained$suffix";
}
else {
is $check_value, undef,
"$value_name: undef maintained$suffix";
}
}
}
done_testing;